Skip to main content
NextStarter
Menu

Testing & CI

The Playwright and Axe-core test suite, Lighthouse CI, and every job the GitHub Actions workflow runs.

End-to-end tests

The tests are Playwright specs in tests/, named *.spec.ts. They run in Chromium, Firefox, and WebKit.

npm run test                              # every spec, every browser
npx playwright test --project=chromium    # one browser
npx playwright test tests/privacy-page.spec.ts   # one spec
npx playwright show-report                # open the last HTML report

Playwright serves the production build with npm run start, so run npm run build first. Locally it reuses a server that is already running on port 3000, including npm run dev. It loads .env as well, so the tests see the same configuration as the app.

Specs for optional features follow the configuration. A spec that needs a variable you have not set skips itself instead of failing, so the suite passes with no keys.

Accessibility scans

Every page spec runs an Axe-core scan in light mode, switches to dark mode with the theme toggle, and scans again. Any violation fails the test. Copy the pattern from an existing spec when you add a page:

import AxeBuilder from "@axe-core/playwright";
import { expect, test } from "@playwright/test";

test("has no detectable accessibility issues", async ({ page }) => {
  await page.goto("/your-page");

  const light = await new AxeBuilder({ page }).analyze();
  expect(light.violations).toEqual([]);

  await page.locator("#themeToggle").first().click();
  // The click can leave the toggle focused with its tooltip mid-animation.
  // Drop focus and wait for it to close so the scan sees a stable page.
  await page.locator("#themeToggle").first().blur();
  await expect(page.getByRole("tooltip")).toHaveCount(0);

  const dark = await new AxeBuilder({ page }).analyze();
  expect(dark.violations).toEqual([]);
});

An automated scan cannot catch every WCAG failure. Keep using semantic HTML, label every control, and check that new UI works with a keyboard alone.

Lighthouse

Lighthouse CI audits the home page against a production build, three times per preset, and asserts against the median. Accessibility, best practices, and SEO must score a flat 100. Performance has a floor of 85, because it is a timing measurement on a shared runner and moves a few points between runs. The thresholds are in lighthouserc.js (mobile) and lighthouserc.desktop.js.

npm run build
npm run lighthouse           # mobile preset
npm run lighthouse:desktop   # desktop preset

Continuous integration

One GitHub Actions workflow, .github/workflows/ci.yml, runs on every push to main, on every pull request into it, and on demand. Every job reads its Node version from .node-version.

eslint
ESLint, with the results uploaded to the repository's Security tab. Also runs weekly.
supply-chain
npm audit signatures, then a check for high or critical advisories. Also runs weekly, to catch a package revoked after it reached the lockfile.
typecheck
npm run typecheck
code-review
Checks the code against the project's conventions and comments the report on pull requests.
react-doctor
A React Doctor scan. Advisory: it comments on pull requests but never fails the build.
test
Builds the site and runs the Playwright suite in all three browsers.
lighthouse
Lighthouse CI, mobile and desktop.

The build needs the site variables. See Environment Variables → In CI.

Before committing

The full local check matches CI:

npm run lint
npm run typecheck
npm run build
npm run test