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

265 lines
10 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 base = 'http://127.0.0.1:18091';
(async () => {
const browser = await chromium.launch({
headless: true,
executablePath:
'C:/Users/macbook/AppData/Local/ms-playwright/chromium-1243/chrome-win64/chrome.exe',
});
const errors = [];
const log = [];
async function login(role) {
const context = await browser.newContext();
await context.route('**/*', (r) =>
r.request().url().startsWith(base) ? r.continue() : r.abort(),
);
const page = await context.newPage();
page.on('pageerror', (e) => errors.push(e.message));
const response = await page.goto(base + '/login');
assert.equal(
response.headers()['x-investmoz-qa-target'],
'investmoz_guidance_20261008_test:55441',
);
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'));
return page;
}
async function json(page, path, method = 'GET', data) {
const response = await 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 },
);
assert.ok(response.status < 300, JSON.stringify(response));
return response.data;
}
try {
console.log('QA browser: normal authentication');
const investor = await login('investor');
const manager = await login('manager');
console.log('QA browser: prepare dossier');
await investor.goto(base + '/balcao-virtual/painel');
await investor
.getByText('Preparar acompanhamento de um projecto', {
exact: true,
})
.click();
await investor
.getByLabel('Título do projecto', { exact: true })
.fill('Percurso completo QA browser');
await investor.locator('input[type=checkbox]').check();
await investor
.getByRole('button', { name: 'Preparar dossier', exact: true })
.click();
await investor.waitForURL(/\/balcao-virtual\/dossiers\//);
const caseId = investor.url().split('/').pop();
await investor
.getByRole('button', {
name: 'Preparar rascunho de pedido',
exact: true,
})
.click();
await investor.waitForURL(/\/balcao-virtual\/pedidos\//);
const requestId = investor.url().split('/').pop();
console.log('QA browser: save draft and submit');
await investor
.getByLabel(/Descrição e objetivo/)
.fill('Preciso de apoio para preparar o investimento fictício QA.');
await investor.getByLabel(/Valor do investimento/).fill('1000.00');
await investor.getByLabel(/Moeda/).first().selectOption('MZN');
await investor
.getByRole('button', { name: 'Guardar rascunho', exact: true })
.click();
await investor.waitForLoadState('networkidle');
await investor.reload();
assert.equal(
await investor.getByLabel('Valor do investimento').inputValue(),
'1000.00',
);
await investor
.getByLabel(
'Revi o conteúdo e autorizo a submissão para análise interna.',
)
.check();
await investor
.getByRole('button', { name: 'Submeter este pedido', exact: true })
.click();
await investor.waitForLoadState('networkidle');
let row = (await json(manager, `/balcao-virtual/pedidos/${requestId}`))
.request;
assert.equal(row.status, 'submitted');
log.push(
'Rascunho recuperado e submetido explicitamente com protocolo interno.',
);
await json(
manager,
`/balcao-virtual/pedidos/${requestId}/estado`,
'POST',
{
version: row.version,
status: 'returned',
reason: 'Indique o mercado previsto. Esclarecimento interno QA.',
editable_fields: ['description'],
},
);
await investor.reload();
await investor
.getByLabel(/Descrição e objetivo/)
.fill(
'Mercado declarado em QA; evidência de procura permanece pendente.',
);
await investor
.getByLabel('Justificação de correcção')
.fill('Resposta ao esclarecimento do responsável.');
await investor
.getByLabel(
'Revi o conteúdo e autorizo a submissão para análise interna.',
)
.check();
await investor
.getByRole('button', { name: 'Submeter este pedido', exact: true })
.click();
await investor.waitForLoadState('networkidle');
row = (await json(manager, `/balcao-virtual/pedidos/${requestId}`))
.request;
for (const status of ['reviewing', 'opinion', 'accepted']) {
await json(
manager,
`/balcao-virtual/pedidos/${requestId}/estado`,
'POST',
{
version: row.version,
status,
reason: 'Resultado interno QA, sem aprovação ou licença institucional.',
},
);
row = (await json(manager, `/balcao-virtual/pedidos/${requestId}`))
.request;
}
await manager.goto(base + `/balcao-virtual/pedidos/${requestId}`);
await manager
.getByLabel('Mensagem privada', { exact: true })
.fill(
'Resposta humana QA: o dossier foi aceite internamente para acompanhamento.',
);
await manager
.getByRole('button', {
name: 'Enviar esta mensagem no pedido',
exact: true,
})
.click();
await manager.waitForLoadState('networkidle');
await investor.reload();
await investor
.getByText(
'Resposta humana QA: o dossier foi aceite internamente para acompanhamento.',
{ exact: true },
)
.waitFor();
await investor.screenshot({
path: 'storage/app/private/virtual-desk/qa-response.png',
fullPage: true,
});
await investor.goto(base + `/balcao-virtual/dossiers/${caseId}`);
const guidance = await json(
investor,
`/balcao-virtual/dossiers/${caseId}/orientacao`,
);
await json(
investor,
`/balcao-virtual/dossiers/${caseId}/recomendacoes`,
'POST',
{
code: 'support_preparation',
context_hash: guidance.fingerprint,
decision: 'accepted',
reason: 'Assumo o acompanhamento deste mesmo pedido.',
},
);
const task = await json(
investor,
`/balcao-virtual/dossiers/${caseId}/tarefas`,
'POST',
{
code: 'support_preparation',
context_hash: guidance.fingerprint,
consent: true,
request_id: requestId,
due_at: new Date(Date.now() + 86400000).toISOString(),
},
);
await investor.reload();
await investor
.getByRole('link', {
name: 'Consultar o pedido e a resposta do responsável',
exact: true,
})
.waitFor();
await investor.goto(base + '/parcerias');
await investor
.getByRole('button', {
name: 'Acompanhar o meu projecto neste portal',
exact: true,
})
.click();
await investor.getByLabel('Contexto do projecto').selectOption(caseId);
await investor.getByText('Regras internas', { exact: false }).waitFor();
log.push(
'Esclarecimento, nova submissão, parecer, aceitação interna e resposta humana preservados.',
);
log.push(
'Tarefa aceite no CRM interno ligada ao mesmo pedido; contexto recuperado na Bolsa de Parcerias.',
);
assert.deepEqual(errors, []);
fs.writeFileSync(
'storage/app/private/virtual-desk/browser-qa-evidence.json',
JSON.stringify(
{
passed: true,
database: 'investmoz_guidance_20261008_test',
port: 55441,
caseId,
requestId,
taskId: task.id,
log,
pageErrors: errors,
},
null,
2,
),
);
console.log(JSON.stringify({ passed: true, log, pageErrors: errors }));
} finally {
await browser.close();
}
})().catch((e) => {
console.error(e);
process.exitCode = 1;
});