Merge "Angular Form Password Validation"

This commit is contained in:
Jenkins 2015-03-11 23:33:56 +00:00 committed by Gerrit Code Review
commit 942deb1502
6 changed files with 170 additions and 1 deletions

71
horizon/static/angular/form/form.js vendored Normal file
View File

@ -0,0 +1,71 @@
(function() {
'use strict';
/**
* @ngdoc overview
* @name hz.widget.form
*
* # hz.widget.form
*
* The `hz.widget.form` provides form directives and services.
*
* | Components |
* |----------------------------------------------------------|
* | {@link hz.widget.form.hzPasswordMatch `hzPasswordMatch`} |
*
*/
var app = angular.module('hz.widget.form', []);
/**
* @ngdoc directive
* @name hzPasswordMatch
*
* @description
* A directive to ensure that password matches.
* Changing the password or confirmation password triggers a validation check.
* However, only the confirmation password will show an error if match is false.
* The goal is to check that confirmation password matches the password,
* not whether the password matches the confirmation password.
* The behavior here is NOT bi-directional.
*
* @requires
* ng-model - model for confirmation password
*
* @scope
* hzPasswordMatch - form model to validate against
*
* @example:
* <form name="form">
* <input type='password' id="psw" ng-model="user.psw" name="psw">
* <input type='password' ng-model="user.cnf" hz-password-match="form.psw">
* </form>
*
* Note that id and name are required for the password input.
* This directive uses the form model and id for validation check.
*/
app.directive('hzPasswordMatch', function(){
return {
restrict: 'A',
require: 'ngModel',
scope: { pw: '=hzPasswordMatch' },
link: function(scope, element, attr, ctrl){
// helper function to check that password matches
function passwordCheck(){
scope.$apply(function(){
var match = (ctrl.$modelValue === scope.pw.$modelValue);
ctrl.$setValidity('match', match);
});
}
// this ensures that typing in either input
// will trigger the password match
var pwElement = $('#'+scope.pw.$name);
pwElement.on('keyup change', passwordCheck);
element.on('keyup change', passwordCheck);
} // end of link
}; // end of return
}); // end of directive
})();

View File

@ -0,0 +1,93 @@
/* jshint globalstrict: true */
'use strict';
describe('hz.widget.form module', function(){
it('should have been defined', function(){
expect(angular.module('hz.widget.form')).toBeDefined();
});
});
describe('form directives', function() {
beforeEach(module('hz'));
beforeEach(module('hz.widgets'));
beforeEach(module('hz.widget.form'));
describe('hzPasswordMatch directive', function() {
var $compile, $rootScope;
var element, password, cpassword;
var markup =
'<form name="form">' +
'<input type="password" ng-model="user.password" name="password">' +
'<input type="password" ng-model="user.cpassword" ' +
'hz-password-match="form.password">' +
'</form>';
beforeEach(inject(function($injector){
$compile = $injector.get('$compile');
$rootScope = $injector.get('$rootScope').$new();
// generate dom from markup
element = $compile(markup)($rootScope);
password = element.children('input[name]');
cpassword = element.children('input[hz-password-match]');
// setup up initial data
$rootScope.user = {};
$rootScope.$digest();
}));
it('should be initially empty', function() {
expect(password.val()).toEqual('');
expect(password.val()).toEqual(cpassword.val());
expect(cpassword.hasClass('ng-valid')).toBe(true);
});
it('should not match if user changes only password', function(done) {
$rootScope.user.password = 'password';
$rootScope.$digest();
cpassword.change();
setTimeout(function(){
expect(cpassword.val()).not.toEqual(password.val());
expect(cpassword.hasClass('ng-invalid')).toBe(true);
done();
}, 1000);
});
it('should not match if user changes only confirmation password', function(done) {
$rootScope.user.cpassword = 'password';
$rootScope.$digest();
cpassword.change();
setTimeout(function(){
expect(cpassword.val()).not.toEqual(password.val());
expect(cpassword.hasClass('ng-invalid')).toBe(true);
done();
}, 1000);
});
it('should match if both passwords are the same', function(done) {
$rootScope.user.password = 'password';
$rootScope.user.cpassword = 'password';
$rootScope.$digest();
cpassword.change();
setTimeout(function(){
expect(cpassword.val()).toEqual(password.val());
expect(cpassword.hasClass('ng-valid')).toBe(true);
done();
}, 1000);
});
it('should not match if both passwords are different', function(done) {
$rootScope.user.password = 'password123';
$rootScope.user.cpassword = 'password345';
$rootScope.$digest();
cpassword.change();
setTimeout(function(){
expect(cpassword.val()).not.toEqual(password.val());
expect(cpassword.hasClass('ng-invalid')).toBe(true);
done();
}, 1000);
});
}); // end of hzPasswordMatch directive
}); // end of form directives

View File

@ -2,6 +2,7 @@
'use strict';
angular.module('hz.widgets', [
'hz.widget.form',
'hz.widget.help-panel',
'hz.widget.wizard',
'hz.widget.table',

View File

@ -204,7 +204,8 @@ horizon.addInitFunction(horizon.forms.init = function () {
}
// Bind event handlers to confirm dangerous actions.
$("body").on("click", "form button.btn-danger", function (evt) {
// Stops angular form buttons from triggering this event
$("body").on("click", "form button:not([ng-click]).btn-danger", function (evt) {
horizon.datatables.confirm(this);
evt.preventDefault();
});

View File

@ -34,6 +34,7 @@
<script src="{{ STATIC_URL }}horizon/lib/d3.js"></script>
<script src='{{ STATIC_URL }}angular/widget.module.js'></script>
<script src='{{ STATIC_URL }}angular/form/form.js'></script>
<script src='{{ STATIC_URL }}angular/help-panel/help-panel.js'></script>
<script src='{{ STATIC_URL }}angular/wizard/wizard.js'></script>
<script src='{{ STATIC_URL }}angular/action-list/action-list.js'></script>

View File

@ -28,6 +28,7 @@ class ServicesTests(test.JasmineTests):
'angular/charts/charts.js',
'angular/charts/chart-tooltip.js',
'angular/charts/pie-chart.js',
'angular/form/form.js',
'angular/help-panel/help-panel.js',
'angular/metadata-tree/metadata-tree-service.js',
'angular/modal/modal.js',
@ -44,6 +45,7 @@ class ServicesTests(test.JasmineTests):
'angular/action-list/action-list.spec.js',
'angular/bind-scope/bind-scope.spec.js',
'angular/charts/pie-chart.spec.js',
'angular/form/form.spec.js',
'angular/help-panel/help-panel.spec.js',
'angular/modal/simple-modal.spec.js',
'angular/table/table.spec.js',