Files
ArtPlayer/test/notice.test.js
T

164 lines
5.1 KiB
JavaScript

import assert from 'node:assert/strict'
// eslint-disable-next-line test/no-import-node-test -- Use the repository Node test runner.
import { test } from 'node:test'
import { loadModules } from './helpers/load.js'
const { Notice, Emitter, beginLifecycle, getScope } = await loadModules({
Notice: 'packages/artplayer/src/notice',
Emitter: 'packages/artplayer/src/utils/emitter',
beginLifecycle: { file: 'packages/artplayer/src/lifecycle/instance', name: 'beginLifecycle' },
getScope: { file: 'packages/artplayer/src/lifecycle/instance', name: 'getScope' },
})
function fixture(t) {
const timers = new Map()
let id = 0
t.mock.method(globalThis, 'setTimeout', (callback, delay) => {
const key = ++id
timers.set(key, { callback, delay })
return key
})
t.mock.method(globalThis, 'clearTimeout', key => timers.delete(key))
const classes = new Set()
const classList = {
add: value => classes.add(value),
remove: value => classes.delete(value),
contains: value => classes.has(value),
}
const inner = { textContent: '' }
const art = Object.assign(new Emitter(), {
template: { $player: { classList }, $noticeInner: inner },
constructor: { NOTICE_TIME: 100 },
})
beginLifecycle(art)
const scope = getScope(art)
const notice = new Notice(art)
return { art, scope, notice, inner, classList, timers }
}
test('notice preserves fields, descriptors, text, live delay and hide semantics', (t) => {
const f = fixture(t)
assert.deepEqual(Object.keys(f.notice), ['art', 'timer'])
assert.deepEqual(Object.getOwnPropertyNames(Object.getPrototypeOf(f.notice)), ['constructor', 'destroy', 'show'])
assert.equal(f.notice.show, false)
assert.equal(f.notice.timer, null)
f.notice.show = new Error(' example ')
assert.equal(f.inner.textContent, 'example')
assert.equal(f.notice.show, true)
assert.equal(f.timers.get(f.notice.timer).delay, 100)
f.art.constructor.NOTICE_TIME = 200
f.notice.show = ' next '
assert.equal(f.inner.textContent, ' next ')
assert.equal(f.timers.size, 1)
assert.equal(f.timers.get(f.notice.timer).delay, 200)
f.notice.show = ''
assert.equal(f.notice.show, false)
assert.equal(f.inner.textContent, ' next ')
assert.equal(f.timers.size, 1)
f.timers.get(f.notice.timer).callback()
assert.equal(f.inner.textContent, '')
})
test('manual notice destroy cancels its timer and permits reuse while alive', (t) => {
const f = fixture(t)
f.notice.show = 'visible'
assert.equal(f.notice.destroy(), undefined)
assert.equal(f.notice.timer, null)
assert.equal(f.timers.size, 0)
assert.equal(f.notice.show, true)
assert.equal(f.inner.textContent, 'visible')
f.notice.destroy()
f.notice.show = 'reused'
assert.equal(f.timers.size, 1)
f.art.emit('destroy')
assert.equal(f.timers.size, 0)
f.notice.show = 'again'
assert.equal(f.timers.size, 1)
f.scope.dispose()
f.notice.show = 'ignored'
assert.equal(f.timers.size, 0)
assert.equal(f.inner.textContent, 'again')
})
test('notice does not allocate a timer after destruction during DOM text writes', (t) => {
const f = fixture(t)
Object.defineProperty(f.inner, 'textContent', {
get: () => '',
set() {
f.scope.dispose()
},
})
f.notice.show = 'destroy during setter'
assert.equal(f.notice.timer, null)
assert.equal(f.timers.size, 0)
assert.equal(f.notice.show, false)
})
test('obsolete notice timer callbacks cannot erase a newer message', (t) => {
const f = fixture(t)
f.notice.show = 'old'
const stale = f.timers.get(f.notice.timer).callback
f.notice.show = 'new'
stale()
assert.equal(f.inner.textContent, 'new')
assert.equal(f.notice.show, true)
f.scope.dispose()
stale()
assert.equal(f.inner.textContent, 'new')
})
test('notice expiry does not hide a message installed by a nested DOM write', (t) => {
const f = fixture(t)
f.notice.show = 'old'
const expire = f.timers.get(f.notice.timer).callback
let value = 'old'
Object.defineProperty(f.inner, 'textContent', {
get: () => value,
set(next) {
value = next
if (next === '')
f.notice.show = 'nested'
},
})
expire()
assert.equal(f.inner.textContent, 'nested')
assert.equal(f.notice.show, true)
assert.equal(f.timers.size, 1)
})
test('nested notice writes keep only the newest timer', (t) => {
const f = fixture(t)
let value = ''
Object.defineProperty(f.inner, 'textContent', {
get: () => value,
set(next) {
value = next
if (next === 'outer')
f.notice.show = 'inner'
},
})
f.notice.show = 'outer'
assert.equal(f.inner.textContent, 'inner')
assert.equal(f.notice.show, true)
assert.equal(f.timers.size, 1)
f.scope.dispose()
assert.equal(f.timers.size, 0)
})
test('notice message updates do not invoke a consumer override of the public destroy method', (t) => {
const f = fixture(t)
let calls = 0
const original = f.notice.destroy
f.notice.destroy = function () {
calls++
return original.call(this)
}
f.notice.show = 'first'
f.notice.show = 'second'
assert.equal(calls, 0)
assert.equal(f.inner.textContent, 'second')
f.notice.destroy()
assert.equal(calls, 1)
f.scope.dispose()
})