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(No notes);
expect(html).toContain("Export image");
expect(html).toContain("Export without note");
expect(html.match(/