mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-09 03:46:16 -08:00
114 lines
5.7 KiB
JavaScript
114 lines
5.7 KiB
JavaScript
import { expect, test } from './fixtures.js'
|
|
|
|
for (let group = 0; group < 3; group++) {
|
|
test(`playback persistence measurements and fresh-state contracts group ${group + 1}`, async ({ page }, testInfo) => {
|
|
const runs = []
|
|
for (const core of group % 2 ? ['candidate', 'published'] : ['published', 'candidate']) {
|
|
await page.goto(`/test/player.html?core=${core}&chapter=published`)
|
|
await page.evaluate(() => {
|
|
localStorage.setItem('artplayer_settings', JSON.stringify({ times: { another: 2 }, external: { retained: true }, volume: 0.7 }))
|
|
window.art = new window.Artplayer({ container: '.player', url: '/test/pattern.mp4', muted: true, autoPlayback: true, id: 'persistence-measurement' })
|
|
document.querySelector('#play').onclick = () => window.art.play()
|
|
})
|
|
await page.locator('#play').click()
|
|
await expect.poll(() => page.evaluate(() => window.art.playing && window.art.currentTime > 0.1)).toBe(true)
|
|
try {
|
|
const result = await page.evaluate(() => {
|
|
const art = window.art
|
|
const refresh = () => art.emit('video:timeupdate', new Event('timeupdate'))
|
|
for (let index = 0; index < 100; index++)
|
|
refresh()
|
|
// Timings run without replacing native Storage/JSON methods.
|
|
const samples = []
|
|
for (let sample = 0; sample < 5; sample++) {
|
|
const started = performance.now()
|
|
for (let index = 0; index < 100; index++)
|
|
refresh()
|
|
samples.push(performance.now() - started)
|
|
}
|
|
const original = { getItem: Storage.prototype.getItem, setItem: Storage.prototype.setItem, parse: JSON.parse, stringify: JSON.stringify }
|
|
const counts = { getItem: 0, setItem: 0, parse: 0, stringify: 0 }
|
|
Storage.prototype.getItem = function (...args) {
|
|
if (args[0] === art.storage.name)
|
|
counts.getItem++
|
|
return original.getItem.apply(this, args)
|
|
}
|
|
Storage.prototype.setItem = function (...args) {
|
|
if (args[0] === art.storage.name)
|
|
counts.setItem++
|
|
return original.setItem.apply(this, args)
|
|
}
|
|
JSON.parse = (...args) => {
|
|
counts.parse++
|
|
return original.parse(...args)
|
|
}
|
|
JSON.stringify = (...args) => {
|
|
counts.stringify++
|
|
return original.stringify(...args)
|
|
}
|
|
try {
|
|
for (let index = 0; index < 100; index++)
|
|
refresh()
|
|
}
|
|
finally {
|
|
Storage.prototype.getItem = original.getItem
|
|
Storage.prototype.setItem = original.setItem
|
|
JSON.parse = original.parse
|
|
JSON.stringify = original.stringify
|
|
}
|
|
// WebKit's native clock can advance between consecutive property reads.
|
|
const beforeRefresh = art.currentTime
|
|
refresh()
|
|
const afterRefresh = art.currentTime
|
|
const stored = art.storage.get()
|
|
const time = { beforeRefresh, afterRefresh }
|
|
const isolated = art.storage.get()
|
|
isolated.times.transient = 999
|
|
isolated.external.retained = false
|
|
const fresh = art.storage.get()
|
|
const calls = []
|
|
const get = art.storage.get
|
|
// A legal custom get can update unrelated settings between record read and save.
|
|
art.storage.get = function (key) {
|
|
calls.push(key ?? null)
|
|
const result = get.call(this, key)
|
|
if (key === 'times') {
|
|
const latest = get.call(this)
|
|
latest.external = { retained: true, revision: 'interleaved' }
|
|
localStorage.setItem(this.name, JSON.stringify(latest))
|
|
}
|
|
return result
|
|
}
|
|
try {
|
|
refresh()
|
|
}
|
|
finally {
|
|
art.storage.get = get
|
|
}
|
|
const merged = art.storage.get()
|
|
const pausedBefore = localStorage.getItem(art.storage.name)
|
|
art.pause()
|
|
refresh()
|
|
const pausedAfter = localStorage.getItem(art.storage.name)
|
|
return { samples, counts, time, stored, fresh, merged, calls, pausedBefore, pausedAfter }
|
|
})
|
|
runs.push({ core, group, result })
|
|
expect(result.stored.times['persistence-measurement']).toBeGreaterThanOrEqual(result.time.beforeRefresh)
|
|
expect(result.stored.times['persistence-measurement']).toBeLessThanOrEqual(result.time.afterRefresh)
|
|
expect(result.stored.times.another).toBe(2)
|
|
expect(result.stored.external).toEqual({ retained: true })
|
|
expect(result.fresh.times).not.toHaveProperty('transient')
|
|
expect(result.fresh.external).toEqual({ retained: true })
|
|
expect(result.merged.external).toEqual({ retained: true, revision: 'interleaved' })
|
|
expect(result.calls).toEqual(['times', null])
|
|
expect(result.pausedAfter).toBe(result.pausedBefore)
|
|
}
|
|
finally {
|
|
await page.evaluate(() => window.art.destroy(false))
|
|
await testInfo.attach(`persistence-${core}`, { contentType: 'application/json', body: JSON.stringify(runs.at(-1) || { core, group, failedBeforeMeasurements: true }) })
|
|
}
|
|
}
|
|
await testInfo.attach('persistence-paired-summary', { contentType: 'application/json', body: JSON.stringify({ runs, limitation: 'Actual video and native same-origin localStorage, followed by synthetic timeupdate dispatch while playing. 100 warmups and five 100-event timing samples without native method wrappers; a separate instrumented window reports Storage/JSON calls. A custom public get tests synchronous interleaving, not a concurrent multi-process storage race. No storage implementation or write cadence change is made.' }) })
|
|
})
|
|
}
|