← 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
A scripts/verify-ageview-3x.mjs
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 →