Skip to content

Commit

Permalink
feat(message-core): subscriber functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Kasey Powers committed Aug 27, 2018
1 parent 5d68c0c commit 343b09d
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 37 deletions.
22 changes: 10 additions & 12 deletions packages/message-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,16 @@
A package wrapping the postMessage function with helper functions and security checks.

## Install
`npm install @availity/message-core`

## Configure
`npm install @availity/message-core`

`AvMessage` requires an `onMessage` function to be defined. It is called with an event and data parameters.
## Methods

```javascript
import AvMessage from '@availity/message-core';
### subscribe

AvMessage.onMessage = (event, data) => {
// handle postMessage event
}
```
`const unsubscribe = avMessage.subscribe(event, fn)` when a message event is received and verified, fn will be called with the event data.

## Methods
it returns a function that can be used to unsubscribe from that event

### enabled

Expand All @@ -31,12 +26,15 @@ returns a string of the windows current domain.

## send

`AvMessage.send(payload, target)` will send the payload to the target if AvMessage is enabled.
`avMessage.send(payload, target)` will send the payload to the target if AvMessage is enabled.
target defaults to the parent window. payload will be stringified if not a string.

## Authors

**Kasey Powers**
* [kaseyepowers@gmail.com](kaseyepowers@gmail.com)

- [kaseyepowers@gmail.com](kaseyepowers@gmail.com)

## License

[MIT](../../LICENSE)
26 changes: 23 additions & 3 deletions packages/message-core/src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class AvMessage {
subscribers = {};

constructor() {
this.isEnabled = true;
this.DEFAULT_EVENT = 'avMessage';
Expand All @@ -15,9 +17,7 @@ class AvMessage {

getEventData(event) {
if (
!this.isEnabled ||
!this.onMessage ||
typeof this.onMessage !== 'function' || // do nothing if not enabled or no onMessage function given
!this.isEnabled || // do nothing if not enabled
!event ||
!event.data ||
!event.origin ||
Expand Down Expand Up @@ -49,6 +49,26 @@ class AvMessage {
this.onMessage(event, data);
}

subscribe(event, fn) {
if (!this.subscribers[event]) {
this.subscribers[event] = [];
}
this.subscribers[event].push(fn);
return () => {
this.subscribers[event] = this.subscribers[event].filter(
val => val !== fn
);
};
}

onMessage(event, data) {
if (this.subscribers[event]) {
this.subscribers[event].forEach(fn => {
fn(data);
});
}
}

// if current domain doesn't match regex DOMAIN, return true.
isDomain(url) {
return !this.DOMAIN.test(this.domain()) || this.DOMAIN.test(url);
Expand Down
72 changes: 50 additions & 22 deletions packages/message-core/src/tests/message.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,56 @@ describe('AvMessage', () => {
expect(avMessage.enabled('hello')).toBe(true);
});

describe('subscribers', () => {
test('onMessage should call all subscribers for event', () => {
const testEvent = 'testEvent';
const fns = [jest.fn(), jest.fn()];
avMessage.subscribers = {
[testEvent]: fns,
};
avMessage.onMessage(`${testEvent}Other`);
fns.forEach(fn => expect(fn).not.toHaveBeenCalled());

const data = { testData: 'hello world' };
avMessage.onMessage(testEvent, data);
fns.forEach(fn => expect(fn).toHaveBeenCalledWith(data));
});

test('subscribe should add function to subscribers', () => {
avMessage.subscribers = {};
const testEvent = 'testEvent';
const fn = 'totally a function';
avMessage.subscribe(testEvent, fn);
expect(avMessage.subscribers).toEqual({
[testEvent]: [fn],
});

const fn2 = 'totally another function';
avMessage.subscribe(testEvent, fn2);
expect(avMessage.subscribers).toEqual({
[testEvent]: [fn, fn2],
});
});

test('subscribe should return function to remove subscribers', () => {
avMessage.subscribers = {};
const testEvent = 'testEvent';
const fn = 'totally a function';
const unsubscribe = avMessage.subscribe(testEvent, fn);

const fn2 = 'totally another function';
avMessage.subscribe(testEvent, fn2);
expect(avMessage.subscribers).toEqual({
[testEvent]: [fn, fn2],
});

unsubscribe();
expect(avMessage.subscribers).toEqual({
[testEvent]: [fn2],
});
});
});

describe('getEventData()', () => {
let spyParse;
const mockEvent = {
Expand Down Expand Up @@ -49,20 +99,6 @@ describe('AvMessage', () => {
expect(avMessage.onMessage).not.toHaveBeenCalled();
});

test('should return early when AvMessages.onMessage not defined', () => {
delete avMessage.onMessage;
avMessage.getEventData(mockEvent);
expect(avMessage.isDomain).not.toHaveBeenCalled();
expect(spyParse).not.toHaveBeenCalled();
});

test('should return early when AvMessages.onMessage not function', () => {
avMessage.onMessage = 'onMessage';
avMessage.getEventData(mockEvent);
expect(avMessage.isDomain).not.toHaveBeenCalled();
expect(spyParse).not.toHaveBeenCalled();
});

test('should return early when event does not have all fields', () => {
const mockEvent1 = Object.assign({}, mockEvent, { data: false });
const mockEvent2 = Object.assign({}, mockEvent, { origin: false });
Expand Down Expand Up @@ -153,14 +189,6 @@ describe('AvMessage', () => {
test('should return location.origin if exists', () => {
expect(avMessage.domain()).toBe(URL);
});

// test('if no location.origin, should return domain generated with hostname', () => {
// expect(avMessage.domain()).toBe(URL);
// });

// test("if no location origin or hostname, should return '*'", () => {
// expect(avMessage.domain()).toBe(URL);
// });
});

test("isDomain should return true if domain() doesn't match regex", () => {
Expand Down

0 comments on commit 343b09d

Please sign in to comment.