pediatric-ai-scribe-v3/e2e/tests/peguide-react.spec.js
Daniel 9ad34a7f35 feat(client): port PE Guide reference libraries + rescue untracked FAQ data
Two things in one commit because they're coupled by a gitignore fix:
(1) PE Guide minimum-viable port, (2) a pre-existing bug where
client/src/data/faq.ts was silently gitignored and never committed.

.gitignore — narrow scope
  Changed `data/` to `/data/`. The old rule matched every nested
  `data/` directory in the tree, including client/src/data/, which
  meant faq.ts from the FAQ port (commit c0038da) never landed in
  git — the Faq page built locally only because the file existed
  on the dev machine. A fresh clone or CI build would fail the
  Vite build at '@/data/faq'. The narrowed rule still ignores the
  runtime DB directory at repo root while allowing project data
  modules to be tracked. This commit also commits the missing
  faq.ts so the FAQ page is actually buildable from git again.

client/src/data/pe-guide.ts
  Verbatim port of lines 23-311 of public/js/peGuide.js — the stable
  reference content:
    • SCALES (12 scales: MRC, DTR, plantar, Beighton, ATR, RR, SpO2,
      Silverman, Westley, Levine murmur, pulse amp, cap refill)
    • SYSTEM_SCALES (per-body-system scale mapping)
    • APTM_LEGEND (5 cardiac auscultation points)
    • INNOCENT_MURMURS (5 benign childhood murmurs)
    • RESP_SOUNDS (7 entries with /audio/respiratory/*.ogg paths)
    • CARDIAC_SOUNDS (6 entries with /audio/cardiac/* paths)
  Counts preserved exactly. Audio files stay under public/audio/ and
  are served unchanged.

What is NOT in this commit — on purpose
  PE_DATA (the ~1000-line age-group × system × component × step
  hierarchy) stays in the vanilla app. The migration checkpoint memory
  explicitly warns about the class of bug where an LLM silently drops
  entries from long clinical arrays. PE_DATA porting needs its own
  session with per-entry counts + visual diff against the vanilla
  source. An amber banner at the top of the React page links to
  /#peGuide (the legacy checklist viewer) so users still reach the
  full exam-step checklist + Generate-Exam-Report flow.

  Also skipped: the big inline APTM_SVG chest diagram. The letter
  legend (A/P/E/T/M) carries the clinical content; the pictorial
  SVG can land later without content risk.

client/src/pages/PeGuide.tsx — viewer
  Grid-of-cards layout: one card per scale, per APTM point, per
  innocent-murmur, and per sound entry. Sound cards use native
  <audio controls> so the browser does the usual play/pause/seek —
  no custom player. data-testid hooks throughout for the spec.

e2e/tests/peguide-react.spec.js — four smoke tests:
  all 12 scales render (this is the count that would fail loudly if
  someone trimmed SCALES later), APTM has all 5 letters, sound
  libraries have the exact respiratory + cardiac keys, and the
  legacy-viewer link is present.

Client tsc -b + vite build clean. Bundle 452.91 kB / 129.35 kB gz.
2026-04-23 23:57:16 +02:00

48 lines
2.1 KiB
JavaScript

// ============================================================
// PE GUIDE (React port) — smoke tests for the reference page.
//
// Covers the subset that landed in the first commit: scales,
// APTM legend, innocent murmurs, respiratory + cardiac sound
// libraries. The full PE_DATA exam-step checklist is still in
// the legacy viewer and linked to from the amber banner.
// ============================================================
const { test, expect, E2E_BASE } = require('../fixtures');
async function openReactPeGuide(page) {
await page.goto(E2E_BASE + '/app/peguide');
await page.waitForSelector('[data-testid^="scale-"]', { timeout: 15000 });
}
test.describe('React PE Guide — reference content renders', () => {
test('all 12 scales render (MRC, DTR, Plantar, Beighton, ATR, RR, SpO2, Silverman, Westley, murmur Levine, pulse amp, cap refill)', async ({ authedPage: _, page }) => {
await openReactPeGuide(page);
const count = await page.locator('[data-testid^="scale-"]').count();
expect(count).toBe(12);
});
test('APTM legend shows all 5 points (A, P, E, T, M)', async ({ authedPage: _, page }) => {
await openReactPeGuide(page);
for (const letter of ['a', 'p', 'e', 't', 'm']) {
await expect(page.locator('[data-testid="aptm-' + letter + '"]')).toBeVisible();
}
});
test('sound libraries: 7 respiratory + 6 cardiac entries', async ({ authedPage: _, page }) => {
await openReactPeGuide(page);
const respiratoryKeys = ['normal', 'wheeze', 'stridor', 'finecrackles', 'coarsecrackles', 'rhonchi', 'pleuralrub'];
const cardiacKeys = ['normal', 'infant-normal', 'vsd', 'mvp', 'stills', 'functional'];
for (const k of respiratoryKeys) {
expect(await page.locator('[data-testid="sound-' + k + '"]').count()).toBeGreaterThan(0);
}
for (const k of cardiacKeys) {
expect(await page.locator('[data-testid="sound-' + k + '"]').count()).toBeGreaterThan(0);
}
});
test('legacy-viewer link for exam-step checklist is present', async ({ authedPage: _, page }) => {
await openReactPeGuide(page);
await expect(page.getByText('Open checklist in legacy viewer')).toBeVisible();
});
});