test(chapter): [PKG-CHAPTER-HOVER-01] wait for stable fullscreen geometry

This commit is contained in:
Harvey Zhao committed 2026-09-14 18:28:07 +08:00
1 parent 492e58a345
commit d89728c0a6
14 files changed
+1797 -14

No files matched your search

@@ -103,3 +103,12 @@ bridges; typesVersions provides the legacy subpath to old resolvers. All bridges
refer to one contract and runtime import paths stay unchanged. The frozen refer to one contract and runtime import paths stay unchanged. The frozen
published baseline is under refactor/baselines; never overwrite it with candidate published baseline is under refactor/baselines; never overwrite it with candidate
behavior. Physical device, full editor and final release checks remain separate tasks. behavior. Physical device, full editor and final release checks remain separate tasks.
The combination suite uses `test/browser/chapter-hover.ts` to wait for Playwright
actionability/stable geometry before reading progress coordinates. Fullscreen state
alone does not mean the bottom controls have finished their CSS transition.
`chapter-hover.spec.js` exercises this boundary with an intentional native Web
Animation after entering real fullscreen. It preserves title/opacity/hit-test
assertions and uses actual installed artifacts in the installed browser scope.
`ARTPLAYER_CHAPTER_HOVER_BASELINE=1` restores the old immediate coordinate read for
diagnosis only. This helper corrects test synchronization, not player behavior.
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,57 @@
# PKG-CHAPTER-HOVER-01 全屏悬停测试同步
## 问题与边界
CI-BROWSER-01 的已安装四包检查曾实际出现 224 通过、1 失败:Chromium 中
5.4.0 核心搭配候选 Chapter,原生全屏后的章节标题先通过文本断言,再隐藏,
opacity 从 0.279 降到 0。原始报告、trace 和截图保留在
`refactor/.cache/ci-browser-installed-full-report`,不改写为通过。
旧测试在 `art.fullscreen === true` 后立即读取进度条 boundingBox 并移动鼠标。
核心 bottom.less 的进度条/控件有 transform transition;全屏布尔状态不代表
几何已稳定。原 trace 的鼠标位置为 (640, 665.9921875),终态截图的进度条已
移动。仅靠后来通过不能证明原因,因此另建受控移动实验。
## 实现与责任
- `test/browser/chapter-hover.ts` 统一几何读取:先通过 Playwright locator 的
`hover({ trial: true })` actionability/stability 检查,再读取 box 并实际移动鼠标。
trial 不发送实际 hover;没有增加固定 sleep、断言超时或 test retries。
- `chapter-combinations.spec.js` 复用该函数;标题、透明度、缩略图、边界及全屏
退出断言保持原样。
- `chapter-hover.spec.js` 在真实原生全屏中用原生 Web Animation 让进度条移动
600ms,结束后通过真实 hit-test 验证指针仍在进度条内,再验证 Middle/opacity。
这是故意注入的几何移动,不是宣称产品 transition 实际持续 600ms。
- `ARTPLAYER_CHAPTER_HOVER_BASELINE=1` 可恢复旧的立即读取路径,保留可重跑的
反例。默认必须使用稳定路径;诊断模式的失败不能豁免正式验收。
- 新 TS helper 纳入严格工具类型检查;安装浏览器范围显式加入该回归文件。
API-08 仅涉及测试如何操作 DOM;生产源码、公开接口/事件/声明/CSS 和安装包
字节都未修改。包架构文档记录入口和边界。本任务不需要重建生产运行时或引入依赖。
## 证据与限制
完整结果、报告摘要、浏览器版本、实际安装入口及 SHA 见
[chapter-hover-validation.json](../baselines/chapter-hover-validation.json)。
先前立即读取路径在受控移动中 4 失败、2 通过;等待稳定后同样 6 项全部通过。
Chromium/Firefox 的失败是最终指针已离开移动后的进度条。首版 WebKit 旧路径
通过,但审查附件发现其 hit-test 与最终矩形不一致。因此补上最终 x/y 矩形范围
断言:最终 baseline 三引擎 6 项全部失败;默认稳定路径在 main/legacy 各 6 项
通过。首版的 2 项通过仍留档,不能把只依赖 hit-test 的结果当作几何正确。
原有组合与首版新增移动回归在 main/legacy 各 66 项
通过,均无重试、跳过或失败。严格 TS、专项 ESLint、根 lint(0 错误/1 个既有
warning)、4 项 browser runner 测试和固定工具链检查通过。
实验确认旧测试的几何竞态及针对该机制的修正,原失败符合相同症状;不能重新播放
原 run 的精确调度,也不能据此声称所有全屏生产缺陷已排除。新旧包字节保持一致,
本次修复归类为测试同步问题。CHAPTER-TIMING-01 的 WebKit restart 观察耗时、
真机、完整发布/分发验收仍由 Chapter-05/06 负责。
验证使用 Node 24.21.0、Yarn 1.22.22、实际安装产物及桌面三引擎。没有远端 CI、
真机、全量 source suite 或 npm 发布;不把模拟触摸称为手机证据。
## 回退与接续
诊断时设置 baseline 环境变量并运行 README 所列命令,可比较真实指针与终态
矩形。交付回退可还原本任务的 helper、回归测试、scope 和文档/状态;不要删除
历史失败报告或把 opacity 断言放宽。后续 UI/CSS 改动须继续运行这两个浏览器文件。
+3 -3
View File
@@ -4,7 +4,7 @@
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 258 项,范围 22 个包及工作区/示例。 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 258 项,范围 22 个包及工作区/示例。
状态:todo 51 / doing 19 / blocked 0 / done 188 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 状态:todo 50 / doing 19 / blocked 0 / done 189 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
@@ -146,7 +146,7 @@
| PKG-CHAPTER-04 | artplayer-plugin-chapter<br>迁移自有源码和公开类型 | PKG-CHAPTER-03, ENG-04, ENG-06 | 承接 03 的自有源码 TS 化,完善公开 Chapters/Option/Result 与 update 推导,关闭声明模块解析和旧消费差异 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容;处理 BASE-TYPE-01/03,保持运行时和新旧 TS 模块解析消费 | M | done | | PKG-CHAPTER-04 | artplayer-plugin-chapter<br>迁移自有源码和公开类型 | PKG-CHAPTER-03, ENG-04, ENG-06 | 承接 03 的自有源码 TS 化,完善公开 Chapters/Option/Result 与 update 推导,关闭声明模块解析和旧消费差异 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容;处理 BASE-TYPE-01/03,保持运行时和新旧 TS 模块解析消费 | M | done |
| PKG-CHAPTER-05 | artplayer-plugin-chapter<br>验证新旧核心和组合 | PKG-CHAPTER-04, CORE-22 | chapter + quality/thumbnail、移动和全屏进度场景 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | M | doing | | PKG-CHAPTER-05 | artplayer-plugin-chapter<br>验证新旧核心和组合 | PKG-CHAPTER-04, CORE-22 | chapter + quality/thumbnail、移动和全屏进度场景 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | M | doing |
| PKG-CHAPTER-06 | artplayer-plugin-chapter<br>验证分发并同步文档 | PKG-CHAPTER-05, ENG-07, PKG-CHAPTER-HOVER-01 | chapter.js 示例、产物和变更记录 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | M | todo | | PKG-CHAPTER-06 | artplayer-plugin-chapter<br>验证分发并同步文档 | PKG-CHAPTER-05, ENG-07, PKG-CHAPTER-HOVER-01 | chapter.js 示例、产物和变更记录 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | M | todo |
| PKG-CHAPTER-HOVER-01 | artplayer-plugin-chapter<br>定位并修复原生全屏章节悬停时序 | PKG-CHAPTER-04, CI-BROWSER-01 | 原生全屏动态几何与指针时序复现、针对根因的修复和真实浏览器回归 | 解释 Chromium 旧核心+新章节的 opacity 失败;保留标题、缩略图和全屏退出断言,不以重试或扩大等待掩盖;区分测试同步与生产缺陷 | M | todo | | PKG-CHAPTER-HOVER-01 | artplayer-plugin-chapter<br>定位并修复原生全屏章节悬停时序 | PKG-CHAPTER-04, CI-BROWSER-01 | 原生全屏动态几何与指针时序复现、针对根因的修复和真实浏览器回归 | 解释 Chromium 旧核心+新章节的 opacity 失败;保留标题、缩略图和全屏退出断言,不以重试或扩大等待掩盖;区分测试同步与生产缺陷 | M | done |
## 5 包迁移:artplayer-plugin-ambilight ## 5 包迁移:artplayer-plugin-ambilight
@@ -669,4 +669,4 @@
- ENG-13: [记录](changes/2026-09-14-ENG-13-observation-window.md) [记录](baselines/observation-window-validation.json) - ENG-13: [记录](changes/2026-09-14-ENG-13-observation-window.md) [记录](baselines/observation-window-validation.json)
- SITE-VCONSOLE-01: [记录](changes/2026-09-14-SITE-VCONSOLE-01-lifecycle.md) [记录](baselines/vconsole-lifecycle-validation.json) - SITE-VCONSOLE-01: [记录](changes/2026-09-14-SITE-VCONSOLE-01-lifecycle.md) [记录](baselines/vconsole-lifecycle-validation.json)
- CI-BROWSER-01: [记录](changes/2026-09-14-CI-BROWSER-01-scopes.md) [记录](baselines/browser-scope-validation.json) - CI-BROWSER-01: [记录](changes/2026-09-14-CI-BROWSER-01-scopes.md) [记录](baselines/browser-scope-validation.json)
- PKG-CHAPTER-HOVER-01: [记录](changes/2026-09-14-CI-BROWSER-01-scopes.md) [记录](baselines/browser-scope-validation.json) - PKG-CHAPTER-HOVER-01: [记录](changes/2026-09-14-PKG-CHAPTER-HOVER-01-geometry.md) [记录](baselines/chapter-hover-validation.json)
+15
View File
@@ -1,5 +1,20 @@
# 进度与证据 # 进度与证据
## PKG-CHAPTER-HOVER-01 全屏悬停测试同步完成
原始 installed 224/1 失败保持归档。受控原生进度条移动实验确认立即读取坐标
可让指针落在终态进度条外:首版旧步骤4失败/2通过,等待actionability后6通过。
审查发现WebKit hit-test与最终矩形不一致,补强数值边界后baseline六项全部失败,
默认路径main/legacy各六项通过;保留首版报告,不把hit-test等同于几何正确。
共享TS helper先等待几何稳定再读取box;保留文字、opacity、缩略图、边界和
全屏退出断言,不更改生产代码或超时。完整main/legacy各66通过,实际安装
字节、新旧核心/插件组合及三桌面引擎均有报告;原run精确调度不能重放。
严格TS、专项lint、根lint(0错误/1既有warning)、4项runner测试、工具链通过。
见[变更](changes/2026-09-14-PKG-CHAPTER-HOVER-01-geometry.md)和
[证据](baselines/chapter-hover-validation.json)。258项:189 done、19 doing、50 todo。
CHAPTER-FULLSCREEN-GEOMETRY-01按测试同步关闭;CHAPTER-TIMING-01、真机及
Chapter-05/06仍未完成。没有远端CI、push或发布;本任务独立本地提交后审计。
## CI-BROWSER-01 源码/安装浏览器范围隔离完成 ## CI-BROWSER-01 源码/安装浏览器范围隔离完成
复现全局安装map使HLS源码用例beforeAll直接失败;新source入口清除继承map后实际 复现全局安装map使HLS源码用例beforeAll直接失败;新source入口清除继承map后实际
+1 -1
View File
@@ -271,4 +271,4 @@
| JASSUB-RENDER-RESOURCES-01 | resolved / 已复现 | Main-thread async rendering leaves received ImageBitmaps open when native canvas operations throw | PKG-JASSUB-10 | | JASSUB-RENDER-RESOURCES-01 | resolved / 已复现 | Main-thread async rendering leaves received ImageBitmaps open when native canvas operations throw | PKG-JASSUB-10 |
| ENG-PERF-OBS-01 | resolved / 已复现 | A nominal 350 ms resource observation can record only 349 ms and fail the required minimum | ENG-13 | | ENG-PERF-OBS-01 | resolved / 已复现 | A nominal 350 ms resource observation can record only 349 ms and fail the required minimum | ENG-13 |
| VCONSOLE-LIFECYCLE-01 | resolved / 已复现 | vConsole delayed log, panel and scroller work outlives its owner | SITE-07, SITE-05, SITE-VCONSOLE-01 | | VCONSOLE-LIFECYCLE-01 | resolved / 已复现 | vConsole delayed log, panel and scroller work outlives its owner | SITE-07, SITE-05, SITE-VCONSOLE-01 |
| CHAPTER-FULLSCREEN-GEOMETRY-01 | open / 已复现 | Chromium 原生全屏后章节 hover 标题曾出现后又隐藏 | PKG-CHAPTER-05, PKG-CHAPTER-HOVER-01 | | CHAPTER-FULLSCREEN-GEOMETRY-01 | resolved / 已复现 | Chromium 原生全屏后章节 hover 标题曾出现后又隐藏 | PKG-CHAPTER-05, PKG-CHAPTER-HOVER-01 |
+9 -2
View File
@@ -6198,7 +6198,7 @@
"id": "CHAPTER-FULLSCREEN-GEOMETRY-01", "id": "CHAPTER-FULLSCREEN-GEOMETRY-01",
"title": "Chromium 原生全屏后章节 hover 标题曾出现后又隐藏", "title": "Chromium 原生全屏后章节 hover 标题曾出现后又隐藏",
"confirmation": "reproduced", "confirmation": "reproduced",
"status": "open", "status": "resolved",
"owners": [ "owners": [
"PKG-CHAPTER-05", "PKG-CHAPTER-05",
"PKG-CHAPTER-HOVER-01" "PKG-CHAPTER-HOVER-01"
@@ -6210,7 +6210,14 @@
], ],
"compatibleResolution": "核对原生全屏布局/controls transition 与鼠标坐标的时序;根据实际根因修复测试同步或生产实现,保持旧接口。", "compatibleResolution": "核对原生全屏布局/controls transition 与鼠标坐标的时序;根据实际根因修复测试同步或生产实现,保持旧接口。",
"closureCriteria": "保留原失败并取得可解释的旧红新绿/稳定真实浏览器证据,不用重试、延时扩大或移除 opacity 断言关闭。", "closureCriteria": "保留原失败并取得可解释的旧红新绿/稳定真实浏览器证据,不用重试、延时扩大或移除 opacity 断言关闭。",
"workspaceState": "Installed 225-case run with two workers: 224 pass, published 5.4.0 core + candidate Chapter Chromium fullscreen case fails at hoverChapter opacity. Text assertion initially passes, opacity falls from 0.279 to 0 and title clears. Trace mouse=(640,665.9921875); screenshot is fullscreen1280x720. Geometry transition is a hypothesis; production defect versus test synchronization remains unverified." "workspaceState": "Shared typed hover helper waits for actionability before coordinate sampling. Production package bytes are unchanged; report-bound old/new core/plugin combinations cover main and legacy on three desktop engines. Physical devices and Chapter-05/06 acceptance remain open.",
"resolutionEvidence": [
"refactor/changes/2026-09-14-PKG-CHAPTER-HOVER-01-geometry.md",
"refactor/baselines/chapter-hover-validation.json",
"test/browser/chapter-hover.ts",
"test/browser/chapter-hover.spec.js"
],
"resolutionRationale": "The immediate bounding-box read permits stale pointer coordinates while progress transforms move. Initial hit-test control gives 4 failures/2 passes before and 6 passes after waiting for locator stability. WebKit hit tests disagree with the final rectangle, so additional explicit bounds assertions were added: baseline 6 failures, stable main/legacy 6 passes each. Complete main/legacy chapter combination suites retain title, opacity, thumbnails and fullscreen assertions (66 passes each). This closes the identified test synchronization race, not all possible production fullscreen defects or CHAPTER-TIMING-01. Original 224/1 failure remains archived; its exact scheduler cannot be replayed."
} }
] ]
} }
+3 -3
View File
@@ -5381,13 +5381,13 @@
"PKG-CHAPTER-04", "PKG-CHAPTER-04",
"CI-BROWSER-01" "CI-BROWSER-01"
], ],
"status": "todo", "status": "done",
"risk": "M", "risk": "M",
"deliverable": "原生全屏动态几何与指针时序复现、针对根因的修复和真实浏览器回归", "deliverable": "原生全屏动态几何与指针时序复现、针对根因的修复和真实浏览器回归",
"acceptance": "解释 Chromium 旧核心+新章节的 opacity 失败;保留标题、缩略图和全屏退出断言,不以重试或扩大等待掩盖;区分测试同步与生产缺陷", "acceptance": "解释 Chromium 旧核心+新章节的 opacity 失败;保留标题、缩略图和全屏退出断言,不以重试或扩大等待掩盖;区分测试同步与生产缺陷",
"evidence": [ "evidence": [
"changes/2026-09-14-CI-BROWSER-01-scopes.md", "changes/2026-09-14-PKG-CHAPTER-HOVER-01-geometry.md",
"baselines/browser-scope-validation.json" "baselines/chapter-hover-validation.json"
] ]
} }
] ]
+1
View File
@@ -9,6 +9,7 @@ export const installedTests = [
'initialization.spec.js', 'initialization.spec.js',
'chapter.spec.js', 'chapter.spec.js',
'chapter-combinations.spec.js', 'chapter-combinations.spec.js',
'chapter-hover.spec.js',
'ambilight-lifecycle.spec.js', 'ambilight-lifecycle.spec.js',
'ambilight-proxy.spec.js', 'ambilight-proxy.spec.js',
'canvas-lifecycle.spec.js', 'canvas-lifecycle.spec.js',
+2 -1
View File
@@ -30,6 +30,7 @@
"build-docs.js", "build-docs.js",
"../packages/artplayer-vitepress/build/**/*.ts", "../packages/artplayer-vitepress/build/**/*.ts",
"build-vconsole.mjs", "build-vconsole.mjs",
"browser-validation/**/*.ts" "browser-validation/**/*.ts",
"../test/browser/chapter-hover.ts"
] ]
} }
+20
View File
@@ -1,5 +1,25 @@
# Browser regression entry # Browser regression entry
Chapter pointer geometry is shared by `chapter-hover.ts` and the combination
suite. It awaits locator actionability with `hover({ trial: true })` before
reading coordinates; `art.fullscreen` can become true while controls still move.
The motion regression enters native fullscreen and adds a controlled 600ms Web
Animation to reproduce stale coordinates. It checks the final hit target, chapter
text and opacity without changing production CSS or increasing assertion timeouts.
It also requires pointer x/y to lie within the final inner rectangle; WebKit's
initial hit-test alone reported a hit outside that recorded rectangle.
Use `ARTPLAYER_CHAPTER_HOVER_BASELINE=1` to rerun the old immediate-read path;
this diagnostic intentionally fails on affected engines and is not an acceptance
mode. Both files belong to source and installed scopes. For installed runs:
```sh
# Set ARTPLAYER_BROWSER_ARTIFACTS to a verified installation map first.
yarn test:browser:installed test/browser/chapter-combinations.spec.js test/browser/chapter-hover.spec.js --workers=2
```
See `refactor/baselines/chapter-hover-validation.json` for the original failure,
controlled red/green observations and actual execution limits.
CI uses `yarn test:browser:source` for the complete source suite and CI uses `yarn test:browser:source` for the complete source suite and
`yarn test:browser:installed` for the reviewed installed-package subset. The source `yarn test:browser:installed` for the reviewed installed-package subset. The source
launcher clears inherited ARTPLAYER_BROWSER_ARTIFACTS; the installed launcher and launcher clears inherited ARTPLAYER_BROWSER_ARTIFACTS; the installed launcher and
+2 -4
View File
@@ -1,4 +1,5 @@
import { devices } from '@playwright/test' import { devices } from '@playwright/test'
import { hoverProgress } from './chapter-hover.ts'
import { expect, test } from './fixtures.js' import { expect, test } from './fixtures.js'
async function openChapter(page, core, chapter) { async function openChapter(page, core, chapter) {
@@ -37,10 +38,7 @@ async function openChapter(page, core, chapter) {
} }
async function hoverChapter(page, percentage, text) { async function hoverChapter(page, percentage, text) {
const inner = page.locator('.art-control-progress-inner') const { box, x } = await hoverProgress(page, percentage)
const box = await inner.boundingBox()
const x = Math.round(box.x + box.width * percentage)
await page.mouse.move(x, box.y + box.height / 2)
await expect(page.locator('.art-chapter-title')).toHaveText(text) await expect(page.locator('.art-chapter-title')).toHaveText(text)
await expect(page.locator('.art-chapter-title')).toHaveCSS('opacity', '1') await expect(page.locator('.art-chapter-title')).toHaveCSS('opacity', '1')
await expect(page.locator('.art-control-thumbnails')).toBeVisible() await expect(page.locator('.art-control-thumbnails')).toBeVisible()
+52
View File
@@ -0,0 +1,52 @@
import { hoverProgress } from './chapter-hover.ts'
import { expect, test } from './fixtures.js'
for (const core of ['published', 'candidate']) {
test(`${core}: chapter hover waits for a moving fullscreen progress bar`, async ({ page }, testInfo) => {
await page.goto(`/test/player.html?core=${core}&chapter=candidate`)
await page.evaluate(() => {
window.art = new window.Artplayer({
container: '.player',
url: '/test/pattern.mp4',
muted: true,
fullscreen: true,
plugins: [window.artplayerPluginChapter({ chapters: [{ start: 0, end: 2, title: 'Opening' }, { start: 2, end: 6, title: 'Middle' }, { start: 6, end: Infinity, title: 'Ending' }] })],
})
})
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
await page.locator('.art-control-fullscreen').click()
await expect.poll(() => page.evaluate(() => window.art.fullscreen)).toBe(true)
await page.evaluate(async () => {
window.progressAnimation = document.querySelector('.art-progress').animate([
{ transform: 'translateY(-60px)' },
{ transform: 'translateY(0)' },
], { duration: 600, easing: 'linear' })
await window.progressAnimation.ready
})
const pointer = await hoverProgress(page, 0.5)
await page.evaluate(() => window.progressAnimation.finished)
const layout = await page.evaluate(({ x, y }) => {
const element = document.querySelector('.art-control-progress-inner')
const rect = element.getBoundingClientRect()
return { x: rect.x, y: rect.y, width: rect.width, height: rect.height, pointerInside: !!document.elementFromPoint(x, y)?.closest('.art-progress'), title: document.querySelector('.art-chapter-title').textContent }
}, pointer)
await testInfo.attach('chapter-hover-motion', { contentType: 'application/json', body: JSON.stringify({ core, baseline: process.env.ARTPLAYER_CHAPTER_HOVER_BASELINE === '1', pointer, layout, motion: 'controlled native Web Animation after actual fullscreen; no media or hit-test mocks' }) })
expect(layout.pointerInside).toBe(true)
expect(pointer.x).toBeGreaterThanOrEqual(layout.x)
expect(pointer.x).toBeLessThanOrEqual(layout.x + layout.width)
expect(pointer.y).toBeGreaterThanOrEqual(layout.y)
expect(pointer.y).toBeLessThanOrEqual(layout.y + layout.height)
await expect(page.locator('.art-chapter-title')).toHaveText('Middle')
await expect(page.locator('.art-chapter-title')).toHaveCSS('opacity', '1')
await page.locator('.art-control-fullscreen').click()
await expect.poll(() => page.evaluate(() => window.art.fullscreen)).toBe(false)
await page.evaluate(() => window.art.destroy())
})
}
test.afterEach(async ({ page }) => {
await page.evaluate(() => {
if (window.art && !window.art.isDestroy)
window.art.destroy()
})
})
+15
View File
@@ -0,0 +1,15 @@
import type { Page } from '@playwright/test'
import assert from 'node:assert/strict'
import process from 'node:process'
export async function hoverProgress(page: Page, percentage: number) {
const inner = page.locator('.art-control-progress-inner')
if (process.env.ARTPLAYER_CHAPTER_HOVER_BASELINE !== '1')
await inner.hover({ trial: true })
const box = await inner.boundingBox()
assert(box, 'Chapter progress must have a visible box')
const x = Math.round(box.x + box.width * percentage)
const y = box.y + box.height / 2
await page.mouse.move(x, y)
return { box, x, y }
}