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}}',
|
||||
saveError: 'Freigabe konnte nicht gespeichert werden. Bitte erneut versuchen.',
|
||||
},
|
||||
'admin.groups.members': {
|
||||
sourceManual: 'Manuell',
|
||||
sourceLdap: 'LDAP',
|
||||
},
|
||||
common: {
|
||||
loading: 'Laden...',
|
||||
close: 'Schliessen',
|
||||
@@ -58,7 +62,9 @@ vi.mock('next-intl', () => ({
|
||||
|
||||
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' },
|
||||
viaGroups: ['Alle Benutzer'],
|
||||
@@ -69,7 +75,8 @@ const mockAccessRows = [
|
||||
viaGroups: [],
|
||||
direct: true,
|
||||
},
|
||||
];
|
||||
],
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
@@ -80,7 +87,7 @@ describe('UserAccessModal', () => {
|
||||
it('renders the group-membership chip list, and the empty state when the user has none', async () => {
|
||||
vi.stubGlobal(
|
||||
'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()} />);
|
||||
@@ -96,10 +103,7 @@ describe('UserAccessModal', () => {
|
||||
vi.fn(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
json: () =>
|
||||
Promise.resolve(
|
||||
mockAccessRows.map((row) => ({ ...row, viaGroups: [] as string[] })),
|
||||
),
|
||||
json: () => Promise.resolve({ groups: [], modules: mockAccess.modules }),
|
||||
}),
|
||||
),
|
||||
);
|
||||
@@ -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 () => {
|
||||
vi.stubGlobal(
|
||||
'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()} />);
|
||||
@@ -125,21 +163,43 @@ describe('UserAccessModal', () => {
|
||||
expect(screen.getByText('Ausschreibungs-Radar')).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);
|
||||
// m2 has no inheriting groups -- renders the em-dash placeholder.
|
||||
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 () => {
|
||||
const fetchMock = vi.fn((url: string, init?: RequestInit) => {
|
||||
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') {
|
||||
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);
|
||||
|
||||
@@ -164,7 +224,9 @@ describe('UserAccessModal', () => {
|
||||
it('shows the hint text when the tenant has no active modules', async () => {
|
||||
vi.stubGlobal(
|
||||
'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()} />);
|
||||
@@ -179,7 +241,7 @@ describe('UserAccessModal', () => {
|
||||
it('gives every direct checkbox an aria-label', async () => {
|
||||
vi.stubGlobal(
|
||||
'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()} />);
|
||||
@@ -194,4 +256,32 @@ describe('UserAccessModal', () => {
|
||||
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