mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-22 13:21:20 -08:00
test(vast): [PKG-VAST-05] checkpoint native tag requests and skip controls
This commit is contained in:
1 parent
75da50a454
commit
09459955c6
13 files changed
+1789
-9
No files matched your search
@@ -11,6 +11,7 @@ import { ensureArchive, hash, readMember } from '../../refactor/scripts/releases
|
||||
import { performanceHtml, performanceScript } from '../../scripts/performance-fixture.mjs'
|
||||
import { getEntryFile } from '../../scripts/projects.js'
|
||||
import { getGlobalName, getViteBuildConfig } from '../../scripts/utils.js'
|
||||
import { skippableVast } from './vast-fixture.mjs'
|
||||
|
||||
const workspace = fileURLToPath(new URL('../../', import.meta.url))
|
||||
|
||||
@@ -94,7 +95,8 @@ add('/test/audio-tone.m4a', fs.readFileSync(path.join(workspace, 'test/browser/m
|
||||
add('/test/thumbnail-pattern.mp4', fs.readFileSync(path.join(workspace, 'test/browser/media/thumbnail-pattern.mp4')), { kind: 'generated-media', file: 'test/browser/media/thumbnail-pattern.mp4' })
|
||||
add('/test/auto-thumbnail-timeline.mp4', fs.readFileSync(path.join(workspace, 'test/browser/media/auto-thumbnail-timeline.mp4')), { kind: 'generated-media', file: 'test/browser/media/auto-thumbnail-timeline.mp4' })
|
||||
|
||||
const mime = { '.html': 'text/html', '.js': 'text/javascript', '.mjs': 'text/javascript', '.json': 'application/json', '.css': 'text/css', '.mp4': 'video/mp4', '.m4a': 'audio/mp4', '.webm': 'video/webm', '.vtt': 'text/vtt', '.svg': 'image/svg+xml', '.png': 'image/png', '.jpg': 'image/jpeg', '.wasm': 'application/wasm', '.woff2': 'font/woff2' }
|
||||
const mime = { '.html': 'text/html', '.js': 'text/javascript', '.mjs': 'text/javascript', '.json': 'application/json', '.xml': 'application/xml', '.css': 'text/css', '.mp4': 'video/mp4', '.m4a': 'audio/mp4', '.webm': 'video/webm', '.vtt': 'text/vtt', '.svg': 'image/svg+xml', '.png': 'image/png', '.jpg': 'image/jpeg', '.wasm': 'application/wasm', '.woff2': 'font/woff2' }
|
||||
add('/test/vast-skippable.xml', Buffer.from(skippableVast(`http://127.0.0.1:${port}`)), { kind: 'local-vast-fixture', file: 'test/browser/vast-fixture.mjs' })
|
||||
add('/test/declaration-cues.vtt', fs.readFileSync(path.join(workspace, 'test/browser/media/declaration-cues.vtt')), { kind: 'test-subtitles', file: 'test/browser/media/declaration-cues.vtt' })
|
||||
add('/test/thumbnail-grid.svg', fs.readFileSync(path.join(workspace, 'test/browser/media/thumbnail-grid.svg')), { kind: 'test-thumbnail-grid', file: 'test/browser/media/thumbnail-grid.svg' })
|
||||
add('/test/legacy-safe-area.js', fs.readFileSync(path.join(workspace, 'test/helpers/legacy-safe-area.js')), { kind: 'frozen-own-source', file: 'test/helpers/legacy-safe-area.js', commit: 'ccf77c4e' })
|
||||
@@ -170,6 +172,11 @@ const server = http.createServer((req, res) => {
|
||||
return
|
||||
}
|
||||
if (files.has(url.pathname)) {
|
||||
if (url.pathname === '/test/vast-skippable.xml' && ['http://imasdk.googleapis.com', 'https://imasdk.googleapis.com'].includes(req.headers.origin)) {
|
||||
res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
|
||||
res.setHeader('Access-Control-Allow-Credentials', 'true')
|
||||
res.setHeader('Vary', 'Origin')
|
||||
}
|
||||
send(req, res, files.get(url.pathname), url.pathname)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export function skippableVast(origin) {
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<VAST version="3.0"><Ad id="artplayer-skippable"><InLine>
|
||||
<AdSystem version="1">ArtPlayer test fixture</AdSystem><AdTitle>Local skippable ad</AdTitle>
|
||||
<Impression><![CDATA[${origin}/assets/vast/nonlinear-320x50.png?tracking=impression]]></Impression>
|
||||
<Creatives><Creative><Linear skipoffset="00:00:05"><Duration>00:00:08</Duration>
|
||||
<TrackingEvents><Tracking event="skip"><![CDATA[${origin}/assets/vast/nonlinear-320x50.png?tracking=skip]]></Tracking></TrackingEvents>
|
||||
<MediaFiles><MediaFile delivery="progressive" type="video/mp4" width="320" height="180"><![CDATA[${origin}/test/pattern.mp4?skippable=1]]></MediaFile></MediaFiles>
|
||||
</Linear></Creative></Creatives></InLine></Ad></VAST>`
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import fs from 'node:fs'
|
||||
import { hash } from '../../refactor/scripts/releases.mjs'
|
||||
import { compilePackage } from '../helpers/load.js'
|
||||
import { expect, test } from './fixtures.js'
|
||||
import { skippableVast } from './vast-fixture.mjs'
|
||||
|
||||
// eslint-disable-next-line antfu/no-top-level-await -- Native SDK bundle is shared by test discovery.
|
||||
const code = await compilePackage('artplayer-plugin-vast', 'umd')
|
||||
|
||||
for (const compatibility of [undefined, 'workspace-1.2']) {
|
||||
test(`real IMA ${compatibility || 'npm-default'} requests playUrl and skips through the SDK button`, async ({ page }, testInfo) => {
|
||||
await page.goto('/test/player.html?core=candidate')
|
||||
await page.addScriptTag({ content: code })
|
||||
await page.evaluate(async (compatibility) => {
|
||||
window.createPlayer('/assets/sample/video.mp4')
|
||||
window.skipEvents = []
|
||||
await window.art.plugins.add(window.artplayerPluginVast((context) => {
|
||||
window.skipContext = context
|
||||
const player = context.init()
|
||||
for (const type of ['AdStarted', 'AdComplete', 'AdSkipped', 'AdSkippableStateChanged', 'AdContentPauseRequested', 'AdContentResumeRequested', 'AdError']) {
|
||||
player.addEventListener(type, event => window.skipEvents.push({ type, at: performance.now(), time: window.art.currentTime, paused: window.art.template.$video.paused, skipOffset: event.detail?.ad?.getSkipTimeOffset(), error: event.detail?.error?.message }))
|
||||
}
|
||||
}, compatibility ? { compatibility } : undefined))
|
||||
}, compatibility)
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.5)
|
||||
await page.evaluate(() => {
|
||||
document.querySelector('#play').onclick = () => window.skipContext.playUrl(`${location.origin}/test/vast-skippable.xml`)
|
||||
})
|
||||
const beforeAd = await page.evaluate(() => window.art.currentTime)
|
||||
const responsePromise = page.waitForResponse(response => response.url().endsWith('/test/vast-skippable.xml'), { timeout: 7000 })
|
||||
await page.locator('#play').click()
|
||||
const response = await responsePromise
|
||||
expect(response.status()).toBe(200)
|
||||
expect(response.headers()['content-type']).toBe('application/xml')
|
||||
expect(['http://imasdk.googleapis.com', 'https://imasdk.googleapis.com']).toContain(response.headers()['access-control-allow-origin'])
|
||||
const xml = await response.text()
|
||||
expect(xml).toBe(skippableVast(new URL(page.url()).origin))
|
||||
await testInfo.attach('native-vast-skip.xml', { contentType: 'application/xml', body: xml })
|
||||
await expect.poll(() => page.evaluate(() => window.skipEvents.some(event => event.type === 'AdStarted'))).toBe(true)
|
||||
await expect.poll(() => page.evaluate(() => [...document.querySelectorAll('video')].some(video => video.currentSrc.includes('/test/pattern.mp4?skippable=1') && video.videoWidth > 0 && video.currentTime > 0))).toBe(true)
|
||||
expect(await page.evaluate(() => window.art.template.$video.paused)).toBe(true)
|
||||
await testInfo.attach('native-vast-before-skip.png', { contentType: 'image/png', body: await page.screenshot() })
|
||||
await expect.poll(() => page.evaluate(() => window.skipEvents.some(event => event.type === 'AdSkippableStateChanged'))).toBe(true)
|
||||
let skipButton
|
||||
await expect.poll(async () => {
|
||||
for (const frame of page.frames()) {
|
||||
const button = frame.getByRole('button', { name: /skip ad/i })
|
||||
if (await button.count() === 1 && await button.isVisible() && await button.isEnabled()) {
|
||||
skipButton = button
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}).toBe(true)
|
||||
await testInfo.attach('native-vast-skip-button.png', { contentType: 'image/png', body: await page.screenshot() })
|
||||
await skipButton.click()
|
||||
await expect.poll(() => page.evaluate(() => window.skipEvents.filter(event => event.type === 'AdSkipped').length)).toBe(1)
|
||||
await expect.poll(() => page.evaluate(() => window.skipEvents.some(event => event.type === 'AdContentResumeRequested'))).toBe(true)
|
||||
await expect(page.locator('[id^="art-"]')).toBeHidden()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(beforeAd + 0.2)
|
||||
const state = await page.evaluate(() => ({ events: window.skipEvents, time: window.art.currentTime, paused: window.art.template.$video.paused, imaVersion: window.google.ima.VERSION }))
|
||||
expect(state.paused).toBe(false)
|
||||
expect(state.events.filter(event => event.type === 'AdError' || event.type === 'AdComplete')).toEqual([])
|
||||
await testInfo.attach('native-vast-skip', { contentType: 'application/json', body: JSON.stringify({ sourceSha256: hash(code), xmlSha256: hash(xml), mediaSha256: hash(fs.readFileSync('test/browser/media/pattern.mp4')), compatibility: compatibility || 'npm-default', beforeAd, ...state }) })
|
||||
await page.evaluate(() => window.art.destroy())
|
||||
await expect(page.locator('[id^="art-"]')).toHaveCount(0)
|
||||
})
|
||||
}
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await testInfo.attach('native-vast-skip-final', { contentType: 'application/json', body: JSON.stringify(await page.evaluate(() => ({ events: window.skipEvents, imaVersion: window.google?.ima?.VERSION, videos: [...document.querySelectorAll('video')].map(video => ({ src: video.currentSrc, time: video.currentTime, width: video.videoWidth, paused: video.paused })) }))) })
|
||||
for (const [index, frame] of page.frames().entries()) {
|
||||
if (frame !== page.mainFrame())
|
||||
await testInfo.attach(`native-vast-frame-${index}`, { contentType: 'text/plain', body: await frame.locator('body').ariaSnapshot().catch(error => String(error)) })
|
||||
}
|
||||
await page.evaluate(() => window.art?.destroy())
|
||||
})
|
||||
@@ -17,6 +17,7 @@ test.afterEach(async ({ page }, testInfo) => {
|
||||
error: window.nativeVastError,
|
||||
imaVersion: window.google?.ima?.VERSION,
|
||||
events: window.nativeVastEvents,
|
||||
mediaSamples: window.nativeVastMediaSamples,
|
||||
videos: [...document.querySelectorAll('video')].map(video => ({ src: video.currentSrc, time: video.currentTime, duration: video.duration, width: video.videoWidth, height: video.videoHeight, paused: video.paused, ended: video.ended })),
|
||||
containerConnected: window.nativeVastContext?.container?.isConnected,
|
||||
coreVersion: window.Artplayer?.version,
|
||||
@@ -41,6 +42,7 @@ for (const core of ['published-5.1.7', 'published', 'candidate']) {
|
||||
await page.evaluate(({ compatibility, historical }) => {
|
||||
window.nativeVastHistorical = historical
|
||||
window.nativeVastEvents = []
|
||||
window.nativeVastMediaSamples = []
|
||||
window.nativeVastRegistration = 'pending'
|
||||
window.createPlayer('/assets/sample/video.mp4')
|
||||
const factory = window.artplayerPluginVast.default || window.artplayerPluginVast
|
||||
@@ -81,7 +83,11 @@ for (const core of ['published-5.1.7', 'published', 'candidate']) {
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.nativeVastEvents.some(event => event.type === 'AdStarted'))).toBe(true)
|
||||
await expect(page.locator('[id^="art-"]')).toBeVisible()
|
||||
await expect.poll(() => page.evaluate(() => [...document.querySelectorAll('video')].some(video => video.currentSrc.includes('/assets/vast/linear-video.mp4') && video.videoWidth > 0 && video.videoHeight > 0 && video.currentTime > 0))).toBe(true)
|
||||
await expect.poll(() => page.evaluate(() => {
|
||||
const videos = [...document.querySelectorAll('video')].map(video => ({ src: video.currentSrc, declaredSrc: video.src, width: video.videoWidth, height: video.videoHeight, time: video.currentTime, readyState: video.readyState, paused: video.paused }))
|
||||
window.nativeVastMediaSamples.push({ at: performance.now(), videos })
|
||||
return videos.some(video => video.src.includes('/assets/vast/linear-video.mp4') && video.width > 0 && video.height > 0 && video.time > 0)
|
||||
})).toBe(true)
|
||||
const decodedAd = await page.evaluate(() => [...document.querySelectorAll('video')].map(video => ({ src: video.currentSrc, width: video.videoWidth, height: video.videoHeight, time: video.currentTime })).filter(video => video.src.includes('/assets/vast/linear-video.mp4')))
|
||||
expect(decodedAd.some(video => video.width > 0 && video.height > 0)).toBe(true)
|
||||
expect(await page.evaluate(() => window.art.template.$video.paused)).toBe(true)
|
||||
|
||||
Reference in new issue
Block a user