Skip to content

Admin test harness and characterization safety net - #38

Closed
theobong wants to merge 18 commits into
mainfrom
chore/admin-campaign-02-test-harness
Closed

theobong wants to merge 18 commits into
mainfrom
chore/admin-campaign-02-test-harness

Conversation

@theobong

@theobong theobong commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

What changed

The operator dashboard gets a browser-like (jsdom) test environment and a characterization safety net, so the cleanup PRs that follow can prove they change nothing visible. The new tests pin today's behavior of hash routing, operator sign-in bootstrap and logout, the query client, error copy, dates, the Leaflet map and every section page's loading, empty, error, populated and deep-link states. No application code changes.

Visible in: none.

Before you start

  • Where: nothing to deploy. Run pnpm test from the repo root on Node 22.
  • Size: 6,505 counted lines. This is a campaign PR; the author approved PRs over the 400-line cap for this cleanup.

Verify

  1. From the repo root, run pnpm install then pnpm test. Expect: 56 test files and 603 tests pass (main has 38 files and 203 tests).
  2. Run TZ=Pacific/Kiritimati LC_ALL=de_DE.UTF-8 pnpm test. Expect: the same 603 pass, because the test runtime pins its locale and time zone.

Regression

The dashboard itself: [Admin]

  1. After merge, open admin.civfix.dev and click through the home tiles. Expect: no change at all. Only test files, the test config and dev dependencies changed.

Not covered

  • This PR adds tests only; there is no in-app behavior to exercise.

Findings addressed

  • ADM-TEST-*: characterization tests for code PRs 3 to 9 will touch:
    • hash routing: known and unknown routes, empty hash, malformed percent-encoding;
    • the operator bootstrap and logout state machine;
    • query client defaults, retry and error toasts, and the key factories;
    • error copy and dates;
    • the Leaflet wrapper: tooltips, pins, remove() on unmount;
    • loading, empty, error, populated, "Load more", filters, search and deep-link states for all 11 sections.
  • The vitest config had no DOM environment, and --passWithNoTests hid a broken include glob.
  • users-page.test.ts actually tested the profile activity navigation module; it is renamed to match.

Every test that pins a known defect ends its name in "(current behavior)", so the fix PRs in this train flip exactly those tests:

  • the tooltip parsed as HTML;
  • the malformed hash throwing on load;
  • the four deep-link overrides;
  • the list/infinite cache collisions;
  • the prompt dialog's focus loss;
  • errorMessage quirks.

Tests changed

Old (deleted source-text assertion) New (rendered behavior)
modal-accessibility: the lightbox markup contains role="dialog" and aria-modal="true", with no aria-hidden The opened lightbox, confirm and prompt dialogs are found by role "dialog", have aria-modal="true", have no aria-hidden, and match the shell's Escape-owner selector while open.
modal-accessibility: both hosts call the focus helper Focus moves inside on open, Tab and Shift+Tab wrap inside the dialog, and focus returns to the opener on Close, Cancel or Escape (lightbox and confirm). The prompt's focus loss is pinned as current behavior.
modal-accessibility: the lightbox source has onError, onLoad and "Refresh photo" A load event clears "Loading photo..."; an error event shows "This photo link expired" with a working "Refresh photo" button.
home-accessibility: tile head, foot and preview rows are <button type="button"> with no role, tabIndex or onKeyDown, and no div inside a button Every focusable element in the 7 tiles is a named native type="button" button (26 counted), with no focusable element or div inside it. No element has role="button" or a tabindex. Tab visits each control exactly once in order. Enter and Space each activate every control exactly once.

Decisions for the reviewer

  • jsdom version: pinned to ^26, because jsdom 30 requires Node ^22.22.2 and the repo's engines field says >=22.
  • Deterministic runtime: vitest.config.ts pins TZ=UTC and en_US.UTF-8 before workers fork, and runs tests in the forks pool.
  • Fake timers: a small test helper registers a jest fake-timer shim, because Testing Library only detects Jest's timers. The two debounce tests are exact to the millisecond.

Verification

  • Node v22.23.2.
  • pnpm lint, pnpm typecheck and pnpm build pass.
  • pnpm test passes: 56 files, 603 tests. It also passed 3 more times, once shuffled, and once under LC_ALL=de_DE.UTF-8 with TZ=Pacific/Kiritimati.
  • New dev dependencies are all MIT: @testing-library/react, @testing-library/dom, @testing-library/user-event, @testing-library/jest-dom and jsdom.
  • origin/main gained the unified mail inbox mid-PR, so the mail page's inbox characterization was re-pinned to the new feed (a merge commit, plus one test commit).
  • An adversarial review found these blockers, all fixed:
    • locale-dependent number assertions;
    • a collision test that could not fail (it now renders the real hooks);
    • defect pins without the flippable "(current behavior)" marker;
    • a home accessibility test that read React internals.

🤖 Generated with Claude Code

@greptile-apps

greptile-apps Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

Safe to merge; no outstanding issues were identified.

Reviews (3) · Last reviewed commit: "page tests wait on the right pane and us..."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants