99 lines
2.9 KiB
TypeScript
99 lines
2.9 KiB
TypeScript
import { EventBus } from '@common/EventBus';
|
|
|
|
describe('EventBus', () => {
|
|
let bus: EventBus;
|
|
|
|
beforeEach(() => {
|
|
bus = new EventBus();
|
|
});
|
|
|
|
it('delivers emitted payload to on() subscribers', () => {
|
|
const spy = jest.fn();
|
|
bus.on<number>('hit', spy);
|
|
bus.emit('hit', 42);
|
|
expect(spy).toHaveBeenCalledWith(42);
|
|
});
|
|
|
|
it('ignores duplicate subscriptions of the same handler', () => {
|
|
const spy = jest.fn();
|
|
bus.on('hit', spy);
|
|
bus.on('hit', spy);
|
|
bus.emit('hit', 1);
|
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
expect(bus.listenerCount('hit')).toBe(1);
|
|
});
|
|
|
|
it('fires once() handlers exactly one time', () => {
|
|
const spy = jest.fn();
|
|
bus.once('hit', spy);
|
|
bus.emit('hit', 1);
|
|
bus.emit('hit', 2);
|
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
expect(bus.listenerCount('hit')).toBe(0);
|
|
});
|
|
|
|
it('off(event, fn) removes only that handler', () => {
|
|
const a = jest.fn();
|
|
const b = jest.fn();
|
|
bus.on('hit', a);
|
|
bus.on('hit', b);
|
|
bus.off('hit', a);
|
|
bus.emit('hit', 1);
|
|
expect(a).not.toHaveBeenCalled();
|
|
expect(b).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('off(event) clears all handlers of that event', () => {
|
|
const a = jest.fn();
|
|
const b = jest.fn();
|
|
bus.on('hit', a);
|
|
bus.on('hit', b);
|
|
bus.off('hit');
|
|
bus.emit('hit', 1);
|
|
expect(a).not.toHaveBeenCalled();
|
|
expect(b).not.toHaveBeenCalled();
|
|
expect(bus.listenerCount('hit')).toBe(0);
|
|
});
|
|
|
|
it('isolates exceptions — one bad listener does not break fan-out', () => {
|
|
const err = jest.fn();
|
|
bus.setErrorHook(err);
|
|
const good = jest.fn();
|
|
bus.on('hit', () => {
|
|
throw new Error('boom');
|
|
});
|
|
bus.on('hit', good);
|
|
bus.emit('hit', 1);
|
|
expect(good).toHaveBeenCalledTimes(1);
|
|
expect(err).toHaveBeenCalledTimes(1);
|
|
expect(err.mock.calls[0][0]).toBe('hit');
|
|
});
|
|
|
|
it('emit on an unknown event is a no-op', () => {
|
|
expect(() => bus.emit('ghost', 1)).not.toThrow();
|
|
});
|
|
|
|
it('clear() drops every handler of every event', () => {
|
|
bus.on('a', () => {});
|
|
bus.on('b', () => {});
|
|
bus.clear();
|
|
expect(bus.listenerCount('a')).toBe(0);
|
|
expect(bus.listenerCount('b')).toBe(0);
|
|
});
|
|
|
|
it('supports off for a non-existent handler without error', () => {
|
|
const spy = jest.fn();
|
|
bus.off('hit', spy);
|
|
expect(bus.listenerCount('hit')).toBe(0);
|
|
});
|
|
|
|
it('handles a once-handler unsubscribing itself during emit', () => {
|
|
const calls: number[] = [];
|
|
bus.once<number>('tick', (n) => calls.push(n));
|
|
bus.on<number>('tick', (n) => calls.push(n * 10));
|
|
bus.emit('tick', 1);
|
|
bus.emit('tick', 2);
|
|
expect(calls).toEqual([1, 10, 20]);
|
|
});
|
|
});
|