Files
2026-07-21 16:50:58 +02:00

111 lines
4.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { expect, test } from "@playwright/test";
const repoRoot = process.cwd();
const gifFlow = path.join(repoRoot, "apps/web/public/tour/flow-builder.gif");
const gifRecipe = path.join(repoRoot, "apps/web/public/tour/recipe-sheet.gif");
const assertFfmpeg = () => {
try {
execFileSync("ffmpeg", ["-version"], { stdio: "ignore" });
} catch {
throw new Error("ffmpeg est requis pour npm run tour:capture (ex. apt install ffmpeg)");
}
};
const webmToGif = (webm: string, gif: string) => {
execFileSync(
"ffmpeg",
[
"-y",
"-i",
webm,
"-vf",
"fps=8,scale=360:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=128:stats_mode=single[p];[s1][p]paletteuse=dither=bayer",
gif
],
{ stdio: "inherit" }
);
};
const seedStableSession = async (context: import("@playwright/test").BrowserContext) => {
await context.addInitScript(() => {
localStorage.setItem("ben-to-cookie-consent-v1", "analytics-rejected");
localStorage.setItem("ben-to.welcome-tour-dismissed", "v1");
});
};
test.describe.configure({ mode: "serial" });
test.describe("GIFs public/tour (Playwright + ffmpeg)", () => {
test("flow-builder.gif", async ({ browser }, testInfo) => {
assertFfmpeg();
fs.mkdirSync(path.dirname(gifFlow), { recursive: true });
const videoDir = testInfo.outputPath("flow-video");
fs.rmSync(videoDir, { recursive: true, force: true });
fs.mkdirSync(videoDir, { recursive: true });
const context = await browser.newContext({
recordVideo: { dir: videoDir, size: { width: 390, height: 760 } },
viewport: { width: 390, height: 760 }
});
await seedStableSession(context);
const page = await context.newPage();
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();
await expect(page.locator(".builder-variant-base-reminder")).toBeVisible({ timeout: 25_000 });
const firstVariant = page.locator(".cards button.card").first();
await firstVariant.waitFor({ state: "visible" });
await firstVariant.click();
await page.waitForTimeout(500);
/* Sarrête ici (étape garniture) pour ne pas dupliquer la fiche recette du second GIF. */
const video = page.video();
await page.close();
const webm = await video?.path();
await context.close();
if (!webm) throw new Error("Aucun fichier vidéo Playwright");
webmToGif(webm, gifFlow);
});
test("recipe-sheet.gif", async ({ browser }, testInfo) => {
assertFfmpeg();
fs.mkdirSync(path.dirname(gifRecipe), { recursive: true });
const videoDir = testInfo.outputPath("recipe-video");
fs.rmSync(videoDir, { recursive: true, force: true });
fs.mkdirSync(videoDir, { recursive: true });
const context = await browser.newContext({
recordVideo: { dir: videoDir, size: { width: 390, height: 760 } },
viewport: { width: 390, height: 760 }
});
await seedStableSession(context);
const page = await context.newPage();
await page.goto("/r/base%3Aonigiri/variant%3Aonigiri-kimchi-mozza");
await expect(page.locator("#recipe-sheet-title")).toBeVisible({ timeout: 25_000 });
const sheet = page.locator("section.recipe-sheet");
await page.waitForTimeout(400);
await sheet.evaluate((el) => {
el.scrollTo({ top: 0, behavior: "auto" });
});
await page.waitForTimeout(450);
await sheet.evaluate((el) => {
el.scrollTo({ top: 400, behavior: "auto" });
});
await page.waitForTimeout(450);
await sheet.evaluate((el) => {
el.scrollTo({ top: 900, behavior: "auto" });
});
await page.waitForTimeout(450);
const video = page.video();
await page.close();
const webm = await video?.path();
await context.close();
if (!webm) throw new Error("Aucun fichier vidéo Playwright");
webmToGif(webm, gifRecipe);
});
});