82 lines
3.1 KiB
JavaScript
82 lines
3.1 KiB
JavaScript
// Headless-chromium CDP driver for the jigsaw initial-image test page.
|
|
// Loads the page repeatedly; each load must end PASS, and the initial image
|
|
// index (document.__initIndex) must vary across fresh loads.
|
|
// Usage: node tools/__jig_init_driver.mjs <url> [loads=8]
|
|
import { spawn } from 'node:child_process';
|
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
|
|
const url = process.argv[2];
|
|
const loads = Math.max(2, parseInt(process.argv[3] || '8', 10));
|
|
if (!url) { console.error('usage: driver <url> [loads]'); process.exit(2); }
|
|
|
|
const BIN = process.env.HOME + '/.cache/ms-playwright/chromium_headless_shell-1234/chrome-headless-shell-linux64/chrome-headless-shell';
|
|
const PORT = 9333;
|
|
const chrome = spawn(BIN, [
|
|
'--headless', '--no-sandbox', '--disable-gpu',
|
|
`--remote-debugging-port=${PORT}`,
|
|
'about:blank',
|
|
], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
chrome.stderr.on('data', (d) => { const s = d.toString(); if (!/Fontconfig|dbus|DBus|ozone/i.test(s)) process.stderr.write(s); });
|
|
|
|
async function httpJson(path) {
|
|
const r = await fetch(`http://127.0.0.1:${PORT}${path}`);
|
|
return r.json();
|
|
}
|
|
|
|
let ws, idc = 0;
|
|
const pending = new Map();
|
|
const send = (method, params = {}) => new Promise((resolve, reject) => {
|
|
const id = ++idc;
|
|
pending.set(id, { resolve, reject });
|
|
ws.send(JSON.stringify({ id, method, params }));
|
|
});
|
|
|
|
try {
|
|
let targets = null;
|
|
for (let i = 0; i < 60; i++) {
|
|
try { targets = await httpJson('/json/list'); break; } catch { await sleep(250); }
|
|
}
|
|
if (!targets) throw new Error('chrome devtools endpoint never came up');
|
|
const page = targets.find((t) => t.type === 'page');
|
|
if (!page) throw new Error('no page target');
|
|
ws = new WebSocket(page.webSocketDebuggerUrl);
|
|
await new Promise((res, rej) => { ws.onopen = res; ws.onerror = rej; });
|
|
ws.onmessage = (m) => {
|
|
const msg = JSON.parse(m.data);
|
|
if (msg.id && pending.has(msg.id)) {
|
|
const { resolve, reject } = pending.get(msg.id);
|
|
pending.delete(msg.id);
|
|
if (msg.error) reject(new Error(msg.error.message)); else resolve(msg.result);
|
|
}
|
|
};
|
|
|
|
await send('Runtime.enable');
|
|
await send('Page.enable');
|
|
|
|
const ev = (expression) => send('Runtime.evaluate', { expression, returnByValue: true }).then((r) => r.result.value);
|
|
|
|
const indexes = [];
|
|
for (let k = 0; k < loads; k++) {
|
|
await send('Page.navigate', { url });
|
|
const t0 = Date.now();
|
|
let title = '';
|
|
while (Date.now() - t0 < 60000) {
|
|
title = (await ev('document.title')) || '';
|
|
if (title === 'PASS' || title.startsWith('FAIL')) break;
|
|
await sleep(500);
|
|
}
|
|
if (title !== 'PASS') throw new Error(`load #${k}: ${title || 'TIMEOUT'}`);
|
|
indexes.push(await ev('document.__initIndex'));
|
|
}
|
|
|
|
const valid = indexes.every((i) => Number.isInteger(i) && i >= 0 && i < 5);
|
|
const distinct = new Set(indexes).size;
|
|
console.log('initial indexes across fresh loads:', indexes.join(', '));
|
|
console.log(`all valid: ${valid}, distinct images: ${distinct}/${loads}`);
|
|
if (!valid || distinct < 2) { console.log('FAIL'); process.exitCode = 1; }
|
|
else console.log('ALL PASS');
|
|
} finally {
|
|
try { ws && ws.close(); } catch {}
|
|
chrome.kill('SIGKILL');
|
|
}
|