← 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();