mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-09 03:46:16 -08:00
162 lines
5.9 KiB
JavaScript
162 lines
5.9 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 { setImmediate } from 'node:timers/promises'
|
|
import { loadModules } from './helpers/load.js'
|
|
|
|
const { clickInit, hoverInit, moveInit, Emitter, beginLifecycle, getScope } = await loadModules({
|
|
clickInit: 'packages/artplayer/src/events/clickInit',
|
|
hoverInit: 'packages/artplayer/src/events/hoverInit',
|
|
moveInit: 'packages/artplayer/src/events/moveInit',
|
|
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() {
|
|
const classes = new Set()
|
|
const $player = { classList: { add: name => classes.add(name), remove: name => classes.delete(name) } }
|
|
const $video = {}
|
|
const calls = []
|
|
const callbacks = new Map()
|
|
const art = Object.assign(new Emitter(), {
|
|
template: { $player, $video },
|
|
constructor: { DBCLICK_TIME: 300, DBCLICK_FULLSCREEN: true, MOBILE_CLICK_PLAY: false, MOBILE_DBCLICK_PLAY: true },
|
|
isFocus: false,
|
|
isInput: false,
|
|
isLock: false,
|
|
fullscreen: false,
|
|
toggle: () => calls.push('toggle'),
|
|
})
|
|
beginLifecycle(art)
|
|
const events = {
|
|
proxy(target, name, callback) {
|
|
callbacks.set(name, callback)
|
|
return () => callbacks.delete(name)
|
|
},
|
|
hover(target, enter, leave) {
|
|
callbacks.set('mouseenter', enter)
|
|
callbacks.set('mouseleave', leave)
|
|
},
|
|
}
|
|
clickInit(art, events)
|
|
hoverInit(art, events)
|
|
moveInit(art, events)
|
|
return { art, classes, calls, callbacks, dispose: () => getScope(art).dispose() }
|
|
}
|
|
|
|
test('click grouping keeps the inclusive threshold, third-click reset and live settings', (t) => {
|
|
const f = fixture()
|
|
let now = 0
|
|
t.mock.method(Date, 'now', () => now)
|
|
f.art.on('click', () => f.calls.push('click'))
|
|
f.art.on('dblclick', () => f.calls.push('dblclick'))
|
|
const click = f.callbacks.get('click')
|
|
click(new Event('click'))
|
|
now = 300
|
|
click(new Event('click'))
|
|
assert.equal(f.art.fullscreen, true)
|
|
now = 301
|
|
click(new Event('click'))
|
|
now = 602
|
|
click(new Event('click'))
|
|
f.art.constructor.DBCLICK_FULLSCREEN = false
|
|
click(new Event('click'))
|
|
assert.equal(f.art.fullscreen, true)
|
|
assert.deepEqual(f.calls, ['click', 'toggle', 'dblclick', 'click', 'toggle', 'click', 'toggle', 'dblclick'])
|
|
f.dispose()
|
|
})
|
|
|
|
for (const name of ['click', 'dblclick']) {
|
|
test(`destruction during ${name} stops the remaining playback or fullscreen action`, () => {
|
|
const f = fixture()
|
|
f.art.constructor.DBCLICK_TIME = 60000
|
|
f.art.on(name, f.dispose)
|
|
f.callbacks.get('click')(new Event('click'))
|
|
if (name === 'dblclick')
|
|
f.callbacks.get('click')(new Event('click'))
|
|
assert.equal(f.art.fullscreen, false)
|
|
assert.deepEqual(f.calls, name === 'click' ? [] : ['toggle'])
|
|
f.callbacks.get('click')(new Event('click'))
|
|
assert.deepEqual(f.calls, name === 'click' ? [] : ['toggle'])
|
|
})
|
|
}
|
|
|
|
test('click callback errors retain identity and stop the default action', () => {
|
|
const f = fixture()
|
|
const failure = new Error('caller failed')
|
|
const callback = () => {
|
|
throw failure
|
|
}
|
|
f.art.on('click', callback)
|
|
assert.throws(() => f.callbacks.get('click')(new Event('click')), error => error === failure)
|
|
assert.deepEqual(f.calls, [])
|
|
f.dispose()
|
|
})
|
|
|
|
test('click-triggered playback rejection remains handled internally', async () => {
|
|
const f = fixture()
|
|
f.art.toggle = () => Promise.reject(new DOMException('interrupted', 'AbortError'))
|
|
f.callbacks.get('click')(new Event('click'))
|
|
await setImmediate()
|
|
f.dispose()
|
|
})
|
|
|
|
test('nested clicks preserve the historical synchronous grouping and event identity', (t) => {
|
|
const f = fixture()
|
|
t.mock.method(Date, 'now', () => 100)
|
|
const first = new Event('click')
|
|
const nested = new Event('click')
|
|
f.art.on('click', (event) => {
|
|
assert.equal(event, first)
|
|
f.calls.push('click')
|
|
f.callbacks.get('click')(nested)
|
|
})
|
|
f.art.on('dblclick', event => f.calls.push(event === nested ? 'nested' : 'next'))
|
|
f.callbacks.get('click')(first)
|
|
assert.equal(f.art.fullscreen, true)
|
|
f.callbacks.get('click')(new Event('click'))
|
|
assert.equal(f.art.fullscreen, false)
|
|
assert.deepEqual(f.calls, ['click', 'nested', 'toggle', 'next'])
|
|
f.dispose()
|
|
})
|
|
|
|
test('focus flags retain input-only semantics and disposal removes only owned subscriptions', () => {
|
|
const f = fixture()
|
|
const events = []
|
|
f.art.on('focus', event => events.push(event))
|
|
const input = { tagName: 'INPUT' }
|
|
const event = { target: input, composedPath: () => [input, f.art.template.$player] }
|
|
f.art.emit('document:click', event)
|
|
assert.equal(f.art.isInput, true)
|
|
assert.equal(f.art.isFocus, true)
|
|
assert.equal(events[0], event)
|
|
input.tagName = 'TEXTAREA'
|
|
f.art.emit('document:contextmenu', event)
|
|
assert.equal(f.art.isInput, false)
|
|
let external = 0
|
|
f.art.on('document:click', () => external++)
|
|
f.dispose()
|
|
f.art.emit('document:click', { target: null, composedPath: () => [] })
|
|
assert.equal(f.art.isFocus, true)
|
|
assert.equal(external, 1)
|
|
assert.equal(events.length, 2)
|
|
})
|
|
|
|
test('hover and move preserve class-before-event ordering and reject stale callbacks', () => {
|
|
const f = fixture()
|
|
const states = []
|
|
const event = new Event('mousemove')
|
|
f.art.on('hover', (state, original) => states.push([state, original, f.classes.has('art-hover')]))
|
|
f.art.on('mousemove', original => states.push(['move', original]))
|
|
f.callbacks.get('mouseenter')(event)
|
|
f.callbacks.get('mousemove')(event)
|
|
f.callbacks.get('mouseleave')(event)
|
|
assert.deepEqual(states, [[true, event, true], ['move', event], [false, event, false]])
|
|
f.dispose()
|
|
f.callbacks.get('mouseenter')(event)
|
|
f.callbacks.get('mousemove')(event)
|
|
assert.equal(f.classes.size, 0)
|
|
assert.equal(states.length, 3)
|
|
})
|