- Move api.server.ts, errors.ts, types.ts, utils.ts, relativeTime.ts to lib/shared/ - Move person relationship components to lib/person/relationship/ - Move Stammbaum components to lib/person/genealogy/ - Move HelpPopover to lib/shared/primitives/ - Update all import paths across routes, specs, and lib files - Update vi.mock() paths in server-project test files - Remove now-empty legacy directories (components/, hooks/, server/, etc.) - Update vite.config.ts coverage include paths for new structure - Update frontend/CLAUDE.md to reflect domain-based lib/ layout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88 lines
2.5 KiB
TypeScript
88 lines
2.5 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { actions } from './+page.server';
|
|
|
|
const mockApi = {
|
|
PATCH: vi.fn(),
|
|
DELETE: vi.fn()
|
|
};
|
|
|
|
vi.mock('$lib/shared/api.server', () => ({
|
|
createApiClient: () => mockApi
|
|
}));
|
|
|
|
beforeEach(() => vi.clearAllMocks());
|
|
|
|
// ─── update action ─────────────────────────────────────────────────────────────
|
|
|
|
describe('groups/[id] — update action', () => {
|
|
it('returns success: true when API responds ok', async () => {
|
|
mockApi.PATCH.mockResolvedValue({ response: { ok: true }, data: {} });
|
|
|
|
const formData = new FormData();
|
|
formData.set('name', 'Editors');
|
|
formData.append('permissions', 'WRITE_ALL');
|
|
|
|
const result = await actions.update({
|
|
params: { id: 'g1' },
|
|
request: { formData: async () => formData },
|
|
fetch
|
|
} as never);
|
|
|
|
expect(result).toEqual({ success: true });
|
|
});
|
|
|
|
it('returns fail with error message when API responds not ok', async () => {
|
|
mockApi.PATCH.mockResolvedValue({
|
|
response: { ok: false, status: 409 },
|
|
error: { code: 'GROUP_NOT_FOUND' }
|
|
});
|
|
|
|
const formData = new FormData();
|
|
formData.set('name', 'Editors');
|
|
|
|
const result = await actions.update({
|
|
params: { id: 'g1' },
|
|
request: { formData: async () => formData },
|
|
fetch
|
|
} as never);
|
|
|
|
expect((result as { status: number }).status).toBe(409);
|
|
});
|
|
});
|
|
|
|
// ─── delete action ─────────────────────────────────────────────────────────────
|
|
|
|
describe('groups/[id] — delete action', () => {
|
|
it('redirects to /admin/groups on successful delete', async () => {
|
|
mockApi.DELETE.mockResolvedValue({ response: { ok: true } });
|
|
|
|
let redirectUrl: string | null = null;
|
|
try {
|
|
await actions.delete({
|
|
params: { id: 'g1' },
|
|
fetch
|
|
} as never);
|
|
} catch (e: unknown) {
|
|
// SvelteKit redirect throws a Response-like object
|
|
const r = e as { location?: string; status?: number };
|
|
redirectUrl = r.location ?? null;
|
|
}
|
|
|
|
expect(redirectUrl).toBe('/admin/groups');
|
|
});
|
|
|
|
it('returns fail with error message when delete API responds not ok', async () => {
|
|
mockApi.DELETE.mockResolvedValue({
|
|
response: { ok: false, status: 403 },
|
|
error: { code: 'FORBIDDEN' }
|
|
});
|
|
|
|
const result = await actions.delete({
|
|
params: { id: 'g1' },
|
|
fetch
|
|
} as never);
|
|
|
|
expect((result as { status: number }).status).toBe(403);
|
|
});
|
|
});
|