diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0587c47dcc..191977c516 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,12 @@ on: branches: - main +# Set default permissions for all jobs +permissions: + contents: read # Needed to check out code + checks: write # Needed to report test results + pull-requests: write # Needed to add comments/annotations to PRs + jobs: test: runs-on: ubuntu-latest @@ -51,6 +57,5 @@ jobs: - name: Prettier / Format Check run: npm run format - - name: Tests + - name: Extension Tests run: xvfb-run -a npm run test - if: runner.os == 'Linux' diff --git a/.gitignore b/.gitignore index a380392074..e6899656c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ out dist node_modules +tmp .vscode-test/ *.vsix diff --git a/.vscode-test.mjs b/.vscode-test.mjs index da0108114e..ce9992d0d1 100644 --- a/.vscode-test.mjs +++ b/.vscode-test.mjs @@ -1,8 +1,14 @@ import { defineConfig } from "@vscode/test-cli" +import path from "path" export default defineConfig({ - files: "out/**/*.test.js", + files: "out/test/**/*.test.js", mocha: { + ui: "bdd", timeout: 20000, // Maximum time (in ms) that a test can run before failing }, + workspaceFolder: "test-workspace", + version: "stable", + extensionDevelopmentPath: path.resolve("./"), + launchArgs: ["--disable-extensions"], }) diff --git a/package-lock.json b/package-lock.json index 96a127d497..4d042f8b28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,6 +60,7 @@ "@vscode/test-electron": "^2.4.0", "esbuild": "^0.21.5", "eslint": "^8.57.0", + "glob": "^10.3.10", "husky": "^9.1.7", "npm-run-all": "^4.1.5", "prettier": "^3.3.3", diff --git a/src/test/extension.test.ts b/src/test/extension.test.ts index 440a353ec6..063cf3bfc7 100644 --- a/src/test/extension.test.ts +++ b/src/test/extension.test.ts @@ -4,7 +4,7 @@ import path from "path" import "should" import * as vscode from "vscode" -const packagePath = path.join(__dirname, "..", "..", "..", "package.json") +const packagePath = path.join(__dirname, "..", "..", "package.json") describe("Cline Extension", () => { after(() => { @@ -23,4 +23,69 @@ describe("Cline Extension", () => { await new Promise((resolve) => setTimeout(resolve, 400)) await vscode.commands.executeCommand("cline.plusButtonClicked") }) + + // New test to verify xvfb and webview functionality + it("should create and display a webview panel", async () => { + // Create a webview panel + const panel = vscode.window.createWebviewPanel("testWebview", "CI/CD Test", vscode.ViewColumn.One, { + enableScripts: true, + }) + + // Set some HTML content + panel.webview.html = ` + + + + + xvfb Test + + +
Testing xvfb display server
+ + + ` + + // Verify panel exists + should.exist(panel) + panel.visible.should.be.true() + + // Clean up + panel.dispose() + }) + + // Test webview message passing + it("should handle webview messages", async () => { + const panel = vscode.window.createWebviewPanel("testWebview", "Message Test", vscode.ViewColumn.One, { + enableScripts: true, + }) + + // Set up message handling + const messagePromise = new Promise((resolve) => { + panel.webview.onDidReceiveMessage((message) => resolve(message.text), undefined) + }) + + // Add message sending script + panel.webview.html = ` + + + + + Message Test + + + + + + ` + + // Wait for message + const message = await messagePromise + message.should.equal("test-message") + + // Clean up + panel.dispose() + }) }) diff --git a/src/test/webview/chat-native.test.ts b/src/test/webview/chat-native.test.ts new file mode 100644 index 0000000000..775af4b7ae --- /dev/null +++ b/src/test/webview/chat-native.test.ts @@ -0,0 +1,116 @@ +import * as vscode from "vscode" +import { describe, it, beforeEach, afterEach } from "mocha" +import { strict as assert } from "assert" +import { join } from "path" +describe("Chat Integration Tests", () => { + let panel: vscode.WebviewPanel + let disposables: vscode.Disposable[] = [] + + beforeEach(async () => { + // Create VSCode webview panel + panel = vscode.window.createWebviewPanel("testWebview", "Chat Test", vscode.ViewColumn.One, { + enableScripts: true, + retainContextWhenHidden: true, + }) + + // Set up minimal test webview + panel.webview.html = ` + + + + + + + +
+ + + ` + }) + + afterEach(() => { + panel.dispose() + disposables.forEach((d) => d.dispose()) + disposables = [] + }) + + it("should send chat messages", async () => { + // Set up message listener + const messagePromise = new Promise((resolve) => { + panel.webview.onDidReceiveMessage((message) => { + if (message.type === "newTask") { + resolve(message) + } + }) + }) + + // Trigger send message + await panel.webview.postMessage({ + type: "sendMessage", + text: "Create a hello world app", + }) + + // Verify message was sent + const message = await messagePromise + assert.equal(message.type, "newTask") + assert.equal(message.text, "Create a hello world app") + }) + + it("should toggle between plan and act modes", async () => { + // Set up state change listener + const stateChangePromise = new Promise((resolve) => { + panel.webview.onDidReceiveMessage((message) => { + if (message.type === "chatSettings") { + resolve(message) + } + }) + }) + + // Trigger mode toggle + await panel.webview.postMessage({ type: "toggleMode" }) + + // Verify mode changed + const stateChange = await stateChangePromise + assert.equal(stateChange.chatSettings.mode, "act") + }) + + it("should handle tool approval flow", async () => { + // Set up approval listener + const approvalPromise = new Promise((resolve) => { + panel.webview.onDidReceiveMessage((message) => { + if (message.type === "askResponse") { + resolve(message) + } + }) + }) + + // Trigger tool approval + await panel.webview.postMessage({ + type: "invoke", + invoke: "primaryButtonClick", + }) + + // Verify approval was sent + const response = await approvalPromise + assert.equal(response.type, "askResponse") + assert.equal(response.askResponse, "yesButtonClicked") + }) +}) diff --git a/tsconfig.test.json b/tsconfig.test.json index 40ca1269d2..45dd541b8b 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -8,7 +8,9 @@ "compilerOptions": { "module": "commonjs", "moduleResolution": "node", - "types": ["node", "mocha", "should", "vscode"] + "types": ["node", "mocha", "should", "vscode"], + "outDir": "out", + "rootDir": "src" }, "include": ["src/**/*.test.ts"] } diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index ce0a4180ca..adbd59ae31 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -803,6 +803,7 @@ const ChatTextArea = forwardRef( }} /> { if (typeof ref === "function") { ref(el) @@ -909,6 +910,7 @@ const ChatTextArea = forwardRef( }} /> */}
{ if (!textAreaDisabled) { @@ -923,6 +925,7 @@ const ChatTextArea = forwardRef( ( ( - + Plan Act