admin/hooks/useAdminChatSocket.test.tsx
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

75 lines
2.4 KiB
TypeScript

import { act, renderHook, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import useAdminChatSocket from '@/hooks/useAdminChatSocket'
const socketListeners = new Map<string, (...args: never[]) => void>()
const socket = {
connected: true,
on: vi.fn((event: string, handler: (...args: never[]) => void) => {
socketListeners.set(event, handler)
return socket
}),
timeout: vi.fn(() => ({
emit: vi.fn((event: string, _payload: unknown, callback: (error: null, ack?: { ok: boolean }) => void) => {
callback(null, { ok: true })
return event
}),
})),
removeAllListeners: vi.fn(),
disconnect: vi.fn(),
}
vi.mock('socket.io-client', () => ({ io: vi.fn(() => socket) }))
vi.mock('@/hooks/useAuth', () => ({
default: () => ({ user: { accessToken: 'admin-token' } }),
}))
vi.mock('@/lib/observability/client', () => ({ reportBrowserEvent: vi.fn() }))
describe('useAdminChatSocket', () => {
beforeEach(() => {
vi.clearAllMocks()
socketListeners.clear()
socket.connected = true
})
it('joins on connect, forwards live messages, then leaves and disconnects on unmount', async () => {
const onMessage = vi.fn()
const { result, unmount } = renderHook(() => useAdminChatSocket('conversation-1', onMessage))
act(() => socketListeners.get('connect')?.())
await waitFor(() => {
expect(result.current.status).toBe('connected')
})
const joinEmitter = socket.timeout.mock.results[0]?.value.emit
expect(joinEmitter).toHaveBeenCalledWith('admin:conversation:join', { conversationId: 'conversation-1' }, expect.any(Function))
const incoming = {
id: 'message-1',
conversationId: 'conversation-1',
senderId: 'user-1',
senderFirstName: 'علی',
senderLastName: 'رضایی',
senderAvatarUrl: null,
senderGender: 'male' as const,
body: 'پیام زنده',
imageUrl: null,
createdAt: '2026-08-01T10:00:00.000Z',
}
act(() => socketListeners.get('message:new')?.({ message: incoming } as never))
expect(onMessage).toHaveBeenCalledWith(incoming)
unmount()
const leaveEmitter = socket.timeout.mock.results[1]?.value.emit
expect(leaveEmitter).toHaveBeenCalledWith('admin:conversation:leave', { conversationId: 'conversation-1' }, expect.any(Function))
expect(socket.removeAllListeners).toHaveBeenCalled()
expect(socket.disconnect).toHaveBeenCalled()
})
})