mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
174 lines
6.9 KiB
JavaScript
174 lines
6.9 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
// eslint-disable-next-line test/no-import-node-test -- Node historical regression runner.
|
|
import test from 'node:test'
|
|
import { canvasEnvironment, canvasHistorical } from './helpers/canvas.js'
|
|
|
|
function deferred() {
|
|
let resolve, reject
|
|
const promise = new Promise((yes, no) => {
|
|
resolve = yes
|
|
reject = no
|
|
})
|
|
return { promise, resolve, reject }
|
|
}
|
|
|
|
for (const implementation of await canvasHistorical()) {
|
|
test(`Canvas ${implementation.name}: historical pending bitmap draws and reschedules after pause or destroy`, async () => {
|
|
for (const operation of ['video:pause', 'destroy']) {
|
|
const pending = deferred()
|
|
let closed = 0
|
|
const bitmap = { close() {
|
|
closed++
|
|
} }
|
|
const env = await canvasEnvironment(implementation, { createImageBitmap: () => pending.promise })
|
|
env.factory()(env.art)
|
|
env.art.emit('video:play')
|
|
env.art.emit(operation)
|
|
assert.equal(env.frames.size, 0)
|
|
pending.resolve(bitmap)
|
|
await env.flush()
|
|
assert.equal(env.draws.length, 1)
|
|
assert.equal(closed, 1)
|
|
assert.equal(env.frames.size, 1)
|
|
assert.equal(env.emitted.at(-1).name, 'artplayerProxyCanvas:draw')
|
|
}
|
|
})
|
|
|
|
test(`Canvas ${implementation.name}: historical repeated play creates concurrent stale bitmap loops`, async () => {
|
|
const pending = [deferred(), deferred()]
|
|
let index = 0
|
|
const env = await canvasEnvironment(implementation, { createImageBitmap: () => pending[index++].promise })
|
|
env.factory()(env.art)
|
|
env.art.emit('video:play')
|
|
env.art.emit('video:play')
|
|
const newer = { close() {} }
|
|
const older = { close() {} }
|
|
pending[1].resolve(newer)
|
|
await env.flush()
|
|
pending[0].resolve(older)
|
|
await env.flush()
|
|
assert.deepEqual(env.draws.map(args => args[0]), [newer, older])
|
|
assert.equal(env.frames.size, 2)
|
|
env.art.destroy()
|
|
assert.equal(env.frames.size, 1, 'Only the latest stored RAF handle is cancelled')
|
|
})
|
|
|
|
test(`Canvas ${implementation.name}: historical draw failure emits its error but leaks the acquired bitmap`, async () => {
|
|
const failure = new Error('draw failed')
|
|
let closed = 0
|
|
const bitmap = { close() {
|
|
closed++
|
|
} }
|
|
const env = await canvasEnvironment(implementation, { createImageBitmap: () => Promise.resolve(bitmap), onDraw() {
|
|
throw failure
|
|
} })
|
|
env.factory()(env.art)
|
|
env.art.emit('video:play')
|
|
await env.flush()
|
|
assert.equal(closed, 0)
|
|
assert.equal(env.emitted.at(-1).name, 'artplayerProxyCanvas:error')
|
|
assert.equal(env.emitted.at(-1).args[0], failure)
|
|
assert.equal(env.frames.size, 1)
|
|
})
|
|
|
|
test(`Canvas ${implementation.name}: historical bitmap rejection emits the original error without direct-video fallback`, async () => {
|
|
const failure = new Error('bitmap rejected')
|
|
const env = await canvasEnvironment(implementation, { createImageBitmap: () => Promise.reject(failure) })
|
|
env.factory()(env.art)
|
|
env.art.emit('video:play')
|
|
await env.flush()
|
|
assert.equal(env.draws.length, 0)
|
|
assert.equal(env.emitted.at(-1).name, 'artplayerProxyCanvas:error')
|
|
assert.equal(env.emitted.at(-1).args[0], failure)
|
|
assert.equal(env.frames.size, 1)
|
|
})
|
|
|
|
test(`Canvas ${implementation.name}: callback errors occur after bitmap close and replace the draw event with an error`, async () => {
|
|
const failure = new Error('callback failed')
|
|
let closed = 0
|
|
const env = await canvasEnvironment(implementation, { createImageBitmap: () => Promise.resolve({ close() {
|
|
closed++
|
|
} }) })
|
|
env.factory(() => {
|
|
throw failure
|
|
})(env.art)
|
|
env.art.emit('resize')
|
|
await env.flush()
|
|
assert.equal(closed, 1)
|
|
assert.equal(env.emitted.filter(event => event.name === 'artplayerProxyCanvas:draw').length, 0)
|
|
assert.equal(env.emitted.at(-1).args[0], failure)
|
|
})
|
|
|
|
test(`Canvas ${implementation.name}: historical callback destruction still emits draw and queues a new frame`, async () => {
|
|
const env = await canvasEnvironment(implementation)
|
|
env.factory(() => env.art.destroy())(env.art)
|
|
env.art.emit('video:play')
|
|
await env.flush()
|
|
assert.deepEqual(env.emitted.map(event => event.name), ['video:play', 'destroy', 'artplayerProxyCanvas:draw'])
|
|
assert.equal(env.frames.size, 1)
|
|
})
|
|
|
|
test(`Canvas ${implementation.name}: historical destroy before setup timeout retains host handlers and attaches media listeners later`, async () => {
|
|
const env = await canvasEnvironment(implementation)
|
|
env.factory()(env.art)
|
|
env.art.destroy()
|
|
assert.equal(env.timers.size, 1)
|
|
assert.equal([...env.listeners.values()].reduce((count, set) => count + set.size, 0), 5)
|
|
env.flushTimers()
|
|
assert.equal(env.proxies.length, 5)
|
|
env.emitVideo({ type: 'play' })
|
|
await env.flush()
|
|
assert.equal(env.frames.size, 1)
|
|
})
|
|
|
|
test(`Canvas ${implementation.name}: historical late attachment to a destroyed host still registers and draws`, async () => {
|
|
const env = await canvasEnvironment(implementation)
|
|
env.art.destroy()
|
|
const result = env.factory()(env.art)
|
|
assert.equal(result, env.canvas)
|
|
env.flushTimers()
|
|
env.emitVideo({ type: 'play' })
|
|
await env.flush()
|
|
assert.equal(env.proxies.length, 5)
|
|
assert.equal(env.draws.length, 1)
|
|
assert.equal(env.frames.size, 1)
|
|
})
|
|
|
|
test(`Canvas ${implementation.name}: historical setup subscription failure leaves deferred work and the partial host registration`, async () => {
|
|
const env = await canvasEnvironment(implementation)
|
|
const on = env.art.on
|
|
const failure = new Error('subscription failed')
|
|
env.art.on = (...args) => {
|
|
on(...args)
|
|
throw failure
|
|
}
|
|
assert.throws(() => env.factory()(env.art), error => error === failure)
|
|
assert.equal(env.timers.size, 1)
|
|
assert.equal([...env.listeners.values()].reduce((count, set) => count + set.size, 0), 1)
|
|
env.flushTimers()
|
|
assert.equal(env.proxies.length, 5)
|
|
})
|
|
|
|
test(`Canvas ${implementation.name}: historical zero media dimensions reach unguarded resize and drawing`, async () => {
|
|
const env = await canvasEnvironment(implementation)
|
|
env.video.videoWidth = 0
|
|
env.video.videoHeight = 0
|
|
env.factory()(env.art)
|
|
env.art.emit('resize')
|
|
await env.flush()
|
|
assert(Number.isNaN(env.canvas.height), 'This controlled element does not apply native unsigned dimension coercion')
|
|
assert.equal(env.canvas.style.padding, 'NaNpx 0px')
|
|
assert.equal(env.draws.length, 1)
|
|
})
|
|
|
|
test(`Canvas ${implementation.name}: historical missing 2D context repeats a TypeError path with no capability guard`, async () => {
|
|
const env = await canvasEnvironment(implementation, { noContext: true })
|
|
env.factory()(env.art)
|
|
env.art.emit('video:play')
|
|
await env.flush()
|
|
assert.equal(env.emitted.at(-1).name, 'artplayerProxyCanvas:error')
|
|
assert.equal(env.emitted.at(-1).args[0].name, 'TypeError')
|
|
assert.equal(env.frames.size, 1)
|
|
})
|
|
}
|