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 expect.poll(async () => page.getByTestId("scene-stats").innerText(), { timeout: 120_000 }).toMatch(/Objects 6/); await page.waitForTimeout(1500); const canvas = page.locator(".viewport-area canvas.viewport-canvas").first(); await canvas.waitFor({ timeout: 60_000 }); await canvas.focus(); await page.waitForTimeout(300); return canvas; } async function dragMiddle(page: import("@playwright/test").Page, canvas: import("@playwright/test").Locator): Promise { const box = await canvas.boundingBox(); if (!box) throw new Error("viewport has no box"); await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); await page.mouse.down({ button: "middle" }); await page.mouse.move(box.x + box.width / 2 + 50, box.y + box.height / 2 + 25, { steps: 10 }); await page.mouse.up({ button: "middle" }); await page.waitForTimeout(700); } test("Camera view aligns to the scene camera and restores the last view", async ({ page }) => { test.setTimeout(300_000); const canvas = await openStudio(page); await expect(page.locator("main.blender-app")).toHaveAttribute("data-project-snapshot-revision", /^[0-9]+$/); const before = { position: await canvas.getAttribute("data-camera-position"), yaw: await canvas.getAttribute("data-camera-yaw"), pitch: await canvas.getAttribute("data-camera-pitch"), revision: await page.locator("main.blender-app").getAttribute("data-project-snapshot-revision"), }; await page.keyboard.press("Numpad0"); await expect(canvas).toHaveAttribute("data-camera-view", "CAMERA", { timeout: 30_000 }); await expect.poll(async () => canvas.getAttribute("data-camera-position"), { timeout: 30_000 }).toBe("7.2,-7.2,5.4"); await expect(page.locator("main.blender-app")).toHaveAttribute("data-project-snapshot-revision", before.revision ?? "0"); await dragMiddle(page, canvas); await expect(canvas).toHaveAttribute("data-camera-view", "CAMERA"); await expect(page.locator("main.blender-app")).toHaveAttribute("data-project-snapshot-revision", before.revision ?? "0"); const zoomBefore = await canvas.getAttribute("data-camera-zoom"); await canvas.evaluate((element) => element.dispatchEvent(new WheelEvent("wheel", { bubbles: true, cancelable: true, deltaY: -120 }))); await expect.poll(async () => canvas.getAttribute("data-camera-zoom"), { timeout: 10_000 }).not.toBe(zoomBefore); await expect(page.locator("main.blender-app")).toHaveAttribute("data-project-snapshot-revision", before.revision ?? "0"); await page.keyboard.press("Numpad0"); await expect(canvas).toHaveAttribute("data-camera-view", "USER", { timeout: 30_000 }); await expect.poll(async () => canvas.getAttribute("data-camera-position"), { timeout: 30_000 }).toBe(before.position); await expect(canvas).toHaveAttribute("data-camera-yaw", before.yaw ?? ""); await expect(canvas).toHaveAttribute("data-camera-pitch", before.pitch ?? ""); }); test("Camera to View writes the camera and Camera to View lock moves it", async ({ page }) => { test.setTimeout(300_000); const canvas = await openStudio(page); const before = Number(await page.locator("main.blender-app").getAttribute("data-project-snapshot-revision") ?? "0"); await page.keyboard.press("Control+Alt+Numpad0"); await expect.poll(async () => Number(await page.locator("main.blender-app").getAttribute("data-project-snapshot-revision") ?? "0"), { timeout: 60_000 }).toBeGreaterThan(before); await expect(canvas).toHaveAttribute("data-camera-view", "CAMERA", { timeout: 30_000 }); await page.getByRole("button", { name: "锁定相机", exact: true }).evaluate((element) => (element as HTMLElement).click()); await expect(canvas).toHaveAttribute("data-camera-locked", "true"); const lockedRevision = Number(await page.locator("main.blender-app").getAttribute("data-project-snapshot-revision") ?? "0"); await dragMiddle(page, canvas); await expect.poll(async () => Number(await page.locator("main.blender-app").getAttribute("data-project-snapshot-revision") ?? "0"), { timeout: 60_000 }).toBeGreaterThan(lockedRevision); await expect(canvas).toHaveAttribute("data-camera-view", "CAMERA"); const otherCanvas = page.locator(".camera-view-area canvas.viewport-canvas").first(); if (await otherCanvas.count()) await expect(otherCanvas).not.toHaveAttribute("data-camera-locked", "true"); await page.reload(); await page.locator("main.blender-app").waitFor({ timeout: 120_000 }); const reloaded = page.locator(".viewport-area canvas.viewport-canvas").first(); await reloaded.waitFor({ timeout: 60_000 }); await expect.poll(async () => reloaded.getAttribute("data-camera-locked"), { timeout: 30_000 }).toBe("true"); });