#!/usr/bin/env node // 端到端检查真实 App(index.html → 打开文件 → 渲染 → 视图面板),输出状态文本与截图。 // 用法:node scripts/probe-app.mjs [文件 ...] import fs from 'node:fs' import path from 'node:path' import { spawn } from 'node:child_process' import { chromium } from 'playwright-core' const root = process.cwd() const ven = path.resolve(root, '..', 'ven') const outDir = path.join(root, 'tools', 'out') const port = Number(process.env.PROBE_PORT || 5199) fs.mkdirSync(outDir, { recursive: true }) const SUPPORTED = /\.(dwg|dxf|pdf|epub|xps|oxps|cbz|png|jpe?g|gif|bmp)$/i let files = process.argv.slice(2).filter((a) => SUPPORTED.test(a)) if (!files.length) { const dir = path.join(root, 'testfile') files = fs.readdirSync(dir).filter((n) => SUPPORTED.test(n)).map((n) => path.join('testfile', n)) } const chrome = [process.env.CHROME_PATH, path.join(ven, 'chrome-for-testing', 'chrome-linux64', 'chrome')].find( (p) => p && fs.existsSync(p) ) const vite = spawn( process.execPath, [path.join(root, 'node_modules', 'vite', 'bin', 'vite.js'), '--port', String(port), '--strictPort', '--host', '127.0.0.1'], { cwd: root, stdio: 'ignore' } ) await new Promise((r) => setTimeout(r, 3000)) const browser = await chromium.launch({ executablePath: chrome, args: [ '--no-sandbox', '--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader', '--disable-dev-shm-usage', '--js-flags=--max-old-space-size=4096' ] }) const report = [] try { for (const file of files) { const abs = path.resolve(root, file) const label = path.basename(abs) const page = await browser.newPage({ viewport: { width: 1100, height: 860 }, deviceScaleFactor: 1 }) const errors = [] page.on('pageerror', (e) => errors.push(String(e.message).slice(0, 200))) await page.goto(`http://127.0.0.1:${port}/index.html`, { waitUntil: 'load' }) if (process.env.PROBE_NO_ANIM === '1') { await page.addStyleTag({ content: '*{animation:none!important;transition:none!important;backdrop-filter:none!important}' }) } const t0 = Date.now() await page.setInputFiles('input[type=file]', abs) let status = '' try { await page.waitForFunction( () => { const el = document.querySelector('.viewer-status') const text = el?.textContent || '' return text.includes('实体') }, null, { timeout: 180000 } ) status = (await page.textContent('.viewer-status')) || '' } catch { status = `(超时) ${(await page.textContent('.viewer-status').catch(() => '')) || ''}` } const elapsed = Date.now() - t0 await page.waitForTimeout(800) const progress = await page.evaluate(() => window.__progress || null) if (progress) console.log(' worker 进度:', progress.map((p) => `${p.ms}ms/${p.phase}`).join(' ')) const timings = await page.evaluate(() => window.__loadTimings || null) if (timings) console.log(' 阶段耗时:', JSON.stringify(timings)) const shot = path.join(outDir, `app-${label.replace(/[^\w.\u4e00-\u9fa5-]/g, '_')}.png`) await page.screenshot({ path: shot }) // 打开“视图与图层”面板,检查视图列表与诊断信息 let panel = '' try { await page.click('[data-action="layers"]', { timeout: 5000 }) await page.waitForTimeout(500) const sheetShot = shot.replace(/\.png$/, '-panel.png') await page.screenshot({ path: sheetShot }) panel = (await page.textContent('.sheet').catch(() => '')) || '' } catch (error) { panel = `面板打开失败: ${error.message.slice(0, 80)}` } // 点第一个“游离块”视图,确认它确实是一张能看的图纸 let viewShot = '' try { const rows = await page.$$('.view-row') if (rows.length > 1) { await rows[1].click() await page.waitForTimeout(1200) viewShot = shot.replace(/\.png$/, '-view1.png') await page.screenshot({ path: viewShot }) } } catch {} console.log(`\n=== ${label} ===`) console.log(` 打开+渲染墙钟 ${elapsed}ms`) console.log(` 状态栏: ${status.trim()}`) const views = panel.match(/[MLB][^\n]{0,60}实体/g)?.slice(0, 6) ?? [] if (views.length) console.log(` 视图列表: ${views.map((v) => v.replace(/\s+/g, ' ')).join(' | ')}`) const diag = panel.match(/渲染提示[^\n]{0,160}/)?.[0] if (diag) console.log(` 诊断: ${diag.replace(/\s+/g, ' ')}`) if (errors.length) console.log(` 页面错误: ${errors.slice(0, 2).join(' | ')}`) report.push({ file: label, elapsed, status: status.trim(), views, diag, errors, screenshot: path.relative(root, shot), viewScreenshot: viewShot ? path.relative(root, viewShot) : undefined }) await page.close() } } finally { await browser.close() vite.kill('SIGTERM') } fs.writeFileSync(path.join(outDir, 'app-report.json'), JSON.stringify(report, null, 2)) setTimeout(() => process.exit(0), 200)