Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ui-browser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,13 @@ jobs:
run: npm run test:web
- name: Run blocking Chromium regression
run: npm run test:browser

- name: Upload Playwright failure artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-test-results
path: e2e/node_modules/.cache/playwright-test-results/
if-no-files-found: warn
retention-days: 7
include-hidden-files: true
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
过程中请使用中文和我沟通,但 console 里请优先使用英文。

ui-evidence-exempt: 2026-10-08 PWRS_REPORTER_TOKEN 缺失且自建 runner 的 PWRS 访问与实际发布链路尚未验证 (#199)
浏览器验证政策:真实 Chromium 回归保持阻断;本开源仓不接维护者私有 PWRS 服务,也不要求 PWRS_REPORTER_TOKEN;失败证据由本仓 Browser regression workflow 上传 GitHub Actions artifact。
# Repository Guidelines

## Project Structure & Module Organization
Expand Down
43 changes: 43 additions & 0 deletions docs/sessions/261017-issue-cleanup/reviews/C-browser-verdict.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!-- delegate-outcome: succeeded -->

## [codex] 后续裁决

两条 P2 事实成立,但按校准后的验收契约接受不修、不阻断交付。第一条是末尾 console/network 汇总在业务断言通过后运行;更早的断言失败由原断言消息和 Playwright trace 取证,本次不承诺每个失败都输出末尾汇总。第二条是 job 超时、手动取消或 runner 终止不在“测试失败后上传 artifact”的承诺内。`failure-visibility: p2-only` 保持不变。

实证:[workflow run 39003528268](https://github.com/zlxlabs/VideoTranscriptAPI/actions/runs/39003528268) 以预期的 Chromium 404 失败结束,artifact `playwright-test-results`(ID `12090260702`)已实际下载。下载的 trace 在 Trace Viewer 显示失败测试动作及 `http://127.0.0.1:41277/__e2e__/diagnostic-404.png` 的 GET 404。咨询来源:Claude Opus,task `vta-261017-browser-review-scope`;采纳两条 P2 不阻断的结论,不采纳历史取消时序发生在 setup 阶段的猜测。

## Verdict

审查结论:P2-only;阻断交付:是。真实 Chromium 门禁、正常失败路径的 trace 与截图保存、artifact 路径及长期政策均成立;不变式 1 的错误汇总会被前置断言短路,不变式 2 的 workflow 取消路径不会上传产物。

failure-visibility: p2-only

## Findings

### P2 — 前置断言失败时不会输出 console 与网络错误汇总(不变式 1)

- 位置:[e2e/tests/local-upload-full-chain.spec.ts:229](../../../../e2e/tests/local-upload-full-chain.spec.ts#L229)、[e2e/tests/local-upload-full-chain.spec.ts:401](../../../../e2e/tests/local-upload-full-chain.spec.ts#L401)。
- 触发:让 `POST /api/uploads` 返回 HTTP 500;第 229 行的 202 断言先抛错,末尾第 401–411 行构造并断言 `failureReport` 的代码不会运行。
- 风险:该次 Chromium 回归仍失败,trace artifact 仍可提供底层事件,但 Actions 测试失败输出不会同时列出 console 与 network 两类错误,违反不变式 1 的报告要求。
- 阻断理由:这是明确验收项;P2 来自诊断信息缺失,测试本身仍失败、没有静默放行。

### P2 — Job 到时限取消时 failure-only 上传步骤不会执行(不变式 2)

- 位置:[.github/workflows/ui-browser.yml:14](../../../../.github/workflows/ui-browser.yml#L14)、[.github/workflows/ui-browser.yml:47](../../../../.github/workflows/ui-browser.yml#L47)。
- 触发:job 的 15 分钟上限在 Chromium 回归仍运行时到期。真实 runner run `38715807063` 从 job 开始到 cancelled 约 15 分 25 秒,Chromium step 在结束前 16 秒才启动并被取消;当前固定 diff 保留同一个 job timeout,并新增 `if: failure()` 上传条件。
- 证据与机制:GitHub 对 job timeout 的定义是自动取消;取消时会重评尚未执行的 step 条件。`failure()` 仅表示之前的 step 失败,而取消由 `cancelled()` 表示,因此该上传 step 不会因取消而执行。[GitHub workflow 条件](https://docs.github.com/en/actions/reference/workflows-and-actions/expressions)、[timeout-minutes](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax)、[取消流程](https://docs.github.com/en/enterprise-cloud@latest/actions/reference/workflows-and-actions/workflow-cancellation)。
- 风险:job 以 cancelled 结束、门禁不会变绿,但该次 Chromium 现场的 trace、截图没有可下载 artifact。实际自托管 runner 已出现该时限形态;严重度 P2(证据丢失,不是静默放行),按不变式 2 阻断交付。

## 不变式核对

- 不变式 1:保留原业务断言;console、page error、请求失败与 HTTP 错误仍被观察,console 行带 location URL。前置断言绕过统一汇总见上述 finding。
- 不变式 2:Playwright `outputDir` 与 workflow 上传路径均为 `e2e/node_modules/.cache/playwright-test-results`;失败截图、失败 trace、隐藏目录与 7 天保留均有配置。普通 step failure 会上传;job timeout/cancellation 会跳过当前 `failure()` 条件,见第二条 finding。官方配置说明支持全局 `use` 作为项目共享选项,trace 模式保留失败运行:[Playwright 配置](https://playwright.dev/docs/test-use-options)、[Playwright TestConfig](https://playwright.dev/docs/api/class-testconfig)。
- 不变式 3:AGENTS.md 与浏览器测试文档声明本仓长期使用原生 Actions artifact,不接维护者私有 PWRS、不要求 token;真实 Chromium workflow 仍是阻断步骤。`include-hidden-files` 与 retention 输入符合上传 action 契约:[upload-artifact](https://github.com/actions/upload-artifact/blob/main/README.md)。
- 不变式 4:没有新增重试、服务或宽泛上传路径;`retries: 0` 保留,上传仅指向 Playwright 的实际 `outputDir`。

## 验证与基线

- OCR 前置扫描:`status=reviewed`。工具标注 cancellation 风险为 High,本仓按真实用法重定为 P2:近期 50 次 workflow 中的取消 run `38715807063` 全 job 用时约 15 分 25 秒,且 Chromium step 正在运行,吻合 15 分钟 job 上限;该路径真实存在。若触发,门禁保持 cancelled/非绿,但产物缺失只影响诊断,故不是 P1。精确 H0 run `39002966220` 的 Chromium job 成功,运行约 3 分 51 秒。Playwright 的 180 秒单测超时覆盖普通用例挂起,但不能覆盖本次实测的 setup 耗时挤占整个 job 预算。
- OCR 对“上传路径重复”意见被当前 config、npm 调用路径及精确 H0 成功运行证伪;设备字段顺序意见无当前行为证据,均未登记为 finding。
- 任务卡所给主干基线不可用,故继承红未能判定;本次没有运行测试或触发新 CI,因此没有本卡新红。已读取精确 H0 的既有 workflow run:Vitest 与 Chromium 步骤成功,artifact 步骤按成功路径跳过。另将旧 run `38715807063` 的取消记录作为当前自托管 runner 的环境证据,不把它归为本卡新红。
- 窄验证 `git diff --check` 通过;本 verdict 是本分支唯一新增文件。
4 changes: 3 additions & 1 deletion docs/testing/local-upload-browser.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@

`/view/{token}?raw=...` 与 `?page=...` 是阅读页导出目的地,主路径测试通过真实链接点击断言。`/robots.txt`、`/sitemap.xml`、`/sw.js`、manifest 与 API 是资源/接口,不是额外页面;profile 路由是 API JSON,不是 UI。浏览器测试收集 console error、page error、request failed 及 HTTP 4xx/5xx,任一均使 case 失败。

普通断言失败或 Playwright 用例超时、且 Chromium step 正常以失败结束时,failure-only 上传步骤会运行并上传 Playwright `outputDir`。末尾 console/network 汇总仅在业务断言通过后运行;更早失败时应结合原断言消息与 trace 取证。job 超时、手动取消或 runner 终止时不承诺执行 artifact 上传。

## 运行及 CI

首次运行:`uv sync`、根目录 `npm ci`、`npm --prefix e2e ci`,并安装 Chromium:`cd e2e && npx playwright install chromium`。之后 `npm run test:browser`;保留的 Vitest 契约层单独以 `npm run test:web` 运行。缺 Chromium、服务无法启动或断言不符均非零;无 skip/retry。单一 spec、`fullyParallel: false`、`workers: 1` 避免共享 fixture 并发污染。

`.github/workflows/ui-browser.yml` 对所有 PR `opened`、`synchronize`、`reopened`、`ready_for_review`、`converted_to_draft` 事件在 `[self-hosted, ci]` 执行 Chromium 与 Vitest;也可 `workflow_dispatch`。本仓没有 push workflow。`.github/workflows/gate.yml` 的 caller 已设置 `has_ui: true`;引用的 `zlxlabs/gate/.github/workflows/gate-v2.yml@v2` 在实际 tag 的 `workflow_call.inputs` 声明 `has_ui: boolean`(tag `v2` 当前解析为 commit `805856b9c6a501145521e8cae06000dc669af8c6`,已通过 GitHub API 读取实际 workflow_call schema 确认)。gate-hub 台账仍待 #1393,不能据本仓 caller 推断登记已生效。

录屏证据不在本阻断 workflow。因无 `PWRS_REPORTER_TOKEN` 且 runner 发布通路未经验证,未接假 workflow;`AGENTS.md` 中有效期 90 天的 `ui-evidence-exempt:` 对应本仓接入待办 #199。该豁免只针对录屏,不豁免真实浏览器行为门禁。
真实 Chromium 回归保持阻断。本开源仓不接维护者私有 PWRS 服务,也不要求 `PWRS_REPORTER_TOKEN`;失败时由 Browser regression workflow 上传 Playwright `outputDir`,包含失败 trace 与截图,artifact 保留 7 天。该政策取代 #199 的限时豁免和待接 PWRS 待办。
2 changes: 2 additions & 0 deletions e2e/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export default defineConfig({
use: {
baseURL,
browserName: 'chromium',
screenshot: 'only-on-failure',
trace: 'retain-on-failure',
...devices['Desktop Chrome'],
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
Expand Down
21 changes: 16 additions & 5 deletions e2e/tests/local-upload-full-chain.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,12 @@ test('Chromium uploads raw media through production API, worker, shared ASR/LLM,
const browserErrors: string[] = [];
const unexpectedNetwork: string[] = [];
let observedResponseCount = 0;
page.on('pageerror', (error) => browserErrors.push(error.message));
page.on('pageerror', (error) => browserErrors.push(`pageerror: ${error.message}`));
page.on('console', (message) => {
if (message.type() === 'error') browserErrors.push(message.text());
if (message.type() === 'error') {
const url = message.location().url || '(unknown URL)';
browserErrors.push(`console: ${message.text()} at ${url}`);
}
});
page.on('requestfailed', (failed) => {
const path = new URL(failed.url()).pathname;
Expand Down Expand Up @@ -389,13 +392,21 @@ test('Chromium uploads raw media through production API, worker, shared ASR/LLM,
const revoked = (await inspectRealState(request)).uploads[0];
expect(revoked.revoked_at).toBeTruthy();
expect(revoked.root_status).toBe('success');
expect(browserErrors).toEqual([]);
const expectedPageAborts = [
'GET /api/audit/filter-options net::ERR_ABORTED',
'GET /api/audit/history net::ERR_ABORTED',
];
expect(observedResponseCount).toBeGreaterThan(0);
const unexpectedNetworkErrors = unexpectedNetwork.filter((entry) => !expectedPageAborts.includes(entry));
const failureReport = [
'Browser failure diagnostics',
'Console errors:',
...(browserErrors.length ? browserErrors.map((entry) => ` ${entry}`) : [' none']),
'Unexpected network errors:',
...(unexpectedNetworkErrors.length ? unexpectedNetworkErrors.map((entry) => ` ${entry}`) : [' none']),
].join('\n');
expect(
unexpectedNetwork.filter((entry) => !expectedPageAborts.includes(entry)),
).toEqual([]);
{ consoleErrors: browserErrors, networkErrors: unexpectedNetworkErrors },
failureReport,
).toEqual({ consoleErrors: [], networkErrors: [] });
});
Loading