Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions src/core/webview/__tests__/webviewMessageHandler.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
import type { Mock } from "vitest"
import * as vscode from "vscode"

// Mock dependencies - must come before imports
vi.mock("../../../api/providers/fetchers/modelCache")
vi.mock("@roo-code/cloud", () => ({
CloudService: {
instance: {
shareTask: vi.fn(),
},
},
}))

// Mock vscode module
vi.mock("vscode", () => ({
window: {
showInformationMessage: vi.fn(),
showErrorMessage: vi.fn(),
},
env: {
clipboard: {
writeText: vi.fn(),
},
},
workspace: {
workspaceFolders: [],
},
}))

import { webviewMessageHandler } from "../webviewMessageHandler"
import type { ClineProvider } from "../ClineProvider"
import { getModels } from "../../../api/providers/fetchers/modelCache"
import type { ModelRecord } from "../../../shared/api"
import { CloudService } from "@roo-code/cloud"

const mockGetModels = getModels as Mock<typeof getModels>

Expand Down Expand Up @@ -275,6 +300,141 @@ describe("webviewMessageHandler - requestRouterModels", () => {
})
})

describe("webviewMessageHandler - shareCurrentTask", () => {
const mockShareClineProvider = {
getCurrentCline: vi.fn(),
postMessageToWebview: vi.fn(),
log: vi.fn(),
} as unknown as ClineProvider

beforeEach(() => {
vi.clearAllMocks()
})

it("copies share URL to clipboard on successful share", async () => {
const mockTaskId = "test-task-id"
const mockShareUrl = "https://roo-code.com/share/test-task-id"
const mockClineMessages = [{ type: "say", say: "user_feedback", text: "test message" }]

// Mock getCurrentCline to return a task with ID and messages
mockShareClineProvider.getCurrentCline = vi.fn().mockReturnValue({
taskId: mockTaskId,
clineMessages: mockClineMessages,
})

// Mock CloudService.instance.shareTask to return success
vi.mocked(CloudService.instance.shareTask).mockResolvedValue({
success: true,
shareUrl: mockShareUrl,
})

await webviewMessageHandler(mockShareClineProvider, {
type: "shareCurrentTask",
visibility: "organization",
})

// Verify clipboard.writeText was called with the share URL
expect(vscode.env.clipboard.writeText).toHaveBeenCalledWith(mockShareUrl)

// Verify success notification was shown
expect(vscode.window.showInformationMessage).toHaveBeenCalled()

// Verify webview message was sent
expect(mockShareClineProvider.postMessageToWebview).toHaveBeenCalledWith({
type: "shareTaskSuccess",
visibility: "organization",
text: mockShareUrl,
})

// Verify CloudService.shareTask was called with correct parameters
expect(CloudService.instance.shareTask).toHaveBeenCalledWith(mockTaskId, "organization", mockClineMessages)
})

it("does not copy to clipboard when share fails", async () => {
const mockTaskId = "test-task-id"
const mockClineMessages = [{ type: "say", say: "user_feedback", text: "test message" }]

// Mock getCurrentCline to return a task with ID and messages
mockShareClineProvider.getCurrentCline = vi.fn().mockReturnValue({
taskId: mockTaskId,
clineMessages: mockClineMessages,
})

// Mock CloudService.instance.shareTask to return failure
vi.mocked(CloudService.instance.shareTask).mockResolvedValue({
success: false,
error: "Authentication failed",
})

await webviewMessageHandler(mockShareClineProvider, {
type: "shareCurrentTask",
visibility: "public",
})

// Verify clipboard.writeText was NOT called
expect(vscode.env.clipboard.writeText).not.toHaveBeenCalled()

// Verify error notification was shown
expect(vscode.window.showErrorMessage).toHaveBeenCalled()

// Verify no success webview message was sent
expect(mockShareClineProvider.postMessageToWebview).not.toHaveBeenCalledWith(
expect.objectContaining({
type: "shareTaskSuccess",
}),
)
})

it("shows error when no active task", async () => {
// Mock getCurrentCline to return null (no active task)
mockShareClineProvider.getCurrentCline = vi.fn().mockReturnValue(null)

await webviewMessageHandler(mockShareClineProvider, {
type: "shareCurrentTask",
visibility: "organization",
})

// Verify clipboard.writeText was NOT called
expect(vscode.env.clipboard.writeText).not.toHaveBeenCalled()

// Verify error notification was shown
expect(vscode.window.showErrorMessage).toHaveBeenCalled()

// Verify CloudService.shareTask was NOT called
expect(CloudService.instance.shareTask).not.toHaveBeenCalled()
})

it("handles CloudService exceptions gracefully", async () => {
const mockTaskId = "test-task-id"
const mockClineMessages = [{ type: "say", say: "user_feedback", text: "test message" }]

// Mock getCurrentCline to return a task with ID and messages
mockShareClineProvider.getCurrentCline = vi.fn().mockReturnValue({
taskId: mockTaskId,
clineMessages: mockClineMessages,
})

// Mock CloudService.instance.shareTask to throw an exception
vi.mocked(CloudService.instance.shareTask).mockRejectedValue(new Error("Network error"))

await webviewMessageHandler(mockShareClineProvider, {
type: "shareCurrentTask",
visibility: "organization",
})

// Verify clipboard.writeText was NOT called
expect(vscode.env.clipboard.writeText).not.toHaveBeenCalled()

// Verify error notification was shown
expect(vscode.window.showErrorMessage).toHaveBeenCalled()

// Verify error was logged
expect(mockShareClineProvider.log).toHaveBeenCalledWith(
expect.stringContaining("[shareCurrentTask] Unexpected error:"),
)
})
})

it("prefers config values over message values for LiteLLM", async () => {
const mockModels: ModelRecord = {}
mockGetModels.mockResolvedValue(mockModels)
Expand Down
3 changes: 3 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ export const webviewMessageHandler = async (
const result = await CloudService.instance.shareTask(shareTaskId, visibility, clineMessages)

if (result.success && result.shareUrl) {
// Copy the share URL to clipboard
await vscode.env.clipboard.writeText(result.shareUrl)

// Show success notification
const messageKey =
visibility === "public"
Expand Down