← back to Quadrille Showroom
scripts/test-2min.js
71 lines
#!/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); });