# Browser-driven batch validation Checks for the `val/*` validation stack — the branch-per-batch series used to sign off the range between the deployed commit and `main`. These complement, and do not replace, `go test` and the in-process integration harness (`internal/testharness`, `test/integration`, `make integration-test`). Pick by what needs proving: | Prove | Use | |---|---| | A function's logic, a query's shape, a guard's behaviour | Go unit test | | A push/pull/delete round trip across appview + hold + S3 | `make integration-test` | | A fragment renders into the right target, a job outlives its request, a 500 dressed as an empty state | these scripts | ## Running ```bash npm i -D @playwright/test && npx playwright install chromium node test/e2e/login.mjs # interactive, once per hold rebuild node test/e2e/batch00-admin-jobs.mjs # then the batch checks ``` Env overrides: `ATCR_HOLD_URL`, `ATCR_APPVIEW_URL`, `ATCR_E2E_PROFILE`, `ATCR_E2E_SEED`. ## Things that will cost you an afternoon **Use `127.0.0.1`, never `localhost`.** The appview canonicalises to `http://127.0.0.1:5000` and answers `localhost` with a 307. Any snippet written against `localhost:5000` measures the redirect, not the endpoint. **Admin sessions are in-memory and die on every hold rebuild.** They live in `ui.sessions` (`pkg/hold/admin/admin.go`), not the `admin_sessions` table, which is vestigial for this path. Air rebuilds the hold whenever tracked source changes — including a batch checkout — so budget one interactive login per switch. There is no test-mode bypass; `server.test_mode` only affects OAuth redirect URLs. **Never drive the admin panel with curl.** Sessions are pinned to User-Agent and client IP prefix, and a mismatch does not merely reject the request — it calls `deleteSession` and logs you out. Drive everything through `ctx.request`, which inherits the browser's cookie jar and UA. **Closing every Playwright page disposes `ctx.request`.** It fails with "Request context disposed". Keep one `about:blank` page open when the test needs the browser out of the way. **Crew delete is a `