Browse API packages

API · v0.20.1

test

Standard library testing

Member signatures use schematic object.member(...) API notation. Exported type declarations are valid TypeScript.

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.

ts
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.

ts
test.case(name: string, body: Body): void;

test.ended#

Registers cleanup to run after all cases in the current file.

ts
test.ended(end: End): void;

test.group#

Registers cases under a group that Proby can target by name.

ts
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.

ts
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.

ts
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.
ts
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.

ts
test.intercept(
  base_url: string,
  setup: (setup: Setup) => void,
): Intercept;

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.

ts
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.

ts
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.

ts
test.E(error: unknown): EncodedError;

test.equals#

Tests deep equality across supported primitive and structured values.

ts
test.equals<T>(x: T, y: T): boolean;

test.extend#

Creates an aggregate whose asserter includes custom methods.

ts
test.extend<Subject, Extensions>(
  factory: Factory<Subject, Extensions>,
): ExtendedTest<Extensions>;

test.includes#

Tests deep inclusion across strings and supported structured values.

ts
test.includes<T>(x: T, y: T): boolean;

test.repository#

Suite repository used by test runners to register and execute files.

ts
test.repository: Repository;

test.undef#

Pre-cast undefined typed as never for negative tests.

ts
test.undef: never;

Types#

Asserter#

Creates a fluent assertion for an optional actual value.

ts
type Asserter = <const T>(actual?: T) => Assert<T>;

Env#

Per-file environment hooks consumed by Proby from sibling .env.ts files.

ts
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.

ts
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.

ts
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.

ts
get actual(): T;

Result instance: expected#

Expected value recorded by the assertion.

ts
get expected(): T;

Result instance: passed#

Whether the assertion passed.

ts
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.

ts
get name(): string;

Test instance: group#

Optional registration group.

ts
get group(): string | undefined;

Test instance: results#

Assertion results recorded while the case ran.

ts
get results(): Result<unknown>[];

Test instance: report#

Records one assertion result.

ts
report<T>(actual: T, expected: T, passed: boolean): void;

Test instance: run#

Executes the test body and returns this completed test.

ts
run(): Promise<this>;

TestConfig#

Test-runner configuration accepted by test.config.

ts
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.

ts
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.

ts
get not(): this;

Example

ts
assert(false).not.true(); // passes

Assert.equals#

Records whether the actual and expected values are deeply equal.

ts
equals(expected: unknown): this;

Example

ts
assert({ ready: true }).equals({ ready: true }); // passes

Assert.nequals#

Records whether the actual and expected values are not deeply equal.

ts
nequals(expected: unknown): this;

Example

ts
assert(1).nequals(2); // passes

Assert.includes#

Records string substring or array member inclusion.

ts
includes(expected: unknown): this;

Example

ts
assert("rcompat").includes("compat"); // passes

Assert.type#

Records a pass when the actual and expected compile-time types match.

ts
type<const Expected extends Equals<Actual, Expected> extends true
    ? unknown
    : Print<Actual>>(_expected?: Expected): this;

Example

ts
assert("ready").type<string>(); // passes

Assert.nottype#

Constrains the actual and expected compile-time types to differ.

ts
nottype<const Expected extends Not<Equals<Actual, Expected>> extends true
    ? unknown
    : "actual and expected types are same">(_expected?: Expected): this;

Example

ts
assert<"ready">().nottype<"waiting">(); // type-checks

Assert.true#

Records whether the actual value is exactly true.

ts
true(): this;

Example

ts
assert(true).true(); // passes

Assert.false#

Records whether the actual value is exactly false.

ts
false(): this;

Example

ts
assert(false).false(); // passes

Assert.null#

Records whether the actual value is null.

ts
null(): this;

Example

ts
assert(null).null(); // passes

Assert.undefined#

Records whether the actual value is undefined.

ts
undefined(): this;

Example

ts
assert(undefined).undefined(); // passes

Assert.defined#

Records whether the actual value is not undefined.

ts
defined(): this;

Example

ts
assert(null).defined(); // passes

Assert.instance#

Records whether the actual value is an instance of a constructor.

ts
instance(expected: UnknownFunction): this;

Example

ts
assert(new Date()).instance(Date); // passes

Assert.throws#

Records whether the asserted callback throws or rejects as expected.

Async callbacks return a promise and must be awaited.

ts
throws(expected?: ExpectedError): AssertionResult<Actual, this>;

Example

ts
assert(() => { throw new TypeError("invalid"); }).throws(TypeError);
// passes

Assert.tries#

Records whether the asserted callback returns or resolves successfully.

Async callbacks return a promise and must be awaited.

ts
tries(): AssertionResult<Actual, this>;

Example

ts
assert(() => 42).tries(); // passes

Assert.pass#

Records an unconditional passing assertion.

ts
pass(): void;

Example

ts
assert().pass(); // passes

Assert.fail#

Marks an intentional compile-time failure or records a runtime failure.

ts
fail<const Expected extends Not<Equals<Actual, Expected>> extends true
    ? unknown
    : "was supposed to fail but didn't">(): void;
fail(reason: string): void;

Example

ts
assert("actual").fail("expected another value"); // fails

AssertionResult#

Async assertion result selected from the asserted callback's return type.

ts
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.

ts
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.

ts
type Body = (asserter: Asserter) => MaybePromise<void>;

EncodedError#

Error shape used by test assertions.

ts
type EncodedError = {
  // Error message.
  message: string;
  // Optional application error code.
  code?: string;
};

End#

Cleanup callback run after a suite's test cases.

ts
type End = () => MaybePromise<void>;

Equals#

Exact compile-time type equality.

ts
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.

ts
type Exact<Config extends TestConfig> = Config & Record<
  Exclude<keyof Config, keyof TestConfig>, never
>;

ExpectedError#

Optional error message, code, or constructor matched by throws.

ts
type ExpectedError = string | (new (...args: any[]) => Error) | undefined;

ExtendedAssert#

Assertion enriched with custom methods.

ts
type ExtendedAssert<T, Extensions> = Assert<T> & Extensions;

ExtendedAsserter#

Creates an assertion enriched with custom methods.

ts
type ExtendedAsserter<Extensions> =
  <const T>(actual?: T) => ExtendedAssert<T, Extensions>;

ExtendedBody#

Test-case callback receiving an extended asserter.

ts
type ExtendedBody<Extensions> =
  (asserter: ExtendedAsserter<Extensions>) => MaybePromise<void>;

Factory#

Creates custom assertion methods for the current subject.

ts
type Factory<Subject, Extensions> =
  (assert: Asserter, subject: Subject) => Extensions;

Forwarded#

Base methods other than the replaced case registration method.

ts
type Forwarded = Omit<Base, "case">;

Handler#

Handler for an intercepted request.

ts
type Handler = (request: Request) => unknown;

Intercept#

Recorded requests and restoration controls for an intercept.

ts
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.

ts
type MockFunction = (...args: any[]) => any;

MockHandle#

Tracked module exports and restoration controls.

ts
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.

ts
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.

ts
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.

ts
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.

ts
type AnyFunction = (...args: any[]) => any;

Tracked#

A function augmented with its recorded calls and called state.

ts
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;
};