Reference app + automation sample for QA / QA Automation roles.
This repo is intentionally small but realistic: an Admin Portal (web + API + DB) with cookie auth + RBAC and a Playwright suite that runs API tests + UI tests in a single runner.
- Contract testing: explicit OpenAPI contract artifact + provider verification in Vitest for key endpoints
- Layered test strategy: unit tests, API integration tests, provider contract verification, DB validation, Playwright smoke, accessibility smoke, and performance sample
- Realistic QA surface area: cookie auth, RBAC, Dockerized app stack, seeded test users, and CI-ready automation
- End-to-end setup:
docker compose up→ app is usable + tests can run in CI - API automation: auth, RBAC negative tests, and Zod-based schema assertions
- DB validation: Playwright API flow verifies the persisted Postgres record after user creation
- Contract testing: explicit OpenAPI provider contract verification for key endpoints in Vitest integration tests
- UI automation: stable selectors (
data-testid), role-based UI visibility checks, and axe-based accessibility smoke - Test ergonomics: tagging (
@smoke), HTML report, trace/video on failure (CI)
- Test pyramid: API unit + integration (Vitest) → E2E smoke (Playwright API + UI)
- CI:
pnpm test:api→docker compose up -d --build→pnpm test:smoke(uploads Playwright report/artifacts) - Flaky policy: stable locators (
data-testid/ role / label), no hard sleeps, isolate test data (API-based setup when possible) - Accessibility smoke: Playwright +
@axe-core/playwrighton the main admin list pages - Performance: JMeter plan
tests/perf/jmeter/admin-portal-api.jmx(authenticated API read load) + HTML report intests/perf/results/html/ - DB validation (Postgres examples):
SELECT "role","status",COUNT(*) AS cnt FROM "User" GROUP BY 1,2 ORDER BY 1,2;SELECT "key",COUNT(*) AS cnt FROM "Project" GROUP BY 1 HAVING COUNT(*)>1;
- API: Fastify + Prisma + Postgres
- Web: React + Vite (served by Nginx in Docker),
/apireverse-proxied to API - Automation: Playwright (
@playwright/test) for API + UI
flowchart TB
subgraph Compose["Docker Compose"]
DB[(Postgres)]
API[Fastify API]
WEB["Web (React) + Nginx"]
WEB -->|/api reverse proxy| API
API --> DB
end
subgraph Automation["Automation"]
Vitest["Vitest<br/>(API unit + integration)"]
PW["Playwright<br/>(API + UI smoke)"]
JMeter["JMeter<br/>(perf)"]
end
Vitest --> API
PW --> API
PW --> WEB
JMeter --> API
PW --> Artifacts["Artifacts<br/>playwright-report/ + test-results/"]
docker compose up -d --build- Web:
http://localhost:8080(override withWEB_HOST_PORT) - API:
http://localhost:3000(override withAPI_HOST_PORT) - DB:
localhost:5432(override withDB_HOST_PORT)
If you already have ports 3000/8080 in use:
WEB_HOST_PORT=18080 API_HOST_PORT=13000 docker compose up -d --buildIf you also need to move Postgres for local DB validation:
WEB_HOST_PORT=18080 API_HOST_PORT=13000 DB_HOST_PORT=15432 docker compose up -d --buildSeeded users:
admin@example.com/admin123viewer@example.com/viewer123
Prereqs: Node 20+ and pnpm (recommended via corepack).
corepack enable
corepack pnpm install
docker compose up -d --build
corepack pnpm -F @qa-sample/e2e exec playwright install chromium
corepack pnpm -F @qa-sample/e2e test:smokeAPI unit + integration tests (Vitest):
corepack pnpm test:apiThis includes:
- unit tests for auth guards
- integration tests for auth / RBAC / pagination
- provider contract verification for key API endpoints
- DB validation against the real Postgres record for created users
Accessibility smoke (Playwright UI):
tests/e2e/tests/ui/admin.spec.ts- Covers
/usersand/projects - Runs axe against the rendered page after the smoke navigation succeeds
This repo includes a minimal provider contract testing layer for core API endpoints:
- Contract artifact:
contracts/admin-api.openapi.yaml - Provider verification:
apps/api/test/integration/contract.auth.test.ts - Provider verification:
apps/api/test/integration/contract.me.test.ts - Provider verification:
apps/api/test/integration/contract.users.test.ts
What is verified:
POST /auth/loginGET /meGET /users
The contract is intentionally small and stable. It locks down:
- response status codes
- required response fields
- field types / enums
- shared error envelope
It does not lock down volatile details like JWT contents or the exact cookie string.
If you changed Docker ports via WEB_HOST_PORT / API_HOST_PORT, set:
WEB_BASE_URL(defaulthttp://localhost:8080)API_BASE_URL(defaulthttp://localhost:3000)DATABASE_URL(defaultpostgresql://postgres:postgres@localhost:5432/qa_admin_portal?schema=public)
Example:
$env:WEB_BASE_URL="http://localhost:18080"
$env:API_BASE_URL="http://localhost:13000"
corepack pnpm -F @qa-sample/e2e test:smokeThis repo includes a basic JMeter test plan for authenticated API read load:
- Test plan:
tests/perf/jmeter/admin-portal-api.jmx - Output:
tests/perf/results/
Run:
docker compose up -d --build
corepack pnpm perf:jmeterTune load (examples):
corepack pnpm perf:jmeter -- -Jthreads=25 -JrampUp=10 -Jduration=120
corepack pnpm perf:jmeter -- -JthinkMs=100Report:
- HTML:
tests/perf/results/html/index.html - Raw:
tests/perf/results/results.jtl
POST /auth/login(setsaccess_tokencookie)POST /auth/logoutGET /meGET /users(auth required; list/search/paging)POST /users/PATCH /users/:id/DELETE /users/:id(admin only)GET /projectsPOST /projects/PATCH /projects/:id/DELETE /projects/:id(admin only)
apps/api # Fastify + Prisma API
apps/web # React UI
contracts # OpenAPI contract used for provider verification
tests/e2e # Playwright API + UI tests (one runner)
GitHub Actions workflow: .github/workflows/ci.yml
- Builds and starts services via Docker Compose
- Runs API unit + integration tests first, including provider contract verification
- Installs Playwright Chromium
- Runs
pnpm test:smokeafter API verification passes - Uploads
playwright-report/andtest-results/artifacts
- Ports in use: set
WEB_HOST_PORT/API_HOST_PORTbeforedocker compose up - Reset DB:
docker compose down -v(removes the docker volume) - HTML report:
corepack pnpm -F @qa-sample/e2e exec playwright show-report