Crust logoCrust

Testing

Test Crust applications with captured output and fake interactive terminals.

@crustjs/testing adds helpers for raw execute() capture and fake interactive terminals. Use core run() for ordinary typed action tests.

Install

npm install -D @crustjs/testing

@crustjs/core is a required peer. @crustjs/prompts is an optional peer needed only by the @crustjs/testing/interactive entry point; the root entry point works without it. TypeScript is an optional peer.

Application contracts

interface ExecutableApp {
  execute(options?: { argv?: string[]; io?: CaptureIO }): Promise<number>;
}

captureExecute() accepts this structural contract. runInteractive() (from @crustjs/testing/interactive) accepts a Crust application and infers its command path and input.

An inert definition must be added to an application before the typed helpers can address it. Core's typed invocation rules are documented under run() path and input.

app.run(path, input?)

const outcome = await app.run(["build"], { flags: { minify: true } });
if (outcome.status === "failed") throw outcome.error;
expect(outcome.stdout).toContain("Built");

Use run() to capture stdout, stderr, and the Command Action outcome without terminal parsing. See the core run() reference for result branches and capture boundaries.

captureExecute(app, argv)

const captured = await captureExecute(app, ["unknown"]);
console.log(captured.exitCode); // 1
console.log(captured.stderr); // Error: Unknown command "unknown".

captureExecute() drives raw argv through execute() in-process and returns stdout, stderr, and exitCode. It restores the previous process exit state after capture. Use it for parser errors, rendered failures, and Extension onError output.

Prop

Type

runInteractive(app, path, input?)

Import from @crustjs/testing/interactive; this entry point requires @crustjs/prompts.

const run = runInteractive(app, ["greet"]);
await run.waitFor(/Name/);
run.type("Ada");
run.keys("return");
await run.done;
console.log(run.screen()); // ✓ Name? Ada

waitFor() polls the ANSI-stripped screen and defaults to a 5000 ms timeout. It fails as soon as the invocation fails or completes without a match, rather than waiting for the timeout. Call type() or keys() after the expected prompt appears, then await done.

done rejects with the error from a failed outcome. One ambient withTerminalIO scope sends prompts, progress, and ctx.stderr() to the fake screen.

ctx.stdout() is not included, so use run() for stdout assertions.

Prop

Type

Custom prompts

import { renderPrompt } from "@crustjs/prompts/testing";

@crustjs/testing does not re-export prompt-author helpers. Use renderPrompt() for custom (options, io?) prompts.

On this page