This guide documents the validation steps that exist in the current Next.js-based website repo.
The current project scripts are:
pnpm devpnpm buildpnpm startpnpm testpnpm test:e2epnpm coveragepnpm update-snappnpm lintpnpm formatpnpm format:check
Unit tests are colocated with the source they cover using __tests__
directories, following the same layout used in adoptium.net.
The first baseline suite currently covers src/lib/posts.ts from src/lib/tests/posts.test.ts.
Component tests live inside each component folder, for example
src/components/Header/__tests__/Header.test.tsx. Snapshot files are added
selectively under __snapshots__ when they provide stable, useful coverage.
See 04-components.md for the full component layout
convention.
App-level tests live under src/app/__tests__, for example
src/app/__tests__/not-found.test.tsx and
src/app/__tests__/sitemap.test.ts.
Unit tests run in jsdom, which does not load the stylesheet. That means a component test can only assert that a component emits a class — never what that class does once the cascade has resolved. Both halves have to be true for the page to work, and the gap between them is where styling regressions live.
Two real examples, both of which passed lint, Prettier and the full unit suite:
- Nav links rendered
md:text-charcoalinside abg-charcoaloverlay. The class was present, the snapshot matched, and the menu was charcoal text on a charcoal background — blank to a reader. - A rule in
globals.cssmoved between cascade layers and every.containersilently capped at 1280px instead of 1820px.
Playwright tests under e2e/ close that gap by asserting on computed
styles in a real browser. They deliberately check invariants rather than
pinned values, so a redesign that changes colours and spacing does not have to
rewrite them:
- navigation text meets the WCAG AA contrast ratio against its effective background, at three viewport widths
- every menu item sits inside the viewport when the mobile overlay is open (the overlay locks body scrolling, so anything outside it is unreachable)
- no page scrolls sideways
One of those widths is 700px. It is not a device size — it is the gap that
opens whenever the JS breakpoint, the utility prefix and a media query in
globals.css stop agreeing. Nobody opens a browser at 700px by accident, which
is exactly why a regression there survives review.
Run them locally with:
pnpm build
pnpm test:e2eThe suite serves the production build, so the CSS under test is the CSS that
ships. The first run downloads Chromium via pnpm exec playwright install chromium.
Prefer assertions that stay true across a redesign. expect(ratio).toBeGreaterThan(4.5)
survives a new palette; expect(color).toBe("rgb(184, 26, 86)") does not, and a
test that has to be updated on every visual change gets deleted rather than
fixed.
The contrast helper abstains where it cannot know the answer — if any ancestor paints a gradient or image, there is no single background colour to compare against, so the element is skipped. That keeps hero sections from producing false failures.
Run these before opening a pull request:
pnpm format:check
pnpm lint
pnpm test
pnpm build
pnpm test:e2eIf you changed files under src/, it is often useful to run:
pnpm formatthen re-run:
pnpm format:checkRuns Prettier on source files under src/**/*.{js,jsx,ts,tsx,json}.
Notes:
- this does not automatically format Markdown docs
- this does not rewrite files outside the configured
src/glob
Checks whether the files covered by the Prettier glob are already formatted.
Use this before committing source changes.
Runs ESLint across the repo using the Next.js configuration in
eslint.config.js.
This catches common TypeScript, React, and App Router issues.
Runs the Vitest suite once in jsdom.
Tests live beside the code they cover inside __tests__ directories, for
example src/lib/__tests__/posts.test.ts.
Runs the Playwright suite in e2e/ against a real browser. Needs a production
build first (pnpm build), because the config serves the site with
pnpm start.
See Browser Tests for what these cover and why they exist alongside the Vitest suite.
Runs the same Vitest suite with V8 coverage enabled.
Use this when you want a local coverage report while expanding the baseline test surface.
Runs the Vitest suite in snapshot update mode.
Use this after intentionally changing stable UI output that already has snapshot coverage.
Runs:
pnpm sync:data && next buildThis is the closest thing to a production validation step and should pass before opening a PR.
Runs:
pnpm sync:data && next devUse this while developing to preview page changes at http://localhost:3000.
The current CI workflow runs:
pnpm install --frozen-lockfilepnpm format:checkpnpm lintpnpm testpnpm buildpnpm test:e2e
If one of these fails locally, it will likely fail in GitHub Actions too.
CI runs on pull requests into main and into websiteRedesign. The second
entry exists because the redesign branch is long-lived: PRs into it previously
merged without any of these checks running at all.
- open
/blog - open the individual post route
- confirm the post is not accidentally left as draft
- verify featured and inline image paths
- open the route locally
- verify front matter values render correctly
- confirm stripped shortcodes are not needed for the page
There is no translation or locale test matrix because the site does not currently implement i18n.
Keep validation focused on the single English site.