mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
7a22d742ab
Adds new and expanded test suites across client and server to cover the OAuth 2.1 scope system, MCP session manager, collab service, unified memories helpers, OIDC service, budget slice, and OAuth authorize page. Also extends SonarQube coverage exclusions to include bootstrapping files (migrations, scheduler, main.tsx, types.ts) that are not meaningfully testable.
120 lines
5.1 KiB
TypeScript
120 lines
5.1 KiB
TypeScript
// FE-COMP-SCOPE-001 to FE-COMP-SCOPE-009
|
|
import { render, screen, waitFor } from '../../../tests/helpers/render';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { resetAllStores } from '../../../tests/helpers/store';
|
|
import ScopeGroupPicker from './ScopeGroupPicker';
|
|
|
|
beforeEach(() => {
|
|
resetAllStores();
|
|
});
|
|
|
|
describe('ScopeGroupPicker', () => {
|
|
it('FE-COMP-SCOPE-001: renders scope groups', () => {
|
|
render(<ScopeGroupPicker selected={[]} onChange={vi.fn()} />);
|
|
// Several group headers should be visible
|
|
expect(screen.getAllByRole('button').length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('FE-COMP-SCOPE-002: shows Select All button when nothing selected', () => {
|
|
render(<ScopeGroupPicker selected={[]} onChange={vi.fn()} />);
|
|
expect(screen.getByRole('button', { name: /select all/i })).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-COMP-SCOPE-003: Select All calls onChange with all scopes', async () => {
|
|
const user = userEvent.setup();
|
|
const onChange = vi.fn();
|
|
render(<ScopeGroupPicker selected={[]} onChange={onChange} />);
|
|
await user.click(screen.getByRole('button', { name: /select all/i }));
|
|
expect(onChange).toHaveBeenCalledTimes(1);
|
|
const called = onChange.mock.calls[0][0] as string[];
|
|
expect(called.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('FE-COMP-SCOPE-004: shows Deselect All button when all selected', async () => {
|
|
// First collect all scopes by clicking Select All and capturing the callback
|
|
const user = userEvent.setup();
|
|
const captured: string[][] = [];
|
|
const { rerender } = render(
|
|
<ScopeGroupPicker selected={[]} onChange={s => captured.push(s)} />
|
|
);
|
|
await user.click(screen.getByRole('button', { name: /select all/i }));
|
|
const allScopes = captured[0];
|
|
|
|
// Now rerender with all scopes selected
|
|
rerender(<ScopeGroupPicker selected={allScopes} onChange={vi.fn()} />);
|
|
expect(screen.getByRole('button', { name: /deselect all/i })).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-COMP-SCOPE-005: Deselect All calls onChange with empty array', async () => {
|
|
const user = userEvent.setup();
|
|
const captured: string[][] = [];
|
|
|
|
// Get all scopes first
|
|
const { rerender } = render(
|
|
<ScopeGroupPicker selected={[]} onChange={s => captured.push(s)} />
|
|
);
|
|
await user.click(screen.getByRole('button', { name: /select all/i }));
|
|
const allScopes = captured[0];
|
|
|
|
const onChange = vi.fn();
|
|
rerender(<ScopeGroupPicker selected={allScopes} onChange={onChange} />);
|
|
await user.click(screen.getByRole('button', { name: /deselect all/i }));
|
|
expect(onChange).toHaveBeenCalledWith([]);
|
|
});
|
|
|
|
it('FE-COMP-SCOPE-006: expanding a group reveals individual scope checkboxes', async () => {
|
|
const user = userEvent.setup();
|
|
render(<ScopeGroupPicker selected={[]} onChange={vi.fn()} />);
|
|
|
|
// Groups are collapsed by default — checkboxes for individual scopes not visible
|
|
const groupToggles = screen.getAllByRole('button').filter(b =>
|
|
!b.textContent?.toLowerCase().includes('select all') &&
|
|
!b.textContent?.toLowerCase().includes('deselect all')
|
|
);
|
|
// Click the first group expand button
|
|
await user.click(groupToggles[0]);
|
|
// Individual scope checkboxes should now appear (more than just group-level ones)
|
|
const checkboxes = screen.getAllByRole('checkbox');
|
|
expect(checkboxes.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('FE-COMP-SCOPE-007: group checkbox selects all scopes in the group', async () => {
|
|
const user = userEvent.setup();
|
|
const onChange = vi.fn();
|
|
render(<ScopeGroupPicker selected={[]} onChange={onChange} />);
|
|
|
|
const groupCheckboxes = screen.getAllByRole('checkbox');
|
|
await user.click(groupCheckboxes[0]);
|
|
expect(onChange).toHaveBeenCalledTimes(1);
|
|
const called = onChange.mock.calls[0][0] as string[];
|
|
expect(called.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('FE-COMP-SCOPE-008: individual scope toggle adds/removes that scope', async () => {
|
|
const user = userEvent.setup();
|
|
const onChange = vi.fn();
|
|
render(<ScopeGroupPicker selected={[]} onChange={onChange} />);
|
|
|
|
// Expand first group
|
|
const groupToggles = screen.getAllByRole('button').filter(b =>
|
|
!b.textContent?.toLowerCase().includes('select all') &&
|
|
!b.textContent?.toLowerCase().includes('deselect all')
|
|
);
|
|
await user.click(groupToggles[0]);
|
|
|
|
// There are now individual scope checkboxes — click the second one (first is group-level)
|
|
const checkboxes = screen.getAllByRole('checkbox');
|
|
await user.click(checkboxes[1]); // individual scope
|
|
expect(onChange).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('FE-COMP-SCOPE-009: count badge shown when some scopes selected in group', () => {
|
|
// Get any single scope key from the first group via Select All trick + manual slice
|
|
// We'll just select a scope by triggering group checkbox and passing it in
|
|
const firstGroupScope = 'trips:read'; // known scope from SCOPE_GROUPS
|
|
render(<ScopeGroupPicker selected={[firstGroupScope]} onChange={vi.fn()} />);
|
|
// Count badge like "(1/N)" should be visible
|
|
expect(screen.getByText(/\(\d+\/\d+\)/)).toBeInTheDocument();
|
|
});
|
|
});
|