175 lines
7.0 KiB
JavaScript
175 lines
7.0 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);
|
|
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);
|
|
}
|