Checkpoint web parity through Chromium input tasks
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

This commit is contained in:
mes123456
2026-08-19 10:39:03 -04:00
parent 5a11045ca5
commit 380cbed4ff
634 changed files with 41862 additions and 212 deletions

View File

@@ -0,0 +1,98 @@
import fs from "node:fs";
import path from "node:path";
import { expect, test } from "@playwright/test";
const root = path.resolve(import.meta.dirname, "../../..");
const report = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M12-06C/desktop-main-report.json"), "utf8"));
const fixtureRoot = path.join(root, "tests/files/web/m12_glb_main_v1");
function stableSnapshot(snapshot: Record<string, any>) {
return {
objects: snapshot.nodes.filter((node: any) => node.id?.startsWith("object:")).map((node: any) => node.id).sort(),
meshes: snapshot.meshes.map((mesh: any) => mesh.id).sort(),
materials: snapshot.materials.map((material: any) => material.id).sort(),
images: snapshot.images.map((image: any) => image.id).sort(),
armatures: (snapshot.armatures ?? []).map((armature: any) => armature.id).sort(),
actions: snapshot.animations.map((animation: any) => animation.id).sort(),
};
}
async function sha256(bytes: ArrayBuffer): Promise<string> {
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
return Array.from(digest, (value) => value.toString(16).padStart(2, "0")).join("");
}
test("persists desktop-imported GLB Main data through WebEngine save and reopen", async ({ page }) => {
await page.goto("/");
const fixtures = report.fixtures.map((fixture: { id: string; blend: { file: string; sha256: string }; graph: { stableIds: Record<string, string[]> } }) => ({
id: fixture.id,
bytes: Array.from(fs.readFileSync(path.join(fixtureRoot, fixture.blend.file))),
sourceSha256: fixture.blend.sha256,
expected: fixture.graph.stableIds,
}));
const result = await page.evaluate(async (input) => {
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
const output = [];
const stableSnapshot = (snapshot: Record<string, any>) => ({
objects: snapshot.nodes.filter((node: any) => node.id?.startsWith("object:")).map((node: any) => node.id).sort(),
meshes: snapshot.meshes.map((mesh: any) => mesh.id).sort(),
materials: snapshot.materials.map((material: any) => material.id).sort(),
images: snapshot.images.map((image: any) => image.id).sort(),
armatures: (snapshot.armatures ?? []).map((armature: any) => armature.id).sort(),
actions: snapshot.animations.map((animation: any) => animation.id).sort(),
});
const digestSha256 = async (bytes: ArrayBuffer): Promise<string> => {
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
return Array.from(digest, (value) => value.toString(16).padStart(2, "0")).join("");
};
for (const fixture of input) {
const client = new WebEngineClient({ timeoutMs: 30_000 });
try {
const opened = await client.openBlend(Uint8Array.from(fixture.bytes).buffer);
const before = opened.snapshot;
const beforeIds = stableSnapshot(before);
const objectId = before.activeObjectId ?? before.nodes.find((node: any) => node.id?.startsWith("object:"))?.id;
if (!objectId) throw new Error(`${fixture.id} did not produce an active Main object`);
const edited = await client.applyCommand({ type: "setObjectVisibility", objectId, visible: false });
const saved = await client.saveBlend();
const savedSha256 = await digestSha256(saved);
if (savedSha256 === fixture.sourceSha256) throw new Error(`${fixture.id} save did not serialize the Main visibility edit`);
const reopened = await client.openBlend(saved);
const after = reopened.snapshot;
const afterIds = stableSnapshot(after);
const afterObject = after.nodes.find((node: any) => node.id === objectId);
const resources = await client.openResourceStatus();
output.push({
id: fixture.id,
before: beforeIds,
after: afterIds,
expected: fixture.expected,
revision: [before.revision, edited.snapshot.revision, after.revision],
savedSha256,
sourceSha256: fixture.sourceSha256,
visibilityAfterReopen: afterObject?.visible,
resources,
});
}
finally {
client.terminate();
}
}
return output;
}, fixtures);
for (const item of result) {
expect(item.before).toEqual(item.expected);
expect(item.after).toEqual(item.before);
expect(item.revision[1]).toBeGreaterThan(item.revision[0]);
expect(item.revision[2]).toBeGreaterThan(0);
expect(item.savedSha256).not.toEqual(item.sourceSha256);
expect(item.visibilityAfterReopen).toBe(false);
expect(item.resources).toMatchObject({ activeRequests: 0, liveInputBytes: 0, liveStagingFiles: 0 });
expect(item.before.objects.every((id: string) => id.startsWith("object:"))).toBe(true);
expect(item.before.meshes.every((id: string) => id.startsWith("mesh:"))).toBe(true);
expect(item.before.materials.every((id: string) => id.startsWith("material:"))).toBe(true);
expect(item.before.images.every((id: string) => id.startsWith("image:"))).toBe(true);
}
expect(result).toHaveLength(5);
});