import { createRequire } from "node:module"; import fs from "node:fs"; import path from "node:path"; const { chromium } = createRequire(import.meta.url)(path.resolve("web/node_modules/playwright")); const browser = await chromium.launch({ headless: true, executablePath: "/usr/bin/google-chrome-stable", args: ["--no-sandbox","--use-gl=swiftshader","--enable-unsafe-swiftshader","--enable-unsafe-webgpu","--enable-dawn-features=allow_unsafe_apis","--use-webgpu-adapter=swiftshader"] }); const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, acceptDownloads: true }); const page = await ctx.newPage(); await page.goto("http://127.0.0.1:5401/", { waitUntil: "domcontentloaded" }); await page.waitForFunction(() => document.body.innerText.includes("WASM ABI: ready"), undefined, { timeout: 120000 }); await page.getByTestId("blend-file-input").setInputFiles(path.resolve("tests/files/web/basic_scene.blend")); await page.waitForFunction(() => /Active Basic/.test(document.querySelector('[data-testid="scene-stats"]')?.textContent ?? ""), undefined, { timeout: 120000 }); await page.waitForTimeout(2000); const snap = () => page.evaluate(() => { const m = document.querySelector("main"); return { ws: m?.getAttribute("data-workspace"), rev: m?.getAttribute("data-current-main-revision"), dirty: m?.getAttribute("data-dirty"), area: m?.getAttribute("data-editor-workflow-area"), stats: document.querySelector('[data-testid="scene-stats"]')?.textContent, engine: document.querySelector('[data-testid="engine-status"]')?.textContent, saveStatus: m?.getAttribute("data-user-action-save-status"), openStatus: m?.getAttribute("data-user-action-open-status"), }; }); console.log("A-label aria:", JSON.stringify(await page.evaluate(() => [...document.querySelectorAll("[aria-label]")].map(n => n.getAttribute("aria-label"))))); console.log("A-workspaces:", JSON.stringify(await page.evaluate(() => [...document.querySelectorAll('nav[aria-label="工作区列表"] button')].map(b => (b.textContent||"").trim())))); await page.evaluate(() => document.querySelector('[aria-label="添加立方体"]')?.click()); await page.waitForTimeout(4000); console.log("B-after-add:", JSON.stringify(await snap())); await page.evaluate(() => document.querySelector('[aria-label="撤销"]')?.click()); await page.waitForTimeout(4000); console.log("B-after-undo:", JSON.stringify(await snap())); await page.evaluate(() => document.querySelector('[aria-label="重做"]')?.click()); await page.waitForTimeout(4000); console.log("B-after-redo:", JSON.stringify(await snap())); const dlP = page.waitForEvent("download", { timeout: 40000 }); await page.evaluate(() => document.querySelector('[aria-label="保存项目"]')?.click()); const dl = await dlP; const p = await dl.path(); const bytes = p ? fs.readFileSync(p) : Buffer.alloc(0); console.log("C-download:", dl.suggestedFilename(), bytes.length, "path=", Boolean(p)); console.log("C-after-save:", JSON.stringify(await snap())); await page.getByTestId("blend-file-input").setInputFiles(p); for (let i = 0; i < 12; i++) { await page.waitForTimeout(3000); console.log("C-reopen-"+i+":", JSON.stringify(await snap())); } await browser.close();