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