mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
test(vtt-thumbnail): [PKG-VTT-THUMB-02] reproduce parsing and lifecycle failures
This commit is contained in:
1 parent
d4ede650e8
commit
05244d1104
10 files changed
+245
-11
No files matched your search
+3
-2
@@ -51,7 +51,7 @@
|
||||
"ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:docs && yarn test:imports",
|
||||
"test:imports": "node --test test/esm.test.js test/i18n.test.js test/ssr.test.js",
|
||||
"typecheck": "node scripts/typecheck.mjs",
|
||||
"test:unit": "node --test test/auto-thumbnail-frames.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast.test.js test/vast-lifecycle.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js",
|
||||
"test:unit": "node --test test/vtt-thumbnail.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast.test.js test/vast-lifecycle.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js",
|
||||
"test:coverage": "node --test test/coverage.test.js && node scripts/coverage.mjs",
|
||||
"test": "yarn test:node && yarn test:baseline",
|
||||
"test:browser": "playwright test",
|
||||
@@ -85,7 +85,8 @@
|
||||
"test:iframe-types-package": "node refactor/scripts/iframe-package-types.mjs",
|
||||
"test:thumbnail": "node --test test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js refactor/scripts/thumbnail-contract.test.mjs refactor/scripts/thumbnail-types.test.mjs",
|
||||
"test:thumbnail-types-package": "node refactor/scripts/thumbnail-package-types.mjs",
|
||||
"test:auto-thumbnail": "node --test test/auto-thumbnail-frames.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js refactor/scripts/auto-thumbnail-contract.test.mjs"
|
||||
"test:auto-thumbnail": "node --test test/auto-thumbnail-frames.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js refactor/scripts/auto-thumbnail-contract.test.mjs",
|
||||
"test:vtt-thumbnail": "node --test test/vtt-thumbnail.test.js refactor/scripts/vtt-thumbnail-contract.test.mjs"
|
||||
},
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"task": "PKG-VTT-THUMB-02",
|
||||
"status": "done",
|
||||
"parentCommit": "d4ede650e8188dab3c21524cf2e524887412a436",
|
||||
"checks": [
|
||||
{
|
||||
"command": "node --test test/vtt-thumbnail.test.js",
|
||||
"result": "pass",
|
||||
"tests": 108,
|
||||
"log": "refactor/.cache/vtt-thumbnail02-initial.log"
|
||||
},
|
||||
{
|
||||
"command": "yarn test:vtt-thumbnail",
|
||||
"result": "pass",
|
||||
"tests": 199,
|
||||
"log": "refactor/.cache/vtt-thumbnail02-targeted.log"
|
||||
},
|
||||
{
|
||||
"command": "yarn ci:check",
|
||||
"result": "pass",
|
||||
"tests": {
|
||||
"unit": 1680,
|
||||
"engineering": 14,
|
||||
"baseline": 323,
|
||||
"total": 2017
|
||||
},
|
||||
"log": "refactor/.cache/vtt-thumbnail02-ci.log",
|
||||
"logSha256": "1d478933a55cde3b947b5a20a6dc7f1d346729a8afaa6adf5075365f7fcd0643"
|
||||
}
|
||||
],
|
||||
"fixture": {
|
||||
"file": "docs/assets/sample/bbb-thumbnails.vtt",
|
||||
"sha256": "1b88c30cf67d5c3da8281a150e8277858781c8077df0c6bfd2f32550da7323ba",
|
||||
"cues": 120
|
||||
},
|
||||
"limitations": [
|
||||
"Historical failures are reproduced, not fixed",
|
||||
"Controlled host only; no native browser, installed package or final core claim",
|
||||
"Twelve fixtures preserve actual archive/source distinctions from task 01"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# PKG-VTT-THUMB-02 异常与生命周期复现
|
||||
|
||||
新增 test/vtt-thumbnail.test.js,108 项历史测试全部通过。这里的通过表示
|
||||
**成功复现历史结果**,不表示候选已经修复。十二个固定夹具复用 01 的归档/源码校验;
|
||||
1.0.0 仅有效随包源码,1.0.1–1.0.3 编译产物显式使用其可接受的 compact 时间戳。
|
||||
|
||||
## 证据与处置
|
||||
|
||||
| 场景 | 真实旧代码在可控 host 中的结果 | 03 的处置方向 |
|
||||
| --- | --- | --- |
|
||||
| fetch 尚未完成即 destroy | 没有请求选项或取消信号,返回后仍添加控件并响应 hover | 注册前建立生命周期,阻止迟到安装并处理取消 |
|
||||
| 移动拖动后 destroy | 500ms 计时器和 setBar 仍保留;迟到回调继续改样式 | 所有权覆盖监听器、计时器和控件,阻断销毁后写入 |
|
||||
| fetch 或 response.text 拒绝 | 原始异常通过注册 Promise 拒绝,无控件/计时器 | 保持原始拒绝,并释放新增生命周期资源 |
|
||||
| HTTP 404 但 body 为有效 VTT | 未检查 ok,仍显示图片 | 后续显式拒绝 HTTP 失败,作为缺陷修复记录 |
|
||||
| 空文本或仅 WEBVTT | 添加空控件,hover 隐藏 | 保留有效空结果,避免误判为请求异常 |
|
||||
| 错误时间、无 payload、无 hash、cue ID、NOTE | TypeError,安装之前拒绝 | 分开有效标准扩展与格式损坏,给出可定位错误,不静默写入坏 CSS |
|
||||
| 重排 yxhw、坏坐标、缺坐标 | 字符串按键映射;坏值直接形成 CSS | 正常几何保持,异常输入补明确检查,不能把重排键当成不存在 |
|
||||
| restart 后修改 option.vtt | 不重新请求,继续使用注册时的 cue 列表 | 不能未经公开行为评估新增自动重新下载 |
|
||||
| cue 间隙、NaN 进度 | 隐藏预览,不选择另一个 cue | 保持边界行为 |
|
||||
|
||||
另用真实 docs/assets/sample/bbb-thumbnails.vtt 的 **120 个 cue**逐个触发命中,
|
||||
核对图片路径、x/y 背景位置、宽高。该文件 SHA 与运行结果在验证记录中;
|
||||
这仍是受控样式记录,不等于浏览器 CSS 接受了这些值或真实图片已加载。
|
||||
|
||||
复现不修改候选生产源码、声明或 dist,也不增加依赖。新增 `yarn test:vtt-thumbnail`
|
||||
用于运行本包历史契约和故障测试;故障测试同时纳入 `test:unit`,CI 会持续执行。
|
||||
VTT-THUMB-LIFE-01 从 source-observed 提升为 reproduced,所有风险继续 open。
|
||||
|
||||
## 后续任务与回退
|
||||
|
||||
03 拆分纯解析/查找、请求、视图和注册资源,添加面向修复结果的候选测试,先在旧版上
|
||||
观察失败再验证候选;当前历史断言永久保留为对照,不能直接改成新行为消除差异。
|
||||
04 严格 TS 和声明消费者;05 真实核心/浏览器,06 构建安装包与 demo/编辑器。
|
||||
执行结果见 [验证记录](../baselines/vtt-thumbnail-failures.json)。本任务独立本地提交;
|
||||
回退只移除这批故障测试、命令和记录,不改变播放器运行逻辑。没有推送或发布。
|
||||
@@ -57,7 +57,7 @@ Node10 是 TS 模块解析模式名,不代表支持 Node.js 10。现代 es2020
|
||||
| artplayer-plugin-jassub / 1.1.0 | ASS、worker/WASM/font 加载、切源/销毁;短 ASS+明确来源字体 | 内嵌资源哈希已有;版本对应、跨源/worker 限制、实际渲染待 | PKG-JASSUB-01/02/05/06 |
|
||||
| artplayer-plugin-multiple-subtitles / 1.2.0 | VTT 合并/排序/解析错误、URL 回收;双语言/空/坏 VTT | parser 来源头已观察;特有行为和浏览器字幕待 | PKG-MULTI-SUB-01/02/05/06 |
|
||||
| artplayer-plugin-vast / 1.2.0 | Glomex/Google IMA、广告错误/恢复、重复初始化;供应商测试广告标签 | 依赖版本已记录;实际 IMA 网络/广告流程待,PR 用受控 adapter | PKG-VAST-01/02/05/06 |
|
||||
| artplayer-plugin-vtt-thumbnail / 1.1.0 | WebVTT 时间和 sprite xywh、图片失败/跨源;本地 VTT+sprite | 发布/浏览器/边界待 | PKG-VTT-THUMB-01/02/05/06 |
|
||||
| artplayer-plugin-vtt-thumbnail / 1.1.0 | WebVTT 时间和 sprite xywh、图片失败/跨源;本地 VTT+sprite | 五个实际发布归档和可控历史边界/故障已核对;原生图片、浏览器、核心组合仍待 05/06 | PKG-VTT-THUMB-01/02/05/06 |
|
||||
| artplayer-proxy-canvas / 1.1.0 | video-like 属性/事件、Canvas 绘制、seek/销毁;MP4 | shim 与实际媒体顺序、跨源/移动待 | PKG-CANVAS-01/02/05/06 |
|
||||
| artplayer-proxy-mediabunny / 1.2.0 | Mediabunny 1.56.1 当前解析;WebCodecs/AudioContext、HLS/轨道拓扑、AV 同步 | 库依赖不等于 codec 可用;准确 codec/profile、设备能力、长播放待 | PKG-MB-01/02/07/08/09/10 |
|
||||
| artplayer-tool-iframe / 1.1.0 | 跨窗协议、ready/错误/销毁;同源与两个受控不同 origin 页面 | 消息/嵌套浏览器及沙箱权限待 | PKG-IFRAME-01/02/05/06 |
|
||||
|
||||
+3
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 222 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 95 / doing 12 / blocked 0 / done 115 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 94 / doing 12 / blocked 0 / done 116 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
| ID | 范围 / 步骤 | 前置依赖 | 交付物 | 验收条件 | 风险 | 状态 |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| PKG-VTT-THUMB-01 | artplayer-plugin-vtt-thumbnail<br>核对包契约与历史用法 | BASE-05 | vtt/style、异步返回、预览控件名与样式契约 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | M | done |
|
||||
| PKG-VTT-THUMB-02 | artplayer-plugin-vtt-thumbnail<br>建立特有行为与错误测试 | PKG-VTT-THUMB-01, ENG-03, ENG-05 | VTT 解析、xywh、相对 URL、时间边界、请求失败和定位 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | M | todo |
|
||||
| PKG-VTT-THUMB-02 | artplayer-plugin-vtt-thumbnail<br>建立特有行为与错误测试 | PKG-VTT-THUMB-01, ENG-03, ENG-05 | VTT 解析、xywh、相对 URL、时间边界、请求失败和定位 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | M | done |
|
||||
| PKG-VTT-THUMB-03 | artplayer-plugin-vtt-thumbnail<br>整理内部职责与资源 | PKG-VTT-THUMB-02, CORE-09, CORE-19 | 纯解析/区间查找与 DOM、请求/定时器生命周期分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | M | todo |
|
||||
| PKG-VTT-THUMB-04 | artplayer-plugin-vtt-thumbnail<br>迁移自有源码和公开类型 | PKG-VTT-THUMB-03, ENG-04, ENG-06, CORE-07 | cue/rect/result 和参数可选性保持兼容 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | M | todo |
|
||||
| PKG-VTT-THUMB-05 | artplayer-plugin-vtt-thumbnail<br>验证新旧核心和组合 | PKG-VTT-THUMB-04, CORE-22 | 鼠标和移动进度、全屏、切源、chapter 组合 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | M | todo |
|
||||
@@ -498,6 +498,7 @@
|
||||
- PKG-AUTO-THUMB-02: [记录](baselines/auto-thumbnail-failures.md) [记录](baselines/auto-thumbnail-failures-validation.json) [记录](changes/2026-09-13-PKG-AUTO-THUMB-02-failures.md)
|
||||
- PKG-AUTO-THUMB-03: [记录](changes/2026-09-13-PKG-AUTO-THUMB-03-lifecycle-checkpoint.md) [记录](baselines/auto-thumbnail-lifecycle-checkpoint.json) [记录](changes/2026-09-13-PKG-AUTO-THUMB-03-hidden-renderer.md) [记录](baselines/auto-thumbnail-hidden-renderer.json) [记录](baselines/auto-thumbnail-timeline-media.json) [记录](changes/2026-09-13-PKG-AUTO-THUMB-03-frame-presentation.md) [记录](baselines/auto-thumbnail-frame-presentation.json)
|
||||
- PKG-VTT-THUMB-01: [记录](baselines/vtt-thumbnail-release.json) [记录](baselines/vtt-thumbnail-contract.md) [记录](changes/2026-09-13-PKG-VTT-THUMB-01-contract.md)
|
||||
- PKG-VTT-THUMB-02: [记录](baselines/vtt-thumbnail-failures.json) [记录](changes/2026-09-13-PKG-VTT-THUMB-02-failures.md)
|
||||
- PKG-HLS-01: [记录](baselines/hls-control-contract.md) [记录](baselines/hls-control-release.json) [记录](changes/2026-09-11-PKG-HLS-01-contract.md)
|
||||
- PKG-HLS-02: [记录](hls-validation.md) [记录](baselines/hls-sdk.json) [记录](baselines/hls-validation.json) [记录](changes/2026-09-11-PKG-HLS-02-tests.md)
|
||||
- PKG-HLS-03: [记录](changes/2026-09-12-PKG-HLS-03-modules.md) [记录](baselines/hls-modules-validation.json) [记录](hls-validation.md)
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# 进度与证据
|
||||
|
||||
## PKG-VTT-THUMB-02 特有失败与生命周期复现完成
|
||||
|
||||
108 项新历史测试复现销毁后安装/计时器写入、请求拒绝、HTTP 状态忽略、非法 VTT/坐标、
|
||||
切源不重载和边界;真实 demo 的 120 cue 坐标逐条核对。包命令共 199 项通过,
|
||||
完整 CI 2017 项(1680 单元、14 工程、323 基线)通过。历史复现不是候选修复,
|
||||
原生图片/核心组合仍待 05/06;见[变更](changes/2026-09-13-PKG-VTT-THUMB-02-failures.md)和
|
||||
[验证](baselines/vtt-thumbnail-failures.json)。下一步 03 拆分并修复资源所有权,04 严格 TS。
|
||||
222 项:116 done、12 doing、94 todo;独立本地提交,不推送、不发布。
|
||||
|
||||
|
||||
## PKG-VTT-THUMB-01 历史契约完成
|
||||
|
||||
五份真实 npm 归档、34 成员、十份 Git 输入已冻结,91 项包契约及 9 项索引/风险检查通过。
|
||||
|
||||
@@ -191,6 +191,6 @@
|
||||
| AUTO-THUMB-PIXEL-01 | open / 已复现 | Historical auto-thumbnail yields black sampled JPEG cells on Windows WebKit while played native video and static JPEG controls yield color | PKG-AUTO-THUMB-03, PKG-AUTO-THUMB-05 |
|
||||
| VTT-THUMB-DIST-01 | open / 已复现 | Historical 1.0.0 main/legacy contain invalid regular expressions and cannot load | PKG-VTT-THUMB-06 |
|
||||
| VTT-THUMB-PARSE-01 | open / 已复现 | Historical compiled arrows differ from shipped source; parsing floors bounds and assumes alternating lines | PKG-VTT-THUMB-02, PKG-VTT-THUMB-03 |
|
||||
| VTT-THUMB-LIFE-01 | open / 源码/产物事实 | Fetch completion, setBar listeners and mobile timers have no destroy ownership | PKG-VTT-THUMB-02, PKG-VTT-THUMB-03, PKG-VTT-THUMB-05 |
|
||||
| VTT-THUMB-LIFE-01 | open / 已复现 | Fetch completion, setBar listeners and mobile timers have no destroy ownership | PKG-VTT-THUMB-02, PKG-VTT-THUMB-03, PKG-VTT-THUMB-05 |
|
||||
| VTT-THUMB-TYPE-01 | open / 已复现 | Published declarations falsely describe async registration as a synchronous result | PKG-VTT-THUMB-04 |
|
||||
| VTT-THUMB-EXPORT-01 | open / 已复现 | Older CommonJS default objects and thumbnails control names differ from latest direct export and vtt-thumbnail name | PKG-VTT-THUMB-04, PKG-VTT-THUMB-05, PKG-VTT-THUMB-06 |
|
||||
+7
-3
@@ -4149,7 +4149,9 @@
|
||||
"evidence": [
|
||||
"refactor/baselines/vtt-thumbnail-release.json",
|
||||
"refactor/baselines/vtt-thumbnail-contract.md",
|
||||
"refactor/scripts/vtt-thumbnail-contract.test.mjs"
|
||||
"refactor/scripts/vtt-thumbnail-contract.test.mjs",
|
||||
"test/vtt-thumbnail.test.js",
|
||||
"refactor/baselines/vtt-thumbnail-failures.json"
|
||||
],
|
||||
"compatibleResolution": "Preserve valid cue selection and geometry; test malformed data and standard VTT separately from old compiler defects.",
|
||||
"closureCriteria": "Parser fixtures include malformed, empty, real demo VTT, fractional and shared bounds, relative URLs and historical accepted formats."
|
||||
@@ -4157,7 +4159,7 @@
|
||||
{
|
||||
"id": "VTT-THUMB-LIFE-01",
|
||||
"title": "Fetch completion, setBar listeners and mobile timers have no destroy ownership",
|
||||
"confirmation": "source-observed",
|
||||
"confirmation": "reproduced",
|
||||
"status": "open",
|
||||
"owners": [
|
||||
"PKG-VTT-THUMB-02",
|
||||
@@ -4167,7 +4169,9 @@
|
||||
"evidence": [
|
||||
"refactor/baselines/vtt-thumbnail-release.json",
|
||||
"refactor/baselines/vtt-thumbnail-contract.md",
|
||||
"refactor/scripts/vtt-thumbnail-contract.test.mjs"
|
||||
"refactor/scripts/vtt-thumbnail-contract.test.mjs",
|
||||
"test/vtt-thumbnail.test.js",
|
||||
"refactor/baselines/vtt-thumbnail-failures.json"
|
||||
],
|
||||
"compatibleResolution": "Own requests, listeners, timers and controls per registration without stale writes or cross-instance cleanup.",
|
||||
"closureCriteria": "Deferred request, destroy, failure, timer, repeated registration and native core scenarios demonstrate cleanup."
|
||||
|
||||
+5
-2
@@ -1876,11 +1876,14 @@
|
||||
"ENG-03",
|
||||
"ENG-05"
|
||||
],
|
||||
"status": "todo",
|
||||
"status": "done",
|
||||
"risk": "M",
|
||||
"deliverable": "VTT 解析、xywh、相对 URL、时间边界、请求失败和定位",
|
||||
"acceptance": "旧版本行为可重跑,成功/失败/切源/销毁有必要断言",
|
||||
"evidence": []
|
||||
"evidence": [
|
||||
"baselines/vtt-thumbnail-failures.json",
|
||||
"changes/2026-09-13-PKG-VTT-THUMB-02-failures.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "PKG-VTT-THUMB-03",
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Frozen historical failure characterization.
|
||||
import test from 'node:test'
|
||||
import { acceptedVttText, vttText, vttThumbnailEnvironment, vttThumbnailHistorical } from './helpers/vtt-thumbnail.js'
|
||||
|
||||
for (const implementation of await vttThumbnailHistorical()) {
|
||||
const text = acceptedVttText(implementation)
|
||||
test(`VTT-thumbnail ${implementation.name} historical: pending fetch creates controls after destroy without cancellation`, async () => {
|
||||
const env = vttThumbnailEnvironment(implementation, { text, deferred: true })
|
||||
const pending = env.factory({ vtt: '/old.vtt' })(env.art)
|
||||
await env.emit('destroy')
|
||||
assert.equal(env.requests[0].length, 1, 'No AbortSignal or request options')
|
||||
env.resolve()
|
||||
assert.equal((await pending).name, 'artplayerPluginVttThumbnail')
|
||||
assert.equal(env.controls.length, 1)
|
||||
await env.hover(0.3)
|
||||
assert.equal(env.styles.backgroundImage, 'url(/sheet.jpg)')
|
||||
assert.equal(env.listeners.get('destroy'), undefined)
|
||||
})
|
||||
|
||||
test(`VTT-thumbnail ${implementation.name} historical: network and response-body failures preserve the original rejection`, async () => {
|
||||
for (const phase of ['fetch', 'text']) {
|
||||
const env = vttThumbnailEnvironment(implementation, { text, deferred: phase === 'fetch' })
|
||||
const failure = new Error(`${phase} failed`)
|
||||
if (phase === 'text')
|
||||
env.box.fetch = async () => ({ text: async () => { throw failure } })
|
||||
const pending = env.factory({ vtt: '/cues.vtt' })(env.art)
|
||||
if (phase === 'fetch')
|
||||
env.reject(failure)
|
||||
await assert.rejects(pending, error => error === failure)
|
||||
assert.equal(env.controls.length, 0)
|
||||
assert.equal(env.listeners.size, 0)
|
||||
assert.equal(env.timers.size, 0)
|
||||
}
|
||||
})
|
||||
|
||||
test(`VTT-thumbnail ${implementation.name} historical: HTTP failure status is ignored when its body parses`, async () => {
|
||||
const env = vttThumbnailEnvironment(implementation)
|
||||
env.box.fetch = async () => ({ ok: false, status: 404, text: async () => text })
|
||||
await env.factory({ vtt: '/missing.vtt' })(env.art)
|
||||
await env.hover(0.3)
|
||||
assert.equal(env.styles.backgroundImage, 'url(/sheet.jpg)')
|
||||
})
|
||||
|
||||
test(`VTT-thumbnail ${implementation.name} historical: empty and header-only responses create an empty hidden preview`, async () => {
|
||||
for (const source of ['', 'WEBVTT\r\n\r\n']) {
|
||||
const env = vttThumbnailEnvironment(implementation, { text: source })
|
||||
await env.factory({ vtt: '/empty.vtt' })(env.art)
|
||||
assert.equal(env.controls.length, 1)
|
||||
await env.hover(0.3)
|
||||
assert.equal(env.styles.display, 'none')
|
||||
}
|
||||
})
|
||||
|
||||
test(`VTT-thumbnail ${implementation.name} historical: unmatched timestamps, missing payload, cue IDs and NOTE reject before mounting`, async () => {
|
||||
for (const source of [
|
||||
'WEBVTT\n\nnot a timestamp\nsheet.jpg#xywh=0,0,80,45',
|
||||
'WEBVTT\n\n00:00.000 --> 00:05.000',
|
||||
'WEBVTT\n\n00:00.000 --> 00:05.000\nsheet.jpg',
|
||||
'WEBVTT\n\ncue-id\n00:00.000 --> 00:05.000\nsheet.jpg#xywh=0,0,80,45',
|
||||
'WEBVTT\n\nNOTE comment\n\n00:00.000 --> 00:05.000\nsheet.jpg#xywh=0,0,80,45',
|
||||
]) {
|
||||
const env = vttThumbnailEnvironment(implementation, { text: acceptedVttText(implementation, source) })
|
||||
await assert.rejects(env.factory({ vtt: '/invalid.vtt' })(env.art), error => error.name === 'TypeError')
|
||||
assert.equal(env.controls.length, 0)
|
||||
}
|
||||
})
|
||||
|
||||
test(`VTT-thumbnail ${implementation.name} historical: raw reordered xywh keys and malformed coordinate values reach styling`, async () => {
|
||||
for (const [coordinates, expected] of [
|
||||
['yxhw=20,10,45,80', { width: '80px', height: '45px', backgroundPosition: '-10px -20px' }],
|
||||
['xywh=bad,-2,80', { width: '80px', height: 'undefinedpx', backgroundPosition: '-badpx --2px' }],
|
||||
]) {
|
||||
const env = vttThumbnailEnvironment(implementation, { text: acceptedVttText(implementation, `WEBVTT\n\n00:00.000 --> 00:10.000\nsheet.jpg#${coordinates}`) })
|
||||
await env.factory({ vtt: '/cues.vtt' })(env.art)
|
||||
await env.hover(0.3)
|
||||
for (const [key, value] of Object.entries(expected)) assert.equal(env.styles[key], value)
|
||||
}
|
||||
})
|
||||
|
||||
test(`VTT-thumbnail ${implementation.name} historical: restart retains the original cue list without another request`, async () => {
|
||||
const env = vttThumbnailEnvironment(implementation, { text })
|
||||
const options = { vtt: '/old/cues.vtt' }
|
||||
await env.factory(options)(env.art)
|
||||
options.vtt = '/new/cues.vtt'
|
||||
await env.emit('restart', '/new-video.mp4')
|
||||
await env.hover(0.7)
|
||||
assert.equal(env.requests.length, 1)
|
||||
assert.equal(env.styles.backgroundImage, 'url(/old/second.jpg)')
|
||||
})
|
||||
|
||||
test(`VTT-thumbnail ${implementation.name} historical: gaps and non-finite percentages hide without selecting another cue`, async () => {
|
||||
const env = vttThumbnailEnvironment(implementation, { text: acceptedVttText(implementation, vttText.replace('00:00:05.100', '00:00:07.100')) })
|
||||
await env.factory({ vtt: '/cues.vtt' })(env.art)
|
||||
await env.hover(0.3)
|
||||
assert.equal(env.styles.backgroundImage, 'url(/sheet.jpg)')
|
||||
await env.hover(0.6)
|
||||
assert.equal(env.styles.display, 'none')
|
||||
await env.hover(Number.NaN)
|
||||
assert.equal(env.styles.display, 'none')
|
||||
})
|
||||
|
||||
if (implementation.profile !== 'mouse-source-only') {
|
||||
test(`VTT-thumbnail ${implementation.name} historical: destroy leaves the mobile timer and setBar callback active`, async () => {
|
||||
const env = vttThumbnailEnvironment(implementation, { text, mobile: true })
|
||||
await env.factory({ vtt: '/cues.vtt' })(env.art)
|
||||
await env.hover(0.3, 'played', {})
|
||||
const timer = [...env.timers.values()][0]
|
||||
await env.emit('destroy')
|
||||
assert.equal(env.timers.size, 1)
|
||||
assert.equal(env.listeners.get('setBar').length, 1)
|
||||
env.styles.display = 'sentinel'
|
||||
timer.callback()
|
||||
assert.equal(env.styles.display, 'none', 'A stale callback still writes the old element')
|
||||
await env.hover(0.7)
|
||||
assert.equal(env.styles.backgroundImage, 'url(/second.jpg)')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
test('VTT-thumbnail frozen current implementation parses every cue in the real local demo VTT', async () => {
|
||||
const implementation = (await vttThumbnailHistorical()).find(item => item.name === 'frozen-workspace-source')
|
||||
const text = fs.readFileSync('docs/assets/sample/bbb-thumbnails.vtt', 'utf8')
|
||||
const rows = text.match(/bbb-sprite\.jpg#xywh=[^\r\n]+/g)
|
||||
assert(rows.length > 100)
|
||||
const env = vttThumbnailEnvironment(implementation, { text })
|
||||
await env.factory({ vtt: '/assets/sample/bbb-thumbnails.vtt' })(env.art)
|
||||
env.art.duration = rows.length * 5
|
||||
for (const [index, row] of rows.entries()) {
|
||||
await env.hover((index * 5 + 2.5) / env.art.duration)
|
||||
const [x, y, width, height] = row.split('=')[1].split(',')
|
||||
assert.equal(env.styles.backgroundImage, 'url(/assets/sample/bbb-sprite.jpg)')
|
||||
assert.equal(env.styles.backgroundPosition, `-${x}px -${y}px`)
|
||||
assert.equal(env.styles.width, `${width}px`)
|
||||
assert.equal(env.styles.height, `${height}px`)
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user