describe('events', () => {
let dispatcher;
beforeEach(() => {
const EventEmitter = require('../events').default;
dispatcher = new EventEmitter();
});
it('can dispatch an event with no listeners', () => {
dispatcher.emit('event', 123);
});
it('handles a listener being attached multiple times', () => {
const callback = jest.fn();
dispatcher.addListener('event', callback);
dispatcher.addListener('event', callback);
dispatcher.emit('event', 123);
expect(callback).toHaveBeenCalledTimes(1);
expect(callback).toHaveBeenCalledWith(123);
});
it('notifies all attached listeners of events', () => {
const callback1 = jest.fn();
const callback2 = jest.fn();
const callback3 = jest.fn();
dispatcher.addListener('event', callback1);
dispatcher.addListener('event', callback2);
dispatcher.addListener('other-event', callback3);
dispatcher.emit('event', 123);
expect(callback1).toHaveBeenCalledTimes(1);
expect(callback1).toHaveBeenCalledWith(123);
expect(callback2).toHaveBeenCalledTimes(1);
expect(callback2).toHaveBeenCalledWith(123);
expect(callback3).not.toHaveBeenCalled();
});
it('calls later listeners before re-throwing if an earlier one throws', () => {
const callbackThatThrows = jest.fn(() => {
throw Error('expected');
});
const callback = jest.fn();
dispatcher.addListener('event', callbackThatThrows);
dispatcher.addListener('event', callback);
expect(() => {
dispatcher.emit('event', 123);
}).toThrow('expected');
expect(callbackThatThrows).toHaveBeenCalledTimes(1);
expect(callbackThatThrows).toHaveBeenCalledWith(123);
expect(callback).toHaveBeenCalledTimes(1);
expect(callback).toHaveBeenCalledWith(123);
});
it('preserves the first thrown value and reports later errors', () => {
const laterError = new Error('later error');
const errorHandler = jest.fn(event => {
event.preventDefault();
});
const firstCallback = jest.fn(() => {
throw null;
});
const secondCallback = jest.fn(() => {
throw laterError;
});
const thirdCallback = jest.fn();
dispatcher.addListener('event', firstCallback);
dispatcher.addListener('event', secondCallback);
dispatcher.addListener('event', thirdCallback);
let caughtValue = undefined;
window.addEventListener('error', errorHandler);
try {
dispatcher.emit('event', 123);
} catch (error) {
caughtValue = error;
} finally {
window.removeEventListener('error', errorHandler);
}
expect(caughtValue).toBe(null);
expect(errorHandler).toHaveBeenCalledTimes(1);
expect(errorHandler.mock.calls[0][0]).toEqual(
expect.objectContaining({
error: laterError,
message: 'later error',
}),
);
expect(thirdCallback).toHaveBeenCalledWith(123);
});
it('removes attached listeners', () => {
const callback1 = jest.fn();
const callback2 = jest.fn();
dispatcher.addListener('event', callback1);
dispatcher.addListener('other-event', callback2);
dispatcher.removeListener('event', callback1);
dispatcher.emit('event', 123);
expect(callback1).not.toHaveBeenCalled();
dispatcher.emit('other-event', 123);
expect(callback2).toHaveBeenCalledTimes(1);
expect(callback2).toHaveBeenCalledWith(123);
});
it('removes all listeners', () => {
const callback1 = jest.fn();
const callback2 = jest.fn();
const callback3 = jest.fn();
dispatcher.addListener('event', callback1);
dispatcher.addListener('event', callback2);
dispatcher.addListener('other-event', callback3);
dispatcher.removeAllListeners();
dispatcher.emit('event', 123);
dispatcher.emit('other-event', 123);
expect(callback1).not.toHaveBeenCalled();
expect(callback2).not.toHaveBeenCalled();
expect(callback3).not.toHaveBeenCalled();
});
it('should call the initial listeners even if others are added or removed during a dispatch', () => {
const callback1 = jest.fn(() => {
dispatcher.removeListener('event', callback2);
dispatcher.addListener('event', callback3);
});
const callback2 = jest.fn();
const callback3 = jest.fn();
dispatcher.addListener('event', callback1);
dispatcher.addListener('event', callback2);
dispatcher.emit('event', 123);
expect(callback1).toHaveBeenCalledTimes(1);
expect(callback1).toHaveBeenCalledWith(123);
expect(callback2).toHaveBeenCalledTimes(1);
expect(callback2).toHaveBeenCalledWith(123);
expect(callback3).not.toHaveBeenCalled();
dispatcher.emit('event', 456);
expect(callback1).toHaveBeenCalledTimes(2);
expect(callback1).toHaveBeenCalledWith(456);
expect(callback2).toHaveBeenCalledTimes(1);
expect(callback3).toHaveBeenCalledTimes(1);
expect(callback3).toHaveBeenCalledWith(456);
});
});