mirror of
https://github.com/cline/cline.git
synced 2026-09-11 05:47:07 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98c44a2825 | ||
|
|
b99c077671 | ||
|
|
2dac0ea1f8 |
@@ -91,7 +91,9 @@ jobs:
|
||||
run: npm run pretest
|
||||
|
||||
- name: Unit Tests
|
||||
run: npm run test:unit
|
||||
run: |
|
||||
npm install --no-save nyc
|
||||
npx nyc --nycrc-path .nycrc.unit.json --reporter=lcov npm run test:unit
|
||||
|
||||
# Run extension tests with coverage
|
||||
- name: Extension Integration Tests with Coverage
|
||||
|
||||
@@ -20,6 +20,8 @@ webview-ui/src/**/*.js.map
|
||||
|
||||
# Ignore coverage directories and files
|
||||
coverage
|
||||
coverage-unit
|
||||
coverage-integration
|
||||
# But don't ignore the coverage scripts in .github/scripts/
|
||||
!.github/scripts/coverage/
|
||||
|
||||
@@ -35,3 +37,6 @@ test-results
|
||||
|
||||
## CLI pre-release ##
|
||||
/cli
|
||||
|
||||
.nyc_output/
|
||||
coverage-integration/
|
||||
|
||||
+6
-1
@@ -3,7 +3,12 @@
|
||||
"ts"
|
||||
],
|
||||
"spec": [
|
||||
"src/**/__tests__/*.ts"
|
||||
"src/**/__tests__/*.ts",
|
||||
"src/**/*.test.ts"
|
||||
],
|
||||
"ignore": [
|
||||
"src/test/**/*.test.ts",
|
||||
"src/test/e2e/**/*.test.ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register",
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"all": true,
|
||||
"check-coverage": false,
|
||||
"reporter": [
|
||||
"text",
|
||||
"lcov"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.js"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.d.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.ts",
|
||||
"**/*.spec.js",
|
||||
"**/test/**",
|
||||
"**/tests/**",
|
||||
"**/node_modules/**",
|
||||
"**/dist/**",
|
||||
"**/out/**",
|
||||
"**/coverage/**",
|
||||
"src/test/**",
|
||||
"src/generated/**"
|
||||
],
|
||||
"extension": [
|
||||
".ts",
|
||||
".js"
|
||||
],
|
||||
"cache": true,
|
||||
"sourceMap": true,
|
||||
"instrument": true,
|
||||
"report-dir": "./coverage-unit"
|
||||
}
|
||||
+11
-6
@@ -2,17 +2,22 @@ import { defineConfig } from "@vscode/test-cli"
|
||||
import path from "path"
|
||||
|
||||
export default defineConfig({
|
||||
files: "{out/**/*.test.js,src/**/*.test.js,!src/test/e2e/**/*.test.js,!out/src/test/e2e/**/*.test.js}",
|
||||
files: "src/test/*.test.ts",
|
||||
mocha: {
|
||||
ui: "bdd",
|
||||
timeout: 20000, // Maximum time (in ms) that a test can run before failing
|
||||
/** Set up alias path resolution during tests
|
||||
* @See {@link file://./test-setup.js}
|
||||
*/
|
||||
require: ["./test-setup.js"],
|
||||
timeout: 20000,
|
||||
require: ["ts-node/register/transpile-only", "./test-setup-integration.js"],
|
||||
},
|
||||
workspaceFolder: "test-workspace",
|
||||
version: "stable",
|
||||
extensionDevelopmentPath: path.resolve("./"),
|
||||
launchArgs: ["--disable-extensions"],
|
||||
env: {
|
||||
TS_NODE_PROJECT: "./tsconfig.test.json",
|
||||
},
|
||||
coverage: {
|
||||
reporter: ["lcov", "text"],
|
||||
include: ["src/**/*.ts"],
|
||||
exclude: ["**/*.d.ts", "**/*.test.ts", "**/*.spec.ts", "**/test/**", "**/tests/**", "src/test/**", "src/generated/**"],
|
||||
},
|
||||
})
|
||||
|
||||
+1
-2
@@ -356,8 +356,7 @@
|
||||
"test:ci": "node scripts/test-ci.js",
|
||||
"test:integration": "vscode-test",
|
||||
"test:unit": "TS_NODE_PROJECT='./tsconfig.unit-test.json' mocha",
|
||||
"test:coverage": "vscode-test --coverage",
|
||||
"test:sca-server": "npx tsx watch scripts/test-standalone-core-api-server.ts",
|
||||
"test:coverage": "vscode-test --coverage && mv coverage coverage-integration",
|
||||
"test:tp-orchestrator": "npx tsx scripts/testing-platform-orchestrator.ts",
|
||||
"e2e": "playwright test -c playwright.config.ts",
|
||||
"test:e2e": "playwright install && vsce package --allow-package-secrets sendgrid --out dist/e2e.vsix && node src/test/e2e/utils/build.mjs && playwright test",
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@ import { expect } from "chai"
|
||||
import { afterEach, beforeEach, describe, it } from "mocha"
|
||||
import * as sinon from "sinon"
|
||||
import * as vscode from "vscode"
|
||||
import type { TaskMetadata } from "./ContextTrackerTypes"
|
||||
import { ModelContextTracker } from "./ModelContextTracker"
|
||||
import type { TaskMetadata } from "../core/context/context-tracking/ContextTrackerTypes"
|
||||
import { ModelContextTracker } from "../core/context/context-tracking/ModelContextTracker"
|
||||
|
||||
describe("ModelContextTracker", () => {
|
||||
let sandbox: sinon.SinonSandbox
|
||||
+2
-2
@@ -3,8 +3,8 @@ import { setVscodeHostProviderMock } from "@/test/host-provider-test-utils"
|
||||
import "should"
|
||||
import * as sinon from "sinon"
|
||||
import * as vscode from "vscode"
|
||||
import { TerminalProcess } from "./TerminalProcess"
|
||||
import { TerminalRegistry } from "./TerminalRegistry"
|
||||
import { TerminalProcess } from "../integrations/terminal/TerminalProcess"
|
||||
import { TerminalRegistry } from "../integrations/terminal/TerminalRegistry"
|
||||
|
||||
declare module "vscode" {
|
||||
// https://github.com/microsoft/vscode/blob/f0417069c62e20f3667506f4b7e53ca0004b4e3e/src/vscode-dts/vscode.d.ts#L7442
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { expect } from "chai"
|
||||
import { describe, it } from "mocha"
|
||||
import * as vscode from "vscode"
|
||||
import { DiagnosticSeverity } from "@/shared/proto/index.cline"
|
||||
import { convertToFileDiagnostics, convertVscodeDiagnostics } from "./getDiagnostics"
|
||||
import { convertToFileDiagnostics, convertVscodeDiagnostics } from "../hosts/vscode/hostbridge/workspace/getDiagnostics"
|
||||
|
||||
describe("getDiagnostics conversion functions", () => {
|
||||
describe("convertToFileDiagnostics", () => {
|
||||
@@ -1,10 +1,15 @@
|
||||
// This file contains `declare module "vscode"` so we must import it.
|
||||
import "../providers/vscode-lm"
|
||||
import "../core/api/providers/vscode-lm"
|
||||
import { describe, it } from "mocha"
|
||||
import "should"
|
||||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import * as vscode from "vscode"
|
||||
import { asObjectSafe, convertToAnthropicMessage, convertToAnthropicRole, convertToVsCodeLmMessages } from "./vscode-lm-format"
|
||||
import {
|
||||
asObjectSafe,
|
||||
convertToAnthropicMessage,
|
||||
convertToAnthropicRole,
|
||||
convertToVsCodeLmMessages,
|
||||
} from "../core/api/transform/vscode-lm-format"
|
||||
|
||||
describe("asObjectSafe", () => {
|
||||
it("should handle falsy values", () => {
|
||||
@@ -8,6 +8,10 @@ export const env = {
|
||||
dispose: () => {},
|
||||
}
|
||||
},
|
||||
clipboard: {
|
||||
readText: () => Promise.resolve("mock clipboard content"),
|
||||
writeText: (_text: string) => Promise.resolve(),
|
||||
},
|
||||
}
|
||||
|
||||
export const workspace = {
|
||||
@@ -25,6 +29,15 @@ export const workspace = {
|
||||
},
|
||||
}
|
||||
},
|
||||
workspaceFolders: [],
|
||||
textDocuments: [],
|
||||
openTextDocument: (_uri: any) =>
|
||||
Promise.resolve({
|
||||
uri: _uri,
|
||||
fileName: _uri.fsPath || _uri,
|
||||
isDirty: false,
|
||||
save: () => Promise.resolve(true),
|
||||
}),
|
||||
}
|
||||
|
||||
// Export other commonly used VSCode API mocks as needed
|
||||
@@ -32,6 +45,49 @@ export const window = {
|
||||
showErrorMessage: (_message: string) => Promise.resolve(),
|
||||
showWarningMessage: (_message: string) => Promise.resolve(),
|
||||
showInformationMessage: (_message: string) => Promise.resolve(),
|
||||
createTextEditorDecorationType: (_options: any) => ({
|
||||
key: "mock-decoration-type",
|
||||
dispose: () => {},
|
||||
}),
|
||||
createTerminal: (_options?: any) => ({
|
||||
name: _options?.name || "mock-terminal",
|
||||
processId: Promise.resolve(1234),
|
||||
creationOptions: _options || {},
|
||||
exitStatus: undefined,
|
||||
state: { isInteractedWith: false },
|
||||
shellIntegration: {
|
||||
executeCommand: (_command: string) => ({
|
||||
read: () => ({
|
||||
next: () => Promise.resolve({ value: "mock output", done: false }),
|
||||
[Symbol.asyncIterator]: function () {
|
||||
return this
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
sendText: (_text: string) => {},
|
||||
show: () => {},
|
||||
hide: () => {},
|
||||
dispose: () => {},
|
||||
}),
|
||||
tabGroups: {
|
||||
all: [],
|
||||
},
|
||||
visibleTextEditors: [],
|
||||
showTextDocument: (_document: any, _column?: any) =>
|
||||
Promise.resolve({
|
||||
document: _document,
|
||||
viewColumn: _column || ViewColumn.One,
|
||||
edit: (_callback: (editBuilder: any) => void) => {
|
||||
const editBuilder = {
|
||||
insert: (_position: Position, _text: string) => {},
|
||||
replace: (_location: Range | Position, _value: string) => {},
|
||||
delete: (_location: Range) => {},
|
||||
}
|
||||
_callback(editBuilder)
|
||||
return Promise.resolve(true)
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
export const commands = {
|
||||
@@ -43,6 +99,131 @@ export const Uri = {
|
||||
parse: (uri: string) => ({ fsPath: uri, toString: () => uri }),
|
||||
}
|
||||
|
||||
export class Range {
|
||||
start: Position
|
||||
end: Position
|
||||
|
||||
constructor(startLine: number | Position, startCharacter: number | Position, endLine?: number, endCharacter?: number) {
|
||||
if (typeof startLine === "number" && typeof startCharacter === "number") {
|
||||
this.start = new Position(startLine, startCharacter)
|
||||
this.end = new Position(endLine || startLine, endCharacter || startCharacter)
|
||||
} else {
|
||||
this.start = startLine as Position
|
||||
this.end = startCharacter as Position
|
||||
}
|
||||
}
|
||||
|
||||
with(start?: Position, end?: Position): Range {
|
||||
return new Range(start || this.start, end || this.end)
|
||||
}
|
||||
}
|
||||
|
||||
export class Position {
|
||||
line: number
|
||||
character: number
|
||||
|
||||
constructor(line: number, character: number) {
|
||||
this.line = line
|
||||
this.character = character
|
||||
}
|
||||
|
||||
translate(lineDelta: number = 0, characterDelta: number = 0): Position {
|
||||
return new Position(this.line + lineDelta, this.character + characterDelta)
|
||||
}
|
||||
}
|
||||
|
||||
// VSCode Language Model API mocks
|
||||
export class LanguageModelTextPart {
|
||||
value: string
|
||||
constructor(value: string) {
|
||||
this.value = value
|
||||
}
|
||||
}
|
||||
|
||||
export class LanguageModelToolCallPart {
|
||||
name: string
|
||||
toolCallId: string
|
||||
input: any
|
||||
constructor(name: string, toolCallId: string, input: any) {
|
||||
this.name = name
|
||||
this.toolCallId = toolCallId
|
||||
this.input = input
|
||||
}
|
||||
}
|
||||
|
||||
export class LanguageModelToolResultPart {
|
||||
toolCallId: string
|
||||
content: any
|
||||
constructor(toolCallId: string, content: any) {
|
||||
this.toolCallId = toolCallId
|
||||
this.content = content
|
||||
}
|
||||
}
|
||||
|
||||
export const LanguageModelChatMessageRole = {
|
||||
User: 1,
|
||||
Assistant: 2,
|
||||
}
|
||||
|
||||
// Diagnostic severity levels
|
||||
export const DiagnosticSeverity = {
|
||||
Error: 0,
|
||||
Warning: 1,
|
||||
Information: 2,
|
||||
Hint: 3,
|
||||
}
|
||||
|
||||
// Theme icon mock
|
||||
export class ThemeIcon {
|
||||
id: string
|
||||
constructor(id: string) {
|
||||
this.id = id
|
||||
}
|
||||
}
|
||||
|
||||
// View column enum
|
||||
export const ViewColumn = {
|
||||
One: 1,
|
||||
Two: 2,
|
||||
Three: 3,
|
||||
Active: -1,
|
||||
Beside: -2,
|
||||
}
|
||||
|
||||
// Workspace edit and diagnostic classes
|
||||
export class WorkspaceEdit {
|
||||
constructor() {}
|
||||
|
||||
insert(uri: any, position: Position, value: string) {
|
||||
// Mock implementation
|
||||
}
|
||||
}
|
||||
|
||||
export class Diagnostic {
|
||||
range: Range
|
||||
message: string
|
||||
severity: number
|
||||
source?: string
|
||||
|
||||
constructor(range: Range, message: string, severity: number = DiagnosticSeverity.Error) {
|
||||
this.range = range
|
||||
this.message = message
|
||||
this.severity = severity
|
||||
}
|
||||
}
|
||||
|
||||
// Clipboard mock
|
||||
export const env_clipboard = {
|
||||
readText: () => Promise.resolve("mock clipboard content"),
|
||||
writeText: (_text: string) => Promise.resolve(),
|
||||
}
|
||||
|
||||
// Add clipboard to env
|
||||
export const env_extended = {
|
||||
...env,
|
||||
clipboard: env_clipboard,
|
||||
}
|
||||
|
||||
export const ExtensionContextMock = {}
|
||||
export const StatusBarAlignmentMock = { Left: 1, Right: 2 }
|
||||
export const ViewColumnMock = { One: 1, Two: 2, Three: 3 }
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
const tsConfigPaths = require("tsconfig-paths")
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const Module = require("module")
|
||||
|
||||
const baseUrl = path.resolve(__dirname)
|
||||
|
||||
// Load the main tsconfig.json for path mappings
|
||||
const tsConfig = JSON.parse(fs.readFileSync(path.join(baseUrl, "tsconfig.json"), "utf-8"))
|
||||
|
||||
// Register path mappings for TypeScript source files (not compiled)
|
||||
// This allows @utils/shell, @core/*, etc. to resolve correctly for integration tests
|
||||
tsConfigPaths.register({
|
||||
baseUrl: baseUrl,
|
||||
paths: tsConfig.compilerOptions.paths,
|
||||
})
|
||||
|
||||
// Mock the @google/genai module to avoid ESM compatibility issues in tests
|
||||
// The module is ES6 only, but the integration tests need CommonJS compatibility.
|
||||
const originalRequire = Module.prototype.require
|
||||
Module.prototype.require = function (id) {
|
||||
// Intercept requires for @google/genai
|
||||
if (id === "@google/genai") {
|
||||
// Return a mock instead of the actual module
|
||||
return {
|
||||
GoogleGenerativeAI: class MockGoogleGenerativeAI {
|
||||
constructor() {}
|
||||
getGenerativeModel() {
|
||||
return {
|
||||
generateContentStream: async function* () {
|
||||
yield { text: () => "mock response" }
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
return originalRequire.call(this, id)
|
||||
}
|
||||
@@ -46,6 +46,8 @@ export default defineConfig({
|
||||
coverage: {
|
||||
provider: "v8",
|
||||
reportOnFailure: true,
|
||||
reporter: ["text", "lcov"],
|
||||
reportsDirectory: "./coverage",
|
||||
},
|
||||
},
|
||||
build: {
|
||||
|
||||
Reference in New Issue
Block a user