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(800); } async function openAreaMenu(page: import("@playwright/test").Page, area: import("@playwright/test").Locator): Promise { await area.locator(".area-type-switch .area-menu button").evaluate((element) => (element as HTMLElement).click()); await page.getByRole("menu").last().waitFor({ timeout: 10_000 }); } test("Area menu splits, joins, swaps, maximizes and resets the web layout", async ({ page }) => { test.setTimeout(300_000); await openStudio(page); const app = page.locator("main.blender-app"); const grid = page.locator(".workspace-grid"); const properties = page.locator(".properties-area"); await expect.poll(async () => grid.locator(":scope > .area-frame").count()).toBe(4); await openAreaMenu(page, properties); await page.getByRole("menuitem", { name: "垂直分割(左右)", exact: true }).click(); await expect(app).toHaveAttribute("data-area-layout", "custom"); await expect.poll(async () => grid.locator(":scope > .area-frame").count()).toBe(5); const split = page.locator('.area-frame[data-web-area-id="area-1"]'); await expect(split).toBeVisible(); await openAreaMenu(page, split); await page.getByRole("menuitem", { name: "最大化区域", exact: true }).click(); await expect.poll(async () => grid.locator(":scope > .area-frame").count()).toBe(1); await openAreaMenu(page, split); await page.getByRole("menuitem", { name: "还原大小", exact: true }).click(); await expect.poll(async () => grid.locator(":scope > .area-frame").count()).toBe(5); await openAreaMenu(page, split); await page.getByRole("menuitem", { name: "交换编辑器", exact: true }).click(); const swapItem = page.getByRole("menuitem").filter({ hasText: "viewport-area" }).first(); await expect(swapItem).toBeVisible({ timeout: 10_000 }); await swapItem.click(); await expect.poll(async () => page.locator('.area-frame[data-web-area-id="area-1"]').getAttribute("data-editor")).not.toBe("Properties"); await openAreaMenu(page, split); await page.getByRole("menuitem", { name: "复制区域", exact: true }).click(); await expect.poll(async () => grid.locator(":scope > .area-frame").count()).toBe(6); const duplicated = page.locator('.area-frame[data-web-area-id="area-2"]'); await expect(duplicated).toBeVisible(); await openAreaMenu(page, duplicated); await page.getByRole("menuitem", { name: "合并 / 停靠", exact: true }).click(); let joined = false; for (const label of ["合并到左侧", "合并到右侧", "合并到上方", "合并到下方"]) { const item = page.getByRole("menuitem", { name: label, exact: true }); if (await item.count() && await item.isEnabled()) { await item.click(); joined = true; break; } } expect(joined).toBe(true); await expect.poll(async () => grid.locator(":scope > .area-frame").count()).toBe(5); await openAreaMenu(page, page.locator(".workspace-grid > .area-frame").first()); await page.getByRole("menuitem", { name: "重置工作区布局", exact: true }).click(); await expect(app).toHaveAttribute("data-area-layout", "default"); await expect.poll(async () => grid.locator(":scope > .area-frame").count()).toBe(4); }); test("Custom area layout survives an OPFS reload", async ({ page }) => { test.setTimeout(300_000); await openStudio(page); const properties = page.locator(".properties-area"); await openAreaMenu(page, properties); await page.getByRole("menuitem", { name: "水平分割(上下)", exact: true }).click(); await expect.poll(async () => page.locator(".workspace-grid > .area-frame").count()).toBe(5); await page.waitForTimeout(900); await page.reload(); await page.locator("main.blender-app").waitFor({ timeout: 120_000 }); await expect.poll(async () => page.locator("main.blender-app").getAttribute("data-area-layout"), { timeout: 30_000 }).toBe("custom"); await expect.poll(async () => page.locator(".workspace-grid > .area-frame").count(), { timeout: 30_000 }).toBe(5); await expect(page.locator('.area-frame[data-web-area-id="area-1"]')).toBeVisible(); });