From 4d55dfd9d9187c7da61a49dc13574d85e2e9d3d5 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Mon, 28 May 2018 18:27:51 +0300 Subject: [PATCH] test(service-worker): allow `SwPush` tests to run on Node.js (#24162) PR Close #24162 --- packages/service-worker/src/push.ts | 5 ++-- packages/service-worker/test/comm_spec.ts | 30 +++++++++++++---------- packages/service-worker/testing/mock.ts | 22 ++++++++++++----- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/packages/service-worker/src/push.ts b/packages/service-worker/src/push.ts index 265e01527b..c2d828fbf6 100644 --- a/packages/service-worker/src/push.ts +++ b/packages/service-worker/src/push.ts @@ -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); } } diff --git a/packages/service-worker/test/comm_spec.ts b/packages/service-worker/test/comm_spec.ts index 6b44da30e1..7fcaf3c849 100644 --- a/packages/service-worker/test/comm_spec.ts +++ b/packages/service-worker/test/comm_spec.ts @@ -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(); }); }); }); }); diff --git a/packages/service-worker/testing/mock.ts b/packages/service-worker/testing/mock.ts index 925219aef3..3eb1cfe588 100644 --- a/packages/service-worker/testing/mock.ts +++ b/packages/service-worker/testing/mock.ts @@ -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 { - return Promise.resolve(this.subscription); - } + getSubscription(): Promise { return Promise.resolve(this.subscription); } subscribe(options?: PushSubscriptionOptionsInit): Promise { this.subscription = new MockPushSubscription() as any; @@ -74,7 +86,5 @@ export class MockPushManager { } export class MockPushSubscription { - unsubscribe(): Promise { - return Promise.resolve(true); - } + unsubscribe(): Promise { return Promise.resolve(true); } }