Co-Study4Grid / scripts /parity_e2e /DEMO_REPLAY_README.md
github-actions[bot]
Deploy 7688ef1
13d4e44
|
Raw
History Blame Contribute Delete
12.2 kB

Demo-scenario replay — Layer 3c

A scenario-driven E2E spec that translates the manual Fiche_demo_CoStudy4Grid into an automated regression net on config_small_grid. Complements the existing four parity layers (static / session-fidelity / gesture-sequence / invariants) by binding the demo's storyline to structural invariants on the live DOM.

Files in this folder

File Role
fixtures/demo_small_grid_log.golden.json Golden trace. Curated interaction_log.json from a real demo run on config_small_grid. 48 events (raw capture was 79; trimming rationale in _meta.normalizations). Re-capture by playing the demo and overwriting this file.
fixtures/demo_scenario.ts Fiche-as-data. Each checkpoint maps one paragraph of the fiche → expected events + structural invariants. This is the file non-developers edit to extend coverage.
demo_replay.spec.ts Layer A — structural invariants. Walks the scenario, drives gestures with Playwright, asserts invariants on the live DOM, then diffs the captured interactionLogger log against the golden trace.
demo_visual_snapshots.spec.ts Layer B — normalised SVG/HTML snapshots. Captures 3 stable surfaces (action card, overview map, combine modal) and diffs against text goldens under __snapshots__/. Scrubs auto-ids + rounds float coords + sorts attributes so the diffs stay readable.
demo_meta_invariants.spec.ts Layer D — meta-invariants. Cheap, broad sanity checks: no console errors, no empty visible text, no undefined/null/NaN ids, no degenerate viewBox, pin-count consistency.
playwright.config.ts Shared; testMatch now picks up demo_*.spec.ts alongside the existing *parity.spec.ts.
package.json Shared with the existing parity spec.

How the three layers interact

  Fiche markdown                Scenario (data)              Runner (code)
  =================             ==================           ===============
  Étape 2 — Jouer       ─────►  events: [                    addContingencyAndApply()
  une contingence                  contingency_element_added, ─┐
                                   contingency_applied         │ Playwright
                                ]                              │ gesture
                                invariants: [               ◄──┘
                                   .nad-contingency-target × 1,
                                   .nad-overloaded ≥ 1,         <── DOM assertion
                                   sidebar-summary-contingency  <── visible
                                ]                       
                                                              <── log capture +
                                                                  golden diff

The runner does NOT know what "étape 2" means semantically — it only knows how to dispatch a gesture (e.g. addContingencyAndApply) and how to assert an invariant (CSS selector + count/visible/class). Adding a fiche step means adding rows to demo_scenario.ts and, occasionally, a new dispatcher in the runner.

Running locally

# One-time
cd frontend && npm install && npm run build && cd ..
cd scripts/parity_e2e && npm install && npx playwright install chromium

# Run the demo replay only
npx playwright test demo_replay.spec.ts

# Run a single act
npx playwright test demo_replay.spec.ts -g "Acte 1"

# Headed mode (for debugging visual invariants)
npx playwright test demo_replay.spec.ts --headed

Prerequisites (landed)

  1. Interaction logger bridge (main.tsx) — exposes window.__interactionLogger in dev / VITE_EXPOSE_LOGGER builds so Playwright can call getLog(). Production builds are unaffected; Vite tree-shakes the bridge away.

  2. data-testid hooks landed across the 5 components the demo scenario relies on:

    • main.tsx — logger bridge.
    • SidebarSummary.tsxsidebar-summary-contingency, sidebar-summary-overloads.
    • SldOverlay.tsxsld-overlay + data-vl-name=${vl}.
    • AppSidebar.tsxcontingency-trigger on the Trigger button.
    • VisualizationPanel.tsxtab-button-${id}, tab-detach-${id}, data-tab-active="true|false" on each tab.
    • ActionFeed.tsxanalyze-suggest, display-prioritized-actions.
    • ActionCard.tsxfavorite-${id}, reject-${id} on the rail buttons.
  3. Real backend mode (now landed): set COSTUDY4GRID_REAL_BACKEND=1. The mock-route layer short-circuits and playwright.global-setup.ts POSTs /api/config with the small_grid paths so subsequent specs hit a pre-loaded state. Two flavours:

    # (a) Run uvicorn yourself, point Playwright at it (default).
    uvicorn expert_backend.main:app --port 8000 &
    COSTUDY4GRID_REAL_BACKEND=1 npx playwright test
    
    # (b) Let Playwright spawn + teardown uvicorn for you.
    COSTUDY4GRID_REAL_BACKEND=1 \
    COSTUDY4GRID_SPAWN_BACKEND=1 \
    npx playwright test
    

    Override the backend URL with COSTUDY4GRID_BACKEND_URL=http://host:port when running against a non-default address. The global-setup waits up to 60 s for /api/user-config to respond before failing.

Numerical contract: pytest companion

Étape D of the test plan lives at expert_backend/tests/test_demo_scenario_small_grid.py. It mirrors the numerical assertions the demo trace embeds (1 overload detected, 10 prioritised actions including disco_BEON / node_merging_PYMONP3 / load_shedding_BEON3, two superposition pairs converging to the recorded rho values) — directly against the real backend via FastAPI's TestClient. Skipped when the small_grid data is missing or when the conftest mock layer is active.

# Run the demo-numerical suite (slow — loads the real network).
pytest expert_backend/tests/test_demo_scenario_small_grid.py -m slow

# Or run a single assertion:
pytest expert_backend/tests/test_demo_scenario_small_grid.py::TestDemoScenarioSmallGrid::test_compute_superposition_matches_golden_trace

Tolerances on the superposition pairs (COMBINED_PAIR_* constants in the test file) are 1% absolute on simulated_max_rho — loose enough to absorb minor loadflow drift, tight enough to catch a real regression. Re-tune by re-running the demo and copying the new simulated_max_rho values from the saved interaction_log.json.

What the scaffold does today

Checkpoint Gesture wired Invariants wired Notes
Étape 1 — Charger
Étape 2 — Contingence
Étape 3 — Impact view
Étape 4 — First guess Mock backend returns an open_coupling_COUCHP6_uuid candidate from /api/actions for the dropdown.
Étape 5 — Asset zoom SLD open is driven by dblclick on the mock VL node.
Étape 6 — Impact action Composable from toggleViewMode; no DOM invariant.
Étape 7 — Detach Real popup automation is best-effort: the gesture is logged even if Chromium blocks the popup.
Étape 8 — Analyze
Étape 8b — Overflow layers Driven via simulateOverflowIframeGestures — postMessages the 6 cs4g:* envelopes directly to the parent window. Validates the message → log pipeline; iframe-side rendering is not exercised (covered by alphaDeesp's own tests).
Étape 8c — Display
Étape 9 — Explore
Étape 10 — Overview pins
Étape 11 — MW re-simulate
Étape 12 — Combine Pair-pick UI is exercised through the modal body; superposition mock returns the recorded rho values from COMBINED_PAIR_EXPECTED_RHO.
Étape 13 — Save

= wired in the runner today, = deferred (rationale in the Notes column).

Layers of visual verification (recap)

Layer Implemented here Cost Catches
A — Structural invariants (count, visible, class, attribute) demo_replay.spec.ts very low 90 % of UI regressions (missing halo, lost pin, broken filter)
B — Normalised SVG/HTML snapshots demo_visual_snapshots.spec.ts low Diagram-rendering drift, lost attribute, renamed class
C — Pixel diff (targeted) — (not planned for now) high Theme / token / font regressions — already gated by the design-token rule in check_code_quality.py
D — Meta-invariants (no empty text, no console errors, valid ids) demo_meta_invariants.spec.ts low Catastrophic mis-renders

Layer C remains deliberately out of scope — the design-token gate in check_code_quality.py already locks down the visual contract for colours/spacing/radius, and pixel diffs on pypowsybl NADs are prohibitively noisy. If a specific surface ever needs pixel-level guarantees (e.g. the printed legend in a PR-screenshot deliverable), extend demo_visual_snapshots.spec.ts with toHaveScreenshot() on a viewport-frozen locator.

CI wiring (landed)

.github/workflows/parity.yml now ships two Playwright jobs:

Job When What it runs Cost
demo-meta-invariants every push/PR demo_meta_invariants.spec.ts only ~ 1.5 min
layer3b-behavioural-e2e nightly + PRs with e2e label ALL specs (e2e_parity + 3 demo specs) ~ 3 min

The fast lane runs on every commit because the meta-invariants catch console errors and undefined-id leaks at low cost — bugs no static check can see. The full lane stays gated to keep the per-commit CI minutes bounded.

OS-agnostic snapshots

playwright.config.ts sets:

snapshotPathTemplate: '{testFilePath}-snapshots/{arg}{ext}',

This drops the default {-projectName}{-platform} suffix. Rationale: the snapshots in demo_visual_snapshots.spec.ts are **text-serialised

  • normalised DOM/SVG** — they carry no antialias, no font rendering, no pixel data. The same baseline holds on macOS (dev), Linux (CI) and Windows. Per-channel separation (chromium vs firefox) would matter only if we add a non-chromium project, which we don't.

If you regenerate the baselines, the resulting files are <surface>.txt (no suffix) — commit them as-is. Existing baselines generated with the default template (e.g. <surface>-chromium-darwin.txt) need to be renamed or regenerated once after this config change:

cd scripts/parity_e2e
rm -rf demo_visual_snapshots.spec.ts-snapshots/
npx playwright test demo_visual_snapshots.spec.ts --update-snapshots
git add demo_visual_snapshots.spec.ts-snapshots/

Recapturing the golden trace

When the demo evolves (new fiche steps, new analysis output, new actions on small_grid), re-record:

1. Start backend with small_grid config + frontend dev server.
2. Play the fiche end-to-end.
3. Click Save Results.
4. Copy `<output_folder>/.../interaction_log.json` →
   `scripts/parity_e2e/fixtures/demo_small_grid_log.golden.json`.
5. Re-run the normalization (scrub absolute paths, trim startup noise,
   re-number seq). Today this is manual; a small `normalize_log.py`
   script is a natural follow-up.
6. Re-run `npx playwright test demo_replay.spec.ts` and update any
   numeric tolerances in `COMBINED_PAIR_EXPECTED_RHO` if the recommender
   has drifted within band.

Why no pixel diffs in this spec?

Three reasons, documented for posterity:

  1. The NAD is 12 MB and viewport-dependent. Pan/zoom + antialias variance produce 100s of noise pixels per run. Diff threshold tuning becomes a full-time job.
  2. Design-token regressions are already gated by the check_code_quality.py zero-hex-literals rule (root CLAUDE.md §Design tokens).
  3. Structural invariants catch the bugs operators notice — a halo that disappears, a pin that stops rendering, a legend that vanishes — without coupling the test to a specific render of those shapes.

Layer C will land for 3-4 stable surfaces (legend, action card, combine modal, overview at a frozen viewBox) in a follow-up spec.