Skip to content

Commit 7af7b83

Browse files
committed
test(queries): unmount rendered roots between tests
renderHookWithClient created a React root per test but never tore it down, so trees stayed mounted with live QueryClient observers until worker teardown and async notifications could cross test boundaries. Audited every test in the repo using createRoot: 51 of 53 already unmount. The two that did not were both mine — voice.test.tsx here and chats.test.tsx from #6223 — so both are fixed and the pattern is now uniform.
1 parent 4eda6c0 commit 7af7b83

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

apps/sim/hooks/queries/chats.test.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { act, type ReactNode } from 'react'
55
import { sleep } from '@sim/utils/helpers'
66
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
77
import { createRoot, type Root } from 'react-dom/client'
8-
import { beforeEach, describe, expect, it, vi } from 'vitest'
8+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
99

1010
const { mockRequestJson, mockInvalidateDeploymentQueries } = vi.hoisted(() => ({
1111
mockRequestJson: vi.fn(),
@@ -23,11 +23,15 @@ vi.mock('@/hooks/queries/deployments', async (importOriginal) => ({
2323

2424
import { useCreateChat, useUpdateChat } from '@/hooks/queries/chats'
2525

26+
/** Trees rendered by a test, torn down in afterEach so observers do not leak across tests. */
27+
const mountedRoots: Root[] = []
28+
2629
function renderHookWithClient<T>(useHook: () => T): { getResult: () => T } {
2730
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
2831
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
2932
const container = document.createElement('div')
3033
const root: Root = createRoot(container)
34+
mountedRoots.push(root)
3135
let result: T | undefined
3236

3337
function Probe() {
@@ -71,6 +75,12 @@ const FORM_DATA = {
7175
includeToolCalls: false,
7276
}
7377

78+
afterEach(() => {
79+
act(() => {
80+
for (const root of mountedRoots.splice(0)) root.unmount()
81+
})
82+
})
83+
7484
beforeEach(() => {
7585
vi.clearAllMocks()
7686
mockRequestJson.mockResolvedValue({ chatUrl: 'https://sim.ai/chat/my-chat', chatId: 'chat-1' })

apps/sim/hooks/queries/voice.test.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,23 @@ import { act, type ReactNode } from 'react'
55
import { sleep } from '@sim/utils/helpers'
66
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
77
import { createRoot, type Root } from 'react-dom/client'
8-
import { beforeEach, describe, expect, it, vi } from 'vitest'
8+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
99

1010
const { mockRequestJson } = vi.hoisted(() => ({ mockRequestJson: vi.fn() }))
1111

1212
vi.mock('@/lib/api/client/request', () => ({ requestJson: mockRequestJson }))
1313

1414
import { useVoiceSettings, voiceSettingsKeys } from '@/hooks/queries/voice'
1515

16+
/** Trees rendered by a test, torn down in afterEach so observers do not leak across tests. */
17+
const mountedRoots: Root[] = []
18+
1619
function renderHookWithClient<T>(useHook: () => T): { getResult: () => T } {
1720
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
1821
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
1922
const container = document.createElement('div')
2023
const root: Root = createRoot(container)
24+
mountedRoots.push(root)
2125
let result: T | undefined
2226

2327
function Probe() {
@@ -48,6 +52,12 @@ async function flush() {
4852
})
4953
}
5054

55+
afterEach(() => {
56+
act(() => {
57+
for (const root of mountedRoots.splice(0)) root.unmount()
58+
})
59+
})
60+
5161
beforeEach(() => {
5262
vi.clearAllMocks()
5363
})

0 commit comments

Comments
 (0)