59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import { isValidElement } from 'react';
|
|
import { describe, expect, it } from 'vitest';
|
|
import type { RouteObject } from 'react-router-dom';
|
|
import { routes } from '@/routes';
|
|
import { Placeholder } from '@/spa/placeholder';
|
|
|
|
function collectPaths(routeObjects: RouteObject[]): string[] {
|
|
return routeObjects.flatMap((route) => [
|
|
...(route.path ? [route.path] : []),
|
|
...(route.children ? collectPaths(route.children) : []),
|
|
]);
|
|
}
|
|
|
|
function findRoute(routeObjects: RouteObject[], path: string): RouteObject | undefined {
|
|
for (const route of routeObjects) {
|
|
if (route.path === path) return route;
|
|
const nested = route.children ? findRoute(route.children, path) : undefined;
|
|
if (nested) return nested;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
describe('SPA route table', () => {
|
|
it('covers every v1 parity route from the Phase P RFC', () => {
|
|
expect(collectPaths(routes).sort()).toEqual(
|
|
[
|
|
'/',
|
|
'/admin',
|
|
'/auth/provider/:provider',
|
|
'/chat',
|
|
'/login',
|
|
'/projects',
|
|
'/projects/:id',
|
|
'/register',
|
|
'/settings',
|
|
'/tasks',
|
|
].sort(),
|
|
);
|
|
});
|
|
|
|
it('separates guest and authenticated route groups', () => {
|
|
const guestPaths = collectPaths(routes.at(0)?.children ?? []);
|
|
const authPaths = collectPaths(routes.at(1)?.children ?? []);
|
|
expect(guestPaths).toContain('/login');
|
|
expect(guestPaths).not.toContain('/chat');
|
|
expect(authPaths).toContain('/chat');
|
|
});
|
|
|
|
it.each(['/login', '/register', '/auth/provider/:provider'])(
|
|
'renders a real guest page instead of the P1 placeholder at %s',
|
|
(path) => {
|
|
const element = findRoute(routes, path)?.element;
|
|
expect(isValidElement(element)).toBe(true);
|
|
if (!isValidElement(element)) throw new Error(`Missing route element for ${path}`);
|
|
expect(element.type).not.toBe(Placeholder);
|
|
},
|
|
);
|
|
});
|