test(service-worker): allow `SwPush` tests to run on Node.js (#24162)
PR Close #24162
This commit is contained in:
parent
86bf5f3912
commit
4d55dfd9d9
|
@ -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); }
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
@ -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(); }); });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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); }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue