GenerateSaaS

Testing

Run fast Vitest unit tests per package and lean Playwright end-to-end smoke checks locally, with commands, conventions, and tips for adding new specs.

The project ships two test layers: fast Vitest unit tests per package, and Playwright end-to-end tests that drive the running app in a real browser. The generated .github/workflows/ci.yml runs lint, typecheck, and unit tests on every push and PR to main; e2e ships local-only (it builds the app, which CI otherwise leaves to the deploy platform).

Unit tests (Vitest)

Run these from the repo root.

CommandWhat it does
pnpm testturbo run test - run every package's Vitest suite
pnpm --filter <pkg> testrun one package's suite
pnpm --filter <pkg> test:watchwatch mode while developing

Unit tests live alongside the code they cover (a package's tests/ directory) and assert behaviour and outcomes, not implementation details. Conventions:

  • Write a test for every new function, bug fix, and any untested code you touch - each test should catch a real regression.
  • Build fixtures with typed factory helpers using Partial<T> from a package's exported types - never re-declare types by hand.
  • Put temp file paths under os.tmpdir(); never hard-code /tmp.

End-to-end tests (Playwright)

The e2e suite lives in apps/web-next/e2e/ and uses Playwright with a single Chromium project. The specs are deliberately lean - read-only render/smoke checks of the critical journeys an anonymous visitor hits:

  • marketing: landing, pricing, primary nav, theme toggle, cookie-consent banner
  • auth: the /auth form renders and toggles between password and magic-link sign-in
  • guards: an unauthenticated /dashboard visit redirects to /auth

Because they never sign in or write data, they need only a booted app with a reachable database - no email server, no seeding.

Running it

CommandWhat it does
pnpm --filter web-next e2eopen Playwright's interactive UI mode (local)
pnpm --filter web-next e2e:ciinstall the browser, build + start the app, run headless
pnpm e2e:citurbo run e2e:ci - the full headless run

Start your local database first:

pnpm infra   # start Docker services (Postgres, …) - present if you chose Docker infra at init

Playwright's webServer builds and starts a production server on port 3000 before the tests, and reuses an already-running dev server on that port - so a pnpm --filter web-next dev you already have open is reused with no rebuild.

Reports land in apps/web-next/playwright-report/ (open with pnpm --filter web-next exec playwright show-report); the report and test-results/ are git-ignored.

E2e ships local-only - the generated .github/workflows/ci.yml runs lint, typecheck, and unit tests but has no e2e job, so it doesn't rebuild the apps on every push (the deploy platform already builds them). To run e2e in CI, add a job to .github/workflows/ci.yml yourself that calls pnpm e2e:ci.

Adding a spec

Create apps/web-next/e2e/<journey>.spec.ts importing { expect, test } from "@playwright/test".
Prefer stable selectors - element ids, ARIA roles, and visible text - over CSS classes.
For an interaction that depends on client hydration, wrap the click-then-assert in expect(...).toPass() so a pre-hydration no-op click simply retries.

To cover authenticated journeys, seed a verified user server-side through the auth API and inject the session cookie into Playwright's storage state - that keeps email and captcha out of the browser flow and stays deterministic.

On this page