SsuperslateDocs

End-to-end testing

Run and extend the Playwright browser suite against an isolated local product stack.

End-to-end testing

Playwright verifies the browser-to-API boundary that component and server tests cannot: actual routes, cookies, redirects, form interaction, browser state, and cross-origin policy. The suite is run in CI on Chromium after database-backed tests.

The suite is not a substitute for real provider checkout, mailbox, cloud, backup, or production browser verification. Those remain release smoke tests under Go live.

Run the suite locally

Install Chromium once for the pinned Playwright version:

pnpm exec playwright install chromium

Start Postgres, create a disposable database, and run the root command with that exact database:

docker compose up -d postgres
createdb app_e2e
DATABASE_URL='postgresql://app:app@localhost:5432/app_e2e?sslmode=disable' \
  pnpm test:e2e

The Playwright configuration migrates that database, starts the API on 127.0.0.1:8000, starts the web app on 127.0.0.1:5173, and uses test-only local origins. It disables external email and Slack delivery. The suite creates test users and data, so never point it at staging, production, or a shared developer database.

Remove the disposable database when finished:

dropdb --if-exists --force app_e2e

What the current suite proves

e2e/auth.spec.ts covers public and protected authentication behavior. e2e/product-smoke.spec.ts covers the authenticated application shell and key browser boundaries. CI installs Chromium and runs the same pnpm test:e2e command with its disposable PostgreSQL service.

Add browser coverage when a change affects:

  • login, sign-out, verification, reset, magic link, OAuth, session expiry, cookies, or redirects;
  • a new protected page, form, user-visible mutation, loading/empty/error state, or route fallback;
  • billing checkout entry points, provider return handling, entitlement presentation, or portal entry;
  • upload selection/confirmation, PWA update/offline behavior, or an origin-sensitive integration.

Keep provider-side payment completion, external email delivery, DNS, and cloud ingress in their provider or deployment smoke instead of putting real credentials into Playwright.

Add a browser test

Create e2e/<feature>.spec.ts. Start from user-visible behavior, use accessible role/label queries, and make the result observable. Prefer a short independent flow over a sequence coupled to another test's data.

import {expect, test} from '@playwright/test'

test('a signed-in user can create a saved link', async ({page}) => {
  await page.goto('/saved-links')
  await page.getByRole('button', {name: 'Add saved link'}).click()
  await page.getByLabel('URL').fill('https://example.com')
  await page.getByLabel('Label').fill('Example')
  await page.getByRole('button', {name: 'Save link'}).click()

  await expect(page.getByRole('link', {name: 'Example'})).toBeVisible()
})

The example assumes its domain, route, labels, and browser flow have already been built. Do not add test-only product endpoints or bypass Better Auth to make a test convenient. For a durable feature, first follow the worked feature recipe and the domain golden path.

Failure evidence

Playwright retains traces, screenshots, and video on failure. The API process output is written to .e2e/server.log. Inspect the first browser assertion, browser console/network evidence, and server request ID before changing selectors, environment, auth, or application code.

When a browser test fails only in CI, record the exact command, database name, Playwright version, trace, and redacted server log. Do not paste cookies, passwords, auth links, provider payloads, or environment files into an issue or support request.

Keep the suite useful

  • Test the product outcome, not CSS class names, implementation state, or a timing guess.
  • Use deterministic fixtures and independently created users for ownership boundaries.
  • Assert unauthenticated redirects, invalid input, failures, and success for a changed critical flow.
  • Keep browser data isolated to the named disposable database.
  • Run Testing as well: browser E2E complements contracts, real Postgres integration, server tests, and the production build.

On this page