Files
ArtPlayer/test/browser/playback-properties.spec.js

83 lines
4.4 KiB
JavaScript

import { expect, test } from './fixtures.js'
for (const core of ['published', 'candidate']) {
test(`${core}: real media preserves timeline, volume, rate and property descriptors`, async ({ page }) => {
await page.goto(`/test/player.html?core=${core}&chapter=published`)
await page.evaluate(() => window.createPlayer('/test/pattern.mp4?properties=media'))
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
const result = await page.evaluate(() => {
const { art } = window
const seeks = []
const muted = []
art.on('seek', (...args) => seeks.push(args))
art.on('muted', value => muted.push(value))
art.seek = '2.5 seconds'
const seekNotice = art.template.$noticeInner.textContent
art.forward = 1
art.backward = 0.5
art.volume = 0.42
const volumeNotice = art.template.$noticeInner.textContent
art.muted = 'truthy'
art.playbackRate = 1.5
const rateNotice = art.template.$noticeInner.textContent
art.playbackRate = 0
const resetNotice = art.template.$noticeInner.textContent
const descriptors = Object.fromEntries(['currentTime', 'seek', 'forward', 'backward', 'volume', 'muted', 'playbackRate', 'loaded', 'loadedTime', 'played', 'state'].map((name) => {
const descriptor = Object.getOwnPropertyDescriptor(art, name)
return [name, { getter: typeof descriptor.get === 'function', setter: typeof descriptor.set === 'function', enumerable: descriptor.enumerable, configurable: descriptor.configurable }]
}))
const unreadable = Object.fromEntries(['seek', 'forward', 'backward', 'switch', 'quality'].map(name => [name, art[name] === undefined]))
const buffered = Array.from({ length: art.video.buffered.length }, (_, index) => art.video.buffered.end(index))
const result = { seeks, muted, time: art.currentTime, volume: art.volume, mediaMuted: art.video.muted, rate: art.playbackRate, seekNotice, volumeNotice, rateNotice, resetNotice, descriptors, unreadable, played: art.played, duration: art.duration, buffered, loadedTime: art.loadedTime, loaded: art.loaded, error: art.video.error?.code || 0 }
art.destroy()
return result
})
expect(result.seeks).toEqual([[2.5, '2.5 seconds'], [3.5, 3.5], [3, 3]])
expect(result.muted).toEqual(['truthy'])
expect(result.time).toBeCloseTo(3, 2)
expect(result.volume).toBeCloseTo(0.42, 6)
expect(result.mediaMuted).toBe(true)
expect(result.rate).toBe(1)
expect(result.seekNotice).toBe('00:02 / 00:08')
expect(result.volumeNotice).toBe('Volume: 42')
expect(result.rateNotice).toBe('Rate: 1.5x')
expect(result.resetNotice).toBe('Rate: Normal')
expect(result.played).toBeCloseTo(3 / result.duration, 6)
// WebKit may empty its buffered ranges during seek; compare the actual media surface.
expect(result.loadedTime).toBe(result.buffered.at(-1) || 0)
expect(result.loaded).toBeCloseTo(result.loadedTime / result.duration, 6)
expect(result.error).toBe(0)
expect(Object.values(result.unreadable)).toEqual([true, true, true, true, true])
for (const [name, descriptor] of Object.entries(result.descriptors)) {
expect(descriptor).toEqual({
getter: !['seek', 'forward', 'backward'].includes(name),
setter: !['loaded', 'loadedTime', 'played'].includes(name),
enumerable: false,
configurable: false,
})
}
})
test(`${core}: invalid time inputs and rate no-op retain native behavior`, async ({ page }) => {
await page.goto(`/test/player.html?core=${core}&chapter=published`)
await page.evaluate(() => window.createPlayer('/test/pattern.mp4?properties=invalid'))
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
const result = await page.evaluate(() => {
const { art } = window
art.currentTime = 2
const times = []
for (const input of [Number.NaN, undefined, null, '', 'invalid']) {
art.currentTime = input
times.push(art.currentTime)
}
art.notice.show = 'keep'
art.playbackRate = 1
art.playbackRate = Number.NaN
const result = { times, rate: art.playbackRate, notice: art.template.$noticeInner.textContent, noRuntimeType: window.Artplayer.PlaybackControls === undefined }
art.destroy()
return result
})
expect(result).toEqual({ times: [2, 2, 2, 2, 2], rate: 1, notice: 'keep', noRuntimeType: true })
})
}