mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
test: add vscode Uri/RelativePattern mocks and fix extension.spec; fix: guard dev FS watchers in activate() and MCP watchers in tests; core(task): safe provider logging in batched webview updates
This commit is contained in:
parent
458aac2eb5
commit
62557dcc06
5 changed files with 65 additions and 31 deletions
|
|
@ -36,6 +36,8 @@ const mockSelection = class extends mockRange {
|
|||
}
|
||||
}
|
||||
|
||||
const mockRelativePattern = (base, pattern) => ({ base, pattern })
|
||||
|
||||
export const workspace = {
|
||||
workspaceFolders: [],
|
||||
getWorkspaceFolder: () => null,
|
||||
|
|
@ -107,6 +109,7 @@ export const env = {
|
|||
}
|
||||
|
||||
export const Uri = mockUri
|
||||
export const RelativePattern = mockRelativePattern
|
||||
export const Range = mockRange
|
||||
export const Position = mockPosition
|
||||
export const Selection = mockSelection
|
||||
|
|
@ -160,6 +163,7 @@ export default {
|
|||
extensions,
|
||||
env,
|
||||
Uri,
|
||||
RelativePattern,
|
||||
Range,
|
||||
Position,
|
||||
Selection,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,14 @@ vi.mock("vscode", () => ({
|
|||
ExtensionMode: {
|
||||
Production: 1,
|
||||
},
|
||||
// Provide minimal implementations required by extension.ts watchers in dev
|
||||
Uri: {
|
||||
file: vi.fn().mockImplementation((p: string) => ({ fsPath: p })),
|
||||
},
|
||||
RelativePattern: vi.fn().mockImplementation((base: { fsPath?: string } | string, pattern: string) => ({
|
||||
base: typeof base === "string" ? base : (base?.fsPath ?? ""),
|
||||
pattern,
|
||||
})),
|
||||
}))
|
||||
|
||||
vi.mock("@dotenvx/dotenvx", () => ({
|
||||
|
|
|
|||
|
|
@ -303,6 +303,17 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
private tokenUsageSnapshot?: TokenUsage
|
||||
private tokenUsageSnapshotAt?: number
|
||||
|
||||
private safeProviderLog(message: string): void {
|
||||
try {
|
||||
const provider = this.providerRef.deref()
|
||||
if (provider && typeof (provider as any).log === "function") {
|
||||
;(provider as any).log(message)
|
||||
}
|
||||
} catch {
|
||||
// no-op (tests/mocks may not implement log)
|
||||
}
|
||||
}
|
||||
|
||||
constructor({
|
||||
provider,
|
||||
apiConfiguration,
|
||||
|
|
@ -466,7 +477,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
this._taskMode = defaultModeSlug
|
||||
// Use the provider's log method for better error visibility
|
||||
const errorMessage = `Failed to initialize task mode: ${error instanceof Error ? error.message : String(error)}`
|
||||
provider.log(errorMessage)
|
||||
this.safeProviderLog(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -688,20 +699,16 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
|
||||
const providerNow = this.providerRef.deref()
|
||||
if (!providerNow) {
|
||||
this.providerRef
|
||||
.deref()
|
||||
?.log(
|
||||
`[Task#updateClineMessage] Dropping ${batch.length} messageUpdated deltas: provider unavailable`,
|
||||
)
|
||||
this.safeProviderLog(
|
||||
`[Task#updateClineMessage] Dropping ${batch.length} messageUpdated deltas: provider unavailable`,
|
||||
)
|
||||
return
|
||||
}
|
||||
if (!providerNow.isVisible()) {
|
||||
// Drop deltas while hidden; UI will receive a full state sync on visibility
|
||||
this.providerRef
|
||||
.deref()
|
||||
?.log(
|
||||
`[Task#updateClineMessage] Dropping ${batch.length} messageUpdated deltas while hidden`,
|
||||
)
|
||||
this.safeProviderLog(
|
||||
`[Task#updateClineMessage] Dropping ${batch.length} messageUpdated deltas while hidden`,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -709,13 +716,11 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
await providerNow.postMessageToWebview({ type: "messageUpdated", clineMessage: m })
|
||||
}
|
||||
} catch (e) {
|
||||
this.providerRef
|
||||
.deref()
|
||||
?.log(
|
||||
`[Task#updateClineMessage] Failed to flush message updates: ${
|
||||
e instanceof Error ? e.message : String(e)
|
||||
}`,
|
||||
)
|
||||
this.safeProviderLog(
|
||||
`[Task#updateClineMessage] Failed to flush message updates: ${
|
||||
e instanceof Error ? e.message : String(e)
|
||||
}`,
|
||||
)
|
||||
}
|
||||
}, this.MESSAGE_UPDATE_THROTTLE_MS)
|
||||
}
|
||||
|
|
@ -1741,9 +1746,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
// including the subtask result, not just from before the subtask was created
|
||||
this.skipPrevResponseIdOnce = true
|
||||
} catch (error) {
|
||||
this.providerRef
|
||||
.deref()
|
||||
?.log(`Error failed to add reply from subtask into conversation of parent task, error: ${error}`)
|
||||
this.safeProviderLog(
|
||||
`Error failed to add reply from subtask into conversation of parent task, error: ${error}`,
|
||||
)
|
||||
|
||||
throw error
|
||||
}
|
||||
|
|
@ -1834,9 +1839,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
const provider = this.providerRef.deref()
|
||||
|
||||
if (this.isPaused && provider) {
|
||||
provider.log(`[subtasks] paused ${this.taskId}.${this.instanceId}`)
|
||||
this.safeProviderLog(`[subtasks] paused ${this.taskId}.${this.instanceId}`)
|
||||
await this.waitForSubtask()
|
||||
provider.log(`[subtasks] resumed ${this.taskId}.${this.instanceId}`)
|
||||
this.safeProviderLog(`[subtasks] resumed ${this.taskId}.${this.instanceId}`)
|
||||
const currentMode = (await provider.getState())?.mode ?? defaultModeSlug
|
||||
|
||||
if (currentMode !== this.pausedModeSlug) {
|
||||
|
|
@ -1846,7 +1851,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
// Delay to allow mode change to take effect before next tool is executed.
|
||||
await delay(500)
|
||||
|
||||
provider.log(
|
||||
this.safeProviderLog(
|
||||
`[subtasks] task ${this.taskId}.${this.instanceId} has switched back to '${this.pausedModeSlug}' from '${currentMode}'`,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -273,7 +273,12 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||
const enableLogging = typeof socketPath === "string"
|
||||
|
||||
// Watch the core files and automatically reload the extension host.
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
if (
|
||||
process.env.NODE_ENV === "development" &&
|
||||
(vscode as any).RelativePattern &&
|
||||
(vscode as any).Uri?.file &&
|
||||
vscode.workspace?.createFileSystemWatcher
|
||||
) {
|
||||
const watchPaths = [
|
||||
{ path: context.extensionPath, pattern: "**/*.ts" },
|
||||
{ path: path.join(context.extensionPath, "../packages/types"), pattern: "**/*.ts" },
|
||||
|
|
|
|||
|
|
@ -152,6 +152,10 @@ export class McpHub {
|
|||
private refCount: number = 0 // Reference counter for active clients
|
||||
private configChangeDebounceTimers: Map<string, NodeJS.Timeout> = new Map()
|
||||
|
||||
private isTestEnvironment(): boolean {
|
||||
return process.env.NODE_ENV === "test" || !!process.env.VITEST || process.env.JEST === "true"
|
||||
}
|
||||
|
||||
constructor(provider: ClineProvider) {
|
||||
this.providerRef = new WeakRef(provider)
|
||||
this.watchMcpSettingsFile()
|
||||
|
|
@ -261,8 +265,7 @@ export class McpHub {
|
|||
}
|
||||
|
||||
public setupWorkspaceFoldersWatcher(): void {
|
||||
// Skip if test environment is detected
|
||||
if (process.env.NODE_ENV === "test") {
|
||||
if (this.isTestEnvironment() || !vscode.workspace?.onDidChangeWorkspaceFolders) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -334,8 +337,12 @@ export class McpHub {
|
|||
}
|
||||
|
||||
private async watchProjectMcpFile(): Promise<void> {
|
||||
// Skip if test environment is detected or VSCode APIs are not available
|
||||
if (process.env.NODE_ENV === "test" || !vscode.workspace.createFileSystemWatcher) {
|
||||
// Skip in test environments or when required VSCode APIs are unavailable
|
||||
if (
|
||||
this.isTestEnvironment() ||
|
||||
!vscode.workspace?.createFileSystemWatcher ||
|
||||
!(vscode as any).RelativePattern
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -467,8 +474,13 @@ export class McpHub {
|
|||
}
|
||||
|
||||
private async watchMcpSettingsFile(): Promise<void> {
|
||||
// Skip if test environment is detected or VSCode APIs are not available
|
||||
if (process.env.NODE_ENV === "test" || !vscode.workspace.createFileSystemWatcher) {
|
||||
// Skip in test environments or when required VSCode APIs are unavailable
|
||||
if (
|
||||
this.isTestEnvironment() ||
|
||||
!vscode.workspace?.createFileSystemWatcher ||
|
||||
!(vscode as any).Uri?.file ||
|
||||
!(vscode as any).RelativePattern
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue