← back to Quadrille Showroom
scripts/verify-qh6.mjs
79 lines
import pw from '/Users/macstudio3/.npm-global/lib/node_modules/playwright/index.js';
const { chromium } = pw;
const errors = [];
const browser = await chromium.launch({ channel: 'chrome', headless: true, args: ['--use-angle=metal', '--ignore-gpu-blocklist', '--window-size=1440,900'] });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
page.on('pageerror', e => errors.push('pageerror: ' + e.message));
page.on('console', m => { if (m.type() === 'error') errors.push('console.error: ' + m.text()); });
// Clean slate — clear any saved room-type/view so we test true first-boot.
await page.addInitScript(() => { try { localStorage.clear(); } catch (e) {} });
await page.goto('http://localhost:7690/', { waitUntil: 'domcontentloaded' });
await page.waitForFunction(() => window._qh && window._qh.wingBoards && window._qh.wingBoards.length >= 50, { timeout: 25000 });
await page.waitForTimeout(4000); // let boot settle
const out = {};
// (a) ONLOAD: only table+chair + empty room, no room-type dressing
out.boot = await page.evaluate(() => {
const qh = window._qh, scene = qh.scene;
const roomTypeGroups = scene.children.filter(c => (c.name || '').startsWith('roomType:')).map(c => c.name);
const furnitureRoots = scene.children.filter(c => c.name === 'furnitureRoot').length;
return {
currentRoomType: qh.currentRoomType,
roomTypeGroups,
furnitureRoots,
furnitureChildCount: qh.furnitureChildCount,
furnitureStyle: qh.furnitureStyle
};
});
// Screenshots per style at DEFAULT camera (dead-ahead rest pose).
const styles = ['contemporary', 'traditional', 'transitional', 'brutalist'];
for (const s of styles) {
await page.evaluate((k) => window._qh.swapFurniture(k, { includeExtras: false }), s);
await page.waitForTimeout(700);
// reset to canonical rest pose so all 4 shots are the same framing
await page.evaluate(() => { if (window._qh.setSpin) window._qh.setSpin(0, -0.08); });
await page.waitForTimeout(500);
await page.screenshot({ path: `/tmp/qh6-${s}.png` });
}
// (c) all 4 swap + (d) exactly 1 furnitureRoot after 10 swaps (leak test)
const order = ['contemporary', 'traditional', 'transitional', 'brutalist'];
let swapOK = true;
for (let i = 0; i < 10; i++) {
const k = order[i % order.length];
const res = await page.evaluate((key) => {
window._qh.swapFurniture(key, { includeExtras: false });
return { style: window._qh.furnitureStyle, present: window._qh.furniturePresent, childCount: window._qh.furnitureChildCount };
}, k);
if (res.style !== k || !res.present || res.childCount < 1) swapOK = false;
await page.waitForTimeout(120);
}
out.leak = await page.evaluate(() => {
const scene = window._qh.scene;
return { furnitureRootCount: scene.children.filter(c => c.name === 'furnitureRoot').length, style: window._qh.furnitureStyle };
});
out.swapAll4OK = swapOK;
// (f) "At the Table" seated view still works
out.tableView = await page.evaluate(async () => {
try {
window._viewmode.set('table');
return { set: true };
} catch (e) { return { set: false, err: String(e) }; }
});
await page.waitForTimeout(1500);
out.tableViewAfter = await page.evaluate(() => {
const cam = window._qh.camera.position;
return { camY: +cam.y.toFixed(3), camZ: +cam.z.toFixed(3), errorsSoFar: 0 };
});
await page.screenshot({ path: '/tmp/qh6-tableview.png' });
out.errors = errors;
console.log(JSON.stringify(out, null, 2));
await browser.close();