From b725df8e51bb9e975aad3e3f800ce78eba34d779 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 20 Oct 2026 16:19:39 +0800 Subject: [PATCH 1/2] chore: retire prototype workflow entry points --- .github/workflows/visactor-trusted-ci.yml | 36 - common/config/flow-ci.json | 46 -- common/scripts/flow-ci-dispatch.mjs | 35 - common/scripts/flow-ci-dispatch.test.mjs | 46 -- packages/vtable/__tests__/visual/README.md | 77 +- .../vtable/__tests__/visual/cases/README.md | 6 - .../__tests__/visual/playwright.config.mjs | 2 - packages/vtable/__tests__/visual/report.mjs | 114 +-- packages/vtable/__tests__/visual/reporter.mjs | 4 - .../vtable/__tests__/visual/visual.spec.mjs | 33 +- .../vtable/scripts/visual-candidate.test.mjs | 469 ------------ packages/vtable/scripts/visual-json.test.mjs | 99 --- packages/vtable/scripts/visual-suite.test.mjs | 259 ------- packages/vtable/scripts/visual-test.mjs | 50 +- packages/vtable/scripts/visual/build.mjs | 34 +- packages/vtable/scripts/visual/candidate.mjs | 674 ------------------ packages/vtable/scripts/visual/runner.mjs | 80 +-- packages/vtable/scripts/visual/runtime.mjs | 17 +- packages/vtable/scripts/visual/suite.mjs | 379 ---------- 19 files changed, 34 insertions(+), 2426 deletions(-) delete mode 100644 .github/workflows/visactor-trusted-ci.yml delete mode 100644 common/config/flow-ci.json delete mode 100644 common/scripts/flow-ci-dispatch.mjs delete mode 100644 common/scripts/flow-ci-dispatch.test.mjs delete mode 100644 packages/vtable/scripts/visual-candidate.test.mjs delete mode 100644 packages/vtable/scripts/visual-json.test.mjs delete mode 100644 packages/vtable/scripts/visual-suite.test.mjs delete mode 100644 packages/vtable/scripts/visual/candidate.mjs delete mode 100644 packages/vtable/scripts/visual/suite.mjs diff --git a/.github/workflows/visactor-trusted-ci.yml b/.github/workflows/visactor-trusted-ci.yml deleted file mode 100644 index 4921a4045e..0000000000 --- a/.github/workflows/visactor-trusted-ci.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: VisActor trusted CI dispatch -run-name: VisActor trusted CI / ${{ inputs.request_digest }} - -# 只受理默认分支的固定控制请求;PR 代码不会在持有 GitHub 凭据的 job 中执行。 -on: - workflow_dispatch: - inputs: - request: - description: Version-bound CI request from the trusted controller - required: true - type: string - request_digest: - description: SHA-256 of the canonical request - required: true - type: string - -permissions: - contents: read - -jobs: - dispatch: - runs-on: ubuntu-24.04 - timeout-minutes: 5 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - ref: ${{ github.sha }} - persist-credentials: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: '22.22.2' - - name: Confirm the bound request (does not execute tests) - env: - FLOW_CI_REQUEST: ${{ inputs.request }} - FLOW_CI_REQUEST_DIGEST: ${{ inputs.request_digest }} - run: node common/scripts/flow-ci-dispatch.mjs diff --git a/common/config/flow-ci.json b/common/config/flow-ci.json deleted file mode 100644 index 4218c8f205..0000000000 --- a/common/config/flow-ci.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "schemaVersion": 1, - "kind": "trusted_ci_policy", - "repository": "VisActor/VTable", - "baseRef": "develop", - "nodeMajor": 22, - "rushVersion": "5.148.0", - "pnpmVersion": "10.7.0", - "unitPackages": [ - "react-vtable", - "vtable", - "vtable-editors", - "vtable-gantt", - "vtable-plugins", - "vtable-search", - "vtable-sheet", - "vue-vtable" - ], - "buildTargets": [ - "@visactor/react-vtable", - "@visactor/vtable", - "@visactor/vtable-editors", - "@visactor/vtable-gantt", - "@visactor/vtable-plugins", - "@visactor/vtable-search", - "@visactor/vtable-sheet", - "@visactor/vue-vtable", - "@visactor/vtable-export", - "@visactor/vtable-calendar" - ], - "visualProduct": "vtable", - "visualSelection": "full", - "requiredChecks": [ - "VisActor Flow / Build", - "VisActor Flow / Unit", - "VisActor Flow / Repository visual" - ], - "firefoxPaste": true, - "visualSuites": [ - "core" - ], - "buildFormats": [ - "es", - "cjs" - ] -} diff --git a/common/scripts/flow-ci-dispatch.mjs b/common/scripts/flow-ci-dispatch.mjs deleted file mode 100644 index 123f540773..0000000000 --- a/common/scripts/flow-ci-dispatch.mjs +++ /dev/null @@ -1,35 +0,0 @@ -import { createHash } from 'node:crypto'; -import fs from 'node:fs/promises'; -import { fileURLToPath } from 'node:url'; -import path from 'node:path'; - -/** 固定键顺序;派发摘要绑定完整输入,不依赖作者提供的展示标题。 */ -export function canonical(value) { - if (Array.isArray(value)) return value.map(canonical); - if (value && typeof value === 'object') return Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])])); - return value; -} - -/** 默认分支只确认受理身份;候选源码、构建及测试由无凭据隔离侧执行。 */ -export function dispatchReceipt(text, expectedDigest, policy, env) { - if (typeof text !== 'string' || Buffer.byteLength(text) > 4096) throw new Error('派发输入超限'); - const request = JSON.parse(text); - const keys = ['schemaVersion', 'kind', 'requestId', 'repository', 'pullRequest', 'headSha', 'baseSha', 'controlCommit', 'policyDigest', 'dependencyDigest']; - const sha = value => typeof value === 'string' && /^[a-f0-9]{40}$/.test(value); - const hash = value => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value); - if (!request || Array.isArray(request) || Object.keys(request).sort().join() !== keys.sort().join() || - request.schemaVersion !== 1 || request.kind !== 'ci_request' || !hash(request.requestId) || - request.repository !== policy.repository || !Number.isSafeInteger(request.pullRequest) || request.pullRequest < 1 || - ![request.headSha, request.baseSha, request.controlCommit].every(sha) || ![request.policyDigest, request.dependencyDigest, expectedDigest].every(hash)) throw new Error('派发身份非法'); - const requestDigest = createHash('sha256').update(JSON.stringify(canonical(request))).digest('hex'); - if (requestDigest !== expectedDigest || env.GITHUB_EVENT_NAME !== 'workflow_dispatch' || - env.GITHUB_REPOSITORY !== policy.repository || env.GITHUB_REF !== 'refs/heads/' + policy.baseRef || - env.GITHUB_SHA !== request.controlCommit || env.GITHUB_RUN_ATTEMPT !== '1' || !/^[1-9][0-9]*$/.test(env.GITHUB_RUN_ID ?? '')) throw new Error('派发不是当前可信默认分支的首次运行'); - return { schemaVersion: 1, kind: 'ci_dispatch_receipt', requestDigest, request, workflowRunId: env.GITHUB_RUN_ID, - controlCommit: env.GITHUB_SHA, testsExecuted: false }; -} - -if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { - const policy = JSON.parse(await fs.readFile(new URL('../config/flow-ci.json', import.meta.url), 'utf8')); - console.log(JSON.stringify(dispatchReceipt(process.env.FLOW_CI_REQUEST, process.env.FLOW_CI_REQUEST_DIGEST, policy, process.env))); -} diff --git a/common/scripts/flow-ci-dispatch.test.mjs b/common/scripts/flow-ci-dispatch.test.mjs deleted file mode 100644 index 64a39d56bc..0000000000 --- a/common/scripts/flow-ci-dispatch.test.mjs +++ /dev/null @@ -1,46 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { createHash } from 'node:crypto'; -import fs from 'node:fs/promises'; -import { canonical, dispatchReceipt } from './flow-ci-dispatch.mjs'; - -const policy = JSON.parse(await fs.readFile(new URL('../config/flow-ci.json', import.meta.url), 'utf8')); -const request = { schemaVersion: 1, kind: 'ci_request', requestId: 'a'.repeat(64), repository: policy.repository, pullRequest: 1, - headSha: 'b'.repeat(40), baseSha: 'c'.repeat(40), controlCommit: 'd'.repeat(40), policyDigest: 'e'.repeat(64), dependencyDigest: 'f'.repeat(64) }; -const hash = value => createHash('sha256').update(JSON.stringify(canonical(value))).digest('hex'); -const env = { GITHUB_EVENT_NAME: 'workflow_dispatch', GITHUB_REPOSITORY: policy.repository, GITHUB_REF: 'refs/heads/develop', - GITHUB_SHA: request.controlCommit, GITHUB_RUN_ATTEMPT: '1', GITHUB_RUN_ID: '123' }; - -test('仅受理固定默认分支和精确输入,不宣称执行了测试', () => { - const result = dispatchReceipt(JSON.stringify(request), hash(request), policy, env); - assert.equal(result.testsExecuted, false); assert.equal(result.requestDigest, hash(request)); - assert.equal(result.workflowRunId, '123'); assert.deepEqual(result.request, request); -}); - -for (const change of [{ GITHUB_EVENT_NAME: 'pull_request_target' }, { GITHUB_EVENT_NAME: 'pull_request' }, - { GITHUB_REF: 'refs/pull/1/merge' }, { GITHUB_REF: 'refs/heads/untrusted' }, { GITHUB_REPOSITORY: 'fork/repository' }, - { GITHUB_SHA: '0'.repeat(40) }, { GITHUB_RUN_ATTEMPT: '2' }, { GITHUB_RUN_ID: '0' }]) test('拒绝不可信运行上下文 ' + JSON.stringify(change), () => { - assert.throws(() => dispatchReceipt(JSON.stringify(request), hash(request), policy, { ...env, ...change })); -}); - -for (const change of [{ pullRequest: 0 }, { repository: 'fork/repository' }, { headSha: 'main' }, { extra: true }, - { policyDigest: null }, { dependencyDigest: 'x' }, { kind: 'test_result' }]) test('拒绝未绑定输入 ' + JSON.stringify(change), () => { - const value = { ...request, ...change }; assert.throws(() => dispatchReceipt(JSON.stringify(value), hash(value), policy, env)); -}); - -test('摘要变化和超限输入不能复用受理记录', () => { - assert.throws(() => dispatchReceipt(JSON.stringify({ ...request, headSha: '0'.repeat(40) }), hash(request), policy, env)); - assert.throws(() => dispatchReceipt(' '.repeat(4097), hash(request), policy, env)); -}); - - -test('固定单测清单覆盖当前所有产品包,不因默认分支新增包而漏测', async () => { - const root = new URL('../../packages/', import.meta.url); const expected = []; - for (const entry of await fs.readdir(root, { withFileTypes: true })) { - if (!entry.isDirectory()) continue; - const name = entry.name; - try { const pkg = JSON.parse(await fs.readFile(new URL(name + '/package.json', root), 'utf8')); if (pkg.scripts?.test) expected.push(name); } - catch (error) { if (error.code !== 'ENOENT') throw error; } - } - assert.deepEqual([...policy.unitPackages].sort(), expected.sort()); -}); diff --git a/packages/vtable/__tests__/visual/README.md b/packages/vtable/__tests__/visual/README.md index 1bb50f9f0f..7bd5901701 100644 --- a/packages/vtable/__tests__/visual/README.md +++ b/packages/vtable/__tests__/visual/README.md @@ -4,7 +4,7 @@ ## 准备 -在仓库根目录使用 Node.js 22。VTable 与 VChart 统一固定 Playwright 1.63.0,安装 Rush 依赖和锁文件对应的 Chromium;两仓继续使用各自的执行配置与基线: +在仓库根目录使用 Node.js 22,安装 Rush 依赖和锁文件对应的 Chromium: ```sh node common/scripts/install-run-rush.js install --ignore-hooks @@ -32,72 +32,6 @@ node packages/vtable/scripts/visual-test.mjs --baseline <完整的40位commit-sh 正常结束、失败或 Ctrl+C 后,工具会回收本轮测试子进程、浏览器、HTTP 服务和临时基线 worktree。SIGKILL/断电后,先确认进程已结束,再检查 `git worktree list` 和 `.vtable-visual/running.lock`;不要删除其他任务的 worktree。 -## 冻结套件导出 - -平台接入和归档可以复用同一个命令导出完整用例与当前构建,不需要 BugServer 登录: - -```sh -node packages/vtable/scripts/visual-test.mjs --export-suite .vtable-visual/suites/current -node packages/vtable/scripts/visual-test.mjs --export-suite .vtable-visual/suites/next --previous-suite .vtable-visual/suites/current/suite-manifest.json -``` - -导出目录必须是新目录,位于工作区外或 Git 忽略目录;已有目录不会被覆盖。此模式不能与单例、目录、基线、列举或环境预检选项混用。它使用既有构建链生成所需的 `current-.js`,冻结原格式用例、页面、Playwright 运行器和辅助文件,并保存包版本与依赖锁文件。完整清单包含实际使用的 editors、gantt、plugins、sheet 和锁定 VChart 资源;VChart 版本来自已安装包,不使用 latest 或 CDN。此模式不下载官方基线、不运行截图、不接受视觉差异。 - -`suite-manifest.json` 使用 `schemaVersion=1`、`contractVersion=1.0`、`kind=suite_manifest`。所有文件记录相对路径、字节大小和 SHA-256;`suiteDigest` 标识规范化完整清单,`filesDigest` 标识冻结文件集合,`bundleDigest` 标识实际产品资源。`caseDigest` 包含用例、相对导入、公共运行器和使用的资源,截图键固定为 `final`,对应现有单例最终截图。历史 BugServer IDs 仅记录来源,不用于覆盖 Photo 用例,也不会写回源码。命令的 JSON 输出包含清单位置及按稳定 case ID 识别的 add/update/archive;归档只表示清单差异,不删除平台数据。 - -导出时校验唯一 ID/模块、完整资源、导入路径、符号链接及构建期工作区变化;失败退出 `2`,回收本次子进程,保留日志和 export-failure.json,移除不可消费的成功清单;重试使用新目录。套件列举和导入会执行 JavaScript:平台须把导出/发现放入隔离 worker,不能在有内网或业务凭据的控制进程执行。清单中的环境要求固定 Node 22、实际 Playwright 版本和现有截图设置;完整镜像、浏览器、字体和网络隔离仍由平台环境验收负责,导出成功不表示视觉测试或隔离验收通过。 - -确定性和非法输入检查: - -```sh -node --test packages/vtable/scripts/visual-suite.test.mjs -``` - -## 冻结候选与指定只读基准 - -同一用例还可以验证冻结构建并与平台提供的 PNG 集合比较,不重新构建或采集基准: - -```sh -node packages/vtable/scripts/visual-test.mjs --run-suite .vtable-visual/suites/current --run-request /path/to/run-request.json --baseline-set /path/to/baselines -# 可选 --output <新目录>;默认输出到 .vtable-visual/runs/ -``` - -三个输入必须配套,不能混用 `--case`、`--dir`、`--baseline`、`--self-compare` 或导出选项。输出须是新目录,位于工作区外或 Git 忽略目录,不能包含或覆盖输入;已有目录不会被覆盖。`suite-manifest.json` 需要 `adapterVersion=1.1.0`,增加实际提交的 `sourceParents` 与 `sourceDirty`;旧导出须重新生成。 - -`run-request.json` 使用公共接口契约 v1 的 `run_request`:固定仓库、产品、被测提交与源摘要、suite/bundle/policy 摘要、环境 ID、assetRevision、selection 和 executionKey。仅支持 `engines=["playwright"]`,Photo 的 suite/environment 为 `null`,截图键为 `final`。`full` 必须包含冻结清单全部用例;`targeted` 必须逐项列出 ID,不扩展为全量。选择摘要为规范化 items 的 SHA-256,执行键为规范化 `{identity, engines, selectionDigest, rerunSequence}` 的 SHA-256。`pr_head` 必须测试 head;`pr_merge` 必须绑定实际父提交 `[baseSha, headSha]`。本机 `local_workspace` 可包含 dirty 修改,但属于辅助证据,不能替代可信 CI 结果。 - -基准目录提供 `baseline-manifest.json`: - -```json -{ - "schemaVersion": 1, - "contractVersion": "1.0", - "kind": "baseline_manifest", - "artifactId": "selected-baselines", - "repository": "VisActor/VTable", - "productKey": "VTable", - "environmentId": "trusted-worker-environment-id", - "assetRevision": null, - "renderer": null, - "entries": [ - {"caseId": "list-basic", "screenshotId": "final", "baselineExpected": false, "path": null, "digest": null} - ] -} -``` - -此示例只声明一个首次基准项。请求目标 `identity.baseRef` 必须为 `develop`;`inputArtifacts` 必须包含该 artifactId、清单**原始文件字节**的 SHA-256、`mediaType=application/json` 和 `visibility=public|restricted`。可见性元信息不赋予执行器服务权限。环境 ID 和 assetRevision 必须与请求一致,所有选中截图均须有唯一条目;路径使用普通相对 POSIX 文件路径,禁止符号链接、越界和内容摘要不符。既有 PNG 填写 `baselineExpected=true`、相对 `path` 与 PNG 的 SHA-256;既有基准意外丢失仍声明 `true`,不得改成首次基准。只要使用既有 PNG,`renderer` 必须精确记录其实际 `{node, platform, arch, playwright, browser, settings}`,并与运行时实测一致。平台环境 ID 的完整镜像、字体、权限、内核和网络约束仍由可信控制器验收,本入口不建立操作系统隔离。 - -基准只复制到结果目录后用于原生 `toMatchSnapshot`,不开启基准更新;运行前后校验输入和副本不变。用例继续执行原有 mount、exercise/verify、有效绘制、连续稳定截图和 release。交互后再次检查绘制,空白、资源错误或语义断言失败不能发布候选图。 - -机器结果读取 `run-result.json`(`kind=run_result`、contractVersion=1.0)。逐截图保留三项检查、七类状态 `matched` / `different` / `no_baseline` / `assertion_failed` / `execution_error` / `skipped` / `cancelled`、图像摘要与证据路径;`complete` 仅表示全部选中截图具有终态报告,不表示比较通过。首次基准项经断言和稳定性验证可输出 `role=candidate` 的 PNG,但状态仍为 `no_baseline`、rawOutcome=failed、退出 `2`;意外缺基准另带 `MISSING_BASELINE`。只有完整的原生比较证据可返回 `different` 和退出 `1`;全部匹配返回 `0`;缺图、不完整、执行或清理错误返回 `2`。标准基准的接受、写回与服务接入另由协调流程负责,本入口没有同步或接受命令。 - -`summary.json`、`agent-summary.md` 和三图 HTML 是同次结果的查看入口;其中“完整比较”仍仅表示所选截图均完成有基准的比较。不要用首次候选已验证推断比较通过,也不要把指定基准或自比较称为官方 develop 通过。 - -```sh -# 先完成构建或套件导出,随后运行真实浏览器及失败路径专项 -node --test packages/vtable/scripts/visual-candidate.test.mjs -``` - ## 用例维护 目录说明及来源 ID 规则见 [用例指南](./cases/README.md)。开发完成后运行相关目录;提交 PR 前运行默认全量比较并检查差异。新功能或缺陷修复可以加独立用例。不得用放宽截图阈值、跳过语义断言或接受差异代替修复不稳定用例。工具自检: @@ -105,12 +39,3 @@ node --test packages/vtable/scripts/visual-candidate.test.mjs ```sh node --test packages/vtable/scripts/visual-test.test.mjs ``` - - -## 本地比较的机器输出 - -`node packages/vtable/scripts/visual-test.mjs --json` 保留默认全量和官方 develop 比较;也可附带现有 `--case`、`--dir`、`--baseline` 或 `--self-compare`。此选项仅用于本地比较,不与列举、单独预检、套件导出或冻结候选模式混用。 - -stdout 只有一个 `schemaVersion=1`、`kind=local_visual_result` 对象,进度写入 stderr。对象保留真实 `exitCode`、`status`、`complete`、精确 `selection`、结果 `counts`、源码 `source.before/after`、实际 `baseline`、环境和三份报告绝对路径。退出码仍为通过 `0`、差异 `1`、错误 `2`;准备失败时未取得的范围、源码或基线为 null,不推断成功。源码摘要覆盖 HEAD、未提交和未跟踪源码,检查完成并清理后再次核对;发生变化记为 `WORKSPACE_CHANGED`、退出 `2`。目录或单例完成仅表示所选用例完成,`--self-compare` 只表示本地稳定性,均不代表官方全量、CI 或合入资格。 - -依赖及浏览器继续由原生预检负责,不自动安装或修复。报告只位于本次返回的运行目录;取消或清理失败保留错误证据。机器契约专项:`node --test packages/vtable/scripts/visual-json.test.mjs`。可安装的公共 `visactor-flow check` 复用此入口,不另行实现截图比较。 diff --git a/packages/vtable/__tests__/visual/cases/README.md b/packages/vtable/__tests__/visual/cases/README.md index 9ca8f0530b..1b2d86d10f 100644 --- a/packages/vtable/__tests__/visual/cases/README.md +++ b/packages/vtable/__tests__/visual/cases/README.md @@ -19,9 +19,3 @@ 从 BugServer 迁移来源时,缺少 `interactions` 字段仅表示没有附加录制动作;仍须检查源码中的事件、定时器、异步调用、后续 API 更新及资源。保留目标配置、数据边界、动作顺序和最终状态。不得复制业务原文、内部地址、凭证、原始数据或截图到公开仓库;来源不明时先在仓库外私有目录复核。 新增或修改后先运行单例 `--self-compare --case `,再运行对应目录;提交前运行默认全量比较。若用例在官方基线不支持,报告为执行错误,应如实说明,不能把自比较当作双侧通过。 - -## 指定基准与首次候选 - -冻结套件执行仍使用同一份显式清单和原用例格式,步骤见 [工具说明](../README.md#冻结候选与指定只读基准)。新增用例保留稳定 ID、真实语义断言和交互状态验证;首次基准由输入清单明确声明 `baselineExpected=false`,不跳过绘制或稳定性检查。通过这些检查的 PNG 只进入运行结果暂存,`no_baseline` 不表示比较通过,也不自动变为正式基准。缺少既有基准、空白、缺图或断言失败必须保留错误。 - -运行器的故障注入和协议反例放在 `scripts/visual-candidate.test.mjs` 的独立副本中,不登记到产品截图清单、不提交破坏性 case。修改公共运行器后仍需执行相关测试和 PR 前官方 develop 全量比较。 diff --git a/packages/vtable/__tests__/visual/playwright.config.mjs b/packages/vtable/__tests__/visual/playwright.config.mjs index ab90ca2a5d..17caa4b118 100644 --- a/packages/vtable/__tests__/visual/playwright.config.mjs +++ b/packages/vtable/__tests__/visual/playwright.config.mjs @@ -5,8 +5,6 @@ import { settings } from './settings.mjs'; const runDir = process.env.VISUAL_RUN_DIR; const phase = process.env.VISUAL_PHASE; if (!runDir || !['baseline', 'current'].includes(phase)) throw new Error('请通过 visual-test.mjs 运行'); -if (process.env.VISUAL_EXTERNAL_BASELINE === '1' && phase !== 'current') - throw new Error('指定基准模式只运行候选侧,不允许更新基准'); export default defineConfig({ testDir: '.', diff --git a/packages/vtable/__tests__/visual/report.mjs b/packages/vtable/__tests__/visual/report.mjs index f404f7d7a1..b485945b20 100644 --- a/packages/vtable/__tests__/visual/report.mjs +++ b/packages/vtable/__tests__/visual/report.mjs @@ -23,8 +23,6 @@ function selectionDescription(report) { const label = selection.mode === 'all' ? '全部本地用例' - : selection.mode === 'selection' - ? '指定集合 ' + selection.value : `${selection.mode === 'directory' ? '目录' : '单用例'} ${selection.value}`; return `${label};选中 ${selection.selectedCount} / ${selection.totalCount};其余 ${ selection.totalCount - selection.selectedCount @@ -299,14 +297,9 @@ function agentSummary(report) { /** 生成无需服务和网络的三图报告;原图链接保留完整分辨率。 */ function renderHtml(report) { - const labels = { passed: '通过', diff: '视觉差异', error: '执行错误', not_run: '未完成', no_baseline: '无指定基准' }; + const labels = { passed: '通过', diff: '视觉差异', error: '执行错误', not_run: '未完成' }; const link = (file, label) => `${escape(label)}`; - const baseline = - report.baseline?.repository === 'frozen-images' - ? '指定冻结基准' - : report.baseline?.repository === 'working-tree' - ? '工作区自比较' - : '官方基线'; + const baseline = report.baseline?.repository === 'working-tree' ? '工作区自比较' : '官方基线'; const cards = report.cases .map( item => `
@@ -397,9 +390,9 @@ function renderHtml(report) { ) .join('')}