Added tests
Change-Id: I493675212f4b1548b32a6d92ce3664d184bc0e04
diff --git a/conf/karma-auto.conf.js b/conf/karma-auto.conf.js
index c0b9509..e66de9c 100644
--- a/conf/karma-auto.conf.js
+++ b/conf/karma-auto.conf.js
@@ -1,4 +1,5 @@
const conf = require('./gulp.conf');
+const pkg = require('../package.json');
module.exports = function (config) {
const configuration = {
@@ -10,7 +11,8 @@
outputDir: 'test-reports'
},
browsers: [
- 'PhantomJS'
+ 'PhantomJS',
+ 'Chrome'
],
frameworks: [
'jasmine',
@@ -46,6 +48,7 @@
require('karma-junit-reporter'),
require('karma-coverage'),
require('karma-phantomjs-launcher'),
+ require('karma-chrome-launcher'),
require('karma-phantomjs-shim'),
require('karma-ng-html2js-preprocessor'),
require('karma-webpack'),
diff --git a/package.json b/package.json
index 982fb96..ff66a45 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"json-loader": "^0.5.4",
"karma": "^1.3.0",
"karma-angular-filesort": "^1.0.0",
+ "karma-chrome-launcher": "^2.0.0",
"karma-coverage": "^1.1.1",
"karma-es6-shim": "^1.0.0",
"karma-jasmine": "^1.0.2",
diff --git a/src/app/core/services/navigation.spec.ts b/src/app/core/services/navigation.spec.ts
new file mode 100644
index 0000000..1096ec7
--- /dev/null
+++ b/src/app/core/services/navigation.spec.ts
@@ -0,0 +1,47 @@
+import * as angular from 'angular';
+import 'angular-mocks';
+import 'angular-ui-router';
+import {xosCore} from '../index';
+import {IXosNavigationService, IXosNavigationRoute} from './navigation';
+
+let service: IXosNavigationService;
+
+const defaultRoutes: IXosNavigationRoute[] = [
+ {label: 'Home', state: 'xos.dashboard'}
+];
+
+describe('The Navigation service', () => {
+
+ beforeEach(angular.mock.module(xosCore));
+
+ beforeEach(angular.mock.inject((
+ NavigationService: IXosNavigationService,
+ ) => {
+ service = NavigationService;
+ }));
+
+ it('should return navigation routes', () => {
+ expect(service.query()).toEqual(defaultRoutes);
+ });
+
+ it('should add a route', () => {
+ const testRoutes: IXosNavigationRoute[] = [
+ {label: 'TestState', state: 'xos.test'},
+ {label: 'TestUrl', url: 'test'}
+ ];
+ service.add(testRoutes[0]);
+ service.add(testRoutes[1]);
+ expect(service.query()).toEqual(defaultRoutes.concat(testRoutes));
+ });
+
+ it('should not add route that have both url and state', () => {
+ function wrapper() {
+ service.add({
+ label: 'Fail',
+ url: 'f',
+ state: 'f'
+ });
+ }
+ expect(wrapper).toThrowError('[XosNavigation] You can\'t provide both state and url');
+ });
+});
diff --git a/src/app/core/services/navigation.ts b/src/app/core/services/navigation.ts
index 8cb3b66..04eb575 100644
--- a/src/app/core/services/navigation.ts
+++ b/src/app/core/services/navigation.ts
@@ -26,6 +26,9 @@
}
add(route: IXosNavigationRoute) {
+ if (angular.isDefined(route.state) && angular.isDefined(route.url)) {
+ throw new Error('[XosNavigation] You can\'t provide both state and url');
+ }
this.routes.push(route);
}
}
diff --git a/src/app/core/services/page-title.spec.ts b/src/app/core/services/page-title.spec.ts
new file mode 100644
index 0000000..4ff2be6
--- /dev/null
+++ b/src/app/core/services/page-title.spec.ts
@@ -0,0 +1,31 @@
+import * as angular from 'angular';
+import 'angular-mocks';
+import 'angular-ui-router';
+import {xosCore} from '../index';
+import {IXosPageTitleService} from './page-title';
+import IWindowService = angular.IWindowService;
+import {StyleConfig} from '../../config/style.config';
+
+let service: IXosPageTitleService, $window: IWindowService;
+describe('The PageTitle service', () => {
+
+ beforeEach(angular.mock.module(xosCore));
+
+ beforeEach(angular.mock.inject((
+ PageTitle: IXosPageTitleService,
+ _$window_: IWindowService
+ ) => {
+ service = PageTitle;
+ $window = _$window_;
+ }));
+
+ it('should get the page title', () => {
+ $window.document.title = 'test';
+ expect(service.get()).toEqual('test');
+ });
+
+ it('should set a page title', () => {
+ service.set('sample');
+ expect($window.document.title).toEqual(`${StyleConfig.projectName} - sample`);
+ });
+});
diff --git a/src/app/core/services/page-title.ts b/src/app/core/services/page-title.ts
index 6f7e0c3..fb1e5a7 100644
--- a/src/app/core/services/page-title.ts
+++ b/src/app/core/services/page-title.ts
@@ -11,7 +11,6 @@
private $window: angular.IWindowService,
private $transitions: any // missing definition
) {
- console.log('page title');
this.$transitions.onSuccess({ to: '**' }, (transtion) => {
this.set(this.formatStateName(transtion.$to().name));
});
diff --git a/src/app/core/services/runtime-states.spec.ts b/src/app/core/services/runtime-states.spec.ts
new file mode 100644
index 0000000..5dd44db
--- /dev/null
+++ b/src/app/core/services/runtime-states.spec.ts
@@ -0,0 +1,30 @@
+import * as angular from 'angular';
+import 'angular-mocks';
+import 'angular-ui-router';
+import {xosCore} from '../index';
+import {IRuntimeStatesService} from './runtime-states';
+
+let service: IRuntimeStatesService, $state: ng.ui.IStateService;
+
+describe('The Navigation service', () => {
+
+ beforeEach(angular.mock.module(xosCore));
+
+ beforeEach(angular.mock.inject((
+ RuntimeStates: IRuntimeStatesService,
+ _$state_: ng.ui.IStateService
+ ) => {
+ service = RuntimeStates;
+ $state = _$state_;
+ }));
+
+ it('should add a state', () => {
+ service.addState('testState', {
+ url: 'test-state',
+ template: 'test-state'
+ });
+
+ expect($state.get('testState').url).toEqual('test-state');
+ expect($state.get('testState').template).toEqual('test-state');
+ });
+});
diff --git a/src/app/datasources/index.ts b/src/app/datasources/index.ts
index 936920e..c82f317 100644
--- a/src/app/datasources/index.ts
+++ b/src/app/datasources/index.ts
@@ -1,4 +1,3 @@
-import {CoreRest} from './rest/core.rest';
import {ModelRest} from './rest/model.rest';
import {AuthService} from './rest/auth.rest';
import {WebSocketEvent} from './websocket/global';
@@ -10,8 +9,7 @@
export const xosDataSources = 'xosDataSources';
angular
- .module('xosDataSources', ['ngCookies'])
- .service('CoreRest', CoreRest)
+ .module('xosDataSources', ['ngCookies', 'ngResource'])
.service('ModelRest', ModelRest)
.service('AuthService', AuthService)
.service('WebSocket', WebSocketEvent);
diff --git a/src/app/datasources/rest/auth.rest.spec.ts b/src/app/datasources/rest/auth.rest.spec.ts
new file mode 100644
index 0000000..85aefb4
--- /dev/null
+++ b/src/app/datasources/rest/auth.rest.spec.ts
@@ -0,0 +1,84 @@
+import * as angular from 'angular';
+import 'angular-mocks';
+import 'angular-resource';
+import 'angular-cookies';
+import {xosDataSources} from '../index';
+import {AppConfig} from '../../config/app.config';
+import {IXosAuthService} from './auth.rest';
+
+let service: IXosAuthService;
+let httpBackend: ng.IHttpBackendService;
+let $scope;
+let $cookies;
+
+describe('The AuthService service', () => {
+
+ beforeEach(angular.mock.module(xosDataSources));
+
+ beforeEach(() => {
+ angular.mock.module(xosDataSources);
+ });
+
+
+ beforeEach(angular.mock.inject((
+ AuthService: IXosAuthService,
+ $httpBackend: ng.IHttpBackendService,
+ _$rootScope_: ng.IRootScopeService,
+ _$cookies_: ng.cookies.ICookiesService
+ ) => {
+ service = AuthService;
+ httpBackend = $httpBackend;
+ $scope = _$rootScope_;
+ $cookies = _$cookies_;
+ }));
+
+ describe('when logging in', () => {
+ beforeEach(() => {
+ httpBackend.expectPOST(`${AppConfig.apiEndpoint}/utility/login/`)
+ .respond({
+ user: JSON.stringify({usernane: 'test@xos.org'}),
+ xoscsrftoken: 'token',
+ xossessionid: 'session'
+ });
+ });
+ it('should store user auth in cookies', (done) => {
+ service.login({username: 'test', password: 'xos'})
+ .then((res) => {
+ expect($cookies.get('xoscsrftoken')).toEqual('token');
+ expect($cookies.get('xossessionid')).toEqual('session');
+ expect($cookies.get('xosuser')).toEqual(JSON.stringify({usernane: 'test@xos.org'}));
+ done();
+ })
+ .catch(e => {
+ done(e);
+ });
+ $scope.$apply();
+ httpBackend.flush();
+ });
+ });
+
+ describe('when logging out', () => {
+ beforeEach(() => {
+ httpBackend.expectPOST(`${AppConfig.apiEndpoint}/utility/logout/`)
+ .respond({
+ user: JSON.stringify({usernane: 'test@xos.org'}),
+ xoscsrftoken: 'token',
+ xossessionid: 'session'
+ });
+ });
+ it('should remove user auth from cookies', (done) => {
+ service.logout()
+ .then((res) => {
+ expect($cookies.get('xoscsrftoken')).toEqual(null);
+ expect($cookies.get('xossessionid')).toEqual(null);
+ expect($cookies.get('xosuser')).toEqual(null);
+ done();
+ })
+ .catch(e => {
+ done(e);
+ });
+ $scope.$apply();
+ httpBackend.flush();
+ });
+ });
+});
diff --git a/src/app/datasources/rest/auth.rest.ts b/src/app/datasources/rest/auth.rest.ts
index a94599a..f71d295 100644
--- a/src/app/datasources/rest/auth.rest.ts
+++ b/src/app/datasources/rest/auth.rest.ts
@@ -12,6 +12,11 @@
xossessionid: string;
};
}
+
+export interface IXosAuthService {
+ login(data: IAuthRequestData): Promise<any>;
+ logout(): Promise<any>;
+}
export class AuthService {
@@ -38,4 +43,22 @@
});
return d.promise;
}
+
+ public logout(): Promise<any> {
+ const d = this.$q.defer();
+ this.$http.post(`${AppConfig.apiEndpoint}/utility/login/`, {
+ xoscsrftoken: this.$cookies.get('xoscsrftoken'),
+ xossessionid: this.$cookies.get('xossessionid')
+ })
+ .then(() => {
+ this.$cookies.remove('xoscsrftoken');
+ this.$cookies.remove('xossessionid');
+ this.$cookies.remove('xosuser');
+ d.resolve();
+ })
+ .catch(e => {
+ d.reject(e);
+ });
+ return d.promise;
+ }
}
diff --git a/src/app/datasources/rest/core.rest.ts b/src/app/datasources/rest/core.rest.ts
deleted file mode 100644
index 2c10e10..0000000
--- a/src/app/datasources/rest/core.rest.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import {AppConfig} from '../../config/app.config';
-export class CoreRest {
-
- /** @ngInject */
- constructor(
- private $http: angular.IHttpService,
- private $q: angular.IQService
- ) {
- }
-
- public query(): Promise<any> {
- const d = this.$q.defer();
- this.$http.get(`${AppConfig.apiEndpoint}/core/`)
- .then(res => d.resolve(res.data))
- .catch(d.reject);
- return d.promise;
- }
-}
diff --git a/src/app/datasources/rest/model.rest.spec.ts b/src/app/datasources/rest/model.rest.spec.ts
new file mode 100644
index 0000000..f57c0c8
--- /dev/null
+++ b/src/app/datasources/rest/model.rest.spec.ts
@@ -0,0 +1,76 @@
+import * as angular from 'angular';
+import 'angular-mocks';
+import 'angular-resource';
+import 'angular-cookies';
+import {IXosResourceService} from './model.rest';
+import {xosDataSources} from '../index';
+import {AppConfig} from '../../config/app.config';
+
+let service: IXosResourceService;
+let resource: ng.resource.IResourceClass<any>;
+let httpBackend: ng.IHttpBackendService;
+let $resource;
+let $scope;
+
+describe('The ModelRest service', () => {
+
+ beforeEach(angular.mock.module(xosDataSources));
+
+ beforeEach(() => {
+ angular.mock.module(xosDataSources);
+ });
+
+
+ beforeEach(angular.mock.inject((
+ ModelRest: IXosResourceService,
+ $httpBackend: ng.IHttpBackendService,
+ _$resource_: ng.resource.IResourceService,
+ _$rootScope_: ng.IRootScopeService
+ ) => {
+ service = ModelRest;
+ httpBackend = $httpBackend;
+ $resource = _$resource_;
+ $scope = _$rootScope_;
+ }));
+
+ it('should return a resource based on the URL', () => {
+ resource = service.getResource('/core/test');
+ expect(resource.constructor).toEqual($resource.constructor);
+ });
+
+ it('should have a query method', (done) => {
+ httpBackend.expectGET(`${AppConfig.apiEndpoint}/core/test`)
+ .respond([
+ {status: 'ok'}
+ ]);
+ resource = service.getResource('/core/test');
+ resource.query().$promise
+ .then((res) => {
+ expect(res[0].status).toEqual('ok');
+ done();
+ })
+ .catch(e => {
+ done(e);
+ });
+ $scope.$apply();
+ httpBackend.flush();
+ });
+
+ it('should have a get method', (done) => {
+ httpBackend.expectGET(`${AppConfig.apiEndpoint}/core/test/1`)
+ .respond([
+ {status: 'ok'}
+ ]);
+ resource = service.getResource('/core/test');
+ resource.get({id: 1}).$promise
+ .then((res) => {
+ expect(res[0].status).toEqual('ok');
+ done();
+ })
+ .catch(e => {
+ done(e);
+ });
+ $scope.$apply();
+ httpBackend.flush();
+ });
+});
diff --git a/src/app/datasources/rest/model.rest.ts b/src/app/datasources/rest/model.rest.ts
index 45431d4..908ca0f 100644
--- a/src/app/datasources/rest/model.rest.ts
+++ b/src/app/datasources/rest/model.rest.ts
@@ -16,6 +16,6 @@
}
public getResource(url: string): ng.resource.IResourceClass<ng.resource.IResource<any>> {
- return this.resource = this.$resource(`${AppConfig.apiEndpoint}${url}`);
+ return this.resource = this.$resource(`${AppConfig.apiEndpoint}${url}/:id`);
}
}
diff --git a/src/app/datasources/rest/modeldefs.rest.spec.ts b/src/app/datasources/rest/modeldefs.rest.spec.ts
new file mode 100644
index 0000000..6469cca
--- /dev/null
+++ b/src/app/datasources/rest/modeldefs.rest.spec.ts
@@ -0,0 +1,49 @@
+import * as angular from 'angular';
+import 'angular-mocks';
+import 'angular-resource';
+import 'angular-cookies';
+import {xosDataSources} from '../index';
+import {AppConfig} from '../../config/app.config';
+import {IModeldefsService} from './modeldefs.rest';
+
+let service: IModeldefsService;
+let httpBackend: ng.IHttpBackendService;
+let $scope;
+
+describe('The ModelDefs service', () => {
+
+ beforeEach(angular.mock.module(xosDataSources));
+
+ beforeEach(() => {
+ angular.mock.module(xosDataSources);
+ });
+
+
+ beforeEach(angular.mock.inject((
+ ModelDefs: IModeldefsService,
+ $httpBackend: ng.IHttpBackendService,
+ _$resource_: ng.resource.IResourceService,
+ _$rootScope_: ng.IRootScopeService
+ ) => {
+ service = ModelDefs;
+ httpBackend = $httpBackend;
+ $scope = _$rootScope_;
+ }));
+
+ it('should have a get method', (done) => {
+ httpBackend.expectGET(`${AppConfig.apiEndpoint}/utility/modeldefs/`)
+ .respond([
+ {name: 'ok'}
+ ]);
+ service.get()
+ .then((res) => {
+ expect(res[0].name).toEqual('ok');
+ done();
+ })
+ .catch(e => {
+ done(e);
+ });
+ $scope.$apply();
+ httpBackend.flush();
+ });
+});
diff --git a/src/app/datasources/stores/model.store.spec.ts b/src/app/datasources/stores/model.store.spec.ts
new file mode 100644
index 0000000..48798df
--- /dev/null
+++ b/src/app/datasources/stores/model.store.spec.ts
@@ -0,0 +1,117 @@
+import * as angular from 'angular';
+import 'angular-mocks';
+import 'angular-resource';
+import {IModelStoreService, ModelStore} from './model.store';
+import {Subject} from 'rxjs';
+import {IWSEvent} from '../websocket/global';
+import {StoreHelpers} from '../helpers/store.helpers';
+import {ModelRest} from '../rest/model.rest';
+import {AppConfig} from '../../config/app.config';
+
+let service: IModelStoreService;
+let httpBackend: ng.IHttpBackendService;
+let $scope;
+let WebSocket;
+
+class MockWs {
+ private _list;
+ constructor() {
+ this._list = new Subject<IWSEvent>();
+ }
+ list() {
+ return this._list.asObservable();
+ }
+
+ next(event: IWSEvent) {
+ this._list.next(event);
+ }
+}
+
+const queryData = [
+ {id: 1, name: 'foo'},
+ {id: 1, name: 'bar'}
+];
+
+describe('The ModelStore service', () => {
+
+ beforeEach(() => {
+ angular
+ .module('ModelStore', ['ngResource'])
+ .service('WebSocket', MockWs)
+ .service('StoreHelpers', StoreHelpers) // TODO mock
+ .service('ModelRest', ModelRest) // TODO mock
+ .service('ModelStore', ModelStore);
+
+ angular.mock.module('ModelStore');
+ });
+
+ beforeEach(angular.mock.inject((
+ ModelStore: IModelStoreService,
+ $httpBackend: ng.IHttpBackendService,
+ _$rootScope_: ng.IRootScopeService,
+ _WebSocket_: any
+ ) => {
+ service = ModelStore;
+ httpBackend = $httpBackend;
+ $scope = _$rootScope_;
+ WebSocket = _WebSocket_;
+
+ // ModelRest will call the backend
+ httpBackend.expectGET(`${AppConfig.apiEndpoint}/core/tests`)
+ .respond(queryData);
+ }));
+
+ it('should return an Observable', () => {
+ expect(typeof service.query('test').subscribe).toBe('function');
+ });
+
+ it('the first event should be the resource response', (done) => {
+ let event = 0;
+ service.query('test')
+ .subscribe(collection => {
+ event++;
+ if (event === 2) {
+ expect(collection[0].id).toEqual(queryData[0].id);
+ expect(collection[1].id).toEqual(queryData[1].id);
+ done();
+ }
+ });
+ $scope.$apply();
+ httpBackend.flush();
+ });
+
+ describe('when a web-socket event is received for that model', () => {
+ it('should update the collection', (done) => {
+ let event = 0;
+ service.query('test')
+ .subscribe(
+ collection => {
+ event++;
+ if (event === 3) {
+ expect(collection[0].id).toEqual(queryData[0].id);
+ expect(collection[1].id).toEqual(queryData[1].id);
+ expect(collection[2].id).toEqual(3);
+ expect(collection[2].name).toEqual('baz');
+ done();
+ }
+ },
+ err => {
+ console.log(err);
+ done(err);
+ }
+ );
+ window.setTimeout(() => {
+ WebSocket.next({
+ model: 'test',
+ msg: {
+ changed_fields: ['id'],
+ object: {id: 3, name: 'baz'},
+ pk: 3
+ }
+ });
+ }, 1);
+ $scope.$apply();
+ httpBackend.flush();
+ });
+ });
+});
diff --git a/src/app/datasources/stores/model.store.ts b/src/app/datasources/stores/model.store.ts
index f31d571..d306f9d 100644
--- a/src/app/datasources/stores/model.store.ts
+++ b/src/app/datasources/stores/model.store.ts
@@ -11,11 +11,11 @@
export class ModelStore {
static $inject = ['WebSocket', 'StoreHelpers', 'ModelRest'];
- private _slices: BehaviorSubject<any[]> = new BehaviorSubject([]);
+ private _collection: BehaviorSubject<any[]> = new BehaviorSubject([]);
constructor(
private webSocket: IWSEventService,
private storeHelpers: IStoreHelpersService,
- private sliceService: IXosResourceService
+ private ModelRest: IXosResourceService
) {
}
@@ -25,18 +25,19 @@
.filter((e: IWSEvent) => e.model === model)
.subscribe(
(event: IWSEvent) => {
- this.storeHelpers.updateCollection(event, this._slices);
- }
+ this.storeHelpers.updateCollection(event, this._collection);
+ },
+ err => console.error
);
- return this._slices.asObservable();
+ return this._collection.asObservable();
}
private loadInitialData(model: string) {
const endpoint = `/core/${model.toLowerCase()}s/`;
- this.sliceService.getResource(endpoint).query().$promise
+ this.ModelRest.getResource(endpoint).query().$promise
.then(
res => {
- this._slices.next(res);
+ this._collection.next(res);
},
err => console.log(`Error retrieving ${model}`, err)
);
diff --git a/src/app/datasources/websocket/global.ts b/src/app/datasources/websocket/global.ts
index aaf142a..89a8607 100644
--- a/src/app/datasources/websocket/global.ts
+++ b/src/app/datasources/websocket/global.ts
@@ -19,7 +19,6 @@
private _events: Subject<IWSEvent> = new Subject<IWSEvent>();
private socket;
constructor() {
- console.log('socket.io');
this.socket = io(AppConfig.websocketClient);
this.socket.on('event', (data: IWSEvent): void => {
this._events.next(data);
diff --git a/src/app/main.spec.ts b/src/app/main.spec.ts
index 2409163..4ac2d04 100644
--- a/src/app/main.spec.ts
+++ b/src/app/main.spec.ts
@@ -12,7 +12,7 @@
angular.mock.module('app');
});
- it('should render the header, title, techs and footer', angular.mock.inject(($rootScope: ng.IRootScopeService, $compile: ng.ICompileService) => {
+ it('should render the header and footer', angular.mock.inject(($rootScope: ng.IRootScopeService, $compile: ng.ICompileService) => {
const element = $compile('<app></app>')($rootScope);
$rootScope.$digest();
expect(element.find('xos-header').length).toEqual(1);
diff --git a/src/index.spec.js b/src/index.spec.js
index 3aaa277..8822bb5 100644
--- a/src/index.spec.js
+++ b/src/index.spec.js
@@ -2,4 +2,4 @@
context.keys().forEach(function(f) {
// console.log(f);
context(f);
-});
+});
\ No newline at end of file