Build a tiny test runner — a stripped-down Jest — whose headline feature is nested suites. You register tests with test(name, fn) and group them with describe(name, fn), where a describe can contain other describes. When the suite runs, every test must be reported by its full spec path: the names of all the suites wrapped around it, joined to the test's own name. A bare 'adds' is useless when three suites each have an adds test; 'math > addition > adds' points straight at the failing one.
// createTestRunner() takes no arguments and returns four functions.
function createTestRunner(): {
// Open a suite scope. Calls to describe/test inside fn nest under `name`.
describe(name: string, fn: () => void): void;
// Register a test. It PASSES if fn does not throw, FAILS if it throws.
test(name: string, fn: () => void): void;
it(name: string, fn: () => void): void; // alias for test
// Execute every registered test, in registration order.
run(): {
// One row per test. `name` is the full path; `error` (the thrown
// message) is present only when status is 'fail'.
results: Array<{ name: string; status: 'pass' | 'fail'; error?: string }>;
summary: { passed: number; failed: number; total: number };
};
};
// One describe level: the test name is prefixed with its suite, joined by ' > '.
const { describe, test, run } = createTestRunner();
describe('math', () => {
test('adds', () => {});
});
run();
// → {
// results: [{ name: 'math > adds', status: 'pass' }],
// summary: { passed: 1, failed: 0, total: 1 },
// }
// Nesting and a failure. Sibling tests still run; the full path is reported.
const { describe, test, run } = createTestRunner();
describe('parser', () => {
describe('numbers', () => {
test('parses 42', () => {});
test('rejects NaN', () => {
throw new Error('got NaN');
});
});
});
run();
// → {
// results: [
// { name: 'parser > numbers > parses 42', status: 'pass' },
// { name: 'parser > numbers > rejects NaN', status: 'fail', error: 'got NaN' },
// ],
// summary: { passed: 1, failed: 1, total: 2 },
// }
' > '. Join the enclosing describe names plus the test name with the string ' > ' (space, greater-than, space). A top-level test with no surrounding describe is reported as just its own name.fn; if it returns normally the test is 'pass'. If it throws, the test is 'fail' and you record the thrown error's message.run().results array lists tests in the order their test(...) calls ran, regardless of how deeply they were nested.fn runs to completion synchronously. You do not need to support async/await tests (that is the sibling test-runner-iv), nor beforeEach/afterEach hooks (that is test-runner-iii).describe whose body registers no tests simply contributes nothing to the results.