test(ci): [CI-01] centralize installed roster and cover ASR and Cast

This commit is contained in:
Harvey Zhao committed 2026-09-15 02:56:43 +08:00
1 parent b90de99bc2
commit 8379ac7bbf
30 files changed
+3028 -58

No files matched your search

+1 -1
View File
@@ -140,7 +140,7 @@ jobs:
# These extra installed packages are required by the browser helpers.
# Other ecosystem installation/SDK gates remain tracked by CI-01.
run: |
yarn test:package --include=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 2>&1 | tee refactor/.cache/ci/browser-packages.log
yarn test:package --browser 2>&1 | tee refactor/.cache/ci/browser-packages.log
node --input-type=module -e 'import fs from "node:fs"; const { output } = JSON.parse(fs.readFileSync("refactor/.cache/packages/latest.json")); fs.appendFileSync(process.env.GITHUB_ENV, `ARTPLAYER_BROWSER_ARTIFACTS=${output}/browser-artifacts.json\n`);'
- name: Run all source browser checks
if: ${{ !cancelled() }}
@@ -110,7 +110,7 @@ ARTPLAYER_BROWSER_ARTIFACTS is set; it rejects ARTPLAYER_ADS_ARTIFACT overrides
and never falls back to source on an invalid installation. Each candidate case
attaches its installed file/archive identity. Without a map, existing explicit
artifact or source-build selection remains available. See the repository
scripts/browser-validation/README.md for the six-package preparation command.
scripts/browser-validation/README.md for the shared `yarn test:package --browser` command.
This integration does not complete the separate Ads type, visibility or device gates.
Tests distinguish actual media decoding from injected errors/controlled clocks. Baseline and
@@ -159,3 +159,13 @@ Safari and physical devices remain PKG-ASR-05 acceptance work. CORS restrictions
and main-video-only capture are explicit integration boundaries, not a claim
that every media source can be recognized. Do not use the demo's external
recognizer service as a prerequisite for local audio verification.
## Shared installed browser validation
Six browser files select verified installed ASR bytes through browser-candidate.js. The Audio Track combination independently verifies both installed plugins. Published ASR controls retain their frozen archives; attachments distinguish selected published, installed and source inputs. Tests cover native local PCM, direct/capture routing, CORS, volume/mute and ownership. Deliberately forced Firefox binding rejection remains labeled; no recognition service or physical speaker output is claimed.
Use `yarn test:package --browser`, then `yarn test:browser:installed`. The common
roster in scripts/browser-validation/scope.ts drives both preparation and required
inputs. Explicit artifact overrides are rejected when an installed map is present;
missing or stale inputs fail without source fallback. Source mode retains deliberate
artifact selection. See ../../refactor/changes/2026-09-15-CI-01-asr-cast-installed.md.
@@ -95,7 +95,7 @@ demo still belong to Audio-06. See refactor/baselines/audio-types-validation.jso
audio-fixture.js records the selected published or candidate input. The older test label "source audio" means the candidate slot; consult audio-inputs.selected for its actual origin. Native WAV capability controls remain separate from decoded AAC acceptance.
The shared `yarn test:browser:installed` roster includes this package after preparing
the nine-package tarballs with `yarn test:package --include=...`; use the exact command
the checked tarballs with `yarn test:package --browser`; see the scope rules
in ../../scripts/browser-validation/README.md. Attachments identify candidate file,
archive and source hashes. Missing or stale maps fail without source fallback.
Without a map, source and deliberate artifact checks retain their existing behavior.
@@ -232,7 +232,7 @@ require the host's existing getter to have every generated-sheet field.
autoThumbnailCandidate reads verified installed bytes through browser-candidate.js. Native video/JPEG lifecycle and pixel files retain their stub player host and exact presentation-callback/fallback limits. The historical file still loads its frozen old artifacts. Installed mode rejects frozen-workspace and explicit-artifact overrides. These are native extraction checks, not full core integration or first-frame acceptance on every engine.
Use the exact twelve-package preparation command and scope rules in
Use `yarn test:package --browser` and the shared scope rules in
../../scripts/browser-validation/README.md. Missing, stale or changed installation
inputs fail without source fallback. Attachments distinguish selected installed,
source and published inputs. Source mode retains existing explicit artifact use.
@@ -156,3 +156,13 @@ The installed consumer verifier compares both actual historical tarballs and an
isolated candidate package, validates individual negative statement diagnostics,
and checks runtime entry identity. The editor declaration is generated semantically
from the root declaration. Keep internal `tsconfig.json` and source out of npm packs.
## Shared installed browser validation
chromecast-lifecycle.spec.js selects the verified installed bundle. It uses a controlled Cast SDK with real core DOM, clicks and local video; receiver discovery, actual Cast SDK and remote playback remain PKG-CAST-05. The artifact map must not be interpreted as physical device acceptance.
Use `yarn test:package --browser`, then `yarn test:browser:installed`. The common
roster in scripts/browser-validation/scope.ts drives both preparation and required
inputs. Explicit artifact overrides are rejected when an installed map is present;
missing or stale inputs fail without source fallback. Source mode retains deliberate
artifact selection. See ../../refactor/changes/2026-09-15-CI-01-asr-cast-installed.md.
@@ -218,7 +218,7 @@ limitations, physical devices and full release acceptance remain separately trac
dash-control.spec.js exercises native MP4 and real core DOM with controlled SDK methods. dash-sdk.spec.js loads the actual frozen dash.js 4.5.2/5.2.1 SDKs and MPD fixtures. Its SDK capability attachment does not claim a plugin was loaded; dash-plugin-inputs records the selected plugin only in integration cases. Installed mode rejects diagnostic SDK substitutions and synthetic recovery probes.
Use the exact twelve-package preparation command and scope rules in
Use `yarn test:package --browser` and the shared scope rules in
../../scripts/browser-validation/README.md. Missing, stale or changed installation
inputs fail without source fallback. Attachments distinguish selected installed,
source and published inputs. Source mode retains existing explicit artifact use.
@@ -147,7 +147,7 @@ See ../../refactor/changes/2026-09-15-PKG-HLS-PACK-01-config.md.
hls-control.spec.js uses a frozen SDK without workers; hls-sdk.spec.js uses the frozen SDK version matrix and real workers. Both select verified installed plugin bytes and retain published plugin controls.
Use the exact twelve-package preparation command and scope rules in
Use `yarn test:package --browser` and the shared scope rules in
../../scripts/browser-validation/README.md. Missing, stale or changed installation
inputs fail without source fallback. Attachments distinguish selected installed,
source and published inputs. Source mode retains existing explicit artifact use.
@@ -189,7 +189,7 @@ See `../../refactor/baselines/multiple-subtitles-contract.md` for exact historic
multipleSubtitlesCandidate uses browser-candidate.js for verified installed bytes. An installed map rejects ARTPLAYER_MULTIPLE_SUBTITLES_ARTIFACT overrides. Actual historical plugins and no-plugin controls retain their original inputs.
The shared `yarn test:browser:installed` roster includes this package after preparing
the nine-package tarballs with `yarn test:package --include=...`; use the exact command
the checked tarballs with `yarn test:package --browser`; see the scope rules
in ../../scripts/browser-validation/README.md. Attachments identify candidate file,
archive and source hashes. Missing or stale maps fail without source fallback.
Without a map, source and deliberate artifact checks retain their existing behavior.
@@ -165,7 +165,7 @@ historical evidence and outstanding acceptance work.
vttThumbnailCandidate uses browser-candidate.js for verified installed bytes. An installed map rejects ARTPLAYER_VTT_THUMBNAIL_BASELINE and ARTPLAYER_VTT_THUMBNAIL_ARTIFACT overrides. Frozen published control inputs and their cue-format differences remain explicit.
The shared `yarn test:browser:installed` roster includes this package after preparing
the nine-package tarballs with `yarn test:package --include=...`; use the exact command
the checked tarballs with `yarn test:package --browser`; see the scope rules
in ../../scripts/browser-validation/README.md. Attachments identify candidate file,
archive and source hashes. Missing or stale maps fail without source fallback.
Without a map, source and deliberate artifact checks retain their existing behavior.
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,57 @@
# CI-01 统一包清单并接入 ASR、Chromecast 安装验证
## 工程与加载边界
新增 yarn test:package --browser,从 browser-validation/scope.ts 的 installedPackages
生成额外包清单,与安装浏览器预检使用同一数据来源。默认 core/chapter 与显式
--include 保留;未知/重复/空选项及 --browser 与 --include/--release 混用被拒绝。
工作流、契约负例和维护文档同步,不能在 CI 偷换为手写部分包列表。
此 CLI 属于固定 Node 24 工程工具,不改变消费者运行环境或公开播放器接口。
新增 ASR 六文件与 Chromecast 一文件。ASR 使用实际 WebAudio/Worklet、本地媒体、
原生或明确强制的 capture fallback、音量/CORS/Audio Track 组合;组合分别核验
ASR 和 Audio Track 安装身份。Chromecast 使用真实核心 DOM/点击/本地视频及受控
SDK,不验证接收设备、发现或远端媒体。其余未知真实环境保持各自任务门槛。
候选加载器拒绝安装 map 与显式 artifact 同时使用;已发布输入仍按冻结归档加载。
VAST 主测试 externalize 后替换 wrapper 依赖,不能直接换为完整 bundle 并冒充
真实安装验收,因此未纳入这个范围;实际 IMA 路径继续独立验证。没有修改生产
源码、公开 API、超时、业务断言、依赖或锁文件。历史包内说明中的硬编码清单
改为指向 --browser 与共同清单,防止未来列表和文档漂移。
## 已验证的工程范围
Windows x64,Node 24.21.0、Yarn 1.22.22。--browser 实际创建十四包 run-N2u2Kr,
正常构建/pack/独立安装/冻结复装与完整文件核验通过。通用消费者依然仅验证
core/chapter 的 36 运行时、5 旧类型模式和 8 精确类型模式。
相关单元 357/357、工程 48/48、scoped lint、library 类型、严格工具链、工作流
契约和 actionlint 1.7.12 通过(未调用 shellcheck/pyflakes)。初始未使用 import/
顺序及既有测试行格式错误已改正;旧工作流负例依赖手写包名,修改为禁止手写
部分名单后通过。中间失败日志保留,最终结果明确区分。
完整 installed collection 为 39 文件/1308 项;本轮实际执行新增七文件的 171 项,
三引擎、workers=2、retries=0。结果、实际输入指纹与能力限制见机器证据。
这不代表完整十四包矩阵或远端 CI 全绿;之前 Chapter/Audio/DASH 失败继续保留。
部分既有包的文档措辞在准备快照后更新,发布前仍需重新打包绑定最终内容。
CI-01 仍 doing,ASR/CAST 真机门槛未关闭,没有推送或发布。回退此检查点恢复
十二包名单和手写准备命令,不涉及生产代码回退。
## 实测结果
Chromium 153.0.8010.12 与 Firefox 155.0 各 57/57 通过;WebKit 26.6 为 15 通过、
42 WebAudio 能力跳过。合计 129 通过、42 跳过、0 失败,耗时 173.947 秒,
实际退出 0、无重试;这是新增范围的结果,不覆盖完整 1308 项安装清单。
Chromecast 45 项均为受控 SDK 生命周期;ASR 84 项通过中 48 项选择候选,
其余保留旧包控制。候选 ASR/Audio Track 的八项组合分别记录两个安装身份,
不能把身份条数重复算作测试条数;在 skip 前记录的候选身份不计处理能力通过。
故意继承安装 map 后,source 入口清除它,三个 Chromium 定向用例 3/3 通过:
ASR 原生 Worklet、ASR/Audio Track 组合、Cast 注册点击。四个实际选包身份均为
source-build(组合包含两个包)。源码回归仍是定向范围,不代表全量源码矩阵。
所有输入、命令、统计和 skip 注解见
[机器证据](../baselines/ci-asr-cast-installed-validation.json)。归档目录为
ci01-asr-cast-installed-report 与 ci01-asr-cast-source-report;原始日志和实际退出
状态均保留。无新增运行失败,但 ASR 真机/物理输出、Cast 接收设备和整体发布
门槛继续开放。
+6 -1
View File
@@ -2,7 +2,8 @@
浏览器CI现在分别执行 `yarn test:browser:source` 和
`yarn test:browser:installed`。前者清除继承的安装map并保留全部spec;后者要求已
核验的core/chapter/Ambilight/Canvas/Document PiP/Ads/Audio Track/VTT Thumbnail/Multiple Subtitles/HLS Control/DASH Control/Auto Thumbnail安装,执行明确列出的installed子集。两步不
核验的统一包清单,执行明确列出的 installed 子集;清单维护在
`scripts/browser-validation/scope.ts`。两步不
吞失败,普通失败后仍保存另一范围的证据,分别上传browser-source/browser-installed。
入口与报告规则见[维护说明](../scripts/browser-validation/README.md)。source全量
耗时/60分钟预算、其余插件安装矩阵与远端运行仍待CI-01/CI-04,不计为已通过。
@@ -165,3 +166,7 @@ common.js/bootstrap.js 由 browser 下的 TS 生成,源代码进入 typecheck:
和根 lint,产物由 check:site-assets 比较;不再对生成 common.js 应用源码 lint。
build:ts 生成声明 URL 清单后重建站点浏览器产物,check:editor-types 保持只读。
新增 test:site-editor 并接入 test:node,新增真实 Monaco 交互测试走现有三引擎矩阵。
安装准备使用 `yarn test:package --browser`,从 browser-validation/scope.ts 的同一
包清单生成,不再在工作流复制列表。默认 core/chapter 消费者及显式 --include 保留;
此选项不表示全生态类型、SDK 或发布准入。
+1 -1
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)
- 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)
- 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)
+9
View File
@@ -1,5 +1,14 @@
# 进度与证据
## CI-01 统一安装包清单并接入 ASR / Chromecast
新增 --browser 从同一清单准备十四包,实际安装核验通过;消除 CI 手写列表并
保留默认/显式子集方式。新增七文件三引擎 171 项:129 通过、42 WebAudio 能力
跳过;Cast 仅受控 SDK,设备门槛不变。源码定向 3/3、单元 357/357、工程 48/48
通过,见[记录](changes/2026-09-15-CI-01-asr-cast-installed.md)。完整清单为
39 文件/1308 项,本轮未全跑。197/263 完成,CI-01 继续 doing;下一步处理其余
包的完整产物/资源映射,VAST 的依赖替换测试不能冒充已安装完整 bundle。
## CI-01 自适应控制和自动缩略图接入安装矩阵
十二包实际安装通过,清单扩展到 32 文件/1137 项。新增七文件三引擎实跑
+2 -2
View File
@@ -90,10 +90,10 @@ export function validateCIWorkflow(source) {
const vueIndex = browser.steps.findIndex(step => step.run === 'yarn test:vue-consumer 2>&1 | tee refactor/.cache/ci/vue-consumer.log')
assert(vueIndex > consumerIndex && !Object.hasOwn(browser.steps[vueIndex], 'if'), 'Run Vue installed consumers after restoring canonical Node')
assert(browser.steps.some(step => step.uses?.startsWith('actions/upload-artifact@') && step.if === 'always()' && step.with.path.split('\n').includes('refactor/.cache/vue-consumer-*/')), 'Retain Vue consumer failure evidence')
const extraIndex = browser.steps.findIndex(step => step.run?.startsWith('yarn test:package --include=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 '))
const extraIndex = browser.steps.findIndex(step => step.run?.startsWith('yarn test:package --browser '))
const sourceEngineIndex = browser.steps.findIndex(step => step.run === 'yarn test:browser:source 2>&1 | tee refactor/.cache/ci/browser-source.log')
const engineIndex = browser.steps.findIndex(step => step.run === 'yarn test:browser:installed 2>&1 | tee refactor/.cache/ci/browser-installed.log')
assert(extraIndex > Math.max(consumerIndex, reactIndex, vueIndex) && extraIndex < engineIndex, 'Prepare installed Ambilight/Canvas/Document PiP/Ads after consumer probes and before browser checks')
assert(extraIndex > Math.max(consumerIndex, reactIndex, vueIndex) && extraIndex < engineIndex, 'Prepare the shared installed browser roster after consumer probes and before browser checks')
assert(!Object.hasOwn(browser.steps[extraIndex], 'if') && browser.steps[extraIndex].run.includes('GITHUB_ENV') && browser.steps[extraIndex].run.includes('ARTPLAYER_BROWSER_ARTIFACTS='), 'Always select the additional installed browser artifact map')
assert(sourceEngineIndex > extraIndex && sourceEngineIndex < engineIndex, 'Retain complete source checks before the additional installed suite')
for (const index of [sourceEngineIndex, engineIndex]) {
+9 -6
View File
@@ -33,17 +33,20 @@ for (const [name, mutate] of [
['conditional Node setup', w => w.jobs.checks.steps.find(s => s.uses?.startsWith('actions/setup-node@')).if = 'false'],
['wrong consumer Node version', w => w.jobs['browser-smoke'].steps.find(s => s.id === 'consumer-node-20').with['node-version'] = '24'],
['skipped installed consumer', w => w.jobs['browser-smoke'].steps.find(s => s.run?.includes('--expected-node 22.12.0')).if = 'false'],
['missing browser plugin installation', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:package --include=')).run = 'echo omitted'],
['missing Document PiP browser installation', (w) => {
const step = w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:package --include='))
step.run = step.run.replace(',artplayer-plugin-document-pip', '')
['missing browser plugin installation', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:package --browser')).run = 'echo omitted'],
['manual partial browser package roster', (w) => {
const step = w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:package --browser'))
step.run = step.run.replace('--browser', '--include=artplayer-plugin-ambilight')
}],
['skipped browser plugin installation', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:package --include=')).if = 'false'],
['skipped browser plugin installation', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:package --browser')).if = 'false'],
['missing source browser checks', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:browser:source ')).run = 'echo omitted'],
['filtered source browser checks', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:browser:source ')).run += ' --grep subset'],
['source failure prevents installed evidence', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:browser:installed ')).if = 'success()'],
['ignored installed failure', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:browser:installed '))['continue-on-error'] = true],
['missing separate source evidence', w => { const upload = w.jobs['browser-smoke'].steps.find(s => s.uses?.startsWith('actions/upload-artifact@')); upload.with.path = upload.with.path.replace('refactor/.cache/browser-source/\n', '') }],
['missing separate source evidence', (w) => {
const upload = w.jobs['browser-smoke'].steps.find(s => s.uses?.startsWith('actions/upload-artifact@'))
upload.with.path = upload.with.path.replace('refactor/.cache/browser-source/\n', '')
}],
['browser runtime not restored', w => w.jobs['browser-smoke'].steps.find(s => s.id === 'restore-canonical-node').with = { 'node-version': '22.12.0', 'package-manager-cache': false }],
['skipped React consumer', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:react-consumer')).if = 'false'],
['removed React consumer', w => w.jobs['browser-smoke'].steps = w.jobs['browser-smoke'].steps.filter(s => !s.run?.startsWith('yarn test:react-consumer'))],
+3 -1
View File
@@ -768,7 +768,9 @@
"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"
"baselines/ci-adaptive-installed-validation.json",
"changes/2026-09-15-CI-01-asr-cast-installed.md",
"baselines/ci-asr-cast-installed-validation.json"
]
},
{
+16 -2
View File
@@ -14,8 +14,8 @@ also retains explicit historical and committed-artifact cases; its name does not
promise that every dependency is rebuilt from current source in every test.
`yarn test:browser:installed` requires ARTPLAYER_BROWSER_ARTIFACTS from
`yarn test:package --include=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`.
Both launcher and config validate the selected twelve packages' installed hashes
`yarn test:package --browser`.
Both launcher and config validate the selected packages' installed hashes
and source/build freshness. `scope.ts` lists the currently supported installed
test files. This is a growing installed subset, not full ecosystem acceptance.
Source checks remain complete when the installed list grows.
@@ -83,3 +83,17 @@ rejects diagnostic SDK patches/debug replacements and synthetic recovery probes,
while source diagnostics remain explicit. Auto Thumbnail uses native media/JPEG
with a stub host and retains its presentation/fallback pixel limitations. See
../../refactor/changes/2026-09-15-CI-01-adaptive-installed.md for executed results.
`yarn test:package --browser` derives additional packages from installedPackages
in scope.ts, so CI preparation and browser preflight share one roster. The default
command still prepares core/chapter; --include retains explicit subsets. Unknown,
duplicate, empty or conflicting --browser/--release/--include options fail before
building. --browser is preparation, never full release acceptance.
ASR adds six native audio/routing/CORS files, including a composition that checks
ASR and Audio Track identities separately. Historical inputs, unsupported WebAudio
and deliberately forced fallback branches remain labeled. Chromecast adds its
controlled SDK lifecycle file with actual core DOM/local media; it does not run
a Cast receiver. VAST controlled tests externalize and replace a dependency, so
they are not advertised as installed bundle coverage. Its real IMA suite retains
its independent gate. See ../../refactor/changes/2026-09-15-CI-01-asr-cast-installed.md.
+8 -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']
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']
export const installedTests = [
'playback.spec.js',
'lifecycle.spec.js',
@@ -36,6 +36,13 @@ export const installedTests = [
'auto-thumbnail.spec.js',
'auto-thumbnail-lifecycle.spec.js',
'auto-thumbnail-pixels.spec.js',
'asr-audio.spec.js',
'asr-playback.spec.js',
'asr-fallback.spec.js',
'asr-cors.spec.js',
'asr-capture-cors.spec.js',
'asr-audio-track.spec.js',
'chromecast-lifecycle.spec.js',
]
export function browserInvocation(scope: string, args: string[], environment: NodeJS.ProcessEnv) {
+16 -2
View File
@@ -5,8 +5,21 @@ import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { ensureArchive, hash, readMember } from '../refactor/scripts/releases.mjs'
import { installedPackages } from './browser-validation/scope.ts'
import { consumerDirectory, names as defaultNames, readJson, removeConsumer, run, runtimeConsumer, typeConsumers, workspace, writeJson } from './package-consumer.mjs'
export function packageOptions(args) {
assert(args.every(arg => ['--release', '--browser'].includes(arg) || arg.startsWith('--include=')), 'Unknown package-check option')
const includes = args.filter(arg => arg.startsWith('--include='))
assert(includes.length <= 1 && new Set(args).size === args.length, 'Duplicate package-check option')
const browser = args.includes('--browser')
const release = args.includes('--release')
assert(!browser || (!release && includes.length === 0), 'Browser preparation cannot be combined with release or explicit includes')
const include = browser ? installedPackages.filter(name => !defaultNames.includes(name)) : includes[0]?.slice('--include='.length).split(',') || []
assert(include.every(Boolean) && new Set(include).size === include.length, 'Empty or duplicate additional package')
return { release, include }
}
export function checkFiles(manifest, files, historical = []) {
for (const field of ['main', 'module', 'types', 'legacy']) {
assert(typeof manifest[field] === 'string', `Missing ${field}: ${manifest.name}`)
@@ -80,6 +93,8 @@ export async function checkPackages({ release = false, include = [] } = {}) {
'artplayer-plugin-hls-control': async () => (await import('../refactor/scripts/hls-contract.mjs')).verifyHlsContract(),
'artplayer-plugin-dash-control': async () => (await import('../refactor/scripts/dash-contract.mjs')).verifyDashContract(),
'artplayer-plugin-auto-thumbnail': async () => (await import('../refactor/scripts/auto-thumbnail-contract.mjs')).verifyAutoThumbnailContract(),
'artplayer-plugin-asr': async () => (await import('../refactor/scripts/asr-contract.mjs')).verifyAsrContract(),
'artplayer-plugin-chromecast': async () => (await import('../refactor/scripts/chromecast-contract.mjs')).verifyChromecastContract(),
'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(),
@@ -164,8 +179,7 @@ export async function checkPackages({ release = false, include = [] } = {}) {
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
const include = process.argv.find(arg => arg.startsWith('--include='))?.slice('--include='.length).split(',') || []
checkPackages({ release: process.argv.includes('--release'), include }).catch((error) => {
checkPackages(packageOptions(process.argv.slice(2))).catch((error) => {
console.error(error)
process.exitCode = 1
})
+8 -7
View File
@@ -1,25 +1,26 @@
import fs from 'node:fs'
import process from 'node:process'
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'
let asrCode
let audioCode
let inputs
test.beforeAll(async () => {
expect(process.env.ARTPLAYER_BROWSER_ARTIFACTS, 'These source/explicit artifact checks do not represent isolated installed packages').toBeFalsy()
asrCode = process.env.ARTPLAYER_ASR_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_ASR_ARTIFACT, 'utf8') : await compilePackage('artplayer-plugin-asr', 'umd')
audioCode = process.env.ARTPLAYER_AUDIO_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_AUDIO_ARTIFACT, 'utf8') : await compilePackage('artplayer-plugin-audio-track', 'umd')
const asr = await browserCandidate('artplayer-plugin-asr', process.env.ARTPLAYER_ASR_ARTIFACT)
asrCode = asr.code
const audio = await browserCandidate('artplayer-plugin-audio-track', process.env.ARTPLAYER_AUDIO_ARTIFACT)
audioCode = audio.code
const media = fs.readFileSync(new URL('./media/audio-tone.m4a', import.meta.url))
const manifest = JSON.parse(fs.readFileSync(new URL('./media/audio-tone.json', import.meta.url)))
expect(hash(media)).toBe(manifest.sha256)
inputs = {
asr: { input: process.env.ARTPLAYER_ASR_ARTIFACT || 'workspace source', sha256: hash(asrCode) },
audioTrack: { input: process.env.ARTPLAYER_AUDIO_ARTIFACT || 'workspace source', sha256: hash(audioCode) },
asr: { provenance: asr.provenance, sha256: hash(asrCode) },
audioTrack: { provenance: audio.provenance, sha256: hash(audioCode) },
media: { file: 'test/browser/media/audio-tone.m4a', sha256: hash(media), bytes: media.length },
recognitionService: false,
scope: 'Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance',
scope: 'Native media/WebAudio composition; not physical speaker or device acceptance',
}
})
+6 -3
View File
@@ -2,14 +2,17 @@ import fs from 'node:fs'
import process from 'node:process'
import { verifyAsrContract } from '../../refactor/scripts/asr-contract.mjs'
import { hash, readMember } from '../../refactor/scripts/releases.mjs'
import { compilePackage } from '../helpers/load.js'
import { browserCandidate } from '../helpers/browser-candidate.js'
import { expect, test } from './fixtures.js'
let contract
let candidateProvenance
let candidateCode
test.beforeAll(async () => {
contract = await verifyAsrContract()
candidateCode = process.env.ARTPLAYER_ASR_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_ASR_ARTIFACT) : await compilePackage('artplayer-plugin-asr', 'umd')
const candidate = await browserCandidate('artplayer-plugin-asr', process.env.ARTPLAYER_ASR_ARTIFACT)
candidateCode = candidate.code
candidateProvenance = candidate.provenance
})
for (const version of ['2.0.0', '2.1.0', 'candidate']) {
@@ -18,7 +21,7 @@ for (const version of ['2.0.0', '2.1.0', 'candidate']) {
const release = [contract.baseline.release, ...contract.baseline.previous].find(release => release.version === version)
const code = version === 'candidate' ? candidateCode : readMember(contract.archives.get(version), 'package/dist/artplayer-plugin-asr.js')
const media = fs.readFileSync(new URL('./media/audio-tone.m4a', import.meta.url))
await testInfo.attach('asr-inputs', { contentType: 'application/json', body: JSON.stringify({ version, core, pluginSha256: hash(code), integrity: release?.integrity || null, candidate: process.env.ARTPLAYER_ASR_ARTIFACT || 'workspace source', mediaSha256: hash(media), media: 'local mono AAC 440 Hz tone', networkService: false }) })
await testInfo.attach('asr-inputs', { contentType: 'application/json', body: JSON.stringify({ version, core, pluginSha256: hash(code), selected: version === 'candidate' ? candidateProvenance : { kind: 'published', version, sha256: hash(code) }, integrity: release?.integrity || null, mediaSha256: hash(media), media: 'local mono AAC 440 Hz tone', networkService: false }) })
await page.goto(`/test/player.html?core=${core}`)
const capabilities = await page.evaluate(() => ({ AudioContext: typeof window.AudioContext, webkitAudioContext: typeof window.webkitAudioContext, AudioWorkletNode: typeof window.AudioWorkletNode }))
await testInfo.attach('asr-audio-capabilities', { contentType: 'application/json', body: JSON.stringify(capabilities) })
+6 -4
View File
@@ -1,13 +1,15 @@
import fs from 'node:fs'
import process from 'node:process'
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'
let provenance
let code
test.beforeAll(async () => {
expect(process.env.ARTPLAYER_BROWSER_ARTIFACTS, 'Explicit source/artifact tests do not represent installed packages').toBeFalsy()
code = process.env.ARTPLAYER_ASR_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_ASR_ARTIFACT, 'utf8') : await compilePackage('artplayer-plugin-asr', 'umd')
const candidate = await browserCandidate('artplayer-plugin-asr', process.env.ARTPLAYER_ASR_ARTIFACT)
code = candidate.code
provenance = candidate.provenance
})
test.afterEach(async ({ page }, testInfo) => {
@@ -33,7 +35,7 @@ for (const core of ['published', 'candidate']) {
})
await page.goto(`/test/player.html?core=${core}`)
const capabilities = await page.evaluate(() => ({ context: typeof (window.AudioContext || window.webkitAudioContext), worklet: typeof window.AudioWorkletNode, capture: typeof HTMLMediaElement.prototype.captureStream, mozCapture: typeof HTMLMediaElement.prototype.mozCaptureStream }))
await testInfo.attach('capture-cors-inputs', { contentType: 'application/json', body: JSON.stringify({ core, capabilities, plugin: process.env.ARTPLAYER_ASR_ARTIFACT || 'workspace source', pluginSha256: hash(code), mediaSha256: hash(fs.readFileSync(new URL('./media/audio-tone.m4a', import.meta.url))), nativeCapture: true, forcedException: false, analyserBinding: false, physicalAudioOutput: false, scope: 'Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output' }) })
await testInfo.attach('capture-cors-inputs', { contentType: 'application/json', body: JSON.stringify({ core, capabilities, provenance, pluginSha256: hash(code), mediaSha256: hash(fs.readFileSync(new URL('./media/audio-tone.m4a', import.meta.url))), nativeCapture: true, forcedException: false, analyserBinding: false, physicalAudioOutput: false, scope: 'Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output' }) })
if (capabilities.context === 'undefined') {
expect(process.platform).toBe('win32')
expect(testInfo.project.name).toBe('webkit')
+6 -3
View File
@@ -2,14 +2,17 @@ import fs from 'node:fs'
import process from 'node:process'
import { verifyAsrContract } from '../../refactor/scripts/asr-contract.mjs'
import { hash, readMember } from '../../refactor/scripts/releases.mjs'
import { compilePackage } from '../helpers/load.js'
import { browserCandidate } from '../helpers/browser-candidate.js'
import { expect, test } from './fixtures.js'
let contract
let candidateProvenance
let candidateCode
test.beforeAll(async () => {
contract = await verifyAsrContract()
candidateCode = process.env.ARTPLAYER_ASR_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_ASR_ARTIFACT) : await compilePackage('artplayer-plugin-asr', 'umd')
const candidate = await browserCandidate('artplayer-plugin-asr', process.env.ARTPLAYER_ASR_ARTIFACT)
candidateCode = candidate.code
candidateProvenance = candidate.provenance
})
for (const version of ['2.1.0', 'candidate']) {
@@ -26,7 +29,7 @@ for (const version of ['2.1.0', 'candidate']) {
test.skip(true, 'Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance')
}
const code = version === 'candidate' ? candidateCode : readMember(contract.archives.get(version), 'package/dist/artplayer-plugin-asr.js')
await testInfo.attach('asr-cors-inputs', { contentType: 'application/json', body: JSON.stringify({ version, core, scenario, pluginSha256: hash(code), mediaSha256: hash(fs.readFileSync(new URL('./media/audio-tone.m4a', import.meta.url))), physicalAudioOutput: false, securityBypass: false }) })
await testInfo.attach('asr-cors-inputs', { contentType: 'application/json', body: JSON.stringify({ version, core, scenario, pluginSha256: hash(code), selected: version === 'candidate' ? candidateProvenance : { kind: 'published', version, sha256: hash(code) }, mediaSha256: hash(fs.readFileSync(new URL('./media/audio-tone.m4a', import.meta.url))), physicalAudioOutput: false, securityBypass: false }) })
const responses = []
page.on('response', (response) => {
if (/\/test\/(?:asr-cors\.m4a|audio-tone\.m4a|asr-opaque-redirect)(?:\?|$)/u.test(response.url()))
+6 -4
View File
@@ -1,15 +1,17 @@
import fs from 'node:fs'
import process from 'node:process'
import { verifyAsrContract } from '../../refactor/scripts/asr-contract.mjs'
import { hash, readMember } from '../../refactor/scripts/releases.mjs'
import { compilePackage } from '../helpers/load.js'
import { browserCandidate } from '../helpers/browser-candidate.js'
import { expect, test } from './fixtures.js'
let contract
let candidateProvenance
let candidateCode
test.beforeAll(async () => {
contract = await verifyAsrContract()
candidateCode = process.env.ARTPLAYER_ASR_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_ASR_ARTIFACT) : await compilePackage('artplayer-plugin-asr', 'umd')
const candidate = await browserCandidate('artplayer-plugin-asr', process.env.ARTPLAYER_ASR_ARTIFACT)
candidateCode = candidate.code
candidateProvenance = candidate.provenance
})
for (const { version, captureOnly } of [{ version: '2.1.0', captureOnly: false }, { version: 'candidate', captureOnly: false }, { version: 'candidate', captureOnly: true }]) {
@@ -19,7 +21,7 @@ for (const { version, captureOnly } of [{ version: '2.1.0', captureOnly: false }
await page.goto(`/test/player.html?core=${core}`)
const capabilities = await page.evaluate(() => ({ context: typeof AudioContext, worklet: typeof AudioWorkletNode, capture: typeof HTMLMediaElement.prototype.captureStream, mozCapture: typeof HTMLMediaElement.prototype.mozCaptureStream }))
const forceRejection = testInfo.project.name === 'firefox' && !captureOnly
await testInfo.attach('asr-fallback-inputs', { contentType: 'application/json', body: JSON.stringify({ version, core, captureOnly, pluginSha256: hash(code), capabilities, forcedException: forceRejection, reason: captureOnly ? 'Explicit capture mode never binds the element; no forced exceptions' : forceRejection ? 'Firefox permits a second native media source; force only binding rejection to reach real captureStream/Worklet' : 'Native duplicate binding rejection', externalContext: 'Native media-element source remains owned and running', physicalOutput: false }) })
await testInfo.attach('asr-fallback-inputs', { contentType: 'application/json', body: JSON.stringify({ version, core, captureOnly, pluginSha256: hash(code), selected: version === 'candidate' ? candidateProvenance : { kind: 'published', version, sha256: hash(code) }, capabilities, forcedException: forceRejection, reason: captureOnly ? 'Explicit capture mode never binds the element; no forced exceptions' : forceRejection ? 'Firefox permits a second native media source; force only binding rejection to reach real captureStream/Worklet' : 'Native duplicate binding rejection', externalContext: 'Native media-element source remains owned and running', physicalOutput: false }) })
if (capabilities.context !== 'function') {
expect(process.platform).toBe('win32')
expect(testInfo.project.name).toBe('webkit')
+6 -3
View File
@@ -2,14 +2,17 @@ import fs from 'node:fs'
import process from 'node:process'
import { verifyAsrContract } from '../../refactor/scripts/asr-contract.mjs'
import { hash, readMember } from '../../refactor/scripts/releases.mjs'
import { compilePackage } from '../helpers/load.js'
import { browserCandidate } from '../helpers/browser-candidate.js'
import { expect, test } from './fixtures.js'
let contract
let candidateProvenance
let candidateCode
test.beforeAll(async () => {
contract = await verifyAsrContract()
candidateCode = process.env.ARTPLAYER_ASR_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_ASR_ARTIFACT) : await compilePackage('artplayer-plugin-asr', 'umd')
const candidate = await browserCandidate('artplayer-plugin-asr', process.env.ARTPLAYER_ASR_ARTIFACT)
candidateCode = candidate.code
candidateProvenance = candidate.provenance
})
for (const version of ['2.1.0', 'candidate']) {
@@ -23,7 +26,7 @@ for (const version of ['2.1.0', 'candidate']) {
expect(testInfo.project.name).toBe('webkit')
test.skip(true, 'Windows WebKit lacks native AudioContext/AudioWorkletNode')
}
await testInfo.attach('asr-playback-inputs', { contentType: 'application/json', body: JSON.stringify({ version, core, pluginSha256: hash(code), mediaSha256: hash(fs.readFileSync(new URL('./media/audio-tone.m4a', import.meta.url))), meter: 'Native unity analyser inserted between plugin gain and destination', physicalAudioOutput: false }) })
await testInfo.attach('asr-playback-inputs', { contentType: 'application/json', body: JSON.stringify({ version, core, pluginSha256: hash(code), selected: version === 'candidate' ? candidateProvenance : { kind: 'published', version, sha256: hash(code) }, mediaSha256: hash(fs.readFileSync(new URL('./media/audio-tone.m4a', import.meta.url))), meter: 'Native unity analyser inserted between plugin gain and destination', physicalAudioOutput: false }) })
await page.addScriptTag({ content: code.toString() })
await page.evaluate(() => {
const NativeContext = window.AudioContext
+6 -6
View File
@@ -1,14 +1,14 @@
import fs from 'node:fs'
import process from 'node:process'
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'
let provenance
let candidate
test.beforeAll(async () => {
candidate = process.env.ARTPLAYER_CHROMECAST_ARTIFACT
? fs.readFileSync(process.env.ARTPLAYER_CHROMECAST_ARTIFACT, 'utf8')
: await compilePackage('artplayer-plugin-chromecast', 'umd')
const input = await browserCandidate('artplayer-plugin-chromecast', process.env.ARTPLAYER_CHROMECAST_ARTIFACT)
candidate = input.code
provenance = input.provenance
})
async function setup(page, core, testInfo, loaded = true) {
@@ -112,7 +112,7 @@ async function setup(page, core, testInfo, loaded = true) {
}
}, loaded)
await expect.poll(() => page.evaluate(() => window.castPlayers.every(art => art.isReady))).toBe(true)
await testInfo.attach('chromecast-controlled-browser-inputs', { contentType: 'application/json', body: JSON.stringify({ core, artifact: process.env.ARTPLAYER_CHROMECAST_ARTIFACT || 'candidate source', sha256: hash(candidate), sdk: 'controlled stub; no receiver, discovery, real SDK or Cast media playback acceptance', native: 'ArtPlayer core, DOM controls, browser click dispatch and local video' }) })
await testInfo.attach('chromecast-controlled-browser-inputs', { contentType: 'application/json', body: JSON.stringify({ core, provenance, sha256: hash(candidate), sdk: 'controlled stub; no receiver, discovery, real SDK or Cast media playback acceptance', native: 'ArtPlayer core, DOM controls, browser click dispatch and local video' }) })
return external
}
+12 -2
View File
@@ -4,7 +4,8 @@ import path from 'node:path'
import process from 'node:process'
// eslint-disable-next-line test/no-import-node-test -- This fixture exercises the repository's Node test runner.
import { test } from 'node:test'
import { checkFiles, checkPackages, publishedConsumer } from '../scripts/package-check.mjs'
import { installedPackages } from '../scripts/browser-validation/scope.ts'
import { checkFiles, checkPackages, packageOptions, publishedConsumer } from '../scripts/package-check.mjs'
import { removeConsumer, runtimeConsumer } from '../scripts/package-consumer.mjs'
import { emitterContracts } from './contracts/emitter.js'
import { ambilightCandidate } from './helpers/ambilight.js'
@@ -15,6 +16,15 @@ import { dpipCandidate } from './helpers/dpip.js'
import { multipleSubtitlesCandidate } from './helpers/multiple-subtitles.js'
import { vttThumbnailCandidate } from './helpers/vtt-thumbnail.js'
test('Package CLI keeps legacy defaults and prepares the same roster the browser requires', () => {
assert.deepEqual(packageOptions([]), { release: false, include: [] })
assert.deepEqual(packageOptions(['--release']), { release: true, include: [] })
assert.deepEqual(packageOptions(['--include=artplayer-plugin-asr']), { release: false, include: ['artplayer-plugin-asr'] })
assert.deepEqual(['artplayer', 'artplayer-plugin-chapter', ...packageOptions(['--browser']).include], installedPackages)
for (const args of [['--browser', '--release'], ['--browser', '--include=artplayer-plugin-asr'], ['--broser'], ['--include='], ['--include=x,x'], ['--include=x', '--include=y'], ['--browser', '--browser']])
assert.throws(() => packageOptions(args))
})
test('Additional browser packages cannot be mistaken for full release consumer acceptance', async () => {
await assert.rejects(checkPackages({ release: true, include: ['artplayer-plugin-ambilight'] }), /not full release acceptance/)
})
@@ -41,7 +51,7 @@ test('Explicit installed plugin maps never fall back to source or frozen workspa
delete process.env.ARTPLAYER_AUTO_THUMBNAIL_BASELINE
process.env.ARTPLAYER_AUTO_THUMBNAIL_ARTIFACT = 'override.js'
await assert.rejects(autoThumbnailCandidate(), /cannot override artplayer-plugin-auto-thumbnail artifact/)
for (const name of ['artplayer-plugin-hls-control', 'artplayer-plugin-dash-control']) {
for (const name of ['artplayer-plugin-hls-control', 'artplayer-plugin-dash-control', 'artplayer-plugin-asr', 'artplayer-plugin-chromecast']) {
await assert.rejects(browserCandidate(name), { code: 'ENOENT' })
await assert.rejects(browserCandidate(name, 'override.js'), new RegExp(`cannot override ${name} artifact`))
}
+6 -2
View File
@@ -9,8 +9,8 @@ step through `ARTPLAYER_BROWSER_ARTIFACTS`. See
browser scope and the remaining package-specific release gates.
Run `yarn test:package` with the pinned Node and Yarn after a frozen install. The
default consumer scope is core and chapter. For Ambilight/Canvas/Document PiP/Ads/Audio Track/VTT Thumbnail/Multiple Subtitles/HLS Control/DASH Control/Auto Thumbnail browser work,
run `yarn test:package --include=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`.
default consumer scope is core and chapter. For the shared installed browser scope,
run `yarn test:package --browser`.
These four additional packages have reviewed published contracts and use the same
source snapshot/build/pack/offline-install/frozen-reinstall pipeline. Installed
files are copied only after archive hash checks. The report explicitly separates
@@ -97,3 +97,7 @@ bundles or contradictory frozen-workspace flags fail instead of rebuilding sourc
removed historical files, then deliberately removes an actual published default
export and required entry in an isolated consumer to prove runtime failures are
detected. It is part of `yarn test:node`.
The --browser option follows the shared installed browser roster automatically;
do not copy a manual package list into CI. Explicit --include remains supported
for a deliberate subset. --browser cannot combine with --include or --release.