Interfaces

I
BuildError

An esbuild failure, captured for display on the run's error page.

I
BuildState

One group's esbuild output and in-flight build bookkeeping, kept warm between watch-mode rebuilds. Lives at state.group.build, so it shares the group's lifetime rather than being threaded alongside the config as a second, independently-passable bag.

  • activeRebuild: Promise<void> | null

    Set when a parallel rebuild is in-flight during a watch-mode rerun. The /tests.js route awaits this before serving, so Chrome can navigate concurrently while esbuild finishes. Cleared by run after the build settles.

  • allTestCode: Buffer | string | null

    Full test bundle source, or null before the first build completes.

  • fallbackPage: FallbackPage | null

    Replaces the normal test page for this run, or null when the run renders tests as usual. The web server's / route serves this page and the Playwright page is navigated there. Cleared at the start of every new build attempt.

  • filteredTestCode: Buffer | string

    Bundle filtered to files that failed on the previous run (used by re-run mode).

  • htmlPathsToRunTests: string[]

    Absolute paths of every HTML file that will be opened in the browser to run tests.

  • lastBuildErrored: boolean

    true if the most recent build ended in an esbuild error. Keeps state.watch.lastBuildEndMs pinned to the last good build so a fix arriving after the error is never suppressed. Written by every run, read only in watch mode.

  • preBuildPromise: Promise<void> | null

    In-flight build promise started by run.ts before Chrome setup completes (initial run) or before run is called (reruns), so esbuild races navigation. Consumed and cleared by the first run() call.

I
ChromeHandle

Handles to a pre-launched Chrome, available synchronously the moment it is spawned — before the CDP endpoint is known. Enough to reap the process and its temp dir, which is all the process.on('exit') safety net and shutdownPrelaunch() need.

  • proc: ChildProcess

    The spawned Chrome child process.

  • shutdown: () => Promise<void>

    Kills Chrome and awaits async temp-dir cleanup. Safe to call before CDP is ready, and idempotent with Chrome's own dead-on-arrival cleanup. Call before process.exit().

I
Config

Full resolved qunitx configuration for a single run, merging package.json settings, CLI flags, and runtime state. Most fields are read-only after Config.setup() resolves; underscore-prefixed fields are mutable runtime slots populated during the run lifecycle.

  • browser: "chromium" | "firefox" | "webkit"

    Browser engine used for the test run ('chromium' | 'firefox' | 'webkit').

  • cwd: string

    Working directory this run resolves against: relative inputs, bare-specifier resolution inside the test bundle, and --before/--after hook paths. process.cwd() for the CLI; the JS API's cwd option otherwise. Distinct from projectRoot, which is wherever the nearest package.json sits — running from a subdirectory keeps that subdirectory's node_modules on the resolution chain, exactly as Node itself would.

  • extensions: string[]

    File extensions treated as test files (default: ['js', 'ts']).

  • failFast: boolean

    When true, abort the run after the first test failure (default: false).

  • fsTree: FSTree

    Current file-system snapshot, diffed in watch mode to detect added / removed files.

  • htmlPaths: string[]

    Absolute paths to HTML fixture files that wrap the compiled test bundle.

  • output: string

    Directory where the compiled test bundle and output HTML are written (default: 'tmp').

  • plugins: EsbuildPlugin[]

    Custom esbuild plugins applied during the test bundle build. Loaded from qunitx.config.{ts,js,mjs} in the project root. Common use cases: SFC formats like .vue (esbuild-plugin-vue-next), Svelte (esbuild-svelte), or any project-specific resolvers/loaders.

  • port: number

    TCP port the local test server listens on (default: 1234, auto-increments on conflict).

  • projectRoot: string

    Absolute path to the project root (directory containing package.json).

  • state: RunState

    Mutable state for this run; see RunState for the sharing rules.

  • testFileLookupPaths: string[]

    Paths searched when globbing for test files.

  • timeout: number

    Maximum milliseconds to wait for the full test suite before timing out (default: 20000).

  • webServer: HTTPServer

    The run's HTTP server, exposed purely as --before / --after hook surface — qunitx itself never reads it back. Hooks use it to register extra routes (mock APIs) before tests start.

I
Connections

Live handles for the three resources allocated at the start of a test run. Passed through the run pipeline and closed together on shutdown.

I
Counter

Running totals of test outcomes for a single test run. Mutated in place as TAP events arrive from the browser.

I
DaemonState

The daemon's persistent, cross-run handles, lent to a single run via RunState.daemon.

  • browser: Browser

    The daemon's Browser. run() reuses it instead of launching, and does not close it.

  • esbuildCache: EsbuildCache

    Persistent incremental-context slot, keeping the module graph warm across daemon runs.

  • pageSlot: { page: Page | null; }

    Persistent Page slot, reused across runs to save a newPage() (~70-130ms). Read through RunState.reusablePageSlot(), never directly — reuse is only valid for single-group runs.

I
EarlyChrome

A ChromeHandle plus the CDP endpoint, resolved once Chrome is listening.

I
EsbuildCache

A slot holding esbuild's incremental build context plus the key it was built for. Two lifetimes share this shape: the per-process one on BuildState (watch mode) and the daemon's persistent one, which survives across runs. buildIncrementally takes either — it disposes and recreates the context whenever the key changes.

I
FileCoverage

Per-source-file line coverage, accumulated across every executed bundle. Keyed by absolute source path. Lines are 1-based, matching editor/lcov conventions.

I
GroupState

RunState scoped to a single concurrent group — one fresh object per group of a run.

  • build: BuildState

    This group's bundle output and build bookkeeping.

  • groupMode: boolean

    true while running as one of several concurrent groups.

  • index: number

    Index within the run's group array; 0 for watch and single-group runs.

  • lastQUnitResult: QUnitResult | null

    QUNIT_RESULT delivered via the WS 'done' message; avoids a page.evaluate() CDP round-trip.

  • lastRanFiles: string[] | null

    Test files this group ran on the last run. Failure attribution falls back to this when a failing assertion's stack can't be resolved to one file — scoped per group so an unattributable failure blames only the files that group ran, not the whole invocation.

  • pendingConsoleHandlers: Set<Promise<void>> | null

    In-flight console handler promises; awaited before browser/page close so Firefox BiDi round-trips complete.

  • phase: "bundling" | "connecting" | "loading" | "running" | "done"

    Current lifecycle phase of this group's run.

  • selectors: QUnitSelector[] | undefined

    Exact test selections for this group, derived from lineTargets. Applied in the browser via QUnit.config.testFilter, which QUnit ANDs after filter/module. Per-group: each line-targeted file runs as its own group so untargeted files stay unfiltered.

  • signals: RunSignals

    Callbacks the run pipeline waits on, resolved as the browser reaches each milestone.

  • sourceMapDecoder: SourceMapDecoder | null

    Decoded inline source map for this group's bundle; resolves stack frames to original sources.

  • testEndCounts: Map<string, number>

    Tracks testEnd arrivals per test fullName in this group's run. Reset in lockstep with the run counter — explicitly NOT on every WS 'connection' event, which was the bug that broke no-html-test in CI run 26042614416.

  • wsConnectionCount: number

    Diagnostic-only: how many distinct WS connections this group's wss handler has accepted. Reset per WebServer.setup call. > 1 means the browser opened multiple WebSocket connections within one run — the prime suspect for the 2× test-execution flake (WS retry path in the injected runtime).

I
HtmlAssets

The run's resolved HTML fixtures and the assets they reference. Populated once by buildCachedContent and not written again, so every concurrent group can share one copy.

I
JUnitCase

One collected JUnit <testcase> — accumulated per testEnd and serialized into junit.xml at run end when --reporter=junit is active.

I
QUnitResult

The run summary the browser-side runtime publishes on window.QUNIT_RESULT.

I
RunGroup

One concurrent group of a run: the files bundled into a single page, and where that page's artifacts were written.

  • files: string[]

    Absolute paths of the test files this group bundled and ran together.

  • index: number

    Position in the run's group list, and the group-<index> suffix on output.

  • output: string

    Absolute path of this group's build output directory.

I
RunResults

Outcome totals and failure bookkeeping accumulated across every group of a single run. Every field here is mutated in place — see RunState for why replacement is unsafe.

  • aborted: boolean

    Whether the browser confirmed it dropped this run's remaining queue — qq, session.abort(), or an aborted signal. Lives here rather than on the group because groups share this object by reference, so one aborted group marks the whole run without any cross-group plumbing.

  • counter: Counter

    Running test-outcome counts, mutated in place as TAP events arrive.

  • coverage: CoverageFileMap | null

    Accumulator for per-source line coverage when coverage is enabled; null when it is off. Reassigned only by RunState.reset (a fresh Map per run), never by a group.

  • failedFiles: Set<string>

    Absolute paths of test files with ≥1 failure in the current run, attributed per-test via source maps. Every group adds into this one set; persisted to the failure cache at run end.

  • failedTests: FailedTestRecord[]

    Per-test metadata for the current run's failures; accumulated alongside failedFiles.

I
RunSignals

One-shot callbacks wiring the browser's progress back into the run pipeline. Each is installed by the code that awaits it and fired by the web server as the corresponding event arrives.

I
RunState

Mutable state for one test run, kept separate from the resolved settings on Config.

  • aborters: Set<() => void>

    One callback per live server that tells its connected pages to drop the rest of the QUnit queue — what qq, session.abort() and an aborted signal all go through.

  • console: Console

    Where this run's text goes: the TAP document, every reporter line, every # diagnostic and every forwarded page log. processConsole for the CLI, silentConsole for a programmatic run that only wants the result value. Shared by reference across concurrent groups.

  • daemon: DaemonState | null

    Non-null exactly when this run is executing inside the persistent daemon process — it is the daemon-mode flag as well as the handles. Daemon runs reuse the shared browser, suppress the per-connection TAP header, and leave that browser open at the end of the run.

  • group: GroupState

    RunState for this group only. The group spread replaces this object (everything else in RunState is shared by reference), so it is the one place per-group slots may live.

  • groupCount: number

    Number of concurrent groups in this run; 1 for watch and single-group runs.

  • groups: RunGroup[]

    How this run's files were split across those groups — the descriptive record RunResult reports. Assigned once per run alongside groupCount and shared by reference, so a group can read the whole split rather than only its own slice.

  • htmlAssets: HtmlAssets

    HTML fixtures and their referenced assets, resolved once by buildCachedContent before any group config is spread off. Frozen from that point on, so all groups share one copy.

  • reporters: Reporter[]

    Active reporter instances for this run, built by Reporters.create in Config.setup. One set for the whole run, so a stateful reporter sees every group rather than one slice.

  • results: RunResults

    Whole-run accumulators, shared by reference across every concurrent group.

  • signal: AbortSignal

    Cancels this run when it fires, or absent when nothing can. Carried here rather than threaded alongside the config because it is per-run input like everything else in state — and it is what lets a verb reach it from the resolved config instead of re-reading the raw arguments.

  • watch: WatchState

    File-watcher build bookkeeping. Only meaningful in watch mode, where there is one group.

I
WatchState

Build bookkeeping owned by the file watcher, used to decide whether a filesystem event should dispatch a rebuild. Watch mode runs exactly one group, so nothing here is contended.

  • building: boolean

    true while esbuild is actively compiling.

  • builtContentHash: Record<string, string>

    Per-file content hash of what was last dispatched to a build. Both the fs.watch change handler and the macOS/Deno rescan compare against this instead of mtime — mtime has 1-second resolution on some filesystems (macOS/HFS+), so rapid same-second writes with different content are indistinguishable by mtime; the hash catches them and drops echoes.

  • justAddedAt: Map<string, number>

    filePath → ms of when each file was last processed as an 'add', so a 'change' echo arriving inside ADD_SUPPRESS_WINDOW_MS can be suppressed.

  • justAddedFiles: Set<string>

    File paths added since the last build, used to decide whether a rebuild is needed.

  • lastBuildEndMs: number

    Timestamp (ms) of the most recent successful build, used for debounce logic. 0 before the first build.

  • pendingBuildTrigger: (() => void) | null

    Queued build-trigger callback; fires once the in-progress build completes.

Type Aliases

T
CoverageFileMap = Map<string, FileCoverage>

Absolute source path → its accumulated FileCoverage.

T
FallbackPage =
{ kind: "build-error"; error: BuildError; }
| { kind: "no-tests"; files: string[]; }

Why a run is showing something other than its tests: the last esbuild run failed, or every test file compiled but registered 0 QUnit tests (files holds their display paths).

T
FSTree = Record<string, null>

Snapshot of the project's file-system structure: a map of relative paths to null. Diffed against a fresh snapshot in watch mode to detect added or removed test files.