490 lines
19 KiB
JavaScript
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;
|
|
}
|
|
})();
|