test(260805-d0r): rewrite modal test fixtures to { groups, modules } shape
- Regression: group without any module grant stays visible as a chip - New: mixed empty-modules + populated-groups case, LDAP/MANUAL badge case
This commit is contained in:
@@ -23,6 +23,10 @@ const messages: Record<string, Record<string, unknown>> = {
|
|||||||
'{module} direkt für {user} {granted, select, true {freigeben} other {entziehen}}',
|
'{module} direkt für {user} {granted, select, true {freigeben} other {entziehen}}',
|
||||||
saveError: 'Freigabe konnte nicht gespeichert werden. Bitte erneut versuchen.',
|
saveError: 'Freigabe konnte nicht gespeichert werden. Bitte erneut versuchen.',
|
||||||
},
|
},
|
||||||
|
'admin.groups.members': {
|
||||||
|
sourceManual: 'Manuell',
|
||||||
|
sourceLdap: 'LDAP',
|
||||||
|
},
|
||||||
common: {
|
common: {
|
||||||
loading: 'Laden...',
|
loading: 'Laden...',
|
||||||
close: 'Schliessen',
|
close: 'Schliessen',
|
||||||
@@ -58,7 +62,9 @@ vi.mock('next-intl', () => ({
|
|||||||
|
|
||||||
import { UserAccessModal } from './components/UserAccessModal';
|
import { UserAccessModal } from './components/UserAccessModal';
|
||||||
|
|
||||||
const mockAccessRows = [
|
const mockAccess = {
|
||||||
|
groups: [{ id: 'g1', name: 'Alle Benutzer', source: 'MANUAL' as const }],
|
||||||
|
modules: [
|
||||||
{
|
{
|
||||||
module: { id: 'm1', name: 'Ausschreibungs-Radar', category: 'procurement' },
|
module: { id: 'm1', name: 'Ausschreibungs-Radar', category: 'procurement' },
|
||||||
viaGroups: ['Alle Benutzer'],
|
viaGroups: ['Alle Benutzer'],
|
||||||
@@ -69,7 +75,8 @@ const mockAccessRows = [
|
|||||||
viaGroups: [],
|
viaGroups: [],
|
||||||
direct: true,
|
direct: true,
|
||||||
},
|
},
|
||||||
];
|
],
|
||||||
|
};
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
cleanup();
|
cleanup();
|
||||||
@@ -80,7 +87,7 @@ describe('UserAccessModal', () => {
|
|||||||
it('renders the group-membership chip list, and the empty state when the user has none', async () => {
|
it('renders the group-membership chip list, and the empty state when the user has none', async () => {
|
||||||
vi.stubGlobal(
|
vi.stubGlobal(
|
||||||
'fetch',
|
'fetch',
|
||||||
vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockAccessRows) })),
|
vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockAccess) })),
|
||||||
);
|
);
|
||||||
|
|
||||||
render(<UserAccessModal userId="u1" username="Maxi Musterfrau" onClose={vi.fn()} />);
|
render(<UserAccessModal userId="u1" username="Maxi Musterfrau" onClose={vi.fn()} />);
|
||||||
@@ -96,10 +103,7 @@ describe('UserAccessModal', () => {
|
|||||||
vi.fn(() =>
|
vi.fn(() =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
ok: true,
|
ok: true,
|
||||||
json: () =>
|
json: () => Promise.resolve({ groups: [], modules: mockAccess.modules }),
|
||||||
Promise.resolve(
|
|
||||||
mockAccessRows.map((row) => ({ ...row, viaGroups: [] as string[] })),
|
|
||||||
),
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -113,10 +117,44 @@ describe('UserAccessModal', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('REGRESSION: a group without any module grant stays visible as a chip', async () => {
|
||||||
|
// Deliberately no module row references the group name -- viaGroups is
|
||||||
|
// empty everywhere, so any found text is unambiguously the chip and not
|
||||||
|
// the "Über Gruppe(n)" column.
|
||||||
|
vi.stubGlobal(
|
||||||
|
'fetch',
|
||||||
|
vi.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
ok: true,
|
||||||
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
groups: [{ id: 'g1', name: 'Gruppe A', source: 'MANUAL' as const }],
|
||||||
|
modules: [
|
||||||
|
{
|
||||||
|
module: { id: 'm1', name: 'Ausschreibungs-Radar', category: 'procurement' },
|
||||||
|
viaGroups: [],
|
||||||
|
direct: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<UserAccessModal userId="u1" username="Maxi Musterfrau" onClose={vi.fn()} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Gruppe A')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
screen.queryByText('Dieser Benutzer ist keiner Gruppe zugeordnet.'),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it('renders the module access table with one inherited and one non-inherited module', async () => {
|
it('renders the module access table with one inherited and one non-inherited module', async () => {
|
||||||
vi.stubGlobal(
|
vi.stubGlobal(
|
||||||
'fetch',
|
'fetch',
|
||||||
vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockAccessRows) })),
|
vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockAccess) })),
|
||||||
);
|
);
|
||||||
|
|
||||||
render(<UserAccessModal userId="u1" username="Maxi Musterfrau" onClose={vi.fn()} />);
|
render(<UserAccessModal userId="u1" username="Maxi Musterfrau" onClose={vi.fn()} />);
|
||||||
@@ -125,21 +163,43 @@ describe('UserAccessModal', () => {
|
|||||||
expect(screen.getByText('Ausschreibungs-Radar')).toBeInTheDocument();
|
expect(screen.getByText('Ausschreibungs-Radar')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
expect(screen.getByText('DKV Flotte')).toBeInTheDocument();
|
expect(screen.getByText('DKV Flotte')).toBeInTheDocument();
|
||||||
// m1 is inherited via 'Alle Benutzer' -- shown as a chip in the via-groups column.
|
// m1 is inherited via 'Alle Benutzer' -- shown as a chip in the via-groups column
|
||||||
|
// AND as a group-membership chip.
|
||||||
expect(screen.getAllByText('Alle Benutzer').length).toBeGreaterThan(0);
|
expect(screen.getAllByText('Alle Benutzer').length).toBeGreaterThan(0);
|
||||||
// m2 has no inheriting groups -- renders the em-dash placeholder.
|
// m2 has no inheriting groups -- renders the em-dash placeholder.
|
||||||
expect(screen.getByText('–')).toBeInTheDocument();
|
expect(screen.getByText('–')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shows both the group chips and the no-active-modules hint when modules is empty but groups is not', async () => {
|
||||||
|
vi.stubGlobal(
|
||||||
|
'fetch',
|
||||||
|
vi.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
ok: true,
|
||||||
|
json: () => Promise.resolve({ groups: mockAccess.groups, modules: [] }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<UserAccessModal userId="u1" username="Maxi Musterfrau" onClose={vi.fn()} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getAllByText('Alle Benutzer').length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
screen.getByText('Für diesen Mandanten sind keine Module aktiviert.'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it('rolls back the direct checkbox and shows a visible error when the request fails', async () => {
|
it('rolls back the direct checkbox and shows a visible error when the request fails', async () => {
|
||||||
const fetchMock = vi.fn((url: string, init?: RequestInit) => {
|
const fetchMock = vi.fn((url: string, init?: RequestInit) => {
|
||||||
if (typeof url === 'string' && url.endsWith('/module-grants/users/u1')) {
|
if (typeof url === 'string' && url.endsWith('/module-grants/users/u1')) {
|
||||||
return Promise.resolve({ ok: true, json: () => Promise.resolve(mockAccessRows) });
|
return Promise.resolve({ ok: true, json: () => Promise.resolve(mockAccess) });
|
||||||
}
|
}
|
||||||
if (typeof url === 'string' && url.endsWith('/module-grants') && init?.method === 'POST') {
|
if (typeof url === 'string' && url.endsWith('/module-grants') && init?.method === 'POST') {
|
||||||
return Promise.resolve({ ok: false, status: 500, text: () => Promise.resolve('boom') });
|
return Promise.resolve({ ok: false, status: 500, text: () => Promise.resolve('boom') });
|
||||||
}
|
}
|
||||||
return Promise.resolve({ ok: true, json: () => Promise.resolve(mockAccessRows) });
|
return Promise.resolve({ ok: true, json: () => Promise.resolve(mockAccess) });
|
||||||
});
|
});
|
||||||
vi.stubGlobal('fetch', fetchMock);
|
vi.stubGlobal('fetch', fetchMock);
|
||||||
|
|
||||||
@@ -164,7 +224,9 @@ describe('UserAccessModal', () => {
|
|||||||
it('shows the hint text when the tenant has no active modules', async () => {
|
it('shows the hint text when the tenant has no active modules', async () => {
|
||||||
vi.stubGlobal(
|
vi.stubGlobal(
|
||||||
'fetch',
|
'fetch',
|
||||||
vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve([]) })),
|
vi.fn(() =>
|
||||||
|
Promise.resolve({ ok: true, json: () => Promise.resolve({ groups: [], modules: [] }) }),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
render(<UserAccessModal userId="u1" username="Maxi Musterfrau" onClose={vi.fn()} />);
|
render(<UserAccessModal userId="u1" username="Maxi Musterfrau" onClose={vi.fn()} />);
|
||||||
@@ -179,7 +241,7 @@ describe('UserAccessModal', () => {
|
|||||||
it('gives every direct checkbox an aria-label', async () => {
|
it('gives every direct checkbox an aria-label', async () => {
|
||||||
vi.stubGlobal(
|
vi.stubGlobal(
|
||||||
'fetch',
|
'fetch',
|
||||||
vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockAccessRows) })),
|
vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockAccess) })),
|
||||||
);
|
);
|
||||||
|
|
||||||
render(<UserAccessModal userId="u1" username="Maxi Musterfrau" onClose={vi.fn()} />);
|
render(<UserAccessModal userId="u1" username="Maxi Musterfrau" onClose={vi.fn()} />);
|
||||||
@@ -194,4 +256,32 @@ describe('UserAccessModal', () => {
|
|||||||
expect(box).toHaveAccessibleName();
|
expect(box).toHaveAccessibleName();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shows the LDAP origin badge on a chip with source LDAP and the Manuell badge on source MANUAL', async () => {
|
||||||
|
vi.stubGlobal(
|
||||||
|
'fetch',
|
||||||
|
vi.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
ok: true,
|
||||||
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
groups: [
|
||||||
|
{ id: 'g1', name: 'Manuelle Gruppe', source: 'MANUAL' as const },
|
||||||
|
{ id: 'g2', name: 'LDAP-Gruppe', source: 'LDAP' as const },
|
||||||
|
],
|
||||||
|
modules: [],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<UserAccessModal userId="u1" username="Maxi Musterfrau" onClose={vi.fn()} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Manuelle Gruppe')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('LDAP-Gruppe')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Manuell')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('LDAP')).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user