Choose validation around the behavior a change can break. This repository combines strict source checks, content-generation tests, Angular unit tests, and production browser regressions; each catches a different class of failure.
Required commands
pnpm check
pnpm test --watch=falseRun these sequentially. Their lifecycle hooks write shared generated files. Before every push, run both check and noninteractive unit tests after the final change. If anything changes afterward, run them again. For changes that need browser validation, run pnpm test:e2e locally; it builds production output before launching Playwright.
pnpm check includes Biome formatting, Angular application and strict-template compilation, strict tooling and E2E typechecks, ESLint, and article MDX/BibTeX checks. Documentation structure, internal links, and anchors are validated by its generation hook. It does not execute tests or build production pages.
pnpm test --watch=false runs Node content/tooling tests followed by Angular unit tests. If the host's CPU count causes Vitest worker failures, use VITEST_MAX_WORKERS=2 pnpm test --watch=false.
Match tests to changes
| Change | Useful evidence |
|---|---|
| Prose or handbook wording | Generation and link checks; inspect the relevant rendered page |
| Parser, compiler, cache, or publication | Small input fixtures, invalid inputs, no-op behavior, failure recovery, removed outputs |
| Angular state or lifecycle | User-visible state changes, dependency overrides, teardown and late asynchronous completion |
| Routes or metadata | Prerendered HTML, direct visits, client navigation, canonical and noindex behavior |
| Search | Chinese/English queries, ranking, real keyboard events, focus containment/restoration, cold and warm loading |
| Formula, diagram, or reading layout | Real MathJax plus Chromium/WebKit at narrow and desktop widths, both themes |
| Hosting or Markdown | Actual Cloudflare Worker requests, negotiation, MIME, redirects, direct exports, missing routes |
| Performance | Served production resources including lazy chunks and worker requests |
Do not add a test that merely repeats a constant from the implementation or checks incidental markup. Regression tests should fail for the underlying bug: for example, removing cleanup, loading the full search index eagerly, or serving an HTML 404 with a Markdown MIME type.
Unit-test conventions
Tooling tests live beside helpers as scripts/lib/*.spec.mts and run through tsx. Use temporary directories for file-generation tests and dispose them. Verify that failed generation preserves the prior successful published outputs and that removed sources prune derived files.
Angular tests live beside application code. Override injectable services with TestBed; the Angular unit-test builder does not support vi.mock for relative imports. Use controlled promises and observable effects for lifecycle tests instead of arbitrary delays. Verify listeners, views, observers, timers, and late imports are released on navigation.
Browser setup and focused runs
Install the matching browser versions after dependency upgrades:
pnpm exec playwright install --with-deps chromium webkit
pnpm build
pnpm exec playwright test e2e/docs.spec.ts --project=desktop --project=mobileCalling Playwright directly reuses existing production output, so build after changing source. BLOG_PREVIEW_PORT and BLOG_CLOUDFLARE_PORT select isolated server ports when defaults are occupied. Playwright owns its servers and does not reuse an existing development server. See host troubleshooting for Linux library, font, and workerd issues.
The Playwright configuration starts the preview and Wrangler Node entrypoints
directly so their server processes exit when the test run finishes under native
pnpm.
Shared browser fixtures stub third-party analytics/comments and external webfonts.
Generated formulas are already static; reading/scroll tests must not load a browser
math engine. Real-rendering tests verify static output, retained-TeX copying, stable
right-click geometry, and dynamic TeX views using pinned MathJax and local fonts.
The dynamic renderer's custom context menu must remain disabled.
Navigation tests wait for article/font readiness, not unrelated external images'
load event. Do not use stubbed traffic as rendering evidence.
Wait for .post-body[data-rendered="true"] before testing attached article controls. This marker does not await lazy article-specific enhancements or MathJax; those have their own readiness conditions. Scroll offscreen controls into view before focusing them to avoid smooth-scroll races with the next navigation.
CI and performance evidence
The quality workflow runs source checks and unit tests. The quality job generates once and uses *:generated commands afterward; the final required check status depends only on that job. Browser tests are not run in GitHub CI; run them locally when relevant. Keep generated and normal commands aligned when adding checks.
Resource budgets cover representative production pages and cold search with worker traffic included. Warm queries must add no resources. Use the per-request reports to explain changes rather than increasing a threshold without investigation. Local browser runs retain reports and visual diagnostics, including successful rendering captures; screenshots are diagnostic artifacts rather than pixel baselines. See performance records. The long-article Chromium diagnostic applies fourfold CPU throttling and waits for article font/layout readiness before sampling; its host timings are diagnostic, while engine loading and body growth remain assertions.