Module: routes/tests

Test CRUD, AI generation, single-test run, review, bulk actions, and export. Mounted at /api/v1 (INF-005).

REFACTOR-NOTE (post-AUTO-003b): this file mixes 8 concerns — CRUD, AI generation, single-test runs, review actions, approvals (revoke + approval-stats), interactive recorder, visual baselines, and exports. Splitting along those lines (routes/approvals.js, routes/recorder.js, routes/baselines.js, routes/exports.js) is sensible but should land in a dedicated PR rather than bundled with feature work — the route- order constraints below ("bulk must be declared BEFORE :testId wildcards") are easy to regress in a mechanical move. Tracked as a follow-up MNT item.

Endpoints

Method Path Description
GET /api/v1/projects/:id/tests List tests for a project
GET /api/v1/tests List all tests
GET /api/v1/tests/:testId Get a single test
PATCH /api/v1/tests/:testId Edit test (steps, name, code, etc.)
POST /api/v1/projects/:id/tests Create a manual test (Draft)
DELETE /api/v1/projects/:id/tests/:testId Delete a test
POST /api/v1/projects/:id/tests/generate AI-generate test(s) from description
POST /api/v1/tests/:testId/run Run a single test
PATCH /api/v1/projects/:id/tests/:testId/approve Approve (Draft → Approved)
PATCH /api/v1/projects/:id/tests/:testId/reject Reject
PATCH /api/v1/projects/:id/tests/:testId/restore Restore to Draft
POST /api/v1/projects/:id/tests/bulk Bulk approve/reject/restore/delete
GET /api/v1/projects/:id/tests/counts Per-status test counts
GET /api/v1/projects/:id/tests/export/zephyr Zephyr Scale CSV export
GET /api/v1/projects/:id/tests/export/testrail TestRail CSV export
GET /api/v1/projects/:id/tests/traceability Traceability matrix
GET /api/v1/projects/:id/export/playwright Export approved tests as Playwright ZIP
Source:

Members

(inner, constant) RECORDER_DEVICE_VALUES

DIF-015c Gap 5 — allowlist of device names accepted at the route layer. Built once at module load from the same DEVICE_PRESETS the RunRegressionModal dropdown surfaces, so the recorder's accepted inputs stay byte-aligned with the rest of the regression suite.

Source:

Methods

(inner) clampIterationCap()

CAP-001: clamp the fixture iteration cap to the [1, 100] range. Default of 10 mirrors the per-project default documented in NEXT.md so a project without an explicit iterationCap row still gets bounded dispatch.

Source:

(inner) parseCsvRows(text) → {Array.<Object>}

CAP-001: parse RFC 4180-flavoured CSV into row objects keyed by the first line's headers. Supports double-quoted fields with embedded commas, CRLF newlines inside quoted fields, and "" as an escaped double-quote.

Intentionally not a full RFC 4180 implementation — we drop trailing blank lines and unquoted whitespace around delimiters because fixture CSVs are typically hand-edited or exported from spreadsheets where those quirks are the norm. Pulling in a CSV dependency would be overkill for this scope (see AGENT.md "Do not add large dependencies").

Parameters:
Name Type Description
text string
Source:
Returns:

Rows; empty array when text has fewer than 2 non-empty logical lines (header + at least one data row).

Type
Array.<Object>

(inner) parseTags()

Normalise a tags query-string value into a clean string[] suitable for filters.tags on the testRepo paged/count helpers. Accepts either a repeated query param (Express parses ?tags=a&tags=b as an array) or a single comma-joined string (?tags=a,b). Empty strings and whitespace- only entries are dropped. Returns undefined when there's nothing to filter on so callers can omit the key from the filters object entirely (the repo treats a missing key and an empty array differently — empty array would match nothing).

Source:

(inner) resolveEnvOrThrow(environmentId, project) → {Object|null}

DIF-012: Resolve and validate an optional environmentId against the given project, returning the env row when valid. Returns null when no envId was supplied; throws an Error with httpStatus and message fields when the envId is invalid (unknown or belongs to a different project) so callers can return res.status(httpStatus).json({error}).

Mirrors the validation contract in routes/runs.js and routes/trigger.js so all four entry points (crawl, run, generate, record) share one source of truth.

Parameters:
Name Type Description
environmentId string | null | undefined
project Object

— already-resolved, workspace-scoped project row.

Source:
Returns:
Type
Object | null