docs: add UI test strategy to refactor spec
This commit is contained in:
@@ -17,9 +17,22 @@ presentational component tree with a `utils/` library. Zero behavior change.
|
||||
|
||||
- No new features, no new filters, no new columns.
|
||||
- No state-management library (Redux/Zustand) — `useState`/`useMemo` in `App`.
|
||||
- No test framework installation. Verification is `npm run build` + manual smoke.
|
||||
- No changes to `main.jsx`, `index.html`, `vite.config.js`, `package.json`,
|
||||
`Dockerfile`, `docker-compose.yml`, or any of the data-build Python scripts.
|
||||
- No E2E browser automation (Playwright/Cypress) — component-level UI tests are
|
||||
the right granularity for a single-page read-only app with no backend.
|
||||
- No changes to `main.jsx`, `index.html`, `vite.config.js` (beyond adding Vitest
|
||||
config), Dockerfile, `docker-compose.yml`, or any of the data-build Python
|
||||
scripts.
|
||||
|
||||
## Tech Stack (tests)
|
||||
|
||||
- **Test framework:** Vitest + `@testing-library/react` + `@testing-library/jest-dom`
|
||||
+ `jsdom` + `@testing-library/user-event`. Vite-native, configured via a
|
||||
`test` block in `vite.config.js` (no separate config file). All
|
||||
devDependencies — ship no code to production.
|
||||
- **Coverage target:** not a number — every behavior bullet under `Verification`
|
||||
step 3 has a corresponding test. The component tests catch single-component
|
||||
regressions; the App test catches wiring regressions (URL state, derived
|
||||
data, modal open/close).
|
||||
|
||||
## Target File Tree
|
||||
|
||||
@@ -41,6 +54,17 @@ react-app/src/
|
||||
movers.js ← computeMovers(filtered) -> { drops, rises }
|
||||
columns.jsx ← buildColumns({ isMobile, showFactionCol, onSelectSize })
|
||||
history.js ← buildChartGeometry(history, dims) -> geometry
|
||||
test/
|
||||
setup.js ← vitest setup (imports jest-dom matchers)
|
||||
fixtures/
|
||||
data.json ← minimal mock dataset for tests
|
||||
App.test.jsx ← end-to-end UI tests of the full App
|
||||
FilterBar.test.jsx ← search + faction + change select behavior
|
||||
UnitTable.test.jsx ← DataGrid renders rows; size cell click
|
||||
MoversSection.test.jsx ← drops + rises render; click opens modal
|
||||
GraphModal.test.jsx ← modal opens with history; size dropdown
|
||||
SizeCell.test.jsx ← single vs multi-size rendering + click
|
||||
utils.test.js ← smoke tests for format/url/movers/history
|
||||
```
|
||||
|
||||
## Module Specs
|
||||
@@ -231,24 +255,82 @@ unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
1. `cd react-app && npm run build` — must succeed. Catches circular imports,
|
||||
1. `cd react-app && npm test` — full Vitest suite must pass. This is the
|
||||
primary verification for the refactor. Tests cover every UI behavior
|
||||
listed in the manual smoke checklist below.
|
||||
2. `cd react-app && npm run build` — must succeed. Catches circular imports,
|
||||
missing exports, JSX syntax errors, broken path aliases.
|
||||
2. `cd react-app && npm run dev` and manually verify, in order:
|
||||
3. `cd react-app && npm run dev` and manually verify, in order (these mirror
|
||||
the test cases; the tests are the source of truth, this is a final
|
||||
eyeball check):
|
||||
a. Page loads, data populates, "Loading…" disappears.
|
||||
b. Search field filters by name/faction_name/size (case-insensitive).
|
||||
c. Faction select filters to one faction (Faction column disappears).
|
||||
d. Change select filters by direction (up/down/no-change/new-only/old-only).
|
||||
e. URL query string updates as any filter changes; reload restores state.
|
||||
f. Click a `#` cell with multiple sizes → row's Old/New/Δ% updates to the
|
||||
selected size's values; click the same cell again to switch back.
|
||||
f. Click a `#` cell with multiple sizes → row's Old/New/Δ% updates.
|
||||
g. Click any other cell → modal opens with the correct unit's history.
|
||||
h. Modal size dropdown switches the chart's history when the unit has
|
||||
multiple sizes.
|
||||
h. Modal size dropdown switches the chart's history when applicable.
|
||||
i. Empty-history units render the "No historical data" message.
|
||||
j. Movers cards reflect the currently filtered set; clicking a mover row
|
||||
opens the same modal.
|
||||
3. `git diff --stat` — should show the new files plus a much smaller
|
||||
`App.jsx`. No file outside `react-app/src/` should change.
|
||||
4. `git diff --stat` — should show the new files plus a much smaller
|
||||
`App.jsx`. No file outside `react-app/src/` (plus the `package.json`
|
||||
devDeps and the `vitest` config block in `vite.config.js`) should change.
|
||||
|
||||
## Test Strategy
|
||||
|
||||
**Framework:** Vitest + `@testing-library/react` + `@testing-library/jest-dom`
|
||||
+ `jsdom` + `@testing-library/user-event`. Configured in `vite.config.js` via
|
||||
a `test:` block (no separate config file). All test code lives under
|
||||
`react-app/src/test/`.
|
||||
|
||||
**Test categories:**
|
||||
|
||||
1. **Pure-function smoke tests** (`test/utils.test.js`) — one or two assertions
|
||||
per helper in `utils/format.js`, `utils/url.js`, `utils/movers.js`,
|
||||
`utils/history.js`. No DOM, no React. Fast, deterministic, document the
|
||||
contracts the components rely on.
|
||||
|
||||
2. **Component tests** (`test/FilterBar.test.jsx`, `UnitTable.test.jsx`,
|
||||
`MoversSection.test.jsx`, `GraphModal.test.jsx`, `SizeCell.test.jsx`) —
|
||||
render the component in isolation with realistic props (drawn from the
|
||||
`test/fixtures/data.json` fixture), drive user behavior with
|
||||
`userEvent`, assert on rendered text / class / role. The handler props
|
||||
(`onSelect`, `onSelectUnit`, `onCellClick`) are jest `vi.fn()`s and
|
||||
assertions check they were called with the right args.
|
||||
|
||||
3. **End-to-end App test** (`test/App.test.jsx`) — render `<App />` inside
|
||||
the real `ThemeProvider`, mock `fetch` to return the fixture, then
|
||||
exercise the full user journey from the README's feature list:
|
||||
- Page loads → "Loading…" disappears → table rows appear.
|
||||
- Type into search → rows narrow; clear → rows restore.
|
||||
- Select a faction → rows filter to that faction; the Faction column disappears.
|
||||
- Select "↓ Cheaper" → only negative `change_pct` rows remain.
|
||||
- URL query string reflects the filters (read `window.location.search`).
|
||||
- Reload page with `?faction=...&dir=...&q=...` → filters rehydrate from URL.
|
||||
- Click a multi-size `#` cell, choose a different size → row's Old/New/Δ
|
||||
values update.
|
||||
- Click any other cell → modal opens with the unit's name and history.
|
||||
- Click an empty-history unit → "No historical data" appears.
|
||||
- Click a row in the movers card → same modal opens.
|
||||
|
||||
`data-testid` attributes will be added sparingly to `App.jsx` and a couple
|
||||
of components only where the test needs a stable handle that the
|
||||
accessible role/text does not provide (e.g. the DataGrid's internal cells,
|
||||
which don't expose stable roles).
|
||||
|
||||
**Mocking strategy:** `globalThis.fetch` is stubbed in each test that needs
|
||||
data (the App test; component tests use fixture data directly via props).
|
||||
No other module is mocked — the tests run against the real implementations
|
||||
of the components and utils. The `ThemeProvider` wrapper is a small helper
|
||||
in `test/setup.js` to avoid repeating it in every test file.
|
||||
|
||||
**Coverage target:** not a number — every behavior bullet under
|
||||
`Verification` step 3 has a test. The component tests give us confidence
|
||||
that a one-off regression in a single component is caught; the App test
|
||||
gives us confidence that the orchestrator (URL state, augmented/filtered
|
||||
derivation, modal open/close) is wired correctly.
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
@@ -283,6 +365,19 @@ After each step, `npm run build` confirms nothing is broken mid-refactor.
|
||||
- **URL effect timing.** The two URL effects in `App` (read on mount, write
|
||||
on change) move to `App` unchanged. The two helpers in `utils/url.js` only
|
||||
encapsulate the `window` calls; they don't change the lifecycle.
|
||||
- **Test data fidelity.** The fixture must cover the variety of cases the
|
||||
real `data.json` has: at least one unit with multiple sizes, one with no
|
||||
history, one with `change_pct === 0`, one with `original === null` (new
|
||||
unit), one with `new === null` (removed unit). Each App test case
|
||||
references a specific unit from the fixture by name so the test is
|
||||
self-documenting.
|
||||
- **DataGrid in jsdom.** `@mui/x-data-grid` works under jsdom but the
|
||||
internal virtual scroller can be slow. We test by querying by role
|
||||
(`cell`, `row`) and by visible text. We do not test internal column
|
||||
resize / sort behavior (out of scope — the app uses none of it).
|
||||
- **Test runtime.** Vitest in jsdom is fast enough that the full suite
|
||||
should run in <10s. If a particular test is slow, the fix is to render
|
||||
smaller fixtures, not to mock React.
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user