Publish curated ITWorx-Pulse source from 0e0633c8ac91cb71312a26837f791035a78d81b8
Public source validation / validate (push) Failing after 2m10s
Public source validation / validate (push) Failing after 2m10s
This commit is contained in:
1 parent
a4956c2af9
commit
db8093b608
88 files changed
+3685
-469
No files matched your search
@@ -0,0 +1,104 @@
|
||||
import { cleanup, render, screen, waitFor, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { DashboardManagerPage } from '../../src/DashboardManagerPage';
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
function json(value: unknown, status = 200): Response {
|
||||
return new Response(JSON.stringify(value), { status, headers: { 'Content-Type': status >= 400 ? 'application/problem+json' : 'application/json' } });
|
||||
}
|
||||
|
||||
const editorSession = { subject: 'editor-1', role: 'editor', environment: 'test', permissions: { view: true, operate: true, edit: true, admin: false } };
|
||||
const viewerSession = { subject: 'viewer-1', role: 'viewer', environment: 'test', permissions: { view: true, operate: false, edit: false, admin: false } };
|
||||
const dashboard = { id: 'dashboard-1', slug: 'operations', name: 'Operations', description: 'Dagelijkse operatie', ownerId: 'editor-1', scope: 'shared', revision: 1, currentVersion: 2 };
|
||||
|
||||
describe('DashboardManagerPage', () => {
|
||||
it('creates a validated dashboard from a selected template', async () => {
|
||||
const requests: Array<{ url: string; init?: RequestInit }> = [];
|
||||
vi.stubGlobal('fetch', vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const url = String(input); requests.push({ url, init });
|
||||
if (url === '/api/v1/session') return json(editorSession);
|
||||
if (url.startsWith('/api/v1/dashboards?')) return json({ items: [] });
|
||||
if (url === '/api/v1/dashboards' && init?.method === 'POST') return json({ dashboard: { ...dashboard, id: 'created-1', slug: 'nachtzicht', name: 'Nachtzicht', description: '', revision: 1, currentVersion: 1 } }, 201);
|
||||
return json({ code: 'NOT_FOUND' }, 404);
|
||||
}));
|
||||
|
||||
const user = userEvent.setup();
|
||||
render(<DashboardManagerPage />);
|
||||
await user.click(await screen.findByRole('button', { name: 'Nieuw dashboard' }));
|
||||
await user.type(screen.getByLabelText('Naam'), 'Nachtzicht');
|
||||
await user.selectOptions(screen.getByLabelText('Template'), 'blank');
|
||||
await user.click(screen.getByRole('button', { name: 'Dashboard maken' }));
|
||||
|
||||
expect(await screen.findByText('Nachtzicht')).toBeVisible();
|
||||
const create = requests.find((request) => request.url === '/api/v1/dashboards' && request.init?.method === 'POST');
|
||||
expect(create).toBeDefined();
|
||||
expect(JSON.parse(String(create?.init?.body))).toMatchObject({ schemaVersion: 2, name: 'Nachtzicht', widgets: [] });
|
||||
});
|
||||
|
||||
it('renames, restores and archives with the current revision', async () => {
|
||||
const requests: Array<{ url: string; init?: RequestInit }> = [];
|
||||
vi.stubGlobal('fetch', vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const url = String(input); requests.push({ url, init });
|
||||
if (url === '/api/v1/session') return json(editorSession);
|
||||
if (url.startsWith('/api/v1/dashboards?')) return json({ items: [dashboard] });
|
||||
if (url === '/api/v1/dashboards/dashboard-1' && init?.method === 'PATCH') return json({ dashboard: { ...dashboard, name: 'Operations EU', revision: 2 } });
|
||||
if (url.endsWith('/versions')) return json({ items: [
|
||||
{ id: 'version-2', dashboardId: 'dashboard-1', number: 2, schemaVersion: 2, document: {}, changeSummary: 'metadata', createdBy: 'editor-1', createdAt: '2026-09-01T10:00:00Z' },
|
||||
{ id: 'version-1', dashboardId: 'dashboard-1', number: 1, schemaVersion: 2, document: {}, changeSummary: 'initial', createdBy: 'editor-1', createdAt: '2026-09-01T09:00:00Z' },
|
||||
] });
|
||||
if (url.endsWith('/restore/1') && init?.method === 'POST') return json({ dashboard: { ...dashboard, name: 'Operations EU', revision: 3, currentVersion: 3 } });
|
||||
if (url === '/api/v1/dashboards/dashboard-1' && init?.method === 'DELETE') return json({ dashboard: { ...dashboard, name: 'Operations EU', revision: 4, currentVersion: 3, archivedAt: '2026-09-02T00:00:00Z' } });
|
||||
return json({ code: 'NOT_FOUND' }, 404);
|
||||
}));
|
||||
|
||||
const user = userEvent.setup();
|
||||
render(<DashboardManagerPage />);
|
||||
let row = (await screen.findByText('Operations')).closest('li');
|
||||
expect(row).not.toBeNull();
|
||||
await user.click(within(row!).getByRole('button', { name: 'Beheren' }));
|
||||
const name = within(row!).getByLabelText('Naam');
|
||||
await user.clear(name); await user.type(name, 'Operations EU');
|
||||
await user.click(within(row!).getByRole('button', { name: 'Naam en beschrijving opslaan' }));
|
||||
row = (await screen.findByText('Operations EU')).closest('li');
|
||||
await user.click(within(row!).getByRole('button', { name: 'Beheren' }));
|
||||
await user.click(within(row!).getByRole('button', { name: 'Versies bekijken' }));
|
||||
await user.click(await within(row!).findByRole('button', { name: 'Herstellen als huidige versie' }));
|
||||
await waitFor(() => expect(requests.some((request) => request.url.endsWith('/restore/1') && new Headers(request.init?.headers).get('If-Match') === '2')).toBe(true));
|
||||
await user.click(within(row!).getByRole('button', { name: 'Archiveren' }));
|
||||
await user.click(within(row!).getByRole('button', { name: 'Ja, archiveren' }));
|
||||
await waitFor(() => expect(requests.some((request) => request.init?.method === 'DELETE' && new Headers(request.init.headers).get('If-Match') === '3')).toBe(true));
|
||||
expect(await screen.findByText('Geen actieve dashboards beschikbaar.')).toBeVisible();
|
||||
});
|
||||
|
||||
it('hides write controls for viewers and explains the boundary', async () => {
|
||||
vi.stubGlobal('fetch', vi.fn(async (input: RequestInfo | URL) => String(input) === '/api/v1/session' ? json(viewerSession) : json({ items: [dashboard] })));
|
||||
render(<DashboardManagerPage />);
|
||||
expect(await screen.findByText('Alleen bekijken')).toBeVisible();
|
||||
expect(screen.queryByRole('button', { name: 'Nieuw dashboard' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Beheren' })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('reloads and gives actionable feedback after a revision conflict', async () => {
|
||||
let lists = 0;
|
||||
vi.stubGlobal('fetch', vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const url = String(input);
|
||||
if (url === '/api/v1/session') return json(editorSession);
|
||||
if (url.startsWith('/api/v1/dashboards?')) { lists += 1; return json({ items: [{ ...dashboard, revision: lists }] }); }
|
||||
if (url === '/api/v1/dashboards/dashboard-1' && init?.method === 'PATCH') return json({ code: 'REVISION_CONFLICT', detail: 'conflict' }, 409);
|
||||
return json({}, 404);
|
||||
}));
|
||||
const user = userEvent.setup();
|
||||
render(<DashboardManagerPage />);
|
||||
const row = (await screen.findByText('Operations')).closest('li')!;
|
||||
await user.click(within(row).getByRole('button', { name: 'Beheren' }));
|
||||
await user.click(within(row).getByRole('button', { name: 'Naam en beschrijving opslaan' }));
|
||||
expect(await screen.findByText(/intussen gewijzigd/)).toBeVisible();
|
||||
await waitFor(() => expect(lists).toBe(2));
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user