interface RunResult

Everything a finished run produced.

A run whose tests failed is a successful run. run() resolves with ok: false; it does not reject. Rejection is reserved for the run not happening — a bad option, an unreadable input, a project with no package.json. Distinguishing "the suite says no" from "the runner could not answer" is the whole point of the split, and it is what lets catch mean something.

// Defined, not invoked: a real result comes back from `run()`.
function summarize(result: RunResult) {
  return `${result.counts.passed}/${result.counts.total} passed in ${result.durationMs}ms`;
}

Properties

ok: boolean

true when every test passed and nothing else went wrong.

exitCode: number

What the CLI would have exited with: 0 when ok, 1 otherwise.

durationMs: number

Wall-clock duration of the test phase, in milliseconds.

Outcome totals.

Every finished test, in the order the browser reported them.

The subset of tests that failed — the list you almost always want first.

files: string[]

Absolute paths of the test files this run executed — the ones scoped to, in a filtered watch rerun, rather than everything being watched.

failedFiles: string[]

Absolute paths of the test files with at least one failure, attributed via source maps.

qunitx's own diagnostics for this run, in emission order.

console.* calls and uncaught errors from the page — warnings and errors unless debug.

Capped at the most recent MAX_BROWSER_LOGS; browserLogsDropped counts what was dropped. The cap is not tidiness: unlike tests and notices, page output is bounded by nothing — one for loop around console.warn produced 50,001 entries and 252 MB of retained heap. The newest are kept because they are the ones adjacent to the failure being diagnosed.

How many page-log entries were dropped to stay under the cap. 0 in every ordinary run.

Line coverage, or null when coverage was not requested.

junitXml: string | null

The JUnit XML document, when junit was requested. Written to disk as well.

startedAt: number

Epoch ms when the test phase began.

finishedAt: number

Epoch ms when it ended.

How the files were split across concurrent groups — one entry per group, never empty. groups.length is the concurrency the run actually used; 1 for watch and single-file runs.

Worth having because the split is not reproducible from the outside: it is recomputed each run from recorded timings and the core count. When a test only fails alongside a particular neighbour, this is what identifies the neighbour — and run({ inputs: result.groups[2].files }) re-runs that bundle on its own.

There is deliberately no URL here. A group's server is closed by the time this result exists, so an address would be a link that never resolves; RunGroup.output is the durable equivalent, still on disk, and it is what --open points a browser at.

status: "completed" | "aborted" | "failFast"

How the run finished: whether it got through everything it selected, and if not, what stopped it. Distinct from RunResult.ok, which is the verdict — a completed run can be entirely red, and an aborted one can have no failures at all.

A boolean aborted could not tell a suite that stopped at its first failure from one that genuinely has two tests — both come back ok: false with a small counts.total — so the three endings are named instead:

  • completed — every selected test reached an outcome.
  • aborted — something cut it short: session.abort(), the CLI's qq, or a signal. An already-aborted signal lands here too, having launched no browser at all.
  • failFastfailFast was set and a test failed, so the rest of the queue was dropped. Reported whenever the policy ended the run, including when the failure was the last test.

completed is the only one where counts.total is the whole selection. Check it before reporting a red run as red, or a UI says "1 failure" about a suite it never finished.

What the run resolved to — see ResolvedRun.