import fs from "node:fs"; import path from "node:path"; import { test, expect } from "@playwright/test"; import { NativeUIWorkflow, observeNative } from "../fixtures/native-ui-workflow"; const root = path.resolve(import.meta.dirname, "../../.."); const fixture = path.join(root, "tests/files/web/basic_scene.blend"); test("probe menu access in both views then invoke pack", async ({ page }, testInfo) => { test.setTimeout(180_000); await observeNative(page); await page.goto("/"); const flow = new NativeUIWorkflow(page, testInfo.outputPath("cases")); await flow.command(() => page.getByTestId("blend-file-input").setInputFiles(fixture)); await expect(page.getByTestId("engine-status")).toContainText("SceneIR", { timeout: 60000 }); const log: any = {}; const probeHit = () => page.evaluate(() => { const btn = Array.from(document.querySelectorAll('nav.menu-bar button')).find(b => b.getAttribute('aria-label') === '文件') as HTMLElement | undefined; if (!btn) return { error: 'menu button missing' }; const r = btn.getBoundingClientRect(); const stack = document.elementsFromPoint(r.left + r.width / 2, r.top + r.height / 2).slice(0, 5).map(el => `${el.tagName}.${(el as HTMLElement).className}`.slice(0, 60)); return { rect: { x: r.left, y: r.top, w: r.width, h: r.height }, stack }; }); const openFileMenu = async () => { await page.getByRole('button', { name: '文件', exact: true }).click({ timeout: 8000 }); await page.waitForTimeout(300); return page.locator('[role="menu"]').evaluateAll(items => items.map(i => i.getAttribute('aria-label'))); }; try { log.hitDesktopView = await probeHit(); log.menusDesktopView = await openFileMenu().catch((e: any) => `ERR:${e.message.split('\n')[0]}`); const toolsToggle = page.getByTestId('native-web-tools-toggle'); log.hasToggle = await toolsToggle.count(); if (log.hasToggle) { await toolsToggle.click({ force: true }); await page.waitForTimeout(500); } log.hitToolsView = await probeHit(); log.menusToolsView = await openFileMenu().catch((e: any) => `ERR:${e.message.split('\n')[0]}`); await page.evaluate(() => { const w = (window as any); w.__probeSent = []; const worker = w.__nativeWorkflowEngine as Worker; const orig = worker.postMessage.bind(worker); worker.postMessage = ((...a: any[]) => { w.__probeSent.push(a[0]?.cmd ?? a[0]?.command?.type ?? '?'); return orig(...(a as [any])); }) as any; }); const external = page.getByRole('menuitem', { name: '外部数据', exact: true }); log.externalCount = await external.count(); if (log.externalCount) { await external.click({ force: true }); await page.waitForTimeout(300); const pack = page.getByRole('menuitem', { name: '打包资源', exact: true }); log.packDisabled = await pack.isDisabled(); await pack.click({ force: true }); await page.waitForTimeout(4000); log.sent = await page.evaluate(() => (window as any).__probeSent); log.status = await page.getByTestId('engine-status').innerText(); } } catch (error: any) { log.error = String(error?.message ?? error).slice(0, 400); } fs.writeFileSync("/tmp/pack-menu-probe.json", JSON.stringify(log, null, 2)); });