diff --git a/Tests/e2e-test-framework.md b/Tests/e2e-test-framework.md new file mode 100644 index 0000000..a9a4c73 --- /dev/null +++ b/Tests/e2e-test-framework.md @@ -0,0 +1,309 @@ +# PPMS — E2E Test Framework Reference + +This document describes the Playwright-based end-to-end test framework for the +PPMS portal: its stack, directory layout, configuration, shared utilities, and +the conventions every spec must follow. + +--- + +## Stack + +| Layer | Tool | Version | +|---|---|---| +| Test runner | `@playwright/test` | 1.60 | +| Browser | Chromium (headless) | bundled with Playwright | +| Language | TypeScript | inherits from app `tsconfig.json` | +| Package manager | pnpm | same as portal app | +| App server | Next.js 15 dev server (`pnpm dev`) | auto-started by Playwright config | + +--- + +## Directory Layout + +``` +App/pelagia-portal/ +├── playwright.config.ts # Root config — workers, retries, baseURL, webServer +└── tests/ + ├── e2e/ + │ ├── helpers/ + │ │ ├── login.ts # Shared login(), createDraftPo(), submitPo(), USERS + │ │ └── auth.js # Legacy plain-JS login helper (pre-existing) + │ ├── dashboard/ + │ │ └── po-status-badges.js + │ ├── inventory/ + │ │ ├── items-tags.spec.ts + │ │ └── cart-icon.spec.ts + │ ├── mobile/ + │ │ ├── desktop-required.spec.ts + │ │ ├── manager-approvals.spec.ts + │ │ ├── accounts-payments.spec.ts + │ │ └── bottom-nav.spec.ts + │ ├── admin-bordered-buttons.spec.ts + │ ├── approvals-edit-highlight.spec.ts + │ ├── export-gate.spec.ts + │ ├── notification-bell.spec.ts + │ ├── partial-receipt.spec.ts + │ ├── payment-history.spec.ts + │ ├── po-submit-button.spec.ts + │ ├── profile.spec.ts + │ ├── rebrand.spec.ts + │ └── vendor-auto-verify.spec.ts + ├── integration/ # Vitest integration tests (separate suite) + └── unit/ # Vitest unit tests (separate suite) +``` + +--- + +## Configuration (`playwright.config.ts`) + +```ts +export default defineConfig({ + testDir: "./tests/e2e", + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 1, // 1 local retry reduces flakiness from auth concurrency + workers: process.env.CI ? 1 : 2, // 2 local workers — more causes NextAuth bcrypt flooding + reporter: "html", + use: { + baseURL: "http://localhost:3000", + trace: "on-first-retry", + }, + webServer: { + command: "pnpm dev", + url: "http://localhost:3000", + reuseExistingServer: !process.env.CI, // reuse running dev server locally + }, +}); +``` + +### Why workers: 2 + +The app uses NextAuth v5 with bcrypt password hashing for every login. Under high +parallelism (the default of ~50% CPU cores) all workers attempt to authenticate +simultaneously, overwhelming the dev server and causing login redirects to time out. +Two workers provide enough parallelism to keep the suite fast without triggering +the concurrency limit. + +--- + +## Shared Helpers (`tests/e2e/helpers/login.ts`) + +### `USERS` — seed credentials + +```ts +export const USERS = { + TECH: { email: "tech@pelagia.local", password: "tech1234" }, + MANNING: { email: "manning@pelagia.local", password: "manning1234" }, + ACCOUNTS: { email: "accounts@pelagia.local", password: "accounts1234" }, + MANAGER: { email: "manager@pelagia.local", password: "manager1234" }, + SUPERUSER: { email: "superuser@pelagia.local", password: "super1234" }, + AUDITOR: { email: "auditor@pelagia.local", password: "audit1234" }, + ADMIN: { email: "admin@pelagia.local", password: "admin1234" }, +}; +``` + +### `login(page, creds)` + +Navigates to `/login`, fills credentials, and waits up to **20 s** for the +redirect away from `/login`. The 20 s timeout is intentional — the bcrypt hash +check plus DB round-trip can exceed the Playwright default 5 s under any load. + +```ts +await login(page, USERS.MANAGER); +``` + +### `createDraftPo(page, title)` + +Creates a minimal PO as DRAFT and returns the absolute PO URL. Uses +**`name`-attribute selectors** because the PO form labels have no `htmlFor`/`id` +binding — `getByLabel()` will not resolve. + +```ts +const poUrl = await createDraftPo(page, "Test PO - boiler parts"); +``` + +### `submitPo(page, title)` + +Same as `createDraftPo` but clicks the **Submit for Approval** button instead of +Save as Draft. Returns the PO URL after redirect. + +--- + +## Selector Conventions + +### Critical: PO form has no accessible label bindings + +The new-PO form (`/po/new`) and the edit form use `