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