# Testing

This repo has three test tiers (see `App/CLAUDE.md` → Commands):

| Tier | Tool | Scope | Command |
|---|---|---|---|
| Unit | Vitest (jsdom) | pure functions / components | `pnpm test` |
| Integration | Vitest (node + real DB) | server actions against a Postgres DB | `pnpm test:integration` |
| E2E (local) | Playwright | full UI against a local `pnpm dev` | `pnpm test:e2e` |

This document covers a fourth, purpose-built tier:

## Staging closed-issue verification (`App/tests/staging/`)

A **feature-level** Playwright suite that drives the **running staging instance**
(`pm2 ppms-staging`, port 3200 on pms1 — see [`../automation/README.md`](../automation/README.md) → *Staging*)
to verify that every closed portal issue is actually fixed on the deployed build.
Unlike the local E2E suite it does not start a dev server; it logs in and clicks
through the real staging app, exactly as a user would.

### Why a dedicated tier

Staging runs against `pelagia_test`, a daily mirror of production. That mirror only
contains real `@pelagiamarine.com` users — most are SSO-only and none have a password
we know — so the credentials login can't be used for automated testing. To solve this
without touching production, the refresh seeds **deterministic test users** (one per
role) with known passwords.

### Test users (seeding)

[`App/prisma/seed-test-users.ts`](../App/prisma/seed-test-users.ts) idempotently
upserts one credential-capable login per role on the throwaway `@pelagia.local`
domain (no collision with real accounts):

| Email | Password | Role |
|---|---|---|
| `tech@pelagia.local` | `tech1234` | TECHNICAL |
| `manning@pelagia.local` | `manning1234` | MANNING |
| `accounts@pelagia.local` | `accounts1234` | ACCOUNTS |
| `manager@pelagia.local` | `manager1234` | MANAGER |
| `superuser@pelagia.local` | `super1234` | SUPERUSER |
| `auditor@pelagia.local` | `audit1234` | AUDITOR |
| `admin@pelagia.local` | `admin1234` | ADMIN |
| `site@pelagia.local` | `site1234` | SITE_STAFF |

[`automation/refresh-test-db.sh`](../automation/refresh-test-db.sh) runs this seed
automatically after every daily refresh of `pelagia_test`, so the logins always exist
on staging. To seed manually (e.g. before a one-off run):

```bash
DATABASE_URL="postgresql://…/pelagia_test" pnpm tsx prisma/seed-test-users.ts
```

### Running the suite

From a machine that can reach pms1, open SSH tunnels to the staging app **and** the
DB (the suite reads a few fixture ids straight from `pelagia_test` so it stays stable
across the daily refresh):

```bash
ssh -N -L 3200:localhost:3200 -L 15432:localhost:5432 shad0w@<pms1>
```

Then, from `App/`:

```bash
PLAYWRIGHT_BASE_URL=http://localhost:3200 \
DATABASE_URL="postgresql://pelagia_user:…@localhost:15432/pelagia_test" \
  pnpm exec playwright test --config playwright.staging.config.ts
```

- `PLAYWRIGHT_BASE_URL` — the staging app (default `http://localhost:3200`).
- `DATABASE_URL` — the tunnelled staging DB, used only for read-only fixture lookups
  (which approved/closed PO to open, expected counts). Every assertion runs against
  the live UI.

### What each script verifies (issue → script map)

One spec file per issue; the filename is the mapping. `SKIP` means the staging data
currently has no row to exercise the case (the spec self-skips with a message).

| Issue | Script | Verifies | Result |
|---|---|---|---|
| — | `00-smoke.spec.ts` | staging reachable + all seeded users can log in | PASS |
| #4 | `issue-04-po-date-field.spec.ts` | optional, back/forward-datable PO Date field on the PO form | PASS |
| #5 | `issue-05-approved-date-as-po-date.spec.ts` | approved PO detail shows the approval date as the PO Date | PASS |
| #6 | `issue-06-closed-list-filters.spec.ts` | manager sees all CLOSED POs; submitter's Closed view excludes APPROVED | PASS |
| #8 | `issue-08-export-includes-description.spec.ts` | exported PO includes the line-item optional description | PASS |
| #10 | `issue-10-attachments-grouped.spec.ts` | PO detail groups attachments by type (Submission/Payment/Delivery) | SKIP (no attachment data on staging) |
| #11 | `issue-11-terms-catalogue.spec.ts` | admin T&C catalogue page + dynamic PO terms editor | PASS |
| #12 | `issue-12-approved-this-month-card.spec.ts` | manager 'Approved This Month' card shows the correct live count (was stuck at 0) | PASS |
| #13 | `issue-13-payments-this-month-card.spec.ts` | accounts 'Payments completed this month' card | **KNOWN FAIL — not implemented on staging** |
| #14 | `issue-14-email-to-vendor.spec.ts` | 'Email to vendor' button on an approved PO with a vendor email | PASS |
| #19 | `issue-19-place-of-delivery-dropdown.spec.ts` | Place of Delivery is a dropdown + admin delivery-locations page | PASS |
| #24/#40 | `issue-24-40-logout-tooltip.spec.ts` | logout tooltip reads 'Log out' | **KNOWN FAIL — still 'Sign out' (these were pipeline test issues)** |
| #26/#41 | `issue-26-41-total-po-card.spec.ts` | 'Total Purchase Orders' card count correct (#41) + links to history (#26) | PASS |
| #31 | `issue-31-history-multi-status.spec.ts` | PO history filter accepts multiple OR-ed statuses | PASS |
| #32 | `issue-32-approved-month-clickthrough.spec.ts` | 'Approved This Month' card links to history filtered by approval date | PASS |
| #44 | `issue-44-line-item-units.spec.ts` | line-item unit dropdown includes months and year(s) | PASS |
| #50 | `issue-50-rupee-compact-format.spec.ts` | approved-spend card uses ₹ with compact L/Cr formatting | PASS |
| #53 | `issue-53-cancel-po-modal.spec.ts` | manager Cancel-PO modal with type-'cancel'-to-confirm guard | PASS |
| #57 | `issue-57-vendor-search-catalogue.spec.ts` | /catalogue/vendors searchable by vendor id, id shown next to name | PASS |
| #96 | `issue-96-sidebar-collapsible.spec.ts` | sidebar sections collapsible, collapsed by default, single-open | PASS |
| #104 | `issue-104-history-pagination.spec.ts` | /history items-per-page pagination | PASS |
| #109 | `issue-109-new-po-vendor-search.spec.ts` | new-PO vendor field is a searchable combobox (name + code) | PASS |
| #75/#76/#79/#81/#83/#86 | `crewing-epics.spec.ts` | each crewing epic's primary surface renders for an authorised role | PASS |

### Issues not covered by a staging spec (and why)

| Issue | Reason |
|---|---|
| #1 | "Add CHANGELOG.md" — pipeline bootstrap; verified by the file existing at the repo root, not a runtime feature. |
| #3, #42 | Explicit pipeline / token test issues ("no action needed; close without a fix"). |
| #7 | Inventory-on-approval — the inventory surface is gated by `NEXT_PUBLIC_INVENTORY_ENABLED`, which is **false** on staging, so it is not UI-verifiable there. Covered by the `tests/integration` inventory tests. |
| #17 | GST CAPTCHA extraction — depends on the live external GST portal (GstService); not deterministically testable. |
| #18 | "Prompt to save draft on navigate-away" — a client-side `beforeunload` guard; not reliably driveable in headless Playwright. |
| Crewing deep flows (#77 pipeline, #78 onboarding, #80 PPE, #82 appraisal, #85 sign-off) | State-machine flows covered by the existing integration suites (`tests/integration/applications.test.ts`, `onboarding.test.ts`, `appraisal.test.ts`, `signoff.test.ts`, etc.). The staging suite smoke-checks the epic surfaces render. |

### Findings (verification result)

Running the suite against staging surfaced **two closed issues that are not actually
fixed** on the current build:

- **#13** — the Accounts dashboard has no "Payments completed this month" card (only
  "Ready for Payment" and "Payment Queue Value").
- **#24 / #40** — the logout control tooltip still reads "Sign out", not "Log out".
  (Both were pipeline / button-simulation test issues, likely closed without a code
  change.)

These are encoded as `test.fail()` specs so the suite stays green while the gaps are
recorded; if either is fixed later, its spec flips to passing and flags the
annotation for removal.
