import * as mitt from 'mitt'; import { EVENT_BUS_TOKEN, EventBus, typedEventBusToken } from '../event-bus'; jest.mock('mitt', () => jest.fn()); const allowListedEvents = [ ':widgets:message', '[EH] Module Link Clicked', '[EH] Module Loaded', '[internal] reset', 'blink', 'CLOSE_COMPONENT', 'CLOSE_JOB_EDIT_FLYOUT', 'company-profile-request-save', 'company-profile-saved-error', 'company-profile-saved', 'dismiss-discrepancy-event', 'dismiss-discrepancy-map-event', 'dispatch-JobEditFlyout-fetchData', 'DRAWER_CREATED_CUSTOMER', 'employees-emails-processed', 'employees-request-save', 'employees-saved-error', 'employees-saved', 'ium-ep-cancel-changes', 'ium-ep-form-can-be-submitted-change', 'ium-ep-save-changes', 'loaded', 'MESSAGE_TECHNICIAN', 'my-custom-event', 'NEW_EVENT', 'NonJobEventSelectedEvent', 'ON_WIDGET_INIT', 'OPEN_ASSIST_SCREEN', 'OPEN_CREATE_CUSTOMER_DRAWER', 'OPEN_DC_SIDE_PANEL_COMPONENT', 'OPEN_JOB_EDIT_FLYOUT', 'OPEN_RESCHEDULE_FLYOUT', 'open-troubleshooter', 'resolve-discrepancy-map-event', 'restore-discrepancy-event', 'setLabel', 'SHOW_POP_UP_MESSAGE', 'TEAMS_SELECTED', 'technicians-emails-processed', 'technicians-request-save', 'technicians-saved-error', 'technicians-saved', 'timesheet-activity-saved', 'TOGGLE_DC_SIDE_PANEL_COMPONENT', 'updateUserEmail', 'VIEW_NON_JOB_EVENT', 'visibility', ]; describe(`[web-components] ${EventBus.name}`, () => { let eventBus: EventBus; let event: string; const handler = jest.fn(); const mittOn = jest.fn(); const mittOff = jest.fn(); const mittEmit = jest.fn(); beforeEach(() => { jest.spyOn(mitt, 'default').mockImplementation((): any => ({ on: mittOn, off: mittOff, emit: mittEmit, })); eventBus = new EventBus(); event = 'example:event'; }); function withInvalidEvent(test: () => void) { describe('with invalid event', () => { beforeEach(() => (event = 'foo')); test(); }); } function itRejectsInvalidEvent(subject: () => void) { withInvalidEvent(() => { test('throws error', () => { expect(subject).toThrow(); }); }); } describe('on', () => { const subject = (testEvent?: string | symbol) => eventBus.on(testEvent ?? event, handler); test("calls the private mitt instance's 'on' method", () => { subject(); expect(mittOn).toHaveBeenCalledWith(event, handler); }); itRejectsInvalidEvent(subject); test.each([ '', ':', ':name', 'module:', Symbol(), Symbol.for(':'), Symbol.for(':name'), Symbol.for('module:'), ])('rejects "%s"', disallowed => { expect(() => subject(disallowed)).toThrow(); }); test.each(allowListedEvents)('allows "%s" as string or Symbol', allowed => { expect(() => subject(allowed)).not.toThrow(); expect(() => subject(Symbol.for(allowed))).not.toThrow(); }); withInvalidEvent(() => { describe('when in production environment', () => { const nodeEnv = process.env.NODE_ENV; beforeEach(() => (process.env.NODE_ENV = 'production')); afterEach(() => (process.env.NODE_ENV = nodeEnv)); test('accepts event', () => { expect(subject).not.toThrow(); }); }); }); }); describe('off', () => { const subject = () => eventBus.off(event, handler); test("calls the private mitt instance's 'off' method", () => { subject(); expect(mittOff).toHaveBeenCalledWith(event, handler); }); }); describe('emit', () => { const data = { foo: 'bar' }; const subject = () => eventBus.emit(event, data); test("calls the private mitt instance's 'emit' method", () => { subject(); expect(mittEmit).toHaveBeenCalledWith(event, data); }); itRejectsInvalidEvent(subject); }); describe('job.on', () => { const subject = () => eventBus.job.on(event, handler); test("calls the private mitt instance's 'on' method", () => { subject(); expect(mittOn).toHaveBeenCalledWith(event, handler); }); itRejectsInvalidEvent(subject); }); describe('job.off', () => { const subject = () => eventBus.job.off(event, handler); test("calls the private mitt instance's 'off' method", () => { subject(); expect(mittOff).toHaveBeenCalledWith(event, handler); }); }); describe('job.emit', () => { const subject = () => eventBus.job.emit(event); describe("when emit's 'emit' resolves", () => { beforeEach(() => { mittEmit.mockImplementation((_: any, obj: any) => { return obj.resolve(); }); }); test('the promise resolves', async () => { await expect(subject()).resolves.toBeUndefined(); }); }); describe("when emit's 'emit' rejects", () => { const setup = (reason?: any) => mittEmit.mockImplementation((_: any, obj: any) => { return obj.reject(reason); }); describe('with a string reason', () => { const reason = 'reason'; beforeEach(() => { setup(reason); }); test('the promise rejects with the reason', async () => { await expect(subject()).rejects.toBe(reason); }); }); describe('with an error', () => { const error = Error('reason'); beforeEach(() => { setup(error); }); test('the promise rejects with the error', async () => { await expect(subject()).rejects.toBe(error); }); }); describe('with no argument', () => { beforeEach(() => { setup(); }); test('the promise rejects', async () => { await expect(subject()).rejects.toBeUndefined(); }); }); }); itRejectsInvalidEvent(subject); }); }); describe(`[web-components] ${typedEventBusToken.name}`, () => { const subject = () => typedEventBusToken(); test('returns EVENT_BUS_TOKEN', () => { expect(subject()).toBe(EVENT_BUS_TOKEN); }); });