Files
ArtPlayer/test/builtin-layers.test.js

122 lines
4.7 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 { lock, miniProgressBar, Emitter, beginLifecycle, getScope, ownEntry, releaseEntry } = await loadModules({
lock: 'packages/artplayer/src/plugins/lock',
miniProgressBar: 'packages/artplayer/src/plugins/miniProgressBar',
Emitter: 'packages/artplayer/src/utils/emitter',
beginLifecycle: { file: 'packages/artplayer/src/lifecycle/instance', name: 'beginLifecycle' },
getScope: { file: 'packages/artplayer/src/lifecycle/instance', name: 'getScope' },
ownEntry: { file: 'packages/artplayer/src/component/resources', name: 'ownEntry' },
releaseEntry: { file: 'packages/artplayer/src/component/resources', name: 'releaseEntry' },
})
function fixture(t) {
class Element extends EventTarget {
ownerDocument = { activeElement: null }
attributes = new Map()
setAttribute(key, value) { this.attributes.set(key, String(value)) }
getAttribute(key) { return this.attributes.get(key) ?? null }
hasAttribute(key) { return this.attributes.has(key) }
removeAttribute(key) { this.attributes.delete(key) }
contains(node) { return node === this || this.children.some(child => child.contains(node)) }
querySelectorAll() { return this.children }
style = {}
children = []
appendChild(child) {
this.children.push(child)
this.lastElementChild = child
}
}
const original = Object.getOwnPropertyDescriptor(globalThis, 'Element')
Object.defineProperty(globalThis, 'Element', { configurable: true, value: Element })
t.after(() => original ? Object.defineProperty(globalThis, 'Element', original) : Reflect.deleteProperty(globalThis, 'Element'))
const classes = new Set()
const classList = { add: value => classes.add(value), remove: value => classes.delete(value), contains: value => classes.has(value) }
const element = new Element()
let option
const art = Object.assign(new Emitter(), {
template: { $player: { classList }, $bottom: new Element() },
i18n: { get: key => key },
isLock: false,
icons: { lock: new Element(), unlock: new Element() },
layers: { add(value) {
option = value
ownEntry(art, element)
option.mounted(element)
return element
} },
})
beginLifecycle(art)
const plugin = lock(art)
return { art, plugin, classes, classList, element, click: () => option.click(), scope: getScope(art) }
}
test('lock preserves state/class/flag ordering, repeated notifications and icon visibility', (t) => {
const f = fixture(t)
assert.deepEqual(Object.keys(f.plugin), ['name', 'state'])
const seen = []
f.art.on('lock', state => seen.push([state, f.plugin.state, f.art.isLock]))
f.plugin.state = true
f.plugin.state = true
assert.equal(f.art.icons.lock.style.display, 'inline-flex')
assert.equal(f.art.icons.unlock.style.display, 'none')
f.click()
assert.equal(f.art.icons.lock.style.display, 'none')
assert.equal(f.art.icons.unlock.style.display, 'inline-flex')
assert.deepEqual(seen, [[true, true, true], [true, true, true], [false, false, false]])
f.scope.dispose()
})
test('lock icon subscriptions are removed with the layer but the live state API remains usable', (t) => {
const f = fixture(t)
releaseEntry(f.element)
f.plugin.state = true
assert.equal(f.plugin.state, true)
assert.equal(f.art.isLock, true)
assert.equal(f.art.icons.lock.style.display, 'none')
assert.equal(f.art.e.lock, undefined)
f.scope.dispose()
})
test('destroyed lock does not emit or mutate state through retained setters and clicks', (t) => {
const f = fixture(t)
f.scope.dispose()
let events = 0
f.art.on('lock', () => events++)
f.plugin.state = true
f.click()
assert.equal(f.plugin.state, false)
assert.equal(f.art.isLock, false)
assert.equal(events, 0)
})
test('destruction during lock class mutation stops the subsequent flag and event', (t) => {
const f = fixture(t)
const original = f.classList.add
f.classList.add = (name) => {
original(name)
f.scope.dispose()
}
let events = 0
f.art.on('lock', () => events++)
f.plugin.state = true
assert.equal(f.art.isLock, false)
assert.equal(events, 0)
})
test('miniProgressBar preserves its control-state class and releases the subscription', (t) => {
const f = fixture(t)
assert.deepEqual(miniProgressBar(f.art), { name: 'mini-progress-bar' })
f.art.emit('control', false)
assert.equal(f.classes.has('art-mini-progress-bar'), true)
f.art.emit('control', true)
assert.equal(f.classes.has('art-mini-progress-bar'), false)
f.scope.dispose()
f.art.emit('control', false)
assert.equal(f.classes.has('art-mini-progress-bar'), false)
assert.equal(f.art.e.control, undefined)
})