Files
ArtPlayer/refactor/scripts/canvas-contract.test.mjs

155 lines
7.2 KiB
JavaScript

import assert from 'node:assert/strict'
import fs from 'node:fs'
// eslint-disable-next-line test/no-import-node-test -- Node baseline runner.
import test from 'node:test'
import vm from 'node:vm'
import { canvasEnvironment, canvasHistorical } from '../../test/helpers/canvas.js'
import { verifyCanvasContract } from './canvas-contract.mjs'
import { ensureArchive, hash, readMember } from './releases.mjs'
const contract = await verifyCanvasContract()
test('Canvas Git-associated core 5.1.7 has proxy support that the actual npm 5.1.7 declaration lacks', async () => {
const release = JSON.parse(fs.readFileSync('refactor/baselines/vast-core.json')).release
assert.equal(release.version, '5.1.7')
const file = 'package/types/option.d.ts'
const declaration = readMember(await ensureArchive(release), file)
assert.equal(hash(declaration), release.files[file])
assert.doesNotMatch(declaration.toString(), /proxy\??\s*:/)
assert.match(contract.coreSources.get('1.0.0:packages/artplayer/src/index.js'), /proxy:\s*undefined/)
assert.match(contract.coreSources.get('1.0.0:packages/artplayer/src/template.js'), /option\.proxy\.call\(this\.art, this\.art\)/)
})
test('Canvas actual archives and frozen workspace retain exact files, versions and Git associations', () => {
assert.equal(contract.baseline.release.version, '1.1.0')
assert.equal(contract.baseline.previous[0].version, '1.0.0')
assert.equal(contract.baseline.release.historicalCore.version, '5.3.1')
assert.equal(contract.baseline.previous[0].historicalCore.version, '5.1.7')
assert.equal([...contract.archives].length, 2)
assert.equal([...contract.sources].length, 6)
})
test('Canvas published CJS namespaces, callable exports, script globals and actual ESM stay distinct', async () => {
for (const release of [contract.baseline.release, ...contract.baseline.previous]) {
for (const field of ['main', 'legacy']) {
const source = readMember(contract.archives.get(release.version), `package/${release.manifest[field].replace(/^\.\//, '')}`).toString()
const module = { exports: {} }
vm.runInNewContext(source, { module, exports: module.exports, window: {} })
assert.equal(typeof module.exports, release.version === '1.0.0' ? 'object' : 'function')
assert.equal(typeof (module.exports.default || module.exports)(), 'function')
const global = { window: {} }
vm.runInNewContext(source, global)
assert.equal(typeof (global.artplayerProxyCanvas || global.window.artplayerProxyCanvas), 'function')
}
}
const source = readMember(contract.archives.get('1.1.0'), 'package/dist/artplayer-proxy-canvas.mjs').toString()
const namespace = await import(`data:text/javascript,${encodeURIComponent(source)}`)
assert.deepEqual(Object.keys(namespace), ['default'])
assert.equal(typeof namespace.default(), 'function')
})
test('Canvas published declarations preserve callback/result shape and expose the historical optional-argument change', () => {
const old = readMember(contract.archives.get('1.0.0'), 'package/types/artplayer-proxy-canvas.d.ts').toString()
const current = readMember(contract.archives.get('1.1.0'), 'package/types/artplayer-proxy-canvas.d.ts').toString()
assert.match(old, /export = artplayerProxyCanvas/)
assert.match(old, /\(option: Option\)/)
assert.match(current, /export default artplayerProxyCanvas/)
assert.match(current, /\(option\?: Option\)/)
for (const source of [old, current]) {
assert.match(source, /ctx: CanvasRenderingContext2D, video: HTMLVideoElement/)
assert.match(source, /type Result = HTMLCanvasElement/)
}
})
for (const implementation of await canvasHistorical()) {
test(`Canvas ${implementation.name}: bitmap closes before the public callback and draw event`, async () => {
const sequence = []
const bitmap = { close() {
sequence.push('close')
} }
const env = await canvasEnvironment(implementation, {
createImageBitmap(video) {
assert.equal(video, env.video)
sequence.push('bitmap')
return Promise.resolve(bitmap)
},
onDraw() { sequence.push('draw') },
})
env.factory(() => sequence.push('callback'))(env.art)
env.art.on('artplayerProxyCanvas:draw', () => sequence.push('event'))
env.art.emit('resize')
await env.flush()
assert.equal(env.draws[0][0], bitmap)
assert.deepEqual(sequence, ['bitmap', 'draw', 'close', 'callback', 'event'])
assert.equal(env.frames.size, 0)
})
test(`Canvas ${implementation.name}: synchronous canvas return, own methods and live bound media forwarding`, async () => {
const env = await canvasEnvironment(implementation)
const result = env.factory()(env.art)
assert.equal(result, env.canvas)
assert.equal(result.title, 'canvas-title')
result.src = 'sample.mp4'
result.currentTime = 3
assert.equal(env.video.src, 'sample.mp4')
assert.equal(env.video.currentTime, 3)
env.video.videoWidth = 999
assert.equal(result.videoWidth, 999)
const descriptor = Object.getOwnPropertyDescriptor(result, 'src')
assert.equal(descriptor.enumerable, true)
assert.equal(descriptor.configurable, true)
const play = result.play
await play('argument')
assert.equal(env.calls[0].receiver, env.video)
assert.deepEqual(env.calls[0].args, ['argument'])
const toDataURL = result.toDataURL
assert.equal(toDataURL('image/png'), 'data:canvas')
assert.equal(env.calls[1].receiver, env.canvas)
result.addEventListener('click', () => {})
assert.equal(env.calls[2].name, 'canvas-listener')
assert.equal(env.proxies.length, 0)
assert.equal(env.timers.size, 1)
env.flushTimers()
assert.equal(env.proxies.length, env.art.constructor.config.events.length)
const event = { type: 'seeked', target: env.video }
env.emitVideo(event)
assert.equal(env.emitted.at(-1).name, 'video:seeked')
assert.equal(env.emitted.at(-1).args[0], event)
})
test(`Canvas ${implementation.name}: callback precedes draw event, metadata/resize preserve aspect and autoSize rules`, async () => {
const env = await canvasEnvironment(implementation)
const sequence = []
env.factory((context, video) => {
assert.equal(context, env.context)
assert.equal(video, env.video)
sequence.push('callback')
})(env.art)
env.art.on('artplayerProxyCanvas:draw', (context, video) => {
assert.equal(context, env.context)
assert.equal(video, env.video)
sequence.push('event')
})
env.flushTimers()
env.emitVideo({ type: 'loadedmetadata' })
assert.deepEqual([env.canvas.width, env.canvas.height], [320, 180])
env.art.emit('resize')
await env.flush()
assert.deepEqual([env.canvas.width, env.canvas.height], [640, 360])
assert.equal(env.canvas.style.padding, '60px 0px')
assert.deepEqual(env.draws[0], [env.video, 0, 0, 640, 360])
assert.deepEqual(sequence, ['callback', 'event'])
env.art.option.autoSize = true
env.art.template.$player.clientWidth = 100
env.art.emit('resize')
await env.flush()
assert.equal(env.canvas.width, 640)
assert.equal(env.draws.length, 2, 'autoSize suppresses sizing, not the historical resize draw')
env.emitVideo({ type: 'play' })
await env.flush()
assert.equal(env.frames.size, 1)
env.emitVideo({ type: 'pause' })
assert.equal(env.frames.size, 0)
})
}