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:
37
frontend/src/lib/timeline/timelineFilterBoundary.spec.ts
Normal file
37
frontend/src/lib/timeline/timelineFilterBoundary.spec.ts
Normal 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user