aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/base-pubsub.spec.ts183
-rw-r--r--src/base-pubsub.ts126
-rw-r--r--src/plugin-pubsub.ts29
-rw-r--r--src/plugin-pubusb.spec.ts53
4 files changed, 391 insertions, 0 deletions
diff --git a/src/base-pubsub.spec.ts b/src/base-pubsub.spec.ts
new file mode 100644
index 0000000..5737b18
--- /dev/null
+++ b/src/base-pubsub.spec.ts
@@ -0,0 +1,183 @@
+declare const window: Window;
+
+import {BasePubSub} from './base-pubsub';
+
+describe('BasePubSub Tests', () => {
+ let basePubSub: BasePubSub;
+
+ let testSub: string = 'testSub';
+ let testWindow = window;
+ let testSubUrl: string = 'http://127.0.0.1';
+
+ beforeEach(() => {
+ basePubSub = new BasePubSub('testId');
+ });
+
+ describe('constructor tests', () => {
+ it('should init class property', () => {
+ expect(basePubSub.subscribers.size).toBe(0);
+ expect(basePubSub.eventsCallbacks.length).toBe(0);
+ expect(basePubSub.eventsToWait.size).toBe(0);
+ expect(basePubSub.clientId).toBe('testId');
+ expect(basePubSub.lastEventNotified).toBe('');
+ });
+ });
+
+ describe('register function tests', () => {
+
+ it('Should add new subscriber with the sent url to subscribers array ' +
+ 'when calling register function with url', () => {
+ basePubSub.register(testSub, testWindow, testSubUrl);
+
+ let actualSub = basePubSub.subscribers.get(testSub);
+
+ expect(basePubSub.subscribers.size).toBe(1);
+ expect(actualSub.window).toBe(testWindow);
+ expect(actualSub.locationUrl).toBe(testSubUrl);
+ });
+
+ it('Should add new subscriber with the window location.href to subscribers array ' +
+ 'when calling register function without url', () => {
+ basePubSub.register(testSub, testWindow, undefined);
+
+ let actualSub = basePubSub.subscribers.get(testSub);
+
+ expect(basePubSub.subscribers.size).toBe(1);
+ expect(actualSub.window).toBe(testWindow);
+ expect(actualSub.locationUrl).toBe(window.location.href);
+ });
+ });
+
+ describe('unregister function tests', () => {
+
+ it('Should remove subscriber from subscribers list', () => {
+ basePubSub.register(testSub, testWindow, testSubUrl);
+
+ expect(basePubSub.subscribers.size).toBe(1);
+
+ basePubSub.unregister(testSub);
+
+ expect(basePubSub.subscribers.size).toBe(0);
+ });
+ });
+
+ describe('on function tests', () => {
+ let callback = () => {return true};
+
+ it('Should add new callback to events callback array', () => {
+ basePubSub.on(callback);
+
+ expect(basePubSub.eventsCallbacks.length).toBe(1);
+
+ let actualCallback = basePubSub.eventsCallbacks[0];
+
+ expect(actualCallback).toBe(callback);
+ });
+
+ it('Should not add callback to events callback array if it already exists', () => {
+ basePubSub.on(callback);
+
+ expect(basePubSub.eventsCallbacks.length).toBe(1);
+
+ basePubSub.on(callback);
+
+ expect(basePubSub.eventsCallbacks.length).toBe(1);
+ });
+ });
+
+ describe('off function tests', () => {
+ let callback = () => {return true};
+
+ it('Should remove callback from events callback array', () => {
+ basePubSub.on(callback);
+
+ expect(basePubSub.eventsCallbacks.length).toBe(1);
+
+ basePubSub.off(callback);
+
+ expect(basePubSub.eventsCallbacks.length).toBe(0);
+ });
+ });
+
+ describe('isWaitingForEvent function tests', () => {
+ let eventsMap = new Map<string, Array<string>>();
+ eventsMap.set('eventsKey', ['WINDOW_OUT']);
+
+ beforeEach(() => {
+ basePubSub.eventsToWait = eventsMap;
+ });
+
+ it('Should return true when the event is found in the events to wait array', () => {
+ let isWaiting = basePubSub.isWaitingForEvent('WINDOW_OUT');
+
+ expect(isWaiting).toBeTruthy();
+ });
+
+ it('Should return false when the event is not found in the events to wait array', () => {
+ let isWaiting = basePubSub.isWaitingForEvent('CHECK_IN');
+
+ expect(isWaiting).toBeFalsy();
+ });
+ });
+
+ describe('notify function tests', () => {
+ let eventType: string = 'CHECK_IN';
+ let callback;
+ beforeEach(() => {
+ callback = jest.fn();
+ });
+
+ it('should only update the last event notified property when no subscribers registered', () => {
+ basePubSub.notify(eventType);
+
+ expect(basePubSub.lastEventNotified).toBe(eventType);
+ });
+
+ it('should call post message with the right parameters when there are subscribers registered', () => {
+ testWindow.postMessage = jest.fn();
+ basePubSub.register(testSub, testWindow, testSubUrl);
+ basePubSub.notify(eventType);
+
+ let sub = basePubSub.subscribers.get(testSub);
+
+ let eventObj = {
+ type: eventType,
+ data: undefined,
+ originId: basePubSub.clientId
+ };
+
+ expect(sub.window.postMessage).toHaveBeenCalledWith(eventObj, sub.locationUrl);
+ });
+
+ it('should execute the callback function when calling notify with subscription function with no subscribers', () => {
+ let callback = jest.fn();
+
+ basePubSub.notify(eventType).subscribe(callback);
+
+ expect(callback).toHaveBeenCalled();
+ });
+
+ it('should execute the callback function when calling notify with subscription function ' +
+ 'with connected subscribers after all been notified', () => {
+ basePubSub.register(testSub, testWindow, testSubUrl);
+
+ basePubSub.notify(eventType).subscribe(callback);
+
+ expect(callback).toHaveBeenCalled();
+ });
+
+ it('should register an action completed function to pub sub when an event that is in the events to wait list ' +
+ 'is being fired', () => {
+ let eventsMap = new Map<string, Array<string>>();
+ eventsMap.set(testSub, ['CHECK_IN']);
+ basePubSub.on = jest.fn();
+
+ basePubSub.register(testSub, testWindow, testSubUrl);
+ basePubSub.eventsToWait = eventsMap;
+
+ basePubSub.notify(eventType).subscribe(callback);
+
+ expect(basePubSub.on).toHaveBeenCalled();
+ });
+ })
+}); \ No newline at end of file
diff --git a/src/base-pubsub.ts b/src/base-pubsub.ts
new file mode 100644
index 0000000..36b959a
--- /dev/null
+++ b/src/base-pubsub.ts
@@ -0,0 +1,126 @@
+declare const window: Window;
+
+export class BasePubSub {
+
+ subscribers: Map<string, ISubscriber>;
+ eventsCallbacks: Function[];
+ clientId: string;
+ eventsToWait: Map<string, string[]>;
+ lastEventNotified: string;
+
+ constructor(pluginId: string) {
+ this.subscribers = new Map<string, ISubscriber>();
+ this.eventsCallbacks = [];
+ this.eventsToWait = new Map<string, string[]>();
+ this.clientId = pluginId;
+ this.lastEventNotified = '';
+ this.onMessage = this.onMessage.bind(this);
+
+ window.addEventListener('message', this.onMessage);
+ }
+
+ public register(subscriberId: string, subscriberWindow: Window, subscriberUrl: string) {
+ const subscriber = {
+ window: subscriberWindow,
+ locationUrl: subscriberUrl || subscriberWindow.location.href
+ } as ISubscriber;
+
+ this.subscribers.set(subscriberId, subscriber);
+ }
+
+ public unregister(subscriberId: string) {
+ this.subscribers.delete(subscriberId);
+ }
+
+ public on(callback: Function) {
+ const functionExists = this.eventsCallbacks.find((func: Function) => {
+ return callback.toString() === func.toString();
+ });
+
+ if (!functionExists) {
+ this.eventsCallbacks.push(callback);
+ }
+ }
+
+ public off(callback: Function) {
+ const index = this.eventsCallbacks.indexOf(callback);
+ this.eventsCallbacks.splice(index, 1);
+ }
+
+ public notify(eventType: string, eventData?: any) {
+ const eventObj = {
+ type: eventType,
+ data: eventData,
+ originId: this.clientId
+ } as IPubSubEvent;
+
+ this.subscribers.forEach( (subscriber: ISubscriber, subscriberId: string) => {
+ subscriber.window.postMessage(eventObj, subscriber.locationUrl);
+ });
+
+ this.lastEventNotified = eventType;
+
+ return {
+ subscribe: function(callbackFn) {
+
+ if (this.subscribers.size !== 0) {
+ const subscribersToNotify = Array.from(this.subscribers.keys());
+
+ const checkNotifyComplete = (subscriberId: string) => {
+
+ const index = subscribersToNotify.indexOf(subscriberId);
+ subscribersToNotify.splice(index, 1);
+
+ if (subscribersToNotify.length === 0) {
+ callbackFn();
+ }
+ };
+
+ this.subscribers.forEach((subscriber: ISubscriber, subscriberId: string) => {
+ if (this.eventsToWait.has(subscriberId) &&
+ this.eventsToWait.get(subscriberId).indexOf(eventType) !== -1) {
+
+ const actionCompletedFunction = (actionCompletedEventData, subId = subscriberId) => {
+ if (actionCompletedEventData.type === 'ACTION_COMPLETED') {
+ checkNotifyComplete(subId);
+ }
+ this.off(actionCompletedFunction);
+
+ };
+ this.on(actionCompletedFunction);
+ } else {
+ checkNotifyComplete(subscriberId);
+ }
+ });
+ } else {
+ callbackFn();
+ }
+ }.bind(this)
+ };
+ }
+
+ public isWaitingForEvent(eventName: string): boolean {
+ return Array.from(this.eventsToWait.values()).some((eventsList: string[]) =>
+ eventsList.indexOf(eventName) !== -1
+ );
+ }
+
+ protected onMessage(event: any) {
+ if (this.subscribers.has(event.data.originId)) {
+ this.eventsCallbacks.forEach((callback: Function) => {
+ callback(event.data, event);
+ });
+ }
+ }
+}
+
+export interface IPubSubEvent {
+ type: string;
+ originId: string;
+ data: any;
+}
+
+export interface ISubscriber {
+ window: Window;
+ locationUrl: string;
+}
diff --git a/src/plugin-pubsub.ts b/src/plugin-pubsub.ts
new file mode 100644
index 0000000..ec4afb2
--- /dev/null
+++ b/src/plugin-pubsub.ts
@@ -0,0 +1,29 @@
+import { BasePubSub } from './base-pubsub';
+
+declare const window: Window;
+
+export class PluginPubSub extends BasePubSub {
+
+ constructor(pluginId: string, parentUrl: string, eventsToWait?: string[]) {
+ super(pluginId);
+ this.register('sdc-hub', window.parent, parentUrl);
+ this.subscribe(eventsToWait);
+ }
+
+ public subscribe(eventsToWait?: string[]) {
+ const registerData = {
+ pluginId: this.clientId,
+ eventsToWait: eventsToWait || []
+ };
+
+ this.notify('PLUGIN_REGISTER', registerData);
+ }
+
+ public unsubscribe() {
+ const unregisterData = {
+ pluginId: this.clientId
+ };
+
+ this.notify('PLUGIN_UNREGISTER', unregisterData);
+ }
+}
diff --git a/src/plugin-pubusb.spec.ts b/src/plugin-pubusb.spec.ts
new file mode 100644
index 0000000..1eb6eda
--- /dev/null
+++ b/src/plugin-pubusb.spec.ts
@@ -0,0 +1,53 @@
+import {PluginPubSub} from './plugin-pubsub';
+
+declare const window: Window;
+
+describe('BasePubSub Tests', () => {
+ let pluginPubSub: PluginPubSub;
+
+ let testSub: string = 'testSub';
+ let testParentUrl: string = 'http://127.0.0.1';
+ let testEventsToWait: Array<string> = ['CHECK_IN', 'WINDOW_OUT'];
+
+ beforeEach(() => {
+ pluginPubSub = new PluginPubSub(testSub, testParentUrl, testEventsToWait);
+ });
+
+ describe('constructor tests', () => {
+ it('should init class property', () => {
+ expect(pluginPubSub.subscribers.size).toBe(1);
+ expect(pluginPubSub.eventsCallbacks.length).toBe(0);
+ expect(pluginPubSub.eventsToWait.size).toBe(0);
+ expect(pluginPubSub.clientId).toBe('testSub');
+ });
+ });
+
+ describe('subscribe function tests', () => {
+ it('should call notify function with the PLUGIN_REGISTER event and the register data', () => {
+ pluginPubSub.notify = jest.fn();
+
+ let wantedRegisterData = {
+ pluginId: testSub,
+ eventsToWait: []
+ };
+
+ pluginPubSub.subscribe();
+
+ expect(pluginPubSub.notify).toHaveBeenCalledWith('PLUGIN_REGISTER', wantedRegisterData);
+ })
+ });
+
+ describe('unsubscribe function tests', () => {
+ it('should call notify function with the PLUGIN_UNREGISTER event and the unregister data', () => {
+ pluginPubSub.notify = jest.fn();
+
+ let wantedUnregisterData = {
+ pluginId: testSub,
+ };
+
+ pluginPubSub.unsubscribe();
+
+ expect(pluginPubSub.notify).toHaveBeenCalledWith('PLUGIN_UNREGISTER', wantedUnregisterData);
+ })
+ });
+}); \ No newline at end of file