diff --git a/apps/vscode/src/sdk/cline-session-factory.test.ts b/apps/vscode/src/sdk/cline-session-factory.test.ts index 0f5845a0b6..be7ceb0022 100644 --- a/apps/vscode/src/sdk/cline-session-factory.test.ts +++ b/apps/vscode/src/sdk/cline-session-factory.test.ts @@ -272,6 +272,22 @@ describe("normalizeProviderReasoningSettings", () => { // --------------------------------------------------------------------------- describe("buildSessionConfig", () => { + it("resolves Cline OAuth credentials after defaulting to the Cline provider", async () => { + mocks.stateManager.getApiConfiguration.mockReturnValue({} as any) + mocks.providerSettingsManager.getProviderSettings.mockReturnValue({ + provider: "cline", + auth: { + accessToken: "workos:test-access-token", + refreshToken: "test-refresh-token", + }, + } as any) + + const config = await buildSessionConfig({ cwd: "/tmp/workspace" }) + + expect(config.providerId).toBe("cline") + expect(config.apiKey).toBe("workos:test-access-token") + }) + it("enables basic SDK compaction when global useAutoCondense is true", async () => { mocks.stateManager.getGlobalSettingsKey.mockImplementation((key: string) => { if (key === "useAutoCondense") { diff --git a/apps/vscode/src/sdk/cline-session-factory.ts b/apps/vscode/src/sdk/cline-session-factory.ts index d3ffaece8a..df2c991d30 100644 --- a/apps/vscode/src/sdk/cline-session-factory.ts +++ b/apps/vscode/src/sdk/cline-session-factory.ts @@ -462,6 +462,7 @@ export async function buildSessionConfig(input: SessionConfigInput): Promise { E2E_WORKSPACE_TYPES.forEach(({ title, workspaceType }) => { e2e.extend({ workspaceType, - })(title, async ({ helper, page, sidebar }) => { + })(title, async ({ helper, sidebar }) => { await helper.signin(sidebar) const inputbox = sidebar.getByTestId("chat-input") @@ -37,15 +37,10 @@ e2e.describe("Diff Editor", () => { // Wait for the sidebar to load the file edit request await sidebar.waitForSelector('span:has-text("Cline wants to edit this file:")') - // Cline Diff Editor should open with the file name and diff - await expect(page.getByText("test.ts: Original ↔ Cline's")).toBeVisible() - - // Diff editor should show the original and modified content - const diffEditor = page.locator( - ".monaco-editor.modified-in-monaco-diff-editor > .overflow-guard > .monaco-scrollable-element.editor-scrollable > .lines-content > div:nth-child(4)", - ) - await diffEditor.click() - await expect(diffEditor).toBeVisible() + // The SDK-backed path renders a pending edit approval before the user saves it. + await expect(sidebar.getByText(/\/test\.ts/)).toBeVisible() + await expect(sidebar.getByRole("button", { name: "Save" })).toBeVisible() + await expect(sidebar.getByRole("button", { name: "Reject" })).toBeVisible() }) }) }) diff --git a/apps/vscode/src/test/e2e/fixtures/server/index.ts b/apps/vscode/src/test/e2e/fixtures/server/index.ts index 6c5aa95dd4..86537cf9de 100644 --- a/apps/vscode/src/test/e2e/fixtures/server/index.ts +++ b/apps/vscode/src/test/e2e/fixtures/server/index.ts @@ -474,10 +474,15 @@ export class ClineApiServerMock { const parsed = JSON.parse(body) const { _messages, model = "claude-3-5-sonnet-20241022", stream = true } = parsed let responseText = E2E_MOCK_API_RESPONSES.DEFAULT + const isEditRequest = body.includes("edit_request") + log("Chat completion mock selection:", { + isEditRequest, + isReplaceResult: body.includes("[replace_in_file for 'test.ts'] Result:"), + }) if (body.includes("[replace_in_file for 'test.ts'] Result:")) { responseText = E2E_MOCK_API_RESPONSES.REPLACE_REQUEST } - if (body.includes("edit_request")) { + if (isEditRequest) { responseText = E2E_MOCK_API_RESPONSES.EDIT_REQUEST } if (body.includes("[diff.test.ts] Hello, Cline!")) { @@ -498,6 +503,63 @@ export class ClineApiServerMock { const randomUUID = uuidv4() + if (isEditRequest) { + const toolCallId = `call_${randomUUID}` + const toolCallChunk = { + id: generationId, + object: "chat.completion.chunk", + created: Math.floor(Date.now() / 1000), + model, + choices: [ + { + index: 0, + delta: { + tool_calls: [ + { + index: 0, + id: toolCallId, + type: "function", + function: { + name: "editor", + arguments: JSON.stringify({ + path: "test.ts", + old_text: 'export const name = "john"', + new_text: 'export const name = "cline"', + }), + }, + }, + ], + }, + finish_reason: null, + }, + ], + } + const finalChunk = { + id: generationId, + object: "chat.completion.chunk", + created: Math.floor(Date.now() / 1000), + model, + choices: [ + { + index: 0, + delta: {}, + finish_reason: "tool_calls", + }, + ], + usage: { + prompt_tokens: 140, + completion_tokens: responseText.length, + total_tokens: 140 + responseText.length, + cost: (140 + responseText.length) * 0.00015, + }, + } + res.write(`data: ${JSON.stringify(toolCallChunk)}\n\n`) + res.write(`data: ${JSON.stringify(finalChunk)}\n\n`) + res.write("data: [DONE]\n\n") + res.end() + return + } + responseText += `\n\nGenerated UUID: ${randomUUID}` const chunks = responseText.split(" ") diff --git a/apps/vscode/src/test/e2e/utils/helpers.ts b/apps/vscode/src/test/e2e/utils/helpers.ts index e3fc2f86ac..0fad7acd09 100644 --- a/apps/vscode/src/test/e2e/utils/helpers.ts +++ b/apps/vscode/src/test/e2e/utils/helpers.ts @@ -378,6 +378,7 @@ export const e2e = test await use(async (workspacePath: string) => { // Create isolated Cline data directory for this test const clineTestDir = mkdtempSync(path.join(os.tmpdir(), "cline-e2e-")) + const clineDataDir = path.join(clineTestDir, "data") const app = await _electron.launch({ executablePath, @@ -387,6 +388,7 @@ export const e2e = test E2E_TEST: "true", CLINE_ENVIRONMENT: "local", CLINE_DIR: clineTestDir, // Isolate test data from user's ~/.cline + CLINE_DATA_DIR: clineDataDir, // Keep SDK/shared storage off the user's real Cline data dir GRPC_RECORDER_FILE_NAME: E2ETestHelper.generateTestFileName(testInfo.title, testInfo.project.name), // GRPC_RECORDER_ENABLED: "true", // GRPC_RECORDER_TESTS_FILTERS_ENABLED: "true"