Files
ArtPlayer/test/browser/notice.spec.js
T

169 lines
6.2 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)
await page.evaluate(() => {
window.Artplayer.NOTICE_TIME = 24733
window.art.notice.destroy()
})
}
for (const core of ['published', 'candidate']) {
test(`${core}: notice updates do not call a consumer override of destroy`, async ({ page }) => {
await setup(page, core)
expect(await page.evaluate(() => {
const notice = window.art.notice
const original = notice.destroy
let calls = 0
notice.destroy = function () {
calls++
return original.call(this)
}
notice.show = 'first'
notice.show = 'second'
const implicit = calls
notice.destroy()
const explicit = calls
window.art.destroy()
return { implicit, explicit }
})).toEqual({ implicit: 0, explicit: 1 })
})
test(`${core}: notice preserves public shape, Error text and false hide semantics`, async ({ page }) => {
await setup(page, core)
expect(await page.evaluate(() => {
const art = window.art
const notice = art.notice
notice.show = new Error(' trimmed ')
const visible = notice.show
const text = art.template.$noticeInner.textContent
const timer = notice.timer
notice.show = false
const result = {
fields: Object.keys(notice),
prototype: Object.getOwnPropertyNames(Object.getPrototypeOf(notice)),
visible,
text,
hidden: !notice.show,
retained: art.template.$noticeInner.textContent,
sameTimer: timer === notice.timer,
}
notice.destroy()
art.destroy()
return result
})).toEqual({ fields: ['art', 'timer'], prototype: ['constructor', 'destroy', 'show'], visible: true, text: 'trimmed', hidden: true, retained: 'trimmed', sameTimer: true })
})
test(`${core}: notice expires naturally during real playback`, async ({ page }) => {
await setup(page, core)
await page.evaluate(() => window.art.play())
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.1)
await page.evaluate(() => {
window.Artplayer.NOTICE_TIME = 20
window.art.notice.show = 'transient'
})
await expect.poll(() => page.evaluate(() => ({ visible: window.art.notice.show, text: window.art.template.$noticeInner.textContent }))).toEqual({ visible: false, text: '' })
expect(await page.evaluate(() => window.art.playing)).toBe(true)
await page.evaluate(() => window.art.destroy())
})
test(`${core}: manual notice destroy permits reuse and does not hide its DOM`, async ({ page }) => {
await setup(page, core)
expect(await page.evaluate(() => {
const notice = window.art.notice
notice.show = 'visible'
notice.destroy()
const visible = notice.show
const timer = notice.timer
notice.show = 'reused'
const reused = window.art.template.$noticeInner.textContent
notice.destroy()
window.art.destroy()
return { visible, timer, reused }
})).toEqual({ visible: true, timer: null, reused: 'reused' })
})
test(`${core}: destruction during notice text assignment cannot schedule a late timer`, async ({ page }) => {
await setup(page, core)
expect(await page.evaluate(() => {
const art = window.art
const notice = art.notice
const inner = art.template.$noticeInner
const descriptor = Object.getOwnPropertyDescriptor(Node.prototype, 'textContent')
Object.defineProperty(inner, 'textContent', {
configurable: true,
get() {
return descriptor.get.call(this)
},
set(value) {
descriptor.set.call(this, value)
art.destroy(false)
},
})
notice.show = 'destroy during write'
const result = { destroyed: art.isDestroy, timer: notice.timer !== null, visible: notice.show }
notice.destroy()
return result
})).toEqual({ destroyed: true, timer: core === 'published', visible: core === 'published' })
})
test(`${core}: expiry does not hide a notice installed during the clearing write`, async ({ page }) => {
await setup(page, core)
expect(await page.evaluate(() => {
const art = window.art
const inner = art.template.$noticeInner
const descriptor = Object.getOwnPropertyDescriptor(Node.prototype, 'textContent')
const original = window.setTimeout
let expire
window.setTimeout = function (callback, delay, ...args) {
if (delay === 24733)
expire = callback
return original.call(this, callback, delay, ...args)
}
art.notice.show = 'old'
Object.defineProperty(inner, 'textContent', {
configurable: true,
get() {
return descriptor.get.call(this)
},
set(value) {
descriptor.set.call(this, value)
if (value === '')
art.notice.show = 'nested'
},
})
expire()
const result = { text: inner.textContent, visible: art.notice.show }
window.setTimeout = original
art.notice.destroy()
art.destroy()
return result
})).toEqual({ text: 'nested', visible: core === 'candidate' })
})
}
test('candidate: retained callbacks and assignments cannot revive destroyed notice resources', async ({ page }) => {
await setup(page, 'candidate')
expect(await page.evaluate(() => {
const art = window.art
const original = window.setTimeout
const callbacks = []
window.setTimeout = function (callback, delay, ...args) {
if (delay === 24733)
callbacks.push(callback)
return original.call(this, callback, delay, ...args)
}
art.notice.show = 'old'
art.notice.show = 'new'
callbacks[0]()
const before = art.template.$noticeInner.textContent
art.destroy(false)
callbacks[1]()
art.notice.show = 'ignored'
window.setTimeout = original
return { before, after: art.template.$noticeInner.textContent, timer: art.notice.timer, calls: callbacks.length }
})).toEqual({ before: 'new', after: 'new', timer: null, calls: 2 })
})