mirror of
https://github.com/hanzoai/firebase-js-sdk.git
synced 2026-09-24 16:59:10 +00:00
Fix TokenDetails in messaging/ (#649)
Makes sure that we use only one type of TokenDetails object, both in the code and in the database. Updates DB and adds a function to fix the old values. Fixes and adds tests.
This commit is contained in:
committed by
GitHub
parent
64fdd73f22
commit
14896fb83b
@@ -22,7 +22,7 @@ import {
|
||||
Observer,
|
||||
PartialObserver
|
||||
} from '@firebase/util';
|
||||
import { arrayBufferToBase64 } from '../helpers/array-buffer-to-base64';
|
||||
import { isArrayBufferEqual } from '../helpers/is-array-buffer-equal';
|
||||
import { TokenDetails } from '../interfaces/token-details';
|
||||
import { ERROR_CODES, ERROR_MAP } from '../models/errors';
|
||||
import { IIDModel } from '../models/iid-model';
|
||||
@@ -128,7 +128,7 @@ export abstract class ControllerInterface {
|
||||
);
|
||||
if (isTokenValid) {
|
||||
const now = Date.now();
|
||||
if (now < tokenDetails.createTime! + TOKEN_EXPIRATION_MILLIS) {
|
||||
if (now < tokenDetails.createTime + TOKEN_EXPIRATION_MILLIS) {
|
||||
return tokenDetails.fcmToken;
|
||||
} else {
|
||||
return this.updateToken(
|
||||
@@ -166,10 +166,13 @@ export abstract class ControllerInterface {
|
||||
const allDetails: TokenDetails = {
|
||||
swScope: swReg.scope,
|
||||
vapidKey: publicVapidKey,
|
||||
subscription: pushSubscription,
|
||||
fcmSenderId: this.messagingSenderId_,
|
||||
fcmToken: updatedToken,
|
||||
fcmPushSet: tokenDetails.fcmPushSet
|
||||
fcmPushSet: tokenDetails.fcmPushSet,
|
||||
createTime: Date.now(),
|
||||
endpoint: pushSubscription.endpoint,
|
||||
auth: pushSubscription.getKey('auth')!,
|
||||
p256dh: pushSubscription.getKey('p256dh')!
|
||||
};
|
||||
|
||||
await this.tokenDetailsModel_.saveTokenDetails(allDetails);
|
||||
@@ -197,10 +200,13 @@ export abstract class ControllerInterface {
|
||||
const allDetails: TokenDetails = {
|
||||
swScope: swReg.scope,
|
||||
vapidKey: publicVapidKey,
|
||||
subscription: pushSubscription,
|
||||
fcmSenderId: this.messagingSenderId_,
|
||||
fcmToken: tokenDetails.token,
|
||||
fcmPushSet: tokenDetails.pushSet
|
||||
fcmPushSet: tokenDetails.pushSet,
|
||||
createTime: Date.now(),
|
||||
endpoint: pushSubscription.endpoint,
|
||||
auth: pushSubscription.getKey('auth')!,
|
||||
p256dh: pushSubscription.getKey('p256dh')!
|
||||
};
|
||||
await this.tokenDetailsModel_.saveTokenDetails(allDetails);
|
||||
await this.vapidDetailsModel_.saveVapidDetails(swReg.scope, publicVapidKey);
|
||||
@@ -328,8 +334,6 @@ export abstract class ControllerInterface {
|
||||
|
||||
/**
|
||||
* Returns the current Notification Permission state.
|
||||
* @private
|
||||
* @return {string} The currenct permission state.
|
||||
*/
|
||||
getNotificationPermission_(): NotificationPermission {
|
||||
return (Notification as any).permission;
|
||||
@@ -359,17 +363,21 @@ function isTokenStillValid(
|
||||
publicVapidKey: Uint8Array,
|
||||
tokenDetails: TokenDetails
|
||||
): boolean {
|
||||
if (arrayBufferToBase64(publicVapidKey) !== tokenDetails.vapidKey) {
|
||||
if (
|
||||
!isArrayBufferEqual(publicVapidKey.buffer, tokenDetails.vapidKey.buffer)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// getKey() isn't defined in the PushSubscription externs file, hence
|
||||
// subscription.getKey('<key name>').
|
||||
return (
|
||||
pushSubscription.endpoint === tokenDetails.endpoint &&
|
||||
arrayBufferToBase64(pushSubscription.getKey('auth')!) ===
|
||||
tokenDetails.auth &&
|
||||
arrayBufferToBase64(pushSubscription.getKey('p256dh')!) ===
|
||||
tokenDetails.p256dh
|
||||
const isEndpointEqual = pushSubscription.endpoint === tokenDetails.endpoint;
|
||||
const isAuthEqual = isArrayBufferEqual(
|
||||
pushSubscription.getKey('auth'),
|
||||
tokenDetails.auth
|
||||
);
|
||||
const isP256dhEqual = isArrayBufferEqual(
|
||||
pushSubscription.getKey('p256dh'),
|
||||
tokenDetails.p256dh
|
||||
);
|
||||
|
||||
return isEndpointEqual && isAuthEqual && isP256dhEqual;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright 2018 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export function isArrayBufferEqual(
|
||||
a: ArrayBuffer | undefined | null,
|
||||
b: ArrayBuffer | undefined | null
|
||||
): boolean {
|
||||
if (a == null || b == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (a.byteLength !== b.byteLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const viewA = new DataView(a);
|
||||
const viewB = new DataView(b);
|
||||
|
||||
for (let i = 0; i < a.byteLength; i++) {
|
||||
if (viewA.getUint8(i) !== viewB.getUint8(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -17,13 +17,11 @@
|
||||
export interface TokenDetails {
|
||||
fcmToken: string;
|
||||
swScope: string;
|
||||
vapidKey: string | Uint8Array;
|
||||
subscription: PushSubscription;
|
||||
vapidKey: Uint8Array;
|
||||
fcmSenderId: string;
|
||||
fcmPushSet: string;
|
||||
|
||||
createTime?: number;
|
||||
endpoint?: string;
|
||||
auth?: string;
|
||||
p256dh?: string;
|
||||
endpoint: string;
|
||||
auth: ArrayBuffer;
|
||||
p256dh: ArrayBuffer;
|
||||
createTime: number;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export abstract class DBInterface {
|
||||
* This function should create and update object stores.
|
||||
*/
|
||||
protected abstract onDbUpgrade(
|
||||
db: IDBDatabase,
|
||||
request: IDBOpenDBRequest,
|
||||
event: IDBVersionChangeEvent
|
||||
): void;
|
||||
|
||||
@@ -121,8 +121,7 @@ export abstract class DBInterface {
|
||||
this.dbPromise = null;
|
||||
reject(request.error);
|
||||
};
|
||||
request.onupgradeneeded = event =>
|
||||
this.onDbUpgrade(request.result, event);
|
||||
request.onupgradeneeded = event => this.onDbUpgrade(request, event);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { arrayBufferToBase64 } from '../helpers/array-buffer-to-base64';
|
||||
import { base64ToArrayBuffer } from '../helpers/base64-to-array-buffer';
|
||||
import { TokenDetails } from '../interfaces/token-details';
|
||||
import { cleanV1 } from './clean-v1-undefined';
|
||||
import { DBInterface } from './db-interface';
|
||||
@@ -22,31 +22,70 @@ import { ERROR_CODES } from './errors';
|
||||
|
||||
export class TokenDetailsModel extends DBInterface {
|
||||
protected readonly dbName: string = 'fcm_token_details_db';
|
||||
protected readonly dbVersion: number = 2;
|
||||
protected readonly dbVersion: number = 3;
|
||||
protected readonly objectStoreName: string = 'fcm_token_object_Store';
|
||||
|
||||
protected onDbUpgrade(db: IDBDatabase, evt: IDBVersionChangeEvent): void {
|
||||
if (evt.oldVersion < 1) {
|
||||
// New IDB instance
|
||||
const objectStore = db.createObjectStore(this.objectStoreName, {
|
||||
keyPath: 'swScope'
|
||||
});
|
||||
protected onDbUpgrade(
|
||||
request: IDBOpenDBRequest,
|
||||
event: IDBVersionChangeEvent
|
||||
): void {
|
||||
const db: IDBDatabase = request.result;
|
||||
|
||||
// Make sure the sender ID can be searched
|
||||
objectStore.createIndex('fcmSenderId', 'fcmSenderId', {
|
||||
unique: false
|
||||
});
|
||||
// Lack of 'break' statements is intentional.
|
||||
switch (event.oldVersion) {
|
||||
case 0: {
|
||||
// New IDB instance
|
||||
const objectStore = db.createObjectStore(this.objectStoreName, {
|
||||
keyPath: 'swScope'
|
||||
});
|
||||
|
||||
objectStore.createIndex('fcmToken', 'fcmToken', {
|
||||
unique: true
|
||||
});
|
||||
}
|
||||
// Make sure the sender ID can be searched
|
||||
objectStore.createIndex('fcmSenderId', 'fcmSenderId', {
|
||||
unique: false
|
||||
});
|
||||
|
||||
if (evt.oldVersion < 2) {
|
||||
// Prior to version 2, we were using either 'fcm_token_details_db'
|
||||
// or 'undefined' as the database name due to bug in the SDK
|
||||
// So remove the old tokens and databases.
|
||||
cleanV1();
|
||||
objectStore.createIndex('fcmToken', 'fcmToken', { unique: true });
|
||||
}
|
||||
|
||||
case 1: {
|
||||
// Prior to version 2, we were using either 'fcm_token_details_db'
|
||||
// or 'undefined' as the database name due to bug in the SDK
|
||||
// So remove the old tokens and databases.
|
||||
cleanV1();
|
||||
}
|
||||
|
||||
case 2: {
|
||||
const objectStore = request.transaction.objectStore(
|
||||
this.objectStoreName
|
||||
);
|
||||
const cursorRequest = objectStore.openCursor();
|
||||
cursorRequest.onsuccess = () => {
|
||||
const cursor: IDBCursorWithValue = cursorRequest.result;
|
||||
if (cursor) {
|
||||
const value = cursor.value;
|
||||
const newValue: Partial<TokenDetails> = { ...value };
|
||||
|
||||
if (!value.createTime) {
|
||||
newValue.createTime = Date.now();
|
||||
}
|
||||
|
||||
if (typeof value.vapidKey === 'string') {
|
||||
newValue.vapidKey = base64ToArrayBuffer(value.vapidKey);
|
||||
}
|
||||
|
||||
if (typeof value.auth === 'string') {
|
||||
newValue.auth = base64ToArrayBuffer(value.auth).buffer;
|
||||
}
|
||||
|
||||
if (typeof value.auth === 'string') {
|
||||
newValue.p256dh = base64ToArrayBuffer(value.p256dh).buffer;
|
||||
}
|
||||
|
||||
cursor.update(newValue);
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +116,20 @@ export class TokenDetailsModel extends DBInterface {
|
||||
}
|
||||
}
|
||||
|
||||
if (input.subscription) {
|
||||
if (!(input.subscription instanceof PushSubscription)) {
|
||||
if (input.endpoint) {
|
||||
if (typeof input.endpoint !== 'string' || input.endpoint.length === 0) {
|
||||
throw this.errorFactory.create(ERROR_CODES.BAD_SUBSCRIPTION);
|
||||
}
|
||||
}
|
||||
|
||||
if (input.auth) {
|
||||
if (!(input.auth instanceof ArrayBuffer)) {
|
||||
throw this.errorFactory.create(ERROR_CODES.BAD_SUBSCRIPTION);
|
||||
}
|
||||
}
|
||||
|
||||
if (input.p256dh) {
|
||||
if (!(input.p256dh instanceof ArrayBuffer)) {
|
||||
throw this.errorFactory.create(ERROR_CODES.BAD_SUBSCRIPTION);
|
||||
}
|
||||
}
|
||||
@@ -149,7 +200,7 @@ export class TokenDetailsModel extends DBInterface {
|
||||
throw this.errorFactory.create(ERROR_CODES.BAD_VAPID_KEY);
|
||||
}
|
||||
|
||||
if (!tokenDetails.subscription) {
|
||||
if (!tokenDetails.endpoint || !tokenDetails.auth || !tokenDetails.p256dh) {
|
||||
throw this.errorFactory.create(ERROR_CODES.BAD_SUBSCRIPTION);
|
||||
}
|
||||
|
||||
@@ -167,19 +218,7 @@ export class TokenDetailsModel extends DBInterface {
|
||||
|
||||
this.validateInputs(tokenDetails);
|
||||
|
||||
const details = {
|
||||
swScope: tokenDetails.swScope,
|
||||
vapidKey: arrayBufferToBase64(tokenDetails.vapidKey as Uint8Array),
|
||||
endpoint: tokenDetails.subscription.endpoint,
|
||||
auth: arrayBufferToBase64(tokenDetails.subscription.getKey('auth')!),
|
||||
p256dh: arrayBufferToBase64(tokenDetails.subscription.getKey('p256dh')!),
|
||||
fcmSenderId: tokenDetails.fcmSenderId,
|
||||
fcmToken: tokenDetails.fcmToken,
|
||||
fcmPushSet: tokenDetails.fcmPushSet,
|
||||
createTime: Date.now()
|
||||
};
|
||||
|
||||
return this.put(details);
|
||||
return this.put(tokenDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,8 @@ export class VapidDetailsModel extends DBInterface {
|
||||
protected readonly dbVersion: number = 1;
|
||||
protected readonly objectStoreName: string = 'fcm_vapid_object_Store';
|
||||
|
||||
protected onDbUpgrade(db: IDBDatabase): void {
|
||||
protected onDbUpgrade(request: IDBOpenDBRequest): void {
|
||||
const db: IDBDatabase = request.result;
|
||||
db.createObjectStore(this.objectStoreName, { keyPath: 'swScope' });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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 } from 'chai';
|
||||
import { arrayBufferToBase64 } from '../src/helpers/array-buffer-to-base64';
|
||||
|
||||
describe('Firebase Messaging > array-buffer-to-base64', () => {
|
||||
it('should return expected value for p256dh example', () => {
|
||||
const buffer = new Uint8Array([
|
||||
4,
|
||||
181,
|
||||
98,
|
||||
240,
|
||||
48,
|
||||
62,
|
||||
75,
|
||||
119,
|
||||
193,
|
||||
227,
|
||||
154,
|
||||
69,
|
||||
250,
|
||||
216,
|
||||
53,
|
||||
110,
|
||||
157,
|
||||
120,
|
||||
62,
|
||||
76,
|
||||
213,
|
||||
249,
|
||||
11,
|
||||
62,
|
||||
12,
|
||||
19,
|
||||
149,
|
||||
36,
|
||||
5,
|
||||
82,
|
||||
140,
|
||||
37,
|
||||
141,
|
||||
134,
|
||||
132,
|
||||
98,
|
||||
87,
|
||||
152,
|
||||
175,
|
||||
98,
|
||||
53,
|
||||
83,
|
||||
196,
|
||||
242,
|
||||
202,
|
||||
155,
|
||||
19,
|
||||
173,
|
||||
157,
|
||||
216,
|
||||
45,
|
||||
147,
|
||||
20,
|
||||
12,
|
||||
151,
|
||||
160,
|
||||
147,
|
||||
159,
|
||||
205,
|
||||
219,
|
||||
75,
|
||||
133,
|
||||
156,
|
||||
129,
|
||||
152
|
||||
]).buffer;
|
||||
const expectedValue =
|
||||
'BLVi8DA-S3fB45pF-tg1bp14PkzV-Qs-DBOVJAVSjCWNhoRi' +
|
||||
'V5ivYjVTxPLKmxOtndgtkxQMl6CTn83bS4WcgZg';
|
||||
expect(arrayBufferToBase64(buffer)).to.equal(expectedValue);
|
||||
});
|
||||
|
||||
it('should return expected value for auth example', () => {
|
||||
const buffer = new Uint8Array([
|
||||
255,
|
||||
237,
|
||||
107,
|
||||
177,
|
||||
171,
|
||||
78,
|
||||
84,
|
||||
131,
|
||||
221,
|
||||
231,
|
||||
87,
|
||||
188,
|
||||
22,
|
||||
232,
|
||||
71,
|
||||
15
|
||||
]).buffer;
|
||||
const expectedValue = '_-1rsatOVIPd51e8FuhHDw';
|
||||
expect(arrayBufferToBase64(buffer)).to.equal(expectedValue);
|
||||
});
|
||||
});
|
||||
@@ -13,79 +13,83 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { FirebaseApp } from '@firebase/app-types';
|
||||
import { assert } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import { SWController } from '../src/controllers/sw-controller';
|
||||
import { WindowController } from '../src/controllers/window-controller';
|
||||
import { base64ToArrayBuffer } from '../src/helpers/base64-to-array-buffer';
|
||||
import { ERROR_CODES } from '../src/models/errors';
|
||||
import { IIDModel } from '../src/models/iid-model';
|
||||
import { TokenDetailsModel } from '../src/models/token-details-model';
|
||||
import { makeFakeApp } from './make-fake-app';
|
||||
import { makeFakeSubscription } from './make-fake-subscription';
|
||||
import { makeFakeSWReg } from './make-fake-sw-reg';
|
||||
import { deleteDatabase } from './testing-utils/db-helper';
|
||||
|
||||
const FAKE_SUBSCRIPTION = makeFakeSubscription();
|
||||
const EXAMPLE_TOKEN_SAVE = {
|
||||
fcmToken: 'ExampleFCMToken1337',
|
||||
fcmSenderId: '1234567890',
|
||||
endpoint: 'https://example.google.com/1234',
|
||||
swScope: 'firebase-messaging-sw-scope',
|
||||
auth: '12345',
|
||||
p256dh: '123456789098765642421'
|
||||
swScope: '/example-scope',
|
||||
vapidKey: base64ToArrayBuffer(
|
||||
'BNJxw7sCGkGLOUP2cawBaBXRuWZ3lw_PmQMgreLVVvX_b' +
|
||||
'4emEWVURkCF8fUTHEFe2xrEgTt5ilh5xD94v0pFe_I'
|
||||
),
|
||||
fcmSenderId: '1234567',
|
||||
fcmToken: 'qwerty',
|
||||
fcmPushSet: '7654321',
|
||||
endpoint: FAKE_SUBSCRIPTION.endpoint,
|
||||
auth: FAKE_SUBSCRIPTION.getKey('auth')!,
|
||||
p256dh: FAKE_SUBSCRIPTION.getKey('p256dh')!,
|
||||
createTime: Date.now()
|
||||
};
|
||||
|
||||
describe('Firebase Messaging > *Controller.deleteToken()', () => {
|
||||
const sandbox = sinon.sandbox.create();
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let app: FirebaseApp;
|
||||
let globalMessagingService: WindowController | SWController;
|
||||
|
||||
const app = makeFakeApp({
|
||||
messagingSenderId: EXAMPLE_TOKEN_SAVE.fcmSenderId
|
||||
});
|
||||
|
||||
const configureRegistrationMocks = (ServiceClass, fakeReg) => {
|
||||
function configureRegistrationMocks(
|
||||
ServiceClass: typeof WindowController | typeof SWController,
|
||||
fakeReg: ServiceWorkerRegistration | null
|
||||
): void {
|
||||
sandbox.stub(ServiceClass.prototype, 'getSWRegistration_').callsFake(() => {
|
||||
return fakeReg;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const generateFakeReg = getSubResult => {
|
||||
function generateFakeReg(
|
||||
getSubscription: () => Promise<PushSubscription | null>
|
||||
): ServiceWorkerRegistration {
|
||||
const registration = makeFakeSWReg();
|
||||
|
||||
Object.defineProperty(registration, 'pushManager', {
|
||||
value: {
|
||||
getSubscription: () => {
|
||||
if (typeof getSubResult === 'function') {
|
||||
return getSubResult();
|
||||
}
|
||||
|
||||
return getSubResult;
|
||||
}
|
||||
}
|
||||
value: { getSubscription }
|
||||
});
|
||||
return Promise.resolve(registration);
|
||||
};
|
||||
|
||||
let globalMessagingService;
|
||||
const cleanUp = () => {
|
||||
sandbox.restore();
|
||||
|
||||
const deletePromises: any[] = [];
|
||||
if (globalMessagingService) {
|
||||
deletePromises.push(globalMessagingService.delete());
|
||||
}
|
||||
return Promise.all(deletePromises)
|
||||
.then(() => deleteDatabase('fcm_token_details_db'))
|
||||
.then(() => (globalMessagingService = null));
|
||||
};
|
||||
return registration;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
return cleanUp();
|
||||
sandbox = sinon.sandbox.create();
|
||||
app = makeFakeApp({
|
||||
messagingSenderId: EXAMPLE_TOKEN_SAVE.fcmSenderId
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
return cleanUp();
|
||||
afterEach(async () => {
|
||||
sandbox.restore();
|
||||
|
||||
if (globalMessagingService) {
|
||||
await globalMessagingService.delete();
|
||||
}
|
||||
|
||||
await deleteDatabase('fcm_token_details_db');
|
||||
});
|
||||
|
||||
it('should handle no token to delete', () => {
|
||||
globalMessagingService = new WindowController(app);
|
||||
return globalMessagingService.deleteToken().then(
|
||||
return globalMessagingService.deleteToken(undefined as any).then(
|
||||
() => {
|
||||
throw new Error('Expected error to be thrown.');
|
||||
},
|
||||
@@ -96,7 +100,7 @@ describe('Firebase Messaging > *Controller.deleteToken()', () => {
|
||||
});
|
||||
|
||||
it('should handle no registration', () => {
|
||||
configureRegistrationMocks(WindowController, Promise.resolve(null));
|
||||
configureRegistrationMocks(WindowController, null);
|
||||
|
||||
sandbox
|
||||
.stub(TokenDetailsModel.prototype, 'deleteToken')
|
||||
@@ -114,7 +118,9 @@ describe('Firebase Messaging > *Controller.deleteToken()', () => {
|
||||
it('should handle get subscription error', () => {
|
||||
configureRegistrationMocks(
|
||||
WindowController,
|
||||
generateFakeReg(() => Promise.reject(new Error('Unknown error')))
|
||||
generateFakeReg(async () => {
|
||||
throw new Error('Unknown error');
|
||||
})
|
||||
);
|
||||
|
||||
sandbox
|
||||
@@ -141,7 +147,7 @@ describe('Firebase Messaging > *Controller.deleteToken()', () => {
|
||||
it(`should handle null getSubscription() ${ServiceClass.name}`, () => {
|
||||
configureRegistrationMocks(
|
||||
ServiceClass,
|
||||
generateFakeReg(Promise.resolve(null))
|
||||
generateFakeReg(async () => null)
|
||||
);
|
||||
|
||||
sandbox
|
||||
@@ -159,14 +165,14 @@ describe('Firebase Messaging > *Controller.deleteToken()', () => {
|
||||
|
||||
it(`should handle error on unsubscribe ${ServiceClass.name}`, () => {
|
||||
const errorMsg = 'unsubscribe-error-1234567890';
|
||||
const fakeSubscription = {
|
||||
const fakeSubscription: any = {
|
||||
endpoint: EXAMPLE_TOKEN_SAVE.endpoint,
|
||||
unsubscribe: () => Promise.reject(new Error(errorMsg))
|
||||
};
|
||||
|
||||
configureRegistrationMocks(
|
||||
ServiceClass,
|
||||
generateFakeReg(Promise.resolve(fakeSubscription))
|
||||
generateFakeReg(async () => fakeSubscription)
|
||||
);
|
||||
|
||||
sandbox
|
||||
@@ -192,14 +198,14 @@ describe('Firebase Messaging > *Controller.deleteToken()', () => {
|
||||
});
|
||||
|
||||
it(`should handle error on deleteToken ${ServiceClass.name}`, () => {
|
||||
const fakeSubscription = {
|
||||
const fakeSubscription: any = {
|
||||
endpoint: EXAMPLE_TOKEN_SAVE.endpoint,
|
||||
unsubscribe: async () => {}
|
||||
};
|
||||
|
||||
configureRegistrationMocks(
|
||||
ServiceClass,
|
||||
generateFakeReg(Promise.resolve(fakeSubscription))
|
||||
generateFakeReg(async () => fakeSubscription)
|
||||
);
|
||||
|
||||
sandbox
|
||||
@@ -228,14 +234,14 @@ describe('Firebase Messaging > *Controller.deleteToken()', () => {
|
||||
});
|
||||
|
||||
it(`should delete with valid unsubscribe ${ServiceClass.name}`, () => {
|
||||
const fakeSubscription = {
|
||||
const fakeSubscription: any = {
|
||||
endpoint: EXAMPLE_TOKEN_SAVE.endpoint,
|
||||
unsubscribe: async () => {}
|
||||
};
|
||||
|
||||
configureRegistrationMocks(
|
||||
ServiceClass,
|
||||
generateFakeReg(Promise.resolve(fakeSubscription))
|
||||
generateFakeReg(async () => fakeSubscription)
|
||||
);
|
||||
|
||||
sandbox
|
||||
|
||||
@@ -21,8 +21,9 @@ import {
|
||||
} from '../src/controllers/controller-interface';
|
||||
import { SWController } from '../src/controllers/sw-controller';
|
||||
import { WindowController } from '../src/controllers/window-controller';
|
||||
import { arrayBufferToBase64 } from '../src/helpers/array-buffer-to-base64';
|
||||
import { base64ToArrayBuffer } from '../src/helpers/base64-to-array-buffer';
|
||||
import { isArrayBufferEqual } from '../src/helpers/is-array-buffer-equal';
|
||||
import { TokenDetails } from '../src/interfaces/token-details';
|
||||
import { ERROR_CODES } from '../src/models/errors';
|
||||
import { DEFAULT_PUBLIC_VAPID_KEY } from '../src/models/fcm-details';
|
||||
import { IIDModel } from '../src/models/iid-model';
|
||||
@@ -37,44 +38,47 @@ const ONE_DAY = 24 * 60 * 60 * 1000;
|
||||
describe('Firebase Messaging > *Controller.getToken()', () => {
|
||||
const sandbox = sinon.sandbox.create();
|
||||
const now = Date.now();
|
||||
const expiredDate = new Date(now - ONE_DAY);
|
||||
const expiredDate = now - ONE_DAY * 8; // 8 days ago
|
||||
const FAKE_SUBSCRIPTION = makeFakeSubscription();
|
||||
|
||||
const EXAMPLE_FCM_TOKEN = 'ExampleFCMToken1337';
|
||||
const EXAMPLE_SENDER_ID = '1234567890';
|
||||
const CUSTOM_VAPID_KEY =
|
||||
const CUSTOM_VAPID_KEY = base64ToArrayBuffer(
|
||||
'BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXK' +
|
||||
'pNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo';
|
||||
const DEFAULT_VAPID_KEY = arrayBufferToBase64(DEFAULT_PUBLIC_VAPID_KEY);
|
||||
'pNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo'
|
||||
);
|
||||
const ENDPOINT = FAKE_SUBSCRIPTION.endpoint;
|
||||
const AUTH = FAKE_SUBSCRIPTION.getKey('auth')!;
|
||||
const P256DH = FAKE_SUBSCRIPTION.getKey('p256dh')!;
|
||||
|
||||
const EXAMPLE_TOKEN_DETAILS_DEFAULT_VAPID = {
|
||||
const EXAMPLE_TOKEN_DETAILS_DEFAULT_VAPID: TokenDetails = {
|
||||
swScope: '/example-scope',
|
||||
vapidKey: DEFAULT_VAPID_KEY,
|
||||
endpoint: FAKE_SUBSCRIPTION.endpoint,
|
||||
auth: arrayBufferToBase64(FAKE_SUBSCRIPTION['getKey']('auth')!),
|
||||
p256dh: arrayBufferToBase64(FAKE_SUBSCRIPTION['getKey']('p256dh')!),
|
||||
vapidKey: DEFAULT_PUBLIC_VAPID_KEY,
|
||||
endpoint: ENDPOINT,
|
||||
auth: AUTH,
|
||||
p256dh: P256DH,
|
||||
fcmSenderId: EXAMPLE_SENDER_ID,
|
||||
fcmToken: 'qwerty1',
|
||||
fcmPushSet: '87654321',
|
||||
createTime: now
|
||||
};
|
||||
const EXAMPLE_TOKEN_DETAILS_CUSTOM_VAPID = {
|
||||
const EXAMPLE_TOKEN_DETAILS_CUSTOM_VAPID: TokenDetails = {
|
||||
swScope: '/example-scope',
|
||||
vapidKey: CUSTOM_VAPID_KEY,
|
||||
endpoint: FAKE_SUBSCRIPTION.endpoint,
|
||||
auth: arrayBufferToBase64(FAKE_SUBSCRIPTION['getKey']('auth')!),
|
||||
p256dh: arrayBufferToBase64(FAKE_SUBSCRIPTION['getKey']('p256dh')!),
|
||||
endpoint: ENDPOINT,
|
||||
auth: AUTH,
|
||||
p256dh: P256DH,
|
||||
fcmSenderId: EXAMPLE_SENDER_ID,
|
||||
fcmToken: 'qwerty2',
|
||||
fcmPushSet: '7654321',
|
||||
createTime: now
|
||||
};
|
||||
const EXAMPLE_EXPIRED_TOKEN_DETAILS = {
|
||||
const EXAMPLE_EXPIRED_TOKEN_DETAILS: TokenDetails = {
|
||||
swScope: '/example-scope',
|
||||
vapidKey: DEFAULT_VAPID_KEY,
|
||||
endpoint: FAKE_SUBSCRIPTION.endpoint,
|
||||
auth: arrayBufferToBase64(FAKE_SUBSCRIPTION['getKey']('auth')!),
|
||||
p256dh: arrayBufferToBase64(FAKE_SUBSCRIPTION['getKey']('p256dh')!),
|
||||
vapidKey: DEFAULT_PUBLIC_VAPID_KEY,
|
||||
endpoint: ENDPOINT,
|
||||
auth: AUTH,
|
||||
p256dh: P256DH,
|
||||
fcmSenderId: EXAMPLE_SENDER_ID,
|
||||
fcmToken: 'qwerty3',
|
||||
fcmPushSet: '654321',
|
||||
@@ -231,7 +235,7 @@ describe('Firebase Messaging > *Controller.getToken()', () => {
|
||||
|
||||
let vapidKeyToUse = DEFAULT_PUBLIC_VAPID_KEY;
|
||||
if (VapidSetup['name'] === 'custom') {
|
||||
vapidKeyToUse = base64ToArrayBuffer(CUSTOM_VAPID_KEY);
|
||||
vapidKeyToUse = CUSTOM_VAPID_KEY;
|
||||
}
|
||||
sandbox
|
||||
.stub(ServiceClass.prototype, 'getPublicVapidKey_')
|
||||
@@ -264,9 +268,7 @@ describe('Firebase Messaging > *Controller.getToken()', () => {
|
||||
.callsFake(() => Promise.resolve(subscription));
|
||||
sandbox
|
||||
.stub(ServiceClass.prototype, 'getPublicVapidKey_')
|
||||
.callsFake(() =>
|
||||
Promise.resolve(base64ToArrayBuffer(CUSTOM_VAPID_KEY))
|
||||
);
|
||||
.callsFake(() => Promise.resolve(CUSTOM_VAPID_KEY));
|
||||
|
||||
sandbox
|
||||
.stub(ControllerInterface.prototype, 'getNotificationPermission_')
|
||||
@@ -348,7 +350,7 @@ describe('Firebase Messaging > *Controller.getToken()', () => {
|
||||
|
||||
let vapidKeyToUse = DEFAULT_PUBLIC_VAPID_KEY;
|
||||
if (VapidSetup['name'] === 'custom') {
|
||||
vapidKeyToUse = base64ToArrayBuffer(CUSTOM_VAPID_KEY);
|
||||
vapidKeyToUse = CUSTOM_VAPID_KEY;
|
||||
}
|
||||
sandbox
|
||||
.stub(ServiceClass.prototype, 'getPublicVapidKey_')
|
||||
@@ -398,10 +400,16 @@ describe('Firebase Messaging > *Controller.getToken()', () => {
|
||||
|
||||
assert.equal(tokenModelArgs[0].swScope, registration.scope);
|
||||
assert.equal(
|
||||
arrayBufferToBase64(tokenModelArgs[0].vapidKey),
|
||||
tokenModelArgs[0].vapidKey,
|
||||
VapidSetup['details']['vapidKey']
|
||||
);
|
||||
assert.equal(tokenModelArgs[0].subscription, subscription);
|
||||
|
||||
assert.equal(tokenModelArgs[0].endpoint, ENDPOINT);
|
||||
assert.equal(isArrayBufferEqual(tokenModelArgs[0].auth, AUTH), true);
|
||||
assert.equal(
|
||||
isArrayBufferEqual(tokenModelArgs[0].p256dh, P256DH),
|
||||
true
|
||||
);
|
||||
assert.equal(tokenModelArgs[0].fcmSenderId, EXAMPLE_SENDER_ID);
|
||||
assert.equal(tokenModelArgs[0].fcmToken, TOKEN_DETAILS['token']);
|
||||
assert.equal(tokenModelArgs[0].fcmPushSet, TOKEN_DETAILS['pushSet']);
|
||||
@@ -428,7 +436,7 @@ describe('Firebase Messaging > *Controller.getToken()', () => {
|
||||
const existingTokenDetails = VapidSetup['details'];
|
||||
let vapidKeyToUse = DEFAULT_PUBLIC_VAPID_KEY;
|
||||
if (VapidSetup['name'] === 'custom') {
|
||||
vapidKeyToUse = base64ToArrayBuffer(CUSTOM_VAPID_KEY);
|
||||
vapidKeyToUse = CUSTOM_VAPID_KEY;
|
||||
}
|
||||
sandbox
|
||||
.stub(ServiceClass.prototype, 'getPublicVapidKey_')
|
||||
@@ -539,9 +547,7 @@ describe('Firebase Messaging > *Controller.getToken()', () => {
|
||||
};
|
||||
|
||||
// now update the VAPID key.
|
||||
getPublicVapidKeyStub.callsFake(() =>
|
||||
Promise.resolve(base64ToArrayBuffer(CUSTOM_VAPID_KEY))
|
||||
);
|
||||
getPublicVapidKeyStub.callsFake(() => Promise.resolve(CUSTOM_VAPID_KEY));
|
||||
|
||||
const saveTokenDetailsStub = sandbox.stub(
|
||||
TokenDetailsModel.prototype,
|
||||
|
||||
@@ -13,8 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { FirebaseApp } from '@firebase/app-types';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import { makeFakeApp } from './make-fake-app';
|
||||
import { makeFakeSWReg } from './make-fake-sw-reg';
|
||||
|
||||
@@ -43,29 +46,26 @@ class MockControllerInterface extends ControllerInterface {
|
||||
}
|
||||
|
||||
describe('Firebase Messaging > *ControllerInterface', () => {
|
||||
const sandbox = sinon.sandbox.create();
|
||||
const app = makeFakeApp({
|
||||
messagingSenderId: '12345'
|
||||
});
|
||||
|
||||
const cleanup = () => {
|
||||
sandbox.restore();
|
||||
};
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let app: FirebaseApp;
|
||||
|
||||
beforeEach(() => {
|
||||
return cleanup();
|
||||
sandbox = sinon.sandbox.create();
|
||||
app = makeFakeApp({
|
||||
messagingSenderId: '12345'
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
return cleanup();
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('INTERNAL.delete()', () => {
|
||||
it('should call delete()', () => {
|
||||
const controller = new MockControllerInterface(app);
|
||||
sandbox.spy(controller, 'delete');
|
||||
const spy = sandbox.spy(controller, 'delete');
|
||||
controller.INTERNAL.delete();
|
||||
expect(controller.delete['callCount']).to.equal(1);
|
||||
expect(spy.callCount).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,12 +84,14 @@ describe('Firebase Messaging > *ControllerInterface', () => {
|
||||
});
|
||||
|
||||
describe('getPushSubscription()', () => {
|
||||
controllersToTest.forEach(ControllerInTest => {
|
||||
for (const ControllerInTest of controllersToTest) {
|
||||
it(`should return rejection error in ${ControllerInTest.name}`, () => {
|
||||
const injectedError = new Error('Inject error.');
|
||||
const reg = makeFakeSWReg();
|
||||
sandbox.stub(reg, 'pushManager').value({
|
||||
getSubscription: () => Promise.reject(injectedError)
|
||||
getSubscription: async () => {
|
||||
throw injectedError;
|
||||
}
|
||||
});
|
||||
|
||||
const controller = new ControllerInTest(app);
|
||||
@@ -109,7 +111,7 @@ describe('Firebase Messaging > *ControllerInterface', () => {
|
||||
const exampleSubscription = {};
|
||||
const reg = makeFakeSWReg();
|
||||
sandbox.stub(reg, 'pushManager').value({
|
||||
getSubscription: () => Promise.resolve(exampleSubscription)
|
||||
getSubscription: async () => exampleSubscription
|
||||
});
|
||||
|
||||
const controller = new ControllerInTest(app);
|
||||
@@ -125,13 +127,13 @@ describe('Firebase Messaging > *ControllerInterface', () => {
|
||||
const reg = makeFakeSWReg();
|
||||
sandbox.stub(reg, 'pushManager').value({
|
||||
getSubscription: async () => {},
|
||||
subscribe: options => {
|
||||
subscribe: async options => {
|
||||
expect(options).to.deep.equal({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: DEFAULT_PUBLIC_VAPID_KEY
|
||||
});
|
||||
|
||||
return Promise.resolve(exampleSubscription);
|
||||
return exampleSubscription;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -142,7 +144,7 @@ describe('Firebase Messaging > *ControllerInterface', () => {
|
||||
expect(subscription).to.equal(exampleSubscription);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('useServiceWorker()', () => {
|
||||
|
||||
@@ -34,7 +34,12 @@ class MockDbInterface extends DBInterface {
|
||||
readonly dbVersion: number = 1;
|
||||
readonly objectStoreName: string = 'test-obj-store';
|
||||
|
||||
protected onDbUpgrade(db: IDBDatabase, event: IDBVersionChangeEvent): void {
|
||||
protected onDbUpgrade(
|
||||
request: IDBOpenDBRequest,
|
||||
event: IDBVersionChangeEvent
|
||||
): void {
|
||||
const db: IDBDatabase = request.result;
|
||||
|
||||
const objectStore = db.createObjectStore(this.objectStoreName, {
|
||||
keyPath: 'key'
|
||||
});
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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 } from 'chai';
|
||||
import { arrayBufferToBase64 } from '../src/helpers/array-buffer-to-base64';
|
||||
import { base64ToArrayBuffer } from '../src/helpers/base64-to-array-buffer';
|
||||
import { isArrayBufferEqual } from '../src/helpers/is-array-buffer-equal';
|
||||
|
||||
describe('Firebase Messaging > Helpers', () => {
|
||||
describe('Array Buffer - Base64 conversion', () => {
|
||||
it('returns expected value for p256dh example', () => {
|
||||
// prettier-ignore
|
||||
const buffer = new Uint8Array([
|
||||
4, 181, 98, 240, 48, 62, 75, 119, 193, 227, 154, 69, 250, 216, 53, 110,
|
||||
157, 120, 62, 76, 213, 249, 11, 62, 12, 19, 149, 36, 5, 82, 140, 37, 141,
|
||||
134, 132, 98, 87, 152, 175, 98, 53, 83, 196, 242, 202, 155, 19, 173, 157,
|
||||
216, 45, 147, 20, 12, 151, 160, 147, 159, 205, 219, 75, 133, 156, 129, 152
|
||||
]);
|
||||
const expectedValue =
|
||||
'BLVi8DA-S3fB45pF-tg1bp14PkzV-Qs-DBOVJAVSjCWNhoRi' +
|
||||
'V5ivYjVTxPLKmxOtndgtkxQMl6CTn83bS4WcgZg';
|
||||
expect(arrayBufferToBase64(buffer)).to.equal(expectedValue);
|
||||
});
|
||||
|
||||
it('returns expected value for auth example', () => {
|
||||
// prettier-ignore
|
||||
const buffer = new Uint8Array([
|
||||
255, 237, 107, 177, 171, 78, 84, 131, 221, 231, 87, 188, 22, 232, 71, 15
|
||||
]);
|
||||
const expectedValue = '_-1rsatOVIPd51e8FuhHDw';
|
||||
expect(arrayBufferToBase64(buffer)).to.equal(expectedValue);
|
||||
});
|
||||
|
||||
it('converts back and forth', () => {
|
||||
// prettier-ignore
|
||||
const array = new Uint8Array([
|
||||
4, 181, 98, 240, 48, 62, 75, 119, 193, 227, 154, 69, 250, 216, 53, 110,
|
||||
157, 120, 62, 76, 213, 249, 11, 62, 12, 19, 149, 36, 5, 82, 140, 37, 141,
|
||||
134, 132, 98, 87, 152, 175, 98, 53, 83, 196, 242, 202, 155, 19, 173, 157,
|
||||
216, 45, 147, 20, 12, 151, 160, 147, 159, 205, 219, 75, 133, 156, 129, 152
|
||||
]);
|
||||
const string =
|
||||
'BLVi8DA-S3fB45pF-tg1bp14PkzV-Qs-DBOVJAVSjCWNhoRi' +
|
||||
'V5ivYjVTxPLKmxOtndgtkxQMl6CTn83bS4WcgZg';
|
||||
|
||||
expect(arrayBufferToBase64(base64ToArrayBuffer(string))).to.equal(string);
|
||||
expect(
|
||||
isArrayBufferEqual(
|
||||
base64ToArrayBuffer(arrayBufferToBase64(array)).buffer,
|
||||
array.buffer
|
||||
)
|
||||
).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isArrayBufferEqual', () => {
|
||||
let buffer: ArrayBuffer;
|
||||
|
||||
beforeEach(() => {
|
||||
buffer = new Uint8Array([1, 2, 3, 4, 5]).buffer;
|
||||
});
|
||||
|
||||
it('returns true if array buffers are same', () => {
|
||||
expect(isArrayBufferEqual(buffer, buffer)).to.be.true;
|
||||
});
|
||||
|
||||
it('returns true if array buffers are equal', () => {
|
||||
const equalBuffer = new Uint8Array([1, 2, 3, 4, 5]).buffer;
|
||||
expect(isArrayBufferEqual(buffer, equalBuffer)).to.be.true;
|
||||
});
|
||||
|
||||
it('returns false if array buffers are not equal', () => {
|
||||
const differentBuffer = new Uint8Array([1, 2, 3, 2, 1]).buffer;
|
||||
expect(isArrayBufferEqual(buffer, differentBuffer)).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false if array buffers have different lengths', () => {
|
||||
const longerBuffer = new Uint8Array([1, 2, 3, 4, 5, 6]).buffer;
|
||||
expect(isArrayBufferEqual(buffer, longerBuffer)).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false if either array buffer is undefined or null', () => {
|
||||
expect(isArrayBufferEqual(buffer, undefined)).to.be.false;
|
||||
expect(isArrayBufferEqual(buffer, null)).to.be.false;
|
||||
expect(isArrayBufferEqual(undefined, buffer)).to.be.false;
|
||||
expect(isArrayBufferEqual(null, buffer)).to.be.false;
|
||||
expect(isArrayBufferEqual(undefined, undefined)).to.be.false;
|
||||
expect(isArrayBufferEqual(null, null)).to.be.false;
|
||||
expect(isArrayBufferEqual(undefined, null)).to.be.false;
|
||||
expect(isArrayBufferEqual(null, undefined)).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -15,47 +15,40 @@
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { arrayBufferToBase64 } from '../../src/helpers/array-buffer-to-base64';
|
||||
|
||||
/** Token details that are fed to the database */
|
||||
export declare interface InputDetails {
|
||||
swScope: string;
|
||||
vapidKey: Uint8Array;
|
||||
subscription: PushSubscription;
|
||||
fcmSenderId: string;
|
||||
fcmToken: string;
|
||||
fcmPushSet: string;
|
||||
}
|
||||
import { isArrayBufferEqual } from '../../src/helpers/is-array-buffer-equal';
|
||||
import { TokenDetails } from '../../src/interfaces/token-details';
|
||||
|
||||
/** Compares the input details and the saved ones */
|
||||
export function compareDetails(
|
||||
input: InputDetails,
|
||||
saved: object,
|
||||
now: number
|
||||
): void {
|
||||
const subscriptionKeys = ['endpoint', 'auth', 'p256dh'];
|
||||
const subscriptionValues = {
|
||||
endpoint: input.subscription.endpoint,
|
||||
auth: arrayBufferToBase64(input.subscription.getKey('auth')!),
|
||||
p256dh: arrayBufferToBase64(input.subscription.getKey('p256dh')!)
|
||||
export function compareDetails(input: TokenDetails, saved: TokenDetails): void {
|
||||
const subscription = {
|
||||
endpoint: input.endpoint,
|
||||
auth: input.auth,
|
||||
p256dh: input.p256dh
|
||||
};
|
||||
|
||||
subscriptionKeys.forEach(keyName => {
|
||||
assert.equal(saved[keyName], subscriptionValues[keyName]);
|
||||
});
|
||||
for (const key of Object.keys(subscription)) {
|
||||
assert.equal(
|
||||
isArrayBufferEqual(saved[key], subscription[key]),
|
||||
true,
|
||||
`${key} does not match`
|
||||
);
|
||||
}
|
||||
|
||||
Object.keys(saved).forEach(keyName => {
|
||||
if (subscriptionKeys.indexOf(keyName) !== -1) {
|
||||
for (const key of Object.keys(saved)) {
|
||||
if (Object.keys(subscription).indexOf(key) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyName === 'createTime') {
|
||||
assert.equal(saved[keyName], now);
|
||||
} else if (keyName === 'vapidKey') {
|
||||
assert.equal(saved[keyName], arrayBufferToBase64(input[keyName]));
|
||||
if (key === 'createTime') {
|
||||
assert.equal(saved[key], Date.now(), `${key} does not match`);
|
||||
} else if (key === 'vapidKey') {
|
||||
assert.equal(
|
||||
isArrayBufferEqual(saved[key].buffer, input[key].buffer),
|
||||
true,
|
||||
`${key} does not match`
|
||||
);
|
||||
} else {
|
||||
assert.equal(saved[keyName], input[keyName]);
|
||||
assert.equal(saved[key], input[key], `${key} does not match`);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,370 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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 { assert } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { arrayBufferToBase64 } from '../src/helpers/array-buffer-to-base64';
|
||||
import { base64ToArrayBuffer } from '../src/helpers/base64-to-array-buffer';
|
||||
import { TokenDetails } from '../src/interfaces/token-details';
|
||||
import { ERROR_CODES } from '../src/models/errors';
|
||||
import { TokenDetailsModel } from '../src/models/token-details-model';
|
||||
import { makeFakeSubscription } from './make-fake-subscription';
|
||||
import { deleteDatabase } from './testing-utils/db-helper';
|
||||
import { compareDetails } from './testing-utils/detail-comparator';
|
||||
|
||||
const BAD_INPUTS: any[] = ['', [], {}, true, null, 123];
|
||||
|
||||
describe('Firebase Messaging > TokenDetailsModel', () => {
|
||||
let clock: sinon.SinonFakeTimers;
|
||||
let globalTokenModel: TokenDetailsModel;
|
||||
let exampleInput: TokenDetails;
|
||||
let fakeSubscription: PushSubscription;
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers();
|
||||
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
|
||||
fakeSubscription = makeFakeSubscription();
|
||||
exampleInput = {
|
||||
swScope: '/example-scope',
|
||||
vapidKey: base64ToArrayBuffer(
|
||||
'BNJxw7sCGkGLOUP2cawBaBXRuWZ3lw_PmQMgreLVVvX_b' +
|
||||
'4emEWVURkCF8fUTHEFe2xrEgTt5ilh5xD94v0pFe_I'
|
||||
),
|
||||
fcmSenderId: '1234567',
|
||||
fcmToken: 'qwerty',
|
||||
fcmPushSet: '7654321',
|
||||
endpoint: fakeSubscription.endpoint,
|
||||
auth: fakeSubscription.getKey('auth')!,
|
||||
p256dh: fakeSubscription.getKey('p256dh')!,
|
||||
createTime: Date.now()
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await globalTokenModel.closeDatabase();
|
||||
await deleteDatabase('fcm_token_details_db');
|
||||
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
describe('Database update v2 -> v3', () => {
|
||||
it('converts database contents', async () => {
|
||||
class OldDBTokenDetailsModel extends TokenDetailsModel {
|
||||
protected readonly dbVersion: number = 2;
|
||||
}
|
||||
|
||||
const oldDBTokenDetailsModel = new OldDBTokenDetailsModel();
|
||||
|
||||
// Old (v2) version of exampleInput
|
||||
// vapidKey, auth and p256dh are strings, createTime does not exist
|
||||
await oldDBTokenDetailsModel.put({
|
||||
swScope: '/example-scope',
|
||||
vapidKey:
|
||||
'BNJxw7sCGkGLOUP2cawBaBXRuWZ3lw_PmQMgreLVVvX_b' +
|
||||
'4emEWVURkCF8fUTHEFe2xrEgTt5ilh5xD94v0pFe_I',
|
||||
fcmSenderId: '1234567',
|
||||
fcmToken: 'qwerty',
|
||||
fcmPushSet: '7654321',
|
||||
endpoint: fakeSubscription.endpoint,
|
||||
auth: arrayBufferToBase64(fakeSubscription.getKey('auth')!),
|
||||
p256dh: arrayBufferToBase64(fakeSubscription.getKey('p256dh')!)
|
||||
});
|
||||
await oldDBTokenDetailsModel.closeDatabase();
|
||||
|
||||
// Get the same token using DB v3
|
||||
const tokenDetails = await globalTokenModel.get<TokenDetails>(
|
||||
exampleInput.swScope
|
||||
);
|
||||
|
||||
assert.exists(tokenDetails);
|
||||
compareDetails(tokenDetails!, exampleInput);
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveToken', () => {
|
||||
it('should throw on bad input', () => {
|
||||
const promises = BAD_INPUTS.map(badInput => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
exampleInput.swScope = badInput;
|
||||
return globalTokenModel.saveTokenDetails(exampleInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SCOPE, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad vapid key input', () => {
|
||||
const promises = BAD_INPUTS.map(badInput => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
exampleInput.vapidKey = badInput;
|
||||
return globalTokenModel.saveTokenDetails(exampleInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_VAPID_KEY, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad endpoint input', () => {
|
||||
const promises = BAD_INPUTS.map(badInput => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
exampleInput.endpoint = badInput;
|
||||
return globalTokenModel.saveTokenDetails(exampleInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SUBSCRIPTION, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad auth input', () => {
|
||||
const promises = BAD_INPUTS.map(badInput => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
exampleInput.auth = badInput;
|
||||
return globalTokenModel.saveTokenDetails(exampleInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SUBSCRIPTION, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad p256dh input', () => {
|
||||
const promises = BAD_INPUTS.map(badInput => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
exampleInput.p256dh = badInput;
|
||||
return globalTokenModel.saveTokenDetails(exampleInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SUBSCRIPTION, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad send id input', () => {
|
||||
const promises = BAD_INPUTS.map(badInput => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
exampleInput.fcmSenderId = badInput;
|
||||
return globalTokenModel.saveTokenDetails(exampleInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SENDER_ID, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad token input', () => {
|
||||
const promises = BAD_INPUTS.map(badInput => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
exampleInput.fcmToken = badInput;
|
||||
return globalTokenModel.saveTokenDetails(exampleInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_TOKEN, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad pushSet input', () => {
|
||||
const promises = BAD_INPUTS.map(badInput => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
exampleInput.fcmPushSet = badInput;
|
||||
return globalTokenModel.saveTokenDetails(exampleInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_PUSH_SET, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should save valid details', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
return globalTokenModel.saveTokenDetails(exampleInput);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTokenDetailsFromToken', () => {
|
||||
for (const badInput of BAD_INPUTS) {
|
||||
it(`should throw on bad scope input ${JSON.stringify(badInput)}`, () => {
|
||||
return globalTokenModel.getTokenDetailsFromSWScope(badInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SCOPE, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
for (const badInput of BAD_INPUTS) {
|
||||
it(`should throw on bad FCM Token input: '${badInput}'`, () => {
|
||||
return globalTokenModel.getTokenDetailsFromToken(badInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_TOKEN, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
it('should get from scope', () => {
|
||||
return globalTokenModel
|
||||
.getTokenDetailsFromSWScope(exampleInput.swScope)
|
||||
.then(details => {
|
||||
assert.notExists(details);
|
||||
return globalTokenModel.saveTokenDetails(exampleInput);
|
||||
})
|
||||
.then(() => {
|
||||
return globalTokenModel.getTokenDetailsFromSWScope(
|
||||
exampleInput.swScope
|
||||
);
|
||||
})
|
||||
.then(details => {
|
||||
assert.exists(details);
|
||||
compareDetails(exampleInput, details!);
|
||||
});
|
||||
});
|
||||
|
||||
it('should get from token', () => {
|
||||
return globalTokenModel
|
||||
.getTokenDetailsFromToken(exampleInput.fcmToken)
|
||||
.then(details => {
|
||||
assert.notExists(details);
|
||||
return globalTokenModel.saveTokenDetails(exampleInput);
|
||||
})
|
||||
.then(() => {
|
||||
return globalTokenModel.getTokenDetailsFromToken(
|
||||
exampleInput.fcmToken
|
||||
);
|
||||
})
|
||||
.then(details => {
|
||||
assert.exists(details);
|
||||
compareDetails(exampleInput, details!);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteToken', () => {
|
||||
it('should handle no input', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
return globalTokenModel.deleteToken(undefined as any).then(
|
||||
() => {
|
||||
throw new Error('Expected this to throw an error due to no token');
|
||||
},
|
||||
err => {
|
||||
assert.equal(
|
||||
'messaging/' + ERROR_CODES.INVALID_DELETE_TOKEN,
|
||||
err.code
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle empty string', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
return globalTokenModel.deleteToken('').then(
|
||||
() => {
|
||||
throw new Error('Expected this to throw an error due to no token');
|
||||
},
|
||||
err => {
|
||||
assert.equal(
|
||||
'messaging/' + ERROR_CODES.INVALID_DELETE_TOKEN,
|
||||
err.code
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should delete current token', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
return globalTokenModel
|
||||
.saveTokenDetails(exampleInput)
|
||||
.then(() => {
|
||||
return globalTokenModel.deleteToken(exampleInput.fcmToken);
|
||||
})
|
||||
.then(details => {
|
||||
compareDetails(exampleInput, details);
|
||||
return globalTokenModel.getTokenDetailsFromToken(
|
||||
exampleInput.fcmToken
|
||||
);
|
||||
})
|
||||
.then(tokenDetails => {
|
||||
assert.equal(null, tokenDetails);
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle deleting a non-existant token', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
return globalTokenModel.deleteToken('bad-token').then(
|
||||
() => {
|
||||
throw new Error('Expected this delete to throw and error.');
|
||||
},
|
||||
err => {
|
||||
assert.equal(
|
||||
'messaging/' + ERROR_CODES.DELETE_TOKEN_NOT_FOUND,
|
||||
err.code
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,109 +0,0 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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 { assert } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { arrayBufferToBase64 } from '../src/helpers/array-buffer-to-base64';
|
||||
import { base64ToArrayBuffer } from '../src/helpers/base64-to-array-buffer';
|
||||
import { ERROR_CODES } from '../src/models/errors';
|
||||
import { TokenDetailsModel } from '../src/models/token-details-model';
|
||||
import { makeFakeSubscription } from './make-fake-subscription';
|
||||
import { deleteDatabase } from './testing-utils/db-helper';
|
||||
import { compareDetails } from './testing-utils/detail-comparator';
|
||||
|
||||
const EXAMPLE_INPUT = {
|
||||
swScope: '/example-scope',
|
||||
vapidKey: base64ToArrayBuffer(
|
||||
'BNJxw7sCGkGLOUP2cawBaBXRuWZ3lw_PmQMgreLVVvX_b' +
|
||||
'4emEWVURkCF8fUTHEFe2xrEgTt5ilh5xD94v0pFe_I'
|
||||
),
|
||||
subscription: makeFakeSubscription(),
|
||||
fcmSenderId: '1234567',
|
||||
fcmToken: 'qwerty',
|
||||
fcmPushSet: '7654321'
|
||||
};
|
||||
|
||||
describe('Firebase Messaging > TokenDetailsModel.deleteToken()', () => {
|
||||
let globalTokenModel: TokenDetailsModel;
|
||||
|
||||
beforeEach(() => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await globalTokenModel.closeDatabase();
|
||||
await deleteDatabase('fcm_token_details_db');
|
||||
});
|
||||
|
||||
it('should handle no input', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
return globalTokenModel.deleteToken(undefined as any).then(
|
||||
() => {
|
||||
throw new Error('Expected this to throw an error due to no token');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.INVALID_DELETE_TOKEN, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle empty string', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
return globalTokenModel.deleteToken('').then(
|
||||
() => {
|
||||
throw new Error('Expected this to throw an error due to no token');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.INVALID_DELETE_TOKEN, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should delete current token', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
const now = Date.now();
|
||||
const clock = sinon.useFakeTimers(now);
|
||||
return globalTokenModel
|
||||
.saveTokenDetails(EXAMPLE_INPUT)
|
||||
.then(() => {
|
||||
return globalTokenModel.deleteToken(EXAMPLE_INPUT.fcmToken);
|
||||
})
|
||||
.then(details => {
|
||||
compareDetails(EXAMPLE_INPUT, details, now);
|
||||
return globalTokenModel.getTokenDetailsFromToken(
|
||||
EXAMPLE_INPUT.fcmToken
|
||||
);
|
||||
})
|
||||
.then(tokenDetails => {
|
||||
assert.equal(null, tokenDetails);
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle deleting a non-existant token', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
return globalTokenModel.deleteToken('bad-token').then(
|
||||
() => {
|
||||
throw new Error('Expected this delete to throw and error.');
|
||||
},
|
||||
err => {
|
||||
assert.equal(
|
||||
'messaging/' + ERROR_CODES.DELETE_TOKEN_NOT_FOUND,
|
||||
err.code
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,121 +0,0 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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 { assert } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { arrayBufferToBase64 } from '../src/helpers/array-buffer-to-base64';
|
||||
import { base64ToArrayBuffer } from '../src/helpers/base64-to-array-buffer';
|
||||
import { ERROR_CODES } from '../src/models/errors';
|
||||
import { TokenDetailsModel } from '../src/models/token-details-model';
|
||||
import { makeFakeSubscription } from './make-fake-subscription';
|
||||
import { deleteDatabase } from './testing-utils/db-helper';
|
||||
import { compareDetails } from './testing-utils/detail-comparator';
|
||||
|
||||
const EXAMPLE_INPUT = {
|
||||
swScope: '/example-scope',
|
||||
vapidKey: base64ToArrayBuffer(
|
||||
'BNJxw7sCGkGLOUP2cawBaBXRuWZ3lw_PmQMgreLVVvX_b' +
|
||||
'4emEWVURkCF8fUTHEFe2xrEgTt5ilh5xD94v0pFe_I'
|
||||
),
|
||||
subscription: makeFakeSubscription(),
|
||||
fcmSenderId: '1234567',
|
||||
fcmToken: 'qwerty',
|
||||
fcmPushSet: '7654321'
|
||||
};
|
||||
|
||||
describe('Firebase Messaging > TokenDetailsModel.getTokenDetailsFromToken()', () => {
|
||||
let globalTokenModel: TokenDetailsModel;
|
||||
|
||||
beforeEach(() => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await globalTokenModel.closeDatabase();
|
||||
await deleteDatabase('fcm_token_details_db');
|
||||
});
|
||||
|
||||
['', [], {}, true, null, 123].forEach(badInput => {
|
||||
it(`should throw on bad scope input ${JSON.stringify(badInput)}`, () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
return globalTokenModel.getTokenDetailsFromSWScope(badInput as any).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SCOPE, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
['', [], {}, true, null, 123].forEach(badInput => {
|
||||
it(`should throw on bad FCM Token input: '${badInput}'`, () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
return globalTokenModel.getTokenDetailsFromToken(badInput as any).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_TOKEN, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should get from scope', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
const now = Date.now();
|
||||
const clock = sinon.useFakeTimers(now);
|
||||
return globalTokenModel
|
||||
.getTokenDetailsFromSWScope(EXAMPLE_INPUT.swScope)
|
||||
.then(details => {
|
||||
assert.notExists(details);
|
||||
return globalTokenModel.saveTokenDetails(EXAMPLE_INPUT);
|
||||
})
|
||||
.then(() => {
|
||||
return globalTokenModel.getTokenDetailsFromSWScope(
|
||||
EXAMPLE_INPUT.swScope
|
||||
);
|
||||
})
|
||||
.then(details => {
|
||||
assert.exists(details);
|
||||
compareDetails(EXAMPLE_INPUT, details!, now);
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get from token', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
const now = Date.now();
|
||||
const clock = sinon.useFakeTimers(now);
|
||||
return globalTokenModel
|
||||
.getTokenDetailsFromToken(EXAMPLE_INPUT.fcmToken)
|
||||
.then(details => {
|
||||
assert.notExists(details);
|
||||
return globalTokenModel.saveTokenDetails(EXAMPLE_INPUT);
|
||||
})
|
||||
.then(() => {
|
||||
return globalTokenModel.getTokenDetailsFromToken(
|
||||
EXAMPLE_INPUT.fcmToken
|
||||
);
|
||||
})
|
||||
.then(details => {
|
||||
assert.exists(details);
|
||||
compareDetails(EXAMPLE_INPUT, details!, now);
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,171 +0,0 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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 { assert } from 'chai';
|
||||
import { base64ToArrayBuffer } from '../src/helpers/base64-to-array-buffer';
|
||||
import { ERROR_CODES } from '../src/models/errors';
|
||||
import { TokenDetailsModel } from '../src/models/token-details-model';
|
||||
import { makeFakeSubscription } from './make-fake-subscription';
|
||||
import { deleteDatabase } from './testing-utils/db-helper';
|
||||
|
||||
const EXAMPLE_INPUT = {
|
||||
swScope: '/example-scope',
|
||||
vapidKey: base64ToArrayBuffer(
|
||||
'BNJxw7sCGkGLOUP2cawBaBXRuWZ3lw_PmQMgreLVVvX_b' +
|
||||
'4emEWVURkCF8fUTHEFe2xrEgTt5ilh5xD94v0pFe_I'
|
||||
),
|
||||
subscription: makeFakeSubscription(),
|
||||
fcmSenderId: '1234567',
|
||||
fcmToken: 'qwerty',
|
||||
fcmPushSet: '7654321'
|
||||
};
|
||||
|
||||
describe('Firebase Messaging > TokenDetailsModel.saveToken()', () => {
|
||||
let globalTokenModel: TokenDetailsModel;
|
||||
|
||||
beforeEach(() => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await globalTokenModel.closeDatabase();
|
||||
await deleteDatabase('fcm_token_details_db');
|
||||
});
|
||||
|
||||
it('should throw on bad input', () => {
|
||||
const badInputs = ['', [], {}, true, null, 123];
|
||||
|
||||
const promises = badInputs.map((badInput: any) => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
const validInput = { ...EXAMPLE_INPUT };
|
||||
validInput.swScope = badInput;
|
||||
return globalTokenModel.saveTokenDetails(validInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SCOPE, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad vapid key input', () => {
|
||||
const badInputs = ['', [], {}, true, null, 123];
|
||||
|
||||
const promises = badInputs.map((badInput: any) => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
const validInput = { ...EXAMPLE_INPUT };
|
||||
validInput.vapidKey = badInput;
|
||||
return globalTokenModel.saveTokenDetails(validInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_VAPID_KEY, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad subscription input', () => {
|
||||
const badInputs = ['', [], {}, true, null, 123];
|
||||
|
||||
const promises = badInputs.map((badInput: any) => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
const validInput = { ...EXAMPLE_INPUT };
|
||||
validInput.subscription = badInput;
|
||||
return globalTokenModel.saveTokenDetails(validInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SUBSCRIPTION, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad send id input', () => {
|
||||
const badInputs = ['', [], {}, true, null, 123];
|
||||
|
||||
const promises = badInputs.map((badInput: any) => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
const validInput = { ...EXAMPLE_INPUT };
|
||||
validInput.fcmSenderId = badInput;
|
||||
return globalTokenModel.saveTokenDetails(validInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SENDER_ID, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad token input', () => {
|
||||
const badInputs = ['', [], {}, true, null, 123];
|
||||
|
||||
const promises = badInputs.map((badInput: any) => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
const validInput = { ...EXAMPLE_INPUT };
|
||||
validInput.fcmToken = badInput;
|
||||
return globalTokenModel.saveTokenDetails(validInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_TOKEN, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should throw on bad pushSet input', () => {
|
||||
const badInputs = ['', [], {}, true, null, 123];
|
||||
|
||||
const promises = badInputs.map((badInput: any) => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
const validInput = { ...EXAMPLE_INPUT };
|
||||
validInput.fcmPushSet = badInput;
|
||||
return globalTokenModel.saveTokenDetails(validInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_PUSH_SET, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
it('should save valid details', () => {
|
||||
globalTokenModel = new TokenDetailsModel();
|
||||
return globalTokenModel.saveTokenDetails(EXAMPLE_INPUT);
|
||||
});
|
||||
});
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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 { assert } from 'chai';
|
||||
import { base64ToArrayBuffer } from '../src/helpers/base64-to-array-buffer';
|
||||
import { ERROR_CODES } from '../src/models/errors';
|
||||
import { VapidDetailsModel } from '../src/models/vapid-details-model';
|
||||
import { deleteDatabase } from './testing-utils/db-helper';
|
||||
|
||||
const EXAMPLE_SCOPE = '/example-scope';
|
||||
const EXAMPLE_VAPID_STRING =
|
||||
'BNJxw7sCGkGLOUP2cawBaBXRuWZ3lw_PmQMgreLVVvX_b' +
|
||||
'4emEWVURkCF8fUTHEFe2xrEgTt5ilh5xD94v0pFe_I';
|
||||
const EXAMPLE_VAPID_KEY = base64ToArrayBuffer(EXAMPLE_VAPID_STRING);
|
||||
|
||||
describe('Firebase Messaging > VapidDetailsModel.deleteToken()', () => {
|
||||
let vapidModel: VapidDetailsModel;
|
||||
|
||||
beforeEach(() => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await vapidModel.closeDatabase();
|
||||
await deleteDatabase('fcm_vapid_details_db');
|
||||
});
|
||||
|
||||
it('should throw on bad scope input', () => {
|
||||
const badInputs = ['', [], {}, true, null, 123];
|
||||
badInputs.forEach(badInput => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel
|
||||
.saveVapidDetails(badInput as any, EXAMPLE_VAPID_KEY)
|
||||
.then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SCOPE, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete non existant details', () => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel.deleteVapidDetails(EXAMPLE_SCOPE).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal(
|
||||
'messaging/' + ERROR_CODES.DELETE_SCOPE_NOT_FOUND,
|
||||
err.code
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should save and delete details', () => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel
|
||||
.saveVapidDetails(EXAMPLE_SCOPE, EXAMPLE_VAPID_KEY)
|
||||
.then(() => {
|
||||
return vapidModel.deleteVapidDetails(EXAMPLE_SCOPE);
|
||||
})
|
||||
.then(vapidKey => {
|
||||
assert.deepEqual(vapidKey, EXAMPLE_VAPID_KEY);
|
||||
return vapidModel.getVapidFromSWScope(EXAMPLE_SCOPE);
|
||||
})
|
||||
.then(vapid => {
|
||||
assert.equal(vapid, null);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,68 +0,0 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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 { assert } from 'chai';
|
||||
import { base64ToArrayBuffer } from '../src/helpers/base64-to-array-buffer';
|
||||
import { ERROR_CODES } from '../src/models/errors';
|
||||
import { VapidDetailsModel } from '../src/models/vapid-details-model';
|
||||
import { deleteDatabase } from './testing-utils/db-helper';
|
||||
|
||||
const EXAMPLE_SCOPE = '/example-scope';
|
||||
const EXAMPLE_VAPID_STRING =
|
||||
'BNJxw7sCGkGLOUP2cawBaBXRuWZ3lw_PmQMgreLVVvX_b' +
|
||||
'4emEWVURkCF8fUTHEFe2xrEgTt5ilh5xD94v0pFe_I';
|
||||
const EXAMPLE_VAPID_KEY = base64ToArrayBuffer(EXAMPLE_VAPID_STRING);
|
||||
|
||||
describe('Firebase Messaging > VapidDetailsModel.getVapidFromSWScope()', () => {
|
||||
let vapidModel: VapidDetailsModel;
|
||||
|
||||
beforeEach(() => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await vapidModel.closeDatabase();
|
||||
await deleteDatabase('fcm_vapid_details_db');
|
||||
});
|
||||
|
||||
const badInputs = ['', [], {}, true, null, 123];
|
||||
badInputs.forEach(badInput => {
|
||||
it(`should throw on bad scope input ${badInput}`, () => {
|
||||
return vapidModel.getVapidFromSWScope(badInput as any).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SCOPE, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should get vapid key', () => {
|
||||
return vapidModel
|
||||
.getVapidFromSWScope(EXAMPLE_SCOPE)
|
||||
.then(vapidKey => {
|
||||
assert.equal(null, vapidKey);
|
||||
return vapidModel.saveVapidDetails(EXAMPLE_SCOPE, EXAMPLE_VAPID_KEY);
|
||||
})
|
||||
.then(() => {
|
||||
return vapidModel.getVapidFromSWScope(EXAMPLE_SCOPE);
|
||||
})
|
||||
.then(vapidKey => {
|
||||
assert.deepEqual(EXAMPLE_VAPID_KEY, vapidKey);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,76 +0,0 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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 { assert } from 'chai';
|
||||
import { base64ToArrayBuffer } from '../src/helpers/base64-to-array-buffer';
|
||||
import { ERROR_CODES } from '../src/models/errors';
|
||||
import { VapidDetailsModel } from '../src/models/vapid-details-model';
|
||||
import { deleteDatabase } from './testing-utils/db-helper';
|
||||
|
||||
const EXAMPLE_SCOPE = '/example-scope';
|
||||
const EXAMPLE_VAPID_STRING =
|
||||
'BNJxw7sCGkGLOUP2cawBaBXRuWZ3lw_PmQMgreLVVvX_b' +
|
||||
'4emEWVURkCF8fUTHEFe2xrEgTt5ilh5xD94v0pFe_I';
|
||||
const EXAMPLE_VAPID_KEY = base64ToArrayBuffer(EXAMPLE_VAPID_STRING);
|
||||
|
||||
describe('Firebase Messaging > VapidDetailsModel.saveVapidDetails()', () => {
|
||||
let vapidModel: VapidDetailsModel;
|
||||
|
||||
beforeEach(() => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await vapidModel.closeDatabase();
|
||||
await deleteDatabase('fcm_vapid_details_db');
|
||||
});
|
||||
|
||||
it('should throw on bad scope input', () => {
|
||||
const badInputs = ['', [], {}, true, null, 123];
|
||||
badInputs.forEach(badInput => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel
|
||||
.saveVapidDetails(badInput as any, EXAMPLE_VAPID_KEY)
|
||||
.then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SCOPE, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw on bad vapid input', () => {
|
||||
const badInputs = ['', [], {}, true, null, 123];
|
||||
badInputs.forEach(badInput => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel.saveVapidDetails(EXAMPLE_SCOPE, badInput as any).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_VAPID_KEY, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should save valid details', () => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel.saveVapidDetails(EXAMPLE_SCOPE, EXAMPLE_VAPID_KEY);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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 { assert } from 'chai';
|
||||
import { base64ToArrayBuffer } from '../src/helpers/base64-to-array-buffer';
|
||||
import { ERROR_CODES } from '../src/models/errors';
|
||||
import { VapidDetailsModel } from '../src/models/vapid-details-model';
|
||||
import { deleteDatabase } from './testing-utils/db-helper';
|
||||
|
||||
const EXAMPLE_SCOPE = '/example-scope';
|
||||
const EXAMPLE_VAPID_STRING =
|
||||
'BNJxw7sCGkGLOUP2cawBaBXRuWZ3lw_PmQMgreLVVvX_b' +
|
||||
'4emEWVURkCF8fUTHEFe2xrEgTt5ilh5xD94v0pFe_I';
|
||||
const EXAMPLE_VAPID_KEY = base64ToArrayBuffer(EXAMPLE_VAPID_STRING);
|
||||
|
||||
const BAD_INPUTS: any[] = ['', [], {}, true, null, 123];
|
||||
|
||||
describe('Firebase Messaging > VapidDetailsModel.saveVapidDetails()', () => {
|
||||
let vapidModel: VapidDetailsModel;
|
||||
|
||||
beforeEach(() => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await vapidModel.closeDatabase();
|
||||
await deleteDatabase('fcm_vapid_details_db');
|
||||
});
|
||||
|
||||
describe('saveVapidDetails', () => {
|
||||
it('should throw on bad scope input', () => {
|
||||
for (const badInput of BAD_INPUTS) {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel.saveVapidDetails(badInput, EXAMPLE_VAPID_KEY).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SCOPE, err.code);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw on bad vapid input', () => {
|
||||
for (const badInput of BAD_INPUTS) {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel.saveVapidDetails(EXAMPLE_SCOPE, badInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_VAPID_KEY, err.code);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should save valid details', () => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel.saveVapidDetails(EXAMPLE_SCOPE, EXAMPLE_VAPID_KEY);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVapidFromSWScope', () => {
|
||||
for (const badInput of BAD_INPUTS) {
|
||||
it(`should throw on bad scope input ${badInput}`, () => {
|
||||
return vapidModel.getVapidFromSWScope(badInput).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SCOPE, err.code);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
it('should get vapid key', () => {
|
||||
return vapidModel
|
||||
.getVapidFromSWScope(EXAMPLE_SCOPE)
|
||||
.then(vapidKey => {
|
||||
assert.equal(null, vapidKey);
|
||||
return vapidModel.saveVapidDetails(EXAMPLE_SCOPE, EXAMPLE_VAPID_KEY);
|
||||
})
|
||||
.then(() => {
|
||||
return vapidModel.getVapidFromSWScope(EXAMPLE_SCOPE);
|
||||
})
|
||||
.then(vapidKey => {
|
||||
assert.deepEqual(EXAMPLE_VAPID_KEY, vapidKey);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteToken', () => {
|
||||
it('should throw on bad scope input', () => {
|
||||
for (const badInput of BAD_INPUTS) {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel.saveVapidDetails(badInput, EXAMPLE_VAPID_KEY).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal('messaging/' + ERROR_CODES.BAD_SCOPE, err.code);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should delete non existant details', () => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel.deleteVapidDetails(EXAMPLE_SCOPE).then(
|
||||
() => {
|
||||
throw new Error('Expected promise to reject');
|
||||
},
|
||||
err => {
|
||||
assert.equal(
|
||||
'messaging/' + ERROR_CODES.DELETE_SCOPE_NOT_FOUND,
|
||||
err.code
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should save and delete details', () => {
|
||||
vapidModel = new VapidDetailsModel();
|
||||
return vapidModel
|
||||
.saveVapidDetails(EXAMPLE_SCOPE, EXAMPLE_VAPID_KEY)
|
||||
.then(() => {
|
||||
return vapidModel.deleteVapidDetails(EXAMPLE_SCOPE);
|
||||
})
|
||||
.then(vapidKey => {
|
||||
assert.deepEqual(vapidKey, EXAMPLE_VAPID_KEY);
|
||||
return vapidModel.getVapidFromSWScope(EXAMPLE_SCOPE);
|
||||
})
|
||||
.then(vapid => {
|
||||
assert.equal(vapid, null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user