angular-auto-validate
Version:
An automatic validation module for AngularJS which gets rid of excess html in favour of dynamic element modification to notify the user of validation errors
513 lines (396 loc) • 21 kB
JavaScript
(function (angular, sinon) {
'use strict';
describe('jcs-autoValidate validationManager', function () {
var sandbox, $rootScope, $compile, $q, validator, validationManager, modelCtrl, defer, elementUtils,
setModelCtrl = function () {
modelCtrl = {
$parsers: [],
$formatters: [],
$name: 'name'
};
},
compileElement = function (html, includesForm) {
html = includesForm ? html : '<form ng-submit="">' + html + '</form>';
var element = angular.element(html);
$compile(element)($rootScope);
$rootScope.$digest();
element = includesForm ? element : angular.element(element[0].children[0]);
return element;
},
getDefaultFormOptions = function () {
var internalFormController = {};
return {
forceValidation: false,
disabled: false,
validateNonVisibleControls: false,
validateOnFormSubmit: false,
internalFormController: internalFormController,
getFormController: function () {
return internalFormController;
}
};
};
beforeEach(module('jcs-autoValidate'));
describe('validationManager', function () {
beforeEach(inject(function ($injector) {
sandbox = sinon.sandbox.create();
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
$q = $injector.get('$q');
defer = $q.defer();
validator = $injector.get('validator');
validationManager = $injector.get('validationManager');
elementUtils = $injector.get('jcs-elementUtils');
sandbox.stub(validator, 'makeValid');
sandbox.stub(validator, 'makeInvalid');
sandbox.stub(validator, 'getErrorMessage').returns(defer.promise);
setModelCtrl();
}));
afterEach(function () {
sandbox.restore();
setModelCtrl();
});
it('should be defined', function () {
expect(validationManager).to.exist;
});
describe('validateElement', function () {
it('should return true if the control is pristine and not make the element valid', function () {
var el = angular.element('<input type="text" ng-model="propOne" required="" ng-minlength="10"/>'),
result;
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
modelCtrl.$pristine = true;
result = validationManager.validateElement(modelCtrl, el, getDefaultFormOptions());
expect(result).to.equal(true);
expect(validator.makeValid.called).to.equal(false);
expect(validator.makeInvalid.called).to.equal(false);
});
it('should make the element valid and return true if the control is not pristine and has no validation requirements', function () {
var el = angular.element('<input type="text" ng-model="propOne" required="" ng-minlength="10"/>'),
result;
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
modelCtrl.$pristine = false;
result = validationManager.validateElement(modelCtrl, el, getDefaultFormOptions());
expect(result).to.equal(true);
expect(validator.makeValid.called).to.equal(true);
expect(validator.makeInvalid.called).to.equal(false);
});
it('should not validate the element if it is not visible on the screen', function () {
var el = angular.element('<input type="text" ng-model="propOne" required="" ng-minlength="10"/>'),
result;
sandbox.stub(elementUtils, 'isElementVisible').returns(false);
modelCtrl.$pristine = false;
result = validationManager.validateElement(modelCtrl, el, getDefaultFormOptions());
expect(result).to.equal(true);
expect(validator.makeValid.called).to.equal(false);
expect(validator.makeInvalid.called).to.equal(false);
});
it('should validate the element if it is not visible on the screen as the form option validateNonVisibleControls is true', function () {
var el = compileElement('<input type="text" ng-model="propOne" required="" ng-minlength="10"/>'),
result,
frmOptions = getDefaultFormOptions();
frmOptions.validateNonVisibleControls = true;
sandbox.stub(elementUtils, 'isElementVisible').returns(false);
modelCtrl.$pristine = false;
modelCtrl.$invalid = true;
modelCtrl.$errors = {
required: true
};
result = validationManager.validateElement(modelCtrl, el, frmOptions);
defer.resolve('error message');
$rootScope.$apply();
expect(result).to.equal(false);
expect(validator.makeValid.called).to.equal(false);
expect(validator.makeInvalid.called).to.equal(true);
});
it('should validate the element regardless of whether there is a parent form', function () {
var el = compileElement('<input type="text" ng-model="propOne" required="" ng-minlength="10"/>', true),
result,
frmOptions = getDefaultFormOptions();
frmOptions.validateNonVisibleControls = true;
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
modelCtrl.$pristine = false;
modelCtrl.$invalid = true;
modelCtrl.$errors = {
required: true
};
result = validationManager.validateElement(modelCtrl, el, frmOptions);
defer.resolve('error message');
$rootScope.$apply();
expect(result).to.equal(false);
expect(validator.makeValid.called).to.equal(false);
expect(validator.makeInvalid.called).to.equal(true);
});
it('should not validate the element if validateOnSubmit is true and the form has not been submitted yet', function () {
var el = angular.element('<input type="text" ng-model="propOne" required="" ng-minlength="10"/>'),
result;
var formOptions = getDefaultFormOptions();
formOptions.internalFormController.$submitted = false;
formOptions.validateOnFormSubmit = true;
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
modelCtrl.$pristine = false;
result = validationManager.validateElement(modelCtrl, el, formOptions);
expect(result).to.equal(true);
expect(validator.makeValid.called).to.equal(false);
expect(validator.makeInvalid.called).to.equal(false);
});
it('should validate the element if validateOnSubmit is true and the form has been submitted', function () {
var el = angular.element('<input type="text" ng-model="propOne"/>'),
result;
var formOptions = getDefaultFormOptions();
formOptions.internalFormController.$submitted = true;
formOptions.validateOnFormSubmit = true;
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
defer.resolve('error message');
modelCtrl.$parsers.push(angular.noop);
modelCtrl.$pristine = false;
modelCtrl.$invalid = true;
modelCtrl.$error = {
required: true
};
result = validationManager.validateElement(modelCtrl, el, formOptions);
$rootScope.$apply();
expect(result).to.equal(false);
expect(validator.makeValid.called).to.equal(false);
expect(validator.makeInvalid.called).to.equal(true);
});
it('should call validator to make element valid when there are $formatters and the form is valid', function () {
var el = angular.element('<input type="text" ng-model="propOne"/>');
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
modelCtrl.$formatters.push(angular.noop);
modelCtrl.$pristine = false;
modelCtrl.$invalid = false;
validationManager.validateElement(modelCtrl, el, getDefaultFormOptions());
expect(validator.makeValid.calledOnce).to.equal(true);
expect(validator.makeValid.calledWith(el)).to.equal(true);
});
it('should call validator to make element invalid when there are $formatters and the form is invalid', function () {
var el = angular.element('<input type="text" ng-model="propOne" />'),
errorMsg = 'msg';
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
modelCtrl.$formatters.push(angular.noop);
modelCtrl.$pristine = false;
modelCtrl.$invalid = true;
modelCtrl.$error = {
required: true
};
defer.resolve(errorMsg);
validationManager.validateElement(modelCtrl, el, getDefaultFormOptions());
$rootScope.$apply();
expect(validator.makeInvalid.calledOnce).to.equal(true);
expect(validator.makeInvalid.calledWith(el, errorMsg)).to.equal(true);
});
it('should call validator to make element invalid when there are $parsers and the form is invalid', function () {
var el = angular.element('<input type="text" ng-model="propOne"/>'),
errorMsg = 'msg';
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
modelCtrl.$parsers.push(angular.noop);
modelCtrl.$pristine = false;
modelCtrl.$invalid = true;
modelCtrl.$error = {
required: true
};
defer.resolve(errorMsg);
validationManager.validateElement(modelCtrl, el, getDefaultFormOptions());
$rootScope.$apply();
expect(validator.makeInvalid.calledOnce).to.equal(true);
expect(validator.makeInvalid.calledWith(el, errorMsg)).to.equal(true);
});
it('should return true if the input has no validation specified', function () {
var el = angular.element('<input type="text" ng-model="propertyOne" />'),
result;
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
result = validationManager.validateElement(modelCtrl, el, getDefaultFormOptions());
expect(result).to.equal(true);
});
it('should call validator to make element valid if the control is marked as a custom form control', function () {
var el = angular.element('<div type="text" ng-model="propOne" register-custom-form-control=""></div>');
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
modelCtrl.$formatters.push(angular.noop);
modelCtrl.$pristine = false;
modelCtrl.$invalid = false;
validationManager.validateElement(modelCtrl, el, getDefaultFormOptions());
expect(validator.makeValid.calledOnce).to.equal(true);
expect(validator.makeValid.calledWith(el)).to.equal(true);
});
it('should call validator to make element invalid it the control is marked as a custom form control', function () {
var el = angular.element('<div type="text" ng-model="propOne" register-custom-form-control=""></div>'),
errorMsg = 'msg';
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
modelCtrl.$formatters.push(angular.noop);
modelCtrl.$pristine = false;
modelCtrl.$invalid = true;
modelCtrl.$error = {
required: true
};
defer.resolve(errorMsg);
validationManager.validateElement(modelCtrl, el, getDefaultFormOptions());
$rootScope.$apply();
expect(validator.makeInvalid.calledOnce).to.equal(true);
expect(validator.makeInvalid.calledWith(el, errorMsg)).to.equal(true);
});
});
describe('validateForm', function () {
it('should return false it the form element is undefined', function () {
var result = validationManager.validateForm();
expect(result).to.equal(false);
});
it('should return true it the form controller is set to not auto validate the form', function () {
var form = compileElement('<form role="form" novalidate="novalidate" ng-submit="submit();" disable-dynamic-validation><input ng-model="name" required /></form>', true),
result = validationManager.validateForm(form);
expect(result).to.equal(true);
});
it('should only validate elements on the parent and not disabled subform', function () {
var frm = compileElement('<form name="frm1" ng-submit=""></form>', true),
subFrm = compileElement('<ng-form name="subFrm1" disable-dynamic-validation></ng-form>', true),
inpt = compileElement('<input type="text" ng-model="name" ng-minlength="2" />', true),
inpt2 = compileElement('<input type="text" ng-model="surname" ng-minlength="2" />', true),
isValid;
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
frm.append(inpt);
subFrm.append(inpt2);
inpt.controller('ngModel').$setValidity('minlength', false);
inpt.controller('ngModel').$pristine = false;
inpt2.controller('ngModel').$pristine = false;
inpt2.controller('ngModel').$setValidity('minlength', false);
frm.append(subFrm);
$rootScope.$apply();
defer.resolve('errorMsg');
isValid = validationManager.validateForm(frm);
$rootScope.$apply();
expect(isValid).to.equal(false);
expect(validator.makeInvalid.calledOnce).to.equal(true);
});
it('should only validate elements on the parent and not disabled subform where the subform is via an attribute', function () {
var frm = compileElement('<form name="frm1" ng-submit=""></form>', true),
subFrm = compileElement('<div ng-form name="subFrm1" disable-dynamic-validation></div>', true),
inpt = compileElement('<input type="text" ng-model="name" ng-minlength="2" />', true),
inpt2 = compileElement('<input type="text" ng-model="surname" ng-minlength="2" />', true),
isValid;
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
frm.append(inpt);
subFrm.append(inpt2);
inpt.controller('ngModel').$setValidity('minlength', false);
inpt.controller('ngModel').$pristine = false;
inpt2.controller('ngModel').$pristine = false;
inpt2.controller('ngModel').$setValidity('minlength', false);
frm.append(subFrm);
$rootScope.$apply();
defer.resolve('errorMsg');
isValid = validationManager.validateForm(frm);
$rootScope.$apply();
expect(isValid).to.equal(false);
expect(validator.makeInvalid.calledOnce).to.equal(true);
});
it('should call validator makeInvalid once when a single form input element is invalid', function () {
var frm = compileElement('<form name="frm1"></form>', true),
inpt = compileElement('<input type="text" ng-model="name" ng-minlength="2" />', true),
isValid;
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
frm.append(inpt);
inpt.controller('ngModel').$setValidity('minlength', false);
inpt.controller('ngModel').$pristine = false;
$rootScope.$apply();
defer.resolve('errorMsg');
isValid = validationManager.validateForm(frm);
$rootScope.$apply();
expect(isValid).to.equal(false);
expect(validator.makeInvalid.calledOnce).to.equal(true);
});
it('should call validator makeInvalid twice when a single form input element is invalid', function () {
var frm = compileElement('<form name="frm1"></form>', true),
inpt = compileElement('<input type="text" ng-model="name" ng-minlength="2" />', true),
inpt2 = compileElement('<input type="text" ng-model="name" ng-maxlength="2" />', true),
isValid;
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
frm.append(inpt);
frm.append(inpt2);
inpt.controller('ngModel').$setValidity('minlength', false);
inpt.controller('ngModel').$pristine = false;
inpt2.controller('ngModel').$setValidity('maxlength', false);
inpt2.controller('ngModel').$pristine = false;
$rootScope.$apply();
defer.resolve('errorMsg');
isValid = validationManager.validateForm(frm);
$rootScope.$apply();
expect(isValid).to.equal(false);
expect(validator.makeInvalid.calledTwice).to.equal(true);
});
it('should call validator makeInvalid once when a single form input element is invalid in a child ng-form', function () {
var frm = compileElement('<form name="frm1"></form>', true),
ngFrm = compileElement('<ng-form name="childFrm"></ng-form>', true),
inpt = compileElement('<input type="text" ng-model="name" ng-minlength="2" />', true),
isValid;
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
ngFrm.append(inpt);
frm.append(ngFrm);
inpt.controller('ngModel').$setValidity('minlength', false);
inpt.controller('ngModel').$pristine = false;
$rootScope.$apply();
defer.resolve('errorMsg');
isValid = validationManager.validateForm(frm);
$rootScope.$apply();
expect(isValid).to.equal(false);
expect(validator.makeInvalid.calledOnce).to.equal(true);
});
it('should call validator makeInvalid once when a single form input element is invalid and the form is submitted', function () {
var frm = compileElement('<form name="frm1" ng-submit=""></form>', true),
inpt = compileElement('<input type="text" ng-model="name" required="required" ng-minlength="2" />', true);
sandbox.stub(elementUtils, 'isElementVisible').returns(true);
frm.append(inpt);
$rootScope.$apply();
frm.on('submit', function (event) {
event.preventDefault();
});
defer.resolve('errorMsg');
frm.trigger('submit');
$rootScope.$apply();
expect(validator.makeInvalid.calledOnce).to.equal(true);
});
});
describe('resetForm', function () {
it('should call validator.makeDefault for every input type element in the form', function () {
var frm = compileElement('<form name="frm1"><input type="text" ng-model="name" ng-minlength="2" /><input type="text" ng-model="lastname" ng-maxlength="2" /></form>', true);
sandbox.stub(validator, 'makeDefault');
$rootScope.$apply();
validationManager.resetForm(frm);
$rootScope.$apply();
expect(validator.makeDefault.calledTwice).to.equal(true);
});
it('should call validator makeDefault once when a single form input element is invalid in a child ng-form', function () {
var frm = compileElement('<form name="frm1"><ng-form name="childFrm"><input type="text" ng-model="name" ng-minlength="2" /></ng-form></form>', true);
sandbox.stub(validator, 'makeDefault');
$rootScope.$apply();
validationManager.resetForm(frm);
$rootScope.$apply();
expect(validator.makeDefault.calledOnce).to.equal(true);
});
it('should set the model controller of the input element to pristine', function () {
var frm = compileElement('<form name="frm1"></form>', true),
ngFrm = compileElement('<ng-form name="childFrm"></ng-form>', true),
inpt = compileElement('<input type="text" ng-model="name" ng-minlength="2" />', true),
inptNgModelController = inpt.controller('ngModel');
sandbox.stub(validator, 'makeDefault');
ngFrm.append(inpt);
frm.append(ngFrm);
$rootScope.$apply();
inptNgModelController.$setViewValue('123');
$rootScope.$apply();
expect(inptNgModelController.$pristine).to.equal(false);
validationManager.resetForm(frm);
$rootScope.$apply();
expect(inptNgModelController.$pristine).to.equal(true);
});
});
describe('resetElement', function () {
it('should call validator.makeDefault with the given element', function () {
var inpt = compileElement('<input type="text" ng-model="name" ng-minlength="2" />');
sandbox.stub(validator, 'makeDefault');
$rootScope.$apply();
validationManager.resetElement(inpt);
$rootScope.$apply();
expect(validator.makeDefault.calledOnce).to.equal(true);
});
});
});
});
}(angular, sinon));