QA plan: React chat UI (#17)
Story: React + Vite chat UI (question, answer, sources) (#17)
Capability: Web UI (#16)
Spec: specs/ui.md
Test scope
Section titled “Test scope”| Type | Coverage |
|---|---|
| Unit (Vitest + RTL) | Question textarea renders; Enter submits, Shift+Enter inserts newline |
| Unit | Loading state renders while request is in flight |
| Unit | Answer and source chunks render correctly given a mock API response |
| Unit | Source list is collapsed by default; clicking expands |
| Unit | Citation markers in the answer text match source indices |
| Unit | QA history: multiple question/answer pairs render in order |
| Unit | Error state renders an error message in the answer area; page does not crash |
| Unit | npm run build produces a dist/ directory (verified by CI step, not a Vitest test) |
| Accessibility | Key interactive elements (textarea, submit button, source toggle) have accessible labels |
| e2e (Playwright) | Full interaction flow: type question, submit, see answer and sources, sources collapse/expand |
Note: API key intake and badge behavior are in Story #63 (separate plan). This plan covers the base chat UI without auth state.
Entry criteria
Section titled “Entry criteria”- React + Vite project scaffolded with Vitest and React Testing Library configured
- Mock API response fixture available in tests
Exit criteria
Section titled “Exit criteria”- All Vitest unit tests pass
- Build step:
npm run buildexits 0 and producesdist/withindex.htmland hashed assets - Playwright e2e passes against local Vite dev server (mocked API backend)
- Accessibility: no critical violations on
axescan of the rendered page - No TypeScript errors (
tsc --noEmitexits 0)
Environments
Section titled “Environments”Unit: local and CI (Vitest headless). e2e: local (headed) and CI (Playwright headless against Vite dev server with mocked API). No AWS access required.
Tooling
Section titled “Tooling”Vitest, React Testing Library, Playwright, axe-core (via @axe-core/playwright or equivalent). tsc for type checking.
Fixtures and data
Section titled “Fixtures and data”mockApiResponse: a hardcoded JSON object matching the/queryresponse schema with 2 sourcesmockApiError: simulates a network error response- MSW (Mock Service Worker) or Playwright route intercept to stub API calls in tests
How tests are written
Section titled “How tests are written”test("source list is collapsed by default", async () => { // Given a rendered QAPair with two sources render(<QAPair answer="Test answer" sources={mockSources} />); // When no interaction has occurred // Then source content is not visible expect(screen.queryByText(mockSources[0].content)).not.toBeInTheDocument();});- Vite dev proxy configuration may differ from CloudFront routing in production. Add an e2e smoke test against the QA CloudFront URL (covered in Wire UI plan #18) rather than relying on the proxy alone.
- CSS modules class-name instability can cause RTL
getByRolefailures if aria roles are missing. Prefer role-based and label-based queries over class-based selectors in all test assertions.
Capability acceptance criteria link
Section titled “Capability acceptance criteria link”Spec: question input submits, answer displays, source chunks are collapsible, input clears after submit, npm run build produces deployable dist/. All are direct exit criteria above.
© 2026 Benjamin Arunski