textmachine/frontend/src/generality.test.ts

44 lines
2.4 KiB
TypeScript

import { expect, it } from 'vitest';
// The canon's default review question: "will a language pair that is NOT yet in the repo work
// without editing code?" (CLAUDE.md §2). On the frontend this norm breaks in two ways: a script
// code hard-wired into the markup (`lang="zh"` will silently survive the arrival of ja→ru, and
// Japanese kanji will be drawn with Chinese glyph shapes, while a screen reader will take a Chinese
// voice), and a language branch in the styles (`:lang(zh) { … }` — the same pair-specificity, only
// written in CSS: a new pair would need the styles edited). The first was found by the S1 review in
// live showcase code, the second by the orchestrator's S2 review, so it is checked by a machine and
// not by eye.
//
// ⚠ A BOUNDARY named explicitly: the test sees the markup and the SELECTORS, but not the VALUES of
// the styles. Pair-specificity can hide in a value as well — a named typeface of a script
// ("Noto Sans CJK SC" in the font token) acts like `:lang(zh)`, only silently and without a
// selector. Exactly such a token did arrive in S2; it was removed by a human at review, not by a
// gate. The list of such boundaries is in `FRONTEND_PLAN.md` §5.4.1.
// The options are a literal at every call: `import.meta.glob` is parsed by the bundler statically
// and does not accept a hoisted constant.
const components = import.meta.glob('./**/*.tsx', {
query: '?raw',
eager: true,
import: 'default',
});
const styleSheets = import.meta.glob('./**/*.css', {
query: '?raw',
eager: true,
import: 'default',
});
it.each(Object.keys(components))('%s does not hard-wire a language into the markup', (file) => {
const literals = [...(components[file] as string).matchAll(/\blang=(["'][^"']*["'])/g)].map(
(m) => m[1],
);
expect(literals, 'lang is taken from the book data: lang={book.sourceLanguage}').toEqual([]);
});
it.each(Object.keys(styleSheets))('%s does not branch by language', (file) => {
// Comments are stripped: a rule is obliged to be explained in the same file where it applies, and
// the explanation contains the forbidden form verbatim — otherwise the test catches its own
// documentation.
const css = (styleSheets[file] as string).replaceAll(/\/\*[\S\s]*?\*\//g, '');
const selectors = [...css.matchAll(/:lang\([^)]*\)/g)].map((m) => m[0]);
expect(selectors, 'language specificity lives in the pair data, not in a selector').toEqual([]);
});