Automated browser testing for the modern web development stack.
—
TestCafe's programmatic API enables you to create, configure, and run tests through JavaScript code rather than the command line, providing fine-grained control over test execution and integration with other tools.
Create and configure TestCafe instances programmatically.
/**
* Creates a TestCafe instance for programmatic test execution
* @param hostname - Hostname for TestCafe servers (default: 'localhost')
* @param port1 - Port for main TestCafe server (default: auto-assigned)
* @param port2 - Port for file server (default: auto-assigned)
* @param sslOptions - SSL configuration for HTTPS
* @param developmentMode - Enable development mode features
* @param retryTestPages - Retry test pages on failure
* @param cache - Enable caching
* @param configFile - Path to configuration file
* @returns Promise resolving to TestCafe instance
*/
function createTestCafe(
hostname?: string,
port1?: number,
port2?: number,
sslOptions?: SSLOptions,
developmentMode?: boolean,
retryTestPages?: boolean,
cache?: boolean,
configFile?: string
): Promise<TestCafe>;
// Alternative: Configuration object approach
function createTestCafe(config?: TestCafeConfiguration): Promise<TestCafe>;
interface TestCafe {
/** Create a test runner instance */
createRunner(): Runner;
/** Create browser connection for remote browsers */
createBrowserConnection(): Promise<BrowserConnection>;
/** Create live mode runner */
createLiveModeRunner(): LiveModeRunner;
/** Close TestCafe instance and cleanup resources */
close(): Promise<void>;
}
interface SSLOptions {
key?: string;
cert?: string;
pfx?: string;
passphrase?: string;
}
interface TestCafeConfiguration {
hostname?: string;
port1?: number;
port2?: number;
ssl?: SSLOptions;
developmentMode?: boolean;
retryTestPages?: boolean;
cache?: boolean;
configFile?: string;
}Usage Examples:
import createTestCafe from 'testcafe';
// Basic TestCafe instance
async function runBasicTests() {
const testcafe = await createTestCafe('localhost', 1337, 1338);
try {
const runner = testcafe.createRunner();
const failedCount = await runner
.src(['tests/fixture1.js', 'tests/fixture2.js'])
.browsers(['chrome', 'firefox'])
.run();
console.log(`Tests failed: ${failedCount}`);
} finally {
await testcafe.close();
}
}
// Configuration object approach
async function runConfiguredTests() {
const testcafe = await createTestCafe({
hostname: 'localhost',
port1: 1337,
port2: 1338,
ssl: {
key: './ssl/private-key.pem',
cert: './ssl/certificate.pem'
},
developmentMode: true
});
try {
// Test execution code
} finally {
await testcafe.close();
}
}
// Auto-port assignment
async function runAutoPortTests() {
const testcafe = await createTestCafe(); // Uses default localhost and auto-assigned ports
try {
const runner = testcafe.createRunner();
await runner.src('tests/**/*.js').browsers('chrome').run();
} finally {
await testcafe.close();
}
}Configure test execution through the Runner interface.
interface Runner {
/** Set test source files or globs */
src(source: string | string[]): Runner;
/** Set target browsers */
browsers(browsers: string | string[]): Runner;
/** Set test execution concurrency */
concurrency(concurrency: number): Runner;
/** Set test reporter */
reporter(reporter: string | ReporterPlugin, output?: string): Runner;
/** Set test filter */
filter(filter: (testName: string, fixtureName: string, fixturePath: string, testMeta: object, fixtureMeta: object) => boolean): Runner;
/** Run tests and return number of failed tests */
run(options?: RunOptions): Promise<number>;
/** Stop test execution */
stop(): Promise<void>;
}
interface RunOptions {
/** Skip JS errors in tests */
skipJsErrors?: boolean;
/** Disable page reloads */
disablePageReloads?: boolean;
/** Quarantine mode for unstable tests */
quarantineMode?: boolean | QuarantineOptions;
/** Debug mode */
debugMode?: boolean;
/** Debug on fail */
debugOnFail?: boolean;
/** Skip uncaught errors */
skipUncaughtErrors?: boolean;
/** Selector timeout */
selectorTimeout?: number;
/** Assertion timeout */
assertionTimeout?: number;
/** Page load timeout */
pageLoadTimeout?: number;
/** Test execution speed */
speed?: number;
/** Stop on first test failure */
stopOnFirstFail?: boolean;
}
interface QuarantineOptions {
/** Number of attempts to run failing tests */
attemptLimit?: number;
/** Number of successful runs required to pass */
successThreshold?: number;
}Usage Examples:
async function configureRunner() {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
const failedCount = await runner
.src(['tests/login.js', 'tests/checkout.js'])
.browsers(['chrome:headless', 'firefox:headless'])
.concurrency(3)
.reporter('spec')
.filter((testName, fixtureName) => {
return testName.includes('critical') || fixtureName.includes('smoke');
})
.run({
skipJsErrors: true,
quarantineMode: true,
speed: 0.8,
stopOnFirstFail: false,
selectorTimeout: 10000,
assertionTimeout: 5000
});
if (failedCount > 0) {
console.error(`${failedCount} tests failed`);
process.exit(1);
}
} finally {
await testcafe.close();
}
}
// Multiple reporter configuration
async function runWithMultipleReporters() {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
await runner
.src('tests/**/*.js')
.browsers('chrome')
.reporter([
{
name: 'spec',
output: process.stdout
},
{
name: 'json',
output: 'reports/test-results.json'
},
{
name: 'xunit',
output: 'reports/test-results.xml'
}
])
.run();
} finally {
await testcafe.close();
}
}Manage browser connections and configurations.
interface BrowserConnection {
/** Browser connection URL for remote browsers */
url: string;
/** Unique identifier for the connection */
id: string;
/** Connection ready promise */
ready: Promise<void>;
/** Establish browser connection */
establish(userAgent: string): Promise<void>;
/** Close browser connection */
close(): Promise<void>;
}
interface LiveModeRunner extends Runner {
/** Watch files for changes */
watchFiles(files: string | string[]): LiveModeRunner;
/** Start live mode */
run(): Promise<void>;
/** Stop live mode */
stop(): Promise<void>;
}Usage Examples:
// Remote browser connection
async function setupRemoteBrowser() {
const testcafe = await createTestCafe();
try {
const connection = await testcafe.createBrowserConnection();
console.log(`Connect remote browser to: ${connection.url}`);
// Wait for browser to connect
await connection.ready;
console.log('Remote browser connected');
const runner = testcafe.createRunner();
await runner
.src('tests/remote-tests.js')
.browsers(connection)
.run();
} finally {
await testcafe.close();
}
}
// Live mode testing
async function runLiveMode() {
const testcafe = await createTestCafe();
try {
const liveRunner = testcafe.createLiveModeRunner();
await liveRunner
.src('tests/live-tests.js')
.browsers('chrome')
.watchFiles(['tests/**/*.js', 'src/**/*.js'])
.run();
} finally {
await testcafe.close();
}
}
// Multiple browser connections
async function runMultipleBrowsers() {
const testcafe = await createTestCafe();
try {
const connection1 = await testcafe.createBrowserConnection();
const connection2 = await testcafe.createBrowserConnection();
console.log(`Browser 1: ${connection1.url}`);
console.log(`Browser 2: ${connection2.url}`);
await Promise.all([connection1.ready, connection2.ready]);
const runner = testcafe.createRunner();
await runner
.src('tests/cross-browser.js')
.browsers([connection1, connection2, 'chrome:headless'])
.concurrency(3)
.run();
} finally {
await testcafe.close();
}
}Advanced TestCafe configuration and customization options.
// Custom reporter integration
interface ReporterPlugin {
reportTaskStart(startTime: Date, userAgents: string[], testCount: number): void;
reportFixtureStart(name: string, path: string, meta: object): void;
reportTestStart(name: string, meta: object): void;
reportTestDone(name: string, testRunInfo: TestRunInfo): void;
reportTaskDone(endTime: Date, passed: number, warnings: string[], result: object): void;
}
interface TestRunInfo {
errs: TestError[];
durationMs: number;
unstable: boolean;
screenshotPath: string;
screenshots: Screenshot[];
videos: Video[];
quarantine: object;
skipped: boolean;
}
// Custom browser provider
interface BrowserProvider {
openBrowser(id: string, pageUrl: string, browserName: string): Promise<void>;
closeBrowser(id: string): Promise<void>;
resizeWindow(id: string, width: number, height: number): Promise<void>;
takeScreenshot(id: string, screenshotPath: string, pageWidth: number, pageHeight: number): Promise<void>;
}Usage Examples:
// Custom reporter
class CustomReporter {
reportTaskStart(startTime, userAgents, testCount) {
console.log(`Starting ${testCount} tests at ${startTime}`);
console.log(`User agents: ${userAgents.join(', ')}`);
}
reportFixtureStart(name, path, meta) {
console.log(`\n=== Fixture: ${name} ===`);
console.log(`Path: ${path}`);
}
reportTestStart(name, meta) {
console.log(`\n► Running: ${name}`);
}
reportTestDone(name, testRunInfo) {
const status = testRunInfo.errs.length > 0 ? 'FAILED' : 'PASSED';
const duration = testRunInfo.durationMs;
console.log(`◄ ${status}: ${name} (${duration}ms)`);
if (testRunInfo.errs.length > 0) {
testRunInfo.errs.forEach(err => {
console.error(` Error: ${err.errMsg}`);
});
}
if (testRunInfo.screenshots.length > 0) {
console.log(` Screenshots: ${testRunInfo.screenshots.length}`);
}
}
reportTaskDone(endTime, passed, warnings, result) {
console.log(`\n=== Test Results ===`);
console.log(`Passed: ${passed}`);
console.log(`Failed: ${result.failedCount}`);
console.log(`Total: ${result.passedCount + result.failedCount}`);
console.log(`Duration: ${endTime - result.startTime}ms`);
if (warnings.length > 0) {
console.log(`Warnings: ${warnings.length}`);
}
}
}
async function runWithCustomReporter() {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
await runner
.src('tests/**/*.js')
.browsers('chrome')
.reporter(new CustomReporter())
.run();
} finally {
await testcafe.close();
}
}
// Configuration with environment variables
async function runEnvironmentSpecificTests() {
const config = {
hostname: process.env.TESTCAFE_HOSTNAME || 'localhost',
port1: parseInt(process.env.TESTCAFE_PORT1) || undefined,
port2: parseInt(process.env.TESTCAFE_PORT2) || undefined,
developmentMode: process.env.NODE_ENV === 'development'
};
const testcafe = await createTestCafe(config);
try {
const runner = testcafe.createRunner();
const browsers = process.env.BROWSERS
? process.env.BROWSERS.split(',')
: ['chrome:headless'];
const sources = process.env.TEST_FILES
? process.env.TEST_FILES.split(',')
: ['tests/**/*.js'];
await runner
.src(sources)
.browsers(browsers)
.concurrency(parseInt(process.env.CONCURRENCY) || 1)
.run({
speed: parseFloat(process.env.TEST_SPEED) || 1,
skipJsErrors: process.env.SKIP_JS_ERRORS === 'true',
quarantineMode: process.env.QUARANTINE_MODE === 'true'
});
} finally {
await testcafe.close();
}
}Real-world integration examples with build systems and CI/CD.
// Integration with build systems
async function integrationWithBuildSystem() {
// Pre-test setup
console.log('Building application...');
await buildApplication();
console.log('Starting test server...');
const server = await startTestServer();
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
const failedCount = await runner
.src('tests/integration/**/*.js')
.browsers(['chrome:headless'])
.run({
pageLoadTimeout: 30000,
assertionTimeout: 10000
});
// Post-test cleanup
console.log('Stopping test server...');
await server.close();
if (failedCount > 0) {
throw new Error(`${failedCount} tests failed`);
}
} finally {
await testcafe.close();
}
}
// Parallel test execution
async function runParallelTests() {
const testGroups = [
'tests/unit/**/*.js',
'tests/integration/**/*.js',
'tests/e2e/**/*.js'
];
const results = await Promise.all(
testGroups.map(async (group) => {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
const failedCount = await runner
.src(group)
.browsers('chrome:headless')
.run();
return { group, failedCount };
} finally {
await testcafe.close();
}
})
);
const totalFailed = results.reduce((sum, result) => sum + result.failedCount, 0);
results.forEach(result => {
console.log(`${result.group}: ${result.failedCount} failed`);
});
if (totalFailed > 0) {
process.exit(1);
}
}
// Conditional test execution
async function runConditionalTests() {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
// Filter tests based on environment or conditions
const testFilter = (testName, fixtureName, fixturePath, testMeta, fixtureMeta) => {
// Skip slow tests in CI
if (process.env.CI && testMeta.slow) {
return false;
}
// Run only smoke tests in staging
if (process.env.ENVIRONMENT === 'staging' && !testMeta.smoke) {
return false;
}
// Skip browser-specific tests
if (testMeta.browserSpecific && !testMeta.browserSpecific.includes(process.env.BROWSER)) {
return false;
}
return true;
};
await runner
.src('tests/**/*.js')
.browsers(process.env.BROWSER || 'chrome:headless')
.filter(testFilter)
.run();
} finally {
await testcafe.close();
}
}Install with Tessl CLI
npx tessl i tessl/npm-testcafe