diff --git a/src/pages/home/home.ts b/src/pages/home/home.ts index a89febc87..785a1e371 100644 --- a/src/pages/home/home.ts +++ b/src/pages/home/home.ts @@ -69,7 +69,6 @@ export class HomePage { public showIntegration: any; private isNW: boolean; - private isWindowsPhoneApp: boolean; private updatingWalletId: object; private zone: any; @@ -99,7 +98,6 @@ export class HomePage { this.updatingWalletId = {}; this.cachedBalanceUpdateOn = ''; this.isNW = this.platformProvider.isNW; - this.isWindowsPhoneApp = this.platformProvider.isWP; this.showReorderBtc = false; this.showReorderBch = false; this.zone = new NgZone({ enableLongStackTrace: false }); @@ -197,10 +195,6 @@ export class HomePage { private checkFeedbackInfo() { this.persistenceProvider.getFeedbackInfo().then((info: any) => { - if (this.isWindowsPhoneApp) { - this.showRateCard = false; - return; - } if (!info) { this.initFeedBackInfo(); } else { diff --git a/src/pages/send/confirm/confirm.ts b/src/pages/send/confirm/confirm.ts index a15d91482..9188bc284 100644 --- a/src/pages/send/confirm/confirm.ts +++ b/src/pages/send/confirm/confirm.ts @@ -54,7 +54,6 @@ export class ConfirmPage { // Platform info public isCordova: boolean; - public isWindowsPhoneApp: boolean; // custom fee flag public usingCustomFee: boolean = false; @@ -86,7 +85,6 @@ export class ConfirmPage { this.config = this.configProvider.get(); this.configFeeLevel = this.config.wallet.settings.feeLevel ? this.config.wallet.settings.feeLevel : 'normal'; this.isCordova = this.platformProvider.isCordova; - this.isWindowsPhoneApp = this.platformProvider.isCordova && this.platformProvider.isWP; } ionViewWillEnter() { @@ -253,23 +251,11 @@ export class ConfirmPage { private setButtonText(isMultisig: boolean, isPayPro: boolean): void { if (isPayPro) { - // if (this.isCordova && !this.isWindowsPhoneApp) { - // this.buttonText = this.translate.instant('Slide to pay'); - // } else { this.buttonText = this.translate.instant('Click to pay'); - // } } else if (isMultisig) { - // if (this.isCordova && !this.isWindowsPhoneApp) { - // this.buttonText = this.translate.instant('Slide to accept'); - // } else { this.buttonText = this.translate.instant('Click to accept'); - // } } else { - // if (this.isCordova && !this.isWindowsPhoneApp) { - // this.buttonText = this.translate.instant('Slide to send'); - // } else { this.buttonText = this.translate.instant('Click to send'); - // } } } diff --git a/src/pages/settings/settings.html b/src/pages/settings/settings.html index cae3ba11a..3eb4957f3 100644 --- a/src/pages/settings/settings.html +++ b/src/pages/settings/settings.html @@ -24,7 +24,7 @@ Send feedback - + Share {{ appName }} diff --git a/src/pages/settings/settings.ts b/src/pages/settings/settings.ts index f099ed4cc..51b9fabba 100644 --- a/src/pages/settings/settings.ts +++ b/src/pages/settings/settings.ts @@ -43,7 +43,6 @@ export class SettingsPage { public config: any; public selectedAlternative: any; public isCordova: boolean; - public isWindowsPhoneApp: boolean; public lockMethod: string; public exchangeServices: any[] = []; public bitpayCardEnabled: boolean = false; @@ -65,7 +64,6 @@ export class SettingsPage { this.walletsBch = []; this.walletsBtc = []; this.isCordova = this.platformProvider.isCordova; - this.isWindowsPhoneApp = this.platformProvider.isWP; } ionViewDidLoad() { diff --git a/src/pages/txp-details/txp-details.ts b/src/pages/txp-details/txp-details.ts index 88e9c8b82..41f347b06 100644 --- a/src/pages/txp-details/txp-details.ts +++ b/src/pages/txp-details/txp-details.ts @@ -42,7 +42,6 @@ export class TxpDetailsPage { private GLIDERA_LOCK_TIME: number; private countDown: any; private isCordova: boolean; - private isWindowsPhoneApp: boolean; constructor( private navParams: NavParams, @@ -70,7 +69,6 @@ export class TxpDetailsPage { this.currentSpendUnconfirmed = config.spendUnconfirmed; this.loading = false; this.isCordova = this.platformProvider.isCordova; - this.isWindowsPhoneApp = this.platformProvider.isCordova && this.platformProvider.isWP; this.copayers = this.wallet.status.wallet.copayers; this.copayerId = this.wallet.credentials.copayerId; this.isShared = this.wallet.credentials.n > 1; @@ -120,18 +118,10 @@ export class TxpDetailsPage { }).length == this.tx.requiredSignatures - 1; if (lastSigner) { - // if (this.isCordova && !this.isWindowsPhoneApp) { - // this.buttonText = this.translate.instant('Slide to send'); - // } else { this.buttonText = this.translate.instant('Click to send'); - // } this.successText = this.translate.instant('Payment Sent'); } else { - // if (this.isCordova && !this.isWindowsPhoneApp) { - // this.buttonText = this.translate.instant('Slide to accept'); - // } else { this.buttonText = this.translate.instant('Click to accept'); - // } this.successText = this.translate.instant('Payment Accepted'); } } diff --git a/src/providers/app/app.spec.ts b/src/providers/app/app.spec.ts new file mode 100644 index 000000000..503d2ffc3 --- /dev/null +++ b/src/providers/app/app.spec.ts @@ -0,0 +1,45 @@ +import { TestBed, inject, async } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { XHRBackend, Response, ResponseOptions } from '@angular/http'; +import { MockBackend, MockConnection } from '@angular/http/testing'; +import { AppProvider } from './app'; +import { NgLoggerModule, Level } from '@nsalaun/ng-logger'; +import { Logger } from '../../providers/logger/logger'; +import { + TranslateModule, + TranslateService, + TranslateLoader, + TranslateFakeLoader +} from '@ngx-translate/core'; +import { LanguageProvider } from '../../providers/language/language'; +import { ConfigProvider } from '../../providers/config/config'; +import { PersistenceProvider } from '../../providers/persistence/persistence'; +import { PlatformProvider } from '../platform/platform'; +import { Platform } from 'ionic-angular'; +import { File } from '@ionic-native/file'; + +describe('AppProvider', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + NgLoggerModule.forRoot(Level.LOG), + TranslateModule.forRoot({ + loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } + }) + ], + providers: [ + AppProvider, + Logger, + LanguageProvider, + ConfigProvider, + PersistenceProvider, + PlatformProvider, + Platform, + File, + {provide: XHRBackend, useClass: MockBackend} + ] + }); + + }); +}); diff --git a/src/providers/config/config.spec.ts b/src/providers/config/config.spec.ts new file mode 100644 index 000000000..4ac397aab --- /dev/null +++ b/src/providers/config/config.spec.ts @@ -0,0 +1,59 @@ +import { TestBed, inject, async } from '@angular/core/testing'; +import { NgLoggerModule, Level } from '@nsalaun/ng-logger'; +import { Logger } from '../../providers/logger/logger'; +import { + TranslateModule, + TranslateService, + TranslateLoader, + TranslateFakeLoader +} from '@ngx-translate/core'; +import { PersistenceProvider } from '../../providers/persistence/persistence'; +import { PlatformProvider } from '../platform/platform'; +import { Platform } from 'ionic-angular'; +import { File } from '@ionic-native/file'; +import { ConfigProvider } from './config'; + +describe('Config Provider', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + NgLoggerModule.forRoot(Level.LOG), + TranslateModule.forRoot({ + loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } + }) + ], + providers: [ + ConfigProvider, + PersistenceProvider, + PlatformProvider, + Platform, + File, + Logger + ] + }); + }); + + it('should get defaults', inject([ConfigProvider], (configProvider: ConfigProvider) => { + let defaults = configProvider.getDefaults(); + expect(defaults).not.toBeNull(); + })); + + it('should get cache', inject([ConfigProvider], (configProvider: ConfigProvider) => { + let cache = configProvider.get(); + expect(cache).not.toBeNull(); + console.log('cache', cache); + })); + + it('should load', inject([ConfigProvider], (configProvider: ConfigProvider) => { + configProvider.load(); + })); + + it('should set options with an object', inject([ConfigProvider], (configProvider: ConfigProvider) => { + let defaults = configProvider.getDefaults(); + //configProvider.set(defaults); + })); + + it('should set options with a string', inject([ConfigProvider], (configProvider: ConfigProvider) => { + //configProvider.set('{"option1":"a","option2":"b"}'); + })); +}); diff --git a/src/providers/config/config.ts b/src/providers/config/config.ts index b668d5020..46fcd5805 100644 --- a/src/providers/config/config.ts +++ b/src/providers/config/config.ts @@ -235,7 +235,10 @@ export class ConfigProvider { }); } - public set(newOpts: object) { + /** + * @param newOpts object or string (JSON) + */ + public set(newOpts: any) { let config = _.cloneDeep(configDefault); if (_.isString(newOpts)) { diff --git a/src/providers/derivation-path-helper/derivation-path-helper.spec.ts b/src/providers/derivation-path-helper/derivation-path-helper.spec.ts index 786a9aa21..bd2c7d9a2 100644 --- a/src/providers/derivation-path-helper/derivation-path-helper.spec.ts +++ b/src/providers/derivation-path-helper/derivation-path-helper.spec.ts @@ -63,8 +63,16 @@ describe('Derivation Path Helper Provider', () => { /* Unsupported paths */ it('should fail trying to parse an unsupported derivation path', () => { - const result: any = service.parse("p/145'/0'/0'"); - expect(result).toBeDefined(); - expect(result).toBeFalsy; + let result: any = service.parse("p/145'/0'/0'"); + expect(result).toBe(false); + + result = service.parse("m/145'/0'/0'"); + expect(result).toBe(false); + + result = service.parse("m/44'/9'/0'"); + expect(result).toBe(false); + + result = service.parse("m/44'/0'/a'"); + expect(result).toBe(false); }); }); diff --git a/src/providers/derivation-path-helper/derivation-path-helper.ts b/src/providers/derivation-path-helper/derivation-path-helper.ts index 763a33306..19ea5ff6d 100644 --- a/src/providers/derivation-path-helper/derivation-path-helper.ts +++ b/src/providers/derivation-path-helper/derivation-path-helper.ts @@ -10,7 +10,7 @@ export class DerivationPathHelperProvider { this.defaultTestnet = "m/44'/1'/0'"; } - parse(str: string) { + parse(str: string): any { var arr = str.split('/'); var ret = { derivationStrategy: '', @@ -56,4 +56,4 @@ export class DerivationPathHelperProvider { return ret; }; -} \ No newline at end of file +} diff --git a/src/providers/logger/logger.spec.ts b/src/providers/logger/logger.spec.ts new file mode 100644 index 000000000..a00161d37 --- /dev/null +++ b/src/providers/logger/logger.spec.ts @@ -0,0 +1,148 @@ +import { TestBed, getTestBed, inject, async } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { NgLoggerModule, Level } from '@nsalaun/ng-logger'; +import { Logger } from '../../providers/logger/logger'; +import { + TranslateModule, + TranslateService, + TranslateLoader, + TranslateFakeLoader +} from '@ngx-translate/core'; +import { Platform } from 'ionic-angular'; + +fdescribe('LoggerProvider', () => { + let injector: TestBed; + let service: Logger; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + NgLoggerModule.forRoot(Level.LOG), + TranslateModule.forRoot({ + loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } + }) + ], + providers: [ + Logger, + Platform + ] + }); + injector = getTestBed(); + service = injector.get(Logger); + httpMock = injector.get(HttpTestingController); + }); + + it('should be able use optional params for errors', () => { + service.error('So long and thanks for all the fish!', 'dolphins', 'mice', 'humans'); + }); + + it('should be able use optional params for debug', () => { + service.debug('The answer to life, the universe, and everything is 42.', 'dolphins', 'mice', 'humans'); + }); + + it('should be able use optional params for warnings', () => { + service.warn('Mostly harmless', 'dolphins', 'mice', 'humans'); + }); + + it('should be able use optional params for info', () => { + service.info("Who's going to dinner at the restaurant at the end of the universe?", 'Arthur', 'Zaphod', 'Trillian'); + }); + + it('should get levels', () => { + const levels = service.getLevels(); + expect(levels).toEqual([ + { level: 'error', weight: 1, label: 'Error' }, + { level: 'warn', weight: 2, label: 'Warning' }, + { level: 'info', weight: 3, label: 'Info', default: true }, + { level: 'debug', weight: 4, label: 'Debug' } + ]); + }); + + it('should get weight', () => { + let weight = service.getWeight(1); + expect(weight).toEqual( + { level: 'error', weight: 1, label: 'Error' } + ); + weight = service.getWeight(2); + expect(weight).toEqual( + { level: 'warn', weight: 2, label: 'Warning' } + ); + weight = service.getWeight(3); + expect(weight).toEqual( + { level: 'info', weight: 3, label: 'Info', default: true } + ); + weight = service.getWeight(4); + expect(weight).toEqual( + { level: 'debug', weight: 4, label: 'Debug' } + ); + }); + + it('should get the defaul weight', () => { + let defaultWeight = service.getDefaultWeight(); + expect(defaultWeight).toEqual( + { level: 'info', weight: 3, label: 'Info', default: true } + ); + }); + + it('should get logs by filtered weight', () => { + let filteredLogs; + + service.debug('Heart of Gold'); + service.debug('Volgon ship'); + filteredLogs = service.get(4); + expect(filteredLogs.length).toBe(2); + + service.info("Don't panic"); + service.info("Take peanuts"); + service.info("Don't forget a towel"); + filteredLogs = service.get(3); + expect(filteredLogs.length).toBe(3); + console.log(filteredLogs); + + service.error('Planet not found'); + filteredLogs = service.get(1); + expect(filteredLogs.length).toBe(1); + }); + + it('should get logs when not filtered by weight', () => { + service.warn('Beware the Bugblatter Beast of Traal'); + service.error('Heart of Gold has been stolen'); + service.info('Zaphod is President'); + service.debug('Marvin is depressed'); + + let logs = service.get(); + + expect(logs[0].msg).toEqual('Beware the Bugblatter Beast of Traal'); + expect(logs[1].msg).toEqual('Heart of Gold has been stolen'); + expect(logs[2].msg).toEqual('Zaphod is President'); + expect(logs[3].msg).toEqual('Marvin is depressed'); + }); + + it('should process args', () => { + let processedArgs = service.processingArgs(['bulldozer', 'bathrobe', 'satchel']); + expect(processedArgs).toEqual('bulldozer bathrobe satchel'); + + processedArgs = service.processingArgs('babel fish'); + expect(processedArgs).toEqual('b a b e l f i s h'); + + processedArgs = service.processingArgs(['babel', undefined, 'fish']); + expect(processedArgs).toEqual('babel undefined fish'); + + processedArgs = service.processingArgs(['babel', false, 'fish']); + expect(processedArgs).toEqual('babel null fish'); + + processedArgs = service.processingArgs(['babel', { message: 'Save the Humans' }, 'fish']); + expect(processedArgs).toEqual('babel Save the Humans fish'); + + processedArgs = service.processingArgs(['babel', { 'improbability': 'infinite' }, 'fish']); + expect(processedArgs).toEqual('babel {"improbability":"infinite"} fish'); + + // cyclical reference; yeah, baby! to break JSON.stringify + let a = { b: { a: { } } }; + a.b.a = a; + processedArgs = service.processingArgs(['babel', a, 'fish']); + expect(processedArgs).toEqual('babel undefined fish'); + }); +}); diff --git a/src/providers/logger/logger.ts b/src/providers/logger/logger.ts index 876afa914..0c214fd24 100644 --- a/src/providers/logger/logger.ts +++ b/src/providers/logger/logger.ts @@ -50,7 +50,7 @@ export class Logger { this.add('warn', args); } - public getLevels(): void { + public getLevels(): any { return this.levels; } @@ -75,6 +75,10 @@ export class Logger { }); } + /** + * Returns logs of <= to filteredWeight + * @param {number} filteredWeight Weight (1-4) to use when filtering logs. optional + */ public get(filterWeight?: number): any { let filteredLogs = this.logs; if (filterWeight != undefined) { diff --git a/src/providers/persistence/persistence.spec.ts b/src/providers/persistence/persistence.spec.ts index 5c7715f0e..d8de9948a 100644 --- a/src/providers/persistence/persistence.spec.ts +++ b/src/providers/persistence/persistence.spec.ts @@ -39,6 +39,43 @@ describe('Persistence Provider', () => { expect(persistenceProvider.storage.constructor.name).toBe('FileStorage'); }); }); + + describe('without local storage', () => { + let localStorageBackup; + + beforeEach(() => { + // remove window.localStorage + localStorageBackup = window.localStorage; + console.log('before clearing', window.localStorage); + Object.defineProperties(window, { + localStorage: { + value: null, + writable: true + } + }); + console.log('after clearing', window.localStorage); + }); + + afterEach(() => { + // restore window.localStorage + console.log('before restoring', window.localStorage); + Object.defineProperties(window, { + localStorage: { + value: localStorageBackup, + writable: false + } + }); + console.log('after restoring', window.localStorage); + }); + + it('should throw an error if local storage is not available', () => { + expect(() => { + platformMock.isCordova = false; + createAndLoad(); + } ).toThrow(new Error('localstorage not available')); + }); + }); + describe('When platform is not Cordova', () => { beforeEach(() => { platformMock.isCordova = false; @@ -73,5 +110,24 @@ describe('Persistence Provider', () => { expect(err.message).toEqual('Key already exists'); }); }); + + it('should be able to delete a profile', (done) => { + let p = { name: 'My profile' }; + persistenceProvider + .storeNewProfile(p) + .catch(err => expect(err).toBeNull) + .then(() => { + return persistenceProvider.getProfile(); + }) + .then(profile => { + expect(typeof profile).toEqual('object'); + expect(profile.name).toEqual('My profile'); + return persistenceProvider.deleteProfile(); + }).then(() => { + return persistenceProvider.getProfile(); + }).then(profile => { + expect(profile).toBeNull(); + }).then(done); + }); }); }); diff --git a/src/providers/platform/platform.spec.ts b/src/providers/platform/platform.spec.ts new file mode 100644 index 000000000..0267ced85 --- /dev/null +++ b/src/providers/platform/platform.spec.ts @@ -0,0 +1,127 @@ +import { PlatformProvider } from './platform'; + +import { TestBed, getTestBed, inject, async } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { NgLoggerModule, Level } from '@nsalaun/ng-logger'; +import { Logger } from '../../providers/logger/logger'; +import { + TranslateModule, + TranslateService, + TranslateLoader, + TranslateFakeLoader +} from '@ngx-translate/core'; +import { Platform } from 'ionic-angular'; + +describe('PlatformProvider', () => { + let injector: TestBed; + let service: PlatformProvider; + let httpMock: HttpTestingController; + let userAgent; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + NgLoggerModule.forRoot(Level.LOG), + TranslateModule.forRoot({ + loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } + }) + ], + providers: [ + PlatformProvider, + Logger, + Platform + ] + }); + injector = getTestBed(); + service = injector.get(PlatformProvider); + httpMock = injector.get(HttpTestingController); + }); + + it('should get browser name', () => { + let name = service.getBrowserName(); + expect(name).toBe('chrome'); + }); + + it('should return "unknown" if browser is unknown', () => { + // change userAgent + userAgent = window.navigator.userAgent; + console.log('before changing userAgent', window.navigator.userAgent); + Object.defineProperties(window.navigator, { + userAgent: { + value: 'someUnknownCoolBrowser v1.0', + writable: true + } + }); + console.log('after changing userAgent', window.navigator.userAgent); + + let name = service.getBrowserName(); + expect(name).toBe('unknown'); + + // restore userAgent + console.log('before restoring userAgent', window.navigator.userAgent); + Object.defineProperties(window.navigator, { + userAgent: { + value: userAgent, + writable: false + } + }); + console.log('after restoring userAgent', window.navigator.userAgent); + }); +}); + +describe('PlatformProvider without navigator', () => { + let injector: TestBed; + let service: PlatformProvider; + let httpMock: HttpTestingController; + let navi; + + beforeEach(() => { + navi = window.navigator; + console.log('before changing navigator', window.navigator); + // change navigator + Object.defineProperties(window, { + navigator: { + value: null, + writable: true + } + }); + console.log('after changing navigator', window.navigator); + }); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + NgLoggerModule.forRoot(Level.LOG), + TranslateModule.forRoot({ + loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } + }) + ], + providers: [ + PlatformProvider, + Logger, + Platform + ] + }); + injector = getTestBed(); + service = injector.get(PlatformProvider); + httpMock = injector.get(HttpTestingController); + }); + + afterEach(() => { + console.log('before restoring navigator', window.navigator); + Object.defineProperties(window, { + navigator: { + value: navi, + writable: false + } + }); + console.log('after restoring navigator', window.navigator); + }); + + it('should have a dummy user agent', () => { + let ua = service.ua; + expect(ua).toBe('dummy user-agent'); + }); +}); diff --git a/src/providers/platform/platform.ts b/src/providers/platform/platform.ts index 3c252af08..c0c776a14 100644 --- a/src/providers/platform/platform.ts +++ b/src/providers/platform/platform.ts @@ -6,7 +6,6 @@ import { Platform } from 'ionic-angular'; export class PlatformProvider { public isAndroid: boolean; public isIOS: boolean; - public isWP: boolean; public isSafari: boolean; public isCordova: boolean; public isNW: boolean; @@ -30,7 +29,6 @@ export class PlatformProvider { this.isAndroid = this.platform.is('android'); this.isIOS = this.platform.is('ios'); - this.isWP = this.platform.is('windows') && this.platform.is('mobile'); this.ua = ua; this.isCordova = this.platform.is('cordova'); this.isNW = this.isNodeWebkit(); @@ -54,13 +52,10 @@ export class PlatformProvider { } public isNodeWebkit(): boolean { - let isNode = (typeof process !== "undefined" && typeof require !== "undefined"); - if (isNode) { - try { - return (typeof require('nw.gui') !== "undefined"); - } catch (e) { - return false; - } + try { + return (typeof require('nw.gui') !== "undefined"); + } catch (e) { + return false; } } } diff --git a/src/providers/popup/popup.spec.ts b/src/providers/popup/popup.spec.ts new file mode 100644 index 000000000..878b5bbc7 --- /dev/null +++ b/src/providers/popup/popup.spec.ts @@ -0,0 +1,48 @@ +import { TestBed, inject, async } from '@angular/core/testing'; +import { + AlertController, + App, + Config, + Platform +} from 'ionic-angular'; +import { NgLoggerModule, Level } from '@nsalaun/ng-logger'; +import { Logger } from '../../providers/logger/logger'; +import { + TranslateModule, + TranslateService, + TranslateLoader, + TranslateFakeLoader +} from '@ngx-translate/core'; +import { PopupProvider } from './popup'; + +describe('PopupProvider', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + NgLoggerModule.forRoot(Level.LOG), + TranslateModule.forRoot({ + loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } + }) + ], + providers: [ + PopupProvider, + AlertController, + App, + Config, + Platform, + Logger, + TranslateService + ] + }); + }); + + it('should exist', inject([PopupProvider], (popupProvider: PopupProvider) => { + expect(popupProvider).not.toBeUndefined(); + })); + + it('should have an alert', inject([PopupProvider], (popupProvider: PopupProvider) => { + spyOn(popupProvider, 'ionicAlert'); + popupProvider.ionicAlert('title', 'subtitle', 'ok text'); + expect(popupProvider.ionicAlert).toHaveBeenCalledWith('title', 'subtitle', 'ok text'); + })); +}); diff --git a/src/providers/push-notifications/push-notifications.ts b/src/providers/push-notifications/push-notifications.ts index 00a911478..41399ad7c 100644 --- a/src/providers/push-notifications/push-notifications.ts +++ b/src/providers/push-notifications/push-notifications.ts @@ -39,7 +39,7 @@ export class PushNotificationsProvider { this.logger.info('PushNotificationsProvider initialized.'); this.isIOS = this.platformProvider.isIOS; this.isAndroid = this.platformProvider.isAndroid; - this.usePushNotifications = this.platformProvider.isCordova && !this.platformProvider.isWP; + this.usePushNotifications = this.platformProvider.isCordova; if (this.usePushNotifications) { diff --git a/src/providers/rate/rate.spec.ts b/src/providers/rate/rate.spec.ts new file mode 100644 index 000000000..ad257df54 --- /dev/null +++ b/src/providers/rate/rate.spec.ts @@ -0,0 +1,215 @@ +import { TestBed, getTestBed, inject, async } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { RateProvider } from './rate'; +import { NgLoggerModule, Level } from '@nsalaun/ng-logger'; +import { Logger } from '../../providers/logger/logger'; +import { + TranslateModule, + TranslateService, + TranslateLoader, + TranslateFakeLoader +} from '@ngx-translate/core'; + +describe('RateProvider', () => { + let injector: TestBed; + let service: RateProvider; + let httpMock: HttpTestingController; + + const btcResponse = [{"code":"BTC","name":"Bitcoin","rate":1},{"code":"USD","name":"US Dollar","rate":11535.74},{"code":"BCH","name":"Bitcoin Cash","rate":7.65734}]; + const bchResponse = [{"code":"BTC","name":"Bitcoin","rate":0.130377},{"code":"USD","name":"US Dollar","rate":1503.3},{"code":"BCH","name":"Bitcoin Cash","rate":1}]; + let btcUrl: string = 'https://bitpay.com/api/rates'; + let bchUrl: string = 'https://bitpay.com/api/rates/bch'; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + NgLoggerModule.forRoot(Level.LOG), + TranslateModule.forRoot({ + loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } + }) + ], + providers: [ + RateProvider, + Logger + ] + }); + injector = getTestBed(); + service = injector.get(RateProvider); + httpMock = injector.get(HttpTestingController); + }); + + it('should see if rates are available', () => { + service.updateRatesBtc().then(response => { + expect(service.isAvailable()).toBe(true); + }); + + httpMock.match(btcUrl)[1].flush(btcResponse); + httpMock.match(bchUrl)[0].flush(bchResponse); + httpMock.verify(); + }); + + it('should get BTC rates', () => { + service.updateRatesBtc().then(response => { + expect(service.isAvailable()).toBe(true); + expect(service.getRate('BTC')).toEqual(1); + expect(service.getRate('USD')).toEqual(11535.74); + expect(service.getRate('BCH')).toEqual(7.65734); + }); + + httpMock.match(btcUrl)[1].flush(btcResponse); + httpMock.match(bchUrl)[0].flush(bchResponse); + httpMock.verify(); + }); + + it('should get BCH rates', () => { + service.updateRatesBch().then(response => { + expect(service.isAvailable()).toBe(true); + expect(service.getRate('BTC', 'bch')).toEqual(0.130377); + expect(service.getRate('USD', 'bch')).toEqual(1503.3); + expect(service.getRate('BCH', 'bch')).toEqual(1); + }); + + httpMock.match(btcUrl)[0].flush(btcResponse); + httpMock.match(bchUrl)[1].flush(bchResponse); + httpMock.verify(); + }); + + it('should catch an error on when call to update btc rates fails', () => { + service.getBCH = (): Promise => { + let prom = new Promise((resolve, reject) => { + reject('test rejection'); + }); + return prom; + }; + + service.updateRatesBch() + .catch((err: any) => { + expect(err).not.toBeNull(); + }); + }); + + it('should catch an error on when call to update bch rates fails', () => { + service.getBTC = (): Promise => { + let prom = new Promise((resolve, reject) => { + reject('test rejection'); + }); + return prom; + }; + + service.updateRatesBtc() + .catch((err: any) => { + expect(err).not.toBeNull(); + }); + }); + + it('should covert BCH satoshis to fiat', () => { + // before we have rates + expect(service.toFiat(0.25*1e+8, 'USD', 'bch')).toBeNull(); + + // after we have rates + service.updateRatesBch().then(response => { + expect(service.isAvailable()).toBe(true); + expect(service.toFiat(1*1e+8, 'USD', 'bch')).toEqual(1503.3); + expect(service.toFiat(0.5*1e+8, 'USD', 'bch')).toEqual(751.65); + expect(service.toFiat(0.25*1e+8, 'USD', 'bch')).toEqual(375.825); + }); + + httpMock.match(btcUrl)[0].flush(btcResponse); + httpMock.match(bchUrl)[1].flush(bchResponse); + httpMock.verify(); + }); + + it('should covert fiat to BCH satoshis', () => { + // before we have rates + expect(service.fromFiat(0.25*1e+8, 'USD', 'bch')).toBeNull(); + + // after we have rates + service.updateRatesBch().then(response => { + expect(service.isAvailable()).toBe(true); + expect(service.fromFiat(1503.3, 'USD', 'bch')).toEqual(1*1e+8); + expect(service.fromFiat(751.65, 'USD', 'bch')).toEqual(0.5*1e+8); + expect(service.fromFiat(375.825, 'USD', 'bch')).toEqual(0.25*1e+8); + }); + + httpMock.match(btcUrl)[0].flush(btcResponse); + httpMock.match(bchUrl)[1].flush(bchResponse); + httpMock.verify(); + }); + + it('should covert BTC satoshis to fiat', () => { + // before we have rates + expect(service.toFiat(0.25*1e+8, 'USD', 'btc')).toBeNull(); + + // after we have rates + service.updateRatesBtc().then(response => { + expect(service.isAvailable()).toBe(true); + expect(service.toFiat(1*1e+8, 'USD', 'btc')).toEqual(11535.74); + expect(service.toFiat(0.5*1e+8, 'USD', 'btc')).toEqual(5767.87); + expect(service.toFiat(0.25*1e+8, 'USD', 'btc')).toEqual(2883.935); + }); + + httpMock.match(btcUrl)[1].flush(btcResponse); + httpMock.match(bchUrl)[0].flush(bchResponse); + httpMock.verify(); + }); + + it('should covert fiat to BTC satoshis', () => { + // before we have rates + expect(service.fromFiat(0.25*1e+8, 'USD', 'btc')).toBeNull(); + + // after we have rates + service.updateRatesBtc().then(response => { + expect(service.isAvailable()).toBe(true); + expect(service.fromFiat(11535.74, 'USD', 'btc')).toEqual(1*1e+8); + expect(service.fromFiat(5767.87, 'USD', 'btc')).toEqual(0.5*1e+8); + expect(service.fromFiat(2883.935, 'USD', 'btc')).toEqual(0.25*1e+8); + }); + + httpMock.match(btcUrl)[1].flush(btcResponse); + httpMock.match(bchUrl)[0].flush(bchResponse); + httpMock.verify(); + }); + + it('should list alternatives', () => { + // before we have rates + expect(service.listAlternatives(false)).toEqual([]); + expect(service.listAlternatives(true)).toEqual([]); + + // after we have rates + service.updateRatesBtc().then(response => { + expect(service.isAvailable()).toBe(true); + expect(service.listAlternatives(false)).toEqual([ + {name: 'Bitcoin', isoCode: 'BTC'}, + {name: 'US Dollar', isoCode: 'USD'}, + {name: 'Bitcoin Cash', isoCode: 'BCH'} + ]); + expect(service.listAlternatives(true)).toEqual([ + {name: 'Bitcoin', isoCode: 'BTC'}, + {name: 'Bitcoin Cash', isoCode: 'BCH'}, + {name: 'US Dollar', isoCode: 'USD'} + ]); + }); + + httpMock.match(btcUrl)[1].flush(btcResponse); + httpMock.match(bchUrl)[0].flush(bchResponse); + httpMock.verify(); + }); + + it('should resolve when rates are available', () => { + // before we have rates + expect(service.isAvailable()).toBe(false); + + service.whenRatesAvailable().then(response => { + // after we have rates + expect(service.isAvailable()).toBe(true); + + // hit the if in whenRatesAvailable + service.whenRatesAvailable(); + }); + + httpMock.match(btcUrl)[1].flush(btcResponse); + httpMock.match(bchUrl)[0].flush(bchResponse); + httpMock.verify(); + }); +}); diff --git a/src/providers/rate/rate.ts b/src/providers/rate/rate.ts index 0f1bfb72d..08970f50b 100644 --- a/src/providers/rate/rate.ts +++ b/src/providers/rate/rate.ts @@ -32,10 +32,9 @@ export class RateProvider { this.updateRatesBch(); } - private updateRatesBtc(): Promise { + public updateRatesBtc(): Promise { return new Promise((resolve, reject) => { this.getBTC().then((dataBTC: any) => { - _.each(dataBTC, (currency: any) => { this.rates[currency.code] = currency.rate; this.alternatives.push({ @@ -53,7 +52,7 @@ export class RateProvider { }); } - private updateRatesBch(): Promise { + public updateRatesBch(): Promise { return new Promise((resolve, reject) => { this.getBCH().then((dataBCH: any) => { _.each(dataBCH, (currency: any) => { @@ -67,7 +66,7 @@ export class RateProvider { }); } - private getBTC(): Promise { + public getBTC(): Promise { return new Promise((resolve, reject) => { this.http.get(this.rateServiceUrl).subscribe((data: any) => { resolve(data); @@ -75,7 +74,7 @@ export class RateProvider { }); } - private getBCH(): Promise { + public getBCH(): Promise { return new Promise((resolve, reject) => { this.http.get(this.bchRateServiceUrl).subscribe((data: any) => { resolve(data); diff --git a/src/providers/release/release.spec.ts b/src/providers/release/release.spec.ts index 5cc4867f0..18df82424 100644 --- a/src/providers/release/release.spec.ts +++ b/src/providers/release/release.spec.ts @@ -58,11 +58,19 @@ describe('Release Provider', () => { githubReq.flush({ tag_name: latestAppVersion }); }); + it('should use appVersion when no currentVersion is supplied', () => { + let result = releaseService.checkForUpdates(latestAppVersion); + + expect(result.updateAvailable).toBeNull; + expect(result.availableVersion).toBeNull; + expect(result.error).toBeNull; + }); + it('should check unsuccessfully the current app version format', () => { const result = releaseService.checkForUpdates(latestAppVersion, 'V..3.3.3'); expect(result.updateAvailable).toBeNull; - expect(result.availabeVersion).toBeNull; + expect(result.availableVersion).toBeNull; expect(result.error).toBeDefined(); expect(result.error).toMatch('Cannot'); expect(result.error).toMatch('version tag'); @@ -75,7 +83,7 @@ describe('Release Provider', () => { ); expect(result.updateAvailable).toBeNull; - expect(result.availabeVersion).toBeNull; + expect(result.availableVersion).toBeNull; expect(result.error).toBeDefined(); expect(result.error).toMatch('Cannot'); expect(result.error).toMatch('release tag'); @@ -86,7 +94,7 @@ describe('Release Provider', () => { expect(result.error).toBeNull; expect(result.updateAvailable).toBeNull; - expect(result.availabeVersion).toBeNull; + expect(result.availableVersion).toBeNull; }); it('should be a new version available', () => { @@ -97,6 +105,6 @@ describe('Release Provider', () => { expect(result.error).toBeNull; expect(result.updateAvailable).toBeTruthy; - expect(result.availabeVersion).toEqual(latestAppVersion); + expect(result.availableVersion).toEqual(latestAppVersion); }); }); diff --git a/src/providers/release/release.ts b/src/providers/release/release.ts index 617781440..d37d91c3d 100644 --- a/src/providers/release/release.ts +++ b/src/providers/release/release.ts @@ -36,12 +36,16 @@ export class ReleaseProvider { }; } - public checkForUpdates(latestVersion: string, currentVersion?: string) { + public checkForUpdates(latestVersion: string, currentVersion?: string): { + updateAvailable: boolean | null, + availableVersion: string | null, + error: string | null + } { if (!currentVersion) currentVersion = this.appVersion; let result = { updateAvailable: null, - availabeVersion: null, + availableVersion: null, error: null }; @@ -62,7 +66,7 @@ export class ReleaseProvider { return result; else { result.updateAvailable = true; - result.availabeVersion = latestVersion; + result.availableVersion = latestVersion; return result; } }