burgee/testing
Every export of burgee/testing, with its signature and doc comment: processRuntime, captureConsole, codeOf, fakeClock, fakeRuntime, finish and 7 more, plus 7 types.
burgee/testing — run a burgee CLI in-process, with argv, env, stdin, cwd and
TTY-ness injected, and get back { code, stdout, stderr, json }. Requirement T1.
A separate entry point so it is paid for per import (K6): a user's shipped CLI
imports burgee and never pulls a byte of this.
import { processRuntime, captureConsole, codeOf, … } from 'burgee/testing';Functions
captureConsole
Route console.* into the fake runtime while a run lasts. Hosts print through
console in a few paths a public seam does not cover (yargs' help printer, a
handler that never met the output layer); a harness that let those reach the real
terminal would be measuring the wrong thing.
function captureConsole(rt: FakeRuntime): () => void;| Parameter | Type |
|---|---|
rt | FakeRuntime |
Returns () => void
codeOf
The E1 code an unwound error carries, or RUNTIME when it carries none.
function codeOf(e: unknown): ExitCode;| Parameter | Type |
|---|---|
e | unknown |
Returns ExitCode
fakeClock
Deterministic time for the harness. Starts at start (0 by default) and never moves on its own.
function fakeClock(start?: number): FakeClock;| Parameter | Type |
|---|---|
start (optional) | number |
Returns FakeClock
fakeRuntime
A Runtime whose every part is under the test's control.
function fakeRuntime(opts: RunOptions): FakeRuntime;| Parameter | Type |
|---|---|
opts | RunOptions |
Returns FakeRuntime
finish
Assemble the result (R2). json is set only when --json was in argv and stdout
parsed; a parse failure turns the run into RUNTIME with the parse error on
stderr, so a broken envelope cannot pass a test by accident.
function finish(rt: FakeRuntime, code: ExitCode, startedAt: number): RunResult;| Parameter | Type |
|---|---|
rt | FakeRuntime |
code | ExitCode |
startedAt | number |
Returns RunResult
isExitCode
True for the seven codes in the contract and nothing else.
function isExitCode(n: unknown): n is ExitCode;| Parameter | Type |
|---|---|
n | unknown |
Returns n is ExitCode
runBurgee
Run a burgee program in-process — the T1 harness for burgee itself. Env is injected,
never swapped: the engine reads env-bound options from the runtime it is given, so
process.env is untouched by construction. Exit unwinds through RuntimeExit.
function runBurgee(program: Manifest, opts: RunOptions): Promise<RunResult>;| Parameter | Type |
|---|---|
program | Manifest |
opts | RunOptions |
Returns Promise<RunResult>
stripAnsi
Strip ANSI escape sequences — the decision from the intent: stdout stays raw.
linegauge's strip, not a regex of this file's own. The one this used to carry,
ESC[[0-9;]*[A-Za-z], left the private modes (ESC[?25l, which every spinner writes),
the colon form of an extended colour (ESC[38:2::255:0:0m, which chalk emits for
truecolor) and every OSC 8 hyperlink in the text a test asserted against.
function stripAnsi(text: string): string;| Parameter | Type |
|---|---|
text | string |
Returns string
swapEnv
function swapEnv(env: Record<string, string> | undefined): () => void;| Parameter | Type |
|---|---|
env | Record<string, string> | undefined |
Returns () => void
Classes
RuntimeExit
Thrown by fakeRuntime.exit so a handler that exits unwinds to the harness.
class RuntimeExit extends Error {
readonly code: ExitCode;
constructor(code: ExitCode);
}Constants
ExitCode
E1 — exit codes are a contract. No other literal may reach process.exitCode.
const ExitCode: {
/** Command completed. */
readonly OK: 0;
/** The command ran and failed. Never accompanied by help text (E2). */
readonly RUNTIME: 1;
/** Bad arguments, unknown command, missing flag, prompt needed in a non-TTY (P2). */
readonly USAGE: 2;
/** Config file or environment could not be loaded or validated (V1). */
readonly CONFIG: 3;
/** The user or caller cancelled. */
readonly CANCELLED: 4;
/**
* E6 — the far side said no: a credential is missing, expired, or refused.
*
* Its own code because it is the most actionable one in the survey. `RUNTIME` means *it
* failed, read the message*; `AUTH` means *log in and run it again*, and a script or an
* agent can branch on that without parsing prose. `USAGE` says fix the script, `CONFIG`
* says fix the runner, and this says fix the credential — three different responses that
* collapsed into one code before it existed.
*
* **5, where `gh` uses 4.** Four is `CANCELLED` here and has been since the contract was
* written, and moving a published code to match another tool's is a breaking change for
* every consumer that already branches on it. The survey's other citation, `aws` v2, uses
* 252/253/254 and agrees with nobody either; what matters is that the code is stable and
* documented, not that it matches a particular neighbour.
*/
readonly AUTH: 5;
/** SIGINT after the terminal was restored (E5). */
readonly SIGINT: 130;
};
type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];ExitCodeValue
E1 — exit codes are a contract. No other literal may reach process.exitCode.
const ExitCode: {
/** Command completed. */
readonly OK: 0;
/** The command ran and failed. Never accompanied by help text (E2). */
readonly RUNTIME: 1;
/** Bad arguments, unknown command, missing flag, prompt needed in a non-TTY (P2). */
readonly USAGE: 2;
/** Config file or environment could not be loaded or validated (V1). */
readonly CONFIG: 3;
/** The user or caller cancelled. */
readonly CANCELLED: 4;
/**
* E6 — the far side said no: a credential is missing, expired, or refused.
*
* Its own code because it is the most actionable one in the survey. `RUNTIME` means *it
* failed, read the message*; `AUTH` means *log in and run it again*, and a script or an
* agent can branch on that without parsing prose. `USAGE` says fix the script, `CONFIG`
* says fix the runner, and this says fix the credential — three different responses that
* collapsed into one code before it existed.
*
* **5, where `gh` uses 4.** Four is `CANCELLED` here and has been since the contract was
* written, and moving a published code to match another tool's is a breaking change for
* every consumer that already branches on it. The survey's other citation, `aws` v2, uses
* 252/253/254 and agrees with nobody either; what matters is that the code is stable and
* documented, not that it matches a particular neighbour.
*/
readonly AUTH: 5;
/** SIGINT after the terminal was restored (E5). */
readonly SIGINT: 130;
};
type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];processRuntime
The real Runtime, for a caller that injects none.
argv, env and cwd are getters for the reason the block above gives; isTTY is one
too, because a stream's isTTY is a property of whatever stream is installed now, and
the harness swaps streams. stdin/stdout/stderr likewise.
const processRuntime: Runtime;Interfaces
Clock
Time, as the layers above the parser see it (R14 of cli-output-stack). now is
monotonic milliseconds from an arbitrary origin; schedule runs fn after ms and
hands back the cancel. Typed structurally so the output stack can accept a Runtime
without importing one.
interface Clock {
now(): number;
schedule(fn: () => void, ms: number): () => void;
}FakeClock
A Clock that moves only when the test says so (R14): tick is the only source of time.
interface FakeClock extends Clock {
/**
* Advance by `ms`, running every callback that falls due, earliest first, then by order
* scheduled. A callback that schedules inside the window runs in the same tick, so one that
* reschedules itself at `0` would never leave the loop (real Node yields between turns);
* a tick runs at most `TICK_CAP` callbacks and throws, naming the cap, when exceeded.
*/
tick(ms: number): void;
/** Callbacks scheduled and neither run nor cancelled. */
pending(): number;
}FakeRuntime
interface FakeRuntime extends Runtime {
out: string[];
err: string[];
clock: FakeClock;
}RunOptions
interface RunOptions {
argv: string[];
env?: Record<string, string>;
stdin?: string | Readable;
cwd?: string;
/** `true` = every stream is a TTY; an object sets each; default: none is. */
tty?: boolean | Partial<Runtime['isTTY']>;
/** The clock the runtime reports; a fresh `fakeClock()` at 0 when not given. */
clock?: FakeClock;
}RunResult
interface RunResult {
code: ExitCode;
stdout: string;
stderr: string;
/** Parsed stdout when `--json` was in argv and stdout parsed; see `finish`. */
json?: unknown;
durationMs: number;
}Runtime
The world, as the layers above the parser see it (design R1 of
cli-testing-harness). Nothing above the parser reads process.* directly; it
reads its Runtime, so a test can substitute every part of it.
interface Runtime {
argv: string[];
env: Record<string, string | undefined>;
cwd: string;
stdin: NodeJS.ReadableStream;
stdout: Writer;
stderr: Writer;
isTTY: {
stdin: boolean;
stdout: boolean;
stderr: boolean;
};
/** Ends the run with an E1 code. In the real runtime this never returns. */
exit(code: ExitCode): never;
/** `performance.now` and `setTimeout` in the real runtime; a manual tick in the harness. */
clock: Clock;
}Writer
Anything that accepts text; process.stdout satisfies it, so does an array push.
interface Writer {
write(chunk: string): unknown;
}burgee/cli
Every export of burgee/cli, with its signature and doc comment: brandCommand, devCommand, migrateCommand, pluginCheckCommand, program.
burgee/commander
burgee/commander is a drop-in for commander: the same API, documented by commander itself. Every name it exports, and where its reference lives.