// Rôle : vérifie les modales d'échange texte, les erreurs et la persistance des imports réels. import { expect, test } from "@playwright/test"; import { Buffer } from "node:buffer"; async function setup(page) { await page.goto("/#/toolboxes"); await page.getByText("Importer une toolbox", { exact: true }).locator('input[type="file"]').setInputFiles({ name: "exchange.json", mimeType: "application/json", buffer: Buffer.from(JSON.stringify({ toolbox: { id: "exchange", name: "Text exchange", modules: [ { id: "table", type: "table", title: "Tableau" }, { id: "calc", type: "calculator", title: "Calculateur" }, ] }, modules: { table: { rows: 2, columns: 2, cells: {} }, calc: { entries: [{ id: "original", label: "Original", value: 7, formula: "7" }] } }, })), }); await page.getByRole("link", { name: "Ouvrir Text exchange", exact: true }).first().click(); } test("table text import/export preserves formulas and labels, copies read-only text and resets cancelled drafts", async ({ page, context }) => { await context.grantPermissions(["clipboard-read", "clipboard-write"]); await setup(page); const table = page.locator('[data-module-type="table"]'); const importButton = table.getByRole("button", { name: "Importer TSV", exact: true }); await expect(importButton).toBeHidden(); await table.getByRole("button", { name: "Afficher l'import/export de Tableau", exact: true }).click(); await expect(table.getByRole("button", { name: "Exporter TSV", exact: true })).toBeDisabled(); const originalOverflow = await page.locator("body").evaluate((body) => body.style.overflow); await importButton.click(); const dialog = page.getByRole("dialog", { name: "Importer un tableau TSV", exact: true }); await expect(dialog.getByRole("textbox")).toBeFocused(); await expect(page.locator("body")).toHaveCSS("overflow", "hidden"); await dialog.getByRole("textbox").fill("cancelled"); await dialog.getByRole("button", { name: "Annuler", exact: true }).click(); await expect.poll(() => page.locator("body").evaluate((body) => body.style.overflow)).toBe(originalOverflow); await importButton.click(); await expect(dialog.getByRole("textbox")).toHaveValue(""); await dialog.getByRole("button", { name: "Importer le TSV", exact: true }).click(); await expect(dialog.getByRole("alert")).toContainText("Le texte à importer est vide."); await expect(dialog.getByRole("textbox")).toHaveAttribute("aria-invalid", "true"); const text = "@size: 2x2\n@columns: Quantity\tTotal\n@rows: Ore\tBars\n@tsv\n3\t=A1*2\n5\t=A2*2"; await dialog.getByRole("textbox").fill(text); await expect(dialog.getByRole("alert")).toHaveCount(0); await expect(dialog.getByRole("textbox")).toHaveAttribute("aria-invalid", "false"); await dialog.getByRole("button", { name: "Importer le TSV", exact: true }).click(); await expect(dialog).toBeHidden(); await expect(table.getByRole("textbox", { name: "Cellule B2", exact: true })).toHaveValue("10"); await page.reload(); await expect(table.getByRole("textbox", { name: "Intitulé de colonne A", exact: true })).toHaveValue("Quantity"); await expect(table.getByRole("textbox", { name: "Intitulé de ligne 1", exact: true })).toHaveValue("Ore"); await expect(table.getByRole("textbox", { name: "Cellule B1", exact: true })).toHaveValue("6"); await table.getByRole("button", { name: "Afficher l'import/export de Tableau", exact: true }).click(); await table.getByRole("button", { name: "Exporter TSV", exact: true }).click(); const exported = page.getByRole("dialog", { name: "Exporter un tableau TSV", exact: true }); await expect(exported.getByRole("textbox")).toHaveAttribute("readonly", ""); await expect(exported.getByRole("textbox")).toHaveValue(text); await exported.getByRole("button", { name: "Copier le TSV", exact: true }).click(); await expect.poll(() => page.evaluate(() => globalThis.navigator.clipboard.readText())).toBe(text); await expect(exported).toBeVisible(); await exported.getByRole("textbox").press("Escape"); await expect(exported).toBeHidden(); await expect.poll(() => page.locator("body").evaluate((body) => body.style.overflow)).toBe(originalOverflow); await table.getByRole("button", { name: "Masquer l'import/export de Tableau", exact: true }).click(); await expect(importButton).toBeHidden(); }); test("invalid calculator imports preserve existing results and a corrected import replaces them with a persistent hierarchy", async ({ page }) => { await setup(page); const calc = page.locator('[data-module-type="calculator"]'); await calc.getByRole("button", { name: "Afficher l'import/export de Calculateur", exact: true }).click(); const openImport = calc.getByRole("button", { name: "Importer du texte", exact: true }); await openImport.click(); const dialog = page.getByRole("dialog", { name: "Importer des résultats", exact: true }); await dialog.getByRole("textbox").fill("invalid"); await dialog.getByRole("button", { name: "Importer le texte", exact: true }).click(); await expect(dialog.getByRole("alert")).toContainText("ligne calculateur invalide"); await expect(calc.locator(".calculator-entry em")).toHaveText(["Original"]); await dialog.getByRole("button", { name: "Fermer", exact: true }).click(); await page.reload(); await expect(calc.locator(".calculator-entry em")).toHaveText(["Original"]); await calc.getByRole("button", { name: "Afficher l'import/export de Calculateur", exact: true }).click(); await openImport.click(); await expect(dialog.getByRole("textbox")).toHaveValue(""); await expect(dialog.getByRole("alert")).toHaveCount(0); const text = "@scrollResults: true\nOre: 10 | formula=10\n Bars: 20 | formula=base*2"; await dialog.getByRole("textbox").fill(text); await dialog.getByRole("button", { name: "Importer le texte", exact: true }).click(); await expect(dialog).toBeHidden(); await expect(calc.locator(".calculator-entry em")).toHaveText(["Ore", "Bars"]); await page.reload(); await expect(calc.locator(".calculator-entry em")).toHaveText(["Ore", "Bars"]); await expect(calc.getByRole("button", { name: "Liste scrollable", exact: true })).toHaveAttribute("aria-pressed", "true"); await calc.getByRole("button", { name: "Afficher l'import/export de Calculateur", exact: true }).click(); await calc.getByRole("button", { name: "Exporter en format texte", exact: true }).click(); const exported = page.getByRole("dialog", { name: "Exporter des résultats", exact: true }); await expect(exported.getByRole("textbox")).toHaveValue(text); await page.locator(".text-import-modal-root .confirm-backdrop").click({ position: { x: 10, y: 10 } }); await expect(exported).toBeHidden(); });