mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
99 lines
5.7 KiB
JavaScript
99 lines
5.7 KiB
JavaScript
import fs from 'node:fs'
|
|
import process from 'node:process'
|
|
import { hash } from '../../refactor/scripts/releases.mjs'
|
|
import { expect, test } from './fixtures.js'
|
|
|
|
const baseline = process.env.ARTPLAYER_SLIDER_BASELINE
|
|
const before = baseline ? fs.readFileSync(baseline, 'utf8') : undefined
|
|
const attributes = ['aria-disabled', 'aria-valuemin', 'aria-valuemax', 'aria-valuenow', 'aria-valuetext']
|
|
|
|
for (let group = 0; group < 3; group++) {
|
|
test(`slider DOM updates and duplicate-event timing group ${group + 1}`, async ({ page }, testInfo) => {
|
|
const results = []
|
|
const variants = before ? (group % 2 ? ['candidate', 'before'] : ['before', 'candidate']) : ['candidate']
|
|
for (const variant of variants) {
|
|
if (variant === 'before')
|
|
await page.route('**/candidate/artplayer.js', route => route.fulfill({ contentType: 'text/javascript', body: before }))
|
|
else
|
|
await page.unroute('**/candidate/artplayer.js')
|
|
await page.goto('/test/player.html?core=candidate&chapter=published')
|
|
await page.evaluate(() => window.createPlayer('/test/pattern.mp4'))
|
|
await page.locator('#play').click()
|
|
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.2)
|
|
await page.evaluate(() => window.art.pause())
|
|
const result = await page.evaluate((attributes) => {
|
|
const art = window.art
|
|
const element = document.querySelector('.art-control-progress')
|
|
const refresh = () => art.emit('video:timeupdate', new Event('timeupdate'))
|
|
refresh()
|
|
const initial = Object.fromEntries(attributes.map(name => [name, element.getAttribute(name)]))
|
|
// Keep MutationObserver out of the timing loop: these are synthetic duplicate events.
|
|
for (let i = 0; i < 300; i++)
|
|
refresh()
|
|
const timings = []
|
|
for (let sample = 0; sample < 5; sample++) {
|
|
const started = performance.now()
|
|
for (let i = 0; i < 1000; i++)
|
|
refresh()
|
|
timings.push(performance.now() - started)
|
|
}
|
|
const observer = new MutationObserver(() => {})
|
|
observer.observe(element, { attributes: true, attributeFilter: attributes })
|
|
let events = 0
|
|
const count = () => events++
|
|
art.on('video:timeupdate', count)
|
|
for (let i = 0; i < 100; i++)
|
|
refresh()
|
|
const duplicates = observer.takeRecords().map(record => record.attributeName)
|
|
// External DOM changes must still be repaired on the next refresh.
|
|
element.setAttribute('aria-valuemax', '999')
|
|
element.removeAttribute('aria-disabled')
|
|
observer.takeRecords()
|
|
refresh()
|
|
const repaired = observer.takeRecords().map(record => record.attributeName)
|
|
const final = Object.fromEntries(attributes.map(name => [name, element.getAttribute(name)]))
|
|
observer.disconnect()
|
|
art.off('video:timeupdate', count)
|
|
window.sliderNativeUpdates = { updates: [], events: 0 }
|
|
const nativeObserver = new MutationObserver(records => window.sliderNativeUpdates.updates.push(...records.map(record => record.attributeName)))
|
|
nativeObserver.observe(element, { attributes: true, attributeFilter: attributes })
|
|
const nativeCount = () => window.sliderNativeUpdates.events++
|
|
art.on('video:timeupdate', nativeCount)
|
|
window.finishSliderNativeUpdates = () => {
|
|
window.sliderNativeUpdates.updates.push(...nativeObserver.takeRecords().map(record => record.attributeName))
|
|
nativeObserver.disconnect()
|
|
art.off('video:timeupdate', nativeCount)
|
|
return { ...window.sliderNativeUpdates, value: Number(element.getAttribute('aria-valuenow')), time: art.currentTime }
|
|
}
|
|
return { initial, final, timings, duplicates, repaired, events, time: art.currentTime, duration: art.duration }
|
|
}, attributes)
|
|
await page.locator('#play').click()
|
|
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(result.time + 0.8)
|
|
await page.evaluate(() => window.art.pause())
|
|
const native = await page.evaluate(() => window.finishSliderNativeUpdates())
|
|
const record = { group, variant, before: variant === 'before' ? { file: baseline, sha256: hash(before) } : null, result, native }
|
|
results.push(record)
|
|
try {
|
|
expect(result.events).toBe(101)
|
|
expect(result.final).toEqual(result.initial)
|
|
expect(Number(result.final['aria-valuemax'])).toBe(result.duration)
|
|
expect(result.duplicates).toHaveLength(variant === 'before' ? 500 : 0)
|
|
expect(result.repaired).toEqual(variant === 'before' ? attributes : ['aria-disabled', 'aria-valuemax'])
|
|
expect(native.events).toBeGreaterThan(0)
|
|
expect(Math.abs(native.value - native.time)).toBeLessThan(0.3)
|
|
if (variant === 'candidate') {
|
|
expect(native.updates).not.toContain('aria-disabled')
|
|
expect(native.updates).not.toContain('aria-valuemin')
|
|
expect(native.updates).not.toContain('aria-valuemax')
|
|
expect(native.updates).toContain('aria-valuenow')
|
|
}
|
|
}
|
|
finally {
|
|
await page.evaluate(() => window.art.destroy(false))
|
|
await testInfo.attach(`slider-updates-${variant}`, { contentType: 'application/json', body: JSON.stringify(record) })
|
|
}
|
|
}
|
|
await testInfo.attach('slider-paired-summary', { contentType: 'application/json', body: JSON.stringify({ results, limitation: 'Three alternating same-engine groups when a baseline artifact is supplied; 300-event warmup plus five samples of 1000 synthetic paused timeupdate events, without a MutationObserver in the timed loop. Separate DOM operation counts and actual native playback checks. Not total playback FPS, input latency or full BASE-06 initialization/resource acceptance.' }) })
|
|
})
|
|
}
|