// 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); if (m.error) p.reject(m.error); else 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); }