import { expect, test } from "@playwright/test"; test("Omni Apple silicon links open the published guide and MLX recipe", async ({ page }) => { for (const slash of ["", "/"]) { await page.goto(`/FastVideo/cookbook/minimax-h3/fasth3-omniref${slash}`); const guide = page.getByRole("link", { name: "FastH3 Omni on MLX", exact: true }); await expect(guide).toHaveAttribute("href", "/FastVideo/inference/fasth3_omniref_mlx"); await guide.click(); await expect(page).toHaveURL(/\/FastVideo\/inference\/fasth3_omniref_mlx\/?$/); await expect(page.getByRole("heading", { level: 1 })).toBeVisible(); } await page.goto("/FastVideo/cookbook/minimax-h3/fasth3-omniref"); await page.getByRole("link", { name: "MLX recipe", exact: true }).click(); await expect(page.getByLabel("Inference command")).toContainText("--mlx-checkpoint"); }); test("GitHub stars refresh automatically and survive a failed refresh", async ({ page }) => { await page.clock.install(); let requests = 0; await page.route("https://api.github.com/repos/hao-ai-lab/FastVideo", async (route) => { requests++; if (requests === 2) return route.fulfill({ status: 503, body: "Unavailable" }); await route.fulfill({ json: { stargazers_count: requests === 1 ? 4612 : 5234 } }); }); await page.goto("/FastVideo"); const stars = page.locator(".home-github-stars"); await expect(stars).toHaveText("4.6k"); await expect(stars).toHaveAccessibleName("FastVideo on GitHub, 4,612 stars"); await page.clock.fastForward(5 * 60 * 1000); await expect.poll(() => requests).toBe(2); await expect(stars).toHaveText("4.6k"); await page.clock.fastForward(5 * 60 * 1000); await expect(stars).toHaveText("5.2k"); }); test("GitHub rate limits leave a usable Star link without a fabricated count", async ({ page }) => { await page.route("https://api.github.com/repos/hao-ai-lab/FastVideo", (route) => route.fulfill({ status: 403, json: { message: "API rate limit exceeded" } }), ); await page.goto("/FastVideo"); const stars = page.getByRole("link", { name: "Star FastVideo on GitHub", exact: true }); await expect(stars).toHaveText("Star"); await expect(stars).toHaveAttribute("href", "https://github.com/hao-ai-lab/FastVideo"); }); test("documentation navigation opens transferred guides", async ({ page }) => { await page.goto("/FastVideo"); await expect(page.getByRole("heading", { name: "Welcome to FastVideo", exact: true })).toBeVisible(); await page.locator("#blume-nav-drawer").getByRole("link", { name: "Installation", exact: true }).click(); await expect(page).toHaveURL(/getting_started\/installation/, { timeout: 30000 }); await expect(page.getByRole("heading", { level: 1 })).toBeVisible({ timeout: 30000 }); }); test("the welcome page introduces the project and opens the cookbook", async ({ page }) => { await page.goto("/FastVideo"); const logo = page.getByRole("heading", { level: 1, name: "Welcome to FastVideo" }).getByRole("img"); await expect(logo).toBeVisible(); expect(await logo.evaluate((image: HTMLImageElement) => image.complete && image.naturalWidth > 0)).toBe(true); const column = await page.locator(".home-page").boundingBox(); const image = await logo.boundingBox(); expect(Math.abs(image!.x + image!.width / 2 - (column!.x + column!.width / 2))).toBeLessThan(2); await expect(page.getByRole("link", { name: "Hao AI Lab", exact: true })).toHaveAttribute( "href", "https://haoailab.com/", ); const lab = page.locator(".home-lab-link img"); expect(await lab.evaluate((image: HTMLImageElement) => image.complete && image.naturalWidth > 0)).toBe(true); const trending = page.locator(".home-trendshift a"); await expect(trending).toHaveAttribute( "href", "https://trendshift.io/repositories/14509?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-14509", ); await expect(trending.locator("img")).toHaveAttribute("src", "https://trendshift.io/api/badge/repositories/14509"); await expect(page.locator(".home-news time")).toHaveCount(2); await expect(page.locator(".home-news")).toContainText("Kandinsky 6"); await expect(page.getByRole("heading", { name: "Projects and research", exact: false })).toBeVisible(); await expect(page.getByRole("link", { name: "DanceGRPO", exact: true })).toBeVisible(); await expect(page.getByText("Use the navigation menu on the left to explore different sections:")).toHaveCount(0); await expect(page.getByText("About FastVideo and performance overview", { exact: true })).toHaveCount(0); await expect(page.locator(".home-page > p").first()).toContainText( "FastVideo is an inference and post-training framework", ); await page.getByRole("link", { name: /^Cookbook Choose a model recipe/ }).click(); await expect(page).toHaveURL(/FastVideo\/cookbook/); await expect(page.getByRole("heading", { name: "Cookbook", exact: true })).toBeVisible(); }); test("Fast Models connects the homepage, family collections, and checkpoints", async ({ page }) => { await page.goto("/FastVideo"); await page.locator(".home-project-links").getByRole("link", { name: "Fast Models", exact: true }).click(); await expect(page).toHaveURL(/FastVideo\/fast-models$/); await expect(page.getByRole("heading", { level: 1, name: "Fast Models", exact: true })).toBeVisible(); for (const family of ["FastH3", "FastWan", "FastMetal"]) { await expect(page.getByRole("heading", { level: 2, name: family, exact: false })).toBeVisible(); } await expect(page.getByRole("link", { name: "FastMetal collection", exact: true })).toHaveAttribute( "href", "https://huggingface.co/collections/FastVideo/fastmetal", ); await expect(page.locator('a[href="https://huggingface.co/FastVideo/FastWan-QAD-FP8-1.3B"]').first()).toBeVisible(); const checkpoints = await page .locator('a[href^="https://huggingface.co/FastVideo/"]') .evaluateAll((links) => [...new Set(links.map((link) => link.getAttribute("href")))]); expect(checkpoints.length).toBeGreaterThanOrEqual(18); await expect( page.locator("#blume-nav-drawer").getByRole("link", { name: "Fast Models", exact: true }), ).toHaveAttribute("aria-current", "page"); await page.setViewportSize({ width: 390, height: 844 }); expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); }); test("V1 API renders Python signatures within the Blume shell", async ({ page }) => { await page.goto("/FastVideo/getting_started/v1_api"); await expect(page.getByRole("heading", { name: "V1 API", exact: true })).toBeVisible(); await expect(page.locator('[id="fastvideo.entrypoints.video_generator.VideoGenerator"]')).toBeVisible(); await expect(page.locator(".python-reference")).toContainText("from_pretrained"); for (const description of [ "The inference arguments", "The executor class to use for inference", "Whether to log statistics", "Optional multiprocessing.Queue to forward worker logs to", ]) { await expect(page.locator(".python-reference")).toContainText(description); } await expect(page.getByRole("button", { name: "Search", exact: true })).toBeVisible(); }); test("the API overview resolves Python references and guide links", async ({ page }) => { await page.goto("/FastVideo/api"); const reference = page.locator(".python-reference"); await expect(reference.getByRole("link", { name: "API contracts and sampling", exact: true })).toHaveAttribute( "href", "/FastVideo/api/fastvideo/api#fastvideo.api", ); await reference.getByRole("link", { name: "fastvideo.VideoGenerator", exact: true }).click(); await expect(page.locator('[id="fastvideo.VideoGenerator"]')).toBeVisible(); await page.goto("/FastVideo/api"); await page.locator(".python-reference").getByRole("link", { name: "V1 API", exact: true }).click(); await expect(page).toHaveURL(/\/FastVideo\/getting_started\/v1_api$/); await expect(page.getByRole("heading", { name: "V1 API", exact: true })).toBeVisible(); }); test("generated module links stay inside the transferred Python reference", async ({ page }) => { await page.goto("/FastVideo/api/fastvideo"); await page.getByRole("link", { name: "configs", exact: true }).click(); await expect(page).toHaveURL(/FastVideo\/api\/fastvideo\/configs/); }); test("search finds a transferred guide and opens it by keyboard", async ({ page }) => { test.setTimeout(180000); await page.goto("/FastVideo"); await page.getByRole("button", { name: "Search", exact: true }).click(); const query = page.getByRole("combobox", { name: "Search docs", exact: true }); await query.fill("offloading"); await expect(page.getByRole("option", { name: /^Offloading/ }).first()).toBeVisible({ timeout: 120000 }); await query.press("Enter"); await expect(page).toHaveURL(/FastVideo\/inference\/offloading/, { timeout: 30000 }); }); test("the welcome cards reflow on mobile without horizontal scrolling", async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); await page.goto("/FastVideo"); await expect(page.getByRole("heading", { name: "Welcome to FastVideo", exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: /Quick start Generate/ })).toBeVisible(); expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true); }); test("the model catalog filters by family and checkpoint name", async ({ page }) => { await page.goto("/FastVideo/fast-models"); const catalog = page.locator(".model-catalog"); await expect(catalog.getByRole("status")).toHaveText("21 checkpoints"); await page.getByRole("button", { name: "FastMetal", exact: true }).click(); await expect(catalog.getByRole("status")).toHaveText("3 checkpoints"); await page.getByRole("searchbox", { name: "Search checkpoints", exact: true }).fill("14B"); await expect(catalog.getByRole("status")).toHaveText("1 checkpoint"); await page.getByRole("button", { name: "All models", exact: true }).click(); await page.getByRole("searchbox").fill("NVFP4"); const codes = await catalog.locator(".model-checkpoints code").allInnerTexts(); expect(codes.length).toBeGreaterThan(0); expect(codes.every((text) => text.includes("NVFP4"))).toBe(true); await expect(catalog.getByText("FastVideo/FastVideo-FastH3-8-Step-V2-FP8", { exact: true })).toHaveCount(0); await page.getByRole("searchbox").fill("no-such-model"); await expect(catalog.getByRole("status")).toHaveText("0 checkpoints"); await page.getByRole("button", { name: "Clear filters", exact: true }).click(); await expect(catalog.getByRole("status")).toHaveText("21 checkpoints"); }); test("hardware guides are grouped below general inference tasks", async ({ page }) => { await page.goto("/FastVideo"); const nav = page.locator("#blume-nav-drawer"); await expect(nav.getByRole("link", { name: "Two DGX Sparks", exact: true })).not.toBeVisible(); await nav.getByText("Hardware", { exact: true }).click(); await nav.getByRole("link", { name: "Two DGX Sparks", exact: true }).click(); await expect(page).toHaveURL(/getting_started\/installation\/spark_pair/); }); test("family runbooks use React model cards without the old cookbook runtime", async ({ page }) => { await page.goto("/FastVideo/cookbook/wan/"); await expect(page.locator(".recipe-card").first()).toBeVisible(); await expect(page.getByRole("button", { name: "Search", exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: "← All recipes", exact: true })).toHaveAttribute( "href", /\/FastVideo\/cookbook$/, ); await expect(page.locator('script[src*="/assets/cookbook.js"],link[href*="cookbook-runbooks.css"]')).toHaveCount(0); }); test("generated example titles render once and retain their original fragment", async ({ page }) => { await page.goto("/FastVideo/inference/examples/basic"); await expect(page.getByRole("heading", { name: "Basic Video Generation Tutorial", exact: true })).toHaveCount(1); await expect(page.locator('[id="basic-video-generation-tutorial"]')).toHaveCount(1); await expect(page.locator("article")).toContainText("examples/inference/basic"); }); test("hardware overview distinguishes runtime support and AMD documentation limits", async ({ page }) => { await page.goto("/FastVideo/getting_started/hardware"); const table = page.getByRole("table"); for (const platform of ["RTX", "data center", "DGX Spark", "Apple Silicon", "AMD"]) { await expect(table).toContainText(platform); } await page.getByRole("link", { name: "ROCm support and current limits", exact: true }).click(); await expect(page.getByRole("heading", { level: 1 })).toHaveText("AMD and ROCm"); await expect(page.locator("article")).toContainText("not currently documented"); }); test("homepage project buttons have visible borders in dark mode", async ({ page }) => { await page.emulateMedia({ colorScheme: "dark" }); await page.goto("/FastVideo"); await expect(page.locator("html")).toHaveAttribute("data-theme", "dark"); const colors = await page.locator(".home-project-links a").evaluateAll((links) => links.map((link) => { const style = getComputedStyle(link); return { border: style.borderTopColor, width: style.borderTopWidth, background: getComputedStyle(document.body).backgroundColor, }; }), ); expect(colors).toHaveLength(3); for (const color of colors) { expect(color.width).toBe("1px"); const luminance = (value: string) => { const channels = value .match(/[\d.]+/g)! .slice(0, 3) .map(Number) .map((channel) => (value.startsWith("color(srgb") ? channel : channel / 255)) .map((channel) => (channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4)); return channels[0] * 0.2126 + channels[1] * 0.7152 + channels[2] * 0.0722; }; const border = luminance(color.border); const background = luminance(color.background); expect((Math.max(border, background) + 0.05) / (Math.min(border, background) + 0.05)).toBeGreaterThanOrEqual(3); } });