pediatric-ai-scribe-v3/scripts/e2e.sh
Daniel d71714b65d test(lint): static reference linter — catches dead-code + orphan refs
You were right that Playwright has been catching the easy bugs while
the high-signal bugs (lightbox stranded after the Bedside reorg, PVC
clinically wrong, prod not rebuilt) all had to be caught by you as
the user. Adding a static linter so the class of bug that produced
the lightbox regression fails CI next time instead of the app.

scripts/lint-references.js walks public/js and validates every
getElementById('X') and querySelector('#X') resolves to an id that
is defined SOMEWHERE in the repo — either a static HTML attribute,
a .id = 'X' assignment, or an id="X" substring inside a JS template
string. It also walks HTML for asset references (data-img-src,
<img src>, <audio src>, <link href>) and verifies each root-absolute
path maps to a real file on disk.

Running it on the current tree surfaced two real problems:

1. shadess.js:917 reached for #wv-note-transcript when collecting
   refine source context. The actual id is #wv-transcript (no -note-
   infix — the transcript element is shared across well-visit sub-
   panels). The bug meant a generated well-visit note's Refine call
   silently missed the original transcript as source context; the AI
   still "worked" but with less signal and no error. Fixed.

2. public/js/adminMilestones.js (221 lines) referenced 17 ids that
   were removed a year+ ago in commit 3173ce6 ("Remove milestone
   admin UI, add CMS content refresh button"). That commit dropped
   the HTML but forgot the JS, which has been dead-loaded on every
   page view since. All its addEventListener calls are guarded with
   optional chaining, so nothing errored — just silent cruft.
   Deleted the file and dropped the <script defer> tag from
   index.html.

scripts/e2e.sh runs the linter as a preflight before the Playwright
container starts; a broken reference now fails the suite before any
test even boots.

Allowlist is kept small and prefix-based for the handful of id families
that are built dynamically by JS (bedside em-* sections, BP chart
elements, etc.). When a new component is added, its ids get picked up
automatically by the repo-wide collect() pass; the allowlist rarely
needs to grow.

Suite: 294 passed / 0 failed.
2026-04-23 18:58:38 +02:00

32 lines
1.2 KiB
Bash
Executable file

#!/usr/bin/env bash
# Runs Playwright smoke tests inside the official Playwright container against
# the running PedScribe app. Usage: npm run e2e (or ./scripts/e2e.sh)
set -euo pipefail
cd "$(dirname "$0")/.."
IMAGE="mcr.microsoft.com/playwright:v1.50.0-noble"
# --- PREFLIGHT: static reference linter ---
# Catches the class of bug where a JS file reaches for an id that no
# HTML element (or dynamic id assignment anywhere in the repo) ever
# produces — the lightbox + adminMilestones dead-code bugs were both
# this shape and both went undetected until someone tripped over them
# in the real app. Fails the build before tests even start.
echo "==> Static reference lint"
docker run --rm -v "$PWD:/work" -w /work node:20-alpine \
node scripts/lint-references.js
# Attach the Playwright container to the same Docker network as the app so it
# can resolve pediatric-ai-scribe by service name. Internal container port is 3000.
NETWORK="${E2E_NETWORK:-ped-ai_default}"
BASE_URL="${BASE_URL:-http://pediatric-ai-scribe:3000}"
docker run --rm --ipc=host \
--network="$NETWORK" \
-v "$PWD/e2e":/work \
-w /work \
-e BASE_URL="$BASE_URL" \
-e CI=true \
"$IMAGE" \
sh -c "npm install --no-audit --no-fund --silent && npx playwright test"