Files
workinf_Blender_Wasm/web/tests/e2e/curve-topology-operator.spec.ts
mes123456 f3e21c6b98
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
chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer,
web workflow engine and protocol, tooling, docs and task records.
Large generated evidence, user-testing archives, build snapshots and
imported test assets remain untracked.

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

79 lines
4.1 KiB
TypeScript

import { expect, test } from "@playwright/test";
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
const fixture = path.resolve(import.meta.dirname, "../../../tests/files/web/nonmesh_scene.blend");
const golden = JSON.parse(fs.readFileSync(path.resolve(import.meta.dirname, "../../../tests/golden/M9-05/curve-toggle-cyclic.json"), "utf8"));
test("M9-05 exposes TOGGLE_CYCLIC only after Main, undo, save and Blender golden verification", async ({ page }) => {
test.setTimeout(120_000);
const fixtureBytes = fs.readFileSync(fixture);
expect(crypto.createHash("sha256").update(fixtureBytes).digest("hex")).toBe(golden.fixtureSha256);
await page.goto("/");
await expect(page.getByTestId("engine-status")).toContainText("ready, open a .blend file", { timeout: 20_000 });
await page.getByTestId("blend-file-input").setInputFiles(fixture);
const curveRow = page.locator('[data-data-id="curve:WebCurveData"]');
await expect(curveRow).toBeVisible();
await curveRow.click();
const app = page.locator(".blender-app");
const editor = page.getByTestId("curve-topology-editor");
const cyclic = page.getByRole("checkbox", { name: "Curve cyclic U" });
await expect(editor).toHaveAttribute("data-ready-operators", "TOGGLE_CYCLIC");
await expect(editor).toHaveAttribute("data-spline-types", golden.splineTypes.join(","));
await expect(editor).toHaveAttribute("data-point-count", String(golden.splinePointCounts.reduce((sum: number, count: number) => sum + count, 0)));
await expect(editor).toHaveAttribute("data-cyclic-u", golden.beforeCyclicU.join(","));
await expect(cyclic).not.toBeChecked();
const selectedRevision = Number(await app.getAttribute("data-current-main-revision"));
await cyclic.click();
await expect(editor).toHaveAttribute("data-cyclic-u", golden.afterCyclicU.join(","));
await expect(cyclic).toBeChecked();
await expect(app).toHaveAttribute("data-dirty", "true");
const toggledRevision = Number(await app.getAttribute("data-current-main-revision"));
expect(toggledRevision).toBe(selectedRevision + 1);
await page.getByRole("button", { name: "撤销" }).click();
await expect(editor).toHaveAttribute("data-cyclic-u", golden.beforeCyclicU.join(","));
const undoRevision = Number(await app.getAttribute("data-current-main-revision"));
expect(undoRevision).toBe(toggledRevision + 1);
await page.getByRole("button", { name: "重做" }).click();
await expect(editor).toHaveAttribute("data-cyclic-u", golden.afterCyclicU.join(","));
const redoRevision = Number(await app.getAttribute("data-current-main-revision"));
expect(redoRevision).toBe(undoRevision + 1);
const download = page.waitForEvent("download");
await page.getByRole("button", { name: "保存项目" }).click();
await download;
await expect(app).toHaveAttribute("data-dirty", "false");
await page.getByRole("button", { name: "关闭项目" }).click();
await expect(editor).toHaveCount(0);
await page.getByRole("button", { name: "恢复项目" }).click();
await expect(curveRow).toBeVisible();
await curveRow.click();
await expect(editor).toHaveAttribute("data-cyclic-u", golden.reopenedCyclicU.join(","));
await expect(cyclic).toBeChecked();
fs.mkdirSync(path.resolve(import.meta.dirname, "../../../tests/golden/WBV2-P1131-001"), { recursive: true });
fs.writeFileSync(
path.resolve(import.meta.dirname, "../../../tests/golden/WBV2-P1131-001/curve-topology-operator-production.json"),
`${JSON.stringify({
schemaVersion: 1,
task: "WBV2-P1131-001",
operation: "CURVE_TOPOLOGY_OPERATOR_MAIN_CHROMIUM",
browser: "chromium",
queueMutation: false,
commandEquivalent: { command: "setCurveTopology", sourceOperator: "CURVE_OT_cyclic_toggle", status: "EQUIVALENT_BEHAVIOR_ONLY" },
fixtureSha256: golden.fixtureSha256,
curveName: golden.curveName,
splineIndex: golden.splineIndex,
beforeCyclicU: golden.beforeCyclicU,
afterCyclicU: golden.afterCyclicU,
reopenedCyclicU: golden.reopenedCyclicU,
revisions: { selected: selectedRevision, toggled: toggledRevision, undo: undoRevision, redo: redoRevision },
}, null, 2)}\n`,
);
});