190 lines
5.9 KiB
TypeScript
190 lines
5.9 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
import { EventBus } from '../src/core/EventBus';
|
|
|
|
describe('EventBus', () => {
|
|
let consoleSpy: ReturnType<typeof vi.spyOn>;
|
|
|
|
beforeEach(() => {
|
|
consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe('on / emit', () => {
|
|
it('should register a handler and call it on emit', () => {
|
|
const bus = new EventBus();
|
|
const handler = vi.fn();
|
|
bus.on('test-event', handler);
|
|
bus.emit('test-event', 'data1', 'data2');
|
|
expect(handler).toHaveBeenCalledTimes(1);
|
|
expect(handler).toHaveBeenCalledWith('data1', 'data2');
|
|
});
|
|
|
|
it('should call multiple handlers on same event', () => {
|
|
const bus = new EventBus();
|
|
const handler1 = vi.fn();
|
|
const handler2 = vi.fn();
|
|
bus.on('test-event', handler1);
|
|
bus.on('test-event', handler2);
|
|
bus.emit('test-event');
|
|
expect(handler1).toHaveBeenCalledTimes(1);
|
|
expect(handler2).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should not call handlers for different events', () => {
|
|
const bus = new EventBus();
|
|
const handler = vi.fn();
|
|
bus.on('event-a', handler);
|
|
bus.emit('event-b');
|
|
expect(handler).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should handle emit on non-existent event', () => {
|
|
const bus = new EventBus();
|
|
expect(() => bus.emit('non-existent', 'data')).not.toThrow();
|
|
});
|
|
|
|
it('should pass multiple arguments to handler', () => {
|
|
const bus = new EventBus();
|
|
const handler = vi.fn();
|
|
bus.on('multi-arg', handler);
|
|
bus.emit('multi-arg', 1, 'two', { three: 3 }, [4, 5]);
|
|
expect(handler).toHaveBeenCalledWith(1, 'two', { three: 3 }, [4, 5]);
|
|
});
|
|
|
|
it('should not throw if a handler throws', () => {
|
|
const bus = new EventBus();
|
|
const handler1 = vi.fn(() => {
|
|
throw new Error('Handler error');
|
|
});
|
|
const handler2 = vi.fn();
|
|
bus.on('error-event', handler1);
|
|
bus.on('error-event', handler2);
|
|
expect(() => bus.emit('error-event')).not.toThrow();
|
|
expect(handler2).toHaveBeenCalledTimes(1);
|
|
expect(consoleSpy).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('off', () => {
|
|
it('should remove a registered handler', () => {
|
|
const bus = new EventBus();
|
|
const handler = vi.fn();
|
|
bus.on('test-event', handler);
|
|
bus.off('test-event', handler);
|
|
bus.emit('test-event');
|
|
expect(handler).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should not remove other handlers', () => {
|
|
const bus = new EventBus();
|
|
const handler1 = vi.fn();
|
|
const handler2 = vi.fn();
|
|
bus.on('test-event', handler1);
|
|
bus.on('test-event', handler2);
|
|
bus.off('test-event', handler1);
|
|
bus.emit('test-event');
|
|
expect(handler1).not.toHaveBeenCalled();
|
|
expect(handler2).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should handle off for non-existent event', () => {
|
|
const bus = new EventBus();
|
|
const handler = vi.fn();
|
|
expect(() => bus.off('non-existent', handler)).not.toThrow();
|
|
});
|
|
|
|
it('should handle off for non-existent handler', () => {
|
|
const bus = new EventBus();
|
|
const handler1 = vi.fn();
|
|
const handler2 = vi.fn();
|
|
bus.on('test-event', handler1);
|
|
bus.off('test-event', handler2);
|
|
bus.emit('test-event');
|
|
expect(handler1).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should only remove one instance if handler is registered multiple times', () => {
|
|
const bus = new EventBus();
|
|
const handler = vi.fn();
|
|
bus.on('test-event', handler);
|
|
bus.on('test-event', handler);
|
|
bus.off('test-event', handler);
|
|
bus.emit('test-event');
|
|
expect(handler).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('once', () => {
|
|
it('should call handler only once', () => {
|
|
const bus = new EventBus();
|
|
const handler = vi.fn();
|
|
bus.once('once-event', handler);
|
|
bus.emit('once-event');
|
|
bus.emit('once-event');
|
|
expect(handler).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should pass arguments to once handler', () => {
|
|
const bus = new EventBus();
|
|
const handler = vi.fn();
|
|
bus.once('once-event', handler);
|
|
bus.emit('once-event', 'arg1', 'arg2');
|
|
expect(handler).toHaveBeenCalledWith('arg1', 'arg2');
|
|
});
|
|
|
|
it('should work with on and once mixed', () => {
|
|
const bus = new EventBus();
|
|
const onHandler = vi.fn();
|
|
const onceHandler = vi.fn();
|
|
bus.on('mixed-event', onHandler);
|
|
bus.once('mixed-event', onceHandler);
|
|
bus.emit('mixed-event');
|
|
bus.emit('mixed-event');
|
|
expect(onHandler).toHaveBeenCalledTimes(2);
|
|
expect(onceHandler).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should not throw if once handler throws', () => {
|
|
const bus = new EventBus();
|
|
const handler = vi.fn(() => {
|
|
throw new Error('Once handler error');
|
|
});
|
|
bus.once('once-error', handler);
|
|
expect(() => bus.emit('once-error')).not.toThrow();
|
|
expect(consoleSpy).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('destroy', () => {
|
|
it('should clear all handlers', () => {
|
|
const bus = new EventBus();
|
|
const handler1 = vi.fn();
|
|
const handler2 = vi.fn();
|
|
bus.on('event-a', handler1);
|
|
bus.on('event-b', handler2);
|
|
bus.destroy();
|
|
bus.emit('event-a');
|
|
bus.emit('event-b');
|
|
expect(handler1).not.toHaveBeenCalled();
|
|
expect(handler2).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should handle destroy on empty bus', () => {
|
|
const bus = new EventBus();
|
|
expect(() => bus.destroy()).not.toThrow();
|
|
});
|
|
|
|
it('should work with once handlers after destroy', () => {
|
|
const bus = new EventBus();
|
|
const handler = vi.fn();
|
|
bus.once('event', handler);
|
|
bus.destroy();
|
|
bus.emit('event');
|
|
expect(handler).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|