Browse Source

Modify Full Integration Test Structure to allow for multiple tests that start new tasks
Add test for mode switching functionality

ColemanRoo 10 months ago
parent
commit
4585281dc8

+ 1 - 0
.env.e2e

@@ -0,0 +1 @@
+OPENROUTER_API_KEY=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

+ 416 - 136
package-lock.json

@@ -59,8 +59,9 @@
 				"@types/debug": "^4.1.12",
 				"@types/diff": "^5.2.1",
 				"@types/diff-match-patch": "^1.0.36",
+				"@types/glob": "^8.1.0",
 				"@types/jest": "^29.5.14",
-				"@types/mocha": "^10.0.7",
+				"@types/mocha": "^10.0.10",
 				"@types/node": "20.x",
 				"@types/string-similarity": "^4.0.2",
 				"@typescript-eslint/eslint-plugin": "^7.14.1",
@@ -69,11 +70,13 @@
 				"@vscode/test-electron": "^2.4.0",
 				"esbuild": "^0.24.0",
 				"eslint": "^8.57.0",
+				"glob": "^11.0.1",
 				"husky": "^9.1.7",
 				"jest": "^29.7.0",
 				"jest-simple-dot-reporter": "^1.0.5",
 				"lint-staged": "^15.2.11",
 				"mkdirp": "^3.0.1",
+				"mocha": "^11.1.0",
 				"npm-run-all": "^4.1.5",
 				"prettier": "^3.4.2",
 				"rimraf": "^6.0.1",
@@ -3304,6 +3307,7 @@
 			"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
 			"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
 			"dev": true,
+			"license": "ISC",
 			"dependencies": {
 				"string-width": "^5.1.2",
 				"string-width-cjs": "npm:string-width@^4.2.0",
@@ -4150,6 +4154,7 @@
 			"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
 			"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
 			"dev": true,
+			"license": "MIT",
 			"optional": true,
 			"engines": {
 				"node": ">=14"
@@ -5921,6 +5926,17 @@
 			"dev": true,
 			"license": "MIT"
 		},
+		"node_modules/@types/glob": {
+			"version": "8.1.0",
+			"resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz",
+			"integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"@types/minimatch": "^5.1.2",
+				"@types/node": "*"
+			}
+		},
 		"node_modules/@types/graceful-fs": {
 			"version": "4.1.9",
 			"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
@@ -5964,11 +5980,19 @@
 				"pretty-format": "^29.0.0"
 			}
 		},
+		"node_modules/@types/minimatch": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
+			"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
+			"dev": true,
+			"license": "MIT"
+		},
 		"node_modules/@types/mocha": {
 			"version": "10.0.10",
 			"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz",
 			"integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==",
-			"dev": true
+			"dev": true,
+			"license": "MIT"
 		},
 		"node_modules/@types/ms": {
 			"version": "2.1.0",
@@ -6305,6 +6329,16 @@
 				"node": ">=18"
 			}
 		},
+		"node_modules/@vscode/test-cli/node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
 		"node_modules/@vscode/test-cli/node_modules/chokidar": {
 			"version": "3.6.0",
 			"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -6329,6 +6363,191 @@
 				"fsevents": "~2.3.2"
 			}
 		},
+		"node_modules/@vscode/test-cli/node_modules/cliui": {
+			"version": "7.0.4",
+			"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+			"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"string-width": "^4.2.0",
+				"strip-ansi": "^6.0.0",
+				"wrap-ansi": "^7.0.0"
+			}
+		},
+		"node_modules/@vscode/test-cli/node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/@vscode/test-cli/node_modules/glob": {
+			"version": "10.4.5",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+			"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"foreground-child": "^3.1.0",
+				"jackspeak": "^3.1.2",
+				"minimatch": "^9.0.4",
+				"minipass": "^7.1.2",
+				"package-json-from-dist": "^1.0.0",
+				"path-scurry": "^1.11.1"
+			},
+			"bin": {
+				"glob": "dist/esm/bin.mjs"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/@vscode/test-cli/node_modules/jackspeak": {
+			"version": "3.4.3",
+			"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+			"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+			"dev": true,
+			"license": "BlueOak-1.0.0",
+			"dependencies": {
+				"@isaacs/cliui": "^8.0.2"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			},
+			"optionalDependencies": {
+				"@pkgjs/parseargs": "^0.11.0"
+			}
+		},
+		"node_modules/@vscode/test-cli/node_modules/lru-cache": {
+			"version": "10.4.3",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+			"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+			"dev": true,
+			"license": "ISC"
+		},
+		"node_modules/@vscode/test-cli/node_modules/mocha": {
+			"version": "10.8.2",
+			"resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz",
+			"integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-colors": "^4.1.3",
+				"browser-stdout": "^1.3.1",
+				"chokidar": "^3.5.3",
+				"debug": "^4.3.5",
+				"diff": "^5.2.0",
+				"escape-string-regexp": "^4.0.0",
+				"find-up": "^5.0.0",
+				"glob": "^8.1.0",
+				"he": "^1.2.0",
+				"js-yaml": "^4.1.0",
+				"log-symbols": "^4.1.0",
+				"minimatch": "^5.1.6",
+				"ms": "^2.1.3",
+				"serialize-javascript": "^6.0.2",
+				"strip-json-comments": "^3.1.1",
+				"supports-color": "^8.1.1",
+				"workerpool": "^6.5.1",
+				"yargs": "^16.2.0",
+				"yargs-parser": "^20.2.9",
+				"yargs-unparser": "^2.0.0"
+			},
+			"bin": {
+				"_mocha": "bin/_mocha",
+				"mocha": "bin/mocha.js"
+			},
+			"engines": {
+				"node": ">= 14.0.0"
+			}
+		},
+		"node_modules/@vscode/test-cli/node_modules/mocha/node_modules/glob": {
+			"version": "8.1.0",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+			"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+			"deprecated": "Glob versions prior to v9 are no longer supported",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"fs.realpath": "^1.0.0",
+				"inflight": "^1.0.4",
+				"inherits": "2",
+				"minimatch": "^5.0.1",
+				"once": "^1.3.0"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/@vscode/test-cli/node_modules/mocha/node_modules/minimatch": {
+			"version": "5.1.6",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+			"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"brace-expansion": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/@vscode/test-cli/node_modules/mocha/node_modules/supports-color": {
+			"version": "8.1.1",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+			"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/supports-color?sponsor=1"
+			}
+		},
+		"node_modules/@vscode/test-cli/node_modules/mocha/node_modules/yargs": {
+			"version": "16.2.0",
+			"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+			"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"cliui": "^7.0.2",
+				"escalade": "^3.1.1",
+				"get-caller-file": "^2.0.5",
+				"require-directory": "^2.1.1",
+				"string-width": "^4.2.0",
+				"y18n": "^5.0.5",
+				"yargs-parser": "^20.2.2"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/@vscode/test-cli/node_modules/path-scurry": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+			"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+			"dev": true,
+			"license": "BlueOak-1.0.0",
+			"dependencies": {
+				"lru-cache": "^10.2.0",
+				"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.18"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
 		"node_modules/@vscode/test-cli/node_modules/readdirp": {
 			"version": "3.6.0",
 			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -6341,6 +6560,62 @@
 				"node": ">=8.10.0"
 			}
 		},
+		"node_modules/@vscode/test-cli/node_modules/string-width": {
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/@vscode/test-cli/node_modules/strip-ansi": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/@vscode/test-cli/node_modules/wrap-ansi": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+			"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-styles": "^4.0.0",
+				"string-width": "^4.1.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+			}
+		},
+		"node_modules/@vscode/test-cli/node_modules/yargs-parser": {
+			"version": "20.2.9",
+			"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+			"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+			"dev": true,
+			"license": "ISC",
+			"engines": {
+				"node": ">=10"
+			}
+		},
 		"node_modules/@vscode/test-electron": {
 			"version": "2.4.1",
 			"resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz",
@@ -7998,7 +8273,8 @@
 			"version": "9.2.2",
 			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
 			"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
-			"dev": true
+			"dev": true,
+			"license": "MIT"
 		},
 		"node_modules/encoding-sniffer": {
 			"version": "0.2.0",
@@ -9224,21 +9500,25 @@
 			}
 		},
 		"node_modules/glob": {
-			"version": "10.4.5",
-			"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
-			"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+			"version": "11.0.1",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz",
+			"integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==",
 			"dev": true,
+			"license": "ISC",
 			"dependencies": {
 				"foreground-child": "^3.1.0",
-				"jackspeak": "^3.1.2",
-				"minimatch": "^9.0.4",
+				"jackspeak": "^4.0.1",
+				"minimatch": "^10.0.0",
 				"minipass": "^7.1.2",
 				"package-json-from-dist": "^1.0.0",
-				"path-scurry": "^1.11.1"
+				"path-scurry": "^2.0.0"
 			},
 			"bin": {
 				"glob": "dist/esm/bin.mjs"
 			},
+			"engines": {
+				"node": "20 || >=22"
+			},
 			"funding": {
 				"url": "https://github.com/sponsors/isaacs"
 			}
@@ -9254,6 +9534,22 @@
 				"node": ">= 6"
 			}
 		},
+		"node_modules/glob/node_modules/minimatch": {
+			"version": "10.0.1",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
+			"integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"brace-expansion": "^2.0.1"
+			},
+			"engines": {
+				"node": "20 || >=22"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
 		"node_modules/globals": {
 			"version": "13.24.0",
 			"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
@@ -10266,18 +10562,19 @@
 			}
 		},
 		"node_modules/jackspeak": {
-			"version": "3.4.3",
-			"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
-			"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz",
+			"integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==",
 			"dev": true,
+			"license": "BlueOak-1.0.0",
 			"dependencies": {
 				"@isaacs/cliui": "^8.0.2"
 			},
+			"engines": {
+				"node": "20 || >=22"
+			},
 			"funding": {
 				"url": "https://github.com/sponsors/isaacs"
-			},
-			"optionalDependencies": {
-				"@pkgjs/parseargs": "^0.11.0"
 			}
 		},
 		"node_modules/jake": {
@@ -11899,6 +12196,7 @@
 			"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
 			"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
 			"dev": true,
+			"license": "ISC",
 			"engines": {
 				"node": ">=16 || 14 >=14.17"
 			}
@@ -11924,10 +12222,11 @@
 			}
 		},
 		"node_modules/mocha": {
-			"version": "10.8.2",
-			"resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz",
-			"integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==",
+			"version": "11.1.0",
+			"resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz",
+			"integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==",
 			"dev": true,
+			"license": "MIT",
 			"dependencies": {
 				"ansi-colors": "^4.1.3",
 				"browser-stdout": "^1.3.1",
@@ -11936,7 +12235,7 @@
 				"diff": "^5.2.0",
 				"escape-string-regexp": "^4.0.0",
 				"find-up": "^5.0.0",
-				"glob": "^8.1.0",
+				"glob": "^10.4.5",
 				"he": "^1.2.0",
 				"js-yaml": "^4.1.0",
 				"log-symbols": "^4.1.0",
@@ -11946,8 +12245,8 @@
 				"strip-json-comments": "^3.1.1",
 				"supports-color": "^8.1.1",
 				"workerpool": "^6.5.1",
-				"yargs": "^16.2.0",
-				"yargs-parser": "^20.2.9",
+				"yargs": "^17.7.2",
+				"yargs-parser": "^21.1.1",
 				"yargs-unparser": "^2.0.0"
 			},
 			"bin": {
@@ -11955,16 +12254,7 @@
 				"mocha": "bin/mocha.js"
 			},
 			"engines": {
-				"node": ">= 14.0.0"
-			}
-		},
-		"node_modules/mocha/node_modules/ansi-regex": {
-			"version": "5.0.1",
-			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-			"dev": true,
-			"engines": {
-				"node": ">=8"
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
 			}
 		},
 		"node_modules/mocha/node_modules/chokidar": {
@@ -11991,48 +12281,72 @@
 				"fsevents": "~2.3.2"
 			}
 		},
-		"node_modules/mocha/node_modules/cliui": {
-			"version": "7.0.4",
-			"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
-			"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+		"node_modules/mocha/node_modules/glob": {
+			"version": "10.4.5",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+			"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
 			"dev": true,
+			"license": "ISC",
 			"dependencies": {
-				"string-width": "^4.2.0",
-				"strip-ansi": "^6.0.0",
-				"wrap-ansi": "^7.0.0"
+				"foreground-child": "^3.1.0",
+				"jackspeak": "^3.1.2",
+				"minimatch": "^9.0.4",
+				"minipass": "^7.1.2",
+				"package-json-from-dist": "^1.0.0",
+				"path-scurry": "^1.11.1"
+			},
+			"bin": {
+				"glob": "dist/esm/bin.mjs"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
 			}
 		},
-		"node_modules/mocha/node_modules/emoji-regex": {
-			"version": "8.0.0",
-			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-			"dev": true
-		},
-		"node_modules/mocha/node_modules/glob": {
-			"version": "8.1.0",
-			"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
-			"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
-			"deprecated": "Glob versions prior to v9 are no longer supported",
+		"node_modules/mocha/node_modules/glob/node_modules/minimatch": {
+			"version": "9.0.5",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+			"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
 			"dev": true,
+			"license": "ISC",
 			"dependencies": {
-				"fs.realpath": "^1.0.0",
-				"inflight": "^1.0.4",
-				"inherits": "2",
-				"minimatch": "^5.0.1",
-				"once": "^1.3.0"
+				"brace-expansion": "^2.0.1"
 			},
 			"engines": {
-				"node": ">=12"
+				"node": ">=16 || 14 >=14.17"
 			},
 			"funding": {
 				"url": "https://github.com/sponsors/isaacs"
 			}
 		},
+		"node_modules/mocha/node_modules/jackspeak": {
+			"version": "3.4.3",
+			"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+			"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+			"dev": true,
+			"license": "BlueOak-1.0.0",
+			"dependencies": {
+				"@isaacs/cliui": "^8.0.2"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			},
+			"optionalDependencies": {
+				"@pkgjs/parseargs": "^0.11.0"
+			}
+		},
+		"node_modules/mocha/node_modules/lru-cache": {
+			"version": "10.4.3",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+			"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+			"dev": true,
+			"license": "ISC"
+		},
 		"node_modules/mocha/node_modules/minimatch": {
 			"version": "5.1.6",
 			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
 			"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
 			"dev": true,
+			"license": "ISC",
 			"dependencies": {
 				"brace-expansion": "^2.0.1"
 			},
@@ -12040,42 +12354,33 @@
 				"node": ">=10"
 			}
 		},
-		"node_modules/mocha/node_modules/readdirp": {
-			"version": "3.6.0",
-			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
-			"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+		"node_modules/mocha/node_modules/path-scurry": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+			"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
 			"dev": true,
+			"license": "BlueOak-1.0.0",
 			"dependencies": {
-				"picomatch": "^2.2.1"
+				"lru-cache": "^10.2.0",
+				"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
 			},
 			"engines": {
-				"node": ">=8.10.0"
-			}
-		},
-		"node_modules/mocha/node_modules/string-width": {
-			"version": "4.2.3",
-			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-			"dev": true,
-			"dependencies": {
-				"emoji-regex": "^8.0.0",
-				"is-fullwidth-code-point": "^3.0.0",
-				"strip-ansi": "^6.0.1"
+				"node": ">=16 || 14 >=14.18"
 			},
-			"engines": {
-				"node": ">=8"
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
 			}
 		},
-		"node_modules/mocha/node_modules/strip-ansi": {
-			"version": "6.0.1",
-			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+		"node_modules/mocha/node_modules/readdirp": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+			"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
 			"dev": true,
 			"dependencies": {
-				"ansi-regex": "^5.0.1"
+				"picomatch": "^2.2.1"
 			},
 			"engines": {
-				"node": ">=8"
+				"node": ">=8.10.0"
 			}
 		},
 		"node_modules/mocha/node_modules/supports-color": {
@@ -12093,50 +12398,6 @@
 				"url": "https://github.com/chalk/supports-color?sponsor=1"
 			}
 		},
-		"node_modules/mocha/node_modules/wrap-ansi": {
-			"version": "7.0.0",
-			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-			"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-			"dev": true,
-			"dependencies": {
-				"ansi-styles": "^4.0.0",
-				"string-width": "^4.1.0",
-				"strip-ansi": "^6.0.0"
-			},
-			"engines": {
-				"node": ">=10"
-			},
-			"funding": {
-				"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-			}
-		},
-		"node_modules/mocha/node_modules/yargs": {
-			"version": "16.2.0",
-			"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
-			"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
-			"dev": true,
-			"dependencies": {
-				"cliui": "^7.0.2",
-				"escalade": "^3.1.1",
-				"get-caller-file": "^2.0.5",
-				"require-directory": "^2.1.1",
-				"string-width": "^4.2.0",
-				"y18n": "^5.0.5",
-				"yargs-parser": "^20.2.2"
-			},
-			"engines": {
-				"node": ">=10"
-			}
-		},
-		"node_modules/mocha/node_modules/yargs-parser": {
-			"version": "20.2.9",
-			"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
-			"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
-			"dev": true,
-			"engines": {
-				"node": ">=10"
-			}
-		},
 		"node_modules/monaco-vscode-textmate-theme-converter": {
 			"version": "0.1.7",
 			"resolved": "https://registry.npmjs.org/monaco-vscode-textmate-theme-converter/-/monaco-vscode-textmate-theme-converter-0.1.7.tgz",
@@ -12961,26 +13222,31 @@
 			"dev": true
 		},
 		"node_modules/path-scurry": {
-			"version": "1.11.1",
-			"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
-			"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
+			"integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
 			"dev": true,
+			"license": "BlueOak-1.0.0",
 			"dependencies": {
-				"lru-cache": "^10.2.0",
-				"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+				"lru-cache": "^11.0.0",
+				"minipass": "^7.1.2"
 			},
 			"engines": {
-				"node": ">=16 || 14 >=14.18"
+				"node": "20 || >=22"
 			},
 			"funding": {
 				"url": "https://github.com/sponsors/isaacs"
 			}
 		},
 		"node_modules/path-scurry/node_modules/lru-cache": {
-			"version": "10.4.3",
-			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
-			"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
-			"dev": true
+			"version": "11.0.2",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz",
+			"integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==",
+			"dev": true,
+			"license": "ISC",
+			"engines": {
+				"node": "20 || >=22"
+			}
 		},
 		"node_modules/path-type": {
 			"version": "5.0.0",
@@ -14270,6 +14536,7 @@
 			"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
 			"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
 			"dev": true,
+			"license": "MIT",
 			"dependencies": {
 				"eastasianwidth": "^0.2.0",
 				"emoji-regex": "^9.2.2",
@@ -14288,6 +14555,7 @@
 			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
 			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
 			"dev": true,
+			"license": "MIT",
 			"dependencies": {
 				"emoji-regex": "^8.0.0",
 				"is-fullwidth-code-point": "^3.0.0",
@@ -14302,6 +14570,7 @@
 			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
 			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
 			"dev": true,
+			"license": "MIT",
 			"engines": {
 				"node": ">=8"
 			}
@@ -14310,13 +14579,15 @@
 			"version": "8.0.0",
 			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
 			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-			"dev": true
+			"dev": true,
+			"license": "MIT"
 		},
 		"node_modules/string-width-cjs/node_modules/strip-ansi": {
 			"version": "6.0.1",
 			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
 			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
 			"dev": true,
+			"license": "MIT",
 			"dependencies": {
 				"ansi-regex": "^5.0.1"
 			},
@@ -14411,6 +14682,7 @@
 			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
 			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
 			"dev": true,
+			"license": "MIT",
 			"dependencies": {
 				"ansi-regex": "^5.0.1"
 			},
@@ -14423,6 +14695,7 @@
 			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
 			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
 			"dev": true,
+			"license": "MIT",
 			"engines": {
 				"node": ">=8"
 			}
@@ -15378,6 +15651,7 @@
 			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
 			"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
 			"dev": true,
+			"license": "MIT",
 			"dependencies": {
 				"ansi-styles": "^6.1.0",
 				"string-width": "^5.0.1",
@@ -15396,6 +15670,7 @@
 			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
 			"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
 			"dev": true,
+			"license": "MIT",
 			"dependencies": {
 				"ansi-styles": "^4.0.0",
 				"string-width": "^4.1.0",
@@ -15413,6 +15688,7 @@
 			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
 			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
 			"dev": true,
+			"license": "MIT",
 			"engines": {
 				"node": ">=8"
 			}
@@ -15421,13 +15697,15 @@
 			"version": "8.0.0",
 			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
 			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-			"dev": true
+			"dev": true,
+			"license": "MIT"
 		},
 		"node_modules/wrap-ansi-cjs/node_modules/string-width": {
 			"version": "4.2.3",
 			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
 			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
 			"dev": true,
+			"license": "MIT",
 			"dependencies": {
 				"emoji-regex": "^8.0.0",
 				"is-fullwidth-code-point": "^3.0.0",
@@ -15442,6 +15720,7 @@
 			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
 			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
 			"dev": true,
+			"license": "MIT",
 			"dependencies": {
 				"ansi-regex": "^5.0.1"
 			},
@@ -15454,6 +15733,7 @@
 			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
 			"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
 			"dev": true,
+			"license": "MIT",
 			"engines": {
 				"node": ">=12"
 			},

+ 5 - 1
package.json

@@ -283,6 +283,7 @@
 		"publish:marketplace": "vsce publish && ovsx publish",
 		"publish": "npm run build && changeset publish && npm install --package-lock-only",
 		"version-packages": "changeset version && npm install --package-lock-only",
+		"e2e:test": "npm run build && tsc -p tsconfig.e2e.json && npx dotenvx run -f .env.e2e -- node ./out-e2e/test/runTest.js",
 		"vscode:prepublish": "npm run package",
 		"vsix": "rimraf bin && mkdirp bin && npx vsce package --out bin",
 		"watch": "npm-run-all -p watch:*",
@@ -342,8 +343,9 @@
 		"@types/debug": "^4.1.12",
 		"@types/diff": "^5.2.1",
 		"@types/diff-match-patch": "^1.0.36",
+		"@types/glob": "^8.1.0",
 		"@types/jest": "^29.5.14",
-		"@types/mocha": "^10.0.7",
+		"@types/mocha": "^10.0.10",
 		"@types/node": "20.x",
 		"@types/string-similarity": "^4.0.2",
 		"@typescript-eslint/eslint-plugin": "^7.14.1",
@@ -354,10 +356,12 @@
 		"mkdirp": "^3.0.1",
 		"rimraf": "^6.0.1",
 		"eslint": "^8.57.0",
+		"glob": "^11.0.1",
 		"husky": "^9.1.7",
 		"jest": "^29.7.0",
 		"jest-simple-dot-reporter": "^1.0.5",
 		"lint-staged": "^15.2.11",
+		"mocha": "^11.1.0",
 		"npm-run-all": "^4.1.5",
 		"prettier": "^3.4.2",
 		"ts-jest": "^29.2.5",

+ 23 - 0
src/test/runTest.ts

@@ -0,0 +1,23 @@
+import * as path from "path"
+
+import { runTests } from "@vscode/test-electron"
+
+async function main() {
+	try {
+		// The folder containing the Extension Manifest package.json
+		// Passed to `--extensionDevelopmentPath`
+		const extensionDevelopmentPath = path.resolve(__dirname, "../../")
+
+		// The path to the extension test script
+		// Passed to --extensionTestsPath
+		const extensionTestsPath = path.resolve(__dirname, "./suite/index")
+
+		// Download VS Code, unzip it and run the integration test
+		await runTests({ extensionDevelopmentPath, extensionTestsPath })
+	} catch {
+		console.error("Failed to run tests")
+		process.exit(1)
+	}
+}
+
+main()

+ 0 - 12
src/test/extension.test.ts → src/test/suite/extension.test.ts

@@ -48,16 +48,4 @@ suite("Roo Code Extension", () => {
 			assert.ok(commands.includes(cmd), `Command ${cmd} should be registered`)
 		}
 	})
-
-	test("Webview panel can be created", () => {
-		const view = vscode.window.createWebviewPanel(
-			"roo-cline.SidebarProvider",
-			"Roo Code",
-			vscode.ViewColumn.One,
-			{},
-		)
-
-		assert.ok(view, "Failed to create webview panel")
-		view.dispose()
-	})
 })

+ 91 - 0
src/test/suite/index.ts

@@ -0,0 +1,91 @@
+import * as path from "path"
+import Mocha from "mocha"
+import { glob } from "glob"
+import { ClineAPI } from "../../exports/cline"
+import { ClineProvider } from "../../core/webview/ClineProvider"
+import * as vscode from "vscode"
+
+declare global {
+	var api: ClineAPI
+	var provider: ClineProvider
+	var extension: vscode.Extension<ClineAPI> | undefined
+	var panel: vscode.WebviewPanel | undefined
+}
+
+export async function run(): Promise<void> {
+	// Create the mocha test
+	const mocha = new Mocha({
+		ui: "tdd",
+		timeout: 60000,
+	})
+
+	const testsRoot = path.resolve(__dirname, "..")
+
+	try {
+		// Find all test files
+		const files = await glob("**/**.test.js", { cwd: testsRoot })
+
+		// Add files to the test suite
+		files.forEach((f: string) => mocha.addFile(path.resolve(testsRoot, f)))
+
+		//Set up global extension, api, provider, and panel
+		globalThis.extension = vscode.extensions.getExtension("RooVeterinaryInc.roo-cline")
+		if (!globalThis.extension) {
+			throw new Error("Extension not found")
+		}
+
+		globalThis.api = globalThis.extension.isActive
+			? globalThis.extension.exports
+			: await globalThis.extension.activate()
+		globalThis.provider = globalThis.api.sidebarProvider
+		globalThis.provider.storeSecret("openRouterApiKey", process.env.OPENROUTER_API_KEY || "sk-or-v1-fake-api-key")
+		await globalThis.provider.updateGlobalState("apiProvider", "openrouter")
+		await globalThis.provider.updateGlobalState("openRouterModelId", "anthropic/claude-3.5-sonnet")
+
+		globalThis.panel = vscode.window.createWebviewPanel(
+			"roo-cline.SidebarProvider",
+			"Roo Code",
+			vscode.ViewColumn.One,
+			{
+				enableScripts: true,
+				enableCommandUris: true,
+				retainContextWhenHidden: true,
+				localResourceRoots: [globalThis.extension?.extensionUri],
+			},
+		)
+
+		await globalThis.provider.resolveWebviewView(globalThis.panel)
+
+		let startTime = Date.now()
+		const timeout = 60000
+		const interval = 1000
+
+		while (Date.now() - startTime < timeout) {
+			if (globalThis.provider.viewLaunched) {
+				break
+			}
+
+			await new Promise((resolve) => setTimeout(resolve, interval))
+		}
+
+		// Run the mocha test
+		return new Promise((resolve, reject) => {
+			try {
+				mocha.run((failures: number) => {
+					if (failures > 0) {
+						reject(new Error(`${failures} tests failed.`))
+					} else {
+						resolve()
+					}
+				})
+			} catch (err) {
+				console.error(err)
+				reject(err)
+			}
+		})
+	} catch (err) {
+		console.error("Error while running tests:")
+		console.error(err)
+		throw err
+	}
+}

+ 99 - 0
src/test/suite/modes.test.ts

@@ -0,0 +1,99 @@
+import * as assert from "assert"
+import * as vscode from "vscode"
+
+suite("Roo Code Modes", () => {
+	test("Should handle switching modes correctly", async function () {
+		const timeout = 30000
+		const interval = 1000
+
+		if (!globalThis.extension) {
+			assert.fail("Extension not found")
+		}
+
+		try {
+			let startTime = Date.now()
+
+			// Ensure the webview is launched.
+			while (Date.now() - startTime < timeout) {
+				if (globalThis.provider.viewLaunched) {
+					break
+				}
+
+				await new Promise((resolve) => setTimeout(resolve, interval))
+			}
+
+			await globalThis.provider.updateGlobalState("mode", "Ask")
+			await globalThis.provider.updateGlobalState("alwaysAllowModeSwitch", true)
+			await globalThis.provider.updateGlobalState("autoApprovalEnabled", true)
+
+			// Start a new task.
+			await globalThis.api.startNewTask(
+				"For each mode (Code, Architect, Ask) respond with the mode name and what it specializes in after switching to that mode, do not start with the current mode, be sure to say 'I AM DONE' after the task is complete",
+			)
+
+			// Wait for task to appear in history with tokens.
+			startTime = Date.now()
+
+			while (Date.now() - startTime < timeout) {
+				const messages = globalThis.provider.messages
+
+				if (
+					messages.some(
+						({ type, text }) =>
+							type === "say" && text?.includes("I AM DONE") && !text?.includes("be sure to say"),
+					)
+				) {
+					break
+				}
+
+				await new Promise((resolve) => setTimeout(resolve, interval))
+			}
+			if (globalThis.provider.messages.length === 0) {
+				assert.fail("No messages received")
+			}
+
+			assert.ok(
+				globalThis.provider.messages.some(
+					({ type, text }) => type === "say" && text?.includes(`"request":"[switch_mode to 'code' because:`),
+				),
+				"Did not receive expected response containing 'Roo wants to switch to code mode'",
+			)
+			assert.ok(
+				globalThis.provider.messages.some(
+					({ type, text }) => type === "say" && text?.includes("software engineer"),
+				),
+				"Did not receive expected response containing 'I am Roo in Code mode, specializing in software engineering'",
+			)
+
+			assert.ok(
+				globalThis.provider.messages.some(
+					({ type, text }) =>
+						type === "say" && text?.includes(`"request":"[switch_mode to 'architect' because:`),
+				),
+				"Did not receive expected response containing 'Roo wants to switch to architect mode'",
+			)
+			assert.ok(
+				globalThis.provider.messages.some(
+					({ type, text }) =>
+						type === "say" && (text?.includes("technical planning") || text?.includes("technical leader")),
+				),
+				"Did not receive expected response containing 'I am Roo in Architect mode, specializing in analyzing codebases'",
+			)
+
+			assert.ok(
+				globalThis.provider.messages.some(
+					({ type, text }) => type === "say" && text?.includes(`"request":"[switch_mode to 'ask' because:`),
+				),
+				"Did not receive expected response containing 'Roo wants to switch to ask mode'",
+			)
+			assert.ok(
+				globalThis.provider.messages.some(
+					({ type, text }) =>
+						type === "say" && (text?.includes("technical knowledge") || text?.includes("technical assist")),
+				),
+				"Did not receive expected response containing 'I am Roo in Ask mode, specializing in answering questions'",
+			)
+		} finally {
+		}
+	})
+})

+ 54 - 0
src/test/suite/task.test.ts

@@ -0,0 +1,54 @@
+import * as assert from "assert"
+import * as vscode from "vscode"
+
+suite("Roo Code Task", () => {
+	test("Should handle prompt and response correctly", async function () {
+		const timeout = 30000
+		const interval = 1000
+
+		if (!globalThis.extension) {
+			assert.fail("Extension not found")
+		}
+
+		try {
+			// Ensure the webview is launched.
+			let startTime = Date.now()
+
+			while (Date.now() - startTime < timeout) {
+				if (globalThis.provider.viewLaunched) {
+					break
+				}
+
+				await new Promise((resolve) => setTimeout(resolve, interval))
+			}
+
+			await globalThis.api.startNewTask("Hello world, what is your name? Respond with 'My name is ...'")
+
+			// Wait for task to appear in history with tokens.
+			startTime = Date.now()
+
+			while (Date.now() - startTime < timeout) {
+				const state = await globalThis.provider.getState()
+				const task = state.taskHistory?.[0]
+
+				if (task && task.tokensOut > 0) {
+					break
+				}
+
+				await new Promise((resolve) => setTimeout(resolve, interval))
+			}
+
+			if (globalThis.provider.messages.length === 0) {
+				assert.fail("No messages received")
+			}
+
+			assert.ok(
+				globalThis.provider.messages.some(
+					({ type, text }) => type === "say" && text?.includes("My name is Roo"),
+				),
+				"Did not receive expected response containing 'My name is Roo'",
+			)
+		} finally {
+		}
+	})
+})

+ 0 - 77
src/test/task.test.ts

@@ -1,77 +0,0 @@
-import * as assert from "assert"
-import * as vscode from "vscode"
-
-import { ClineAPI } from "../exports/cline"
-import { ClineProvider } from "../core/webview/ClineProvider"
-
-suite("Roo Code Task", () => {
-	test("Should handle prompt and response correctly", async function () {
-		const timeout = 30000
-		const interval = 1000
-
-		const extension = vscode.extensions.getExtension("RooVeterinaryInc.roo-cline")
-
-		if (!extension) {
-			assert.fail("Extension not found")
-		}
-
-		const api: ClineAPI = await extension.activate()
-		const provider = api.sidebarProvider as ClineProvider
-		await provider.updateGlobalState("apiProvider", "openrouter")
-		await provider.updateGlobalState("openRouterModelId", "anthropic/claude-3.5-sonnet")
-		await provider.storeSecret("openRouterApiKey", process.env.OPENROUTER_API_KEY || "sk-or-v1-fake-api-key")
-
-		// Create webview panel with development options.
-		const panel = vscode.window.createWebviewPanel("roo-cline.SidebarProvider", "Roo Code", vscode.ViewColumn.One, {
-			enableScripts: true,
-			enableCommandUris: true,
-			retainContextWhenHidden: true,
-			localResourceRoots: [extension.extensionUri],
-		})
-
-		try {
-			// Initialize provider with panel.
-			await provider.resolveWebviewView(panel)
-
-			// Wait for webview to launch.
-			let startTime = Date.now()
-
-			while (Date.now() - startTime < timeout) {
-				if (provider.viewLaunched) {
-					break
-				}
-
-				await new Promise((resolve) => setTimeout(resolve, interval))
-			}
-
-			await api.startNewTask("Hello world, what is your name? Respond with 'My name is ...'")
-
-			// Wait for task to appear in history with tokens.
-			startTime = Date.now()
-
-			while (Date.now() - startTime < timeout) {
-				const state = await provider.getState()
-				const task = state.taskHistory?.[0]
-
-				if (task && task.tokensOut > 0) {
-					break
-				}
-
-				await new Promise((resolve) => setTimeout(resolve, interval))
-			}
-
-			if (provider.messages.length === 0) {
-				assert.fail("No messages received")
-			}
-
-			// console.log("Provider messages:", JSON.stringify(provider.messages, null, 2))
-
-			assert.ok(
-				provider.messages.some(({ type, text }) => type === "say" && text?.includes("My name is Roo")),
-				"Did not receive expected response containing 'My name is Roo'",
-			)
-		} finally {
-			panel.dispose()
-		}
-	})
-})

+ 1 - 1
src/utils/__tests__/git.test.ts

@@ -107,7 +107,7 @@ describe("git utils", () => {
 				{ cwd },
 				expect.any(Function),
 			)
-		}, 20000)
+		})
 
 		it("should return empty array when git is not installed", async () => {
 			exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {