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