diff --git a/app/js/stores/CurrentUserStore.js b/app/js/stores/CurrentUserStore.js
index e55766d..0cf37e7 100644
--- a/app/js/stores/CurrentUserStore.js
+++ b/app/js/stores/CurrentUserStore.js
@@ -29,7 +29,7 @@ const CurrentUserStore = Reflux.createStore({
if ( this.user ) {
this.setUser(this.user);
} else {
- AuthAPI.checkLoginStatus().then(user => {
+ AuthAPI.checkLoginStatus().then((user) => {
this.hasBeenChecked = true;
this.setUser(user);
}).catch(err => {
@@ -40,7 +40,7 @@ const CurrentUserStore = Reflux.createStore({
},
loginUser(user) {
- AuthAPI.login(user).then(user => {
+ AuthAPI.login(user).then((user) => {
this.setUser(user);
}).catch(err => {
this.throwError(err);
@@ -55,4 +55,4 @@ const CurrentUserStore = Reflux.createStore({
});
-export default CurrentUserStore;
\ No newline at end of file
+export default CurrentUserStore;
diff --git a/app/js/utils/APIUtils.js b/app/js/utils/APIUtils.js
index 5e27fc9..1f28404 100644
--- a/app/js/utils/APIUtils.js
+++ b/app/js/utils/APIUtils.js
@@ -1,14 +1,15 @@
'use strict';
-import {camelizeKeys} from 'humps';
-import request from 'superagent';
+import request from 'superagent';
+import {camel} from 'change-case';
+import helpers from './Helpers';
const APIUtils = {
root: '//localhost:3000/api/',
normalizeResponse(response) {
- return camelizeKeys(response.body);
+ return helpers.processObjectKeys(response, key => { return camel(key); });
},
get(path) {
@@ -84,4 +85,4 @@ const APIUtils = {
};
-export default APIUtils;
\ No newline at end of file
+export default APIUtils;
diff --git a/app/js/utils/Helpers.js b/app/js/utils/Helpers.js
new file mode 100644
index 0000000..07921da
--- /dev/null
+++ b/app/js/utils/Helpers.js
@@ -0,0 +1,28 @@
+'use strict';
+
+import _ from 'lodash';
+
+const Helpers = {
+
+ processObjectKeys(obj, convert) {
+ let output;
+
+ if ( _.isDate(obj) || _.isRegExp(obj) || !_.isObject(obj) ) {
+ return obj;
+ } else if ( _.isArray(obj) ) {
+ output = _.map(obj, item => {
+ return this.processObjectKeys(item, convert);
+ });
+ } else {
+ output = {};
+ _.forOwn(obj, (value, key) => {
+ output[convert(key)] = this.processObjectKeys(obj[key], convert);
+ });
+ }
+
+ return output;
+ }
+
+};
+
+export default Helpers;
diff --git a/gulp/tasks/test.js b/gulp/tasks/test.js
index 68eb028..8436ddd 100644
--- a/gulp/tasks/test.js
+++ b/gulp/tasks/test.js
@@ -27,17 +27,18 @@ gulp.task('test', () => {
// Ensure that all window/DOM related properties
// are available to all tests
- global.document = jsdom('
');
- global.window = document.parentWindow;
- global.location = { href: '' };
- global.navigator = {};
- global.navigator.userAgent = 'jsdom';
- global.navigator.appVersion = '';
+ global.document = jsdom('', {
+ url: 'http://localhost'
+ });
+ global.window = document.defaultView;
+ global.navigator = window.navigator;
+ global.KeyboardEvent = window.KeyboardEvent;
// Ensure that 'sinon' and 'chai' library methods will be
// available to all tests
global.sinon = require('sinon');
global.assert = require('chai').assert;
+ require('sinon-as-promised');
return (gjc.createTask({
src: files,
diff --git a/package.json b/package.json
index 0cfefe5..9baa798 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"browser-sync": "^2.7.13",
"browserify": "^12.0.1",
"chai": "^3.5.0",
+ "change-case": "^2.3.1",
"debowerify": "^1.3.1",
"del": "^2.2.0",
"eslint-plugin-react": "^3.6.3",
@@ -47,7 +48,6 @@
"gulp-uglify": "^1.2.0",
"gulp-util": "^3.0.6",
"history": "^1.17.0",
- "humps": "^1.0.0",
"istanbul": "^0.4.0",
"jsdom": "3.1.2",
"lodash": "^3.10.0",
@@ -60,6 +60,7 @@
"reflux": "^0.3.0",
"run-sequence": "^1.1.1",
"sinon": "^1.17.1",
+ "sinon-as-promised": "^4.0.0",
"superagent": "^1.2.0",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.2.x",
diff --git a/tests/App.test.js b/tests/App.test.js
index 6a7b185..bde3c92 100644
--- a/tests/App.test.js
+++ b/tests/App.test.js
@@ -3,57 +3,62 @@
import React from 'react';
import TestUtils from 'react-addons-test-utils';
+import fixtures from '../utils/fixtures';
+import copyObject from '../utils/copyObject';
import App from '../app/js/App';
import CurrentUserStore from '../app/js/stores/CurrentUserStore';
import CurrentUserActions from '../app/js/actions/CurrentUserActions';
-import {ListenerMixin} from 'reflux';
-import TestHelpers from '../utils/testHelpers';
describe('App', function() {
- it('#componentDidMount should listen to the user store and check login status', function() {
- sandbox.mock(ListenerMixin).expects('listenTo').withArgs(CurrentUserStore, App._onUserChange).once();
- sandbox.mock(CurrentUserActions).expects('checkLoginStatus').once();
+ const USER = copyObject(fixtures.user);
+ let rendered;
+ let props;
- TestUtils.renderIntoDocument(
-
-
+ function renderComponent() {
+ rendered = TestUtils.renderIntoDocument(
+
+
);
+ }
+
+ beforeEach(function() {
+ props = {
+ params: {},
+ query: {}
+ };
+
+ renderComponent();
+ });
+
+ it('#componentDidMount should listen to the user store and check login status', function() {
+ sandbox.stub(CurrentUserStore, 'listen');
+ sandbox.stub(CurrentUserActions, 'checkLoginStatus');
+
+ rendered.componentDidMount();
+
+ sinon.assert.calledOnce(CurrentUserStore.listen);
+ sinon.assert.calledWith(CurrentUserStore.listen, rendered.onUserChange);
+ sinon.assert.calledOnce(CurrentUserActions.checkLoginStatus);
});
it('#onUserChange should set the error state if an error is received', function() {
const err = { message: 'Test error message.' };
- const app = TestUtils.renderIntoDocument(
-
-
-
- );
- app.onUserChange(err, null);
- app.state.error.should.eql(err);
+ rendered.onUserChange(err, null);
+
+ assert.deepEqual(rendered.state.error, err);
});
it('#onUserChange should set the user state and clear error if a new user is received', function() {
- const user = TestHelpers.fixtures.user;
- const app = TestUtils.renderIntoDocument(
-
-
-
- );
+ rendered.onUserChange(null, USER);
- app.onUserChange(null, user);
- app.state.currentUser.should.eql(user);
- Should(app.state.error).be.null();
+ assert.strictEqual(rendered.state.currentUser, USER);
+ assert.isNull(rendered.state.error);
});
it('#renderChildren should return all the cloned children', function() {
- const app = TestUtils.renderIntoDocument(
-
-
-
- );
-
- TestUtils.findRenderedDOMComponentWithClass(app, 'test-child').should.not.throw();
+ assert.doesNotThrow(TestUtils.findRenderedDOMComponentWithClass.bind(null, rendered, 'test-child'));
});
-});
\ No newline at end of file
+});
diff --git a/tests/components/Header.test.js b/tests/components/Header.test.js
index 8232b76..4c7c1b1 100644
--- a/tests/components/Header.test.js
+++ b/tests/components/Header.test.js
@@ -22,7 +22,7 @@ describe('Component: Header', function() {
});
it('should render properly', function() {
- assert.DoesNotThrow(TestUtils.findRenderedDOMComponentWithTag.bind(null, rendered, 'header'));
+ assert.doesNotThrow(TestUtils.findRenderedDOMComponentWithTag.bind(null, rendered, 'header'));
});
});
diff --git a/tests/pages/HomePage.test.js b/tests/pages/HomePage.test.js
index c78350b..af35846 100644
--- a/tests/pages/HomePage.test.js
+++ b/tests/pages/HomePage.test.js
@@ -1,5 +1,6 @@
'use strict';
+import React from 'react';
import TestUtils from 'react-addons-test-utils';
import HomePage from '../../app/js/pages/HomePage';
diff --git a/tests/pages/NotFoundPage.test.js b/tests/pages/NotFoundPage.test.js
index 99f6684..1cf7bfb 100644
--- a/tests/pages/NotFoundPage.test.js
+++ b/tests/pages/NotFoundPage.test.js
@@ -1,5 +1,6 @@
'use strict';
+import React from 'react';
import TestUtils from 'react-addons-test-utils';
import NotFoundPage from '../../app/js/pages/NotFoundPage';
diff --git a/tests/pages/SearchPage.test.js b/tests/pages/SearchPage.test.js
index 0769ded..261e0f4 100644
--- a/tests/pages/SearchPage.test.js
+++ b/tests/pages/SearchPage.test.js
@@ -1,5 +1,6 @@
'use strict';
+import React from 'react';
import TestUtils from 'react-addons-test-utils';
import SearchPage from '../../app/js/pages/SearchPage';
@@ -21,7 +22,7 @@ describe('Page: Search', function() {
});
it('should render properly', function() {
- TestUtils.findRenderedDOMComponentWithClass.bind(null, rendered, 'search-page').should.not.throw();
+ assert.doesNotThrow(TestUtils.findRenderedDOMComponentWithClass.bind(null, rendered, 'search-page'));
});
it('should update state on input box change', function() {
@@ -31,7 +32,7 @@ describe('Page: Search', function() {
input.value = newValue;
TestUtils.Simulate.change(input);
- rendered.state.query.should.eql(newValue);
+ assert.strictEqual(rendered.state.query, newValue);
});
it('should render the updated state in the related span', function() {
diff --git a/tests/stores/CurrentUserStore.test.js b/tests/stores/CurrentUserStore.test.js
index d22293d..7ae54e3 100644
--- a/tests/stores/CurrentUserStore.test.js
+++ b/tests/stores/CurrentUserStore.test.js
@@ -1,13 +1,18 @@
'use strict';
-import TestHelpers from '../../utils/testHelpers';
+import fixtures from '../../utils/fixtures';
+import copyObject from '../../utils/copyObject';
import CurrentUserStore from '../../app/js/stores/CurrentUserStore';
import CurrentUserActions from '../../app/js/actions/CurrentUserActions';
import AuthAPI from '../../app/js/utils/AuthAPI';
describe('Store: CurrentUser', function() {
- const USER = TestHelpers.fixtures.user;
+ const USER = copyObject(fixtures.user);
+
+ beforeEach(function() {
+ CurrentUserStore.user = null;
+ });
it('#setUser should set this.user and trigger the store', function() {
sandbox.stub(CurrentUserStore, 'trigger');
@@ -29,38 +34,50 @@ describe('Store: CurrentUser', function() {
sinon.assert.calledWith(CurrentUserStore.trigger, err);
});
- it('#checkLoginStatus should check user\'s login status on action', function() {
- sandbox.stub(AuthAPI, 'checkLoginStatus').returns(Promise.resolve(USER));
- sandbox.stub(CurrentUserStore, 'setUser');
+ it('#checkLoginStatus should check user\'s login status on action', function(done) {
+ sandbox.stub(AuthAPI, 'checkLoginStatus').resolves(USER);
+ sandbox.spy(CurrentUserStore, 'setUser');
- CurrentUserActions.checkLoginStatus();
+ sandbox.stub(CurrentUserStore, 'trigger', () => {
+ sinon.assert.calledOnce(AuthAPI.checkLoginStatus);
+ sinon.assert.calledOnce(CurrentUserStore.setUser);
+ sinon.assert.calledWith(CurrentUserStore.setUser, USER);
- sinon.assert.calledOnce(AuthAPI.checkLoginStatus);
- sinon.assert.calledOnce(CurrentUserStore.setUser);
- sinon.assert.calledWith(CurrentUserStore.setUser, USER);
+ done();
+ });
+
+ CurrentUserActions.checkLoginStatus();
});
- it('#loginUser should log user in on action if API response is successful', function() {
- sandbox.stub(AuthAPI, 'login').returns(Promise.resolve(USER));
- sandbox.stub(CurrentUserStore, 'setUser');
+ it('#loginUser should log user in on action if API response is successful', function(done) {
+ sandbox.stub(AuthAPI, 'login').resolves(USER);
+ sandbox.spy(CurrentUserStore, 'setUser');
- CurrentUserActions.login(USER);
+ sandbox.stub(CurrentUserStore, 'trigger', () => {
+ sinon.assert.calledOnce(AuthAPI.login);
+ sinon.assert.calledWith(AuthAPI.login, USER);
+ sinon.assert.calledOnce(CurrentUserStore.setUser);
+ sinon.assert.calledWith(CurrentUserStore.setUser, USER);
- sinon.assert.calledOnce(AuthAPI.login);
- sinon.assert.calledWith(AuthAPI.login, USER);
- sinon.assert.calledOnce(CurrentUserStore.setUser);
- sinon.assert.calledWith(CurrentUserStore.setUser, USER);
+ done();
+ });
+
+ CurrentUserActions.login(USER);
});
- it('#logoutUser should log user out on action', function() {
- sandbox.stub(AuthAPI, 'logout').returns(Promise.resolve());
- sandbox.stub(CurrentUserStore, 'setUser');
+ it('#logoutUser should log user out on action', function(done) {
+ sandbox.stub(AuthAPI, 'logout').resolves();
+ sandbox.spy(CurrentUserStore, 'setUser');
- CurrentUserActions.logout();
+ sandbox.stub(CurrentUserStore, 'trigger', () => {
+ sinon.assert.calledOnce(AuthAPI.logout);
+ sinon.assert.calledOnce(CurrentUserStore.setUser);
+ sinon.assert.calledWith(CurrentUserStore.setUser, null);
- sinon.assert.calledOnce(AuthAPI.logout);
- sinon.assert.calledOnce(CurrentUserStore.setUser);
- sinon.assert.calledWith(CurrentUserStore.setUser, null);
+ done();
+ });
+
+ CurrentUserActions.logout();
});
});
diff --git a/tests/utils/APIUtils.test.js b/tests/utils/APIUtils.test.js
index 923e109..b71d04e 100644
--- a/tests/utils/APIUtils.test.js
+++ b/tests/utils/APIUtils.test.js
@@ -1,72 +1,83 @@
'use strict';
-import request from 'superagent';
-import {camelizeKeys} from 'humps';
-import APIUtils from '../../app/js/utils/APIUtils';
+import request from 'superagent';
+import APIUtils from '../../app/js/utils/APIUtils';
describe('Util: APIUtils', function() {
- it('should normalize the body of a response object with varying keys', function() {
- let testResponse = {
- status: 200,
- body: {
- camel_case: 'test', //eslint-disable-line camelcase
- PascalCase: 'test',
- camelCase: 'test'
- }
+ it('#normalizeResponse should normalize the body of a response object with varying keys', function() {
+ const beforeObj = {
+ camel_case: 'yes', //eslint-disable-line camelcase
+ WhatIsThisCase: 'yes'
};
+ const afterObj = { camelCase: 'yes', whatIsThisCase: 'yes' };
- APIUtils.normalizeResponse(testResponse).should.eql(camelizeKeys(testResponse.body));
+ assert.deepEqual(APIUtils.normalizeResponse(beforeObj), afterObj);
});
- it('should make a GET request', function() {
- let path = 'auth/check';
+ it('#get should make a GET request', function() {
+ const path = 'auth/check';
- sandbox.mock(request).expects('get').withArgs('http://localhost:3000/api/' + path);
+ sandbox.stub(request, 'get');
APIUtils.get(path);
+
+ sinon.assert.calledOnce(request.get);
+ sinon.assert.calledWith(request.get, APIUtils.root + path);
});
- it('should make a POST request', function() {
- let path = 'auth/login';
- let user = {
+ it('#post should make a POST request', function() {
+ const path = 'auth/login';
+ const user = {
username: 'test',
password: 'test'
};
- sandbox.mock(request).expects('post').withArgs('http://localhost:3000/api/' + path, user);
+ sandbox.stub(request, 'post');
APIUtils.post(path, user);
+
+ sinon.assert.calledOnce(request.post);
+ sinon.assert.calledWith(request.post, APIUtils.root + path, user);
});
- it('should make a PATCH request', function() {
- let path = 'user/1';
- let user = {
+ it('#patch should make a PATCH request', function() {
+ const path = 'user/1';
+ const user = {
email: 'new@test.com'
};
- sandbox.mock(request).expects('patch').withArgs('http://localhost:3000/api/' + path, user);
+ sandbox.stub(request, 'patch');
APIUtils.patch(path, user);
+
+ sinon.assert.calledOnce(request.patch);
+ sinon.assert.calledWith(request.patch, APIUtils.root + path, user);
});
- it('should make a PUT request', function() {
- let path = 'user/1';
- let user = {
+ it('#put should make a PUT request', function() {
+ const path = 'user/1';
+ const user = {
email: 'new@test.com'
};
- sandbox.mock(request).expects('put').withArgs('http://localhost:3000/api/' + path, user);
+ sandbox.stub(request, 'put');
APIUtils.put(path, user);
+
+ sinon.assert.calledOnce(request.put);
+ sinon.assert.calledWith(request.put, APIUtils.root + path, user);
});
- it('should make a DEL request', function() {
- let path = 'user/1';
+ it('#del should make a DELETE request', function() {
+ const path = 'user/1';
- sandbox.mock(request).expects('del').withArgs('http://localhost:3000/api/' + path);
+ sandbox.stub(request, 'del');
APIUtils.del(path);
+
+ sinon.assert.calledOnce(request.del);
+ sinon.assert.calledWith(request.del, APIUtils.root + path);
});
-});
\ No newline at end of file
+});
diff --git a/tests/utils/AuthAPI.test.js b/tests/utils/AuthAPI.test.js
index 3d56252..90c5ce2 100644
--- a/tests/utils/AuthAPI.test.js
+++ b/tests/utils/AuthAPI.test.js
@@ -1,41 +1,45 @@
'use strict';
-import APIUtils from '../../app/js/utils/APIUtils';
-import AuthAPI from '../../app/js/utils/AuthAPI';
-import TestHelpers from '../../utils/testHelpers';
+import fixtures from '../../utils/fixtures';
+import copyObject from '../../utils/copyObject';
+import APIUtils from '../../app/js/utils/APIUtils';
+import AuthAPI from '../../app/js/utils/AuthAPI';
describe('Util: AuthAPI', function() {
- const user = TestHelpers.fixtures.user;
+ const USER = copyObject(fixtures.user);
- it('should make a request to check a user\'s login status', function(done) {
- let path = 'auth/check';
+ it('#checkLoginStatus should make a request to check a user\'s login status', function() {
+ const path = 'auth/check';
- sandbox.mock(APIUtils).expects('get').withArgs(path);
+ sandbox.stub(APIUtils, 'get');
AuthAPI.checkLoginStatus();
- done();
+ sinon.assert.calledOnce(APIUtils.get);
+ sinon.assert.calledWith(APIUtils.get, path);
});
- it('should make a request to login a user', function(done) {
- let path = 'auth/login';
+ it('#login should make a request to login a user', function() {
+ const path = 'auth/login';
- sandbox.mock(APIUtils).expects('post').withArgs(path, user);
+ sandbox.stub(APIUtils, 'post');
- AuthAPI.login(user);
+ AuthAPI.login(USER);
- done();
+ sinon.assert.calledOnce(APIUtils.post);
+ sinon.assert.calledWith(APIUtils.post, path, USER);
});
- it('should make a request to log a user out', function(done) {
- let path = 'auth/logout';
+ it('#logout should make a request to log a user out', function() {
+ const path = 'auth/logout';
- sandbox.mock(APIUtils).expects('post').withArgs(path);
+ sandbox.stub(APIUtils, 'post');
AuthAPI.logout();
- done();
+ sinon.assert.calledOnce(APIUtils.post);
+ sinon.assert.calledWith(APIUtils.post, path);
});
-});
\ No newline at end of file
+});