@caribou-crew/retrace-js
v0.0.44
Published
The base test-runner adapter for [retrace](../../retrace). Marks flow-part groups and reports where checkpoints belong, using the environment `retrace run` injects into the test command:
Readme
@caribou-crew/retrace-js
The base test-runner adapter for retrace. Marks flow-part
groups and reports where checkpoints belong, using the environment
retrace run injects into the test command:
| Variable | Set by retrace run for |
| --------------------- | -------------------------------------------------- |
| RETRACE_RUN_DIR | file-writing adapters (this package, checkpoints) |
| RETRACE_PROXY_URL | pointing the app under test at retrace's proxy |
| RETRACE_MARKER_URL | HTTP-only runners that cannot write files (Maestro) |
| RETRACE_STRICT | 1/true/yes/on = fail loudly outside a run |
Zero runtime dependencies: node:fs, node:path, and global fetch.
Usage
import { group, endGroup } from '@caribou-crew/retrace-js';
await group('checkout');
// ...exercise the checkout flow...
await endGroup();Outside a retrace run (plain vitest/jest/CI without retrace), group
and endGroup are no-ops — your suite runs unmodified. Set
RETRACE_STRICT=1 to make a missing handshake a loud error instead, per the
spec's "fail loudly" requirement:
import { requireHandshake } from '@caribou-crew/retrace-js';
requireHandshake(); // throws MISSING_HANDSHAKE_MESSAGE if RETRACE_STRICT=1
// and neither RETRACE_RUN_DIR nor RETRACE_MARKER_URL is setAPI
handshake(env?)— reads the four env vars into aHandshake. Never throws for a missing run; throws ifRETRACE_STRICTholds a value outside1/true/yes/on/0/false/no/off(case-insensitive) or unset/empty.requireHandshake(env?)—handshake()plus: throwsMISSING_HANDSHAKE_MESSAGEwhen strict mode is on and there is no run to write to.group(name, options?)/endGroup()— append a flow-part marker, togroups.jsonlinRETRACE_RUN_DIRwhen set, else POSTed toRETRACE_MARKER_URL.namemust be non-empty, not start with., and match^[A-Za-z0-9._-]+$— this reproduces the full guardretrace/runs.ValidateComponentsenforces, not just its regex — a name failing any of those clauses throws rather than silently writing nowhere.shotsDir(env?)— the directory checkpoints belong in (RETRACE_RUN_DIR/shots), ornulloutside a run. Used by@caribou-crew/retrace-playwright.MISSING_HANDSHAKE_MESSAGE— the shared strict-mode error text, imported verbatim by@caribou-crew/retrace-playwrightand@caribou-crew/retrace-maestro.
Publishing
package.json sets "private": true deliberately: it enforces "no
accidental npm publish" at the package level, on top of (not instead of)
npm's trusted publishing
being set up for this package on npmjs.com — see
.github/workflows/publish.yml's comments for what that setup involves.
Publishing this package is the maintainer's call to make; when they do,
clearing private here is part of enabling it.
Commit-level suite reports
createSuiteAttempt bridges an existing test runner to the suite dashboard. It
records explicit results; it does not execute tests or decide whether a visual
or wire comparison passes. Configure the expected flows in retrace.suites.json
(see suite reporting). Independent web, iOS and
Android jobs join one build only when source, suite version, baseline and policy
identities agree.
import { createSuiteAttempt } from '@caribou-crew/retrace-js';
import { writeFile } from 'node:fs/promises';
const report = createSuiteAttempt({
suiteId: 'legacy-to-taxi', suiteVersion: 'v1', attemptId: process.env.CI_JOB_ID,
platform: 'web',
git: { sha: process.env.GIT_COMMIT, branch: process.env.GIT_BRANCH, dirty: false },
baselineId: 'legacy-2026-09-20', policyId: 'reviewed-parity-v1',
});
// After running the flow and reading its actual assertion/comparison results:
report.record({
flowId: 'login',
planes: { functional: 'pass', wire: 'pass', visual: 'incomplete' },
reason: 'Visual reference not reviewed yet',
});
await writeFile('suite-report.json', JSON.stringify(report.finish()));Then run retrace suite import --file suite-report.json in the project root.
Use a unique attempt ID for every job/retry; existing reports cannot be
replaced. A dirty build requires workspaceId identifying the actual source
snapshot (for example, a digest of the source bundle); a Git SHA alone cannot
identify uncommitted changes. Omitted flows remain not-run. Keep error reasons
redacted, and keep private reports/artifacts out of source control.
