test(vast): [PKG-VAST-05] checkpoint native tag requests and skip controls

This commit is contained in:
Harvey Zhao committed 2026-09-14 23:57:20 +08:00
1 parent 75da50a454
commit 09459955c6
13 files changed
+1789 -9

No files matched your search

@@ -128,4 +128,9 @@ availability is an explicit environment gate. No SDK boundary is substituted.
Reports include bundle/media hashes, XML, SDK/browser versions, event order, decoded
ad frames and resumed content state. See the validation record for actual results;
adding this command does not establish device or final distribution acceptance.
The native skip case requests a local HTTP VAST tag via `playUrl`, verifies its
response and clicks IMA's own enabled skip button after the skippable event. Its
countdown already has an accessible skip name; visibility alone is insufficient.
The local XML server permits only IMA origins on that fixture route. Native SDK
timeouts remain failures even when a late ad subsequently plays or skips correctly.
Physical device validation remains05 and full distribution06.
+1 -1
View File
@@ -5,7 +5,7 @@ import base from './playwright.config.js'
assert(!process.env.ARTPLAYER_BROWSER_ARTIFACTS, 'Native VAST source validation does not accept unrelated artifact maps')
export default defineConfig(base, {
testMatch: ['**/vast.native.js', '**/vast-recovery.native.js'],
testMatch: ['**/vast.native.js', '**/vast-recovery.native.js', '**/vast-skip.native.js'],
timeout: 30000,
workers: 1,
outputDir: 'refactor/.cache/vast-native/results',
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,56 @@
# PKG-VAST-05:playUrl、真实跳过按钮和失败分层
前置HEAD75da50a45。本次继续05,保持doing;生产源码、公开类型、依赖和产物没有
变化,不把测试夹具修正包装成播放器缺陷修复。全部原报告和新证据见
[vast-skip-validation.json](../baselines/vast-skip-validation.json)。
## 新增的实际覆盖
`test/browser/vast-skip.native.js`在候选核心的两种VAST模式下调用playUrl,真实IMA
iframe通过HTTP读取广告XML,核对200、MIME、CORS与响应原文;广告实际解码后,
等待AdSkippableStateChanged和可用的SDK按钮,真实点击,再验证AdSkipped、主片
恢复及销毁。没有调用模拟事件、强制隐藏广告、伪造时间或通过skipAd替代UI。
[Google AdsManager接口](https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/reference/interface/google.ima.AdsManager#skip)
明确限制:当IMA自己显示跳过按钮时,程序skip方法不负责跳过。因此本项直接测试
SDK按钮。观察到的SDK为3.789.0,浏览器与前检查点相同。
`vast-fixture.mjs`生成8秒pattern.mp4广告、5秒跳过点、独立本地跟踪图片;
`server.mjs`增加一个测试XML映射及XML MIME。仅这个资源允许明确的http/https
imasdk.googleapis.com来源并返回凭据CORS和Vary:Origin,不给其他路由统一加CORS。
正常PR套件只服务该本地资源,不自动运行外部SDK测试。没有新增依赖或脚本。
## 实际结果与失败记录
| 批次 | 结果 | 原因或实际范围 |
| --- | --- | --- |
| 4秒素材,首次HTTP标签 | 0/2 | 测试服务缺少CORS,真实iframe请求被拒绝;主片继续播放 |
| 补CORS,仍用4秒/1秒跳过点 | 0/2 | 广告真实完成但没有跳过状态/按钮;没有推断所有短广告都不支持跳过 |
| 8秒/5秒素材,旧按钮等待 | 0/2 | 1项SDK9000/标签等待超时;1项在倒计时已暴露的Skip Ad控件上过早点击 |
| 等待真实可跳过事件和可用按钮 | 5/6 | 三引擎两模式;Chromium默认项收到9000后虽完成跳过/恢复仍判失败 |
| WebKit旧插件媒体采样诊断 | 3/3 | 预先固定3次执行、无重试;未修改原首帧断言,不关闭历史失败 |
| 既有播放/切源/错误/Range回归 | 12/12 | 三引擎、新旧核心;验证共享测试服务未破坏已有路径 |
所有浏览器进程退出后才读取最终JSON/归档,记录实际退出码。SDK加载和广告请求
超时仍保持失败,没有提高SDK默认5秒/10秒预算;HTTP响应等待由默认整例30秒
收紧到7秒以更快暴露资源读取失败。首三轮失败用于修正可定位的夹具问题,不作为
成功证据;未反复运行相同断言直到变绿。最终5/6不是完整兼容通过。
## 旧WebKit首帧问题的新增证据
上个检查点的失败trace里,主脚本和bridge都返回200,AdStarted之后有实际
“Linear Ad”画面(画面时钟00:00:00,12),随后AdComplete/主片恢复也发生了。
它不能再被表述为没有播放广告;但当时首帧video属性断言为何始终不成立仍未确定。
`vast.native.js`现在保存每次采样的currentSrc/src、尺寸、时间、readyState和paused,
原判定不变。固定三次诊断都通过,不能覆盖旧失败或宣称间歇问题已修复。
## 后续与回退
专项只读lint、plan/risk-register生成与检查通过;风险/浏览器runner工程测试6通过,
固定Node24.21.0/Yarn1.22.22严格工具链检查通过。git diff --check通过。没有为
该测试改动重建生产包;上一步运行时、类型与分发文件保持原样。
更新包架构、维护说明和风险/进度,05仍doing。下一步是SDK9000后迟到广告的可控
真实SDK复现与处置、历史首帧观测差异、物理iOS/Android验证及06分发。不能把这些
环境门槛拖延其他依赖已满足的源码任务。没有修改用户VPN/浏览器保护,也未使用
VAST脚本加载豁免。该检查点可独立回退;未push、未发布npm。
+1 -1
View File
@@ -602,7 +602,7 @@
- PKG-VAST-02: [记录](changes/2026-09-12-PKG-VAST-02-tests.md) [记录](baselines/vast-behavior-validation.json) [记录](baselines/vast-core.json) [记录](vast-validation.md)
- PKG-VAST-03: [记录](changes/2026-09-12-PKG-VAST-03-lifecycle.md) [记录](baselines/vast-lifecycle-validation.json) [记录](vast-compatibility-decision.md) [记录](changes/2026-09-14-PKG-VAST-03-compatibility.md) [记录](baselines/vast-compatibility-validation.json)
- PKG-VAST-04: [记录](changes/2026-09-14-PKG-VAST-04-types.md) [记录](baselines/vast-types-validation.json) [记录](vast-type-decision.md) [记录](vast-validation.md)
- PKG-VAST-05: [记录](changes/2026-09-14-PKG-VAST-05-native-checkpoint.md) [记录](baselines/vast-native-validation.json)
- PKG-VAST-05: [记录](changes/2026-09-14-PKG-VAST-05-native-checkpoint.md) [记录](baselines/vast-native-validation.json) [记录](changes/2026-09-14-PKG-VAST-05-skip-checkpoint.md) [记录](baselines/vast-skip-validation.json)
- PKG-CAST-01: [记录](baselines/chromecast-release.json) [记录](baselines/chromecast-contract.md) [记录](baselines/chromecast-contract-validation.json) [记录](changes/2026-09-13-PKG-CAST-01-contracts.md)
- PKG-CAST-02: [记录](changes/2026-09-13-PKG-CAST-02-failures.md) [记录](baselines/chromecast-failures-validation.json)
- PKG-CAST-03: [记录](changes/2026-09-13-PKG-CAST-03-runtime.md) [记录](baselines/chromecast-runtime-validation.json)
+9
View File
@@ -1,5 +1,14 @@
# 进度与证据
## PKG-VAST-05 增补真实playUrl和SDK跳过按钮
真实HTTP标签、广告解码、跳过事件/按钮点击及主片恢复最终5/6通过;Chromium默认
项虽完成跳过/恢复,因先出现SDK9000仍判失败。修复测试资源CORS与倒计时按钮
误判,保留三轮失败,不修改生产代码。共享服务播放/切源/错误/Range回归12/12。
旧WebKit失败trace确有广告画面;新增原始video采样后固定3次诊断通过,原失败
仍未关闭。见[记录](changes/2026-09-14-PKG-VAST-05-skip-checkpoint.md)和
[证据](baselines/vast-skip-validation.json)。05保持doing,任务总数和完成数不变。
## PKG-VAST-05 真实 IMA 桌面验证检查点,仍进行中
新增独立`test:vast-native`,真实远端IMA3.789.0与本地广告媒体,不替换SDK。
+8 -4
View File
@@ -777,11 +777,13 @@
"refactor/vast-validation.md",
"refactor/baselines/vast-behavior-validation.json",
"refactor/changes/2026-09-14-PKG-VAST-05-native-checkpoint.md",
"refactor/baselines/vast-native-validation.json"
"refactor/baselines/vast-native-validation.json",
"refactor/changes/2026-09-14-PKG-VAST-05-skip-checkpoint.md",
"refactor/baselines/vast-skip-validation.json"
],
"compatibleResolution": "@glomex/vast-ima-player loadImaSdk loads external IMA; preserve event/content/ad state and teardown, do not equate mocks with vendor service acceptance.",
"closureCriteria": "固定 SDK/外部资源版本和环境;成功/失败/切换/销毁有真实证据,设备和网络限制逐项记录。",
"workspaceState": "Real IMA3.789.0 executes with immutable npm1.0.0/candidate bundles: final27 desktop combinations22 passed5 failed; error303 recovery/recreation/active-ad destruction6 passed. Initial27 were23 passed4 failed and are preserved. SDK9000/late loading and historical WebKit ad-frame failures remain open. IAB SDK200 but bridge document ERR_BLOCKED_BY_CLIENT; physical devices/playUrl/skip not accepted. No VPN exception or timeout widening/retry-to-green."
"workspaceState": "Real IMA3.789.0 executes with immutable npm1.0.0/candidate bundles: final27 desktop combinations22 passed5 failed; error303 recovery/recreation/active-ad destruction6 passed. Initial27 were23 passed4 failed and are preserved. SDK9000/late loading and historical WebKit ad-frame failures remain open. IAB SDK200 but bridge document ERR_BLOCKED_BY_CLIENT; physical devices/playUrl/skip not accepted. No VPN exception or timeout widening/retry-to-green. Native playUrl/IMA skip UI adds5/6 passes (one9000 failure despite later skip), shared server12/12 passes. Historical WebKit trace contains real ad frames; three fixed raw-media diagnostic runs passed without changing the assertion, so the original intermittent failure is not closed."
},
{
"id": "SDK-08",
@@ -3335,11 +3337,13 @@
"refactor/changes/2026-09-14-PKG-VAST-03-compatibility.md",
"refactor/baselines/vast-compatibility-validation.json",
"refactor/changes/2026-09-14-PKG-VAST-05-native-checkpoint.md",
"refactor/baselines/vast-native-validation.json"
"refactor/baselines/vast-native-validation.json",
"refactor/changes/2026-09-14-PKG-VAST-05-skip-checkpoint.md",
"refactor/baselines/vast-skip-validation.json"
],
"compatibleResolution": "Reproduce pending SDK/callback destruction, errors, duplicate IDs and stale ad events before adding compatible cleanup and state ownership.",
"closureCriteria": "Tests prove cleanup during load/callback rejection/core destruction and distinguish explicit plugin recreation from terminal core destruction; vendor media restoration is separately verified.",
"workspaceState": "Task03 controlled82 Node/162 browser checks remain valid; task05 adds6 real IMA303 recovery/recreation/active-ad core destruction passes across three engines/two modes. Native full combination failures, devices and remaining ad lifecycle paths keep this risk open; no claim of complete vendor/device acceptance."
"workspaceState": "Task03 controlled82 Node/162 browser checks remain valid; task05 adds6 real IMA303 recovery/recreation/active-ad core destruction passes across three engines/two modes. Native full combination failures, devices and remaining ad lifecycle paths keep this risk open; no claim of complete vendor/device acceptance. Native playUrl/IMA skip UI adds5/6 passes (one9000 failure despite later skip), shared server12/12 passes. Historical WebKit trace contains real ad frames; three fixed raw-media diagnostic runs passed without changing the assertion, so the original intermittent failure is not closed."
},
{
"id": "VAST-DIST-01",
+3 -1
View File
@@ -3140,7 +3140,9 @@
"acceptance": "最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成",
"evidence": [
"changes/2026-09-14-PKG-VAST-05-native-checkpoint.md",
"baselines/vast-native-validation.json"
"baselines/vast-native-validation.json",
"changes/2026-09-14-PKG-VAST-05-skip-checkpoint.md",
"baselines/vast-skip-validation.json"
]
},
{
+8
View File
@@ -117,3 +117,11 @@ SDK清理由夹具在证据后显式补做,不能据DOM被核心移除就认
广告播放中核心销毁。当前结果见[05检查点](changes/2026-09-14-PKG-VAST-05-native-checkpoint.md):
最终组合22/27,恢复6/6;SDK超时/迟到、旧插件首帧及真机未关闭。独立入口失败应
保留为失败,不能用默认受控套件通过抵消,也不能为稳定CI添加宽泛skip。
`vast-skip.native.js`经真实HTTP调用playUrl,只允许IMA来源读取测试XML,验证响应
原文/MIME/CORS及广告解码;等待SDK可跳过事件和可用按钮后点击,断言AdSkipped与
主片恢复。SDK倒计时控件提前有Skip Ad名称,不能只靠名称和visible就点击。
8秒生成视频配5秒跳过点;4秒/1秒素材未出现跳过状态的结果独立保留,不泛化。
最终5/6(另1项9000失败)、共享服务12/12及固定WebKit诊断3/3见
[增补记录](changes/2026-09-14-PKG-VAST-05-skip-checkpoint.md)。媒体采样仅用于
记录,未放宽原首帧断言;三次通过不能覆盖先前间歇失败。
+8 -1
View File
@@ -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
}
+10
View File
@@ -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>`
}
+78
View File
@@ -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())
})
+7 -1
View File
@@ -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)