// Rôle : vérifie la gestion, la limite et les déplacements entre espaces toolbox. import { expect, test } from "@playwright/test"; import { resetSokkoStorage } from "./helpers/storage.js"; async function seedToolbox(page, toolbox, links = {}) { await resetSokkoStorage(page); await page.evaluate(async ({ value, links }) => { const db = await import("/src/utils/indexedDbStorage.js"); await db.setToolboxSnapshot([value], links, {}); }, { value: toolbox, links }); await page.goto(`/#/toolbox/${toolbox.id}`); await page.reload(); } async function addSpace(page, name) { await page.getByRole("button", { name: "Nouvel espace", exact: true }).click(); const dialog = page.getByRole("dialog", { name: "Créer un espace" }); await dialog.getByLabel("Nom").fill(name); await dialog.getByRole("button", { name: "Enregistrer" }).click(); } async function dragModuleToTab(page, title, tab, hoverMs = 0) { await page.getByRole("button", { name: `Déplacer ${title}`, exact: true }).hover(); await page.mouse.down(); const box = await tab.boundingBox(); await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2, { steps: 4 }); if (hoverMs) await page.waitForTimeout(hoverMs); await page.mouse.up(); } test("creates at most five spaces and moves tools with the accessible selector", async ({ page }) => { await seedToolbox(page, { id: "spaces", name: "Spaces", modules: [{ id: "a", type: "counters", title: "Alpha" }], moduleOrder: { one: ["a"], two: [["a"], []] } }); await expect(page.getByRole("tab")).toHaveCount(0); await addSpace(page, "Boss"); const compactAddButton = page.getByRole("button", { name: "Nouvel espace", exact: true }); await expect(compactAddButton).toHaveClass(/toolbox-space-add-compact/); await expect(compactAddButton).not.toContainText("Nouvel espace"); for (const name of ["Farm", "Build", "Notes"]) await addSpace(page, name); await expect(page.getByRole("tab")).toHaveCount(5); await expect(page.getByRole("button", { name: "Limite de 5 espaces atteinte" })).toBeDisabled(); await page.getByRole("button", { name: "Renommer l’espace actif" }).click(); const renameDialog = page.getByRole("dialog", { name: "Renommer l’espace" }); await renameDialog.getByLabel("Nom").fill("Divers"); await renameDialog.getByRole("button", { name: "Enregistrer" }).click(); await expect(page.getByRole("tab", { name: "Divers" })).toHaveAttribute("aria-selected", "true"); await page.getByRole("tab", { name: "Défaut" }).click(); const alpha = page.locator("[data-module-id='a']"); await alpha.getByRole("button", { name: "Configuration", exact: true }).click(); const organization = alpha.locator(".module-configuration-organization"); const data = alpha.locator(".module-configuration-data"); await expect(organization).toBeVisible(); await expect(data).toBeVisible(); const organizationBox = await organization.boundingBox(); const dataBox = await data.boundingBox(); expect(dataBox.y).toBeCloseTo(organizationBox.y, 0); expect(dataBox.x).toBeGreaterThan(organizationBox.x + organizationBox.width); expect(dataBox.width).toBeCloseTo(organizationBox.width, 0); await alpha.getByRole("combobox", { name: "Déplacer vers un autre espace", exact: true }).selectOption({ label: "Divers" }); await expect(page.locator("[data-module-id='a']")).toHaveCount(0); await page.getByRole("tab", { name: "Divers" }).click(); await expect(page.locator("[data-module-id='a']")).toBeVisible(); await page.getByRole("button", { name: "Supprimer l’espace actif" }).click(); await page.getByRole("dialog", { name: "Supprimer l’espace" }).getByRole("button", { name: "Supprimer l’espace actif" }).click(); await expect(page.getByRole("button", { name: "Nouvel espace" })).toBeEnabled(); await expect(page.getByRole("tab", { name: "Défaut" })).toHaveAttribute("aria-selected", "true"); await expect(page.locator("[data-module-id='a']")).toBeVisible(); await page.reload(); await expect(page.getByRole("tab", { name: "Défaut" })).toHaveAttribute("aria-selected", "true"); }); test("keeps the compact header order and isolates configuration from add and collapsed content", async ({ page }) => { await page.setViewportSize({ width: 390, height: 900 }); await seedToolbox(page, { id: "module-configuration", name: "Module configuration", modules: [{ id: "a", type: "counters", title: "Alpha" }], moduleOrder: { one: ["a"], two: [["a"], []] } }); const tool = page.locator("[data-module-id='a']"); await expect(tool.locator(".module-header-actions > button")).toHaveCount(5); expect(await tool.locator(".module-header-actions > button").evaluateAll((buttons) => buttons.map((button) => button.className.split(" ")[0]))).toEqual([ "module-scroll-button", "module-edit-button", "module-configuration-button", "module-delete-button", "module-collapse-button" ]); const add = tool.getByRole("button", { name: "Afficher l'ajout de Alpha", exact: true }); const configuration = tool.getByRole("button", { name: "Configuration", exact: true }); await add.click(); await expect(tool.locator(".module-add-panel")).toBeVisible(); await configuration.click(); await expect(add).toHaveAttribute("aria-pressed", "false"); await expect(tool.locator(".module-add-panel")).toHaveCount(0); const panel = tool.getByRole("region", { name: "Configuration de Alpha", exact: true }); await expect(panel).toBeVisible(); await expect(panel.getByRole("button", { name: "Importer du texte", exact: true })).toBeVisible(); const toolBox = await tool.boundingBox(); const panelBox = await panel.boundingBox(); expect(panelBox.width).toBeGreaterThan(toolBox.width - 24); expect(panelBox.width).toBeLessThan(toolBox.width); await tool.getByRole("button", { name: "Masquer Alpha", exact: true }).click(); await expect(panel).toHaveCount(0); await expect(tool.locator(".module-content")).toHaveCount(0); await configuration.click(); await expect(panel).toBeVisible(); await expect(tool.locator(".module-content")).toBeHidden(); await expect(panel.getByRole("button", { name: "Importer du texte", exact: true })).toBeVisible(); await configuration.press("Escape"); await expect(panel).toHaveCount(0); await expect(configuration).toHaveAttribute("aria-expanded", "false"); }); test("shows the tab insertion indicator on the correct side while reordering", async ({ page }) => { await seedToolbox(page, { id: "reorder-spaces", name: "Reorder spaces", modules: [], spaces: [ { id: "default", name: "Défaut", moduleOrder: { one: [], two: [[], []] } }, { id: "boss", name: "Boss", moduleOrder: { one: [], two: [[], []] } }, { id: "farm", name: "Farm", moduleOrder: { one: [], two: [[], []] } } ], defaultSpaceId: "default", activeSpaceId: "default" }); const farmTabWrap = page.getByRole("tab", { name: "Farm" }).locator(".."); const farmBox = await farmTabWrap.boundingBox(); const bossDragHandle = page.getByRole("button", { name: "Réordonner l’espace Boss" }); await expect.poll(() => bossDragHandle.evaluate((element) => globalThis.getComputedStyle(element).marginLeft)).toBe("4px"); await bossDragHandle.hover(); await page.mouse.down(); await page.mouse.move(farmBox.x + farmBox.width - 2, farmBox.y + farmBox.height / 2, { steps: 4 }); await expect(farmTabWrap).toHaveClass(/is-drop-target/); await expect(farmTabWrap).toHaveClass(/drop-after/); await expect.poll(() => farmTabWrap.evaluate((element) => globalThis.getComputedStyle(element, "::before").right)).toBe("0px"); await page.mouse.up(); await expect(page.getByRole("tab")).toHaveText(["Défaut", "Farm", "Boss"]); }); test("quick drop stays on the source while prolonged hover opens the destination", async ({ page }) => { await seedToolbox(page, { id: "drag-spaces", name: "Drag spaces", modules: [ { id: "a", type: "counters", title: "Alpha" }, { id: "b", type: "counters", title: "Beta" } ], spaces: [ { id: "default", name: "Défaut", moduleOrder: { one: ["a", "b"], two: [["a"], ["b"]] } }, { id: "boss", name: "Boss", moduleOrder: { one: [], two: [[], []] } } ], defaultSpaceId: "default", activeSpaceId: "default" }); const bossTab = page.getByRole("tab", { name: "Boss" }); await dragModuleToTab(page, "Alpha", bossTab); await expect(page.getByRole("tab", { name: "Défaut" })).toHaveAttribute("aria-selected", "true"); await expect(page.locator("[data-module-id='a']")).toHaveCount(0); await dragModuleToTab(page, "Beta", bossTab, 700); await expect(bossTab).toHaveAttribute("aria-selected", "true"); await expect(page.locator("[data-module-id='a']")).toBeVisible(); await expect(page.locator("[data-module-id='b']")).toBeVisible(); }); test("the linked game drawer shares the persisted active space", async ({ page }) => { await seedToolbox(page, { id: "drawer-spaces", name: "Drawer spaces", modules: [{ id: "a", type: "counters", title: "Alpha" }], spaces: [ { id: "default", name: "Défaut", moduleOrder: { one: [], two: [[], []] } }, { id: "boss", name: "Boss", moduleOrder: { one: ["a"], two: [["a"], []] } } ], defaultSpaceId: "default", activeSpaceId: "boss" }, { mhwilds: "drawer-spaces" }); await page.goto("/#/games/mhwilds"); await page.getByRole("button", { name: "Afficher la toolbox", exact: true }).click(); const drawer = page.locator("#toolbox-drawer"); await expect(drawer.getByRole("tab", { name: "Boss" })).toHaveAttribute("aria-selected", "true"); await expect(drawer.locator("[data-module-id='a']")).toBeVisible(); await drawer.getByRole("tab", { name: "Défaut" }).click(); await expect(drawer.locator("[data-module-id='a']")).toHaveCount(0); await page.reload(); await page.getByRole("button", { name: "Afficher la toolbox", exact: true }).click(); await expect(page.locator("#toolbox-drawer").getByRole("tab", { name: "Défaut" })).toHaveAttribute("aria-selected", "true"); });