stricttest v0.2.0 /typescript/src/sandbox
On this page

Bare-run refusal for Node suites: why the threshold is a function the consumer calls rather than a runner hook, and where the honest call sites are.

#typescript/src/sandbox

#typescript/src/sandbox

Bare-run refusal: a suite that is supposed to run inside the sandbox refuses to run outside it.

#Why this is a helper the consumer calls, and not a hook

The Python plugin refuses at pytest_collection_modifyitems: pytest hands it the whole selected test list before a single test body runs, so the plugin can count the run and abort it before anything happens.

Node's built-in runner offers no equivalent, and the gap is structural rather than a missing API. node --test runs each test FILE in its own child process; a module loaded with --import is loaded in those children, never in the parent that owns the run (NODE_TEST_CONTEXT=child-v8 is set in every one of them). A setup module therefore sees exactly one file and can never learn how many tests the whole run selected. Custom reporters do run in the parent, but they are fed events as tests complete -- far too late to refuse a run, and with no way to abort one.

So the count has to come from wherever the consumer genuinely knows it, and this module is a plain function the consumer calls there. The two honest call sites:

  • A programmatic runner entry. node:test's run({ files }) is called

with the file list, so the entry point knows the size of the run before it starts. Use policy: "threshold" there:

```ts import { run } from "node:test"; import { glob } from "node:fs/promises"; import { requireSandbox } from "stricttest";

const files = await Array.fromAsync(glob("dist-test/tests/ *\/.test.js")); requireSandbox({ policy: "threshold", threshold: 10, count: files.length }); run({ files }).pipe(process.stdout); ```

  • **A --imported setup module**, for suites that run through plain

node --test. A child cannot count the run, so the only honest stance there is policy: "always" -- every bare run is refused, including the small targeted one. That is strictly stronger than the threshold and costs the fast bare inner loop; a suite that wants that loop back needs the runner entry above.

There is deliberately no third shape that guesses. A cross-process counter that refused halfway through a run would have already executed tests bare, which is the thing being prevented.

#DEFAULT_SANDBOX_ENV

TS typescript
export const DEFAULT_SANDBOX_ENV = "STRICTTEST_SANDBOX"

The environment variable the sandbox runner sets to "1".

#DEFAULT_RUNNER_COMMAND

TS typescript
export const DEFAULT_RUNNER_COMMAND = "scripts/test.sh"

The command shown in the refusal message.

#SandboxRequiredError

TS typescript
export class SandboxRequiredError extends Error

Thrown by {@link requireSandbox} when a run is refused.

#SandboxPolicy

TS typescript
export type SandboxPolicy =

How much of a bare run to refuse. The choice is required: a suite declares where it stands rather than inheriting a default.

  • "always": refuse every run outside the sandbox.
  • "threshold": refuse a run of more than threshold items outside the

sandbox, where count is how many this run has. Smaller runs stay bare for iteration speed.

#RequireSandboxOptions

TS typescript
export type RequireSandboxOptions = SandboxPolicy &

#insideSandbox

TS typescript
export function insideSandbox(sandboxEnv = DEFAULT_SANDBOX_ENV): boolean

Whether this process is running inside the sandbox runner.

#requireSandbox

TS typescript
export function requireSandbox(options: RequireSandboxOptions): void

Refuse this run if it is happening outside the sandbox, per the declared policy. Throws {@link SandboxRequiredError}; returns silently otherwise.

Search