Skip to main content
The war.re project uses two distinct testing layers: Jest component tests that run in Node against a jsdom environment, and Playwright end-to-end tests that drive a real browser against the static production build. Type checking and linting round out the quality toolchain. Each tool has a specific scope — read on to understand where each layer lives and how to run it.
main/ has no Jest unit tests — only the Playwright e2e suite applies there. All Jest tests live exclusively in subdomains/ryan/.

Unit Tests — Jest + React Testing Library

Jest and React Testing Library are set up in subdomains/ryan/ to test individual React components in isolation. The suite covers the four components that contain meaningful rendering logic:
Test fileComponent under testWhat it covers
Activity.test.tsxActivityDuration formatting (years/months/singular/plural), “Present” end date, heading and metadata rendering, date range display
ActivityList.test.tsxActivityListList rendering with multiple items, empty-list behaviour
Experience.test.tsxExperienceSection heading rendering
ExperienceItem.test.tsxExperienceItemLink rendering when a URL is provided, plain-text fallback without a URL

Running unit tests

Run the full Jest suite from inside subdomains/ryan/:
cd subdomains/ryan

yarn test               # run all tests
yarn test:coverage      # run all tests and generate a coverage report
To run a single test file, pass its path directly to Jest via the --testPathPattern flag:
cd subdomains/ryan
yarn test -- --testPathPattern=Activity.test.tsx

Example: Activity duration tests

The Activity component contains the only real business logic in the codebase — a formatDuration function that is private to the module. The tests exercise it through the rendered <h6> duration badge:
it('renders years and months together', () => {
  render(<Activity name="Engineer" start="January 2020" end="March 2022" items={[]} />)
  expect(duration()).toBe('[2 Years 2 Months]')
})

it('measures the span up to the current date', () => {
  // beforeEach pins the clock to 2024-06-15 with jest.useFakeTimers()
  render(<Activity name="Engineer" start="March 2024" end="Present" items={[]} />)
  expect(duration()).toBe('[3 Months]')
})

End-to-End Tests — Playwright

Both main/ and subdomains/ryan/ include a Playwright e2e suite. The tests open the app in a real browser and verify that key pages render correctly, external links are safe, metadata is present, and the visual appearance is stable across changes. Playwright snapshots are stored at e2e/__snapshots__/ inside each app directory. When you run yarn e2e for the first time, Playwright creates baseline screenshots; subsequent runs compare against those baselines.

What the tests cover

main/ smoke tests (e2e/smoke.spec.ts):
  • The /n content route renders the homepage with correct headings and title
  • Visual screenshot comparison of the full page (content.png)
  • All external links carry target="_blank" and rel="noopener"
  • Social icon images expose descriptive alt text
  • Canonical URL and Open Graph metadata are correct
  • The root / redirects to /n via meta-refresh
  • Unknown routes return a 404 response
subdomains/ryan/ smoke tests (e2e/smoke.spec.ts):
  • The /n content route renders the resume with the correct title
  • All four major resume sections are visible (Technical Experience, Relevant Work Experience, Related Activities, Education)
  • Visual screenshot comparison of the full page (content.png)
  • All external links carry target="_blank" and rel="noopener"
  • Canonical URL and Open Graph metadata are correct
  • The root / redirects to /n via meta-refresh
  • Unknown routes return a 404 response

Running e2e tests

Run yarn build before yarn e2e. Playwright serves the static out/ directory — if you skip the build step, the tests will fail because there is no output to serve.
cd main
yarn build
yarn e2e              # smoke tests
yarn e2e:coverage     # smoke tests with V8 source-map coverage
yarn e2e:coverage is only available in main/. It maps Playwright V8 coverage data back to TypeScript source files using monocart-coverage-reports.

Updating snapshots

If you intentionally change the visual output of a page, update the Playwright baseline screenshots by running:
yarn e2e -- --update-snapshots

Type Checking

Both apps include a typecheck script that runs the TypeScript compiler in no-emit mode. A pretypecheck lifecycle hook automatically runs next typegen first to regenerate Next.js type definitions (such as route types) before the compiler sees your code:
# Run from inside either app directory
yarn typecheck
The effective command sequence is:
next typegen   # runs automatically via pretypecheck
tsc --noEmit   # runs as typecheck
TypeScript errors surface as non-zero exit codes, which makes yarn typecheck safe to use in CI.

Linting

Both apps run ESLint with the next/core-web-vitals configuration, which enforces React, accessibility, and Next.js-specific rules:
# Run from inside either app directory
yarn lint
To auto-fix lint issues where possible, you can pass the --fix flag directly:
yarn lint -- --fix