import { expect, test } from "@playwright/test"; import path from "node:path"; const basicBlend = path.resolve(import.meta.dirname, "../../../tests/files/web/basic_scene.blend"); test("boots and edits with the single-thread engine without cross-origin isolation", async ({ page }) => { const engineAssetUrls: string[] = []; page.on("request", (request) => { const url = new URL(request.url()); if (/web_engine|pthread/i.test(url.pathname)) engineAssetUrls.push(url.pathname); }); const documentResponse = await page.goto("/"); expect(documentResponse).not.toBeNull(); const headers = await documentResponse!.allHeaders(); expect(headers["cross-origin-opener-policy"]).toBeUndefined(); expect(headers["cross-origin-embedder-policy"]).toBeUndefined(); expect(headers["cross-origin-resource-policy"]).toBeUndefined(); const platform = await page.evaluate(async () => { const { detectBrowserCapabilities, gateWasmThreadingCapability, selectWebEngineVariant, } = await import("/src/platform/capabilities.ts"); const capabilities = detectBrowserCapabilities(); const pthreadGate = gateWasmThreadingCapability(capabilities); const manifest = await fetch("/engine-manifest.json", { cache: "no-store" }).then((response) => response.json()) as { schemaVersion: number; memory?: { shared: boolean }; variants?: Array<{ id: "single" | "pthread"; memory: { shared: boolean }; resources: { js: { url: string }; wasm: { url: string } }; }>; }; const singleMemory = manifest.schemaVersion === 2 ? manifest.variants?.find((variant) => variant.id === "single")?.memory : manifest.memory; const selection = selectWebEngineVariant( manifest as Parameters[0], "AUTO", capabilities, ); if (!selection.selectedVariant) throw new Error("AUTO did not select a single-thread variant"); const selected = selection.selectedVariant; const wasmBinary = await fetch(selected.resources.wasm.url, { cache: "no-store" }) .then((response) => { if (!response.ok) throw new Error(`single WASM request failed: ${response.status}`); return response.arrayBuffer(); }); const imported = await import(/* @vite-ignore */ selected.resources.js.url) as { default: (options: { wasmBinary: ArrayBuffer }) => Promise<{ HEAPU8: Uint8Array; _web_engine_create(): number; _web_engine_destroy(handle: number): void; _web_engine_get_live_handles(): number; }>; }; const module = await imported.default({ wasmBinary }); const variantHandle = module._web_engine_create(); const variantSharedMemory = typeof SharedArrayBuffer !== "undefined" && module.HEAPU8.buffer instanceof SharedArrayBuffer; module._web_engine_destroy(variantHandle); return { crossOriginIsolated: capabilities.crossOriginIsolated, sharedArrayBuffer: capabilities.sharedArrayBuffer, worker: capabilities.worker, pthreadGate, autoSelectedVariant: selected.id, variantHandle, variantSharedMemory, variantLiveAfterDestroy: module._web_engine_get_live_handles(), manifestSchemaVersion: manifest.schemaVersion, manifestSharedMemory: singleMemory?.shared, }; }); expect(platform).toMatchObject({ crossOriginIsolated: false, sharedArrayBuffer: false, worker: true, autoSelectedVariant: "single", variantSharedMemory: false, variantLiveAfterDestroy: 0, manifestSchemaVersion: 2, manifestSharedMemory: false, pthreadGate: { taskId: "M6-02", capability: "WASM_PTHREAD_ENGINE", status: "BLOCKED", }, }); expect(platform.variantHandle).toBeGreaterThan(0); expect(platform.pthreadGate.issues.map((issue) => issue.path)).toEqual([ "crossOriginIsolated", "sharedArrayBuffer", ]); await expect(page.getByTestId("engine-status")).toContainText("Engine: ready", { timeout: 30_000 }); await expect(page.locator(".status-bar")).toContainText("WASM ABI: ready"); expect(engineAssetUrls.some((url) => url.endsWith("/vendor/blender/web_engine.wasm"))).toBe(true); expect(engineAssetUrls.some((url) => url.endsWith("/vendor/blender/single/web_engine.wasm"))).toBe(true); expect(engineAssetUrls.some((url) => /pthread/i.test(url))).toBe(false); await page.setInputFiles("[data-testid=blend-file-input]", basicBlend); await expect(page.getByTestId("scene-stats")).toContainText("Objects 3 · Vertices 8 · Faces 6"); await page.getByRole("button", { name: "添加立方体" }).click(); await expect(page.getByTestId("scene-stats")).toContainText("Objects 4 · Vertices 16 · Faces 18"); });