279 lines
8.2 KiB
JavaScript
279 lines
8.2 KiB
JavaScript
const tsConfigPaths = require("tsconfig-paths")
|
|
const fs = require("fs")
|
|
const path = require("path")
|
|
const Module = require("module")
|
|
|
|
const baseUrl = path.resolve(__dirname)
|
|
|
|
const tsConfig = JSON.parse(fs.readFileSync(path.join(baseUrl, "tsconfig.json"), "utf-8"))
|
|
|
|
/**
|
|
* The aliases point towards the `src` directory.
|
|
* However, `tsc` doesn't compile paths by itself
|
|
* (https://www.typescriptlang.org/docs/handbook/modules/reference.html#paths-does-not-affect-emit)
|
|
* So we need to use tsconfig-paths to resolve the aliases when running tests,
|
|
* but pointing to `out` instead.
|
|
*/
|
|
const outPaths = {}
|
|
Object.keys(tsConfig.compilerOptions.paths).forEach((key) => {
|
|
const value = tsConfig.compilerOptions.paths[key]
|
|
outPaths[key] = value.map((path) => path.replace("src", "out/src"))
|
|
})
|
|
|
|
tsConfigPaths.register({
|
|
baseUrl: baseUrl,
|
|
paths: outPaths,
|
|
})
|
|
|
|
// Mock the @google/genai module to avoid ESM compatibility issues in tests
|
|
// The module is ES6 only, but the integration tests are compiled to commonJS.
|
|
const originalRequire = Module.prototype.require
|
|
Module.prototype.require = function (id) {
|
|
// Intercept requires for @google/genai
|
|
if (id === "@google/genai") {
|
|
// Return the mock instead
|
|
const mockPath = path.join(baseUrl, "out/src/test/fixtures/google-genai-mock.js")
|
|
return originalRequire.call(this, mockPath)
|
|
}
|
|
|
|
// The SDK packages are ESM-only and expose only an `import` condition.
|
|
// Integration tests run the tsc-built `out/` tree as CommonJS in VS Code's
|
|
// extension host, so `require("@cline/core")` fails before tests start.
|
|
// Mock the small surface needed by legacy VS Code integration tests.
|
|
if (id === "@cline/core") {
|
|
const createNoopTelemetry = () => ({
|
|
setDistinctId() {},
|
|
setMetadata() {},
|
|
updateMetadata() {},
|
|
setCommonProperties() {},
|
|
updateCommonProperties() {},
|
|
isEnabled: () => false,
|
|
capture() {},
|
|
captureRequired() {},
|
|
recordCounter() {},
|
|
recordHistogram() {},
|
|
recordGauge() {},
|
|
flush: async () => {},
|
|
dispose: async () => {},
|
|
})
|
|
|
|
class ProviderSettingsManager {
|
|
constructor(_options) {
|
|
this.state = { providers: {}, lastUsedProvider: undefined }
|
|
}
|
|
|
|
read() {
|
|
return this.state
|
|
}
|
|
|
|
getLastUsedProviderSettings() {
|
|
return undefined
|
|
}
|
|
|
|
getProviderSettings(_provider) {
|
|
return undefined
|
|
}
|
|
|
|
saveProviderSettings(_settings, _options) {}
|
|
}
|
|
|
|
const getProviderAuthStorageId = (providerId) => {
|
|
const normalized = String(providerId || "")
|
|
.trim()
|
|
.toLowerCase()
|
|
if (normalized === "cline" || normalized === "cline-pass") return "cline"
|
|
if (normalized === "oca" || normalized === "openai-codex") return normalized
|
|
return undefined
|
|
}
|
|
const formatClineApiKey = (token) => {
|
|
const trimmed = String(token || "").trim()
|
|
return trimmed.toLowerCase().startsWith("workos:") ? trimmed : `workos:${trimmed}`
|
|
}
|
|
const getProviderAuthHandler = (providerId) => {
|
|
const storageProviderId = getProviderAuthStorageId(providerId)
|
|
if (!storageProviderId) return undefined
|
|
return {
|
|
providerId,
|
|
storageProviderId,
|
|
getApiKey(settings) {
|
|
const accessToken = settings?.auth?.accessToken?.trim?.()
|
|
if (accessToken) return storageProviderId === "cline" ? formatClineApiKey(accessToken) : accessToken
|
|
return settings?.apiKey?.trim?.() || settings?.auth?.apiKey?.trim?.() || undefined
|
|
},
|
|
}
|
|
}
|
|
const resolveProviderApiKeyFromSettings = (manager, providerId) => {
|
|
const handler = getProviderAuthHandler(providerId)
|
|
const storageProviderId = handler?.storageProviderId ?? providerId
|
|
const settings = manager.getProviderSettings(storageProviderId)
|
|
return handler?.getApiKey(settings) ?? settings?.apiKey?.trim?.()
|
|
}
|
|
const listLocalProviders = async (manager) => ({ providers: [], settingsPath: manager.getFilePath?.() ?? "" })
|
|
|
|
return {
|
|
createClineTelemetryServiceConfig: (config = {}) => ({
|
|
enabled: false,
|
|
metadata: {
|
|
extension_version: "test",
|
|
cline_type: "test",
|
|
platform: "test",
|
|
platform_version: "test",
|
|
os_type: "test",
|
|
os_version: "test",
|
|
},
|
|
...config,
|
|
}),
|
|
createConfiguredTelemetryHandle: () => ({
|
|
telemetry: createNoopTelemetry(),
|
|
flush: async () => {},
|
|
dispose: async () => {},
|
|
}),
|
|
ClineCore: class {
|
|
constructor() {
|
|
this.runtimeAddress = undefined
|
|
this.pendingPrompts = {
|
|
list: async () => [],
|
|
update: async () => ({ updated: false }),
|
|
delete: async () => ({ updated: false }),
|
|
}
|
|
}
|
|
|
|
static async create() {
|
|
return new this()
|
|
}
|
|
|
|
async start() {
|
|
return { sessionId: "test-session" }
|
|
}
|
|
|
|
async send() {
|
|
return undefined
|
|
}
|
|
|
|
async getAccumulatedUsage() {
|
|
return { usage: undefined }
|
|
}
|
|
|
|
async abort() {}
|
|
async stop() {}
|
|
async dispose() {}
|
|
async get() {
|
|
return undefined
|
|
}
|
|
async list() {
|
|
return []
|
|
}
|
|
async listHistory() {
|
|
return []
|
|
}
|
|
async delete() {
|
|
return false
|
|
}
|
|
async readMessages() {
|
|
return []
|
|
}
|
|
async update() {
|
|
return { updated: false }
|
|
}
|
|
async ingestHookEvent() {}
|
|
async updateSessionModel() {}
|
|
subscribe() {
|
|
return () => {}
|
|
}
|
|
},
|
|
ProviderSettingsManager,
|
|
listLocalProviders,
|
|
resolveProviderConfig: async () => undefined,
|
|
getProviderConfigFields: () => [],
|
|
fetchClineRecommendedModels: async () => ({ recommended: [], free: [] }),
|
|
readGlobalSettings: () => ({ telemetryOptOut: false }),
|
|
setTelemetryOptOutGlobally: () => undefined,
|
|
prepareRemoteConfigCoreIntegration: () => undefined,
|
|
createDefaultExecutors: () => ({}),
|
|
createMcpTools: () => ({}),
|
|
createOAuthClientCallbacks: () => ({}),
|
|
getProviderAuthHandler,
|
|
getProviderAuthStorageId,
|
|
resolveProviderApiKeyFromSettings,
|
|
getValidClineCredentials: async () => undefined,
|
|
loginClineOAuth: async () => undefined,
|
|
loginOcaOAuth: async () => undefined,
|
|
loginOpenAICodex: async () => undefined,
|
|
}
|
|
}
|
|
|
|
if (id === "@cline/shared") {
|
|
return {
|
|
buildClineSystemPrompt: () => "",
|
|
createTool: (tool) => tool,
|
|
formatDisplayUserInput: (input) => (typeof input === "string" ? input : JSON.stringify(input)),
|
|
}
|
|
}
|
|
|
|
if (id === "@cline/shared/storage") {
|
|
return {
|
|
resolveGlobalSettingsPath: () => path.join(baseUrl, ".vscode-test", "shared-global-settings.json"),
|
|
}
|
|
}
|
|
|
|
if (id === "@cline/llms") {
|
|
return {
|
|
getAllProviders: async () => [],
|
|
getGeneratedModelsForProvider: () => ({}),
|
|
getProviderCollectionSync: () => undefined,
|
|
MODEL_COLLECTIONS_BY_PROVIDER_ID: {},
|
|
}
|
|
}
|
|
|
|
if (id === "vitest") {
|
|
const assert = require("node:assert/strict")
|
|
const makeMockFn = (implementation = () => undefined) => {
|
|
const fn = (...args) => implementation(...args)
|
|
fn.mock = { calls: [] }
|
|
const wrapped = (...args) => {
|
|
wrapped.mock.calls.push(args)
|
|
return implementation(...args)
|
|
}
|
|
wrapped.mock = fn.mock
|
|
wrapped.mockImplementation = (next) => {
|
|
implementation = next
|
|
return wrapped
|
|
}
|
|
wrapped.mockResolvedValue = (value) => wrapped.mockImplementation(() => Promise.resolve(value))
|
|
wrapped.mockReturnValue = (value) => wrapped.mockImplementation(() => value)
|
|
return wrapped
|
|
}
|
|
const expect = (actual) => ({
|
|
toBe: (expected) => assert.equal(actual, expected),
|
|
toEqual: (expected) => assert.deepEqual(actual, expected),
|
|
toBeDefined: () => assert.notEqual(actual, undefined),
|
|
toBeTruthy: () => assert.ok(actual),
|
|
toBeGreaterThanOrEqual: (expected) => assert.ok(actual >= expected),
|
|
toContain: (expected) => assert.ok(actual.includes(expected)),
|
|
toHaveLength: (expected) => assert.equal(actual.length, expected),
|
|
not: {
|
|
toBe: (expected) => assert.notEqual(actual, expected),
|
|
toContain: (expected) => assert.ok(!actual.includes(expected)),
|
|
},
|
|
})
|
|
return {
|
|
afterAll: globalThis.after ?? (() => undefined),
|
|
afterEach: globalThis.afterEach ?? (() => undefined),
|
|
beforeAll: globalThis.before ?? (() => undefined),
|
|
beforeEach: globalThis.beforeEach ?? (() => undefined),
|
|
describe: globalThis.describe ?? (() => undefined),
|
|
expect,
|
|
it: globalThis.it ?? (() => undefined),
|
|
vi: {
|
|
fn: makeMockFn,
|
|
mock: () => undefined,
|
|
mocked: (value) => value,
|
|
clearAllMocks: () => undefined,
|
|
restoreAllMocks: () => undefined,
|
|
},
|
|
}
|
|
}
|
|
|
|
return originalRequire.call(this, id)
|
|
}
|