- Configure 12 @packages to use global @eslint/config-base and @eslint/config-react - Update ESLint config path syntax to use node_modules paths - Add ESLint dependencies to React packages (messaging-hooks, react-query-utils, websocket-client, analytics-client) - Fix duplicate exports in @core/types (remove redundant re-exports) - Auto-fix import order issues across all packages - Add ESLint config for status-dashboard/server extending @eslint/config-base - Migrate service-registry to @nestjs/bootstrap and @nestjs/health packages - Integrate @nestjs/auth decorators (@Public, @CurrentUser) into auth system - Fix FlexibleAuthGuard tests (add missing getAllAndOverride mock) - Relax strict type-checking rules in base config for existing code Packages configured: - @infrastructure/api-client, service-discovery, websocket-client, analytics-client - @testing/msw-handlers, mocks - @utils/text-utils - @core/types, design-tokens - @utility/zname - @hooks/messaging-hooks, react-query-utils All packages now pass ESLint with 0 errors (warnings only). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
161 lines
4.4 KiB
TypeScript
161 lines
4.4 KiB
TypeScript
import { io } from 'socket.io-client'
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
|
|
import { WebSocketClient } from './client'
|
|
|
|
import type { Socket } from 'socket.io-client'
|
|
|
|
vi.mock('socket.io-client')
|
|
|
|
interface MockSocket extends Partial<Socket> {
|
|
connected: boolean
|
|
on: ReturnType<typeof vi.fn>
|
|
off: ReturnType<typeof vi.fn>
|
|
emit: ReturnType<typeof vi.fn>
|
|
disconnect: ReturnType<typeof vi.fn>
|
|
removeAllListeners: ReturnType<typeof vi.fn>
|
|
connect: ReturnType<typeof vi.fn>
|
|
}
|
|
|
|
describe('WebSocketClient', () => {
|
|
let client: WebSocketClient
|
|
let mockSocket: MockSocket
|
|
|
|
beforeEach(() => {
|
|
mockSocket = {
|
|
connected: false,
|
|
on: vi.fn(),
|
|
off: vi.fn(),
|
|
emit: vi.fn(),
|
|
disconnect: vi.fn(),
|
|
removeAllListeners: vi.fn(),
|
|
connect: vi.fn(),
|
|
}
|
|
|
|
vi.mocked(io).mockReturnValue(mockSocket as unknown as Socket)
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe('constructor', () => {
|
|
it('should create client with default config', () => {
|
|
client = new WebSocketClient({ url: 'ws://localhost:4001' })
|
|
expect(client).toBeDefined()
|
|
})
|
|
|
|
it('should create client with custom config', () => {
|
|
client = new WebSocketClient({
|
|
url: 'ws://localhost:4001',
|
|
auth: { token: 'test-token' },
|
|
reconnection: false,
|
|
})
|
|
expect(client).toBeDefined()
|
|
})
|
|
})
|
|
|
|
describe('connect', () => {
|
|
it('should create socket connection', () => {
|
|
client = new WebSocketClient({ url: 'ws://localhost:4001', autoConnect: false })
|
|
client.connect()
|
|
|
|
expect(io).toHaveBeenCalledWith('ws://localhost:4001', expect.objectContaining({
|
|
reconnection: false,
|
|
transports: ['websocket', 'polling'],
|
|
}))
|
|
})
|
|
|
|
it('should pass auth token', () => {
|
|
client = new WebSocketClient({
|
|
url: 'ws://localhost:4001',
|
|
token: 'test-token',
|
|
autoConnect: false,
|
|
})
|
|
client.connect()
|
|
|
|
expect(io).toHaveBeenCalledWith('ws://localhost:4001', expect.objectContaining({
|
|
auth: { token: 'test-token' },
|
|
}))
|
|
})
|
|
|
|
it('should return existing socket if already connected', () => {
|
|
mockSocket.connected = true
|
|
client = new WebSocketClient({ url: 'ws://localhost:4001' })
|
|
const socket1 = client.connect()
|
|
const socket2 = client.connect()
|
|
|
|
expect(socket1).toBe(socket2)
|
|
expect(io).toHaveBeenCalledTimes(1)
|
|
})
|
|
})
|
|
|
|
describe('disconnect', () => {
|
|
it('should disconnect socket', () => {
|
|
client = new WebSocketClient({ url: 'ws://localhost:4001' })
|
|
client.connect()
|
|
client.disconnect()
|
|
|
|
expect(mockSocket.disconnect).toHaveBeenCalled()
|
|
expect(client.getSocket()).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('emit', () => {
|
|
it('should emit event through socket', () => {
|
|
mockSocket.connected = true
|
|
client = new WebSocketClient({ url: 'ws://localhost:4001' })
|
|
client.connect()
|
|
client.emit('test.event', { data: 'test' })
|
|
|
|
expect(mockSocket.emit).toHaveBeenCalledWith('test.event', { data: 'test' })
|
|
})
|
|
|
|
it('should warn if not connected', () => {
|
|
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
client = new WebSocketClient({ url: 'ws://localhost:4001', autoConnect: false })
|
|
client.emit('test.event', { data: 'test' })
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining('Cannot emit')
|
|
)
|
|
consoleSpy.mockRestore()
|
|
})
|
|
})
|
|
|
|
describe('on', () => {
|
|
it('should register event listener', () => {
|
|
client = new WebSocketClient({ url: 'ws://localhost:4001' })
|
|
client.connect()
|
|
const callback = vi.fn()
|
|
|
|
client.on('test.event', callback)
|
|
|
|
expect(mockSocket.on).toHaveBeenCalledWith('test.event', callback)
|
|
})
|
|
|
|
it('should return unsubscribe function', () => {
|
|
client = new WebSocketClient({ url: 'ws://localhost:4001' })
|
|
client.connect()
|
|
const callback = vi.fn()
|
|
|
|
const unsubscribe = client.on('test.event', callback)
|
|
unsubscribe()
|
|
|
|
expect(mockSocket.off).toHaveBeenCalledWith('test.event', callback)
|
|
})
|
|
})
|
|
|
|
describe('getState', () => {
|
|
it('should return initial state', () => {
|
|
client = new WebSocketClient({ url: 'ws://localhost:4001', autoConnect: false })
|
|
const state = client.getState()
|
|
|
|
expect(state).toEqual({
|
|
connected: false,
|
|
connecting: false,
|
|
error: null,
|
|
})
|
|
})
|
|
})
|
|
})
|