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 { describe, expect, it } from "vitest";
|
||||||
import { TOOLS } from "../v1/registry";
|
|
||||||
import { setLocale } from "./locale.svelte";
|
|
||||||
import { normalizeForSearch, scoreDoc, type SearchDoc } from "./matching";
|
import { normalizeForSearch, scoreDoc, type SearchDoc } from "./matching";
|
||||||
import { toolSearchDoc } from "./tool-strings";
|
|
||||||
|
|
||||||
describe("normalizeForSearch", () => {
|
describe("normalizeForSearch", () => {
|
||||||
it("нижний регистр", () => {
|
it("нижний регистр", () => {
|
||||||
@@ -53,49 +50,3 @@ describe("scoreDoc", () => {
|
|||||||
expect(scoreDoc(doc, normalizeForSearch("повёрнут"))).not.toBeNull();
|
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