mirror of
https://github.com/cline/cline.git
synced 2026-09-21 05:10:09 +08:00
fix(vscode): stabilize SDK e2e login flow (#11441)
This commit is contained in:
committed by
Dominic Cooney
parent
072e237d01
commit
268462ddcf
@@ -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") {
|
||||
|
||||
@@ -462,6 +462,7 @@ export async function buildSessionConfig(input: SessionConfigInput): Promise<Cor
|
||||
let modelId: string | undefined
|
||||
let apiKey: string | undefined
|
||||
let baseUrl: string | undefined
|
||||
let apiConfig: ApiConfiguration | undefined
|
||||
// Cloud-provider structured options. The core runtime reads these from
|
||||
// CoreSessionConfig.providerConfig; without them the SDK gateway never receives
|
||||
// region/project/auth fields for inference calls.
|
||||
@@ -470,7 +471,7 @@ export async function buildSessionConfig(input: SessionConfigInput): Promise<Cor
|
||||
|
||||
try {
|
||||
const stateManager = StateManager.get()
|
||||
const apiConfig = stateManager.getApiConfiguration()
|
||||
apiConfig = stateManager.getApiConfiguration()
|
||||
|
||||
// Resolve the provider for the current mode
|
||||
const modeProvider = mode === "plan" ? apiConfig.planModeApiProvider : apiConfig.actModeApiProvider
|
||||
@@ -531,6 +532,9 @@ export async function buildSessionConfig(input: SessionConfigInput): Promise<Cor
|
||||
// session factory share one source of truth for default models.
|
||||
providerId = providerId ?? DEFAULT_PROVIDER_ID
|
||||
modelId = modelId ?? getDefaultModelIdForProvider(providerId) ?? getDefaultModelIdForProvider(DEFAULT_PROVIDER_ID) ?? ""
|
||||
if (!apiKey && apiConfig) {
|
||||
apiKey = resolveApiKey(providerId, apiConfig)
|
||||
}
|
||||
apiKey = apiKey ?? ""
|
||||
const reasoningConfig = resolveProviderReasoningConfig(providerId)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ e2e.describe("Diff Editor", () => {
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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(" ")
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user