Files
menchuang/frontend/scripts/patrol-render-all.mjs

72 lines
3.4 KiB
JavaScript

// 全量样板渲染巡检: 逐套在效果图渲染页切换样板, 收集 JS 异常 + 画布活性(非空白像素占比)
// 用法: node scripts/patrol-render-all.mjs <token> [--shot=12]
import puppeteer from 'puppeteer-core'
import fs from 'fs'
const token = process.argv[2]
const shotN = Number((process.argv.find(a => a.startsWith('--shot=')) ?? '--shot=12').split('=')[1]) || 12
const browser = await puppeteer.launch({
executablePath: 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
headless: 'new', args: ['--window-size=1440,900', '--enable-unsafe-swiftshader'],
defaultViewport: { width: 1440, height: 900 },
})
const page = await browser.newPage()
const errs = []
page.on('pageerror', e => errs.push(String(e).slice(0, 200)))
page.on('console', m => { if (m.type() === 'error' && !m.text().includes('favicon')) errs.push('[console] ' + m.text().slice(0, 150)) })
await page.goto(`http://localhost:8000/rendering?token=${encodeURIComponent(token)}`, { waitUntil: 'networkidle2' })
await new Promise(r => setTimeout(r, 1500))
// 取全部样板名
const names = await page.evaluate(async () => {
const token = localStorage.getItem('hmc_token')
const res = await fetch('/api/windoor/templates?page=1&page_size=254', { headers: { Authorization: `Bearer ${token}` } })
const d = (await res.json()).data
const rows = Array.isArray(d) ? d : d.data
return rows.map(r => r.name)
})
console.log('样板总数:', names.length)
const results = []
for (let i = 0; i < names.length; i++) {
const name = names[i]
errs.length = 0
try {
// 打开下拉 → 输入过滤 → 选中
await page.evaluate(() => document.querySelector('.el-select')?.querySelector('.el-select__wrapper, .el-input__inner')?.dispatchEvent(new MouseEvent('click', { bubbles: true })))
await new Promise(r => setTimeout(r, 350))
await page.keyboard.type(name, { delay: 5 })
await new Promise(r => setTimeout(r, 550))
const picked = await page.evaluate(nm => {
const items = [...document.querySelectorAll('.el-select-dropdown__item')]
const el = items.find(x => x.textContent.trim() === nm)
if (!el) return false
el.click()
return true
}, name)
if (!picked) { results.push([name, 'SKIP', '下拉未命中']); await page.keyboard.press('Escape'); continue }
await new Promise(r => setTimeout(r, 1000))
// 画布活性: 非透明像素占比
const alive = await page.evaluate(() => {
const cv = document.querySelector('.render-canvas')
if (!cv || !cv.width) return -1
const ctx = cv.getContext('2d')
const d = ctx.getImageData(0, 0, cv.width, cv.height).data
let n = 0
for (let p = 3; p < d.length; p += 40) if (d[p] > 0) n++
return n / (d.length / 40)
})
results.push([name, errs.length ? 'ERR' : (alive > 0.02 ? 'OK' : 'BLANK'),
errs.length ? errs[0] : `alive=${alive.toFixed(3)}`])
} catch (e) {
results.push([name, 'ERR', String(e).slice(0, 120)])
}
if ((i + 1) % 40 === 0) console.log(`进度 ${i + 1}/${names.length}`)
}
const bad = results.filter(r => r[1] !== 'OK' && r[1] !== 'SKIP')
console.log(`\n结果: OK=${results.filter(r => r[1] === 'OK').length} BLANK=${results.filter(r => r[1] === 'BLANK').length} ERR=${results.filter(r => r[1] === 'ERR').length} SKIP=${results.filter(r => r[1] === 'SKIP').length}`)
for (const r of bad) console.log(' ' + r.join(' | '))
fs.writeFileSync('scripts/.render-patrol.json', JSON.stringify(results))
await browser.close()