Documentation
IntroductionArchitectureMonorepo & ToolingRunning Locally
Screen Flow MapUser JourneysPersonas & RolesWeb ScreensClient Portal
Convex OverviewData ModelFunctions ReferenceAuth & SessionsRoles & PermissionsJobs & Providers
Web Design SystemAdmin Console DesignPatterns & Conventions
Web AppiOS AppMarketing Website
Testing & HarnessCI Pipeline

Testing & Harness

Testing & Harness

Three layers of verification, tracked on one scoreboard: probe 34/34 screens · flows 14/14 · backend tests 27/27 (the "closed 2026-07-13" milestone in docs/v2-rewrite/08-v2.1-gap-analysis.md).

Layer 3 — Business flows · 14/14
Playwright asserts observable outcomes: counts change, toasts fire, a second session updates live, zero-touch portal journey
Layer 2 — Screen probe · 34/34
Playwright renders every screen against seeded data; any console error or error text fails the run
Layer 1 — Backend unit tests · 27/27
Vitest + convex-test: authorization, policy overrides, tenant isolation, typed not-found, self-access
All three run in CI on every v2.1 PR — locally: pnpm test (backend) · node v2-probe.mjs · node v2-flows.mjs
Three layers of verification — each layer catches what the one below can't see

Layer 1 — Backend unit tests (Vitest)

packages/backend, run with pnpm -C packages/backend test.

  • Vitest 4, environment: "edge-runtime" (@edge-runtime/vm), with convex-test inlined — tests run the real Convex functions against an in-memory backend built from the actual schema: convexTest(schema, import.meta.glob("./**/*.ts")).
  • One suite: convex/authz.test.ts (~27 cases), authorization and tenant isolation:
    • Staff data access (QA-001) — owner lists staff incl. pay; manager/employee/customer are FORBIDDEN by default; a rolePolicies override to manager flips the outcome.
    • Settings access (QA-001) — manager reads all settings surfaces; employee/customer get typed FORBIDDEN.
    • Platform surface (QA-002) — amIPlatformOwner true only for active org owners; applicationsList rejects others.
    • Typed not-found (QA-003) — malformed ids return null across every detail read; cross-tenant ids return null (no existence leak); valid in-org ids resolve.
    • Org resolution — unknown slug → ORG_NOT_FOUND, bad token → UNAUTHENTICATED, non-member → FORBIDDEN.
    • Time-clock self-access — employees only ever receive their own entries.
  • Gap worth knowing: no unit coverage yet for pricing/tax math, the cron sweep, providers, or the visit state machine beyond what authz exercises — those are covered end-to-end by the flows harness instead.

Layer 2 — Screen probe (Playwright)

tools/walkthrough/v2-probe.mjs (repo root; the walkthrough dir is shared with v1's video tooling — plain Node + playwright, no test-runner config).

  1. Logs in through the real UI (demo_owner@pawdentity.test → picks Demo Boarding).
  2. Visits a hardcoded list of 29 routes (accounts, dashboard, board, all the /new forms, calendar, catalog, notes, messages, schedule, staff, staff/me/hours, all 7 reports, settings, portal, platform) plus 5 drill-downs (row-click into client/pet/visit/order detail and a message thread) — the "34 screens".
  3. Fails the run on any visible error text (Unhandled Runtime Error, Application error, 404, 500, Build Error) or any non-favicon console/page error.

Layer 3 — Business flows (Playwright)

tools/walkthrough/v2-flows.mjs — 14 end-to-end flows asserting observable results, printed as PASS | FAIL | SKIP per flow (nonzero exit on FAIL):

  1. Board check-in planned → in_house with live count increment + desk agreement e-sign gate
  2. Log a care event (feeding) verified via toast
  3. Check-out in_house → completed with live count decrement
  4. Record a payment on an unpaid order
  5. Create a note
  6. Realtime cross-session — a second login (jackson@) sees the board update with no refresh
  7. Medication needed → administered
  8. Pet issue report → resolve
  9. deposit_due → deposit_met
  10. Portal self-serve — client ava.west@example.com saves a card → books a stay → pays the deposit, zero staff touches 11–14. v2.1 tail: calendar week/day views, catalog inventory badge, brand-logo upload in settings, public /our-customers directory

Running everything locally

cd v2/packages/backend && pnpm test        # 27 authz cases

# stack up (see Running Locally), then:
cd tools/walkthrough
npm install && npx playwright install chromium
node v2-probe.mjs
node v2-flows.mjs

Philosophy

The harness favors observable behavior over implementation: flows assert what a user sees (counts change, toasts appear, a second session updates live) rather than internal state. The probe is a cheap tripwire — every screen must render clean before any deeper claim is made. CI runs all three layers on every v2.1 PR; see CI Pipeline.