Skip to content

Commit 5993924

Browse files
committed
Replace as any casts with typed mocks in more test files
Test files updated (47 as any casts removed): - sdk-event-handlers.test.ts: 6 -> 0 casts - message-updater.test.ts: 6 -> 0 casts - read-subtree.test.ts: 6 -> 0 casts - openrouter-ai-sdk/chat/index.test.ts: 6 -> 0 casts - usage-refresh-on-completion.test.ts: 6 -> 0 casts - run-terminal-command.test.ts: 5 -> 0 casts - agent-mode-toggle.test.ts: 5 -> 0 casts - steps.test.ts: 5 -> 0 casts Used typed interfaces (SpawnAgentInfo, SubagentStartEvent, ToolResultEvent, ReadSubtreeResultEntry, MockDb, TestMessageMetadata, RenderContentElement) and proper type assertions instead of unsafe any casts.
1 parent bd20540 commit 5993924

File tree

8 files changed

+190
-116
lines changed

8 files changed

+190
-116
lines changed

cli/src/__tests__/integration/usage-refresh-on-completion.test.ts

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
import { QueryClient } from '@tanstack/react-query'
2-
import {
3-
describe,
4-
test,
5-
expect,
6-
beforeEach,
7-
afterEach,
8-
mock,
9-
spyOn,
10-
} from 'bun:test'
2+
import { describe, test, expect, beforeEach, afterEach, mock, spyOn } from 'bun:test'
113

124
import { usageQueryKeys } from '../../hooks/use-usage-query'
135
import { useChatStore } from '../../state/chat-store'
@@ -80,10 +72,7 @@ describe('Usage Refresh on SDK Completion', () => {
8072
expect(useChatStore.getState().inputMode).toBe('usage')
8173

8274
// Spy on invalidateQueries
83-
const invalidateSpy = mock(
84-
queryClient.invalidateQueries.bind(queryClient),
85-
)
86-
queryClient.invalidateQueries = invalidateSpy as any
75+
const invalidateSpy = spyOn(queryClient, 'invalidateQueries')
8776

8877
// Simulate SDK run completion triggering invalidation
8978
const isUsageMode = useChatStore.getState().inputMode === 'usage'
@@ -101,10 +90,7 @@ describe('Usage Refresh on SDK Completion', () => {
10190
test('should invalidate multiple times for sequential runs', () => {
10291
useChatStore.getState().setInputMode('usage')
10392

104-
const invalidateSpy = mock(
105-
queryClient.invalidateQueries.bind(queryClient),
106-
)
107-
queryClient.invalidateQueries = invalidateSpy as any
93+
const invalidateSpy = spyOn(queryClient, 'invalidateQueries')
10894

10995
// Simulate three sequential SDK runs
11096
for (let i = 0; i < 3; i++) {
@@ -123,10 +109,7 @@ describe('Usage Refresh on SDK Completion', () => {
123109
useChatStore.getState().setInputMode('default')
124110
expect(useChatStore.getState().inputMode).toBe('default')
125111

126-
const invalidateSpy = mock(
127-
queryClient.invalidateQueries.bind(queryClient),
128-
)
129-
queryClient.invalidateQueries = invalidateSpy as any
112+
const invalidateSpy = spyOn(queryClient, 'invalidateQueries')
130113

131114
// Simulate SDK run completion check
132115
const isUsageMode = useChatStore.getState().inputMode === 'usage'
@@ -145,10 +128,7 @@ describe('Usage Refresh on SDK Completion', () => {
145128
// User closes banner before run completes
146129
useChatStore.getState().setInputMode('default')
147130

148-
const invalidateSpy = mock(
149-
queryClient.invalidateQueries.bind(queryClient),
150-
)
151-
queryClient.invalidateQueries = invalidateSpy as any
131+
const invalidateSpy = spyOn(queryClient, 'invalidateQueries')
152132

153133
// Simulate run completion
154134
const isUsageMode = useChatStore.getState().inputMode === 'usage'
@@ -165,13 +145,12 @@ describe('Usage Refresh on SDK Completion', () => {
165145
// Even if banner is visible in store, query won't run if enabled=false
166146
useChatStore.getState().setInputMode('usage')
167147

168-
const fetchMock = mock(globalThis.fetch)
169-
globalThis.fetch = fetchMock as any
148+
const fetchSpy = spyOn(globalThis, 'fetch')
170149

171150
// Query with enabled=false won't execute
172151
// (This would be the behavior when useUsageQuery({ enabled: false }) is called)
173152

174-
expect(fetchMock).not.toHaveBeenCalled()
153+
expect(fetchSpy).not.toHaveBeenCalled()
175154
})
176155
})
177156

@@ -180,11 +159,10 @@ describe('Usage Refresh on SDK Completion', () => {
180159
getAuthTokenSpy.mockReturnValue(undefined)
181160
useChatStore.getState().setInputMode('usage')
182161

183-
const fetchMock = mock(globalThis.fetch)
184-
globalThis.fetch = fetchMock as any
162+
const fetchSpy = spyOn(globalThis, 'fetch')
185163

186164
// Query won't execute without auth token
187-
expect(fetchMock).not.toHaveBeenCalled()
165+
expect(fetchSpy).not.toHaveBeenCalled()
188166
})
189167
})
190168
})

cli/src/__tests__/unit/agent-mode-toggle.test.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,18 @@ describe('AgentModeToggle - resolveAgentModeClick', () => {
5050
})
5151
})
5252

53+
// Extended Date.now type with test helper method
54+
interface MockDateNow {
55+
(): number
56+
set: (v: number) => void
57+
}
58+
5359
describe('useHoverToggle timing (controller)', () => {
5460
let originalSetTimeout: typeof setTimeout
5561
let originalClearTimeout: typeof clearTimeout
5662
let originalNow: typeof Date.now
5763

58-
let timers: { id: number; ms: number; fn: Function; active: boolean }[]
64+
let timers: { id: number; ms: number; fn: () => void; active: boolean }[]
5965
let nextId: number
6066

6167
const runAll = () => {
@@ -73,21 +79,22 @@ describe('useHoverToggle timing (controller)', () => {
7379
originalNow = Date.now
7480

7581
let now = 1_000
76-
Date.now = () => now
77-
;(Date.now as any).set = (v: number) => {
78-
now = v
79-
}
82+
const mockDateNow: MockDateNow = Object.assign(
83+
() => now,
84+
{ set: (v: number) => { now = v } }
85+
)
86+
Date.now = mockDateNow
8087

81-
globalThis.setTimeout = ((fn: Function, ms?: number) => {
88+
globalThis.setTimeout = ((fn: () => void, ms?: number) => {
8289
const id = nextId++
8390
timers.push({ id, ms: Number(ms ?? 0), fn, active: true })
84-
return id as any
85-
}) as any
91+
return id as unknown as ReturnType<typeof setTimeout>
92+
}) as typeof setTimeout
8693

87-
globalThis.clearTimeout = ((id?: any) => {
88-
const rec = timers.find((t) => t.id === id)
94+
globalThis.clearTimeout = ((id?: ReturnType<typeof clearTimeout>) => {
95+
const rec = timers.find((t) => t.id === (id as unknown as number))
8996
if (rec) rec.active = false
90-
}) as any
97+
}) as typeof clearTimeout
9198
})
9299

93100
afterEach(() => {
@@ -122,7 +129,7 @@ describe('useHoverToggle timing (controller)', () => {
122129
ctl.closeNow(true)
123130
ctl.scheduleOpen()
124131
expect(timers.length).toBe(0)
125-
;(Date.now as any).set(1_000 + REOPEN_SUPPRESS_MS + 1)
132+
;(Date.now as MockDateNow).set(1_000 + REOPEN_SUPPRESS_MS + 1)
126133
ctl.scheduleOpen()
127134
expect(timers.length).toBe(1)
128135
expect(timers[0].ms).toBe(OPEN_DELAY_MS)

cli/src/components/tools/__tests__/run-terminal-command.test.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@ import { describe, expect, test } from 'bun:test'
33
import { parseTerminalOutput, RunTerminalCommandComponent } from '../run-terminal-command'
44

55
import type { ToolBlock } from '../types'
6+
import type { ReactElement } from 'react'
7+
8+
// Use ChatTheme import for proper typing
9+
import type { ChatTheme } from '../../../types/theme-system'
10+
11+
// Type for the render result content element
12+
interface RenderContentElement extends ReactElement {
13+
props: {
14+
timeoutSeconds?: number
15+
}
16+
}
617

718
// Helper to create a mock tool block
819
const createToolBlock = (
@@ -36,7 +47,7 @@ describe('RunTerminalCommandComponent', () => {
3647
describe('render', () => {
3748
test('returns content and collapsedPreview', () => {
3849
const toolBlock = createToolBlock('ls -la', createJsonOutput('file1\nfile2'))
39-
const mockTheme = {} as any
50+
const mockTheme = {} as ChatTheme
4051
const mockOptions = {
4152
availableWidth: 80,
4253
indentationOffset: 0,
@@ -146,7 +157,7 @@ describe('RunTerminalCommandComponent', () => {
146157
})
147158

148159
describe('timeout extraction', () => {
149-
const mockTheme = {} as any
160+
const mockTheme = {} as ChatTheme
150161
const mockOptions = {
151162
availableWidth: 80,
152163
indentationOffset: 0,
@@ -158,23 +169,23 @@ describe('RunTerminalCommandComponent', () => {
158169

159170
const result = RunTerminalCommandComponent.render(toolBlock, mockTheme, mockOptions)
160171

161-
expect((result.content as any).props.timeoutSeconds).toBeUndefined()
172+
expect((result.content as RenderContentElement).props.timeoutSeconds).toBeUndefined()
162173
})
163174

164175
test('passes timeoutSeconds for positive timeout', () => {
165176
const toolBlock = createToolBlock('npm test', createJsonOutput('tests passed'), 60)
166177

167178
const result = RunTerminalCommandComponent.render(toolBlock, mockTheme, mockOptions)
168179

169-
expect((result.content as any).props.timeoutSeconds).toBe(60)
180+
expect((result.content as RenderContentElement).props.timeoutSeconds).toBe(60)
170181
})
171182

172183
test('passes timeoutSeconds for no timeout (-1)', () => {
173184
const toolBlock = createToolBlock('long-running-task', createJsonOutput('done'), -1)
174185

175186
const result = RunTerminalCommandComponent.render(toolBlock, mockTheme, mockOptions)
176187

177-
expect((result.content as any).props.timeoutSeconds).toBe(-1)
188+
expect((result.content as RenderContentElement).props.timeoutSeconds).toBe(-1)
178189
})
179190
})
180191

cli/src/utils/__tests__/message-updater.test.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ import {
66
DEFAULT_FLUSH_INTERVAL_MS,
77
} from '../message-updater'
88

9-
import type { ChatMessage, ContentBlock } from '../../types/chat'
9+
import type { ChatMessage, ContentBlock, TextContentBlock } from '../../types/chat'
10+
11+
// Type for metadata with runState for testing
12+
interface TestMessageMetadata {
13+
bashCwd?: string
14+
runState?: { id: string }
15+
}
1016

1117
const baseMessages: ChatMessage[] = [
1218
{
@@ -50,7 +56,7 @@ describe('createMessageUpdater', () => {
5056

5157
expect(state[0].blocks?.[0]).toEqual(block)
5258
expect(state[0].isComplete).toBe(true)
53-
expect((state[0].metadata as any).runState).toEqual({ id: 'run-1' })
59+
expect((state[0].metadata as TestMessageMetadata).runState).toEqual({ id: 'run-1' })
5460
})
5561

5662
test('setError preserves content and blocks, sets userError, and marks complete', () => {
@@ -75,7 +81,7 @@ describe('createMessageUpdater', () => {
7581
expect(state[0].userError).toBe('boom')
7682
expect(state[0].isComplete).toBe(true)
7783
expect(state[0].blocks).toHaveLength(1)
78-
expect((state[0].blocks![0] as any).content).toBe('existing block')
84+
expect((state[0].blocks![0] as TextContentBlock).content).toBe('existing block')
7985
})
8086

8187
test('clearUserError removes userError field from message', () => {
@@ -175,8 +181,8 @@ describe('createBatchedMessageUpdater', () => {
175181
expect(setMessagesCallCount).toBe(1)
176182
expect(state[0].content).toBe('first')
177183
expect(state[0].blocks).toHaveLength(2)
178-
expect((state[0].blocks![0] as any).content).toBe('block1')
179-
expect((state[0].blocks![1] as any).content).toBe('block2')
184+
expect((state[0].blocks![0] as TextContentBlock).content).toBe('block1')
185+
expect((state[0].blocks![1] as TextContentBlock).content).toBe('block2')
180186

181187
updater.dispose()
182188
})
@@ -241,8 +247,8 @@ describe('createBatchedMessageUpdater', () => {
241247
expect(state[0].isComplete).toBe(true)
242248
// Existing blocks are preserved and pending block was flushed
243249
expect(state[0].blocks).toHaveLength(2)
244-
expect((state[0].blocks![0] as any).content).toBe('existing block')
245-
expect((state[0].blocks![1] as any).content).toBe('pending block')
250+
expect((state[0].blocks![0] as TextContentBlock).content).toBe('existing block')
251+
expect((state[0].blocks![1] as TextContentBlock).content).toBe('pending block')
246252
})
247253

248254
test('updates after dispose are applied immediately', () => {
@@ -358,7 +364,7 @@ describe('createBatchedMessageUpdater', () => {
358364

359365
// Both existing and new metadata should be present
360366
expect(state[0].metadata?.bashCwd).toBe('/existing/path')
361-
expect(state[0].metadata?.runState).toEqual({ id: 'run-123' })
367+
expect((state[0].metadata as TestMessageMetadata)?.runState).toEqual({ id: 'run-123' })
362368
expect(state[0].isComplete).toBe(true)
363369
})
364370

0 commit comments

Comments
 (0)