mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-06-16 02:14:27 +08:00
This is complement to the previous commit. It also refactors the input compile helpers to make it cleaner and more consistent.
508 lines
16 KiB
JavaScript
508 lines
16 KiB
JavaScript
'use strict';
|
|
|
|
/* globals getInputCompileHelper: false */
|
|
|
|
describe('validators', function() {
|
|
|
|
var helper, $rootScope;
|
|
|
|
beforeEach(function() {
|
|
helper = getInputCompileHelper(this);
|
|
});
|
|
|
|
afterEach(function() {
|
|
helper.dealoc();
|
|
});
|
|
|
|
|
|
beforeEach(inject(function(_$rootScope_) {
|
|
$rootScope = _$rootScope_;
|
|
}));
|
|
|
|
|
|
describe('pattern', function() {
|
|
|
|
it('should validate in-lined pattern', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-pattern="/^\\d\\d\\d-\\d\\d-\\d\\d\\d\\d$/" />');
|
|
|
|
helper.changeInputValueTo('x000-00-0000x');
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
helper.changeInputValueTo('000-00-0000');
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('000-00-0000x');
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
helper.changeInputValueTo('123-45-6789');
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('x');
|
|
expect(inputElm).toBeInvalid();
|
|
});
|
|
|
|
|
|
it('should listen on ng-pattern when pattern is observed', function() {
|
|
var value, patternVal = /^\w+$/;
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-pattern="pat" attr-capture />');
|
|
helper.attrs.$observe('pattern', function(v) {
|
|
value = helper.attrs.pattern;
|
|
});
|
|
|
|
$rootScope.$apply(function() {
|
|
$rootScope.pat = patternVal;
|
|
});
|
|
|
|
expect(value).toBe(patternVal);
|
|
});
|
|
|
|
|
|
it('should validate in-lined pattern with modifiers', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-pattern="/^abc?$/i" />');
|
|
|
|
helper.changeInputValueTo('aB');
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('xx');
|
|
expect(inputElm).toBeInvalid();
|
|
});
|
|
|
|
|
|
it('should validate pattern from scope', function() {
|
|
$rootScope.regexp = /^\d\d\d-\d\d-\d\d\d\d$/;
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-pattern="regexp" />');
|
|
|
|
helper.changeInputValueTo('x000-00-0000x');
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
helper.changeInputValueTo('000-00-0000');
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('000-00-0000x');
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
helper.changeInputValueTo('123-45-6789');
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('x');
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
$rootScope.$apply(function() {
|
|
$rootScope.regexp = /abc?/;
|
|
});
|
|
|
|
helper.changeInputValueTo('ab');
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('xx');
|
|
expect(inputElm).toBeInvalid();
|
|
});
|
|
|
|
|
|
it('should perform validations when the ngPattern scope value changes', function() {
|
|
$rootScope.regexp = /^[a-z]+$/;
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-pattern="regexp" />');
|
|
|
|
helper.changeInputValueTo('abcdef');
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('123');
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
$rootScope.$apply(function() {
|
|
$rootScope.regexp = /^\d+$/;
|
|
});
|
|
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('abcdef');
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
$rootScope.$apply(function() {
|
|
$rootScope.regexp = '';
|
|
});
|
|
|
|
expect(inputElm).toBeValid();
|
|
});
|
|
|
|
|
|
it('should register "pattern" with the model validations when the pattern attribute is used', function() {
|
|
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" pattern="^\\d+$" />');
|
|
|
|
helper.changeInputValueTo('abcd');
|
|
expect(inputElm).toBeInvalid();
|
|
expect($rootScope.form.input.$error.pattern).toBe(true);
|
|
|
|
helper.changeInputValueTo('12345');
|
|
expect(inputElm).toBeValid();
|
|
expect($rootScope.form.input.$error.pattern).not.toBe(true);
|
|
});
|
|
|
|
|
|
it('should not throw an error when scope pattern can\'t be found', function() {
|
|
expect(function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="foo" ng-pattern="fooRegexp" />');
|
|
$rootScope.$apply("foo = 'bar'");
|
|
}).not.toThrowMatching(/^\[ngPattern:noregexp\] Expected fooRegexp to be a RegExp but was/);
|
|
});
|
|
|
|
|
|
it('should throw an error when the scope pattern is not a regular expression', function() {
|
|
expect(function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="foo" ng-pattern="fooRegexp" />');
|
|
$rootScope.$apply(function() {
|
|
$rootScope.fooRegexp = {};
|
|
$rootScope.foo = 'bar';
|
|
});
|
|
}).toThrowMatching(/^\[ngPattern:noregexp\] Expected fooRegexp to be a RegExp but was/);
|
|
});
|
|
|
|
|
|
it('should be invalid if entire string does not match pattern', function() {
|
|
var inputElm = helper.compileInput('<input type="text" name="test" ng-model="value" pattern="\\d{4}">');
|
|
helper.changeInputValueTo('1234');
|
|
expect($rootScope.form.test.$error.pattern).not.toBe(true);
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('123');
|
|
expect($rootScope.form.test.$error.pattern).toBe(true);
|
|
expect(inputElm).not.toBeValid();
|
|
|
|
helper.changeInputValueTo('12345');
|
|
expect($rootScope.form.test.$error.pattern).toBe(true);
|
|
expect(inputElm).not.toBeValid();
|
|
});
|
|
|
|
|
|
it('should be cope with patterns that start with ^', function() {
|
|
var inputElm = helper.compileInput('<input type="text" name="test" ng-model="value" pattern="^\\d{4}">');
|
|
helper.changeInputValueTo('1234');
|
|
expect($rootScope.form.test.$error.pattern).not.toBe(true);
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('123');
|
|
expect($rootScope.form.test.$error.pattern).toBe(true);
|
|
expect(inputElm).not.toBeValid();
|
|
|
|
helper.changeInputValueTo('12345');
|
|
expect($rootScope.form.test.$error.pattern).toBe(true);
|
|
expect(inputElm).not.toBeValid();
|
|
});
|
|
|
|
|
|
it('should be cope with patterns that end with $', function() {
|
|
var inputElm = helper.compileInput('<input type="text" name="test" ng-model="value" pattern="\\d{4}$">');
|
|
helper.changeInputValueTo('1234');
|
|
expect($rootScope.form.test.$error.pattern).not.toBe(true);
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('123');
|
|
expect($rootScope.form.test.$error.pattern).toBe(true);
|
|
expect(inputElm).not.toBeValid();
|
|
|
|
helper.changeInputValueTo('12345');
|
|
expect($rootScope.form.test.$error.pattern).toBe(true);
|
|
expect(inputElm).not.toBeValid();
|
|
});
|
|
});
|
|
|
|
|
|
describe('minlength', function() {
|
|
|
|
it('should invalidate values that are shorter than the given minlength', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-minlength="3" />');
|
|
|
|
helper.changeInputValueTo('aa');
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
helper.changeInputValueTo('aaa');
|
|
expect(inputElm).toBeValid();
|
|
});
|
|
|
|
|
|
it('should listen on ng-minlength when minlength is observed', function() {
|
|
var value = 0;
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-minlength="min" attr-capture />');
|
|
helper.attrs.$observe('minlength', function(v) {
|
|
value = int(helper.attrs.minlength);
|
|
});
|
|
|
|
$rootScope.$apply('min = 5');
|
|
|
|
expect(value).toBe(5);
|
|
});
|
|
|
|
|
|
it('should observe the standard minlength attribute and register it as a validator on the model', function() {
|
|
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" minlength="{{ min }}" />');
|
|
$rootScope.$apply('min = 10');
|
|
|
|
helper.changeInputValueTo('12345');
|
|
expect(inputElm).toBeInvalid();
|
|
expect($rootScope.form.input.$error.minlength).toBe(true);
|
|
|
|
$rootScope.$apply('min = 5');
|
|
|
|
expect(inputElm).toBeValid();
|
|
expect($rootScope.form.input.$error.minlength).not.toBe(true);
|
|
});
|
|
|
|
|
|
it('should validate when the model is initalized as a number', function() {
|
|
$rootScope.value = 12345;
|
|
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" minlength="3" />');
|
|
expect($rootScope.value).toBe(12345);
|
|
expect($rootScope.form.input.$error.minlength).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
|
|
describe('maxlength', function() {
|
|
|
|
it('should invalidate values that are longer than the given maxlength', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-maxlength="5" />');
|
|
|
|
helper.changeInputValueTo('aaaaaaaa');
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
helper.changeInputValueTo('aaa');
|
|
expect(inputElm).toBeValid();
|
|
});
|
|
|
|
|
|
it('should only accept empty values when maxlength is 0', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-maxlength="0" />');
|
|
|
|
helper.changeInputValueTo('');
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('a');
|
|
expect(inputElm).toBeInvalid();
|
|
});
|
|
|
|
|
|
it('should accept values of any length when maxlength is negative', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-maxlength="-1" />');
|
|
|
|
helper.changeInputValueTo('');
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('aaaaaaaaaa');
|
|
expect(inputElm).toBeValid();
|
|
});
|
|
|
|
|
|
it('should accept values of any length when maxlength is non-numeric', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-maxlength="{{maxlength}}" />');
|
|
helper.changeInputValueTo('aaaaaaaaaa');
|
|
|
|
$rootScope.$apply('maxlength = "5"');
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
$rootScope.$apply('maxlength = "abc"');
|
|
expect(inputElm).toBeValid();
|
|
|
|
$rootScope.$apply('maxlength = ""');
|
|
expect(inputElm).toBeValid();
|
|
|
|
$rootScope.$apply('maxlength = null');
|
|
expect(inputElm).toBeValid();
|
|
|
|
$rootScope.someObj = {};
|
|
$rootScope.$apply('maxlength = someObj');
|
|
expect(inputElm).toBeValid();
|
|
});
|
|
|
|
|
|
it('should listen on ng-maxlength when maxlength is observed', function() {
|
|
var value = 0;
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-maxlength="max" attr-capture />');
|
|
helper.attrs.$observe('maxlength', function(v) {
|
|
value = int(helper.attrs.maxlength);
|
|
});
|
|
|
|
$rootScope.$apply('max = 10');
|
|
|
|
expect(value).toBe(10);
|
|
});
|
|
|
|
|
|
it('should observe the standard maxlength attribute and register it as a validator on the model', function() {
|
|
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" maxlength="{{ max }}" />');
|
|
$rootScope.$apply('max = 1');
|
|
|
|
helper.changeInputValueTo('12345');
|
|
expect(inputElm).toBeInvalid();
|
|
expect($rootScope.form.input.$error.maxlength).toBe(true);
|
|
|
|
$rootScope.$apply('max = 6');
|
|
|
|
expect(inputElm).toBeValid();
|
|
expect($rootScope.form.input.$error.maxlength).not.toBe(true);
|
|
});
|
|
|
|
|
|
it('should assign the correct model after an observed validator became valid', function() {
|
|
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" maxlength="{{ max }}" />');
|
|
|
|
$rootScope.$apply('max = 1');
|
|
helper.changeInputValueTo('12345');
|
|
expect($rootScope.value).toBeUndefined();
|
|
|
|
$rootScope.$apply('max = 6');
|
|
expect($rootScope.value).toBe('12345');
|
|
});
|
|
|
|
|
|
it('should assign the correct model after an observed validator became invalid', function() {
|
|
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" maxlength="{{ max }}" />');
|
|
|
|
$rootScope.$apply('max = 6');
|
|
helper.changeInputValueTo('12345');
|
|
expect($rootScope.value).toBe('12345');
|
|
|
|
$rootScope.$apply('max = 1');
|
|
expect($rootScope.value).toBeUndefined();
|
|
});
|
|
|
|
|
|
it('should leave the value as invalid if observed maxlength changed, but is still invalid', function() {
|
|
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" maxlength="{{ max }}" />');
|
|
$rootScope.$apply('max = 1');
|
|
|
|
helper.changeInputValueTo('12345');
|
|
expect(inputElm).toBeInvalid();
|
|
expect($rootScope.form.input.$error.maxlength).toBe(true);
|
|
expect($rootScope.value).toBeUndefined();
|
|
|
|
$rootScope.$apply('max = 3');
|
|
|
|
expect(inputElm).toBeInvalid();
|
|
expect($rootScope.form.input.$error.maxlength).toBe(true);
|
|
expect($rootScope.value).toBeUndefined();
|
|
});
|
|
|
|
|
|
it('should not notify if observed maxlength changed, but is still invalid', function() {
|
|
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" ng-change="ngChangeSpy()" ' +
|
|
'maxlength="{{ max }}" />');
|
|
|
|
$rootScope.$apply('max = 1');
|
|
helper.changeInputValueTo('12345');
|
|
|
|
$rootScope.ngChangeSpy = jasmine.createSpy();
|
|
$rootScope.$apply('max = 3');
|
|
|
|
expect($rootScope.ngChangeSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
|
|
it('should leave the model untouched when validating before model initialization', function() {
|
|
$rootScope.value = '12345';
|
|
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" minlength="3" />');
|
|
expect($rootScope.value).toBe('12345');
|
|
});
|
|
|
|
|
|
it('should validate when the model is initalized as a number', function() {
|
|
$rootScope.value = 12345;
|
|
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" maxlength="10" />');
|
|
expect($rootScope.value).toBe(12345);
|
|
expect($rootScope.form.input.$error.maxlength).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
|
|
describe('required', function() {
|
|
|
|
it('should allow bindings via ngRequired', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-required="required" />');
|
|
|
|
$rootScope.$apply("required = false");
|
|
|
|
helper.changeInputValueTo('');
|
|
expect(inputElm).toBeValid();
|
|
|
|
|
|
$rootScope.$apply("required = true");
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
$rootScope.$apply("value = 'some'");
|
|
expect(inputElm).toBeValid();
|
|
|
|
helper.changeInputValueTo('');
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
$rootScope.$apply("required = false");
|
|
expect(inputElm).toBeValid();
|
|
});
|
|
|
|
|
|
it('should invalid initial value with bound required', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" required="{{required}}" />');
|
|
|
|
$rootScope.$apply('required = true');
|
|
|
|
expect(inputElm).toBeInvalid();
|
|
});
|
|
|
|
|
|
it('should be $invalid but $pristine if not touched', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="name" name="alias" required />');
|
|
|
|
$rootScope.$apply("name = null");
|
|
|
|
expect(inputElm).toBeInvalid();
|
|
expect(inputElm).toBePristine();
|
|
|
|
helper.changeInputValueTo('');
|
|
expect(inputElm).toBeInvalid();
|
|
expect(inputElm).toBeDirty();
|
|
});
|
|
|
|
|
|
it('should allow empty string if not required', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="foo" />');
|
|
helper.changeInputValueTo('a');
|
|
helper.changeInputValueTo('');
|
|
expect($rootScope.foo).toBe('');
|
|
});
|
|
|
|
|
|
it('should set $invalid when model undefined', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="notDefined" required />');
|
|
expect(inputElm).toBeInvalid();
|
|
});
|
|
|
|
|
|
it('should consider bad input as an error before any other errors are considered', function() {
|
|
var inputElm = helper.compileInput('<input type="text" ng-model="value" required />', { badInput: true });
|
|
var ctrl = inputElm.controller('ngModel');
|
|
ctrl.$parsers.push(function() {
|
|
return undefined;
|
|
});
|
|
|
|
helper.changeInputValueTo('abc123');
|
|
|
|
expect(ctrl.$error.parse).toBe(true);
|
|
expect(inputElm).toHaveClass('ng-invalid-parse');
|
|
expect(inputElm).toBeInvalid(); // invalid because of the number validator
|
|
});
|
|
|
|
|
|
it('should allow `false` as a valid value when the input type is not "checkbox"', function() {
|
|
var inputElm = helper.compileInput('<input type="radio" ng-value="true" ng-model="answer" required />' +
|
|
'<input type="radio" ng-value="false" ng-model="answer" required />');
|
|
|
|
$rootScope.$apply();
|
|
expect(inputElm).toBeInvalid();
|
|
|
|
$rootScope.$apply("answer = true");
|
|
expect(inputElm).toBeValid();
|
|
|
|
$rootScope.$apply("answer = false");
|
|
expect(inputElm).toBeValid();
|
|
});
|
|
});
|
|
});
|