import { createElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { describe, expect, it } from "vitest"; import { GuideImageExport, imageExportFileName, scaledExportFontSize, } from "@/components/public/guide-image-export"; import { MaterialPoster } from "@/components/public/material-poster"; import { ConstellationImage } from "@/components/public/constellation-image"; import { WeaponComparisonImage } from "@/components/public/weapon-image-export"; import { TeamExportImage, type TeamExportData } from "@/components/public/team-search"; import type { PublicGuide } from "@/lib/guides/queries"; describe("image export controls", () => { it.each([1, 4])("renders the shared Team Calc image with %i member(s)", (memberCount) => { const members: TeamExportData["members"] = Array.from({ length: memberCount }, (_, position) => ({ position, constellation: String(position), refinement: "1", damage: "1000", character: { key: `character-${position}`, name: `Character ${position + 1}`, imageUrl: "/character.png", element: "Fire", rarity: 5, }, weapon: { key: `weapon-${position}`, name: `Weapon ${position + 1}`, imageUrl: "/weapon.png", rarity: 5, }, artifact: { key: `artifact-${position}`, name: `Artifact ${position + 1}`, imageUrl: "/artifact.png", rarity: 5, }, secondArtifact: null, })); const team: TeamExportData = { title: "Generator Team", rotationSeconds: "20", combo: "E > Q", members, }; const html = renderToStaticMarkup(); expect(html).toContain("Generator Team"); expect(html).toContain("Rotation duration: 20 seconds"); expect(html).toContain("E > Q"); expect(html).toContain(`DPS: ${memberCount * 50}`); expect(html.match(/Character \d/gu)).toHaveLength(memberCount); }); it("omits the placeholder condition from weapon export images", () => { const guide = { character: { name: "Raiden", splashImageUrl: "/splash.png" }, coverUrl: "/cover.png", } as PublicGuide; const group = { rows: [], name: null, note: null } as unknown as PublicGuide["weaponGroups"][number]; const html = renderToStaticMarkup( , ); expect(html).toContain("เปรียบเทียบอาวุธ Raiden"); expect(html).toContain("Raiden’s Weapon Comparison"); expect(html).not.toContain("Condition 1"); expect(html).not.toContain("เงื่อนไข 1"); }); it("shows one export trigger and keeps settings in its dialog", () => { const html = renderToStaticMarkup(No notes); expect(html).toContain("บันทึกภาพ"); expect(html.match(/ { const html = renderToStaticMarkup(Content); expect(html).toContain("บันทึกภาพ"); expect(html.match(/ { const html = renderToStaticMarkup( Poster , ); expect(html).toContain("บันทึกภาพ"); expect(html).not.toContain("ตั้งค่าภาพที่จะบันทึก"); expect(html.match(/ { const html = renderToStaticMarkup( Content , ); expect(html).toContain("รวมโน้ต"); expect(html).toContain("ไม่รวมโน้ต"); expect(html).toContain("ไทย"); expect(html).toContain("English"); expect(html).toContain("ตัวอย่างภาพส่งออก"); expect(html).toContain("h-[clamp(8rem,50svh,32rem)]"); expect(html).toContain('data-slot="dialog-footer"'); expect(html).toContain("sticky bottom-0"); expect(html.match(/>บันทึกภาพ<\/button>/gu)).toHaveLength(1); }); it("hides the persistent language chooser when language is prompted on export", () => { const html = renderToStaticMarkup( Content , ); expect(html).not.toContain("ภาษาในภาพส่งออก"); expect(html).not.toContain("English"); expect(html.match(/ { const html = renderToStaticMarkup(No sections); expect(html.match(/\sdisabled=""/gu)).toHaveLength(1); }); 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(createElement(ConstellationImage, { guide, group })); expect(html).toContain("Raiden’s Constellations"); expect(html).toContain("กลุ่มดาว Raiden"); expect(html).toContain("Team Damage"); expect(html).toContain("ดาเมจทั้งทีม"); expect(html).toContain('data-export-note="true"'); expect(html).toContain(note); }); }); describe("image export filenames", () => { it("adds the selected language after the note variant", () => { expect(imageExportFileName("Raiden.png", "th", false)).toBe("Raiden-th.png"); expect(imageExportFileName("Raiden.png", "en", true)).toBe("Raiden-without-notes-en.png"); }); it("can preserve English-only export filenames", () => { expect(imageExportFileName("Raiden-materials.png", "th", false, false)).toBe("Raiden-materials.png"); }); }); describe("image export text fitting", () => { it("keeps the original font size when text already fits", () => { expect(scaledExportFontSize(32, 180, 240)).toBe(32); }); it("scales overflowing text to the available width", () => { expect(scaledExportFontSize(32, 320, 240)).toBe(24); }); it("has no minimum font-size floor for exceptionally long text", () => { expect(scaledExportFontSize(32, 3200, 100)).toBe(1); }); }); describe("material poster text", () => { const props = { character: { name: "Raiden", nameCardImageUrl: null, baseStats: [], ascension: [], leveling: [] }, characterArtUrl: null, artworkOffsetX: 0, artworkOffsetY: 0, artworkScale: 100, artworkRotation: 0, topColors: ["#000000", "#000000"] as [string, string], bottomColors: ["#000000", "#000000"] as [string, string], }; it("always uses English, including on the website", () => { const html = renderToStaticMarkup(); expect(html).toContain("MATERIALS"); expect(html).toContain("LV.90"); expect(html).not.toContain("วัสดุ"); expect(html).not.toContain("เลเวล 90"); }); it("shows a fifth base stat in its own box", () => { const html = renderToStaticMarkup( , ); expect(html).toContain("Energy Recharge%"); expect(html).toContain("132%"); expect(html).toContain("h-[7cqw]"); expect(html).toContain("top-[49%]"); }); it("optimizes scale-aware character artwork and bleeding layers for export", () => { const characterArtUrl = "https://buzz-cdn.astrxl.dev/posters/raiden.png"; const html = renderToStaticMarkup( , ); expect(html).toContain("/_next/image?url=https%3A%2F%2Fbuzz-cdn.astrxl.dev%2Fposters%2Fraiden.png"); expect(html).toContain("w=3840"); expect(html).toContain('sizes="3200px"'); expect(html).not.toContain(`src="${characterArtUrl}"`); }); it("rotates the character artwork and bleeding layers together", () => { const html = renderToStaticMarkup( , ); expect(html).toContain("rotate(45deg)"); }); });