// Countries/origin per remaining consumer. Synthetic public intakes only; no notifications or external workers are invoked. const { start, BASE, DIR, fs, path, assert, sanitized, } = require('./portal-visual-qa-lib.cjs'); void (async () => { const q = await start( process.argv.includes('--search-only') ? 'country-search-chromium-20261010' : 'country-paths-chromium-20261010', ); q.proof.forms = q.prior.forms || {}; q.proof.recovery = []; let page, ctx, management; async function props(response) { const html = await response.text(), encoded = html.match(/data-page="([^"]+)"/); if (encoded && encoded[1].startsWith('{')) return JSON.parse( encoded[1] .replace(/"/g, '"') .replace(/&/g, '&') .replace(/'/g, "'"), ).props; const script = html.match( /]*type="application\/json"[^>]*>([\s\S]*?)<\/script>/, ); assert(script); return JSON.parse(script[1]).props; } async function countStats(p) { return ( p.originStatistics.interests.find((r) => r.code === 'ZA')?.count || 0 ); } async function audit(route) { await page.addScriptTag({ path: path.join(DIR, 'qa-a11y-toolkit/package/axe.min.js'), }); const result = await page.evaluate(async () => { const r = await axe.run(document, { runOnly: { type: 'tag', values: [ 'wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice', ], }, }); return { violations: r.violations.map((v) => ({ id: v.id, targets: v.nodes.map((n) => n.target), })), viewport: innerWidth, scrollWidth: document.documentElement.scrollWidth, }; }); (q.proof.accessibility ||= []).push({ route, ...result }); q.save(); assert.equal(result.violations.length, 0); assert(result.scrollWidth <= result.viewport + 1); } try { const routes = [ '/formularios/investidor', '/formularios/empresa', '/apoio', '/manifestar-interesse', ]; if (process.argv.includes('--search-only')) { q.phase( 'Remaining country-search criteria only; no submissions or authentication', ); ({ page, ctx } = await q.context('anonymous-country-search', 390)); q.proof.search = []; for (const route of routes) { await q.open(page, route); await page.getByRole('heading').first().waitFor(); const country = page.getByRole('combobox', { name: 'País do requerente', exact: true, }); const origin = page.getByRole('combobox', { name: 'País de origem declarado desta solicitação', exact: true, }); await page .getByRole('textbox', { name: 'Pesquisar país do requerente', exact: true, }) .fill('Portugal'); assert.equal(await country.locator('option').count(), 2); await country.selectOption('PT'); await page .getByRole('textbox', { name: 'Pesquisar país do requerente', exact: true, }) .fill('França'); assert.equal(await country.inputValue(), 'PT'); assert.equal( await country.locator('option[value="FR"]').count(), 1, ); await page .getByRole('textbox', { name: 'Pesquisar país de origem declarado desta solicitação', exact: true, }) .fill('África do Sul'); assert.equal(await origin.locator('option').count(), 2); await origin.selectOption('ZA'); assert.equal(await country.inputValue(), 'PT'); assert.equal(await origin.inputValue(), 'ZA'); q.proof.search.push({ route, country_query: 'Portugal', country_result: 'PT', changed_query: 'França', previous_selection_preserved: 'PT', origin_query: 'África do Sul', origin_result: 'ZA', }); q.save(); } q.proof.business_data_writes = 0; q.proof.result = 'passed_read_only_four_country_searches'; } else if (process.argv.includes('--probe-labels')) { q.phase( 'Read-only accessible names for country and declared origin before correction', ); ({ page, ctx } = await q.context('anonymous-country-labels', 390)); q.proof.labels = []; for (const route of routes) { await q.open(page, route); await page.getByRole('heading').first().waitFor(); const controls = await page .locator('select') .evaluateAll((nodes) => nodes .filter((n) => Array.from(n.options).some( (o) => o.value === 'PT', ), ) .map((n) => ({ accessible_label: n.getAttribute('aria-label'), options: n.options.length, visual_label: n .closest('label') ?.textContent?.trim() .slice(0, 150), })), ); q.proof.labels.push({ route, controls }); q.save(); } q.proof.result = 'diagnosed_C89_indistinguishable_country_names'; } else { q.phase( 'Management baseline for positive declared-origin statistics', ); management = await q.context('manager-country-stats', 1440); await q.login(management.page, 'manager'); const baseline = await props( await q.open( management.page, '/management/interests?origin=ZA', ), ); q.proof.baseline_known_origin = q.prior.baseline_known_origin ?? (await countStats(baseline)); q.save(); ({ page, ctx } = await q.context( 'anonymous-country-submissions', 390, )); for (const [i, route] of routes.entries()) { const entry = (q.proof.forms[route] ||= { name: 'QA países origem formulário ' + i + ' 2026-10-10', email: 'qa-ab-countries-' + i + '-20261010@example.invalid', country: 'PT', origin: 'ZA', }); // The public API requires a DNS mail domain. Invalid/reserved no-mail domains are correctly rejected; QA outgoing notifications are disabled. if (route === '/manifestar-interesse' && !entry.reference) { entry.rejected_synthetic_email = entry.email.endsWith( '.invalid', ) ? entry.email : entry.rejected_synthetic_email; entry.email = 'qa-investmoz-countries-' + i + '-20261010-9bd17fc0482e@gmail.com'; } if (entry.reference) continue; // If a prior attempt saved before capturing its confirmation, recover by this exact synthetic identity before considering another submission. const existing = await props( await q.open( management.page, '/management/interests?q=' + encodeURIComponent(entry.email), ), ); const found = existing.requests.data.find( (r) => r.email === entry.email, ); if (found) { entry.reference = found.reference; entry.id = found.id; entry.recovered_existing_attempt = true; q.save(); continue; } q.phase( 'Public form ' + route + ': country/origin -> submission -> visible protocol', ); await q.open(page, route); await page.getByRole('heading').first().waitFor(); const form = page.locator('form').filter({ has: page.getByRole('button', { name: route === '/manifestar-interesse' ? 'Enviar manifestação de interesse' : 'Enviar e obter protocolo', exact: true, }), }); await form .getByLabel( route === '/manifestar-interesse' ? /^Nome completo/ : /^Nome do representante/, ) .fill(entry.name); await form.getByLabel(/^Email/).fill(entry.email); const country = form.getByRole('combobox', { name: 'País do requerente', exact: true, }); const origin = form.getByRole('combobox', { name: 'País de origem declarado desta solicitação', exact: true, }); assert.equal(await country.locator('option').count(), 250); await country.selectOption('PT'); await origin.selectOption('ZA'); if (route === '/manifestar-interesse') { await form .getByLabel(/^Tipo de investidor/) .selectOption('individual'); await form .getByLabel(/^Sector de interesse/) .selectOption({ index: 1 }); await form .getByLabel(/^Mensagem/) .fill( 'Ensaio sintético QA de países e origem declarada; nenhum pedido de comunicação externa.', ); } else { await form .getByLabel('Sector ou assunto', { exact: true }) .fill( 'Ensaio sintético QA de cobertura por formulário', ); if (route === '/formularios/empresa') await form .getByLabel('Nome da empresa', { exact: true }) .fill('Empresa sintética QA países 20261010'); await form .getByLabel('Descreva o pedido', { exact: true }) .fill( 'Ensaio sintético QA de países e origem declarada; nenhum pedido de comunicação externa.', ); } await form.getByRole('checkbox').check(); await q.shot(page, 'form-' + i + '-country-origin-390'); await audit(route); const posted = page.waitForResponse( (r) => r.request().method() === 'POST' && new URL(r.url()).pathname === (route === '/manifestar-interesse' ? '/api/v1/interest-requests' : '/apoio'), ); await form .getByRole('button', { name: route === '/manifestar-interesse' ? 'Enviar manifestação de interesse' : 'Enviar e obter protocolo', exact: true, }) .click(); const response = await posted; if (![200, 201, 302, 303].includes(response.status())) { q.proof.rejected_form = { route, status: response.status(), validation: await response.json().catch(() => null), }; q.save(); throw Error( 'Public form rejected; inspect sanitized validation evidence', ); } if (route === '/manifestar-interesse') entry.reference = (await response.json()).data.reference; else { await page .getByText(/Pedido registado\. Protocolo:/) .waitFor(); entry.reference = ( await page .getByText(/Pedido registado\. Protocolo:/) .innerText() ).match(/AP-\d{4}-[A-Z0-9]+/)[0]; } await page .getByText(new RegExp(entry.reference)) .first() .waitFor(); entry.submitted_via_ui = true; entry.post_status = response.status(); q.save(); await q.shot(page, 'form-' + i + '-protocol'); } await ctx.close(); q.phase( 'Recovery by authorized management, country/origin filters and positive statistics', ); ({ page, ctx } = management); for (const [_route, entry] of Object.entries(q.proof.forms)) { const result = await props( await q.open( page, '/management/interests?q=' + encodeURIComponent(entry.reference) + '&country=PT&origin=ZA', ), ); assert.equal(result.requests.data.length, 1); const row = result.requests.data[0]; assert.equal(row.email, entry.email); assert.equal(row.declaredOrigin.code, 'ZA'); entry.id = row.id; entry.filtered_recovery = true; const link = page.getByRole('link', { name: entry.reference, exact: true, }); await link.click(); await page.waitForURL(BASE + '/management/interests/' + row.id); await page .getByText('África do Sul (ZA)', { exact: true }) .waitFor(); await page.getByText('PT', { exact: true }).waitFor(); entry.management_detail_presented = true; await q.shot(page, 'detail-' + row.id); q.save(); } const positive = await props( await q.open( page, '/management/interests?country=PT&origin=ZA', ), ); q.proof.known_origin_after = await countStats(positive); assert.equal( q.proof.known_origin_after, q.proof.baseline_known_origin + 4, ); await page .getByText( 'Estatísticas de solicitações por origem declarada', { exact: true }, ) .click(); await page .getByText('África do Sul: ' + q.proof.known_origin_after, { exact: true, }) .waitFor(); await q.shot(page, 'management-positive-origin-statistics'); await page .getByRole('combobox', { name: 'Filtrar país do requerente', exact: true, }) .selectOption('ZA'); await page .getByRole('button', { name: 'Pesquisar', exact: true }) .click(); await page.waitForURL( (u) => u.searchParams.get('country') === 'ZA', ); for (const entry of Object.values(q.proof.forms)) assert.equal( await page .getByRole('link', { name: entry.reference, exact: true, }) .count(), 0, ); q.proof.country_is_independent_from_declared_origin = true; await ctx.close(); q.phase( 'Fresh QA18 session: recover previous profile, business and land drafts without rewriting', ); ({ page, ctx } = await q.context( 'investor18-country-recovery', 390, )); await q.login(page, 'investor18'); const prior = JSON.parse( fs.readFileSync( path.join( DIR, 'consumers-mobile-accessibility-chromium-20261010.json', ), 'utf8', ), ); await q.open(page, '/investidor/profile'); assert.equal( await page .locator('#residence_country_codeaccount') .inputValue(), 'PT', ); q.proof.recovery.push({ route: '/investidor/profile', country: 'PT', fresh_session: true, business_data_writes: 0, }); for (const [route, countryName, originName] of [ [ '/investidor/negocios/' + prior.business_id, 'País de origem ou constituição', 'País de origem declarado desta solicitação (opcional)', ], [ '/investidor/terra-duat/' + prior.land_id + '/editar', 'País, se aplicável', 'País de origem declarado da solicitação (opcional)', ], ]) { await q.open(page, route); await page .getByRole('combobox', { name: countryName, exact: true }) .waitFor(); assert.equal( await page .getByRole('combobox', { name: countryName, exact: true, }) .inputValue(), 'PT', ); assert.equal( await page .getByRole('combobox', { name: originName, exact: true, }) .inputValue(), 'FR', ); q.proof.recovery.push({ route, country: 'PT', origin: 'FR', fresh_session: true, business_data_writes: 0, }); await q.shot( page, route.includes('/negocios/') ? 'business-fresh-session-390' : 'land-fresh-session-390', ); q.save(); } q.proof.checks = { four_public_forms_submitted_and_recovered: true, independent_country_origin_filters: true, positive_known_origin_statistics: true, previous_business_and_land_recovered_new_session: true, }; q.proof.external_dispatches = 0; q.proof.result = 'passed'; } } catch (e) { q.proof.result = 'failed'; q.proof.error = sanitized(e); if ( page && !['/login', '/two-factor-challenge'].includes( new URL(page.url()).pathname, ) ) { q.proof.failure_state = { path: new URL(page.url()).pathname, labels: await page.locator('label').allTextContents(), alerts: await page.getByRole('alert').allTextContents(), }; await q.shot(page, 'failure').catch(() => {}); } } finally { q.proof.finished_at = new Date().toISOString(); q.save(); await q.browser.close(); console.log( JSON.stringify({ result: q.proof.result, phase: q.proof.phase, checks: q.proof.checks, receipt: q.receipt, }), ); if (q.proof.result === 'failed') process.exitCode = 1; } })();