test(chapter): [PKG-CHAPTER-05] correlate restart event and observation timing

This commit is contained in:
Harvey Zhao committed 2026-09-14 03:36:06 +08:00
1 parent 814bdc11db
commit e93d3cbb67
8 files changed
+2880 -6

No files matched your search

+8
View File
@@ -217,3 +217,11 @@ hashes identify the actual selected ASS, not the white baseline. Screenshots are
attached at named phases; errors from individual polling attempts are retained.
The fixture's automatic failure screenshot runs after cleanup and may show an
empty player; use the named pre-cleanup screenshots for visual claims.
Chapter quality combinations attach `chapter-restart-observations`: completed
read calls include host wall time/elapsed time and browser monotonic time/origin.
Public/native chapter events also carry browser timestamps. Preserve those clocks
when diagnosing a timeout: the historical failure includes a single ~10-second
evaluate returning an already-recorded restart, not seven seconds of fast empty
reads. These observations do not expand the timeout or waive the open timing and
physical-device acceptance in PKG-CHAPTER-05.
+26 -3
View File
@@ -26,9 +26,9 @@ async function openChapter(page, core, chapter) {
] })],
})
for (const name of ['restart', 'seek', 'fullscreen', 'fullscreenWeb'])
window.art.on(name, value => window.chapterEvents.push({ name, value }))
window.art.on(name, value => window.chapterEvents.push({ name, value, at: performance.now() }))
for (const name of ['loadedmetadata', 'loadeddata', 'canplay', 'seeking', 'seeked', 'playing', 'pause', 'error'])
window.art.video.addEventListener(name, () => window.chapterMediaEvents.push({ name, time: window.art.currentTime, source: window.art.video.currentSrc, paused: window.art.video.paused, readyState: window.art.video.readyState }))
window.art.video.addEventListener(name, () => window.chapterMediaEvents.push({ name, at: performance.now(), time: window.art.currentTime, source: window.art.video.currentSrc, paused: window.art.video.paused, readyState: window.art.video.readyState }))
document.querySelector('#play').onclick = () => window.art.play()
document.querySelector('#pause').onclick = () => window.art.pause()
})
@@ -62,8 +62,30 @@ async function hoverChapter(page, percentage, text) {
return { box, x, layout }
}
async function waitForChapterRestart(page, testInfo) {
const calls = []
try {
await expect.poll(async () => {
const started = Date.now()
const state = await page.evaluate(() => ({
at: performance.now(),
timeOrigin: performance.timeOrigin,
events: window.chapterEvents.filter(event => event.name === 'restart').map(event => event.value),
}))
calls.push({ started, elapsed: Date.now() - started, ...state })
return state.events
}).toEqual(['/test/pattern.mp4?quality=A'])
}
finally {
await testInfo.attach('chapter-restart-observations', { contentType: 'application/json', body: JSON.stringify(calls) })
}
}
test.afterEach(async ({ page }, testInfo) => {
const state = await page.evaluate(() => ({
at: performance.now(),
timeOrigin: performance.timeOrigin,
qualitySelectedAt: window.chapterQualitySelectedAt,
events: window.chapterEvents,
nativeEvents: window.chapterMediaEvents,
time: window.art?.currentTime,
@@ -121,9 +143,10 @@ for (const core of ['published', 'candidate']) {
expect(await page.locator('.art-chapter .art-progress-played').evaluateAll(nodes => [nodes[0].style.width, nodes[2].style.width])).toEqual(['100%', '0px'])
await page.locator('.art-control-quality').hover()
await expect(page.locator('.art-control-quality .art-selector-list')).toHaveCSS('opacity', '1')
await page.evaluate(() => window.chapterQualitySelectedAt = performance.now())
await page.locator('.art-control-quality .art-selector-item').filter({ hasText: 'Quality A' }).click()
await expect.poll(() => page.evaluate(() => window.art.url.includes('quality=A'))).toBe(true)
await expect.poll(() => page.evaluate(() => window.chapterEvents.filter(event => event.name === 'restart').map(event => event.value))).toEqual(['/test/pattern.mp4?quality=A'])
await waitForChapterRestart(page, testInfo)
await expect.poll(() => page.evaluate(() => !window.art.video.seeking && window.art.video.readyState >= 2)).toBe(true)
const restoredTime = await page.evaluate(() => window.art.currentTime)
const historicalReset = core === 'published' && browserName === 'webkit'