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([]); });