first commit

This commit is contained in:
2026-07-21 16:50:58 +02:00
commit 256599626e
407 changed files with 30489 additions and 0 deletions
+110
View File
@@ -0,0 +1,110 @@
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);
});
});