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

268 lines
10 KiB
JavaScript

const {
start,
BASE: _BASE,
DIR,
assert,
fs,
path,
sanitized,
} = require('./portal-visual-qa-lib.cjs');
void (async () => {
const q = await start('next-accessibility-chromium-20261010');
let page, _ctx;
try {
({ page, ctx: _ctx } = await q.context('promoter'));
await q.login(page, 'promoter');
q.proof.pages = [];
const fixture = JSON.parse(
fs.readFileSync(
path.join(DIR, 'next-organization-chromium-20261010.json'),
'utf8',
),
).fixture;
for (const route of [
'/investidor/profile',
'/investidor/manifestacoes/' + fixture.new_submission,
'/minhas-oportunidades/36/edit',
'/minhas-oportunidades/51/resources',
'/parcerias/salas/1',
]) {
q.phase('Keyboard, accessible names and states ' + route);
await q.open(page, route);
await page.locator('main').first().waitFor();
await page.waitForLoadState('networkidle');
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',
'wcag21aa',
'best-practice',
],
},
});
return {
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,
})),
})),
incomplete: r.incomplete.map((v) => ({
id: v.id,
help: v.help,
nodes: v.nodes.map((n) => n.target),
})),
passes: r.passes.length,
};
});
const item = { route, ...result, keyboard: [] };
q.proof.pages.push(item);
if (route.startsWith('/investidor/manifestacoes/')) {
for (const name of [
'País de residência',
'País de constituição da entidade',
'País de origem declarado desta solicitação',
])
assert.equal(
await page
.getByRole('combobox', { name, exact: true })
.count(),
1,
);
assert.equal(
await page
.getByRole('combobox', {
name: 'País de constituição da entidade',
exact: true,
})
.inputValue(),
'FR',
);
assert(
await page
.getByText('Esta cópia conserva a origem', {
exact: false,
})
.isVisible(),
);
item.countries_named_and_source_shown = true;
}
q.save();
await page.evaluate(() => {
document.activeElement?.blur();
window.scrollTo(0, 0);
});
let skip = false;
for (let i = 0; i < 35; i++) {
await page.keyboard.press('Tab');
const focused = await page.evaluate(() => {
const e = document.activeElement,
s = getComputedStyle(e);
return {
tag: e.tagName,
id: e.id,
role: e.getAttribute('role'),
name:
e.getAttribute('aria-label') ||
e.textContent?.trim().slice(0, 100) ||
e.getAttribute('name'),
href: e.getAttribute('href'),
outline: {
style: s.outlineStyle,
width: s.outlineWidth,
color: s.outlineColor,
},
visible: e.getBoundingClientRect().height > 0,
};
});
item.keyboard.push(focused);
if (focused.href === '#portal-content') {
skip = true;
await q.shot(page, 'skip-' + q.proof.pages.length, false);
await page.keyboard.press('Enter');
assert(
await page
.locator('#portal-content')
.evaluate((e) => document.activeElement === e),
);
item.skip_reaches_content = true;
break;
}
}
assert(skip, 'Keyboard reaches skip link');
await page.keyboard.press('Tab');
item.after_skip = await page.evaluate(() => ({
tag: document.activeElement.tagName,
id: document.activeElement.id,
}));
const menu = page.getByRole('button', {
name: /Explorar investimentos/,
});
if (await menu.isVisible()) {
await menu.focus();
await page.keyboard.press('Enter');
assert.equal(await menu.getAttribute('aria-expanded'), 'true');
await page.keyboard.press('Escape');
assert.equal(await menu.getAttribute('aria-expanded'), 'false');
assert(
await menu.evaluate((e) => document.activeElement === e),
);
item.menu_keyboard_escape_returns_focus = true;
}
item.states = await page.getByRole('status').allTextContents();
item.alerts = await page.getByRole('alert').allTextContents();
if (route === '/parcerias/salas/1') {
await page
.getByRole('heading', {
name: 'Documentos autorizados',
exact: true,
})
.scrollIntoViewIfNeeded();
item.document_state = await page
.getByRole('status', {
name: 'Documentos disponíveis nesta sala',
exact: true,
})
.textContent();
}
if (route === '/investidor/profile') {
const box = page.getByRole('region', {
name: 'Cadastro mestre: Synthetic QA organization',
exact: true,
}),
country = box.getByRole('combobox', {
name: 'País de constituição da organização',
exact: true,
});
await country.selectOption('');
await box
.getByLabel('Fonte ou referência deste país', {
exact: true,
})
.fill('');
const wait = page.waitForResponse(
(r) =>
r.request().method() === 'PUT' &&
new URL(r.url()).pathname.endsWith('/country'),
);
await box
.getByRole('button', {
name: 'Guardar país no cadastro mestre',
exact: true,
})
.click();
assert([302, 303].includes((await wait).status()));
await page.waitForFunction(
() =>
document.activeElement?.getAttribute('aria-invalid') ===
'true',
);
assert(
await country.evaluate((e) => document.activeElement === e),
);
item.validation = await country.evaluate((e) => ({
invalid: e.getAttribute('aria-invalid'),
focused: document.activeElement === e,
error: document.getElementById(
e.getAttribute('aria-describedby'),
)?.textContent,
}));
assert(item.validation.error?.includes('país de constituição'));
assert.equal(await box.getByRole('alert').count(), 2);
await q.shot(page, 'master-error-focus', false);
}
await q.shot(page, 'keyboard-' + q.proof.pages.length, false);
q.save();
}
q.proof.checks = {
keyboard_and_skip_routes: q.proof.pages.every(
(p) => p.skip_reaches_content,
),
automated_violations: q.proof.pages.reduce(
(n, p) => n + p.violations.length,
0,
),
screen_reader_not_tested: true,
real_devices_not_tested: true,
};
q.proof.result = q.proof.checks.automated_violations
? 'violations_found'
: 'passed';
} catch (e) {
q.proof.result = 'failed';
q.proof.error = sanitized(e);
if (
page &&
!['/login', '/two-factor-challenge'].includes(
new URL(page.url()).pathname,
)
)
await q.shot(page, 'failure', false).catch(() => {});
} finally {
q.proof.completed_at = new Date().toISOString();
q.save();
await q.browser.close();
}
console.log(
JSON.stringify({
result: q.proof.result,
checks: q.proof.checks,
error: q.proof.error,
pages: q.proof.pages?.map((p) => ({
route: p.route,
violations: p.violations,
})),
}),
);
if (q.proof.result === 'failed') process.exitCode = 1;
})();