import { expect, test } from "@playwright/test"; import path from "node:path"; import { fileURLToPath } from "node:url"; const HERE = path.dirname(fileURLToPath(import.meta.url)); const STUDIO = path.resolve(HERE, "../../app/public/tutorial/pbr/studio.blend"); test.use({ viewport: { width: 1600, height: 900 } }); async function openStudio(page: import("@playwright/test").Page): Promise { await page.goto("/?ui=web"); await page.locator("main.blender-app").waitFor({ timeout: 120_000 }); await page.locator('[data-testid="blend-file-input"]').setInputFiles(STUDIO); await page.getByText(/Engine: SceneIR/).first().waitFor({ timeout: 120_000 }); await expect.poll(async () => page.getByTestId("scene-stats").innerText(), { timeout: 120_000 }).toMatch(/Objects 6/); } async function workspace(page: import("@playwright/test").Page, name: string): Promise { await page.locator(".workspace-tabs button", { hasText: name }).evaluate((element) => (element as HTMLElement).click()); await page.waitForTimeout(600); } test("Area headers switch editor types and restore the default", async ({ page }) => { test.setTimeout(240_000); await openStudio(page); const area = page.locator(".properties-area"); await expect(area.locator(".area-type-switch select")).toBeVisible(); await area.locator(".area-type-switch select").selectOption("Spreadsheet"); await expect(area).toHaveAttribute("data-editor", "Spreadsheet"); await expect(area.getByTestId("spreadsheet-view")).toBeVisible(); await area.locator(".area-type-switch select").selectOption("Properties"); await expect(area).toHaveAttribute("data-editor", "Properties"); await expect(area.locator(".area-type-switch select")).toBeVisible(); }); test("Camera viewport is real and Graph Editor edits animation keys", async ({ page }) => { test.setTimeout(300_000); await openStudio(page); await workspace(page, "Animation"); await expect(page.locator(".camera-view-area canvas").first()).toBeVisible(); const key = page.getByRole("button", { name: "插入位置关键帧", exact: true }); await expect(key).toBeVisible(); const before = Number(await page.locator("main.blender-app").getAttribute("data-project-snapshot-revision") ?? "0"); await key.evaluate((element) => (element as HTMLElement).click()); const graph = page.getByTestId("graph-editor"); await graph.waitFor({ timeout: 60_000 }); await expect.poll(async () => Number(await graph.getAttribute("data-channel-count")), { timeout: 60_000 }).toBeGreaterThan(0); await expect.poll(async () => Number(await graph.getAttribute("data-keyframe-count")), { timeout: 60_000 }).toBeGreaterThan(0); await expect.poll(async () => Number(await page.locator("main.blender-app").getAttribute("data-project-snapshot-revision") ?? "0"), { timeout: 60_000 }).toBeGreaterThan(before); await page.getByLabel("当前帧").fill("24"); await page.waitForTimeout(800); const countBefore = Number(await graph.getAttribute("data-keyframe-count")); await graph.getByRole("button", { name: "插入关键帧", exact: true }).click(); await expect.poll(async () => Number(await graph.getAttribute("data-keyframe-count")), { timeout: 60_000 }).toBeGreaterThan(countBefore); const dot = graph.locator("circle").first(); const box = await dot.boundingBox(); if (!box) throw new Error("graph keyframe dot has no box"); await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); await page.mouse.down(); await page.mouse.move(box.x + box.width / 2 + 35, box.y + box.height / 2, { steps: 8 }); await page.mouse.up(); await expect.poll(async () => graph.getAttribute("data-selected-frame"), { timeout: 30_000 }).not.toBe(""); await expect(graph.getByText(/关键帧已移动/)).toBeVisible({ timeout: 30_000 }); });