Files
Investidor-V2/tools/zones-browser-smoke.mjs
T
nala 250ac35fd1
tests / ci (push) Canceled after 0s
first commit
2026-09-28 10:01:09 +02:00

59 lines
4.1 KiB
JavaScript

// Read-only browser smoke test; reuse the dedicated QA Edge debugger on 9224.
import fs from 'node:fs';
const base = process.argv[2] ?? 'http://127.0.0.1:8000';
if (!/^http:\/\/127\.0\.0\.1:\d+$/.test(base)) throw Error('Local QA server required');
const target = await (await fetch('http://127.0.0.1:9224/json/new?about:blank', {method: 'PUT'})).json();
const ws = new WebSocket(target.webSocketDebuggerUrl);
await new Promise(resolve => ws.addEventListener('open', resolve, {once: true}));
let sequence = 0;
const pending = new Map();
const errors = [];
ws.onmessage = event => {
const message = JSON.parse(event.data);
if (message.method === 'Runtime.exceptionThrown') errors.push(message.params.exceptionDetails.text);
if (message.id) { const callback = pending.get(message.id); pending.delete(message.id); callback(message.error ? {error: message.error} : message.result); }
};
const send = (method, params = {}) => new Promise(resolve => { const id = ++sequence; pending.set(id, resolve); ws.send(JSON.stringify({id, method, params})); });
const evaluate = async expression => {
const result = await send('Runtime.evaluate', {expression, returnByValue: true});
if (result.exceptionDetails || result.error) throw Error(JSON.stringify(result));
return result.result?.value;
};
await send('Page.enable'); await send('Runtime.enable');
const observations = [];
try {
for (const width of [390, 1440]) {
await send('Emulation.setDeviceMetricsOverride', {width, height: 900, deviceScaleFactor: 1, mobile: width < 600});
for (const path of ['/zonas?category=zee', '/zonas?category=zfi', '/zonas?category=industrial_park', '/oportunidades', '/oportunidades?view=combined']) {
await send('Page.navigate', {url: base + path});
let ready = false;
for (let attempt = 0; attempt < 100; attempt++) {
await new Promise(resolve => setTimeout(resolve, 100));
ready = await evaluate(`document.readyState==='complete' && location.href===${JSON.stringify(base + path)} && !!document.querySelector('h1')`);
if (ready) break;
}
if (!ready) throw Error('Page not ready: ' + path);
const result = await evaluate(`({title:document.querySelector('h1').textContent,overflow:document.documentElement.scrollWidth>innerWidth+1,breadcrumb:!!document.querySelector('nav[aria-label="Caminho de navegação"]'),support:[...document.querySelectorAll('a')].some(a=>a.textContent.includes('Contactar Apoio')),dimensions:['Por Cluster','Por Corredor','Por ZEE','Por ZFI'].every(text=>[...document.querySelectorAll('a')].some(a=>a.textContent===text))})`);
if (result.overflow || !result.breadcrumb || !result.support || !result.dimensions) throw Error(JSON.stringify({path, width, ...result}));
observations.push({path, width, ...result});
}
}
await send('Network.enable');
await send('Network.setBlockedURLs', {urls: ['*api/v1/investment/map*']});
await send('Page.navigate', {url: base + '/oportunidades?view=map'});
let fallback = false;
for (let attempt = 0; attempt < 100; attempt++) {
await new Promise(resolve => setTimeout(resolve, 100));
fallback = await evaluate(`document.body.innerText.includes('Mapa indisponível') && [...document.querySelectorAll('article a')].some(a=>a.getAttribute('href')?.startsWith('/oportunidades/'))`);
if (fallback) break;
}
if (!fallback) throw Error('Map failure did not preserve investment list');
observations.push({scenario: 'map_request_blocked', fallback_list: true});
await send('Network.setBlockedURLs', {urls: []});
if (errors.length) throw Error('JavaScript errors: ' + errors.join('; '));
fs.writeFileSync('docs/investmoz/zones/browser-validation.json', JSON.stringify({date: new Date().toISOString(), scope: 'Public pages and empty states only; no institutional workflow/UAT claim', observations, errors}, null, 2));
console.log(JSON.stringify({passed: observations.length, javascript_errors: errors.length}));
} finally {
await send('Page.close'); ws.close();
}