Files
Investidor-V2/tools/portal-consumers-visual-qa.cjs
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

490 lines
19 KiB
JavaScript

const {
start,
BASE: _BASE,
DIR,
path,
assert,
sanitized,
} = require('./portal-visual-qa-lib.cjs');
void (async () => {
const q = await start('consumers-mobile-accessibility-chromium-20261010');
const resume = process.env.INVESTMOZ_VISUAL_RESUME === 'remaining';
q.proof.coverage = resume ? q.prior.coverage || [] : [];
q.proof.accessibility = resume ? q.prior.accessibility || [] : [];
q.proof.prior_evidence_started_at = resume ? q.prior.started_at : null;
q.proof.land_id =
q.prior.land_id ||
q.prior.events
?.findLast(
(e) =>
e.method === 'POST' &&
e.path === '/investidor/terra-duat' &&
e.redirect,
)
?.redirect.split('/')
.pop();
q.proof.business_id = q.prior.business_id;
let page, ctx;
const axePath = path.join(DIR, 'qa-a11y-toolkit/package/axe.min.js');
async function audit(route, width) {
await page.locator('h1').first().waitFor();
await page.addScriptTag({ path: axePath });
const scan = await page.evaluate(async () => {
const r = await window.axe.run(document, {
runOnly: {
type: 'tag',
values: [
'wcag2a',
'wcag2aa',
'wcag21a',
'wcag21aa',
'best-practice',
],
},
});
return {
version: r.testEngine.version,
passes: r.passes.length,
incomplete: r.incomplete.map((v) => ({
id: v.id,
targets: v.nodes.map((n) => n.target),
})),
violations: r.violations.map((v) => ({
id: v.id,
impact: v.impact,
help: v.help,
nodes: v.nodes.map((n) => ({
target: n.target,
failureSummary: n.failureSummary,
})),
})),
};
});
const layout = await page.evaluate(() => ({
viewport: innerWidth,
scrollWidth: document.documentElement.scrollWidth,
language: document.documentElement.lang,
mainLandmarks:
document.querySelectorAll('main,[role="main"]').length,
}));
const row = { route, width, ...scan, layout };
q.proof.accessibility.push(row);
await q.shot(page, route.replace(/[^a-zA-Z0-9]/g, '_') + '-' + width);
q.save();
return row;
}
async function countryCoverage(route) {
const response = await q.open(page, route);
await page.locator('h1').first().waitFor();
const selects = await page.locator('select').evaluateAll((nodes) =>
nodes
.filter(
(n) =>
Array.from(n.options).some((o) => o.value === 'MZ') &&
Array.from(n.options).some((o) => o.value === 'PT'),
)
.map((n) => ({
label:
n.getAttribute('aria-label') ||
n.labels?.[0]?.textContent?.trim() ||
'',
id: n.id,
options: n.options.length,
current: n.value,
})),
);
q.proof.coverage.push({
route,
status: response.status(),
country_selectors: selects,
persistence_verified: false,
});
q.save();
return selects;
}
try {
if (!resume) {
q.phase(
'Public mobile/reflow and accessibility, Financial Market private',
);
({ page, ctx } = await q.context('anonymous', 390));
for (const route of [
'/explorar/agronegocio',
'/agronegocio',
'/explorar/energia-mineracao',
'/turismo',
'/infraestruturas',
'/oportunidades?view=map',
]) {
await q.open(page, route);
await audit(route, 390);
}
await page.setViewportSize({ width: 320, height: 844 });
for (const route of ['/agronegocio', '/oportunidades?view=map']) {
await q.open(page, route);
await audit(route, 320);
}
for (const route of [
'/mercado-financeiro',
'/explorar/mercado-financeiro',
])
assert.equal((await q.open(page, route)).status(), 404);
q.proof.checks.financial_market_not_public = true;
await ctx.close();
q.phase(
'QA18 country/profile persistence, land and submission consumers',
);
({ page, ctx } = await q.context('investor18', 390));
await q.login(page, 'investor18');
await countryCoverage('/investidor/profile');
const residence = page.locator('#residence_country_codeaccount');
const before = await residence.inputValue();
await residence.selectOption('PT');
const save = page.waitForResponse(
(r) =>
r.request().method() === 'PUT' &&
new URL(r.url()).pathname === '/investidor/profile',
);
await page
.getByRole('button', {
name: 'Guardar perfil de investimento',
exact: true,
})
.click();
assert([200, 302, 303].includes((await save).status()));
await page.reload({ waitUntil: 'domcontentloaded' });
assert.equal(await residence.inputValue(), 'PT');
q.proof.coverage.at(-1).persistence_verified = true;
q.proof.coverage.at(-1).before = before;
await audit('/investidor/profile', 390);
await countryCoverage(
q.proof.land_id
? '/investidor/terra-duat/' + q.proof.land_id + '/editar'
: '/investidor/terra-duat/novo',
);
await page
.getByRole('combobox', {
name: 'País, se aplicável',
exact: true,
})
.selectOption('PT');
await page
.getByRole('combobox', {
name: 'País de origem declarado da solicitação (opcional)',
exact: true,
})
.selectOption('FR');
const landSave = page.waitForResponse(
(r) =>
['POST', 'PUT'].includes(r.request().method()) &&
new URL(r.url()).pathname.startsWith(
'/investidor/terra-duat',
),
);
await page
.getByRole('button', { name: 'Guardar rascunho', exact: true })
.click();
assert([200, 302, 303].includes((await landSave).status()));
if (!q.proof.land_id) {
await page.waitForURL(
/\/investidor\/terra-duat\/[0-9a-f-]{36}(?:\/editar)?$/,
);
q.proof.land_id = new URL(page.url()).pathname.split('/')[3];
q.save();
}
await q.open(
page,
'/investidor/terra-duat/' + q.proof.land_id + '/editar',
);
assert.equal(
await page
.getByRole('combobox', {
name: 'País, se aplicável',
exact: true,
})
.inputValue(),
'PT',
);
assert.equal(
await page
.getByRole('combobox', {
name: 'País de origem declarado da solicitação (opcional)',
exact: true,
})
.inputValue(),
'FR',
);
q.proof.coverage.at(-1).persistence_verified = true;
await audit(
'/investidor/terra-duat/' + q.proof.land_id + '/editar',
390,
);
await countryCoverage('/investidor/negocios');
if (!q.proof.business_id) {
await page
.getByLabel('Título privado', { exact: true })
.fill('QA países negócio — ' + q.stamp);
await page
.getByRole('button', {
name: 'Criar rascunho privado',
exact: true,
})
.click();
await page.waitForURL(/\/investidor\/negocios\/[0-9a-f-]{36}$/);
q.proof.business_id = new URL(page.url()).pathname
.split('/')
.pop();
q.save();
} else
await q.open(
page,
'/investidor/negocios/' + q.proof.business_id,
);
await page
.getByRole('combobox', {
name: 'País de origem ou constituição',
exact: true,
})
.selectOption('PT');
await page
.getByRole('combobox', {
name: 'País de origem declarado desta solicitação (opcional)',
exact: true,
})
.selectOption('FR');
await page
.getByRole('button', { name: 'Guardar rascunho', exact: true })
.click();
await page.reload({ waitUntil: 'domcontentloaded' });
assert.equal(
await page
.getByRole('combobox', {
name: 'País de origem ou constituição',
exact: true,
})
.inputValue(),
'PT',
);
assert.equal(
await page
.getByRole('combobox', {
name: 'País de origem declarado desta solicitação (opcional)',
exact: true,
})
.inputValue(),
'FR',
);
q.proof.coverage.push({
route: '/investidor/negocios/' + q.proof.business_id,
country: 'PT',
origin: 'FR',
persistence_verified: true,
});
await audit('/investidor/negocios/' + q.proof.business_id, 390);
for (const route of [
'/formularios/investidor',
'/formularios/empresa',
'/manifestar-interesse',
'/apoio',
]) {
await countryCoverage(route);
await audit(route, 390);
}
} else {
q.phase(
'Resume remaining mobile tool/maps/statistics; preserve prior consumer evidence',
);
({ page, ctx } = await q.context('investor18', 390));
await q.login(page, 'investor18');
q.proof.checks.financial_market_not_public = true;
}
const ab = require(
path.join(DIR, 'investor-ab-chromium-20261010.json'),
);
assert.equal(ab.result, 'passed');
for (const [name, e] of Object.entries(ab.scenarios)) {
await q.open(page, '/investimentos/' + e.id);
await page
.getByRole('heading', { name: e.title, exact: true })
.waitFor();
await audit('/investimentos/' + e.id, 390);
await page
.getByRole('link', { name: 'Plano e simulador', exact: true })
.click();
await page.waitForURL(
(u) => u.searchParams.get('context') === e.id,
);
await page
.getByText('Dossier privado recuperado da sua conta.', {
exact: true,
})
.waitFor();
await page
.getByRole('navigation', { name: 'Etapas do formulário' })
.getByRole('button', { name: /Estrutura financeira/ })
.click();
await page
.getByRole('region', {
name: 'Ferramentas de planeamento financeiro',
})
.waitFor();
await audit('/simulador-fiscal?context=' + name, 390);
q.proof.coverage.push({
route: '/investimentos/' + e.id,
consumer: 'mobile dossier and contextual tool',
scenario: name,
context_preserved:
new URL(page.url()).searchParams.get('context') === e.id,
persistence_verified: true,
});
}
await q.logout(page);
await ctx.close();
q.phase('Promoter dashboard, canonical territorial consumer');
({ page, ctx } = await q.context('promoter', 390));
await q.login(page, 'promoter');
await q.open(page, '/dashboard');
await audit('/dashboard', 390);
await q.open(page, '/minhas-oportunidades/30/edit');
await audit('/minhas-oportunidades/30/edit', 390);
const cartography = page.getByRole('region', {
name: 'Cobertura cartográfica da seleção',
});
if (await cartography.count()) {
await cartography
.first()
.getByText(
'Limite administrativo disponível para este território.',
{ exact: true },
)
.waitFor();
await cartography
.locator('.leaflet-overlay-pane svg path')
.first()
.waitFor({ state: 'attached' });
const polygons = await cartography
.locator('.leaflet-overlay-pane svg path')
.count();
q.proof.coverage.push({
route: '/minhas-oportunidades/30/edit',
consumer: 'selected canonical location',
territory_registered: true,
geometry_available: true,
rendered_polygon_paths: polygons,
basemap:
'external tiles deliberately blocked; fallback separate',
persistence_verified: false,
});
assert(polygons > 0);
await q.shot(page, 'canonical-map-390');
}
await q.logout(page);
await ctx.close();
q.phase(
'Manager country filters, per-origin statistics and map consumers',
);
({ page, ctx } = await q.context('manager', 1440));
await q.login(page, 'manager');
await q.open(page, '/management/interests');
await page
.getByLabel('Filtrar origem declarada', { exact: true })
.selectOption('ZA');
await page
.getByRole('button', { name: 'Pesquisar', exact: true })
.click();
await page.waitForURL(
(u) =>
u.pathname === '/management/interests' &&
u.searchParams.get('origin') === 'ZA',
);
await page
.getByText('Estatísticas de solicitações por origem declarada', {
exact: true,
})
.click();
await page
.getByRole('heading', { name: 'Pedidos do Balcão', exact: true })
.waitFor();
q.proof.coverage.push({
route: '/management/interests?origin=ZA',
consumer: 'country filter/statistics',
ui_filter: 'ZA',
counts: await page.locator('details').last().innerText(),
persistence_verified: false,
drafts_exclusion:
'UI describes exclusion; new A/B drafts not counted as submission',
});
await audit('/management/interests?origin=ZA', 1440);
await q.open(page, '/management/crm/leads');
await page
.getByText('Solicitações por origem declarada', { exact: true })
.click();
await page
.getByLabel('Origem das estatísticas', { exact: true })
.selectOption('ZA');
await page
.getByRole('button', { name: 'Filtrar estatísticas', exact: true })
.click();
await page.waitForURL((u) => u.searchParams.get('origin') === 'ZA');
await audit('/management/crm/leads?origin=ZA', 1440);
for (const route of [
'/oportunidades?view=map',
'/management/projects/create',
'/management/clusters/create',
'/management/corridors/create',
]) {
const r = await q.open(page, route);
q.proof.coverage.push({
route,
status: r.status(),
territorial_selectors: await page.getByRole('combobox').count(),
rendered_polygons: await page
.locator('.leaflet-overlay-pane svg path')
.count(),
persistence_verified: false,
});
await audit(route, 1440);
}
await q.logout(page);
await ctx.close();
q.proof.checks.mobile_no_page_overflow = q.proof.accessibility.every(
(r) => r.layout.scrollWidth <= r.layout.viewport + 1,
);
q.proof.checks.automatic_accessibility_zero_violations =
q.proof.accessibility.every((r) => r.violations.length === 0);
q.proof.manual_assistive_technology_acceptance = false;
q.proof.result = 'completed_with_findings';
} 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,
coverage: q.proof.coverage.length,
accessibility_pages: q.proof.accessibility.length,
checks: q.proof.checks,
receipt: q.receipt,
}),
);
if (q.proof.result === 'failed') process.exitCode = 1;
}
})();