Skip to content

Commit d6c54f7

Browse files
committed
test(roo-import): add unit tests for importRooHandoff command handler
1 parent 9220b8d commit d6c54f7

2 files changed

Lines changed: 108 additions & 31 deletions

File tree

src/activate/__tests__/registerCommands.spec.ts

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { Mock } from "vitest"
22
import * as vscode from "vscode"
33
import { ClineProvider } from "../../core/webview/ClineProvider"
4+
import * as RooImport from "../../services/roo-import/RooImport"
45

5-
import { getVisibleProviderOrLog } from "../registerCommands"
6+
import { getVisibleProviderOrLog, handleImportRooHandoff } from "../registerCommands"
67

78
vi.mock("execa", () => ({
89
execa: vi.fn(),
@@ -29,6 +30,11 @@ vi.mock("vscode", () => ({
2930

3031
vi.mock("../../core/webview/ClineProvider")
3132

33+
vi.mock("../../services/roo-import/RooImport", () => ({
34+
importRooHandoffFromPath: vi.fn(),
35+
promptAndImportRooHandoff: vi.fn(),
36+
}))
37+
3238
describe("getVisibleProviderOrLog", () => {
3339
let mockOutputChannel: vscode.OutputChannel
3440

@@ -65,3 +71,68 @@ describe("getVisibleProviderOrLog", () => {
6571
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith("Cannot find any visible Roo Code instances.")
6672
})
6773
})
74+
75+
describe("handleImportRooHandoff", () => {
76+
let mockOutputChannel: vscode.OutputChannel
77+
let mockContext: vscode.ExtensionContext
78+
let mockProvider: Partial<ClineProvider>
79+
80+
beforeEach(() => {
81+
mockOutputChannel = { appendLine: vi.fn() } as any
82+
mockContext = { globalStorageUri: { fsPath: "/tmp/zoo" } } as any
83+
mockProvider = {
84+
providerSettingsManager: {} as any,
85+
contextProxy: {} as any,
86+
customModesManager: {} as any,
87+
postStateToWebview: vi.fn().mockResolvedValue(undefined),
88+
}
89+
vi.clearAllMocks()
90+
;(ClineProvider.getInstance as Mock).mockResolvedValue(mockProvider)
91+
})
92+
93+
it("returns undefined when no provider is available", async () => {
94+
;(ClineProvider.getInstance as Mock).mockResolvedValue(undefined)
95+
96+
const result = await handleImportRooHandoff(undefined, mockContext, mockOutputChannel)
97+
98+
expect(result).toBeUndefined()
99+
})
100+
101+
it("calls importRooHandoffFromPath when a handoff path is given", async () => {
102+
const mockResult = { success: true, tasksCopied: 2 }
103+
vi.mocked(RooImport.importRooHandoffFromPath).mockResolvedValue(mockResult)
104+
105+
const result = await handleImportRooHandoff("/path/to/handoff.json", mockContext, mockOutputChannel)
106+
107+
expect(RooImport.importRooHandoffFromPath).toHaveBeenCalledWith("/path/to/handoff.json", expect.any(Object))
108+
expect(mockProvider.postStateToWebview).toHaveBeenCalled()
109+
expect(result).toBe(mockResult)
110+
})
111+
112+
it("calls promptAndImportRooHandoff when no path is given", async () => {
113+
const mockResult = { success: true, tasksCopied: 0 }
114+
vi.mocked(RooImport.promptAndImportRooHandoff).mockResolvedValue(mockResult)
115+
116+
const result = await handleImportRooHandoff(undefined, mockContext, mockOutputChannel)
117+
118+
expect(RooImport.promptAndImportRooHandoff).toHaveBeenCalled()
119+
expect(result).toBe(mockResult)
120+
})
121+
122+
it("does not call postStateToWebview when import returns undefined", async () => {
123+
vi.mocked(RooImport.promptAndImportRooHandoff).mockResolvedValue(undefined)
124+
125+
await handleImportRooHandoff(undefined, mockContext, mockOutputChannel)
126+
127+
expect(mockProvider.postStateToWebview).not.toHaveBeenCalled()
128+
})
129+
130+
it("logs and returns undefined when the import throws", async () => {
131+
vi.mocked(RooImport.importRooHandoffFromPath).mockRejectedValue(new Error("disk full"))
132+
133+
const result = await handleImportRooHandoff("/path/to/handoff.json", mockContext, mockOutputChannel)
134+
135+
expect(result).toBeUndefined()
136+
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith("[Roo Import] Failed: disk full")
137+
})
138+
})

src/activate/registerCommands.ts

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,41 @@ export type RegisterCommandOptions = {
6262
provider: ClineProvider
6363
}
6464

65+
export async function handleImportRooHandoff(
66+
handoffPath: string | undefined,
67+
context: vscode.ExtensionContext,
68+
outputChannel: vscode.OutputChannel,
69+
) {
70+
const visibleProvider = await ClineProvider.getInstance()
71+
if (!visibleProvider) {
72+
return undefined
73+
}
74+
75+
const importOptions = {
76+
context,
77+
providerSettingsManager: visibleProvider.providerSettingsManager,
78+
contextProxy: visibleProvider.contextProxy,
79+
customModesManager: visibleProvider.customModesManager,
80+
outputChannel,
81+
}
82+
83+
try {
84+
const result = handoffPath
85+
? await importRooHandoffFromPath(handoffPath, importOptions)
86+
: await promptAndImportRooHandoff(importOptions)
87+
88+
if (result) {
89+
await visibleProvider.postStateToWebview()
90+
}
91+
92+
return result
93+
} catch (error) {
94+
const message = error instanceof Error ? error.message : String(error)
95+
outputChannel.appendLine(`[Roo Import] Failed: ${message}`)
96+
return undefined
97+
}
98+
}
99+
65100
export const registerCommands = (options: RegisterCommandOptions) => {
66101
const { context } = options
67102

@@ -145,36 +180,7 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
145180
filePath,
146181
)
147182
},
148-
importRooHandoff: async (handoffPath?: string) => {
149-
const visibleProvider = await ClineProvider.getInstance()
150-
if (!visibleProvider) {
151-
return undefined
152-
}
153-
154-
const importOptions = {
155-
context,
156-
providerSettingsManager: visibleProvider.providerSettingsManager,
157-
contextProxy: visibleProvider.contextProxy,
158-
customModesManager: visibleProvider.customModesManager,
159-
outputChannel,
160-
}
161-
162-
try {
163-
const result = handoffPath
164-
? await importRooHandoffFromPath(handoffPath, importOptions)
165-
: await promptAndImportRooHandoff(importOptions)
166-
167-
if (result) {
168-
await visibleProvider.postStateToWebview()
169-
}
170-
171-
return result
172-
} catch (error) {
173-
const message = error instanceof Error ? error.message : String(error)
174-
outputChannel.appendLine(`[Roo Import] Failed: ${message}`)
175-
return undefined
176-
}
177-
},
183+
importRooHandoff: (handoffPath?: string) => handleImportRooHandoff(handoffPath, context, outputChannel),
178184
focusInput: async () => {
179185
try {
180186
await focusPanel(tabPanel, sidebarPanel)

0 commit comments

Comments
 (0)