From 77f357e0568e400d9fe700f14cc68433a83a8d3e Mon Sep 17 00:00:00 2001 From: Shinuwa Date: Mon, 14 Sep 2026 09:09:42 +0200 Subject: [PATCH] Verify compiled card surfaces across desktop and mobile --- .beads/interactions.jsonl | 1 + docs/FEATURE_CHECKLIST.md | 5 ++ e2e/card-surfaces.spec.js | 95 ++++++++++++++++++++++++++++++++++++ tests/static-styles.test.mjs | 10 ---- 4 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 e2e/card-surfaces.spec.js diff --git a/.beads/interactions.jsonl b/.beads/interactions.jsonl index d0920a8..2e9278b 100644 --- a/.beads/interactions.jsonl +++ b/.beads/interactions.jsonl @@ -47,3 +47,4 @@ {"id":"int-e554bed2958d709f2965a55fea9aa956","kind":"field_change","created_at":"2026-09-13T06:53:17.211928645Z","actor":"Shinuwa","issue_id":"sokko-g-zsd","extra":{"field":"status","new_value":"open","old_value":"in_progress"}} {"id":"int-6f399629fa8cc8212d36ca21853eaa52","kind":"field_change","created_at":"2026-09-13T17:46:00.430152831Z","actor":"Shinuwa","issue_id":"sokko-g-zsd","extra":{"field":"status","new_value":"open","old_value":"in_progress"}} {"id":"int-3823a391b02ae7a3d58956b24c10e998","kind":"field_change","created_at":"2026-09-13T18:43:29.457174095Z","actor":"Shinuwa","issue_id":"sokko-g-zsd","extra":{"field":"status","new_value":"open","old_value":"in_progress"}} +{"id":"int-e3a2bea901894d43a31eb628b588648a","kind":"field_change","created_at":"2026-09-14T07:08:33.027716428Z","actor":"Shinuwa","issue_id":"sokko-g-zsd","extra":{"field":"status","new_value":"open","old_value":"in_progress"}} diff --git a/docs/FEATURE_CHECKLIST.md b/docs/FEATURE_CHECKLIST.md index 3b63b36..a11d06b 100644 --- a/docs/FEATURE_CHECKLIST.md +++ b/docs/FEATURE_CHECKLIST.md @@ -66,6 +66,11 @@ Checklist à suivre lors de l'ajout ou de la modification d'un outil, d'une page - Vérifier les cartes, panels et scrollbars. - Vérifier les états hover/focus. - Vérifier mobile et desktop quand le changement touche la mise en page. +- Les tests `e2e/card-surfaces.spec.js` compilent le Sass complet et vérifient les + styles calculés des cartes NDS, home jeux et MHW en desktop/mobile : fonds, + bordures, séparateurs dorés, hover sans redimensionnement, focus clavier NDS + et faces MHW. Ils ne créent pas de snapshots visuels et ne remplacent pas les + parcours des pages réelles. ## Validation avant push diff --git a/e2e/card-surfaces.spec.js b/e2e/card-surfaces.spec.js new file mode 100644 index 0000000..97f9adf --- /dev/null +++ b/e2e/card-surfaces.spec.js @@ -0,0 +1,95 @@ +// Rôle : vérifie les surfaces de cartes à partir du Sass compilé et des styles calculés. +import { test, expect } from "@playwright/test"; +import { compile } from "sass"; + +let css; +test.beforeAll(() => { + css = compile("website/src/styles/main.scss").css; +}); + +async function mountCards(page) { + await page.setContent(` +
+
+
Cover
+
+ + IconGame + +
+
Art
Endemic
+
+
+
+
Front
+
Back
+
+
+
+ `); + await page.addStyleTag({ content: css }); + await page.mouse.move(0, 0); +} + +async function expectSurface(card, { hovered = false, face = false } = {}) { + await expect(card).toHaveCSS("border-top-width", "1px"); + await expect(card).toHaveCSS("border-top-style", "solid"); + await expect(card).toHaveCSS("border-top-color", "rgba(0, 0, 0, 0)"); + await expect(card).toHaveCSS("background-clip", hovered ? "padding-box, border-box" : "padding-box, border-box, border-box"); + const fill = hovered ? (face ? "0.95" : "0.96") : (face ? "0.92" : "0.88"); + await expect(card).toHaveCSS("background-image", new RegExp(`rgba\\(${hovered && !face ? "21, 26, 48" : "16, 20, 38"}, ${fill.replace(".", "\\.")}\\)`)); + await expect(card).toHaveCSS("background-image", hovered ? /139, 92, 246/ : /246, 196, 83/); + await expect(card).toHaveCSS("background-image", hovered ? /196, 181, 253/ : /34, 211, 238/); + if (hovered) await expect(card).toHaveCSS("box-shadow", /139, 92, 246/); + const radius = await card.evaluate((element) => element.ownerDocument.defaultView.getComputedStyle(element).getPropertyValue("--radius-md").trim()); + expect(radius).not.toBe(""); + await expect(card).toHaveCSS("border-top-left-radius", radius); +} + +for (const width of [1280, 390]) { + test(`compiled card surfaces preserve borders, dividers and hover at ${width}px`, async ({ page }) => { + await page.setViewportSize({ width, height: 900 }); + await mountCards(page); + for (const id of ["global", "home", "endemic"]) { + const card = page.locator(`#${id}`); + await expectSurface(card); + const before = await card.boundingBox(); + await card.hover(); + await expectSurface(card, { hovered: true }); + await expect(card).toHaveCSS("transform", "none"); + const after = await card.boundingBox(); + expect(after.width).toBe(before.width); + expect(after.height).toBe(before.height); + await page.mouse.move(0, 0); + await expectSurface(card); + } + for (const selector of [".nds-card-cover", ".game-home-card-icon", ".mhwilds-card-art"]) { + for (const divider of await page.locator(selector).all()) { + await expect(divider).toHaveCSS("border-bottom", "1px solid rgba(246, 196, 83, 0.42)"); + } + } + expect(await page.locator("body").evaluate((element) => element.scrollWidth)).toBeLessThanOrEqual(width); + }); + + test(`compiled monster faces and keyboard card state remain distinct at ${width}px`, async ({ page }) => { + await page.setViewportSize({ width, height: 900 }); + await mountCards(page); + const global = page.locator("#global"); + await global.getByRole("button").focus(); + await expectSurface(global, { hovered: true }); + const monster = page.locator("#monster"); + for (const face of await monster.locator(".mhwilds-card-face").all()) { + await expectSurface(face, { face: true }); + await expect(face).toHaveCSS("backface-visibility", "hidden"); + } + await monster.hover(); + await expect(monster).toHaveCSS("background-image", "none"); + await expect(monster).toHaveCSS("box-shadow", "none"); + for (const face of await monster.locator(".mhwilds-card-face").all()) { + await expectSurface(face, { face: true, hovered: true }); + await expect(face).toHaveCSS("box-shadow", /14px/); + } + await monster.evaluate((element) => element.classList.add("is-flipped")); + await expect(monster.locator(".mhwilds-card-inner")).toHaveCSS("transform", "matrix3d(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1)"); + }); +} diff --git a/tests/static-styles.test.mjs b/tests/static-styles.test.mjs index 3534bf5..46eb1db 100644 --- a/tests/static-styles.test.mjs +++ b/tests/static-styles.test.mjs @@ -19,7 +19,6 @@ test("style entrypoint and theme layers are wired", async () => { const styleMixins = await readFile("website/src/styles/_mixins.scss", "utf8"); const styleShell = await readFile("website/src/styles/_shell.scss", "utf8"); const styleCards = await readFile("website/src/styles/_cards.scss", "utf8"); - const styleSurfaces = await readFile("website/src/styles/design-system/_surfaces.scss", "utf8"); const styleControls = await readFile("website/src/styles/design-system/_controls.scss", "utf8"); const styleAttributes = await readFile("website/src/styles/design-system/_attributes.scss", "utf8"); const styleControlsPatterns = await readFile("website/src/styles/design-system/controls/_patterns.scss", "utf8"); @@ -247,9 +246,6 @@ test("style entrypoint and theme layers are wired", async () => { assert.match(styleGames, /\.game-layout/); assert.match(styleGames, /\.game-grid/); assert.match(styleGames, /\.filter-panel/); - assert.match(styleSurfaces, /@mixin card-surface/); - assert.match(styleSurfaces, /@mixin card-surface-hover/); - assert.match(styleSurfaces, /@mixin card-cover-divider/); assert.match(styleControls, /@forward "controls\/patterns"/); assert.match(styleAttributes, /@use "attributes\/patterns"/); assert.match(styleControlsPatterns, /@mixin panel-surface/); @@ -282,14 +278,8 @@ test("style entrypoint and theme layers are wired", async () => { assert.match(ndsPatternConsumers, /nds-toast/); assert.doesNotMatch(styleCards, /@use "design-system\/surfaces"/); assert.doesNotMatch(styleCards, /controls\.card-pattern/); - assert.match(styleGames, /@use "\.\.\/design-system\/surfaces"/); - assert.match(styleGames, /surfaces\.card-surface/); - assert.match(styleGames, /surfaces\.card-cover-divider/); assert.match(styleMhwilds, /\.mhwilds-card/); assert.match(styleMhwilds, /\.mhwilds-flip-indicator/); - assert.match(styleMhwilds, /@use "design-system\/surfaces"/); - assert.match(styleMhwilds, /surfaces\.card-surface/); - assert.match(styleMhwilds, /surfaces\.card-cover-divider/); assert.doesNotMatch(styleMhwilds, /\.game-(home-card|layout|grid)/); assert.doesNotMatch(styleCards, /\.compact-data-card\s*\{/); assert.doesNotMatch(