Files
gods-eye-view/scripts/qa-layer-panel.mjs
T
Sameh Khamis 7edf8a577e feat(globe): answer the clear and share actions through the globe actions store
The top-center bar is now GlobeActions.vue. The shell publishes whether a
clear is under way and answers the clear and share buttons through the
globe actions store, instead of binding listeners and writing attributes on
the buttons. A shell disposed mid-clear resets the busy state, so the next
shell's bar is not left busy. bindClearLayersControl stays exported and is
deprecated.
2026-11-01 13:23:51 -08:00

138 lines
4.6 KiB
JavaScript

#!/usr/bin/env node
/** Browser proof of Layers panel replacement and subscription ownership. */
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();
let failures = 0;
const errors = [];
page.on('pageerror', (error) => errors.push(error.message));
const check = (name, passed) => {
console.log(`[${passed ? 'PASS' : 'FAIL'}] ${name}`);
if (!passed) failures++;
};
try {
await page.goto(
`${process.env.QA_BASE_URL || 'http://localhost:4173'}/?welcome=0`,
{ waitUntil: 'domcontentloaded' },
);
await page.waitForFunction(() => window.__godsEyeView?.dataManager, {
timeout: 60000,
});
const results = await page.evaluate(async () => {
const manager = window.__godsEyeView.dataManager;
const entry = document.querySelector(
'script[type="module"][src*="/src/main.js"]',
);
const { application } = await import(entry.src);
const { presentation } = application.getComponents().data;
const container = presentation.panel._toggleContainer;
const id = 'qa-panel-lifecycle';
let listener = null;
let enabled = 0;
window.__gevQaRegisterLayer(manager, {
id,
name: '<b>Literal layer</b>',
icon: '◌',
source: 'Local fixture',
updateInterval: -1,
init() {},
enable() {
enabled++;
},
disable() {},
update() {},
destroy() {},
getStats: () => ({ count: 1250, lastUpdate: Date.now(), stale: true }),
getRowControls: () => ({ chips: [] }),
setRowControlsListener: (callback) => {
listener = callback;
},
});
const result = [];
try {
presentation.mount(container);
const row = () => container.querySelector(`[data-layer-id="${id}"]`);
const first = row().querySelector('.data-toggle-btn');
result.push([
'descriptor text is literal content',
row().querySelector('.data-name').textContent ===
'<b>Literal layer</b>' && !row().querySelector('b'),
]);
result.push([
'row subscription is installed',
typeof listener === 'function',
]);
presentation.mount(container);
first.click();
await Promise.resolve();
result.push([
'a detached row cannot issue an enable action',
enabled === 0 && !manager.isEnabled(id),
]);
await manager.setEnabled(id, true);
result.push([
'count is presented from the supplied snapshot',
row().querySelector('.data-count').textContent === '1.3K',
]);
result.push([
'feed state reflects the settled layer snapshot',
row().querySelector('.data-toggle-btn').dataset.feedState === 'stale',
]);
const ui = window.__godsEyeView.styleManager;
const clearButton = document.getElementById('clear-selected-layers');
clearButton.click();
// The bar renders the busy state on Vue's next flush.
await Promise.resolve();
result.push([
'native clear activation presents busy state',
clearButton.getAttribute('aria-busy') === 'true' &&
!!ui._contextControls._clearSelectedLayersPromise,
]);
await ui._contextControls._clearSelectedLayersPromise;
await Promise.resolve();
result.push([
'clear settles through the existing layer transaction',
!manager.isEnabled(id) &&
clearButton.getAttribute('aria-busy') === 'false',
]);
await manager.setEnabled(id, true);
const final = row().querySelector('.data-toggle-btn');
presentation._panel.destroy();
final.click();
await Promise.resolve();
result.push([
'destroyed panel releases row subscriptions',
listener === null,
]);
result.push([
'destroyed controls cannot change the layer',
manager.isEnabled(id),
]);
} finally {
presentation._panel?.destroy();
presentation._panel = null;
await window.__gevQaUnregisterLayer(manager, id);
presentation.mount(container);
}
result.push([
'ordinary layer rows return after reassembly',
container.querySelectorAll('.data-toggle-row').length > 10,
]);
return result;
});
for (const [name, passed] of results) check(name, passed);
check('no uncaught browser errors', errors.length === 0);
} finally {
await browser.close();
}
console.log(`RESULT: ${failures} failures`);
process.exitCode = failures ? 1 : 0;