textmachine/frontend/src/routes.test.ts

28 lines
1.3 KiB
TypeScript

import { isValidElement } from 'react';
import { Navigate } from 'react-router';
import { expect, it } from 'vitest';
import { routes } from './routes';
// scripts/shot.mjs keeps ITS OWN list of routes — Node cannot load TSX without a build,
// and that is a deliberate trade for simplicity. The price of the trade: a new screen forgotten
// in shot.mjs is neither photographed nor checked by axe, while `npm run shot` still returns
// success. A lock is cheaper than discipline: a divergence of the two lists fails as a test.
const shot = Object.values(
import.meta.glob('../scripts/shot.mjs', { query: '?raw', eager: true, import: 'default' }),
)[0] as string;
it('the list of routes in scripts/shot.mjs matches routes.tsx', () => {
const declared = /const KNOWN_ROUTES = \[(.*?)\];/s.exec(shot)?.[1];
expect(declared, 'KNOWN_ROUTES not found in scripts/shot.mjs').toBeDefined();
const known = [...(declared as string).matchAll(/'([^']+)'/g)].map((m) => m[1]);
// There is nothing to photograph in a redirect: it has no screen of its own.
const screens = routes
.filter((route) => !(isValidElement(route.element) && route.element.type === Navigate))
.map((route) => route.path)
.filter((path) => path !== undefined);
expect(known.toSorted()).toEqual(screens.toSorted());
});