docs: reflect testing infra + Documenso/portal auth conventions in CLAUDE.md
- Quick reference: add commands for every Playwright project + dev tsx helpers - Conventions: document the Documenso webhook auth pattern (X-Documenso-Secret plaintext, not HMAC), the v1.13/2.x response shape normalization layer, the email template module location + responsive table layout, and the PortalAuthShell pattern that unifies the in-app and email branding - Environment: document EMAIL_REDIRECT_TO and IMAP_* dev/test-only vars - New Testing section enumerating the five Playwright projects (setup, smoke, exhaustive, destructive, realapi, visual) and what each covers Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
35
CLAUDE.md
35
CLAUDE.md
@@ -13,6 +13,19 @@ pnpm db:generate # Generate Drizzle migrations
|
|||||||
pnpm db:push # Push schema to DB
|
pnpm db:push # Push schema to DB
|
||||||
pnpm db:studio # Drizzle Studio GUI
|
pnpm db:studio # Drizzle Studio GUI
|
||||||
pnpm db:seed # Seed database (tsx src/lib/db/seed.ts)
|
pnpm db:seed # Seed database (tsx src/lib/db/seed.ts)
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
pnpm exec vitest run # Unit + integration (~3s)
|
||||||
|
pnpm exec playwright test --project=smoke # Click-through smoke (~10min)
|
||||||
|
pnpm exec playwright test --project=exhaustive # Full UI exhaustive
|
||||||
|
pnpm exec playwright test --project=destructive # Archive/delete flows
|
||||||
|
pnpm exec playwright test --project=realapi # Real Documenso/IMAP (opt-in)
|
||||||
|
pnpm exec playwright test --project=visual # Pixel-diff baselines
|
||||||
|
pnpm exec playwright test --project=visual --update-snapshots # Regenerate baselines
|
||||||
|
|
||||||
|
# Dev helpers
|
||||||
|
pnpm tsx scripts/dev-trigger-portal-invite.ts # Send a portal activation email
|
||||||
|
pnpm tsx scripts/dev-imap-probe.ts # Dump recent IMAP inbox messages
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tech stack
|
## Tech stack
|
||||||
@@ -75,6 +88,10 @@ src/
|
|||||||
- **Polymorphic ownership:** Yachts and invoice billing-entities use `<entity>_type` + `<entity>_id` column pairs (`'client' | 'company'`). Resolve owner identity through `src/lib/services/yachts.service.ts` / `eoi-context.ts` rather than reading the columns ad hoc — those services apply the type discriminator.
|
- **Polymorphic ownership:** Yachts and invoice billing-entities use `<entity>_type` + `<entity>_id` column pairs (`'client' | 'company'`). Resolve owner identity through `src/lib/services/yachts.service.ts` / `eoi-context.ts` rather than reading the columns ad hoc — those services apply the type discriminator.
|
||||||
- **EOI generation:** Two pathways share the same `EoiContext` (`src/lib/services/eoi-context.ts`). Documenso pathway calls the template-generate endpoint via `documenso-payload.ts`; in-app pathway fills the same source PDF (`assets/eoi-template.pdf`) via `src/lib/pdf/fill-eoi-form.ts` (pdf-lib AcroForm). Routed through `generateAndSign(...)` in `src/lib/services/document-templates.ts` with a `pathway` parameter.
|
- **EOI generation:** Two pathways share the same `EoiContext` (`src/lib/services/eoi-context.ts`). Documenso pathway calls the template-generate endpoint via `documenso-payload.ts`; in-app pathway fills the same source PDF (`assets/eoi-template.pdf`) via `src/lib/pdf/fill-eoi-form.ts` (pdf-lib AcroForm). Routed through `generateAndSign(...)` in `src/lib/services/document-templates.ts` with a `pathway` parameter.
|
||||||
- **Merge fields:** Token catalog lives in `src/lib/templates/merge-fields.ts`; the `createTemplateSchema` validator uses `VALID_MERGE_TOKENS` as an allow-list, so unknown tokens are rejected at template creation time.
|
- **Merge fields:** Token catalog lives in `src/lib/templates/merge-fields.ts`; the `createTemplateSchema` validator uses `VALID_MERGE_TOKENS` as an allow-list, so unknown tokens are rejected at template creation time.
|
||||||
|
- **Documenso webhooks:** Documenso (both v1.13 and 2.x) authenticates outbound webhooks by sending the configured secret in plaintext via the `X-Documenso-Secret` header — there is no HMAC. The receiver at `src/app/api/webhooks/documenso/route.ts` does a timing-safe equality check via `verifyDocumensoSecret`. Event names arrive as the uppercase Prisma enum on the wire (`DOCUMENT_SIGNED`, `DOCUMENT_COMPLETED`, etc.) even though the UI displays them as lowercase-dotted. The route also normalizes lowercase-dotted variants for forward-compat.
|
||||||
|
- **Documenso API responses:** 2.x renamed `id` → `documentId` and recipient `id` → `recipientId`; v1.13 still uses `id`. `src/lib/services/documenso-client.ts` runs every response through `normalizeDocument()` which reads either field name and surfaces the legacy `id` form to downstream consumers.
|
||||||
|
- **Email templates:** Branded HTML lives in `src/lib/email/templates/`. The portal-auth flow uses `portal-auth.ts` (activation + reset). All templates use the legacy table-based layout with the Port Nimara logo + blurred overhead background, max-width 600px and `width:100%` for responsive shrink. The `<img>` URLs reference `s3.portnimara.com` directly (will move to `/public` later).
|
||||||
|
- **Portal auth pages:** `/portal/login`, `/portal/activate`, `/portal/reset-password` all wrap their content in `<PortalAuthShell>` (`src/components/portal/portal-auth-shell.tsx`) which renders the same blurred background + logo + white card the email templates use, so the in-app and email surfaces look unified.
|
||||||
- **Routes:** Multi-tenant via `[portSlug]` dynamic segment. Typed routes enabled.
|
- **Routes:** Multi-tenant via `[portSlug]` dynamic segment. Typed routes enabled.
|
||||||
- **Pre-commit:** Husky + lint-staged runs ESLint fix + Prettier on staged `.ts`/`.tsx` files. The hook also blocks `.env*` files (including `.env.example`) from being committed; pass them via a separate workflow if needed.
|
- **Pre-commit:** Husky + lint-staged runs ESLint fix + Prettier on staged `.ts`/`.tsx` files. The hook also blocks `.env*` files (including `.env.example`) from being committed; pass them via a separate workflow if needed.
|
||||||
|
|
||||||
@@ -82,6 +99,24 @@ src/
|
|||||||
|
|
||||||
Copy `.env.example` to `.env` for local dev. See `src/lib/env.ts` for the full schema. Set `SKIP_ENV_VALIDATION=1` to bypass validation (used in Docker build).
|
Copy `.env.example` to `.env` for local dev. See `src/lib/env.ts` for the full schema. Set `SKIP_ENV_VALIDATION=1` to bypass validation (used in Docker build).
|
||||||
|
|
||||||
|
Optional dev/test-only env vars (not in `.env.example`):
|
||||||
|
|
||||||
|
- `EMAIL_REDIRECT_TO=<address>` — when set, every outbound email is rerouted to this address regardless of the requested recipient and the subject is prefixed with `[redirected from <original>]`. Dev safety net so seeded fake-client emails don't escape; **must be unset in production**.
|
||||||
|
- `IMAP_HOST` / `IMAP_PORT` / `IMAP_USER` / `IMAP_PASS` — read by `tests/e2e/realapi/portal-imap-activation.spec.ts` to fetch the activation email from a real mailbox during the IMAP round-trip test. The spec skips when any are missing.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Five Playwright projects, defined in `playwright.config.ts`:
|
||||||
|
|
||||||
|
- `setup` — global setup (seeds users, port, berths, system settings).
|
||||||
|
- `smoke` — fast click-through over every major flow. Run on every change (~10 min, 125 specs).
|
||||||
|
- `exhaustive` — deeper UI coverage that takes longer.
|
||||||
|
- `destructive` — archive/delete/cancel paths against throwaway entities.
|
||||||
|
- `realapi` — opt-in suite that hits real external services (Documenso send-side + IMAP round-trip). Requires `DOCUMENSO_API_*`, `SMTP_*`, `IMAP_*` env. Cloudflared tunnel needs to be running so Documenso can call the local webhook receiver.
|
||||||
|
- `visual` — pixel-diff baselines for stable list/landing pages. Snapshots committed under `tests/e2e/visual/snapshots.spec.ts-snapshots/`. Regenerate with `--update-snapshots` after intentional UI changes.
|
||||||
|
|
||||||
|
Vitest covers unit + integration with mocked external services (`tests/unit/`, `tests/integration/`).
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
- `Dockerfile` - Production multi-stage build (deps -> build -> runner)
|
- `Dockerfile` - Production multi-stage build (deps -> build -> runner)
|
||||||
|
|||||||
Reference in New Issue
Block a user