mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-19 03:11:20 -08:00
test(multiple-subtitles): [PKG-MULTI-SUB-09] isolate old-core source seeking from caption loss
This commit is contained in:
1 parent
efed847cdf
commit
31f9024bff
11 files changed
+8419
-18
No files matched your search
+10
-1
@@ -221,7 +221,8 @@ Explicit single-active-cue probes record historical and candidate overlap defect
|
||||
their passing assertions do not mean simultaneous captions are compatible.
|
||||
Candidate paused Firefox offsets are corrected by CORE-SUBTITLE-OFFSET-01. Old
|
||||
Firefox hosts retain explicit defect observations; their behavior is not silently
|
||||
counted as correct. WebKit source-switch failures remain under investigation.
|
||||
counted as correct. Task09 classified the apparent WebKit source-switch caption
|
||||
loss as an old-host seek landing near zero, reproducible without any plugin.
|
||||
See `refactor/changes/2026-09-14-PKG-MULTI-SUB-05-combinations.md` before changing
|
||||
expectations. Reports include native active cues and rendered text. The new frozen
|
||||
5.1.2 core route verifies archive integrity and member hashes before serving.
|
||||
@@ -235,3 +236,11 @@ without creating an Artplayer instance to compare native timing edits, cue
|
||||
reinsertion and mode toggling. It records actual time within the required interval,
|
||||
not a fabricated millisecond-exact seek landing. These diagnostic observations
|
||||
do not claim every native engine strategy is correct.
|
||||
|
||||
`multiple-subtitles-switch.spec.js` records source Promise settlement, native
|
||||
currentTime writes/events and actual seek landing with no plugin, candidate and
|
||||
published1.2.0. Old first-seek misses are retained as observations; every candidate
|
||||
first seek must land. The caption combination fixture separately waits for native
|
||||
source seeking to finish and for fullscreen-enter notification before testing
|
||||
subsequent commands. It retains exact caption and enter/exit order assertions;
|
||||
do not add delays or plugin retries to cover old core readiness behavior.
|
||||
@@ -84,6 +84,7 @@ for (const core of cores.filter(core => !['published-5.1.2', 'published-5.1.7'].
|
||||
})
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => (document.fullscreenElement || document.webkitFullscreenElement) === window.art.template.$player)).toBe(true)
|
||||
await expect.poll(() => page.evaluate(() => window.fullscreenEvents)).toEqual([true])
|
||||
await expect(page.locator('.art-subtitle-a')).toBeVisible()
|
||||
expect(await page.evaluate(() => (document.fullscreenElement || document.webkitFullscreenElement).contains(window.art.template.$subtitle))).toBe(true)
|
||||
await page.evaluate(() => {
|
||||
@@ -94,6 +95,9 @@ for (const core of cores.filter(core => !['published-5.1.2', 'published-5.1.7'].
|
||||
await expect(page.locator('.art-subtitle-a')).toHaveCount(0)
|
||||
await expect(page.locator('.art-subtitle-b')).toHaveText('Translation')
|
||||
await page.evaluate(() => window.art.switchUrl('/test/pattern.mp4?subtitles-combination'))
|
||||
// Old core promises can settle during their native restoration seek.
|
||||
// The immediate-seek path is retained separately in multiple-subtitles-switch.spec.js.
|
||||
await expect.poll(() => page.evaluate(() => window.art.video.seeking)).toBe(false)
|
||||
await seek(page, 1.5)
|
||||
await expect(page.locator('.art-subtitle-b')).toHaveText('Translation')
|
||||
await page.evaluate(() => window.art.plugins.multipleSubtitles.reset())
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import { hash } from '../../refactor/scripts/releases.mjs'
|
||||
import { multipleSubtitlesCandidate, multipleSubtitlesHistorical } from '../helpers/multiple-subtitles.js'
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
const candidate = await multipleSubtitlesCandidate()
|
||||
const published = (await multipleSubtitlesHistorical()).find(item => item.name === 'published-1.2.0-main')
|
||||
const variants = [null, candidate, published]
|
||||
|
||||
for (const core of ['published-5.3.0', 'published', 'candidate']) {
|
||||
for (const implementation of variants) {
|
||||
test(`Source seek ordering / ${core} / ${implementation?.name || 'no-plugin'}`, async ({ page, browser }, testInfo) => {
|
||||
await page.route('**/test/switch-caption.vtt', route => route.fulfill({ contentType: 'text/vtt', body: 'WEBVTT\n\n00:01.000 --> 00:03.000\nCaption\n' }))
|
||||
await page.goto(`/test/player.html?core=${core}`)
|
||||
if (implementation)
|
||||
await page.addScriptTag({ content: `(() => { const module = { exports: {} }; const exports = module.exports; ${implementation.code}; window.multipleFactory = module.exports.default || module.exports; })();` })
|
||||
await page.evaluate(() => {
|
||||
window.createPlayer('/test/pattern.mp4')
|
||||
const video = window.art.video
|
||||
window.switchTrace = []
|
||||
window.switchState = () => ({ time: video.currentTime, seeking: video.seeking, duration: video.duration, readyState: video.readyState, paused: video.paused, source: video.currentSrc, active: Array.from(window.art.template.$track.track.activeCues || [], cue => cue.text), displayed: window.art.template.$subtitle.textContent })
|
||||
const record = (event, value) => window.switchTrace.push({ event, value, ...window.switchState() })
|
||||
const descriptor = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'currentTime')
|
||||
Object.defineProperty(video, 'currentTime', {
|
||||
configurable: true,
|
||||
get() { return descriptor.get.call(this) },
|
||||
set(value) {
|
||||
record('write-currentTime', value)
|
||||
descriptor.set.call(this, value)
|
||||
},
|
||||
})
|
||||
for (const event of ['loadedmetadata', 'loadeddata', 'canplay', 'seeking', 'seeked', 'emptied', 'pause'])
|
||||
video.addEventListener(event, () => record(event))
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
|
||||
if (implementation) {
|
||||
await page.evaluate(async () => {
|
||||
await window.art.plugins.add(window.multipleFactory({ subtitles: [{ name: 'caption', url: '/test/switch-caption.vtt' }] }))
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.art.template.$track.track.cues?.length)).toBe(1)
|
||||
}
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.1)
|
||||
await page.locator('#pause').click()
|
||||
await page.evaluate(() => {
|
||||
document.querySelector('#play').onclick = () => {
|
||||
window.art.fullscreen = true
|
||||
}
|
||||
})
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => Boolean(document.fullscreenElement || document.webkitFullscreenElement))).toBe(true)
|
||||
await page.evaluate(() => {
|
||||
window.art.fullscreen = false
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => Boolean(document.fullscreenElement || document.webkitFullscreenElement))).toBe(false)
|
||||
const outcomes = []
|
||||
for (let round = 0; round < 3; round++) {
|
||||
await page.evaluate(round => window.art.switchUrl(`/test/pattern.mp4?seek-order=${round}`), round)
|
||||
const settled = await page.evaluate(() => window.switchState())
|
||||
await page.evaluate(() => new Promise((resolve) => {
|
||||
window.art.once('video:seeked', resolve)
|
||||
window.art.seek = 1.5
|
||||
}))
|
||||
await expect.poll(() => page.evaluate(() => window.art.video.seeking)).toBe(false)
|
||||
const firstSeek = await page.evaluate(() => window.switchState())
|
||||
const landed = Math.abs(firstSeek.time - 1.5) < 0.05
|
||||
if (!landed) {
|
||||
await page.evaluate(() => new Promise((resolve) => {
|
||||
window.art.once('video:seeked', resolve)
|
||||
window.art.seek = 1.5
|
||||
}))
|
||||
await expect.poll(() => page.evaluate(() => window.art.video.seeking)).toBe(false)
|
||||
}
|
||||
await expect.poll(() => page.evaluate(() => Math.abs(window.art.currentTime - 1.5))).toBeLessThan(0.05)
|
||||
if (implementation)
|
||||
await expect(page.locator('.art-subtitle-caption')).toHaveText('Caption')
|
||||
outcomes.push({ round, settled, firstSeek, landed, afterSettledSeek: await page.evaluate(() => window.switchState()) })
|
||||
}
|
||||
await testInfo.attach('source-seek-order', { contentType: 'application/json', body: JSON.stringify({ core, plugin: implementation?.name || 'none', pluginSha256: implementation ? hash(implementation.code) : null, browser: browser.version(), outcomes, trace: await page.evaluate(() => window.switchTrace), scope: 'Three real source changes with immediate versus settled seek; old misses are recorded, candidate must honor every first seek' }) })
|
||||
if (core === 'candidate')
|
||||
expect(outcomes.every(item => item.landed)).toBe(true)
|
||||
await page.evaluate(() => window.art.destroy())
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user