test(service-worker): allow `SwPush` tests to run on Node.js (#24162)

PR Close #24162
This commit is contained in:
George Kalpakas 2018-05-28 18:27:51 +03:00 committed by Miško Hevery
parent 86bf5f3912
commit 4d55dfd9d9
3 changed files with 36 additions and 21 deletions

View File

@ -13,7 +13,6 @@ import {map, switchMap, take} from 'rxjs/operators';
import {ERR_SW_NOT_SUPPORTED, NgswCommChannel} from './low_level';
/**
* Subscribe and listen to push notifications from the Service Worker.
*
@ -55,7 +54,7 @@ export class SwPush {
return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED));
}
const pushOptions: PushSubscriptionOptionsInit = {userVisibleOnly: true};
let key = atob(options.serverPublicKey.replace(/_/g, '/').replace(/-/g, '+'));
let key = this.decodeBase64(options.serverPublicKey.replace(/_/g, '/').replace(/-/g, '+'));
let applicationServerKey = new Uint8Array(new ArrayBuffer(key.length));
for (let i = 0; i < key.length; i++) {
applicationServerKey[i] = key.charCodeAt(i);
@ -90,4 +89,6 @@ export class SwPush {
}));
return unsubscribe.pipe(take(1)).toPromise();
}
private decodeBase64(input: string): string { return atob(input); }
}

View File

@ -13,7 +13,7 @@ import {NgswCommChannel} from '../src/low_level';
import {RegistrationOptions, ngswCommChannelFactory} from '../src/module';
import {SwPush} from '../src/push';
import {SwUpdate} from '../src/update';
import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockServiceWorkerRegistration} from '../testing/mock';
import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockServiceWorkerRegistration, patchDecodeBase64} from '../testing/mock';
import {async_fit, async_it} from './async';
{
@ -137,8 +137,13 @@ import {async_fit, async_it} from './async';
});
describe('SwPush', () => {
let unpatchDecodeBase64: () => void;
let push: SwPush;
// Patch `SwPush.decodeBase64()` in Node.js (where `atob` is not available).
beforeAll(() => unpatchDecodeBase64 = patchDecodeBase64(SwPush.prototype as any));
afterAll(() => unpatchDecodeBase64());
beforeEach(() => {
push = new SwPush(comm);
mock.setupSw();
@ -164,9 +169,12 @@ import {async_fit, async_it} from './async';
});
async_it('calls `PushManager.subscribe()` (with appropriate options)', async() => {
const decode = (charCodeArr: Uint8Array) =>
Array.from(charCodeArr).map(c => String.fromCharCode(c)).join('');
// atob('c3ViamVjdHM/') === 'subjects?'
const serverPublicKey = 'c3ViamVjdHM_';
const appServerKeyStr ='subjects?';
const appServerKeyStr = 'subjects?';
const pmSubscribeSpy = spyOn(MockPushManager.prototype, 'subscribe').and.callThrough();
await push.requestSubscription({serverPublicKey});
@ -178,7 +186,7 @@ import {async_fit, async_it} from './async';
});
const actualAppServerKey = pmSubscribeSpy.calls.first().args[0].applicationServerKey;
const actualAppServerKeyStr = new TextDecoder('utf-8').decode(actualAppServerKey);
const actualAppServerKeyStr = decode(actualAppServerKey);
expect(actualAppServerKeyStr).toBe(appServerKeyStr);
});
@ -279,7 +287,7 @@ import {async_fit, async_it} from './async';
describe('messages', () => {
it('receives push messages', () => {
const sendMessage = (type: string, message: string) =>
mock.sendMessage({type, data: {message}});
mock.sendMessage({type, data: {message}});
const receivedMessages: string[] = [];
push.messages.subscribe((msg: {message: string}) => receivedMessages.push(msg.message));
@ -331,24 +339,21 @@ import {async_fit, async_it} from './async';
subscriptionSpy.calls.reset();
// Subscribe.
push.requestSubscription({serverPublicKey: 'test'});
await nextSubEmitPromise;
await push.requestSubscription({serverPublicKey: 'test'});
expect(subscriptionSpy).toHaveBeenCalledTimes(1);
expect(subscriptionSpy).toHaveBeenCalledWith(jasmine.any(MockPushSubscription));
subscriptionSpy.calls.reset();
// Subscribe again.
push.requestSubscription({serverPublicKey: 'test'});
await nextSubEmitPromise;
await push.requestSubscription({serverPublicKey: 'test'});
expect(subscriptionSpy).toHaveBeenCalledTimes(1);
expect(subscriptionSpy).toHaveBeenCalledWith(jasmine.any(MockPushSubscription));
subscriptionSpy.calls.reset();
// Unsubscribe.
push.unsubscribe();
await nextSubEmitPromise;
await push.unsubscribe();
expect(subscriptionSpy).toHaveBeenCalledTimes(1);
expect(subscriptionSpy).toHaveBeenCalledWith(null);
});
@ -369,9 +374,8 @@ import {async_fit, async_it} from './async';
push.requestSubscription({serverPublicKey: 'test'}).catch(err => { done(); });
});
it('gives an error when unsubscribing', done => {
push.unsubscribe().catch(err => { done(); });
});
it('gives an error when unsubscribing',
done => { push.unsubscribe().catch(err => { done(); }); });
});
});

View File

@ -8,6 +8,20 @@
import {Subject} from 'rxjs';
export const patchDecodeBase64 = (proto: {decodeBase64: typeof atob}) => {
let unpatch: () => void = () => undefined;
if ((typeof atob === 'undefined') && (typeof Buffer === 'function')) {
const oldDecodeBase64 = proto.decodeBase64;
const newDecodeBase64 = (input: string) => Buffer.from(input, 'base64').toString('binary');
proto.decodeBase64 = newDecodeBase64;
unpatch = () => { proto.decodeBase64 = oldDecodeBase64; };
}
return unpatch;
};
export class MockServiceWorkerContainer {
private onControllerChange: Function[] = [];
private onMessage: Function[] = [];
@ -63,9 +77,7 @@ export class MockServiceWorkerRegistration {
export class MockPushManager {
private subscription: PushSubscription|null = null;
getSubscription(): Promise<PushSubscription|null> {
return Promise.resolve(this.subscription);
}
getSubscription(): Promise<PushSubscription|null> { return Promise.resolve(this.subscription); }
subscribe(options?: PushSubscriptionOptionsInit): Promise<PushSubscription> {
this.subscription = new MockPushSubscription() as any;
@ -74,7 +86,5 @@ export class MockPushManager {
}
export class MockPushSubscription {
unsubscribe(): Promise<boolean> {
return Promise.resolve(true);
}
unsubscribe(): Promise<boolean> { return Promise.resolve(true); }
}