247 lines
7.8 KiB
TypeScript
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)
|
|
})
|
|
})
|
|
}) |