| name | open-accounting-demo-e2e |
| description | Use when running or debugging open-accounting local demo Playwright E2E, including demo reset, auth setup, API/frontend ports, CORS, stale localhost servers, and branch-code verification. |
Open Accounting Demo E2E
Use this with open-accounting-development for local demo Playwright checks. The resettable local demo is the source of truth unless a hosted demo URL is explicitly requested.
Preflight
- Inspect listeners before running Playwright:
lsof -nP -iTCP:5173 -sTCP:LISTEN || true
lsof -nP -iTCP:8080 -sTCP:LISTEN || true
- Do not trust an existing
localhost:5173 server until verified. It can be another repo. If uncertain, use a clean port such as 5174.
- Do not trust the Docker Compose API on
localhost:8080 for branch-code verification. It may be a stale image. Use curl /health only as liveness, not proof of current source.
- If migrations changed recently or the database state is unknown, run the branch migration command against the local Postgres before demo reset.
- Track PIDs for any branch API or frontend server started for verification and stop them before final status.
Branch API Pattern
Run the current working tree API on an alternate port against the local Postgres container:
DATABASE_URL='postgres://openaccounting:openaccounting@localhost:5432/openaccounting?sslmode=disable' \
PORT=18080 \
JWT_SECRET='development-only-insecure-jwt-secret' \
ALLOWED_ORIGINS='http://localhost:5173,http://localhost:5174' \
DEMO_MODE=true \
DEMO_RESET_SECRET='test-demo-secret' \
go run ./cmd/api
Then reset demo data through that branch API:
curl -fsS -X POST http://localhost:18080/api/demo/reset \
-H 'X-Demo-Secret: test-demo-secret'
If migration state is uncertain:
DATABASE_URL='postgres://openaccounting:openaccounting@localhost:5432/openaccounting?sslmode=disable' \
go run ./cmd/migrate -direction up
If using a frontend port other than 5173, include it in ALLOWED_ORIGINS before starting the API. Confirm CORS if auth reports "Unable to connect to server":
curl -sS -D - -o /tmp/options.out -X OPTIONS http://localhost:18080/api/v1/auth/login \
-H 'Origin: http://localhost:5174' \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: content-type'
Focused Playwright Command
Use a clean frontend port and point the browser at the branch API:
cd frontend
BASE_URL=http://localhost:5174 \
PUBLIC_API_URL=http://localhost:18080 \
DEMO_RESET_SECRET=test-demo-secret \
bunx playwright test --config=playwright.demo.config.ts --project=demo-chromium e2e/demo/<spec>.spec.ts --workers=1
The config starts the frontend server only. Start the API separately first.
Timing Review
Before optimizing broad demo specs or changing shard counts, download the CI
playwright-results-shard-* artifacts and aggregate the JSON reporter files:
gh run download <run-id> --pattern 'playwright-results-shard-*' --dir /tmp/playwright-results
scripts/parse-playwright-spec-times.mjs /tmp/playwright-results/*/demo-test-results.json
Start with the highest-duration spec files, and keep setup/auth timing separate
from route-specific test timing when deciding what to refactor.
Assertion Discipline
- Prefer API waits and visible UI states over fixed sleeps.
- For demo route readiness, add or reuse shared helpers such as
waitForRouteReady(page, '<route-owned selector>') in frontend/e2e/demo/utils.ts; do not add page.waitForTimeout(...) after navigateTo.
- Keep shared navigation waits stable until every dependent spec owns its route readiness. Removing
networkidle from navigateTo globally can make unrelated E2E specs race route data loading.
- When using comma-separated readiness selectors, wait for any matching visible element, not
.first(). Scope route selectors to owned content such as main h1 or route panels so hidden layout navigation links cannot satisfy or block readiness.
- Assert mutation responses and rendered UI, not only that a page did not crash.
- Use unique test data for creates so seeded demo records cannot satisfy assertions.
- Scope generic controls such as selects and buttons to the route panel or filter bar that owns them.
- When filtering records, assert both the expected visible row and at least one expected absent row.
- Keep shared auth and tenant helpers state-based. Tenant setup should skip selection when the current option is already correct, and should not wait for
networkidle when no matching tenant option exists. Remove deprecated demo helper fallbacks instead of preserving compatibility paths.
Failure Triage
- Login page shows another product or brand: wrong frontend server was reused; switch
BASE_URL to a clean port.
- Auth setup says credentials fail: test direct API login before changing seed data.
- Auth setup says unable to connect: check
PUBLIC_API_URL, API liveness, and CORS for the frontend origin.
- UI crashes after successful API response: inspect the screenshot and console context; treat this as a real product bug before weakening the test.
- Keep the branch API process running only for the E2E run; stop it before final status.