A comprehensive testing framework specifically designed for web components with browser-based testing environment, Mocha/Chai/Sinon integration, and support for both local and remote testing via Sauce Labs.
—
The Web Component Tester browser environment provides a comprehensive client-side testing runtime with pre-loaded testing libraries and web component-specific utilities.
The main browser-side API exposed through the global WCT object.
/**
* Global WCT object providing browser-side testing functionality
*/
interface WCT {
/** Shared object for data between test suites */
share: any;
/** Load and run test suites from files */
loadSuites(files: string[]): void;
/** Current browser configuration */
_config: Config;
/** Current test reporter instance */
_reporter: any;
/** Child runner constructor for nested tests */
_ChildRunner: typeof ChildRunner;
}Usage Examples:
// Load test suites dynamically
WCT.loadSuites([
'test/unit-tests.js',
'test/integration-tests.html'
]);
// Share data between test suites
WCT.share.testData = { userId: 123, apiKey: 'test-key' };
// Access shared data in tests
suite('API Tests', function() {
test('uses shared data', function() {
assert.equal(WCT.share.testData.userId, 123);
});
});Web component-specific testing utilities available globally in the browser.
/**
* Safely execute a test step with error handling
* @param callback - Node-style callback function
* @param stepFn - Function to execute safely
*/
function safeStep(callback: (error?: any) => void, stepFn: () => void): void;
/**
* Run test at declaration time (before Mocha starts)
* @param name - Test name
* @param testFn - Test function (sync or async)
*/
function testImmediate(name: string, testFn: Function): void;
/**
* Async-only variant of testImmediate
* @param name - Test name
* @param testFn - Async test function
*/
function testImmediateAsync(name: string, testFn: Function): void;
/**
* Trigger flush of pending events, observations, and DOM updates
* @param callback - Function to call after flush completes
*/
function flush(callback: () => void): void;
/**
* Advance a single animation frame with flush
* @param callback - Function to call after animation frame
*/
function animationFrameFlush(callback: () => void): void;
/**
* Wait for a condition to be met with timeout
* @param fn - Function to test (should throw if condition not met)
* @param next - Function to call when condition is met
* @param intervalOrMutationEl - Polling interval or mutation element
* @param timeout - Maximum wait time in milliseconds
* @param timeoutTime - Internal parameter (calculated automatically)
*/
function waitFor(
fn: () => void,
next: () => void,
intervalOrMutationEl?: number | MutationEl,
timeout?: number,
timeoutTime?: number
): void;Usage Examples:
// Safe step execution
test('handles async operations', function(done) {
safeStep(done, function() {
// This code is wrapped in try/catch
myAsyncOperation().then(function(result) {
assert.equal(result.status, 'success');
});
});
});
// Immediate test execution
testImmediate('runs before Mocha setup', function() {
// This runs at page load time
assert.isTrue(document.readyState !== 'loading');
});
// DOM flush utility
test('waits for DOM updates', function(done) {
element.someProperty = 'new value';
flush(function() {
// DOM is now updated
assert.equal(element.textContent, 'new value');
done();
});
});
// Animation frame utility
test('waits for animations', function(done) {
element.classList.add('animate');
animationFrameFlush(function() {
// Animation frame has completed
assert.isTrue(element.classList.contains('animated'));
done();
});
});
// Wait for condition
test('waits for condition', function(done) {
let ready = false;
setTimeout(() => ready = true, 500);
waitFor(
function() {
if (!ready) throw new Error('Not ready yet');
},
function() {
assert.isTrue(ready);
done();
},
50, // Check every 50ms
1000 // Timeout after 1 second
);
});Client-side configuration interface for customizing the browser environment.
interface Config {
/** Scripts to load before WCT starts */
environmentScripts: string[];
/** HTML imports to load */
environmentImports: string[];
/** Absolute root for client scripts */
root: string | null;
/** Wait for web component frameworks to load */
waitForFrameworks: boolean;
/** Custom wait function */
waitFor: Function | null;
/** Maximum concurrent HTML suites */
numConcurrentSuites: number;
/** Treat console.error as test failure */
trackConsoleError: boolean;
/** Mocha configuration options */
mochaOptions: MochaSetupOptions;
/** Enable debug logging */
verbose: boolean;
}
/**
* Setup browser configuration
* @param options - Configuration options to merge
*/
function setup(options: Partial<Config>): void;
/**
* Get configuration value
* @param key - Configuration key
* @returns Configuration value
*/
function get<K extends keyof Config>(key: K): Config[K];Usage Examples:
<script>
// Configure before loading browser.js
WCT = {
environmentScripts: [
'custom-assertion-library.js',
'test-utilities.js'
],
waitForFrameworks: true,
mochaOptions: {
timeout: 30000,
ui: 'bdd'
},
verbose: true
};
</script>
<script src="../web-component-tester/browser.js"></script>Integration with @polymer/test-fixture for DOM state management.
/**
* Create DOM fixture from template
* @param id - Fixture template ID
* @returns Created DOM element(s)
*/
function fixture(id: string): Element | DocumentFragment;Usage Examples:
<test-fixture id="basic">
<template>
<my-element foo="bar"></my-element>
</template>
</test-fixture>
<test-fixture id="multiple">
<template>
<div>First</div>
<div>Second</div>
</template>
</test-fixture>
<script>
suite('<my-element>', function() {
let element;
setup(function() {
element = fixture('basic');
});
test('has initial properties', function() {
assert.equal(element.foo, 'bar');
});
test('handles multiple elements', function() {
const elements = fixture('multiple');
assert.equal(elements.length, 2);
});
});
</script>Integration with accessibility-developer-tools for automated accessibility testing.
/**
* Run accessibility test suite on a fixture
* @param fixtureId - ID of test fixture to test
* @param ignoredTests - Array of test names to ignore (optional)
* @param beforeEach - Function to run before each test (optional)
*/
function a11ySuite(
fixtureId: string,
ignoredTests?: string[],
beforeEach?: Function
): void;Usage Examples:
<test-fixture id="basic-button">
<template>
<paper-button>Click me</paper-button>
</template>
</test-fixture>
<test-fixture id="form-elements">
<template>
<paper-input label="Name"></paper-input>
<paper-button>Submit</paper-button>
</template>
</test-fixture>
<script>
// Basic accessibility testing
a11ySuite('basic-button');
// Ignore specific tests
a11ySuite('form-elements', [
'AX_COLOR_01', // Skip color contrast test
'AX_FOCUS_02' // Skip focus management test
]);
// Custom setup before each accessibility test
a11ySuite('form-elements', [], function() {
// Setup test state
fixture('form-elements').querySelector('paper-input').value = 'Test';
});
</script>Dynamic test suite loading functionality.
/**
* Load and execute test suites from file URLs
* @param files - Array of test file URLs (.js or .html)
*/
function loadSuites(files: string[]): void;Usage Examples:
<!doctype html>
<html>
<head>
<script src="../web-component-tester/browser.js"></script>
</head>
<body>
<script>
// Load multiple test suites
WCT.loadSuites([
'unit/element-tests.html',
'unit/behavior-tests.js',
'integration/full-tests.html'
]);
</script>
</body>
</html>The browser environment automatically loads these testing libraries:
suite(), test(), setup(), teardown()describe(), it(), before(), after(), beforeEach(), afterEach()expect(value).to.equal(expected)assert.equal(actual, expected)sinon.spy(), sinon.stub()sinon.mock(object)sinon.useFakeTimers()expect(spy).to.have.been.called_.forEach, _.map, etc.)async.parallel, async.series)interface MutationEl {
onMutation(mutationEl: this, cb: () => void): void;
}
interface MochaSetupOptions {
ui?: string;
timeout?: number;
slow?: number;
bail?: boolean;
grep?: string | RegExp;
}Install with Tessl CLI
npx tessl i tessl/npm-web-component-tester