mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-19 19:51:20 -08:00
test(vast): [PKG-VAST-02] cover SDK failures and lifecycle races
This commit is contained in:
1 parent
4f39d6f2b9
commit
d5e1feadf5
16 files changed
+1402
-12
No files matched your search
@@ -0,0 +1,60 @@
|
||||
// Controlled wrapper boundary, not an implementation of Google IMA or media playback.
|
||||
// Self-contained so the same recorder can run in Node and a real browser realm.
|
||||
export function createVastSdk(options = {}) {
|
||||
const state = { players: [], loads: 0, errors: [], ...options }
|
||||
let resolveLoad
|
||||
let rejectLoad
|
||||
const pending = new Promise((resolve, reject) => {
|
||||
resolveLoad = resolve
|
||||
rejectLoad = reject
|
||||
})
|
||||
const ima = { AdsRenderingSettings: class {}, AdsRequest: class {} }
|
||||
const sdk = {
|
||||
loadImaSdk() {
|
||||
state.loads++
|
||||
return state.deferLoad ? pending : Promise.resolve(ima)
|
||||
},
|
||||
PlayerOptions: class {},
|
||||
Player: class {
|
||||
constructor(...args) {
|
||||
if (state.constructError)
|
||||
throw state.constructError
|
||||
this.args = args
|
||||
this.requests = []
|
||||
this.listeners = new Map()
|
||||
this.destroyCalls = 0
|
||||
state.players.push(this)
|
||||
}
|
||||
|
||||
addEventListener(name, callback) {
|
||||
if (state.listenError)
|
||||
throw state.listenError
|
||||
if (!this.listeners.has(name))
|
||||
this.listeners.set(name, new Set())
|
||||
this.listeners.get(name).add(callback)
|
||||
}
|
||||
|
||||
removeEventListener(name, callback) {
|
||||
this.listeners.get(name)?.delete(callback)
|
||||
}
|
||||
|
||||
emit(name, detail) {
|
||||
for (const callback of [...(this.listeners.get(name) || [])]) callback({ type: name, detail })
|
||||
}
|
||||
|
||||
playAds(request) {
|
||||
if (state.playError)
|
||||
throw state.playError
|
||||
this.requests.push(request)
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.destroyCalls++
|
||||
if (state.destroyError)
|
||||
throw state.destroyError
|
||||
this.listeners.clear()
|
||||
}
|
||||
},
|
||||
}
|
||||
return { state, sdk, ima, resolveLoad: () => resolveLoad(ima), rejectLoad }
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import vm from 'node:vm'
|
||||
import { build, transform } from 'esbuild'
|
||||
import { readMember } from '../../refactor/scripts/releases.mjs'
|
||||
import { verifyVastContract } from '../../refactor/scripts/vast-contract.mjs'
|
||||
import { resolveSource } from './load.js'
|
||||
import { createVastSdk } from './vast-sdk.js'
|
||||
|
||||
export async function vastImplementations() {
|
||||
const contract = await verifyVastContract()
|
||||
const archive = contract.archives.get('artplayer-plugin-vast@1.0.0')
|
||||
const { outputFiles } = await build({ entryPoints: [resolveSource('packages/artplayer-plugin-vast/src/index')], bundle: true, write: false, format: 'cjs', platform: 'browser', external: ['@glomex/vast-ima-player'] })
|
||||
const frozen = async source => (await transform(source, { loader: 'js', format: 'cjs', target: 'es2020' })).code
|
||||
return [
|
||||
{ name: 'source', code: outputFiles[0].text },
|
||||
{ name: 'published-1.0.0-source', historical: true, published: true, code: await frozen(readMember(archive, 'package/src/index.js').toString()) },
|
||||
{ name: 'frozen-workspace-1.2.0', historical: true, code: await frozen(contract.sources.get('packages/artplayer-plugin-vast/src/index.js')) },
|
||||
]
|
||||
}
|
||||
|
||||
export function vastEnvironment(implementation, options = {}) {
|
||||
const fixture = createVastSdk(options)
|
||||
const module = { exports: {} }
|
||||
class FixedDate extends Date {
|
||||
static now() { return 1000 }
|
||||
}
|
||||
vm.runInNewContext(implementation.code, {
|
||||
module,
|
||||
exports: module.exports,
|
||||
window: { google: { ima: fixture.ima } },
|
||||
Date: FixedDate,
|
||||
console: { error: (...args) => fixture.state.errors.push(args) },
|
||||
require(name) {
|
||||
assert.equal(name, '@glomex/vast-ima-player', 'Only the external SDK may be substituted')
|
||||
return fixture.sdk
|
||||
},
|
||||
}, { timeout: 5000 })
|
||||
const factory = module.exports.default || module.exports
|
||||
function host() {
|
||||
const listeners = new Map()
|
||||
const calls = []
|
||||
const node = () => ({ style: {}, parentNode: null })
|
||||
const parent = {
|
||||
children: [],
|
||||
appendChild(child) {
|
||||
if (child.parentNode)
|
||||
child.parentNode.removeChild(child)
|
||||
child.parentNode = this
|
||||
this.children.push(child)
|
||||
return child
|
||||
},
|
||||
removeChild(child) {
|
||||
const index = this.children.indexOf(child)
|
||||
assert(index !== -1, 'removeChild requires an owned node')
|
||||
this.children.splice(index, 1)
|
||||
child.parentNode = null
|
||||
return child
|
||||
},
|
||||
}
|
||||
const art = {
|
||||
template: { $player: parent, $video: {} },
|
||||
constructor: { utils: { createElement: node, setStyles: (target, styles) => Object.assign(target.style, styles), append: (target, child) => target.appendChild(child) } },
|
||||
isDestroy: false,
|
||||
on(name, callback) {
|
||||
if (!listeners.has(name))
|
||||
listeners.set(name, new Set())
|
||||
listeners.get(name).add(callback)
|
||||
return art
|
||||
},
|
||||
off(name, callback) {
|
||||
listeners.get(name)?.delete(callback)
|
||||
return art
|
||||
},
|
||||
once(name, callback) {
|
||||
const once = (...args) => {
|
||||
art.off(name, once)
|
||||
callback(...args)
|
||||
}
|
||||
return art.on(name, once)
|
||||
},
|
||||
emit(name, ...args) {
|
||||
calls.push([name, ...args])
|
||||
for (const callback of [...(listeners.get(name) || [])]) callback(...args)
|
||||
return art
|
||||
},
|
||||
destroy() {
|
||||
if (art.isDestroy)
|
||||
return
|
||||
art.isDestroy = true
|
||||
art.emit('destroy')
|
||||
listeners.clear()
|
||||
},
|
||||
}
|
||||
return { art, parent, calls, listeners }
|
||||
}
|
||||
return { ...fixture, factory, host }
|
||||
}
|
||||
|
||||
export function deferred() {
|
||||
let resolve
|
||||
let reject
|
||||
const promise = new Promise((yes, no) => {
|
||||
resolve = yes
|
||||
reject = no
|
||||
})
|
||||
return { promise, resolve, reject }
|
||||
}
|
||||
Reference in new issue
Block a user