A simple yet powerful testing framework for Node.js backend applications and libraries
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Test runner factory for testing reporters, plugins, and creating isolated test environments.
Creates a new instance of the runner factory for testing and development purposes.
/**
* Create an instance of the runner factory
* @returns RunnerFactory instance for creating isolated test environments
*/
function runner(): RunnerFactory;Factory class that provides an API to run dummy suites, groups, and tests for testing reporters and plugins.
/**
* Runner factory exposes the API to run dummy suites, groups and tests.
* You might want to use the factory for testing reporters and plugins usage.
*/
class RunnerFactory {
/** Configure the factory with test configuration and CLI arguments */
configure(config: Config, argv?: string[]): this;
/** Define a custom emitter instance to use */
useEmitter(emitter: Emitter): this;
/** Run a single test using the runner */
async runTest(title: string, callback: TestExecutor<TestContext, undefined>): Promise<RunnerSummary>;
/** Enable/disable the bail mode */
bail(toggle?: boolean): this;
/** Run dummy test suites */
async runSuites(suites: (emitter: Emitter, refiner: Refiner, file?: string) => Suite[]): Promise<RunnerSummary>;
}Usage Examples:
import { runner } from "@japa/runner/factories";
import { assert } from "chai";
// Create factory instance
const factory = runner();
// Configure the factory
factory.configure({
files: ["tests/**/*.spec.ts"],
reporters: {
activated: ["spec"],
},
});
// Run a single test
const summary = await factory.runTest("should work", async (ctx) => {
assert.equal(2 + 2, 4);
});
console.log(`Test result: ${summary.hasError ? "FAILED" : "PASSED"}`);Use the factory to test custom reporters in isolation.
Usage Examples:
import { runner } from "@japa/runner/factories";
import { Emitter, Suite, Test, TestContext } from "@japa/runner/core";
import { assert } from "chai";
// Custom reporter to test
const customReporter = {
name: "test-reporter",
handler: (runner, emitter) => {
const results: string[] = [];
emitter.on("test:start", (payload) => {
results.push(`START: ${payload.title}`);
});
emitter.on("test:end", (payload) => {
const status = payload.hasError ? "FAIL" : "PASS";
results.push(`END: ${payload.title} - ${status}`);
});
return { results };
},
};
// Test the reporter
const factory = runner();
factory.configure({
files: [],
reporters: {
activated: ["test-reporter"],
list: [customReporter],
},
});
// Run test with custom reporter
const summary = await factory.runTest("sample test", async (ctx) => {
assert.isTrue(true);
});
// Verify reporter output
console.log("Reporter captured:", customReporter.handler().results);Use the factory to test custom plugins in isolation.
Usage Examples:
import { runner } from "@japa/runner/factories";
import { assert } from "chai";
// Custom plugin to test
const testPlugin = () => {
const events: string[] = [];
return ({ emitter }) => {
emitter.on("runner:start", () => {
events.push("runner started");
});
emitter.on("test:start", (payload) => {
events.push(`test started: ${payload.title}`);
});
emitter.on("test:end", (payload) => {
events.push(`test ended: ${payload.title}`);
});
emitter.on("runner:end", () => {
events.push("runner ended");
});
// Expose events for testing
return { events };
};
};
// Test the plugin
const factory = runner();
const pluginInstance = testPlugin();
factory.configure({
files: [],
plugins: [pluginInstance],
});
// Run test with plugin
await factory.runTest("plugin test", async (ctx) => {
assert.equal(1 + 1, 2);
});
// Verify plugin behavior
console.log("Plugin events:", pluginInstance().events);Create and run complex test scenarios with multiple suites.
Usage Examples:
import { runner } from "@japa/runner/factories";
import { Suite, Test, TestContext, Group, Emitter, Refiner } from "@japa/runner/core";
import { assert } from "chai";
const factory = runner();
factory.configure({
files: [],
reporters: {
activated: ["spec"],
},
});
// Create test suites programmatically
const summary = await factory.runSuites((emitter, refiner, file) => {
// Unit tests suite
const unitSuite = new Suite("unit", emitter, refiner);
const mathGroup = new Group("Math operations", emitter, refiner);
const addTest = new Test("should add numbers", (test) => new TestContext(test), emitter, refiner, mathGroup);
addTest.run(async (ctx) => {
assert.equal(2 + 3, 5);
}, new Error());
unitSuite.add(mathGroup);
// Integration tests suite
const integrationSuite = new Suite("integration", emitter, refiner);
const apiTest = new Test("should call API", (test) => new TestContext(test), emitter, refiner);
apiTest.run(async (ctx) => {
// Mock API call
const result = await Promise.resolve({ status: "ok" });
assert.equal(result.status, "ok");
}, new Error());
integrationSuite.add(apiTest);
return [unitSuite, integrationSuite];
});
console.log(`Ran ${summary.aggregates.total} tests, ${summary.aggregates.passed} passed`);Use a custom emitter to capture and analyze test events.
Usage Examples:
import { runner } from "@japa/runner/factories";
import { Emitter } from "@japa/runner/core";
import { assert } from "chai";
// Custom emitter with event logging
class LoggingEmitter extends Emitter {
private eventLog: Array<{ event: string; timestamp: number; payload?: any }> = [];
emit(event: string, ...args: any[]) {
this.eventLog.push({
event,
timestamp: Date.now(),
payload: args.length === 1 ? args[0] : args,
});
return super.emit(event, ...args);
}
getEventLog() {
return this.eventLog;
}
clearLog() {
this.eventLog = [];
}
}
// Use custom emitter with factory
const customEmitter = new LoggingEmitter();
const factory = runner();
factory
.useEmitter(customEmitter)
.configure({
files: [],
});
// Run test and capture events
await factory.runTest("event logging test", async (ctx) => {
assert.isTrue(true);
});
// Analyze captured events
const events = customEmitter.getEventLog();
console.log("Captured events:", events.map(e => e.event));Test bail mode behavior where execution stops on first failure.
Usage Examples:
import { runner } from "@japa/runner/factories";
import { assert } from "chai";
const factory = runner();
// Enable bail mode
factory
.bail(true)
.configure({
files: [],
});
// Run suites with bail mode
const summary = await factory.runSuites((emitter, refiner) => {
const suite = new Suite("bail test", emitter, refiner);
// First test (will fail)
const failingTest = new Test("failing test", (test) => new TestContext(test), emitter, refiner);
failingTest.run(async (ctx) => {
assert.equal(1, 2); // This will fail
}, new Error());
// Second test (should not run due to bail)
const passingTest = new Test("passing test", (test) => new TestContext(test), emitter, refiner);
passingTest.run(async (ctx) => {
assert.equal(1, 1);
}, new Error());
suite.add(failingTest);
suite.add(passingTest);
return [suite];
});
console.log(`Bail mode: ${summary.aggregates.total} total, ${summary.aggregates.failed} failed`);Built-in synchronous reporter for testing that throws on test failures.
/**
* Synchronous reporter that throws errors on test failures
* Useful for testing scenarios where you want immediate failure feedback
*/
const syncReporter: ReporterContract;Usage Examples:
import { runner, syncReporter } from "@japa/runner/factories";
import { assert } from "chai";
const factory = runner();
factory.configure({
files: [],
reporters: {
activated: ["sync"],
list: [syncReporter],
},
});
try {
// This will throw if any test fails
await factory.runTest("failing test", async (ctx) => {
assert.equal(1, 2);
});
} catch (error) {
console.log("Test failed as expected:", error.message);
}Utility function for creating test data and scenarios.
/**
* Create dummy tests for testing and development purposes
*/
function createDummyTests(): any; // Implementation specificinterface RunnerFactory {
configure(config: Config, argv?: string[]): this;
useEmitter(emitter: Emitter): this;
runTest(title: string, callback: TestExecutor<TestContext, undefined>): Promise<RunnerSummary>;
bail(toggle?: boolean): this;
runSuites(suites: (emitter: Emitter, refiner: Refiner, file?: string) => Suite[]): Promise<RunnerSummary>;
}
interface RunnerSummary {
hasError: boolean;
aggregates: {
passed: number;
failed: number;
skipped: number;
todo: number;
total: number;
};
failureTree: Array<{
name: string;
errors: Array<{ phase: string; error: Error }>;
children: Array<{
name: string;
errors: Array<{ phase: string; error: Error }>;
}>;
}>;
}type SuiteFactory = (emitter: Emitter, refiner: Refiner, file?: string) => Suite[];