|
|
@@ -1,21 +1,22 @@
|
|
|
+import { vitest, describe, it, expect, beforeEach, type Mock } from "vitest"
|
|
|
import * as vscode from "vscode"
|
|
|
import WorkspaceTracker from "../WorkspaceTracker"
|
|
|
import { ClineProvider } from "../../../core/webview/ClineProvider"
|
|
|
import { listFiles } from "../../../services/glob/list-files"
|
|
|
import { getWorkspacePath } from "../../../utils/path"
|
|
|
|
|
|
-// Mock functions - must be defined before jest.mock calls
|
|
|
-const mockOnDidCreate = jest.fn()
|
|
|
-const mockOnDidDelete = jest.fn()
|
|
|
-const mockDispose = jest.fn()
|
|
|
+// Mock functions - must be defined before vitest.mock calls
|
|
|
+const mockOnDidCreate = vitest.fn()
|
|
|
+const mockOnDidDelete = vitest.fn()
|
|
|
+const mockDispose = vitest.fn()
|
|
|
|
|
|
// Store registered tab change callback
|
|
|
let registeredTabChangeCallback: (() => Promise<void>) | null = null
|
|
|
|
|
|
// Mock workspace path
|
|
|
-jest.mock("../../../utils/path", () => ({
|
|
|
- getWorkspacePath: jest.fn().mockReturnValue("/test/workspace"),
|
|
|
- toRelativePath: jest.fn((path, cwd) => {
|
|
|
+vitest.mock("../../../utils/path", () => ({
|
|
|
+ getWorkspacePath: vitest.fn().mockReturnValue("/test/workspace"),
|
|
|
+ toRelativePath: vitest.fn((path, cwd) => {
|
|
|
// Handle both Windows and POSIX paths by using path.relative
|
|
|
const relativePath = require("path").relative(cwd, path)
|
|
|
// Convert to forward slashes for consistency
|
|
|
@@ -25,7 +26,7 @@ jest.mock("../../../utils/path", () => ({
|
|
|
}),
|
|
|
}))
|
|
|
|
|
|
-// Mock watcher - must be defined after mockDispose but before jest.mock("vscode")
|
|
|
+// Mock watcher - must be defined after mockDispose but before vitest.mock("vscode")
|
|
|
const mockWatcher = {
|
|
|
onDidCreate: mockOnDidCreate.mockReturnValue({ dispose: mockDispose }),
|
|
|
onDidDelete: mockOnDidDelete.mockReturnValue({ dispose: mockDispose }),
|
|
|
@@ -33,16 +34,16 @@ const mockWatcher = {
|
|
|
}
|
|
|
|
|
|
// Mock vscode
|
|
|
-jest.mock("vscode", () => ({
|
|
|
+vitest.mock("vscode", () => ({
|
|
|
window: {
|
|
|
tabGroups: {
|
|
|
- onDidChangeTabs: jest.fn((callback) => {
|
|
|
+ onDidChangeTabs: vitest.fn((callback) => {
|
|
|
registeredTabChangeCallback = callback
|
|
|
return { dispose: mockDispose }
|
|
|
}),
|
|
|
all: [],
|
|
|
},
|
|
|
- onDidChangeActiveTextEditor: jest.fn(() => ({ dispose: jest.fn() })),
|
|
|
+ onDidChangeActiveTextEditor: vitest.fn(() => ({ dispose: vitest.fn() })),
|
|
|
},
|
|
|
workspace: {
|
|
|
workspaceFolders: [
|
|
|
@@ -52,34 +53,36 @@ jest.mock("vscode", () => ({
|
|
|
index: 0,
|
|
|
},
|
|
|
],
|
|
|
- createFileSystemWatcher: jest.fn(() => mockWatcher),
|
|
|
+ createFileSystemWatcher: vitest.fn(() => mockWatcher),
|
|
|
fs: {
|
|
|
- stat: jest.fn().mockResolvedValue({ type: 1 }), // FileType.File = 1
|
|
|
+ stat: vitest.fn().mockResolvedValue({ type: 1 }), // FileType.File = 1
|
|
|
},
|
|
|
},
|
|
|
FileType: { File: 1, Directory: 2 },
|
|
|
}))
|
|
|
|
|
|
-jest.mock("../../../services/glob/list-files")
|
|
|
+vitest.mock("../../../services/glob/list-files", () => ({
|
|
|
+ listFiles: vitest.fn(),
|
|
|
+}))
|
|
|
|
|
|
describe("WorkspaceTracker", () => {
|
|
|
let workspaceTracker: WorkspaceTracker
|
|
|
let mockProvider: ClineProvider
|
|
|
|
|
|
beforeEach(() => {
|
|
|
- jest.clearAllMocks()
|
|
|
- jest.useFakeTimers()
|
|
|
+ vitest.clearAllMocks()
|
|
|
+ vitest.useFakeTimers()
|
|
|
|
|
|
// Reset all mock implementations
|
|
|
registeredTabChangeCallback = null
|
|
|
|
|
|
// Reset workspace path mock
|
|
|
- ;(getWorkspacePath as jest.Mock).mockReturnValue("/test/workspace")
|
|
|
+ ;(getWorkspacePath as Mock).mockReturnValue("/test/workspace")
|
|
|
|
|
|
// Create provider mock
|
|
|
mockProvider = {
|
|
|
- postMessageToWebview: jest.fn().mockResolvedValue(undefined),
|
|
|
- } as unknown as ClineProvider & { postMessageToWebview: jest.Mock }
|
|
|
+ postMessageToWebview: vitest.fn().mockResolvedValue(undefined),
|
|
|
+ } as unknown as ClineProvider & { postMessageToWebview: Mock }
|
|
|
|
|
|
// Create tracker instance
|
|
|
workspaceTracker = new WorkspaceTracker(mockProvider)
|
|
|
@@ -90,24 +93,24 @@ describe("WorkspaceTracker", () => {
|
|
|
|
|
|
it("should initialize with workspace files", async () => {
|
|
|
const mockFiles = [["/test/workspace/file1.ts", "/test/workspace/file2.ts"], false]
|
|
|
- ;(listFiles as jest.Mock).mockResolvedValue(mockFiles)
|
|
|
+ ;(listFiles as Mock).mockResolvedValue(mockFiles)
|
|
|
|
|
|
await workspaceTracker.initializeFilePaths()
|
|
|
- jest.runAllTimers()
|
|
|
+ vitest.runAllTimers()
|
|
|
|
|
|
expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({
|
|
|
type: "workspaceUpdated",
|
|
|
filePaths: expect.arrayContaining(["file1.ts", "file2.ts"]),
|
|
|
openedTabs: [],
|
|
|
})
|
|
|
- expect((mockProvider.postMessageToWebview as jest.Mock).mock.calls[0][0].filePaths).toHaveLength(2)
|
|
|
+ expect((mockProvider.postMessageToWebview as Mock).mock.calls[0][0].filePaths).toHaveLength(2)
|
|
|
})
|
|
|
|
|
|
it("should handle file creation events", async () => {
|
|
|
// Get the creation callback and call it
|
|
|
const [[callback]] = mockOnDidCreate.mock.calls
|
|
|
await callback({ fsPath: "/test/workspace/newfile.ts" })
|
|
|
- jest.runAllTimers()
|
|
|
+ vitest.runAllTimers()
|
|
|
|
|
|
expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({
|
|
|
type: "workspaceUpdated",
|
|
|
@@ -120,12 +123,12 @@ describe("WorkspaceTracker", () => {
|
|
|
// First add a file
|
|
|
const [[createCallback]] = mockOnDidCreate.mock.calls
|
|
|
await createCallback({ fsPath: "/test/workspace/file.ts" })
|
|
|
- jest.runAllTimers()
|
|
|
+ vitest.runAllTimers()
|
|
|
|
|
|
// Then delete it
|
|
|
const [[deleteCallback]] = mockOnDidDelete.mock.calls
|
|
|
await deleteCallback({ fsPath: "/test/workspace/file.ts" })
|
|
|
- jest.runAllTimers()
|
|
|
+ vitest.runAllTimers()
|
|
|
|
|
|
// The last call should have empty filePaths
|
|
|
expect(mockProvider.postMessageToWebview).toHaveBeenLastCalledWith({
|
|
|
@@ -137,32 +140,32 @@ describe("WorkspaceTracker", () => {
|
|
|
|
|
|
it("should handle directory paths correctly", async () => {
|
|
|
// Mock stat to return directory type
|
|
|
- ;(vscode.workspace.fs.stat as jest.Mock).mockResolvedValueOnce({ type: 2 }) // FileType.Directory = 2
|
|
|
+ ;(vscode.workspace.fs.stat as Mock).mockResolvedValueOnce({ type: 2 }) // FileType.Directory = 2
|
|
|
|
|
|
const [[callback]] = mockOnDidCreate.mock.calls
|
|
|
await callback({ fsPath: "/test/workspace/newdir" })
|
|
|
- jest.runAllTimers()
|
|
|
+ vitest.runAllTimers()
|
|
|
|
|
|
expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({
|
|
|
type: "workspaceUpdated",
|
|
|
filePaths: expect.arrayContaining(["newdir"]),
|
|
|
openedTabs: [],
|
|
|
})
|
|
|
- const lastCall = (mockProvider.postMessageToWebview as jest.Mock).mock.calls.slice(-1)[0]
|
|
|
+ const lastCall = (mockProvider.postMessageToWebview as Mock).mock.calls.slice(-1)[0]
|
|
|
expect(lastCall[0].filePaths).toHaveLength(1)
|
|
|
})
|
|
|
|
|
|
it("should respect file limits", async () => {
|
|
|
// Create array of unique file paths for initial load
|
|
|
const files = Array.from({ length: 1001 }, (_, i) => `/test/workspace/file${i}.ts`)
|
|
|
- ;(listFiles as jest.Mock).mockResolvedValue([files, false])
|
|
|
+ ;(listFiles as Mock).mockResolvedValue([files, false])
|
|
|
|
|
|
await workspaceTracker.initializeFilePaths()
|
|
|
- jest.runAllTimers()
|
|
|
+ vitest.runAllTimers()
|
|
|
|
|
|
// Should only have 1000 files initially
|
|
|
const expectedFiles = Array.from({ length: 1000 }, (_, i) => `file${i}.ts`).sort()
|
|
|
- const calls = (mockProvider.postMessageToWebview as jest.Mock).mock.calls
|
|
|
+ const calls = (mockProvider.postMessageToWebview as Mock).mock.calls
|
|
|
|
|
|
expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({
|
|
|
type: "workspaceUpdated",
|
|
|
@@ -176,16 +179,16 @@ describe("WorkspaceTracker", () => {
|
|
|
for (let i = 0; i < 1000; i++) {
|
|
|
await callback({ fsPath: `/test/workspace/extra${i}.ts` })
|
|
|
}
|
|
|
- jest.runAllTimers()
|
|
|
+ vitest.runAllTimers()
|
|
|
|
|
|
- const lastCall = (mockProvider.postMessageToWebview as jest.Mock).mock.calls.slice(-1)[0]
|
|
|
+ const lastCall = (mockProvider.postMessageToWebview as Mock).mock.calls.slice(-1)[0]
|
|
|
expect(lastCall[0].filePaths).toHaveLength(2000)
|
|
|
|
|
|
// Adding one more file beyond 2000 should not increase the count
|
|
|
await callback({ fsPath: "/test/workspace/toomany.ts" })
|
|
|
- jest.runAllTimers()
|
|
|
+ vitest.runAllTimers()
|
|
|
|
|
|
- const finalCall = (mockProvider.postMessageToWebview as jest.Mock).mock.calls.slice(-1)[0]
|
|
|
+ const finalCall = (mockProvider.postMessageToWebview as Mock).mock.calls.slice(-1)[0]
|
|
|
expect(finalCall[0].filePaths).toHaveLength(2000)
|
|
|
})
|
|
|
|
|
|
@@ -196,7 +199,7 @@ describe("WorkspaceTracker", () => {
|
|
|
|
|
|
workspaceTracker.dispose()
|
|
|
expect(mockDispose).toHaveBeenCalled()
|
|
|
- jest.runAllTimers() // Ensure any pending timers are cleared
|
|
|
+ vitest.runAllTimers() // Ensure any pending timers are cleared
|
|
|
|
|
|
// No more updates should happen after dispose
|
|
|
expect(mockProvider.postMessageToWebview).not.toHaveBeenCalled()
|
|
|
@@ -206,24 +209,24 @@ describe("WorkspaceTracker", () => {
|
|
|
expect(registeredTabChangeCallback).not.toBeNull()
|
|
|
|
|
|
// Set initial workspace path and create tracker
|
|
|
- ;(getWorkspacePath as jest.Mock).mockReturnValue("/test/workspace")
|
|
|
+ ;(getWorkspacePath as Mock).mockReturnValue("/test/workspace")
|
|
|
workspaceTracker = new WorkspaceTracker(mockProvider)
|
|
|
|
|
|
// Clear any initialization calls
|
|
|
- jest.clearAllMocks()
|
|
|
+ vitest.clearAllMocks()
|
|
|
|
|
|
// Mock listFiles to return some files
|
|
|
const mockFiles = [["/test/new-workspace/file1.ts"], false]
|
|
|
- ;(listFiles as jest.Mock).mockResolvedValue(mockFiles)
|
|
|
+ ;(listFiles as Mock).mockResolvedValue(mockFiles)
|
|
|
|
|
|
// Change workspace path
|
|
|
- ;(getWorkspacePath as jest.Mock).mockReturnValue("/test/new-workspace")
|
|
|
+ ;(getWorkspacePath as Mock).mockReturnValue("/test/new-workspace")
|
|
|
|
|
|
// Simulate tab change event
|
|
|
await registeredTabChangeCallback!()
|
|
|
|
|
|
// Run the debounce timer for workspaceDidReset
|
|
|
- jest.advanceTimersByTime(300)
|
|
|
+ vitest.advanceTimersByTime(300)
|
|
|
|
|
|
// Should clear file paths and reset workspace
|
|
|
expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({
|
|
|
@@ -234,21 +237,21 @@ describe("WorkspaceTracker", () => {
|
|
|
|
|
|
// Run all remaining timers to complete initialization
|
|
|
await Promise.resolve() // Wait for initializeFilePaths to complete
|
|
|
- jest.runAllTimers()
|
|
|
+ vitest.runAllTimers()
|
|
|
|
|
|
// Should initialize file paths for new workspace
|
|
|
expect(listFiles).toHaveBeenCalledWith("/test/new-workspace", true, 1000)
|
|
|
- jest.runAllTimers()
|
|
|
+ vitest.runAllTimers()
|
|
|
})
|
|
|
|
|
|
it("should not update file paths if workspace changes during initialization", async () => {
|
|
|
// Setup initial workspace path
|
|
|
- ;(getWorkspacePath as jest.Mock).mockReturnValue("/test/workspace")
|
|
|
+ ;(getWorkspacePath as Mock).mockReturnValue("/test/workspace")
|
|
|
workspaceTracker = new WorkspaceTracker(mockProvider)
|
|
|
|
|
|
// Clear any initialization calls
|
|
|
- jest.clearAllMocks()
|
|
|
- ;(mockProvider.postMessageToWebview as jest.Mock).mockClear()
|
|
|
+ vitest.clearAllMocks()
|
|
|
+ ;(mockProvider.postMessageToWebview as Mock).mockClear()
|
|
|
|
|
|
// Create a promise to control listFiles timing
|
|
|
let resolveListFiles: (value: [string[], boolean]) => void
|
|
|
@@ -257,9 +260,9 @@ describe("WorkspaceTracker", () => {
|
|
|
})
|
|
|
|
|
|
// Setup listFiles to use our controlled promise
|
|
|
- ;(listFiles as jest.Mock).mockImplementation(() => {
|
|
|
+ ;(listFiles as Mock).mockImplementation(() => {
|
|
|
// Change workspace path before listFiles resolves
|
|
|
- ;(getWorkspacePath as jest.Mock).mockReturnValue("/test/changed-workspace")
|
|
|
+ ;(getWorkspacePath as Mock).mockReturnValue("/test/changed-workspace")
|
|
|
return listFilesPromise
|
|
|
})
|
|
|
|
|
|
@@ -271,7 +274,7 @@ describe("WorkspaceTracker", () => {
|
|
|
|
|
|
// Wait for initialization to complete
|
|
|
await initPromise
|
|
|
- jest.runAllTimers()
|
|
|
+ vitest.runAllTimers()
|
|
|
|
|
|
// Should not update file paths because workspace changed during initialization
|
|
|
expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith(
|
|
|
@@ -282,7 +285,7 @@ describe("WorkspaceTracker", () => {
|
|
|
)
|
|
|
|
|
|
// Extract the actual file paths to verify format
|
|
|
- const actualFilePaths = (mockProvider.postMessageToWebview as jest.Mock).mock.calls[0][0].filePaths
|
|
|
+ const actualFilePaths = (mockProvider.postMessageToWebview as Mock).mock.calls[0][0].filePaths
|
|
|
|
|
|
// Verify file path array length
|
|
|
expect(actualFilePaths).toHaveLength(2)
|
|
|
@@ -297,13 +300,13 @@ describe("WorkspaceTracker", () => {
|
|
|
expect(registeredTabChangeCallback).not.toBeNull()
|
|
|
|
|
|
// Set initial workspace path
|
|
|
- ;(getWorkspacePath as jest.Mock).mockReturnValue("/test/workspace")
|
|
|
+ ;(getWorkspacePath as Mock).mockReturnValue("/test/workspace")
|
|
|
|
|
|
// Create tracker instance to set initial prevWorkSpacePath
|
|
|
workspaceTracker = new WorkspaceTracker(mockProvider)
|
|
|
|
|
|
// Change workspace path to trigger update
|
|
|
- ;(getWorkspacePath as jest.Mock).mockReturnValue("/test/new-workspace")
|
|
|
+ ;(getWorkspacePath as Mock).mockReturnValue("/test/new-workspace")
|
|
|
|
|
|
// Call workspaceDidReset through tab change event
|
|
|
await registeredTabChangeCallback!()
|
|
|
@@ -312,7 +315,7 @@ describe("WorkspaceTracker", () => {
|
|
|
await registeredTabChangeCallback!()
|
|
|
|
|
|
// Advance timer
|
|
|
- jest.advanceTimersByTime(300)
|
|
|
+ vitest.advanceTimersByTime(300)
|
|
|
|
|
|
// Should only have one call to postMessageToWebview
|
|
|
expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({
|
|
|
@@ -327,9 +330,7 @@ describe("WorkspaceTracker", () => {
|
|
|
expect(registeredTabChangeCallback).not.toBeNull()
|
|
|
|
|
|
// Mock workspace path change to trigger resetTimer
|
|
|
- ;(getWorkspacePath as jest.Mock)
|
|
|
- .mockReturnValueOnce("/test/workspace")
|
|
|
- .mockReturnValueOnce("/test/new-workspace")
|
|
|
+ ;(getWorkspacePath as Mock).mockReturnValueOnce("/test/workspace").mockReturnValueOnce("/test/new-workspace")
|
|
|
|
|
|
// Trigger resetTimer
|
|
|
await registeredTabChangeCallback!()
|
|
|
@@ -338,7 +339,7 @@ describe("WorkspaceTracker", () => {
|
|
|
workspaceTracker.dispose()
|
|
|
|
|
|
// Advance timer
|
|
|
- jest.advanceTimersByTime(300)
|
|
|
+ vitest.advanceTimersByTime(300)
|
|
|
|
|
|
// Should have called dispose on all disposables
|
|
|
expect(mockDispose).toHaveBeenCalled()
|