mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-09 03:46:16 -08:00
167 lines
6.9 KiB
JavaScript
167 lines
6.9 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
// eslint-disable-next-line test/no-import-node-test -- Use the repository Node test runner.
|
|
import { test } from 'node:test'
|
|
import { loadModules, loadPackage, loadPublishedCore } from './helpers/load.js'
|
|
|
|
const { resolveOption } = await loadModules({ resolveOption: 'packages/artplayer/src/option/resolve' })
|
|
const published = await loadPublishedCore({ navigator: { language: navigator.language, userAgent: navigator.userAgent } })
|
|
const current = {
|
|
name: 'workspace',
|
|
Artplayer: (await loadPackage('artplayer')).default,
|
|
}
|
|
function oldResolve(input, defaults) {
|
|
const merged = published.Artplayer.utils.mergeDeep(defaults, input)
|
|
merged.container = input.container
|
|
return published.Artplayer.validator(merged, published.Artplayer.scheme)
|
|
}
|
|
|
|
for (const { name, Artplayer } of [published, current]) {
|
|
test(`option defaults are fresh, keep all fields and callback shape: ${name}`, () => {
|
|
const first = Artplayer.option
|
|
const second = Artplayer.option
|
|
assert.notEqual(first, second)
|
|
for (const key of ['plugins', 'layers', 'controls', 'settings', 'quality', 'highlight', 'contextmenu', 'thumbnails', 'subtitle', 'moreVideoAttr', 'icons', 'i18n', 'customType', 'cssVar'])
|
|
assert.notEqual(first[key], second[key], key)
|
|
assert.notEqual(first.subtitle.style, second.subtitle.style)
|
|
assert.notEqual(first.subtitle.onVttLoad, second.subtitle.onVttLoad)
|
|
assert.equal(first.subtitle.onVttLoad(' text '), ' text ')
|
|
assert.equal(first.subtitle.onVttLoad.length, 1)
|
|
assert.equal(first.subtitle.onVttLoad.name, 'onVttLoad')
|
|
assert(Object.hasOwn(first, 'proxy'))
|
|
assert.equal(first.proxy, undefined)
|
|
assert.equal(first.lang, navigator.language.toLowerCase())
|
|
assert.equal(Artplayer.scheme.layers[0], Artplayer.scheme.settings[0])
|
|
assert.equal(Artplayer.scheme.layers[0], Artplayer.scheme.contextmenu[0])
|
|
})
|
|
}
|
|
|
|
test('default values, keys and nested order match the published getter', () => {
|
|
assert.equal(JSON.stringify(current.Artplayer.option), JSON.stringify(published.Artplayer.option))
|
|
assert.deepEqual(Object.keys(current.Artplayer.option), Object.keys(published.Artplayer.option))
|
|
})
|
|
|
|
test('candidate defaults are readable without navigator and keep independent option containers', () => {
|
|
const descriptor = Object.getOwnPropertyDescriptor(globalThis, 'navigator')
|
|
try {
|
|
for (const mode of ['absent', 'undefined', 'null']) {
|
|
assert(delete globalThis.navigator)
|
|
if (mode !== 'absent')
|
|
Object.defineProperty(globalThis, 'navigator', { configurable: true, value: mode === 'null' ? null : undefined })
|
|
const first = current.Artplayer.option
|
|
const second = current.Artplayer.option
|
|
assert.equal(first.lang, undefined, mode)
|
|
assert(Object.hasOwn(first, 'lang'))
|
|
assert.notEqual(first, second)
|
|
assert.notEqual(first.subtitle, second.subtitle)
|
|
assert.deepEqual(first.plugins, [])
|
|
assert.equal(first.subtitle.onVttLoad('text'), 'text')
|
|
assert.throws(() => resolveOption({ container: '#player' }, first), /option.lang/)
|
|
assert.equal(resolveOption({ container: '#player', lang: 'en' }, first).lang, 'en')
|
|
}
|
|
}
|
|
finally {
|
|
if (descriptor)
|
|
Object.defineProperty(globalThis, 'navigator', descriptor)
|
|
else
|
|
delete globalThis.navigator
|
|
}
|
|
})
|
|
|
|
test('candidate defaults read the current browser language on each access without caching it', () => {
|
|
const descriptor = Object.getOwnPropertyDescriptor(globalThis, 'navigator')
|
|
try {
|
|
let language = 'EN-us'
|
|
Object.defineProperty(globalThis, 'navigator', { configurable: true, value: {
|
|
get language() {
|
|
return language
|
|
},
|
|
} })
|
|
for (const value of ['EN-us', 'zh-CN', '']) {
|
|
language = value
|
|
assert.equal(current.Artplayer.option.lang, value.toLowerCase())
|
|
}
|
|
}
|
|
finally {
|
|
if (descriptor)
|
|
Object.defineProperty(globalThis, 'navigator', descriptor)
|
|
else
|
|
delete globalThis.navigator
|
|
}
|
|
})
|
|
|
|
for (const [name, resolve, Artplayer] of [['published', oldResolve, published.Artplayer], ['workspace', resolveOption, current.Artplayer]]) {
|
|
test(`resolved options preserve unknown fields, merge ownership and collection item identity: ${name}`, () => {
|
|
const callback = () => {}
|
|
const item = { html: 'Quality', url: 'a.mp4' }
|
|
const extension = { value: 1 }
|
|
const input = { container: '#player', url: 'video.mp4', subtitle: { style: { color: 'red' } }, thumbnails: { url: 'sprite.jpg' }, quality: [item], customType: { mp4: callback }, extension }
|
|
const defaults = Artplayer.option
|
|
const option = resolve(input, defaults)
|
|
assert.equal(option.container, input.container)
|
|
assert.notEqual(option, input)
|
|
assert.notEqual(option.subtitle, input.subtitle)
|
|
assert.notEqual(option.subtitle.style, input.subtitle.style)
|
|
assert.equal(option.subtitle.style.color, 'red')
|
|
assert.equal(option.subtitle.encoding, 'utf-8')
|
|
assert.equal(option.thumbnails.column, 10)
|
|
assert.notEqual(option.quality, input.quality)
|
|
assert.equal(option.quality[0], item)
|
|
assert.equal(option.customType.mp4, callback)
|
|
assert.equal(option.extension, extension)
|
|
assert.deepEqual(Object.keys(input.subtitle), ['style'])
|
|
assert.deepEqual(Object.keys(defaults.subtitle.style), [])
|
|
})
|
|
|
|
test(`getter reads and inherited container restoration retain their order: ${name}`, () => {
|
|
const reads = []
|
|
const input = {
|
|
get container() {
|
|
reads.push('container')
|
|
return '#player'
|
|
},
|
|
get url() {
|
|
reads.push('url')
|
|
return 'video.mp4'
|
|
},
|
|
get volume() {
|
|
reads.push('volume')
|
|
return 0.5
|
|
},
|
|
}
|
|
const option = resolve(input, Artplayer.option)
|
|
assert.deepEqual(reads, ['container', 'url', 'volume', 'container'])
|
|
assert.equal(option.volume, 0.5)
|
|
const inherited = Object.create({ container: '#inherited', url: 'ignored.mp4' })
|
|
inherited.customField = 'own'
|
|
const result = resolve(inherited, Artplayer.option)
|
|
assert.equal(result.container, '#inherited')
|
|
assert.equal(result.url, '')
|
|
assert.equal(result.customField, 'own')
|
|
})
|
|
}
|
|
|
|
test('invalid JS input keeps the published validation path, first error and thrown kind', () => {
|
|
const cases = [
|
|
{ volume: undefined },
|
|
{ volume: 'loud', muted: 'yes' },
|
|
{ subtitle: { encoding: undefined } },
|
|
{ thumbnails: { number: 'many' } },
|
|
{ plugins: [42] },
|
|
{ controls: [{ html: 'Button', position: 'middle' }] },
|
|
{ quality: [{ html: 'HD', url: 42 }] },
|
|
{ proxy: null },
|
|
{ moreVideoAttr: [] },
|
|
]
|
|
for (const patch of cases) {
|
|
const input = { container: '#player', url: 'video.mp4', ...patch }
|
|
const errors = []
|
|
for (const [resolve, Artplayer] of [[oldResolve, published.Artplayer], [resolveOption, current.Artplayer]]) {
|
|
assert.throws(() => resolve(input, Artplayer.option), (error) => {
|
|
errors.push({ name: error.name, message: error.message })
|
|
return true
|
|
})
|
|
}
|
|
assert.deepEqual(errors[0], errors[1])
|
|
}
|
|
})
|