mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-28 01:31:35 +00:00
* feat: remove Roomote Control from extension Remove all Roomote Control (remote control) functionality: - Remove BridgeOrchestrator and entire bridge directory from @roo-code/cloud - Remove remoteControlEnabled, featureRoomoteControlEnabled from extension state - Remove extensionBridgeEnabled from CloudUserInfo and user settings - Remove roomoteControlEnabled from organization/user feature schemas - Remove enableBridge from Task and ClineProvider - Remove remote control toggle from CloudView UI - Remove remoteControlEnabled message handler - Remove extension bridge disconnect on logout/deactivate - Update CloudTaskButton to show for all logged-in users - Remove remote control translation strings from all locales - Update all related tests CLO-765 * fix: remove dead getOrganizationMetadata and unused socket.io-client dep * Readmes * Readmes * Types * fix: remove leftover Roomote Control references from locale READMEs and stale BridgeOrchestrator mock * Removes cloudtaskbutton * fix: remove orphaned qrcode packages and dead openInCloud translation keys * pnpmlock * Revert these * Revert these * Revert these * Remove socket.io --------- Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: Bruno Bergher <bruno@roocode.com> Co-authored-by: cte <cestreich@gmail.com>
347 lines
8.8 KiB
TypeScript
347 lines
8.8 KiB
TypeScript
// npx vitest run __tests__/extension.spec.ts
|
|
|
|
import type * as vscode from "vscode"
|
|
import type { AuthState } from "@roo-code/types"
|
|
|
|
vi.mock("vscode", () => ({
|
|
window: {
|
|
createOutputChannel: vi.fn().mockReturnValue({
|
|
appendLine: vi.fn(),
|
|
}),
|
|
registerWebviewViewProvider: vi.fn(),
|
|
registerUriHandler: vi.fn(),
|
|
tabGroups: {
|
|
onDidChangeTabs: vi.fn(),
|
|
},
|
|
onDidChangeActiveTextEditor: vi.fn(),
|
|
},
|
|
workspace: {
|
|
registerTextDocumentContentProvider: vi.fn(),
|
|
getConfiguration: vi.fn().mockReturnValue({
|
|
get: vi.fn().mockReturnValue([]),
|
|
}),
|
|
createFileSystemWatcher: vi.fn().mockReturnValue({
|
|
onDidCreate: vi.fn(),
|
|
onDidChange: vi.fn(),
|
|
onDidDelete: vi.fn(),
|
|
dispose: vi.fn(),
|
|
}),
|
|
onDidChangeWorkspaceFolders: vi.fn(),
|
|
},
|
|
languages: {
|
|
registerCodeActionsProvider: vi.fn(),
|
|
},
|
|
commands: {
|
|
executeCommand: vi.fn(),
|
|
},
|
|
env: {
|
|
language: "en",
|
|
},
|
|
ExtensionMode: {
|
|
Production: 1,
|
|
},
|
|
}))
|
|
|
|
vi.mock("@dotenvx/dotenvx", () => ({
|
|
config: vi.fn(),
|
|
}))
|
|
|
|
// Mock fs so the extension module can safely check for optional .env.
|
|
vi.mock("fs", () => ({
|
|
existsSync: vi.fn().mockReturnValue(false),
|
|
}))
|
|
|
|
const mockCloudServiceInstance = {
|
|
off: vi.fn(),
|
|
on: vi.fn(),
|
|
getUserInfo: vi.fn().mockReturnValue(null),
|
|
isTaskSyncEnabled: vi.fn().mockReturnValue(false),
|
|
authService: {
|
|
getSessionToken: vi.fn().mockReturnValue("test-session-token"),
|
|
},
|
|
}
|
|
|
|
vi.mock("@roo-code/cloud", () => ({
|
|
CloudService: {
|
|
createInstance: vi.fn(),
|
|
hasInstance: vi.fn().mockReturnValue(true),
|
|
get instance() {
|
|
return mockCloudServiceInstance
|
|
},
|
|
},
|
|
getRooCodeApiUrl: vi.fn().mockReturnValue("https://app.roocode.com"),
|
|
}))
|
|
|
|
vi.mock("@roo-code/telemetry", () => ({
|
|
TelemetryService: {
|
|
createInstance: vi.fn().mockReturnValue({
|
|
register: vi.fn(),
|
|
setProvider: vi.fn(),
|
|
shutdown: vi.fn(),
|
|
}),
|
|
get instance() {
|
|
return {
|
|
register: vi.fn(),
|
|
setProvider: vi.fn(),
|
|
shutdown: vi.fn(),
|
|
}
|
|
},
|
|
},
|
|
PostHogTelemetryClient: vi.fn(),
|
|
}))
|
|
|
|
vi.mock("../utils/outputChannelLogger", () => ({
|
|
createOutputChannelLogger: vi.fn().mockReturnValue(vi.fn()),
|
|
createDualLogger: vi.fn().mockReturnValue(vi.fn()),
|
|
}))
|
|
|
|
vi.mock("../shared/package", () => ({
|
|
Package: {
|
|
name: "test-extension",
|
|
outputChannel: "Test Output",
|
|
version: "1.0.0",
|
|
},
|
|
}))
|
|
|
|
vi.mock("../shared/language", () => ({
|
|
formatLanguage: vi.fn().mockReturnValue("en"),
|
|
}))
|
|
|
|
vi.mock("../core/config/ContextProxy", () => ({
|
|
ContextProxy: {
|
|
getInstance: vi.fn().mockResolvedValue({
|
|
getValue: vi.fn(),
|
|
setValue: vi.fn(),
|
|
getValues: vi.fn().mockReturnValue({}),
|
|
getProviderSettings: vi.fn().mockReturnValue({}),
|
|
}),
|
|
},
|
|
}))
|
|
|
|
vi.mock("../integrations/editor/DiffViewProvider", () => ({
|
|
DIFF_VIEW_URI_SCHEME: "test-diff-scheme",
|
|
}))
|
|
|
|
vi.mock("../integrations/terminal/TerminalRegistry", () => ({
|
|
TerminalRegistry: {
|
|
initialize: vi.fn(),
|
|
cleanup: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
vi.mock("../services/mcp/McpServerManager", () => ({
|
|
McpServerManager: {
|
|
cleanup: vi.fn().mockResolvedValue(undefined),
|
|
getInstance: vi.fn().mockResolvedValue(null),
|
|
unregisterProvider: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
vi.mock("../services/code-index/manager", () => ({
|
|
CodeIndexManager: {
|
|
getInstance: vi.fn().mockReturnValue(null),
|
|
},
|
|
}))
|
|
|
|
vi.mock("../services/mdm/MdmService", () => ({
|
|
MdmService: {
|
|
createInstance: vi.fn().mockResolvedValue(null),
|
|
},
|
|
}))
|
|
|
|
vi.mock("../utils/migrateSettings", () => ({
|
|
migrateSettings: vi.fn().mockResolvedValue(undefined),
|
|
}))
|
|
|
|
vi.mock("../utils/autoImportSettings", () => ({
|
|
autoImportSettings: vi.fn().mockResolvedValue(undefined),
|
|
}))
|
|
|
|
vi.mock("../extension/api", () => ({
|
|
API: vi.fn().mockImplementation(() => ({})),
|
|
}))
|
|
|
|
vi.mock("../activate", () => ({
|
|
handleUri: vi.fn(),
|
|
registerCommands: vi.fn(),
|
|
registerCodeActions: vi.fn(),
|
|
registerTerminalActions: vi.fn(),
|
|
CodeActionProvider: vi.fn().mockImplementation(() => ({
|
|
providedCodeActionKinds: [],
|
|
})),
|
|
}))
|
|
|
|
vi.mock("../i18n", () => ({
|
|
initializeI18n: vi.fn(),
|
|
t: vi.fn((key) => key),
|
|
}))
|
|
|
|
// Mock ClineProvider
|
|
vi.mock("../core/webview/ClineProvider", async () => {
|
|
const mockInstance = {
|
|
resolveWebviewView: vi.fn(),
|
|
postMessageToWebview: vi.fn(),
|
|
postStateToWebview: vi.fn(),
|
|
postStateToWebviewWithoutClineMessages: vi.fn(),
|
|
getState: vi.fn().mockResolvedValue({}),
|
|
initializeCloudProfileSyncWhenReady: vi.fn().mockResolvedValue(undefined),
|
|
providerSettingsManager: {},
|
|
contextProxy: { getGlobalState: vi.fn() },
|
|
customModesManager: {},
|
|
upsertProviderProfile: vi.fn().mockResolvedValue(undefined),
|
|
}
|
|
return {
|
|
ClineProvider: Object.assign(
|
|
vi.fn().mockImplementation(() => mockInstance),
|
|
{
|
|
// Static method used by extension.ts
|
|
getVisibleInstance: vi.fn().mockReturnValue(mockInstance),
|
|
sideBarId: "roo-cline-sidebar",
|
|
},
|
|
),
|
|
}
|
|
})
|
|
|
|
// Mock modelCache to prevent network requests during module loading
|
|
const mockRefreshModels = vi.fn().mockResolvedValue({})
|
|
vi.mock("../api/providers/fetchers/modelCache", () => ({
|
|
flushModels: vi.fn(),
|
|
getModels: vi.fn().mockResolvedValue([]),
|
|
initializeModelCacheRefresh: vi.fn(),
|
|
refreshModels: mockRefreshModels,
|
|
}))
|
|
|
|
describe("extension.ts", () => {
|
|
let mockContext: vscode.ExtensionContext
|
|
let authStateChangedHandler:
|
|
| ((data: { state: AuthState; previousState: AuthState }) => void | Promise<void>)
|
|
| undefined
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
|
|
mockContext = {
|
|
extensionPath: "/test/path",
|
|
globalState: {
|
|
get: vi.fn().mockReturnValue(undefined),
|
|
update: vi.fn(),
|
|
},
|
|
subscriptions: [],
|
|
} as unknown as vscode.ExtensionContext
|
|
|
|
authStateChangedHandler = undefined
|
|
})
|
|
|
|
test("does not call dotenvx.config when optional .env does not exist", async () => {
|
|
vi.resetModules()
|
|
vi.clearAllMocks()
|
|
|
|
const fs = await import("fs")
|
|
vi.mocked(fs.existsSync).mockReturnValue(false)
|
|
|
|
const dotenvx = await import("@dotenvx/dotenvx")
|
|
|
|
const { activate } = await import("../extension")
|
|
await activate(mockContext)
|
|
|
|
expect(dotenvx.config).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test("calls dotenvx.config when optional .env exists", async () => {
|
|
vi.resetModules()
|
|
vi.clearAllMocks()
|
|
|
|
const fs = await import("fs")
|
|
vi.mocked(fs.existsSync).mockReturnValue(true)
|
|
|
|
const dotenvx = await import("@dotenvx/dotenvx")
|
|
|
|
const { activate } = await import("../extension")
|
|
await activate(mockContext)
|
|
|
|
expect(dotenvx.config).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
describe("Roo model cache refresh on auth state change (ROO-202)", () => {
|
|
beforeEach(() => {
|
|
vi.resetModules()
|
|
mockRefreshModels.mockClear()
|
|
})
|
|
|
|
test("refreshModels is called with session token when auth state changes to active-session", async () => {
|
|
const mockAuthService = {
|
|
getSessionToken: vi.fn().mockReturnValue("test-session-token"),
|
|
}
|
|
|
|
const { CloudService } = await import("@roo-code/cloud")
|
|
|
|
vi.mocked(CloudService.createInstance).mockImplementation(async (_context, _logger, handlers) => {
|
|
if (handlers?.["auth-state-changed"]) {
|
|
authStateChangedHandler = handlers["auth-state-changed"]
|
|
}
|
|
return {
|
|
off: vi.fn(),
|
|
on: vi.fn(),
|
|
telemetryClient: null,
|
|
authService: mockAuthService,
|
|
hasActiveSession: vi.fn().mockReturnValue(false),
|
|
} as any
|
|
})
|
|
|
|
vi.mocked(CloudService.hasInstance).mockReturnValue(true)
|
|
|
|
// Activate the extension
|
|
const { activate } = await import("../extension")
|
|
await activate(mockContext)
|
|
|
|
// Clear any calls during activation
|
|
mockRefreshModels.mockClear()
|
|
|
|
// Trigger active-session state
|
|
await authStateChangedHandler!({
|
|
state: "active-session" as AuthState,
|
|
previousState: "logged-out" as AuthState,
|
|
})
|
|
|
|
// Verify refreshModels was called with correct parameters including session token
|
|
expect(mockRefreshModels).toHaveBeenCalledWith({
|
|
provider: "roo",
|
|
baseUrl: expect.any(String),
|
|
apiKey: "test-session-token",
|
|
})
|
|
})
|
|
|
|
test("flushModels is called when auth state changes to logged-out", async () => {
|
|
const { flushModels } = await import("../api/providers/fetchers/modelCache")
|
|
const { CloudService } = await import("@roo-code/cloud")
|
|
|
|
vi.mocked(CloudService.createInstance).mockImplementation(async (_context, _logger, handlers) => {
|
|
if (handlers?.["auth-state-changed"]) {
|
|
authStateChangedHandler = handlers["auth-state-changed"]
|
|
}
|
|
return {
|
|
off: vi.fn(),
|
|
on: vi.fn(),
|
|
telemetryClient: null,
|
|
authService: null,
|
|
hasActiveSession: vi.fn().mockReturnValue(false),
|
|
} as any
|
|
})
|
|
|
|
vi.mocked(CloudService.hasInstance).mockReturnValue(true)
|
|
|
|
// Activate the extension
|
|
const { activate } = await import("../extension")
|
|
await activate(mockContext)
|
|
|
|
// Trigger logged-out state
|
|
await authStateChangedHandler!({
|
|
state: "logged-out" as AuthState,
|
|
previousState: "active-session" as AuthState,
|
|
})
|
|
|
|
// Verify flushModels was called to clear the cache on logout
|
|
expect(flushModels).toHaveBeenCalledWith({ provider: "roo" }, false)
|
|
})
|
|
})
|
|
})
|