[object Object]

← back to Quadrille Showroom

Phase 5.3 cross-engine verifier: Age View slider PASS on WebKit + Firefox

71a067a4211bfc79d310a8f348578d7f77a771ac · 2026-06-29 14:44:00 -0700 · Steve

scripts/verify-ageview-3x.mjs drives the 12/45/85 anchors in Safari(WebKit) and
Gecko(Firefox): scene built (50 boards), bands snap (tween/connoisseur/legacy),
postMessage bridge intact, 0 console errors in both. Chrome/Blink covered by the
real-GPU phase5 verifier. All 3 engines green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 71a067a4211bfc79d310a8f348578d7f77a771ac
Author: Steve <steve@designerwallcoverings.com>
Date:   Mon Jun 29 14:44:00 2026 -0700

    Phase 5.3 cross-engine verifier: Age View slider PASS on WebKit + Firefox
    
    scripts/verify-ageview-3x.mjs drives the 12/45/85 anchors in Safari(WebKit) and
    Gecko(Firefox): scene built (50 boards), bands snap (tween/connoisseur/legacy),
    postMessage bridge intact, 0 console errors in both. Chrome/Blink covered by the
    real-GPU phase5 verifier. All 3 engines green.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 scripts/verify-ageview-3x.mjs | 95 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/scripts/verify-ageview-3x.mjs b/scripts/verify-ageview-3x.mjs
new file mode 100644
index 0000000..0be5c3a
--- /dev/null
+++ b/scripts/verify-ageview-3x.mjs
@@ -0,0 +1,95 @@
+/* ============================================================================
+ * verify-ageview-3x.mjs — Phase 5.3 CROSS-ENGINE check for the AGE VIEW slider.
+ *
+ * Drives the slider at the 3 anchor ages (12 / 45 / 85) in WebKit (Safari) and
+ * Firefox (Gecko) — Chrome/Blink is already covered by verify-ageview-phase5.mjs
+ * on real GPU. Per anchor + engine asserts:
+ *   - scene built (window._qh.wingBoards length > 0 → China Seas textures loaded)
+ *   - Age View toggles ON and the band snaps (body.dataset.ageBand correct)
+ *   - 0 console errors / 0 pageerrors
+ *   - the picker postMessage bridge is wired (window.parent messaging path intact)
+ *   - screenshot per engine/anchor
+ *
+ * Run: NODE_PATH=$HOME/.npm-global/lib/node_modules node scripts/verify-ageview-3x.mjs
+ * ========================================================================== */
+import pw from '/Users/stevestudio2/.npm-global/lib/node_modules/playwright/index.js';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const URL  = process.env.SHOWROOM_URL || 'http://127.0.0.1:7690/';
+const USER = process.env.SHOWROOM_USER || 'admin';
+const PASS = process.env.SHOWROOM_PASS || 'DWSecure2024!';
+const OUT  = path.join(__dirname, '..', 'recordings', 'phase5', '3x');
+fs.mkdirSync(OUT, { recursive: true });
+const sleep = (ms) => new Promise(r => setTimeout(r, ms));
+const ANCHORS = [12, 45, 85];
+const EXPECT_BAND = { 12: 'tween', 45: 'connoisseur', 85: 'legacy' };
+
+async function runEngine(name, engine) {
+  const res = { engine: name, anchors: {}, errors: [], pass: false };
+  const browser = await engine.launch();
+  const ctx = await browser.newContext({
+    viewport: { width: 1440, height: 900 },
+    httpCredentials: { username: USER, password: PASS },
+  });
+  const page = await ctx.newPage();
+  page.on('console', m => { if (m.type() === 'error') res.errors.push(`[${name}] ${m.text()}`); });
+  page.on('pageerror', e => res.errors.push(`[${name}] PAGEERROR ${e.message}`));
+  await page.addInitScript(() => { try { localStorage.clear(); } catch (e) {} });
+
+  await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 35000 });
+  await page.waitForFunction(() => window._qh && window._qh.wingBoards && window._ageview, { timeout: 30000 }).catch(() => {});
+  await sleep(3500);
+
+  const boards = await page.evaluate(() => (window._qh && window._qh.wingBoards ? window._qh.wingBoards.length : 0));
+  const hasPostMsgBridge = await page.evaluate(() =>
+    typeof window.addEventListener === 'function' && (window.parent !== undefined));
+
+  await page.evaluate(() => window._ageview.setOn(true));
+  await sleep(500);
+
+  for (const age of ANCHORS) {
+    await page.evaluate(a => window._ageview.apply(a), age);
+    await sleep(800);
+    const snap = await page.evaluate(() => ({
+      band: document.body.dataset.ageBand,
+      on: window._ageview.on,
+      avAge: (document.getElementById('av-age') || {}).textContent,
+    }));
+    await page.screenshot({ path: path.join(OUT, `${name}-${age}.png`) });
+    res.anchors[age] = {
+      ...snap, boards,
+      bandOk: snap.band === EXPECT_BAND[age],
+      pass: snap.band === EXPECT_BAND[age] && snap.on === true && boards > 0,
+    };
+  }
+  res.bridge = hasPostMsgBridge;
+  res.pass = boards > 0 && hasPostMsgBridge && res.errors.length === 0 &&
+             Object.values(res.anchors).every(a => a.pass);
+  await browser.close();
+  return res;
+}
+
+(async () => {
+  const results = [];
+  for (const [nm, eng] of [['webkit', pw.webkit], ['firefox', pw.firefox]]) {
+    try { results.push(await runEngine(nm, eng)); }
+    catch (e) { results.push({ engine: nm, anchors: {}, errors: ['LAUNCH: ' + e.message.split('\n')[0]], pass: false, skipped: true }); }
+  }
+
+  console.log('\n================ PHASE 5.3 CROSS-ENGINE ================');
+  for (const r of results) {
+    console.log(`\n[${r.engine}] boards=${r.anchors[12]?.boards} bridge=${r.bridge} errors=${r.errors.length} → ${r.pass ? 'PASS' : 'FAIL'}`);
+    for (const age of ANCHORS) {
+      const a = r.anchors[age];
+      console.log(`   age ${String(age).padStart(2)}: band=${a.band} (expect ${EXPECT_BAND[age]}) on=${a.on} ${a.pass ? '✓' : '✗'}`);
+    }
+    if (r.errors.length) r.errors.slice(0, 8).forEach(e => console.log('   ERR', e));
+  }
+  const overall = results.every(r => r.pass);
+  fs.writeFileSync(path.join(OUT, '3x-result.json'), JSON.stringify(results, null, 2));
+  console.log('\n  OVERALL CROSS-ENGINE:', overall ? '✅ PASS' : '❌ FAIL');
+  process.exit(overall ? 0 : 1);
+})().catch(e => { console.error('3X CRASH:', e); process.exit(2); });

← d84af5b Phase 5 senior-usability HARD gate: measured verifier + 3 ca  ·  back to Quadrille Showroom  ·  Phase 5.5 /contrarian final pass: prove the AVATAR half + fi 04948ab →