126 lines
4.8 KiB
JavaScript
126 lines
4.8 KiB
JavaScript
import { chromium } from 'playwright-core'
|
|
|
|
const BASE = process.env.FMS_BASE || 'http://localhost:5082'
|
|
const SCROLL_MS = Number(process.env.SCROLL_MS || 3000)
|
|
const ROUNDS = Number(process.env.ROUNDS || 2)
|
|
const WHEEL_INTERVAL = Number(process.env.WHEEL_INTERVAL || 10) // ms 每次滚轮间隔(模拟快速滚轮)
|
|
const chromePath =
|
|
process.env.CHROME_PATH ||
|
|
'C:/Program Files/Google/Chrome/Application/chrome.exe'
|
|
|
|
const seedStorage = `
|
|
sessionStorage.setItem('fms-login', JSON.stringify({
|
|
loginInfo: { token: 'probe-token', user: { id: 1, account: 'admin', name: '探针用户' } },
|
|
}));
|
|
localStorage.setItem('fms-user', JSON.stringify({ userInfo: { orgId: 'PROBE', account: 'admin' } }));
|
|
localStorage.setItem('app', JSON.stringify({ darkMode: false }));
|
|
`
|
|
|
|
const browser = await chromium.launch({
|
|
executablePath: chromePath,
|
|
headless: true,
|
|
args: [
|
|
'--no-sandbox',
|
|
'--disable-dev-shm-usage',
|
|
'--disable-gpu-sandbox',
|
|
'--disable-background-timer-throttling',
|
|
],
|
|
})
|
|
const context = await browser.newContext({ viewport: { width: 1280, height: 800 } })
|
|
await context.addInitScript(seedStorage)
|
|
const page = await context.newPage()
|
|
page.on('pageerror', (err) => console.log('[pageerror]', String(err).slice(0, 300)))
|
|
|
|
console.log(`打开 ${BASE}/dashboard ...`)
|
|
await page.goto(`${BASE}/dashboard`, { waitUntil: 'domcontentloaded', timeout: 30000 })
|
|
await page.waitForSelector('.fms-table', { timeout: 20000 })
|
|
await page.waitForFunction(() => document.querySelectorAll('.stk-tbody-main tr').length > 0)
|
|
await page.waitForTimeout(1200)
|
|
|
|
async function measure(durationMs) {
|
|
// 把鼠标移到表格上,确保滚轮事件落在表格滚动容器上
|
|
const rect = await page.evaluate(() => {
|
|
const root = document.querySelector('.fms-table')
|
|
const r = root?.getBoundingClientRect()
|
|
return r ? { x: r.left + r.width / 2, y: r.top + Math.min(200, r.height / 2) } : null
|
|
})
|
|
if (rect) await page.mouse.move(rect.x, rect.y)
|
|
|
|
// 用真实滚轮事件驱动(transform 模式下 DOM scrollTop 恒为 0,程序化 scrollTop 无效)
|
|
const wheelDriver = (async () => {
|
|
const end = Date.now() + durationMs
|
|
while (Date.now() < end) {
|
|
await page.mouse.wheel(0, 100)
|
|
await page.waitForTimeout(WHEEL_INTERVAL)
|
|
}
|
|
})()
|
|
|
|
const result = await page.evaluate(
|
|
async ({ durationMs }) => {
|
|
let frames = 0
|
|
let minGap = Infinity
|
|
let maxGap = 0
|
|
let prev = performance.now()
|
|
const start = performance.now()
|
|
const root0 = document.querySelector('.fms-table')
|
|
const sc0 = root0
|
|
? [...root0.querySelectorAll('*')].filter((el) => el.scrollHeight > el.clientHeight + 2)[0]
|
|
: null
|
|
const scrollTop0 = sc0?.scrollTop ?? 0
|
|
const vs0 = window.__vs0
|
|
await new Promise((resolve) => {
|
|
const tick = (now) => {
|
|
const gap = now - prev
|
|
prev = now
|
|
frames++
|
|
if (gap < minGap) minGap = gap
|
|
if (gap > maxGap) maxGap = gap
|
|
if (now - start < durationMs) requestAnimationFrame(tick)
|
|
else resolve()
|
|
}
|
|
requestAnimationFrame(tick)
|
|
})
|
|
const elapsed = (performance.now() - start) / 1000
|
|
const root = document.querySelector('.fms-table')
|
|
const scroller = root
|
|
? [...root.querySelectorAll('*')].filter(
|
|
(el) => el.scrollHeight > el.clientHeight + 2,
|
|
)[0]
|
|
: null
|
|
return {
|
|
fps: Math.round((frames / elapsed) * 10) / 10,
|
|
avgFrameMs: Math.round(((elapsed * 1000) / frames) * 10) / 10,
|
|
minGapMs: Math.round(minGap),
|
|
maxGapMs: Math.round(maxGap),
|
|
renderedRows: root?.querySelectorAll('.stk-tbody-main tr').length ?? 0,
|
|
customThumb: root?.querySelectorAll('.stk-sb-thumb.vertical').length ?? 0,
|
|
scrollerOverflow: scroller ? getComputedStyle(scroller).overflowY : null,
|
|
scrolledBy: (scroller?.scrollTop ?? 0) - scrollTop0,
|
|
}
|
|
},
|
|
{ durationMs },
|
|
)
|
|
await wheelDriver
|
|
return result
|
|
}
|
|
|
|
for (let round = 1; round <= ROUNDS; round++) {
|
|
console.log(`\n===== 第 ${round}/${ROUNDS} 轮 =====`)
|
|
const idle = await measure(1200)
|
|
console.log(`[静止] FPS=${idle.fps} avgFrame=${idle.avgFrameMs}ms rows=${idle.renderedRows} thumb=${idle.customThumb}`)
|
|
const scroll = await measure(SCROLL_MS)
|
|
console.log(
|
|
`[滚轮滚动] FPS=${scroll.fps} avgFrame=${scroll.avgFrameMs}ms minGap=${scroll.minGapMs} maxGap=${scroll.maxGapMs} ` +
|
|
`rows=${scroll.renderedRows} thumb=${scroll.customThumb} overflow=${scroll.scrollerOverflow}`,
|
|
)
|
|
await page.evaluate(() => {
|
|
const root = document.querySelector('.fms-table')
|
|
const els = root ? [...root.querySelectorAll('*')] : []
|
|
const sc = els.filter((el) => el.scrollHeight > el.clientHeight + 2)[0]
|
|
if (sc) sc.scrollTop = 0
|
|
})
|
|
await page.waitForTimeout(400)
|
|
}
|
|
|
|
await browser.close()
|