jan/web-app/src/services/__tests__/events.test.ts
2025-07-12 22:46:27 +07:00

247 lines
7.8 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest'
import { EventEmitter } from '../events'
describe('EventEmitter', () => {
let eventEmitter: EventEmitter
beforeEach(() => {
eventEmitter = new EventEmitter()
})
describe('constructor', () => {
it('should create an instance with empty handlers map', () => {
expect(eventEmitter).toBeInstanceOf(EventEmitter)
expect(eventEmitter['handlers']).toBeInstanceOf(Map)
expect(eventEmitter['handlers'].size).toBe(0)
})
})
describe('on method', () => {
it('should register a handler for a new event', () => {
const handler = vi.fn()
eventEmitter.on('test-event', handler)
expect(eventEmitter['handlers'].has('test-event')).toBe(true)
expect(eventEmitter['handlers'].get('test-event')).toContain(handler)
})
it('should add multiple handlers for the same event', () => {
const handler1 = vi.fn()
const handler2 = vi.fn()
eventEmitter.on('test-event', handler1)
eventEmitter.on('test-event', handler2)
const handlers = eventEmitter['handlers'].get('test-event')
expect(handlers).toHaveLength(2)
expect(handlers).toContain(handler1)
expect(handlers).toContain(handler2)
})
it('should handle multiple different events', () => {
const handler1 = vi.fn()
const handler2 = vi.fn()
eventEmitter.on('event1', handler1)
eventEmitter.on('event2', handler2)
expect(eventEmitter['handlers'].has('event1')).toBe(true)
expect(eventEmitter['handlers'].has('event2')).toBe(true)
expect(eventEmitter['handlers'].get('event1')).toContain(handler1)
expect(eventEmitter['handlers'].get('event2')).toContain(handler2)
})
it('should allow the same handler to be registered multiple times', () => {
const handler = vi.fn()
eventEmitter.on('test-event', handler)
eventEmitter.on('test-event', handler)
const handlers = eventEmitter['handlers'].get('test-event')
expect(handlers).toHaveLength(2)
expect(handlers![0]).toBe(handler)
expect(handlers![1]).toBe(handler)
})
})
describe('off method', () => {
it('should remove a handler from an existing event', () => {
const handler = vi.fn()
eventEmitter.on('test-event', handler)
expect(eventEmitter['handlers'].get('test-event')).toContain(handler)
eventEmitter.off('test-event', handler)
expect(eventEmitter['handlers'].get('test-event')).not.toContain(handler)
})
it('should do nothing when trying to remove handler from non-existent event', () => {
const handler = vi.fn()
// Should not throw an error
expect(() => {
eventEmitter.off('non-existent-event', handler)
}).not.toThrow()
})
it('should do nothing when trying to remove non-existent handler', () => {
const handler1 = vi.fn()
const handler2 = vi.fn()
eventEmitter.on('test-event', handler1)
// Should not throw an error
expect(() => {
eventEmitter.off('test-event', handler2)
}).not.toThrow()
// Original handler should still be there
expect(eventEmitter['handlers'].get('test-event')).toContain(handler1)
})
it('should remove only the first occurrence of a handler', () => {
const handler = vi.fn()
eventEmitter.on('test-event', handler)
eventEmitter.on('test-event', handler)
expect(eventEmitter['handlers'].get('test-event')).toHaveLength(2)
eventEmitter.off('test-event', handler)
expect(eventEmitter['handlers'].get('test-event')).toHaveLength(1)
expect(eventEmitter['handlers'].get('test-event')).toContain(handler)
})
it('should remove correct handler when multiple handlers exist', () => {
const handler1 = vi.fn()
const handler2 = vi.fn()
const handler3 = vi.fn()
eventEmitter.on('test-event', handler1)
eventEmitter.on('test-event', handler2)
eventEmitter.on('test-event', handler3)
eventEmitter.off('test-event', handler2)
const handlers = eventEmitter['handlers'].get('test-event')
expect(handlers).toHaveLength(2)
expect(handlers).toContain(handler1)
expect(handlers).not.toContain(handler2)
expect(handlers).toContain(handler3)
})
})
describe('emit method', () => {
it('should call all handlers for an event', () => {
const handler1 = vi.fn()
const handler2 = vi.fn()
eventEmitter.on('test-event', handler1)
eventEmitter.on('test-event', handler2)
eventEmitter.emit('test-event', 'test-data')
expect(handler1).toHaveBeenCalledWith('test-data')
expect(handler2).toHaveBeenCalledWith('test-data')
})
it('should do nothing when emitting non-existent event', () => {
// Should not throw an error
expect(() => {
eventEmitter.emit('non-existent-event', 'data')
}).not.toThrow()
})
it('should pass arguments to handlers', () => {
const handler = vi.fn()
const testData = { message: 'test', number: 42 }
eventEmitter.on('test-event', handler)
eventEmitter.emit('test-event', testData)
expect(handler).toHaveBeenCalledWith(testData)
})
it('should call handlers in the order they were added', () => {
const callOrder: number[] = []
const handler1 = vi.fn(() => callOrder.push(1))
const handler2 = vi.fn(() => callOrder.push(2))
const handler3 = vi.fn(() => callOrder.push(3))
eventEmitter.on('test-event', handler1)
eventEmitter.on('test-event', handler2)
eventEmitter.on('test-event', handler3)
eventEmitter.emit('test-event', null)
expect(callOrder).toEqual([1, 2, 3])
})
it('should handle null and undefined arguments', () => {
const handler = vi.fn()
eventEmitter.on('test-event', handler)
eventEmitter.emit('test-event', null)
expect(handler).toHaveBeenCalledWith(null)
eventEmitter.emit('test-event', undefined)
expect(handler).toHaveBeenCalledWith(undefined)
})
it('should not affect other events', () => {
const handler1 = vi.fn()
const handler2 = vi.fn()
eventEmitter.on('event1', handler1)
eventEmitter.on('event2', handler2)
eventEmitter.emit('event1', 'data1')
expect(handler1).toHaveBeenCalledWith('data1')
expect(handler2).not.toHaveBeenCalled()
})
})
describe('integration tests', () => {
it('should support complete event lifecycle', () => {
const handler1 = vi.fn()
const handler2 = vi.fn()
// Register handlers
eventEmitter.on('lifecycle-event', handler1)
eventEmitter.on('lifecycle-event', handler2)
// Emit event
eventEmitter.emit('lifecycle-event', 'test-data')
expect(handler1).toHaveBeenCalledWith('test-data')
expect(handler2).toHaveBeenCalledWith('test-data')
// Remove one handler
eventEmitter.off('lifecycle-event', handler1)
// Emit again
eventEmitter.emit('lifecycle-event', 'test-data-2')
expect(handler1).toHaveBeenCalledTimes(1) // Still only called once
expect(handler2).toHaveBeenCalledTimes(2) // Called twice
expect(handler2).toHaveBeenLastCalledWith('test-data-2')
})
it('should handle complex data types', () => {
const handler = vi.fn()
const complexData = {
array: [1, 2, 3],
object: { nested: true },
function: () => 'test',
symbol: Symbol('test'),
}
eventEmitter.on('complex-event', handler)
eventEmitter.emit('complex-event', complexData)
expect(handler).toHaveBeenCalledWith(complexData)
})
})
})