From 3c12abfc6dafbc38a8d16e0fea81c5f286d08197 Mon Sep 17 00:00:00 2001 From: Shane Tomlinson Date: Mon, 24 Feb 2014 15:21:18 +0000 Subject: [PATCH] feature(test): Add a front end code coverage task - `grunt coverage` * Use blanket as the coverage reporter. * Install `grunt-blanket-mocha` to handle reporting results to grunt. * Set up new grunt task, `grunt coverage` for reporting. * Add more front end tests where coverage was lacking. * Hook up code coverage results to the intern by running tests in PhantomJS * Only enable code coverage if explicitly requested * Only enable front end tests in dev mode. * Fix code that gave PhantomJS problems (Function.prototype.bind) --- app/scripts/views/complete_reset_password.js | 7 +- app/scripts/views/reset_password.js | 17 ++- app/tests/index.html | 15 --- app/tests/main.js | 35 +++--- app/tests/mocks/window.js | 2 + app/tests/setup.js | 6 +- app/tests/spec/lib/auth-errors.js | 3 +- app/tests/spec/lib/channels/fx-desktop.js | 3 +- app/tests/spec/lib/channels/web.js | 3 +- app/tests/spec/lib/fxa-client.js | 3 +- app/tests/spec/lib/router.js | 58 ++++++++-- app/tests/spec/lib/session.js | 3 +- app/tests/spec/lib/strings.js | 3 +- app/tests/spec/lib/translator.js | 3 +- app/tests/spec/lib/url.js | 3 +- app/tests/spec/lib/xss.js | 3 +- app/tests/spec/views/base.js | 3 +- app/tests/spec/views/change_password.js | 3 +- .../spec/views/complete_reset_password.js | 60 ++++++++++- app/tests/spec/views/complete_sign_up.js | 3 +- app/tests/spec/views/confirm.js | 3 +- .../spec/views/confirm_reset_password.js | 3 +- app/tests/spec/views/delete_account.js | 3 +- app/tests/spec/views/form.js | 3 +- app/tests/spec/views/pp.js | 3 +- app/tests/spec/views/reset_password.js | 51 +++++++-- .../spec/views/reset_password_complete.js | 3 +- app/tests/spec/views/settings.js | 3 +- app/tests/spec/views/sign_in.js | 3 +- app/tests/spec/views/sign_up.js | 3 +- app/tests/spec/views/tooltip.js | 3 +- app/tests/spec/views/tos.js | 3 +- bower.json | 3 +- grunttasks/blanket_mocha.js | 22 ++++ grunttasks/coverage.js | 13 +++ lockdown.json | 102 +++--------------- package.json | 3 +- server/lib/routes.js | 16 ++- server/templates/pages/src/mocha.html | 39 +++++++ tests/functional/mocha.js | 23 +++- tests/server/routes.js | 2 + 41 files changed, 333 insertions(+), 210 deletions(-) delete mode 100644 app/tests/index.html create mode 100644 grunttasks/blanket_mocha.js create mode 100644 grunttasks/coverage.js create mode 100644 server/templates/pages/src/mocha.html diff --git a/app/scripts/views/complete_reset_password.js b/app/scripts/views/complete_reset_password.js index 497bb5ef7..d01175d0d 100644 --- a/app/scripts/views/complete_reset_password.js +++ b/app/scripts/views/complete_reset_password.js @@ -32,17 +32,18 @@ function (_, BaseView, FormView, Template, FxaClient, Session, Url, PasswordMixi }, afterRender: function () { - this.token = Url.searchParam('token'); + var search = this.window.location.search; + this.token = Url.searchParam('token', search); if (! this.token) { return this.displayError(t('no token specified')); } - this.code = Url.searchParam('code'); + this.code = Url.searchParam('code', search); if (! this.code) { return this.displayError(t('no code specified')); } - this.email = Url.searchParam('email'); + this.email = Url.searchParam('email', search); if (! this.email) { return this.displayError(t('no email specified')); } diff --git a/app/scripts/views/reset_password.js b/app/scripts/views/reset_password.js index 7af1f634f..af862429a 100644 --- a/app/scripts/views/reset_password.js +++ b/app/scripts/views/reset_password.js @@ -29,18 +29,13 @@ function (_, FormView, Template, FxaClient, Session) { var email = this.$('.email').val(); var client = new FxaClient(); + var self = this; client.passwordReset(email) - .done(this._onRequestResetSuccess.bind(this), - this._onRequestResetFailure.bind(this)); - - }, - - _onRequestResetSuccess: function () { - this.navigate('confirm_reset_password'); - }, - - _onRequestResetFailure: function (err) { - this.displayError(err); + .then(function() { + self.navigate('confirm_reset_password'); + }, function (err) { + self.displayError(err); + }); } }); diff --git a/app/tests/index.html b/app/tests/index.html deleted file mode 100644 index 933f2eeda..000000000 --- a/app/tests/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Firefox Accounts Unit Tests - - - -
-
- - - - diff --git a/app/tests/main.js b/app/tests/main.js index 6e5b72b53..4d4889978 100644 --- a/app/tests/main.js +++ b/app/tests/main.js @@ -5,29 +5,25 @@ 'use strict'; require.config({ - baseUrl: '../scripts', + baseUrl: '/scripts', paths: { - jquery: '../bower_components/jquery/jquery', - backbone: '../bower_components/backbone/backbone', - underscore: '../bower_components/underscore/underscore', - fxaClient: '../bower_components/fxa-js-client/fxa-client', - text: '../bower_components/requirejs-text/text', - mustache: '../bower_components/mustache/mustache', - stache: '../bower_components/requirejs-mustache/stache', - transit: '../bower_components/jquery.transit/jquery.transit', - modernizr: '../bower_components/modernizr/modernizr', - mocha: '../bower_components/mocha/mocha', - chai: '../bower_components/chai/chai', - p: '../bower_components/p/p', - sinon: '../bower_components/sinon/index' + jquery: '/bower_components/jquery/jquery', + backbone: '/bower_components/backbone/backbone', + underscore: '/bower_components/underscore/underscore', + fxaClient: '/bower_components/fxa-js-client/fxa-client', + text: '/bower_components/requirejs-text/text', + mustache: '/bower_components/mustache/mustache', + stache: '/bower_components/requirejs-mustache/stache', + transit: '/bower_components/jquery.transit/jquery.transit', + modernizr: '/bower_components/modernizr/modernizr', + chai: '/bower_components/chai/chai', + p: '/bower_components/p/p', + sinon: '/bower_components/sinon/index' }, shim: { underscore: { exports: '_' }, - mocha: { - exports: 'mocha' - }, backbone: { deps: [ 'underscore', @@ -51,7 +47,6 @@ require.config({ }); require([ - 'mocha', 'lib/translator', '../tests/setup', '../tests/spec/lib/channels/web', @@ -81,11 +76,11 @@ require([ '../tests/spec/views/complete_reset_password', '../tests/spec/views/reset_password_complete' ], -function (Mocha, Translator) { +function (Translator) { // The translator is expected to be on the window object. window.translator = new Translator(); - var runner = Mocha.run(); + var runner = mocha.run(); runner.on('end', function () { // This is our hook to the Selenium tests that run diff --git a/app/tests/mocks/window.js b/app/tests/mocks/window.js index 8fc2afb87..19cdf885c 100644 --- a/app/tests/mocks/window.js +++ b/app/tests/mocks/window.js @@ -46,6 +46,8 @@ function (_, Backbone) { CustomEvent: function(command, data) { return data; + }, + scrollTo: function(x, y) { } }); diff --git a/app/tests/setup.js b/app/tests/setup.js index 2db315020..a4cfcbf3f 100644 --- a/app/tests/setup.js +++ b/app/tests/setup.js @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -define(['mocha'], function(Mocha) { - Mocha.setup('bdd'); - Mocha.timeout(5000); +define([], function() { + mocha.setup('bdd'); + mocha.timeout(5000); }); diff --git a/app/tests/spec/lib/auth-errors.js b/app/tests/spec/lib/auth-errors.js index be5e4b446..fa31a6dc2 100644 --- a/app/tests/spec/lib/auth-errors.js +++ b/app/tests/spec/lib/auth-errors.js @@ -8,11 +8,10 @@ define([ - 'mocha', 'chai', 'lib/auth-errors' ], -function (mocha, chai, AuthErrors) { +function (chai, AuthErrors) { /*global describe, it*/ var assert = chai.assert; diff --git a/app/tests/spec/lib/channels/fx-desktop.js b/app/tests/spec/lib/channels/fx-desktop.js index fe50cea19..636cb0833 100644 --- a/app/tests/spec/lib/channels/fx-desktop.js +++ b/app/tests/spec/lib/channels/fx-desktop.js @@ -6,14 +6,13 @@ define([ - 'mocha', 'chai', '/tests/mocks/window.js', '/tests/mocks/router.js', 'lib/session', 'lib/channels/fx-desktop' ], -function (mocha, chai, WindowMock, RouterMock, Session, FxDesktopChannel) { +function (chai, WindowMock, RouterMock, Session, FxDesktopChannel) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; var channel; diff --git a/app/tests/spec/lib/channels/web.js b/app/tests/spec/lib/channels/web.js index ef4f2e923..6bd256b6c 100644 --- a/app/tests/spec/lib/channels/web.js +++ b/app/tests/spec/lib/channels/web.js @@ -6,11 +6,10 @@ define([ - 'mocha', 'chai', 'lib/channels/web' ], -function (mocha, chai, WebChannel) { +function (chai, WebChannel) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; var channel; diff --git a/app/tests/spec/lib/fxa-client.js b/app/tests/spec/lib/fxa-client.js index 3c9073ec0..106265046 100644 --- a/app/tests/spec/lib/fxa-client.js +++ b/app/tests/spec/lib/fxa-client.js @@ -6,7 +6,6 @@ define([ - 'mocha', 'chai', 'jquery', '../../mocks/channel', @@ -18,7 +17,7 @@ define([ // FxaClientWrapper is the object that is used in // fxa-content-server views. It wraps FxaClient to // take care of some app-specific housekeeping. -function (mocha, chai, $, ChannelMock, testHelpers, +function (chai, $, ChannelMock, testHelpers, Session, FxaClientWrapper, AuthErrors) { /*global beforeEach, afterEach, describe, it*/ var assert = chai.assert; diff --git a/app/tests/spec/lib/router.js b/app/tests/spec/lib/router.js index b9a0225ec..ea5c8eda6 100644 --- a/app/tests/spec/lib/router.js +++ b/app/tests/spec/lib/router.js @@ -6,14 +6,16 @@ define([ - 'mocha', 'chai', 'underscore', 'backbone', 'router', - '../../mocks/window' + 'views/sign_in', + 'views/sign_up', + 'lib/session', + '../../mocks/window', ], -function (mocha, chai, _, Backbone, Router, WindowMock) { +function (chai, _, Backbone, Router, SignInView, SignUpView, Session, WindowMock) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; @@ -23,6 +25,8 @@ function (mocha, chai, _, Backbone, Router, WindowMock) { beforeEach(function () { navigateUrl = navigateOptions = null; + $('#container').html('
'); + windowMock = new WindowMock(); router = new Router({ window: windowMock @@ -38,24 +42,62 @@ function (mocha, chai, _, Backbone, Router, WindowMock) { afterEach(function () { windowMock = router = navigateUrl = navigateOptions = null; Backbone.Router.prototype.navigate = origNavigate; + $('#container').empty(); }); describe('navigate', function () { - it('Tells the router to havigate to a page', function () { + it('Tells the router to navigate to a page', function () { windowMock.location.search = ''; - router.navigate('signin'); - assert.equal(navigateUrl, 'signin'); + router.navigate('/signin'); + assert.equal(navigateUrl, '/signin'); assert.deepEqual(navigateOptions, { trigger: true }); }); it('preserves window search parameters across screen transition', function () { windowMock.location.search = '?context=fx_desktop_v1'; - router.navigate('forgot'); - assert.equal(navigateUrl, 'forgot?context=fx_desktop_v1'); + router.navigate('/forgot'); + assert.equal(navigateUrl, '/forgot?context=fx_desktop_v1'); assert.deepEqual(navigateOptions, { trigger: true }); }); }); + + describe('redirectToSignup', function () { + it('go to the signup page', function () { + windowMock.location.search = ''; + router.redirectToSignup(); + assert.equal(navigateUrl, '/signup'); + assert.deepEqual(navigateOptions, { trigger: true }); + }); + }); + + describe('showView, then another showView', function () { + var signInView, signUpView; + + beforeEach(function () { + signInView = new SignInView({}); + signUpView = new SignUpView({}); + Session.clear(); + }); + + afterEach(function() { + signInView = signUpView = null; + Session.clear(); + }); + + it('shows a view, then shows the new view', function () { + router.showView(signInView); + assert.ok($('#fxa-signin-header').length); + // session was cleared in beforeEach, simulating a user + // visiting their first page. The user cannot go back. + assert.equal(Session.canGoBack, false); + + router.showView(signUpView); + assert.ok($('#fxa-signup-header').length); + // if there is a back button, it can be shown now. + assert.equal(Session.canGoBack, true); + }); + }); }); }); diff --git a/app/tests/spec/lib/session.js b/app/tests/spec/lib/session.js index e32860a53..5c8622255 100644 --- a/app/tests/spec/lib/session.js +++ b/app/tests/spec/lib/session.js @@ -6,11 +6,10 @@ define([ - 'mocha', 'chai', 'lib/session' ], -function (mocha, chai, Session) { +function (chai, Session) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; diff --git a/app/tests/spec/lib/strings.js b/app/tests/spec/lib/strings.js index 547cd8aaf..4130e4bef 100644 --- a/app/tests/spec/lib/strings.js +++ b/app/tests/spec/lib/strings.js @@ -8,11 +8,10 @@ define([ - 'mocha', 'chai', 'lib/strings' ], -function (mocha, chai, Strings) { +function (chai, Strings) { /*global describe, it*/ var assert = chai.assert; diff --git a/app/tests/spec/lib/translator.js b/app/tests/spec/lib/translator.js index 35b2d61b1..e8323c149 100644 --- a/app/tests/spec/lib/translator.js +++ b/app/tests/spec/lib/translator.js @@ -8,11 +8,10 @@ define([ - 'mocha', 'chai', 'lib/translator' ], -function (mocha, chai, Translator) { +function (chai, Translator) { /*global beforeEach, afterEach, describe, it*/ var assert = chai.assert; diff --git a/app/tests/spec/lib/url.js b/app/tests/spec/lib/url.js index 7cdcc300b..49d25fdba 100644 --- a/app/tests/spec/lib/url.js +++ b/app/tests/spec/lib/url.js @@ -6,13 +6,12 @@ define([ - 'mocha', 'chai', 'underscore', 'lib/url', 'lib/constants' ], -function (mocha, chai, _, Url, Constants) { +function (chai, _, Url, Constants) { var assert = chai.assert; var channel; diff --git a/app/tests/spec/lib/xss.js b/app/tests/spec/lib/xss.js index 47e2cffa3..eccd53d9c 100644 --- a/app/tests/spec/lib/xss.js +++ b/app/tests/spec/lib/xss.js @@ -6,13 +6,12 @@ define([ - 'mocha', 'chai', 'underscore', 'lib/xss', 'lib/constants' ], -function (mocha, chai, _, XSS, Constants) { +function (chai, _, XSS, Constants) { var assert = chai.assert; var channel; diff --git a/app/tests/spec/views/base.js b/app/tests/spec/views/base.js index b56cc217b..0bc23c130 100644 --- a/app/tests/spec/views/base.js +++ b/app/tests/spec/views/base.js @@ -6,7 +6,6 @@ define([ - 'mocha', 'chai', 'jquery', 'views/base', @@ -16,7 +15,7 @@ define([ '../../mocks/router', '../../lib/helpers' ], -function (mocha, chai, jQuery, BaseView, Translator, Template, DOMEventMock, +function (chai, jQuery, BaseView, Translator, Template, DOMEventMock, RouterMock, TestHelpers) { var requiresFocus = TestHelpers.requiresFocus; diff --git a/app/tests/spec/views/change_password.js b/app/tests/spec/views/change_password.js index 964a6b0b8..53daa4cc9 100644 --- a/app/tests/spec/views/change_password.js +++ b/app/tests/spec/views/change_password.js @@ -6,7 +6,6 @@ define([ - 'mocha', 'chai', 'underscore', 'jquery', @@ -15,7 +14,7 @@ define([ 'lib/session', '../../mocks/router' ], -function (mocha, chai, _, $, View, FxaClient, Session, RouterMock) { +function (chai, _, $, View, FxaClient, Session, RouterMock) { var assert = chai.assert; describe('views/change_password', function () { diff --git a/app/tests/spec/views/complete_reset_password.js b/app/tests/spec/views/complete_reset_password.js index 210f68c27..250ad2f03 100644 --- a/app/tests/spec/views/complete_reset_password.js +++ b/app/tests/spec/views/complete_reset_password.js @@ -6,18 +6,23 @@ define([ - 'mocha', 'chai', - 'views/complete_reset_password' + 'views/complete_reset_password', + '../../mocks/window' ], -function (mocha, chai, View) { +function (chai, View, WindowMock) { var assert = chai.assert; describe('views/complete_reset_password', function () { - var view; + var view, windowMock; beforeEach(function () { - view = new View(); + windowMock = new WindowMock(); + + view = new View({ + window: windowMock + }); + view.render(); $('#container').html(view.el); }); @@ -25,6 +30,7 @@ function (mocha, chai, View) { afterEach(function () { view.remove(); view.destroy(); + view = windowMock = null; }); describe('constructor creates it', function () { @@ -33,6 +39,32 @@ function (mocha, chai, View) { }); }); + describe('render', function () { + it('shows an error if the token is missing', function (done) { + windowMock.location.search = '?code=code&email=testuser@testuser.com'; + view.on('error', function () { + done(); + }); + view.render(); + }); + + it('shows an error if the code is missing', function (done) { + windowMock.location.search = '?token=token&email=testuser@testuser.com'; + view.on('error', function () { + done(); + }); + view.render(); + }); + + it('shows an error if the email is missing', function (done) { + windowMock.location.search = '?token=token&code=code'; + view.on('error', function () { + done(); + }); + view.render(); + }); + }); + describe('updatePasswordVisibility', function () { it('pw field set to text when clicked', function () { $('.show-password').click(); @@ -124,6 +156,24 @@ function (mocha, chai, View) { view.showValidationErrors(); }); }); + + describe('submit', function() { + it('shows an error if passwords are the same', function (done) { + view.$('#password').val('password1'); + view.$('#vpassword').val('password2'); + + view.on('error', function() { + done(); + }); + + view.submit(); + }); + + it('submits if passwords are the same', function () { + view.$('[type=password]').val('password'); + view.submit(); + }); + }); }); }); diff --git a/app/tests/spec/views/complete_sign_up.js b/app/tests/spec/views/complete_sign_up.js index 79e41ad64..a54866636 100644 --- a/app/tests/spec/views/complete_sign_up.js +++ b/app/tests/spec/views/complete_sign_up.js @@ -6,7 +6,6 @@ define([ - 'mocha', 'chai', 'views/complete_sign_up', 'lib/session', @@ -14,7 +13,7 @@ define([ '../../mocks/window', '../../lib/helpers' ], -function (mocha, chai, View, Session, FxaClientWrapper, +function (chai, View, Session, FxaClientWrapper, WindowMock, testHelpers) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; diff --git a/app/tests/spec/views/confirm.js b/app/tests/spec/views/confirm.js index 36d14c16d..cc32a57da 100644 --- a/app/tests/spec/views/confirm.js +++ b/app/tests/spec/views/confirm.js @@ -6,13 +6,12 @@ define([ - 'mocha', 'chai', 'views/confirm', 'lib/fxa-client', '../../mocks/router' ], -function (mocha, chai, View, FxaClient, RouterMock) { +function (chai, View, FxaClient, RouterMock) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; diff --git a/app/tests/spec/views/confirm_reset_password.js b/app/tests/spec/views/confirm_reset_password.js index d4bcb7848..025cb2f94 100644 --- a/app/tests/spec/views/confirm_reset_password.js +++ b/app/tests/spec/views/confirm_reset_password.js @@ -6,13 +6,12 @@ define([ - 'mocha', 'chai', 'views/confirm_reset_password', 'lib/fxa-client', '../../mocks/router' ], -function (mocha, chai, View, FxaClient, RouterMock) { +function (chai, View, FxaClient, RouterMock) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; diff --git a/app/tests/spec/views/delete_account.js b/app/tests/spec/views/delete_account.js index 08eb611d6..319228c80 100644 --- a/app/tests/spec/views/delete_account.js +++ b/app/tests/spec/views/delete_account.js @@ -6,7 +6,6 @@ define([ - 'mocha', 'chai', 'jquery', 'views/delete_account', @@ -14,7 +13,7 @@ define([ 'lib/session', '../../mocks/router' ], -function (mocha, chai, $, View, FxaClient, Session, RouterMock) { +function (chai, $, View, FxaClient, Session, RouterMock) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; diff --git a/app/tests/spec/views/form.js b/app/tests/spec/views/form.js index b0c729b3e..2826b6f57 100644 --- a/app/tests/spec/views/form.js +++ b/app/tests/spec/views/form.js @@ -6,14 +6,13 @@ define([ - 'mocha', 'chai', 'jquery', 'views/form', 'stache!templates/test_template', '../../lib/helpers' ], -function (mocha, chai, $, FormView, Template, TestHelpers) { +function (chai, $, FormView, Template, TestHelpers) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; diff --git a/app/tests/spec/views/pp.js b/app/tests/spec/views/pp.js index 271527563..c3be2f714 100644 --- a/app/tests/spec/views/pp.js +++ b/app/tests/spec/views/pp.js @@ -6,12 +6,11 @@ define([ - 'mocha', 'chai', 'views/pp', 'lib/session' ], -function (mocha, chai, View, Session) { +function (chai, View, Session) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; diff --git a/app/tests/spec/views/reset_password.js b/app/tests/spec/views/reset_password.js index e87820dd4..b6569be5a 100644 --- a/app/tests/spec/views/reset_password.js +++ b/app/tests/spec/views/reset_password.js @@ -6,26 +6,33 @@ define([ - 'mocha', 'chai', 'views/reset_password', - '../../mocks/window' + 'lib/fxa-client', + '../../mocks/window', + '../../mocks/router' ], -function (mocha, chai, View, WindowMock) { +function (chai, View, FxaClient, WindowMock, RouterMock) { var assert = chai.assert; describe('views/reset_password', function () { - var view; + var view, router; beforeEach(function () { - view = new View(); + + router = new RouterMock(); + view = new View({ + router: router + }); view.render(); + $('#container').html(view.el); }); afterEach(function () { view.remove(); view.destroy(); + view = router = null; }); describe('constructor creates it', function () { @@ -50,11 +57,11 @@ function (mocha, chai, View, WindowMock) { }); }); - describe('showValidationErrors', function() { + describe('showValidationErrors', function () { it('shows an error if the email is invalid', function (done) { view.$('[type=email]').val('testuser'); - view.on('validation_error', function(which, msg) { + view.on('validation_error', function (which, msg) { assert.ok(msg); done(); }); @@ -62,6 +69,36 @@ function (mocha, chai, View, WindowMock) { view.showValidationErrors(); }); }); + + describe('submit with valid input', function () { + it('submits the email address', function (done) { + var email = 'testuser.' + Math.random() + '@testuser.com'; + var client = new FxaClient(); + client.signUp(email, 'password') + .then(function () { + view.$('input[type=email]').val(email); + + router.on('navigate', function () { + assert.equal(router.page, 'confirm_reset_password'); + done(); + }); + + view.submit(); + }); + }); + }); + + describe('submit with unknown email address', function () { + it('shows an error message', function (done) { + view.$('input[type=email]').val('unknown@testuser.com'); + + view.on('error', function () { + done(); + }); + + view.submit(); + }); + }); }); }); diff --git a/app/tests/spec/views/reset_password_complete.js b/app/tests/spec/views/reset_password_complete.js index 9bde82484..7a83517ee 100644 --- a/app/tests/spec/views/reset_password_complete.js +++ b/app/tests/spec/views/reset_password_complete.js @@ -6,12 +6,11 @@ define([ - 'mocha', 'chai', 'views/reset_password_complete', 'lib/session' ], -function (mocha, chai, View, Session) { +function (chai, View, Session) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; diff --git a/app/tests/spec/views/settings.js b/app/tests/spec/views/settings.js index f6bafcef4..2133ba0a0 100644 --- a/app/tests/spec/views/settings.js +++ b/app/tests/spec/views/settings.js @@ -6,7 +6,6 @@ define([ - 'mocha', 'chai', 'underscore', 'jquery', @@ -15,7 +14,7 @@ define([ 'lib/session', '../../mocks/router' ], -function (mocha, chai, _, $, View, FxaClient, Session, RouterMock) { +function (chai, _, $, View, FxaClient, Session, RouterMock) { var assert = chai.assert; describe('views/settings', function () { diff --git a/app/tests/spec/views/sign_in.js b/app/tests/spec/views/sign_in.js index 57aeb1eea..bb4375b1b 100644 --- a/app/tests/spec/views/sign_in.js +++ b/app/tests/spec/views/sign_in.js @@ -6,7 +6,6 @@ define([ - 'mocha', 'chai', 'views/sign_in', 'lib/session', @@ -14,7 +13,7 @@ define([ '../../mocks/window', '../../mocks/router' ], -function (mocha, chai, View, Session, FxaClient, WindowMock, RouterMock) { +function (chai, View, Session, FxaClient, WindowMock, RouterMock) { var assert = chai.assert; describe('views/sign_in', function () { diff --git a/app/tests/spec/views/sign_up.js b/app/tests/spec/views/sign_up.js index e342d53d8..949b7770d 100644 --- a/app/tests/spec/views/sign_up.js +++ b/app/tests/spec/views/sign_up.js @@ -6,7 +6,6 @@ define([ - 'mocha', 'chai', 'underscore', 'jquery', @@ -15,7 +14,7 @@ define([ 'lib/fxa-client', '../../mocks/router' ], -function (mocha, chai, _, $, View, Session, FxaClient, RouterMock) { +function (chai, _, $, View, Session, FxaClient, RouterMock) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; diff --git a/app/tests/spec/views/tooltip.js b/app/tests/spec/views/tooltip.js index b921c8cf2..f2b5476e9 100644 --- a/app/tests/spec/views/tooltip.js +++ b/app/tests/spec/views/tooltip.js @@ -6,12 +6,11 @@ define([ - 'mocha', 'chai', 'jquery', 'views/tooltip' ], -function (mocha, chai, $, Tooltip) { +function (chai, $, Tooltip) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; diff --git a/app/tests/spec/views/tos.js b/app/tests/spec/views/tos.js index d8fd4b33e..b75106ce0 100644 --- a/app/tests/spec/views/tos.js +++ b/app/tests/spec/views/tos.js @@ -6,12 +6,11 @@ define([ - 'mocha', 'chai', 'views/tos', 'lib/session' ], -function (mocha, chai, View, Session) { +function (chai, View, Session) { /*global describe, beforeEach, afterEach, it*/ var assert = chai.assert; diff --git a/bower.json b/bower.json index 6828d0005..4c22b4b15 100644 --- a/bower.json +++ b/bower.json @@ -16,7 +16,8 @@ "mocha": "~1.16.2", "chai": "~1.8.1", "p": "https://github.com/rkatic/p.git", - "sinon": "http://sinonjs.org/releases/sinon-1.7.1.js" + "sinon": "http://sinonjs.org/releases/sinon-1.7.1.js", + "blanket": "~1.1.5" }, "devDependencies": {} } diff --git a/grunttasks/blanket_mocha.js b/grunttasks/blanket_mocha.js new file mode 100644 index 000000000..3a12ca8b6 --- /dev/null +++ b/grunttasks/blanket_mocha.js @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// blanket_mocha does code coverage analysis on front end code + +module.exports = function (grunt) { + 'use strict'; + + grunt.config('blanket_mocha', { + dist: { + options: { + urls: ['http://localhost:3030/tests/index.html?coverage'], + threshold: 50, + globalThreshold: 65, + log: true, + logErrors: true + } + } + }); +}; + diff --git a/grunttasks/coverage.js b/grunttasks/coverage.js new file mode 100644 index 000000000..a59d39ba6 --- /dev/null +++ b/grunttasks/coverage.js @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Code coverage analysis test! + +module.exports = function (grunt) { + 'use strict'; + + grunt.registerTask('coverage', [ + 'blanket_mocha' + ]); +}; diff --git a/lockdown.json b/lockdown.json index b474a566d..074bb9233 100644 --- a/lockdown.json +++ b/lockdown.json @@ -53,9 +53,6 @@ "asn1": { "0.1.11": "559be18376d08a4ec4dbe80877d27818639b2df7" }, - "ass": { - "0.0.4": "c7a7fa483ddcf5c332c94e495b7b5f7469162bb3" - }, "assert-plus": { "0.1.2": "d93ffdbb67ac5507779be316a7d65146417beef8" }, @@ -75,33 +72,20 @@ "awsbox": { "0.6.2": "4d35b870f8b3dfc80611df0c7d5431aed595204f" }, - "awsboxen": { - "0.5.2": "d2eb558f8996b159b57c911f0b8ced1b89a856d0" - }, "awssum": { - "1.1.0": "e010144aa516af78eca01352aab9203b816611bb", - "1.2.0": "6bf4336594185d76d06d9f515b13b7c874d56a04" + "1.1.0": "e010144aa516af78eca01352aab9203b816611bb" }, "awssum-amazon": { - "1.1.0": "ea9ed3f7116044563bb0b14d47b2f38a684bac47", - "1.3.0": "230788d0f9f91d987169d72502298cb4cc0f57a4" - }, - "awssum-amazon-cloudformation": { - "1.2.1": "e7672690a9e20bc71ec27d304eb7b3679d7daa59" + "1.1.0": "ea9ed3f7116044563bb0b14d47b2f38a684bac47" }, "awssum-amazon-ec2": { - "1.3.2": "fb29a8dffaf6647440b93c64a1868317b4cc856c", "1.4.0": "87c06ed89452436b4aa812cdbb42714800931d83" }, - "awssum-amazon-elb": { - "1.0.0": "6962e553635da77a6785c8529b48dbd0105cfb28" - }, "awssum-amazon-route53": { "1.0.3": "85d1e6aeb22de14ed03e110cc887e482d0362aac", "1.1.0": "069fd7718779613e8fcd06ca5ec197c3182b41a9" }, "base64-js": { - "0.0.2": "024f0f72afa25b75f9c0ee73cd4f55ec1bed9784", "0.0.6": "7b859f79f0bbbd55867ba67a7fab397e24a20947" }, "bigint": { @@ -116,16 +100,10 @@ "binary": { "0.3.0": "9f60553bc5ce8c3386f3b553cff47462adecaa79" }, - "binary-split": { - "0.1.1": "8327184c9f1c4c8e3c06f72bbdcdb4a8e9c756dc" - }, "bindings": { "1.1.0": "f3cc4deec19fe31f255864eb1e6ffad857266ef0", "1.1.1": "951f7ae010302ffc50b265b124032017ed2bf6f3" }, - "blanket": { - "1.1.6": "ff93783dfe08b4f8baa790cd46948f5f3c6fd152" - }, "block-stream": { "0.0.7": "9088ab5ae1e861f4d81b176b4a8046080703deed" }, @@ -139,9 +117,6 @@ "0.4.2": "7a636e9ded4efcefb19cef4947a3c67dfaee911b", "2.2.0": "f5e9502793e9ba777cfae502f71f6fa04d746912" }, - "bops": { - "0.0.6": "082d1d55fa01e60dbdc2ebc2dba37f659554cf3a" - }, "bower": { "1.2.8": "f63c0804a267d5ffaf2fd3fd488367e73dce202f" }, @@ -167,21 +142,12 @@ "buffer-crc32": { "0.2.1": "be3e5382fc02b6d6324956ac1af98aa98b08534c" }, - "buffer-equal": { - "0.0.0": "4a68196ac33522daa17ec99858b302a636b62cf1" - }, "buffers": { "0.1.1": "b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" }, - "bunker": { - "0.1.2": "c88992464a8e2a6ede86930375f92b58077ef97c" - }, "bunyan": { "0.22.1": "020c383bed625af5c6c8834dd8c4aca0dd0f765c" }, - "burrito": { - "0.2.12": "d0d6e6ac81d5e99789c6fa4accb0b0031ea54f6b" - }, "bytes": { "0.2.0": "aad33ec14e3dc2ca74e8e7d451f9ba053ad4f7a0" }, @@ -206,12 +172,8 @@ "character-parser": { "1.0.2": "55384d6afcf8c6b9dd483e8347646a790e4545e7" }, - "charm": { - "0.1.2": "06c21eed1a1b06aeb67553cdc53e23274bac2296" - }, "cheerio": { - "0.10.8": "ece5ad0c8baa9b9adc87394bbdb1c68bc4552ba0", - "0.12.4": "c199626e9e1eb0d4233a91a4793e7f8aaa69a18b" + "0.10.8": "ece5ad0c8baa9b9adc87394bbdb1c68bc4552ba0" }, "cheerio-select": { "0.0.3": "3f2420114f3ccb0b1b075c245ccfaae5d617a388" @@ -251,7 +213,8 @@ "0.6.1": "fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06", "1.1.1": "50d1651868ae60eccff0a2d9f34595376bc6b041", "1.2.0": "fd5713bfa153c7d6cc599378a5ab4c45c535029e", - "1.3.2": "8a8f30ec670a6fdd64af52f1914b907d79ead5b5" + "1.3.2": "8a8f30ec670a6fdd64af52f1914b907d79ead5b5", + "2.0.0": "d1b86f901f8b64bd941bdeadaf924530393be928" }, "commondir": { "0.0.1": "89f00fdcd51b519c578733fec563e6a6da7f5be2" @@ -355,15 +318,9 @@ "decompress-zip": { "0.0.4": "c562431f76af8970afaf50226e42e1c47a57b086" }, - "deep-equal": { - "0.0.0": "99679d3bbd047156fcd450d3d01eeb9068691e83" - }, "deep-extend": { "0.2.8": "6d2893a805286e46d8243137c32fb991b50f4299" }, - "deep-is": { - "0.1.2": "9ced65ea0bc0b09f42a6d79c1b1903f9d913cc18" - }, "deflate-js": { "0.2.3": "f85abb58ebc5151a306147473d57c3e4f7e4426b" }, @@ -382,18 +339,12 @@ "1.0.7": "24bbb001c4a7d5522169e7cabdb2c2814ed91cf4", "1.0.8": "343276308ec991b7bc82267ed55bc1411f971666" }, - "difflet": { - "0.2.6": "ab23b31f5649b6faa8e3d2acbd334467365ca6fa" - }, "directmail": { "0.1.6": "21add6a35cf70e6050b3aee1a816c88149937748" }, "dkim-signer": { "0.1.0": "c57c29b238029fbb08622afc2729ad596bcb417b" }, - "docopt": { - "0.4.0": "3d2ec9240c0929146b694692f45bcbf135301905" - }, "dojo": { "1.9.1": "c5cd9435d025d93e083ae822250f5f7d6dd685ff" }, @@ -468,9 +419,6 @@ "eyes": { "0.1.8": "62cf120234c683785d902348a800ef3e0cc20bc0" }, - "falafel": { - "0.1.6": "3084cf3d41b59d15c813be6f259557fdc82b0660" - }, "faye-websocket": { "0.4.4": "c14c5b3bf14d7417ffbfd990c0a7495cd9f337bc" }, @@ -572,6 +520,9 @@ "grunt-autoprefixer": { "0.7.1": "e9ac2371c23547a881b4b51706f6c2ecbc4c960f" }, + "grunt-blanket-mocha": { + "0.4.0": "23facb79982c4489c7eb5a5fabb66177f4f9851f" + }, "grunt-bower-install": { "0.5.0": "bd9d73f07dc2aaf5bca71134cc6f48802b4de10a" }, @@ -633,7 +584,8 @@ "0.6.1": "3f56adb7da06e814795ee2415b0ebe5fb8903ebb" }, "grunt-lib-phantomjs": { - "0.3.1": "4215feabb81b8d393005b0e784501f20aee70532" + "0.3.1": "4215feabb81b8d393005b0e784501f20aee70532", + "0.4.0": "b78fa18adf1075617bc915ba5563dd7f3c9731ec" }, "grunt-mocha": { "0.4.0": "7d349309390da6f6f84282299f56effec325dea8" @@ -717,7 +669,6 @@ }, "htmlparser2": { "2.6.0": "b28564ea9d1ba56a104ace6a7b0fdda2f315836f", - "3.1.4": "72cbe7d5d56c01acf61fcf7b933331f4e45b36f0", "3.3.0": "cc70d05a59f6542e43f0e685c982e14c924a9efe" }, "http-browserify": { @@ -830,11 +781,7 @@ "keypress": { "0.1.0": "4a3188d4291b66b4f65edb99f806aa9ae293592a" }, - "lazysmtp": { - "0.0.8": "*" - }, "load-grunt-tasks": { - "0.2.1": "19df82abb7fab7ec70ecc5222ce7a301a843dace", "0.3.0": "d5273e3d8689019b31d9cdd42e168399d3b45605" }, "lockdown": { @@ -845,6 +792,7 @@ "1.0.1": "57945732498d92310e5bd4b1ff4f273a79e6c9fc", "1.2.1": "ed47b16e46f06b2b40309b68e9163c17e93ea304", "1.3.1": "a4663b53686b895ff074e2ba504dfb76a8e2b770", + "2.3.0": "dfbdac99cf87a59a022c474730570d8716c267dd", "2.4.1": "5b7723034dda4d262e5a46fb2c58d7cc22f71420" }, "lodash._basebind": { @@ -913,9 +861,6 @@ "mailcomposer": { "0.2.8": "bcb4d84aea08ea6f4c007f927c0ad214986370cc" }, - "mailparser": { - "0.4.0": "5bbe975af3d98ac01dc0d0fced61ea79a7e99eb8" - }, "map-stream": { "0.1.0": "e56aa94c4c8055a16404a0674b78f215f7c8e194" }, @@ -949,7 +894,8 @@ }, "mocha": { "1.12.1": "5212e3f5914eef8c0888ae344e6a7dd2e5ac294a", - "1.13.0": "8d8fa4e310b94cc6efeb3ed26aeca96dea93307c" + "1.13.0": "8d8fa4e310b94cc6efeb3ed26aeca96dea93307c", + "1.14.0": "713db6dc5000191a9d0358195d0908790ecb6157" }, "moment": { "1.7.2": "e66be344be2e9ec1d12f1e16a8ca49bf63417f4f" @@ -1191,12 +1137,8 @@ }, "rimraf": { "2.0.3": "f50a2965e7144e9afd998982f15df706730f56a9", - "2.1.4": "5a6eb62eeda068f51ede50f29b3e5cd22f3d9bb2", "2.2.6": "c59597569b14d956ad29cacc42bdddf5f0ea4f4c" }, - "runforcover": { - "0.0.2": "344f057d8d45d33aebc6cc82204678f69c4857cc" - }, "sauce-connect-launcher": { "0.1.11": "71ac88bdab7bd8396a3f7d9feb165a4e457c3ecd" }, @@ -1231,9 +1173,6 @@ "simplesmtp": { "0.3.21": "79aa14d72a915bb5853ff53a8a44b0823fbe1a8f" }, - "sjcl": { - "1.0.0": "47f3b7c7d87aa58e14f89f5fbc0b44613a129af7" - }, "slash": { "0.1.1": "e3b93c40a94a547a246367b71b8c31a793521a65" }, @@ -1280,16 +1219,11 @@ "symbol": { "0.1.0": "4483f1cd7348dea59bb6c110c95880032c7e73df" }, - "tap": { - "0.4.8": "eb04f3f74ae0c4201bbf778e32a5cfb18d261ef0" - }, "tar": { "0.1.19": "fe45941799e660ce1ea52d875d37481b4bf13eac" }, "temp": { - "0.4.0": "671ad63d57be0fe9d7294664b3fc400636678a60", - "0.5.1": "77ab19c79aa7b593cbe4fac2441768cad987b8df", - "0.6.0": "6b13df5cddf370f2e3a606ca40f202c419173f07" + "0.4.0": "671ad63d57be0fe9d7294664b3fc400636678a60" }, "tempfile": { "0.1.2": "81d4e51f35856592f3b51d568135791af3b038e0" @@ -1323,9 +1257,6 @@ "0.0.16": "704ea0513c15375389ef5b6dde865730528f2245", "0.0.23": "de874aa5e974a85f0a32cdfdbd74663cb3bd9c74" }, - "to-utf8": { - "0.0.1": "d17aea72ff2fba39b9e43601be7b3ff72e089852" - }, "toobusy": { "0.2.4": "d97a3ee760b2462388e15f8526579b4b64bb193d" }, @@ -1340,16 +1271,13 @@ "2.0.1": "352131dfceb93a7532dc7535a4f142510435a394" }, "traverse": { - "0.3.9": "717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9", - "0.5.2": "e203c58d5f7f0e37db6e74c0acb929bb09b61d85", - "0.6.6": "cbdf560fd7b9af632502fed40f918c157ea97137" + "0.3.9": "717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" }, "tunnel-agent": { "0.3.0": "ad681b68f5321ad2827c4cfb1b7d5df2cfe942ee" }, "uglify-js": { "1.0.6": "f0d3aafd463f26a437b9ebc19f4947ab7e8078aa", - "1.1.1": "ee71a97c4cefd06a1a9b20437f34118982aa035b", "1.2.6": "d354b2d3c1cf10ebc18fa78c11a28bdd9ce1580d", "1.3.3": "ddd3e98aa27f5f47e589cfb3f95cddba26096190", "2.2.5": "a6e02a70d839792b9780488b7b8b184c095c99c7", diff --git a/package.json b/package.json index 7be7c1f76..576cd861f 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "start": "grunt server", "preinstall": "./scripts/lockdown", - "postinstall": "bower install", + "postinstall": "bower install;cp node_modules/grunt-blanket-mocha/support/* app/bower_components/blanket/src", "lockdown": "./node_modules/.bin/lockdown-relock", "test": "intern-runner config=tests/intern", "test-browser": "intern-runner config=tests/intern_browser", @@ -31,6 +31,7 @@ "express": "3.3.4", "grunt": "0.4.2", "grunt-autoprefixer": "0.7.1", + "grunt-blanket-mocha": "0.4.0", "grunt-bower-install": "0.5.0", "grunt-bower-requirejs": "0.7.1", "grunt-bump": "0.0.13", diff --git a/server/lib/routes.js b/server/lib/routes.js index d908a2390..e03f19705 100644 --- a/server/lib/routes.js +++ b/server/lib/routes.js @@ -7,6 +7,7 @@ var url = require('url'); var dns = require('dns'); +var config = require('./configuration'); module.exports = function (fxAccountUrl, templates) { @@ -52,6 +53,17 @@ module.exports = function (fxAccountUrl, templates) { }); }); + // front end mocha tests + if (config.get('env') === 'development') { + app.get('/tests/index.html', function (req, res) { + var checkCoverage = 'coverage' in req.query && + req.query['coverage'] !== 'false'; + return res.render('mocha', { + check_coverage: checkCoverage + }); + }); + } + // an array is used instead of a regexp simply because the regexp // became too long. One route is created for each item. var FRONTEND_ROUTES = [ @@ -75,8 +87,8 @@ module.exports = function (fxAccountUrl, templates) { FRONTEND_ROUTES.forEach(function (route) { app.get(route, function (req, res, next) { - // setting the url to / will use the correct index.html for either dev or - // prod mode. + // setting the url to / will use the correct + // index.html for either dev or prod mode. req.url = '/'; next(); }); diff --git a/server/templates/pages/src/mocha.html b/server/templates/pages/src/mocha.html new file mode 100644 index 000000000..ef13e76e9 --- /dev/null +++ b/server/templates/pages/src/mocha.html @@ -0,0 +1,39 @@ + + + + + + Firefox Accounts Unit Tests + + + +
+
+ + + + + {{#check_coverage}} + + + + + {{/check_coverage}} + + diff --git a/tests/functional/mocha.js b/tests/functional/mocha.js index 43a2f26c0..7ba5c75ee 100644 --- a/tests/functional/mocha.js +++ b/tests/functional/mocha.js @@ -9,7 +9,7 @@ define([ ], function (registerSuite, assert, require) { 'use strict'; - var url = 'http://localhost:3030/tests/index.html'; + var url = 'http://localhost:3030/tests/index.html?coverage'; var bodyText; registerSuite({ @@ -42,6 +42,27 @@ define([ } assert.equal(text, '0'); }) + .end() + + // check for code coverage now. + + + // check for the grand total + .waitForElementByCssSelector('.grand-total .rs') + .elementByCssSelector('.grand-total .rs') + .text() + .then(function (text) { + text = text.replace('%', '').trim(); + var covered = parseFloat(text); + assert.ok(covered > 90, 'not enough code coverage!'); + }) + .end() + + // any individual failures? + .elementsByCssSelector('.bl-error .bl-file a') + .then(function(elements) { + assert.equal(elements.length, 0, 'all modules have sufficient coverage'); + }) .end(); } diff --git a/tests/server/routes.js b/tests/server/routes.js index 0b40c3133..1b732305f 100644 --- a/tests/server/routes.js +++ b/tests/server/routes.js @@ -36,6 +36,8 @@ define([ '/complete_reset_password': 200, '/reset_password_complete': 200, '/force_auth': 200, + '/tests/index.html': 200, + '/tests/index.html?coverage': 200, '/non_existent': 404 };