82 lines
4.6 KiB
TypeScript
82 lines
4.6 KiB
TypeScript
import { createElement } from "react";
|
||
import { renderToStaticMarkup } from "react-dom/server";
|
||
import { describe, expect, it } from "vitest";
|
||
|
||
import { LocaleBoundary } from "@/components/i18n/locale-provider";
|
||
import { GuideImageExport } from "@/components/public/guide-image-export";
|
||
import { ConstellationImage } from "@/components/public/constellation-image";
|
||
import type { PublicGuide } from "@/lib/guides/queries";
|
||
import { exportMessages, exportStatLabel, exportText, withoutNotesFileName, type ExportMessageKey } from "@/lib/guides/export-text";
|
||
|
||
describe("export translations", () => {
|
||
it("keeps matching keys and named placeholders in both languages", () => {
|
||
expect(Object.keys(exportMessages.th).sort()).toEqual(Object.keys(exportMessages.en).sort());
|
||
for (const key of Object.keys(exportMessages.en) as ExportMessageKey[]) {
|
||
const placeholders = (text: string) => [...text.matchAll(/\{(\w+)\}/gu)].map((match) => match[1]).sort();
|
||
expect(placeholders(exportMessages.th[key]), key).toEqual(placeholders(exportMessages.en[key]));
|
||
expect(exportMessages.en[key], key).not.toMatch(/[\u0e00-\u0e7f]/u);
|
||
}
|
||
});
|
||
|
||
it("defaults to English and interpolates names without interpreting their contents", () => {
|
||
expect(exportText("weaponTitle", { name: "Raiden" })).toBe("Raiden Weapons");
|
||
expect(exportText("rotation", { seconds: "20.5" })).toBe("Rotation duration: 20.5 seconds");
|
||
expect(exportText("weaponTitle", { name: "{seconds}" })).toBe("{seconds} Weapons");
|
||
expect(exportText("condition", { index: 2 }, "th")).toBe("Condition 2");
|
||
});
|
||
|
||
it("keeps technical labels in English while localising export controls", () => {
|
||
for (const key of ["materials", "materialLevel", "weaponTitle", "talents", "mainStats", "teamDamage"] as const) {
|
||
expect(exportText(key, { name: "Raiden", level: 90 }, "th")).toBe(exportText(key, { name: "Raiden", level: 90 }, "en"));
|
||
}
|
||
expect(exportText("materials", {}, "th")).toBe("MATERIALS");
|
||
expect(exportText("materialLevel", { level: 90 }, "th")).toBe("LV.90");
|
||
expect(exportText("exportImage", {}, "th")).toBe("ส่งออกภาพ");
|
||
});
|
||
|
||
it("uses English stat names for exports while retaining Thai page labels and custom values", () => {
|
||
expect(exportStatLabel("EM")).toBe("Elemental Mastery");
|
||
expect(exportStatLabel("CRIT DAMAGE")).toBe("CRIT DMG");
|
||
expect(exportStatLabel("EM", "th")).toBe("ความชำนาญธาตุ");
|
||
expect(exportStatLabel("Pyro DMG%")).toBe("Pyro DMG%");
|
||
expect(exportStatLabel("Custom value")).toBe("Custom value");
|
||
});
|
||
|
||
it("distinguishes no-note downloads while preserving the image extension", () => {
|
||
expect(withoutNotesFileName("guide.png")).toBe("guide-without-notes.png");
|
||
expect(withoutNotesFileName("guide.v2.webp")).toBe("guide.v2-without-notes.webp");
|
||
expect(withoutNotesFileName("guide")).toBe("guide-without-notes");
|
||
});
|
||
});
|
||
|
||
describe("image export controls", () => {
|
||
it("always offers both export modes even without notes", () => {
|
||
const html = renderToStaticMarkup(<LocaleBoundary locale="en"><GuideImageExport allowWithoutNote fileName="guide.png">No notes</GuideImageExport></LocaleBoundary>);
|
||
expect(html).toContain("Export image");
|
||
expect(html).toContain("Export without note");
|
||
expect(html.match(/<button\b/gu)).toHaveLength(2);
|
||
});
|
||
|
||
it("only offers normal export unless note omission is enabled", () => {
|
||
const html = renderToStaticMarkup(<LocaleBoundary locale="en"><GuideImageExport fileName="overview.png">Overview</GuideImageExport></LocaleBoundary>);
|
||
expect(html.match(/<button\b/gu)).toHaveLength(1);
|
||
expect(html).toContain("Export image");
|
||
expect(html).not.toContain("Export without note");
|
||
});
|
||
|
||
it("disables both modes when an export selection is empty", () => {
|
||
const html = renderToStaticMarkup(<GuideImageExport allowWithoutNote fileName="guide.png" disabled>No sections</GuideImageExport>);
|
||
expect(html.match(/\sdisabled=""/gu)).toHaveLength(2);
|
||
});
|
||
|
||
it.each(["English note", "โน้ตภาษาไทย"])("marks constellation notes for omission regardless of language: %s", (note) => {
|
||
const guide = { character: { name: "Raiden", splashImageUrl: "/splash.png" }, coverUrl: "/cover.png" } as PublicGuide;
|
||
const group = { rows: [], note } as unknown as PublicGuide["constellationGroups"][number];
|
||
const html = renderToStaticMarkup(<LocaleBoundary locale="en">{createElement(ConstellationImage, { guide, group })}</LocaleBoundary>);
|
||
expect(html).toContain("Raiden’s Constellations");
|
||
expect(html).toContain("Team Damage");
|
||
expect(html).toContain('data-export-note="true"');
|
||
expect(html).toContain(note);
|
||
});
|
||
});
|