mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-29 03:32:08 +08:00
test(vscode): share unit vscode mock for next edit
This commit is contained in:
@@ -1009,7 +1009,7 @@
|
||||
"check-kilocode-change": "! grep -rIn 'kilocode_change' . ../kilo-ui/ --exclude='package.json' --exclude='*.md' --exclude-dir='node_modules' --exclude-dir='dist' | grep -v '`kilocode_change`'",
|
||||
"lint": "eslint src webview-ui",
|
||||
"test": "vscode-test",
|
||||
"test:unit": "bun test tests/unit/ && bun test tests/next-edit/",
|
||||
"test:unit": "bun test tests/unit/",
|
||||
"rebuild-sdk": "bun run --cwd ../sdk/js build",
|
||||
"storybook": "storybook dev -p 6007",
|
||||
"build-storybook": "storybook build -o storybook-static",
|
||||
|
||||
@@ -46,6 +46,10 @@ const mockVscode = {
|
||||
version: "1.90.0",
|
||||
workspace: {
|
||||
workspaceFolders: [{ uri: { fsPath: "/repo" } }],
|
||||
textDocuments: [] as Array<unknown>,
|
||||
onDidOpenTextDocument: () => ({ dispose: noop }),
|
||||
onDidChangeTextDocument: () => ({ dispose: noop }),
|
||||
onDidCloseTextDocument: () => ({ dispose: noop }),
|
||||
getConfiguration: () => ({
|
||||
get: <T>(_key: string, value?: T) => value,
|
||||
update: async () => {},
|
||||
@@ -134,6 +138,13 @@ const mockVscode = {
|
||||
public end: { line: number; character: number },
|
||||
) {}
|
||||
},
|
||||
InlineCompletionItem: class {
|
||||
constructor(
|
||||
public insertText: string,
|
||||
public range?: unknown,
|
||||
public command?: unknown,
|
||||
) {}
|
||||
},
|
||||
Disposable: class {
|
||||
constructor(private callback: () => void = noop) {}
|
||||
dispose() {
|
||||
|
||||
+13
-29
@@ -1,24 +1,7 @@
|
||||
import { describe, expect, it, vi } from "vitest"
|
||||
import { afterEach, describe, expect, it } from "bun:test"
|
||||
import * as vscode from "vscode"
|
||||
import { EditHistoryTracker } from "../../src/services/autocomplete/next-edit/editHistoryTracker"
|
||||
|
||||
vi.mock("vscode", () => {
|
||||
const opens: Array<(doc: unknown) => void> = []
|
||||
return {
|
||||
workspace: {
|
||||
textDocuments: [],
|
||||
asRelativePath: (uri: { fsPath: string }) => uri.fsPath.replace("/workspace/", ""),
|
||||
onDidOpenTextDocument: (cb: (doc: unknown) => void) => {
|
||||
opens.push(cb)
|
||||
return { dispose: vi.fn() }
|
||||
},
|
||||
onDidChangeTextDocument: () => ({ dispose: vi.fn() }),
|
||||
onDidCloseTextDocument: () => ({ dispose: vi.fn() }),
|
||||
open: (doc: unknown) => opens.forEach((cb) => cb(doc)),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
type Doc = vscode.TextDocument & { setText(text: string): void }
|
||||
|
||||
function doc(path: string, initial: string): Doc {
|
||||
@@ -32,19 +15,23 @@ function doc(path: string, initial: string): Doc {
|
||||
} as unknown as Doc
|
||||
}
|
||||
|
||||
function docs(...items: Doc[]): void {
|
||||
;(vscode.workspace.textDocuments as unknown as Doc[]).splice(0, Infinity, ...items)
|
||||
}
|
||||
|
||||
function settle(): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, 0))
|
||||
}
|
||||
|
||||
afterEach(() => docs())
|
||||
|
||||
describe("EditHistoryTracker", () => {
|
||||
it("retains chronological edits across files for Mercury context", async () => {
|
||||
const tracker = new EditHistoryTracker({ isFileAllowed: async () => true })
|
||||
const a = doc("/workspace/a.ts", "const a = 1\n")
|
||||
const b = doc("/workspace/b.ts", "const b = 1\n")
|
||||
const open = (vscode.workspace as unknown as { open(doc: vscode.TextDocument): void }).open
|
||||
docs(a, b)
|
||||
const tracker = new EditHistoryTracker({ isFileAllowed: async () => true })
|
||||
|
||||
open(a)
|
||||
open(b)
|
||||
await settle()
|
||||
a.setText("const a = 2\n")
|
||||
await tracker.flush(a)
|
||||
@@ -62,11 +49,10 @@ describe("EditHistoryTracker", () => {
|
||||
})
|
||||
|
||||
it("does not retain edits when the access policy is missing at runtime", async () => {
|
||||
const tracker = new EditHistoryTracker({} as { isFileAllowed: (path: string) => Promise<boolean> })
|
||||
const a = doc("/workspace/a.ts", "const a = 1\n")
|
||||
const open = (vscode.workspace as unknown as { open(doc: vscode.TextDocument): void }).open
|
||||
docs(a)
|
||||
const tracker = new EditHistoryTracker({} as { isFileAllowed: (path: string) => Promise<boolean> })
|
||||
|
||||
open(a)
|
||||
await settle()
|
||||
a.setText("const a = 2\n")
|
||||
await tracker.flush(a)
|
||||
@@ -77,13 +63,11 @@ describe("EditHistoryTracker", () => {
|
||||
|
||||
it("never returns edits from denied documents", async () => {
|
||||
const denied = new Set(["/workspace/.env"])
|
||||
const tracker = new EditHistoryTracker({ isFileAllowed: async (path) => !denied.has(path) })
|
||||
const safe = doc("/workspace/app.ts", "const safe = 1\n")
|
||||
const secret = doc("/workspace/.env", "TOKEN=old\n")
|
||||
const open = (vscode.workspace as unknown as { open(doc: vscode.TextDocument): void }).open
|
||||
docs(safe, secret)
|
||||
const tracker = new EditHistoryTracker({ isFileAllowed: async (path) => !denied.has(path) })
|
||||
|
||||
open(safe)
|
||||
open(secret)
|
||||
await settle()
|
||||
secret.setText("TOKEN=secret\n")
|
||||
await tracker.flush(secret)
|
||||
+6
-35
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, vi } from "vitest"
|
||||
import { describe, expect, it, mock } from "bun:test"
|
||||
import * as vscode from "vscode"
|
||||
import type { KiloConnectionService } from "../../src/services/cli-backend"
|
||||
import {
|
||||
@@ -7,35 +7,6 @@ import {
|
||||
} from "../../src/services/autocomplete/next-edit/NextEditInlineCompletionProvider"
|
||||
import type { NextEditSuggestionManager } from "../../src/services/autocomplete/next-edit/NextEditSuggestionManager"
|
||||
|
||||
vi.mock("vscode", () => {
|
||||
class Position {
|
||||
constructor(
|
||||
public line: number,
|
||||
public character: number,
|
||||
) {}
|
||||
}
|
||||
class Range {
|
||||
constructor(
|
||||
public start: Position,
|
||||
public end: Position,
|
||||
) {}
|
||||
}
|
||||
return {
|
||||
Position,
|
||||
Range,
|
||||
InlineCompletionItem: class {},
|
||||
workspace: {
|
||||
textDocuments: [],
|
||||
onDidOpenTextDocument: () => ({ dispose: vi.fn() }),
|
||||
onDidChangeTextDocument: () => ({ dispose: vi.fn() }),
|
||||
onDidCloseTextDocument: () => ({ dispose: vi.fn() }),
|
||||
},
|
||||
window: {
|
||||
createOutputChannel: () => ({ appendLine: vi.fn(), dispose: vi.fn() }),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
type Subject = {
|
||||
toCompletionItems(
|
||||
document: vscode.TextDocument,
|
||||
@@ -64,7 +35,7 @@ function doc(text: string): vscode.TextDocument {
|
||||
|
||||
describe("NextEditInlineCompletionProvider", () => {
|
||||
it("does not send a document when the access policy is missing at runtime", async () => {
|
||||
const connection = { getClientAsync: vi.fn() }
|
||||
const connection = { getClientAsync: mock() }
|
||||
const provider = new NextEditInlineCompletionProvider({
|
||||
connectionService: connection,
|
||||
} as unknown as NextEditProviderDeps)
|
||||
@@ -82,7 +53,7 @@ describe("NextEditInlineCompletionProvider", () => {
|
||||
})
|
||||
|
||||
it("does not send a document when the access policy fails", async () => {
|
||||
const connection = { getClientAsync: vi.fn() }
|
||||
const connection = { getClientAsync: mock() }
|
||||
const provider = new NextEditInlineCompletionProvider({
|
||||
connectionService: connection as unknown as KiloConnectionService,
|
||||
isFileAllowed: async () => Promise.reject(new Error("unavailable")),
|
||||
@@ -101,7 +72,7 @@ describe("NextEditInlineCompletionProvider", () => {
|
||||
})
|
||||
|
||||
it("stashes same-line rewrites before the cursor for decorated acceptance", () => {
|
||||
const mgr = { clear: vi.fn(), setPending: vi.fn() }
|
||||
const mgr = { clear: mock(), setPending: mock() }
|
||||
const provider = new NextEditInlineCompletionProvider({
|
||||
connectionService: {} as KiloConnectionService,
|
||||
isFileAllowed: async () => true,
|
||||
@@ -129,7 +100,7 @@ describe("NextEditInlineCompletionProvider", () => {
|
||||
})
|
||||
|
||||
it("stashes complete-line deletion intent for acceptance", () => {
|
||||
const mgr = { clear: vi.fn(), setPending: vi.fn() }
|
||||
const mgr = { clear: mock(), setPending: mock() }
|
||||
const provider = new NextEditInlineCompletionProvider({
|
||||
connectionService: {} as KiloConnectionService,
|
||||
isFileAllowed: async () => true,
|
||||
@@ -155,7 +126,7 @@ describe("NextEditInlineCompletionProvider", () => {
|
||||
})
|
||||
|
||||
it("does not classify a blank-line rewrite as deletion", () => {
|
||||
const mgr = { clear: vi.fn(), setPending: vi.fn() }
|
||||
const mgr = { clear: mock(), setPending: mock() }
|
||||
const provider = new NextEditInlineCompletionProvider({
|
||||
connectionService: {} as KiloConnectionService,
|
||||
isFileAllowed: async () => true,
|
||||
Reference in New Issue
Block a user