93 lines
6.3 KiB
JavaScript
93 lines
6.3 KiB
JavaScript
// Isolated QA only. Synthetic account is prepared separately, never against the application DB.
|
|
import fs from 'node:fs';
|
|
const base = 'http://127.0.0.1:18027';
|
|
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 seq = 0;
|
|
const pending = new Map();
|
|
const errors = [];
|
|
ws.onmessage = event => {
|
|
const m = JSON.parse(event.data);
|
|
if (m.method === 'Runtime.exceptionThrown') errors.push(m.params.exceptionDetails.text);
|
|
if (m.id) { const p = pending.get(m.id); pending.delete(m.id); m.error ? p.reject(m.error) : p.resolve(m.result); }
|
|
};
|
|
const send = (method, params = {}) => new Promise((resolve, reject) => { const id = ++seq; pending.set(id, { resolve, reject }); ws.send(JSON.stringify({ id, method, params })); });
|
|
const evaluate = async expression => {
|
|
const r = await send('Runtime.evaluate', { expression, returnByValue: true, awaitPromise: true });
|
|
if (r.exceptionDetails) throw Error('Browser expression failed');
|
|
return r.result?.value;
|
|
};
|
|
const wait = async expression => { for (let n = 0; n < 100; n++) { if (await evaluate(expression)) return; await new Promise(r => setTimeout(r, 100)); } throw Error('Browser condition timed out: ' + expression); };
|
|
const fill = async (selector, value) => {
|
|
await evaluate(`document.querySelector(${JSON.stringify(selector)}).focus()`);
|
|
await send('Input.insertText', { text: value });
|
|
};
|
|
const field = async (label, value) => {
|
|
const selector = `([...document.querySelectorAll('label')].find(l=>l.textContent.includes(${JSON.stringify(label)}))?.querySelector('input,textarea,select'))`;
|
|
await wait(`!!${selector}`);
|
|
await evaluate(`(()=>{const e=${selector}; e.focus(); if(e.tagName==='SELECT'){ e.value=${JSON.stringify(value)}; e.dispatchEvent(new Event('change',{bubbles:true})); }else {e.select();}})()`);
|
|
if (!await evaluate(`${selector}.tagName==='SELECT'`)) await send('Input.insertText', { text: value });
|
|
};
|
|
const step = async number => {
|
|
await wait(`![...document.querySelectorAll('ol button')][${number - 1}].disabled`);
|
|
await evaluate(`[...document.querySelectorAll('ol button')][${number - 1}].click()`);
|
|
await wait(`document.querySelector('h2')?.textContent.startsWith('${number}/8')`);
|
|
};
|
|
const observations = [];
|
|
try {
|
|
await send('Page.enable'); await send('Runtime.enable');
|
|
await send('Emulation.setDeviceMetricsOverride', { width: 390, height: 900, deviceScaleFactor: 1, mobile: true });
|
|
await send('Page.navigate', { url: base + '/login' });
|
|
await wait(`document.readyState==='complete' && (location.pathname!='/login' || !!document.querySelector('input[type="password"]'))`);
|
|
if (await evaluate(`location.pathname==='/login'`)) {
|
|
await fill('input[type="email"]', 'mne-browser@example.test');
|
|
await fill('input[type="password"]', 'SyntheticMneOnly-2026!');
|
|
await evaluate(`document.querySelector('button[type="submit"]').click()`);
|
|
await wait(`!location.pathname.startsWith('/login')`);
|
|
}
|
|
await send('Page.navigate', { url: base + '/investidor/negocios' });
|
|
await wait(`document.body.innerText.includes('Preparar negócio')`);
|
|
await fill('main form input', 'Synthetic wizard browser');
|
|
await evaluate(`document.querySelector('main form button').click()`);
|
|
await wait(`document.querySelector('h2')?.textContent.startsWith('1/8')`);
|
|
await field('Tipo de proponente', 'person');
|
|
await field('Nome ou denominação', 'Synthetic owner');
|
|
await field('NUIT', 'SYNTHETIC');
|
|
await field('Email de contacto', 'synthetic@example.test');
|
|
await field('País', 'MZ');
|
|
await wait(`document.body.innerText.includes('Rascunho guardado no servidor')`);
|
|
await send('Page.reload');
|
|
await wait(`!![...document.querySelectorAll('input')].find(e=>e.value==='Synthetic owner')`);
|
|
observations.push({ scenario: 'autosave_and_reload', passed: true });
|
|
await step(2);
|
|
await field('Qualidade', 'holder'); await field('Titular declarado', 'Synthetic owner'); await field('Declaro legitimidade', 'yes');
|
|
await step(3);
|
|
await evaluate(`(()=>{const e=[...document.querySelectorAll('label')].find(l=>l.querySelector('span')?.textContent==='Sector')?.querySelector('select'); e.value=e.options[1].value;e.dispatchEvent(new Event('change',{bubbles:true}));})()`);
|
|
await field('Descrição reservada','Synthetic private description long enough for this browser-only review.');
|
|
await evaluate(`(()=>{const e=document.querySelector('input[type=date]');const setter=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set;setter.call(e,'2027-12-31');e.dispatchEvent(new Event('input',{bubbles:true}));e.dispatchEvent(new Event('change',{bubbles:true}));})()`);
|
|
await step(5); await field('Localização declarada','Synthetic location, no coordinates');
|
|
await step(7); await field('Referência da fonte','Synthetic source for browser testing');
|
|
await step(8);
|
|
await wait(`document.body.innerText.includes('Rascunho guardado no servidor')`);
|
|
const code = await evaluate(`document.querySelector('main').innerText.match(/MNE-[0-9a-f-]{36}/)[0]`);
|
|
await field('Digite o código',code);
|
|
await evaluate(`document.querySelector('input[type=checkbox]').click()`);
|
|
for (const width of [390,1440]) {
|
|
await send('Emulation.setDeviceMetricsOverride',{width,height:900,deviceScaleFactor:1,mobile:width===390});
|
|
const fits = await evaluate(`document.documentElement.scrollWidth<=innerWidth+1`);
|
|
if (!fits) throw Error('Horizontal overflow');
|
|
observations.push({scenario:'responsive_summary',width,passed:true});
|
|
}
|
|
await evaluate(`[...document.querySelectorAll('button')].find(e=>e.textContent.includes('Confirmar e submeter')).click()`);
|
|
await wait(`document.body.innerText.includes('Submetido') && !document.querySelector('progress')`);
|
|
observations.push({scenario:'private_submission',passed:true});
|
|
if(errors.length) throw Error('Browser exceptions');
|
|
fs.writeFileSync('docs/investmoz/existing-business/wizard-browser-validation.json',JSON.stringify({passed:true,observations,errors},null,2)+'\n');
|
|
console.log(JSON.stringify({passed:true,observations,errors}));
|
|
} finally {
|
|
ws.close();
|
|
await fetch('http://127.0.0.1:9224/json/close/'+target.id);
|
|
}
|
|
|