Test registration, assertions, spies, fetch interception, and test-runner integration for Node, Deno, and Bun.
Module mocking requires Node.js module.registerHooks. Browser bundles can
import the package and use test.config; accessing any other root utility
throws because test registration is unavailable.
import test from "@rcompat/test";
import type {
Asserter, Env, ExtendedTest, Repository, Result, Test,
TestConfig, TestRun,
} from "@rcompat/test";test.case#
Registers a named test case in the current suite.
test.case(name: string, body: Body): void;test.ended#
Registers cleanup to run after all cases in the current file.
test.ended(end: End): void;test.group#
Registers cases under a group that Proby can target by name.
test.group(name: string, fn: () => void): void;test.mock#
Registers tracked replacement exports for a module.
Module mocking requires Node.js module.registerHooks. Calls are cleared
between test cases when the mock belongs to the active repository suite.
Parameters
specifier— Module specifier to intercept.factory— Creates replacement exports.
Returns: Tracked exports and restoration controls.
Throws: When module hooks are unavailable in the active runtime.
test.mock<T extends object>(
specifier: string,
factory: (original: unknown) => T,
): MockHandle<T>;test.spy#
Wraps a function and records every call.
Parameters
fn— Original implementation and function type.mocker— Optional replacement implementation.
Returns: A callable tracker with calls and called properties.
test.spy<F extends AnyFunction>(
fn: F,
mocker?: F,
): Tracked<F>;test.import#
Dynamically imports a module after mocks have been registered.
Parameters
specifier— Module specifier to import.
test.import(specifier: string): Promise<unknown>;test.intercept#
Intercepts fetch requests to one origin and records matched requests.
Parameters
base_url— Origin to intercept.setup— Callback that registers path handlers by HTTP method.
Returns: Recorded requests and restoration controls.
Throws: When the intercepted origin receives an unregistered route.
test.any#
Casts a value to never for compile-time negative tests.
Parameters
value— Value to cast without runtime validation.
Returns: The same runtime value, typed as never.
test.any(value: unknown): never;test.config#
Returns a type-checked test-runner configuration unchanged.
Parameters
input— Test discovery and preload options.
Returns: The original configuration object.
test.config<Config extends TestConfig>(
input: Exact<Config>,
): Config;test.E#
Casts a caught value to the error shape used in tests.
This helper performs no runtime validation.
test.E(error: unknown): EncodedError;test.equals#
Tests deep equality across supported primitive and structured values.
test.equals<T>(x: T, y: T): boolean;test.extend#
Creates an aggregate whose asserter includes custom methods.
test.extend<Subject, Extensions>(
factory: Factory<Subject, Extensions>,
): ExtendedTest<Extensions>;test.includes#
Tests deep inclusion across strings and supported structured values.
test.includes<T>(x: T, y: T): boolean;test.repository#
Suite repository used by test runners to register and execute files.
test.repository: Repository;test.undef#
Pre-cast undefined typed as never for negative tests.
test.undef: never;Types#
Asserter#
Creates a fluent assertion for an optional actual value.
type Asserter = <const T>(actual?: T) => Assert<T>;Env#
Per-file environment hooks consumed by Proby from sibling .env.ts files.
type Env<C = void> = {
// Returns globals to install while the suite runs.
globals(context: C): object;
// Creates optional context before the suite runs.
setup?(): MaybePromise<C>;
// Cleans up the context after the suite ends.
teardown?(context: C): MaybePromise<void>;
};ExtendedTest#
Test aggregate with custom assertion methods and the base utilities.
type ExtendedTest<Extensions> = Forwarded & {
// Registers a case receiving the extended asserter.
case(name: string, body: ExtendedBody<Extensions>): void;
// Composes another set of assertion methods.
extend<ForwardedSubject, ForwardedExtensions>(
factory: Factory<ForwardedSubject, ForwardedExtensions>,
): ExtendedTest<Extensions & ForwardedExtensions>;
};Repository#
Suite repository used by test runners to register and execute files.
interface Repository {
// Registered replacement modules.
readonly mocks: Map<string, unknown>;
// Registers work to run between test cases.
between(fn: () => void): void;
// Registers cleanup for the current suite.
ended(end: End): void;
// Runs registrations under a named group.
group(name: string, fn: () => void): void;
// Iterates registered suites.
next(): Generator<RepositorySuite>;
// Adds a named case to the current suite.
put(name: string, body: Body): void;
// Clears suites and registered mocks.
reset(): void;
// Starts registration for a spec file.
suite(file: FileRef): void;
}Result#
One recorded assertion result.
Type parameters
T— Type shared by the actual and expected values.
class Result is exported as a type.
Result members#
Result instance: actual#
Actual value observed by the assertion.
get actual(): T;Result instance: expected#
Expected value recorded by the assertion.
get expected(): T;Result instance: passed#
Whether the assertion passed.
get passed(): boolean;Test#
A registered test case and its assertion results.
class Test is exported as a type.
Test members#
Test instance: name#
Test case name.
get name(): string;Test instance: group#
Optional registration group.
get group(): string | undefined;Test instance: results#
Assertion results recorded while the case ran.
get results(): Result<unknown>[];Test instance: report#
Records one assertion result.
report<T>(actual: T, expected: T, passed: boolean): void;Test instance: run#
Executes the test body and returns this completed test.
run(): Promise<this>;TestConfig#
Test-runner configuration accepted by test.config.
interface TestConfig {
// Directories searched for spec files.
// Default: `["src", "test"]`
include?: readonly string[];
// Modules preloaded before test files run.
// Default: `[]`
loaders?: readonly string[];
// Whether package directories are scanned as a monorepo.
// Default: `false`
monorepo?: boolean;
// Package directory resolved from the project root.
// Default: `"packages"`
packages?: string;
}TestRun#
One completed test and its execution duration.
type TestRun = {
// Execution time in milliseconds.
duration: number;
// Completed test and assertion results.
test: Test;
};Supporting contracts#
Assert#
Fluent runtime and compile-time assertions for an actual value.
Type parameters
Actual— Inferred actual value or compile-time type.
Assert instances are created by an Asserter.
Assert members#
Assert.not#
Negates the next runtime assertion.
get not(): this;Example
assert(false).not.true(); // passesAssert.equals#
Records whether the actual and expected values are deeply equal.
equals(expected: unknown): this;Example
assert({ ready: true }).equals({ ready: true }); // passesAssert.nequals#
Records whether the actual and expected values are not deeply equal.
nequals(expected: unknown): this;Example
assert(1).nequals(2); // passesAssert.includes#
Records string substring or array member inclusion.
includes(expected: unknown): this;Example
assert("rcompat").includes("compat"); // passesAssert.type#
Records a pass when the actual and expected compile-time types match.
type<const Expected extends Equals<Actual, Expected> extends true
? unknown
: Print<Actual>>(_expected?: Expected): this;Example
assert("ready").type<string>(); // passesAssert.nottype#
Constrains the actual and expected compile-time types to differ.
nottype<const Expected extends Not<Equals<Actual, Expected>> extends true
? unknown
: "actual and expected types are same">(_expected?: Expected): this;Example
assert<"ready">().nottype<"waiting">(); // type-checksAssert.true#
Records whether the actual value is exactly true.
true(): this;Example
assert(true).true(); // passesAssert.false#
Records whether the actual value is exactly false.
false(): this;Example
assert(false).false(); // passesAssert.null#
Records whether the actual value is null.
null(): this;Example
assert(null).null(); // passesAssert.undefined#
Records whether the actual value is undefined.
undefined(): this;Example
assert(undefined).undefined(); // passesAssert.defined#
Records whether the actual value is not undefined.
defined(): this;Example
assert(null).defined(); // passesAssert.instance#
Records whether the actual value is an instance of a constructor.
instance(expected: UnknownFunction): this;Example
assert(new Date()).instance(Date); // passesAssert.throws#
Records whether the asserted callback throws or rejects as expected.
Async callbacks return a promise and must be awaited.
throws(expected?: ExpectedError): AssertionResult<Actual, this>;Example
assert(() => { throw new TypeError("invalid"); }).throws(TypeError);
// passesAssert.tries#
Records whether the asserted callback returns or resolves successfully.
Async callbacks return a promise and must be awaited.
tries(): AssertionResult<Actual, this>;Example
assert(() => 42).tries(); // passesAssert.pass#
Records an unconditional passing assertion.
pass(): void;Example
assert().pass(); // passesAssert.fail#
Marks an intentional compile-time failure or records a runtime failure.
fail<const Expected extends Not<Equals<Actual, Expected>> extends true
? unknown
: "was supposed to fail but didn't">(): void;
fail(reason: string): void;Example
assert("actual").fail("expected another value"); // failsAssertionResult#
Async assertion result selected from the asserted callback's return type.
type AssertionResult<Actual, Assertion> =
Actual extends (...args: any[]) => infer Return
? Return extends PromiseLike<unknown> ? Promise<Assertion> : Assertion
: Assertion;Base#
Root methods forwarded to an extended test aggregate.
type Base = {
case(name: string, body: Body): void;
ended(end: () => MaybePromise<void>): void;
group(name: string, fn: () => void): void;
mock: typeof mock;
spy: typeof spy;
import: typeof import_;
intercept: typeof intercept;
};Body#
Test-case callback receiving its suite asserter.
type Body = (asserter: Asserter) => MaybePromise<void>;EncodedError#
Error shape used by test assertions.
type EncodedError = {
// Error message.
message: string;
// Optional application error code.
code?: string;
};End#
Cleanup callback run after a suite's test cases.
type End = () => MaybePromise<void>;Equals#
Exact compile-time type equality.
type Equals<X, Y> =
(<T>() => T extends X ? true : false) extends <T>() => T extends Y
? true
: false
? true
: false;Exact#
Rejects keys outside the test-runner configuration contract.
type Exact<Config extends TestConfig> = Config & Record<
Exclude<keyof Config, keyof TestConfig>, never
>;ExpectedError#
Optional error message, code, or constructor matched by throws.
type ExpectedError = string | (new (...args: any[]) => Error) | undefined;ExtendedAssert#
Assertion enriched with custom methods.
type ExtendedAssert<T, Extensions> = Assert<T> & Extensions;ExtendedAsserter#
Creates an assertion enriched with custom methods.
type ExtendedAsserter<Extensions> =
<const T>(actual?: T) => ExtendedAssert<T, Extensions>;ExtendedBody#
Test-case callback receiving an extended asserter.
type ExtendedBody<Extensions> =
(asserter: ExtendedAsserter<Extensions>) => MaybePromise<void>;Factory#
Creates custom assertion methods for the current subject.
type Factory<Subject, Extensions> =
(assert: Asserter, subject: Subject) => Extensions;Forwarded#
Base methods other than the replaced case registration method.
type Forwarded = Omit<Base, "case">;Handler#
Handler for an intercepted request.
type Handler = (request: Request) => unknown;Intercept#
Recorded requests and restoration controls for an intercept.
type Intercept = {
// Returns the number of intercepted requests for a path.
calls(path: string): number;
// Returns intercepted requests for a path in call order.
requests(path: string): Request[];
// Restores the fetch implementation active before interception.
restore(): void;
// Restores fetch when used with an explicit resource-management scope.
[Symbol.dispose](): void;
};MockFunction#
Any callable module export that can be tracked.
type MockFunction = (...args: any[]) => any;MockHandle#
Tracked module exports and restoration controls.
type MockHandle<T extends object> = {
[K in keyof T]: T[K] extends MockFunction ? MockTracked<T[K]> : T[K];
} & {
// Removes the registered module mock.
restore(): void;
// Removes the mock when used with an explicit resource-management scope.
[Symbol.dispose](): void;
};MockTracked#
A mocked function augmented with call tracking.
type MockTracked<F extends MockFunction> = F & {
// Argument tuples recorded in call order.
calls: Parameters<F>[];
// Whether the function has been called at least once.
readonly called: boolean;
};RepositorySuite#
A registered file suite exposed to test-runner integrations.
interface RepositorySuite {
// Spec file represented by this suite.
readonly file: FileRef;
// Runs registered suite cleanup callbacks.
end(): Promise<void>;
// Executes tests in registration order.
run(): AsyncGenerator<TestRun, void, unknown>;
}Setup#
Route-registration methods passed to an intercept setup callback.
type Setup = {
// Registers a GET response handler.
get(path: string, handler: Handler): void;
// Registers a POST response handler.
post(path: string, handler: Handler): void;
// Registers a PUT response handler.
put(path: string, handler: Handler): void;
// Registers a PATCH response handler.
patch(path: string, handler: Handler): void;
// Registers a DELETE response handler.
delete(path: string, handler: Handler): void;
};AnyFunction#
Any callable value accepted by test.spy.
type AnyFunction = (...args: any[]) => any;Tracked#
A function augmented with its recorded calls and called state.
type Tracked<F extends AnyFunction> = F & {
// Argument tuples recorded in call order.
calls: Parameters<F>[];
// Whether the function has been called at least once.
called: boolean;
};