Files
ArtPlayer/test/browser/runtime-types.spec.js
T

170 lines
7.0 KiB
JavaScript

import { expect, test } from './fixtures.js'
for (const core of ['published', 'candidate']) {
test(`${core}: constructor callback hosts expose only initialized components`, async ({ page }) => {
await page.goto(`/test/player.html?core=${core}&chapter=published`)
const result = await page.evaluate(async () => {
const observations = []
const receivers = []
const names = ['template', 'events', 'storage', 'icons', 'i18n', 'notice', 'player', 'layers', 'controls', 'contextmenu', 'subtitle', 'info', 'loading', 'hotkey', 'mask', 'setting', 'plugins']
const record = (phase, receiver, instance) => {
receivers.push(receiver)
observations.push({ phase, receiver: receiver === instance, fields: names.filter(name => Object.hasOwn(instance, name)) })
}
let mounted
const settingMounted = new Promise(resolve => mounted = resolve)
let ready
const loaded = new Promise(resolve => ready = resolve)
const art = new window.Artplayer({
container: '.player',
url: '/test/pattern.mp4',
type: 'fixture',
muted: true,
setting: true,
proxy(instance) {
record('proxy', this, instance)
return document.createElement('video')
},
layers: [{ html: 'Layer', mounted() { record('layer', this, this) } }],
controls: [{ html: 'Control', position: 'left', mounted() { record('control', this, this) } }],
contextmenu: [{ html: 'Menu', mounted() { record('contextmenu', this, this) } }],
settings: [{ html: 'Setting', mounted() {
record('setting', this, this)
mounted()
} }],
plugins: [function (instance) {
record('plugin', this, instance)
return { name: 'phases' }
}],
customType: { fixture(video, url, instance) {
record('customType', this, instance)
video.src = url
} },
}, function (instance) {
record('ready', this, instance)
ready()
})
try {
await Promise.all([settingMounted, loaded])
return { names, observations, complete: window.Artplayer.instances.includes(art), receivers: receivers.every(receiver => receiver === art) }
}
finally {
art.destroy()
}
})
expect(result.complete).toBe(true)
expect(result.receivers).toBe(true)
const expected = { proxy: 0, layer: 7, control: 8, contextmenu: 9, plugin: 16, setting: 17, customType: 17, ready: 17 }
expect(result.observations.map(item => item.phase).sort()).toEqual(Object.keys(expected).sort())
for (const item of result.observations) {
expect(item.receiver).toBe(true)
expect(item.fields, item.phase).toEqual(result.names.slice(0, expected[item.phase]))
}
})
test(`${core}: container validation rejects non-div elements`, async ({ page }) => {
await page.goto(`/test/player.html?core=${core}&chapter=published`)
const result = await page.evaluate(() => {
const container = document.createElement('section')
document.body.append(container)
try {
const player = new window.Artplayer({ container, url: '' })
player.destroy()
return 'accepted'
}
catch (error) {
return error.message
}
finally {
container.remove()
}
})
expect(result).toBe('Unsupported container element type, only support \'div\' but got \'section\'')
})
}
test('precise declarations: actual return values and callback receivers agree with the installed view', async ({ page }) => {
await page.goto('/test/player.html?core=candidate&chapter=published')
const result = await page.evaluate(async () => {
const Player = window.Artplayer
const container = document.querySelector('.player')
const callbacks = []
let ready
const loaded = new Promise(resolve => ready = resolve)
const factory = function (instance) {
callbacks.push({ same: this === instance, registryAbsent: this.plugins === undefined })
return { name: 'precise', update(value) {
return value
} }
}
const art = new Player({
container,
url: '/test/pattern.mp4',
muted: true,
plugins: [factory, window.artplayerPluginChapter({ chapters: [{ start: 0, end: Infinity, title: 'Existing plugin' }] })],
}, function (instance) {
ready({ same: this === instance, inInstances: Player.instances.includes(instance) })
})
try {
const readyResult = await loaded
const sync = art.plugins.add(function (instance) {
callbacks.push({ same: this === instance, registry: this.plugins.art === instance })
return { name: 'sync' }
})
const pending = art.plugins.add(async function (instance) {
callbacks.push({ same: this === instance, registry: this.plugins.art === instance })
return { name: 'async' }
})
const asyncResult = await pending
const item = { name: 'typed-setting', html: 'Setting' }
const added = art.setting.add(item)
const updated = art.setting.update({ name: item.name, html: 'Updated' })
const control = art.controls.add({ name: 'typed-control', html: 'Control', position: 'left' })
const style = art.subtitle.style({ color: 'white' })
art.notice.show = 'Visible'
const notice = art.notice.show
art.notice.show = false
const hidden = art.notice.show
const pause = art.pause()
const removed = art.setting.remove(item.name)
return {
ready: readyResult,
callbacks,
constructor: art.constructor === Player,
optionFactory: art.option.plugins[0] === factory,
registry: sync === art.plugins && asyncResult === art.plugins,
promiseBranch: pending instanceof Promise && !(sync instanceof Promise),
commandReads: ['seek', 'forward', 'backward', 'switch', 'quality'].every(name => art[name] === undefined),
setting: added === item && updated === item && removed === undefined && art.setting.find(item.name) === null,
control: control === undefined,
subtitle: style === art.template.$subtitle && Array.isArray(art.subtitle.cues) && Array.isArray(art.subtitle.activeCues),
notice: notice === true && hidden === false,
template: art.template.$container === container && !('html' in art.template) && typeof Player.html === 'string',
pause: pause === undefined,
absentStatics: !('env' in Player) && !('build' in Player),
chapter: typeof art.plugins.artplayerPluginChapter?.update === 'function',
}
}
finally {
art.destroy()
}
})
expect(result).toEqual({
ready: { same: true, inInstances: true },
callbacks: [{ same: true, registryAbsent: true }, { same: true, registry: true }, { same: true, registry: true }],
constructor: true,
optionFactory: true,
registry: true,
promiseBranch: true,
commandReads: true,
setting: true,
control: true,
subtitle: true,
notice: true,
template: true,
pause: true,
absentStatics: true,
chapter: true,
})
})