// Rôle : couvre chargement différé, cache de navigation, erreurs et annulations des données publiques. /* global window, document */ import { test, expect } from "@playwright/test"; import { fileURLToPath, URL } from "node:url"; const probeUrl = `/@fs/${fileURLToPath(new URL("./fixtures/app-data-probe.js", import.meta.url))}`; const siteUrl = "/data/site.json"; const gamesUrl = "/data/games.json"; const diabloUrl = "/data/diablo4/affixes_types.json"; const mhwBase = "/data/mhwilds/"; async function setup(page, route = "/") { await page.route("**/__app-data-probe", (request) => request.fulfill({ contentType: "text/html", body: '
' })); await page.goto("/__app-data-probe"); await page.evaluate(async ({ url, route }) => { const { mountAppDataProbe } = await import(url); window.dataProbe = mountAppDataProbe(document.getElementById("probe"), route); }, { url: probeUrl, route }); } async function state(page) { return JSON.parse(await page.locator("#data-state").textContent()); } async function respond(page, url, payload, status = 200, aborted = false) { await page.evaluate(({ url, payload, status, aborted }) => { window.dataProbe.respond(url, payload, status, aborted); }, { url, payload, status, aborted }); } test("game data loads only on demand and remains cached through navigation", async ({ page }) => { await setup(page); expect((await state(page)).mhwilds.loading).toBe(false); expect(await page.evaluate(() => window.dataProbe.requests().map((request) => request.url))) .toEqual([siteUrl, gamesUrl, siteUrl, gamesUrl]); await respond(page, siteUrl, { title: "Current" }); await respond(page, gamesUrl, { games: [{ id: "mhwilds" }] }); await expect.poll(async () => (await state(page)).siteContent?.title).toBe("Current"); await respond(page, siteUrl, { title: "Obsolete" }, 200, true); await respond(page, gamesUrl, {}, 503, true); const snapshots = await page.evaluate(() => { window.dataProbe.navigate("/games/mhwilds"); return window.dataProbe.snapshots; }); expect(snapshots.length).toBeGreaterThan(0); for (const snapshot of snapshots) { expect(snapshot.mhwilds.loading).toBe(true); expect(snapshot.mhwilds.loaded).toBe(false); expect(snapshot.siteContent.title).toBe("Current"); expect(snapshot.gamesError).toBe(""); } await respond(page, `${mhwBase}monsters.json`, { monsters: [{ id: "rey-dau" }] }); await respond(page, `${mhwBase}endemic_life.json`, {}); await respond(page, `${mhwBase}lists/index.json`, { lists: [{ id: "farm", file: "farm.json" }] }); await respond(page, `${mhwBase}i18n/fr.json`, { hello: "Bonjour" }); await expect.poll(() => page.evaluate(() => window.dataProbe.requests().some((request) => request.url.endsWith("/farm.json")))).toBe(true); await page.evaluate(() => { window.dataProbe.navigate("/games/diablo4"); window.dataProbe.navigate("/"); }); await respond(page, `${mhwBase}lists/farm.json`, { titre: "Farm", items: [{ nom: "Potion", quantite: 2 }] }); await respond(page, diabloUrl, { affixes: [{ id: "damage" }], categories: [{ id: "attack" }] }); await expect.poll(async () => (await state(page)).mhwilds.loaded).toBe(true); await expect.poll(async () => (await state(page)).diablo4.loaded).toBe(true); const loaded = await state(page); expect(loaded.mhwilds.lists[0].categories[0].items[0].quantity).toBe(2); expect(loaded.diablo4.categoryMap.attack).toEqual({ id: "attack" }); const count = await page.evaluate(() => window.dataProbe.requests().length); await page.evaluate(() => { window.dataProbe.navigate("/games/mhwilds/monsters"); window.dataProbe.navigate("/games/diablo4"); }); expect(await page.evaluate(() => window.dataProbe.requests().length)).toBe(count); expect((await state(page)).mhwilds.loading).toBe(false); await page.evaluate(() => window.dataProbe.unmount()); expect(await page.evaluate(() => window.dataProbe.requests().every((request) => request.aborted))).toBe(true); }); test("StrictMode discards obsolete game failures and preserves current editorial errors", async ({ page }) => { await setup(page, "/games/diablo4"); const requests = await page.evaluate(() => window.dataProbe.requests().filter((request) => request.url.includes("diablo4"))); expect(requests.map((request) => request.aborted)).toEqual([true, false]); expect((await state(page)).diablo4.loading).toBe(true); await respond(page, diabloUrl, { affixes: [{ id: "current" }] }); await expect.poll(async () => (await state(page)).diablo4.loaded).toBe(true); await respond(page, diabloUrl, {}, 503, true); await respond(page, siteUrl, {}, 503); await respond(page, gamesUrl, { games: [] }); await expect.poll(async () => (await state(page)).siteContentError).toBe("Impossible de charger le contenu du site."); expect((await state(page)).diablo4.error).toBe(""); expect((await state(page)).diablo4.affixes).toEqual([{ id: "current" }]); await page.evaluate(() => window.dataProbe.unmount()); }); test("failed game loading stops and pending list requests are aborted on unmount", async ({ page }) => { await setup(page, "/games/diablo4"); await respond(page, diabloUrl, {}, 503); await expect.poll(async () => (await state(page)).diablo4.error).toBe("Impossible de charger les données Diablo IV."); expect((await state(page)).diablo4).toMatchObject({ loaded: true, loading: false }); await page.evaluate(() => { window.dataProbe.navigate("/"); window.dataProbe.navigate("/games/diablo4"); window.dataProbe.navigate("/games/mhwilds"); }); expect(await page.evaluate(() => window.dataProbe.requests().filter((request) => request.url.includes("diablo4")).length)).toBe(2); await respond(page, `${mhwBase}monsters.json`, {}); await respond(page, `${mhwBase}endemic_life.json`, {}); await respond(page, `${mhwBase}lists/index.json`, { lists: [{ file: "pending.json" }] }); await respond(page, `${mhwBase}i18n/fr.json`, {}); await expect.poll(() => page.evaluate(() => window.dataProbe.requests().some((request) => request.url.endsWith("/pending.json")))).toBe(true); await page.evaluate(() => window.dataProbe.unmount()); expect(await page.evaluate(() => window.dataProbe.requests().every((request) => request.aborted))).toBe(true); await respond(page, `${mhwBase}lists/pending.json`, {}, 503, true); await respond(page, siteUrl, {}, 503, true); await respond(page, gamesUrl, {}, 503, true); });