From 32f51a330bdc2fccb6b17a2683bfbe7ca7300a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E8=89=AF?= <1204183885@qq.com> Date: Tue, 8 Sep 2026 08:15:03 +0000 Subject: [PATCH] chore: restore regression coverage after WebUI retirement (#11101) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(web-shell): follow up #9812 review findings and deferred suggestions Address the remaining review suggestions from the @qwen-code/webui retirement (#9812) and the deferred items tracked in #11076: - Fix the 01-architecture.md adapter diagram path (missing session/). - Correct the followup-suggestions doc: generation fires on end_turn, and headless/SDK daemon clients should opt out to avoid the per-turn LLM cost. - Pin playwright to 1.61.1 to restore version parity with @playwright/test. - Restore file: scheme rejection coverage in Markdown.test.ts. - Pin the historical sessionStorage key prefix with a literal assertion. - Cover the legacy exported JSONL rejection and ChatRecord happy path. - Restore the shell-substring classifier trap with a live path. - Point useDaemonFollowupSuggestion docs at ChatEditor, not the deleted InputForm. - Replace the theme-toggle timing-race assertion and exercise the unhandledrejection fail-closed path in the document browser gate. Closes #11076 * fix(test): poll the theme class instead of a matcher vitest lacks The browser gate imports expect from vitest and drives playwright's Locator directly, so toHaveClass does not exist on the assertion and typecheck:integration failed with TS2339 on this line. Poll the class attribute through the same expect.poll idiom the neighbouring assertions use, which keeps the retry the assertion was switched to and needs no @playwright/test dependency. Co-authored-by: Qwen-Coder Patrol-Run: qwen-pr-conflict/jmtooas8sln * fix(test): assert the exported theme class exactly `document-main.tsx` toggles `dark` and `light` on mutually exclusively and nothing else writes `documentElement` classes, so the attribute is always exactly one of them. Matching `/light/` as an unanchored substring would also accept `dark light`, which is the theme-toggle breakage this poll exists to catch. Co-authored-by: Qwen-Coder Patrol-Run: qwen-pr-closeout/jmtow5pgslz * fix(test): realpath the exporter main check and pin its JSONL guard Node realpath-resolves `import.meta.url` for the ESM main module but leaves `process.argv[1]` as invoked, so comparing the raw spellings made a symlinked invocation skip `main()` and exit 0 having written nothing — a silent no-op the harness reads as success. Compare realpaths instead and export the predicate so both directions are pinned. Also extract the input gate into `assertRenderableJsonl` so the legacy exported-JSONL rejection can be asserted directly: the previous tests only pinned the two predicates feeding it, so deleting the guard and losing the remediation hint kept every test green. Co-authored-by: Qwen-Coder Patrol-Run: qwen-pr-closeout/jmtow5pgslz * docs(followup): name the real ACP stop reasons, drop "unconditional" The parenthetical this PR added named `interrupted` as a daemon stop reason. The ACP `StopReason` union is `end_turn | max_tokens | max_turn_requests | refusal | cancelled`, so there is no `interrupted`; an interrupted turn reports `cancelled`. Name the four suppressing values the union actually defines so an integrator writing `stopReason === '...'` gets a branch that can fire. "unconditional" was wider than the code and contradicted the condition list on the same page: `Session.ts#maybeEmitFollowupSuggestion` returns early on the stop reason, the todo stop guard, `ui.enableFollowupSuggestions`, PLAN approval mode, a missing chat, and a non-model last history entry. Point at those conditions instead; the cost advice still holds because the `enableFollowupSuggestions === false` return sits before `generatePromptSuggestion` is reached. Co-authored-by: Qwen-Coder Patrol-Run: qwen-pr-closeout/jmtp6vibomd * test(web-shell): drop the duplicate historical-key assertion `persists under the literal historical key prefix`, added on this branch, became a byte-equivalent twin of `writes under the historical WebUI key`, which reached main with #11107 and arrived here through the merge at this PR's head: same `persistStableClientId('client-a', 'session-a')`, same literal `qwen-code-webui-client-id:session:session-a`, same expectation. One fact was reported under two names, so a prefix rename reddened two tests and an auditor could not tell which copy was load-bearing. The surviving test's comment already carries the rename rationale, so dropping the copy loses no coverage. Verified with `cd packages/web-shell && npx vitest run client/daemon/session/clientLifecycle.test.ts`: 19 passed before, 18 passed after. Mutating SESSION_CLIENT_ID_STORAGE_PREFIX to `qwen-code-webshell-client-id:session:` after the dedupe still reddens 3 tests (15 passed), including the surviving `writes under the historical WebUI key`; reverting the mutation returns to 18 passed. Co-authored-by: Qwen-Coder Patrol-Run: qwen-pr-closeout/jmtp6vibomd * test(integration): drop the unreachable unhandledrejection arm The block this PR added after the CDN-failure loop could not reach the `window.addEventListener('unhandledrejection', showLoadError)` handler its comment claimed to exercise. `page.route('**/*', route => route.abort('blockedbyclient'))` fails the `')).toBe(false); }); @@ -145,6 +150,11 @@ describe('isSafeImageSrc', () => { expect(isSafeImageSrc('javascript:alert(1)')).toBe(false); }); + it('blocks file: scheme', () => { + expect(isSafeImageSrc('file:///tmp/private.png')).toBe(false); + expect(isSafeImageSrc('file://attacker.example/share/img.png')).toBe(false); + }); + it('allows relative paths', () => { expect(isSafeImageSrc('/images/logo.png')).toBe(true); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fe18b65e0c..e8e070084e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -141,7 +141,7 @@ importers: specifier: ^8.0.1 version: 8.0.1 playwright: - specifier: ^1.57.0 + specifier: 1.61.1 version: 1.61.1 prettier: specifier: ^3.5.3