Skip to content

dom webview test infrastructure_ja

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

webview(DOM操作コード)のユニットテスト基盤を追加する

日付: 2026-09-14 追跡: Issue #210、sub-issue #211、#212、#213、#214、#215

概要

このリポジトリには DOM を操作するコードをテストする基盤がありません。そのため、webview 層の不具合(たとえば PR #209 の自動レビューで見つかった dragleave のバグ)をユニットテストで押さえられません。現状は次のとおりです。

  • tsconfig.test.json は src/webview/**/* を除外しており、lib に DOM を含めていない。
  • webview 関連の唯一のテスト assistantMessageFormatting.test.ts は、DOM を使わない文字列処理モジュールだけを対象にしている。
  • slashMenu.test.ts がテストしているのは src/slashCommandIds.ts(拡張ホスト側の定数配列)で、DOM を操作する SlashMenu クラスではない。
  • hashMenu.test.ts は存在しない。

この計画では、DOM への依存をコンストラクタで受け取る3つのクラスにユニットテストを追加します。対象は src/webview/chatRenderer.ts(PR #237 で追加されたストリーミング描画を含む)、src/webview/hashMenu.ts、src/webview/slashMenu.ts です。

src/webview/main.ts は対象外です。このモジュールは、acquireVsCodeApi() の呼び出し、document.getElementById による要素の取得、すべての addEventListener の登録を、読み込んだ時点でトップレベルで実行します。テストするには本番コードを initWebview(deps) のようなファクトリ関数に切り出す必要があり、これは別の設計判断です。使われていない @vscode/test-electron の Extension Host ハーネス(src/test/runTests.ts / src/test/suite/index.ts)を配線して E2E で検証することも対象外とします。

設計判断

  • jsdom ではなく happy-dom を使う。 jsdom は whatwg-encoding に依存しており、このパッケージは src/test/suite/dependencyVersions.test.ts のブロックリスト(blockedPackages)に入っている。happy-dom ^20.14.5 はこれに依存せず、インストールされるのは 9 パッケージで、allowScripts への追加も不要、npm audit の指摘も増えない。
  • happy-dom にセキュリティ上の下限を設ける。 GHSA-37j7-fg3j-429f(VM コンテキストから抜け出してリモートでコードを実行できる脆弱性)が 20.0.0 未満に該当するため、dependencyVersions.test.ts で宣言バージョンとインストール済みバージョンがどちらも 20.0.0 以上であることを検証する。完全一致で固定しないのは、Dependabot の更新でテストが壊れないようにするため。
  • Node.js 22.12 以上を必須にする。 happy-dom は ES モジュールとしてのみ配布されている。テストは CommonJS にコンパイルされて require() で読み込むため、Node.js 22.12 からフラグなしで使える require(esm) に依存する。package.json の engines.node を >=22.12.0 とし、README にも同じ要件を書く。
  • tsconfig.test.json は1つのままにする。 lib を ["ES2020", "DOM"] にし、src/webview/**/* の除外を外す。DOM ライブラリは型を追加するだけなので、拡張ホスト側テストの型チェックは変わらない。
  • フィクスチャ HTML は本番コードから生成する。 共通ユーティリティがスタブの vscode.Webview を渡して ChatViewProvider.getHtmlForWebview() を呼び、本番のパネル HTML を happy-dom に読み込む。これにより、フィクスチャが出荷されるマークアップとずれない。プロバイダのモジュールは vscode を最小限のスタブに差し替えて読み込み、読み込みで増えたモジュールキャッシュのエントリをその後で削除する。こうすることで、chatViewProvider.test.ts は引き続き自前のスタブでプロバイダを読み込める。
  • happy-dom のウィンドウを制限する。 JavaScript の実行は無効のまま(happy-dom 20 の既定値)にし、JavaScript ファイル・CSS ファイル・iframe ページの読み込みとページ遷移も無効にする。パネルの <script> タグは解析されるが、取得も実行もされない。
  • DOM API のスタブは作らない。 3つのクラスが呼ぶ DOM API(Element.prototype.scrollIntoView、requestAnimationFrame、KeyboardEvent、HTMLElement.click() を含む)は、すべて happy-dom が実装している。

実装

  • Phase 1 — 依存関係とビルド設定(#211): devDependencies に happy-dom ^20.14.5 を追加し、engines.node を >=22.12.0 に上げる。tsconfig.test.json を更新し、happy-dom の下限テストを追加し、README の Node.js 要件を更新する。
  • Phase 2 — 共通 DOM テストユーティリティ(#212): 新規ファイル src/test/suite/domTestUtils.ts。renderPanelHtml() は本番のパネル HTML を返す。installDom() は制限した happy-dom のウィンドウを作ってパネル HTML を書き込み、window、document、requestAnimationFrame、cancelAnimationFrame をグローバルに設定する。戻り値の dispose() は元のグローバルを戻してウィンドウを閉じる。キャンセル可能な KeyboardEvent を作るヘルパーと、postMessage の呼び出しを記録するスタブも用意する。
  • Phase 3 — chatRenderer.ts のテスト(#213): 新規ファイル src/test/suite/chatRenderer.test.ts。次をカバーする。
    • ユーザーとアシスタントのメッセージ描画(kind、contextLabels、リッチテキストとプレーンテキストの判定)
    • アシスタントの Copy/Append/Replace 操作
    • ピン状態の同期(setPinnedItems と、ピン留め後に作られたカード)
    • ストリーミングの一連の流れ。beginAssistantStream の後、updateAssistantStream が累積テキストを追記ではなく置き換えること、finalizeAssistantMessage がストリーミング中の吹き出しを再利用すること。ストリームがない場合に新しい吹き出しを作るフォールバックも含む
    • 結果カードの Open/Copy/Pin による postMessage
    • 読み込み中表示、エラーバナー、clear() による #welcome の再構築
  • Phase 4 — hashMenu.ts のテスト(#214): 新規ファイル src/test/suite/hashMenu.test.ts。次をカバーする。
    • 大文字小文字を区別しない絞り込みと 50 件の上限
    • メニュー表示中に setFiles を呼んだときの再絞り込み
    • 入力位置の判定の境界ケース(クォート付きパス、スペースを含むパス、文中のトークン、末尾の空白、直前が空白でない #)
    • ArrowUp/ArrowDown/Enter/Tab/Escape の処理と aria-activedescendant
    • キーボードとクリックで選んだときに、該当トークンの範囲だけが置き換わること
  • Phase 5 — SlashMenu の DOM テスト(#215): 既存の src/test/suite/slashMenu.test.ts に suite('SlashMenu (DOM)', ...) を追加する。次をカバーする。
    • コンストラクタがすべてのスラッシュコマンドをすぐに描画すること
    • 前方一致による絞り込み
    • 組み合わせ可能なコマンドの扱い(/mail /teams)
    • 組み合わせできない先行トークンがあるときにメニューを閉じること
    • キーボードとクリックによる選択

検証

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
  • 新しいテストがすべて成功し、既存のスイート(dependencyVersions.test.ts の whatwg-encoding ブロックリストのテストを含む)も成功し続けること。
  • happy-dom の追加で npm audit の指摘が増えないこと。

制約

  • src/webview/main.ts のリファクタリングとテストは対象外。
  • @vscode/test-electron の Extension Host ハーネスの配線は対象外。
  • テストを動かすために本番コードを変更する必要はない。

Clone this wiki locally