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 file | Component under test | What it covers |
|---|
Activity.test.tsx | Activity | Duration formatting (years/months/singular/plural), “Present” end date, heading and metadata rendering, date range display |
ActivityList.test.tsx | ActivityList | List rendering with multiple items, empty-list behaviour |
Experience.test.tsx | Experience | Section heading rendering |
ExperienceItem.test.tsx | ExperienceItem | Link 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: