Improve toolbox checklists, image browsing, and guide integration
All checks were successful
Deploy Sokko G / deploy (push) Successful in 18s

This commit is contained in:
Shinuwa 2026-09-27 22:26:39 +02:00
parent 00e73f0062
commit 386212041f
47 changed files with 744 additions and 112 deletions

View file

@ -1,4 +1,4 @@
// Rôle : vérifie copie, liaison de session et progression des checklists dans les guides.
// Rôle : vérifie copie, liaison persistante et progression des checklists dans les guides.
import { test, expect } from "@playwright/test";
import { readFileSync } from "node:fs";
import { resetSokkoStorage } from "./helpers/storage.js";
@ -6,7 +6,7 @@ import { importChecklistText } from "../website/src/features/toolboxes/modules/t
import { normalizeChecklistData } from "../website/src/features/toolboxes/storage/modules/checklist.js";
const route = "/#/games/aniimo/aniimos";
const sessionKey = "sokkog:guide-checklists";
const storageKey = "sokkog:guide-checklists";
const controls = (page, id) => page.locator('[data-module-type="checklist"]').filter({ has: page.getByRole("button", { name: `Retirer ${id}`, exact: true }) }).locator(".guide-checklist-controls");
const controlsToggle = (page, id) => page.getByRole("button", { name: `Lien avec le guide — ${id}`, exact: true });
const linkButton = (page, id) => controls(page, id).locator(".guide-checklist-link");
@ -125,7 +125,7 @@ test("Aniimo copies visible species or forms and round-trips through checklist i
});
for (const width of [1280, 390]) {
test(`drawer linking, live completion and session restoration at ${width}px`, async ({ page }) => {
test(`drawer linking, page controls and persistent restoration at ${width}px`, async ({ page }) => {
test.setTimeout(60000);
await page.setViewportSize({ width, height: 900 });
await seed(page);
@ -164,6 +164,19 @@ for (const width of [1280, 390]) {
expect(actionsBounds.x + actionsBounds.width / 2).toBeCloseTo(bounds.x + bounds.width / 2, 0);
await page.screenshot({ path: test.info().outputPath("linked-drawer.png") });
await closeDrawer(page);
const pageControls = page.locator(".guide-checklist-page-controls");
await expect(pageControls).toBeVisible();
const pageControlsBounds = await pageControls.boundingBox();
const pageHeadingBounds = await page.locator(".page-heading").boundingBox();
const headingActionsBounds = await page.locator(".game-heading-actions").boundingBox();
expect(pageControlsBounds.y).toBeGreaterThanOrEqual(pageHeadingBounds.y);
expect(pageControlsBounds.y + pageControlsBounds.height).toBeLessThanOrEqual(pageHeadingBounds.y + pageHeadingBounds.height);
if (width > 760) {
expect(headingActionsBounds.height).toBeCloseTo(pageHeadingBounds.height, 0);
expect(pageControlsBounds.y).toBeCloseTo(pageHeadingBounds.y, 0);
}
await expect(pageControls.getByRole("button", { name: "Masquer les cochés", exact: true })).toHaveAttribute("aria-pressed", "false");
await expect(pageControls.getByRole("button", { name: "Dissocier la checklist", exact: true })).toBeVisible();
await expect(card).toHaveAttribute("data-checklist-complete", "true");
await expect(card.locator(".aniimo-front .guide-checklist-mark")).toHaveCount(0);
await card.getByRole("button", { name: /Afficher les capacités/ }).click();
@ -177,7 +190,7 @@ for (const width of [1280, 390]) {
await expect.poll(async () => (await readModule(page, "forms")).items.find((item) => item.label.includes("Forme de base")).qtyCurrent || 0).toBe(0);
await card.getByRole("checkbox", { name: "Cocher dans la checklist" }).check();
await expect(card).toHaveAttribute("data-checklist-complete", "true");
await toggleHidden(page);
await pageControls.getByRole("button", { name: "Masquer les cochés", exact: true }).click();
await expect(card.getByRole("tab")).toHaveCount(2);
await expect(card).toHaveAttribute("data-form", "mountain_woods_form");
expect(normalizeChecklistData(await readModule(page, "forms")).items).toEqual(normalizeChecklistData(before).items);
@ -188,6 +201,8 @@ for (const width of [1280, 390]) {
await drawer.locator('[data-module-type="checklist"]').filter({ has: page.getByRole("button", { name: "Retirer capture", exact: true }) }).getByRole("checkbox", { name: "Terminer Loufeuteau", exact: true }).check();
await closeDrawer(page);
await expect(card).toHaveCount(0);
await expect.poll(() => page.evaluate((key) => globalThis.localStorage.getItem(key), storageKey)).not.toBeNull();
await page.evaluate(() => globalThis.sessionStorage.clear());
await page.reload();
await assertLink(page, "capture", true);
await page.getByRole("searchbox").fill("emberpup");
@ -198,16 +213,15 @@ for (const width of [1280, 390]) {
await closeDrawer(page);
await expect(card).toHaveAttribute("data-checklist-complete", "true");
await page.getByRole("button", { name: "Réinitialiser les filtres", exact: true }).click();
await page.locator(".guide-checklist-page-controls").getByRole("button", { name: "Dissocier la checklist", exact: true }).click();
await openDrawer(page);
await openControls(page, "capture");
await expect(linkButton(page, "capture")).toHaveAttribute("aria-pressed", "true");
await linkButton(page, "capture").click();
await expect(linkButton(page, "capture")).toHaveAttribute("aria-pressed", "false");
await expect(activeButtons(page)).toHaveCount(0);
await expect(hiddenSwitch(page)).toHaveCount(0);
await closeDrawer(page);
await expect(page.locator('[data-checklist-complete="true"]')).toHaveCount(0);
expect(await page.evaluate((key) => globalThis.sessionStorage.getItem(key), sessionKey)).toBeNull();
expect(await page.evaluate((key) => globalThis.localStorage.getItem(key), storageKey)).toBeNull();
expect(await page.evaluate(() => globalThis.document.documentElement.scrollWidth <= globalThis.innerWidth)).toBe(true);
});
}
@ -261,7 +275,7 @@ test("renames retain a zero-match selection; changing toolbox clears the associa
await dialog.getByRole("button", { name: "Valider", exact: true }).click();
await expect(activeButtons(page)).toHaveCount(0);
await expect(page.locator(".guide-checklist-controls")).toHaveCount(1);
expect(await page.evaluate((key) => globalThis.sessionStorage.getItem(key), sessionKey)).toBeNull();
expect(await page.evaluate((key) => globalThis.localStorage.getItem(key), storageKey)).toBeNull();
});
test("deleting a selected checklist clears its link immediately", async ({ page }) => {
@ -276,20 +290,20 @@ test("deleting a selected checklist clears its link immediately", async ({ page
await expect(hiddenSwitch(page)).toHaveCount(0);
await page.reload();
await assertLink(page, "", false);
expect(await page.evaluate((key) => globalThis.sessionStorage.getItem(key), sessionKey)).toBeNull();
expect(await page.evaluate((key) => globalThis.localStorage.getItem(key), storageKey)).toBeNull();
});
test("unavailable session storage retains links across navigation; overview has no guide controls", async ({ page }) => {
test("unavailable local storage retains links in memory across navigation; overview has no guide controls", async ({ page }) => {
await seed(page);
await page.addInitScript((key) => {
for (const method of ["getItem", "setItem", "removeItem"]) {
const original = globalThis.Storage.prototype[method];
globalThis.Storage.prototype[method] = function (name, ...args) {
if (name === key) throw new Error("Session storage unavailable");
if (name === key) throw new Error("Local storage unavailable");
return original.call(this, name, ...args);
};
}
}, sessionKey);
}, storageKey);
await page.reload();
await page.goto(route);
await choose(page, "perfect");
@ -304,4 +318,8 @@ test("unavailable session storage retains links across navigation; overview has
await expect(page.locator(".module-guide-button")).toHaveCount(0);
await page.goto(route);
await assertLink(page, "perfect", true);
await openDrawer(page);
await page.goto("/#/toolboxes");
await expect(page.locator("#toolbox-drawer .drawer-panel")).toHaveCount(0);
await expect(page.locator(".app-shell")).not.toHaveClass(/toolbox-drawer-open/);
});