Four gates, run in this order by pnpm verify, plus an end-to-end suite.
pnpm typecheck # tsc --noEmit
pnpm lint # eslint .
pnpm check:design # design-token discipline
pnpm check:actions # server-action boundary rules
pnpm test # vitest
pnpm build # next build
pnpm test:e2e # playwright, against a production buildTwo checks exist because the failure they catch is invisible to tsc and
expensive to catch any other way.
pnpm check:design (scripts/check-design-tokens.mjs) fails on a raw
Tailwind palette colour, a gray step outside the 100–1000 ramp, font-bold,
transition-all, hover:opacity-*, a hard-coded hex, :focus styling where
:focus-visible is meant, and neutral text on a tinted -subtle surface. A
design system that is only written down erodes; this one is enforced.
pnpm check:actions (scripts/check-server-actions.mjs) fails on an inline
callback inside an exported action in a "use server" module — an inline
.refine() or errorMap on a Zod schema. SWC rejects it with "Server Actions
must be async functions", tsc says nothing, and the only other way to find out
is a five-minute build. It caught four occurrences during the rebuild.
tests/**/*.test.ts. jsdom environment, @/ path alias, tests/setup.ts polyfills the
browser APIs Radix expects (matchMedia, ResizeObserver, pointer capture).
The suite deliberately targets logic that can be wrong in a way types cannot catch:
tests/lib/utils.test.ts— query-string building, list/int param parsing, truncation on word boundaries, HTML stripping.tests/lib/format.test.ts— currency and minor-unit conversion, compact notation, UTC-pinned dates (so server and client agree), relative time with an injectablenow, open-ended range formatting.tests/lib/slug.test.ts— accent folding, collision suffixing, and that a reserved route segment can never be claimed as a slug.tests/lib/plans.test.ts— the pricing invariants: exactly one free tier, yearly cheaper than 12× monthly, limits relaxing as tiers rise, per-day listing cost falling with duration, bigger credit packs being better value, the subscription always beating every pack, andfee + net === amountfor every amount.tests/lib/enums.test.ts— parsesprisma/schema.prismaand asserts every enum value has a label and every label maps to a value that still exists. Adding a database enum value without a label fails the build rather than renderingundefinedin production.tests/server/permissions.test.ts— the capability matrix: an interviewer cannot advance a candidate, a recruiter cannot touch billing, a billing member cannot read candidates, only an owner can hand out ownership.
Two of these earned their keep during the build. The pricing test caught a real inversion where the 150-credit pack undercut the Talent Pro subscription per credit. The formatting test caught a file-size branch that rendered "2 KB" where "2.0 KB" was intended.
e2e/*.spec.ts, run against a production build (next start), in Chromium and on a
Pixel 7 viewport.
The webServer config forces DEV_AUTH_BYPASS=0 regardless of what is in .env.local,
because the point of the suite is to prove the real guards work.
| Spec | Covers |
|---|---|
auth-protection.spec.ts |
The security contract — see below |
marketplace.spec.ts |
Job/project/talent/company listing, filtering, detail pages, 404s |
workspaces.spec.ts |
Every talent, organization, admin and account route renders for the right persona; role boundaries inside an organization |
accessibility.spec.ts |
One <h1> per page, a <main> landmark, alt text, accessible names on every control, skip link, keyboard reachability, no horizontal overflow at 375/768/1280, dark mode |
e2e/auth-protection.spec.ts is the file that proves the bypass is off and the guards do
the work. It asserts:
- No page advertises the development bypass.
- Every route in
PROTECTED_ROUTES— talent, organization, admin, messages, settings — either redirects to/loginor 404s when signed out, and never renders its own path. - The
callbackUrlsurvives the redirect, so signing in returns you where you were going. - Every route in
PUBLIC_ROUTESstill renders for anybody. - A signed-in talent reaches
/tbut not/admin, and not an organization they do not belong to. - An organization owner reaches their own workspace but not a different organization.
- An admin reaches
/admin. - Clearing the session closes the doors again.
- A wrong password and an unknown email fail identically, so the form cannot be used to enumerate which accounts exist.
Cross-organization and cross-role denials return 404, not 403, so probing slugs cannot enumerate which organizations exist. The tests accept either a 404 or a redirect and assert only that the protected content is not reached.
- No visual regression suite. The design system is enforced by token discipline and review rather than by screenshot diffing.
- No load testing.
- Stripe webhooks are exercised through their handlers, not against Stripe's test fixtures; the signature check is not stubbed out, so an end-to-end webhook test would need the Stripe CLI.