44 lines
2.4 KiB
TypeScript
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([]);
|
|
});
|