This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Testing
The index for the test suite. Deep references (migrated from the retired
Docs/):
- Test Plan — unit/integration/E2E coverage + permission matrix
- E2E Test Framework — Playwright stack, helpers, selector conventions
- E2E Test Plan — feature-coverage matrix and test cases
- Playwright Test Design — verification-script design principles
Three layers, all run on PRs. Commands run from App/.
| Layer | Tool | Env | Command |
|---|---|---|---|
| Unit | Vitest | jsdom | pnpm test |
| Integration | Vitest | Node + real DB | pnpm test:integration |
| E2E | Playwright | Chromium + dev server | pnpm test:e2e |
pnpm test # unit (fast, no DB)
pnpm test:watch # unit watch mode
pnpm test:integration # integration (needs seeded DB)
pnpm test:all # unit + integration (pre-merge)
pnpm test:e2e # E2E (needs running dev server)
pnpm test:e2e:ui # Playwright interactive UI
# single files
pnpm test -- tests/unit/po-line-items-editor.test.tsx
pnpm test:integration -- tests/integration/create-po.test.ts
Tests live in tests/unit/, tests/integration/, tests/e2e/.
Unit tests (Vitest, jsdom)
Cover pure logic and components:
| Subject | Cases |
|---|---|
lib/permissions.ts |
All 7 roles × key permissions; requirePermission throws |
lib/po-state-machine.ts |
canPerformAction, getTransition, requiresNote, getAvailableActions |
lib/po-import-parser.ts |
cellStr/cellNum/parseSheet/parseWorkbook (real + synthetic) |
lib/validations/po.ts |
lineItemSchema, createPoSchema, TC defaults |
components/po/po-line-items-editor.tsx |
edit/read-only modes, totals, add/remove |
components/po/po-status-badge.tsx |
all status labels |
lib/utils.ts |
formatCurrency, formatDate, generatePoNumber, status maps |
Integration tests (Vitest + real DB)
Exercise Server Actions against a real Postgres test DB. They run serially in a
single fork (poolOptions.forks.singleFork = true) to avoid DB conflicts; each
suite isolates with a PREFIX constant and cleans up via
afterEach(() => deletePosByTitle(PREFIX)).
- Auth is mocked:
vi.mock("@/auth")+makeSession(userId, role)fromtests/integration/helpers.ts(alsomakePoForm(),fd()). - Side-effects mocked:
@/lib/notifier(no email) andnext/cache(revalidatePath).
| File | Feature |
|---|---|
create-po.test.ts |
Draft, submit, line items, totals, notifications |
approval-actions.test.ts |
Approve / reject / request edits / vendor ID / resubmit |
payment-actions.test.ts |
Payment queue, mark paid |
discard-po.test.ts |
Owner/MANAGER/SUPERUSER discard; status guard; cascade |
vendor-approval.test.ts |
Vendor gate before approval; provide-vendor rules |
manager-po-creation.test.ts |
MANAGER create/submit/discard; ACCOUNTS denied |
products-search.test.ts |
Search API: auth, min-length, fields, max 10, Decimal serialised |
import-api.test.ts |
Excel import: auth (403/401), bad file (400), correct parse |
Prerequisites: a running Postgres with DATABASE_URL, schema applied
(prisma migrate deploy / db push), data seeded (tsx prisma/seed.ts).
E2E tests (Playwright)
Browser-level checks against a live dev server + Postgres. The full E2E framework reference is the E2E Test Framework page; the feature-coverage matrix is the E2E Test Plan.
Config (playwright.config.ts)
testDir: "./tests/e2e",
fullyParallel: true,
retries: process.env.CI ? 2 : 1,
workers: process.env.CI ? 1 : 2, // >2 floods NextAuth bcrypt on login
reporter: "html",
use: { baseURL: "http://localhost:3000", trace: "on-first-retry" },
webServer: { command: "pnpm dev", url: "http://localhost:3000",
reuseExistingServer: !process.env.CI },
workers: 2 locally is deliberate — every login does a bcrypt hash + DB
round-trip, and higher parallelism overwhelms the dev server, timing out the
login redirect.
Shared helpers (tests/e2e/helpers/login.ts)
USERS— the seed credentials (see Getting Started).login(page, creds)— fills/login, waits up to 20 s for the redirect (bcrypt + DB can exceed Playwright's 5 s default).createDraftPo(page, title)/submitPo(page, title)— minimal PO setup.
Selector conventions (gotchas)
| Situation | Symptom | Fix |
|---|---|---|
getByLabel(/title/i) on PO form |
times out — no htmlFor/id |
use locator('input[name="title"]') |
getByText("Technical") on profile |
strict-mode violation (also in header) | scope to page.locator("dd span").filter(...) |
| Many workers logging in | login times out | keep workers ≤ 2; consider storageState |
router.push soft nav |
URL still old after networkidle |
use page.waitForURL(pattern) concurrently |
| Re-clicking an expanded row | row collapses, selectors vanish | expand before soft navigation; React state persists |
Specs log each assertion with a ✓ prefix and skip gracefully when seed
preconditions are absent (rather than failing hard).
Coverage highlights
Rebrand, dashboard status badges, submit button, notification bell, export gate (incl. HTTP 403 on DRAFT, 200 on approved), payment history, partial receipt, vendor auto-verify, admin bordered buttons, profile + signature, inventory Cheapest/★Closest tags, cart icon, item/vendor detail pages, mobile (Desktop-Required overlay, manager cards, accounts payment, bottom nav), edit-highlight diff. See the E2E Test Plan for the full matrix and known flaky/needs-fix items.
Out of scope / known gaps
File upload to R2 (live creds; manual in staging), email body content (notifier
mocked), PDF/XLSX content (endpoint status checked, content asserted
manually), GstService lookup (tested independently), load testing,
cross-browser, automated a11y. CI runs unit + integration + E2E on every PR
(workers: 1, retries: 2, forbidOnly: true in CI mode).
Pelagia Portal (PPMS)
Overview
Build & Run
System
Product
- Feature Catalogue
- Pages and Navigation
- Workflows
- Purchase Orders
- Vendors and GST Lookup
- Inventory and Catalogue
- Inventory on Approval
- Notifications
- File Storage
- Design System
Planned
Quality
Ops
Engineering
Pelagia Portal (PPMS) — internal purchase-order management. Self-hosted on pms1, live at pms.pelagiamarine.com. This wiki tracks the shipped product; authoritative sources are the repo code, App/CLAUDE.md, Docs/, and CHANGELOG.md.