Files
workinf_Blender_Wasm/web/tests/e2e/web-uv-workspace-gate.spec.ts
mes123456 7267db7cb4
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
feat(web): enable UV editing in web UI and use OPFS save/reopen
- Forward workspace and mode to the native context in ?ui=web.
- Locate the writable native UV area; enter Edit explicitly and wait for
  project recovery locks before switching mode.
- Save projects to OPFS by default; add an explicit manual .blend download.
- Add UV E2E coverage and implementation status.

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

91 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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");
const GLB = path.resolve(HERE, "../../../re/猴头.glb");
test.use({ viewport: { width: 1600, height: 900 } });
async function openStudioAndMonkey(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/);
await page.locator('[data-testid="blend-file-input"]').setInputFiles(GLB);
await expect.poll(async () => page.getByTestId("scene-stats").innerText(), { timeout: 120_000 }).toMatch(/Objects 7/);
}
async function ensureEditMode(page: import("@playwright/test").Page): Promise<void> {
const status = page.locator('[data-testid="engine-status"]');
if ((await status.innerText()).includes("模式:Edit")) return;
try {
await expect(status).toContainText("模式:Edit", { timeout: 5_000 });
return;
}
catch { /* fall through to the explicit mode menu */ }
await page.locator(".mode-chip button").first().evaluate((element) => (element as HTMLElement).click());
const editMode = page.getByRole("menuitemcheckbox", { name: /编辑模式/ });
await editMode.waitFor({ timeout: 15_000 });
await editMode.evaluate((element) => (element as HTMLElement).click());
await expect(status).toContainText("模式:Edit", { timeout: 60_000 });
await page.keyboard.press("Escape");
}
async function openUVWorkspace(page: import("@playwright/test").Page): Promise<void> {
// The native desktop canvas can sit above the workspace tab at the same
// coordinates in headed Chromium; invoke the tab's DOM click directly.
await page.locator(".workspace-tabs button", { hasText: "UV Editing" }).evaluate((element) => (element as HTMLElement).click());
await expect(page.locator(".wasm-uv-editor")).toBeVisible({ timeout: 60_000 });
await expect(page.locator(".uv-controls")).toBeVisible({ timeout: 60_000 });
await expect.poll(async () => page.locator('[data-testid="editor-workflow-context"]').innerText(), { timeout: 60_000 })
.toContain("UV Editing");
// Desktop Blender's UV editor requires OB_MODE_EDIT (space_image.cc);
// enter Edit explicitly rather than depending on a startup timing quirk.
await ensureEditMode(page);
await expect.poll(async () => page.locator(".wasm-uv-editor").innerText(), { timeout: 60_000 }).toContain("原生 UV 选择");
}
async function selectAllAndUnwrap(page: import("@playwright/test").Page): Promise<void> {
await page.locator(".wasm-uv-editor button", { hasText: "全选 UV" }).evaluate((element) => (element as HTMLElement).click());
await expect.poll(async () => page.locator(".wasm-uv-editor").innerText(), { timeout: 60_000 }).toContain("UV 选择已提交");
await page.locator(".wasm-uv-editor button", { hasText: "展开所选 UV" }).evaluate((element) => (element as HTMLElement).click());
await expect.poll(async () => page.locator(".wasm-uv-editor").innerText(), { timeout: 60_000 }).toContain("展开 UV已提交");
}
test("UV Editing finds a writable native UV area and commits an unwrap", async ({ page }) => {
test.setTimeout(300_000);
await openStudioAndMonkey(page);
await openUVWorkspace(page);
await expect(page.locator('[data-testid="engine-status"]')).toContainText("模式:Edit", { timeout: 60_000 });
const before = Number(await page.locator("main.blender-app").getAttribute("data-project-snapshot-revision") ?? "0");
await selectAllAndUnwrap(page);
const after = Number(await page.locator("main.blender-app").getAttribute("data-project-snapshot-revision") ?? "0");
expect(after).toBeGreaterThan(before);
});
test("UV workspace survives save and reopen", async ({ page }) => {
test.setTimeout(360_000);
await openStudioAndMonkey(page);
await openUVWorkspace(page);
await selectAllAndUnwrap(page);
// Save through the OPFS project transaction (the download is a side effect
// of the same commit), then reload and reopen the recent OPFS project.
await page.locator('button[aria-label="保存项目"]').evaluate((element) => (element as HTMLElement).click());
await expect(page.locator("main.blender-app")).toHaveAttribute("data-user-action-save-status", "SUCCEEDED", { timeout: 60_000 });
await page.reload();
await page.locator("main.blender-app").waitFor({ timeout: 120_000 });
await expect.poll(async () => page.locator('[data-testid="recent-projects"] option').count(), { timeout: 60_000 }).toBeGreaterThan(1);
await page.locator('[data-testid="recent-projects"]').selectOption({ index: 1 });
const discard = page.getByRole("button", { name: "不保存,继续", exact: true });
if (await discard.count()) await discard.click();
await expect.poll(async () => page.getByTestId("scene-stats").innerText(), { timeout: 120_000 }).toMatch(/Objects 7/);
await openUVWorkspace(page);
await expect(page.locator(".uv-controls")).toBeVisible({ timeout: 60_000 });
await expect.poll(async () => page.locator(".wasm-uv-editor").innerText(), { timeout: 60_000 }).toContain("原生 UV 选择");
});