223 lines
7.6 KiB
JavaScript
223 lines
7.6 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs/promises';
|
|
|
|
const base = 'http://127.0.0.1:8005';
|
|
function client() {
|
|
const cookies = new Map();
|
|
let csrf;
|
|
let version;
|
|
async function request(path, method = 'GET', data, inertia = true) {
|
|
const response = await fetch(base + path, {
|
|
method,
|
|
redirect: 'manual',
|
|
headers: {
|
|
Accept: inertia ? 'application/json' : 'text/html',
|
|
Cookie: [...cookies].map(([k, v]) => `${k}=${v}`).join('; '),
|
|
...(inertia
|
|
? {
|
|
'X-Inertia': 'true',
|
|
'X-Inertia-Version': version ?? '',
|
|
}
|
|
: {}),
|
|
...(data
|
|
? {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': csrf,
|
|
}
|
|
: {}),
|
|
},
|
|
...(data ? { body: JSON.stringify(data) } : {}),
|
|
});
|
|
for (const cookie of response.headers.getSetCookie()) {
|
|
const pair = cookie.split(';', 1)[0],
|
|
i = pair.indexOf('=');
|
|
cookies.set(pair.slice(0, i), pair.slice(i + 1));
|
|
}
|
|
const text = await response.text();
|
|
return {
|
|
status: response.status,
|
|
location: response.headers.get('location'),
|
|
text,
|
|
page: response.headers
|
|
.get('content-type')
|
|
?.includes('application/json')
|
|
? JSON.parse(text)
|
|
: null,
|
|
};
|
|
}
|
|
return {
|
|
request,
|
|
async enter(role) {
|
|
let page = await request('/avaliar', 'GET', undefined, false);
|
|
assert.equal(page.status, 200);
|
|
csrf = page.text.match(/name="_token" value="([^"]+)"/)[1];
|
|
assert.equal(
|
|
(await request('/avaliar/entrar', 'POST', { role }, false))
|
|
.status,
|
|
302,
|
|
);
|
|
page = await request('/avaliar', 'GET', undefined, false);
|
|
csrf = page.text.match(/name="_token" value="([^"]+)"/)[1];
|
|
const dashboard = await request(
|
|
'/dashboard',
|
|
'GET',
|
|
undefined,
|
|
false,
|
|
);
|
|
const encoded = dashboard.text.match(
|
|
/<script data-page="app" type="application\/json">([\s\S]*?)<\/script>/,
|
|
);
|
|
assert.ok(encoded, 'Expected Inertia bootstrap data');
|
|
version = JSON.parse(encoded[1]).version;
|
|
},
|
|
};
|
|
}
|
|
|
|
function csvRows(text) {
|
|
const rows = [];
|
|
let row = [],
|
|
value = '',
|
|
quoted = false;
|
|
for (let i = 0; i < text.length; i++) {
|
|
const c = text[i];
|
|
if (c === '"') {
|
|
if (quoted && text[i + 1] === '"') {
|
|
value += '"';
|
|
i++;
|
|
} else quoted = !quoted;
|
|
} else if (c === ';' && !quoted) {
|
|
row.push(value);
|
|
value = '';
|
|
} else if (c === '\n' && !quoted) {
|
|
row.push(value.replace(/\r$/, ''));
|
|
rows.push(row);
|
|
row = [];
|
|
value = '';
|
|
} else value += c;
|
|
}
|
|
if (value || row.length) {
|
|
row.push(value);
|
|
rows.push(row);
|
|
}
|
|
return rows;
|
|
}
|
|
const path = '/relatorios/medidas-fornecedores';
|
|
const readers = ['admin', 'procurement', 'finance', 'department_head'];
|
|
let example;
|
|
for (const role of [...readers, 'requester', 'warehouse']) {
|
|
const c = client();
|
|
await c.enter(role);
|
|
const report = await c.request(path);
|
|
if (readers.includes(role)) {
|
|
assert.equal(report.status, 200, role);
|
|
assert.equal(report.page.component, 'reports/supplier-improvements');
|
|
const csv = await c.request(path + '.csv', 'GET', undefined, false);
|
|
assert.equal(csv.status, 200, role);
|
|
const rows = csvRows(csv.text);
|
|
assert.equal(rows.length, report.page.props.records.total + 3, role);
|
|
assert.equal(
|
|
Object.values(report.page.props.counts).reduce((a, b) => a + b, 0),
|
|
report.page.props.records.total,
|
|
);
|
|
if (role === 'admin') {
|
|
assert.ok(
|
|
report.page.props.records.total >= 1,
|
|
'Existing explicit QA improvement required',
|
|
);
|
|
const supplier = report.page.props.suppliers[0],
|
|
responsible = report.page.props.responsibles[0];
|
|
const filters = new URLSearchParams({
|
|
supplier_id: String(supplier.id),
|
|
responsible_id: String(responsible.id),
|
|
status: 'closed',
|
|
from: '2026-11-15',
|
|
to: '2026-11-15',
|
|
});
|
|
const filtered = await c.request(path + '?' + filters);
|
|
assert.equal(filtered.status, 200);
|
|
assert.ok(filtered.page.props.records.total >= 1);
|
|
assert.ok(
|
|
filtered.page.props.records.data.every(
|
|
(m) =>
|
|
m.status === 'closed' &&
|
|
m.due_on === '2026-11-15' &&
|
|
m.overdue === false,
|
|
),
|
|
);
|
|
const exported = await c.request(
|
|
path + '.csv?' + filters + '&page=2',
|
|
'GET',
|
|
undefined,
|
|
false,
|
|
);
|
|
assert.equal(exported.status, 200);
|
|
assert.equal(
|
|
csvRows(exported.text).length,
|
|
filtered.page.props.records.total + 3,
|
|
);
|
|
const overdue = await c.request(path + '?overdue=1&status=closed');
|
|
assert.equal(overdue.page.props.records.total, 0);
|
|
const invalid = await c.request(
|
|
path + '?from=2026-11-15&to=2026-01-01',
|
|
);
|
|
assert.equal(invalid.status, 422);
|
|
const first = report.page.props.records.data[0];
|
|
assert.equal(
|
|
(
|
|
await c.request(
|
|
'/fornecedores/avaliacoes/' + first.evaluation_id,
|
|
)
|
|
).status,
|
|
200,
|
|
);
|
|
assert.equal(
|
|
(
|
|
await c.request(
|
|
'/fornecedores/avaliacoes/' +
|
|
first.evaluation_id +
|
|
'/relatorio',
|
|
)
|
|
).status,
|
|
200,
|
|
);
|
|
example = {
|
|
total: report.page.props.records.total,
|
|
filtered_total: filtered.page.props.records.total,
|
|
};
|
|
}
|
|
} else {
|
|
assert.equal(report.status, 403, role);
|
|
assert.equal(
|
|
(await c.request(path + '.csv', 'GET', undefined, false)).status,
|
|
403,
|
|
role,
|
|
);
|
|
}
|
|
assert.equal((await c.request('/logout', 'POST', {}, false)).status, 302);
|
|
}
|
|
await fs.writeFile(
|
|
'docs/proofs/supplier-improvement-report-http.json',
|
|
JSON.stringify(
|
|
{
|
|
checked_at: new Date().toISOString(),
|
|
base,
|
|
path,
|
|
readers,
|
|
denied: ['requester', 'warehouse'],
|
|
...example,
|
|
filters_and_csv_match: true,
|
|
csv_ignores_page: true,
|
|
closed_not_overdue: true,
|
|
invalid_range_refused: true,
|
|
linked_evaluation_and_report: true,
|
|
business_commands: 0,
|
|
visual_review: false,
|
|
},
|
|
null,
|
|
2,
|
|
) + '\n',
|
|
);
|
|
console.log(
|
|
'Consolidated improvement report: six profiles, filters, counts, CSV and evaluation links passed.',
|
|
);
|