squee
Version:
Super quick event emitter!
433 lines (341 loc) • 14.6 kB
text/typescript
import { EventEmitter } from "./index";
describe("EventEmitter", () => {
describe("on", () => {
it("triggers a listener when an event is fired after attaching the listener", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const listener = jasmine.createSpy();
emitter.on(eventName, listener);
// Act
emitter.emit(eventName);
// Assert
expect(listener).toHaveBeenCalled();
});
it("triggers a listener with args when an event is fired with args", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const listener = jasmine.createSpy();
const args = [{}, {}];
emitter.on(eventName, listener);
// Act
emitter.emit(eventName, ...args);
// Assert
expect(listener).toHaveBeenCalledWith(...args);
});
it("triggers multiple listeners when an event is fired after attaching the listeners", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const firstListener = jasmine.createSpy("first");
const secondListener = jasmine.createSpy("second");
const args = [{}, {}];
emitter.on(eventName, firstListener);
emitter.on(eventName, secondListener);
// Act
emitter.emit(eventName, ...args);
// Assert
expect(firstListener).toHaveBeenCalledWith(...args);
expect(secondListener).toHaveBeenCalledWith(...args);
});
it("triggers a listener twice when an event is fired after attaching the listener twice", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const listener = jasmine.createSpy();
emitter.on(eventName, listener);
emitter.on(eventName, listener);
// Act
emitter.emit(eventName);
// Assert
expect(listener).toHaveBeenCalledTimes(2);
});
it("doesn't trigger a listener when an event is fired before attaching the listener", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const listener = jasmine.createSpy();
// Act
emitter.emit(eventName);
// Assert
expect(listener).not.toHaveBeenCalled();
});
it("doesn't trigger a listener after the event is cleared with off", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const listener = jasmine.createSpy();
emitter.on(eventName, listener);
// Act
emitter.off(eventName, listener);
emitter.emit(eventName);
// Assert
expect(listener).not.toHaveBeenCalled();
});
});
describe("onAny", () => {
it("adds a listener called on events with the event name and args", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const args = [{}, {}];
const listener = jasmine.createSpy();
emitter.onAny(listener);
// Act
emitter.emit(eventName, ...args);
// Assert
expect(listener).toHaveBeenCalledWith(eventName, ...args);
});
});
describe("onFirst", () => {
it("triggers a listener when an event is first fired after attaching the listener", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const listener = jasmine.createSpy();
emitter.onFirst(eventName, listener);
// Act
emitter.emit(eventName);
// Assert
expect(listener).toHaveBeenCalled();
});
it("triggers multiple listeners when an event is first fired after attaching the listeners", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const firstListener = jasmine.createSpy("first");
const secondListener = jasmine.createSpy("second");
const args = [{}, {}];
emitter.onFirst(eventName, firstListener);
emitter.onFirst(eventName, secondListener);
// Act
emitter.emit(eventName, ...args);
// Assert
expect(firstListener).toHaveBeenCalledWith(...args);
expect(secondListener).toHaveBeenCalledWith(...args);
});
it("triggers a listener with the first args when an event was already fired and then fired after attaching the listener", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const listener = jasmine.createSpy();
const args = [{}, {}];
emitter.emit(eventName, ...args);
emitter.onFirst(eventName, listener);
// Act
emitter.emit(eventName, "bad");
// Assert
expect(listener).toHaveBeenCalledWith(...args);
});
it("doesn't trigger a listener after the event is cleared with off", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const listener = jasmine.createSpy();
emitter.onFirst(eventName, listener);
emitter.off(eventName, listener);
// Act
emitter.emit(eventName);
// Assert
expect(listener).not.toHaveBeenCalled();
});
it("uses the new first event when the event is cleared with off", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const listener = jasmine.createSpy();
const args = [{}, {}];
emitter.emit(eventName, "bad");
emitter.onFirst(eventName, listener);
emitter.off(eventName);
emitter.onFirst(eventName, listener);
// Act
emitter.emit(eventName, ...args);
// Assert
expect(listener).toHaveBeenCalledWith(...args);
});
it("uses the original first event when the listener is cleared with off then called again", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const listener = jasmine.createSpy();
const args = [{}, {}];
emitter.emit(eventName, ...args);
emitter.onFirst(eventName, listener);
emitter.off(eventName, listener);
emitter.onFirst(eventName, listener);
// Act
emitter.emit(eventName, "bad");
// Assert
expect(listener).toHaveBeenCalledWith(...args);
});
});
describe("off", () => {
it("clears event listener when the listener is registered multiple times", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const listener = jasmine.createSpy();
emitter.on(eventName, listener);
emitter.on(eventName, listener);
emitter.onFirst(eventName, listener);
emitter.onFirst(eventName, listener);
// Act
emitter.off(eventName, listener);
emitter.emit(eventName);
// Assert
expect(listener).not.toHaveBeenCalled();
});
it("clears just the passed event listener when passed an event name and listener", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const permanentListener = jasmine.createSpy();
const temporaryListener = jasmine.createSpy();
emitter.on(eventName, permanentListener);
emitter.on(eventName, temporaryListener);
// Act
emitter.off(eventName, temporaryListener);
emitter.emit(eventName);
// Assert
expect(temporaryListener).not.toHaveBeenCalled();
expect(permanentListener).toHaveBeenCalled();
});
it("clears all event listeners when passed just an event name", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const secondListener = jasmine.createSpy("second");
const firstListener = jasmine.createSpy("first");
emitter.on(eventName, secondListener);
emitter.on(eventName, firstListener);
// Act
emitter.off(eventName);
emitter.emit(eventName);
// Assert
expect(firstListener).not.toHaveBeenCalled();
expect(secondListener).not.toHaveBeenCalled();
});
it("clears all events and event listeners when passed nothing", () => {
// Arrange
const emitter = new EventEmitter();
const firstEventName = "first-event";
const secondEventName = "second-event";
const secondListener = jasmine.createSpy("second");
const firstListener = jasmine.createSpy("first");
emitter.on(firstEventName, secondListener);
emitter.on(secondEventName, firstListener);
// Act
emitter.off();
emitter.emit(firstEventName);
emitter.emit(secondEventName);
// Assert
expect(firstListener).not.toHaveBeenCalled();
expect(secondListener).not.toHaveBeenCalled();
});
it("throws an error when an unregistered listener is passed", () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
// Act
const action = (): void => {
emitter.off(eventName, jasmine.createSpy());
};
// Assert
expect(action).toThrow(`Tried to remove a non-existent listener for event name '${eventName}'.`);
});
});
describe("waitFor", () => {
it("resolves when the event is fired", async () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
// Act
const action = emitter.waitFor(eventName);
emitter.emit(eventName);
// Assert
await action;
});
it("resolves with the first passed arg when the event is fired with args", async () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const arg = {};
// Act
const action = emitter.waitFor(eventName);
emitter.emit(eventName, arg);
// Assert
expect(await action).toBe(arg);
});
it("resolves with the next fired event if attached after the event was already fired", async () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const arg = {};
emitter.emit("bad");
// Act
const action = emitter.waitFor(eventName);
emitter.emit(eventName, arg);
// Assert
expect(await action).toBe(arg);
});
});
describe("waitForFirst", () => {
it("resolves when an event is first fired after attaching the listener", async () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
// Act
const action = emitter.waitForFirst(eventName);
emitter.emit(eventName);
// Assert
await action;
});
it("resolves with the first args object when an event was already fired and then fired after attaching the listener", async () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
const arg = {};
// Act
emitter.emit("bad");
const action = emitter.waitForFirst(eventName);
emitter.emit(eventName, arg);
// Assert
expect(await action).toBe(arg);
});
it("doesn't (quickly) resolve when the event is fired before registration", async () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
let resolved = false;
emitter.emit(eventName);
// Act
// tslint:disable-next-line no-floating-promises
emitter.waitFor(eventName)
.then(() => {
resolved = true;
});
// Assert
await Promise.resolve();
expect(resolved).toBe(false);
});
it("doesn't (quickly) resolve after the event is cleared with off", async () => {
// Arrange
const emitter = new EventEmitter();
const eventName = "event-name";
let resolved = false;
// Act
// tslint:disable-next-line no-floating-promises
emitter.waitForFirst(eventName)
.then(() => {
resolved = true;
});
emitter.off(eventName);
emitter.emit(eventName);
// Assert
await Promise.resolve();
expect(resolved).toBe(false);
});
});
});