continue scaffolding, add blocksService

This commit is contained in:
Jason Dreyzehner 2017-07-07 16:46:53 -04:00
parent d784be5d7e
commit 57ec79a46a
31 changed files with 250 additions and 463 deletions

View File

@ -7,7 +7,7 @@ describe('InsightApp', () => {
});
it('should have a title', () => {
expect(browser.getTitle()).toEqual('Clickers');
expect(browser.getTitle()).toEqual('Blocks');
});
it('should have {nav}', () => {
@ -15,30 +15,22 @@ describe('InsightApp', () => {
});
it('should have correct nav text for Home', () => {
expect(element(by.css('ion-navbar:first-child')).getText()).toContain('Clickers');
expect(element(by.css('ion-navbar:first-child')).getText()).toContain('Blocks');
});
it('has a menu button that displays the left menu', () => {
element(by.css('.bar-button-menutoggle')).click()
.then(() => {
browser.driver.sleep(2000); // wait for the animation
expect(element.all(by.css('.toolbar-title')).first().getText()).toEqual('Pages');
expect(element(by.css('ion-menu')).isPresent()).toEqual(true);
});
});
it('the left menu has a link with title Clickers', () => {
it('the left menu has a link with title Blocks', () => {
element(by.css('.bar-button-menutoggle')).click()
.then(() => {
browser.driver.sleep(2000); // wait for the animation
expect(element.all(by.css('ion-label')).first().getText()).toEqual('Clickers');
});
});
it('the left menu has a link with title Goodbye Ionic', () => {
element(by.css('.bar-button-menutoggle')).click()
.then(() => {
browser.driver.sleep(2000); // wait for the animation
expect(element.all(by.css('ion-label')).last().getText()).toEqual('Goodbye Ionic');
expect(element.all(by.css('ion-label')).first().getText()).toEqual('Blocks');
});
});
});

View File

@ -1,20 +1,20 @@
import { browser, element, by, ElementFinder } from 'protractor';
let message: ElementFinder = element(by.className('message'));
let heading: ElementFinder = element(by.css('h1'));
describe('Page2', () => {
describe('BroadcastTxPage', () => {
beforeEach(() => {
browser.get('');
});
it('should have correct text when Goodbye Ionic is selected', () => {
it('should have the temporary heading', () => {
element(by.css('.bar-button-menutoggle')).click().then(() => {
browser.driver.sleep(2000); // wait for the animation
element.all(by.className('input-wrapper')).then((items) => {
items[1].click();
browser.driver.sleep(2000); // wait for the animation
expect(message.getText()).toEqual('SHOW SIMPLE ALERT\nSHOW MORE ADVANCED ALERT');
expect(heading.getText()).toEqual('Broadcast Transaction');
return items[1];
});
});

View File

@ -1,42 +0,0 @@
import { browser, element, by, ElementFinder } from 'protractor';
let clickerField: ElementFinder = element(by.css('.text-input'));
let addButton: ElementFinder = element.all(by.className('button-outline')).first();
let removeButton: ElementFinder = element.all(by.css('.button-outline-md-danger')).first();
let firstClicker: ElementFinder = element.all(by.tagName('clicker-button')).first().element(by.tagName('button'));
describe('ClickerList', () => {
beforeEach(() => {
browser.get('');
});
it('should switch into clickers page from menu', () => {
element(by.css('.bar-button-menutoggle')).click();
expect(element.all(by.css('.toolbar-title')).last().getText()).toEqual('Clickers');
});
it('has an input box for new Clickers', () => {
expect(element(by.css('.text-input')).isPresent()).toEqual(true);
});
it('should add a Clicker', () => {
'test clicker one'.split('').forEach((c) => clickerField.sendKeys(c));
addButton.click();
browser.driver.sleep(1000);
expect(firstClicker.getText()).toEqual('TEST CLICKER ONE (0)');
});
it('should click a Clicker', () => {
firstClicker.click();
browser.driver.sleep(1000);
expect(firstClicker.getText()).toEqual('TEST CLICKER ONE (1)');
});
it('should delete a Clicker', () => {
removeButton.click();
browser.driver.sleep(1000);
element.all(by.className('clickerList')).count()
.then((count) => expect(count).toEqual(0));
});
});

View File

@ -57,5 +57,8 @@
"tslint-eslint-rules": "4.1.1",
"typescript": "2.3.3"
},
"engines": {
"node": ">=8"
},
"license": "MIT"
}

View File

@ -1,26 +1,22 @@
import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';
import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { InsightApp } from './app.component';
import {
PagesModule,
BlocksPage,
BroadcastTxPage,
NodeStatusPage,
VerifyMessagePage,
} from '../pages';
import { ClickersService, StorageService } from '../services';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { InsightApp } from './app.component';
import { PagesModule, BlocksPage, BroadcastTxPage, NodeStatusPage, VerifyMessagePage } from '../pages';
import { BlocksService, StorageService } from '../services';
@NgModule({
declarations: [
InsightApp,
InsightApp
],
imports: [
BrowserModule,
HttpModule,
PagesModule,
IonicModule.forRoot(InsightApp),
IonicModule.forRoot(InsightApp)
],
bootstrap: [IonicApp],
entryComponents: [
@ -28,14 +24,14 @@ import { ClickersService, StorageService } from '../services';
BlocksPage,
BroadcastTxPage,
NodeStatusPage,
VerifyMessagePage,
VerifyMessagePage
],
providers: [
StatusBar,
SplashScreen,
ClickersService,
StorageService,
{provide: ErrorHandler, useClass: IonicErrorHandler},
BlocksService,
{provide: ErrorHandler, useClass: IonicErrorHandler}
],
})

View File

@ -2,7 +2,7 @@
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Clickers</title>
<title>Insight</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
@ -10,7 +10,7 @@
<link rel="icon" type="image/x-icon" href="assets/icon/favicon.ico">
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#4e8ef7">
<!-- cordova.js required for cordova apps -->
<script src="cordova.js"></script>

View File

@ -0,0 +1,46 @@
import { Block, InsightBlockObject } from './block';
describe('Block', () => {
it('initializes', () => {
let obj: InsightBlockObject = {
height: 474504,
size: 998221,
hash: '000000000000000001763ebcea127d82b5c49b620960e2d881c4ace719d5fe46',
time: 1499346191,
txlength: 1904,
poolInfo: {
poolName: 'AntMiner',
url: 'https://bitmaintech.com/'
}
};
let block: Block = new Block(obj);
expect(block.height).toEqual(obj.height);
expect(block.size).toEqual(obj.size);
expect(block.hash).toEqual(obj.hash);
expect(block.timestamp).toEqual(obj.time);
expect(block.transactionCount).toEqual(obj.txlength);
expect(block.poolName).toEqual(obj.poolInfo.poolName);
});
it('can handle empty poolInfo', () => {
let obj: InsightBlockObject = {
height: 474504,
size: 998221,
hash: '000000000000000001763ebcea127d82b5c49b620960e2d881c4ace719d5fe46',
time: 1499346191,
txlength: 1904,
poolInfo: { }
};
let block: Block = new Block(obj);
expect(block.height).toEqual(obj.height);
expect(block.size).toEqual(obj.size);
expect(block.hash).toEqual(obj.hash);
expect(block.timestamp).toEqual(obj.time);
expect(block.transactionCount).toEqual(obj.txlength);
expect(block.poolName).toEqual(obj.poolInfo.poolName);
});
});

34
app/src/models/block.ts Normal file
View File

@ -0,0 +1,34 @@
export class Block {
public readonly height: number;
public readonly size: number;
public readonly hash: string;
public readonly timestamp: number;
public readonly transactionCount: number;
public readonly poolName: string;
constructor(properties: InsightBlockObject) {
this.height = properties.height;
this.size = properties.size;
this.hash = properties.hash;
this.timestamp = properties.time;
this.transactionCount = properties.txlength;
this.poolName = properties.poolInfo && properties.poolInfo.poolName;
}
public getDate(): Date {
return new Date(this.timestamp * 1000);
}
}
export interface InsightBlockObject {
height?: number;
size?: number;
hash?: string;
time?: number;
txlength?: number;
poolInfo?: {
poolName?: string,
url?: string
};
}

View File

@ -1,26 +0,0 @@
'use strict';
import { Click } from './click';
describe('Click', () => {
it('initialises with defaults', () => {
let click: Click = new Click();
// toString() prints out something like "Thu Jan 07 2016 14:05:14 GMT+1300 (NZDT)"
// comparing millis directly sometimes fails test (as it will be one milli too late!)
let currentDateString: string = new Date().toString();
let defaultDateString: string = new Date(click.getTime()).toString();
expect(currentDateString).toEqual(defaultDateString);
expect(click.getLocation()).toEqual('TODO');
});
it('initialises with overrides', () => {
let current: number = new Date().getTime();
let location: string = 'MY LOCATION';
let click: Click = new Click(current, location);
expect(click.getTime()).toEqual(current);
expect(click.getLocation()).toEqual(location);
});
});

View File

@ -1,20 +0,0 @@
'use strict';
export class Click {
private time: number;
private location: string;
constructor(time?: number, location?: string) {
this.time = time || new Date().getTime();
this.location = location || 'TODO';
}
public getTime(): number {
return this.time;
}
public getLocation(): string {
return this.location;
}
}

View File

@ -1,5 +0,0 @@
export class ClickerMock {
public getCount(): number { return 10; };
public getId(): string { return 'UUUUUID'; };
public getName(): string { return 'TEST CLICKER'; };
}

View File

@ -1,11 +0,0 @@
'use strict';
import { Clicker } from './clicker';
describe('Clicker', () => {
it('initialises with the correct name', () => {
let clicker: Clicker = new Clicker('12434', 'testClicker');
expect(clicker.getName()).toEqual('testClicker');
});
});

View File

@ -1,37 +0,0 @@
'use strict';
import { Click } from './';
// Represents a single Clicker
export class Clicker {
private id: string;
private name: string;
private clicks: Array<Click>;
constructor(id: string, name: string) {
this.id = id;
this.name = name;
this.clicks = [];
}
public doClick(): void {
this.clicks.push(new Click());
}
public addClick(click: Click): void {
this.clicks.push(click);
}
public getCount(): number {
return this.clicks.length;
}
public getId(): string {
return this.id;
}
public getName(): string {
return this.name;
}
}

View File

@ -1,2 +1 @@
export * from './click';
export * from './clicker';
export * from './block';

View File

@ -1 +1 @@
export * from './clicker.mock';
// export * from './X.mock';

View File

@ -14,16 +14,16 @@
<ion-content padding>
<ion-list>
<ion-item *ngFor="let block of blocks">
<ion-item *ngFor="let block of blocks | async">
<ion-row>
<ion-col>
#{{block.height}}
</ion-col>
<ion-col>
{{block.txlength}} transactions
{{block.transactionCount}} transactions
</ion-col>
<ion-col>
<a *ngIf="block.poolInfo.poolName" href="{{block.poolInfo.url}}">{{block.poolInfo.poolName}}</a>
<a *ngIf="block.poolName" href="{{block.url}}">{{block.poolName}}</a>
</ion-col>
</ion-row>
</ion-item>

View File

@ -1,19 +1,25 @@
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { Observable } from 'rxjs';
import { Block } from '../../models';
import { BlocksService } from '../../services';
@Component({
templateUrl: './blocksPage.html',
templateUrl: './blocksPage.html'
})
export class BlocksPage {
public title: string;
private nav: NavController;
private blocks: any;
public blocks: Observable<Block[]>;
constructor(nav: NavController) {
constructor(private nav: NavController, private blocksService: BlocksService) {
this.nav = nav;
this.title = 'Blocks';
this.blocks = [{"height":471569,"size":999095,"hash":"00000000000000000007bf61c6cf9efbb1353b919621768b553bd1ffb2948240","time":1497633772,"txlength":2561,"poolInfo":{}},{"height":471568,"size":999994,"hash":"000000000000000000fc2c10f1b4e55c9b86b8f9e804dc66683312b7d7d955dd","time":1497632268,"txlength":2011,"poolInfo":{}},{"height":471567,"size":989172,"hash":"000000000000000000d70effc2ec625999960bd7929d7a4298124456ecb29c23","time":1497632133,"txlength":1651,"poolInfo":{"poolName":"BTCC Pool","url":"https://pool.btcc.com/"}},{"height":471566,"size":998155,"hash":"00000000000000000127cbc53d017400eb9d17122a8ae36986c077ac7eea25fe","time":1497629915,"txlength":1130,"poolInfo":{"poolName":"AntMiner","url":"https://bitmaintech.com/"}},{"height":471565,"size":998103,"hash":"00000000000000000045844f32f6e9fc6e7575bb96eb71a9ce9fba2849892d5e","time":1497629731,"txlength":1986,"poolInfo":{}},{"height":471564,"size":998101,"hash":"0000000000000000009a5b3fc7ac517f8a275be73f4492adb0cb88943f5c1b9e","time":1497629348,"txlength":2378,"poolInfo":{}},{"height":471563,"size":998258,"hash":"00000000000000000048a5506f3415fcf790106a838953e04d86d8f2583ebd6a","time":1497628552,"txlength":1481,"poolInfo":{"poolName":"AntMiner","url":"https://bitmaintech.com/"}},{"height":471562,"size":998257,"hash":"0000000000000000016f6a483730bd9c1c48c8345782b8365762592a475aa330","time":1497628159,"txlength":646,"poolInfo":{"poolName":"AntMiner","url":"https://bitmaintech.com/"}},{"height":471561,"size":999016,"hash":"000000000000000001434fb0e5cc5e521de59bff70c325185a5c67bd5418c61a","time":1497627986,"txlength":1831,"poolInfo":{}},{"height":471560,"size":998229,"hash":"000000000000000000a1782a9d78672672e4291347c47ac3206ed8716bb4952b","time":1497627524,"txlength":791,"poolInfo":{"poolName":"SlushPool","url":"https://slushpool.com/"}},{"height":471559,"size":999152,"hash":"000000000000000000d58ba85b2ddedc1f467b1e1c49c47cf62d4d970dad7ab5","time":1497627347,"txlength":87,"poolInfo":{}},{"height":471558,"size":778220,"hash":"0000000000000000009a4e72a1863e42fdfe1e08e6d547d4528049065d69454c","time":1497627287,"txlength":1685,"poolInfo":{"poolName":"BTCC Pool","url":"https://pool.btcc.com/"}},{"height":471557,"size":707456,"hash":"00000000000000000068a8adc72fc41299105053e6059a3f6dcd2e0dc2fd2fcb","time":1497626859,"txlength":1661,"poolInfo":{"poolName":"BTCC Pool","url":"https://pool.btcc.com/"}}];
this.blocks = blocksService.latestBlocks;
// this.blocks.subscribe((blocks) => {
// console.log(blocks);
// });
blocksService.getLatestBlocks();
}
}

View File

@ -0,0 +1,24 @@
import { Subject } from 'rxjs';
import { Block } from '../models';
export class BlocksServiceMock {
public readonly latestBlocks: Subject<Array<Block>> = new Subject();
public getLatestBlocks(): void {
this.latestBlocks.next([
new Block({
height: 474504,
size: 998221,
hash: '000000000000000001763ebcea127d82b5c49b620960e2d881c4ace719d5fe46',
time: 1499346191,
txlength: 1904,
poolInfo: {
poolName: 'AntMiner',
url: 'https://bitmaintech.com/'
}
})
]);
}
}

View File

@ -0,0 +1,17 @@
import { BlocksService } from './blocksService';
import { Block } from '../models';
import { TestUtils } from '../test';
let blocks: BlocksService = null;
// describe('BlocksService', () => {
// beforeEach(() => {
// blocks = new BlocksService();
// });
// it('initializes', () => {
// expect(blocks).not.toBeNull();
// });
// });

View File

@ -0,0 +1,26 @@
import { Http, Response } from '@angular/http';
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { Block, InsightBlockObject } from '../models';
@Injectable()
export class BlocksService {
public readonly latestBlocks: Subject<Array<Block>> = new Subject();
constructor(private http: Http) {}
public getLatestBlocks(): void {
this.http.request('https://insight.bitpay.com/api/blocks').subscribe((res: Response) => {
const data: {
blocks: InsightBlockObject[],
length: number,
pagination: {}
} = res.json();
this.latestBlocks.next(data.blocks.map((obj) => {
return new Block(obj);
}));
});
}
}

View File

@ -1,14 +0,0 @@
export class ClickersServiceMock {
public doClick(): boolean {
return true;
}
public newClicker(): boolean {
return true;
}
public getClickers(): Array<string> {
return [];
}
}

View File

@ -1,88 +0,0 @@
import { ClickersService } from './clickers';
import { Clicker } from '../models';
import { StorageMock } from './mocks';
let clickers: ClickersService = null;
describe('ClickersService', () => {
beforeEach(() => {
clickers = new ClickersService(<any>new StorageMock());
spyOn(clickers['storage'], 'set').and.callThrough();
});
it('initialises', () => {
expect(clickers).not.toBeNull();
});
it('initialises with clickers from mock storage', (done: Function) => {
clickers['init']()
.then(() => {
expect(clickers.getClickers().length).toEqual(StorageMock.CLICKER_IDS.length);
done();
});
});
it('can initialise a clicker from string', () => {
let clickerString: string = '{"id":"0g2vt8qtlm","name":"harold","clicks":[{"time":1450410168819,"location":"TODO"},{"time":1450410168945,"location":"TODO"}]}';
let clicker: Clicker = clickers['initClicker'](clickerString);
expect(clicker.getName()).toEqual('harold');
expect(clicker.getCount()).toEqual(2);
});
it('returns undefined for a bad id', () => {
expect(clickers.getClicker('dave')).not.toBeDefined();
});
it('adds a new clicker with the correct name', (done: Function) => {
clickers['init']()
.then(() => {
let idAdded: string = clickers.newClicker('dave');
expect(clickers['storage'].set).toHaveBeenCalledWith(idAdded, jasmine.any(String));
expect(clickers.getClickers()[3].getName()).toEqual('dave');
done();
});
});
it('removes a clicker by id', () => {
let idToRemove: string = clickers.newClicker('dave');
clickers.removeClicker(idToRemove);
expect(clickers['storage'].set).toHaveBeenCalledWith(idToRemove, jasmine.any(String));
});
it('does a click', () => {
let idToClick: string = clickers.newClicker('dave');
let clickedClicker: Clicker = null;
clickers.doClick(idToClick);
expect(clickers['storage'].set).toHaveBeenCalledWith(idToClick, jasmine.any(String));
clickedClicker = clickers.getClicker(idToClick);
expect(clickedClicker.getCount()).toEqual(1);
});
it('loads empty list if given no argument', (done: Function) => {
clickers['initIds'](false)
.then((ids: Array<string>) => {
expect(ids).toEqual([]);
done();
});
});
it('loads IDs from storage', (done: Function) => {
clickers['initIds']()
.then((ids: Array<string>) => {
expect(ids).toEqual(StorageMock.CLICKER_IDS);
done();
});
});
it('loads clickers from storage', (done: Function) => {
clickers['initClickers'](StorageMock.CLICKER_IDS)
.then((resolvedClickers: Array<Clicker>) => {
expect(resolvedClickers.length).toEqual(3);
expect(resolvedClickers[0].getId()).toEqual(StorageMock.CLICKER_IDS[0]);
expect(resolvedClickers[1].getId()).toEqual(StorageMock.CLICKER_IDS[1]);
expect(resolvedClickers[2].getId()).toEqual(StorageMock.CLICKER_IDS[2]);
done();
});
});
});

View File

@ -1,114 +0,0 @@
'use strict';
import { Injectable } from '@angular/core';
import { StorageService } from './storage';
import { Click, Clicker } from '../models';
@Injectable()
export class ClickersService {
private clickers: Array<Clicker>;
private ids: Array<string>; // we need to keep a separate reference to ids so we can lookup when the app loads from scratch
private storage: StorageService;
// don't know why Injection isn't working without @Inject:
// http://stackoverflow.com/questions/34449486/angular-2-0-injected-http-service-is-undefined
constructor(storage: StorageService) {
this.storage = storage;
this.ids = [];
this.clickers = [];
this.init();
}
// as init is async separate logic here so it's testable
private init(): Promise<{}> {
return this.initIds()
.then((ids: Array<string>) => { this.ids = ids; })
.then(() => this.initClickers(this.ids))
.then((clickers: Array<Clicker>) => this.clickers = clickers);
}
// initialise Ids from SQL storage
private initIds(load: boolean = true): Promise<{}> {
return this.storage.get('ids') // return the promise so we can chain initClickers
.then((rawIds: string) => {
if (!rawIds || !load) return [];
// ids are stored as stringified JSON array
return JSON.parse(rawIds);
});
}
// initialise Clickers from SQL storage given an array of ids
private initClickers(ids: Array<string>): Promise<{}> {
// get all existing ids
let proms: Array<Promise<string>> = [];
proms = ids.map(id => this.storage.get(id));
return Promise.all(proms)
.then(clickers => clickers.map(clicker => this.initClicker(clicker)));
}
// initialise a clicker from a raw JSON string out of the DB
private initClicker(clicker: string): Clicker {
const parsedClicker: Object = JSON.parse(clicker);
const newClicker: Clicker = new Clicker(parsedClicker['id'], parsedClicker['name']);
// add the clicks - need to re-instantiate object
for (let click of parsedClicker['clicks']) {
newClicker.addClick(new Click(click.time, click.location));
}
return newClicker;
}
public getClicker(id: string): Clicker {
return this.clickers['find']((clicker: Clicker) => { return clicker.getId() === id; } );
}
public getClickers(): Array<Clicker> {
return this.clickers;
}
public newClicker(name: string): string {
const id: string = this.uid();
const clicker: Clicker = new Clicker(id, name);
// add the clicker to the service
this.clickers.push(clicker);
// add the id to the service (need to keep a separate reference of IDs so we can cold load clickers)
this.ids.push(id);
// save the clicker by id
this.storage.set(id, JSON.stringify(clicker));
// save the service's ids array
this.storage.set('ids', JSON.stringify(this.ids));
return id;
}
public removeClicker(id: string): void {
// remove clicker from the service
this.clickers = this.clickers.filter((clicker: Clicker) => { return clicker.getId() !== id; });
// remove from ids array
this.ids = this.ids.filter((filterId: string) => { return filterId !== id; });
// null id in db
this.storage.remove(id);
// update service's ids array
this.storage.set('ids', JSON.stringify(this.ids));
}
public doClick(id: string): void {
const clicker: Clicker = this.getClicker(id);
clicker.doClick();
// save the clicker with updated click in storage
this.storage.set(clicker.getId(), JSON.stringify(clicker));
}
private uid(): string {
return Math.random().toString(35).substr(2, 10);
}
}

View File

@ -1,2 +1,2 @@
export * from './clickers';
export * from './storage';
export * from './blocksService';
export * from './storageService';

View File

@ -1,2 +1,2 @@
export * from './clickers.mock';
export * from './storage.mock';
export * from './blocksService.mock';
export * from './storageService.mock';

View File

@ -1,43 +0,0 @@
'use strict';
export class StorageMock {
public static CLICKER_IDS: Array<string> = ['yy5d8klsj0', 'q20iexxg4a', 'wao2xajl8a'];
public get(key: string): Promise<{}> {
let rtn: string = null;
switch (key) {
case 'ids':
rtn = JSON.stringify(StorageMock.CLICKER_IDS);
break;
case StorageMock.CLICKER_IDS[0]:
rtn = `{"id":"${StorageMock.CLICKER_IDS[0]}","name":"test1","clicks":[{"time":1450410168819,"location":"TODO"}]}`;
break;
case StorageMock.CLICKER_IDS[1]:
rtn = `{"id":"${StorageMock.CLICKER_IDS[1]}","name":"test2","clicks":[{"time":1450410168819,"location":"TODO"},{"time":1450410168945,"location":"TODO"}]}`;
break;
case StorageMock.CLICKER_IDS[2]:
rtn = `{"id":"${StorageMock.CLICKER_IDS[2]}","name":"test3", "clicks":[{ "time": 1450410168819, "location": "TODO" }, { "time": 1450410168945, "location": "TODO" }] }`;
break;
default:
rtn = 'SHOULD NOT BE HERE!';
}
return new Promise((resolve: Function) => {
resolve(rtn);
});
}
public set(key: string, value: string): Promise<{}> {
return new Promise((resolve: Function) => {
resolve({key: key, value: value});
});
}
public remove(key: string): Promise<{}> {
return new Promise((resolve: Function) => {
resolve({key: key});
});
}
}

View File

@ -0,0 +1,42 @@
export class StorageServiceMock {
public static CLICKER_IDS: Array<string> = ['yy5d8klsj0', 'q20iexxg4a', 'wao2xajl8a'];
public get(key: string): Promise<{}> {
let rtn: string = null;
switch (key) {
case 'ids':
rtn = JSON.stringify(StorageServiceMock.CLICKER_IDS);
break;
case StorageServiceMock.CLICKER_IDS[0]:
rtn = `{"id":"${StorageServiceMock.CLICKER_IDS[0]}","name":"test1","clicks":[{"time":1450410168819,"location":"TODO"}]}`;
break;
case StorageServiceMock.CLICKER_IDS[1]:
rtn = `{"id":"${StorageServiceMock.CLICKER_IDS[1]}","name":"test2","clicks":[{"time":1450410168819,"location":"TODO"},{"time":1450410168945,"location":"TODO"}]}`;
break;
case StorageServiceMock.CLICKER_IDS[2]:
rtn = `{"id":"${StorageServiceMock.CLICKER_IDS[2]}","name":"test3", "clicks":[{ "time": 1450410168819, "location": "TODO" },
{ "time": 1450410168945, "location": "TODO" }] }`;
break;
default:
rtn = 'SHOULD NOT BE HERE!';
}
return new Promise((resolve: Function) => {
resolve(rtn);
});
}
public set(key: string, value: string): Promise<{}> {
return new Promise((resolve: Function) => {
resolve({key: key, value: value});
});
}
public remove(key: string): Promise<{}> {
return new Promise((resolve: Function) => {
resolve({key: key});
});
}
}

View File

@ -1,4 +1,3 @@
'use strict';
import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage';

View File

@ -10,10 +10,12 @@ import 'zone.js/dist/fake-async-test';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { getTestBed, TestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
import { HttpModule } from '@angular/http';
import { MockBackend } from '@angular/http/testing';
import { App, Config, Form, IonicModule, Keyboard, DomController, MenuController, NavController, Platform, GestureController } from 'ionic-angular';
import { ConfigMock, PlatformMock } from './mocks';
import { ClickersServiceMock } from './services/clickers.mock';
import { ClickersService } from './services';
import { BlocksServiceMock } from './services/mocks';
import { BlocksService } from './services';
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare var __karma__: any;
@ -44,7 +46,7 @@ export class TestUtils {
let fixture: any = TestBed.createComponent(components[0]);
return {
fixture: fixture,
instance: fixture.debugElement.componentInstance,
instance: fixture.debugElement.componentInstance
};
});
}
@ -52,19 +54,20 @@ export class TestUtils {
public static configureIonicTestingModule(components: Array<any>): typeof TestBed {
return TestBed.configureTestingModule({
declarations: [
...components,
...components
],
providers: [
App, Form, Keyboard, DomController, MenuController, NavController, GestureController,
{provide: Platform, useClass: PlatformMock},
{provide: Config, useClass: ConfigMock},
{provide: ClickersService, useClass: ClickersServiceMock},
{provide: BlocksService, useClass: BlocksServiceMock}
],
imports: [
FormsModule,
IonicModule,
ReactiveFormsModule,
],
HttpModule
]
});
}

View File

@ -36,7 +36,7 @@
"no-consecutive-blank-lines": true,
"no-console": [false],
"no-construct": false,
"no-constructor-vars": true,
"no-constructor-vars": false,
"no-debugger": true,
"no-duplicate-key": true,
"no-duplicate-variable": true,
@ -75,7 +75,7 @@
"trailing-comma": [
true,
{
"multiline": "always",
"multiline": "never",
"singleline": "never"
}
],