mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-09 03:46:16 -08:00
103 lines
6.3 KiB
JavaScript
103 lines
6.3 KiB
JavaScript
import { expect, test } from './fixtures.js'
|
|
|
|
for (const core of ['published', 'candidate']) {
|
|
test(`${core}: real playback, pause, seek, switch and destroy`, async ({ page, request, diagnostics, browserName }, testInfo) => {
|
|
const caseId = encodeURIComponent(testInfo.testId)
|
|
await page.goto(`/test/player.html?core=${core}`)
|
|
await page.evaluate(url => window.createPlayer(url, true), `/test/pattern.mp4?case=${caseId}`)
|
|
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
|
|
expect(await page.locator('video').evaluate(video => video.duration)).toBeCloseTo(8, 1)
|
|
await expect(page.locator('.art-chapter')).toHaveCount(1)
|
|
await page.locator('#play').click()
|
|
await expect.poll(() => page.locator('video').evaluate(video => video.currentTime)).toBeGreaterThan(0.25)
|
|
// Read decoded frame colors, independent of WebKit Windows' layout-sized videoWidth.
|
|
const pixels = await page.locator('video').evaluate((video) => {
|
|
const canvas = document.createElement('canvas')
|
|
canvas.width = 80
|
|
canvas.height = 45
|
|
const ctx = canvas.getContext('2d')
|
|
ctx.drawImage(video, 0, 0, 80, 45)
|
|
return [30, 45, 75].map(x => [...ctx.getImageData(x, 2, 1, 1).data])
|
|
})
|
|
expect(pixels[0][0]).toBeGreaterThan(180)
|
|
expect(pixels[0][1]).toBeGreaterThan(180)
|
|
expect(pixels[0][2]).toBeLessThan(80)
|
|
expect(pixels[1][0]).toBeLessThan(80)
|
|
expect(pixels[1][2]).toBeGreaterThan(180)
|
|
expect(pixels[2][1]).toBeGreaterThan(180)
|
|
expect(pixels[2][2]).toBeGreaterThan(180)
|
|
const mediaRequests = await (await request.get(`/test/requests.json?case=${caseId}`)).json()
|
|
expect(mediaRequests.some(item => item.path === '/test/pattern.mp4')).toBe(true)
|
|
await testInfo.attach('decoded-frame', { body: JSON.stringify({ pixels, mediaRequests }), contentType: 'application/json' })
|
|
await page.locator('#pause').click()
|
|
await expect.poll(() => page.locator('video').evaluate(video => video.paused)).toBe(true)
|
|
const pausedTime = await page.locator('video').evaluate(video => video.currentTime)
|
|
// A bounded observation interval checks that pause stops the media clock.
|
|
await page.waitForTimeout(200)
|
|
expect(await page.locator('video').evaluate(video => video.currentTime)).toBeCloseTo(pausedTime, 2)
|
|
await page.evaluate(() => {
|
|
window.art.seek = 3
|
|
})
|
|
await expect.poll(() => page.evaluate(() => window.events.includes('video:seeked'))).toBe(true)
|
|
expect(await page.locator('video').evaluate(video => video.currentTime)).toBeCloseTo(3, 1)
|
|
// Reuse a real docs sample, independently of the short synthetic fixture.
|
|
await page.evaluate(url => window.art.switchUrl(url), `/assets/sample/video.mp4?case=${caseId}`)
|
|
await expect.poll(() => page.locator('video').evaluate(video => video.duration)).toBeGreaterThan(90)
|
|
await page.locator('#play').click()
|
|
await expect.poll(() => page.locator('video').evaluate(video => video.currentTime)).toBeGreaterThan(0.25)
|
|
const events = await page.evaluate(() => window.events)
|
|
expect(events).toContain('video:loadedmetadata')
|
|
expect(events).toContain('ready')
|
|
expect(events.indexOf('video:loadedmetadata')).toBeLessThan(events.indexOf('ready'))
|
|
expect(events).toContain('video:playing')
|
|
expect(events).not.toContain('video:error')
|
|
await page.evaluate(() => window.art.destroy())
|
|
await expect(page.locator('.player')).toBeEmpty()
|
|
expect(await page.evaluate(() => window.Artplayer.instances.length)).toBe(0)
|
|
expect(diagnostics.consoleErrors).toEqual([])
|
|
// Engines cancel media range requests as buffering, seeking and source ownership change.
|
|
// Only exact cancellations of this test's two media URLs are expected; retain all in evidence.
|
|
const unexpectedRequests = diagnostics.failedRequests.filter((item) => {
|
|
const url = new URL(item.url)
|
|
const expectedCancellation = (browserName === 'chromium' && item.failure?.errorText === 'net::ERR_ABORTED')
|
|
|| (browserName === 'firefox' && item.failure?.errorText === 'NS_ERROR_PARSED_DATA_CACHED')
|
|
return !(item.resourceType === 'media' && expectedCancellation
|
|
&& url.origin === new URL(page.url()).origin
|
|
&& url.searchParams.get('case') === testInfo.testId
|
|
&& ['/test/pattern.mp4', '/assets/sample/video.mp4'].includes(url.pathname))
|
|
})
|
|
expect(unexpectedRequests).toEqual([])
|
|
})
|
|
}
|
|
|
|
test('controlled HTTP media failure emits a real video error', async ({ page, request }, testInfo) => {
|
|
const caseId = encodeURIComponent(testInfo.testId)
|
|
await page.goto('/test/player.html')
|
|
await page.evaluate(url => window.createPlayer(url), `/test/fail.mp4?case=${caseId}`)
|
|
await expect.poll(() => page.evaluate(() => window.events.includes('video:error'))).toBe(true)
|
|
const requests = await (await request.get(`/test/requests.json?case=${caseId}`)).json()
|
|
expect(requests.some(item => item.path === '/test/fail.mp4')).toBe(true)
|
|
expect((await request.get('/test/fail.mp4')).status()).toBe(503)
|
|
expect(await page.locator('video').evaluate(video => video.error.code)).toBeGreaterThan(0)
|
|
await page.evaluate(() => window.art.destroy())
|
|
await expect(page.locator('.player')).toBeEmpty()
|
|
})
|
|
|
|
test('Range, media identity and docs candidate routes', async ({ request }) => {
|
|
const manifest = await (await request.get('/test/manifest.json')).json()
|
|
const candidate = await request.get('/candidate/artplayer.js')
|
|
const alias = await request.get('/uncompiled/artplayer/index.js')
|
|
expect(await alias.body()).toEqual(await candidate.body())
|
|
expect(manifest.resources['/uncompiled/artplayer/index.js'].aliasOf).toBe('/candidate/artplayer.js')
|
|
const full = await (await request.get('/test/pattern.mp4')).body()
|
|
for (const [range, start, end] of [['bytes=0-31', 0, 32], ['bytes=-16', full.length - 16, full.length], ['bytes=32-', 32, full.length]]) {
|
|
const response = await request.get('/test/pattern.mp4', { headers: { Range: range } })
|
|
expect(response.status()).toBe(206)
|
|
expect(await response.body()).toEqual(full.subarray(start, end))
|
|
}
|
|
expect((await request.get('/test/pattern.mp4', { headers: { Range: 'bytes=999999999-' } })).status()).toBe(416)
|
|
expect((await request.get('/compiled/missing.js')).status()).toBe(404)
|
|
expect((await request.get('/mobile.html')).status()).toBe(200)
|
|
expect((await request.get('/?libs=./uncompiled/artplayer-plugin-chapter/index.js&example=chapter')).status()).toBe(200)
|
|
})
|