Permitir consulta pública dos ecrãs reais com dados privados protegidos
tests / ci (push) Canceled after 0s

This commit is contained in:
2026-10-30 20:14:24 +02:00
parent c6373365bf
commit 2134aa084b
83 changed files with 3894 additions and 3127 deletions
+54
View File
@@ -0,0 +1,54 @@
import { readFileSync } from 'node:fs';
import { App } from '@inertiajs/react';
import type { Page } from '@inertiajs/core';
import type { ComponentType } from 'react';
import { renderToString } from 'react-dom/server';
import { expect, test } from 'vitest';
import AppLayout from '@/layouts/app-layout';
import { procurementNavigation } from '@/lib/procurement-navigation';
const fixtures = JSON.parse(
readFileSync(
new URL(
'../../storage/app/private/public-navigation-fixtures.json',
import.meta.url,
),
'utf8',
),
) as Record<string, Page>;
const modules = import.meta.glob<{
default: ComponentType<Record<string, unknown>>;
}>('../../resources/js/pages/**/*.tsx', { eager: true });
test.each(Object.entries(fixtures))(
'real public page %s renders with the actual application shell and no authenticated user',
(name, initialPage) => {
const Screen = modules[`../../resources/js/pages/${name}.tsx`]?.default;
expect(Screen).toBeDefined();
const Component = (props: Record<string, unknown>) => (
<AppLayout>
<Screen {...props} />
</AppLayout>
);
const html = renderToString(
<App initialPage={initialPage} initialComponent={Component} />,
);
expect(html).toContain('Consulta pública de desenvolvimento');
expect(html).not.toContain('/consulta?');
expect(html).toContain('/dashboard');
},
);
test('public menu links only to the explicit public screens and never grants a staff role', () => {
const paths = (
fixtures.dashboard.props.publicNavigation as { paths: string[] }
).paths;
const groups = procurementNavigation('', false, paths);
expect(groups.length).toBeGreaterThan(3);
for (const entry of groups.flatMap((group) => group.entries))
expect(paths).toContain(new URL(entry.path, 'http://local').pathname);
expect(
groups
.flatMap((group) => group.entries)
.some((entry) => entry.path.startsWith('/administracao')),
).toBe(false);
expect(fixtures.dashboard.props.auth).toEqual({ user: null });
});
+93 -22
View File
@@ -6,19 +6,63 @@ import RequisitionForm from '@/pages/requisitions/form';
import Catalogue from '@/pages/stock/catalogue';
const page = {
component: 'requisitions/form', url: '/requisicoes/1/editar', version: 'catalogue',
props: { errors: {} }, clearHistory: false, encryptHistory: true,
component: 'requisitions/form',
url: '/requisicoes/1/editar',
version: 'catalogue',
props: { errors: {} },
clearHistory: false,
encryptHistory: true,
} satisfies Page;
test('saved article version remains selected when the published source has changed', () => {
const initialPage = { ...page, props: { ...page.props, costCenters: [],
requisition: { id: 1, cost_center_id: 1, priority: 'normal', needed_by: '2026-11-10', justification: 'Pedido', items: [{
id: 4, catalogue_entry_id: 7, catalogue_snapshot: { version: 1, code: 'ART-7' },
description: 'Nome original', category: 'Material', purchase_type: 'consumable', quantity: '2', unit: 'cx', unit_price_minor: 1000, tax_rate: '16',
}] },
catalogue: { ready: true, search: '', more: false, items: [{ id: 7, version: 2, stock_item_id: 3, code: 'ART-7', description: 'Nome actualizado', unit: 'cx', category: 'Material', purchase_type: 'consumable' }] },
} };
const html = renderToString(<App initialPage={initialPage} initialComponent={RequisitionForm} />);
const initialPage = {
...page,
props: {
...page.props,
costCenters: [],
requisition: {
id: 1,
cost_center_id: 1,
priority: 'normal',
needed_by: '2026-11-10',
justification: 'Pedido',
items: [
{
id: 4,
catalogue_entry_id: 7,
catalogue_snapshot: { version: 1, code: 'ART-7' },
description: 'Nome original',
category: 'Material',
purchase_type: 'consumable',
quantity: '2',
unit: 'cx',
unit_price_minor: 1000,
tax_rate: '16',
},
],
},
catalogue: {
ready: true,
search: '',
more: false,
items: [
{
id: 7,
version: 2,
stock_item_id: 3,
code: 'ART-7',
description: 'Nome actualizado',
unit: 'cx',
category: 'Material',
purchase_type: 'consumable',
},
],
},
},
};
const html = renderToString(
<App initialPage={initialPage} initialComponent={RequisitionForm} />,
);
expect(html).toMatch(/<option value="7:1" selected="">/);
expect(html).toContain('<option value="7:2">');
expect(html).toContain('value="Nome original"');
@@ -26,15 +70,42 @@ test('saved article version remains selected when the published source has chang
expect(html).toContain('Preenchimento livre');
});
test.each([true, false])('catalogue editing follows authority (%s)', (canManage) => {
const initialPage = { ...page, component: 'stock/catalogue', props: { ...page.props,
ready: true, canManage, filters: {}, records: { total: 1, prev_page_url: null, next_page_url: null, data: [{
id: 3, code: 'ART-7', name: 'Artigo existente', unit: 'cx', stock_basis: 'a'.repeat(64), version: 1,
category: 'Material', purchase_type: 'consumable', published: true, source_current: true,
}] },
} };
const html = renderToString(<App initialPage={initialPage} initialComponent={Catalogue} />);
expect(html).toContain('Artigo existente');
expect(html.includes('Guardar versão do catálogo')).toBe(canManage);
});
test.each([true, false])(
'catalogue editing follows authority (%s)',
(canManage) => {
const initialPage = {
...page,
component: 'stock/catalogue',
props: {
...page.props,
ready: true,
canManage,
filters: {},
records: {
total: 1,
prev_page_url: null,
next_page_url: null,
data: [
{
id: 3,
code: 'ART-7',
name: 'Artigo existente',
unit: 'cx',
stock_basis: 'a'.repeat(64),
version: 1,
category: 'Material',
purchase_type: 'consumable',
published: true,
source_current: true,
},
],
},
},
};
const html = renderToString(
<App initialPage={initialPage} initialComponent={Catalogue} />,
);
expect(html).toContain('Artigo existente');
expect(html.includes('Guardar versão do catálogo')).toBe(canManage);
},
);