test: update i18n tests

This commit is contained in:
2026-09-13 09:29:28 +05:00
parent 371387ce81
commit b3e2735fc3
7 changed files with 216 additions and 45 deletions
+18 -30
View File
@@ -525,64 +525,52 @@ export const ru: Dict = {
},
},
"png-to-hsl": {
title: "Разбить PNG на HSL",
description:
"Раскладывает изображение на компоненты тона, насыщенности и светлоты.",
options: {
display: { gray: "Оттенки серого", color: "Пространство как RGB" },
},
},
"png-to-hsv": {
title: "Разбить PNG на HSV",
description:
"Раскладывает изображение на компоненты тона, насыщенности и значения (яркости).",
options: {
display: { gray: "Оттенки серого", color: "Пространство как RGB" },
},
},
"png-to-hsi": {
title: "Разбить PNG на HSI",
description:
"Раскладывает изображение на компоненты тона, насыщенности и интенсивности.",
options: {
display: { gray: "Оттенки серого", color: "Пространство как RGB" },
},
},
"png-to-cmyk": {
title: "Конвертировать PNG в цвета CMYK",
description:
"Раскладывает изображение на печатные компоненты Cyan, Magenta, Yellow и Key (чёрный).",
options: {
display: { gray: "Оттенки серого", color: "Пространство как RGB" },
},
},
"png-to-ycbcr": {
title: "Конвертировать PNG в цвета YCbCr",
description:
"Раскладывает изображение на яркость (Y) и цветоразностные компоненты Cb / Cr.",
options: {
display: { gray: "Оттенки серого", color: "Пространство как RGB" },
},
},
"png-to-lab": {
options: {
display: { gray: "Оттенки серого", color: "Пространство как RGB" },
},
},
cmyk: {
title: "Конвертировать PNG в цвета CMYK",
description:
"Раскладывает изображение на печатные компоненты Cyan, Magenta, Yellow и Key (чёрный).",
},
hsl: {
title: "Разбить PNG на HSL",
description:
"Раскладывает изображение на компоненты тона, насыщенности и светлоты.",
},
hsi: {
title: "Разбить PNG на HSI",
description:
"Раскладывает изображение на компоненты тона, насыщенности и интенсивности.",
},
hsv: {
title: "Разбить PNG на HSV",
description:
"Раскладывает изображение на компоненты тона, насыщенности и значения (яркости).",
},
lab: {
title: "Конвертировать PNG в цвета LAB",
description:
"Раскладывает изображение на перцепционную светлоту и оппонентные пары зелёный–пурпур / синий–жёлтый.",
},
ycbcr: {
title: "Конвертировать PNG в цвета YCbCr",
description:
"Раскладывает изображение на яркость (Y) и цветоразностные компоненты Cb / Cr.",
options: {
display: { gray: "Оттенки серого", color: "Пространство как RGB" },
},
},
"black-and-white-png": {
title: "Чёрно-белый PNG по порогу",
+87
View File
@@ -0,0 +1,87 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { getLocale, setLocale } from "./locale.svelte";
import { LOCALE_TAGS } from "./dict";
import { normalizeForSearch } from "./matching";
import { t } from "./t";
import { getTool, TOOLS } from "../registry";
import { toolTitle } from "./schema-tool-strings";
afterEach(() => {
setLocale("ru");
vi.unstubAllGlobals();
});
describe("Смоук §6 i18n (новый UI)", () => {
it("1. html lang следует за локалью", () => {
const doc = { documentElement: { lang: "" } };
vi.stubGlobal("document", doc);
setLocale("en");
expect(doc.documentElement.lang).toBe("en");
setLocale("ru");
expect(doc.documentElement.lang).toBe("ru");
});
it("2. getLocale отражает последний выбор", () => {
setLocale("en");
expect(getLocale()).toBe("en");
setLocale("ru");
expect(getLocale()).toBe("ru");
});
it("3. Ключевые секции переведены без смеси языков", () => {
const samples: Array<[string, string, string]> = [
["header.workspace", "Рабочая область", "Workspace"],
["catalog.heading", "Каталог инструментов", "Tool catalog"],
[
"home.heroTitle",
"Что делаем с изображением?",
"What do you want to do",
],
["chain.inputLegend", "Вход", "Input"],
["resultCard.nextTool", "Следующий инструмент", "Next tool"],
["paramsCard.toolSettings", "Настройки инструмента", "Tool settings"],
["download.busy", "Готовим файл", "Preparing file"],
["dropZone.pickDefault", "Перетащите изображение", "Drop an image"],
];
for (const [key, ruPart, enPart] of samples) {
setLocale("ru");
expect(t(key), key + " @ru").toContain(ruPart);
setLocale("en");
expect(t(key), key + " @en").toContain(enPart);
}
});
it("4. Заголовок инструмента переключается локалями (schema-tool-strings)", () => {
const tool = getTool("crop-png") ?? TOOLS[0];
setLocale("ru");
expect(toolTitle(tool)).toContain("Обрезать");
setLocale("en");
expect(toolTitle(tool)).toBe(tool.title);
});
it("5. Ошибки с vars локализуются на оба языка", () => {
setLocale("ru");
expect(t("errors.badHex", { value: "#zz" })).toBe(
'Некорректный HEX-цвет: "#zz"',
);
expect(t("errors.toolNotFound", { id: "x" })).toContain("не найден");
setLocale("en");
expect(t("errors.badHex", { value: "#zz" })).toBe(
'Invalid HEX color: "#zz"',
);
});
it("6. Ё не мешает нормализации поискового запроса", () => {
expect(normalizeForSearch("ЧЁРНО") === normalizeForSearch("ЧЕРНО")).toBe(
true,
);
expect(normalizeForSearch("ёлка") === normalizeForSearch("елка")).toBe(
true,
);
});
it("7. Теги локалей для форматирования чисел корректны", () => {
expect(LOCALE_TAGS.ru).toBe("ru-RU");
expect(LOCALE_TAGS.en).toBe("en-US");
});
});
+72
View File
@@ -0,0 +1,72 @@
import { describe, expect, it } from "vitest";
import { TOOLS, type ToolEntry } from "../registry";
import { en } from "./en";
import { ru } from "./ru";
function toolKeys(tool: ToolEntry): { fields: string[]; groups: string[] } {
const fields: string[] = [];
for (const field of Object.values(tool.schema?.fields ?? {})) {
const label = (field as { spec?: { label?: string } }).spec?.label;
if (label) fields.push(label.replace(/^fields\./, ""));
}
const groups = (tool.schema?.layout?.groups ?? []).map((g) =>
(g.title ?? "").replace(/^groups\./, ""),
);
return { fields, groups };
}
describe("полнота словарей для нового registry", () => {
it("у каждого инструмента есть перевод ru с непустыми title/description", () => {
for (const tool of TOOLS) {
const strings = ru.tools[tool.id];
expect(strings, `нет перевода ru для ${tool.id}`).toBeDefined();
expect(strings?.title, `${tool.id}: title`).toBeTruthy();
expect(strings?.description, `${tool.id}: description`).toBeTruthy();
}
});
it("каждый label-ключ поля схемы переведён в fields обоих словарей", () => {
for (const tool of TOOLS) {
for (const key of toolKeys(tool).fields) {
expect(en.fields?.[key], `${tool.id}: ${key} в en`).toBeTruthy();
expect(ru.fields?.[key], `${tool.id}: ${key} в ru`).toBeTruthy();
}
}
});
it("каждый groups-ключ схемы переведён в groups обоих словарей", () => {
for (const tool of TOOLS) {
for (const key of toolKeys(tool).groups) {
expect(en.groups?.[key], `${tool.id}: ${key} в en`).toBeTruthy();
expect(ru.groups?.[key], `${tool.id}: ${key} в ru`).toBeTruthy();
}
}
});
it("в словарях нет лишних инструментов", () => {
const ids = new Set(TOOLS.map((tool) => tool.id));
for (const dict of [ru.tools, en.tools]) {
for (const id of Object.keys(dict)) {
expect(ids.has(id), `лишний инструмент в словаре: ${id}`).toBe(true);
}
}
});
it("в fields и groups нет ключей, не используемых схемами", () => {
const usedFields = new Set<string>();
const usedGroups = new Set<string>();
for (const tool of TOOLS) {
const keys = toolKeys(tool);
keys.fields.forEach((k) => usedFields.add(k));
keys.groups.forEach((k) => usedGroups.add(k));
}
for (const dict of [en, ru]) {
for (const key of Object.keys(dict.fields ?? {})) {
expect(usedFields.has(key), `лишний fields-ключ: ${key}`).toBe(true);
}
for (const key of Object.keys(dict.groups ?? {})) {
expect(usedGroups.has(key), `лишний groups-ключ: ${key}`).toBe(true);
}
}
});
});