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 programmatic API provides full control over test execution with event-based progress tracking and comprehensive configuration options.
Runs a suite of web component tests with lifecycle events and comprehensive error handling.
/**
* Runs a suite of web component tests
* @param options - Configuration object or Context instance
* @returns Promise that resolves when all tests complete
*/
function test(options: Config | Context): Promise<void>;Usage Examples:
const wct = require('web-component-tester');
// Basic test run
await wct.test({
suites: ['test/**/*.html'],
verbose: true
});
// Advanced configuration
await wct.test({
suites: ['test/unit/*.html', 'test/integration/*.js'],
root: './app',
testTimeout: 30000,
persistent: false,
plugins: {
local: {
browsers: ['chrome', 'firefox']
}
}
});
// Using Context for advanced control
const { Context } = wct;
const context = new Context({
verbose: true,
suites: ['test/*.html']
});
context.on('test-end', (browser, test, stats) => {
console.log(`Test "${test.title}" ${test.state}`);
});
await wct.test(context);Main configuration object for controlling test execution behavior.
interface Config {
/** Array of test file patterns to run */
suites?: string[];
/** Output stream for test results */
output?: NodeJS.WritableStream;
/** Enable TTY-specific output formatting */
ttyOutput?: boolean;
/** Enable verbose logging */
verbose?: boolean;
/** Suppress output */
quiet?: boolean;
/** Show expanded test results */
expanded?: boolean;
/** Root directory for test files */
root?: string;
/** Test timeout in milliseconds */
testTimeout?: number;
/** Keep browsers open after tests complete */
persistent?: boolean;
/** Additional scripts to load in browser */
extraScripts?: string[];
/** Package name for client-side WCT code */
wctPackageName?: string;
/** Browser-side configuration options */
clientOptions?: {
root?: string;
verbose?: boolean;
environmentScripts?: string[];
};
/** Active browser configurations */
activeBrowsers?: BrowserDef[];
/** Plugin configurations */
plugins?: {[key: string]: any};
/** Browser-specific options */
browserOptions?: any;
/** Skip cleanup after tests */
skipCleanup?: boolean;
}Configuration for individual browser instances.
interface BrowserDef {
/** Browser name (chrome, firefox, safari, etc.) */
browserName?: string;
/** Platform/OS name */
platform?: string;
/** Browser version */
version?: string;
/** Unique browser ID (assigned automatically) */
id?: number;
/** Variant name for dependency testing */
variant?: string;
/** Additional browser-specific capabilities */
[key: string]: any;
}
type Browser = string | BrowserDef;The test runner emits comprehensive lifecycle events for monitoring test progress:
// Lifecycle Events
interface TestEvents {
'run-start': (options: Config) => void;
'browser-init': (browser: BrowserDef, stats: TestStats) => void;
'browser-start': (browser: BrowserDef, metadata: any, stats: TestStats) => void;
'sub-suite-start': (browser: BrowserDef, sharedState: any, stats: TestStats) => void;
'test-start': (browser: BrowserDef, test: TestCase, stats: TestStats) => void;
'test-end': (browser: BrowserDef, test: TestCase, stats: TestStats) => void;
'sub-suite-end': (browser: BrowserDef, sharedState: any, stats: TestStats) => void;
'browser-end': (browser: BrowserDef, error: any, stats: TestStats) => void;
'run-end': (error: any) => void;
// Log Events
'log:debug': (message: string, ...args: any[]) => void;
'log:info': (message: string, ...args: any[]) => void;
'log:warn': (message: string, ...args: any[]) => void;
'log:error': (message: string, ...args: any[]) => void;
}const wct = require('web-component-tester');
const { Context } = wct;
const context = new Context({
suites: ['test/*.html'],
verbose: true
});
// Track test progress
context.on('run-start', (options) => {
console.log('Starting test run with', options.activeBrowsers.length, 'browsers');
});
context.on('browser-start', (browser, metadata, stats) => {
console.log(`Browser ${browser.browserName} started`);
});
context.on('test-end', (browser, test, stats) => {
const status = test.state === 'passed' ? '✓' : '✗';
console.log(`${status} ${test.title} (${browser.browserName})`);
});
context.on('browser-end', (browser, error, stats) => {
if (error) {
console.error(`Browser ${browser.browserName} failed:`, error);
} else {
console.log(`Browser ${browser.browserName} completed: ${stats.passed}/${stats.total} passed`);
}
});
context.on('run-end', (error) => {
if (error) {
console.error('Test run failed:', error);
} else {
console.log('All tests completed successfully');
}
});
await wct.test(context);const wct = require('web-component-tester');
const fs = require('fs');
class CustomReporter {
constructor() {
this.results = [];
}
onTestEnd(browser, test, stats) {
this.results.push({
browser: browser.browserName,
test: test.title,
state: test.state,
duration: test.duration,
timestamp: new Date().toISOString()
});
}
onRunEnd() {
fs.writeFileSync('test-results.json', JSON.stringify(this.results, null, 2));
}
}
const context = new wct.Context({ suites: ['test/*.html'] });
const reporter = new CustomReporter();
context.on('test-end', reporter.onTestEnd.bind(reporter));
context.on('run-end', reporter.onRunEnd.bind(reporter));
await wct.test(context);const wct = require('web-component-tester');
try {
await wct.test({
suites: ['test/*.html'],
testTimeout: 10000
});
console.log('All tests passed!');
} catch (error) {
console.error('Test run failed:', error.message);
// Check if it's a test failure vs system error
if (error.message.includes('failed')) {
process.exit(1); // Test failures
} else {
console.error('System error occurred');
process.exit(2); // System errors
}
}const wct = require('web-component-tester');
await wct.test({
suites: ['test/*.html'],
plugins: {
// Local browser testing
local: {
browsers: ['chrome', 'firefox'],
browserOptions: {
chrome: ['--headless', '--disable-gpu'],
firefox: ['-headless']
}
},
// Sauce Labs testing
sauce: {
username: process.env.SAUCE_USERNAME,
accessKey: process.env.SAUCE_ACCESS_KEY,
browsers: [
{ browserName: 'chrome', platform: 'Windows 10' },
{ browserName: 'firefox', platform: 'macOS 10.15' }
]
}
}
});interface TestCase {
title: string;
state: 'passed' | 'failed' | 'pending';
duration?: number;
error?: Error;
}
interface TestStats {
passed: number;
pending: number;
failed: number;
total: number;
}
interface Context extends EventEmitter {
options: Config;
emitHook(name: string): Promise<void>;
plugins(): Promise<Plugin[]>;
}Install with Tessl CLI
npx tessl i tessl/npm-web-component-tester