voluptasmollitia
Version:
Monorepo for the Firebase JavaScript SDK
167 lines (156 loc) • 5.36 kB
text/typescript
/**
* @license
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect, use } from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import * as sinon from 'sinon';
import * as sinonChai from 'sinon-chai';
import {
_EventType,
_MessageError,
PingRequest,
_PingResponse,
ReceiverMessageEvent,
SenderMessageEvent,
_Status,
_TimeoutDuration
} from '.';
import { FakeServiceWorker } from '../../../test/helpers/fake_service_worker';
import { stubTimeouts, TimerMap } from '../../../test/helpers/timeout_stub';
import { Sender } from './sender';
use(sinonChai);
use(chaiAsPromised);
const oldSetTimeout = setTimeout;
describe('platform_browser/messagechannel/sender', () => {
describe('_send', () => {
let sender: Sender;
let serviceWorker: ServiceWorker;
let pendingTimeouts: TimerMap;
beforeEach(() => {
serviceWorker = (new FakeServiceWorker() as unknown) as ServiceWorker;
sender = new Sender(serviceWorker);
pendingTimeouts = stubTimeouts();
sinon.stub(window, 'clearTimeout');
});
afterEach(() => {
sinon.restore();
});
it('should send an event and wait for a response', async () => {
const response = [
{
fulfilled: true,
value: [_EventType.KEY_CHANGED]
}
];
serviceWorker.addEventListener('message', (event: Event) => {
const messageEvent = event as MessageEvent<
SenderMessageEvent<PingRequest>
>;
messageEvent.ports[0].postMessage({
status: _Status.ACK,
eventId: messageEvent.data.eventId,
eventType: messageEvent.data.eventType,
response: null
} as ReceiverMessageEvent<_PingResponse>);
messageEvent.ports[0].postMessage({
status: _Status.DONE,
eventId: messageEvent.data.eventId,
eventType: messageEvent.data.eventType,
response
} as ReceiverMessageEvent<_PingResponse>);
});
const result = await sender._send<_PingResponse, PingRequest>(
_EventType.PING,
{},
_TimeoutDuration.ACK
);
expect(result).to.have.deep.members(response);
});
it('should timeout if it doesnt see an ACK', async () => {
serviceWorker.addEventListener('message', (_event: Event) => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
Promise.resolve().then(() => {
pendingTimeouts[_TimeoutDuration.ACK]();
});
});
await expect(
sender._send<_PingResponse, PingRequest>(
_EventType.PING,
{},
_TimeoutDuration.ACK
)
).to.be.rejectedWith(Error, _MessageError.UNSUPPORTED_EVENT);
});
it('should work with a long ACK', async () => {
const response = [
{
fulfilled: true,
value: [_EventType.KEY_CHANGED]
}
];
serviceWorker.addEventListener('message', (event: Event) => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
Promise.resolve().then(() => {
pendingTimeouts[_TimeoutDuration.ACK]();
});
const messageEvent = event as MessageEvent<
SenderMessageEvent<PingRequest>
>;
messageEvent.ports[0].postMessage({
status: _Status.ACK,
eventId: messageEvent.data.eventId,
eventType: messageEvent.data.eventType,
response: null
} as ReceiverMessageEvent<_PingResponse>);
messageEvent.ports[0].postMessage({
status: _Status.DONE,
eventId: messageEvent.data.eventId,
eventType: messageEvent.data.eventType,
response
} as ReceiverMessageEvent<_PingResponse>);
});
const result = await sender._send<_PingResponse, PingRequest>(
_EventType.PING,
{},
_TimeoutDuration.LONG_ACK
);
expect(result).to.have.deep.members(response);
});
it('it should timeout if it gets an ACK but not a DONE', async () => {
serviceWorker.addEventListener('message', (event: Event) => {
oldSetTimeout(() => {
pendingTimeouts[_TimeoutDuration.COMPLETION]();
}, 5);
const messageEvent = event as MessageEvent<
SenderMessageEvent<PingRequest>
>;
messageEvent.ports[0].postMessage({
status: _Status.ACK,
eventId: messageEvent.data.eventId,
eventType: messageEvent.data.eventType,
response: null
} as ReceiverMessageEvent<_PingResponse>);
});
await expect(
sender._send<_PingResponse, PingRequest>(
_EventType.PING,
{},
_TimeoutDuration.ACK
)
).to.be.rejectedWith(Error, _MessageError.TIMEOUT);
});
});
});