Verify compiled card surfaces across desktop and mobile
All checks were successful
Deploy Sokko G / deploy (push) Successful in 19s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 19s
This commit is contained in:
parent
713bc3b70e
commit
77f357e056
4 changed files with 101 additions and 10 deletions
|
|
@ -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-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-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-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"}}
|
||||||
|
|
|
||||||
|
|
@ -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 cartes, panels et scrollbars.
|
||||||
- Vérifier les états hover/focus.
|
- Vérifier les états hover/focus.
|
||||||
- Vérifier mobile et desktop quand le changement touche la mise en page.
|
- 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
|
## Validation avant push
|
||||||
|
|
||||||
|
|
|
||||||
95
e2e/card-surfaces.spec.js
Normal file
95
e2e/card-surfaces.spec.js
Normal file
|
|
@ -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(`
|
||||||
|
<main style="display:grid;gap:24px;padding:24px">
|
||||||
|
<article id="global" class="nds-card" data-variant="interactive">
|
||||||
|
<div class="nds-card-cover">Cover</div><button>Open</button>
|
||||||
|
</article>
|
||||||
|
<a id="home" class="nds-card game-home-card" href="#game">
|
||||||
|
<span class="game-home-card-icon">Icon</span><strong>Game</strong>
|
||||||
|
</a>
|
||||||
|
<article id="endemic" class="mhwilds-card endemic-card">
|
||||||
|
<div class="mhwilds-card-art">Art</div><div class="mhwilds-card-body">Endemic</div>
|
||||||
|
</article>
|
||||||
|
<article id="monster" class="mhwilds-card monster-card" tabindex="0">
|
||||||
|
<div class="mhwilds-card-inner">
|
||||||
|
<div class="mhwilds-card-face mhwilds-card-front"><div class="mhwilds-card-art">Front</div></div>
|
||||||
|
<div class="mhwilds-card-face mhwilds-card-back"><div class="mhwilds-card-body">Back</div></div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
`);
|
||||||
|
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)");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,6 @@ test("style entrypoint and theme layers are wired", async () => {
|
||||||
const styleMixins = await readFile("website/src/styles/_mixins.scss", "utf8");
|
const styleMixins = await readFile("website/src/styles/_mixins.scss", "utf8");
|
||||||
const styleShell = await readFile("website/src/styles/_shell.scss", "utf8");
|
const styleShell = await readFile("website/src/styles/_shell.scss", "utf8");
|
||||||
const styleCards = await readFile("website/src/styles/_cards.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 styleControls = await readFile("website/src/styles/design-system/_controls.scss", "utf8");
|
||||||
const styleAttributes = await readFile("website/src/styles/design-system/_attributes.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");
|
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-layout/);
|
||||||
assert.match(styleGames, /\.game-grid/);
|
assert.match(styleGames, /\.game-grid/);
|
||||||
assert.match(styleGames, /\.filter-panel/);
|
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(styleControls, /@forward "controls\/patterns"/);
|
||||||
assert.match(styleAttributes, /@use "attributes\/patterns"/);
|
assert.match(styleAttributes, /@use "attributes\/patterns"/);
|
||||||
assert.match(styleControlsPatterns, /@mixin panel-surface/);
|
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.match(ndsPatternConsumers, /nds-toast/);
|
||||||
assert.doesNotMatch(styleCards, /@use "design-system\/surfaces"/);
|
assert.doesNotMatch(styleCards, /@use "design-system\/surfaces"/);
|
||||||
assert.doesNotMatch(styleCards, /controls\.card-pattern/);
|
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-card/);
|
||||||
assert.match(styleMhwilds, /\.mhwilds-flip-indicator/);
|
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(styleMhwilds, /\.game-(home-card|layout|grid)/);
|
||||||
assert.doesNotMatch(styleCards, /\.compact-data-card\s*\{/);
|
assert.doesNotMatch(styleCards, /\.compact-data-card\s*\{/);
|
||||||
assert.doesNotMatch(
|
assert.doesNotMatch(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue