test(danmuku): [PKG-DANMUKU-08] verify native PiP and current-source mask combinations

This commit is contained in:
Harvey Zhao committed 2026-09-14 20:50:31 +08:00
1 parent 88ce836f85
commit b54a890832
9 files changed
+4741 -9

No files matched your search

@@ -165,7 +165,10 @@ by its original player. Native shared TensorFlow backend state remains unverifie
`yarn test:browser test/browser/danmuku-mask-native.spec.js --workers=1` now covers `yarn test:browser test/browser/danmuku-mask-native.spec.js --workers=1` now covers
actual local model loading, bitmap alpha and owned canvas cleanup, new/old cores, actual local model loading, bitmap alpha and owned canvas cleanup, new/old cores,
real Danmuku delivery after model readiness, pause/seek and CSS web fullscreen. real Danmuku delivery after model readiness, pause/seek and CSS web fullscreen.
Set ARTPLAYER_MASK_ARTIFACT to the legacy global build for the second format. Both Mask and Danmuku compile from current source by default. Set
ARTPLAYER_MASK_ARTIFACT and ARTPLAYER_DANMUKU_ARTIFACT to explicit global builds
for artifact verification; attachments record both inputs and hashes. Missing
specified files are errors, not a source or dist fallback.
Archive each report before another browser run. The startup timestamp diagnostic Archive each report before another browser run. The startup timestamp diagnostic
can still miss a row; do not interpret the post-ready assertions as losslessness. can still miss a row; do not interpret the post-ready assertions as losslessness.
Native acceptance still needs source changes, multiple players, failure recovery, Native acceptance still needs source changes, multiple players, failure recovery,
@@ -1,5 +1,20 @@
# Danmuku maintenance map # Danmuku maintenance map
`test/browser/danmuku-dpip.spec.js` exercises the candidate with old/current cores
in two sequential native Document PiP windows. It checks timestamp-delivered text,
adopted layer and heatmap nodes, stylesheet layout, one surviving track Worker,
close/native-close/destroy cleanup, and no retained scheduling operation. Native
API absence is reported separately; it does not simulate a popup or count as
playback evidence. This is not a background-throttling or device benchmark.
Set `ARTPLAYER_DANMUKU_ARTIFACT` and `ARTPLAYER_DPIP_ARTIFACT` together to check
the corresponding built formats; both loaded hashes appear in each attachment.
The actual Mask/model combination in `danmuku-mask-native.spec.js` now compiles
both plugins from source by default. `ARTPLAYER_MASK_ARTIFACT` and
`ARTPLAYER_DANMUKU_ARTIFACT` override them independently and fail on missing files.
Do not substitute a stale dist file for source checks. Full old-plugin/core,
OS fullscreen, installation, performance and device acceptance remain task 08/09.
The compatibility reference is the actual npm 5.3.0 package. Its archive, The compatibility reference is the actual npm 5.3.0 package. Its archive,
historical declarations, earlier exports and failure evidence are indexed in historical declarations, earlier exports and failure evidence are indexed in
`refactor/baselines/danmuku-release.json` and `refactor/baselines/danmuku-release.json` and
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,68 @@
# PKG-DANMUKU-08 原生画中画与 Mask 源码组合检查点
本任务仍 doing;这是组合覆盖的增量,不是完整弹幕或发布验收。
## 改变与原因
新增 `test/browser/danmuku-dpip.spec.js`,在新旧核心中让播放器先播放,再通过
真实用户点击打开系统 Document PiP 窗口。每个播放器连续打开两次窗口,在窗口中
按媒体时间投放弹幕,检查可见文本、实际尺寸、绝对定位、图层及热力图的 document
归属、真实 Worker 回复和复用。分别以插件关闭、原生关闭、播放器销毁结束。
结束时核对 Worker 只终止一次、调度 RAF/operation 清空、设置/弹幕节点和占位
清零;普通关闭后检查原父节点恢复且媒体时间继续前进。
没有用 iframe 模拟窗口。实际 API 不可用的场景单独输出 unavailable,不能计入
播放通过数;本机 WebKit 如实保留该限制。此测试不声称浏览器后台节流或物理设备
性能已经通过,也不把两次开窗当作两次播放器重新装卸。
`danmuku-mask-native.spec.js` 原默认读取 Mask 与 Danmuku 已有 dist,现在默认并行
构建当前源码 UMD;显式 ARTPLAYER_MASK_ARTIFACT / ARTPLAYER_DANMUKU_ARTIFACT
可分别选择产物,文件不存在直接失败。附件记录两个实际输入与摘要。修正的是测试
的输入来源,不修改生产逻辑,不宣称其他仍默认读取 dist 的测试都已完成来源审查。
包内 Danmuku 与 Mask 架构文档同步了入口、覆盖及限制。没有增加依赖、公开类型、
运行时 API 或 DOM/CSS hook。两包正常生产构建重新执行后,产物与原跟踪内容相同。
## 实际验证
Node24.21.0、Yarn1.22.22、Windows,Chromium153.0.8010.12、Firefox155.0、WebKit26.6。
报告指纹、逐例输入和回收结果见[机器证据](../baselines/danmuku-combination-checkpoint.json)。
| 范围 | 结果及含义 |
| --- | --- |
| 首轮 Chromium 原生 PiP | 4通过、2失败;失败为销毁事件的测试预期错误 |
| 更正后的源码 PiP | 18项退出0:12项原生窗口播放/清理,6项WebKit API不可用 |
| main 两插件构建产物 PiP | 18项退出0,同上;新增可见文本断言和窗口截图 |
| legacy 两插件构建产物 PiP | 18项退出0,同上;新增可见文本断言和窗口截图 |
| 当前 Mask + Danmuku 源码、真实本地模型 | 9项通过,三核心组合 × 三浏览器,含暂停/seek/网页全屏 |
| 新增/修改浏览器文件 ESLint | 通过 |
| Danmuku、Document PiP 正常三格式构建 | 通过;现有跟踪产物未变化 |
三个 PiP 报告各有24次原生开窗,但不能叠加成独立播放器样本。没有跳过或重试。
源码 PiP 报告在额外可见文本断言与截图之前;main/legacy包含这两项增强。
已人工查看 main 的原生窗口截图,视频与顶部弹幕可见,发送栏和进度控件布局正常。
截图在 `refactor/.cache/danmuku08-native-window.png`,原附件保留在 main 报告中。
首轮2个失败不能删除:原先预期 destroy 时还有最后一个 document-pip=false。
现有 Canvas 原生 PiP 测试和 Document PiP 生命周期契约均规定销毁后不再发公开
关闭事件;据此将销毁结尾明确断言为 [true,false,true],普通关闭仍严格断言
[true,false,true,false]。资源释放检查全部保留,没有为了通过而增加合成事件。
真实模型检查继续保留启动期时间窗口的诊断。断言的是模型就绪之后按媒体时间
显示的行,并非启动阶段完全不丢行,也不是私有 GPU/WASM 完整释放证明。
## 接续与重跑
PKG-DANMUKU-08仍需补旧发布插件/新旧核心组合、系统全屏、重复播放器装卸,
并结合07基线比较组合负载性能。09继续最终安装分发、示例、文档及 Worker 入口。
与Mask、Document PiP关联的设备/后台/模型内部资源门槛不由本检查点关闭。
源码入口:`yarn test:browser:source test/browser/danmuku-dpip.spec.js --workers=2`;
模型组合:`yarn test:browser:source test/browser/danmuku-mask-native.spec.js
--grep 'actual model and Danmuku' --workers=2`。产物PiP运行同时指定
ARTPLAYER_DANMUKU_ARTIFACT 和 ARTPLAYER_DPIP_ARTIFACT;每次运行前归档前一份报告。
正常构建分别使用 `yarn build artplayer-plugin-danmuku` 与
`yarn build artplayer-plugin-document-pip`。
本检查点独立本地commit,提交审计完成后再继续下一批;无push、部署或发布。
回退本检查点即可恢复测试输入与覆盖,生产运行时没有变化。
+3 -2
View File
@@ -4,7 +4,7 @@
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 261 项,范围 22 个包及工作区/示例。 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 261 项,范围 22 个包及工作区/示例。
状态:todo 49 / doing 20 / blocked 0 / done 192 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 状态:todo 48 / doing 21 / blocked 0 / done 192 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
@@ -335,7 +335,7 @@
| PKG-DANMUKU-05 | artplayer-plugin-danmuku<br>整理 DOM 渲染、设置、热力图与 worker | PKG-DANMUKU-04, CORE-14, CORE-18 | renderer/setting/heatmap/worker 职责及资源归属 | mount/icons/设置和 worker 协议保持,销毁无后台工作 | H | done | | PKG-DANMUKU-05 | artplayer-plugin-danmuku<br>整理 DOM 渲染、设置、热力图与 worker | PKG-DANMUKU-04, CORE-14, CORE-18 | renderer/setting/heatmap/worker 职责及资源归属 | mount/icons/设置和 worker 协议保持,销毁无后台工作 | H | done |
| PKG-DANMUKU-06 | artplayer-plugin-danmuku<br>迁移 TS 与公开声明 | PKG-DANMUKU-05, ENG-04, ENG-06 | 自有模块、worker 消息、option/item/result 类型 | 旧参数、扩展字段和事件消费通过,vendored 边界清楚 | H | done | | PKG-DANMUKU-06 | artplayer-plugin-danmuku<br>迁移 TS 与公开声明 | PKG-DANMUKU-05, ENG-04, ENG-06 | 自有模块、worker 消息、option/item/result 类型 | 旧参数、扩展字段和事件消费通过,vendored 边界清楚 | H | done |
| PKG-DANMUKU-07 | artplayer-plugin-danmuku<br>弹幕能力稳定性验收 | PKG-DANMUKU-06, PKG-DANMUKU-10, PKG-DANMUKU-11, PKG-DANMUKU-12 | 负载/渲染/资源对比及 mask 可依赖的稳定边界 | 在试点核心中旧插件 API 与关键帧路径通过 | H | done | | PKG-DANMUKU-07 | artplayer-plugin-danmuku<br>弹幕能力稳定性验收 | PKG-DANMUKU-06, PKG-DANMUKU-10, PKG-DANMUKU-11, PKG-DANMUKU-12 | 负载/渲染/资源对比及 mask 可依赖的稳定边界 | 在试点核心中旧插件 API 与关键帧路径通过 | H | done |
| PKG-DANMUKU-08 | artplayer-plugin-danmuku<br>完成新旧核心与组合验收 | PKG-DANMUKU-07, CORE-22 | 最终核心/旧核心、mask/fullscreen/PiP 组合报告 | 性能无未解释退化,反复装卸无累计资源 | H | todo | | PKG-DANMUKU-08 | artplayer-plugin-danmuku<br>完成新旧核心与组合验收 | PKG-DANMUKU-07, CORE-22 | 最终核心/旧核心、mask/fullscreen/PiP 组合报告 | 性能无未解释退化,反复装卸无累计资源 | H | doing |
| PKG-DANMUKU-09 | artplayer-plugin-danmuku<br>完成分发、示例与文档 | PKG-DANMUKU-08, ENG-07 | danmuku.js、README、声明、worker 与产物 | tarball 和静态 icons 等旧调用通过,有独立回退版本 | H | todo | | PKG-DANMUKU-09 | artplayer-plugin-danmuku<br>完成分发、示例与文档 | PKG-DANMUKU-08, ENG-07 | danmuku.js、README、声明、worker 与产物 | tarball 和静态 icons 等旧调用通过,有独立回退版本 | H | todo |
| PKG-DANMUKU-10 | artplayer-plugin-danmuku<br>修复 issue958 密集热力图过高与曲线裁平 | PKG-DANMUKU-05 | 自动密度缩放与曲线边界、历史自定义坐标兼容及真实浏览器红绿证据 | 16000条三种分布在两核心三引擎和source/main/legacy不遮挡;显式轴和自定义points保持旧解释,有单独commit | M | done | | PKG-DANMUKU-10 | artplayer-plugin-danmuku<br>修复 issue958 密集热力图过高与曲线裁平 | PKG-DANMUKU-05 | 自动密度缩放与曲线边界、历史自定义坐标兼容及真实浏览器红绿证据 | 16000条三种分布在两核心三引擎和source/main/legacy不遮挡;显式轴和自定义points保持旧解释,有单独commit | M | done |
| PKG-DANMUKU-11 | artplayer-plugin-danmuku<br>修复 Worker 隐藏等待吞掉可见寿命 | PKG-DANMUKU-06 | 成功放置时的可见计时与延迟回复回归 | 真实 Worker 延迟下完整显示寿命;保留暂停/回收/取消和公开契约 | M | done | | PKG-DANMUKU-11 | artplayer-plugin-danmuku<br>修复 Worker 隐藏等待吞掉可见寿命 | PKG-DANMUKU-06 | 成功放置时的可见计时与延迟回复回归 | 真实 Worker 延迟下完整显示寿命;保留暂停/回收/取消和公开契约 | M | done |
@@ -611,6 +611,7 @@
- PKG-DANMUKU-05: [记录](changes/2026-09-13-PKG-DANMUKU-05-resources.md) [记录](baselines/danmuku-resources-validation.json) - PKG-DANMUKU-05: [记录](changes/2026-09-13-PKG-DANMUKU-05-resources.md) [记录](baselines/danmuku-resources-validation.json)
- PKG-DANMUKU-06: [记录](changes/2026-09-13-PKG-DANMUKU-06-types.md) [记录](baselines/danmuku-types-validation.json) - PKG-DANMUKU-06: [记录](changes/2026-09-13-PKG-DANMUKU-06-types.md) [记录](baselines/danmuku-types-validation.json)
- PKG-DANMUKU-07: [记录](changes/2026-09-13-PKG-DANMUKU-07-stability.md) [记录](baselines/danmuku-stability-validation.json) [记录](danmuku-stability.md) - PKG-DANMUKU-07: [记录](changes/2026-09-13-PKG-DANMUKU-07-stability.md) [记录](baselines/danmuku-stability-validation.json) [记录](danmuku-stability.md)
- PKG-DANMUKU-08: [记录](changes/2026-09-14-PKG-DANMUKU-08-native-combinations.md) [记录](baselines/danmuku-combination-checkpoint.json)
- PKG-CANVAS-01: [记录](changes/2026-09-12-PKG-CANVAS-01-contract.md) [记录](baselines/canvas-contract.md) [记录](baselines/canvas-release.json) [记录](canvas-validation.md) [记录](baselines/canvas-contract-validation.json) - PKG-CANVAS-01: [记录](changes/2026-09-12-PKG-CANVAS-01-contract.md) [记录](baselines/canvas-contract.md) [记录](baselines/canvas-release.json) [记录](canvas-validation.md) [记录](baselines/canvas-contract-validation.json)
- PKG-CANVAS-02: [记录](changes/2026-09-12-PKG-CANVAS-02-tests.md) [记录](baselines/canvas-behavior-validation.json) [记录](canvas-validation.md) - PKG-CANVAS-02: [记录](changes/2026-09-12-PKG-CANVAS-02-tests.md) [记录](baselines/canvas-behavior-validation.json) [记录](canvas-validation.md)
- PKG-CANVAS-03: [记录](changes/2026-09-12-PKG-CANVAS-03-lifecycle.md) [记录](baselines/canvas-lifecycle-validation.json) [记录](canvas-validation.md) - PKG-CANVAS-03: [记录](changes/2026-09-12-PKG-CANVAS-03-lifecycle.md) [记录](baselines/canvas-lifecycle-validation.json) [记录](canvas-validation.md)
+15
View File
@@ -1,5 +1,20 @@
# 进度与证据 # 进度与证据
## PKG-DANMUKU-08 原生画中画与真实 Mask 组合检查点(仍 doing)
新增新旧核心中的两次原生Document PiP开窗,按媒体时间显示弹幕,检查图层/热力图
跨文档归属、Worker复用、播放继续及关闭/原生关闭/销毁后的RAF、operation、DOM回收。
源码/main/legacy各18项退出0:每份12原生播放+6WebKit API不可用记录,不能混计。
main/legacy另有可见文本断言及截图;已查看原生窗口视频、弹幕与发送栏布局。
首轮4/2失败为错误要求destroy后的关闭事件,按已有DPiP/Canvas契约修正,资源断言保留。
真实Mask组合默认输入从已有dist改为当前Mask/Danmuku源码,保留两个显式产物覆盖。
本地真实模型、三核心三引擎暂停/seek/网页全屏9项通过;两插件重新构建产物未变,
专项lint通过。见[记录](changes/2026-09-14-PKG-DANMUKU-08-native-combinations.md)与
[证据](baselines/danmuku-combination-checkpoint.json)。261项:192 done、21 doing、48 todo。
旧插件完整组合、OS全屏、反复装卸/组合性能及最终安装分发仍待08/09接续,不标完成。
本检查点独立本地提交并核验提交审计;无push/部署/发布。
## PKG-DASH-MENU-01 SDK 刷新设置子菜单修复完成 ## PKG-DASH-MENU-01 SDK 刷新设置子菜单修复完成
原legacy设置点击失败的trace显示根面板取代质量子菜单,setting仍显示。 原legacy设置点击失败的trace显示根面板取代质量子菜单,setting仍显示。
+5 -2
View File
@@ -3556,11 +3556,14 @@
"PKG-DANMUKU-07", "PKG-DANMUKU-07",
"CORE-22" "CORE-22"
], ],
"status": "todo", "status": "doing",
"risk": "H", "risk": "H",
"deliverable": "最终核心/旧核心、mask/fullscreen/PiP 组合报告", "deliverable": "最终核心/旧核心、mask/fullscreen/PiP 组合报告",
"acceptance": "性能无未解释退化,反复装卸无累计资源", "acceptance": "性能无未解释退化,反复装卸无累计资源",
"evidence": [] "evidence": [
"changes/2026-09-14-PKG-DANMUKU-08-native-combinations.md",
"baselines/danmuku-combination-checkpoint.json"
]
}, },
{ {
"id": "PKG-DANMUKU-09", "id": "PKG-DANMUKU-09",
+161
View File
@@ -0,0 +1,161 @@
import fs from 'node:fs'
import process from 'node:process'
import { hash } from '../../refactor/scripts/releases.mjs'
import { dpipCandidate } from '../helpers/dpip.js'
import { compilePackage } from '../helpers/load.js'
import { expect, test } from './fixtures.js'
const dpip = await dpipCandidate()
let danmuku
test.beforeAll(async () => {
danmuku = process.env.ARTPLAYER_DANMUKU_ARTIFACT
? fs.readFileSync(process.env.ARTPLAYER_DANMUKU_ARTIFACT, 'utf8')
: await compilePackage('artplayer-plugin-danmuku', 'umd')
})
for (const core of ['published', 'candidate']) {
for (const ending of ['close', 'native-close', 'destroy']) {
test(`${core}: Danmuku, heatmap and Worker survive native Document PiP ${ending}`, async ({ page, context }, testInfo) => {
await page.goto(`/test/player.html?core=${core}`)
const supported = await page.evaluate(() => typeof window.documentPictureInPicture?.requestWindow === 'function')
const evidence = { core, ending, supported, danmuku: { input: process.env.ARTPLAYER_DANMUKU_ARTIFACT || 'source UMD', sha256: hash(danmuku) }, dpip: { input: dpip.name, sha256: hash(dpip.code) }, outcome: 'incomplete', rounds: [] }
if (!supported) {
evidence.outcome = 'native-document-pip-unavailable'
await testInfo.attach('danmuku-dpip', { contentType: 'application/json', body: JSON.stringify(evidence) })
return
}
await page.evaluate(() => {
window.combinationWorkers = []
const NativeWorker = window.Worker
window.Worker = class extends NativeWorker {
constructor(...args) {
super(...args)
this.record = { terminated: 0, replies: 0 }
window.combinationWorkers.push(this.record)
this.addEventListener('message', () => this.record.replies++)
}
terminate() {
this.record.terminated++
return super.terminate()
}
}
})
await page.addScriptTag({ content: danmuku })
await page.addScriptTag({ content: `(() => { const module = { exports: {} }; const exports = module.exports; ${dpip.code}; window.dpipFactory = module.exports.default || module.exports; })();` })
await page.evaluate(() => {
window.combinationVisible = []
window.combinationErrors = []
window.combinationEvents = []
window.art = new window.Artplayer({
container: '.player',
url: '/assets/sample/video.mp4',
muted: true,
plugins: [window.artplayerPluginDanmuku({ danmuku: [], heatmap: true, emitter: true, speed: 4 }), window.dpipFactory({ fallbackToVideoPiP: false })],
})
window.danmuku = window.art.plugins.artplayerPluginDanmuku
window.danmukuOwner = window.danmuku.config({})
window.pip = window.art.plugins.artplayerPluginDocumentPip
window.combinationPlayer = window.art.template.$player
window.combinationLayer = window.art.template.$danmuku
window.combinationParent = window.combinationPlayer.parentNode
window.art.on('document-pip', value => window.combinationEvents.push(value))
window.art.on('artplayerPluginDanmuku:error', error => window.combinationErrors.push(String(error)))
window.art.on('artplayerPluginDanmuku:visible', row => window.combinationVisible.push({ text: row.text, ref: row.$ref, time: window.art.currentTime }))
document.querySelector('#play').onclick = () => window.art.play()
const open = document.createElement('button')
open.id = 'danmuku-open-pip'
open.textContent = 'Open Danmuku PiP'
open.onclick = () => {
window.openingDanmukuPip = window.pip.open()
}
document.body.append(open)
})
try {
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
await page.click('#play')
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.1)
for (let round = 0; round < 2; round++) {
const popupPromise = context.waitForEvent('page', { timeout: 8000 })
await page.click('#danmuku-open-pip')
const popup = await popupPromise
await page.evaluate(() => window.openingDanmukuPip)
await expect(popup.locator('video.art-video')).toBeVisible()
await expect(popup.locator('.artplayer-plugin-danmuku')).toHaveCount(1)
const text = `PiP row ${round}`
await page.evaluate(async (text) => {
await window.danmuku.load([{ text, time: window.art.currentTime + 0.5, mode: 1 }])
}, text)
await expect.poll(() => page.evaluate(text => window.combinationVisible.some(row => row.text === text), text)).toBe(true)
await expect(popup.getByText(text, { exact: true })).toBeVisible()
const snapshot = await page.evaluate((text) => {
const row = window.combinationVisible.find(row => row.text === text)
const rect = row.ref.getBoundingClientRect()
const popup = window.documentPictureInPicture.window
return {
time: window.art.currentTime,
rowTime: row.time,
width: rect.width,
height: rect.height,
adopted: row.ref.ownerDocument === popup.document && window.combinationLayer.ownerDocument === popup.document,
inLayer: window.combinationLayer.contains(row.ref),
position: popup.getComputedStyle(row.ref).position,
player: window.combinationPlayer.ownerDocument === popup.document,
heatmap: window.art.controls.heatmap.ownerDocument === popup.document && !!window.art.controls.heatmap.querySelector('svg'),
workers: window.combinationWorkers.map(worker => ({ ...worker })),
}
}, text)
evidence.rounds.push(snapshot)
expect(snapshot.adopted && snapshot.inLayer && snapshot.player && snapshot.heatmap).toBe(true)
expect(snapshot.width).toBeGreaterThan(0)
expect(snapshot.height).toBeGreaterThan(0)
expect(snapshot.position).toBe('absolute')
expect(snapshot.workers).toHaveLength(1)
expect(snapshot.workers[0].replies).toBeGreaterThan(0)
if (round === 0)
await testInfo.attach('native-danmuku-page', { contentType: 'image/png', body: await popup.screenshot() })
if (round === 1 && ending === 'destroy') {
await page.evaluate(() => window.art.destroy(false))
}
else {
await page.evaluate(ending => ending === 'native-close' ? window.documentPictureInPicture.window.close() : window.pip.close(), ending)
await expect.poll(() => page.evaluate(() => window.pip.isActive)).toBe(false)
expect(await page.evaluate(() => window.combinationPlayer.parentNode === window.combinationParent && window.combinationLayer.ownerDocument === document)).toBe(true)
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(snapshot.time + 0.1)
}
}
if (ending !== 'destroy')
await page.evaluate(() => window.art.destroy(false))
await expect.poll(() => page.evaluate(() => !window.documentPictureInPicture.window || window.documentPictureInPicture.window.closed)).toBe(true)
evidence.final = await page.evaluate(() => ({
active: window.pip.isActive,
workers: window.combinationWorkers,
errors: window.combinationErrors,
events: window.combinationEvents,
frame: window.danmukuOwner.scheduler.frame,
operation: window.danmukuOwner.scheduler.operation,
nodes: window.combinationLayer.children.length,
settings: document.querySelectorAll('.artplayer-plugin-danmuku').length,
placeholders: document.querySelectorAll('.artplayer-document-pip-placeholder').length,
}))
expect(evidence.final.active).toBe(false)
expect(evidence.final.workers).toHaveLength(1)
expect(evidence.final.workers[0].terminated).toBe(1)
expect(evidence.final.errors).toEqual([])
// Destroy suppresses further public events, as in the existing native PiP contract.
expect(evidence.final.events).toEqual(ending === 'destroy' ? [true, false, true] : [true, false, true, false])
expect(evidence.final.frame).toBeNull()
expect(evidence.final.operation).toBeNull()
expect(evidence.final.nodes + evidence.final.settings + evidence.final.placeholders).toBe(0)
evidence.outcome = 'native-playback-danmuku-and-cleanup-passed'
}
finally {
await testInfo.attach('danmuku-dpip', { contentType: 'application/json', body: JSON.stringify(evidence) })
await page.evaluate(() => {
if (window.art && !window.art.isDestroy)
window.art.destroy(false)
})
}
})
}
}
+11 -4
View File
@@ -2,10 +2,11 @@ import assert from 'node:assert/strict'
import fs from 'node:fs' import fs from 'node:fs'
import process from 'node:process' import process from 'node:process'
import { hash } from '../../refactor/scripts/releases.mjs' import { hash } from '../../refactor/scripts/releases.mjs'
import { compilePackage } from '../helpers/load.js'
import { expect, test } from './fixtures.js' import { expect, test } from './fixtures.js'
const artifact = process.env.ARTPLAYER_MASK_ARTIFACT || 'packages/artplayer-plugin-danmuku-mask/dist/artplayer-plugin-danmuku-mask.js' const artifact = process.env.ARTPLAYER_MASK_ARTIFACT || 'source UMD'
const code = fs.readFileSync(artifact, 'utf8') let code
const baseline = JSON.parse(fs.readFileSync('refactor/baselines/danmuku-mask-release.json', 'utf8')) const baseline = JSON.parse(fs.readFileSync('refactor/baselines/danmuku-mask-release.json', 'utf8'))
const assets = baseline.sdk.assets.map(({ file, sha256 }) => { const assets = baseline.sdk.assets.map(({ file, sha256 }) => {
const bytes = fs.readFileSync(file) const bytes = fs.readFileSync(file)
@@ -17,8 +18,14 @@ const assets = baseline.sdk.assets.map(({ file, sha256 }) => {
return { file, sha256: actual, baselineSha256: sha256, normalizedLF } return { file, sha256: actual, baselineSha256: sha256, normalizedLF }
}) })
const media = 'docs/assets/sample/steve-jobs.mp4' const media = 'docs/assets/sample/steve-jobs.mp4'
const danmukuFile = 'packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.js' const danmukuFile = process.env.ARTPLAYER_DANMUKU_ARTIFACT || 'source UMD'
const danmukuCode = fs.readFileSync(danmukuFile, 'utf8') let danmukuCode
test.beforeAll(async () => {
;[code, danmukuCode] = await Promise.all([
process.env.ARTPLAYER_MASK_ARTIFACT ? fs.readFileSync(artifact, 'utf8') : compilePackage('artplayer-plugin-danmuku-mask', 'umd'),
process.env.ARTPLAYER_DANMUKU_ARTIFACT ? fs.readFileSync(danmukuFile, 'utf8') : compilePackage('artplayer-plugin-danmuku', 'umd'),
])
})
for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) { for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) {
test(`Mask actual local MediaPipe model renders, stops and restarts with ${core} core`, async ({ page }, testInfo) => { test(`Mask actual local MediaPipe model renders, stops and restarts with ${core} core`, async ({ page }, testInfo) => {