From 700324be09277882a8ef025a882f14e4ca26e5b1 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Wed, 27 Feb 2013 18:39:06 +0100 Subject: [PATCH] Add unit tests --- .../web/login/layout/base-layout.html.twig | 1 + www/skins/login/js/angular/app/app.js | 144 ++++++++++++++++++ .../js/angular/config/testacular.conf.js | 16 ++ www/skins/login/js/angular/scripts/test.bat | 11 ++ www/skins/login/js/angular/scripts/test.sh | 9 ++ www/skins/login/js/angular/scripts/watchr.rb | 19 +++ .../js/angular/tests/unit/controllersSpec.js | 107 +++++++++++++ www/skins/login/js/main.js | 143 ----------------- 8 files changed, 307 insertions(+), 143 deletions(-) create mode 100644 www/skins/login/js/angular/app/app.js create mode 100644 www/skins/login/js/angular/config/testacular.conf.js create mode 100644 www/skins/login/js/angular/scripts/test.bat create mode 100644 www/skins/login/js/angular/scripts/test.sh create mode 100644 www/skins/login/js/angular/scripts/watchr.rb create mode 100644 www/skins/login/js/angular/tests/unit/controllersSpec.js diff --git a/templates/web/login/layout/base-layout.html.twig b/templates/web/login/layout/base-layout.html.twig index 41e45d3e14..fc72e143dd 100644 --- a/templates/web/login/layout/base-layout.html.twig +++ b/templates/web/login/layout/base-layout.html.twig @@ -71,6 +71,7 @@ + {% endblock header_javascript %} {% endblock header %} diff --git a/www/skins/login/js/angular/app/app.js b/www/skins/login/js/angular/app/app.js new file mode 100644 index 0000000000..f6b4824241 --- /dev/null +++ b/www/skins/login/js/angular/app/app.js @@ -0,0 +1,144 @@ +// controllers +function LoginFormController($scope) { + $scope.$watch('loginForm', function() { + $scope.loginForm.email.errors = {'filled' : true, 'valid' : true}; + $scope.loginForm.password.errors = {'filled' : true, 'valid' : true}; + }); + + $scope.submit = function() { + $scope.$broadcast('event:force-model-update'); + + if (true === $scope.loginForm.$valid) { + $scope.loginForm.email.errors = {'filled' : true, 'valid' : true}; + $scope.loginForm.password.errors = {'filled' : true,'valid' : true}; + // submit + return true; + } + + $scope.loginForm.email.errors.valid = $scope.loginForm.email.$valid; + $scope.loginForm.email.errors.filled = !$scope.loginForm.email.$error.required; + + $scope.loginForm.password.errors.filled = !$scope.loginForm.password.$error.required; + + return false; + }; + + $scope.getInputClass = function(name) { + return _.every($scope.loginForm[name].errors, function(value) { + return value === true; + }) ? '' : 'input-table-error'; + }; +} + +function forgottenPasswordFormCtrl($scope) { + $scope.$watch('forgottenPasswordForm', function() { + $scope.forgottenPasswordForm.email.errors = {'filled' : true, 'valid' : true}; + }); + + $scope.submit = function() { + $scope.$broadcast('event:force-model-update'); + + if (true === $scope.forgottenPasswordForm.$valid) { + $scope.forgottenPasswordForm.email.errors = {'filled' : true, 'valid' : true}; + // submit + return true; + } + + $scope.forgottenPasswordForm.email.errors.valid = $scope.forgottenPasswordForm.email.$valid; + $scope.forgottenPasswordForm.email.errors.filled = !$scope.forgottenPasswordForm.email.$error.required; + + return false; + }; + + $scope.getInputClass = function(name) { + return _.every($scope.forgottenPasswordForm[name].errors, function(value) { + return value === true; + }) ? '' : 'input-table-error'; + }; +} + +function passwordChangeFormCtrl($scope) { + $scope.$watch('passwordChangeForm', function() { + $scope.passwordChangeForm.password.errors = {'filled' : true, 'valid' : true}; + $scope.passwordChangeForm.passwordConfirm.errors = {'filled' : true, 'valid' : true}; + }); + + $scope.submit = function() { + $scope.$broadcast('event:force-model-update'); + + if (true === $scope.passwordChangeForm.$valid) { + $scope.passwordChangeForm.password.errors = {'filled' : true, 'valid' : true}; + $scope.passwordChangeForm.passwordConfirm.errors = {'filled' : true, 'valid' : true}; + // submit + return true; + } + + $scope.passwordChangeForm.password.errors.filled = !$scope.passwordChangeForm.password.$error.required; + $scope.passwordChangeForm.passwordConfirm.errors.filled = !$scope.passwordChangeForm.passwordConfirm.$error.required; + + return false; + }; + + $scope.getInputClass = function(name) { + return _.every($scope.passwordChangeForm[name].errors, function(value) { + return value === true; + }) ? '' : 'input-table-error'; + }; +} + +// bootstrap angular +angular.element(document).ready(function() { + angular.bootstrap(document, ['phraseanetAuthentication']); +}); + + +// angular app +angular.module('phraseanetAuthentication', ['ui']) +// force model update for autofill inputs. Yuck. +.directive('forceModelUpdate', function($compile) { + return { + restrict: 'A', + require: 'ngModel', + link: function(scope, element, attrs, ctrl) { + scope.$on('event:force-model-update', function() { + ctrl.$setViewValue(element.val()); + }); + } + } +}).directive('alert', function () { + return { + restrict:'EA', + template: [ + '
', + '', + '', + '', + '', + '
×
', + '
' + ].join(''), + transclude:true, + replace:true, + scope:{ + type: '@' + }, + compile: function (element, attrs, transclude) { + return function (scope, element, attr) { + if (true === 'type' in attrs) { + switch (attrs.type) { + case 'error' : + scope.icon = 'icon-warning-sign'; + break; + case 'success' : + scope.icon = 'icon-ok-sign'; + break; + case 'info' : + scope.icon = 'icon-info-sign'; + break; + } + } + } + } + }; +}); + diff --git a/www/skins/login/js/angular/config/testacular.conf.js b/www/skins/login/js/angular/config/testacular.conf.js new file mode 100644 index 0000000000..fd8f6c0362 --- /dev/null +++ b/www/skins/login/js/angular/config/testacular.conf.js @@ -0,0 +1,16 @@ +basePath = '../../../../../'; + +files = [ + JASMINE, + JASMINE_ADAPTER, + 'assets/angular/angular.js', + 'assets/angular-ui/build/angular-ui.js', + 'assets/underscore/underscore.js', + 'assets/angular-mocks/angular-mocks.js', + 'skins/login/js/angular/app/*.js', + 'skins/login/js/angular/tests/unit/*.js' +]; + +autoWatch = true; + +browsers = ['firefox']; diff --git a/www/skins/login/js/angular/scripts/test.bat b/www/skins/login/js/angular/scripts/test.bat new file mode 100644 index 0000000000..000242f53e --- /dev/null +++ b/www/skins/login/js/angular/scripts/test.bat @@ -0,0 +1,11 @@ +@echo off + +REM Windows script for running unit tests +REM You have to run server and capture some browser first +REM +REM Requirements: +REM - NodeJS (http://nodejs.org/) +REM - Testacular (npm install -g testacular) + +set BASE_DIR=%~dp0 +testacular start "%BASE_DIR%\..\config\testacular.conf.js" %* diff --git a/www/skins/login/js/angular/scripts/test.sh b/www/skins/login/js/angular/scripts/test.sh new file mode 100644 index 0000000000..4c37cde250 --- /dev/null +++ b/www/skins/login/js/angular/scripts/test.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +BASE_DIR=`dirname $0` + +echo "" +echo "Starting Testacular Server (http://vojtajina.github.com/testacular)" +echo "-------------------------------------------------------------------" + +testacular start $BASE_DIR/../config/testacular.conf.js $* diff --git a/www/skins/login/js/angular/scripts/watchr.rb b/www/skins/login/js/angular/scripts/watchr.rb new file mode 100644 index 0000000000..89ef656d27 --- /dev/null +++ b/www/skins/login/js/angular/scripts/watchr.rb @@ -0,0 +1,19 @@ +#!/usr/bin/env watchr + +# config file for watchr http://github.com/mynyml/watchr +# install: gem install watchr +# run: watch watchr.rb +# note: make sure that you have jstd server running (server.sh) and a browser captured + +log_file = File.expand_path(File.dirname(__FILE__) + '/../logs/jstd.log') + +`cd ..` +`touch #{log_file}` + +puts "String watchr... log file: #{log_file}" + +watch( '(app/js|test/unit)' ) do + `echo "\n\ntest run started @ \`date\`" > #{log_file}` + `scripts/test.sh &> #{log_file}` +end + diff --git a/www/skins/login/js/angular/tests/unit/controllersSpec.js b/www/skins/login/js/angular/tests/unit/controllersSpec.js new file mode 100644 index 0000000000..e255d86eb1 --- /dev/null +++ b/www/skins/login/js/angular/tests/unit/controllersSpec.js @@ -0,0 +1,107 @@ +'use strict'; + +describe('LoginFormController', function(){ + var scope; + beforeEach(inject(function($rootScope) { + scope = $rootScope.$new(); + + scope.loginForm = { + email : { + errors:{}, + $valid : true, + $error : { + required : false + } + }, + password : { + errors:{}, + $valid : true, + $error : { + required : false + } + } + }; + })); + + it('should create model errors with 2 validation for each input', inject(function($controller) { + $controller(LoginFormController, { + $scope: scope + }); + + scope.$digest(); + + expect(Object.keys(scope.loginForm.email.errors).length).toEqual(2); + expect(Object.keys(scope.loginForm.password.errors).length).toEqual(2); + })); + + it('should return input-table-error class when input is not valid', inject(function($controller) { + scope.loginForm.email.errors.valid = false; + scope.loginForm.password.errors.valid = true; + + $controller(LoginFormController, { + $scope: scope + }); + + expect(scope.getInputClass('password')).toBe(''); + expect(scope.getInputClass('email')).toEqual('input-table-error'); + })); + + it('The valid email input validation should be equal to input form validation if form is submited and not valid', inject(function($controller) { + scope.loginForm.$valid = false; + + $controller(LoginFormController, { + $scope: scope + }); + + scope.$digest(); + + expect(scope.loginForm.email.errors.valid).toBe(scope.loginForm.email.$valid); + })); + + it('The filled password and email input validation should be true if input is not required and form is submited and not valid', inject(function($controller) { + scope.loginForm.$valid = false; + + $controller(LoginFormController, { + $scope: scope + }); + + scope.$digest(); + + expect(scope.loginForm.email.errors.filled).not.toBe(scope.loginForm.email.$error.required); + expect(scope.loginForm.password.errors.filled).not.toBe(scope.loginForm.password.$error.required); + })); + + it('Should reset input validation errors when form is submited and valid', inject(function($controller) { + $controller(LoginFormController, { + $scope: scope + }); + + scope.$digest(); + + var startEmailState = _.clone(scope.loginForm.email.errors); + var startPasswordState = _.clone(scope.loginForm.password.errors); + + scope.loginForm.$valid = true; + + scope.loginForm.email.errors.valid = false; + scope.loginForm.password.errors.valid = false; + + scope.submit(); + + expect(scope.loginForm.email.errors).toEqual(startEmailState); + expect(scope.loginForm.password.errors).toEqual(startPasswordState); + })); + + it('should not submit form if it is not valid', inject(function($controller) { + scope.loginForm.$valid = false; + + $controller(LoginFormController, { + $scope: scope + }); + + scope.$digest(); + + expect(scope.submit()).toBe(false); + })); +}); + diff --git a/www/skins/login/js/main.js b/www/skins/login/js/main.js index 63ef38d292..abbd1196e0 100644 --- a/www/skins/login/js/main.js +++ b/www/skins/login/js/main.js @@ -6,146 +6,3 @@ $(document).ready(function() { return false; }); }); - -// angular app -angular.module('phraseanetAuthentication', ['ui']) -// force model update for autofill inputs. Yuck. -.directive('forceModelUpdate', function($compile) { - return { - restrict: 'A', - require: 'ngModel', - link: function(scope, element, attrs, ctrl) { - scope.$on('event:force-model-update', function() { - ctrl.$setViewValue(element.val()); - }); - } - } -}).directive('alert', function () { - return { - restrict:'EA', - template: [ - '
', - '', - '', - '', - '', - '
×
', - '
' - ].join(''), - transclude:true, - replace:true, - scope:{ - type: '@' - }, - compile: function (element, attrs, transclude) { - return function (scope, element, attr) { - if (true === 'type' in attrs) { - switch (attrs.type) { - case 'error' : - scope.icon = 'icon-warning-sign'; - break; - case 'success' : - scope.icon = 'icon-ok-sign'; - break; - case 'info' : - scope.icon = 'icon-info-sign'; - break; - } - } - } - } - }; -}); - -// controllers -function LoginFormController($scope) { - $scope.$watch('loginForm', function() { - $scope.loginForm.email.errors = {'filled' : true, 'valid' : true}; - $scope.loginForm.password.errors = {'filled' : true, 'valid' : true}; - }); - - $scope.submit = function() { - $scope.$broadcast('event:force-model-update'); - - if (true === $scope.loginForm.$valid) { - $scope.loginForm.email.errors = {'filled' : true, 'valid' : true}; - $scope.loginForm.password.errors = {'filled' : true,'valid' : true}; - // submit - return; - } - - $scope.loginForm.email.errors.valid = $scope.loginForm.email.$valid; - $scope.loginForm.email.errors.filled = !$scope.loginForm.email.$error.required; - - $scope.loginForm.password.errors.filled = !$scope.loginForm.password.$error.required; - - return; - }; - - $scope.getInputClass = function(name) { - return _.every($scope.loginForm[name].errors, function(value) { - return value === true; - }) ? '' : 'input-table-error'; - }; -} - -function forgottenPasswordFormCtrl($scope) { - $scope.$watch('forgottenPasswordForm', function() { - $scope.forgottenPasswordForm.email.errors = {'filled' : true, 'valid' : true}; - }); - - $scope.submit = function() { - $scope.$broadcast('event:force-model-update'); - - if (true === $scope.forgottenPasswordForm.$valid) { - $scope.forgottenPasswordForm.email.errors = {'filled' : true, 'valid' : true}; - // submit - return; - } - - $scope.forgottenPasswordForm.email.errors.valid = $scope.forgottenPasswordForm.email.$valid; - $scope.forgottenPasswordForm.email.errors.filled = !$scope.forgottenPasswordForm.email.$error.required; - - return; - }; - - $scope.getInputClass = function(name) { - return _.every($scope.forgottenPasswordForm[name].errors, function(value) { - return value === true; - }) ? '' : 'input-table-error'; - }; -} - -function passwordChangeFormCtrl($scope) { - $scope.$watch('passwordChangeForm', function() { - $scope.passwordChangeForm.password.errors = {'filled' : true, 'valid' : true}; - $scope.passwordChangeForm.passwordConfirm.errors = {'filled' : true, 'valid' : true}; - }); - - $scope.submit = function() { - $scope.$broadcast('event:force-model-update'); - - if (true === $scope.passwordChangeForm.$valid) { - $scope.passwordChangeForm.password.errors = {'filled' : true, 'valid' : true}; - $scope.passwordChangeForm.passwordConfirm.errors = {'filled' : true, 'valid' : true}; - // submit - return; - } - - $scope.passwordChangeForm.password.errors.filled = !$scope.passwordChangeForm.password.$error.required; - $scope.passwordChangeForm.passwordConfirm.errors.filled = !$scope.passwordChangeForm.passwordConfirm.$error.required; - - return; - }; - - $scope.getInputClass = function(name) { - return _.every($scope.passwordChangeForm[name].errors, function(value) { - return value === true; - }) ? '' : 'input-table-error'; - }; -} - -// bootstrap angular -angular.element(document).ready(function() { - angular.bootstrap(document, ['phraseanetAuthentication']); -});