Jest's test runner responsible for orchestrating test execution, managing worker processes, and coordinating parallel test runs
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Abstract base classes and interfaces for building custom test runners with both callback and event-driven patterns. This framework enables extending Jest's test execution capabilities with custom test runners for different testing scenarios.
import {
CallbackTestRunner,
EmittingTestRunner,
type CallbackTestRunnerInterface,
type EmittingTestRunnerInterface,
type OnTestStart,
type OnTestFailure,
type OnTestSuccess,
type UnsubscribeFn
} from "jest-runner";Base class for building custom test runners using callback-style interfaces for test lifecycle events.
/**
* Abstract base class for callback-style test runners
* Suitable for runners that prefer explicit callback handling over events
*/
abstract class CallbackTestRunner extends BaseTestRunner implements CallbackTestRunnerInterface {
readonly supportsEventEmitters = false;
readonly isSerial?: boolean;
constructor(globalConfig: Config.GlobalConfig, context: TestRunnerContext);
/**
* Execute tests using callback-style event handling
* @param tests - Array of test objects to execute
* @param watcher - Test watcher for interrupt handling
* @param onStart - Callback invoked when each test starts
* @param onResult - Callback invoked when each test succeeds
* @param onFailure - Callback invoked when each test fails
* @param options - Execution options including serial mode
*/
abstract runTests(
tests: Array<Test>,
watcher: TestWatcher,
onStart: OnTestStart,
onResult: OnTestSuccess,
onFailure: OnTestFailure,
options: TestRunnerOptions
): Promise<void>;
}Usage Example:
import { CallbackTestRunner } from "jest-runner";
import type { Test, TestResult, SerializableError } from "@jest/test-result";
class CustomCallbackRunner extends CallbackTestRunner {
async runTests(
tests: Array<Test>,
watcher: TestWatcher,
onStart: OnTestStart,
onResult: OnTestSuccess,
onFailure: OnTestFailure,
options: TestRunnerOptions
): Promise<void> {
for (const test of tests) {
if (watcher.isInterrupted()) break;
try {
await onStart(test);
// Custom test execution logic
const result = await this.executeCustomTest(test);
await onResult(test, result);
} catch (error) {
await onFailure(test, this.formatError(error));
}
}
}
private async executeCustomTest(test: Test): Promise<TestResult> {
// Custom test execution implementation
return {
numPassingTests: 1,
numFailingTests: 0,
numPendingTests: 0,
numTodoTests: 0,
testFilePath: test.path,
// ... other TestResult properties
};
}
}Base class for building custom test runners using event-driven interfaces for real-time test progress reporting.
/**
* Abstract base class for event-emitting test runners
* Suitable for runners that need real-time event broadcasting
*/
abstract class EmittingTestRunner extends BaseTestRunner implements EmittingTestRunnerInterface {
readonly supportsEventEmitters = true;
readonly isSerial?: boolean;
constructor(globalConfig: Config.GlobalConfig, context: TestRunnerContext);
/**
* Execute tests using event-driven progress reporting
* @param tests - Array of test objects to execute
* @param watcher - Test watcher for interrupt handling
* @param options - Execution options including serial mode
*/
abstract runTests(
tests: Array<Test>,
watcher: TestWatcher,
options: TestRunnerOptions
): Promise<void>;
/**
* Register event listener for test execution events
* @param eventName - Name of the event to listen for
* @param listener - Function to call when event is emitted
* @returns Unsubscribe function to remove the listener
*/
abstract on<Name extends keyof TestEvents>(
eventName: Name,
listener: (eventData: TestEvents[Name]) => void | Promise<void>
): UnsubscribeFn;
}Usage Example:
import { EmittingTestRunner } from "jest-runner";
import Emittery from "emittery";
class CustomEmittingRunner extends EmittingTestRunner {
private eventEmitter = new Emittery<TestEvents>();
async runTests(
tests: Array<Test>,
watcher: TestWatcher,
options: TestRunnerOptions
): Promise<void> {
for (const test of tests) {
if (watcher.isInterrupted()) break;
try {
await this.eventEmitter.emit('test-file-start', [test]);
// Custom test execution logic
const result = await this.executeCustomTest(test);
await this.eventEmitter.emit('test-file-success', [test, result]);
} catch (error) {
await this.eventEmitter.emit('test-file-failure', [test, error]);
}
}
}
on<Name extends keyof TestEvents>(
eventName: Name,
listener: (eventData: TestEvents[Name]) => void | Promise<void>
): UnsubscribeFn {
return this.eventEmitter.on(eventName, listener);
}
}Type definitions for implementing custom test runners without extending the abstract base classes.
/**
* Interface for callback-style test runners
*/
interface CallbackTestRunnerInterface {
readonly isSerial?: boolean;
readonly supportsEventEmitters?: boolean;
runTests(
tests: Array<Test>,
watcher: TestWatcher,
onStart: OnTestStart,
onResult: OnTestSuccess,
onFailure: OnTestFailure,
options: TestRunnerOptions
): Promise<void>;
}
/**
* Interface for event-emitting test runners
*/
interface EmittingTestRunnerInterface {
readonly isSerial?: boolean;
readonly supportsEventEmitters: true;
runTests(
tests: Array<Test>,
watcher: TestWatcher,
options: TestRunnerOptions
): Promise<void>;
on<Name extends keyof TestEvents>(
eventName: Name,
listener: (eventData: TestEvents[Name]) => void | Promise<void>
): UnsubscribeFn;
}Complete examples showing how to implement both callback and event-driven test runners.
Callback Runner Implementation:
class ESLintTestRunner extends CallbackTestRunner {
async runTests(
tests: Array<Test>,
watcher: TestWatcher,
onStart: OnTestStart,
onResult: OnTestSuccess,
onFailure: OnTestFailure,
options: TestRunnerOptions
): Promise<void> {
// Access inherited properties
const { collectCoverage } = this._globalConfig;
const { changedFiles } = this._context;
for (const test of tests) {
if (watcher.isInterrupted()) break;
await onStart(test);
try {
// Run ESLint on the test file
const results = await this.runESLint(test.path);
const testResult: TestResult = {
numFailingTests: results.errorCount,
numPassingTests: results.errorCount === 0 ? 1 : 0,
numPendingTests: 0,
numTodoTests: 0,
testFilePath: test.path,
console: [],
leaks: false,
// Map ESLint messages to test output
testResults: results.messages.map(msg => ({
title: `ESLint: ${msg.ruleId || 'Error'}`,
status: msg.severity === 2 ? 'failed' : 'passed',
ancestorTitles: [],
fullName: `${test.path}:${msg.line}:${msg.column}`,
})),
};
await onResult(test, testResult);
} catch (error) {
await onFailure(test, {
message: error.message,
stack: error.stack,
type: error.constructor.name,
});
}
}
}
}Event-Driven Runner Implementation:
class PuppeteerTestRunner extends EmittingTestRunner {
private eventEmitter = new Emittery<TestEvents>();
async runTests(
tests: Array<Test>,
watcher: TestWatcher,
options: TestRunnerOptions
): Promise<void> {
const browser = await puppeteer.launch();
try {
for (const test of tests) {
if (watcher.isInterrupted()) break;
await this.eventEmitter.emit('test-file-start', [test]);
try {
const page = await browser.newPage();
const result = await this.runBrowserTest(test, page);
await page.close();
await this.eventEmitter.emit('test-file-success', [test, result]);
} catch (error) {
await this.eventEmitter.emit('test-file-failure', [test, error]);
}
}
} finally {
await browser.close();
}
}
on<Name extends keyof TestEvents>(
eventName: Name,
listener: (eventData: TestEvents[Name]) => void | Promise<void>
): UnsubscribeFn {
return this.eventEmitter.on(eventName, listener);
}
}type OnTestStart = (test: Test) => Promise<void>;
type OnTestFailure = (test: Test, serializableError: SerializableError) => Promise<void>;
type OnTestSuccess = (test: Test, testResult: TestResult) => Promise<void>;
type UnsubscribeFn = () => void;
type JestTestRunner = CallbackTestRunner | EmittingTestRunner;
abstract class BaseTestRunner {
readonly isSerial?: boolean;
abstract readonly supportsEventEmitters: boolean;
protected readonly _globalConfig: Config.GlobalConfig;
protected readonly _context: TestRunnerContext;
constructor(globalConfig: Config.GlobalConfig, context: TestRunnerContext);
}