Files
Investidor-V2/tools/investmoz-v2-navigation-browser.cjs
nala 1106509116
tests / ci (push) Canceled after 0s
vrsao 2
2026-10-08 14:02:01 +02:00

358 lines
14 KiB
JavaScript

const {
chromium,
} = require('C:/Users/macbook/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright');
const fs = require('node:fs');
const assert = require('node:assert/strict');
const { execFileSync } = require('node:child_process');
const base = 'http://127.0.0.1:18092';
const dir = 'storage/app/private/investmoz-v2';
(async () => {
const browser = await chromium.launch({
headless: true,
executablePath:
'C:/Users/macbook/AppData/Local/ms-playwright/chromium-1243/chrome-win64/chrome.exe',
});
const errors = [],
blocked = [],
checks = [];
async function context(width) {
const ctx = await browser.newContext({
viewport: { width, height: width < 640 ? 844 : 1000 },
hasTouch: width < 640,
});
await ctx.route('**/*', (r) => {
if (r.request().url().startsWith(base)) return r.continue();
blocked.push(r.request().url());
return r.abort();
});
const page = await ctx.newPage();
page.on('pageerror', (e) => errors.push(e.message));
return page;
}
async function visit(page, path) {
const r = await page.goto(base + path);
assert.equal(
r.headers()['x-investmoz-qa-target'],
'investmoz_v2_20261008_test:55441',
);
assert.ok(r.status() < 400, path + ':' + r.status());
await page.locator('nav[aria-label="Áreas do portal"]').waitFor();
assert.equal(
await page.locator('nav[aria-label="Áreas do portal"]').count(),
1,
);
await page.waitForLoadState('networkidle');
}
async function login(page, role) {
await visit(page, '/login');
await page
.locator('input[type=email]')
.fill(`guidance-qa-${role}@example.invalid`);
await page.locator('input[type=password]').fill('QA-Guidance-2026!');
await page.locator('button[type=submit]').click();
await page.waitForURL((url) => !url.pathname.startsWith('/login'));
}
async function api(page, path, method = 'GET', data) {
return page.evaluate(
async ({ path, method, data }) => {
const token = decodeURIComponent(
document.cookie
.split('; ')
.find((v) => v.startsWith('XSRF-TOKEN='))
?.slice(11) ?? '',
);
const r = await fetch(path, {
method,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'X-XSRF-TOKEN': token,
},
...(data && method !== 'GET'
? { body: JSON.stringify(data) }
: {}),
});
return { status: r.status, data: await r.json() };
},
{ path, method, data },
);
}
async function menus(page, management, touch) {
const nav = page.locator('nav[aria-label="Áreas do portal"]');
assert.equal(
await nav.getByRole('button', { name: /^Gestão/ }).count(),
management ? 1 : 0,
);
const groups = nav.locator(':scope > ul > li > button');
for (let i = 0; i < (await groups.count()); i++) {
const b = groups.nth(i);
await b.scrollIntoViewIfNeeded();
if (touch) await b.tap();
else {
await b.focus();
await page.keyboard.press('Enter');
}
assert.equal(await b.getAttribute('aria-expanded'), 'true');
const panel = nav.locator(
`[id="${await b.getAttribute('aria-controls')}"]`,
);
const box = await panel.boundingBox(),
width = page.viewportSize().width;
assert.ok(
box.x >= -1 && box.x + box.width <= width + 1,
JSON.stringify({ box, width }),
);
const nested = panel.getByRole('button');
if (await nested.count()) {
const n = nested.first();
await n.click();
assert.equal(await n.getAttribute('aria-expanded'), 'true');
await page.keyboard.press('Escape');
assert.equal(await n.getAttribute('aria-expanded'), 'false');
assert.equal(
await n.evaluate((el) => el === document.activeElement),
true,
);
}
await page.keyboard.press('Escape');
assert.equal(await b.getAttribute('aria-expanded'), 'false');
assert.equal(
await b.evaluate((el) => el === document.activeElement),
true,
);
}
const overflow = await page.evaluate(
() => document.documentElement.scrollWidth > window.innerWidth,
);
assert.equal(overflow, false, page.url());
}
try {
const caseId = JSON.parse(
fs.readFileSync(dir + '/desk-browser-evidence.json'),
).caseId;
for (const width of [1440, 390]) {
const page = await context(width);
await visit(page, '/');
await menus(page, false, width < 640);
await page.screenshot({
path: `${dir}/public-${width}.png`,
fullPage: true,
});
for (const [kind, title] of [
['project', 'V2 QA project'],
['cluster', 'V2 QA cluster'],
]) {
await visit(page, `/carteira?kind=${kind}`);
await page.getByLabel('Pesquisar', { exact: true }).fill(title);
await page
.getByRole('button', { name: 'Pesquisar', exact: true })
.click();
await page
.getByRole('link', { name: title, exact: true })
.click();
await page
.getByRole('heading', { name: title, exact: true })
.waitFor();
assert.ok(
await page
.locator('[data-current-group="true"]')
.textContent()
.then((t) => t.includes('Explorar')),
);
}
await visit(page, '/zonas');
await page.getByLabel('Pesquisar por nome ou código').fill('V2 QA');
await page
.getByRole('button', { name: 'Pesquisar', exact: true })
.click();
await page
.getByRole('link', {
name: 'V2 QA localização fictícia',
exact: true,
})
.click();
await page
.getByRole('heading', {
name: 'V2 QA localização fictícia',
exact: true,
})
.waitFor();
await login(page, 'investor');
await visit(page, '/investidor');
await menus(page, false, width < 640);
await page.screenshot({
path: `${dir}/investor-${width}.png`,
fullPage: true,
});
const draft = await api(
page,
'/api/v1/simulation-drafts/00000000-0000-4000-8000-000000000202',
);
assert.equal(draft.status, 200);
assert.equal(
draft.data.data.economic_snapshot.project_title,
'V2 QA rascunho anterior',
);
const restored = await api(
page,
'/balcao-virtual/dossiers',
'POST',
{
title: 'V2 QA continuidade do rascunho',
simulation_id: '00000000-0000-4000-8000-000000000202',
reuse_consent: true,
client_key: crypto.randomUUID(),
},
);
assert.equal(restored.status, 201);
const guidance = await api(
page,
`/balcao-virtual/dossiers/${restored.data.id}/orientacao`,
);
assert.equal(
guidance.data.facts.project_summary,
'Ideia anterior preservada QA',
);
assert.equal(
guidance.data.phases[1].evidence[0].state,
'investor_declared_not_verified',
);
assert.equal(guidance.data.phases[2].evidence[0].state, 'absent');
for (const path of [
'/como-investir',
'/simulador-fiscal',
`/balcao-virtual/dossiers/${caseId}`,
'/parcerias',
]) {
await visit(page, path);
await page
.getByRole('button', {
name: 'Acompanhar o meu projecto neste portal',
exact: true,
})
.click();
await page
.getByLabel('Contexto do projecto')
.selectOption(caseId);
await page
.getByText('Regras internas', { exact: false })
.waitFor();
await page
.getByRole('button', {
name: 'Acompanhar o meu projecto neste portal',
exact: true,
})
.click();
}
checks.push(
`Publicação QA, pesquisa e fichas; menus públicos e de investidor; rascunho e contexto entre módulos em ${width}px.`,
);
await page.context().close();
}
const owner = await context(1440);
await login(owner, 'investor');
const manager = await context(1440);
await login(manager, 'manager');
await visit(manager, '/management/crm/leads');
await menus(manager, true, false);
execFileSync(
'php',
['tools/investmoz-v2-qa-router.php', 'seed', caseId],
{ stdio: 'pipe' },
);
await visit(owner, `/balcao-virtual/dossiers/${caseId}?phase=location`);
await owner
.getByText('Documento associado, por conferir', { exact: false })
.first()
.waitFor();
const dossier = await api(
manager,
`/balcao-virtual/dossiers/${caseId}`,
);
const document = dossier.data.documents.find(
(d) => d.category === 'location_evidence',
);
assert.ok(document);
await visit(manager, `/balcao-virtual/dossiers/${caseId}`);
const review = manager.locator('form').filter({
has: manager.getByRole('heading', {
name: 'Conferência documental interna',
}),
});
await review
.getByLabel('Documento', { exact: true })
.selectOption(document.id);
await review.getByLabel('Localização', { exact: true }).check();
await review
.getByLabel('O que foi verificado')
.fill('Conferido apenas o acesso descrito na fixture V2 QA.');
await review
.getByLabel('Evidência utilizada')
.fill('Ficheiro sintético, versão e hash registados nesta QA.');
await review
.getByRole('button', {
name: 'Registar conferência com a minha autoria',
})
.click();
await manager.waitForLoadState('networkidle');
await visit(owner, `/balcao-virtual/dossiers/${caseId}?phase=location`);
await owner
.getByText('Verificador: Guidance QA manager', { exact: false })
.waitFor();
await owner
.getByText('Conferido internamente no âmbito descrito', {
exact: false,
})
.first()
.waitFor();
await owner.screenshot({
path: `${dir}/scoped-evidence.png`,
fullPage: true,
});
const other = await context(390);
await login(other, 'other');
assert.equal(
(await api(other, `/balcao-virtual/dossiers/${caseId}/orientacao`))
.status,
403,
);
assert.equal(
(
await api(
other,
'/api/v1/simulation-drafts/00000000-0000-4000-8000-000000000202',
)
).status,
404,
);
assert.equal((await api(other, '/management/crm/leads')).status, 403);
await visit(other, '/investidor');
await menus(other, false, true);
checks.push(
'Conferência pela interface com autor/data/âmbito; gestão autorizada; isolamento de dossier e rascunho entre utilizadores.',
);
assert.deepEqual(errors, []);
fs.writeFileSync(
dir + '/navigation-browser-evidence.json',
JSON.stringify(
{
passed: true,
database: 'investmoz_v2_20261008_test',
port: 55441,
checks,
errors,
blockedExternalRequests: blocked,
},
null,
2,
),
);
console.log(JSON.stringify({ passed: true, checks, errors }));
} finally {
await browser.close();
}
})().catch((e) => {
console.error(e);
process.exitCode = 1;
});