Files
ArtPlayer/test/progress.test.js
T

135 lines
5.6 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 { Emitter, installProgressInteractions, getPosFromEvent, beginLifecycle, getScope, beginSource, ownEntry, releaseEntry } = await loadModules({
Emitter: 'packages/artplayer/src/utils/emitter',
installProgressInteractions: { file: 'packages/artplayer/src/control/progress/interactions', name: 'installProgressInteractions' },
getPosFromEvent: { file: 'packages/artplayer/src/control/progress/position', name: 'getPosFromEvent' },
beginLifecycle: { file: 'packages/artplayer/src/lifecycle/instance', name: 'beginLifecycle' },
getScope: { file: 'packages/artplayer/src/lifecycle/instance', name: 'getScope' },
beginSource: { file: 'packages/artplayer/src/source/operation', name: 'beginSource' },
ownEntry: { file: 'packages/artplayer/src/component/resources', name: 'ownEntry' },
releaseEntry: { file: 'packages/artplayer/src/component/resources', name: 'releaseEntry' },
})
function mouse(name, clientX = 60, button = 0) {
return Object.assign(new Event(name), { clientX, button })
}
function fixture() {
const progress = Object.assign(new EventTarget(), { clientWidth: 100, getBoundingClientRect: () => ({ left: 10 }) })
const indicator = new EventTarget()
const control = Object.assign(new EventTarget(), { querySelector: () => indicator })
const effects = []
const art = Object.assign(new Emitter(), {
template: { $progress: progress },
duration: 200,
isRotate: false,
events: {
proxy(target, name, callback) {
target.addEventListener(name, callback)
return () => target.removeEventListener(name, callback)
},
remove: cleanup => cleanup(),
},
})
Object.defineProperty(art, 'seek', { get: () => 0, set: value => effects.push(['seek', value]) })
art.on('setBar', (kind, percentage) => effects.push(['setBar', kind, percentage]))
beginLifecycle(art)
ownEntry(art, control)
installProgressInteractions(art, control)
return { art, progress, control, indicator, effects }
}
test('progress position clamps outside edges and retains its time and percentage calculations', () => {
const f = fixture()
assert.deepEqual(getPosFromEvent(f.art, mouse('click', 60)), { second: 100, time: '01:40', width: 50, percentage: 0.5 })
assert.equal(getPosFromEvent(f.art, mouse('click', -100)).percentage, 0)
assert.equal(getPosFromEvent(f.art, mouse('click', 1000)).percentage, 1)
f.progress.clientWidth = 0
assert(Number.isNaN(getPosFromEvent(f.art, mouse('click', 10)).percentage))
})
test('live progress clicks and drags preserve setBar before seek and mouseup stops dragging', () => {
const f = fixture()
f.progress.dispatchEvent(mouse('click'))
assert.deepEqual(f.effects, [['setBar', 'played', 0.5], ['seek', 100]])
f.effects.length = 0
f.progress.dispatchEvent(mouse('mousedown'))
f.art.emit('document:mousemove', mouse('mousemove', 85))
f.art.emit('document:mouseup', mouse('mouseup'))
f.art.emit('document:mousemove', mouse('mousemove', 100))
assert.deepEqual(f.effects, [['setBar', 'played', 0.75], ['seek', 150]])
})
for (const mode of ['control removal', 'destruction', 'source replacement']) {
test(`progress click does not seek after ${mode} inside setBar`, () => {
const f = fixture()
f.art.on('setBar', () => {
if (mode === 'control removal')
releaseEntry(f.control)
else if (mode === 'destruction')
getScope(f.art).dispose()
else
beginSource(f.art)
})
f.progress.dispatchEvent(mouse('click'))
assert.deepEqual(f.effects, [['setBar', 'played', 0.5]])
})
}
test('progress drag loses its source when media is replaced and a new mousedown can start again', () => {
const f = fixture()
f.progress.dispatchEvent(mouse('mousedown'))
beginSource(f.art)
f.art.emit('document:mousemove', mouse('mousemove', 85))
assert.deepEqual(f.effects, [])
f.progress.dispatchEvent(mouse('mousedown'))
f.art.emit('document:mousemove', mouse('mousemove', 60))
assert.deepEqual(f.effects, [['setBar', 'played', 0.5], ['seek', 100]])
})
test('progress drag does not seek when its control is removed inside setBar', () => {
const f = fixture()
f.art.on('setBar', () => releaseEntry(f.control))
f.progress.dispatchEvent(mouse('mousedown'))
f.art.emit('document:mousemove', mouse('mousemove', 60))
assert.deepEqual(f.effects, [['setBar', 'played', 0.5]])
})
test('progress geometry reentry cannot emit or seek after disposal', () => {
const f = fixture()
f.progress.getBoundingClientRect = () => {
getScope(f.art).dispose()
return { left: 10 }
}
f.progress.dispatchEvent(mouse('click'))
assert.deepEqual(f.effects, [])
})
test('a newer nested progress click supersedes the earlier seek', () => {
const f = fixture()
let nested = false
f.art.on('setBar', () => {
if (!nested) {
nested = true
f.progress.dispatchEvent(mouse('click', 85))
}
})
f.progress.dispatchEvent(mouse('click', 60))
assert.deepEqual(f.effects, [['setBar', 'played', 0.5], ['setBar', 'played', 0.75], ['seek', 150]])
})
test('right-button mousedown and mouseup inside setBar do not continue dragging', () => {
const f = fixture()
f.progress.dispatchEvent(mouse('mousedown', 60, 2))
f.art.emit('document:mousemove', mouse('mousemove', 85))
assert.deepEqual(f.effects, [])
f.art.on('setBar', () => f.art.emit('document:mouseup', mouse('mouseup')))
f.progress.dispatchEvent(mouse('mousedown'))
f.art.emit('document:mousemove', mouse('mousemove', 60))
assert.deepEqual(f.effects, [['setBar', 'played', 0.5]])
})