extension.test.ts 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. const assert = require("assert")
  2. const vscode = require("vscode")
  3. const path = require("path")
  4. const fs = require("fs")
  5. const dotenv = require("dotenv")
  6. // Load test environment variables
  7. const testEnvPath = path.join(__dirname, ".test_env")
  8. dotenv.config({ path: testEnvPath })
  9. suite("Roo Code Extension Test Suite", () => {
  10. vscode.window.showInformationMessage("Starting Roo Code extension tests.")
  11. test("Extension should be present", () => {
  12. const extension = vscode.extensions.getExtension("RooVeterinaryInc.roo-cline")
  13. assert.notStrictEqual(extension, undefined)
  14. })
  15. test("Extension should activate", async () => {
  16. const extension = vscode.extensions.getExtension("RooVeterinaryInc.roo-cline")
  17. if (!extension) {
  18. assert.fail("Extension not found")
  19. }
  20. await extension.activate()
  21. assert.strictEqual(extension.isActive, true)
  22. })
  23. test("OpenRouter API key and models should be configured correctly", function (done) {
  24. // @ts-ignore
  25. this.timeout(60000) // Increase timeout to 60s for network requests
  26. ;(async () => {
  27. try {
  28. // Get extension instance
  29. const extension = vscode.extensions.getExtension("RooVeterinaryInc.roo-cline")
  30. if (!extension) {
  31. done(new Error("Extension not found"))
  32. return
  33. }
  34. // Verify API key is set and valid
  35. const apiKey = process.env.OPEN_ROUTER_API_KEY
  36. if (!apiKey) {
  37. done(new Error("OPEN_ROUTER_API_KEY environment variable is not set"))
  38. return
  39. }
  40. if (!apiKey.startsWith("sk-or-v1-")) {
  41. done(new Error("OpenRouter API key should have correct format"))
  42. return
  43. }
  44. // Activate extension and get provider
  45. const api = await extension.activate()
  46. if (!api) {
  47. done(new Error("Extension API not found"))
  48. return
  49. }
  50. // Get the provider from the extension's exports
  51. const provider = api.sidebarProvider
  52. if (!provider) {
  53. done(new Error("Provider not found"))
  54. return
  55. }
  56. // Set up the API configuration
  57. await provider.updateGlobalState("apiProvider", "openrouter")
  58. await provider.storeSecret("openRouterApiKey", apiKey)
  59. // Set up timeout to fail test if models don't load
  60. const timeout = setTimeout(() => {
  61. done(new Error("Timeout waiting for models to load"))
  62. }, 30000)
  63. // Wait for models to be loaded
  64. const checkModels = setInterval(async () => {
  65. try {
  66. const models = await provider.readOpenRouterModels()
  67. if (!models) {
  68. return
  69. }
  70. clearInterval(checkModels)
  71. clearTimeout(timeout)
  72. // Verify expected Claude models are available
  73. const expectedModels = [
  74. "anthropic/claude-3.5-sonnet:beta",
  75. "anthropic/claude-3-sonnet:beta",
  76. "anthropic/claude-3.5-sonnet",
  77. "anthropic/claude-3.5-sonnet-20240620",
  78. "anthropic/claude-3.5-sonnet-20240620:beta",
  79. "anthropic/claude-3.5-haiku:beta",
  80. ]
  81. for (const modelId of expectedModels) {
  82. assert.strictEqual(modelId in models, true, `Model ${modelId} should be available`)
  83. }
  84. done()
  85. } catch (error) {
  86. clearInterval(checkModels)
  87. clearTimeout(timeout)
  88. done(error)
  89. }
  90. }, 1000)
  91. // Trigger model loading
  92. await provider.refreshOpenRouterModels()
  93. } catch (error) {
  94. done(error)
  95. }
  96. })()
  97. })
  98. test("Commands should be registered", async () => {
  99. const commands = await vscode.commands.getCommands(true)
  100. // Test core commands are registered
  101. const expectedCommands = [
  102. "roo-cline.plusButtonClicked",
  103. "roo-cline.mcpButtonClicked",
  104. "roo-cline.historyButtonClicked",
  105. "roo-cline.popoutButtonClicked",
  106. "roo-cline.settingsButtonClicked",
  107. "roo-cline.openInNewTab",
  108. "roo-cline.explainCode",
  109. "roo-cline.fixCode",
  110. "roo-cline.improveCode",
  111. ]
  112. for (const cmd of expectedCommands) {
  113. assert.strictEqual(commands.includes(cmd), true, `Command ${cmd} should be registered`)
  114. }
  115. })
  116. test("Views should be registered", () => {
  117. const view = vscode.window.createWebviewPanel(
  118. "roo-cline.SidebarProvider",
  119. "Roo Code",
  120. vscode.ViewColumn.One,
  121. {},
  122. )
  123. assert.notStrictEqual(view, undefined)
  124. view.dispose()
  125. })
  126. test("Should handle prompt and response correctly", async function () {
  127. // @ts-ignore
  128. this.timeout(60000) // Increase timeout for API request
  129. const timeout = 30000
  130. const interval = 1000
  131. // Get extension instance
  132. const extension = vscode.extensions.getExtension("RooVeterinaryInc.roo-cline")
  133. if (!extension) {
  134. assert.fail("Extension not found")
  135. return
  136. }
  137. // Activate extension and get API
  138. const api = await extension.activate()
  139. if (!api) {
  140. assert.fail("Extension API not found")
  141. return
  142. }
  143. // Get provider
  144. const provider = api.sidebarProvider
  145. if (!provider) {
  146. assert.fail("Provider not found")
  147. return
  148. }
  149. // Set up API configuration
  150. await provider.updateGlobalState("apiProvider", "openrouter")
  151. await provider.updateGlobalState("openRouterModelId", "anthropic/claude-3.5-sonnet")
  152. const apiKey = process.env.OPEN_ROUTER_API_KEY
  153. if (!apiKey) {
  154. assert.fail("OPEN_ROUTER_API_KEY environment variable is not set")
  155. return
  156. }
  157. await provider.storeSecret("openRouterApiKey", apiKey)
  158. // Create webview panel with development options
  159. const extensionUri = extension.extensionUri
  160. const panel = vscode.window.createWebviewPanel("roo-cline.SidebarProvider", "Roo Code", vscode.ViewColumn.One, {
  161. enableScripts: true,
  162. enableCommandUris: true,
  163. retainContextWhenHidden: true,
  164. localResourceRoots: [extensionUri],
  165. })
  166. try {
  167. // Initialize webview with development context
  168. panel.webview.options = {
  169. enableScripts: true,
  170. enableCommandUris: true,
  171. localResourceRoots: [extensionUri],
  172. }
  173. // Initialize provider with panel
  174. provider.resolveWebviewView(panel)
  175. // Set up message tracking
  176. let webviewReady = false
  177. let messagesReceived = false
  178. const originalPostMessage = provider.postMessageToWebview.bind(provider)
  179. // @ts-ignore
  180. provider.postMessageToWebview = async (message) => {
  181. if (message.type === "state") {
  182. webviewReady = true
  183. console.log("Webview state received:", message)
  184. if (message.state?.clineMessages?.length > 0) {
  185. messagesReceived = true
  186. console.log("Messages in state:", message.state.clineMessages)
  187. }
  188. }
  189. await originalPostMessage(message)
  190. }
  191. // Wait for webview to launch and receive initial state
  192. let startTime = Date.now()
  193. while (Date.now() - startTime < timeout) {
  194. if (webviewReady) {
  195. // Wait an additional second for webview to fully initialize
  196. await new Promise((resolve) => setTimeout(resolve, 1000))
  197. break
  198. }
  199. await new Promise((resolve) => setTimeout(resolve, interval))
  200. }
  201. if (!webviewReady) {
  202. throw new Error("Timeout waiting for webview to be ready")
  203. }
  204. // Send webviewDidLaunch to initialize chat
  205. await provider.postMessageToWebview({ type: "webviewDidLaunch" })
  206. console.log("Sent webviewDidLaunch")
  207. // Wait for webview to fully initialize
  208. await new Promise((resolve) => setTimeout(resolve, 2000))
  209. // Restore original postMessage
  210. provider.postMessageToWebview = originalPostMessage
  211. // Wait for OpenRouter models to be fully loaded
  212. startTime = Date.now()
  213. while (Date.now() - startTime < timeout) {
  214. const models = await provider.readOpenRouterModels()
  215. if (models && Object.keys(models).length > 0) {
  216. console.log("OpenRouter models loaded")
  217. break
  218. }
  219. await new Promise((resolve) => setTimeout(resolve, interval))
  220. }
  221. // Send prompt
  222. const prompt = "Hello world, what is your name?"
  223. console.log("Sending prompt:", prompt)
  224. // Start task
  225. try {
  226. await api.startNewTask(prompt)
  227. console.log("Task started")
  228. } catch (error) {
  229. console.error("Error starting task:", error)
  230. throw error
  231. }
  232. // Wait for task to appear in history with tokens
  233. startTime = Date.now()
  234. while (Date.now() - startTime < timeout) {
  235. const state = await provider.getState()
  236. const task = state.taskHistory?.[0]
  237. if (task && task.tokensOut > 0) {
  238. console.log("Task completed with tokens:", task)
  239. break
  240. }
  241. await new Promise((resolve) => setTimeout(resolve, interval))
  242. }
  243. // Wait for messages to be processed
  244. startTime = Date.now()
  245. let responseReceived = false
  246. while (Date.now() - startTime < timeout) {
  247. // Check provider.clineMessages
  248. const messages = provider.clineMessages
  249. if (messages && messages.length > 0) {
  250. console.log("Provider messages:", JSON.stringify(messages, null, 2))
  251. // @ts-ignore
  252. const hasResponse = messages.some(
  253. (m: { type: string; text: string }) =>
  254. m.type === "say" && m.text && m.text.toLowerCase().includes("cline"),
  255. )
  256. if (hasResponse) {
  257. console.log('Found response containing "Cline" in provider messages')
  258. responseReceived = true
  259. break
  260. }
  261. }
  262. // Check provider.cline.clineMessages
  263. const clineMessages = provider.cline?.clineMessages
  264. if (clineMessages && clineMessages.length > 0) {
  265. console.log("Cline messages:", JSON.stringify(clineMessages, null, 2))
  266. // @ts-ignore
  267. const hasResponse = clineMessages.some(
  268. (m: { type: string; text: string }) =>
  269. m.type === "say" && m.text && m.text.toLowerCase().includes("cline"),
  270. )
  271. if (hasResponse) {
  272. console.log('Found response containing "Cline" in cline messages')
  273. responseReceived = true
  274. break
  275. }
  276. }
  277. await new Promise((resolve) => setTimeout(resolve, interval))
  278. }
  279. if (!responseReceived) {
  280. console.log("Final provider state:", await provider.getState())
  281. console.log("Final cline messages:", provider.cline?.clineMessages)
  282. throw new Error('Did not receive expected response containing "Cline"')
  283. }
  284. } finally {
  285. panel.dispose()
  286. }
  287. })
  288. })