Files
workinf_Blender_Wasm/web/tests/e2e/web-area-graph-editor.spec.ts
mes123456 d0b6d2a164 feat(web): close remaining editor, layout and persistence gaps
- Area headers get a per-area editor switcher backed by a shared
  AreaEditorContext; Preferences can hide it.
- Animation camera area is a real navigable viewport aligned to the
  scene camera; Graph Editor adds channel selection, keyframe drag/
  insert/delete and interpolation.
- Geometry Nodes node locations round-trip through the native writer
  and reader, so saved graphs keep their layout.
- Texture Paint can make linked library images local via a new native
  makeImageLocal command before packing strokes.
- Compositor IMAGE and Render Layers nodes now persist to Main and
  execute against packed images / the last PBR render.
- Spreadsheet row cap raised to 250k; Outliner gains Ctrl+A and arrow/
  Home/End navigation.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-13 07:47:10 +08:00

67 lines
3.7 KiB
TypeScript

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<void> {
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<void> {
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 });
});