Skip to content

Commit

Permalink
feat: use rxjs to send messages within coordinator host #ADEN-9976
Browse files Browse the repository at this point in the history
feat: use connector instead of host

chore: change interface

fix: fix native connector

chore: fix coordinator spec

chore: fix channel spec

chore: check connector in channel spec

chore: fix receiver spec

chore: add connector factory spec

chore: add native connector spec

chore: add post message connector spec

chore: remove coverage check from host.ts

chore: update dependencies

chore: change operators to functions

chore: change lite transmitter constructor

chore: fix @types/node version

chore: export lite transmitter

chore: fix setup

chore: fix channel

chore: add methods for sending and receiving messages

chore: use methods for sending and receiving messages

chore: create sender and listener

chore: add listener/sender memoization

chore: fix channel spec

chore: fix receiver spec

chore: remove connector

chore: add isRxMode spec

chore: handle not serializable messages

chore: update setup spec

chore: update transmitter spec

chore: remove lite transmitter

chore: remove experimental decorators
  • Loading branch information
Bielik20 committed Mar 20, 2020
1 parent e328199 commit ae8228d
Show file tree
Hide file tree
Showing 38 changed files with 4,775 additions and 4,597 deletions.
6 changes: 2 additions & 4 deletions .lintstagedrc
@@ -1,11 +1,9 @@
{
"*.ts": [
"prettier --write",
"tslint --fix",
"git add"
"tslint --fix"
],
"*.{js,json,css,scss,html}": [
"prettier --write",
"git add"
"prettier --write"
]
}
6 changes: 5 additions & 1 deletion jest.config.js
Expand Up @@ -3,5 +3,9 @@ module.exports = {
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.mock.ts'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.stub.ts',
'!src/models/host.ts', // there is only interface there, jest is incorrectly interpreting it
],
};
8,401 changes: 4,103 additions & 4,298 deletions package-lock.json

Large diffs are not rendered by default.

44 changes: 22 additions & 22 deletions package.json
Expand Up @@ -44,35 +44,35 @@
"semantic-release": "semantic-release"
},
"dependencies": {
"rxjs": "^6.5.3"
"rxjs": "^6.5.4"
},
"devDependencies": {
"@commitlint/cli": "8.2.0",
"@commitlint/config-conventional": "8.2.0",
"@types/jest": "24.0.19",
"@types/node": "12.12.17",
"@commitlint/cli": "^8.3.5",
"@commitlint/config-conventional": "^8.3.4",
"@types/jest": "^25.1.4",
"@types/node": "^12.12.6",
"commitizen": "4.0.3",
"coveralls": "3.0.7",
"cz-conventional-changelog": "3.0.2",
"husky": "3.0.9",
"jest": "24.9.0",
"lint-staged": "9.4.2",
"prettier": "1.18.2",
"rimraf": "3.0.0",
"rollup": "1.25.1",
"coveralls": "^3.0.9",
"cz-conventional-changelog": "^3.1.0",
"husky": "^4.2.3",
"jest": "^25.1.0",
"lint-staged": "^10.0.8",
"prettier": "^1.19.1",
"rimraf": "^3.0.2",
"rollup": "^2.0.5",
"rollup-plugin-commonjs": "10.1.0",
"rollup-plugin-json": "4.0.0",
"rollup-plugin-node-resolve": "5.2.0",
"rollup-plugin-sourcemaps": "0.4.2",
"rollup-plugin-typescript2": "0.24.3",
"rollup-plugin-uglify": "6.0.3",
"semantic-release": "15.13.27",
"source-map-support": "0.5.13",
"ts-jest": "24.1.0",
"ts-node": "8.4.1",
"tslint": "5.20.0",
"rollup-plugin-sourcemaps": "^0.5.0",
"rollup-plugin-typescript2": "^0.26.0",
"rollup-plugin-uglify": "^6.0.4",
"semantic-release": "^17.0.4",
"source-map-support": "^0.5.16",
"ts-jest": "^25.2.1",
"ts-node": "^8.6.2",
"tslint": "^6.1.0",
"tslint-config-prettier": "1.18.0",
"typescript": "3.6.4"
"typescript": "^3.8.3"
},
"config": {
"commitizen": {
Expand Down
6 changes: 3 additions & 3 deletions src/communicator.spec.ts
@@ -1,5 +1,5 @@
import { Communicator } from './communicator';
import { createHostMock, HostMock } from './models/host.mock';
import { createHostStub, HostStub } from './models/host.stub';
import { DEFAULT_OPTIONS } from './models/options';
import { Receiver } from './receiver';
import { Transmitter } from './transmitter';
Expand Down Expand Up @@ -28,10 +28,10 @@ describe('Communicator', () => {

describe('Implementation', () => {
let communicator: Communicator;
let host: HostMock;
let host: HostStub;

beforeEach(() => {
host = createHostMock();
host = createHostStub();
communicator = new Communicator({ host, coordinatorHost: host });
});

Expand Down
18 changes: 8 additions & 10 deletions src/communicator.ts
Expand Up @@ -5,19 +5,17 @@ import { Receiver } from './receiver';
import { Transmitter } from './transmitter';

export class Communicator {
actions$: Observable<Action>;
private readonly options: PostQuecastOptions;
private transmitter: Transmitter;
private receiver: Receiver;
readonly actions$: Observable<Action>;
private readonly transmitter: Transmitter;

constructor(options: Partial<PostQuecastOptions> = {}) {
this.options = {
constructor(_options: Partial<PostQuecastOptions> = {}) {
const options: PostQuecastOptions = {
...DEFAULT_OPTIONS,
...options,
..._options,
};
this.transmitter = new Transmitter(this.options);
this.receiver = new Receiver(this.options);
this.actions$ = this.receiver.actions$;

this.transmitter = new Transmitter(options);
this.actions$ = new Receiver(options).actions$;
}

dispatch<T>(action: Action<T>): void {
Expand Down
15 changes: 15 additions & 0 deletions src/connectors/is-rx-mode.spec.ts
@@ -0,0 +1,15 @@
import { createHostStub } from '../models/host.stub';
import { isRxMode } from './is-rx-mode';

describe('isRxMode', () => {
const host1 = createHostStub();
const host2 = createHostStub();

it('should return true', () => {
expect(isRxMode({ host: host1, coordinatorHost: host1 }));
});

it('should return false', () => {
expect(isRxMode({ host: host1, coordinatorHost: host2 }));
});
});
6 changes: 6 additions & 0 deletions src/connectors/is-rx-mode.ts
@@ -0,0 +1,6 @@
import { LIB_SUBJECT } from '../models/constants';
import { PostQuecastOptions } from '../models/options';

export function isRxMode(options: Pick<PostQuecastOptions, 'host' | 'coordinatorHost'>): boolean {
return options.host === options.coordinatorHost && !!options.coordinatorHost[LIB_SUBJECT];
}
79 changes: 79 additions & 0 deletions src/connectors/listener.spec.ts
@@ -0,0 +1,79 @@
import { LIB_ID, LIB_SUBJECT } from '../models/constants';
import { createHostStub, HostStub } from '../models/host.stub';
import { PostMessageData } from '../models/post-message-data';
import { PostMessageEvent } from '../models/post-message-event';
import { Listener, NativeListener, RxListener } from './listener';

describe('Listener', () => {
let host1: HostStub;
let host2: HostStub;

beforeEach(() => {
host1 = createHostStub();
host2 = createHostStub();
});

it('should create rx listener if the same host', () => {
const spy = jest.spyOn(RxListener, 'make');
const instance = Listener.make({ coordinatorHost: host1, host: host1 });

expect(instance instanceof RxListener).toBe(true);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(host1);
});

it('should create native listener different hosts', () => {
const spy = jest.spyOn(NativeListener, 'make');
const instance = Listener.make({ coordinatorHost: host1, host: host2 });

expect(instance instanceof NativeListener).toBe(true);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(host2);
});

describe('RxListener', () => {
it('should receive messages from host subject', () => {
const rxListener = RxListener.make(host1);
const results: PostMessageEvent[] = [];

rxListener.messages$.subscribe(value => results.push(value));
host1[LIB_SUBJECT].next(makeMessage('test1'));
host1[LIB_SUBJECT].next(makeMessage('test2'));
host1[LIB_SUBJECT].next('invalid' as any);

expect(results.length).toBe(2);
expect(results.map(result => result.data.action.type)).toEqual(['test1', 'test2']);
});
});

describe('NativeListener', () => {
it('should receive messages from host subject', () => {
const nativeListener = NativeListener.make(host1);
const results: PostMessageEvent[] = [];

nativeListener.messages$.subscribe(value => results.push(value));
host1.postMessage(makeMessageData('test1'), '*');
host1.postMessage(makeMessageData('test2'), '*');
host1.postMessage('invalid', '*');

expect(results.length).toBe(2);
expect(results.map(result => result.data.action.type)).toEqual(['test1', 'test2']);
});
});

function makeMessage(type: string): PostMessageEvent {
return {
data: makeMessageData(type),
source: host1,
};
}

function makeMessageData(type: string): PostMessageData {
return {
action: { type, timestamp: 10 },
channelId: 'default',
libId: LIB_ID,
private: true,
};
}
});
12 changes: 12 additions & 0 deletions src/connectors/listener.stub.ts
@@ -0,0 +1,12 @@
import { Subject } from 'rxjs';
import { PostMessageEvent } from '../models/post-message-event';

export type ListenerStub = {
messages$: Subject<PostMessageEvent>;
};

export function createListenerStub(): ListenerStub {
return {
messages$: new Subject(),
};
}
44 changes: 44 additions & 0 deletions src/connectors/listener.ts
@@ -0,0 +1,44 @@
/* tslint:disable:max-classes-per-file */
import { fromEvent, Observable } from 'rxjs';
import { LIB_SUBJECT } from '../models/constants';
import { Host } from '../models/host';
import { PostQuecastOptions } from '../models/options';
import { PostMessageEvent } from '../models/post-message-event';
import { onlyValidMessages } from '../rxjs/only-valid-messages';
import { isRxMode } from './is-rx-mode';

export abstract class Listener {
static make(options: Pick<PostQuecastOptions, 'host' | 'coordinatorHost'>): Listener {
if (isRxMode(options)) {
return RxListener.make(options.coordinatorHost);
}

return NativeListener.make(options.host);
}

messages$: Observable<PostMessageEvent>;
}

export class RxListener implements Listener {
static make(host: Host): RxListener {
return new RxListener(host);
}

readonly messages$: Observable<PostMessageEvent>;

private constructor(host: Host) {
this.messages$ = host[LIB_SUBJECT].asObservable().pipe(onlyValidMessages());
}
}

export class NativeListener implements Listener {
static make(host: Host): NativeListener {
return new NativeListener(host);
}

readonly messages$: Observable<PostMessageEvent>;

private constructor(host: Host) {
this.messages$ = fromEvent(host, 'message').pipe(onlyValidMessages());
}
}
88 changes: 88 additions & 0 deletions src/connectors/sender.spec.ts
@@ -0,0 +1,88 @@
import { LIB_ID, LIB_SUBJECT } from '../models/constants';
import { createHostStub, HostStub } from '../models/host.stub';
import { PostMessageData } from '../models/post-message-data';
import { PostMessageEvent } from '../models/post-message-event';
import { NativeSender, RxSender, Sender } from './sender';

describe('Sender', () => {
let host1: HostStub;
let host2: HostStub;

beforeEach(() => {
host1 = createHostStub();
host2 = createHostStub();
});

it('should create RxSender if the same host', () => {
const spy = jest.spyOn(RxSender, 'make');
const instance = Sender.make({ coordinatorHost: host1, host: host1 });

expect(instance instanceof RxSender).toBe(true);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(host1);
});

it('should create NativeSender different hosts', () => {
const spy = jest.spyOn(NativeSender, 'make');
const instance = Sender.make({ coordinatorHost: host1, host: host2 });

expect(instance instanceof NativeSender).toBe(true);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(host1);
});

describe('RxSender', () => {
it('should send messages through host', () => {
const message1 = makeMessageData('test1');
const message2 = makeMessageData('test2');
const rxSender = RxSender.make(host1);
const results: PostMessageEvent[] = [];

host1[LIB_SUBJECT].subscribe(value => results.push(value));

rxSender.postMessage(message1);
rxSender.postMessage(message2);

expect(results.length).toBe(2);
expect(results.map(result => result.data.action.type)).toEqual(['test1', 'test2']);
expect(results[0].source).toBe(results[1].source);
expect(results[0].source).toBe(host1);
});
});

describe('NativeSender', () => {
it('should send messages through host', () => {
const message1 = makeMessageData('test1');
const message2 = makeMessageData('test2');
const nativeSender = NativeSender.make(host1);

nativeSender.postMessage(message1);
expect(host1.postMessage).toHaveBeenCalledTimes(1);
expect(host1.postMessage).toHaveBeenCalledWith(message1, '*');

host1.postMessage.mockClear();

nativeSender.postMessage(message2);
expect(host1.postMessage).toHaveBeenCalledTimes(1);
expect(host1.postMessage).toHaveBeenCalledWith(message2, '*');
});

it('should fail silently so that not serializable messages pass', () => {
const nativeSender = NativeSender.make(host1);

host1.postMessage.mockImplementation(() => {
throw new Error();
});
nativeSender.postMessage(makeMessageData('test'));
});
});

function makeMessageData(type: string): PostMessageData {
return {
action: { type, timestamp: 10 },
channelId: 'default',
libId: LIB_ID,
private: true,
};
}
});
11 changes: 11 additions & 0 deletions src/connectors/sender.stub.ts
@@ -0,0 +1,11 @@
import { Sender } from './sender';

export type SenderStub = {
[key in keyof Sender]: jest.SpyInstance & Sender[key];
};

export function createSenderStub(): SenderStub {
return {
postMessage: jest.fn(),
};
}

0 comments on commit ae8228d

Please sign in to comment.