mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-12 15:26:16 -08:00
test(canvas): [PKG-CANVAS-01] freeze published proxy and media adapter contracts
This commit is contained in:
1 parent
20f062bc90
commit
5e26d2298d
17 files changed
+814
-7
No files matched your search
@@ -0,0 +1,49 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { archiveFiles, ensureArchive, hash, readMember, refactorDir } from './releases.mjs'
|
||||
|
||||
export async function verifyCanvasContract() {
|
||||
const baseline = JSON.parse(fs.readFileSync(path.join(refactorDir, 'baselines/canvas-release.json'), 'utf8'))
|
||||
const archives = new Map()
|
||||
const coreSources = new Map()
|
||||
for (const release of [baseline.release, ...baseline.previous]) {
|
||||
const archive = await ensureArchive(release)
|
||||
assert.deepEqual(archiveFiles(archive), Object.keys(release.files).sort())
|
||||
for (const [file, expected] of Object.entries(release.files))
|
||||
assert.equal(hash(readMember(archive, file)), expected, `${release.version}/${file}`)
|
||||
assert.deepEqual(JSON.parse(readMember(archive, 'package/package.json')), release.manifest)
|
||||
for (const field of ['main', 'module', 'types', 'legacy']) {
|
||||
const entry = release.manifest[field]
|
||||
if (entry)
|
||||
assert(Object.hasOwn(release.files, `package/${entry.replace(/^\.\//, '')}`))
|
||||
}
|
||||
if (release.historicalCore) {
|
||||
const core = release.historicalCore
|
||||
const source = execFileSync('git', ['show', `${core.commit}:${core.file}`], { encoding: 'utf8' }).replaceAll('\r\n', '\n')
|
||||
assert.equal(hash(source), core.sha256LF)
|
||||
assert.equal(JSON.parse(source).version, core.version)
|
||||
for (const [file, expected] of Object.entries(core.proxySources || {})) {
|
||||
const content = execFileSync('git', ['show', `${core.commit}:${file}`], { encoding: 'utf8' }).replaceAll('\r\n', '\n')
|
||||
assert.equal(hash(content), expected)
|
||||
coreSources.set(`${release.version}:${file}`, content)
|
||||
}
|
||||
}
|
||||
archives.set(release.version, archive)
|
||||
}
|
||||
const sources = new Map()
|
||||
for (const [file, expected] of Object.entries(baseline.source)) {
|
||||
const source = execFileSync('git', ['show', `${baseline.sourceCommit}:${file}`], { encoding: 'utf8' }).replaceAll('\r\n', '\n')
|
||||
assert.equal(hash(source), expected, `Canvas frozen source: ${file}`)
|
||||
sources.set(file, source)
|
||||
}
|
||||
return { baseline, archives, sources, coreSources }
|
||||
}
|
||||
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
const { baseline } = await verifyCanvasContract()
|
||||
console.log(`Canvas proxy verified: ${[baseline.release, ...baseline.previous].map(release => release.version).join(', ')}; 12 members and frozen Git inputs`)
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
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)
|
||||
})
|
||||
}
|
||||
@@ -44,7 +44,7 @@ function fixture(t) {
|
||||
test('Actual contract model assigns all 264 family rows and separates exact published versions from workspace snapshots', () => {
|
||||
assert.equal(actual.policy.packages.length, 22)
|
||||
assert.equal(actual.families.length, 12)
|
||||
assert.equal(actual.policy.versions.filter(version => version.kind === 'published-baseline').length, 9)
|
||||
assert.equal(actual.policy.versions.filter(version => version.kind === 'published-baseline').length, 11)
|
||||
assert.equal(actual.policy.versions.filter(version => version.kind === 'workspace-baseline').length, 22)
|
||||
assert.equal(actual.policy.cases.length, 10)
|
||||
const markdown = renderContracts(actual)
|
||||
|
||||
Reference in new issue
Block a user