vscode拡張機能でGUIベースのアプリ配置ツールを作るための機能名
vscodeでアプリの配置を変更する拡張機能を作ろうと思ったときにAIに質問した内容をメモとして残します
(動作未確認を含む)
要件
・macOS上で機能するvscode拡張機能を提供する
・web版のvscodeで動作する
・HTMLベースのGUIを実装する
・.pycetra.mdのファイルを開いたとき、自動的にGUIを開く
・.pycetra.mdのファイルを開いたとき、そのファイルのデータをGUIに表示する
・GUI側からvscode拡張側へデータを送る
・起動しているアプリの座標を取得する
・起動しているアプリの座標を更新することで、操作スペース(Spaces)内/間をアプリが移動する
・アプリの使用中に、ターミナルを開かない
技術選定
・GUIのe2eのテストをするためのライブラリについて
アプリのGUIとしてweb viewを使用
・要件:HTMLベースのGUIを実装する
const panel = vscode.window.createWebviewPanel
web版のvscodeでアプリが動くようにする設定
・要件: web版のvscodeで動作する
package.json
"browser": "./dist/extension.js",
テキストドキュメントが開かれたときに発生するイベント
・要件:.pycetra.mdのファイルを開いたとき、自動的にGUIを開く
vscode.workspace.onDidOpenTextDocument
vscode拡張側からGUI側へデータを送る
・要件:.pycetra.mdのファイルを開いたとき、そのファイルのデータをGUIに表示する
const panel = vscode.window.createWebviewPanel
panel.webview.postMessage
apple scriptの結果をpythonへ渡す
・要件:起動しているアプリの座標を取得する
・要件:起動しているアプリの座標を更新することで、操作スペース(Spaces)内/間をアプリが移動する
const { exec } = require('child_process');
async function runAppleScript(scriptPath) {
return new Promise((resolve, reject) => {
exec(`osascript ${scriptPath}`, (error, stdout, stderr) => {
if (error) {
reject(`エラー: ${error.message}`);
} else if (stderr) {
reject(`標準エラー: ${stderr}`);
} else {
resolve(stdout.trim());
}
});
});
}
async function runPythonScript(scriptPath, arg) {
return new Promise((resolve, reject) => {
exec(`python ${scriptPath} "${arg}"`, (error, stdout, stderr) => {
if (error) {
reject(`エラー: ${error.message}`);
} else if (stderr) {
reject(`標準エラー: ${stderr}`);
} else {
resolve(stdout.trim());
}
});
});
}
const appleScriptResult = await runAppleScript('path/to/your/example.scpt');
console.log(`AppleScript Result: ${appleScriptResult}`);
const pythonResult = await runPythonScript('path/to/your/script.py', appleScriptResult);
console.log(`Python Script Result: ${pythonResult}`);
(Pythonの処理の中でアプリの座標を計算して座標を更新する。)
ファイルの内容を同期的に読み取る
fs.readFileSync
macで特定のアプリを移動する
・要件:起動しているアプリの座標を更新することで、操作スペース(Spaces)内/間をアプリが移動する
例: Finder
osascript -e 'tell application "Finder" to set bounds of window 3 to {100, 100, 800, 600}'
起動中のアプリ名を取得
・要件:起動しているアプリの座標を取得する
例1: すべてのアプリ
osascript -e 'tell application "System Events" to get {name} of (every process whose background only is false)'
{name} のみでなく、{name, index}など複数の情報にするとアクセシビリティが必要になってしまうためNG
例2: Finder
osascript -e 'tell application "Finder" to get {index, name} of windows'
操作スペースをまたいでアプリを移動
・要件:起動しているアプリの座標を更新することで、操作スペース(Spaces)内/間をアプリが移動する
操作スペース(Spaces)を跨いで移動する機能をosascriptに見つけられなかった。
他のツールですが、yabaiを使うとアプリをスペースを跨いで移動させることができました。
brew install koekeishiya/formulae/yabai
brew services start yabai
brew install jq
// yabaiへアクセシビリティを許可しておく
yabai -m query --windows | jq -r '.[] | select(.app == "Finder") | .id' | xargs -I{} yabai -m window {} --space 3
ターミナルを開かずにコマンドを実行
・要件:アプリの使用中に、ターミナルを開かない
const { execSync, exec } = require('child_process');
web view側からvscode側へpostMessage
・要件:GUI側からvscode拡張側へデータを送る
document.addEventListener('DOMContentLoaded', (event) => {
vscode.postMessage({
command: 'init_message',
});
});
GUI の E2E テストを行うためのライブラリについて
-
目的
- VSCode 拡張の操作から始まり、WebView 上の操作までを一連の流れとしてテストしたい。
- ローカルのファイルへアクセスできること。
-
yabai
コマンドの実行を含む macOS 固有の外部処理もテストしたい。
VSCode 拡張 + WebView E2E テスト 構成案 3 選
VSCode 拡張で下記 3 つの要素をテストする場合に考えられる代表的な 3 つのパターンを表にまとめました。
- (1) VSCode API のテスト
- (2) WebView 内部の E2E テスト
- (3)
yabai
コマンドやローカルファイル操作など外部処理
構成案 | 対応ステップ | 使用技術 | メリット | デメリット |
---|---|---|---|---|
案1: (1 と 2 のみ) vscode-test-web + Playwright |
(1) VSCode 拡張 API (2) WebView のみ対応 |
- vscode-test-web - Playwright |
- ブラウザ上で VSCode 拡張を動かしつつ WebView の DOM 操作が可能 - セットアップが比較的軽量で、Playwright によるテストコードも書きやすい |
- (3) にあたる macOS 特有の yabai コマンドなどローカル依存処理は動かせない- Web 版 VSCode は機能制限があり、ネイティブの VSCode とは挙動が異なるケースあり |
案2: (1,2,3 フルカバー) vscode-test + Playwright + Node.js exec |
(1) VSCode 拡張 API (2) WebView (3) 外部処理 |
- vscode-test - Playwright - Node.js の exec / execSync
|
- ローカルで Electron 版 VS Code を起動し、拡張機能テストと WebView E2E テストを一括管理できる - Node.js を経由して yabai コマンドやファイル操作なども実行可能 |
- VS Code 全体を外部ブラウザから操作するための公式サポートがなく、仕組みが非常に複雑 - WebView パネルの URL を取得し、Playwright と連携させる実装が必要 - テスト実行時に VS Code を起動しっぱなしにするため、環境構築やメンテナンスのコストが高い - バージョンアップや Electron の変更で動かなくなるリスクもある |
案3: (1,2,3 可能・ただしホットリロード不可) Open VSCode Server + Playwright |
(1) VSCode 拡張 API (2) WebView (3) 外部処理 |
- open vscode server - Playwright |
- サーバ上で「本物の」VS Code インスタンスを起動するため (3) の yabai やローカルアクセスも実施可能- ブラウザから Playwright を使って一括で E2E テストを実行できる |
- ホットリロードが難しく、拡張の修正ごとに再起動が必要 |
・案2はAPI制限のないvscode-testの実行のためとても魅力的でしたが、
vscode内のwebviewをPlaywrightから操作する方法を見つけることができず断念しました。
・現状案1(vscode-test-web + Playwright)をメインにこぼれた機能のテストを案3(Open VSCode Server + Playwrightでカバーする形
補足
-
(1) VSCode API のテスト
拡張機能のアクティベーションやコマンド実行、vscode.workspace
やvscode.window
などを利用する機能の検証。 -
(2) WebView 内部 E2E テスト
vscode.window.createWebviewPanel
で生成される WebView 内にロードした HTML/JS の動作を検証。
画面遷移やフォーム入力、DOM 検証などを Playwright / Puppeteer / Cypress などで実行。 -
(3)
yabai
やファイル操作を含む外部処理
macOS 固有のウィンドウ管理ツールyabai
やfs
モジュールによるファイル操作など、OS やローカル環境に依存する処理。
まとめ
- 案 3 は (1) と (2) に加えて (3) のローカル依存処理まで一括で検証できる点が魅力です。しかし VS Code 全体を外部ツールで操作するための公式サポートが無く、非常に高度な連携実装が必要 で、バージョンアップの影響も受けやすいなど、運用コストは高めです。
- Web 版 VSCode を利用する 案 1 や 案 5 は機能制限がある一方、ブラウザベースで動かせる軽快さがメリットです。
- 案 2 や 案 4 のように VSCode サーバ/ローカル環境にフォーカスする方法もありますが、それぞれホットリロード不可やセットアップの複雑さなどのデメリットがあります。
上記を踏まえ、どのステップ (1)(2)(3) をどこまで網羅したいか、また 開発・テスト環境をどこまで複雑に許容できるか を考慮しながら構成を選択するとよいでしょう。
キーワード
- ウィンドウマネージャ: yabai, Amethyst, xmonad
- ステージマネージャ: macOS Venturaの組み込みのウィンドウマネージャ
- 画面分割ツール: ShiftIt, Spectacle, Magnet, BetterSnapTool
Discussion