Files
attack-defense-platform/panel/test_topo_browser.js
T

97 lines
4.8 KiB
JavaScript
Raw Normal View History

2026-09-27 00:31:47 +08:00
// Real-browser test of the PixiJS topology: boot the panel, log in, open the
// Topology tab, and assert a live WebGL canvas actually rendered the graph.
// A JS-parse check cannot catch "init() rejected" or "canvas sized to 0".
const { chromium } = require('/tmp/gltest/node_modules/playwright-core');
(async () => {
const browser = await chromium.launch({
args: ['--no-sandbox', '--use-gl=swiftshader', '--enable-unsafe-swiftshader'],
});
const page = await browser.newPage({ viewport: { width: 1400, height: 900 } });
const errors = [], logs = [];
page.on('pageerror', e => errors.push('pageerror: ' + e.message));
page.on('console', m => { if (m.type() === 'error') errors.push('console: ' + m.text()); else logs.push(m.text()); });
page.on('requestfailed', r => errors.push(`requestfailed: ${r.url()} ${r.failure()?.errorText}`));
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;
}, {});
await page.goto('https://panel.attackdefense.imrnes.team/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', { timeout: 20000 });
// Open the topology tab.
await page.click('.tab[data-view=topo]');
await page.waitForSelector('#topoHost canvas', { timeout: 30000 });
await page.waitForTimeout(4000);
const res = await page.evaluate(async () => {
// The inline script's top-level `let topoGraph` is not a window property,
// so read it through the page's deliberate debug hook.
const g = window.__topoProbe ? window.__topoProbe() : null;
const host = document.getElementById('topoHost');
const cv = host && host.querySelector('canvas');
return {
hasCanvas: !!cv,
canvasW: cv ? cv.width : 0,
canvasH: cv ? cv.height : 0,
cssW: cv ? cv.getBoundingClientRect().width : 0,
cssH: cv ? cv.getBoundingClientRect().height : 0,
pixiLoaded: !!(g && g.PIXI),
nodeViews: g ? g.nodeViews.size : 0,
attackViews: g ? g.attackViews.size : 0,
edgeChildren: g && g.edgeLayer ? g.edgeLayer.children.length : 0,
nodeChildren: g && g.nodeLayer ? g.nodeLayer.children.length : 0,
scale: g ? g.scale : null,
engine: document.getElementById('topoEngine')?.textContent || '',
// Are pixels actually drawn, or an empty background?
zoomLabel: document.getElementById('topoZoomLabel')?.textContent,
};
});
console.log('--- topology render state ---');
for (const [k, v] of Object.entries(res)) console.log(` ${k}: ${v}`);
// Zoom controls must reach the Pixi world transform.
await page.click('#view-topo button:has-text("Zoom In")');
await page.waitForTimeout(400);
const zoomed = await page.evaluate(() => ({ scale: window.__topoProbe().scale, label: document.getElementById('topoZoomLabel').textContent }));
console.log(' after zoom-in ->', JSON.stringify(zoomed));
await page.click('#view-topo button:has-text("\u27f3")');
await page.waitForTimeout(300);
// Reset now means "frame the whole graph in the viewport", not "scale=1": with
// 37 nodes the graph is wider than the canvas, so the fit scale is < 1. Assert
// the real contract — that reset returns to the framed scale and the view
// transform is centred — rather than a magic 1 that only held for small graphs.
const before = await page.evaluate(() => window.__topoProbe().scale);
await page.click('#view-topo button:has-text("\u27f3")');
await page.waitForTimeout(400);
const after = await page.evaluate(() => ({
scale: window.__topoProbe().scale,
panX: window.__topoProbe().panX,
label: document.getElementById('topoZoomLabel').textContent,
}));
console.log(' after zoom -> scale', before.toFixed(3));
console.log(' after reset -> scale', after.scale.toFixed(3), 'label', after.label);
if (Math.abs(after.scale - before) > 0.001) {
console.log(` FAIL: reset did not restore the framed scale (${after.scale} vs ${before})`);
process.exitCode = 1;
} else {
console.log(' PASS: reset restored the framed scale');
}
await page.screenshot({ path: '/tmp/topo_pixi.png' });
console.log(' screenshot -> /tmp/topo_pixi.png');
if (errors.length) { console.log('\n--- ERRORS ---'); errors.slice(0, 12).forEach(e => console.log(' ' + e)); }
else console.log('\n no page errors');
const ok = res.hasCanvas && res.canvasW > 0 && res.canvasH > 0 && res.nodeViews > 0 && res.nodeChildren > 0;
console.log(`\n ${ok ? 'PASS' : 'FAIL'} — canvas ${res.canvasW}x${res.canvasH}, ${res.nodeViews} nodes, ${res.edgeChildren} edge children`);
await browser.close();
process.exit(ok && errors.length === 0 ? 0 : 1);
})();