← back to Quadrille Showroom
Add 2-min soak-test recorder (walk/open/Back-Next/Peruse; logs console errors + FPS)
179338491a1b90be1f6da6d9575c01e0a2a25c4f · 2026-06-27 10:33:24 -0700 · Steve
Files touched
Diff
commit 179338491a1b90be1f6da6d9575c01e0a2a25c4f
Author: Steve <steve@designerwallcoverings.com>
Date: Sat Jun 27 10:33:24 2026 -0700
Add 2-min soak-test recorder (walk/open/Back-Next/Peruse; logs console errors + FPS)
---
scripts/test-2min.js | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/scripts/test-2min.js b/scripts/test-2min.js
new file mode 100644
index 0000000..4ffea68
--- /dev/null
+++ b/scripts/test-2min.js
@@ -0,0 +1,70 @@
+#!/usr/bin/env node
+/**
+ * test-2min.js — a ~2 minute soak test with screen recording. Exercises walk,
+ * open, Back/Next nav, and a long Peruse; captures console errors + periodic FPS.
+ * Writes recordings/test-2min.webm + recordings/test-2min-report.json.
+ */
+const path = require('path');
+const fs = require('fs');
+const { chromium } = require('playwright');
+
+const URL = process.env.SHOWROOM_URL || 'http://127.0.0.1:7690/';
+const OUT = path.join(__dirname, '..', 'recordings');
+fs.mkdirSync(OUT, { recursive: true });
+const sleep = (ms) => new Promise(r => setTimeout(r, ms));
+
+(async () => {
+ const browser = await chromium.launch({ args: ['--use-gl=angle', '--enable-webgl', '--ignore-gpu-blocklist'] });
+ const ctx = await browser.newContext({ viewport: { width: 1600, height: 900 }, recordVideo: { dir: OUT, size: { width: 1600, height: 900 } } });
+ const page = await ctx.newPage();
+ const errors = [], fpsLog = [];
+ page.on('console', m => { if (m.type() === 'error') errors.push(m.text()); });
+ page.on('pageerror', e => errors.push('PAGEERROR: ' + e.message));
+
+ await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 30000 });
+ await page.waitForFunction(() => window._wingBoards && window._wingBoards.length > 0, { timeout: 25000 }).catch(() => {});
+ await sleep(2500);
+
+ const sampleFps = async (tag) => {
+ const fps = await page.evaluate(() => { const el = document.getElementById('fps-counter'); return el ? parseInt(el.textContent) : null; });
+ fpsLog.push({ tag, fps }); return fps;
+ };
+
+ const start = Date.now();
+ let round = 0;
+ // ~120s loop of real interactions
+ while (Date.now() - start < 118000) {
+ round++;
+ // walk in
+ await page.keyboard.down('KeyW'); await sleep(1200); await page.keyboard.up('KeyW');
+ await sleep(800); await sampleFps('walk');
+ // click a board to open (isolated, centred)
+ await page.mouse.click(800, 430); await sleep(2200); await sampleFps('open');
+ // flip through with Next / Back
+ for (let i = 0; i < 4; i++) { await page.click('#btn-fan-right').catch(()=>{}); await sleep(1600); }
+ await page.click('#btn-fan-left').catch(()=>{}); await sleep(1500);
+ await sampleFps('nav');
+ await page.screenshot({ path: path.join(OUT, `t2-r${round}.png`) }).catch(()=>{});
+ // escape, peruse a stretch
+ await page.keyboard.press('Escape'); await sleep(800);
+ await page.keyboard.press('KeyP'); await sleep(11000); await sampleFps('peruse');
+ await page.keyboard.press('Escape'); await sleep(800);
+ }
+
+ const stats = await page.evaluate(() => (window._getSceneStats ? window._getSceneStats() : null));
+ const fpsVals = fpsLog.map(f => f.fps).filter(n => n > 0);
+ const report = {
+ when: new Date().toISOString(), rounds: round, durationSec: Math.round((Date.now() - start) / 1000),
+ errors, errorCount: errors.length,
+ fpsMin: Math.min(...fpsVals), fpsAvg: Math.round(fpsVals.reduce((a, b) => a + b, 0) / fpsVals.length), fpsSamples: fpsLog,
+ sceneStats: stats,
+ };
+ fs.writeFileSync(path.join(OUT, 'test-2min-report.json'), JSON.stringify(report, null, 2));
+ console.log('rounds', round, '| errors', errors.length, '| fps min/avg', report.fpsMin, '/', report.fpsAvg);
+ if (errors.length) console.log('ERRORS:', errors.slice(0, 8).join(' || '));
+
+ await page.close();
+ const vid = await page.video().path().catch(() => null);
+ await ctx.close(); await browser.close();
+ if (vid && fs.existsSync(vid)) { fs.renameSync(vid, path.join(OUT, 'test-2min.webm')); console.log('✓ test-2min.webm'); }
+})().catch(e => { console.error('TEST FAILED:', e.message); process.exit(1); });
← 6e2efdd Usable view: 6ft back + dead-centre + 65° open + ISOLATE (hi
·
back to Quadrille Showroom
·
Photoreal room (SDXL warm-oak floor + limewash plaster walls e7268c6 →