test(ci): [CI-01] validate installed VAST loaders and native IMA

This commit is contained in:
Harvey Zhao committed 2026-09-15 04:51:55 +08:00
1 parent ce67eaa73e
commit a4234230e6
18 files changed
+668 -24

No files matched your search

@@ -134,3 +134,29 @@ 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.
## Complete installed bundles
The shared `yarn test:package --browser` roster includes VAST. Its checked
archive retains the real npm 1.0.0 dist/type paths; the same-name contract also
verifies the frozen SDK archives. Generic package runtime/type fixtures still
cover core/chapter only. Use the separate VAST type consumers for VAST claims.
`vast-package.spec.js` uses `browserCandidate()` to select a normal source build
or the verified installed UMD, including the actual glomex implementation. It
holds/aborts native SDK script requests to verify concurrent load rejection,
fresh attempts and listener cleanup. A second case destroys the host before
fulfilling the script with an inert readiness sentinel, which intentionally has
no IMA player APIs: late construction or callbacks must not run. Main content
still decodes after SDK failure. These are loader/lifetime tests, not Google IMA
ad playback; `vast.spec.js` remains a separate controlled SDK boundary suite.
`yarn test:vast-native` defaults to the existing source build. With
`ARTPLAYER_BROWSER_ARTIFACTS` set, its config requires verified core, Chapter and
VAST installations, and all three native test files load the complete installed
bundle. They preserve real remote IMA, published plugin controls, timeouts and
zero retries. Input provenance is attached before SDK initialization, including
for tests that fail before ad playback. This optional integration run stays
outside normal PR checks. See the
[installed VAST record](../../refactor/changes/2026-09-15-CI-01-vast-installed.md)
for actual results and the remaining external SDK/device/distribution gates.
+3 -2
View File
@@ -1,9 +1,10 @@
import assert from 'node:assert/strict'
import process from 'node:process'
import { defineConfig } from '@playwright/test'
import base from './playwright.config.js'
import { verifyInstalledArtifacts } from './scripts/installed-artifacts.mjs'
assert(!process.env.ARTPLAYER_BROWSER_ARTIFACTS, 'Native VAST source validation does not accept unrelated artifact maps')
if (process.env.ARTPLAYER_BROWSER_ARTIFACTS)
verifyInstalledArtifacts(process.cwd(), process.env.ARTPLAYER_BROWSER_ARTIFACTS, ['artplayer', 'artplayer-plugin-chapter', 'artplayer-plugin-vast'])
export default defineConfig(base, {
testMatch: ['**/vast.native.js', '**/vast-recovery.native.js', '**/vast-skip.native.js'],
timeout: 30000,
@@ -0,0 +1,384 @@
{
"schemaVersion": 1,
"task": "CI-01",
"date": "2026-09-15",
"sourceHead": "ce67eaa73ec177b23de9a4c7e27ba99fdf458ae9",
"candidate": "Working tree included in the accompanying CI-01 complete VAST bundle checkpoint; code fingerprints normalize LF",
"environment": {
"node": "24.21.0",
"yarn": "1.22.22",
"platform": "win32"
},
"inputs": [
{
"file": "scripts/package-check.mjs",
"sha256LF": "61529f6d109d6581ead9f32883b4082a02cd00c9bc6c396b1339a8c30530de09"
},
{
"file": "scripts/browser-validation/scope.ts",
"sha256LF": "746f751b2e1c40f7f2f5a8aafd40ff9c47066fc5716984d2feeda167ecfc41fc"
},
{
"file": "playwright.vast-native.config.js",
"sha256LF": "c1b8f0db85602adc6eda12460af1b0740ad380abceb59d565f12bbbf39ed7135"
},
{
"file": "test/browser-validation.test.js",
"sha256LF": "08b2772f18adc640adce948d00e9de64cb18e5e4f4135286ec62971521f2331f"
},
{
"file": "test/browser/vast-package.spec.js",
"sha256LF": "60fad56473db0b3c7bc062342ced578d9176ab0f449c871dc5a2a54b001c9888"
},
{
"file": "test/browser/vast.native.js",
"sha256LF": "9141ecf01a3c8474225c750f3024f2bbd1222bc6fa0b6446d8fa183680d294a5"
},
{
"file": "test/browser/vast-recovery.native.js",
"sha256LF": "4c1969596742b72b04ad505175073bed5802ac1420922128fd0e1ddcceb11804"
},
{
"file": "test/browser/vast-skip.native.js",
"sha256LF": "cfad59626989094aa92e40d4282d410c4596bf21dd2c908923992a9414b2fb4e"
}
],
"packages": {
"directory": "refactor/.cache/packages/run-yqnfgx",
"reportSha256": "ea83dc2b4e3f66a247e1e700a4115ec0231e97ab2984c74593aef10c1e49f463",
"count": 20,
"names": [
"artplayer",
"artplayer-plugin-chapter",
"artplayer-plugin-ambilight",
"artplayer-proxy-canvas",
"artplayer-plugin-document-pip",
"artplayer-plugin-ads",
"artplayer-plugin-audio-track",
"artplayer-plugin-vtt-thumbnail",
"artplayer-plugin-multiple-subtitles",
"artplayer-plugin-hls-control",
"artplayer-plugin-dash-control",
"artplayer-plugin-auto-thumbnail",
"artplayer-plugin-asr",
"artplayer-plugin-chromecast",
"artplayer-proxy-mediabunny",
"artplayer-plugin-jassub",
"artplayer-plugin-danmuku",
"artplayer-plugin-danmuku-mask",
"artplayer-tool-iframe",
"artplayer-plugin-vast"
],
"vast": {
"name": "artplayer-plugin-vast",
"version": "1.2.0",
"archive": "artplayer-plugin-vast.tgz",
"sha256": "e765c2809cec4494d62811c9d2cd96ea8a3091e4cc75162ece31586b95f5f2d7",
"files": {
"package/ARCHITECTURE.md": "918c8f66ed6010c0b2fb8ca70ccc62101fcc126962829347eb3718242375d0f3",
"package/README.md": "7ec086f19c899e315efcb136e037a02d951b0395d5f0ce239817be87ea4ab4e6",
"package/dist/artplayer-plugin-vast.js": "cebaf6a44f80079841205498c4f855ba4e5c804ae7958c06eadda524d728e079",
"package/dist/artplayer-plugin-vast.legacy.js": "8ebb4ebdd4c0fc5ddc467d5ee15b18ab7e5c078c15bbfd37dc33aa89eac1de92",
"package/dist/artplayer-plugin-vast.mjs": "a63e6e8b27032ea690fe740e9ec0a98646346f1fcd3622740f1f5363f234a72b",
"package/package.json": "d359f65c20ca85939a15d4502fdafd6258401aa4a3e421314e1275fe7102826e",
"package/types/artplayer-plugin-vast.d.ts": "51257361f482f026a9b5ba84ca85d320d9e1457a34d0d259ac3c7b46c4fbd29c",
"package/types/runtime-api.d.ts": "4634b65f2d1bbd6973a40390f3217251b69fc5167198a8409d9fed48500e106d",
"package/types/runtime.d.cts": "cead9984e0e2644e67cc687380ed40aed0d561b9d6f9636bea3b98d71522b13e",
"package/types/runtime.d.mts": "5abf25cba3dfaaea2652c0d3721f58ad243eb4d3a2fef80f5d27eb978973b586",
"package/types/runtime.d.ts": "cead9984e0e2644e67cc687380ed40aed0d561b9d6f9636bea3b98d71522b13e"
}
},
"wallSeconds": 113.47,
"exitCode": 0,
"genericConsumerScope": [
"artplayer",
"artplayer-plugin-chapter"
]
},
"collection": {
"directory": "refactor/.cache/ci01-vast-collection-report",
"reportSha256": "98f1803d4c78173a843e35f3be2889253469fb7bdbcce9f02da93e23849b0b5a",
"count": 2442,
"files": 73,
"collectionOnly": true
},
"source": {
"directory": "refactor/.cache/ci01-vast-source-report",
"reportSha256": "a2e75d564fbd87c467aef377e5c486e5db983633ee816f8575dcf4797607e8ea",
"invocation": {
"scope": "source",
"args": [
"test",
"--config=playwright.source.config.js",
"vast-package.spec.js",
"--workers=2"
],
"node": "24.21.0",
"artifactMap": null,
"installedPackages": 0
},
"result": {
"scope": "source",
"exitCode": 0,
"signal": null,
"args": [
"test",
"--config=playwright.source.config.js",
"vast-package.spec.js",
"--workers=2"
]
},
"stats": {
"startTime": "2026-09-14T20:39:43.739Z",
"duration": 25485.434999999998,
"expected": 36,
"skipped": 0,
"unexpected": 0,
"flaky": 0
},
"counts": {
"chromium/vast-package.spec.js": {
"expected": 12,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"firefox/vast-package.spec.js": {
"expected": 12,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"webkit/vast-package.spec.js": {
"expected": 12,
"unexpected": 0,
"skipped": 0,
"flaky": 0
}
},
"browserVersions": {
"chromium": "153.0.8010.12",
"firefox": "155.0",
"webkit": "26.6"
},
"sdkVersions": [],
"identityCounts": {
"chromium/source-build": 12,
"firefox/source-build": 12,
"webkit/source-build": 12
},
"failures": [],
"invocationSha256": "28d87cf83591b7927e472c05ddd2422b4a7f93d2af69845b6b53cbbe1768d2d7"
},
"installed": {
"directory": "refactor/.cache/ci01-vast-installed-report",
"reportSha256": "a0b7608243bee0371a0d0de83fba648c10fa6b0ee5eb3a06debde84016973b42",
"invocation": {
"scope": "installed",
"args": [
"test",
"--config=playwright.installed.config.js",
"vast-package.spec.js",
"--workers=2"
],
"node": "24.21.0",
"artifactMap": "D:\\github\\ArtPlayer\\refactor\\.cache\\packages\\run-yqnfgx\\browser-artifacts.json",
"installedPackages": 20
},
"result": {
"scope": "installed",
"exitCode": 0,
"signal": null,
"args": [
"test",
"--config=playwright.installed.config.js",
"vast-package.spec.js",
"--workers=2"
]
},
"stats": {
"startTime": "2026-09-14T20:42:41.950Z",
"duration": 23818.322999999997,
"expected": 36,
"skipped": 0,
"unexpected": 0,
"flaky": 0
},
"counts": {
"chromium/vast-package.spec.js": {
"expected": 12,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"firefox/vast-package.spec.js": {
"expected": 12,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"webkit/vast-package.spec.js": {
"expected": 12,
"unexpected": 0,
"skipped": 0,
"flaky": 0
}
},
"browserVersions": {
"chromium": "153.0.8010.12",
"firefox": "155.0",
"webkit": "26.6"
},
"sdkVersions": [],
"identityCounts": {
"chromium/installed": 12,
"firefox/installed": 12,
"webkit/installed": 12
},
"failures": [],
"invocationSha256": "4284fe5664b42cc83055f595a22f0ccb452fe9a44981bcc2f79430f5d7816dfc"
},
"native": {
"directory": "refactor/.cache/ci01-vast-native-report",
"reportSha256": "e65f83cc260088fcb62f9b05795991c7734b74e5d05ad3d0cc190f15461dc788",
"invocation": {
"scope": "installed-native",
"command": "yarn test:vast-native",
"head": "ce67eaa73ec177b23de9a4c7e27ba99fdf458ae9",
"startedAt": "2026-09-14T20:44:01.4909743Z",
"artifactMap": "D:\\github\\ArtPlayer\\refactor\\.cache\\packages\\run-yqnfgx\\browser-artifacts.json"
},
"result": {
"exitCode": 0,
"finishedAt": "2026-09-14T20:48:44.1700556Z"
},
"stats": {
"startTime": "2026-09-14T20:44:02.273Z",
"duration": 281695.31499999994,
"expected": 39,
"skipped": 0,
"unexpected": 0,
"flaky": 0
},
"counts": {
"chromium/vast-recovery.native.js": {
"expected": 2,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"firefox/vast-recovery.native.js": {
"expected": 2,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"webkit/vast-recovery.native.js": {
"expected": 2,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"chromium/vast-skip.native.js": {
"expected": 2,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"firefox/vast-skip.native.js": {
"expected": 2,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"webkit/vast-skip.native.js": {
"expected": 2,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"chromium/vast.native.js": {
"expected": 9,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"firefox/vast.native.js": {
"expected": 9,
"unexpected": 0,
"skipped": 0,
"flaky": 0
},
"webkit/vast.native.js": {
"expected": 9,
"unexpected": 0,
"skipped": 0,
"flaky": 0
}
},
"browserVersions": {
"chromium": "153.0.8010.12",
"firefox": "155.0",
"webkit": "26.6"
},
"sdkVersions": [
"3.789.0"
],
"identityCounts": {
"chromium/installed": 10,
"firefox/installed": 10,
"webkit/installed": 10,
"chromium/published": 3,
"firefox/published": 3,
"webkit/published": 3
},
"failures": []
},
"verification": {
"tests": {
"passed": 94,
"failed": 0,
"durationMs": 3676.2619,
"log": "refactor/.cache/ci01-vast-tests.log",
"logSha256": "aeae1681eef5ab7b20fead22cf3f8a15cc0ebf84025735555abc1cd8b1a2e542"
},
"lint": {
"exitCode": 0,
"log": "refactor/.cache/ci01-vast-lint-complete.log",
"logSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"libraryTypes": {
"exitCode": 0,
"log": "refactor/.cache/ci01-vast-library-types.log",
"logSha256": "647c339f0e832948a0fc7393cfeb4ff4e677c8df7b449abbdf3f519523fd782c"
},
"docsTypes": {
"exitCode": 0,
"log": "refactor/.cache/ci01-vast-docs-types.log",
"logSha256": "6dc9f3b0cbfb35e5b93dc8c9ec04cd2d64146a65c7f581614bcb289777b56795"
},
"ci": {
"exitCode": 0,
"log": "refactor/.cache/ci01-vast-ci.log",
"logSha256": "ae8f7eb4d69bd9bdded1bfd024dbc81778a9f5746ea59e8348416ee5f4117fd8"
},
"toolchain": {
"exitCode": 0,
"log": "refactor/.cache/ci01-vast-toolchain.log",
"logSha256": "e826888421079163997eb19d528152df86b03b7d0507877e099e569b461814fb"
},
"impact": {
"exitCode": 0,
"log": "refactor/.cache/ci01-vast-impact.log",
"logSha256": "4aaebfd09336848065473b9db6e05b17da44d5810422144e0a5ac7341155089d"
}
},
"limitations": [
"The PR loader suite uses the complete glomex bundle but controls network failure/readiness. Its sentinel has no IMA player API and is not advertising playback.",
"The original vast.spec.js continues to substitute glomex and is not represented as installed complete-bundle coverage.",
"Only the separate native run uses the real remote IMA SDK. Current successful runs do not close earlier intermittent SDK9000 or historical WebKit first-frame failures.",
"Published plugin controls retain frozen npm1.0.0 bytes; their existing fixture explicitly cleans up SDK resources after observation.",
"Generic pack runtime/type consumers remain core/chapter only. VAST root/legacy/runtime installed type compatibility was separately verified in PKG-VAST-04.",
"No physical-device evidence, remote CI run, full ecosystem acceptance, publication, or closure of CI-01/PKG-VAST-05."
]
}
@@ -0,0 +1,73 @@
# CI-01 VAST 完整安装包与真实 IMA 验证
## 实现范围
接续 `ce67eaa73ec177b23de9a4c7e27ba99fdf458ae9`。统一 browser 安装清单加入
VAST,正常构建/pack/隔离安装二十包;校验真实 npm 1.0.0 的 dist/types 路径
和冻结 SDK 归档。没有修改生产源码、公开接口/声明、依赖版本或 npm 入口。
原 `vast.spec.js` 通过替换 glomex 验证插件逻辑,继续作为源码范围的受控测试。
新增 `vast-package.spec.js` 加载完整普通构建,包括真实 glomex 实现,分别在
三代核心、两种兼容模式、三个引擎下验证两个边界:
- 两个并发注册共享一次真实 script 请求;网络失败传递同一个原生 error Event,
两个注册均拒绝、各自释放 destroy listener、回调不执行。新注册产生第二次
SDK 请求,失败后仍释放监听;主片继续实际解码和播放。
- SDK 请求尚未完成时销毁核心,然后返回只含 readiness 标记的脚本。这个哨兵
没有 IMA 播放器 API,迟到的结果不能创建 session 或调用用户回调。
这些是完整 glomex 的加载/销毁验证;受控网络和哨兵不计为 Google 广告播放。
两种模式继续使用同一个完整包,已批准的 npm 默认与 workspace-1.2 行为保留。
独立 `test:vast-native` 默认仍构建源码;显式给出安装 map 后,config 核验核心、
Chapter、VAST 文件及构建新鲜度,三份 native 测试直接加载安装 UMD。真实旧 npm
插件仍使用固定归档。初始化前附加来源,失败在播放之前发生时也能核对候选。
没有替换 AdsLoader/AdsManager、修改 SDK 超时、添加重试或把 native 测试移入
普通 PR 的受控范围。
## 实测
Windows、Node 24.21.0、Yarn 1.22.22;Chromium 153.0.8010.12、Firefox
155.0、WebKit 26.6;本轮真实 Google IMA 为 3.789.0。原始报告目录、输入摘要、
逐文件结果和源码/安装/历史身份见
[机器证据](../baselines/ci-vast-installed-validation.json)。
| 检查 | 实际结果 |
| --- | --- |
| `yarn test:package --browser` | 二十包正常构建/打包/隔离安装/离线冻结复装通过;run-yqnfgx,113.47 秒 |
| installed 全量 collection | 73 文件、2,442 项;仅清单收集,不是全量运行 |
| source 完整 SDK 加载边界 | 36/36,25,485.435 ms,三个引擎各 12 项 |
| installed 完整 SDK 加载边界 | 36/36,23,818.323 ms,三个引擎各 12 项 |
| installed-map 真实 IMA 专项 | 39/39,281,695.315 ms;零失败、skip、flaky 或重试 |
| 单元/工程检查 | 94/94,3,676.2619 ms,包含原 VAST 导出/兼容/生命周期回归和 native 配置的无 map/坏 map 验证 |
| 静态检查 | 改动文件只读 ESLint、library/docs-tools 严格类型、CI/影响范围和固定工具链通过 |
真实专项包含三个引擎各 13 项:三代核心 × 两种候选模式和一个历史插件共
9 项播放/主片恢复,加 2 项真实 303 恢复/重建/活跃广告销毁、2 项 playUrl/SDK
跳过按钮。全组 30 个安装候选、9 个历史对照,每个引擎分别 10/3;候选均附带
核验过的安装身份。实际广告解码、SDK 事件顺序、点击可用的跳过按钮和恢复主片
仍由原断言判定。本轮加载正常,没有启用用户许可的 VPN/脚本加载跳过例外。
这次 native 全组通过是本次安装候选证据,不是对过去间歇 SDK9000/迟到广告和
旧插件 WebKit 首帧失败的生产修复。旧报告和开放风险保留;没有循环执行相同
失败直到变绿。加载边界的首次 lint 两个多语句行已格式修正,原 lint 日志保留。
通用 pack 中 36 运行时、5 旧类型、8 精确类型模式仍仅覆盖 core/Chapter;VAST
自己的根/legacy/runtime 十四组安装类型证据继续引用 PKG-VAST-04,不虚报为
本次全包类型重跑。源码加载测试的 map 为 null;安装加载和 native 专项分别
核验同一 run-yqnfgx 的完整 UMD,不使用外部化 glomex 的测试构建。
## 重跑和剩余工作
先执行 test:package --browser,设置其 ARTPLAYER_BROWSER_ARTIFACTS,然后运行
`yarn test:browser:installed vast-package.spec.js --workers=2` 和独立的
`yarn test:vast-native`。后者包含真实远端依赖;移除 map 才回到原源码模式。
源码对照使用 `yarn test:browser:source vast-package.spec.js --workers=2`。
三个入口保持各自报告目录,重跑前先归档旧证据。此次原始报告已在进程终态后
归档为 `refactor/.cache/ci01-vast-{source,installed,native,collection}-report`;
native 的实际调用/退出记录另存同前缀 invocation/result JSON。
CI-01、PKG-VAST-05 仍 doing;197/263 完成不变。物理 iOS/Android、SDK 间歇
行为、完整分发/远端 CI 与发布复盘仍未完成。工作区保持已批准的公开兼容边界,
没有发布、部署、推送或修改浏览器/VPN 设置。代码、测试、架构和证据随独立
CI-01 本地提交,随后运行提交审计。
+4
View File
@@ -1,5 +1,9 @@
# PR 检查和 Pages 操作说明
最新统一安装清单含二十包。VAST 的完整 glomex 加载边界进入普通 installed
范围;真实 IMA 仍由独立 test:vast-native 按显式安装 map 验证,本地 39/39
通过不表示远端 CI 已覆盖真实广告。见[记录](changes/2026-09-15-CI-01-vast-installed.md)。
浏览器CI现在分别执行 `yarn test:browser:source` 和
`yarn test:browser:installed`。前者清除继承的安装map并保留全部spec;后者要求已
核验的统一包清单,执行明确列出的 installed 子集;清单维护在
+2 -2
View File
@@ -509,7 +509,7 @@
- ENG-09: [记录](changes/2026-09-12-ENG-09-integration.md) [记录](baselines/engineering-integration.json)
- ENG-10: [记录](changes/2026-09-10-ENG-10-test-reliability.md) [记录](test-reliability.md)
- ENG-11: [记录](build-analysis.md) [记录](baselines/bundle-attribution.json) [记录](changes/2026-09-11-ENG-11-build-analysis.md)
- CI-01: [记录](ci-setup.md) [记录](changes/2026-09-13-CI-01-matrix-summary.md) [记录](baselines/ci-matrix-validation.json) [记录](changes/2026-09-13-CI-01-node-consumers.md) [记录](baselines/node-consumer-validation.json) [记录](changes/2026-09-14-CI-01-installed-full.md) [记录](baselines/ci-installed-full-validation.json) [记录](changes/2026-09-14-CI-01-source-chromium.md) [记录](baselines/ci-source-chromium-validation.json) [记录](changes/2026-09-15-CI-01-ads-installed.md) [记录](baselines/ci-ads-installed-validation.json) [记录](changes/2026-09-15-CI-01-subtitles-installed.md) [记录](baselines/ci-subtitles-installed-validation.json) [记录](changes/2026-09-15-CI-01-adaptive-installed.md) [记录](baselines/ci-adaptive-installed-validation.json) [记录](changes/2026-09-15-CI-01-asr-cast-installed.md) [记录](baselines/ci-asr-cast-installed-validation.json) [记录](changes/2026-09-15-CI-01-mediabunny-installed.md) [记录](baselines/ci-mediabunny-installed-validation.json) [记录](changes/2026-09-15-CI-01-jassub-installed.md) [记录](baselines/ci-jassub-installed-validation.json) [记录](changes/2026-09-15-CI-01-danmuku-installed.md) [记录](baselines/ci-danmuku-installed-validation.json) [记录](changes/2026-09-15-CI-01-browser-matrix.md) [记录](baselines/ci-browser-matrix-validation.json) [记录](changes/2026-09-15-CI-01-iframe-installed.md) [记录](baselines/ci-iframe-installed-validation.json)
- CI-01: [记录](ci-setup.md) [记录](changes/2026-09-13-CI-01-matrix-summary.md) [记录](baselines/ci-matrix-validation.json) [记录](changes/2026-09-13-CI-01-node-consumers.md) [记录](baselines/node-consumer-validation.json) [记录](changes/2026-09-14-CI-01-installed-full.md) [记录](baselines/ci-installed-full-validation.json) [记录](changes/2026-09-14-CI-01-source-chromium.md) [记录](baselines/ci-source-chromium-validation.json) [记录](changes/2026-09-15-CI-01-ads-installed.md) [记录](baselines/ci-ads-installed-validation.json) [记录](changes/2026-09-15-CI-01-subtitles-installed.md) [记录](baselines/ci-subtitles-installed-validation.json) [记录](changes/2026-09-15-CI-01-adaptive-installed.md) [记录](baselines/ci-adaptive-installed-validation.json) [记录](changes/2026-09-15-CI-01-asr-cast-installed.md) [记录](baselines/ci-asr-cast-installed-validation.json) [记录](changes/2026-09-15-CI-01-mediabunny-installed.md) [记录](baselines/ci-mediabunny-installed-validation.json) [记录](changes/2026-09-15-CI-01-jassub-installed.md) [记录](baselines/ci-jassub-installed-validation.json) [记录](changes/2026-09-15-CI-01-danmuku-installed.md) [记录](baselines/ci-danmuku-installed-validation.json) [记录](changes/2026-09-15-CI-01-browser-matrix.md) [记录](baselines/ci-browser-matrix-validation.json) [记录](changes/2026-09-15-CI-01-iframe-installed.md) [记录](baselines/ci-iframe-installed-validation.json) [记录](changes/2026-09-15-CI-01-vast-installed.md) [记录](baselines/ci-vast-installed-validation.json)
- CI-02: [记录](changes/2026-09-14-CI-02-pages-artifact.md) [记录](baselines/pages-artifact-validation.json) [记录](pages-deployment.md)
- PILOT-01: [记录](changes/2026-09-10-PILOT-01-chapter.md) [记录](baselines/pilot-validation.json)
- CORE-01: [记录](changes/2026-09-10-CORE-01-typed-utils.md) [记录](baselines/core-utils-validation.json)
@@ -603,7 +603,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) [记录](changes/2026-09-14-PKG-VAST-05-skip-checkpoint.md) [记录](baselines/vast-skip-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) [记录](changes/2026-09-15-CI-01-vast-installed.md) [记录](baselines/ci-vast-installed-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 @@
# 进度与证据
## CI-01 VAST 完整安装包与真实 IMA
二十包安装准备通过。VAST 完整 glomex 加载边界在源码、安装范围各 36/36;
真实 IMA 3.789.0 专项 39/39(30 安装候选、9 历史对照),三个引擎均无失败/
跳过/重试,覆盖播放、303 恢复、重建、活跃广告销毁与真实跳过按钮。单元/工程
94/94,见[记录](changes/2026-09-15-CI-01-vast-installed.md)。完整 installed
清单现为 73 文件/2,442 项,本轮未全跑。197/263 完成;CI-01/VAST-05 仍 doing,
以前的间歇失败、物理设备、其余分发和远端 CI 门槛保留。
## CI-01 Iframe 安装包与真实跨窗口组合
统一清单现含十九包,保留 Iframe 真实旧 npm 包与冻结 tool 工作区的不同身份。
+8 -4
View File
@@ -779,11 +779,13 @@
"refactor/changes/2026-09-14-PKG-VAST-05-native-checkpoint.md",
"refactor/baselines/vast-native-validation.json",
"refactor/changes/2026-09-14-PKG-VAST-05-skip-checkpoint.md",
"refactor/baselines/vast-skip-validation.json"
"refactor/baselines/vast-skip-validation.json",
"refactor/changes/2026-09-15-CI-01-vast-installed.md",
"refactor/baselines/ci-vast-installed-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. 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."
"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. CI-01 complete installed bundle checkpoint: source/installed native-script loader and cancellation controls each36/36; real remote IMA3.789.0 across three desktop engines39/39 (30 installed candidate,9 published controls), including303 recovery/recreation/active-ad destroy and actual skip UI. No retries/skips or VPN exception. This new successful run does not close earlier intermittent9000/late-ad/historical WebKit observations, physical-device or full distribution gates."
},
{
"id": "SDK-08",
@@ -3350,11 +3352,13 @@
"refactor/changes/2026-09-14-PKG-VAST-05-native-checkpoint.md",
"refactor/baselines/vast-native-validation.json",
"refactor/changes/2026-09-14-PKG-VAST-05-skip-checkpoint.md",
"refactor/baselines/vast-skip-validation.json"
"refactor/baselines/vast-skip-validation.json",
"refactor/changes/2026-09-15-CI-01-vast-installed.md",
"refactor/baselines/ci-vast-installed-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. 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."
"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. CI-01 complete installed bundle checkpoint: source/installed native-script loader and cancellation controls each36/36; real remote IMA3.789.0 across three desktop engines39/39 (30 installed candidate,9 published controls), including303 recovery/recreation/active-ad destroy and actual skip UI. No retries/skips or VPN exception. This new successful run does not close earlier intermittent9000/late-ad/historical WebKit observations, physical-device or full distribution gates."
},
{
"id": "VAST-DIST-01",
+6 -2
View File
@@ -780,7 +780,9 @@
"changes/2026-09-15-CI-01-browser-matrix.md",
"baselines/ci-browser-matrix-validation.json",
"changes/2026-09-15-CI-01-iframe-installed.md",
"baselines/ci-iframe-installed-validation.json"
"baselines/ci-iframe-installed-validation.json",
"changes/2026-09-15-CI-01-vast-installed.md",
"baselines/ci-vast-installed-validation.json"
]
},
{
@@ -3163,7 +3165,9 @@
"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"
"baselines/vast-skip-validation.json",
"changes/2026-09-15-CI-01-vast-installed.md",
"baselines/ci-vast-installed-validation.json"
]
},
{
+10 -1
View File
@@ -109,7 +109,10 @@ Google的[AdsRequest接口](https://developers.google.com/interactive-media-ads/
广告解码截图、主片播放位置及事件;远端SDK版本会变,结果仅代表对应运行环境。
三核心固定为npm5.1.7、npm5.4.0、候选5.4.1;实际npm1.0.0插件完整bundle通过
归档校验后直接加载,候选两种模式使用同一生产构建配置的内存bundle。历史插件的
归档校验后直接加载,候选两种模式默认使用同一生产构建配置的内存bundle。
设置 ARTPLAYER_BROWSER_ARTIFACTS 后改为核验并加载安装包;config 要求核心、
Chapter、VAST 的安装和源码新鲜度证据,三个 native 文件都保留早期输入身份附件。
历史插件的
SDK清理由夹具在证据后显式补做,不能据DOM被核心移除就认为旧插件拥有SDK清理。
源代码组合通过不等于最终tarball通过,也不等于iOS/Android物理设备通过。
@@ -125,3 +128,9 @@ SDK清理由夹具在证据后显式补做,不能据DOM被核心移除就认
最终5/6(另1项9000失败)、共享服务12/12及固定WebKit诊断3/3见
[增补记录](changes/2026-09-14-PKG-VAST-05-skip-checkpoint.md)。媒体采样仅用于
记录,未放宽原首帧断言;三次通过不能覆盖先前间歇失败。
统一安装清单中的 `vast-package.spec.js` 只覆盖完整打包 glomex 的脚本加载、
并发拒绝、重试请求和销毁边界。它通过网络拦截控制失败,迟到成功仅提供空的
IMA readiness 哨兵;不会执行广告。原 `vast.spec.js` 继续替换 glomex 边界,
不纳入完整安装 bundle 声明。真实广告仍必须单独执行上述 native 入口,并按
实际 SDK/网络结果记录。见[安装检查点](changes/2026-09-15-CI-01-vast-installed.md)。
+8
View File
@@ -29,6 +29,14 @@ Iframe baseline/lifecycle-only/boundaries-only diagnostic flags. The separate
history server still uses `iframeCandidate()` and may receive a core-only map;
its source/explicit tool selection has not become installed-tool acceptance.
VAST's installed entry is `vast-package.spec.js`: complete glomex bundle,
controlled native SDK script failure/readiness, registration cleanup and actual
main-content decoding. The original `vast.spec.js` substitutes the glomex boundary
and remains source-only. Real Google IMA execution stays in `test:vast-native`;
that separate config now accepts a verified core/Chapter/VAST installation map
and all native candidate files record the installed identity before initialization.
Loader tests cannot stand in for native advertising, devices or SDK availability.
Ads media/lifecycle and UI files use `test/helpers/browser-candidate.js` to select
the verified installed plugin whenever an artifact map is present. An additional
ARTPLAYER_ADS_ARTIFACT override is rejected instead of mixing candidates. Missing,
+2 -1
View File
@@ -2,7 +2,7 @@ import type { PlaywrightTestConfig } from '@playwright/test'
import assert from 'node:assert/strict'
export type BrowserScope = 'source' | 'installed'
export const installedPackages = ['artplayer', 'artplayer-plugin-chapter', 'artplayer-plugin-ambilight', 'artplayer-proxy-canvas', 'artplayer-plugin-document-pip', 'artplayer-plugin-ads', 'artplayer-plugin-audio-track', 'artplayer-plugin-vtt-thumbnail', 'artplayer-plugin-multiple-subtitles', 'artplayer-plugin-hls-control', 'artplayer-plugin-dash-control', 'artplayer-plugin-auto-thumbnail', 'artplayer-plugin-asr', 'artplayer-plugin-chromecast', 'artplayer-proxy-mediabunny', 'artplayer-plugin-jassub', 'artplayer-plugin-danmuku', 'artplayer-plugin-danmuku-mask', 'artplayer-tool-iframe']
export const installedPackages = ['artplayer', 'artplayer-plugin-chapter', 'artplayer-plugin-ambilight', 'artplayer-proxy-canvas', 'artplayer-plugin-document-pip', 'artplayer-plugin-ads', 'artplayer-plugin-audio-track', 'artplayer-plugin-vtt-thumbnail', 'artplayer-plugin-multiple-subtitles', 'artplayer-plugin-hls-control', 'artplayer-plugin-dash-control', 'artplayer-plugin-auto-thumbnail', 'artplayer-plugin-asr', 'artplayer-plugin-chromecast', 'artplayer-proxy-mediabunny', 'artplayer-plugin-jassub', 'artplayer-plugin-danmuku', 'artplayer-plugin-danmuku-mask', 'artplayer-tool-iframe', 'artplayer-plugin-vast']
export const installedTests = [
'playback.spec.js',
'lifecycle.spec.js',
@@ -76,6 +76,7 @@ export const installedTests = [
'iframe-navigation.spec.js',
'iframe-player.spec.js',
'iframe-editor.spec.js',
'vast-package.spec.js',
]
export function browserInvocation(scope: string, args: string[], environment: NodeJS.ProcessEnv) {
+1
View File
@@ -116,6 +116,7 @@ export async function checkPackages({ release = false, include = [] } = {}) {
'artplayer-plugin-danmuku': async () => (await import('../refactor/scripts/danmuku-contract.mjs')).verifyDanmukuContract(),
'artplayer-plugin-danmuku-mask': async () => (await import('../refactor/scripts/danmuku-mask-contract.mjs')).verifyDanmukuMaskContract(),
'artplayer-tool-iframe': async () => (await import('../refactor/scripts/iframe-contract.mjs')).verifyIframeContract(),
'artplayer-plugin-vast': async () => (await import('../refactor/scripts/vast-contract.mjs')).verifyVastContract(),
'artplayer-plugin-audio-track': async () => (await import('../refactor/scripts/audio-contract.mjs')).verifyAudioContract(),
'artplayer-plugin-vtt-thumbnail': async () => (await import('../refactor/scripts/vtt-thumbnail-contract.mjs')).verifyVttThumbnailContract(),
'artplayer-plugin-multiple-subtitles': async () => (await import('../refactor/scripts/multiple-subtitles-contract.mjs')).verifyMultipleSubtitlesContract(),
+13
View File
@@ -24,6 +24,19 @@ test('Source invocation clears inherited artifact selection without changing cal
assert(fs.existsSync(path.resolve('test/browser', file)))
})
test('Native VAST keeps its source default and rejects an invalid explicit installation before browser launch', () => {
const env = { ...process.env }
delete env.ARTPLAYER_BROWSER_ARTIFACTS
const command = 'const { default: config } = await import("./playwright.vast-native.config.js"); if (config.timeout !== 30000 || config.workers !== 1) throw new Error("Native execution policy changed");'
const source = spawnSync(process.execPath, ['--input-type=module', '-e', command], { encoding: 'utf8', env, windowsHide: true })
assert.equal(source.status, 0, source.stderr)
const missing = path.resolve('refactor/.cache/absent-vast-native-artifact-map.json')
assert(!fs.existsSync(missing))
const installed = spawnSync(process.execPath, ['--input-type=module', '-e', command], { encoding: 'utf8', env: { ...env, ARTPLAYER_BROWSER_ARTIFACTS: missing }, windowsHide: true })
assert.equal(installed.status, 1)
assert.match(installed.stderr, /ENOENT/)
})
test('Browser invocation rejects ambiguous scopes and output/configuration overrides', () => {
assert.throws(() => browserInvocation('unknown', [], {}), /Choose/)
assert.throws(() => browserInvocation('installed', [], {}), /require ARTPLAYER/)
+105
View File
@@ -0,0 +1,105 @@
import { browserCandidate } from '../helpers/browser-candidate.js'
import { expect, test } from './fixtures.js'
const candidate = await browserCandidate('artplayer-plugin-vast')
const sdkUrl = 'https://imasdk.googleapis.com/js/sdkloader/ima3.js'
async function setup(page, core, compatibility, testInfo) {
const requests = []
await page.route(sdkUrl, route => requests.push(route))
await page.goto(`/test/player.html?core=${core}`)
await page.addScriptTag({ content: candidate.code })
await page.evaluate((compatibility) => {
window.art = new window.Artplayer({ container: '.player', url: '/test/pattern.mp4', muted: true })
window.vastCallbacks = 0
window.vastListeners = new Set()
window.vastSettlements = []
const on = window.art.on
const off = window.art.off
window.art.on = function (name, listener) {
if (name === 'destroy')
window.vastListeners.add(listener)
return on.call(this, name, listener)
}
window.art.off = function (name, listener) {
if (name === 'destroy')
window.vastListeners.delete(listener)
return off.call(this, name, listener)
}
window.registerVast = () => {
const plugin = window.artplayerPluginVast(() => window.vastCallbacks++, { compatibility })
return window.art.plugins.add(plugin).then(
() => window.vastSettlements.push({ status: 'resolved' }),
error => window.vastSettlements.push({ status: 'rejected', error }),
)
}
if (window.artplayerPluginVast.default !== window.artplayerPluginVast)
throw new Error('Default factory alias changed')
}, compatibility)
await testInfo.attach('vast-package-inputs', { contentType: 'application/json', body: JSON.stringify({ core, compatibility: compatibility || 'npm-default', provenance: candidate.provenance, sdkUrl, scope: 'Complete normal bundle including glomex. Native script requests are held/failed or fulfilled with an inert readiness sentinel; no real Google IMA/ad playback claim.' }) })
return requests
}
test.afterEach(async ({ page }, testInfo) => {
const state = await page.evaluate(() => ({ callbacks: window.vastCallbacks, listeners: window.vastListeners?.size, destroyed: window.art?.isDestroy, settlements: window.vastSettlements?.map(({ status, error }) => ({ status, errorType: error?.type })), scripts: [...document.scripts].map(script => script.src).filter(src => src.includes('imasdk.googleapis.com')) }))
await testInfo.attach('vast-package-state', { contentType: 'application/json', body: JSON.stringify(state) })
await page.evaluate(() => {
if (window.art && !window.art.isDestroy)
window.art.destroy()
})
})
for (const core of ['published-5.1.7', 'published', 'candidate']) {
for (const compatibility of [undefined, 'workspace-1.2']) {
test(`VAST package ${core}/${compatibility || 'npm-default'}: shared SDK failure rejects both registrations and permits another request`, async ({ page }, testInfo) => {
const requests = await setup(page, core, compatibility, testInfo)
await page.evaluate(() => {
void window.registerVast()
void window.registerVast()
})
await expect.poll(() => requests.length).toBe(1)
expect(await page.evaluate(() => window.vastListeners.size)).toBe(2)
await requests[0].abort('failed')
await expect.poll(() => page.evaluate(() => window.vastSettlements.length)).toBe(2)
expect(await page.evaluate(() => ({ statuses: window.vastSettlements.map(item => item.status), sameError: window.vastSettlements[0].error === window.vastSettlements[1].error, errorType: window.vastSettlements[0].error.type, callbacks: window.vastCallbacks, listeners: window.vastListeners.size }))).toEqual({ statuses: ['rejected', 'rejected'], sameError: true, errorType: 'error', callbacks: 0, listeners: 0 })
await page.evaluate(() => {
void window.registerVast()
})
await expect.poll(() => requests.length).toBe(2)
await requests[1].abort('failed')
await expect.poll(() => page.evaluate(() => window.vastSettlements.length)).toBe(3)
expect(await page.evaluate(() => ({ status: window.vastSettlements[2].status, callbacks: window.vastCallbacks, listeners: window.vastListeners.size, registered: Boolean(window.art.plugins.artplayerPluginVast) }))).toEqual({ status: 'rejected', callbacks: 0, listeners: 0, registered: false })
await expect(page.locator('.player video')).toHaveCount(1)
await page.locator('video').evaluate(video => video.play())
await expect.poll(() => page.locator('video').evaluate(video => video.currentTime)).toBeGreaterThan(0.2)
const pixel = await page.locator('video').evaluate((video) => {
const canvas = document.createElement('canvas')
canvas.width = 80
canvas.height = 45
const context = canvas.getContext('2d')
context.drawImage(video, 0, 0, 80, 45)
return [...context.getImageData(30, 2, 1, 1).data]
})
expect(pixel[0]).toBeGreaterThan(180)
expect(pixel[1]).toBeGreaterThan(180)
expect(pixel[2]).toBeLessThan(80)
await testInfo.attach('vast-package-content-frame', { contentType: 'application/json', body: JSON.stringify({ pixel }) })
})
test(`VAST package ${core}/${compatibility || 'npm-default'}: destroy before SDK readiness suppresses late callbacks and construction`, async ({ page }, testInfo) => {
const requests = await setup(page, core, compatibility, testInfo)
await page.evaluate(() => {
void window.registerVast()
})
await expect.poll(() => requests.length).toBe(1)
await page.evaluate(() => window.art.destroy())
await expect(page.locator('.player')).toBeEmpty()
expect(await page.evaluate(() => window.vastListeners.size)).toBe(0)
await requests[0].fulfill({ contentType: 'text/javascript', body: 'window.google = { ima: { fixtureReady: true } };' })
await expect.poll(() => page.evaluate(() => window.vastSettlements.map(item => item.status))).toEqual(['resolved'])
expect(await page.evaluate(() => ({ callbacks: window.vastCallbacks, listeners: window.vastListeners.size, destroyed: window.art.isDestroy, sentinel: window.google.ima.fixtureReady }))).toEqual({ callbacks: 0, listeners: 0, destroyed: true, sentinel: true })
await expect(page.locator('.player')).toBeEmpty()
expect(requests).toHaveLength(1)
})
}
}
+5 -4
View File
@@ -1,14 +1,15 @@
import { hash } from '../../refactor/scripts/releases.mjs'
import { compilePackage } from '../helpers/load.js'
import { browserCandidate } from '../helpers/browser-candidate.js'
import { expect, test } from './fixtures.js'
// eslint-disable-next-line antfu/no-top-level-await -- Bundle the real SDK once before test discovery.
const code = await compilePackage('artplayer-plugin-vast', 'umd')
// eslint-disable-next-line antfu/no-top-level-await -- Select the complete source or installed SDK bundle before discovery.
const { code, provenance } = await browserCandidate('artplayer-plugin-vast')
for (const compatibility of [undefined, 'workspace-1.2']) {
test(`real IMA ${compatibility || 'npm-default'} recovers from empty VAST, recreates and destroys an active ad`, async ({ page }, testInfo) => {
await page.goto('/test/player.html?core=candidate')
await page.addScriptTag({ content: code })
await testInfo.attach('native-vast-inputs', { contentType: 'application/json', body: JSON.stringify({ provenance, sourceSha256: hash(code), compatibility: compatibility || 'npm-default', scope: 'Complete glomex bundle with real remote Google IMA; no SDK substitution.' }) })
await page.evaluate(async (compatibility) => {
window.createPlayer('/assets/sample/video.mp4')
window.recoveryEvents = []
@@ -65,7 +66,7 @@ for (const compatibility of [undefined, 'workspace-1.2']) {
expect(state.terminalInit).toBeNull()
expect(state.events.filter(event => event.type === 'AdError')).toHaveLength(1)
await expect(page.locator('[id^="art-"]')).toHaveCount(0)
await testInfo.attach('native-vast-recovery', { contentType: 'application/json', body: JSON.stringify({ sourceSha256: hash(code), compatibility: compatibility || 'npm-default', failure, ...state }) })
await testInfo.attach('native-vast-recovery', { contentType: 'application/json', body: JSON.stringify({ sourceSha256: hash(code), provenance, compatibility: compatibility || 'npm-default', failure, ...state }) })
await testInfo.attach('native-vast-recovery.xml', { contentType: 'application/xml', body: await page.evaluate(() => window.recoveryXml) })
})
}
+5 -4
View File
@@ -1,16 +1,17 @@
import fs from 'node:fs'
import { hash } from '../../refactor/scripts/releases.mjs'
import { compilePackage } from '../helpers/load.js'
import { browserCandidate } from '../helpers/browser-candidate.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')
// eslint-disable-next-line antfu/no-top-level-await -- Select the complete source or installed SDK bundle before discovery.
const { code, provenance } = await browserCandidate('artplayer-plugin-vast')
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 testInfo.attach('native-vast-inputs', { contentType: 'application/json', body: JSON.stringify({ provenance, sourceSha256: hash(code), compatibility: compatibility || 'npm-default', scope: 'Complete glomex bundle with real remote Google IMA; no SDK substitution.' }) })
await page.evaluate(async (compatibility) => {
window.createPlayer('/assets/sample/video.mp4')
window.skipEvents = []
@@ -62,7 +63,7 @@ for (const compatibility of [undefined, 'workspace-1.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 testInfo.attach('native-vast-skip', { contentType: 'application/json', body: JSON.stringify({ sourceSha256: hash(code), provenance, 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)
})
+4 -4
View File
@@ -1,11 +1,11 @@
import fs from 'node:fs'
import { hash, readMember } from '../../refactor/scripts/releases.mjs'
import { verifyVastContract } from '../../refactor/scripts/vast-contract.mjs'
import { compilePackage } from '../helpers/load.js'
import { browserCandidate } from '../helpers/browser-candidate.js'
import { expect, test } from './fixtures.js'
// eslint-disable-next-line antfu/no-top-level-await -- Compile the real bundled SDK before test discovery.
const code = await compilePackage('artplayer-plugin-vast', 'umd')
// eslint-disable-next-line antfu/no-top-level-await -- Select the complete source or installed SDK bundle before discovery.
const { code, provenance } = await browserCandidate('artplayer-plugin-vast')
// eslint-disable-next-line antfu/no-top-level-await -- Verify immutable published bytes before native test discovery.
const contract = await verifyVastContract()
const historicalCode = readMember(contract.archives.get('artplayer-plugin-vast@1.0.0'), 'package/dist/artplayer-plugin-vast.js').toString()
@@ -38,7 +38,7 @@ for (const core of ['published-5.1.7', 'published', 'candidate']) {
test(`real IMA ${core} / ${implementation} plays local VAST media and resumes content`, async ({ page }, testInfo) => {
await page.goto(`/test/player.html?core=${core}`)
await page.addScriptTag({ content: bundle })
await testInfo.attach('native-vast-inputs', { contentType: 'application/json', body: JSON.stringify({ sourceSha256: hash(bundle), sdk: historical ? 'immutable npm1.0.0 bundled SDK; real remote Google IMA' : '@glomex/vast-ima-player@1.21.2 bundled; real remote Google IMA', media: { file: mediaFile, sha256: hash(fs.readFileSync(mediaFile)), contentFile: 'docs/assets/sample/video.mp4', contentSha256: hash(fs.readFileSync('docs/assets/sample/video.mp4')) }, implementation, core, compatibility: compatibility || 'npm-default' }) })
await testInfo.attach('native-vast-inputs', { contentType: 'application/json', body: JSON.stringify({ sourceSha256: hash(bundle), provenance: historical ? { kind: 'published', name: 'artplayer-plugin-vast', version: '1.0.0' } : provenance, sdk: historical ? 'immutable npm1.0.0 bundled SDK; real remote Google IMA' : '@glomex/vast-ima-player@1.21.2 bundled; real remote Google IMA', media: { file: mediaFile, sha256: hash(fs.readFileSync(mediaFile)), contentFile: 'docs/assets/sample/video.mp4', contentSha256: hash(fs.readFileSync('docs/assets/sample/video.mp4')) }, implementation, core, compatibility: compatibility || 'npm-default' }) })
await page.evaluate(({ compatibility, historical }) => {
window.nativeVastHistorical = historical
window.nativeVastEvents = []