369 lines
14 KiB
JavaScript
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;
|
|
}
|
|
})();
|