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

369 lines
14 KiB
JavaScript

const {
start,
BASE,
DIR,
path,
assert,
sanitized,
} = require('./portal-visual-qa-lib.cjs');
void (async () => {
const q = await start('visual-followup-chromium-20261010');
q.proof.accessibility = [];
q.proof.maps = [];
q.proof.country_statistics = [];
q.proof.qa18_authentication_requests = 0;
q.proof.opportunity = q.prior.opportunity || {
title: 'QA integração territorial — ' + q.stamp,
};
let page, ctx;
async function audit(route, width) {
await page.getByRole('heading').first().waitFor();
await page.addScriptTag({
path: path.join(DIR, 'qa-a11y-toolkit/package/axe.min.js'),
});
const scan = await page.evaluate(async () => {
const r = await axe.run(document, {
runOnly: {
type: 'tag',
values: [
'wcag2a',
'wcag2aa',
'wcag21a',
'wcag21aa',
'best-practice',
],
},
});
return {
passes: r.passes.length,
incomplete: r.incomplete.map((v) => v.id),
violations: r.violations.map((v) => ({
id: v.id,
impact: v.impact,
targets: v.nodes.map((n) => n.target),
})),
layout: {
viewport: innerWidth,
scrollWidth: document.documentElement.scrollWidth,
language: document.documentElement.lang,
mainLandmarks:
document.querySelectorAll('main,[role=main]').length,
},
};
});
q.proof.accessibility.push({ route, width, ...scan });
await q.shot(page, route.replace(/\W/g, '_') + '-' + width);
q.save();
}
async function choice(label, text) {
const r = page.getByRole('combobox', {
name: new RegExp(
'^' +
label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
'(?: \\(obrigatório\\))?$',
),
});
await r.fill(text);
await page
.getByRole('option')
.filter({ hasText: text })
.first()
.click();
}
async function map(route, endpoint) {
const select = page.getByRole('combobox', {
name: 'Província',
exact: true,
});
await select.locator('option').nth(1).waitFor({ state: 'attached' });
const options = await select
.locator('option')
.evaluateAll((ns) =>
ns
.filter((n) => n.value)
.map((n) => ({ id: n.value, name: n.textContent })),
);
let selected = options[0],
context;
for (const o of options) {
const r = await q.json(page, endpoint + '?territory_id=' + o.id);
assert.equal(r.status, 200);
if (!context || r.data.selected_geometry_available) {
selected = o;
context = r.data;
}
if (r.data.selected_geometry_available) break;
}
await select.selectOption(selected.id);
const box = page
.getByRole('region', { name: 'Cobertura cartográfica da seleção' })
.first();
await box
.getByText(
/Limite administrativo disponível|Sem limite administrativo acessível/,
)
.waitFor();
if (context.areas.length)
await box
.locator('.leaflet-overlay-pane svg path')
.first()
.waitFor({ state: 'attached' });
const rendered = await box
.locator('.leaflet-overlay-pane svg path')
.count();
assert(!context.areas.length || rendered > 0);
q.proof.maps.push({
route,
selected,
territory_registered: true,
geometry_available_to_profile: context.selected_geometry_available,
context_geometry_count: context.context_geometry_count,
rendered_polygon_paths: rendered,
external_tiles: 'blocked in isolated browser; not tested',
persisted: false,
});
q.save();
return selected;
}
try {
q.phase(
'Public keyboard/reflow/accessibility after corrections, no QA18 login',
);
({ page, ctx } = await q.context('anonymous', 390));
q.proof.keyboard = [];
for (const route of [
'/agronegocio',
'/turismo',
'/manifestar-interesse',
]) {
await q.open(page, route);
await page.getByRole('heading').first().waitFor();
await page.keyboard.press('Tab');
assert.equal(
await page.evaluate(() =>
document.activeElement.getAttribute('href'),
),
'#portal-content',
);
await page.keyboard.press('Enter');
assert.equal(
await page.evaluate(() => document.activeElement.id),
'portal-content',
);
q.proof.keyboard.push({
route,
skip_link_first: true,
target_reached: true,
});
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);
await ctx.close();
q.phase(
'QA21 mobile dashboard/dossier/tool keyboard and opportunity resource map',
);
({ page, ctx } = await q.context('promoter', 390));
await q.login(page, 'promoter');
await audit('/dashboard', 390);
await q.open(page, '/investimentos');
await page.locator('a[href^="/investimentos/"]').first().click();
await page.waitForURL(/\/investimentos\/[0-9a-f-]{36}$/);
q.proof.mobile_case_id = new URL(page.url()).pathname.split('/').pop();
await audit('/investimentos/' + q.proof.mobile_case_id, 390);
await page
.getByRole('link', { name: 'Plano e simulador', exact: true })
.click();
await page
.locator(
'section[aria-labelledby="simulator-step-title"][aria-busy="false"]',
)
.waitFor();
await choice(
'O que pretende calcular ou planear? Escolha o percurso',
'Avaliar o meu investimento',
);
await choice('Nível de detalhe', 'Simples');
await choice('O que pretende fazer?', 'Apenas simular');
await choice('Idioma preferido', 'Português (Moçambique)');
const nav = page.getByRole('navigation', {
name: 'Etapas do formulário',
});
await nav.getByRole('button', { name: /Projecto ou negócio/ }).click();
await page
.getByLabel('Título do projecto', { exact: true })
.fill('QA acessibilidade do plano contextual');
await page
.getByLabel('Resumo executivo', { exact: true })
.fill(
'Ensaio QA de navegação por teclado; nenhuma decisão externa.',
);
await nav.getByRole('button', { name: /Estrutura financeira/ }).click();
const financial = page.getByRole('region', {
name: 'Ferramentas de planeamento financeiro',
});
await choice('Moeda', 'MZN');
await financial.getByLabel('Capital', { exact: true }).fill('100000');
await financial
.getByLabel('Taxa anual (%)', { exact: true })
.fill('12');
await financial.getByLabel('Prazo (meses)', { exact: true }).fill('12');
await audit('/simulador-fiscal?context=QA21', 390);
for (const label of [
'Plano de reembolso indicativo',
'Tabela do fluxo de caixa',
]) {
const box = page.getByRole('region', { name: label, exact: true });
await box.focus();
assert.equal(await box.getAttribute('tabindex'), '0');
await page.keyboard.press('ArrowDown');
}
q.proof.financial_tables_keyboard_focus = true;
await q.open(page, '/dashboard');
await page
.getByRole('link', { name: 'Nova oportunidade', exact: true })
.first()
.click();
await page.waitForURL(BASE + '/minhas-oportunidades/create');
const e = q.proof.opportunity;
if (!e.id) {
await page
.getByLabel('Título oficial', { exact: true })
.fill(e.title);
await page
.getByLabel('Motivo da alteração', { exact: true })
.fill(
'Ensaio QA específico dos seletores e mapas por consumidor.',
);
await page
.getByRole('button', { name: 'Guardar rascunho', exact: true })
.click();
await page.waitForURL(/\/minhas-oportunidades\/\d+\/edit$/);
e.id = Number(new URL(page.url()).pathname.split('/')[2]);
q.save();
} else await q.open(page, '/minhas-oportunidades/' + e.id + '/edit');
await page
.getByRole('link', {
name: 'Imagens, documentos e localizações',
exact: true,
})
.click();
await page.waitForURL(
BASE + '/minhas-oportunidades/' + e.id + '/resources',
);
await map(
new URL(page.url()).pathname,
'/minhas-oportunidades/territory-geometries',
);
await audit(new URL(page.url()).pathname, 390);
assert(
!(await page
.getByText('ClamD real pendente;', { exact: false })
.count()),
);
await q.logout(page);
await ctx.close();
q.phase(
'Management territorial consumers and visible country statistics',
);
({ page, ctx } = await q.context('manager', 1440));
await q.login(page, 'manager');
for (const route of [
'/management/projects/create',
'/management/clusters/create',
'/management/corridors/create',
]) {
assert.equal((await q.open(page, route)).status(), 200);
await map(route, '/management/territory-geometries');
await audit(route, 1440);
}
for (const origin of ['ZA', 'unknown']) {
await q.open(page, '/management/interests');
await page
.getByLabel('Filtrar origem declarada', { exact: true })
.selectOption(origin);
await page
.getByRole('button', { name: 'Pesquisar', exact: true })
.click();
await page.waitForURL(
(u) => u.searchParams.get('origin') === origin,
);
await page
.getByText(
'Estatísticas de solicitações por origem declarada',
{ exact: true },
)
.click();
q.proof.country_statistics.push({
route: '/management/interests?origin=' + origin,
origin,
rendered: await page.locator('details').last().innerText(),
});
await audit('/management/interests?origin=' + origin, 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('unknown');
await page
.getByRole('button', { name: 'Filtrar estatísticas', exact: true })
.click();
await page.waitForURL(
(u) => u.searchParams.get('origin') === 'unknown',
);
await audit('/management/crm/leads?origin=unknown', 1440);
await q.logout(page);
await ctx.close();
q.proof.checks.no_page_overflow = q.proof.accessibility.every(
(r) => r.layout.scrollWidth <= r.width + 1,
);
q.proof.checks.no_automatic_accessibility_violations =
q.proof.accessibility.every((r) => !r.violations.length);
q.proof.checks.portuguese_language = q.proof.accessibility.every(
(r) => r.layout.language === 'pt-MZ',
);
q.proof.manual_screen_reader_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,
checks: q.proof.checks,
receipt: q.receipt,
}),
);
if (q.proof.result === 'failed') process.exitCode = 1;
}
})();