mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
261 lines
11 KiB
JavaScript
261 lines
11 KiB
JavaScript
import { expect, test } from './fixtures.js'
|
|
|
|
async function setup(page, core) {
|
|
await page.goto(`/test/player.html?core=${core}&chapter=published`)
|
|
await page.evaluate(() => window.createPlayer('/test/pattern.mp4'))
|
|
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
|
|
}
|
|
|
|
test('candidate: adopted player enters its current document body and restores placement and focus', async ({ page }) => {
|
|
await setup(page, 'candidate')
|
|
const result = await page.evaluate(() => {
|
|
const frame = document.createElement('iframe')
|
|
document.body.append(frame)
|
|
const target = frame.contentDocument
|
|
const wrapper = target.createElement('div')
|
|
const sibling = target.createElement('span')
|
|
const probe = target.createElement('button')
|
|
const player = window.art.template.$player
|
|
target.body.append(wrapper)
|
|
wrapper.append(player, sibling)
|
|
player.append(probe)
|
|
probe.focus()
|
|
window.Artplayer.FULLSCREEN_WEB_IN_BODY = true
|
|
window.art.fullscreenWeb = true
|
|
const entered = player.parentNode === target.body && player.ownerDocument === target
|
|
const focused = target.activeElement === probe
|
|
window.art.fullscreenWeb = false
|
|
const restored = player.parentNode === wrapper && player.nextSibling === sibling && target.activeElement === probe
|
|
window.art.destroy()
|
|
frame.remove()
|
|
return { entered, focused, restored }
|
|
})
|
|
expect(result).toEqual({ entered: true, focused: true, restored: true })
|
|
})
|
|
|
|
for (const removeHtml of [false, true]) {
|
|
for (const movedBeforeThrow of [false, true]) {
|
|
test(`candidate: failed destroy restoration cannot orphan the player removeHtml=${removeHtml} movedBeforeThrow=${movedBeforeThrow}`, async ({ page }) => {
|
|
await setup(page, 'candidate')
|
|
expect(await page.evaluate(({ removeHtml, movedBeforeThrow }) => {
|
|
const art = window.art
|
|
const { $player: player, $container: container } = art.template
|
|
art.constructor.FULLSCREEN_WEB_IN_BODY = true
|
|
art.fullscreenWeb = true
|
|
const original = container.insertBefore
|
|
const failure = new Error('destroy restore denied')
|
|
container.insertBefore = function (node, anchor) {
|
|
if (movedBeforeThrow)
|
|
original.call(this, node, anchor)
|
|
throw failure
|
|
}
|
|
let sameError = false
|
|
try {
|
|
art.destroy(removeHtml)
|
|
}
|
|
catch (error) {
|
|
sameError = error === failure
|
|
}
|
|
container.insertBefore = original
|
|
return { sameError, orphan: player.isConnected && !container.contains(player), retained: container.contains(player), destroyed: art.isDestroy, registered: art.constructor.instances.includes(art) }
|
|
}, { removeHtml, movedBeforeThrow })).toEqual({ sameError: true, orphan: false, retained: movedBeforeThrow && !removeHtml, destroyed: true, registered: false })
|
|
})
|
|
}
|
|
}
|
|
|
|
test('candidate: web fullscreen listener can exit without stale resize or overwritten restoration', async ({ page }) => {
|
|
await setup(page, 'candidate')
|
|
const result = await page.evaluate(() => {
|
|
const art = window.art
|
|
const player = art.template.$player
|
|
player.style.width = '75%'
|
|
const style = player.style.cssText
|
|
const events = []
|
|
art.on('fullscreenWeb', (value) => {
|
|
events.push(value)
|
|
if (value)
|
|
art.fullscreenWeb = false
|
|
})
|
|
art.on('resize', () => events.push('resize'))
|
|
art.fullscreenWeb = true
|
|
const result = { events, active: art.fullscreenWeb, restored: player.style.cssText === style }
|
|
art.destroy()
|
|
return result
|
|
})
|
|
expect(result).toEqual({ events: [true, false, 'resize'], active: false, restored: true })
|
|
})
|
|
|
|
test('candidate: web fullscreen listener destroy restores ownership without post-destroy resize', async ({ page }) => {
|
|
await setup(page, 'candidate')
|
|
expect(await page.evaluate(() => {
|
|
const art = window.art
|
|
const { $player: player, $container: container } = art.template
|
|
art.constructor.FULLSCREEN_WEB_IN_BODY = true
|
|
const style = player.style.cssText
|
|
const events = []
|
|
art.on('fullscreenWeb', () => art.destroy(false))
|
|
art.on('destroy', () => events.push('destroy'))
|
|
art.on('resize', () => events.push('resize'))
|
|
art.fullscreenWeb = true
|
|
art.fullscreenWeb = true
|
|
return { events, retained: container.contains(player), style: player.style.cssText === style, fullscreen: player.classList.contains('art-fullscreen-web') }
|
|
})).toEqual({ events: ['destroy'], retained: true, style: true, fullscreen: false })
|
|
})
|
|
|
|
test('candidate: a failed body move restores style and keeps its original error', async ({ page }) => {
|
|
await setup(page, 'candidate')
|
|
expect(await page.evaluate(() => {
|
|
const art = window.art
|
|
const { $player: player, $container: container } = art.template
|
|
const style = player.getAttribute('style')
|
|
art.constructor.FULLSCREEN_WEB_IN_BODY = true
|
|
const append = document.body.appendChild
|
|
const failure = new Error('controlled body insertion failure')
|
|
document.body.appendChild = function (node) {
|
|
if (node === player)
|
|
throw failure
|
|
return append.call(this, node)
|
|
}
|
|
let sameError = false
|
|
try {
|
|
art.fullscreenWeb = true
|
|
}
|
|
catch (error) {
|
|
sameError = error === failure
|
|
}
|
|
document.body.appendChild = append
|
|
const result = { sameError, retained: container.contains(player), style: player.getAttribute('style') === style, active: art.fullscreenWeb }
|
|
art.destroy()
|
|
return result
|
|
})).toEqual({ sameError: true, retained: true, style: true, active: false })
|
|
})
|
|
|
|
test('candidate: a failed web fullscreen exit can retry restoring the original placement', async ({ page }) => {
|
|
await setup(page, 'candidate')
|
|
expect(await page.evaluate(() => {
|
|
const art = window.art
|
|
const { $player: player, $container: container } = art.template
|
|
art.constructor.FULLSCREEN_WEB_IN_BODY = true
|
|
const style = player.getAttribute('style')
|
|
art.fullscreenWeb = true
|
|
const insert = container.insertBefore
|
|
const failure = new Error('controlled restore failure')
|
|
container.insertBefore = () => {
|
|
throw failure
|
|
}
|
|
let sameError = false
|
|
try {
|
|
art.fullscreenWeb = false
|
|
}
|
|
catch (error) {
|
|
sameError = error === failure
|
|
}
|
|
container.insertBefore = insert
|
|
art.fullscreenWeb = false
|
|
const result = { sameError, retained: container.contains(player), style: player.getAttribute('style') === style, active: art.fullscreenWeb }
|
|
art.destroy()
|
|
return result
|
|
})).toEqual({ sameError: true, retained: true, style: true, active: false })
|
|
})
|
|
|
|
test('candidate: reentry during web fullscreen restoration preserves the newer active mode', async ({ page }) => {
|
|
await setup(page, 'candidate')
|
|
expect(await page.evaluate(() => {
|
|
const art = window.art
|
|
const { $player: player, $container: container } = art.template
|
|
art.constructor.FULLSCREEN_WEB_IN_BODY = true
|
|
player.style.width = '65%'
|
|
const style = player.getAttribute('style')
|
|
art.fullscreenWeb = true
|
|
const insert = container.insertBefore
|
|
container.insertBefore = function (node, anchor) {
|
|
container.insertBefore = insert
|
|
const result = insert.call(this, node, anchor)
|
|
art.fullscreenWeb = true
|
|
return result
|
|
}
|
|
art.fullscreenWeb = false
|
|
const winner = art.fullscreenWeb && player.parentNode === document.body && player.style.width === '100%'
|
|
art.fullscreenWeb = false
|
|
const restored = container.contains(player) && player.getAttribute('style') === style
|
|
art.destroy()
|
|
return { winner, restored }
|
|
})).toEqual({ winner: true, restored: true })
|
|
})
|
|
|
|
for (const core of ['published', 'candidate']) {
|
|
for (const initial of ['', 'width: 70%; height: 80%; color: red;']) {
|
|
test(`${core}: web fullscreen restores initial style after repeated entry (${initial || 'empty'})`, async ({ page }, testInfo) => {
|
|
await setup(page, core)
|
|
const result = await page.evaluate((initial) => {
|
|
const art = window.art
|
|
art.constructor.FULLSCREEN_WEB_IN_BODY = false
|
|
const player = art.template.$player
|
|
player.style.cssText = initial
|
|
const before = player.style.cssText
|
|
const events = []
|
|
let restoredAtExit
|
|
art.on('fullscreenWeb', (value) => {
|
|
events.push(value)
|
|
if (!value)
|
|
restoredAtExit = player.style.cssText
|
|
})
|
|
art.fullscreenWeb = true
|
|
art.fullscreenWeb = true
|
|
art.fullscreenWeb = false
|
|
const result = { restored: restoredAtExit === before, events, active: art.fullscreenWeb, dimensions: [player.style.width, player.style.height, player.style.color], styles: { before, restoredAtExit, after: player.style.cssText } }
|
|
art.destroy()
|
|
return result
|
|
}, initial)
|
|
const { styles, ...observed } = result
|
|
await testInfo.attach('fullscreen-styles', { contentType: 'application/json', body: JSON.stringify(styles) })
|
|
expect(observed).toEqual({ restored: core === 'candidate', events: [true, true, false], active: false, dimensions: core === 'candidate' ? (initial ? ['70%', '80%', 'red'] : ['', '', '']) : ['100%', '100%', initial ? 'red' : ''] })
|
|
})
|
|
}
|
|
|
|
for (const changeFlag of [false, true]) {
|
|
test(`${core}: body fullscreen restores original parent and sibling when flag changes=${changeFlag}`, async ({ page }) => {
|
|
await setup(page, core)
|
|
const result = await page.evaluate((changeFlag) => {
|
|
const art = window.art
|
|
const player = art.template.$player
|
|
const wrapper = document.createElement('div')
|
|
const sibling = document.createElement('span')
|
|
art.template.$container.append(wrapper)
|
|
wrapper.append(player, sibling)
|
|
art.constructor.FULLSCREEN_WEB_IN_BODY = true
|
|
art.fullscreenWeb = true
|
|
const entered = player.parentNode === document.body
|
|
if (changeFlag)
|
|
art.constructor.FULLSCREEN_WEB_IN_BODY = false
|
|
art.fullscreenWeb = false
|
|
const result = { entered, parent: player.parentNode === wrapper, sibling: player.nextSibling === sibling, inBody: player.parentNode === document.body }
|
|
art.destroy()
|
|
player.remove()
|
|
return result
|
|
}, changeFlag)
|
|
expect(result).toEqual({ entered: true, parent: core === 'candidate', sibling: core === 'candidate', inBody: core === 'published' && changeFlag })
|
|
})
|
|
}
|
|
|
|
for (const removeHtml of [false, true]) {
|
|
test(`${core}: destroy while web fullscreen removeHtml=${removeHtml} restores DOM ownership`, async ({ page }) => {
|
|
await setup(page, core)
|
|
const result = await page.evaluate((removeHtml) => {
|
|
const art = window.art
|
|
const { $player: player, $container: container } = art.template
|
|
const style = player.style.cssText
|
|
art.constructor.FULLSCREEN_WEB_IN_BODY = true
|
|
art.fullscreenWeb = true
|
|
art.destroy(removeHtml)
|
|
const result = { orphan: player.parentNode === document.body, retained: container.contains(player), style: player.style.cssText === style, fullscreen: player.classList.contains('art-fullscreen-web') }
|
|
player.remove()
|
|
return result
|
|
}, removeHtml)
|
|
expect(result).toEqual(core === 'candidate'
|
|
? { orphan: false, retained: !removeHtml, style: true, fullscreen: false }
|
|
: { orphan: true, retained: false, style: false, fullscreen: true })
|
|
})
|
|
}
|
|
}
|