Add toolbox spaces and recover from stale asset bundles
All checks were successful
Deploy Sokko G / deploy (push) Successful in 18s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 18s
This commit is contained in:
parent
0959c6740b
commit
0acf5ecf21
30 changed files with 1252 additions and 179 deletions
|
|
@ -9,10 +9,22 @@ export function mountToolboxTutorialProbe(element, siteContent) {
|
|||
const writes = [];
|
||||
const pendingChanges = [];
|
||||
let setInputs;
|
||||
function createToolbox(id) {
|
||||
const defaultSpaceId = `space-${id}`;
|
||||
return {
|
||||
id,
|
||||
name: id,
|
||||
modules: [],
|
||||
spaces: [{ id: defaultSpaceId, name: "Défaut", moduleOrder: { one: [], two: [[], []] } }],
|
||||
defaultSpaceId,
|
||||
activeSpaceId: defaultSpaceId,
|
||||
tutorial: id !== "c"
|
||||
};
|
||||
}
|
||||
function Probe() {
|
||||
const [inputs, updateInputs] = useState({
|
||||
toolboxId: "a", pendingTutorialToolboxId: "",
|
||||
toolboxes: ["a", "b", "c"].map((id) => ({ id, name: id, modules: [], tutorial: id !== "c" }))
|
||||
toolboxes: ["a", "b", "c"].map(createToolbox)
|
||||
});
|
||||
setInputs = updateInputs;
|
||||
return createElement(ToolboxPage, {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ async function setup(page, columns) {
|
|||
async function savedOrder(page) {
|
||||
return page.evaluate(async () => {
|
||||
const db = await import("/src/utils/indexedDbStorage.js");
|
||||
return (await db.getToolboxes())[0].moduleOrder;
|
||||
return (await db.getToolboxes())[0].spaces[0].moduleOrder;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
37
e2e/production/asset-recovery.spec.js
Normal file
37
e2e/production/asset-recovery.spec.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Rôle : vérifie qu'un ancien HTML rétablit l'application quand son bundle Vite a disparu.
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { readFile } from "node:fs/promises";
|
||||
|
||||
const site = JSON.parse(await readFile("website/public/data/site.json", "utf8"));
|
||||
|
||||
test("a stale entry bundle triggers one fresh document load", async ({ page }) => {
|
||||
let staleDocumentServed = false;
|
||||
let documentRequestCount = 0;
|
||||
|
||||
await page.route("**/*", async (route) => {
|
||||
const request = route.request();
|
||||
const requestUrl = new globalThis.URL(request.url());
|
||||
const isRootDocument = request.resourceType() === "document" && requestUrl.pathname === "/";
|
||||
if (isRootDocument) documentRequestCount += 1;
|
||||
|
||||
if (staleDocumentServed || !isRootDocument) {
|
||||
await route.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
staleDocumentServed = true;
|
||||
const response = await route.fetch();
|
||||
const html = await response.text();
|
||||
await route.fulfill({
|
||||
response,
|
||||
body: html.replace(/(<script type="module"[^>]*src=")[^"]+/, "$1/assets/index-stale.js")
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/").catch(() => {});
|
||||
|
||||
await expect(page.getByRole("heading", { name: site.home.hero.title, exact: true })).toBeVisible();
|
||||
await expect(page).toHaveURL(/\/$|\/#\/$/);
|
||||
expect(staleDocumentServed).toBe(true);
|
||||
expect(documentRequestCount).toBe(2);
|
||||
});
|
||||
|
|
@ -84,9 +84,16 @@ test("invalid calculator imports preserve existing results and a corrected impor
|
|||
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 page.evaluate(() => {
|
||||
globalThis.__sokkogModuleWriteComplete = false;
|
||||
globalThis.addEventListener("sokkog:auto-backup-data-changed", () => {
|
||||
globalThis.__sokkogModuleWriteComplete = true;
|
||||
}, { once: true });
|
||||
});
|
||||
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 expect.poll(() => page.evaluate(() => globalThis.__sokkogModuleWriteComplete)).toBe(true);
|
||||
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");
|
||||
|
|
|
|||
148
e2e/toolbox-spaces.spec.js
Normal file
148
e2e/toolbox-spaces.spec.js
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
// 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();
|
||||
await page.getByRole("combobox", { name: "Déplacer vers… — Alpha" }).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("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");
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue