mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-13 07:46:17 -08:00
test(jassub): [PKG-JASSUB-09] verify composited subtitle screenshots
This commit is contained in:
1 parent
7ef605fba7
commit
814bdc11db
12 files changed
+5509
-14
No files matched your search
@@ -126,6 +126,7 @@
|
||||
"lerna": "8.2.4",
|
||||
"less": "4.5.1",
|
||||
"linkedom": "0.18.13",
|
||||
"pngjs": "7.0.0",
|
||||
"prompts": "2.4.2",
|
||||
"semver": "7.7.4",
|
||||
"servor": "4.0.2",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,63 @@
|
||||
# PKG-JASSUB-09 合成截图验证检查点
|
||||
|
||||
接续 `7ef605fba7e7009b4838ecf391969b17fd0e1315`,任务仍为 doing。前一轮原生
|
||||
ImageBitmap 控制把停顿定位到异步创建与显示读取相关路径;本轮补充完全不复制
|
||||
转移画布的实际合成截图,检查是否只是原来的 canvas readback 引入了问题。
|
||||
|
||||
## 实际改动
|
||||
|
||||
`test/browser/jassub-display.js` 只负责 Node 端 PNG 解码及绿色字形计数;浏览器
|
||||
继续实际播放并运行 JASSUB Worker/WASM。原有 `jassub-native.spec.js` 默认流程
|
||||
保留;显式 `ARTPLAYER_JASSUB_SCREENSHOT=true` 才走截图诊断。页面几何与媒体状态
|
||||
由单独函数读取,避免几何检查间接触发复制画布。
|
||||
|
||||
截图用例使用可识别的绿色 ASS 字形,并在全屏后隐藏字幕验证绿色像素归零,
|
||||
恢复后再次验证可见,防止把视频背景或播放器 UI 误认作字幕。同时检查 seek
|
||||
前后字形签名、播放时间前进、布局尺寸及原有销毁/资源边界。没有屏蔽 Worker
|
||||
错误,没有把默认 asyncRender/offscreenRender 改成 false,没有修改生产源码。
|
||||
|
||||
新增根 devDependency `pngjs@7.0.0`,只用于 Node 测试截图解码;MIT、无运行时
|
||||
依赖,API 来源为 [上游文档](https://github.com/pngjs/pngjs#sync-api)。根 manifest
|
||||
和唯一 yarn.lock 仅增加此项。Node 24.21.0 / Yarn 1.22.22 正常安装、随后 frozen
|
||||
安装和严格工具链检查通过,未使用 ignore-scripts/ignore-engines。没有新增发布
|
||||
依赖、提高播放器浏览器语法目标或重建无变化的发布产物。
|
||||
|
||||
## 保留的中间结果
|
||||
|
||||
- 首轮 Firefox 截图:2 通过 / 1 全屏失败;直接截图也未保证消除停顿,不能声称
|
||||
问题只由原先的 drawImage 复制造成。
|
||||
- 增加截图耗时/调用错误记录后:2 通过 / 1 首次像素失败;随后加入完整尝试错误
|
||||
附件的版本三项通过。添加观测不是生产修复,成功重跑不关闭原风险。
|
||||
- Chromium/WebKit 首轮:4 通过 / 2 失败。两个 WebKit 失败时 before-seek 已到
|
||||
5.389/5.412 秒,采样的已是后一条字幕;甚至通过的 WebKit before-seek 为
|
||||
4.146 秒、像素为零。说明第一版截图用例的短字幕窗口不可靠,不能把这组失败
|
||||
报成 WebKit 的字幕显示缺陷,也不能将那个通过样本作为完整前后字形证明。
|
||||
- 修正仅作用于截图模式:第一条 0-30 秒、第二条 40-110 秒,seek 50 秒;明确
|
||||
before.time < 30 且 before.visible > 100。原非截图模式保持原 ASS 和 seek 10
|
||||
秒,expect/test 超时均未扩大。这样保留有限等待检查,同时不依赖截图在四秒内
|
||||
完成所有前置动作。最终三浏览器 × 三核心九项通过;Chromium/Firefox 使用真实
|
||||
offscreen 能力,Windows WebKit 无 transfer,验证实际主线程 fallback。最终结果
|
||||
见机器记录。
|
||||
|
||||
初版报告中的 limitation/subtitleSha256 尚沿用白色 ASS 文字;在最终测试中已
|
||||
改为实际选中字幕的 hash 和截图模式描述。机器记录明确区分中间证据,不把旧
|
||||
报告错误元数据当作实际输入身份。浏览器 fixture 自动失败截图发生在 finally
|
||||
销毁之后,不能用空白页面推断销毁前的播放状态。命名阶段 PNG 在销毁前采集。
|
||||
|
||||
## 复跑与边界
|
||||
|
||||
```powershell
|
||||
$env:ARTPLAYER_JASSUB_ARTIFACT = 'packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js'
|
||||
$env:ARTPLAYER_JASSUB_OFFSCREEN = 'default'
|
||||
$env:ARTPLAYER_JASSUB_SCREENSHOT = 'true'
|
||||
yarn test:browser test/browser/jassub-native.spec.js --workers=1
|
||||
```
|
||||
|
||||
保留 `jassub-hybrid.spec.js` 和原 readback 红序列,不用截图模式替代全部兼容验收。
|
||||
每轮先归档共享 report/results。原始截图、trace、报告保存在 `.cache/`,提交的
|
||||
[摘要](../baselines/jassub-display-validation.json)保存参数、hash、结果、失败及
|
||||
阶段像素/媒体状态,不能替代所有物理设备和持续 GPU 使用证据。
|
||||
|
||||
本轮不关闭 JASSUB-FIREFOX-OFFSCREEN-01、09 或 05;不宣称整包、完整浏览器组合、
|
||||
npm 准入、远程 CI 或三轮发布复盘完成。回退本检查点恢复原测试与锁文件;没有
|
||||
生产实现需要回退,也没有 push/publish。
|
||||
+1
-1
@@ -605,4 +605,4 @@
|
||||
- PKG-DANMUKU-11: [记录](changes/2026-09-13-PKG-DANMUKU-11-visible-lifetime.md) [记录](baselines/danmuku-lifetime-validation.json)
|
||||
- PKG-DANMUKU-12: [记录](changes/2026-09-13-PKG-DANMUKU-12-frame-sampling.md) [记录](baselines/danmuku-frame-sampling-validation.json)
|
||||
- PKG-JASSUB-08: [记录](changes/2026-09-14-PKG-JASSUB-08-offscreen.md) [记录](baselines/jassub-offscreen-validation.json) [记录](baselines/jassub-offscreen-patch.json)
|
||||
- PKG-JASSUB-09: [记录](changes/2026-09-14-PKG-JASSUB-09-hybrid.md) [记录](baselines/jassub-hybrid-validation.json) [记录](baselines/jassub-hybrid-patch.json)
|
||||
- PKG-JASSUB-09: [记录](changes/2026-09-14-PKG-JASSUB-09-hybrid.md) [记录](baselines/jassub-hybrid-validation.json) [记录](baselines/jassub-hybrid-patch.json) [记录](changes/2026-09-14-PKG-JASSUB-09-firefox-diagnostics.md) [记录](changes/2026-09-14-PKG-JASSUB-09-display.md) [记录](baselines/jassub-display-validation.json)
|
||||
@@ -1,5 +1,19 @@
|
||||
# 进度与证据
|
||||
|
||||
## PKG-JASSUB-09 合成截图验证(doing)
|
||||
|
||||
新增显式截图模式,由 Node pngjs@7.0.0 解码实际页面 PNG,不复制转移画布。
|
||||
绿色 ASS 字形有 seek 前后签名、播放推进、全屏、隐藏归零和恢复可见断言。
|
||||
修正截图用例短字幕窗口导致 WebKit 前置样本过期的问题后,三浏览器与三核心
|
||||
最终九项通过。默认 readback 和其红序列保留;早期截图模式也出现 Firefox
|
||||
首次像素/全屏失败,不能把本轮全绿解释成停顿已修复。09/05 继续 doing。
|
||||
根测试依赖与唯一 Yarn 锁同次更新,frozen 安装、严格工具链、相关 ESLint 和
|
||||
工具链反例测试通过。生产源码/公共类型/分发产物未变。
|
||||
[变更与中间失败](changes/2026-09-14-PKG-JASSUB-09-display.md)及
|
||||
[截图阶段和运行摘要](baselines/jassub-display-validation.json)记录实际覆盖。
|
||||
下一步保留 Firefox 未关闭风险继续取证,同时推进其他可独立完成的插件迁移和
|
||||
组合验收;本问题尚不足以阻止整项重构的其他实施工作。
|
||||
|
||||
## PKG-JASSUB-09 Firefox 原生绘制诊断(doing)
|
||||
|
||||
新增不加载 JASSUB 的 Worker/ImageBitmap 控制,按新核心、已发布核心及无脚本的
|
||||
|
||||
@@ -5652,6 +5652,8 @@
|
||||
"refactor/baselines/jassub-hybrid-validation.json",
|
||||
"refactor/changes/2026-09-14-PKG-JASSUB-09-firefox-diagnostics.md",
|
||||
"refactor/baselines/jassub-firefox-diagnostics.json",
|
||||
"refactor/changes/2026-09-14-PKG-JASSUB-09-display.md",
|
||||
"refactor/baselines/jassub-display-validation.json",
|
||||
"test/browser/jassub-platform.spec.js",
|
||||
"test/browser/jassub-native.spec.js",
|
||||
"test/browser/jassub-hybrid.spec.js"
|
||||
|
||||
+4
-1
@@ -4809,7 +4809,10 @@
|
||||
"evidence": [
|
||||
"changes/2026-09-14-PKG-JASSUB-09-hybrid.md",
|
||||
"baselines/jassub-hybrid-validation.json",
|
||||
"baselines/jassub-hybrid-patch.json"
|
||||
"baselines/jassub-hybrid-patch.json",
|
||||
"changes/2026-09-14-PKG-JASSUB-09-firefox-diagnostics.md",
|
||||
"changes/2026-09-14-PKG-JASSUB-09-display.md",
|
||||
"baselines/jassub-display-validation.json"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -19,6 +19,10 @@ CORE-25 默认选项缺陷已修复并通过相同三 Node 与严格打包检查
|
||||
## 锁文件和依赖维护
|
||||
|
||||
- 根构建工具固定精确版本并放在 devDependencies;发布包的 dependencies/peer 范围保持原样。
|
||||
- PKG-JASSUB-09 新增根 devDependency `pngjs@7.0.0`,在 Node 中解码 Playwright 截图,
|
||||
用实际合成画面与转移画布 readback 对照;不进入发布包。仅新增其精确 Yarn 锁条目,
|
||||
并验证固定 Node/Yarn 的 frozen 安装及严格工具链检查。上游 API 见
|
||||
[pngjs 文档](https://github.com/pngjs/pngjs#sync-api)。
|
||||
- ENG-04 新增 typescript-compat(npm:typescript@4.3.5)作为旧编译器消费探针;源码继续使用 typescript 5.9.3。精确 npm alias 也由锁检查保护;实际覆盖与命令见 [类型检查说明](typechecking.md)。
|
||||
- 添加根开发依赖使用 `yarn add --dev --exact --ignore-workspace-root-check <name>@<version>`,包级依赖使用 `yarn workspace <name> add ...`;运行依赖升级需要独立兼容证据。
|
||||
- manifest 和 yarn.lock 同次提交;不提交 package-lock.json、bun.lock 或第二份安装锁。旧 npm 锁及验证报告保留于 ENG-01 Git 历史,原本地 Yarn 锁也已在忽略缓存中备份。
|
||||
|
||||
@@ -205,3 +205,15 @@ transfer use main-thread fillRect and do not verify the Worker/ImageBitmap path.
|
||||
The report records these distinctions and never infers a JASSUB fix from a control
|
||||
pass. Preserve each raw report/results directory before the next browser command.
|
||||
See [the isolation record](../../refactor/changes/2026-09-14-PKG-JASSUB-09-firefox-diagnostics.md).
|
||||
|
||||
`ARTPLAYER_JASSUB_SCREENSHOT=true` adds a separate observation path to the native
|
||||
JASSUB spec. `jassub-display.js` decodes a clipped composited page PNG using the
|
||||
pinned root-only pngjs development dependency; it never copies the transferred
|
||||
canvas. This mode uses green ASS glyphs, distinct 0-30s and 40-110s cue windows,
|
||||
and seeks to 50s. It checks the initial cue time, visible pixels, changed glyph
|
||||
signature, playback advance, fullscreen geometry and a hidden/restored negative
|
||||
control. Normal readback fixtures and timeouts stay unchanged. Reported subtitle
|
||||
hashes identify the actual selected ASS, not the white baseline. Screenshots are
|
||||
attached at named phases; errors from individual polling attempts are retained.
|
||||
The fixture's automatic failure screenshot runs after cleanup and may show an
|
||||
empty player; use the named pre-cleanup screenshots for visual claims.
|
||||
@@ -0,0 +1,34 @@
|
||||
import { PNG } from 'pngjs'
|
||||
|
||||
// Read the browser's composited PNG in Node, without copying the transferred canvas.
|
||||
export async function captureJassubDisplay(page, phase) {
|
||||
const geometry = await page.evaluate(() => window.jassubGeometry())
|
||||
const started = Date.now()
|
||||
let png
|
||||
try {
|
||||
png = await page.screenshot({ clip: geometry.clip })
|
||||
}
|
||||
catch (error) {
|
||||
await page.evaluate((failure) => {
|
||||
(window.jassubNative.displayFailures ||= []).push(failure)
|
||||
}, { message: error.message, elapsed: Date.now() - started, geometry })
|
||||
throw error
|
||||
}
|
||||
const { data, width, height } = PNG.sync.read(png)
|
||||
let visible = 0
|
||||
let signature = 0
|
||||
for (let i = 0; i < data.length; i += 4) {
|
||||
// The screenshot diagnostic ASS uses green glyphs; normal fixtures stay white.
|
||||
if (data[i] < 70 && data[i + 1] > 180 && data[i + 2] < 70) {
|
||||
visible++
|
||||
signature = (signature + i) >>> 0
|
||||
}
|
||||
}
|
||||
const state = { ...geometry, phase, visible, signature, screenshotWidth: width, screenshotHeight: height, screenshotElapsed: Date.now() - started, observation: 'composited-png' }
|
||||
await page.evaluate((state) => {
|
||||
window.jassubNative.lastPixel = state
|
||||
if (state.phase)
|
||||
window.jassubNative.snapshots.push(state)
|
||||
}, state)
|
||||
return { state, png }
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import fs from 'node:fs'
|
||||
import process from 'node:process'
|
||||
import { ensureArchive, hash, readMember } from '../../refactor/scripts/releases.mjs'
|
||||
import { expect, test } from './fixtures.js'
|
||||
import { captureJassubDisplay } from './jassub-display.js'
|
||||
|
||||
const baseline = JSON.parse(fs.readFileSync('refactor/baselines/jassub-release.json', 'utf8'))
|
||||
const release = baseline.release
|
||||
@@ -14,7 +15,21 @@ const onDemandRender = process.env.ARTPLAYER_JASSUB_ON_DEMAND !== 'false'
|
||||
const defaultOffscreen = process.env.ARTPLAYER_JASSUB_OFFSCREEN === 'default'
|
||||
const readbackFrame = process.env.ARTPLAYER_JASSUB_READBACK_FRAME === 'true'
|
||||
const synchronousRender = process.env.ARTPLAYER_JASSUB_ASYNC_RENDER === 'false'
|
||||
const screenshotReadback = process.env.ARTPLAYER_JASSUB_SCREENSHOT === 'true'
|
||||
const seekTime = screenshotReadback ? 50 : 10
|
||||
async function pixels(page, phase) {
|
||||
if (screenshotReadback) {
|
||||
try {
|
||||
const { state, png } = await captureJassubDisplay(page, phase)
|
||||
if (phase)
|
||||
await test.info().attach(`jassub-display-${phase}`, { body: png, contentType: 'image/png' })
|
||||
return state
|
||||
}
|
||||
catch (error) {
|
||||
await test.info().attach('jassub-display-attempt-error', { body: error.stack || error.message, contentType: 'text/plain' })
|
||||
throw error
|
||||
}
|
||||
}
|
||||
return page.evaluate(async ({ phase, readbackFrame }) => {
|
||||
if (readbackFrame)
|
||||
await new Promise(requestAnimationFrame)
|
||||
@@ -30,9 +45,10 @@ Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour,
|
||||
Style: Default,Liberation Sans,36,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,0,2,10,10,20,1
|
||||
[Events]
|
||||
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
||||
Dialogue: 0,0:00:00.00,0:00:04.00,Default,,0,0,0,,Before seek
|
||||
Dialogue: 0,0:00:05.00,0:00:30.00,Default,,0,0,0,,After seek with actual WASM
|
||||
Dialogue: 0,0:00:00.00,${screenshotReadback ? '0:00:30.00' : '0:00:04.00'},Default,,0,0,0,,Before seek
|
||||
Dialogue: 0,${screenshotReadback ? '0:00:40.00,0:01:50.00' : '0:00:05.00,0:00:30.00'},Default,,0,0,0,,After seek with actual WASM
|
||||
`
|
||||
const testedSubtitles = screenshotReadback ? subtitles.replace('&H00FFFFFF', '&H0000FF00') : subtitles
|
||||
|
||||
for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) {
|
||||
test(`JASSUB ${artifact ? 'candidate artifact' : `published ${release.version}`} renders actual WASM subtitles through seek and layout with ${core} core, customCanvas=${customCanvas}, defaultOffscreen=${defaultOffscreen}`, async ({ page, context }, testInfo) => {
|
||||
@@ -133,9 +149,15 @@ for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) {
|
||||
window.jassub.addEventListener('error', event => reject(event.error), { once: true })
|
||||
})
|
||||
const readback = document.createElement('canvas')
|
||||
window.jassubPixels = (phase) => {
|
||||
window.jassubGeometry = () => {
|
||||
const canvas = window.jassub._canvas
|
||||
const rect = canvas.getBoundingClientRect()
|
||||
const video = window.art.video
|
||||
const quality = video.getVideoPlaybackQuality?.()
|
||||
return { width: canvas.width, height: canvas.height, cssWidth: rect.width, cssHeight: rect.height, clip: { x: rect.x, y: rect.y, width: rect.width, height: rect.height }, time: window.art.currentTime, connected: canvas.isConnected, media: { paused: video.paused, readyState: video.readyState, totalFrames: quality?.totalVideoFrames, droppedFrames: quality?.droppedVideoFrames, decodedFrames: video.webkitDecodedFrameCount, mozPresentedFrames: video.mozPresentedFrames }, onDemand: window.jassub._onDemandRender }
|
||||
}
|
||||
window.jassubPixels = (phase) => {
|
||||
const canvas = window.jassub._canvas
|
||||
readback.width = canvas.width
|
||||
readback.height = canvas.height
|
||||
const context = readback.getContext('2d')
|
||||
@@ -149,32 +171,34 @@ for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) {
|
||||
signature = (signature + i * pixels[i]) >>> 0
|
||||
}
|
||||
}
|
||||
const video = window.art.video
|
||||
const quality = video.getVideoPlaybackQuality?.()
|
||||
const state = { phase, visible, signature, width: canvas.width, height: canvas.height, cssWidth: rect.width, cssHeight: rect.height, time: window.art.currentTime, connected: canvas.isConnected, media: { paused: video.paused, readyState: video.readyState, totalFrames: quality?.totalVideoFrames, droppedFrames: quality?.droppedVideoFrames, decodedFrames: video.webkitDecodedFrameCount, mozPresentedFrames: video.mozPresentedFrames }, onDemand: window.jassub._onDemandRender }
|
||||
const state = { phase, visible, signature, ...window.jassubGeometry() }
|
||||
window.jassubNative.lastPixel = state
|
||||
if (phase)
|
||||
window.jassubNative.snapshots.push(state)
|
||||
return state
|
||||
}
|
||||
}, { subContent: subtitles, onDemandRender, customCanvas, defaultOffscreen, synchronousRender })
|
||||
}, { subContent: testedSubtitles, onDemandRender, customCanvas, defaultOffscreen, synchronousRender })
|
||||
const offscreen = await page.evaluate(() => window.jassubNative.offscreen)
|
||||
expect(offscreen.selected).toBe(defaultOffscreen && !customCanvas && offscreen.available)
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(async () => (await pixels(page)).visible).toBeGreaterThan(100)
|
||||
const before = await pixels(page, 'before-seek')
|
||||
expect(before.connected).toBe(true)
|
||||
if (screenshotReadback) {
|
||||
expect(before.time).toBeLessThan(30)
|
||||
expect(before.visible).toBeGreaterThan(100)
|
||||
}
|
||||
await page.locator('#pause').click()
|
||||
await page.evaluate(async () => {
|
||||
await page.evaluate(async (seekTime) => {
|
||||
const seeked = new Promise(resolve => window.art.video.addEventListener('seeked', resolve, { once: true }))
|
||||
window.art.currentTime = 10
|
||||
window.art.currentTime = seekTime
|
||||
await seeked
|
||||
})
|
||||
}, seekTime)
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(async () => (await pixels(page)).signature).not.toBe(before.signature)
|
||||
await expect.poll(async () => (await pixels(page)).visible).toBeGreaterThan(100)
|
||||
const after = await pixels(page, 'after-seek')
|
||||
expect(after.time).toBeGreaterThanOrEqual(10)
|
||||
expect(after.time).toBeGreaterThanOrEqual(seekTime)
|
||||
expect(after.signature).not.toBe(before.signature)
|
||||
await page.evaluate(() => {
|
||||
window.art.fullscreenWeb = true
|
||||
@@ -182,6 +206,18 @@ for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) {
|
||||
await expect.poll(async () => (await pixels(page)).cssWidth).toBeGreaterThan(before.cssWidth)
|
||||
await expect.poll(async () => (await pixels(page)).visible).toBeGreaterThan(100)
|
||||
await pixels(page, 'fullscreen')
|
||||
if (screenshotReadback) {
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(after.time + 0.2)
|
||||
await page.evaluate(() => {
|
||||
window.art.pause()
|
||||
window.jassub._canvas.style.visibility = 'hidden'
|
||||
})
|
||||
const hidden = await pixels(page, 'hidden-negative-control')
|
||||
expect(hidden.visible).toBe(0)
|
||||
await page.evaluate(() => window.jassub._canvas.style.visibility = '')
|
||||
await expect.poll(async () => (await pixels(page)).visible).toBeGreaterThan(100)
|
||||
await pixels(page, 'restored')
|
||||
}
|
||||
await testInfo.attach('actual-jassub-page', { body: await page.screenshot(), contentType: 'image/png' })
|
||||
if (artifact)
|
||||
await page.evaluate(() => window.jassub.destroy())
|
||||
@@ -199,7 +235,11 @@ for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) {
|
||||
const state = await page.evaluate(() => window.jassubNative).catch(error => ({ unavailable: error.message }))
|
||||
state.readbackFrame = readbackFrame
|
||||
state.synchronousRender = synchronousRender
|
||||
await testInfo.attach('actual-jassub-evidence', { body: JSON.stringify({ core, onDemandRender, customCanvas, defaultOffscreen, source: artifact ? { artifact, sha256: hash(code) } : { version: release.version, integrity: release.integrity, member, sha256: hash(code) }, subtitleSha256: hash(subtitles), resources, external, state, limitation: `Actual worker, WASM and local font; offscreen option ${defaultOffscreen ? 'omitted, actual capability/selection recorded' : 'explicitly false'}. Canvas pixels are read from a separate canvas copying the native display bitmap. No full failure recovery, sustained GPU/memory or physical-device acceptance; candidate adds direct-then-host destruction.` }, null, 2), contentType: 'application/json' })
|
||||
state.screenshotReadback = screenshotReadback
|
||||
const observation = screenshotReadback
|
||||
? 'Composited page PNG decoded in Node; authored green ASS glyphs with hidden/restored negative control. No transferred-canvas copy.'
|
||||
: 'Canvas pixels are read from a separate canvas copying the native display bitmap.'
|
||||
await testInfo.attach('actual-jassub-evidence', { body: JSON.stringify({ core, onDemandRender, customCanvas, defaultOffscreen, source: artifact ? { artifact, sha256: hash(code) } : { version: release.version, integrity: release.integrity, member, sha256: hash(code) }, subtitleSha256: hash(testedSubtitles), resources, external, state, limitation: `Actual worker, WASM and local font; offscreen option ${defaultOffscreen ? 'omitted, actual capability/selection recorded' : 'explicitly false'}. ${observation} No full failure recovery, sustained GPU/memory or physical-device acceptance; candidate adds direct-then-host destruction.` }, null, 2), contentType: 'application/json' })
|
||||
if (!page.isClosed()) {
|
||||
await page.evaluate(() => {
|
||||
if (window.art && !window.art.isDestroy)
|
||||
|
||||
@@ -6747,6 +6747,11 @@ pluralize@^8.0.0:
|
||||
resolved "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz"
|
||||
integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
|
||||
|
||||
pngjs@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-7.0.0.tgz#a8b7446020ebbc6ac739db6c5415a65d17090e26"
|
||||
integrity sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==
|
||||
|
||||
pnpm-workspace-yaml@1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.npmjs.org/pnpm-workspace-yaml/-/pnpm-workspace-yaml-1.5.0.tgz"
|
||||
|
||||
Reference in new issue
Block a user