import assert from "node:assert/strict"; import crypto from "node:crypto"; import fs from "node:fs"; import { createRequire } from "node:module"; import path from "node:path"; const origin = process.env.WEB_ORIGIN ?? "https://49.232.250.35:58014"; const { chromium } = createRequire(import.meta.url)(path.resolve("web/node_modules/playwright")); const wasmPath = path.resolve("web/app/public/vendor/blender/web_engine.wasm"); const expectedWasmSha256 = crypto.createHash("sha256").update(fs.readFileSync(wasmPath)).digest("hex"); const requiredIsolation = { "cross-origin-opener-policy": "same-origin", "cross-origin-embedder-policy": "require-corp", "cross-origin-resource-policy": "same-origin", }; const response = await fetch(`${origin}/`); assert.equal(response.status, 200); for (const [name, expected] of Object.entries(requiredIsolation)) assert.equal(response.headers.get(name), expected); assert.equal(response.headers.get("content-type"), "text/html; charset=utf-8"); assert.match(response.headers.get("content-security-policy") ?? "", /wasm-unsafe-eval/); const manifestResponse = await fetch(`${origin}/wasm-module-manifest.json`, { cache: "no-store" }); assert.equal(manifestResponse.status, 200); const moduleManifest = await manifestResponse.json(); const debug = moduleManifest.modules.find((item) => item.id === "debug-incremental"); assert.equal(debug.resources.single.sha256, expectedWasmSha256); const range = await fetch(`${origin}/vendor/blender/web_engine.wasm`, { headers: { Range: "bytes=0-15" } }); assert.equal(range.status, 206); assert.equal(range.headers.get("content-type"), "application/wasm"); assert.equal((await range.arrayBuffer()).byteLength, 16); const full = new Uint8Array(await (await fetch(`${origin}/vendor/blender/web_engine.wasm`, { cache: "no-store" })).arrayBuffer()); assert.equal(crypto.createHash("sha256").update(full).digest("hex"), expectedWasmSha256); const favicon = await fetch(`${origin}/favicon.svg`); assert.equal(favicon.status, 200); assert.equal(favicon.headers.get("content-type"), "image/svg+xml"); const browser = await chromium.launch({ headless: true, executablePath: process.env.CHROME_PATH ?? "/usr/bin/google-chrome-stable" }); const context = await browser.newContext({ viewport: { width: 1440, height: 900 }, ignoreHTTPSErrors: false }); const page = await context.newPage(); const consoleErrors = []; const requestFailures = []; const badResponses = []; page.on("console", (message) => { if (message.type() === "error") consoleErrors.push(message.text()); }); page.on("requestfailed", (request) => requestFailures.push(`${request.url()} ${request.failure()?.errorText ?? "failed"}`)); page.on("response", (item) => { if (item.status() >= 400) badResponses.push(`${item.status()} ${item.url()}`); }); await page.goto(`${origin}/`, { waitUntil: "domcontentloaded", timeout: 60_000 }); await page.waitForFunction(() => document.body.innerText.includes("WASM ABI: ready"), undefined, { timeout: 120_000 }); await page.getByTestId("blend-file-input").setInputFiles(path.resolve("tests/files/web/basic_scene.blend")); await page.waitForFunction(() => Number(document.body.innerText.match(/Objects (\d+)/)?.[1] ?? 0) > 0, undefined, { timeout: 120_000 }); const beforeObjects = Number(await page.locator("body").evaluate((node) => node.innerText.match(/Objects (\d+)/)?.[1] ?? 0)); await page.getByRole("button", { name: "添加立方体", exact: true }).click(); await page.waitForFunction((before) => Number(document.body.innerText.match(/Objects (\d+)/)?.[1] ?? 0) === before + 1, beforeObjects, { timeout: 60_000 }); const state = await page.evaluate(() => ({ secureContext: window.isSecureContext, crossOriginIsolated: window.crossOriginIsolated, body: document.body.innerText, wasm: performance.getEntriesByType("resource").filter((item) => item.name.includes(".wasm")).map((item) => item.name) })); assert.equal(state.secureContext, true); assert.equal(state.crossOriginIsolated, true); assert.equal(Number(state.body.match(/Objects (\d+)/)?.[1]), beforeObjects + 1); assert.equal(consoleErrors.length, 0); assert.equal(requestFailures.length, 0); assert.equal(badResponses.length, 0); await context.close(); await browser.close(); process.stdout.write(`deployed-web-blender-ok origin=${origin} wasm=${expectedWasmSha256} range=206 mainMutation=${beforeObjects}->${beforeObjects + 1} chromium=ready\n`);