Files
gods-eye-view/scripts/qa-layer-visibility.mjs

125 lines
4.1 KiB
JavaScript

#!/usr/bin/env node
/**
* Real-browser proof for manager-owned periodic work and document visibility.
* Uses the app's development-only layer registration seam; no provider keys.
* Run: node scripts/qa-layer-visibility.mjs --url http://localhost:4173
* Hiding uses a real background tab, never a patched document.hidden getter.
*/
import assert from 'node:assert/strict';
import puppeteer from 'puppeteer';
import { chromeExecutablePath } from './qa-chrome.mjs';
const args = process.argv.slice(2);
const urlIndex = args.indexOf('--url');
const url = new URL(
(urlIndex >= 0 ? args[urlIndex + 1] : undefined) ||
process.env.QA_BASE_URL ||
'http://localhost:4173',
);
url.searchParams.set('welcome', '0');
const browser = await puppeteer.launch({
executablePath: chromeExecutablePath(),
headless: true,
args: [
'--no-sandbox',
'--disable-background-timer-throttling',
'--disable-renderer-backgrounding',
],
});
const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const check = (name, actual, expected) => {
assert.equal(actual, expected, name);
console.log(`PASS ${name}: ${actual}`);
};
try {
const page = await browser.newPage();
const focus = await page.createCDPSession();
await focus.send('Emulation.setFocusEmulationEnabled', { enabled: true });
await page.goto(url.href, { waitUntil: 'domcontentloaded' });
await page.waitForFunction(
() => window.__gevQaRegisterLayer && window.__godsEyeView?.dataManager,
);
await page.evaluate(async () => {
const manager = window.__godsEyeView.dataManager;
window.__qaVisibilityUpdates = 0;
window.__gevQaRegisterLayer(manager, {
id: 'qa-document-visibility',
name: 'Visibility QA',
updateInterval: 750,
init() {},
enable() {},
disable() {},
destroy() {},
async update() {
window.__qaVisibilityUpdates++;
return true;
},
getStats() {
return { count: window.__qaVisibilityUpdates };
},
});
await manager.setEnabled('qa-document-visibility', true, { origin: 'qa' });
});
const count = () => page.evaluate(() => window.__qaVisibilityUpdates);
await page.waitForFunction(() => window.__qaVisibilityUpdates >= 3);
check('periodic updates run while visible', (await count()) >= 3, true);
await focus.send('Emulation.setFocusEmulationEnabled', { enabled: false });
const cover = await browser.newPage();
await cover.bringToFront();
await page.waitForFunction(() => document.visibilityState === 'hidden');
const hiddenCount = await count();
await pause(2200);
check('hidden tab makes no periodic updates', await count(), hiddenCount);
check(
'explicit refresh remains available while hidden',
await page.evaluate(() =>
window.__godsEyeView.dataManager.refreshLayer('qa-document-visibility'),
),
true,
);
check('explicit hidden refresh runs once', await count(), hiddenCount + 1);
await focus.send('Emulation.setFocusEmulationEnabled', { enabled: true });
await page.waitForFunction(
() =>
document.visibilityState === 'visible' &&
window.__qaVisibilityUpdates > 0,
);
await pause(200);
check(
'resume performs one overdue refresh without a catch-up burst',
await count(),
hiddenCount + 2,
);
await page.evaluate(() =>
window.__godsEyeView.dataManager.setEnabled(
'qa-document-visibility',
false,
{ origin: 'qa' },
),
);
const stopped = await count();
await focus.send('Emulation.setFocusEmulationEnabled', { enabled: false });
await cover.bringToFront();
await page.waitForFunction(() => document.visibilityState === 'hidden');
await focus.send('Emulation.setFocusEmulationEnabled', { enabled: true });
await pause(1100);
check(
'disabled layer does not resume on visibility change',
await count(),
stopped,
);
check(
'QA layer destroys cleanly',
await page.evaluate(() =>
window.__gevQaUnregisterLayer(
window.__godsEyeView.dataManager,
'qa-document-visibility',
),
),
true,
);
console.log('RESULT: 7 passed');
} finally {
await browser.close();
}