first commit
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
import { expect, test } from "@playwright/test";
|
||||
import AxeBuilder from "@axe-core/playwright";
|
||||
|
||||
const qualityOutDir = path.resolve(process.cwd(), "quality-out");
|
||||
|
||||
async function writeA11ySummary(violationCount: number, violationIds: string[]): Promise<void> {
|
||||
await mkdir(qualityOutDir, { recursive: true });
|
||||
const payload = {
|
||||
axeViolationCount: violationCount,
|
||||
axeViolationIds: violationIds,
|
||||
recordedAt: new Date().toISOString()
|
||||
};
|
||||
await writeFile(path.join(qualityOutDir, "a11y-summary.json"), JSON.stringify(payload, null, 2), "utf8");
|
||||
}
|
||||
|
||||
test.describe("Construire un bento en 3 étapes", () => {
|
||||
test("builder flow and accessibility baseline", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
const firstBase = page.locator(".cards button.card").first();
|
||||
await firstBase.waitFor({ state: "visible" });
|
||||
await firstBase.click();
|
||||
await page.getByRole("button", { name: /Continuer|Continue|계속|继续/i }).click();
|
||||
const firstVariant = page.locator(".cards button.card").first();
|
||||
await firstVariant.waitFor({ state: "visible" });
|
||||
await expect(page.locator(".builder-variant-base-reminder")).toBeVisible();
|
||||
await firstVariant.click();
|
||||
await page.getByRole("button", { name: /Continuer|Continue|계속|继续/i }).click();
|
||||
await expect(page.locator("#recipe-sheet-title")).toBeVisible();
|
||||
await expect(page.getByRole("heading", { name: /Ingrédients|Ingredients|재료|食材/i })).toBeVisible();
|
||||
|
||||
const firstSpriteImg = page.locator(".recipe-sheet__ingredient-sprite img").first();
|
||||
await expect(firstSpriteImg).toBeVisible();
|
||||
await expect
|
||||
.poll(async () => firstSpriteImg.evaluate((el: HTMLImageElement) => el.naturalWidth > 0 && el.naturalHeight > 0), {
|
||||
timeout: 15_000
|
||||
})
|
||||
.toBeTruthy();
|
||||
|
||||
const accessibilityScanResults = await new AxeBuilder({ page }).analyze();
|
||||
await writeA11ySummary(
|
||||
accessibilityScanResults.violations.length,
|
||||
accessibilityScanResults.violations.map((v) => v.id)
|
||||
);
|
||||
expect(accessibilityScanResults.violations).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Partage et URL recette", () => {
|
||||
test("recipe share URL opens recipe view", async ({ page }) => {
|
||||
await page.goto("/r/base%3Aonigiri/variant%3Aonigiri-kimchi-mozza");
|
||||
await expect(page.locator("#recipe-sheet-title")).toBeVisible({ timeout: 20_000 });
|
||||
await expect(page.getByRole("heading", { name: /Ingrédients|Ingredients|재료|食材/i })).toBeVisible();
|
||||
});
|
||||
|
||||
test("HTML initial contient les métadonnées Open Graph recette", async ({ page }) => {
|
||||
await page.goto("/r/base%3Aonigiri/variant%3Aonigiri-kimchi-mozza");
|
||||
await expect(page.locator('meta[property="og:type"]')).toHaveAttribute("content", "article");
|
||||
await expect(page.locator('meta[property="og:title"]')).toHaveAttribute("content", /Onigiri/);
|
||||
await expect(page.locator('meta[property="og:image"]')).toHaveAttribute("content", /^https?:\/\//);
|
||||
await expect(page.locator('meta[property="og:url"]')).toHaveAttribute(
|
||||
"content",
|
||||
/\/r\/base%3Aonigiri\/variant%3Aonigiri-kimchi-mozza$/
|
||||
);
|
||||
await expect(page.locator('meta[name="twitter:card"]')).toHaveAttribute(
|
||||
"content",
|
||||
"summary_large_image"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Performance (sanity)", () => {
|
||||
test("navigation home produit des métriques de timing", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.waitForLoadState("domcontentloaded");
|
||||
const timing = await page.evaluate(() => {
|
||||
const nav = performance.getEntriesByType("navigation")[0] as PerformanceNavigationTiming | undefined;
|
||||
return nav
|
||||
? {
|
||||
domContentLoaded: nav.domContentLoadedEventEnd - nav.startTime,
|
||||
loadEventEnd: nav.loadEventEnd - nav.startTime
|
||||
}
|
||||
: null;
|
||||
});
|
||||
await mkdir(qualityOutDir, { recursive: true });
|
||||
await writeFile(
|
||||
path.join(qualityOutDir, "perf-summary.json"),
|
||||
JSON.stringify({ navigationTiming: timing, recordedAt: new Date().toISOString() }, null, 2),
|
||||
"utf8"
|
||||
);
|
||||
expect(timing).not.toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user