Skip to content

dom webview test infrastructure

Kazushi Kamegawa edited this page Sep 13, 2026 · 2 revisions

Add DOM/Webview Unit Testing Infrastructure

Date: 2026-09-14 Tracking: Issue #210, sub-issues #211, #212, #213, #214, and #215

Summary

This repository has no infrastructure for testing DOM-manipulating code, so regressions in the webview layer (for example the dragleave bug found by automated review on PR #209) cannot be covered by unit tests:

  • tsconfig.test.json excludes src/webview/**/* and has no DOM lib.
  • The only webview-adjacent test (assistantMessageFormatting.test.ts) covers a pure string-processing module with no DOM usage.
  • slashMenu.test.ts tests src/slashCommandIds.ts (an extension-host constant array), not the SlashMenu DOM class.
  • hashMenu.test.ts does not exist.

This plan adds unit tests for the three DOM-manipulating webview classes that take their DOM dependencies through the constructor: src/webview/chatRenderer.ts (including the streaming rendering added by PR #237), src/webview/hashMenu.ts, and src/webview/slashMenu.ts.

src/webview/main.ts is out of scope. It runs acquireVsCodeApi(), the document.getElementById lookups, and every addEventListener registration as module-load-time side effects, so testing it needs a production refactor (an initWebview(deps) factory), which is a separate design decision. Wiring the dormant @vscode/test-electron Extension Host harness (src/test/runTests.ts / src/test/suite/index.ts) for end-to-end coverage is also out of scope.

Design decisions

  • happy-dom, not jsdom. jsdom depends on whatwg-encoding, which src/test/suite/dependencyVersions.test.ts blocks (blockedPackages). happy-dom ^20.14.5 does not depend on it, installs 9 packages, needs no allowScripts entry, and adds no npm audit findings.
  • Security floor for happy-dom. dependencyVersions.test.ts asserts that the declared and installed happy-dom versions are 20.0.0 or later, because GHSA-37j7-fg3j-429f (VM context escape leading to remote code execution) affects earlier releases. A floor is used instead of an exact pin so Dependabot updates do not break the test.
  • Node.js 22.12 or later. happy-dom is published as an ES module only. The tests are compiled to CommonJS and load it with require(), which relies on Node's require(esm) support, available without a flag from Node.js 22.12. package.json declares engines.node as >=22.12.0, and the README states the same requirement.
  • Single tsconfig.test.json. lib is ["ES2020", "DOM"] and src/webview/**/* is no longer excluded. The DOM lib is additive and does not change how the extension-host tests type-check.
  • Fixture HTML comes from production. The shared utility calls ChatViewProvider.getHtmlForWebview() with a stub vscode.Webview and loads the real panel markup into happy-dom, so the fixture cannot drift from what ships. The provider module is loaded with vscode resolved to a minimal stub, and the module cache entries added by that load are removed afterwards, so chatViewProvider.test.ts still loads the provider with its own stubs.
  • Locked-down happy-dom window. JavaScript evaluation stays disabled (the happy-dom 20 default), and JavaScript file loading, CSS file loading, iframe page loading, and navigation are disabled. The panel's <script> tags are therefore parsed but never fetched or run.
  • No DOM API stubs. happy-dom implements every DOM API the three classes call, including Element.prototype.scrollIntoView, requestAnimationFrame, KeyboardEvent, and HTMLElement.click().

Implementation

  • Phase 1 — Dependencies and build config (#211): Add happy-dom ^20.14.5 to devDependencies, raise engines.node to >=22.12.0, update tsconfig.test.json, add the happy-dom floor test, and update the README Node.js requirement.
  • Phase 2 — Shared DOM test utilities (#212): New src/test/suite/domTestUtils.ts. renderPanelHtml() returns the production panel HTML. installDom() creates the locked-down happy-dom window, writes the panel HTML into it, installs the window, document, requestAnimationFrame, and cancelAnimationFrame globals, and returns a dispose() that restores the previous globals and closes the window. Helpers create cancelable KeyboardEvents and a recording postMessage stub.
  • Phase 3 — chatRenderer.ts tests (#213): New src/test/suite/chatRenderer.test.ts. Covers user and assistant message rendering (kind, contextLabels, rich-text versus plain-text), the Copy/Append/Replace assistant actions, pinned-state sync (setPinnedItems and cards built after pinning), the streaming lifecycle (beginAssistantStream → updateAssistantStream replacing rather than appending the cumulative text → finalizeAssistantMessage reusing the streaming bubble, plus the fallback that creates a bubble when no stream exists), result-card Open/Copy/Pin postMessage calls, loading indicators, error banners, and clear() rebuilding #welcome.
  • Phase 4 — hashMenu.ts tests (#214): New src/test/suite/hashMenu.test.ts. Covers case-insensitive filtering with the 50-item cap, re-filtering on setFiles while open, selection-context edge cases (quoted paths, paths with spaces, tokens mid-text, trailing whitespace, # not preceded by whitespace), ArrowUp/ArrowDown/Enter/Tab/Escape handling with aria-activedescendant, and token-range replacement on keyboard and click selection.
  • Phase 5 — SlashMenu DOM tests (#215): A new suite('SlashMenu (DOM)', ...) block in the existing src/test/suite/slashMenu.test.ts. Covers the constructor's immediate render of all slash commands, prefix filtering, combinable-command handling (/mail /teams), rejection of non-combinable previous tokens, and keyboard and click selection.

Validation

npm install
npx tsc -p ./tsconfig.test.json --noEmit
npm test
npm run lint
npm run security:check
npm install
npx tsc -p ./tsconfig.test.json --noEmit
npm test
npm run lint
npm run security:check
  • All new tests pass, and the existing suites, including the whatwg-encoding blocklist assertion in dependencyVersions.test.ts, keep passing.
  • Adding happy-dom introduces no new npm audit findings.

Constraints

  • src/webview/main.ts is not refactored or unit-tested as part of this work.
  • The @vscode/test-electron Extension Host harness is not wired up as part of this work.
  • No production code changes are needed to run the tests.

Clone this wiki locally