mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-09 03:46:16 -08:00
191 lines
8.0 KiB
JavaScript
191 lines
8.0 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
// eslint-disable-next-line test/no-import-node-test -- Real source compiled into an isolated window for ownership and protocol boundaries.
|
|
import test from 'node:test'
|
|
import { iframeCandidate, iframeEnvironment } from './helpers/iframe.js'
|
|
|
|
const implementation = await iframeCandidate()
|
|
const flush = () => new Promise(resolve => setImmediate(resolve))
|
|
function setup() {
|
|
const env = iframeEnvironment(implementation)
|
|
const frame = new env.Frame()
|
|
const instance = new env.Factory({ iframe: frame, url: '/first' })
|
|
return { ...env, frame, instance }
|
|
}
|
|
function observe(promise) {
|
|
const state = { status: 'pending' }
|
|
promise.then(value => Object.assign(state, { status: 'resolved', value }), error => Object.assign(state, { status: 'rejected', error: error.message }))
|
|
return state
|
|
}
|
|
|
|
test('Iframe source setter failure releases its bound listener and preserves the exact setup error', () => {
|
|
const env = iframeEnvironment(implementation)
|
|
const frame = new env.Frame()
|
|
const failure = new Error('src assignment rejected')
|
|
Object.defineProperty(frame, 'src', {
|
|
get() { return '/' },
|
|
set() {
|
|
throw failure
|
|
},
|
|
})
|
|
assert.throws(() => new env.Factory({ iframe: frame, url: '/' }), error => error === failure)
|
|
assert.equal(env.handlers.get('message').size, 0)
|
|
assert.equal(env.timers.size, 0)
|
|
})
|
|
|
|
test('Iframe partial listener acquisition failure releases ownership and keeps the first error', () => {
|
|
const env = iframeEnvironment(implementation)
|
|
const add = env.box.addEventListener
|
|
const failure = new Error('registration rejected after acquiring')
|
|
env.box.addEventListener = (...args) => {
|
|
add(...args)
|
|
throw failure
|
|
}
|
|
assert.throws(() => new env.Factory({ iframe: new env.Frame(), url: '/' }), error => error === failure)
|
|
assert.equal(env.handlers.get('message').size, 0)
|
|
assert.equal(env.order.includes('src'), false)
|
|
})
|
|
|
|
test('Iframe setup keeps the source error when releasing a partially constructed instance also throws', () => {
|
|
const env = iframeEnvironment(implementation)
|
|
const frame = new env.Frame()
|
|
const failure = new Error('original source error')
|
|
Object.defineProperty(frame, 'src', {
|
|
get() { return '/' },
|
|
set() {
|
|
throw failure
|
|
},
|
|
})
|
|
const remove = env.box.removeEventListener
|
|
let removals = 0
|
|
env.box.removeEventListener = (...args) => {
|
|
removals++
|
|
remove(...args)
|
|
throw new Error('secondary release error')
|
|
}
|
|
assert.throws(() => new env.Factory({ iframe: frame, url: '/' }), error => error === failure)
|
|
assert.equal(removals, 1)
|
|
assert.equal(env.handlers.get('message').size, 0)
|
|
})
|
|
|
|
test('Iframe destroy cancels requests even if listener release throws and does not repeat release', async () => {
|
|
const env = setup()
|
|
const state = observe(env.instance.postMessage({ type: 'waiting' }))
|
|
const failure = new Error('listener release failure')
|
|
const remove = env.box.removeEventListener
|
|
let removals = 0
|
|
env.box.removeEventListener = (...args) => {
|
|
removals++
|
|
remove(...args)
|
|
throw failure
|
|
}
|
|
assert.throws(() => env.instance.destroy(), error => error === failure)
|
|
await flush()
|
|
assert.deepEqual(state, { status: 'rejected', error: 'The instance has been destroyed' })
|
|
assert.equal(env.timers.size, 0)
|
|
assert.equal(env.handlers.get('message').size, 0)
|
|
assert.doesNotThrow(() => env.instance.destroy())
|
|
assert.equal(removals, 1)
|
|
})
|
|
|
|
for (const injected of [false, true]) {
|
|
test(`Iframe failed construction cancels a reentrant ${injected ? 'sent' : 'waiting'} request`, async () => {
|
|
const env = iframeEnvironment(implementation)
|
|
const frame = new env.Frame()
|
|
const failure = new Error('source setter failed after reentry')
|
|
const receive = env.Factory.prototype.onMessage
|
|
let state
|
|
let partial
|
|
env.Factory.prototype.onMessage = function (event) {
|
|
partial = this
|
|
receive.call(this, event)
|
|
state = observe(this.postMessage({ type: 'query' }))
|
|
}
|
|
Object.defineProperty(frame, 'src', {
|
|
get() { return '/' },
|
|
set() {
|
|
env.dispatch({ type: injected ? 'inject' : 'custom' }, frame.contentWindow)
|
|
throw failure
|
|
},
|
|
})
|
|
assert.throws(() => new env.Factory({ iframe: frame, url: '/' }), error => error === failure)
|
|
await flush()
|
|
assert.deepEqual(state, { status: 'rejected', error: 'The instance has been destroyed' })
|
|
assert.equal(partial.destroyed, true)
|
|
assert.equal(env.timers.size, 0)
|
|
assert.equal(Object.keys(partial.promises).length, 0)
|
|
assert.equal(env.handlers.get('message').size, 0)
|
|
})
|
|
}
|
|
|
|
test('Iframe parent rejects foreign handshakes, responses and callbacks but accepts its selected cross-origin peer', async () => {
|
|
const env = setup()
|
|
const seen = []
|
|
env.instance.message(packet => seen.push(packet.type))
|
|
env.dispatch({ type: 'inject' }, {})
|
|
assert.equal(env.instance.injected, false)
|
|
assert.deepEqual(seen, [])
|
|
env.dispatch({ type: 'inject' }, env.frame.contentWindow, 'https://redirected.example')
|
|
const state = observe(env.instance.postMessage({ type: 'query' }))
|
|
env.dispatch({ type: 'custom-result', data: 'forged', id: 1234 }, {})
|
|
await flush()
|
|
assert.deepEqual(state, { status: 'pending' })
|
|
assert.deepEqual(seen, ['inject'])
|
|
env.dispatch({ type: 'custom-result', data: 'real', id: 1234 }, env.frame.contentWindow, 'null')
|
|
await flush()
|
|
assert.deepEqual(state, { status: 'resolved', value: 'real' })
|
|
assert.deepEqual(seen, ['inject', 'custom-result'])
|
|
env.instance.destroy()
|
|
})
|
|
|
|
test('Iframe child rejects a foreign executable command and accepts only its actual parent peer', async () => {
|
|
const env = iframeEnvironment(implementation, true)
|
|
env.box.executions = 0
|
|
const data = { type: 'commit', data: 'window.executions++; return 7', id: 17 }
|
|
await env.Factory.onMessage({ data, source: {}, origin: 'https://sibling.example' })
|
|
assert.equal(env.box.executions, 0)
|
|
assert.equal(env.sent.length, 0)
|
|
await env.Factory.onMessage({ data, source: env.box.parent, origin: 'https://parent.example' })
|
|
assert.equal(env.box.executions, 1)
|
|
assert.equal(env.sent[0].packet.data, 7)
|
|
})
|
|
|
|
for (const data of [null, undefined, false, 3, 'message', {}, { type: null }, { type: 3 }]) {
|
|
test(`Iframe receivers ignore malformed payload ${JSON.stringify(data)} without callbacks or child rejection`, async () => {
|
|
const env = setup()
|
|
let calls = 0
|
|
env.instance.message(() => calls++)
|
|
assert.doesNotThrow(() => env.dispatch(data, env.frame.contentWindow))
|
|
assert.equal(calls, 0)
|
|
assert.equal(env.instance.injected, false)
|
|
const child = iframeEnvironment(implementation, true)
|
|
await assert.doesNotReject(child.Factory.onMessage({ data, source: child.box.parent }))
|
|
assert.equal(child.sent.length, 0)
|
|
env.instance.destroy()
|
|
})
|
|
}
|
|
|
|
test('Iframe public receivers keep source-less and null-source direct invocation as a local API', async () => {
|
|
const env = setup()
|
|
env.instance.onMessage({ data: { type: 'inject' } })
|
|
const promise = env.instance.postMessage({ type: 'query' })
|
|
env.instance.onMessage({ data: { type: 'custom', data: 9, id: 1234 }, source: null })
|
|
assert.equal(await promise, 9)
|
|
const child = iframeEnvironment(implementation, true)
|
|
await child.Factory.onMessage({ data: { type: 'commit', data: 'return 8', id: 9 }, source: null })
|
|
assert.equal(child.sent[0].packet.data, 8)
|
|
env.instance.destroy()
|
|
})
|
|
|
|
test('Iframe trusted protocol retains generic message types and child commit error serialization', async () => {
|
|
const env = setup()
|
|
const seen = []
|
|
env.instance.message(packet => seen.push({ ...packet }))
|
|
env.dispatch({ type: '', data: 'custom empty type' }, env.frame.contentWindow)
|
|
assert.deepEqual(seen, [{ type: '', data: 'custom empty type' }])
|
|
const child = iframeEnvironment(implementation, true)
|
|
await assert.rejects(child.Factory.onMessage({ data: { type: 'commit', data: 3, id: 7 }, source: child.box.parent }), error => error.name === 'TypeError')
|
|
assert.equal(child.sent[0].packet.type, 'error')
|
|
assert.equal(child.sent[0].packet.id, 7)
|
|
env.instance.destroy()
|
|
})
|