Files
compras/scripts/menu-links-check.mjs
T
nala 413c201d97
tests / ci (push) Canceled after 0s
commit5
2026-10-29 22:03:09 +02:00

96 lines
5.6 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import {procurementNavigation, activeProcurementEntry} from '../resources/js/lib/procurement-navigation.ts';
const base = 'http://127.0.0.1:8005';
const cookies = new Map();
let version = '';
async function request(path, {method = 'GET', body, inertia = false} = {}) {
const response = await fetch(base + path, {method, redirect: 'manual', headers: {
Accept: 'application/json', Cookie: [...cookies].map(([k,v]) => `${k}=${v}`).join('; '),
...(body ? {'Content-Type': 'application/x-www-form-urlencoded'} : {}),
...(inertia ? {'X-Inertia': 'true', 'X-Inertia-Version': version} : {}),
}, ...(body ? {body: new URLSearchParams(body)} : {})});
for (const cookie of response.headers.getSetCookie()) {
const pair = cookie.split(';', 1)[0], index = pair.indexOf('=');
cookies.set(pair.slice(0,index),pair.slice(index+1));
}
const text = await response.text();
let data;
try {data = JSON.parse(text);} catch {}
if (!data) {
const page = text.match(/<script data-page="app" type="application\/json">([\s\S]*?)<\/script>/);
if (page) data = JSON.parse(page[1]);
}
if (data?.version) version = data.version;
return {status:response.status, text, data};
}
async function enter(role) {
const landing = await request('/avaliar');
assert.equal(landing.status, 200);
const csrf = landing.text.match(/name="_token" value="([^"]+)"/)[1];
assert.equal((await request('/avaliar/entrar', {method:'POST',body:{role,_token:csrf}})).status,302);
}
await enter('admin');
await request('/dashboard');
const baseline = (await request('/api/supplier-payables')).data;
const results = [];
for (const role of ['admin','procurement','finance','department_head','warehouse','requester']) {
await enter(role);
const dashboard = await request('/dashboard', {inertia:true});
assert.equal(dashboard.status,200);
assert.equal(dashboard.data.props.auth.user.purchasing_role,role);
const groups = procurementNavigation(role,dashboard.data.props.payablesAvailable === true);
const entries = groups.flatMap(group=>group.entries);
for (const entry of entries) {
const response = await request(entry.path,{inertia:!entry.fullPage});
assert.equal(response.status,200,role+' '+entry.path);
if (!entry.fullPage) assert(response.data?.component,role+' Inertia '+entry.path);
assert.equal(activeProcurementEntry(groups,entry.path),entry.id,role+' active '+entry.path);
if (entry.id === 'my_drafts') {
assert.equal(response.data.props.filters.view,'mine');
assert.equal(response.data.props.filters.status,'draft');
}
}
for (const path of ['/settings/profile','/settings/appearance']) assert.equal((await request(path,{inertia:true})).status,200,role+' '+path);
// Hidden administration and finance pages remain enforced on the server.
if (role !== 'admin') assert.equal((await request('/empresa')).status,403,role+' company isolation');
if (!['admin','finance'].includes(role)) assert.equal((await request('/facturas')).status,403,role+' finance isolation');
results.push({role,checked_entries:entries.length,paths:entries.map(entry=>entry.path)});
}
await enter('admin');
const all = await request('/requisicoes?view=all',{inertia:true});
const requestId = all.data.props.requisitions.data[0]?.id;
const processLinks = [];
if (requestId) {
assert.equal((await request('/requisicoes/'+requestId,{inertia:true})).status,200);
const process = await request('/requisicoes/'+requestId+'/processo',{inertia:true});
assert.equal(process.status,200);
function collectLinks(value) {
if (!value || typeof value !== 'object') return;
if (typeof value.href === 'string' && value.href.startsWith('/') && !value.href.startsWith('//')) processLinks.push(value.href);
for (const child of Object.values(value)) if (typeof child === 'object') collectLinks(child);
}
collectLinks(process.data.props);
for (const href of new Set(processLinks)) assert.equal((await request(href)).status,200,'Process link '+href);
}
const invoices = await request('/facturas',{inertia:true});
for (const invoice of invoices.data.props.invoices.data) {
const page = await request('/obrigacoes-fornecedores?invoice='+invoice.id);
assert.equal(page.status,200);
assert(page.text.includes('href="/facturas"'));
}
assert.deepEqual((await request('/api/supplier-payables')).data,baseline,'Business history must remain unchanged');
const groups = procurementNavigation('admin',true);
assert.equal(activeProcurementEntry(groups,'/requisicoes?status=draft&view=mine'),'my_drafts');
assert.equal(activeProcurementEntry(groups,'/ordens/1/recepcoes'),'receipts');
assert.equal(activeProcurementEntry(groups,'/requisicoes/1/cotacoes'),'quotations');
assert.equal(activeProcurementEntry(groups,'/requisicoes/1/relatorio'),'quotations');
assert.equal(activeProcurementEntry(groups,'/requisicoes/1/adjudicacao'),'orders');
assert.equal(activeProcurementEntry(groups,'/stock/operacoes'),'stock_operations');
await fs.writeFile('docs/proofs/menu-links.json',JSON.stringify({checked_at:new Date().toISOString(),results,
request_detail_checked:!!requestId,invoice_count:invoices.data.props.invoices.data.length,
process_links_checked:[...new Set(processLinks)],
canonical_query_order:true,active_subroutes:true,business_history_unchanged:true,business_commands:0},null,2)+'\n');
console.log('All visible menu links passed for six roles, with active entries, query filters, detail links and unchanged business history.');