A3 · Part 3 · How it holds up
Tests and hooks
1630 unit tests, 27 browser tests, and one test that checks what these pages say about the site.
07On this page
The shape of it
There are three layers of checking, each slower than the one before:
- Git hooks on the author's machine, over staged files only.
- Vitest in Node, over data and pure logic. Most of the weight is here.
- Playwright in a real browser, against a production build: one small smoke spec.
There is no CI service. A GitHub workflow used to run the compiler, the linters, the unit tests, the build, about 570 browser tests and Lighthouse on every push and pull request; it was removed, and nothing runs these checks for the author now. The browser suite was cut to the smoke spec described below. The test counts in this chapter are counted from the working tree of the branch it was last edited on.
Unit tests: 24 files, 405 tests
vitest.config.ts collects tests/**/*.test.ts and runs them in a Node
environment with no DOM. No React component is mounted in a unit test; the one exception renders the
topic shell to static markup, to check what it draws for a JavaScript-like and a DSA-like set of
props. That is still a decision, not a gap. The risk in this codebase sits in hand-written content and in a few engines with real
logic, and components are tested where they actually run, in the browser.
| Area | Files | Tests | What they hold in place |
|---|---|---|---|
| Mock interview | mock-engine, mock-session, mock-readiness, mock-guide, mock | 74 | How the interviewer adapts, how a session moves through its stages, the readiness score, and the coaching |
| Content and SEO | content, seo, claims, search-index, privacy, next-config, client-bundle, topic-categories, home-rounds | 129 | Chapter integrity, the sitemap and robots rules, the site's claims about itself, the search budget, the privacy page's list of services, the pinned redirects, the client bundle, the category of every topic, and the interview rounds the home page previews |
| Playground | polyglot, debug-trace, wasm-assets, contrast | 37 | Grading across languages, the step-through tracer, pinned runtime versions, and the contrast of the editor, text and role colours in every theme |
| Whiteboard | whiteboard-model | 17 | Geometry, history and sanitizeEls |
| Themes | theme-roles, theme-contract | 115 | Role tokens instead of fixed colours in every stylesheet, and a complete block for every theme |
| Tooling | comments, error-tracking | 10 | The comment checker itself, and Sentry's options and CSP origin |
The largest hand-written file is mock-engine.test.ts with 37 tests;
theme-roles.test.ts runs 101, most of them one check repeated for each CSS module. The content file,
content.test.ts, has 18. Each of those 18 is a mistake that was really made while
writing:
- a chapter marked ready with an empty body
<pre>and<code>tags that do not balance, which puts half a chapter inside a code block- a diagram with no
aria-label - an exercise pointing at a chapter that does not exist
The whole suite runs in under 10 seconds.
The test that keeps these pages honest
tests/claims.test.ts exists because this topic is full of numbers. It works out the
real figures from lib/content: written chapters, outlines, exercises, interview rounds
and topics. It builds the exact sentence each figure appears in, reads the source file as plain
text, and expects to find that sentence.
It checks 10 claims across six files, one of them the README, plus the topic-count row of the overview table. One of
them is in the scaling chapter. It expects the text Writing the N outlined chapters,
with N computed from the data. Write one more outline and the test fails until the sentence is
updated.
The test does not check whether a claim is wise. It only stops a claim from going stale in silence, which is how the old versions of these chapters went wrong: they were true when written and then drifted.
Browser tests: one smoke spec, 27 tests
playwright.config.ts starts npm run start on port 3100. That is the
production build, not the dev server, because dev mode double-invokes effects and serves
unminified code, so npm run build comes first. npm run test:e2e then runs the spec in about ten
seconds on three workers, with no retries unless CI is set, in which case it retries a failed test once and
fails the run if anyone left a test.only behind.
| Spec | Tests | Covers |
|---|---|---|
smoke.spec.ts | 27 | 8 pages load with their heading and no console error; 19 flows (the home page with its heading and the Menu pill, the Menu opening and closing on Escape, a topic cover and its level picker link, a chapter with its rail and no sideways scroll at 390 pixels wide, the path trail with every exercise list open and no stop covering another, the playground running a JavaScript snippet, an exercise page running code and grading a submission, unknown URLs returning a real 404, a DSA chapter switching its code to Python and back, the binary search player stepping forward and back, marking a DSA chapter read opening its check that a keyboard user answers and finishes, the DSA home page telling a passed chapter's check state in words, and the end-of-chapter popup opening once, Yes opening the check and a dismissal never coming back, a first-time reader arriving from the old /dsa address, finding their level and landing on a plan that Continue follows past the chapters they tested out with the shared storage keys untouched, and a placed reader opening and marking a Revise earlier chapter while a reader with marks sees Re-check my level, the DSA home leading a first-time reader to Check my level with a three-stop preview and a journey of three stages that fold open and shut by keyboard and open from a link, and a placed reader seeing their level, progress ring, up-next card and one Continue button with the plan inside its closed details, and a placement run that survives a reload and steps back while only ?retake=1 starts a retake) |
The eight pages are the problems list, the interview book, the mock interview, review, progress, privacy, this section and
the Git guide. A test that clicks first waits for the element to be hydrated, by looking for the property React puts on a
DOM node once it owns it, so it cannot click before the handler exists and nothing in the spec sleeps. It ignores requests
to /_vercel/. Those scripts exist only on Vercel's edge, so they return 404 locally every time.
The old suite also ran axe over every page and state in all nine themes at two widths, opened every written chapter at 390
pixels wide, probed the composition of every hero stage in every theme and font, and covered the tour, the interview book,
the mock room, review, progress, the workbench, the whiteboard and keyboard focus in detail, including checks that nothing
flashes the wrong state before hydration. None of that is checked in a browser any more. What still guards the colours is
tests/contrast.test.ts and the theme tests, which read the tokens of all nine themes in Node. Layout, overflow,
focus and hydration behaviour on those pages are now checked by looking.
Before code leaves the machine
.husky/pre-commit runs lint-staged over the staged files only:
- ESLint with fixes, then Prettier, for
.ts,.tsxand.mjs - Prettier for CSS, Markdown and JSON
- cspell for every staged file, whatever its type
.husky/pre-push runs npm run comments. This project keeps its source free
of comments, and scripts/comments.mjs enforces that:
- It parses each tracked
.ts,.tsx,.js,.mjs,.cjsand.cssfile with the TypeScript compiler's own scanner. So a//inside a string, a template, a regular expression or JSX text is correctly left alone. - It skips
public/,docs/,node_modules/and.next/. - It keeps the directives that tools read:
eslint-disable,@ts-expect-error,webpackIgnore,prettier-ignore,cspell:and a few more. - With
--fix, it removes each comment together with the line it sat on, then runs Prettier over the files it changed.
Five unit tests cover the checker.
The check runs in the pre-push hook and in npm run check. A push with
--no-verify, or a commit made in the GitHub web editor, skips the hook, and nothing else runs it.
What runs, and when
Nothing here runs by itself except the two git hooks. The rest are commands the author runs.
| Command | What it runs |
|---|---|
npm run check | Typecheck, lint, the comment check, formatting, spelling and the unit tests |
npm run build | The production build, which type-checks again and renders every page; Vercel runs the same command |
npm run test:e2e | The smoke spec, against the build |
npm run build then npm run test:perf | The Lighthouse budgets in .lighthouserc.json, three runs on three URLs, started through npx |
Pushing a branch is also a check. Vercel builds a preview of every pushed branch and reports it as a commit status called
Vercel, with a URL to open. Vercel runs the build but not the linters or the tests.
Lighthouse runs through npx rather than from the project's own dependencies. The CLI pulls in an old
Lighthouse and Puppeteer, and that was thirteen advisories the lockfile did not need for a tool that only runs when someone asks.
What the Dependabot runs showed
The failed runs on Dependabot's pull requests, from when CI still ran, show each layer doing its job. Those pull requests now get no automatic check at all:
-
The Pyodide bump failed in
wasm-assets.test.ts: "expected 314.0.6 to be 314.0.7". The CDN URL uses a version typed intolib/wasmAssets.ts, and the test refuses to letpackage.jsonand that constant disagree. -
The dev-tooling group failed at
npm ciin 23 seconds. A newer Vite wanted a newer esbuild than the exact 0.25.12 the project pins. - The Next group failed on Lighthouse, on a single slow run. That run is why there are now three.
Finished reading?
0 of 26 chapters read in this series