textmachine/frontend/src/generality.test.ts

41 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { expect, it } from 'vitest';
// Ревью-вопрос канона по умолчанию: «заработает ли пара языков, которой в репо ещё НЕТ,
// без правки кода?» (CLAUDE.md §2). На фронте эта норма ломается двумя способами: код
// письменности, зашитый в разметку (`lang="zh"` переживёт приезд ja→ru молча, и японские кандзи
// отрисуются китайскими начертаниями, а скринридер возьмёт китайский голос), и языковая ветка
// в стилях (`:lang(zh) { … }` — та же пар-специфика, только записанная в CSS: новой паре
// понадобилась бы правка стилей). Первое найдено ревью S1 в живом коде витрины, второе — ревью
// оркестратора S2, поэтому проверяется машиной, а не глазами.
//
// ⚠ ГРАНИЦА, названная явно: тест видит разметку и СЕЛЕКТОРЫ, но не ЗНАЧЕНИЯ стилей. Пар-специфика
// умеет прятаться и в значении — именованная гарнитура письменности («Noto Sans CJK SC» в токене
// шрифта) действует как `:lang(zh)`, только молча и без селектора. Ровно такой токен и приехал
// в S2; снял его человек на ревью, не гейт. Перечень таких границ — `FRONTEND_PLAN.md` §5.4.1.
// Опции — литерал на каждом вызове: `import.meta.glob` разбирается сборщиком статически
// и вынесенную константу не принимает.
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 не зашивает язык в разметку', (file) => {
const literals = [...(components[file] as string).matchAll(/\blang=(["'][^"']*["'])/g)].map(
(m) => m[1],
);
expect(literals, 'lang берётся из данных книги: lang={book.sourceLanguage}').toEqual([]);
});
it.each(Object.keys(styleSheets))('%s не ветвится по языку', (file) => {
// Комментарии снимаются: правило обязано объясняться в том же файле, где действует, а
// объяснение содержит запрещённую форму дословно — иначе тест ловит собственную документацию.
const css = (styleSheets[file] as string).replaceAll(/\/\*[\S\s]*?\*\//g, '');
const selectors = [...css.matchAll(/:lang\([^)]*\)/g)].map((m) => m[0]);
expect(selectors, 'языковая специфика живёт в данных пары, а не в селекторе').toEqual([]);
});