The off-screen-layout bug (nodes centred on the scrollable width instead of the viewport) only reproduced at the default window size, so a single-width test would have shipped it. Verifies the graph stays framed at 1920/1400/1100/820, and records which assumption in the pixel census is safe: the measurement uses the canvas buffer size, not the screen size, because the CSS min-width makes narrow viewports scroll.
76 lines
3.2 KiB
JavaScript
76 lines
3.2 KiB
JavaScript
// The off-screen-layout bug only showed at the default width. Prove the fix
|
|
// holds across viewport sizes: the graph must stay framed at every size, and
|
|
// zooming/panning must keep the whole graph reachable.
|
|
const { chromium } = require('/tmp/gltest/node_modules/playwright-core');
|
|
|
|
const BASE = process.env.PANEL_URL || 'https://panel.attackdefense.imrnes.team';
|
|
const SIZES = [
|
|
{ w: 1920, h: 1080, label: 'desktop-wide' },
|
|
{ w: 1400, h: 900, label: 'laptop' },
|
|
{ w: 1100, h: 800, label: 'narrow' },
|
|
{ w: 820, h: 900, label: 'below-canvas-min' },
|
|
];
|
|
|
|
(async () => {
|
|
const browser = await chromium.launch({
|
|
args: ['--no-sandbox', '--use-gl=swiftshader', '--enable-unsafe-swiftshader'],
|
|
});
|
|
const creds = require('fs').readFileSync('/opt/gemastik18-final/panel/.env', 'utf8')
|
|
.split('\n').reduce((a, l) => {
|
|
const m = l.match(/^PANEL_ADMIN_(USER|PASS)=(.*)$/);
|
|
if (m) a[m[1]] = m[2];
|
|
return a;
|
|
}, {});
|
|
|
|
let failures = 0;
|
|
for (const s of SIZES) {
|
|
const page = await browser.newPage({ viewport: { width: s.w, height: s.h } });
|
|
const errs = [];
|
|
page.on('pageerror', e => errs.push(e.message));
|
|
await page.goto(BASE + '/login', { waitUntil: 'domcontentloaded' });
|
|
await page.fill('#u', creds.USER);
|
|
await page.fill('#p', creds.PASS);
|
|
await page.click('#f button[type=submit]');
|
|
await page.waitForSelector('#view-challs.active');
|
|
await page.click('.tab[data-view=topo]');
|
|
await page.waitForSelector('#topoHost canvas');
|
|
await page.waitForTimeout(2200);
|
|
|
|
const r = await page.evaluate(() => {
|
|
const g = window.__topoProbe();
|
|
const rd = g.app.renderer;
|
|
rd.render(g.app.stage);
|
|
const W = rd.width, H = rd.height;
|
|
const buf = new Uint8Array(W * H * 4);
|
|
rd.gl.readPixels(0, 0, W, H, rd.gl.RGBA, rd.gl.UNSIGNED_BYTE, buf);
|
|
let nonBg = 0, minX = W, minY = H, maxX = -1, maxY = -1;
|
|
for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) {
|
|
const i = (y * W + x) * 4;
|
|
if (Math.max(Math.abs(buf[i]-10), Math.abs(buf[i+1]-15), Math.abs(buf[i+2]-28)) > 3) {
|
|
nonBg++;
|
|
if (x < minX) minX = x; if (x > maxX) maxX = x;
|
|
if (y < minY) minY = y; if (y > maxY) maxY = y;
|
|
}
|
|
}
|
|
// A viewport narrower than the CSS min-width scrolls; the visible part of
|
|
// the canvas is what matters, so measure the canvas buffer, not the screen.
|
|
return {
|
|
W, H, pct: +(100 * nonBg / (W * H)).toFixed(2),
|
|
bbox: maxX < 0 ? null : [minX, minY, maxX, maxY],
|
|
inFrame: maxX < 0 ? false : (minX >= 0 && minY >= 0 && maxX <= W && maxY <= H),
|
|
scale: +g.scale.toFixed(3),
|
|
nodes: g.nodeViews.size,
|
|
glErr: rd.gl.getError(),
|
|
};
|
|
});
|
|
|
|
const ok = r.pct > 1 && r.inFrame && r.glErr === 0 && r.nodes > 0 && errs.length === 0;
|
|
if (!ok) failures++;
|
|
console.log(` ${ok ? 'PASS' : 'FAIL'} ${s.label.padEnd(20)} canvas ${r.W}x${r.H} drawn ${String(r.pct).padStart(5)}% scale ${r.scale} bbox ${JSON.stringify(r.bbox)}${errs.length ? ' ERR ' + errs[0] : ''}`);
|
|
await page.close();
|
|
}
|
|
await browser.close();
|
|
console.log(`\n ${failures === 0 ? 'PASS' : 'FAIL'} — ${SIZES.length - failures}/${SIZES.length} viewports framed correctly`);
|
|
process.exit(failures === 0 ? 0 : 1);
|
|
})();
|