Module: testRunner

Thin orchestrator for Playwright test execution with parallel worker support.

Owns the browser lifecycle, per-test loop (sequential or parallel), trace management, and final status transition. Delegates heavy sub-tasks to focused modules:

Module Responsibility
runner/config.js Env constants, artifact dir setup
runner/codeParsing.js extractTestBody (hasCode check)
runner/executeTest.js Single-test execution
runner/feedbackIntegration.js Post-run AI feedback loop

Parallel execution

When parallelWorkers > 1, tests run in concurrent browser contexts within a single Chromium instance. Each worker picks the next queued test, executes it in its own isolated BrowserContext, and reports back. The shared browser process keeps memory usage lower than launching N separate browsers.

Concurrency is controlled by:

  1. PARALLEL_WORKERS env var (default for all runs)
  2. Per-run override via options.parallelWorkers (from Test Dials / API)

Exports

  • runTests — Execute an array of approved tests against a project.
Source:

Methods

(static) computeShardSizes(total, shardCount) → {Array.<number>}

CAP-002 — Compute per-shard sizes using Playwright's --shard=N/M algorithm: the first total % shardCount shards receive one extra item, so shard sizes differ by at most one. Pure function — no I/O, no mutation, no allocations beyond the returned array. The single source of truth for the partition shape; both partitionTestsIntoShards (stamps _shardIndex on test objects) and partitionTestIdsForShards (slices plain-string arrays) derive their output from this.

Parameters:
Name Type Description
total number

Total item count.

shardCount number

1..MAX_WORKERS (caller is responsible for clamp).

Source:
Returns:

sizes[shardIndex] — per-shard item count. Empty trailing shards (possible when shardCount > total) yield 0.

Type
Array.<number>

(static) partitionTestIdsForShards(testIds, shardCount) → {Array.<Array.<string>>}

CAP-002 Phase 2 — Partition test IDs into shardCount contiguous slices using the shared computeShardSizes algorithm. The route layer calls this at enqueue time to pre-compute each BullMQ shard job's testIds payload — the coordinator is the single source of truth for the split; workers never re-derive the partition (avoids drift if a future test sort changes the approved-test order between enqueue and worker pickup).

Implementation note: slices the input array directly rather than allocating a tagged-copy. computeShardSizes is the single source of truth shared with partitionTestsIntoShards, so the algorithm cannot drift between the two callers.

Parameters:
Name Type Description
testIds Array.<string>

Approved test IDs in dispatch order.

shardCount number

1..MAX_WORKERS (caller is responsible for clamp).

Source:
Returns:

slices[shardIndex] is the array of test IDs for that shard. Empty shards (possible when shardCount > testIds.length) yield [] at their slot.

Type
Array.<Array.<string>>

(static) partitionTestsIntoShards(tests, shardCount) → {Object}

CAP-002 — Partition tests into shardCount contiguous slices using the shared computeShardSizes algorithm. Tags each test with _shardIndex in place (callers rely on this to attribute results to the correct shard at completion time) and returns the sizes[] array so the runner can detect "last test in shard S has finished" without re-deriving the partition. Pure function — no DB or side effects — so the partition contract can be exercised in isolation by backend/tests/run-sharding.test.js.

Parameters:
Name Type Description
tests Array.<Object>

Tests in dispatch order (post-smoke-pin).

shardCount number

1..MAX_WORKERS (caller is responsible for clamp).

Source:
Returns:

per-shard test counts.

Type
Object

(static) runTests(project, tests, run, optionsopt) → {Promise.<void>}

Execute an array of approved tests against a project using Playwright. Launches Chromium, runs each test with self-healing (optionally in parallel), collects results, saves traces/videos, runs the AI feedback loop, and finalises the run.

Parameters:
Name Type Attributes Description
project Object

The project { id, name, url }.

tests Array.<Object>

Array of test objects to execute.

run Object

The run record (mutated in place).

options Object <optional>
Properties
Name Type Attributes Description
parallelWorkers number <optional>

Concurrent browser contexts (1–10). Overrides env default.

browser string <optional>

"chromium" | "firefox" | "webkit" (DIF-002). Defaults to chromium.

device string <optional>

Playwright device preset name (DIF-003).

locale string <optional>

BCP 47 locale (AUTO-007).

timezoneId string <optional>

IANA timezone (AUTO-007).

geolocation Object <optional>

{ latitude, longitude } (AUTO-007).

signal AbortSignal <optional>

Abort signal for cancellation.

shardIndex number | null <optional>

CAP-002 Phase 2: when set, the cross-process shard worker passes its 0-based shard index so trace artifacts land at ${TRACES_DIR}/${runId}/shard-${shardIndex}.zip instead of the single-path layout. null (default) preserves the pre-shard zero-regression path — same filename, same run.tracePath, no tracePaths[] JSON column populated. See migration 026.

Source:
Returns:
Type
Promise.<void>

(static) shardTraceArtifactPath(runId, shardIndex) → {string}

CAP-002 Phase 2 (Prerequisite #2) — Compute the public artifact URL for a shard's trace zip. The path mirrors the on-disk layout in TRACES_DIR so signArtifactUrl and the trace-viewer static-file mount can resolve nested paths without special-casing. shardIndex == null (legacy / single-shard runs) returns the flat ${runId}.zip URL — zero regression for every existing consumer of run.tracePath. Pure: no I/O, no DB, exported so backend/tests/run-sharding.test.js can assert the contract directly.

Parameters:
Name Type Description
runId string
shardIndex number | null

0-based shard index, or null for legacy single-path runs.

Source:
Returns:

/artifacts/traces/<runId>.zip or /artifacts/traces/<runId>/shard-<idx>.zip

Type
string