From ae47b41eefbf401c1e0c467ac30d55b21c9a3382 Mon Sep 17 00:00:00 2001 From: Jake Marsh Date: Wed, 11 May 2016 22:06:53 -0700 Subject: [PATCH] clean up remaining tests --- app/js/stores/CurrentUserStore.js | 6 +-- app/js/utils/APIUtils.js | 9 ++-- app/js/utils/Helpers.js | 28 ++++++++++ gulp/tasks/test.js | 13 ++--- package.json | 3 +- tests/App.test.js | 69 +++++++++++++------------ tests/components/Header.test.js | 2 +- tests/pages/HomePage.test.js | 1 + tests/pages/NotFoundPage.test.js | 1 + tests/pages/SearchPage.test.js | 5 +- tests/stores/CurrentUserStore.test.js | 65 +++++++++++++++--------- tests/utils/APIUtils.test.js | 73 +++++++++++++++------------ tests/utils/AuthAPI.test.js | 40 ++++++++------- 13 files changed, 193 insertions(+), 122 deletions(-) create mode 100644 app/js/utils/Helpers.js 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 +});