test(timeline): guard the layer filter against navigation and fetch

A static boundary gate (mirroring the project's no-`{@html}` greps) that reads
TimelineFilters.svelte and /zeitstrahl/+page.svelte and fails if either ever
reintroduces goto(, url.searchParams, api.GET, or fetch( — the filter must stay
presentation-only and fully client-side.

Refs #780
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-14 20:35:49 +02:00
committed by marcel
parent e18282318a
commit 33aff36867

View File

@@ -0,0 +1,37 @@
import { describe, it, expect } from 'vitest';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
// REQ-001/002: the layer filter is presentation-only and fully client-side. It
// must never navigate or fetch — the route derives the filtered view from
// already-loaded data. This static guard mirrors the project's existing
// grep-gates (e.g. the no-`{@html}` checks) and fails the build if a future
// edit reintroduces navigation or a network call into either file.
const read = (relative: string) =>
readFileSync(fileURLToPath(new URL(relative, import.meta.url)), 'utf8');
const FILES = {
'TimelineFilters.svelte': read('./TimelineFilters.svelte'),
'/zeitstrahl/+page.svelte': read('../../routes/zeitstrahl/+page.svelte')
};
const FORBIDDEN: { label: string; pattern: RegExp }[] = [
{ label: 'goto(', pattern: /\bgoto\s*\(/ },
{ label: 'url.searchParams', pattern: /url\.searchParams/ },
{ label: 'api.GET', pattern: /\bapi\.GET\b/ },
{ label: 'fetch(', pattern: /\bfetch\s*\(/ }
];
describe('layer-filter boundary (REQ-001/002)', () => {
for (const [name, source] of Object.entries(FILES)) {
it(`${name} was found and is non-empty`, () => {
expect(source.length).toBeGreaterThan(0);
});
for (const { label, pattern } of FORBIDDEN) {
it(`${name} contains no ${label}`, () => {
expect(pattern.test(source), `${name} must not use ${label}`).toBe(false);
});
}
}
});