Skip to content

Commit 0b347e4

Browse files
committed
test(desktop): check delivered events and real requests, not mock calls
1 parent 2d9cf45 commit 0b347e4

3 files changed

Lines changed: 42 additions & 46 deletions

File tree

‎apps/sim/hooks/queries/desktop-activity.test.tsx‎

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
/** @vitest-environment jsdom */
22

33
import { act } from 'react'
4-
import {
5-
apiClientRequestMock,
6-
apiClientRequestMockFns,
7-
} from '@sim/testing/mocks/api-client-request.mock'
4+
import { jsonResponse } from '@sim/testing/helpers/http'
5+
import { setupGlobalFetchMock } from '@sim/testing/mocks/fetch.mock'
86
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
97
import { createRoot, type Root } from 'react-dom/client'
108
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
119

12-
vi.mock('@/lib/api/client/request', () => apiClientRequestMock)
10+
/** The page's real requests land on this fetch, installed fresh for each test. */
11+
let fetchMock = setupGlobalFetchMock()
1312

14-
const requestJson = apiClientRequestMockFns.mockRequestJson
13+
/** Requests the page sent for desktop activity. */
14+
function activityRequests(): number {
15+
return fetchMock.mock.calls.filter(([input]) => String(input).includes('/api/desktop/activity'))
16+
.length
17+
}
1518

1619
import { useDesktopActivity, watchesDesktopActivity } from '@/hooks/queries/desktop-activity'
1720

@@ -53,8 +56,8 @@ describe('desktop activity polling', () => {
5356
beforeEach(() => {
5457
vi.useFakeTimers()
5558
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true)
56-
requestJson.mockReset()
57-
requestJson.mockResolvedValue({ chats: [] })
59+
fetchMock = setupGlobalFetchMock()
60+
fetchMock.mockImplementation(async () => jsonResponse({ chats: [] }))
5861
})
5962

6063
afterEach(() => {
@@ -66,8 +69,10 @@ describe('desktop activity polling', () => {
6669
})
6770

6871
it('shows nothing once the page stops watching, though the query kept its last result', async () => {
69-
const activity = [{ chatId: 'chat-1' }]
70-
requestJson.mockResolvedValueOnce({ chats: activity })
72+
const activity = [
73+
{ chatId: 'chat-1', streamId: 'stream-1', state: 'running', deviceName: 'Work laptop' },
74+
]
75+
fetchMock.mockImplementationOnce(async () => jsonResponse({ chats: activity }))
7176
const container = document.createElement('div')
7277
document.body.appendChild(container)
7378
root = createRoot(container)
@@ -95,21 +100,21 @@ describe('desktop activity polling', () => {
95100
await mount(false)
96101
await waitTwoMinutes()
97102

98-
expect(requestJson).not.toHaveBeenCalled()
103+
expect(activityRequests()).toBe(0)
99104
})
100105

101106
it('polls for a user with a registered desktop', async () => {
102107
await mount(true)
103108
await waitTwoMinutes()
104109

105-
expect(requestJson.mock.calls.length).toBeGreaterThanOrEqual(4)
110+
expect(activityRequests()).toBeGreaterThanOrEqual(4)
106111
})
107112

108113
it('polls in the desktop app before its first registration reaches the page', async () => {
109114
Object.defineProperty(window, 'simDesktop', { value: {}, configurable: true })
110115
await mount(false)
111116
await waitTwoMinutes()
112117

113-
expect(requestJson.mock.calls.length).toBeGreaterThanOrEqual(4)
118+
expect(activityRequests()).toBeGreaterThanOrEqual(4)
114119
})
115120
})

‎apps/sim/hooks/use-mothership-chat-events.test.ts‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -395,12 +395,6 @@ describe('reflectBackgroundChatStatus', () => {
395395
"leaves the desktop activity alone when a teammate's turn has %s",
396396
(type) => {
397397
showing('/workspace/ws-1/home', false)
398-
const fetchActivity = vi.fn(async () => [])
399-
const unsubscribe = new QueryObserver(queryClient, {
400-
queryKey: desktopActivityKeys.list('ws-1'),
401-
queryFn: fetchActivity,
402-
staleTime: Number.POSITIVE_INFINITY,
403-
}).subscribe(() => {})
404398

405399
reflectBackgroundChatStatus(
406400
queryClient,
@@ -410,8 +404,6 @@ describe('reflectBackgroundChatStatus', () => {
410404
)
411405

412406
expect(activityStale()).toBe(false)
413-
expect(fetchActivity).not.toHaveBeenCalled()
414-
unsubscribe()
415407
}
416408
)
417409

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,35 @@
1-
import { describe, expect, it, vi } from 'vitest'
1+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
2+
import {
3+
type ChatStatusEvent,
4+
chatPubSub,
5+
publishChatStatusChanged,
6+
} from '@/lib/mothership/chat-status'
27

3-
const { publish } = vi.hoisted(() => ({ publish: vi.fn() }))
4-
vi.mock('@/lib/events/pubsub', () => ({
5-
createPubSubChannel: () => ({ publish, subscribe: vi.fn(), dispose: vi.fn() }),
6-
}))
8+
/** Events as a subscriber receives them from the channel (process-local without Redis). */
9+
let received: ChatStatusEvent[] = []
10+
let unsubscribe: () => void = () => {}
711

8-
import { publishChatStatusChanged } from '@/lib/mothership/chat-status'
12+
beforeEach(() => {
13+
received = []
14+
unsubscribe = chatPubSub?.onStatusChanged((event) => received.push(event)) ?? (() => {})
15+
})
16+
17+
afterEach(() => unsubscribe())
918

1019
describe('chat status ownership', () => {
1120
it('names the chat owner on workspace events, so each member can tell their own chats', () => {
1221
publishChatStatusChanged(
1322
{ workspaceId: 'ws-1', userId: 'user-1' },
1423
{ chatId: 'chat-1', type: 'renamed' }
1524
)
16-
expect(publish).toHaveBeenCalledWith({
17-
workspaceId: 'ws-1',
18-
userId: 'user-1',
19-
chatId: 'chat-1',
20-
type: 'renamed',
21-
})
25+
expect(received).toEqual([
26+
{ workspaceId: 'ws-1', userId: 'user-1', chatId: 'chat-1', type: 'renamed' },
27+
])
2228
})
2329

2430
it('publishes a workspace event without an owner when the publisher does not know it', () => {
2531
publishChatStatusChanged({ workspaceId: 'ws-1' }, { chatId: 'chat-1', type: 'renamed' })
26-
expect(publish).toHaveBeenLastCalledWith({
27-
workspaceId: 'ws-1',
28-
chatId: 'chat-1',
29-
type: 'renamed',
30-
})
32+
expect(received).toEqual([{ workspaceId: 'ws-1', chatId: 'chat-1', type: 'renamed' }])
3133
})
3234

3335
it.each(['completed'] as const)(
@@ -37,20 +39,17 @@ describe('chat status ownership', () => {
3739
{ organizationId: 'org-1', userId: 'user-1' },
3840
{ chatId: 'chat-1', type }
3941
)
40-
expect(publish).toHaveBeenCalledWith({
41-
organizationId: 'org-1',
42-
userId: 'user-1',
43-
chatId: 'chat-1',
44-
type,
45-
})
42+
expect(received).toEqual([
43+
{ organizationId: 'org-1', userId: 'user-1', chatId: 'chat-1', type },
44+
])
4645
}
4746
)
4847

4948
it('does not broadcast an organization event without its private owner', () => {
5049
expect(() =>
5150
publishChatStatusChanged({ organizationId: 'org-1' }, { chatId: 'chat-1', type: 'created' })
5251
).toThrow('Invalid organization chat owner')
53-
expect(publish).not.toHaveBeenCalled()
52+
expect(received).toEqual([])
5453
})
5554

5655
it('refuses ambiguous workspace and organization ownership', () => {
@@ -60,6 +59,6 @@ describe('chat status ownership', () => {
6059
{ chatId: 'chat-1', type: 'created' }
6160
)
6261
).toThrow('Invalid organization chat owner')
63-
expect(publish).not.toHaveBeenCalled()
62+
expect(received).toEqual([])
6463
})
6564
})

0 commit comments

Comments
 (0)