Add checklist creation for filtered game results
All checks were successful
Deploy Sokko G / deploy (push) Successful in 18s

This commit is contained in:
Shinuwa 2026-09-29 23:27:10 +02:00
parent 0acf5ecf21
commit 962816bbb9
15 changed files with 414 additions and 40 deletions

View file

@ -58,6 +58,35 @@ async function readModule(page, moduleId) {
}), moduleId);
}
async function readCreatedChecklist(page, title) {
return page.evaluate(async (title) => new Promise((resolve, reject) => {
const request = globalThis.indexedDB.open("sokkog", 1);
request.onsuccess = () => {
const db = request.result;
const tx = db.transaction(["kv", "modules"], "readonly");
const toolboxesRequest = tx.objectStore("kv").get("toolboxes");
const modulesRequest = tx.objectStore("modules").getAll();
tx.oncomplete = () => {
const toolbox = toolboxesRequest.result.find((item) => item.id === "guide");
const module = toolbox.modules.find((item) => item.type === "checklist" && item.title === title);
const data = modulesRequest.result.find((entry) => entry.key === `guide:${module?.id}`)?.data;
const activeSpace = toolbox.spaces.find((space) => space.id === toolbox.activeSpaceId);
db.close();
resolve({ module, data, activeModuleIds: activeSpace?.moduleOrder.one || [] });
};
tx.onerror = () => reject(tx.error);
};
request.onerror = () => reject(request.error);
}), title);
}
async function copyVisibleResults(page) {
await page.locator(".results-copy-button").click();
const dialog = page.getByRole("dialog", { name: "Utiliser les résultats visibles" });
await dialog.getByRole("button", { name: "Copier le format texte", exact: true }).click();
await expect(dialog).toHaveCount(0);
}
async function openDrawer(page) {
const drawer = page.locator("#toolbox-drawer");
if (!await drawer.locator(".drawer-panel").isVisible()) await page.locator(".topbar-actions button").click();
@ -105,25 +134,62 @@ test("Aniimo copies visible species or forms and round-trips through checklist i
const copy = page.locator(".results-copy-button");
await expect(copy).toHaveAccessibleName(/sans les variantes/);
await expect(page.locator('[data-copy-target="true"]')).toHaveText("1 / 88 Aniimos");
await copy.click();
await copyVisibleResults(page);
await expect.poll(() => page.evaluate(() => globalThis.navigator.clipboard.readText())).toBe("Loufeuteau:1");
await page.getByRole("button", { name: "Regrouper les variantes", exact: true }).click();
await expect(copy).toHaveAccessibleName(/toutes les variantes/);
await expect(page.locator('[data-copy-target="true"]')).toHaveText("3 / 208 variantes");
await copy.click();
await copyVisibleResults(page);
const text = await page.evaluate(() => globalThis.navigator.clipboard.readText());
const imported = importChecklistText(text, { uid: (prefix) => `${prefix}-${Math.random()}`, normalizeChecklistData });
expect(imported.items.map((item) => item.label)).toEqual(["Loufeuteau — Forme de base", "Loufeuteau — Forme des montagnes boisées", "Loufeuteau — Hautes-plaines"]);
expect(imported.items.every((item) => item.qtyTarget === 1 && item.qtyCurrent === 0)).toBe(true);
await choose(page, "forms");
await toggleHidden(page);
await copy.click();
await copyVisibleResults(page);
expect(await page.evaluate(() => globalThis.navigator.clipboard.readText())).not.toContain("Forme de base");
await choose(page, "perfect");
await expect(copy).toBeDisabled();
await expect(page.getByRole("heading", { name: "Aucun résultat" })).toBeVisible();
});
test("filtered results explain unavailable creation when no toolbox is linked", async ({ page, context }) => {
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
await page.setViewportSize({ width: 390, height: 844 });
await resetSokkoStorage(page);
await page.goto(route);
await page.getByRole("searchbox").fill("emberpup");
await page.locator(".results-copy-button").click();
const dialog = page.getByRole("dialog", { name: "Utiliser les résultats visibles" });
await expect(dialog.getByText("Liez une toolbox à cette page pour créer directement la checklist.")).toBeVisible();
await expect(dialog.getByRole("button", { name: "Créer dans la toolbox", exact: true })).toBeDisabled();
expect(await page.evaluate(() => globalThis.document.documentElement.scrollWidth <= globalThis.innerWidth)).toBe(true);
await dialog.getByRole("button", { name: "Copier le format texte", exact: true }).click();
await expect.poll(() => page.evaluate(() => globalThis.navigator.clipboard.readText())).toBe("Loufeuteau:1");
});
test("filtered results create a checklist in the linked active space without replacing the guide association", async ({ page }) => {
await seed(page);
await page.goto(route);
await choose(page, "forms");
await page.getByRole("searchbox").fill("emberpup");
const associationBefore = await page.evaluate((key) => globalThis.localStorage.getItem(key), storageKey);
await page.locator(".results-copy-button").click();
const dialog = page.getByRole("dialog", { name: "Utiliser les résultats visibles" });
await expect(dialog.getByText("Guide toolbox", { exact: true })).toBeVisible();
await expect(dialog.getByText("Aniimos", { exact: true })).toBeVisible();
await dialog.getByRole("button", { name: "Créer dans la toolbox", exact: true }).click();
await expect(dialog).toHaveCount(0);
await expect(page.getByText("Checklist créée dans la toolbox associée.")).toBeVisible();
await expect.poll(async () => (await readCreatedChecklist(page, "Aniimos")).module?.id).toBeTruthy();
const created = await readCreatedChecklist(page, "Aniimos");
expect(created.activeModuleIds).toContain(created.module.id);
expect(normalizeChecklistData(created.data).items.map((item) => item.label)).toEqual(["Loufeuteau"]);
expect(normalizeChecklistData(created.data).items.every((item) => item.qtyTarget === 1 && item.qtyCurrent === 0)).toBe(true);
expect(await page.evaluate((key) => globalThis.localStorage.getItem(key), storageKey)).toBe(associationBefore);
});
for (const width of [1280, 390]) {
test(`drawer linking, page controls and persistent restoration at ${width}px`, async ({ page }) => {
test.setTimeout(60000);
@ -239,10 +305,10 @@ for (const width of [1280, 390]) {
});
}
for (const [gameId, category, cardClass, dataPath, dataKey] of [
["mhwilds", "monsters", ".monster-card", "mhwilds/monsters.json", "monsters"],
["mhwilds", "endemic", ".endemic-card", "mhwilds/endemic_life.json", "endemicLife"],
["diablo4", "affixes", ".diablo4-affix-card", "diablo4/affixes_types.json", "affixes"]
for (const [gameId, category, cardClass, dataPath, dataKey, checklistTitle] of [
["mhwilds", "monsters", ".monster-card", "mhwilds/monsters.json", "monsters", "Monstres"],
["mhwilds", "endemic", ".endemic-card", "mhwilds/endemic_life.json", "endemicLife", "Faune endémique"],
["diablo4", "affixes", ".diablo4-affix-card", "diablo4/affixes_types.json", "affixes", "Affixes"]
]) {
test(`${gameId}/${category} links from its drawer and hides completed cards`, async ({ page }) => {
const item = json(dataPath)[dataKey][0];
@ -250,6 +316,10 @@ for (const [gameId, category, cardClass, dataPath, dataKey] of [
await seed(page, gameId, { progress: [row(label, 1)] });
await page.goto(`/#/games/${gameId}/${category}`);
await expect(page.locator(cardClass).first()).toBeVisible();
await page.locator(".results-copy-button").click();
const resultsDialog = page.getByRole("dialog", { name: "Utiliser les résultats visibles" });
await expect(resultsDialog.getByText(checklistTitle, { exact: true })).toBeVisible();
await resultsDialog.getByRole("button", { name: "Fermer", exact: true }).click();
const count = await page.locator(cardClass).count();
await choose(page, "progress");
await expect(page.locator(`${cardClass}[data-checklist-complete="true"]`)).toHaveCount(1);