273 lines
10 KiB
JavaScript
273 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:18092';
|
|
(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 blocked = [];
|
|
const contexts = [];
|
|
const browserTarget = 'investmoz_v2_20261008_test:55441';
|
|
const log = [];
|
|
async function login(role) {
|
|
const context = await browser.newContext();
|
|
await context.route('**/*', (r) =>
|
|
r.request().url().startsWith(base)
|
|
? r.continue()
|
|
: (blocked.push(r.request().url()), r.abort()),
|
|
);
|
|
contexts.push(context);
|
|
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_v2_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/investmoz-v2/desk-browser-evidence.json',
|
|
JSON.stringify(
|
|
{
|
|
passed: true,
|
|
database: 'investmoz_v2_20261008_test',
|
|
port: 55441,
|
|
caseId,
|
|
requestId,
|
|
taskId: task.id,
|
|
log,
|
|
pageErrors: errors,
|
|
blockedExternalRequests: blocked,
|
|
browserTarget,
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|
|
console.log(JSON.stringify({ passed: true, log, pageErrors: errors }));
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
})().catch((e) => {
|
|
console.error(e);
|
|
process.exitCode = 1;
|
|
});
|