136 lines
5.0 KiB
JavaScript
136 lines
5.0 KiB
JavaScript
|
|
#!/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)
|