// Rôle : vérifie la persistance et le remplacement des images de toolbox. import { expect, test } from "@playwright/test"; import { readFile } from "node:fs/promises"; import { Buffer } from "node:buffer"; import { fileURLToPath, URL } from "node:url"; const imagePath = fileURLToPath(new URL("../website/public/static/img/toolbox-icons/sword.png", import.meta.url)); const fixture = JSON.parse(await readFile(new URL("./fixtures/toolbox-export.json", import.meta.url), "utf8")); const cover = `data:image/png;base64,${(await readFile(imagePath)).toString("base64")}`; const { games } = JSON.parse(await readFile(new URL("../website/public/data/games.json", import.meta.url), "utf8")); const gameCover = games.find((game) => game.id === "mhwilds").images.cardCover; function picker(page) { return page.getByRole("button", { name: "Changer l'image de la toolbox" }); } async function importToolbox(page, name, scope = page.getByText("Importer une toolbox", { exact: true })) { await scope.locator('input[type="file"][accept="application/json"]').setInputFiles({ name: "cover.json", mimeType: "application/json", buffer: Buffer.from(JSON.stringify({ ...fixture, toolbox: { ...fixture.toolbox, name, customCover: cover } })), }); } async function uploadCover(page, file = imagePath) { await picker(page).click(); const action = page.getByRole("button", { name: /Ajouter une image de card|Remplacer l'image de card/ }); await expect(action).toBeVisible(); const chooser = page.waitForEvent("filechooser"); await action.click(); await (await chooser).setFiles(file); return page.getByRole("dialog", { name: "Image de card", exact: true }); } async function exportToolbox(page, name) { const download = page.waitForEvent("download"); await page.getByRole("button", { name: `Exporter ${name}`, exact: true }).click(); return JSON.parse(await readFile(await (await download).path(), "utf8")); } test("saves a cropped cover through reload and export/import, then returns to an icon", async ({ page }) => { await page.goto("/#/toolboxes"); await page.getByRole("button", { name: "Nouvelle toolbox" }).click(); const creation = page.getByRole("dialog", { name: "Nouvelle toolbox" }); await creation.getByLabel("Nom").fill("Cover workflow"); await creation.getByRole("button", { name: "Créer" }).click(); await expect(picker(page).locator("img")).toHaveAttribute("src", /toolbox\.png$/); await picker(page).click(); await page.getByRole("button", { name: "Utiliser l'icône sword", exact: true }).click(); const editor = await uploadCover(page); await expect(editor.getByRole("button", { name: "Valider" })).toBeEnabled(); await editor.getByRole("button", { name: "Valider" }).click(); await expect(editor).toBeHidden(); const savedCover = await picker(page).locator("img").getAttribute("src"); expect(savedCover).toMatch(/^data:image\/(webp|jpeg);base64,/); await expect.poll(() => picker(page).locator("img").evaluate((img) => [img.naturalWidth, img.naturalHeight])).toEqual([620, 248]); await page.reload(); await expect(picker(page).locator("img")).toHaveAttribute("src", savedCover); await page.goto("/#/toolboxes"); await expect(page.getByRole("link", { name: "Ouvrir Cover workflow", exact: true }).first().locator("img")).toHaveAttribute("src", savedCover); const exported = await exportToolbox(page, "Cover workflow"); expect(exported.toolbox.customCover).toBe(savedCover); expect(exported.toolbox.icon).toMatch(/sword\.png$/); exported.toolbox.name = "Imported cover"; await page.getByText("Importer une toolbox", { exact: true }).locator('input[type="file"]').setInputFiles({ name: "export.json", mimeType: "application/json", buffer: Buffer.from(JSON.stringify(exported)), }); await page.getByRole("link", { name: "Ouvrir Imported cover", exact: true }).first().click(); await expect(picker(page).locator("img")).toHaveAttribute("src", savedCover); await picker(page).click(); await expect(page.getByRole("button", { name: "Remplacer l'image de card" })).toHaveAttribute("aria-pressed", "true"); await expect(page.getByRole("button", { name: "Utiliser l'icône sword", exact: true })).toHaveAttribute("aria-pressed", "false"); await page.getByRole("button", { name: "Utiliser l'icône sword", exact: true }).click(); await page.reload(); await expect(picker(page).locator("img")).toHaveAttribute("src", /sword\.png$/); await page.goto("/#/toolboxes"); expect((await exportToolbox(page, "Imported cover")).toolbox.customCover || "").toBe(""); }); test("cancelled and unreadable replacements preserve the cover and allow selecting the same file again", async ({ page }) => { await page.goto("/#/toolboxes"); await importToolbox(page, "Preserved cover"); await page.getByRole("link", { name: "Ouvrir Preserved cover", exact: true }).first().click(); let editor = await uploadCover(page); await expect(editor.getByRole("button", { name: "Valider" })).toBeEnabled(); await editor.getByRole("button", { name: "Annuler" }).click(); await expect(picker(page).locator("img")).toHaveAttribute("src", cover); editor = await uploadCover(page); await expect(editor.getByRole("button", { name: "Valider" })).toBeEnabled(); await editor.getByRole("button", { name: "Fermer", exact: true }).click(); editor = await uploadCover(page, { name: "broken.png", mimeType: "image/png", buffer: Buffer.from("not an image") }); await expect(editor.getByText("Image illisible. Choisis un autre fichier.")).toBeVisible(); await expect(editor.getByRole("button", { name: "Valider" })).toBeDisabled(); await editor.getByRole("button", { name: "Annuler" }).click(); await page.reload(); await expect(picker(page).locator("img")).toHaveAttribute("src", cover); }); test("game association takes precedence without discarding the custom cover", async ({ page }) => { await page.goto("/#/games/mhwilds"); await page.getByRole("tab", { name: "Toolbox", exact: true }).click(); const drawer = page.locator("#toolbox-drawer .drawer-panel"); await importToolbox(page, "Linked cover", drawer); await expect(drawer.getByRole("heading", { name: "Linked cover" })).toBeVisible(); await drawer.getByRole("button", { name: "Fermer", exact: true }).click(); await page.goto("/#/toolboxes"); const link = page.getByRole("link", { name: "Ouvrir Linked cover", exact: true }).first(); await expect(link.locator("img")).toHaveAttribute("src", gameCover); await expect.poll(() => link.locator("img").evaluate((img) => img.naturalWidth)).toBeGreaterThan(0); expect((await exportToolbox(page, "Linked cover")).toolbox.customCover).toBe(cover); await link.click(); await expect(page.locator(".toolbox-game-icon img")).toHaveAttribute("src", gameCover); await expect(picker(page)).toHaveCount(0); await page.goto("/#/games/mhwilds"); await page.getByRole("tab", { name: "Toolbox", exact: true }).click(); await drawer.getByRole("button", { name: "Lier une toolbox", exact: true }).click(); const dialog = page.getByRole("dialog", { name: "Lier une toolbox", exact: true }); await dialog.getByLabel("Toolbox associée").selectOption(""); await dialog.getByRole("button", { name: "Valider" }).click(); await drawer.getByRole("button", { name: "Fermer", exact: true }).click(); await page.goto("/#/toolboxes"); await expect(link.locator("img")).toHaveAttribute("src", cover); await link.click(); await expect(picker(page).locator("img")).toHaveAttribute("src", cover); });