Implementation:DevExpress Testcafe Runner Fluent API
| Knowledge Sources | |
|---|---|
| Domains | Testing, Web_Automation |
| Last Updated | 2026-02-12 04:00 GMT |
Overview
Concrete fluent interface for configuring test execution through chainable method calls provided by the TestCafe Runner class.
Description
The Runner class provides a comprehensive fluent API with methods that configure different aspects of test execution and return the runner instance to enable chaining. Each method stores configuration in the runner's internal _options object and some methods enforce single-call constraints using the apiMethodWasCalled FlagList to prevent conflicting configurations.
Methods fall into two categories: single-call methods (src, browsers, reporter, clientScripts) that throw errors if called multiple times, and multi-call methods (concurrency, screenshots, video, filter, etc.) that can be called multiple times with later calls overriding earlier ones.
Usage
Use the fluent API to configure a runner after creation and before calling run. Chain methods in a logical order: test sources, browsers, reporters, then optional configuration like screenshots, videos, and concurrency. The API enables readable, self-documenting test setup code.
Code Reference
Source Location
- Repository: testcafe
- File: src/runner/index.js
- Lines: 700-798 (fluent API methods)
Signature
class Runner {
// Test sources
src(...sources: string[]): Runner
// Browsers
browsers(...browsers: string[]): Runner
// Concurrency
concurrency(n: number): Runner
// Reporter
reporter(name: string, output?: Writable): Runner
// Test filtering
filter(fn: (testName, fixtureName, fixturePath, testMeta, fixtureMeta) => boolean): Runner
// Proxy configuration
useProxy(proxy: string, proxyBypass?: string | string[]): Runner
// Screenshots
screenshots(path: string, takeOnFails?: boolean, pathPattern?: string): Runner
screenshots(options: ScreenshotOptions): Runner
// Video recording
video(path: string, options?: VideoOptions, encodingOptions?: VideoEncodingOptions): Runner
// Tested application
startApp(command: string, initDelay?: number): Runner
// TypeScript configuration
tsConfigPath(path: string): Runner
// Client scripts
clientScripts(...scripts: (string | ClientScriptInit)[]): Runner
// Compiler options
compilerOptions(opts: CompilerOptions): Runner
// Execute tests
run(options?: RunOptions): Promise<number>
// Stop execution
stop(): Promise<void>
}
Import
const createTestCafe = require('testcafe');
const testcafe = await createTestCafe();
const runner = testcafe.createRunner();
// Use fluent API methods on runner
I/O Contract
Single-Call Methods (throw if called multiple times)
| Method | Parameters | Description |
|---|---|---|
| src | ...sources: string[] | Test file paths or glob patterns |
| browsers | ...browsers: string[] | Browser aliases or connection objects |
| reporter | name: string, output?: Writable | Reporter name and optional output stream |
| clientScripts | ...scripts | Client-side scripts to inject |
Multi-Call Methods (can be called multiple times)
| Method | Parameters | Description |
|---|---|---|
| concurrency | n: number | Number of parallel browser instances (default: 1) |
| filter | fn: Function | Test filter function (testName, fixtureName, etc.) |
| useProxy | proxy: string, proxyBypass?: string/string[] | Proxy server and bypass rules |
| screenshots | path/options | Screenshot path and configuration |
| video | path: string, options?, encodingOptions? | Video recording configuration |
| startApp | command: string, initDelay?: number | Command to start tested application |
| tsConfigPath | path: string | Path to TypeScript configuration file |
| compilerOptions | opts: object | Compiler-specific options |
Outputs
All methods return the Runner instance (this) for chaining.
Usage Examples
Basic Fluent Configuration
const createTestCafe = require('testcafe');
(async () => {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
await runner
.src('tests/**/*.js')
.browsers('chrome', 'firefox')
.reporter('spec')
.concurrency(2)
.run();
}
finally {
await testcafe.close();
}
})();
Advanced Configuration with Screenshots and Video
const createTestCafe = require('testcafe');
(async () => {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
await runner
.src('tests/**/*.js')
.browsers('chrome:headless')
.reporter('spec')
.screenshots({
path: 'screenshots/',
takeOnFails: true,
pathPattern: '${DATE}_${TIME}/test-${TEST_INDEX}/${USERAGENT}/${FILE_INDEX}.png',
fullPage: true
})
.video('videos/', {
failedOnly: true,
singleFile: false
})
.concurrency(4)
.run();
}
finally {
await testcafe.close();
}
})();
Test Filtering
const createTestCafe = require('testcafe');
(async () => {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
await runner
.src('tests/**/*.js')
.browsers('chrome')
.reporter('spec')
.filter((testName, fixtureName, fixturePath, testMeta, fixtureMeta) => {
// Only run tests marked as 'smoke'
return testMeta.type === 'smoke';
})
.run();
}
finally {
await testcafe.close();
}
})();
Starting Tested Application
const createTestCafe = require('testcafe');
(async () => {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
await runner
.src('tests/**/*.js')
.browsers('chrome')
.reporter('spec')
.startApp('node server.js', 5000) // Start server, wait 5s
.run();
}
finally {
await testcafe.close();
}
})();
Client Scripts Injection
const createTestCafe = require('testcafe');
(async () => {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
await runner
.src('tests/**/*.js')
.browsers('chrome')
.reporter('spec')
.clientScripts([
'scripts/mock-date.js',
{ module: 'lodash' },
{
content: 'window.myGlobal = "test";',
page: 'https://example.com'
}
])
.run();
}
finally {
await testcafe.close();
}
})();
TypeScript Configuration
const createTestCafe = require('testcafe');
(async () => {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
await runner
.src('tests/**/*.ts')
.browsers('chrome')
.reporter('spec')
.tsConfigPath('./tsconfig.test.json')
.compilerOptions({
typescript: {
configPath: './tsconfig.test.json',
customCompilerModulePath: './custom-typescript'
}
})
.run();
}
finally {
await testcafe.close();
}
})();
Proxy Configuration
const createTestCafe = require('testcafe');
(async () => {
const testcafe = await createTestCafe();
try {
const runner = testcafe.createRunner();
await runner
.src('tests/**/*.js')
.browsers('chrome')
.reporter('spec')
.useProxy('http://proxy.company.com:8080', [
'localhost',
'*.internal.com'
])
.run();
}
finally {
await testcafe.close();
}
})();
Implementation Details
Single-Call Enforcement
Methods like src, browsers, reporter, and clientScripts check the apiMethodWasCalled flag and throw if called multiple times:
src(...sources) {
if (this.apiMethodWasCalled.src)
throw new GeneralError(RUNTIME_ERRORS.multipleAPIMethodCallForbidden, OPTION_NAMES.src);
this._options[OPTION_NAMES.src] = this._prepareArrayParameter(sources);
this.apiMethodWasCalled.src = true;
return this;
}
This prevents accidental configuration conflicts and makes the API more predictable.
Array Parameter Preparation
Methods accepting multiple arguments use _prepareArrayParameter to flatten nested arrays:
_prepareArrayParameter(array) {
array = flatten(array); // lodash flattenDeep
if (this.isCli)
return array.length === 0 ? void 0 : array;
return array;
}
This allows both spread arguments and array arguments:
- runner.src('test1.js', 'test2.js')
- runner.src(['test1.js', 'test2.js'])
- runner.src('test1.js', ['test2.js', 'test3.js'])
Configuration Storage
All methods store configuration in this._options, which is later merged with the global configuration object during run preparation:
concurrency(concurrency) {
this._options.concurrency = concurrency;
return this;
}