Compare commits

...
Author SHA1 Message Date
jiclotus 98c44a2825 adding new configuration 2025-09-12 15:39:12 +02:00
jiclotus b99c077671 moving integration tests 2025-09-12 14:08:18 +02:00
jiclotus 2dac0ea1f8 qlty github action configuration 2025-09-12 12:02:44 +02:00
17 changed files with 295 additions and 17 deletions
+3 -1
View File
@@ -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
+5
View File
@@ -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
View File
@@ -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",
+35
View File
@@ -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
View File
@@ -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
View File
@@ -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",
@@ -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
@@ -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
@@ -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", () => {
+181
View File
@@ -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 }
+39
View File
@@ -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)
}
+2
View File
@@ -46,6 +46,8 @@ export default defineConfig({
coverage: {
provider: "v8",
reportOnFailure: true,
reporter: ["text", "lcov"],
reportsDirectory: "./coverage",
},
},
build: {