Files
ArtPlayer/test/browser/setting-ownership.spec.js
T

156 lines
6.6 KiB
JavaScript

import { expect, test } from './fixtures.js'
for (const action of ['failure', 'navigate', 'remove', 'destroy']) {
test(`candidate: cached panel failure preserves navigation after ${action}`, async ({ page }) => {
await page.goto('/test/player.html?core=candidate&chapter=published')
const result = await page.evaluate((action) => {
const first = { name: 'first', html: 'First', selector: [{ name: 'one', html: 'One' }] }
const second = { name: 'second', html: 'Second', selector: [{ name: 'two', html: 'Two' }] }
const art = new window.Artplayer({ container: '.player', url: '', setting: true, settings: [first, second] })
const setting = art.setting
setting.show = true
setting.render(second.selector)
setting.render(first.selector)
const panel = setting.cache.get(first.selector)
const callbacks = [...first.$events, ...first.selector[0].$events]
const resize = setting.resize.bind(setting)
const layout = setting.$parent.style.cssText
const failure = new Error('cached layout failed')
setting.resize = () => {
setting.resize = resize
resize()
if (action === 'navigate')
setting.render()
if (action === 'remove')
setting.remove('second')
if (action === 'destroy')
art.destroy()
throw failure
}
let originalError = false
try {
setting.render(second.selector)
}
catch (error) {
originalError = error === failure
}
const result = {
originalError,
active: setting.active === first.selector ? 'first' : setting.active === setting.option ? 'root' : 'second',
panels: setting.cache.size,
firstVisible: panel.classList.contains('art-current'),
connected: panel.isConnected,
layoutRestored: setting.$parent.style.cssText === layout,
listeners: callbacks.every(callback => art.events.destroyEvents.has(callback)),
}
if (action !== 'destroy')
art.destroy()
return result
}, action)
expect(result).toEqual({
originalError: true,
active: action === 'failure' ? 'first' : action === 'destroy' ? 'second' : 'root',
panels: action === 'remove' ? 2 : action === 'destroy' ? 0 : 3,
firstVisible: action === 'failure',
connected: action !== 'destroy',
layoutRestored: action !== 'remove',
listeners: action !== 'destroy',
})
})
}
for (const core of ['published', 'candidate']) {
test(`${core}: sharing an active setting has an explicit ownership baseline`, async ({ page }, testInfo) => {
await page.goto(`/test/player.html?core=${core}&chapter=published`)
const result = await page.evaluate(async () => {
const container = document.createElement('div')
document.body.append(container)
const calls = []
const item = { name: 'shared', html: 'Shared', onClick() {
calls.push(this.id)
} }
const first = new window.Artplayer({ container: '.player', url: '', setting: true, settings: [item] })
const row = item.$item
const firstOption = item.$option
let second
let error
try {
second = new window.Artplayer({ container, url: '', setting: true, settings: [item] })
}
catch (failure) {
error = failure.message
}
row.click()
item.$item.click()
await Promise.resolve()
const result = { error: error || null, firstOption: item.$option === firstOption, firstHandle: item.$item === row, firstCalls: calls.filter(id => id === first.id).length, secondCalls: calls.filter(id => id === second?.id).length, firstRows: first.setting.$parent.querySelectorAll('.art-setting-item').length, secondRows: second?.setting.$parent.querySelectorAll('.art-setting-item').length ?? null }
second?.destroy()
first.destroy()
container.remove()
return result
})
await testInfo.attach('setting-ownership', { contentType: 'application/json', body: JSON.stringify(result) })
expect(result).toEqual(core === 'published'
? { error: null, firstOption: true, firstHandle: true, firstCalls: 2, secondCalls: 0, firstRows: 1, secondRows: 0 }
: { error: 'Setting item [shared] already belongs to another active player', firstOption: true, firstHandle: true, firstCalls: 2, secondCalls: 0, firstRows: 1, secondRows: null })
})
}
for (const action of ['remove', 'destroy']) {
test(`candidate: a setting subtree can move to another instance after ${action}`, async ({ page }) => {
await page.goto('/test/player.html?core=candidate&chapter=published')
const result = await page.evaluate(async (action) => {
const container = document.createElement('div')
document.body.append(container)
const calls = []
const child = { name: 'child', html: 'Child', onClick() {
calls.push(this.id)
} }
const parent = { name: 'parent', html: 'Parent', selector: [child] }
const first = new window.Artplayer({ container: '.player', url: '', setting: true, settings: [parent] })
first.setting.render(parent.selector)
const oldRow = child.$item
const events = child.$events
const getter = Object.getOwnPropertyDescriptor(child, '$parents').get
const second = new window.Artplayer({ container, url: '', setting: true })
let rejected = false
try {
second.setting.add(parent)
}
catch (error) {
rejected = error.name === 'ArtPlayerError'
}
oldRow.click()
await Promise.resolve()
const protectedOwner = calls.length === 1 && calls[0] === first.id && second.setting.option.length === 0
calls.length = 0
if (action === 'remove')
first.setting.remove('parent')
else
first.destroy()
const returned = second.setting.add(parent) === parent
second.setting.render(parent.selector)
first.destroy()
oldRow.click()
child.$item.click()
await Promise.resolve()
const result = {
rejected,
protectedOwner,
returned,
getter: Object.getOwnPropertyDescriptor(child, '$parents').get === getter,
eventArray: child.$events === events,
parent: child.$parent === parent,
root: child.$parents === second.setting.option,
oldConnected: oldRow.isConnected,
newConnected: child.$item.isConnected,
newOwnerOnly: calls.length === 1 && calls[0] === second.id,
}
second.destroy()
container.remove()
return result
}, action)
expect(result).toEqual({ rejected: true, protectedOwner: true, returned: true, getter: true, eventArray: true, parent: true, root: true, oldConnected: false, newConnected: true, newOwnerOnly: true })
})
}