Files
gridpilot.gg/tests/integration/website/websiteRouteInventory.ts
2026-01-03 02:42:47 +01:00

194 lines
6.5 KiB
TypeScript

import * as fs from 'fs';
import * as path from 'path';
export type RouteAccess = 'public' | 'auth' | 'admin' | 'sponsor';
export type RouteParams = Record<string, string>;
export type WebsiteRouteDefinition = {
pathTemplate: string;
params?: RouteParams;
access: RouteAccess;
expectedPathTemplate?: string;
allowNotFound?: boolean;
};
function walkDir(rootDir: string): string[] {
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
const results: string[] = [];
for (const entry of entries) {
if (entry.name.startsWith('.')) continue;
const fullPath = path.join(rootDir, entry.name);
if (entry.isDirectory()) {
results.push(...walkDir(fullPath));
continue;
}
results.push(fullPath);
}
return results;
}
function toPathTemplate(appDir: string, pageFilePath: string): string {
const rel = path.relative(appDir, pageFilePath);
const segments = rel.split(path.sep);
// drop trailing "page.tsx"
segments.pop();
// root page.tsx
if (segments.length === 0) return '/';
return `/${segments.join('/')}`;
}
export function listNextAppPageTemplates(appDir?: string): string[] {
const resolvedAppDir = appDir ?? path.join(process.cwd(), 'apps', 'website', 'app');
const files = walkDir(resolvedAppDir);
const pages = files.filter((f) => path.basename(f) === 'page.tsx');
return pages.map((pagePath) => toPathTemplate(resolvedAppDir, pagePath));
}
export function resolvePathTemplate(pathTemplate: string, params: RouteParams = {}): string {
return pathTemplate.replace(/\[([^\]]+)\]/g, (_match, key: string) => {
const replacement = params[key];
if (!replacement) {
throw new Error(`Missing route param "${key}" for template "${pathTemplate}"`);
}
return replacement;
});
}
// Default IDs used to resolve dynamic routes in smoke tests.
// These values must be supported by the docker mock API in docker-compose.test.yml.
const LEAGUE_ID = 'league-1';
const DRIVER_ID = 'driver-1';
const TEAM_ID = 'team-1';
const RACE_ID = 'race-1';
const PROTEST_ID = 'protest-1';
const ROUTE_META: Record<string, Omit<WebsiteRouteDefinition, 'pathTemplate'>> = {
'/': { access: 'public' },
'/404': { access: 'public' },
'/500': { access: 'public' },
'/admin': { access: 'admin' },
'/admin/users': { access: 'admin' },
'/auth/forgot-password': { access: 'public' },
'/auth/iracing': { access: 'public' },
'/auth/login': { access: 'public' },
'/auth/reset-password': { access: 'public' },
'/auth/signup': { access: 'public' },
'/dashboard': { access: 'auth' },
'/drivers': { access: 'public' },
'/drivers/[id]': { access: 'public', params: { id: DRIVER_ID } },
'/leaderboards': { access: 'public' },
'/leaderboards/drivers': { access: 'public' },
'/leagues': { access: 'public' },
'/leagues/create': { access: 'auth' },
'/leagues/[id]': { access: 'public', params: { id: LEAGUE_ID } },
'/leagues/[id]/roster/admin': { access: 'admin', params: { id: LEAGUE_ID } },
'/leagues/[id]/rulebook': { access: 'public', params: { id: LEAGUE_ID } },
'/leagues/[id]/schedule': { access: 'public', params: { id: LEAGUE_ID } },
'/leagues/[id]/schedule/admin': { access: 'admin', params: { id: LEAGUE_ID } },
'/leagues/[id]/settings': { access: 'admin', params: { id: LEAGUE_ID } },
'/leagues/[id]/sponsorships': { access: 'admin', params: { id: LEAGUE_ID } },
'/leagues/[id]/standings': { access: 'public', params: { id: LEAGUE_ID } },
'/leagues/[id]/stewarding': { access: 'admin', params: { id: LEAGUE_ID } },
'/leagues/[id]/stewarding/protests/[protestId]': {
access: 'admin',
params: { id: LEAGUE_ID, protestId: PROTEST_ID },
},
'/leagues/[id]/wallet': { access: 'admin', params: { id: LEAGUE_ID } },
'/onboarding': { access: 'auth' },
'/profile': { access: 'auth' },
'/profile/leagues': { access: 'auth' },
'/profile/liveries': { access: 'auth' },
'/profile/liveries/upload': { access: 'auth' },
'/profile/settings': { access: 'auth' },
'/profile/sponsorship-requests': { access: 'auth' },
'/races': { access: 'public' },
'/races/all': { access: 'public' },
'/races/[id]': { access: 'public', params: { id: RACE_ID } },
'/races/[id]/results': { access: 'public', params: { id: RACE_ID } },
'/races/[id]/stewarding': { access: 'admin', params: { id: RACE_ID } },
'/sponsor': { access: 'sponsor', expectedPathTemplate: '/sponsor/dashboard' },
'/sponsor/billing': { access: 'sponsor' },
'/sponsor/campaigns': { access: 'sponsor' },
'/sponsor/dashboard': { access: 'sponsor' },
'/sponsor/leagues': { access: 'sponsor' },
'/sponsor/leagues/[id]': { access: 'sponsor', params: { id: LEAGUE_ID } },
'/sponsor/settings': { access: 'sponsor' },
'/sponsor/signup': { access: 'public' },
'/teams': { access: 'public' },
'/teams/leaderboard': { access: 'public' },
'/teams/[id]': { access: 'public', params: { id: TEAM_ID } },
};
export function getWebsiteRouteInventory(): WebsiteRouteDefinition[] {
const discovered = listNextAppPageTemplates();
const missingMeta = discovered.filter((template) => !ROUTE_META[template]);
if (missingMeta.length > 0) {
throw new Error(
`Missing ROUTE_META entries for discovered pages:\n${missingMeta
.slice()
.sort()
.map((t) => `- ${t}`)
.join('\n')}`,
);
}
const extraMeta = Object.keys(ROUTE_META).filter((template) => !discovered.includes(template));
if (extraMeta.length > 0) {
throw new Error(
`ROUTE_META contains templates that are not present as page.tsx routes:\n${extraMeta
.slice()
.sort()
.map((t) => `- ${t}`)
.join('\n')}`,
);
}
return discovered
.slice()
.sort()
.map((pathTemplate) => ({ pathTemplate, ...ROUTE_META[pathTemplate] }));
}
export function getWebsiteParamEdgeCases(): WebsiteRouteDefinition[] {
return [
{ pathTemplate: '/races/[id]', params: { id: 'does-not-exist' }, access: 'public', allowNotFound: true },
{ pathTemplate: '/leagues/[id]', params: { id: 'does-not-exist' }, access: 'public', allowNotFound: true },
];
}
export function getWebsiteFaultInjectionRoutes(): WebsiteRouteDefinition[] {
return [
{ pathTemplate: '/leagues/[id]', params: { id: LEAGUE_ID }, access: 'public' },
{ pathTemplate: '/leagues/[id]/schedule/admin', params: { id: LEAGUE_ID }, access: 'admin' },
{ pathTemplate: '/sponsor/dashboard', access: 'sponsor' },
{ pathTemplate: '/races/[id]', params: { id: RACE_ID }, access: 'public' },
];
}
export function getWebsiteAuthDriftRoutes(): WebsiteRouteDefinition[] {
return [{ pathTemplate: '/sponsor/dashboard', access: 'sponsor' }];
}