mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
124 lines
3.8 KiB
JavaScript
124 lines
3.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/** Exercise the real UI disposal path, including unrelated resource owners. */
|
|
import puppeteer from 'puppeteer';
|
|
const browser = await puppeteer.launch({
|
|
headless: true,
|
|
args: [
|
|
'--no-sandbox',
|
|
...(process.platform === 'darwin'
|
|
? ['--use-angle=metal', '--enable-gpu']
|
|
: ['--use-gl=angle', '--use-angle=swiftshader']),
|
|
],
|
|
});
|
|
const page = await browser.newPage();
|
|
const errors = [];
|
|
page.on('pageerror', (error) => errors.push(error.message));
|
|
let failures = 0;
|
|
function check(name, ok) {
|
|
console.log(`[${ok ? 'PASS' : 'FAIL'}] ${name}`);
|
|
if (!ok) failures++;
|
|
}
|
|
try {
|
|
await page.goto(
|
|
`${process.env.QA_BASE_URL || 'http://localhost:4173'}/?welcome=0`,
|
|
{ waitUntil: 'domcontentloaded' },
|
|
);
|
|
await page.waitForFunction(
|
|
() =>
|
|
window.__godsEyeView?.styleManager?._dataManager &&
|
|
document.getElementById('loading-screen')?.classList.contains('hidden'),
|
|
{ timeout: 60000 },
|
|
);
|
|
const result = await page.evaluate(async () => {
|
|
const ui = window.__godsEyeView.styleManager;
|
|
const counts = {};
|
|
const watchObserver = (name) => {
|
|
const observer = ui[name];
|
|
if (!observer) return false;
|
|
const disconnect = observer.disconnect.bind(observer);
|
|
counts[name] = 0;
|
|
observer.disconnect = () => {
|
|
counts[name]++;
|
|
return disconnect();
|
|
};
|
|
return true;
|
|
};
|
|
const observed = [
|
|
'_commandDockTrayObserver',
|
|
'_draggableResizeObserver',
|
|
'_leftStackResizeObserver',
|
|
'_rightStackResizeObserver',
|
|
'_leftStackMutationObserver',
|
|
'_rightStackMutationObserver',
|
|
].filter(watchObserver);
|
|
const cctvUnsubscribe = ui._cctvUnsubscribe;
|
|
counts.cctv = 0;
|
|
if (cctvUnsubscribe)
|
|
ui._cctvUnsubscribe = () => {
|
|
counts.cctv++;
|
|
return cctvUnsubscribe();
|
|
};
|
|
const resizeHandler = ui._windowResizeHandler;
|
|
const removeEventListener = window.removeEventListener;
|
|
counts.resize = 0;
|
|
window.removeEventListener = function (type, callback, options) {
|
|
if (type === 'resize' && callback === resizeHandler) counts.resize++;
|
|
return removeEventListener.call(this, type, callback, options);
|
|
};
|
|
try {
|
|
await ui.dispose();
|
|
const once = JSON.stringify(counts);
|
|
await ui.dispose();
|
|
window.dispatchEvent(new Event('resize'));
|
|
await new Promise((resolve) =>
|
|
requestAnimationFrame(() => requestAnimationFrame(resolve)),
|
|
);
|
|
return {
|
|
connected: Boolean(
|
|
cctvUnsubscribe &&
|
|
resizeHandler &&
|
|
observed.includes('_commandDockTrayObserver'),
|
|
),
|
|
observersReleased: observed.every(
|
|
(name) => counts[name] === 1 && ui[name] === null,
|
|
),
|
|
subscriptionReleased: counts.cctv === 1 && ui._cctvUnsubscribe === null,
|
|
resizeReleased: counts.resize === 1 && ui._windowResizeHandler === null,
|
|
controlsReleased:
|
|
ui._radioControls.destroyed && ui._locationControls.destroyed,
|
|
idempotent: once === JSON.stringify(counts),
|
|
};
|
|
} finally {
|
|
window.removeEventListener = removeEventListener;
|
|
}
|
|
});
|
|
check(
|
|
'real UI has the expected live resources before disposal',
|
|
result.connected,
|
|
);
|
|
check(
|
|
'UI disposal disconnects each active panel observer once',
|
|
result.observersReleased,
|
|
);
|
|
check(
|
|
'UI disposal releases the CCTV state subscription',
|
|
result.subscriptionReleased,
|
|
);
|
|
check(
|
|
'UI disposal removes the registered window resize handler',
|
|
result.resizeReleased,
|
|
);
|
|
check(
|
|
'Radio and Location controls are disposed with the UI',
|
|
result.controlsReleased,
|
|
);
|
|
check('repeated UI disposal is inert', result.idempotent);
|
|
check(
|
|
'disposal and subsequent resize produce no uncaught browser errors',
|
|
errors.length === 0,
|
|
);
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
if (failures) process.exitCode = 1;
|