mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-09-14 21:46:35 +00:00
test: remove v1 test, later need impl new ones
This commit is contained in:
@@ -1,8 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { TOOLS } from "../v1/registry";
|
||||
import { setLocale } from "./locale.svelte";
|
||||
import { normalizeForSearch, scoreDoc, type SearchDoc } from "./matching";
|
||||
import { toolSearchDoc } from "./tool-strings";
|
||||
|
||||
describe("normalizeForSearch", () => {
|
||||
it("нижний регистр", () => {
|
||||
@@ -52,50 +49,4 @@ describe("scoreDoc", () => {
|
||||
it("запрос с опечаткой ё/е всё равно находит", () => {
|
||||
expect(scoreDoc(doc, normalizeForSearch("повёрнут"))).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("кросс-языковой поиск на реальном реестре", () => {
|
||||
it("английский запрос находит инструмент при русской локали", () => {
|
||||
setLocale("ru");
|
||||
const hits = TOOLS.filter((tool) => {
|
||||
const s = scoreDoc(toolSearchDoc(tool), normalizeForSearch("rotate"));
|
||||
return s !== null && s > 0;
|
||||
}).map((tool) => tool.id);
|
||||
expect(hits).toContain("rotate-png");
|
||||
expect(hits).toContain("rotate-free-png");
|
||||
});
|
||||
|
||||
it("русский запрос находит инструмент при английской локали", () => {
|
||||
setLocale("en");
|
||||
try {
|
||||
for (const q of ["повернуть", "пово"]) {
|
||||
const hits = TOOLS.filter((tool) => {
|
||||
const s = scoreDoc(toolSearchDoc(tool), normalizeForSearch(q));
|
||||
return s !== null && s > 0;
|
||||
}).map((tool) => tool.id);
|
||||
expect(hits, `запрос «${q}» при en-локали`).toContain("rotate-png");
|
||||
expect(hits, `запрос «${q}» при en-локали`).toContain(
|
||||
"rotate-free-png",
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
setLocale("ru");
|
||||
}
|
||||
});
|
||||
|
||||
it("поиск работает и по описанию, не только по названию", () => {
|
||||
setLocale("en");
|
||||
try {
|
||||
const hits = TOOLS.filter((tool) => {
|
||||
const s = scoreDoc(
|
||||
toolSearchDoc(tool),
|
||||
normalizeForSearch("полупрозрачные"),
|
||||
);
|
||||
return s !== null && s > 0;
|
||||
}).map((tool) => tool.id);
|
||||
expect(hits).toContain("png-is-transparent");
|
||||
} finally {
|
||||
setLocale("ru");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isChainable, TOOLS } from "../v1/registry";
|
||||
import { normalizeForSearch, scoreDoc } from "./matching";
|
||||
import { toolSearchDoc } from "./tool-strings";
|
||||
|
||||
function hits(q: string): string[] {
|
||||
const nq = normalizeForSearch(q);
|
||||
return TOOLS.filter((t) => {
|
||||
const s = scoreDoc(toolSearchDoc(t), nq);
|
||||
return s !== null && s > 0;
|
||||
}).map((t) => t.id);
|
||||
}
|
||||
|
||||
describe("полнота поиска", () => {
|
||||
it("генератор (не chainable) находится поиском — раньше отфильтровывался", () => {
|
||||
expect(isChainable(TOOLS.find((t) => t.id === "color-wheel-png")!)).toBe(
|
||||
false,
|
||||
);
|
||||
expect(hits("color wheel")).toContain("color-wheel-png");
|
||||
});
|
||||
|
||||
it("анализатор-маска тоже ищется", () => {
|
||||
expect(hits("уникальных цветов")).toContain("unique-color-mask-png");
|
||||
});
|
||||
});
|
||||
@@ -1,96 +0,0 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { TOOLS } from "../v1/registry";
|
||||
import { LOCALE_TAGS } from "./dict";
|
||||
import { setLocale } from "./locale.svelte";
|
||||
import { normalizeForSearch, scoreDoc } from "./matching";
|
||||
import { t } from "./t";
|
||||
import { toolSearchDoc } from "./tool-strings";
|
||||
|
||||
afterEach(() => {
|
||||
setLocale("ru");
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
describe("Смоук §6 i18n", () => {
|
||||
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("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"],
|
||||
["download.busy", "Готовим файл", "Preparing file"],
|
||||
];
|
||||
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("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. Поиск кросс-языковой в обе стороны", () => {
|
||||
const ids = (q: string) =>
|
||||
TOOLS.filter((tool) => {
|
||||
const s = scoreDoc(toolSearchDoc(tool), normalizeForSearch(q));
|
||||
return s !== null && s > 0;
|
||||
}).map((tool) => tool.id);
|
||||
|
||||
setLocale("ru");
|
||||
let hits = ids("rotate");
|
||||
expect(hits).toContain("rotate-png");
|
||||
hits = ids("повер");
|
||||
expect(hits).toContain("rotate-png");
|
||||
|
||||
setLocale("en");
|
||||
hits = ids("пово");
|
||||
expect(hits).toContain("rotate-free-png");
|
||||
hits = ids("rotate");
|
||||
expect(hits).toContain("rotate-png");
|
||||
});
|
||||
|
||||
it("6b. Ё не мешает совпадению", () => {
|
||||
const blackWhite = TOOLS.find((tool) => tool.id === "black-and-white-png")!;
|
||||
setLocale("ru");
|
||||
const hit = scoreDoc(
|
||||
toolSearchDoc(blackWhite),
|
||||
normalizeForSearch("ЧЁРНО"),
|
||||
);
|
||||
expect(hit).not.toBeNull();
|
||||
const withoutYo = scoreDoc(
|
||||
toolSearchDoc(blackWhite),
|
||||
normalizeForSearch("черно"),
|
||||
);
|
||||
expect(withoutYo).not.toBeNull();
|
||||
});
|
||||
|
||||
it("7. Теги локалей для форматирования чисел корректны", () => {
|
||||
expect(LOCALE_TAGS.ru).toBe("ru-RU");
|
||||
expect(LOCALE_TAGS.en).toBe("en-US");
|
||||
});
|
||||
});
|
||||
@@ -1,63 +0,0 @@
|
||||
import type { ParamDef, ToolEntry } from "$lib/v1/registry";
|
||||
import { getMergedDict } from "./locale.svelte";
|
||||
import type { SearchDoc } from "./matching";
|
||||
import { ru } from "./ru";
|
||||
|
||||
/**
|
||||
* Строки инструмента на активной локали.
|
||||
* Русский текст живёт в самом реестре (базовая локаль),
|
||||
* переводы — в секции tools словарей; отсутствие ключа даёт фолбэк на реестр.
|
||||
*/
|
||||
export function toolTitle(tool: ToolEntry): string {
|
||||
return getMergedDict().tools[tool.id]?.title ?? tool.title;
|
||||
}
|
||||
|
||||
export function toolDescription(tool: ToolEntry): string {
|
||||
return getMergedDict().tools[tool.id]?.description ?? tool.description;
|
||||
}
|
||||
|
||||
export function paramLabel(tool: ToolEntry, param: ParamDef): string {
|
||||
const viaDict = getMergedDict().tools[tool.id]?.params?.[param.id];
|
||||
if (viaDict) return viaDict;
|
||||
const viaDef = param as { label?: string };
|
||||
return viaDef.label ?? param.id;
|
||||
}
|
||||
|
||||
export function optionLabel(
|
||||
tool: ToolEntry,
|
||||
param: ParamDef,
|
||||
value: string,
|
||||
): string {
|
||||
const viaDict = getMergedDict().tools[tool.id]?.options?.[param.id]?.[value];
|
||||
if (viaDict) return viaDict;
|
||||
if (param.type === "select") {
|
||||
return param.options.find((o) => o.value === value)?.label ?? value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function dedupe(values: string[]): string[] {
|
||||
return [...new Set(values.filter((v) => v.length > 0))];
|
||||
}
|
||||
|
||||
/**
|
||||
* Поисковый документ инструмента: строки активной локали, русского и английского
|
||||
* (реестр) вместе — запрос находит инструмент по любому из языков независимо
|
||||
* от того, какой сейчас включён.
|
||||
*/
|
||||
export function toolSearchDoc(tool: ToolEntry): SearchDoc {
|
||||
const active = getMergedDict().tools[tool.id];
|
||||
return {
|
||||
id: tool.id,
|
||||
titles: dedupe([
|
||||
active?.title ?? "",
|
||||
ru.tools[tool.id]?.title ?? "",
|
||||
tool.title,
|
||||
]),
|
||||
descriptions: dedupe([
|
||||
active?.description ?? "",
|
||||
ru.tools[tool.id]?.description ?? "",
|
||||
tool.description,
|
||||
]),
|
||||
};
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { TOOLS } from "../v1/registry";
|
||||
import { en } from "./en";
|
||||
import { ru } from "./ru";
|
||||
|
||||
describe("секция tools словарей покрывает реестр", () => {
|
||||
it("у каждого инструмента есть перевод ru с непустыми title/description", () => {
|
||||
for (const tool of TOOLS) {
|
||||
const strings = ru.tools[tool.id];
|
||||
expect(strings, `нет перевода для ${tool.id}`).toBeDefined();
|
||||
expect(strings?.title, `${tool.id}: title`).toBeTruthy();
|
||||
expect(strings?.description, `${tool.id}: description`).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
it("переведены подписи всех параметров", () => {
|
||||
for (const tool of TOOLS) {
|
||||
const params = ru.tools[tool.id]?.params ?? {};
|
||||
for (const param of tool.params) {
|
||||
const label = params[param.id];
|
||||
expect(
|
||||
label,
|
||||
`${tool.id}.${param.id}: нет перевода подписи`,
|
||||
).toBeDefined();
|
||||
expect(label?.length).toBeGreaterThan(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("переведены подписи всех опций select", () => {
|
||||
for (const tool of TOOLS) {
|
||||
const options = ru.tools[tool.id]?.options ?? {};
|
||||
for (const param of tool.params) {
|
||||
if (param.type !== "select") continue;
|
||||
const labels = options[param.id] ?? {};
|
||||
for (const option of param.options) {
|
||||
const label = labels[option.value];
|
||||
expect(
|
||||
label,
|
||||
`${tool.id}.${param.id}[${option.value}]: нет перевода`,
|
||||
).toBeDefined();
|
||||
expect(label.length).toBeGreaterThan(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
const tool = TOOLS.find((t) => t.id === id)!;
|
||||
const paramIds = new Set(tool.params.map((p) => p.id));
|
||||
for (const paramId of Object.keys(dict[id].params ?? {})) {
|
||||
expect(
|
||||
paramIds.has(paramId),
|
||||
`${id}: лишний параметр ${paramId}`,
|
||||
).toBe(true);
|
||||
}
|
||||
const selectIds = new Set(
|
||||
tool.params.filter((p) => p.type === "select").map((p) => p.id),
|
||||
);
|
||||
for (const paramId of Object.keys(dict[id].options ?? {})) {
|
||||
expect(
|
||||
selectIds.has(paramId),
|
||||
`${id}: options у не-select параметра ${paramId}`,
|
||||
).toBe(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("тексты-результаты анализаторов переведены в обоих словарях", () => {
|
||||
const expectations: Array<[string, string[]]> = [
|
||||
["png-is-grayscale", ["grayscaleYes", "grayscaleNo"]],
|
||||
["png-is-transparent", ["transparentYes", "transparentNo"]],
|
||||
[
|
||||
"png-orientation",
|
||||
["orientationPortrait", "orientationLandscape", "orientationSquare"],
|
||||
],
|
||||
];
|
||||
for (const [id, keys] of expectations) {
|
||||
for (const key of keys) {
|
||||
expect(en.tools[id]?.results?.[key], `${id}.${key} (en)`).toBeDefined();
|
||||
expect(ru.tools[id]?.results?.[key], `${id}.${key} (ru)`).toBeDefined();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user