From 2cf728210efe13bd8a611ed1907846f92a7e4c25 Mon Sep 17 00:00:00 2001 From: funman300 Date: Tue, 2 Jun 2026 13:41:07 -0700 Subject: [PATCH] feat(e2e): add window.__FERROUS_DEBUG__ bridge to /play for automation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit play.html now loads solitaire_wasm.js alongside the Bevy canvas and exposes the same window.__FERROUS_DEBUG__ object as /play-classic. The bridge runs an independent SolitaireGame (WASM logic layer) seeded from ?seed= / ?draw3= URL params; Bevy renders the visual game in parallel without coupling. Methods exposed: seed, state, legalMoves, moveHistory, snapshot, applyLegalMove, applyMove, draw, undo, serialize, fromSaved, newGame, failureReport, replayPayload, runAutoplay — matching the /play-classic contract so the shared Playwright harness targets either route without modification. cycle_metrics.js: add --route play-classic|play flag (default play-classic). Routes to /${route}?seed=N. The resume-overlay clear step is skipped for /play since the Bevy build uses localStorage-backed WasmStorage, not a #resume-overlay element. Co-Authored-By: Claude Sonnet 4.6 --- solitaire_server/e2e/scripts/cycle_metrics.js | 21 +- solitaire_server/web/play.html | 196 +++++++++++++++++- 2 files changed, 208 insertions(+), 9 deletions(-) diff --git a/solitaire_server/e2e/scripts/cycle_metrics.js b/solitaire_server/e2e/scripts/cycle_metrics.js index fa29ab8..71149a5 100644 --- a/solitaire_server/e2e/scripts/cycle_metrics.js +++ b/solitaire_server/e2e/scripts/cycle_metrics.js @@ -103,6 +103,9 @@ async function main() { const policyArg = readArg("--policy", "loop_aware"); const policy = policyArg === "baseline" ? "baseline" : "loop_aware"; const outPath = readArg("--out", "/tmp/playwright-cycle-metrics.json"); + // --route play-classic (default) or --route play + const routeArg = readArg("--route", "play-classic"); + const route = routeArg === "play" ? "play" : "play-classic"; const maxCycleRateAll = parseOptionalNumber("--max-cycle-rate-all"); const maxCycleRateDraw1 = parseOptionalNumber("--max-cycle-rate-draw1"); const maxCycleRateDraw3 = parseOptionalNumber("--max-cycle-rate-draw3"); @@ -155,17 +158,19 @@ async function main() { } }); - await page.goto(`${baseUrl}/play-classic?seed=${seed}${suffix}`, { + await page.goto(`${baseUrl}/${route}?seed=${seed}${suffix}`, { waitUntil: "domcontentloaded", }); - const resumeVisible = await page - .locator("#resume-overlay:not(.hidden)") - .isVisible() - .catch(() => false); - if (resumeVisible) { - await page.evaluate(() => localStorage.removeItem("fs_game_save")); - await page.reload({ waitUntil: "domcontentloaded" }); + if (route === "play-classic") { + const resumeVisible = await page + .locator("#resume-overlay:not(.hidden)") + .isVisible() + .catch(() => false); + if (resumeVisible) { + await page.evaluate(() => localStorage.removeItem("fs_game_save")); + await page.reload({ waitUntil: "domcontentloaded" }); + } } await page.waitForFunction( diff --git a/solitaire_server/web/play.html b/solitaire_server/web/play.html index 9751687..9b0ca6f 100644 --- a/solitaire_server/web/play.html +++ b/solitaire_server/web/play.html @@ -14,7 +14,201 @@