Fix toolbox tutorial state across navigation
Some checks failed
Deploy Sokko G / deploy (push) Failing after 2s
Some checks failed
Deploy Sokko G / deploy (push) Failing after 2s
This commit is contained in:
parent
75d4d99a87
commit
898fd5286f
5 changed files with 111 additions and 11 deletions
39
e2e/fixtures/toolbox-tutorial-probe.js
Normal file
39
e2e/fixtures/toolbox-tutorial-probe.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// Rôle : monte la page toolbox sous StrictMode pour contrôler les demandes de tutoriel et les changements d'identité.
|
||||
import { createElement, StrictMode, useState } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { flushSync } from "react-dom";
|
||||
import { ToolboxPage } from "../../website/src/features/toolboxes/ToolboxPages.jsx";
|
||||
|
||||
export function mountToolboxTutorialProbe(element, siteContent) {
|
||||
const root = createRoot(element);
|
||||
const writes = [];
|
||||
const pendingChanges = [];
|
||||
let setInputs;
|
||||
function Probe() {
|
||||
const [inputs, updateInputs] = useState({
|
||||
toolboxId: "a", pendingTutorialToolboxId: "",
|
||||
toolboxes: ["a", "b", "c"].map((id) => ({ id, name: id, modules: [], tutorial: id !== "c" }))
|
||||
});
|
||||
setInputs = updateInputs;
|
||||
return createElement(ToolboxPage, {
|
||||
...inputs, siteContent, actions: {},
|
||||
getToolboxGame: () => null,
|
||||
getModuleData: (_toolbox, _module, fallback) => fallback,
|
||||
updateModuleData: () => {},
|
||||
updateToolbox: (toolbox) => {
|
||||
writes.push(toolbox);
|
||||
updateInputs((current) => ({ ...current, toolboxes: current.toolboxes.map((item) => item.id === toolbox.id ? toolbox : item) }));
|
||||
},
|
||||
setPendingTutorialToolboxId: (id) => {
|
||||
pendingChanges.push(id);
|
||||
updateInputs((current) => ({ ...current, pendingTutorialToolboxId: id }));
|
||||
}
|
||||
});
|
||||
}
|
||||
flushSync(() => root.render(createElement(StrictMode, null, createElement(Probe))));
|
||||
return {
|
||||
writes, pendingChanges,
|
||||
update: (patch) => flushSync(() => setInputs((current) => ({ ...current, ...patch }))),
|
||||
unmount: () => flushSync(() => root.unmount())
|
||||
};
|
||||
}
|
||||
62
e2e/toolbox-tutorial.spec.js
Normal file
62
e2e/toolbox-tutorial.spec.js
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// Rôle : vérifie les étapes, la fermeture et l'isolation des tutoriels toolbox sous StrictMode.
|
||||
/* global window, document, fetch */
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { fileURLToPath, URL } from "node:url";
|
||||
|
||||
const probeUrl = `/@fs/${fileURLToPath(new URL("./fixtures/toolbox-tutorial-probe.js", import.meta.url))}`;
|
||||
|
||||
async function setup(page) {
|
||||
await page.goto("/");
|
||||
await page.evaluate(async (url) => {
|
||||
const { mountToolboxTutorialProbe } = await import(url);
|
||||
const siteContent = await fetch("/data/site.json").then((response) => response.json());
|
||||
document.getElementById("app").style.display = "none";
|
||||
const element = document.createElement("div");
|
||||
document.body.prepend(element);
|
||||
window.tutorialProbe = mountToolboxTutorialProbe(element, siteContent);
|
||||
}, probeUrl);
|
||||
}
|
||||
|
||||
test("tutorial restarts on another toolbox and consumes each start flag once", async ({ page }) => {
|
||||
const errors = [];
|
||||
page.on("pageerror", (error) => errors.push(error.message));
|
||||
await setup(page);
|
||||
const tour = page.locator(".toolbox-tutorial-tooltip");
|
||||
const count = tour.locator(".toolbox-tutorial-count");
|
||||
await expect(count).toHaveText(/^1\//);
|
||||
await tour.locator("footer button").click();
|
||||
await expect(count).toHaveText(/^2\//);
|
||||
await page.evaluate(() => window.tutorialProbe.update({}));
|
||||
await expect(count).toHaveText(/^2\//);
|
||||
await page.evaluate(() => window.tutorialProbe.update({ toolboxId: "b" }));
|
||||
await expect(count).toHaveText(/^1\//);
|
||||
expect(await page.evaluate(() => window.tutorialProbe.writes.map(({ id, tutorial }) => ({ id, tutorial })))).toEqual([
|
||||
{ id: "a", tutorial: false }, { id: "b", tutorial: false }
|
||||
]);
|
||||
await page.evaluate(() => window.tutorialProbe.update({ toolboxId: "c" }));
|
||||
await expect(tour).toHaveCount(0);
|
||||
await page.evaluate(() => window.tutorialProbe.update({ toolboxId: "a" }));
|
||||
await expect(tour).toHaveCount(0);
|
||||
await page.evaluate(() => window.tutorialProbe.unmount());
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
test("pending tutorial survives unrelated toolboxes and stays dismissed after rerenders", async ({ page }) => {
|
||||
await setup(page);
|
||||
const tour = page.locator(".toolbox-tutorial-tooltip");
|
||||
await expect(tour).toBeVisible();
|
||||
await tour.locator(".toolbox-tutorial-close").click();
|
||||
await page.evaluate(() => window.tutorialProbe.update({ pendingTutorialToolboxId: "c" }));
|
||||
await expect(tour).toHaveCount(0);
|
||||
expect(await page.evaluate(() => window.tutorialProbe.pendingChanges)).toEqual([]);
|
||||
await page.evaluate(() => window.tutorialProbe.update({ toolboxId: "c" }));
|
||||
await expect(tour.locator(".toolbox-tutorial-count")).toHaveText(/^1\//);
|
||||
expect(await page.evaluate(() => window.tutorialProbe.pendingChanges)).toEqual([""]);
|
||||
await tour.locator(".toolbox-tutorial-close").click();
|
||||
await page.evaluate(() => window.tutorialProbe.update({}));
|
||||
await expect(tour).toHaveCount(0);
|
||||
await page.evaluate(() => window.tutorialProbe.update({ pendingTutorialToolboxId: "c" }));
|
||||
await expect(tour.locator(".toolbox-tutorial-count")).toHaveText(/^1\//);
|
||||
await page.evaluate(() => window.tutorialProbe.unmount());
|
||||
await expect(tour).toHaveCount(0);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue