@angular-wave/angular.ts 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +1 -0
- package/.eslintrc.cjs +29 -0
- package/.github/workflows/playwright.yml +27 -0
- package/CHANGELOG.md +17974 -0
- package/CODE_OF_CONDUCT.md +3 -0
- package/CONTRIBUTING.md +246 -0
- package/DEVELOPERS.md +488 -0
- package/LICENSE +22 -0
- package/Makefile +31 -0
- package/README.md +115 -0
- package/RELEASE.md +98 -0
- package/SECURITY.md +16 -0
- package/TRIAGING.md +135 -0
- package/css/angular.css +22 -0
- package/dist/angular-ts.cjs.js +36843 -0
- package/dist/angular-ts.esm.js +36841 -0
- package/dist/angular-ts.umd.js +36848 -0
- package/dist/build/angular-animate.js +4272 -0
- package/dist/build/angular-aria.js +426 -0
- package/dist/build/angular-message-format.js +1072 -0
- package/dist/build/angular-messages.js +829 -0
- package/dist/build/angular-mocks.js +3757 -0
- package/dist/build/angular-parse-ext.js +1275 -0
- package/dist/build/angular-resource.js +911 -0
- package/dist/build/angular-route.js +1266 -0
- package/dist/build/angular-sanitize.js +891 -0
- package/dist/build/angular-touch.js +368 -0
- package/dist/build/angular.js +36600 -0
- package/e2e/unit.spec.ts +15 -0
- package/images/android-chrome-192x192.png +0 -0
- package/images/android-chrome-512x512.png +0 -0
- package/images/apple-touch-icon.png +0 -0
- package/images/favicon-16x16.png +0 -0
- package/images/favicon-32x32.png +0 -0
- package/images/favicon.ico +0 -0
- package/images/site.webmanifest +1 -0
- package/index.html +104 -0
- package/package.json +47 -0
- package/playwright.config.ts +78 -0
- package/public/circle.html +1 -0
- package/public/my_child_directive.html +1 -0
- package/public/my_directive.html +1 -0
- package/public/my_other_directive.html +1 -0
- package/public/test.html +1 -0
- package/rollup.config.js +31 -0
- package/src/animations/animateCache.js +55 -0
- package/src/animations/animateChildrenDirective.js +105 -0
- package/src/animations/animateCss.js +1139 -0
- package/src/animations/animateCssDriver.js +291 -0
- package/src/animations/animateJs.js +367 -0
- package/src/animations/animateJsDriver.js +67 -0
- package/src/animations/animateQueue.js +851 -0
- package/src/animations/animation.js +506 -0
- package/src/animations/module.js +779 -0
- package/src/animations/ngAnimateSwap.js +119 -0
- package/src/animations/rafScheduler.js +50 -0
- package/src/animations/shared.js +378 -0
- package/src/constants.js +20 -0
- package/src/core/animate.js +845 -0
- package/src/core/animateCss.js +73 -0
- package/src/core/animateRunner.js +195 -0
- package/src/core/attributes.js +199 -0
- package/src/core/cache.js +45 -0
- package/src/core/compile.js +4727 -0
- package/src/core/controller.js +225 -0
- package/src/core/exceptionHandler.js +63 -0
- package/src/core/filter.js +146 -0
- package/src/core/interpolate.js +442 -0
- package/src/core/interval.js +188 -0
- package/src/core/intervalFactory.js +57 -0
- package/src/core/location.js +1086 -0
- package/src/core/parser/parse.js +2562 -0
- package/src/core/parser/parse.md +13 -0
- package/src/core/q.js +746 -0
- package/src/core/rootScope.js +1596 -0
- package/src/core/sanitizeUri.js +85 -0
- package/src/core/sce.js +1161 -0
- package/src/core/taskTrackerFactory.js +125 -0
- package/src/core/timeout.js +121 -0
- package/src/core/urlUtils.js +187 -0
- package/src/core/utils.js +1349 -0
- package/src/directive/a.js +37 -0
- package/src/directive/attrs.js +283 -0
- package/src/directive/bind.js +51 -0
- package/src/directive/bind.md +142 -0
- package/src/directive/change.js +12 -0
- package/src/directive/change.md +25 -0
- package/src/directive/cloak.js +12 -0
- package/src/directive/cloak.md +24 -0
- package/src/directive/events.js +75 -0
- package/src/directive/events.md +166 -0
- package/src/directive/form.js +725 -0
- package/src/directive/init.js +15 -0
- package/src/directive/init.md +41 -0
- package/src/directive/input.js +1783 -0
- package/src/directive/list.js +46 -0
- package/src/directive/list.md +22 -0
- package/src/directive/ngClass.js +249 -0
- package/src/directive/ngController.js +64 -0
- package/src/directive/ngCsp.js +82 -0
- package/src/directive/ngIf.js +134 -0
- package/src/directive/ngInclude.js +217 -0
- package/src/directive/ngModel.js +1356 -0
- package/src/directive/ngModelOptions.js +509 -0
- package/src/directive/ngOptions.js +670 -0
- package/src/directive/ngRef.js +90 -0
- package/src/directive/ngRepeat.js +650 -0
- package/src/directive/ngShowHide.js +255 -0
- package/src/directive/ngSwitch.js +178 -0
- package/src/directive/ngTransclude.js +98 -0
- package/src/directive/non-bindable.js +11 -0
- package/src/directive/non-bindable.md +17 -0
- package/src/directive/script.js +30 -0
- package/src/directive/select.js +624 -0
- package/src/directive/style.js +25 -0
- package/src/directive/style.md +23 -0
- package/src/directive/validators.js +329 -0
- package/src/exts/aria.js +544 -0
- package/src/exts/messages.js +852 -0
- package/src/filters/filter.js +207 -0
- package/src/filters/filter.md +69 -0
- package/src/filters/filters.js +239 -0
- package/src/filters/json.md +16 -0
- package/src/filters/limit-to.js +43 -0
- package/src/filters/limit-to.md +19 -0
- package/src/filters/order-by.js +183 -0
- package/src/filters/order-by.md +83 -0
- package/src/index.js +13 -0
- package/src/injector.js +1034 -0
- package/src/jqLite.js +1117 -0
- package/src/loader.js +1320 -0
- package/src/public.js +215 -0
- package/src/routeToRegExp.js +41 -0
- package/src/services/anchorScroll.js +135 -0
- package/src/services/browser.js +321 -0
- package/src/services/cacheFactory.js +398 -0
- package/src/services/cookieReader.js +72 -0
- package/src/services/document.js +64 -0
- package/src/services/http.js +1537 -0
- package/src/services/httpBackend.js +206 -0
- package/src/services/log.js +160 -0
- package/src/services/templateRequest.js +139 -0
- package/test/angular.spec.js +2153 -0
- package/test/aria/aria.spec.js +1245 -0
- package/test/binding.spec.js +504 -0
- package/test/build-test.html +14 -0
- package/test/injector.spec.js +2327 -0
- package/test/jasmine/jasmine-5.1.2/boot0.js +65 -0
- package/test/jasmine/jasmine-5.1.2/boot1.js +133 -0
- package/test/jasmine/jasmine-5.1.2/jasmine-html.js +963 -0
- package/test/jasmine/jasmine-5.1.2/jasmine.css +320 -0
- package/test/jasmine/jasmine-5.1.2/jasmine.js +10824 -0
- package/test/jasmine/jasmine-5.1.2/jasmine_favicon.png +0 -0
- package/test/jasmine/jasmine-browser.json +17 -0
- package/test/jasmine/jasmine.json +9 -0
- package/test/jqlite.spec.js +2133 -0
- package/test/loader.spec.js +219 -0
- package/test/messages/messages.spec.js +1146 -0
- package/test/min-err.spec.js +174 -0
- package/test/mock-test.html +13 -0
- package/test/module-test.html +15 -0
- package/test/ng/anomate.spec.js +606 -0
- package/test/ng/cache-factor.spec.js +334 -0
- package/test/ng/compile.spec.js +17956 -0
- package/test/ng/controller-provider.spec.js +227 -0
- package/test/ng/cookie-reader.spec.js +98 -0
- package/test/ng/directive/a.spec.js +192 -0
- package/test/ng/directive/bind.spec.js +334 -0
- package/test/ng/directive/boolean.spec.js +136 -0
- package/test/ng/directive/change.spec.js +71 -0
- package/test/ng/directive/class.spec.js +858 -0
- package/test/ng/directive/click.spec.js +38 -0
- package/test/ng/directive/cloak.spec.js +44 -0
- package/test/ng/directive/constoller.spec.js +194 -0
- package/test/ng/directive/element-style.spec.js +92 -0
- package/test/ng/directive/event.spec.js +282 -0
- package/test/ng/directive/form.spec.js +1518 -0
- package/test/ng/directive/href.spec.js +143 -0
- package/test/ng/directive/if.spec.js +402 -0
- package/test/ng/directive/include.spec.js +828 -0
- package/test/ng/directive/init.spec.js +68 -0
- package/test/ng/directive/input.spec.js +3810 -0
- package/test/ng/directive/list.spec.js +170 -0
- package/test/ng/directive/model-options.spec.js +1008 -0
- package/test/ng/directive/model.spec.js +1905 -0
- package/test/ng/directive/non-bindable.spec.js +55 -0
- package/test/ng/directive/options.spec.js +3583 -0
- package/test/ng/directive/ref.spec.js +575 -0
- package/test/ng/directive/repeat.spec.js +1675 -0
- package/test/ng/directive/script.spec.js +52 -0
- package/test/ng/directive/scrset.spec.js +67 -0
- package/test/ng/directive/select.spec.js +2541 -0
- package/test/ng/directive/show-hide.spec.js +253 -0
- package/test/ng/directive/src.spec.js +157 -0
- package/test/ng/directive/style.spec.js +178 -0
- package/test/ng/directive/switch.spec.js +647 -0
- package/test/ng/directive/validators.spec.js +717 -0
- package/test/ng/document.spec.js +52 -0
- package/test/ng/filter/filter.spec.js +714 -0
- package/test/ng/filter/filters.spec.js +35 -0
- package/test/ng/filter/limit-to.spec.js +251 -0
- package/test/ng/filter/order-by.spec.js +891 -0
- package/test/ng/filter.spec.js +149 -0
- package/test/ng/http-backend.spec.js +398 -0
- package/test/ng/http.spec.js +4071 -0
- package/test/ng/interpolate.spec.js +642 -0
- package/test/ng/interval.spec.js +343 -0
- package/test/ng/location.spec.js +3488 -0
- package/test/ng/on.spec.js +229 -0
- package/test/ng/parse.spec.js +4655 -0
- package/test/ng/prop.spec.js +805 -0
- package/test/ng/q.spec.js +2904 -0
- package/test/ng/root-element.spec.js +16 -0
- package/test/ng/sanitize-uri.spec.js +249 -0
- package/test/ng/sce.spec.js +660 -0
- package/test/ng/scope.spec.js +3442 -0
- package/test/ng/template-request.spec.js +236 -0
- package/test/ng/timeout.spec.js +351 -0
- package/test/ng/url-utils.spec.js +156 -0
- package/test/ng/utils.spec.js +144 -0
- package/test/original-test.html +21 -0
- package/test/public.spec.js +34 -0
- package/test/sanitize/bing-html.spec.js +36 -0
- package/test/server/express.js +158 -0
- package/test/test-utils.js +11 -0
- package/tsconfig.json +17 -0
- package/types/angular.d.ts +138 -0
- package/types/global.d.ts +9 -0
- package/types/index.d.ts +2357 -0
- package/types/jqlite.d.ts +558 -0
- package/vite.config.js +14 -0
|
@@ -0,0 +1,3810 @@
|
|
|
1
|
+
import { publishExternalAPI } from "../../../src/public";
|
|
2
|
+
import { createInjector } from "../../../src/injector";
|
|
3
|
+
import { jqLite } from "../../../src/jqLite";
|
|
4
|
+
import {
|
|
5
|
+
EMAIL_REGEXP,
|
|
6
|
+
ISO_DATE_REGEXP,
|
|
7
|
+
URL_REGEXP,
|
|
8
|
+
} from "../../../src/directive/input";
|
|
9
|
+
import { forEach } from "../../../src/core/utils";
|
|
10
|
+
|
|
11
|
+
describe("input", () => {
|
|
12
|
+
let $compile;
|
|
13
|
+
let scope;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
publishExternalAPI().decorator("$exceptionHandler", function () {
|
|
17
|
+
return (exception, cause) => {
|
|
18
|
+
throw new Error(exception.message);
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
createInjector(["ng"]).invoke((_$compile_, $rootScope) => {
|
|
22
|
+
$compile = _$compile_;
|
|
23
|
+
scope = $rootScope.$new();
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should bind to a model", () => {
|
|
28
|
+
const inputElm = $compile(
|
|
29
|
+
'<input type="text" ng-model="name" name="alias" ng-change="change()" />',
|
|
30
|
+
)(scope);
|
|
31
|
+
|
|
32
|
+
scope.$apply("name = 'misko'");
|
|
33
|
+
|
|
34
|
+
expect(inputElm.val()).toBe("misko");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should update the model on "blur" event', () => {
|
|
38
|
+
const inputElm = $compile(
|
|
39
|
+
'<input type="text" ng-model="name" name="alias" ng-change="change()" />',
|
|
40
|
+
)(scope);
|
|
41
|
+
inputElm[0].setAttribute("value", "adam");
|
|
42
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
43
|
+
expect(scope.name).toEqual("adam");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should not add the property to the scope if name is unspecified", () => {
|
|
47
|
+
$compile('<input type="text" ng-model="name">')(scope);
|
|
48
|
+
|
|
49
|
+
expect(scope.name).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should not set the `val` property when the value is equal to the current value", () => {
|
|
53
|
+
// This is a workaround for Firefox validation. Look at #12102.
|
|
54
|
+
const input = jqLite('<input type="text" ng-model="foo" required/>');
|
|
55
|
+
let setterCalls = 0;
|
|
56
|
+
scope.foo = "";
|
|
57
|
+
Object.defineProperty(input[0], "value", {
|
|
58
|
+
get() {
|
|
59
|
+
return "";
|
|
60
|
+
},
|
|
61
|
+
set() {
|
|
62
|
+
setterCalls++;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
$compile(input)(scope);
|
|
66
|
+
scope.$digest();
|
|
67
|
+
expect(setterCalls).toBe(0);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("compositionevents", () => {
|
|
71
|
+
it('should not update the model between "compositionstart" and "compositionend"', () => {
|
|
72
|
+
//$sniffer.android = false;
|
|
73
|
+
|
|
74
|
+
const inputElm = $compile(
|
|
75
|
+
'<input type="text" ng-model="name" name="alias"" />',
|
|
76
|
+
)(scope);
|
|
77
|
+
inputElm[0].setAttribute("value", "a");
|
|
78
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
79
|
+
expect(scope.name).toEqual("a");
|
|
80
|
+
|
|
81
|
+
inputElm[0].dispatchEvent(new Event("compositionstart"));
|
|
82
|
+
inputElm[0].setAttribute("value", "adam");
|
|
83
|
+
expect(scope.name).toEqual("a");
|
|
84
|
+
inputElm[0].dispatchEvent(new Event("compositionend"));
|
|
85
|
+
inputElm[0].setAttribute("value", "adam");
|
|
86
|
+
expect(scope.name).toEqual("adam");
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("interpolated names", () => {
|
|
91
|
+
it("should interpolate input names", () => {
|
|
92
|
+
scope.nameID = "47";
|
|
93
|
+
const inputElm = $compile(
|
|
94
|
+
'<form name="form"><input type="text" ng-model="name" name="name{{nameID}}" /></form>',
|
|
95
|
+
)(scope);
|
|
96
|
+
expect(scope.form.name47.$pristine).toBeTruthy();
|
|
97
|
+
inputElm.find("input")[0].setAttribute("value", "caitp");
|
|
98
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
99
|
+
expect(scope.form.name47.$dirty).toBeTruthy();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("should rename form controls in form when interpolated name changes", () => {
|
|
103
|
+
scope.nameID = "A";
|
|
104
|
+
const inputElm = $compile(
|
|
105
|
+
'<form name="form"><input type="text" ng-model="name" name="name{{nameID}}" /></form>',
|
|
106
|
+
)(scope);
|
|
107
|
+
expect(scope.form.nameA.$name).toBe("nameA");
|
|
108
|
+
const oldModel = scope.form.nameA;
|
|
109
|
+
scope.nameID = "B";
|
|
110
|
+
scope.$digest();
|
|
111
|
+
expect(scope.form.nameA).toBeUndefined();
|
|
112
|
+
expect(scope.form.nameB).toBe(oldModel);
|
|
113
|
+
expect(scope.form.nameB.$name).toBe("nameB");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("should rename form controls in null form when interpolated name changes", () => {
|
|
117
|
+
scope.nameID = "A";
|
|
118
|
+
const inputElm = $compile(
|
|
119
|
+
'<input type="text" ng-model="name" name="name{{nameID}}" />',
|
|
120
|
+
)(scope);
|
|
121
|
+
const model = inputElm.controller("ngModel");
|
|
122
|
+
expect(model.$name).toBe("nameA");
|
|
123
|
+
|
|
124
|
+
scope.nameID = "B";
|
|
125
|
+
scope.$digest();
|
|
126
|
+
expect(model.$name).toBe("nameB");
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('"change" event', () => {
|
|
131
|
+
let assertBrowserSupportsChangeEvent;
|
|
132
|
+
|
|
133
|
+
beforeEach(() => {
|
|
134
|
+
assertBrowserSupportsChangeEvent = function (inputEventSupported) {
|
|
135
|
+
const inputElm = $compile(
|
|
136
|
+
'<input type="text" ng-model="name" name="alias" />',
|
|
137
|
+
)(scope);
|
|
138
|
+
|
|
139
|
+
//inputElm.val("mark");
|
|
140
|
+
inputElm[0].setAttribute("value", "mark");
|
|
141
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
142
|
+
expect(scope.name).toEqual("mark");
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should update the model event if the browser does not support the "input" event', () => {
|
|
147
|
+
assertBrowserSupportsChangeEvent(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it(
|
|
151
|
+
'should update the model event if the browser supports the "input" ' +
|
|
152
|
+
"event so that form auto complete works",
|
|
153
|
+
() => {
|
|
154
|
+
assertBrowserSupportsChangeEvent(true);
|
|
155
|
+
},
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
describe('"keydown", "paste", "cut" and "drop" events', () => {
|
|
159
|
+
it('should update the model on "paste" event if the input value changes', () => {
|
|
160
|
+
const inputElm = $compile(
|
|
161
|
+
'<input type="text" ng-model="name" name="alias" ng-change="change()" />',
|
|
162
|
+
)(scope);
|
|
163
|
+
|
|
164
|
+
inputElm[0].dispatchEvent(new Event("keydown"));
|
|
165
|
+
expect(inputElm[0].classList.contains("ng-pristine")).toBeTrue();
|
|
166
|
+
|
|
167
|
+
inputElm[0].setAttribute("value", "mark");
|
|
168
|
+
inputElm[0].dispatchEvent(new Event("paste"));
|
|
169
|
+
expect(scope.name).toEqual("mark");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should update the model on "drop" event if the input value changes', () => {
|
|
173
|
+
const inputElm = $compile(
|
|
174
|
+
'<input type="text" ng-model="name" name="alias" ng-change="change()" />',
|
|
175
|
+
)(scope);
|
|
176
|
+
|
|
177
|
+
inputElm[0].dispatchEvent(new Event("keydown"));
|
|
178
|
+
expect(inputElm[0].classList.contains("ng-pristine")).toBeTrue();
|
|
179
|
+
|
|
180
|
+
inputElm[0].setAttribute("value", "mark");
|
|
181
|
+
inputElm[0].dispatchEvent(new Event("drop"));
|
|
182
|
+
expect(scope.name).toEqual("mark");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should update the model on "cut" event', () => {
|
|
186
|
+
const inputElm = $compile(
|
|
187
|
+
'<input type="text" ng-model="name" name="alias" ng-change="change()" />',
|
|
188
|
+
)(scope);
|
|
189
|
+
|
|
190
|
+
inputElm[0].setAttribute("value", "john");
|
|
191
|
+
inputElm[0].dispatchEvent(new Event("cut"));
|
|
192
|
+
expect(scope.name).toEqual("john");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should cancel the delayed dirty if a change occurs", () => {
|
|
196
|
+
const inputElm = $compile('<input type="text" ng-model="name" />')(
|
|
197
|
+
scope,
|
|
198
|
+
);
|
|
199
|
+
const ctrl = inputElm.controller("ngModel");
|
|
200
|
+
|
|
201
|
+
inputElm[0].dispatchEvent(
|
|
202
|
+
new Event("keydown", { target: inputElm[0] }),
|
|
203
|
+
);
|
|
204
|
+
inputElm.val("f");
|
|
205
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
206
|
+
expect(inputElm[0].classList.contains("ng-dirty")).toBeTrue();
|
|
207
|
+
|
|
208
|
+
ctrl.$setPristine();
|
|
209
|
+
scope.$apply();
|
|
210
|
+
|
|
211
|
+
expect(inputElm[0].classList.contains("ng-pristine")).toBeTrue();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe("ngTrim", () => {
|
|
215
|
+
it("should update the model and trim the value", () => {
|
|
216
|
+
const inputElm = $compile(
|
|
217
|
+
'<input type="text" ng-model="name" name="alias" ng-change="change()" />',
|
|
218
|
+
)(scope);
|
|
219
|
+
|
|
220
|
+
inputElm[0].setAttribute("value", " a ");
|
|
221
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
222
|
+
expect(scope.name).toEqual("a");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("should update the model and not trim the value", () => {
|
|
226
|
+
const inputElm = $compile(
|
|
227
|
+
'<input type="text" ng-model="name" name="alias" ng-trim="false" />',
|
|
228
|
+
)(scope);
|
|
229
|
+
|
|
230
|
+
inputElm[0].setAttribute("value", " a ");
|
|
231
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
232
|
+
expect(scope.name).toEqual(" a ");
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("should allow complex reference binding", () => {
|
|
237
|
+
const inputElm = $compile(
|
|
238
|
+
'<input type="text" ng-model="obj[\'abc\'].name"/>',
|
|
239
|
+
)(scope);
|
|
240
|
+
|
|
241
|
+
scope.$apply("obj = { abc: { name: 'Misko'} }");
|
|
242
|
+
expect(inputElm.val()).toEqual("Misko");
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("should ignore input without ngModel directive", () => {
|
|
246
|
+
const inputElm = $compile(
|
|
247
|
+
'<input type="text" name="whatever" required />',
|
|
248
|
+
)(scope);
|
|
249
|
+
|
|
250
|
+
inputElm[0].setAttribute("value", "");
|
|
251
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
252
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBe(false);
|
|
253
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBe(false);
|
|
254
|
+
expect(inputElm[0].classList.contains("ng-pristine")).toBe(false);
|
|
255
|
+
expect(inputElm[0].classList.contains("ng-dirty")).toBe(false);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("should report error on assignment error", () => {
|
|
259
|
+
expect(() => {
|
|
260
|
+
const inputElm = $compile(
|
|
261
|
+
'<input type="text" ng-model="throw \'\'">',
|
|
262
|
+
)(scope);
|
|
263
|
+
}).toThrowError(/Syntax Error/);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("should render as blank if null", () => {
|
|
267
|
+
const inputElm = $compile('<input type="text" ng-model="age" />')(
|
|
268
|
+
scope,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
scope.$apply("age = null");
|
|
272
|
+
|
|
273
|
+
expect(scope.age).toBeNull();
|
|
274
|
+
expect(inputElm.val()).toEqual("");
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("should render 0 even if it is a number", () => {
|
|
278
|
+
const inputElm = $compile('<input type="text" ng-model="value" />')(
|
|
279
|
+
scope,
|
|
280
|
+
);
|
|
281
|
+
scope.$apply("value = 0");
|
|
282
|
+
|
|
283
|
+
expect(inputElm.val()).toBe("0");
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("should render the $viewValue when $modelValue is empty", () => {
|
|
287
|
+
const inputElm = $compile('<input type="text" ng-model="value" />')(
|
|
288
|
+
scope,
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
const ctrl = inputElm.controller("ngModel");
|
|
292
|
+
|
|
293
|
+
ctrl.$modelValue = null;
|
|
294
|
+
|
|
295
|
+
expect(ctrl.$isEmpty(ctrl.$modelValue)).toBe(true);
|
|
296
|
+
|
|
297
|
+
ctrl.$viewValue = "abc";
|
|
298
|
+
ctrl.$render();
|
|
299
|
+
|
|
300
|
+
expect(inputElm.val()).toBe("abc");
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// INPUT TYPES
|
|
305
|
+
describe("month", () => {
|
|
306
|
+
// IN ANGULAR.JS month types were converted to Date object. This is not standard behavior
|
|
307
|
+
it("should allow a String object in format 'YYYY-MM'", () => {
|
|
308
|
+
const inputElm = $compile('<input type="month" ng-model="january"/>')(
|
|
309
|
+
scope,
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
scope.$apply(() => {
|
|
313
|
+
scope.january = "2013-01";
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
expect(inputElm.val()).toBe("2013-01");
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("should throw if the model is a Date object", () => {
|
|
320
|
+
const inputElm = $compile('<input type="month" ng-model="march"/>')(
|
|
321
|
+
scope,
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
expect(() => {
|
|
325
|
+
scope.$apply(() => {
|
|
326
|
+
scope.march = new Date(2013, 2, 1);
|
|
327
|
+
});
|
|
328
|
+
}).toThrowError(/datefmt/);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("should throw if the model is a Invalid string", () => {
|
|
332
|
+
const inputElm = $compile('<input type="month" ng-model="march"/>')(
|
|
333
|
+
scope,
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
expect(() => {
|
|
337
|
+
scope.$apply(() => {
|
|
338
|
+
scope.march = "fail";
|
|
339
|
+
});
|
|
340
|
+
}).toThrowError(/datefmt/);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("should not change the model if the input is an invalid month string", () => {
|
|
344
|
+
const inputElm = $compile('<input type="month" ng-model="value"/>')(
|
|
345
|
+
scope,
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
scope.$apply(() => {
|
|
349
|
+
scope.value = "2013-01";
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
expect(inputElm.val()).toBe("2013-01");
|
|
353
|
+
|
|
354
|
+
inputElm[0].setAttribute("value", "stuff");
|
|
355
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
356
|
+
expect(inputElm.val()).toBe("2013-01");
|
|
357
|
+
expect(scope.value).toBe("2013-01");
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("should render as blank if null", () => {
|
|
361
|
+
const inputElm = $compile('<input type="month" ng-model="test" />')(
|
|
362
|
+
scope,
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
scope.$apply("test = null");
|
|
366
|
+
|
|
367
|
+
expect(scope.test).toBeNull();
|
|
368
|
+
expect(inputElm.val()).toEqual("");
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it("should come up blank when no value specified", () => {
|
|
372
|
+
const inputElm = $compile('<input type="month" ng-model="test" />')(
|
|
373
|
+
scope,
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
expect(inputElm.val()).toBe("");
|
|
377
|
+
|
|
378
|
+
scope.$apply("test = null");
|
|
379
|
+
|
|
380
|
+
expect(scope.test).toBeNull();
|
|
381
|
+
expect(inputElm.val()).toBe("");
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it("should parse empty string to null", () => {
|
|
385
|
+
const inputElm = $compile('<input type="month" ng-model="test" />')(
|
|
386
|
+
scope,
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
inputElm[0].setAttribute("value", "");
|
|
390
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
391
|
+
expect(scope.test).toBeNull();
|
|
392
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("should set scope to a string value", () => {
|
|
396
|
+
const inputElm = $compile('<input type="month" ng-model="value" />')(
|
|
397
|
+
scope,
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
inputElm[0].setAttribute("value", "2013-07");
|
|
401
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
402
|
+
expect(scope.value).toBe("2013-07");
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
describe("min", () => {
|
|
406
|
+
let inputElm;
|
|
407
|
+
beforeEach(() => {
|
|
408
|
+
scope.minVal = "2013-01";
|
|
409
|
+
inputElm = $compile(
|
|
410
|
+
'<form name="form"><input type="month" ng-model="value" name="alias" min="{{ minVal }}" /></form>',
|
|
411
|
+
)(scope);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it("should invalidate", () => {
|
|
415
|
+
inputElm.find("input")[0].setAttribute("value", "2012-12");
|
|
416
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
417
|
+
expect(
|
|
418
|
+
inputElm.find("input")[0].classList.contains("ng-invalid"),
|
|
419
|
+
).toBeTrue();
|
|
420
|
+
expect(scope.value).toBeFalsy();
|
|
421
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("should validate", () => {
|
|
425
|
+
inputElm.find("input")[0].setAttribute("value", "2013-07");
|
|
426
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
427
|
+
expect(
|
|
428
|
+
inputElm.find("input")[0].classList.contains("ng-valid"),
|
|
429
|
+
).toBeTrue();
|
|
430
|
+
expect(scope.value).toBe("2013-07");
|
|
431
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it("should revalidate when the min value changes", () => {
|
|
435
|
+
inputElm.find("input")[0].setAttribute("value", "2013-07");
|
|
436
|
+
expect(
|
|
437
|
+
inputElm.find("input")[0].classList.contains("ng-valid"),
|
|
438
|
+
).toBeTrue();
|
|
439
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
440
|
+
|
|
441
|
+
scope.$apply(() => {
|
|
442
|
+
scope.minVal = "2014-01";
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
expect(
|
|
446
|
+
inputElm.find("input")[0].classList.contains("ng-invalid"),
|
|
447
|
+
).toBeTrue();
|
|
448
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it("should validate if min is empty", () => {
|
|
452
|
+
scope.minVal = undefined;
|
|
453
|
+
scope.value = "2014-01";
|
|
454
|
+
scope.$digest();
|
|
455
|
+
|
|
456
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
describe("max", () => {
|
|
461
|
+
let inputElm;
|
|
462
|
+
beforeEach(() => {
|
|
463
|
+
scope.maxVal = "2013-01";
|
|
464
|
+
inputElm = $compile(
|
|
465
|
+
'<form name="form"><input type="month" ng-model="value" name="alias" max="{{ maxVal }}" /></form>',
|
|
466
|
+
)(scope);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it("should validate", () => {
|
|
470
|
+
inputElm.find("input")[0].setAttribute("value", "2012-03");
|
|
471
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
472
|
+
expect(
|
|
473
|
+
inputElm.find("input")[0].classList.contains("ng-valid"),
|
|
474
|
+
).toBeTrue();
|
|
475
|
+
expect(scope.value).toBe("2012-03");
|
|
476
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it("should invalidate", () => {
|
|
480
|
+
inputElm.find("input")[0].setAttribute("value", "2013-05");
|
|
481
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
482
|
+
expect(
|
|
483
|
+
inputElm.find("input")[0].classList.contains("ng-invalid"),
|
|
484
|
+
).toBeTrue();
|
|
485
|
+
expect(scope.value).toBeUndefined();
|
|
486
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it("should revalidate when the max value changes", () => {
|
|
490
|
+
inputElm.find("input")[0].setAttribute("value", "2012-07");
|
|
491
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
492
|
+
expect(
|
|
493
|
+
inputElm.find("input")[0].classList.contains("ng-valid"),
|
|
494
|
+
).toBeTrue();
|
|
495
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
496
|
+
|
|
497
|
+
scope.maxVal = "2012-01";
|
|
498
|
+
scope.$digest();
|
|
499
|
+
|
|
500
|
+
expect(
|
|
501
|
+
inputElm.find("input")[0].classList.contains("ng-invalid"),
|
|
502
|
+
).toBeTrue();
|
|
503
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it("should validate if max is empty", () => {
|
|
507
|
+
scope.maxVal = undefined;
|
|
508
|
+
scope.value = "2012-03";
|
|
509
|
+
scope.$digest();
|
|
510
|
+
|
|
511
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
describe("week", () => {
|
|
517
|
+
it("should throw if model is a Date object", () => {
|
|
518
|
+
const inputElm = $compile('<input type="week" ng-model="secondWeek"/>')(
|
|
519
|
+
scope,
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
expect(() => {
|
|
523
|
+
scope.$apply(() => {
|
|
524
|
+
scope.secondWeek = new Date(2013, 0, 11);
|
|
525
|
+
});
|
|
526
|
+
}).toThrowError(/datefmt/);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it("should set the view if the model is a valid String object", () => {
|
|
530
|
+
const inputElm = $compile('<input type="week" ng-model="secondWeek"/>')(
|
|
531
|
+
scope,
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
scope.$apply(() => {
|
|
535
|
+
scope.secondWeek = "2013-W02";
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
expect(inputElm.val()).toBe("2013-W02");
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("should set scope to a string value", () => {
|
|
542
|
+
const inputElm = $compile(
|
|
543
|
+
'<input type="week" ng-model="secondWeek" />',
|
|
544
|
+
)(scope);
|
|
545
|
+
|
|
546
|
+
scope.$apply(() => {
|
|
547
|
+
scope.secondWeek = "2013-W02";
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
expect(scope.secondWeek).toBe("2013-W02");
|
|
551
|
+
// input type week in Chrome does not react to changes on the attribute. Value must be set directly
|
|
552
|
+
inputElm[0].value = "2014-W03";
|
|
553
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
554
|
+
|
|
555
|
+
expect(scope.secondWeek).toBe("2014-W03");
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it("should set the model undefined if the input is an invalid week string", () => {
|
|
559
|
+
const inputElm = $compile('<input type="week" ng-model="secondWeek"/>')(
|
|
560
|
+
scope,
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
scope.$apply(() => {
|
|
564
|
+
scope.secondWeek = "2013-W02";
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
expect(inputElm.val()).toBe("2013-W02");
|
|
568
|
+
|
|
569
|
+
// set to text for browsers with datetime-local validation.
|
|
570
|
+
inputElm[0].value = "stuff";
|
|
571
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
572
|
+
expect(inputElm.val()).toBe("");
|
|
573
|
+
expect(scope.value).toBeUndefined();
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
it("should render as blank if null", () => {
|
|
577
|
+
const inputElm = $compile('<input type="week" ng-model="test" />')(
|
|
578
|
+
scope,
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
scope.$apply("test = null");
|
|
582
|
+
|
|
583
|
+
expect(scope.test).toBeNull();
|
|
584
|
+
expect(inputElm.val()).toEqual("");
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it("should come up blank when no value specified", () => {
|
|
588
|
+
const inputElm = $compile('<input type="week" ng-model="test" />')(
|
|
589
|
+
scope,
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
expect(inputElm.val()).toBe("");
|
|
593
|
+
|
|
594
|
+
scope.$apply("test = null");
|
|
595
|
+
|
|
596
|
+
expect(scope.test).toBeNull();
|
|
597
|
+
expect(inputElm.val()).toBe("");
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it("should parse empty string to null", () => {
|
|
601
|
+
const inputElm = $compile('<input type="week" ng-model="test" />')(
|
|
602
|
+
scope,
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
scope.$apply(() => {
|
|
606
|
+
scope.test = "2013-W02";
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
inputElm[0].value = "";
|
|
610
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
611
|
+
expect(scope.test).toBeNull();
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
describe("min", () => {
|
|
615
|
+
let inputElm;
|
|
616
|
+
beforeEach(() => {
|
|
617
|
+
scope.minVal = "2013-W01";
|
|
618
|
+
inputElm = $compile(
|
|
619
|
+
'<form name="form"><input type="week" ng-model="value" name="alias" min="{{ minVal }}" /></from>',
|
|
620
|
+
)(scope);
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it("should invalidate", () => {
|
|
624
|
+
inputElm.find("input")[0].value = "2012-W12";
|
|
625
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
626
|
+
expect(scope.value).toBeFalsy();
|
|
627
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
it("should validate", () => {
|
|
631
|
+
inputElm.find("input")[0].value = "2013-W03";
|
|
632
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
633
|
+
expect(
|
|
634
|
+
inputElm.find("input")[0].classList.contains("ng-valid"),
|
|
635
|
+
).toBeTrue();
|
|
636
|
+
expect(scope.value).toBe("2013-W03");
|
|
637
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it("should revalidate when the min value changes", () => {
|
|
641
|
+
inputElm.find("input")[0].value = "2013-W03";
|
|
642
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
643
|
+
expect(
|
|
644
|
+
inputElm.find("input")[0].classList.contains("ng-valid"),
|
|
645
|
+
).toBeTrue();
|
|
646
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
647
|
+
|
|
648
|
+
scope.minVal = "2014-W01";
|
|
649
|
+
scope.$digest();
|
|
650
|
+
|
|
651
|
+
expect(
|
|
652
|
+
inputElm.find("input")[0].classList.contains("ng-invalid"),
|
|
653
|
+
).toBeTrue();
|
|
654
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it("should validate if min is empty", () => {
|
|
658
|
+
scope.minVal = undefined;
|
|
659
|
+
scope.value = "2013-W03";
|
|
660
|
+
scope.$digest();
|
|
661
|
+
|
|
662
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
describe("max", () => {
|
|
667
|
+
let inputElm;
|
|
668
|
+
|
|
669
|
+
beforeEach(() => {
|
|
670
|
+
scope.maxVal = "2013-W01";
|
|
671
|
+
inputElm = $compile(
|
|
672
|
+
'<form name="form"><input type="week" ng-model="value" name="alias" max="{{ maxVal }}" /></form>',
|
|
673
|
+
)(scope);
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it("should validate", () => {
|
|
677
|
+
inputElm.find("input")[0].value = "2012-W01";
|
|
678
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
679
|
+
expect(
|
|
680
|
+
inputElm.find("input")[0].classList.contains("ng-valid"),
|
|
681
|
+
).toBeTrue();
|
|
682
|
+
expect(scope.value).toBe("2012-W01");
|
|
683
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
it("should invalidate", () => {
|
|
687
|
+
inputElm.find("input")[0].value = "2013-W03";
|
|
688
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
689
|
+
expect(
|
|
690
|
+
inputElm.find("input")[0].classList.contains("ng-invalid"),
|
|
691
|
+
).toBeTrue();
|
|
692
|
+
expect(scope.value).toBeUndefined();
|
|
693
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it("should revalidate when the max value changes", () => {
|
|
697
|
+
inputElm.find("input")[0].value = "2012-W03";
|
|
698
|
+
inputElm.find("input")[0].dispatchEvent(new Event("change"));
|
|
699
|
+
expect(
|
|
700
|
+
inputElm.find("input")[0].classList.contains("ng-valid"),
|
|
701
|
+
).toBeTrue();
|
|
702
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
703
|
+
|
|
704
|
+
scope.maxVal = "2012-W01";
|
|
705
|
+
scope.$digest();
|
|
706
|
+
|
|
707
|
+
expect(
|
|
708
|
+
inputElm.find("input")[0].classList.contains("ng-invalid"),
|
|
709
|
+
).toBeTrue();
|
|
710
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
it("should validate if max is empty", () => {
|
|
714
|
+
scope.maxVal = undefined;
|
|
715
|
+
scope.value = "2012-W01";
|
|
716
|
+
scope.$digest();
|
|
717
|
+
|
|
718
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
describe("datetime-local", () => {
|
|
724
|
+
it("should throw if model is a Date object", () => {
|
|
725
|
+
const inputElm = $compile(
|
|
726
|
+
'<input type="datetime-local" ng-model="lunchtime"/>',
|
|
727
|
+
)(scope);
|
|
728
|
+
|
|
729
|
+
expect(() => {
|
|
730
|
+
scope.$apply(() => {
|
|
731
|
+
scope.lunchtime = new Date(2013, 11, 31, 23, 59, 59, 500);
|
|
732
|
+
});
|
|
733
|
+
}).toThrowError(/datefmt/);
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
it("should set the view if the model if a valid String.", () => {
|
|
737
|
+
const inputElm = $compile(
|
|
738
|
+
'<input type="datetime-local" ng-model="halfSecondToNextYear"/>',
|
|
739
|
+
)(scope);
|
|
740
|
+
|
|
741
|
+
scope.$apply(() => {
|
|
742
|
+
scope.halfSecondToNextYear = "2013-12-16T11:30";
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
expect(inputElm.val()).toBe("2013-12-16T11:30");
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
it("should bind to the model if a valid String.", () => {
|
|
749
|
+
const inputElm = $compile(
|
|
750
|
+
'<input type="datetime-local" ng-model="halfSecondToNextYear"/>',
|
|
751
|
+
)(scope);
|
|
752
|
+
|
|
753
|
+
inputElm[0].value = "2013-12-16T11:30";
|
|
754
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
755
|
+
|
|
756
|
+
expect(inputElm.val()).toBe("2013-12-16T11:30");
|
|
757
|
+
expect(scope.halfSecondToNextYear).toBe("2013-12-16T11:30");
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
it("should set the model null if the view is invalid", () => {
|
|
761
|
+
const inputElm = $compile(
|
|
762
|
+
'<input type="datetime-local" ng-model="breakMe"/>',
|
|
763
|
+
)(scope);
|
|
764
|
+
|
|
765
|
+
scope.$apply(() => {
|
|
766
|
+
scope.breakMe = "2013-12-16T11:30";
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
expect(inputElm.val()).toBe("2013-12-16T11:30");
|
|
770
|
+
|
|
771
|
+
// set to text for browsers with datetime-local validation.
|
|
772
|
+
|
|
773
|
+
inputElm[0].value = "stuff";
|
|
774
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
775
|
+
expect(inputElm.val()).toBe("");
|
|
776
|
+
expect(scope.breakMe).toBeNull();
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
it("should render as blank if null", () => {
|
|
780
|
+
const inputElm = $compile(
|
|
781
|
+
'<input type="datetime-local" ng-model="test" />',
|
|
782
|
+
)(scope);
|
|
783
|
+
|
|
784
|
+
scope.$apply("test = null");
|
|
785
|
+
|
|
786
|
+
expect(scope.test).toBeNull();
|
|
787
|
+
expect(inputElm.val()).toEqual("");
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
it("should come up blank when no value specified", () => {
|
|
791
|
+
const inputElm = $compile(
|
|
792
|
+
'<input type="datetime-local" ng-model="test" />',
|
|
793
|
+
)(scope);
|
|
794
|
+
|
|
795
|
+
expect(inputElm.val()).toBe("");
|
|
796
|
+
|
|
797
|
+
scope.$apply("test = null");
|
|
798
|
+
|
|
799
|
+
expect(scope.test).toBeNull();
|
|
800
|
+
expect(inputElm.val()).toBe("");
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
it("should parse empty string to null", () => {
|
|
804
|
+
const inputElm = $compile(
|
|
805
|
+
'<input type="datetime-local" ng-model="test" />',
|
|
806
|
+
)(scope);
|
|
807
|
+
|
|
808
|
+
scope.$apply(() => {
|
|
809
|
+
scope.test = "2013-12-16T11:30";
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
inputElm[0].value = "";
|
|
813
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
814
|
+
expect(scope.test).toBeNull();
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
describe("min", () => {
|
|
818
|
+
let inputElm;
|
|
819
|
+
beforeEach(() => {
|
|
820
|
+
scope.minVal = "2000-01-01T12:30:00";
|
|
821
|
+
let formElm = $compile(
|
|
822
|
+
`<form name="form">
|
|
823
|
+
<input type="datetime-local" ng-model="value" name="alias" min="{{ minVal }}" />
|
|
824
|
+
</form>`,
|
|
825
|
+
)(scope);
|
|
826
|
+
inputElm = formElm.find("input");
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
it("should invalidate", () => {
|
|
830
|
+
inputElm[0].value = "1999-12-31T01:02:00";
|
|
831
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
832
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
833
|
+
expect(scope.value).toBeFalsy();
|
|
834
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it("should validate", () => {
|
|
838
|
+
inputElm[0].value = "2000-01-01T23:02:00";
|
|
839
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
840
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
841
|
+
expect(scope.value).toBe("2000-01-01T23:02");
|
|
842
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
it("should revalidate when the min value changes", () => {
|
|
846
|
+
inputElm[0].value = "2000-02-01T01:02:00";
|
|
847
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
848
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
849
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
850
|
+
|
|
851
|
+
scope.minVal = "2010-01-01T01:02:00";
|
|
852
|
+
scope.$digest();
|
|
853
|
+
|
|
854
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
855
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
it("should validate if min is empty", () => {
|
|
859
|
+
scope.minVal = undefined;
|
|
860
|
+
scope.value = "2010-01-01T01:02:00";
|
|
861
|
+
scope.$digest();
|
|
862
|
+
|
|
863
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
864
|
+
});
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
describe("max", () => {
|
|
868
|
+
let inputElm;
|
|
869
|
+
beforeEach(() => {
|
|
870
|
+
scope.maxVal = "2019-01-01T01:02:00";
|
|
871
|
+
let formElm = $compile(
|
|
872
|
+
'<form name="form"><input type="datetime-local" ng-model="value" name="alias" max="{{ maxVal }}" /></form>',
|
|
873
|
+
)(scope);
|
|
874
|
+
inputElm = formElm.find("input");
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
it("should invalidate", () => {
|
|
878
|
+
inputElm[0].value = "2019-12-31T01:02:00";
|
|
879
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
880
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
881
|
+
expect(scope.value).toBeFalsy();
|
|
882
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
it("should validate", () => {
|
|
886
|
+
inputElm[0].value = "2000-01-01T01:02:00";
|
|
887
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
888
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
889
|
+
expect(scope.value).toBe("2000-01-01T01:02");
|
|
890
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
it("should revalidate when the max value changes", () => {
|
|
894
|
+
inputElm[0].value = "2000-02-01T01:02:00";
|
|
895
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
896
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
897
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
898
|
+
|
|
899
|
+
scope.maxVal = "2000-01-01T01:02:00";
|
|
900
|
+
scope.$digest();
|
|
901
|
+
|
|
902
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
903
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
it("should validate if max is empty", () => {
|
|
907
|
+
scope.maxVal = undefined;
|
|
908
|
+
scope.value = "2000-01-01T01:02:00";
|
|
909
|
+
scope.$digest();
|
|
910
|
+
|
|
911
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
it("should validate when timezone is provided.", () => {
|
|
915
|
+
inputElm = $compile(
|
|
916
|
+
'<input type="datetime-local" ng-model="value" name="alias" ' +
|
|
917
|
+
'max="{{ maxVal }}" ng-model-options="{timezone: \'UTC\', allowInvalid: true}"/>',
|
|
918
|
+
)(scope);
|
|
919
|
+
scope.maxVal = "2013-01-01T00:00:00";
|
|
920
|
+
scope.value = "2012-01-01T00:00:00";
|
|
921
|
+
scope.$digest();
|
|
922
|
+
|
|
923
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
924
|
+
expect(scope.form.alias.$valid).toBeTruthy();
|
|
925
|
+
|
|
926
|
+
scope.value = "";
|
|
927
|
+
inputElm[0].value = "2013-01-01T00:00:00";
|
|
928
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
929
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
930
|
+
expect(scope.form.alias.$valid).toBeTruthy();
|
|
931
|
+
});
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
it("should validate even if max value changes on-the-fly", () => {
|
|
935
|
+
scope.max = "2013-01-01T01:02:00";
|
|
936
|
+
const inputElm = $compile(
|
|
937
|
+
'<input type="datetime-local" ng-model="value" name="alias" max="{{max}}" />',
|
|
938
|
+
)(scope);
|
|
939
|
+
|
|
940
|
+
inputElm[0].value = "2014-01-01T12:34:00";
|
|
941
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
942
|
+
|
|
943
|
+
scope.max = "2001-01-01T01:02:00";
|
|
944
|
+
scope.$digest();
|
|
945
|
+
|
|
946
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
947
|
+
|
|
948
|
+
scope.max = "2024-01-01T01:02:00";
|
|
949
|
+
scope.$digest();
|
|
950
|
+
|
|
951
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
it("should validate even if min value changes on-the-fly", () => {
|
|
955
|
+
scope.min = "2013-01-01T01:02:00";
|
|
956
|
+
const inputElm = $compile(
|
|
957
|
+
'<input type="datetime-local" ng-model="value" name="alias" min="{{min}}" />',
|
|
958
|
+
)(scope);
|
|
959
|
+
|
|
960
|
+
inputElm[0].value = "2010-01-01T12:34:00";
|
|
961
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
962
|
+
|
|
963
|
+
scope.min = "2014-01-01T01:02:00";
|
|
964
|
+
scope.$digest();
|
|
965
|
+
|
|
966
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
967
|
+
|
|
968
|
+
scope.min = "2009-01-01T01:02:00";
|
|
969
|
+
scope.$digest();
|
|
970
|
+
|
|
971
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
it("should validate even if ng-max value changes on-the-fly", () => {
|
|
975
|
+
scope.max = "2013-01-01T01:02:00";
|
|
976
|
+
const inputElm = $compile(
|
|
977
|
+
'<input type="datetime-local" ng-model="value" name="alias" ng-max="max" />',
|
|
978
|
+
)(scope);
|
|
979
|
+
|
|
980
|
+
inputElm[0].value = "2014-01-01T12:34:00";
|
|
981
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
982
|
+
|
|
983
|
+
scope.max = "2001-01-01T01:02:00";
|
|
984
|
+
scope.$digest();
|
|
985
|
+
|
|
986
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
987
|
+
|
|
988
|
+
scope.max = "2024-01-01T01:02:00";
|
|
989
|
+
scope.$digest();
|
|
990
|
+
|
|
991
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
it("should validate even if ng-min value changes on-the-fly", () => {
|
|
995
|
+
scope.min = "2013-01-01T01:02:00";
|
|
996
|
+
const inputElm = $compile(
|
|
997
|
+
'<input type="datetime-local" ng-model="value" name="alias" ng-min="min" />',
|
|
998
|
+
)(scope);
|
|
999
|
+
|
|
1000
|
+
inputElm[0].value = "2010-01-01T12:34:00";
|
|
1001
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1002
|
+
|
|
1003
|
+
scope.min = "2014-01-01T01:02:00";
|
|
1004
|
+
scope.$digest();
|
|
1005
|
+
|
|
1006
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1007
|
+
|
|
1008
|
+
scope.min = "2009-01-01T01:02:00";
|
|
1009
|
+
scope.$digest();
|
|
1010
|
+
|
|
1011
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1012
|
+
});
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
describe("time", () => {
|
|
1016
|
+
it("should throw if model is a Date object", () => {
|
|
1017
|
+
const inputElm = $compile('<input type="time" ng-model="lunchtime"/>')(
|
|
1018
|
+
scope,
|
|
1019
|
+
);
|
|
1020
|
+
expect(() => {
|
|
1021
|
+
scope.$apply(() => {
|
|
1022
|
+
scope.lunchtime = new Date(1970, 0, 1, 15, 41, 0, 500);
|
|
1023
|
+
});
|
|
1024
|
+
}).toThrowError(/datefmt/);
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
it("should set the view if the model is a valid String object.", () => {
|
|
1028
|
+
const inputElm = $compile(
|
|
1029
|
+
'<input type="time" ng-model="threeFortyOnePm"/>',
|
|
1030
|
+
)(scope);
|
|
1031
|
+
|
|
1032
|
+
scope.$apply(() => {
|
|
1033
|
+
scope.threeFortyOnePm = "15:41:00.500";
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
expect(inputElm.val()).toBe("15:41:00.500");
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
it("should bind to mode if a valid String object.", () => {
|
|
1040
|
+
const inputElm = $compile(
|
|
1041
|
+
'<input type="time" ng-model="threeFortyOnePm"/>',
|
|
1042
|
+
)(scope);
|
|
1043
|
+
|
|
1044
|
+
inputElm[0].value = "15:41:00.500";
|
|
1045
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1046
|
+
|
|
1047
|
+
expect(inputElm.val()).toBe("15:41:00.500");
|
|
1048
|
+
expect(scope.threeFortyOnePm).toBe("15:41:00.500");
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
it("should set the model to null if the view is invalid", () => {
|
|
1052
|
+
const inputElm = $compile('<input type="time" ng-model="breakMe"/>')(
|
|
1053
|
+
scope,
|
|
1054
|
+
);
|
|
1055
|
+
|
|
1056
|
+
scope.$apply(() => {
|
|
1057
|
+
scope.breakMe = "16:25:00.000";
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
expect(inputElm.val()).toBe("16:25:00.000");
|
|
1061
|
+
|
|
1062
|
+
inputElm[0].value = "stuff";
|
|
1063
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1064
|
+
expect(inputElm.val()).toBe("");
|
|
1065
|
+
expect(scope.breakMe).toBeNull();
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
it("should set blank if null", () => {
|
|
1069
|
+
const inputElm = $compile('<input type="time" ng-model="test" />')(
|
|
1070
|
+
scope,
|
|
1071
|
+
);
|
|
1072
|
+
|
|
1073
|
+
scope.$apply("test = null");
|
|
1074
|
+
|
|
1075
|
+
expect(scope.test).toBeNull();
|
|
1076
|
+
expect(inputElm.val()).toEqual("");
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
it("should set blank when no value specified", () => {
|
|
1080
|
+
const inputElm = $compile('<input type="time" ng-model="test" />')(
|
|
1081
|
+
scope,
|
|
1082
|
+
);
|
|
1083
|
+
|
|
1084
|
+
expect(inputElm.val()).toBe("");
|
|
1085
|
+
|
|
1086
|
+
scope.$apply("test = null");
|
|
1087
|
+
|
|
1088
|
+
expect(scope.test).toBeNull();
|
|
1089
|
+
expect(inputElm.val()).toBe("");
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
it("should parse empty string to null", () => {
|
|
1093
|
+
const inputElm = $compile('<input type="time" ng-model="test" />')(
|
|
1094
|
+
scope,
|
|
1095
|
+
);
|
|
1096
|
+
|
|
1097
|
+
scope.$apply(() => {
|
|
1098
|
+
scope.test = "16:25:00";
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
inputElm[0].value = "";
|
|
1102
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1103
|
+
expect(scope.test).toBeNull();
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
it("should allow to specify the milliseconds", () => {
|
|
1107
|
+
const inputElm = $compile('<input type="time" ng-model="value"" />')(
|
|
1108
|
+
scope,
|
|
1109
|
+
);
|
|
1110
|
+
|
|
1111
|
+
inputElm[0].value = "01:02:03.500";
|
|
1112
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1113
|
+
expect(scope.value).toBe("01:02:03.500");
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
it("should allow to specify single digit milliseconds", () => {
|
|
1117
|
+
const inputElm = $compile('<input type="time" ng-model="value"" />')(
|
|
1118
|
+
scope,
|
|
1119
|
+
);
|
|
1120
|
+
|
|
1121
|
+
inputElm[0].value = "01:02:03.4";
|
|
1122
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1123
|
+
expect(scope.value).toBe("01:02:03.4");
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
it("should allow to specify the seconds", () => {
|
|
1127
|
+
const inputElm = $compile('<input type="time" ng-model="value"" />')(
|
|
1128
|
+
scope,
|
|
1129
|
+
);
|
|
1130
|
+
|
|
1131
|
+
inputElm[0].value = "01:02:03";
|
|
1132
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1133
|
+
expect(scope.value).toBe("01:02:03");
|
|
1134
|
+
|
|
1135
|
+
scope.$apply(() => {
|
|
1136
|
+
scope.value = "01:02:03.000";
|
|
1137
|
+
});
|
|
1138
|
+
expect(inputElm.val()).toBe("01:02:03.000");
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
describe("min", () => {
|
|
1142
|
+
let inputElm;
|
|
1143
|
+
beforeEach(() => {
|
|
1144
|
+
scope.minVal = "09:30:00";
|
|
1145
|
+
let formElm = $compile(
|
|
1146
|
+
'<form name="form"><input type="time" ng-model="value" name="alias" min="{{ minVal }}" /></form>',
|
|
1147
|
+
)(scope);
|
|
1148
|
+
inputElm = formElm.find("input");
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
it("should invalidate", () => {
|
|
1152
|
+
inputElm[0].value = "01:02:03";
|
|
1153
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1154
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1155
|
+
expect(scope.value).toBeFalsy();
|
|
1156
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
it("should validate", () => {
|
|
1160
|
+
inputElm[0].value = "23:02:00";
|
|
1161
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1162
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1163
|
+
expect(scope.value).toBe("23:02:00");
|
|
1164
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
it("should revalidate when the min value changes", () => {
|
|
1168
|
+
inputElm[0].value = "23:02:00";
|
|
1169
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1170
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1171
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
1172
|
+
|
|
1173
|
+
scope.minVal = "23:55:00";
|
|
1174
|
+
scope.$digest();
|
|
1175
|
+
|
|
1176
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1177
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1180
|
+
it("should validate if min is empty", () => {
|
|
1181
|
+
scope.minVal = undefined;
|
|
1182
|
+
scope.value = "23:55:00";
|
|
1183
|
+
scope.$digest();
|
|
1184
|
+
|
|
1185
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
1186
|
+
});
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
describe("max", () => {
|
|
1190
|
+
let inputElm;
|
|
1191
|
+
beforeEach(() => {
|
|
1192
|
+
scope.maxVal = "22:30:00";
|
|
1193
|
+
let formElm = $compile(
|
|
1194
|
+
'<form name="form"><input type="time" ng-model="value" name="alias" max="{{ maxVal }}" /></form>',
|
|
1195
|
+
)(scope);
|
|
1196
|
+
inputElm = formElm.find("input");
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
it("should invalidate", () => {
|
|
1200
|
+
inputElm[0].value = "23:00:00";
|
|
1201
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1202
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1203
|
+
expect(scope.value).toBeFalsy();
|
|
1204
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
it("should validate", () => {
|
|
1208
|
+
inputElm[0].value = "05:30:00";
|
|
1209
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1210
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1211
|
+
expect(scope.value).toBe("05:30:00");
|
|
1212
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
it("should validate if max is empty", () => {
|
|
1216
|
+
scope.maxVal = undefined;
|
|
1217
|
+
scope.value = "05:30:00";
|
|
1218
|
+
scope.$digest();
|
|
1219
|
+
|
|
1220
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
1221
|
+
});
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
it("should validate even if max value changes on-the-fly", () => {
|
|
1225
|
+
scope.max = "04:02:00";
|
|
1226
|
+
const inputElm = $compile(
|
|
1227
|
+
'<input type="time" ng-model="value" name="alias" max="{{max}}" />',
|
|
1228
|
+
)(scope);
|
|
1229
|
+
|
|
1230
|
+
inputElm[0].value = "05:34:00";
|
|
1231
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1232
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1233
|
+
|
|
1234
|
+
scope.max = "06:34:00";
|
|
1235
|
+
scope.$digest();
|
|
1236
|
+
|
|
1237
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
it("should validate even if min value changes on-the-fly", () => {
|
|
1241
|
+
scope.min = "08:45:00";
|
|
1242
|
+
const inputElm = $compile(
|
|
1243
|
+
'<input type="time" ng-model="value" name="alias" min="{{min}}" />',
|
|
1244
|
+
)(scope);
|
|
1245
|
+
|
|
1246
|
+
inputElm[0].value = "06:15:00";
|
|
1247
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1248
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1249
|
+
|
|
1250
|
+
scope.min = "05:50:00";
|
|
1251
|
+
scope.$digest();
|
|
1252
|
+
|
|
1253
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
it("should validate even if ng-max value changes on-the-fly", () => {
|
|
1257
|
+
scope.max = "04:02:00";
|
|
1258
|
+
const inputElm = $compile(
|
|
1259
|
+
'<input type="time" ng-model="value" name="alias" ng-max="max" />',
|
|
1260
|
+
)(scope);
|
|
1261
|
+
|
|
1262
|
+
inputElm[0].value = "05:34:00";
|
|
1263
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1264
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1265
|
+
|
|
1266
|
+
scope.max = "06:34:00";
|
|
1267
|
+
scope.$digest();
|
|
1268
|
+
|
|
1269
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
it("should validate even if ng-min value changes on-the-fly", () => {
|
|
1273
|
+
scope.min = "08:45:00";
|
|
1274
|
+
const inputElm = $compile(
|
|
1275
|
+
'<input type="time" ng-model="value" name="alias" ng-min="min" />',
|
|
1276
|
+
)(scope);
|
|
1277
|
+
|
|
1278
|
+
inputElm[0].value = "06:15:00";
|
|
1279
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1280
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1281
|
+
|
|
1282
|
+
scope.min = "05:50:00";
|
|
1283
|
+
scope.$digest();
|
|
1284
|
+
|
|
1285
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1286
|
+
});
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
describe("date", () => {
|
|
1290
|
+
it("should throw if model is a Date object.", () => {
|
|
1291
|
+
const inputElm = $compile('<input type="date" ng-model="birthday"/>')(
|
|
1292
|
+
scope,
|
|
1293
|
+
);
|
|
1294
|
+
|
|
1295
|
+
expect(() => {
|
|
1296
|
+
scope.$apply(() => {
|
|
1297
|
+
scope.birthday = new Date("a");
|
|
1298
|
+
});
|
|
1299
|
+
}).toThrowError(/datefmt/);
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
it("should set the view when the model is an valid String", () => {
|
|
1303
|
+
const inputElm = $compile('<input type="date" ng-model="val"/>')(scope);
|
|
1304
|
+
|
|
1305
|
+
scope.$apply(() => {
|
|
1306
|
+
scope.val = "1977-10-22";
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
expect(inputElm.val()).toBe("1977-10-22");
|
|
1310
|
+
});
|
|
1311
|
+
|
|
1312
|
+
it("should bind to scope when the model is an valid String", () => {
|
|
1313
|
+
const inputElm = $compile('<input type="date" ng-model="val"/>')(scope);
|
|
1314
|
+
|
|
1315
|
+
inputElm[0].value = "1977-10-22";
|
|
1316
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1317
|
+
|
|
1318
|
+
expect(scope.val).toBe("1977-10-22");
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
it("should set the model to null if the view is invalid", () => {
|
|
1322
|
+
const inputElm = $compile('<input type="date" ng-model="arrMatey"/>')(
|
|
1323
|
+
scope,
|
|
1324
|
+
);
|
|
1325
|
+
|
|
1326
|
+
scope.$apply(() => {
|
|
1327
|
+
scope.arrMatey = "2014-09-14";
|
|
1328
|
+
});
|
|
1329
|
+
|
|
1330
|
+
expect(inputElm.val()).toBe("2014-09-14");
|
|
1331
|
+
|
|
1332
|
+
// set to text for browsers with date validation.
|
|
1333
|
+
inputElm[0].value = "1-2-3";
|
|
1334
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1335
|
+
expect(inputElm.val()).toBe("");
|
|
1336
|
+
expect(scope.arrMatey).toBeNull();
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
it("should render as blank if null", () => {
|
|
1340
|
+
const inputElm = $compile('<input type="date" ng-model="test" />')(
|
|
1341
|
+
scope,
|
|
1342
|
+
);
|
|
1343
|
+
|
|
1344
|
+
scope.$apply("test = null");
|
|
1345
|
+
|
|
1346
|
+
expect(scope.test).toBeNull();
|
|
1347
|
+
expect(inputElm.val()).toEqual("");
|
|
1348
|
+
});
|
|
1349
|
+
|
|
1350
|
+
it("should come up blank when no value specified", () => {
|
|
1351
|
+
const inputElm = $compile('<input type="date" ng-model="test" />')(
|
|
1352
|
+
scope,
|
|
1353
|
+
);
|
|
1354
|
+
|
|
1355
|
+
expect(inputElm.val()).toBe("");
|
|
1356
|
+
|
|
1357
|
+
scope.$apply("test = null");
|
|
1358
|
+
|
|
1359
|
+
expect(scope.test).toBeNull();
|
|
1360
|
+
expect(inputElm.val()).toBe("");
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
it("should parse empty string to null", () => {
|
|
1364
|
+
const inputElm = $compile('<input type="date" ng-model="test" />')(
|
|
1365
|
+
scope,
|
|
1366
|
+
);
|
|
1367
|
+
|
|
1368
|
+
scope.$apply(() => {
|
|
1369
|
+
scope.test = "2014-09-14";
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
inputElm[0].value = "";
|
|
1373
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1374
|
+
expect(scope.test).toBeNull();
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
describe("min", () => {
|
|
1378
|
+
it("should invalidate", () => {
|
|
1379
|
+
const formElm = $compile(
|
|
1380
|
+
'<form name="form"><input type="date" ng-model="value" name="alias" min="2000-01-01" /></form>',
|
|
1381
|
+
)(scope);
|
|
1382
|
+
const inputElm = formElm.find("input");
|
|
1383
|
+
inputElm[0].value = "1999-12-31";
|
|
1384
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1385
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1386
|
+
expect(scope.value).toBeFalsy();
|
|
1387
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
1388
|
+
});
|
|
1389
|
+
|
|
1390
|
+
it("should validate", () => {
|
|
1391
|
+
const formElm = $compile(
|
|
1392
|
+
'<form name="form"><input type="date" ng-model="value" name="alias" min="2000-01-01" /></form>',
|
|
1393
|
+
)(scope);
|
|
1394
|
+
const inputElm = formElm.find("input");
|
|
1395
|
+
inputElm[0].value = "2000-01-01";
|
|
1396
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1397
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1398
|
+
expect(scope.value).toBe("2000-01-01");
|
|
1399
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
1400
|
+
});
|
|
1401
|
+
|
|
1402
|
+
it("should validate if min is empty", () => {
|
|
1403
|
+
const formElm = $compile(
|
|
1404
|
+
'<form name="form"><input name="alias" ng-model="value" type="date" min >',
|
|
1405
|
+
)(scope);
|
|
1406
|
+
const inputElm = formElm.find("input");
|
|
1407
|
+
|
|
1408
|
+
scope.value = "2000-01-01";
|
|
1409
|
+
scope.$digest();
|
|
1410
|
+
|
|
1411
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
1412
|
+
});
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1415
|
+
describe("max", () => {
|
|
1416
|
+
it("should invalidate", () => {
|
|
1417
|
+
const formElm = $compile(
|
|
1418
|
+
'<form name="form"><input type="date" ng-model="value" name="alias" max="2019-01-01" /></form>',
|
|
1419
|
+
)(scope);
|
|
1420
|
+
const inputElm = formElm.find("input");
|
|
1421
|
+
|
|
1422
|
+
inputElm[0].value = "2019-12-31";
|
|
1423
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1424
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1425
|
+
expect(scope.value).toBeFalsy();
|
|
1426
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
1427
|
+
});
|
|
1428
|
+
|
|
1429
|
+
it("should validate", () => {
|
|
1430
|
+
const formElm = $compile(
|
|
1431
|
+
'<form name="form"><input type="date" ng-model="value" name="alias" max="2019-01-01" /></form>',
|
|
1432
|
+
)(scope);
|
|
1433
|
+
const inputElm = formElm.find("input");
|
|
1434
|
+
|
|
1435
|
+
inputElm[0].value = "2000-01-01";
|
|
1436
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1437
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1438
|
+
expect(scope.value).toBe("2000-01-01");
|
|
1439
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
1440
|
+
});
|
|
1441
|
+
|
|
1442
|
+
it("should parse ISO-based date strings as a valid max date value", () => {
|
|
1443
|
+
$compile(
|
|
1444
|
+
'<form name="form"><input name="myControl" type="date" max="{{ max }}" ng-model="value"></form>',
|
|
1445
|
+
)(scope);
|
|
1446
|
+
|
|
1447
|
+
scope.value = "2020-01-01";
|
|
1448
|
+
scope.max = new Date(2014, 10, 10, 0, 0, 0).toISOString();
|
|
1449
|
+
scope.$digest();
|
|
1450
|
+
|
|
1451
|
+
expect(scope.form.myControl.$error.max).toBeTruthy();
|
|
1452
|
+
});
|
|
1453
|
+
|
|
1454
|
+
it("should validate if max is empty", () => {
|
|
1455
|
+
$compile(
|
|
1456
|
+
'<form name="form"><input type="date" name="alias" ng-model="value" max /></form>',
|
|
1457
|
+
)(scope);
|
|
1458
|
+
|
|
1459
|
+
scope.value = "2020-01-01";
|
|
1460
|
+
scope.$digest();
|
|
1461
|
+
|
|
1462
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
1463
|
+
});
|
|
1464
|
+
});
|
|
1465
|
+
|
|
1466
|
+
it("should validate even if max value changes on-the-fly", () => {
|
|
1467
|
+
scope.max = "2013-01-01";
|
|
1468
|
+
const inputElm = $compile(
|
|
1469
|
+
'<input type="date" ng-model="value" name="alias" max="{{max}}" />',
|
|
1470
|
+
)(scope);
|
|
1471
|
+
|
|
1472
|
+
inputElm[0].value = "2014-01-01";
|
|
1473
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1474
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1475
|
+
|
|
1476
|
+
scope.max = "2001-01-01";
|
|
1477
|
+
scope.$digest();
|
|
1478
|
+
|
|
1479
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1480
|
+
|
|
1481
|
+
scope.max = "2021-01-01";
|
|
1482
|
+
scope.$digest();
|
|
1483
|
+
|
|
1484
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1485
|
+
});
|
|
1486
|
+
|
|
1487
|
+
it("should validate even if min value changes on-the-fly", () => {
|
|
1488
|
+
scope.min = "2013-01-01";
|
|
1489
|
+
const inputElm = $compile(
|
|
1490
|
+
'<input type="date" ng-model="value" name="alias" min="{{min}}" />',
|
|
1491
|
+
)(scope);
|
|
1492
|
+
|
|
1493
|
+
inputElm[0].value = "2010-01-01";
|
|
1494
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1495
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1496
|
+
|
|
1497
|
+
scope.min = "2014-01-01";
|
|
1498
|
+
scope.$digest();
|
|
1499
|
+
|
|
1500
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1501
|
+
|
|
1502
|
+
scope.min = "2009-01-01";
|
|
1503
|
+
scope.$digest();
|
|
1504
|
+
|
|
1505
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
it("should validate even if ng-max value changes on-the-fly", () => {
|
|
1509
|
+
scope.max = "2013-01-01";
|
|
1510
|
+
const inputElm = $compile(
|
|
1511
|
+
'<input type="date" ng-model="value" name="alias" ng-max="max" />',
|
|
1512
|
+
)(scope);
|
|
1513
|
+
|
|
1514
|
+
inputElm[0].value = "2014-01-01";
|
|
1515
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1516
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1517
|
+
|
|
1518
|
+
scope.max = "2001-01-01";
|
|
1519
|
+
scope.$digest();
|
|
1520
|
+
|
|
1521
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1522
|
+
|
|
1523
|
+
scope.max = "2021-01-01";
|
|
1524
|
+
scope.$digest();
|
|
1525
|
+
|
|
1526
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1529
|
+
it("should validate even if ng-min value changes on-the-fly", () => {
|
|
1530
|
+
scope.min = "2013-01-01";
|
|
1531
|
+
const inputElm = $compile(
|
|
1532
|
+
'<input type="date" ng-model="value" name="alias" ng-min="min" />',
|
|
1533
|
+
)(scope);
|
|
1534
|
+
|
|
1535
|
+
inputElm[0].value = "2010-01-01";
|
|
1536
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1537
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1538
|
+
|
|
1539
|
+
scope.min = "2014-01-01";
|
|
1540
|
+
scope.$digest();
|
|
1541
|
+
|
|
1542
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1543
|
+
|
|
1544
|
+
scope.min = "2009-01-01";
|
|
1545
|
+
scope.$digest();
|
|
1546
|
+
|
|
1547
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1548
|
+
});
|
|
1549
|
+
|
|
1550
|
+
it("should allow Date objects as valid ng-max values", () => {
|
|
1551
|
+
scope.max = new Date(2012, 1, 1, 1, 2, 0);
|
|
1552
|
+
const inputElm = $compile(
|
|
1553
|
+
'<input type="date" ng-model="value" name="alias" ng-max="max" />',
|
|
1554
|
+
)(scope);
|
|
1555
|
+
|
|
1556
|
+
inputElm[0].value = "2014-01-01";
|
|
1557
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1558
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1559
|
+
|
|
1560
|
+
scope.max = new Date(2013, 1, 1, 1, 2, 0);
|
|
1561
|
+
scope.$digest();
|
|
1562
|
+
|
|
1563
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1564
|
+
|
|
1565
|
+
scope.max = new Date(2014, 1, 1, 1, 2, 0);
|
|
1566
|
+
scope.$digest();
|
|
1567
|
+
|
|
1568
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1569
|
+
});
|
|
1570
|
+
|
|
1571
|
+
it("should allow Date objects as valid ng-min values", () => {
|
|
1572
|
+
scope.min = new Date(2013, 1, 1, 1, 2, 0);
|
|
1573
|
+
const inputElm = $compile(
|
|
1574
|
+
'<input type="date" ng-model="value" name="alias" ng-min="min" />',
|
|
1575
|
+
)(scope);
|
|
1576
|
+
|
|
1577
|
+
inputElm[0].value = "2010-01-01";
|
|
1578
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1579
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1580
|
+
|
|
1581
|
+
scope.min = new Date(2014, 1, 1, 1, 2, 0);
|
|
1582
|
+
scope.$digest();
|
|
1583
|
+
|
|
1584
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1585
|
+
|
|
1586
|
+
scope.min = new Date(2009, 1, 1, 1, 2, 0);
|
|
1587
|
+
scope.$digest();
|
|
1588
|
+
|
|
1589
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1590
|
+
});
|
|
1591
|
+
|
|
1592
|
+
describe("ISO_DATE_REGEXP", () => {
|
|
1593
|
+
[
|
|
1594
|
+
// Validate date
|
|
1595
|
+
["00:00:00.0000+01:01", false], // date must be specified
|
|
1596
|
+
["2010.06.15T00:00:00.0000+01:01", false], // date must use dash separator
|
|
1597
|
+
["x2010-06-15T00:00:00.0000+01:01", false], // invalid leading characters
|
|
1598
|
+
|
|
1599
|
+
// Validate year
|
|
1600
|
+
["2010-06-15T00:00:00.0000+01:01", true], // year has four or more digits
|
|
1601
|
+
["20100-06-15T00:00:00.0000+01:01", true], // year has four or more digits
|
|
1602
|
+
["-06-15T00:00:00.0000+01:01", false], // year has too few digits
|
|
1603
|
+
["2-06-15T00:00:00.0000+01:01", false], // year has too few digits
|
|
1604
|
+
["20-06-15T00:00:00.0000+01:01", false], // year has too few digits
|
|
1605
|
+
["201-06-15T00:00:00.0000+01:01", false], // year has too few digits
|
|
1606
|
+
|
|
1607
|
+
// Validate month
|
|
1608
|
+
["2010-01-15T00:00:00.0000+01:01", true], // month has two digits
|
|
1609
|
+
["2010--15T00:00:00.0000+01:01", false], // month has too few digits
|
|
1610
|
+
["2010-0-15T00:00:00.0000+01:01", false], // month has too few digits
|
|
1611
|
+
["2010-1-15T00:00:00.0000+01:01", false], // month has too few digits
|
|
1612
|
+
["2010-111-15T00:00:00.0000+01:01", false], // month has too many digits
|
|
1613
|
+
["2010-22-15T00:00:00.0000+01:01", false], // month is too large
|
|
1614
|
+
|
|
1615
|
+
// Validate day
|
|
1616
|
+
["2010-01-01T00:00:00.0000+01:01", true], // day has two digits
|
|
1617
|
+
["2010-01-T00:00:00.0000+01:01", false], // day has too few digits
|
|
1618
|
+
["2010-01-1T00:00:00.0000+01:01", false], // day has too few digits
|
|
1619
|
+
["2010-01-200T00:00:00.0000+01:01", false], // day has too many digits
|
|
1620
|
+
["2010-01-41T00:00:00.0000+01:01", false], // day is too large
|
|
1621
|
+
|
|
1622
|
+
// Validate time
|
|
1623
|
+
["2010-01-01", false], // time must be specified
|
|
1624
|
+
["2010-01-0101:00:00.0000+01:01", false], // missing date time separator
|
|
1625
|
+
["2010-01-01V01:00:00.0000+01:01", false], // invalid date time separator
|
|
1626
|
+
["2010-01-01T01-00-00.0000+01:01", false], // time must use colon separator
|
|
1627
|
+
|
|
1628
|
+
// Validate hour
|
|
1629
|
+
["2010-01-01T01:00:00.0000+01:01", true], // hour has two digits
|
|
1630
|
+
["2010-01-01T-01:00:00.0000+01:01", false], // hour must be positive
|
|
1631
|
+
["2010-01-01T:00:00.0000+01:01", false], // hour has too few digits
|
|
1632
|
+
["2010-01-01T1:00:00.0000+01:01", false], // hour has too few digits
|
|
1633
|
+
["2010-01-01T220:00:00.0000+01:01", false], // hour has too many digits
|
|
1634
|
+
["2010-01-01T32:00:00.0000+01:01", false], // hour is too large
|
|
1635
|
+
|
|
1636
|
+
// Validate minutes
|
|
1637
|
+
["2010-01-01T01:00:00.0000+01:01", true], // minute has two digits
|
|
1638
|
+
["2010-01-01T01:-00:00.0000+01:01", false], // minute must be positive
|
|
1639
|
+
["2010-01-01T01::00.0000+01:01", false], // minute has too few digits
|
|
1640
|
+
["2010-01-01T01:0:00.0000+01:01", false], // minute has too few digits
|
|
1641
|
+
["2010-01-01T01:100:00.0000+01:01", false], // minute has too many digits
|
|
1642
|
+
["2010-01-01T01:60:00.0000+01:01", false], // minute is too large
|
|
1643
|
+
|
|
1644
|
+
// Validate seconds
|
|
1645
|
+
["2010-01-01T01:00:00.0000+01:01", true], // second has two digits
|
|
1646
|
+
["2010-01-01T01:00:-00.0000+01:01", false], // second must be positive
|
|
1647
|
+
["2010-01-01T01:00:.0000+01:01", false], // second has too few digits
|
|
1648
|
+
["2010-01-01T01:00:0.0000+01:01", false], // second has too few digits
|
|
1649
|
+
["2010-01-01T01:00:100.0000+01:01", false], // second has too many digits
|
|
1650
|
+
["2010-01-01T01:00:60.0000+01:01", false], // second is too large
|
|
1651
|
+
|
|
1652
|
+
// Validate milliseconds
|
|
1653
|
+
["2010-01-01T01:00:00+01:01", false], // millisecond must be specified
|
|
1654
|
+
["2010-01-01T01:00:00.-0000+01:01", false], // millisecond must be positive
|
|
1655
|
+
["2010-01-01T01:00:00:0000+01:01", false], // millisecond must use period separator
|
|
1656
|
+
["2010-01-01T01:00:00.+01:01", false], // millisecond has too few digits
|
|
1657
|
+
|
|
1658
|
+
// Validate timezone
|
|
1659
|
+
["2010-06-15T00:00:00.0000", false], // timezone must be specified
|
|
1660
|
+
|
|
1661
|
+
// Validate timezone offset
|
|
1662
|
+
["2010-06-15T00:00:00.0000+01:01", true], // timezone offset can be positive hours and minutes
|
|
1663
|
+
["2010-06-15T00:00:00.0000-01:01", true], // timezone offset can be negative hours and minutes
|
|
1664
|
+
["2010-06-15T00:00:00.0000~01:01", false], // timezone has postive/negative indicator
|
|
1665
|
+
["2010-06-15T00:00:00.000001:01", false], // timezone has postive/negative indicator
|
|
1666
|
+
["2010-06-15T00:00:00.0000+00:01Z", false], // timezone invalid trailing characters
|
|
1667
|
+
["2010-06-15T00:00:00.0000+00:01 ", false], // timezone invalid trailing characters
|
|
1668
|
+
|
|
1669
|
+
// Validate timezone hour offset
|
|
1670
|
+
["2010-06-15T00:00:00.0000+:01", false], // timezone hour offset has too few digits
|
|
1671
|
+
["2010-06-15T00:00:00.0000+0:01", false], // timezone hour offset has too few digits
|
|
1672
|
+
["2010-06-15T00:00:00.0000+211:01", false], // timezone hour offset too many digits
|
|
1673
|
+
["2010-06-15T00:00:00.0000+31:01", false], // timezone hour offset value too large
|
|
1674
|
+
|
|
1675
|
+
// Validate timezone minute offset
|
|
1676
|
+
["2010-06-15T00:00:00.0000+00:-01", false], // timezone minute offset must be positive
|
|
1677
|
+
["2010-06-15T00:00:00.0000+00.01", false], // timezone minute offset must use colon separator
|
|
1678
|
+
["2010-06-15T00:00:00.0000+0101", false], // timezone minute offset must use colon separator
|
|
1679
|
+
["2010-06-15T00:00:00.0000+010", false], // timezone minute offset must use colon separator
|
|
1680
|
+
["2010-06-15T00:00:00.0000+00", false], // timezone minute offset has too few digits
|
|
1681
|
+
["2010-06-15T00:00:00.0000+00:", false], // timezone minute offset has too few digits
|
|
1682
|
+
["2010-06-15T00:00:00.0000+00:0", false], // timezone minute offset has too few digits
|
|
1683
|
+
["2010-06-15T00:00:00.0000+00:211", false], // timezone minute offset has too many digits
|
|
1684
|
+
["2010-06-15T00:00:00.0000+01010", false], // timezone minute offset has too many digits
|
|
1685
|
+
["2010-06-15T00:00:00.0000+00:61", false], // timezone minute offset is too large
|
|
1686
|
+
|
|
1687
|
+
// Validate timezone UTC
|
|
1688
|
+
["2010-06-15T00:00:00.0000Z", true], // UTC timezone can be indicated with Z
|
|
1689
|
+
["2010-06-15T00:00:00.0000K", false], // UTC timezone indicator is invalid
|
|
1690
|
+
["2010-06-15T00:00:00.0000 Z", false], // UTC timezone indicator has extra space
|
|
1691
|
+
["2010-06-15T00:00:00.0000ZZ", false], // UTC timezone indicator invalid trailing characters
|
|
1692
|
+
["2010-06-15T00:00:00.0000Z ", false], // UTC timezone indicator invalid trailing characters
|
|
1693
|
+
].forEach((item) => {
|
|
1694
|
+
it("should validate date: $prop", () => {
|
|
1695
|
+
const date = item[0];
|
|
1696
|
+
const valid = item[1];
|
|
1697
|
+
|
|
1698
|
+
expect(ISO_DATE_REGEXP.test(date)).toBe(valid);
|
|
1699
|
+
});
|
|
1700
|
+
});
|
|
1701
|
+
});
|
|
1702
|
+
});
|
|
1703
|
+
|
|
1704
|
+
describe("number", () => {
|
|
1705
|
+
// Helpers for min / max tests
|
|
1706
|
+
const subtract = function (value) {
|
|
1707
|
+
return value - 5;
|
|
1708
|
+
};
|
|
1709
|
+
|
|
1710
|
+
const add = function (value) {
|
|
1711
|
+
return value + 5;
|
|
1712
|
+
};
|
|
1713
|
+
|
|
1714
|
+
it("should reset the model if view is invalid", () => {
|
|
1715
|
+
const inputElm = $compile('<input type="number" ng-model="age"/>')(
|
|
1716
|
+
scope,
|
|
1717
|
+
);
|
|
1718
|
+
|
|
1719
|
+
scope.$apply("age = 123");
|
|
1720
|
+
expect(inputElm.val()).toBe("123");
|
|
1721
|
+
|
|
1722
|
+
inputElm[0].value = "123X";
|
|
1723
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1724
|
+
|
|
1725
|
+
expect(inputElm.val()).toBe("");
|
|
1726
|
+
expect(scope.age).toBeNull();
|
|
1727
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1728
|
+
});
|
|
1729
|
+
|
|
1730
|
+
it("should render as blank if null", () => {
|
|
1731
|
+
const inputElm = $compile('<input type="number" ng-model="age" />')(
|
|
1732
|
+
scope,
|
|
1733
|
+
);
|
|
1734
|
+
|
|
1735
|
+
scope.$apply("age = null");
|
|
1736
|
+
|
|
1737
|
+
expect(scope.age).toBeNull();
|
|
1738
|
+
expect(inputElm.val()).toEqual("");
|
|
1739
|
+
});
|
|
1740
|
+
|
|
1741
|
+
it("should come up blank when no value specified", () => {
|
|
1742
|
+
const inputElm = $compile('<input type="number" ng-model="age" />')(
|
|
1743
|
+
scope,
|
|
1744
|
+
);
|
|
1745
|
+
|
|
1746
|
+
expect(inputElm.val()).toBe("");
|
|
1747
|
+
|
|
1748
|
+
scope.$apply("age = null");
|
|
1749
|
+
|
|
1750
|
+
expect(scope.age).toBeNull();
|
|
1751
|
+
expect(inputElm.val()).toBe("");
|
|
1752
|
+
});
|
|
1753
|
+
|
|
1754
|
+
it("should parse empty string to null", () => {
|
|
1755
|
+
const inputElm = $compile('<input type="number" ng-model="age" />')(
|
|
1756
|
+
scope,
|
|
1757
|
+
);
|
|
1758
|
+
|
|
1759
|
+
scope.$apply("age = 10");
|
|
1760
|
+
|
|
1761
|
+
inputElm[0].value = "";
|
|
1762
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1763
|
+
|
|
1764
|
+
expect(scope.age).toBeNull();
|
|
1765
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1768
|
+
it("should only invalidate the model if suffering from bad input when the data is parsed", () => {
|
|
1769
|
+
const inputElm = $compile('<input type="number" ng-model="age" />')(
|
|
1770
|
+
scope,
|
|
1771
|
+
);
|
|
1772
|
+
|
|
1773
|
+
expect(scope.age).toBeUndefined();
|
|
1774
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1775
|
+
|
|
1776
|
+
inputElm[0].value = "this-will-fail-because-of-the-badInput-flag";
|
|
1777
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1778
|
+
|
|
1779
|
+
expect(scope.age).toBeNull();
|
|
1780
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1781
|
+
});
|
|
1782
|
+
|
|
1783
|
+
it("should validate number if transition from bad input to empty string", () => {
|
|
1784
|
+
const inputElm = $compile('<input type="number" ng-model="age" />')(
|
|
1785
|
+
scope,
|
|
1786
|
+
);
|
|
1787
|
+
inputElm[0].value = "10a";
|
|
1788
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1789
|
+
|
|
1790
|
+
inputElm[0].value = "";
|
|
1791
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1792
|
+
expect(scope.age).toBeNull();
|
|
1793
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1794
|
+
});
|
|
1795
|
+
|
|
1796
|
+
it("should validate with undefined viewValue when $validate() called", () => {
|
|
1797
|
+
const inputElm = $compile(
|
|
1798
|
+
'<form name="form"><input type="number" name="alias" ng-model="value" /></form>',
|
|
1799
|
+
)(scope);
|
|
1800
|
+
|
|
1801
|
+
scope.form.alias.$validate();
|
|
1802
|
+
|
|
1803
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1804
|
+
expect(scope.form.alias.$error.number).toBeUndefined();
|
|
1805
|
+
});
|
|
1806
|
+
|
|
1807
|
+
it("should throw if the model value is not a number", () => {
|
|
1808
|
+
scope.value = "one";
|
|
1809
|
+
expect(() => {
|
|
1810
|
+
$compile('<input type="number" ng-model="value" />')(scope);
|
|
1811
|
+
scope.$digest();
|
|
1812
|
+
}).toThrowError(/numfmt/);
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1815
|
+
it("should parse exponential notation", () => {
|
|
1816
|
+
const formElm = $compile(
|
|
1817
|
+
'<form name="form"><input type="number" name="alias" ng-model="value" /></form>',
|
|
1818
|
+
)(scope);
|
|
1819
|
+
const inputElm = formElm.find("input");
|
|
1820
|
+
|
|
1821
|
+
// #.###e+##
|
|
1822
|
+
scope.form.alias.$setViewValue("1.23214124123412412e+26");
|
|
1823
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1824
|
+
expect(scope.value).toBe(1.23214124123412412e26);
|
|
1825
|
+
|
|
1826
|
+
// #.###e##
|
|
1827
|
+
scope.form.alias.$setViewValue("1.23214124123412412e26");
|
|
1828
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1829
|
+
expect(scope.value).toBe(1.23214124123412412e26);
|
|
1830
|
+
|
|
1831
|
+
// #.###e-##
|
|
1832
|
+
scope.form.alias.$setViewValue("1.23214124123412412e-26");
|
|
1833
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1834
|
+
expect(scope.value).toBe(1.23214124123412412e-26);
|
|
1835
|
+
|
|
1836
|
+
// ####e+##
|
|
1837
|
+
scope.form.alias.$setViewValue("123214124123412412e+26");
|
|
1838
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1839
|
+
expect(scope.value).toBe(123214124123412412e26);
|
|
1840
|
+
|
|
1841
|
+
// ####e##
|
|
1842
|
+
scope.form.alias.$setViewValue("123214124123412412e26");
|
|
1843
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1844
|
+
expect(scope.value).toBe(123214124123412412e26);
|
|
1845
|
+
|
|
1846
|
+
// ####e-##
|
|
1847
|
+
scope.form.alias.$setViewValue("123214124123412412e-26");
|
|
1848
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1849
|
+
expect(scope.value).toBe(123214124123412412e-26);
|
|
1850
|
+
|
|
1851
|
+
// #.###E+##
|
|
1852
|
+
scope.form.alias.$setViewValue("1.23214124123412412E+26");
|
|
1853
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1854
|
+
expect(scope.value).toBe(1.23214124123412412e26);
|
|
1855
|
+
|
|
1856
|
+
// #.###E##
|
|
1857
|
+
scope.form.alias.$setViewValue("1.23214124123412412E26");
|
|
1858
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1859
|
+
expect(scope.value).toBe(1.23214124123412412e26);
|
|
1860
|
+
|
|
1861
|
+
// #.###E-##
|
|
1862
|
+
scope.form.alias.$setViewValue("1.23214124123412412E-26");
|
|
1863
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1864
|
+
expect(scope.value).toBe(1.23214124123412412e-26);
|
|
1865
|
+
|
|
1866
|
+
// ####E+##
|
|
1867
|
+
scope.form.alias.$setViewValue("123214124123412412E+26");
|
|
1868
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1869
|
+
expect(scope.value).toBe(123214124123412412e26);
|
|
1870
|
+
|
|
1871
|
+
// ####E##
|
|
1872
|
+
scope.form.alias.$setViewValue("123214124123412412E26");
|
|
1873
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1874
|
+
expect(scope.value).toBe(123214124123412412e26);
|
|
1875
|
+
|
|
1876
|
+
// ####E-##
|
|
1877
|
+
scope.form.alias.$setViewValue("123214124123412412E-26");
|
|
1878
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1879
|
+
expect(scope.value).toBe(123214124123412412e-26);
|
|
1880
|
+
});
|
|
1881
|
+
|
|
1882
|
+
it("should bind to scope if input is valid", () => {
|
|
1883
|
+
const inputElm = $compile('<input type="number" ng-model="age"/>')(
|
|
1884
|
+
scope,
|
|
1885
|
+
);
|
|
1886
|
+
const ctrl = inputElm.controller("ngModel");
|
|
1887
|
+
|
|
1888
|
+
let previousParserFail = false;
|
|
1889
|
+
let laterParserFail = false;
|
|
1890
|
+
|
|
1891
|
+
ctrl.$parsers.unshift((value) =>
|
|
1892
|
+
previousParserFail ? undefined : value,
|
|
1893
|
+
);
|
|
1894
|
+
|
|
1895
|
+
ctrl.$parsers.push((value) => (laterParserFail ? undefined : value));
|
|
1896
|
+
|
|
1897
|
+
inputElm[0].value = "123X";
|
|
1898
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1899
|
+
expect(inputElm.val()).toBe("");
|
|
1900
|
+
|
|
1901
|
+
expect(scope.age).toBeNull();
|
|
1902
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1903
|
+
expect(ctrl.$error.number).toBeUndefined();
|
|
1904
|
+
|
|
1905
|
+
inputElm[0].value = "123";
|
|
1906
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1907
|
+
expect(inputElm.val()).toBe("123");
|
|
1908
|
+
expect(scope.age).toBe(123);
|
|
1909
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1910
|
+
expect(ctrl.$error.number).toBeFalsy();
|
|
1911
|
+
expect(ctrl.$error.parse).toBe(undefined);
|
|
1912
|
+
});
|
|
1913
|
+
|
|
1914
|
+
describe("min", () => {
|
|
1915
|
+
it("should validate", () => {
|
|
1916
|
+
const formElm = $compile(
|
|
1917
|
+
'<form name="form"><input type="number" ng-model="value" name="alias" min="10" /></form>',
|
|
1918
|
+
)(scope);
|
|
1919
|
+
const inputElm = formElm.find("input");
|
|
1920
|
+
|
|
1921
|
+
inputElm[0].value = "1";
|
|
1922
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1923
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1924
|
+
expect(scope.value).toBeFalsy();
|
|
1925
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
1926
|
+
|
|
1927
|
+
inputElm[0].value = "100";
|
|
1928
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1929
|
+
|
|
1930
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1931
|
+
expect(scope.value).toBe(100);
|
|
1932
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
1933
|
+
});
|
|
1934
|
+
|
|
1935
|
+
it("should validate against the viewValue", () => {
|
|
1936
|
+
const formElm = $compile(
|
|
1937
|
+
'<form name="form"><input type="number" ng-model-options="{allowInvalid: true}" ng-model="value" name="alias" min="10" /></form>',
|
|
1938
|
+
)(scope);
|
|
1939
|
+
const inputElm = formElm.find("input");
|
|
1940
|
+
const ngModelCtrl = inputElm.controller("ngModel");
|
|
1941
|
+
ngModelCtrl.$parsers.push(subtract);
|
|
1942
|
+
|
|
1943
|
+
inputElm[0].value = "10";
|
|
1944
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1945
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1946
|
+
expect(scope.value).toBe(5);
|
|
1947
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
1948
|
+
|
|
1949
|
+
ngModelCtrl.$parsers.pop();
|
|
1950
|
+
ngModelCtrl.$parsers.push(add);
|
|
1951
|
+
|
|
1952
|
+
inputElm[0].value = "5";
|
|
1953
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1954
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1955
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
1956
|
+
expect(scope.value).toBe(10);
|
|
1957
|
+
});
|
|
1958
|
+
|
|
1959
|
+
it("should validate even if min value changes on-the-fly", () => {
|
|
1960
|
+
scope.min = undefined;
|
|
1961
|
+
const inputElm = $compile(
|
|
1962
|
+
'<input type="number" ng-model="value" name="alias" min="{{min}}" />',
|
|
1963
|
+
)(scope);
|
|
1964
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1965
|
+
|
|
1966
|
+
inputElm[0].value = "15";
|
|
1967
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
1968
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1969
|
+
|
|
1970
|
+
scope.min = 10;
|
|
1971
|
+
scope.$digest();
|
|
1972
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1973
|
+
|
|
1974
|
+
scope.min = 20;
|
|
1975
|
+
scope.$digest();
|
|
1976
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1977
|
+
|
|
1978
|
+
scope.min = null;
|
|
1979
|
+
scope.$digest();
|
|
1980
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1981
|
+
|
|
1982
|
+
scope.min = "20";
|
|
1983
|
+
scope.$digest();
|
|
1984
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
1985
|
+
|
|
1986
|
+
scope.min = "abc";
|
|
1987
|
+
scope.$digest();
|
|
1988
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
1989
|
+
});
|
|
1990
|
+
});
|
|
1991
|
+
|
|
1992
|
+
describe("ngMin", () => {
|
|
1993
|
+
it("should validate", () => {
|
|
1994
|
+
const formElm = $compile(
|
|
1995
|
+
'<form name="form"><input type="number" ng-model="value" name="alias" ng-min="50" /></form>',
|
|
1996
|
+
)(scope);
|
|
1997
|
+
const inputElm = formElm.find("input");
|
|
1998
|
+
|
|
1999
|
+
inputElm[0].value = "1";
|
|
2000
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2001
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2002
|
+
expect(scope.value).toBeFalsy();
|
|
2003
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
2004
|
+
|
|
2005
|
+
inputElm[0].value = "100";
|
|
2006
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2007
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2008
|
+
expect(scope.value).toBe(100);
|
|
2009
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
2010
|
+
});
|
|
2011
|
+
|
|
2012
|
+
it("should validate against the viewValue", () => {
|
|
2013
|
+
const formElm = $compile(
|
|
2014
|
+
'<form name="form"><input type="number" ng-model="value" name="alias" ng-min="10" /></form>',
|
|
2015
|
+
)(scope);
|
|
2016
|
+
const inputElm = formElm.find("input");
|
|
2017
|
+
const ngModelCtrl = inputElm.controller("ngModel");
|
|
2018
|
+
ngModelCtrl.$parsers.push(subtract);
|
|
2019
|
+
|
|
2020
|
+
inputElm[0].value = "10";
|
|
2021
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2022
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2023
|
+
expect(scope.value).toBe(5);
|
|
2024
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
2025
|
+
|
|
2026
|
+
ngModelCtrl.$parsers.pop();
|
|
2027
|
+
ngModelCtrl.$parsers.push(add);
|
|
2028
|
+
|
|
2029
|
+
inputElm[0].value = "5";
|
|
2030
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2031
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2032
|
+
expect(scope.form.alias.$error.min).toBeTruthy();
|
|
2033
|
+
expect(scope.value).toBe(undefined);
|
|
2034
|
+
});
|
|
2035
|
+
|
|
2036
|
+
it("should validate even if the ngMin value changes on-the-fly", () => {
|
|
2037
|
+
scope.min = undefined;
|
|
2038
|
+
const inputElm = $compile(
|
|
2039
|
+
'<input type="number" ng-model="value" name="alias" ng-min="min" />',
|
|
2040
|
+
)(scope);
|
|
2041
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2042
|
+
|
|
2043
|
+
inputElm[0].value = "15";
|
|
2044
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2045
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2046
|
+
|
|
2047
|
+
scope.min = 10;
|
|
2048
|
+
scope.$digest();
|
|
2049
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2050
|
+
|
|
2051
|
+
scope.min = 20;
|
|
2052
|
+
scope.$digest();
|
|
2053
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2054
|
+
|
|
2055
|
+
scope.min = null;
|
|
2056
|
+
scope.$digest();
|
|
2057
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2058
|
+
|
|
2059
|
+
scope.min = "20";
|
|
2060
|
+
scope.$digest();
|
|
2061
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2062
|
+
|
|
2063
|
+
scope.min = "abc";
|
|
2064
|
+
scope.$digest();
|
|
2065
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2066
|
+
});
|
|
2067
|
+
});
|
|
2068
|
+
|
|
2069
|
+
describe("max", () => {
|
|
2070
|
+
it("should validate", () => {
|
|
2071
|
+
const formElm = $compile(
|
|
2072
|
+
'<form name="form"><input type="number" ng-model="value" name="alias" max="10" /></form>',
|
|
2073
|
+
)(scope);
|
|
2074
|
+
const inputElm = formElm.find("input");
|
|
2075
|
+
|
|
2076
|
+
inputElm[0].value = "20";
|
|
2077
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2078
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2079
|
+
expect(scope.value).toBeUndefined();
|
|
2080
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
2081
|
+
|
|
2082
|
+
inputElm[0].value = "0";
|
|
2083
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2084
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2085
|
+
expect(scope.value).toBe(0);
|
|
2086
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
2087
|
+
});
|
|
2088
|
+
|
|
2089
|
+
it("should validate against the viewValue", () => {
|
|
2090
|
+
const formElm = $compile(
|
|
2091
|
+
'<form name="form"><input type="number"' +
|
|
2092
|
+
'ng-model-options="{allowInvalid: true}" ng-model="value" name="alias" max="10" /></form>',
|
|
2093
|
+
)(scope);
|
|
2094
|
+
const inputElm = formElm.find("input");
|
|
2095
|
+
const ngModelCtrl = inputElm.controller("ngModel");
|
|
2096
|
+
ngModelCtrl.$parsers.push(add);
|
|
2097
|
+
|
|
2098
|
+
inputElm[0].value = "10";
|
|
2099
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2100
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2101
|
+
expect(scope.value).toBe(15);
|
|
2102
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
2103
|
+
|
|
2104
|
+
ngModelCtrl.$parsers.pop();
|
|
2105
|
+
ngModelCtrl.$parsers.push(subtract);
|
|
2106
|
+
|
|
2107
|
+
inputElm[0].value = "15";
|
|
2108
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2109
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2110
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
2111
|
+
expect(scope.value).toBe(10);
|
|
2112
|
+
});
|
|
2113
|
+
|
|
2114
|
+
it("should validate even if max value changes on-the-fly", () => {
|
|
2115
|
+
scope.max = undefined;
|
|
2116
|
+
const inputElm = $compile(
|
|
2117
|
+
'<input type="number" ng-model="value" name="alias" max="{{max}}" />',
|
|
2118
|
+
)(scope);
|
|
2119
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2120
|
+
|
|
2121
|
+
inputElm[0].value = "5";
|
|
2122
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2123
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2124
|
+
|
|
2125
|
+
scope.max = 10;
|
|
2126
|
+
scope.$digest();
|
|
2127
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2128
|
+
|
|
2129
|
+
scope.max = 0;
|
|
2130
|
+
scope.$digest();
|
|
2131
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2132
|
+
|
|
2133
|
+
scope.max = null;
|
|
2134
|
+
scope.$digest();
|
|
2135
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2136
|
+
|
|
2137
|
+
scope.max = "4";
|
|
2138
|
+
scope.$digest();
|
|
2139
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2140
|
+
|
|
2141
|
+
scope.max = "abc";
|
|
2142
|
+
scope.$digest();
|
|
2143
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2144
|
+
});
|
|
2145
|
+
});
|
|
2146
|
+
|
|
2147
|
+
describe("ngMax", () => {
|
|
2148
|
+
it("should validate", () => {
|
|
2149
|
+
const formElm = $compile(
|
|
2150
|
+
'<form name="form"><input type="number" ng-model="value" name="alias" ng-max="5" /></form>',
|
|
2151
|
+
)(scope);
|
|
2152
|
+
const inputElm = formElm.find("input");
|
|
2153
|
+
|
|
2154
|
+
inputElm[0].value = "20";
|
|
2155
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2156
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2157
|
+
expect(scope.value).toBeUndefined();
|
|
2158
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
2159
|
+
|
|
2160
|
+
inputElm[0].value = "0";
|
|
2161
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2162
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2163
|
+
expect(scope.value).toBe(0);
|
|
2164
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
2165
|
+
});
|
|
2166
|
+
|
|
2167
|
+
it("should validate against the viewValue", () => {
|
|
2168
|
+
const formElm = $compile(
|
|
2169
|
+
'<form name="form"><input type="number"' +
|
|
2170
|
+
'ng-model-options="{allowInvalid: true}" ng-model="value" name="alias" ng-max="10" /></form>',
|
|
2171
|
+
)(scope);
|
|
2172
|
+
const inputElm = formElm.find("input");
|
|
2173
|
+
const ngModelCtrl = inputElm.controller("ngModel");
|
|
2174
|
+
ngModelCtrl.$parsers.push(add);
|
|
2175
|
+
|
|
2176
|
+
inputElm[0].value = "10";
|
|
2177
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2178
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2179
|
+
expect(scope.value).toBe(15);
|
|
2180
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
2181
|
+
|
|
2182
|
+
ngModelCtrl.$parsers.pop();
|
|
2183
|
+
ngModelCtrl.$parsers.push(subtract);
|
|
2184
|
+
|
|
2185
|
+
inputElm[0].value = "15";
|
|
2186
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2187
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2188
|
+
expect(scope.form.alias.$error.max).toBeTruthy();
|
|
2189
|
+
expect(scope.value).toBe(10);
|
|
2190
|
+
});
|
|
2191
|
+
|
|
2192
|
+
it("should validate even if the ngMax value changes on-the-fly", () => {
|
|
2193
|
+
scope.max = undefined;
|
|
2194
|
+
const inputElm = $compile(
|
|
2195
|
+
'<input type="number" ng-model="value" name="alias" ng-max="max" />',
|
|
2196
|
+
)(scope);
|
|
2197
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2198
|
+
|
|
2199
|
+
inputElm[0].value = "5";
|
|
2200
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2201
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2202
|
+
|
|
2203
|
+
scope.max = 10;
|
|
2204
|
+
scope.$digest();
|
|
2205
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2206
|
+
|
|
2207
|
+
scope.max = 0;
|
|
2208
|
+
scope.$digest();
|
|
2209
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2210
|
+
|
|
2211
|
+
scope.max = null;
|
|
2212
|
+
scope.$digest();
|
|
2213
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2214
|
+
|
|
2215
|
+
scope.max = "4";
|
|
2216
|
+
scope.$digest();
|
|
2217
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2218
|
+
|
|
2219
|
+
scope.max = "abc";
|
|
2220
|
+
scope.$digest();
|
|
2221
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2222
|
+
});
|
|
2223
|
+
});
|
|
2224
|
+
|
|
2225
|
+
forEach(
|
|
2226
|
+
{
|
|
2227
|
+
step: 'step="{{step}}"',
|
|
2228
|
+
ngStep: 'ng-step="step"',
|
|
2229
|
+
},
|
|
2230
|
+
(attrHtml, attrName) => {
|
|
2231
|
+
describe(attrName, () => {
|
|
2232
|
+
it("should validate", () => {
|
|
2233
|
+
scope.step = 10;
|
|
2234
|
+
scope.value = 20;
|
|
2235
|
+
const formElm = $compile(
|
|
2236
|
+
`<form name="form"><input type="number" ng-model="value" name="alias" ${attrHtml} /></form>`,
|
|
2237
|
+
)(scope);
|
|
2238
|
+
const inputElm = formElm.find("input");
|
|
2239
|
+
scope.$digest();
|
|
2240
|
+
expect(inputElm.val()).toBe("20");
|
|
2241
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2242
|
+
expect(scope.value).toBe(20);
|
|
2243
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
2244
|
+
|
|
2245
|
+
inputElm[0].value = "18";
|
|
2246
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2247
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2248
|
+
expect(inputElm.val()).toBe("18");
|
|
2249
|
+
expect(scope.value).toBeUndefined();
|
|
2250
|
+
expect(scope.form.alias.$error.step).toBeTruthy();
|
|
2251
|
+
|
|
2252
|
+
inputElm[0].value = "10";
|
|
2253
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2254
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2255
|
+
expect(inputElm.val()).toBe("10");
|
|
2256
|
+
expect(scope.value).toBe(10);
|
|
2257
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
2258
|
+
|
|
2259
|
+
scope.$apply("value = 12");
|
|
2260
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2261
|
+
expect(inputElm.val()).toBe("12");
|
|
2262
|
+
expect(scope.value).toBe(12);
|
|
2263
|
+
expect(scope.form.alias.$error.step).toBeTruthy();
|
|
2264
|
+
});
|
|
2265
|
+
|
|
2266
|
+
it("should validate even if the step value changes on-the-fly", () => {
|
|
2267
|
+
scope.step = 10;
|
|
2268
|
+
const formElm = $compile(
|
|
2269
|
+
`<form name="form"><input type="number" ng-model="value" name="alias" ${attrHtml} /></form>`,
|
|
2270
|
+
)(scope);
|
|
2271
|
+
const inputElm = formElm.find("input");
|
|
2272
|
+
inputElm[0].value = "10";
|
|
2273
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2274
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2275
|
+
expect(scope.value).toBe(10);
|
|
2276
|
+
|
|
2277
|
+
// Step changes, but value matches
|
|
2278
|
+
scope.$apply("step = 5");
|
|
2279
|
+
expect(inputElm.val()).toBe("10");
|
|
2280
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2281
|
+
expect(scope.value).toBe(10);
|
|
2282
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
2283
|
+
|
|
2284
|
+
// Step changes, value does not match
|
|
2285
|
+
scope.$apply("step = 6");
|
|
2286
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2287
|
+
expect(scope.value).toBeUndefined();
|
|
2288
|
+
expect(inputElm.val()).toBe("10");
|
|
2289
|
+
expect(scope.form.alias.$error.step).toBeTruthy();
|
|
2290
|
+
|
|
2291
|
+
// null = valid
|
|
2292
|
+
scope.$apply("step = null");
|
|
2293
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2294
|
+
expect(scope.value).toBe(10);
|
|
2295
|
+
expect(inputElm.val()).toBe("10");
|
|
2296
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
2297
|
+
|
|
2298
|
+
// Step val as string
|
|
2299
|
+
scope.$apply('step = "7"');
|
|
2300
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2301
|
+
expect(scope.value).toBeUndefined();
|
|
2302
|
+
expect(inputElm.val()).toBe("10");
|
|
2303
|
+
expect(scope.form.alias.$error.step).toBeTruthy();
|
|
2304
|
+
|
|
2305
|
+
// unparsable string is ignored
|
|
2306
|
+
scope.$apply('step = "abc"');
|
|
2307
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2308
|
+
expect(scope.value).toBe(10);
|
|
2309
|
+
expect(inputElm.val()).toBe("10");
|
|
2310
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
2311
|
+
});
|
|
2312
|
+
|
|
2313
|
+
it('should use the correct "step base" when `[min]` is specified', () => {
|
|
2314
|
+
scope.min = 5;
|
|
2315
|
+
scope.step = 10;
|
|
2316
|
+
scope.value = 10;
|
|
2317
|
+
const inputElm = $compile(
|
|
2318
|
+
`<input type="number" ng-model="value" min="{{min}}" ${attrHtml} />`,
|
|
2319
|
+
)(scope);
|
|
2320
|
+
const ngModel = inputElm.controller("ngModel");
|
|
2321
|
+
scope.$digest();
|
|
2322
|
+
expect(inputElm.val()).toBe("10");
|
|
2323
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2324
|
+
expect(ngModel.$error.step).toBe(true);
|
|
2325
|
+
expect(scope.value).toBe(10); // an initially invalid value should not be changed
|
|
2326
|
+
|
|
2327
|
+
inputElm[0].value = "15";
|
|
2328
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2329
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2330
|
+
expect(scope.value).toBe(15);
|
|
2331
|
+
|
|
2332
|
+
scope.$apply("step = 3");
|
|
2333
|
+
expect(inputElm.val()).toBe("15");
|
|
2334
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2335
|
+
expect(ngModel.$error.step).toBe(true);
|
|
2336
|
+
expect(scope.value).toBeUndefined();
|
|
2337
|
+
|
|
2338
|
+
inputElm[0].value = "8";
|
|
2339
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2340
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2341
|
+
expect(scope.value).toBe(8);
|
|
2342
|
+
|
|
2343
|
+
scope.$apply("min = 10; step = 20");
|
|
2344
|
+
inputElm[0].value = "30";
|
|
2345
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2346
|
+
expect(inputElm.val()).toBe("30");
|
|
2347
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2348
|
+
expect(scope.value).toBe(30);
|
|
2349
|
+
|
|
2350
|
+
scope.$apply("min = 5");
|
|
2351
|
+
expect(inputElm.val()).toBe("30");
|
|
2352
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2353
|
+
expect(ngModel.$error.step).toBe(true);
|
|
2354
|
+
expect(scope.value).toBeUndefined();
|
|
2355
|
+
|
|
2356
|
+
scope.$apply("step = 0.00000001");
|
|
2357
|
+
expect(inputElm.val()).toBe("30");
|
|
2358
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2359
|
+
expect(scope.value).toBe(30);
|
|
2360
|
+
|
|
2361
|
+
// 0.3 - 0.2 === 0.09999999999999998
|
|
2362
|
+
scope.$apply("min = 0.2; step = (0.3 - 0.2)");
|
|
2363
|
+
inputElm[0].value = "0.3";
|
|
2364
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2365
|
+
expect(inputElm.val()).toBe("0.3");
|
|
2366
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2367
|
+
expect(ngModel.$error.step).toBe(true);
|
|
2368
|
+
expect(scope.value).toBeUndefined();
|
|
2369
|
+
});
|
|
2370
|
+
|
|
2371
|
+
it("should correctly validate even in cases where the JS floating point arithmetic fails", () => {
|
|
2372
|
+
scope.step = 0.1;
|
|
2373
|
+
const inputElm = $compile(
|
|
2374
|
+
`<input type="number" ng-model="value" ${attrHtml} />`,
|
|
2375
|
+
)(scope);
|
|
2376
|
+
const ngModel = inputElm.controller("ngModel");
|
|
2377
|
+
|
|
2378
|
+
expect(inputElm.val()).toBe("");
|
|
2379
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2380
|
+
expect(scope.value).toBeUndefined();
|
|
2381
|
+
|
|
2382
|
+
inputElm[0].value = "0.3";
|
|
2383
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2384
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2385
|
+
expect(scope.value).toBe(0.3);
|
|
2386
|
+
|
|
2387
|
+
inputElm[0].value = "2.9999999999999996";
|
|
2388
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2389
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2390
|
+
expect(ngModel.$error.step).toBe(true);
|
|
2391
|
+
expect(scope.value).toBeUndefined();
|
|
2392
|
+
|
|
2393
|
+
// 0.5 % 0.1 === 0.09999999999999998
|
|
2394
|
+
inputElm[0].value = "0.5";
|
|
2395
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2396
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2397
|
+
expect(scope.value).toBe(0.5);
|
|
2398
|
+
|
|
2399
|
+
// // 3.5 % 0.1 === 0.09999999999999981
|
|
2400
|
+
inputElm[0].value = "3.5";
|
|
2401
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2402
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2403
|
+
expect(scope.value).toBe(3.5);
|
|
2404
|
+
|
|
2405
|
+
// 1.16 % 0.01 === 0.009999999999999896
|
|
2406
|
+
// 1.16 * 100 === 115.99999999999999
|
|
2407
|
+
scope.step = 0.01;
|
|
2408
|
+
inputElm[0].value = "1.16";
|
|
2409
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2410
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2411
|
+
expect(scope.value).toBe(1.16);
|
|
2412
|
+
});
|
|
2413
|
+
});
|
|
2414
|
+
},
|
|
2415
|
+
);
|
|
2416
|
+
|
|
2417
|
+
describe("required", () => {
|
|
2418
|
+
it("should be valid even if value is 0", () => {
|
|
2419
|
+
const formElm = $compile(
|
|
2420
|
+
'<form name="form"><input type="number" ng-model="value" name="alias" required /></form>',
|
|
2421
|
+
)(scope);
|
|
2422
|
+
const inputElm = formElm.find("input");
|
|
2423
|
+
|
|
2424
|
+
inputElm[0].value = "0";
|
|
2425
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2426
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2427
|
+
expect(scope.value).toBe(0);
|
|
2428
|
+
expect(scope.form.alias.$error.required).toBeFalsy();
|
|
2429
|
+
});
|
|
2430
|
+
|
|
2431
|
+
it("should be valid even if value 0 is set from model", () => {
|
|
2432
|
+
const formElm = $compile(
|
|
2433
|
+
'<form name="form"><input type="number" ng-model="value" name="alias" required /></form>',
|
|
2434
|
+
)(scope);
|
|
2435
|
+
const inputElm = formElm.find("input");
|
|
2436
|
+
scope.$apply("value = 0");
|
|
2437
|
+
|
|
2438
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2439
|
+
expect(inputElm.val()).toBe("0");
|
|
2440
|
+
expect(scope.form.alias.$error.required).toBeFalsy();
|
|
2441
|
+
});
|
|
2442
|
+
|
|
2443
|
+
it("should register required on non boolean elements", () => {
|
|
2444
|
+
const formElm = $compile(
|
|
2445
|
+
'<form name="form"><div ng-model="value" name="alias" required></form>',
|
|
2446
|
+
)(scope);
|
|
2447
|
+
const inputElm = formElm.find("div");
|
|
2448
|
+
|
|
2449
|
+
scope.$apply("value = ''");
|
|
2450
|
+
|
|
2451
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2452
|
+
expect(scope.form.alias.$error.required).toBeTruthy();
|
|
2453
|
+
});
|
|
2454
|
+
|
|
2455
|
+
it("should not invalidate number if ng-required=false and viewValue has not been committed", () => {
|
|
2456
|
+
const inputElm = $compile(
|
|
2457
|
+
'<input type="number" ng-model="value" name="alias" ng-required="required">',
|
|
2458
|
+
)(scope);
|
|
2459
|
+
|
|
2460
|
+
scope.$apply("required = false");
|
|
2461
|
+
|
|
2462
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2463
|
+
});
|
|
2464
|
+
});
|
|
2465
|
+
|
|
2466
|
+
describe("ngRequired", () => {
|
|
2467
|
+
describe("when the ngRequired expression initially evaluates to true", () => {
|
|
2468
|
+
it("should be valid even if value is 0", () => {
|
|
2469
|
+
const formElm = $compile(
|
|
2470
|
+
'<form name="form"><input type="number" ng-model="value" name="numberInput" ng-required="true" /></form>',
|
|
2471
|
+
)(scope);
|
|
2472
|
+
const inputElm = formElm.find("input");
|
|
2473
|
+
|
|
2474
|
+
inputElm[0].value = "0";
|
|
2475
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2476
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2477
|
+
expect(scope.value).toBe(0);
|
|
2478
|
+
expect(scope.form.numberInput.$error.required).toBeFalsy();
|
|
2479
|
+
});
|
|
2480
|
+
|
|
2481
|
+
it("should be valid even if value 0 is set from model", () => {
|
|
2482
|
+
const formElm = $compile(
|
|
2483
|
+
'<form name="form"><input type="number" ng-model="value" name="numberInput" ng-required="true" /></form>',
|
|
2484
|
+
)(scope);
|
|
2485
|
+
const inputElm = formElm.find("input");
|
|
2486
|
+
|
|
2487
|
+
scope.$apply("value = 0");
|
|
2488
|
+
|
|
2489
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2490
|
+
expect(inputElm.val()).toBe("0");
|
|
2491
|
+
expect(scope.form.numberInput.$error.required).toBeFalsy();
|
|
2492
|
+
});
|
|
2493
|
+
|
|
2494
|
+
it("should register required on non boolean elements", () => {
|
|
2495
|
+
const formElm = $compile(
|
|
2496
|
+
'<form name="form"><div ng-model="value" name="numberInput" ng-required="true"></form>',
|
|
2497
|
+
)(scope);
|
|
2498
|
+
const inputElm = formElm.find("div");
|
|
2499
|
+
|
|
2500
|
+
scope.$apply("value = ''");
|
|
2501
|
+
|
|
2502
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2503
|
+
expect(scope.form.numberInput.$error.required).toBeTruthy();
|
|
2504
|
+
});
|
|
2505
|
+
|
|
2506
|
+
it("should change from invalid to valid when the value is empty and the ngRequired expression changes to false", () => {
|
|
2507
|
+
const formElm = $compile(
|
|
2508
|
+
'<form name="form"><input type="number" ng-model="value" name="numberInput" ng-required="ngRequiredExpr" /></form>',
|
|
2509
|
+
)(scope);
|
|
2510
|
+
const inputElm = formElm.find("input");
|
|
2511
|
+
|
|
2512
|
+
scope.$apply("ngRequiredExpr = true");
|
|
2513
|
+
|
|
2514
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2515
|
+
expect(scope.value).toBeUndefined();
|
|
2516
|
+
expect(scope.form.numberInput.$error.required).toBeTruthy();
|
|
2517
|
+
|
|
2518
|
+
scope.$apply("ngRequiredExpr = false");
|
|
2519
|
+
|
|
2520
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2521
|
+
expect(scope.value).toBeUndefined();
|
|
2522
|
+
expect(scope.form.numberInput.$error.required).toBeFalsy();
|
|
2523
|
+
});
|
|
2524
|
+
});
|
|
2525
|
+
|
|
2526
|
+
describe("when the ngRequired expression initially evaluates to false", () => {
|
|
2527
|
+
it("should be valid even if value is empty", () => {
|
|
2528
|
+
const formElm = $compile(
|
|
2529
|
+
'<form name="form"><input type="number" ng-model="value" name="numberInput" ng-required="false" /></form>',
|
|
2530
|
+
)(scope);
|
|
2531
|
+
const inputElm = formElm.find("input");
|
|
2532
|
+
|
|
2533
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2534
|
+
expect(scope.value).toBeUndefined();
|
|
2535
|
+
expect(scope.form.numberInput.$error.required).toBeFalsy();
|
|
2536
|
+
expect(scope.form.numberInput.$error.number).toBeFalsy();
|
|
2537
|
+
});
|
|
2538
|
+
|
|
2539
|
+
it("should be valid if value is non-empty", () => {
|
|
2540
|
+
const formElm = $compile(
|
|
2541
|
+
'<form name="form"><input type="number" ng-model="value" name="numberInput" ng-required="false" /></form>',
|
|
2542
|
+
)(scope);
|
|
2543
|
+
const inputElm = formElm.find("input");
|
|
2544
|
+
|
|
2545
|
+
inputElm[0].value = "42";
|
|
2546
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2547
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2548
|
+
expect(scope.value).toBe(42);
|
|
2549
|
+
expect(scope.form.numberInput.$error.required).toBeFalsy();
|
|
2550
|
+
});
|
|
2551
|
+
|
|
2552
|
+
it("should not register required on non boolean elements", () => {
|
|
2553
|
+
const formElm = $compile(
|
|
2554
|
+
'<form name="form"><div ng-model="value" name="numberInput" ng-required="false"><form>',
|
|
2555
|
+
)(scope);
|
|
2556
|
+
const inputElm = formElm.find("div");
|
|
2557
|
+
|
|
2558
|
+
scope.$apply("value = ''");
|
|
2559
|
+
|
|
2560
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2561
|
+
expect(scope.form.numberInput.$error.required).toBeFalsy();
|
|
2562
|
+
});
|
|
2563
|
+
|
|
2564
|
+
it("should change from valid to invalid when the value is empty and the ngRequired expression changes to true", () => {
|
|
2565
|
+
const formElm = $compile(
|
|
2566
|
+
'<form name="form"><input type="number" ng-model="value" name="numberInput" ng-required="ngRequiredExpr" /><form>',
|
|
2567
|
+
)(scope);
|
|
2568
|
+
const inputElm = formElm.find("input");
|
|
2569
|
+
|
|
2570
|
+
scope.$apply("ngRequiredExpr = false");
|
|
2571
|
+
|
|
2572
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2573
|
+
expect(scope.value).toBeUndefined();
|
|
2574
|
+
expect(scope.form.numberInput.$error.required).toBeFalsy();
|
|
2575
|
+
|
|
2576
|
+
scope.$apply("ngRequiredExpr = true");
|
|
2577
|
+
|
|
2578
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2579
|
+
expect(scope.value).toBeUndefined();
|
|
2580
|
+
expect(scope.form.numberInput.$error.required).toBeTruthy();
|
|
2581
|
+
});
|
|
2582
|
+
});
|
|
2583
|
+
});
|
|
2584
|
+
|
|
2585
|
+
describe("minlength", () => {
|
|
2586
|
+
it("should invalidate values that are shorter than the given minlength", () => {
|
|
2587
|
+
const inputElm = $compile(
|
|
2588
|
+
'<input type="number" ng-model="value" ng-minlength="3" />',
|
|
2589
|
+
)(scope);
|
|
2590
|
+
|
|
2591
|
+
inputElm[0].value = "12";
|
|
2592
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2593
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2594
|
+
|
|
2595
|
+
inputElm[0].value = "123";
|
|
2596
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2597
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2598
|
+
});
|
|
2599
|
+
|
|
2600
|
+
it("should observe the standard minlength attribute and register it as a validator on the model", () => {
|
|
2601
|
+
const formElm = $compile(
|
|
2602
|
+
'<form name="form"><input type="number" name="input" ng-model="value" minlength="{{ min }}" /></form>',
|
|
2603
|
+
)(scope);
|
|
2604
|
+
const inputElm = formElm.find("input");
|
|
2605
|
+
scope.$apply(() => {
|
|
2606
|
+
scope.min = 10;
|
|
2607
|
+
});
|
|
2608
|
+
|
|
2609
|
+
inputElm[0].value = "12345";
|
|
2610
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2611
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2612
|
+
expect(scope.form.input.$error.minlength).toBe(true);
|
|
2613
|
+
|
|
2614
|
+
scope.$apply(() => {
|
|
2615
|
+
scope.min = 5;
|
|
2616
|
+
});
|
|
2617
|
+
|
|
2618
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2619
|
+
expect(scope.form.input.$error.minlength).not.toBe(true);
|
|
2620
|
+
});
|
|
2621
|
+
});
|
|
2622
|
+
|
|
2623
|
+
describe("maxlength", () => {
|
|
2624
|
+
it("should invalidate values that are longer than the given maxlength", () => {
|
|
2625
|
+
const inputElm = $compile(
|
|
2626
|
+
'<input type="number" ng-model="value" ng-maxlength="5" />',
|
|
2627
|
+
)(scope);
|
|
2628
|
+
|
|
2629
|
+
inputElm[0].value = "12345678";
|
|
2630
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2631
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2632
|
+
|
|
2633
|
+
inputElm[0].value = "123";
|
|
2634
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2635
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2636
|
+
});
|
|
2637
|
+
|
|
2638
|
+
it("should observe the standard maxlength attribute and register it as a validator on the model", () => {
|
|
2639
|
+
const formElm = $compile(
|
|
2640
|
+
'<form name="form"><input type="number" name="input" ng-model="value" maxlength="{{ max }}" /></form>',
|
|
2641
|
+
)(scope);
|
|
2642
|
+
const inputElm = formElm.find("input");
|
|
2643
|
+
scope.$apply(() => {
|
|
2644
|
+
scope.max = 1;
|
|
2645
|
+
});
|
|
2646
|
+
|
|
2647
|
+
inputElm[0].value = "12345";
|
|
2648
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2649
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
2650
|
+
expect(scope.form.input.$error.maxlength).toBe(true);
|
|
2651
|
+
|
|
2652
|
+
scope.$apply(() => {
|
|
2653
|
+
scope.max = 6;
|
|
2654
|
+
});
|
|
2655
|
+
|
|
2656
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2657
|
+
expect(scope.form.input.$error.maxlength).not.toBe(true);
|
|
2658
|
+
});
|
|
2659
|
+
});
|
|
2660
|
+
});
|
|
2661
|
+
|
|
2662
|
+
describe("range", () => {
|
|
2663
|
+
const rangeTestEl = jqLite('<input type="range">');
|
|
2664
|
+
const supportsRange = rangeTestEl[0].type === "range";
|
|
2665
|
+
|
|
2666
|
+
it("should render as 50 if null", () => {
|
|
2667
|
+
const inputElm = $compile('<input type="range" ng-model="age" />')(
|
|
2668
|
+
scope,
|
|
2669
|
+
);
|
|
2670
|
+
|
|
2671
|
+
inputElm[0].value = "25";
|
|
2672
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2673
|
+
expect(scope.age).toBe(25);
|
|
2674
|
+
|
|
2675
|
+
scope.$apply("age = null");
|
|
2676
|
+
|
|
2677
|
+
expect(inputElm.val()).toEqual("50");
|
|
2678
|
+
});
|
|
2679
|
+
|
|
2680
|
+
it("should set model to 50 when no value specified and default min/max", () => {
|
|
2681
|
+
const inputElm = $compile('<input type="range" ng-model="age" />')(
|
|
2682
|
+
scope,
|
|
2683
|
+
);
|
|
2684
|
+
|
|
2685
|
+
expect(inputElm.val()).toBe("50");
|
|
2686
|
+
|
|
2687
|
+
scope.$apply("age = null");
|
|
2688
|
+
|
|
2689
|
+
expect(scope.age).toBe(50);
|
|
2690
|
+
});
|
|
2691
|
+
|
|
2692
|
+
it("should parse non-number values to 50 when default min/max", () => {
|
|
2693
|
+
const inputElm = $compile('<input type="range" ng-model="age" />')(
|
|
2694
|
+
scope,
|
|
2695
|
+
);
|
|
2696
|
+
|
|
2697
|
+
scope.$apply("age = 10");
|
|
2698
|
+
expect(inputElm.val()).toBe("10");
|
|
2699
|
+
|
|
2700
|
+
inputElm[0].value = "";
|
|
2701
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2702
|
+
expect(scope.age).toBe(50);
|
|
2703
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2704
|
+
});
|
|
2705
|
+
|
|
2706
|
+
it("should parse the input value to a Number", () => {
|
|
2707
|
+
const inputElm = $compile('<input type="range" ng-model="age" />')(
|
|
2708
|
+
scope,
|
|
2709
|
+
);
|
|
2710
|
+
|
|
2711
|
+
inputElm[0].value = "75";
|
|
2712
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2713
|
+
expect(scope.age).toBe(75);
|
|
2714
|
+
});
|
|
2715
|
+
|
|
2716
|
+
it("should only invalidate the model if suffering from bad input when the data is parsed", () => {
|
|
2717
|
+
scope.age = 60;
|
|
2718
|
+
|
|
2719
|
+
const inputElm = $compile('<input type="range" ng-model="age" />')(
|
|
2720
|
+
scope,
|
|
2721
|
+
);
|
|
2722
|
+
|
|
2723
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2724
|
+
|
|
2725
|
+
inputElm[0].value = "this-will-fail-because-of-the-badInput-flag";
|
|
2726
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2727
|
+
|
|
2728
|
+
expect(scope.age).toBe(50);
|
|
2729
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2730
|
+
});
|
|
2731
|
+
|
|
2732
|
+
it("should throw if the model value is not a number", () => {
|
|
2733
|
+
expect(() => {
|
|
2734
|
+
scope.value = "one";
|
|
2735
|
+
const inputElm = $compile('<input type="range" ng-model="value" />')(
|
|
2736
|
+
scope,
|
|
2737
|
+
);
|
|
2738
|
+
scope.$digest();
|
|
2739
|
+
}).toThrowError(/numfmt/);
|
|
2740
|
+
});
|
|
2741
|
+
|
|
2742
|
+
describe("min", () => {
|
|
2743
|
+
it("should initialize correctly with non-default model and min value", () => {
|
|
2744
|
+
scope.value = -3;
|
|
2745
|
+
scope.min = -5;
|
|
2746
|
+
const formElm = $compile(
|
|
2747
|
+
'<form name="form"><input type="range" ng-model="value" name="alias" min="{{min}}" /></form>',
|
|
2748
|
+
)(scope);
|
|
2749
|
+
const inputElm = formElm.find("input");
|
|
2750
|
+
scope.$digest();
|
|
2751
|
+
|
|
2752
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2753
|
+
expect(inputElm.val()).toBe("-3");
|
|
2754
|
+
expect(scope.value).toBe(-3);
|
|
2755
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
2756
|
+
});
|
|
2757
|
+
|
|
2758
|
+
// Browsers that implement range will never allow you to set the value < min values
|
|
2759
|
+
it("should adjust invalid input values", () => {
|
|
2760
|
+
const formElm = $compile(
|
|
2761
|
+
'<form name="form"><input type="range" ng-model="value" name="alias" min="10" /></form>',
|
|
2762
|
+
)(scope);
|
|
2763
|
+
const inputElm = formElm.find("input");
|
|
2764
|
+
|
|
2765
|
+
inputElm[0].value = "5";
|
|
2766
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2767
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2768
|
+
expect(scope.value).toBe(10);
|
|
2769
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
2770
|
+
|
|
2771
|
+
inputElm[0].value = "100";
|
|
2772
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2773
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2774
|
+
expect(scope.value).toBe(100);
|
|
2775
|
+
expect(scope.form.alias.$error.min).toBeFalsy();
|
|
2776
|
+
});
|
|
2777
|
+
|
|
2778
|
+
it("should set the model to the min val if it is less than the min val", () => {
|
|
2779
|
+
scope.value = -10;
|
|
2780
|
+
// Default min is 0
|
|
2781
|
+
const inputElm = $compile(
|
|
2782
|
+
'<input type="range" ng-model="value" name="alias" min="{{min}}" />',
|
|
2783
|
+
)(scope);
|
|
2784
|
+
scope.$digest();
|
|
2785
|
+
|
|
2786
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2787
|
+
expect(inputElm.val()).toBe("0");
|
|
2788
|
+
expect(scope.value).toBe(0);
|
|
2789
|
+
|
|
2790
|
+
scope.$apply("value = 5; min = 10");
|
|
2791
|
+
|
|
2792
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2793
|
+
expect(inputElm.val()).toBe("10");
|
|
2794
|
+
expect(scope.value).toBe(10);
|
|
2795
|
+
});
|
|
2796
|
+
|
|
2797
|
+
it("should adjust the element and model value when the min value changes on-the-fly", () => {
|
|
2798
|
+
scope.min = 10;
|
|
2799
|
+
const inputElm = $compile(
|
|
2800
|
+
'<input type="range" ng-model="value" name="alias" min="{{min}}" />',
|
|
2801
|
+
)(scope);
|
|
2802
|
+
scope.$digest();
|
|
2803
|
+
|
|
2804
|
+
inputElm[0].value = "15";
|
|
2805
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2806
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2807
|
+
|
|
2808
|
+
scope.min = 20;
|
|
2809
|
+
scope.$digest();
|
|
2810
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2811
|
+
expect(scope.value).toBe(20);
|
|
2812
|
+
expect(inputElm.val()).toBe("20");
|
|
2813
|
+
|
|
2814
|
+
scope.min = null;
|
|
2815
|
+
scope.$digest();
|
|
2816
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2817
|
+
expect(scope.value).toBe(20);
|
|
2818
|
+
expect(inputElm.val()).toBe("20");
|
|
2819
|
+
|
|
2820
|
+
scope.min = "15";
|
|
2821
|
+
scope.$digest();
|
|
2822
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2823
|
+
expect(scope.value).toBe(20);
|
|
2824
|
+
expect(inputElm.val()).toBe("20");
|
|
2825
|
+
|
|
2826
|
+
scope.min = "abc";
|
|
2827
|
+
scope.$digest();
|
|
2828
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2829
|
+
expect(scope.value).toBe(20);
|
|
2830
|
+
expect(inputElm.val()).toBe("20");
|
|
2831
|
+
});
|
|
2832
|
+
});
|
|
2833
|
+
|
|
2834
|
+
describe("max", () => {
|
|
2835
|
+
// Browsers that implement range will never allow you to set the value > max value
|
|
2836
|
+
it("should initialize correctly with non-default model and max value", () => {
|
|
2837
|
+
scope.value = 130;
|
|
2838
|
+
scope.max = 150;
|
|
2839
|
+
const formElm = $compile(
|
|
2840
|
+
'<form name="form"><input type="range" ng-model="value" name="alias" max="{{max}}" /></form>',
|
|
2841
|
+
)(scope);
|
|
2842
|
+
const inputElm = formElm.find("input");
|
|
2843
|
+
scope.$digest();
|
|
2844
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2845
|
+
expect(inputElm.val()).toBe("130");
|
|
2846
|
+
expect(scope.value).toBe(130);
|
|
2847
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
2848
|
+
});
|
|
2849
|
+
|
|
2850
|
+
it("should validate", () => {
|
|
2851
|
+
const formElm = $compile(
|
|
2852
|
+
'<form name="form"><input type="range" ng-model="value" name="alias" max="10" /></form>',
|
|
2853
|
+
)(scope);
|
|
2854
|
+
const inputElm = formElm.find("input");
|
|
2855
|
+
|
|
2856
|
+
inputElm[0].value = "20";
|
|
2857
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2858
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2859
|
+
expect(scope.value).toBe(10);
|
|
2860
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
2861
|
+
|
|
2862
|
+
inputElm[0].value = "0";
|
|
2863
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2864
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2865
|
+
expect(scope.value).toBe(0);
|
|
2866
|
+
expect(scope.form.alias.$error.max).toBeFalsy();
|
|
2867
|
+
});
|
|
2868
|
+
|
|
2869
|
+
it("should set the model to the max val if it is greater than the max val", () => {
|
|
2870
|
+
scope.value = 110;
|
|
2871
|
+
// Default max is 100
|
|
2872
|
+
const inputElm = $compile(
|
|
2873
|
+
'<input type="range" ng-model="value" name="alias" max="{{max}}" />',
|
|
2874
|
+
)(scope);
|
|
2875
|
+
scope.$digest();
|
|
2876
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2877
|
+
expect(inputElm.val()).toBe("100");
|
|
2878
|
+
expect(scope.value).toBe(100);
|
|
2879
|
+
|
|
2880
|
+
scope.$apply("value = 90; max = 10");
|
|
2881
|
+
|
|
2882
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2883
|
+
expect(inputElm.val()).toBe("10");
|
|
2884
|
+
expect(scope.value).toBe(10);
|
|
2885
|
+
});
|
|
2886
|
+
|
|
2887
|
+
it("should adjust the element and model value if the max value changes on-the-fly", () => {
|
|
2888
|
+
scope.max = 10;
|
|
2889
|
+
const inputElm = $compile(
|
|
2890
|
+
'<input type="range" ng-model="value" name="alias" max="{{max}}" />',
|
|
2891
|
+
)(scope);
|
|
2892
|
+
scope.$digest();
|
|
2893
|
+
inputElm[0].value = "5";
|
|
2894
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2895
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2896
|
+
|
|
2897
|
+
scope.max = 0;
|
|
2898
|
+
scope.$digest();
|
|
2899
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2900
|
+
expect(scope.value).toBe(0);
|
|
2901
|
+
expect(inputElm.val()).toBe("0");
|
|
2902
|
+
|
|
2903
|
+
scope.max = null;
|
|
2904
|
+
scope.$digest();
|
|
2905
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2906
|
+
expect(scope.value).toBe(0);
|
|
2907
|
+
expect(inputElm.val()).toBe("0");
|
|
2908
|
+
|
|
2909
|
+
scope.max = "4";
|
|
2910
|
+
scope.$digest();
|
|
2911
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2912
|
+
expect(scope.value).toBe(0);
|
|
2913
|
+
expect(inputElm.val()).toBe("0");
|
|
2914
|
+
|
|
2915
|
+
scope.max = "abc";
|
|
2916
|
+
scope.$digest();
|
|
2917
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2918
|
+
expect(scope.value).toBe(0);
|
|
2919
|
+
expect(inputElm.val()).toBe("0");
|
|
2920
|
+
});
|
|
2921
|
+
});
|
|
2922
|
+
|
|
2923
|
+
describe("min and max", () => {
|
|
2924
|
+
it("should set the correct initial value when min and max are specified", () => {
|
|
2925
|
+
scope.max = 80;
|
|
2926
|
+
scope.min = 40;
|
|
2927
|
+
const inputElm = $compile(
|
|
2928
|
+
'<input type="range" ng-model="value" name="alias" max="{{max}}" min="{{min}}" />',
|
|
2929
|
+
)(scope);
|
|
2930
|
+
scope.$digest();
|
|
2931
|
+
|
|
2932
|
+
expect(inputElm.val()).toBe("60");
|
|
2933
|
+
expect(scope.value).toBe(60);
|
|
2934
|
+
});
|
|
2935
|
+
|
|
2936
|
+
it("should set element and model value to min if max is less than min", () => {
|
|
2937
|
+
scope.min = 40;
|
|
2938
|
+
const inputElm = $compile(
|
|
2939
|
+
'<input type="range" ng-model="value" name="alias" max="{{max}}" min="{{min}}" />',
|
|
2940
|
+
)(scope);
|
|
2941
|
+
scope.$digest();
|
|
2942
|
+
expect(inputElm.val()).toBe("70");
|
|
2943
|
+
expect(scope.value).toBe(70);
|
|
2944
|
+
|
|
2945
|
+
scope.max = 20;
|
|
2946
|
+
scope.$digest();
|
|
2947
|
+
|
|
2948
|
+
expect(inputElm.val()).toBe("40");
|
|
2949
|
+
expect(scope.value).toBe(40);
|
|
2950
|
+
});
|
|
2951
|
+
});
|
|
2952
|
+
|
|
2953
|
+
describe("step", () => {
|
|
2954
|
+
// Browsers that implement range will never allow you to set a value that doesn't match the step value
|
|
2955
|
+
// However, currently only Firefox fully implements the spec when setting the value after the step value changes.
|
|
2956
|
+
// Other browsers fail in various edge cases, which is why they are not tested here.
|
|
2957
|
+
|
|
2958
|
+
it("should round the input value to the nearest step on user input", () => {
|
|
2959
|
+
const formElm = $compile(
|
|
2960
|
+
'<form name="form"><input type="range" ng-model="value" name="alias" step="5" /></form>',
|
|
2961
|
+
)(scope);
|
|
2962
|
+
const inputElm = formElm.find("input");
|
|
2963
|
+
|
|
2964
|
+
inputElm[0].value = "5";
|
|
2965
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2966
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2967
|
+
expect(scope.value).toBe(5);
|
|
2968
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
2969
|
+
|
|
2970
|
+
inputElm[0].value = "10";
|
|
2971
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2972
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2973
|
+
expect(scope.value).toBe(10);
|
|
2974
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
2975
|
+
|
|
2976
|
+
inputElm[0].value = "9";
|
|
2977
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2978
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2979
|
+
expect(scope.value).toBe(10);
|
|
2980
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
2981
|
+
|
|
2982
|
+
inputElm[0].value = "7";
|
|
2983
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2984
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2985
|
+
expect(scope.value).toBe(5);
|
|
2986
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
2987
|
+
|
|
2988
|
+
inputElm[0].value = "7.5";
|
|
2989
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
2990
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
2991
|
+
expect(scope.value).toBe(10);
|
|
2992
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
2993
|
+
});
|
|
2994
|
+
|
|
2995
|
+
it("should round the input value to the nearest step when setting the model", () => {
|
|
2996
|
+
const formElm = $compile(
|
|
2997
|
+
'<form name="form"><input type="range" ng-model="value" name="alias" step="5" /></form>',
|
|
2998
|
+
)(scope);
|
|
2999
|
+
const inputElm = formElm.find("input");
|
|
3000
|
+
|
|
3001
|
+
scope.$apply("value = 10");
|
|
3002
|
+
expect(inputElm.val()).toBe("10");
|
|
3003
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
3004
|
+
expect(scope.value).toBe(10);
|
|
3005
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
3006
|
+
|
|
3007
|
+
scope.$apply("value = 5");
|
|
3008
|
+
expect(inputElm.val()).toBe("5");
|
|
3009
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
3010
|
+
expect(scope.value).toBe(5);
|
|
3011
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
3012
|
+
|
|
3013
|
+
scope.$apply("value = 7.5");
|
|
3014
|
+
expect(inputElm.val()).toBe("10");
|
|
3015
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
3016
|
+
expect(scope.value).toBe(10);
|
|
3017
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
3018
|
+
|
|
3019
|
+
scope.$apply("value = 7");
|
|
3020
|
+
expect(inputElm.val()).toBe("5");
|
|
3021
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
3022
|
+
expect(scope.value).toBe(5);
|
|
3023
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
3024
|
+
|
|
3025
|
+
scope.$apply("value = 9");
|
|
3026
|
+
expect(inputElm.val()).toBe("10");
|
|
3027
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
3028
|
+
expect(scope.value).toBe(10);
|
|
3029
|
+
expect(scope.form.alias.$error.step).toBeFalsy();
|
|
3030
|
+
});
|
|
3031
|
+
});
|
|
3032
|
+
});
|
|
3033
|
+
|
|
3034
|
+
describe("email", () => {
|
|
3035
|
+
it("should validate e-mail", () => {
|
|
3036
|
+
const formElm = $compile(
|
|
3037
|
+
'<form name="form">' +
|
|
3038
|
+
'<input type="email" ng-model="email" name="alias" />' +
|
|
3039
|
+
"</form>",
|
|
3040
|
+
)(scope);
|
|
3041
|
+
const inputElm = formElm.find("input");
|
|
3042
|
+
|
|
3043
|
+
const widget = scope.form.alias;
|
|
3044
|
+
inputElm[0].value = "vojta@google.com";
|
|
3045
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3046
|
+
|
|
3047
|
+
expect(scope.email).toBe("vojta@google.com");
|
|
3048
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
3049
|
+
expect(widget.$error.email).toBeFalsy();
|
|
3050
|
+
|
|
3051
|
+
inputElm[0].value = "invalid@";
|
|
3052
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3053
|
+
expect(scope.email).toBeUndefined();
|
|
3054
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
3055
|
+
expect(widget.$error.email).toBeTruthy();
|
|
3056
|
+
});
|
|
3057
|
+
|
|
3058
|
+
describe("EMAIL_REGEXP", () => {
|
|
3059
|
+
/* global EMAIL_REGEXP: false */
|
|
3060
|
+
it("should validate email", () => {
|
|
3061
|
+
/* basic functionality */
|
|
3062
|
+
expect(EMAIL_REGEXP.test("a@b.com")).toBe(true);
|
|
3063
|
+
expect(EMAIL_REGEXP.test("a@b.museum")).toBe(true);
|
|
3064
|
+
expect(EMAIL_REGEXP.test("a@B.c")).toBe(true);
|
|
3065
|
+
/* domain label separation, hyphen-minus, syntax */
|
|
3066
|
+
expect(EMAIL_REGEXP.test("a@b.c.")).toBe(false);
|
|
3067
|
+
expect(EMAIL_REGEXP.test("a@.b.c")).toBe(false);
|
|
3068
|
+
expect(EMAIL_REGEXP.test("a@-b.c")).toBe(false);
|
|
3069
|
+
expect(EMAIL_REGEXP.test("a@b-.c")).toBe(false);
|
|
3070
|
+
expect(EMAIL_REGEXP.test("a@b-c")).toBe(true);
|
|
3071
|
+
expect(EMAIL_REGEXP.test("a@-")).toBe(false);
|
|
3072
|
+
expect(EMAIL_REGEXP.test("a@.")).toBe(false);
|
|
3073
|
+
expect(EMAIL_REGEXP.test("a@host_name")).toBe(false);
|
|
3074
|
+
/* leading or sole digit */
|
|
3075
|
+
expect(EMAIL_REGEXP.test("a@3b.c")).toBe(true);
|
|
3076
|
+
expect(EMAIL_REGEXP.test("a@3")).toBe(true);
|
|
3077
|
+
/* TLD eMail address */
|
|
3078
|
+
expect(EMAIL_REGEXP.test("a@b")).toBe(true);
|
|
3079
|
+
/* domain valid characters */
|
|
3080
|
+
expect(
|
|
3081
|
+
EMAIL_REGEXP.test(
|
|
3082
|
+
"a@abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789",
|
|
3083
|
+
),
|
|
3084
|
+
).toBe(true);
|
|
3085
|
+
/* domain invalid characters */
|
|
3086
|
+
expect(EMAIL_REGEXP.test("a@")).toBe(false);
|
|
3087
|
+
expect(EMAIL_REGEXP.test("a@ ")).toBe(false);
|
|
3088
|
+
expect(EMAIL_REGEXP.test("a@!")).toBe(false);
|
|
3089
|
+
expect(EMAIL_REGEXP.test('a@"')).toBe(false);
|
|
3090
|
+
expect(EMAIL_REGEXP.test("a@#")).toBe(false);
|
|
3091
|
+
expect(EMAIL_REGEXP.test("a@$")).toBe(false);
|
|
3092
|
+
expect(EMAIL_REGEXP.test("a@%")).toBe(false);
|
|
3093
|
+
expect(EMAIL_REGEXP.test("a@&")).toBe(false);
|
|
3094
|
+
expect(EMAIL_REGEXP.test("a@'")).toBe(false);
|
|
3095
|
+
expect(EMAIL_REGEXP.test("a@(")).toBe(false);
|
|
3096
|
+
expect(EMAIL_REGEXP.test("a@)")).toBe(false);
|
|
3097
|
+
expect(EMAIL_REGEXP.test("a@*")).toBe(false);
|
|
3098
|
+
expect(EMAIL_REGEXP.test("a@+")).toBe(false);
|
|
3099
|
+
expect(EMAIL_REGEXP.test("a@,")).toBe(false);
|
|
3100
|
+
expect(EMAIL_REGEXP.test("a@/")).toBe(false);
|
|
3101
|
+
expect(EMAIL_REGEXP.test("a@:")).toBe(false);
|
|
3102
|
+
expect(EMAIL_REGEXP.test("a@;")).toBe(false);
|
|
3103
|
+
expect(EMAIL_REGEXP.test("a@<")).toBe(false);
|
|
3104
|
+
expect(EMAIL_REGEXP.test("a@=")).toBe(false);
|
|
3105
|
+
expect(EMAIL_REGEXP.test("a@>")).toBe(false);
|
|
3106
|
+
expect(EMAIL_REGEXP.test("a@?")).toBe(false);
|
|
3107
|
+
expect(EMAIL_REGEXP.test("a@@")).toBe(false);
|
|
3108
|
+
expect(EMAIL_REGEXP.test("a@[")).toBe(false);
|
|
3109
|
+
expect(EMAIL_REGEXP.test("a@\\")).toBe(false);
|
|
3110
|
+
expect(EMAIL_REGEXP.test("a@]")).toBe(false);
|
|
3111
|
+
expect(EMAIL_REGEXP.test("a@^")).toBe(false);
|
|
3112
|
+
expect(EMAIL_REGEXP.test("a@_")).toBe(false);
|
|
3113
|
+
expect(EMAIL_REGEXP.test("a@`")).toBe(false);
|
|
3114
|
+
expect(EMAIL_REGEXP.test("a@{")).toBe(false);
|
|
3115
|
+
expect(EMAIL_REGEXP.test("a@|")).toBe(false);
|
|
3116
|
+
expect(EMAIL_REGEXP.test("a@}")).toBe(false);
|
|
3117
|
+
expect(EMAIL_REGEXP.test("a@~")).toBe(false);
|
|
3118
|
+
expect(EMAIL_REGEXP.test("a@İ")).toBe(false);
|
|
3119
|
+
expect(EMAIL_REGEXP.test("a@ı")).toBe(false);
|
|
3120
|
+
/* domain length, label and total */
|
|
3121
|
+
expect(
|
|
3122
|
+
EMAIL_REGEXP.test(
|
|
3123
|
+
"a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
3124
|
+
),
|
|
3125
|
+
).toBe(true);
|
|
3126
|
+
expect(
|
|
3127
|
+
EMAIL_REGEXP.test(
|
|
3128
|
+
"a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
3129
|
+
),
|
|
3130
|
+
).toBe(false);
|
|
3131
|
+
/* eslint-disable max-len */
|
|
3132
|
+
expect(
|
|
3133
|
+
EMAIL_REGEXP.test(
|
|
3134
|
+
"a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
3135
|
+
),
|
|
3136
|
+
).toBe(true);
|
|
3137
|
+
expect(
|
|
3138
|
+
EMAIL_REGEXP.test(
|
|
3139
|
+
"a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.x",
|
|
3140
|
+
),
|
|
3141
|
+
).toBe(true);
|
|
3142
|
+
expect(
|
|
3143
|
+
EMAIL_REGEXP.test(
|
|
3144
|
+
"a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xx",
|
|
3145
|
+
),
|
|
3146
|
+
).toBe(false);
|
|
3147
|
+
expect(
|
|
3148
|
+
EMAIL_REGEXP.test(
|
|
3149
|
+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xx",
|
|
3150
|
+
),
|
|
3151
|
+
).toBe(true);
|
|
3152
|
+
expect(
|
|
3153
|
+
EMAIL_REGEXP.test(
|
|
3154
|
+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxx",
|
|
3155
|
+
),
|
|
3156
|
+
).toBe(false);
|
|
3157
|
+
/* eslint-enable */
|
|
3158
|
+
/* local-part valid characters and dot-atom syntax */
|
|
3159
|
+
expect(EMAIL_REGEXP.test("'@x")).toBe(true);
|
|
3160
|
+
expect(
|
|
3161
|
+
EMAIL_REGEXP.test(
|
|
3162
|
+
"-!#$%&*+/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ@x",
|
|
3163
|
+
),
|
|
3164
|
+
).toBe(true);
|
|
3165
|
+
expect(EMAIL_REGEXP.test("^_`abcdefghijklmnopqrstuvwxyz{|}~@x")).toBe(
|
|
3166
|
+
true,
|
|
3167
|
+
);
|
|
3168
|
+
expect(EMAIL_REGEXP.test(".@x")).toBe(false);
|
|
3169
|
+
expect(EMAIL_REGEXP.test("'.@x")).toBe(false);
|
|
3170
|
+
expect(EMAIL_REGEXP.test(".'@x")).toBe(false);
|
|
3171
|
+
expect(EMAIL_REGEXP.test("'.'@x")).toBe(true);
|
|
3172
|
+
/* local-part invalid characters */
|
|
3173
|
+
expect(EMAIL_REGEXP.test("@x")).toBe(false);
|
|
3174
|
+
expect(EMAIL_REGEXP.test(" @x")).toBe(false);
|
|
3175
|
+
expect(EMAIL_REGEXP.test('"@x')).toBe(false);
|
|
3176
|
+
expect(EMAIL_REGEXP.test("(@x")).toBe(false);
|
|
3177
|
+
expect(EMAIL_REGEXP.test(")@x")).toBe(false);
|
|
3178
|
+
expect(EMAIL_REGEXP.test(",@x")).toBe(false);
|
|
3179
|
+
expect(EMAIL_REGEXP.test(":@x")).toBe(false);
|
|
3180
|
+
expect(EMAIL_REGEXP.test(";@x")).toBe(false);
|
|
3181
|
+
expect(EMAIL_REGEXP.test("<@x")).toBe(false);
|
|
3182
|
+
expect(EMAIL_REGEXP.test(">@x")).toBe(false);
|
|
3183
|
+
expect(EMAIL_REGEXP.test("@@x")).toBe(false);
|
|
3184
|
+
expect(EMAIL_REGEXP.test("[@x")).toBe(false);
|
|
3185
|
+
expect(EMAIL_REGEXP.test("\\@x")).toBe(false);
|
|
3186
|
+
expect(EMAIL_REGEXP.test("]@x")).toBe(false);
|
|
3187
|
+
expect(EMAIL_REGEXP.test("İ@x")).toBe(false);
|
|
3188
|
+
expect(EMAIL_REGEXP.test("ı@x")).toBe(false);
|
|
3189
|
+
/* local-part size limit */
|
|
3190
|
+
expect(
|
|
3191
|
+
EMAIL_REGEXP.test(
|
|
3192
|
+
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@x",
|
|
3193
|
+
),
|
|
3194
|
+
).toBe(true);
|
|
3195
|
+
expect(
|
|
3196
|
+
EMAIL_REGEXP.test(
|
|
3197
|
+
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@x",
|
|
3198
|
+
),
|
|
3199
|
+
).toBe(false);
|
|
3200
|
+
/* content (local-part + ‘@’ + domain) is required */
|
|
3201
|
+
expect(EMAIL_REGEXP.test("")).toBe(false);
|
|
3202
|
+
expect(EMAIL_REGEXP.test("a")).toBe(false);
|
|
3203
|
+
expect(EMAIL_REGEXP.test("aa")).toBe(false);
|
|
3204
|
+
});
|
|
3205
|
+
});
|
|
3206
|
+
});
|
|
3207
|
+
|
|
3208
|
+
describe("url", () => {
|
|
3209
|
+
it("should validate url", () => {
|
|
3210
|
+
const formElm = $compile(
|
|
3211
|
+
'<form name="form">' +
|
|
3212
|
+
'<input type="url" ng-model="url" name="alias" />',
|
|
3213
|
+
+"</form>",
|
|
3214
|
+
)(scope);
|
|
3215
|
+
const inputElm = formElm.find("input");
|
|
3216
|
+
|
|
3217
|
+
const widget = scope.form.alias;
|
|
3218
|
+
|
|
3219
|
+
inputElm[0].value = "http://www.something.com";
|
|
3220
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3221
|
+
expect(scope.url).toBe("http://www.something.com");
|
|
3222
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
3223
|
+
expect(widget.$error.url).toBeFalsy();
|
|
3224
|
+
|
|
3225
|
+
inputElm[0].value = "invalid.com";
|
|
3226
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3227
|
+
expect(scope.url).toBeUndefined();
|
|
3228
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
3229
|
+
expect(widget.$error.url).toBeTruthy();
|
|
3230
|
+
});
|
|
3231
|
+
|
|
3232
|
+
describe("URL_REGEXP", () => {
|
|
3233
|
+
// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
|
|
3234
|
+
// Note: We are being more lenient, because browsers are too.
|
|
3235
|
+
const urls = [
|
|
3236
|
+
["scheme://hostname", true],
|
|
3237
|
+
[
|
|
3238
|
+
"scheme://username:password@host.name:7678/pa/t.h?q=u&e=r&y#fragment",
|
|
3239
|
+
true,
|
|
3240
|
+
],
|
|
3241
|
+
|
|
3242
|
+
// Validating `scheme`
|
|
3243
|
+
["://example.com", false],
|
|
3244
|
+
["0scheme://example.com", false],
|
|
3245
|
+
[".scheme://example.com", false],
|
|
3246
|
+
["+scheme://example.com", false],
|
|
3247
|
+
["-scheme://example.com", false],
|
|
3248
|
+
["_scheme://example.com", false],
|
|
3249
|
+
["scheme0://example.com", true],
|
|
3250
|
+
["scheme.://example.com", true],
|
|
3251
|
+
["scheme+://example.com", true],
|
|
3252
|
+
["scheme-://example.com", true],
|
|
3253
|
+
["scheme_://example.com", false],
|
|
3254
|
+
|
|
3255
|
+
// Validating `:` and `/` after `scheme`
|
|
3256
|
+
["scheme//example.com", false],
|
|
3257
|
+
["scheme:example.com", true],
|
|
3258
|
+
["scheme:/example.com", true],
|
|
3259
|
+
["scheme:///example.com", true],
|
|
3260
|
+
|
|
3261
|
+
// Validating `username` and `password`
|
|
3262
|
+
["scheme://@example.com", true],
|
|
3263
|
+
["scheme://username@example.com", true],
|
|
3264
|
+
["scheme://u0s.e+r-n_a~m!e@example.com", true],
|
|
3265
|
+
["scheme://u#s$e%r^n&a*m;e@example.com", true],
|
|
3266
|
+
["scheme://:password@example.com", true],
|
|
3267
|
+
["scheme://username:password@example.com", true],
|
|
3268
|
+
["scheme://username:pass:word@example.com", true],
|
|
3269
|
+
["scheme://username:p0a.s+s-w_o~r!d@example.com", true],
|
|
3270
|
+
["scheme://username:p#a$s%s^w&o*r;d@example.com", true],
|
|
3271
|
+
|
|
3272
|
+
// Validating `hostname`
|
|
3273
|
+
["scheme:", false], // Chrome, FF: true
|
|
3274
|
+
["scheme://", false], // Chrome, FF: true
|
|
3275
|
+
["scheme:// example.com:", false], // Chrome, FF: true
|
|
3276
|
+
["scheme://example com:", false], // Chrome, FF: true
|
|
3277
|
+
["scheme://:", false], // Chrome, FF: true
|
|
3278
|
+
["scheme://?", false], // Chrome, FF: true
|
|
3279
|
+
["scheme://#", false], // Chrome, FF: true
|
|
3280
|
+
["scheme://username:password@:", false], // Chrome, FF: true
|
|
3281
|
+
["scheme://username:password@/", false], // Chrome, FF: true
|
|
3282
|
+
["scheme://username:password@?", false], // Chrome, FF: true
|
|
3283
|
+
["scheme://username:password@#", false], // Chrome, FF: true
|
|
3284
|
+
["scheme://host.name", true],
|
|
3285
|
+
["scheme://123.456.789.10", true],
|
|
3286
|
+
["scheme://[1234:0000:0000:5678:9abc:0000:0000:def]", true],
|
|
3287
|
+
["scheme://[1234:0000:0000:5678:9abc:0000:0000:def]:7678", true],
|
|
3288
|
+
["scheme://[1234:0:0:5678:9abc:0:0:def]", true],
|
|
3289
|
+
["scheme://[1234::5678:9abc::def]", true],
|
|
3290
|
+
["scheme://~`!@$%^&*-_=+|\\;'\",.()[]{}<>", true],
|
|
3291
|
+
|
|
3292
|
+
// Validating `port`
|
|
3293
|
+
["scheme://example.com/no-port", true],
|
|
3294
|
+
["scheme://example.com:7678", true],
|
|
3295
|
+
["scheme://example.com:76T8", false], // Chrome, FF: true
|
|
3296
|
+
["scheme://example.com:port", false], // Chrome, FF: true
|
|
3297
|
+
|
|
3298
|
+
// Validating `path`
|
|
3299
|
+
["scheme://example.com/", true],
|
|
3300
|
+
["scheme://example.com/path", true],
|
|
3301
|
+
["scheme://example.com/path/~`!@$%^&*-_=+|\\;:'\",./()[]{}<>", true],
|
|
3302
|
+
|
|
3303
|
+
// Validating `query`
|
|
3304
|
+
["scheme://example.com?query", true],
|
|
3305
|
+
["scheme://example.com/?query", true],
|
|
3306
|
+
["scheme://example.com/path?query", true],
|
|
3307
|
+
["scheme://example.com/path?~`!@$%^&*-_=+|\\;:'\",.?/()[]{}<>", true],
|
|
3308
|
+
|
|
3309
|
+
// Validating `fragment`
|
|
3310
|
+
["scheme://example.com#fragment", true],
|
|
3311
|
+
["scheme://example.com/#fragment", true],
|
|
3312
|
+
["scheme://example.com/path#fragment", true],
|
|
3313
|
+
["scheme://example.com/path/#fragment", true],
|
|
3314
|
+
["scheme://example.com/path?query#fragment", true],
|
|
3315
|
+
[
|
|
3316
|
+
"scheme://example.com/path?query#~`!@#$%^&*-_=+|\\;:'\",.?/()[]{}<>",
|
|
3317
|
+
true,
|
|
3318
|
+
],
|
|
3319
|
+
|
|
3320
|
+
// Validating miscellaneous
|
|
3321
|
+
["scheme://☺.✪.⌘.➡/䨹", true],
|
|
3322
|
+
["scheme://مثال.إختبار", true],
|
|
3323
|
+
["scheme://例子.测试", true],
|
|
3324
|
+
["scheme://उदाहरण.परीक्षा", true],
|
|
3325
|
+
|
|
3326
|
+
// Legacy tests
|
|
3327
|
+
["http://server:123/path", true],
|
|
3328
|
+
["https://server:123/path", true],
|
|
3329
|
+
["file:///home/user", true],
|
|
3330
|
+
["mailto:user@example.com?subject=Foo", true],
|
|
3331
|
+
["r2-d2.c3-p0://localhost/foo", true],
|
|
3332
|
+
["abc:/foo", true],
|
|
3333
|
+
["http://example.com/path;path", true],
|
|
3334
|
+
["http://example.com/[]$'()*,~)", true],
|
|
3335
|
+
["http:", false], // FF: true
|
|
3336
|
+
["a@B.c", false],
|
|
3337
|
+
["a_B.c", false],
|
|
3338
|
+
["0scheme://example.com", false],
|
|
3339
|
+
["http://example.com:9999/``", true],
|
|
3340
|
+
].forEach((item) => {
|
|
3341
|
+
it("should validate url: $prop", () => {
|
|
3342
|
+
const url = item[0];
|
|
3343
|
+
const valid = item[1];
|
|
3344
|
+
|
|
3345
|
+
/* global URL_REGEXP: false */
|
|
3346
|
+
expect(URL_REGEXP.test(url)).toBe(valid);
|
|
3347
|
+
});
|
|
3348
|
+
});
|
|
3349
|
+
});
|
|
3350
|
+
});
|
|
3351
|
+
|
|
3352
|
+
describe("radio", () => {
|
|
3353
|
+
["click", "change"].forEach((event) => {
|
|
3354
|
+
it("should update the model on $prop event", () => {
|
|
3355
|
+
const inputElm = $compile(
|
|
3356
|
+
'<input type="radio" ng-model="color" value="white" />' +
|
|
3357
|
+
'<input type="radio" ng-model="color" value="red" />' +
|
|
3358
|
+
'<input type="radio" ng-model="color" value="blue" />',
|
|
3359
|
+
)(scope);
|
|
3360
|
+
|
|
3361
|
+
scope.$apply("color = 'white'");
|
|
3362
|
+
expect(inputElm[0].checked).toBe(true);
|
|
3363
|
+
expect(inputElm[1].checked).toBe(false);
|
|
3364
|
+
expect(inputElm[2].checked).toBe(false);
|
|
3365
|
+
|
|
3366
|
+
scope.$apply("color = 'red'");
|
|
3367
|
+
expect(inputElm[0].checked).toBe(false);
|
|
3368
|
+
expect(inputElm[1].checked).toBe(true);
|
|
3369
|
+
expect(inputElm[2].checked).toBe(false);
|
|
3370
|
+
|
|
3371
|
+
if (event === "change") inputElm[2].checked = true;
|
|
3372
|
+
if (event === "click") inputElm[2].click();
|
|
3373
|
+
inputElm[2].dispatchEvent(new Event("change"));
|
|
3374
|
+
expect(scope.color).toBe("blue");
|
|
3375
|
+
});
|
|
3376
|
+
});
|
|
3377
|
+
|
|
3378
|
+
it("should treat the value as a string when evaluating checked-ness", () => {
|
|
3379
|
+
const inputElm = $compile(
|
|
3380
|
+
'<input type="radio" ng-model="model" value="0" />',
|
|
3381
|
+
)(scope);
|
|
3382
|
+
|
|
3383
|
+
scope.$apply("model = '0'");
|
|
3384
|
+
expect(inputElm[0].checked).toBe(true);
|
|
3385
|
+
|
|
3386
|
+
scope.$apply("model = 0");
|
|
3387
|
+
expect(inputElm[0].checked).toBe(false);
|
|
3388
|
+
});
|
|
3389
|
+
|
|
3390
|
+
it("should allow {{expr}} as value", () => {
|
|
3391
|
+
scope.some = 11;
|
|
3392
|
+
const inputElm = $compile(
|
|
3393
|
+
'<input type="radio" ng-model="value" value="{{some}}" />' +
|
|
3394
|
+
'<input type="radio" ng-model="value" value="{{other}}" />',
|
|
3395
|
+
)(scope);
|
|
3396
|
+
|
|
3397
|
+
scope.$apply(() => {
|
|
3398
|
+
scope.value = "blue";
|
|
3399
|
+
scope.some = "blue";
|
|
3400
|
+
scope.other = "red";
|
|
3401
|
+
});
|
|
3402
|
+
|
|
3403
|
+
expect(inputElm[0].checked).toBe(true);
|
|
3404
|
+
expect(inputElm[1].checked).toBe(false);
|
|
3405
|
+
|
|
3406
|
+
inputElm[1].click();
|
|
3407
|
+
inputElm[1].dispatchEvent(new Event("change"));
|
|
3408
|
+
expect(scope.value).toBe("red");
|
|
3409
|
+
|
|
3410
|
+
scope.$apply("other = 'non-red'");
|
|
3411
|
+
|
|
3412
|
+
expect(inputElm[0].checked).toBe(false);
|
|
3413
|
+
expect(inputElm[1].checked).toBe(false);
|
|
3414
|
+
});
|
|
3415
|
+
|
|
3416
|
+
it("should allow the use of ngTrim", () => {
|
|
3417
|
+
scope.some = 11;
|
|
3418
|
+
const inputElm = $compile(
|
|
3419
|
+
'<input type="radio" ng-model="value" value="opt1" />' +
|
|
3420
|
+
'<input type="radio" ng-model="value" value=" opt2 " />' +
|
|
3421
|
+
'<input type="radio" ng-model="value" ng-trim="false" value=" opt3 " />' +
|
|
3422
|
+
'<input type="radio" ng-model="value" ng-trim="false" value="{{some}}" />' +
|
|
3423
|
+
'<input type="radio" ng-model="value" ng-trim="false" value=" {{some}} " />',
|
|
3424
|
+
)(scope);
|
|
3425
|
+
|
|
3426
|
+
scope.$apply(() => {
|
|
3427
|
+
scope.value = "blue";
|
|
3428
|
+
scope.some = "blue";
|
|
3429
|
+
});
|
|
3430
|
+
|
|
3431
|
+
expect(inputElm[0].checked).toBe(false);
|
|
3432
|
+
expect(inputElm[1].checked).toBe(false);
|
|
3433
|
+
expect(inputElm[2].checked).toBe(false);
|
|
3434
|
+
expect(inputElm[3].checked).toBe(true);
|
|
3435
|
+
expect(inputElm[4].checked).toBe(false);
|
|
3436
|
+
|
|
3437
|
+
inputElm[1].click();
|
|
3438
|
+
inputElm[1].dispatchEvent(new Event("change"));
|
|
3439
|
+
expect(scope.value).toBe("opt2");
|
|
3440
|
+
inputElm[2].click();
|
|
3441
|
+
inputElm[2].dispatchEvent(new Event("change"));
|
|
3442
|
+
expect(scope.value).toBe(" opt3 ");
|
|
3443
|
+
inputElm[3].click();
|
|
3444
|
+
inputElm[3].dispatchEvent(new Event("change"));
|
|
3445
|
+
expect(scope.value).toBe("blue");
|
|
3446
|
+
inputElm[4].click();
|
|
3447
|
+
inputElm[4].dispatchEvent(new Event("change"));
|
|
3448
|
+
expect(scope.value).toBe(" blue ");
|
|
3449
|
+
|
|
3450
|
+
scope.$apply("value = ' opt2 '");
|
|
3451
|
+
expect(inputElm[1].checked).toBe(false);
|
|
3452
|
+
scope.$apply("value = 'opt2'");
|
|
3453
|
+
expect(inputElm[1].checked).toBe(true);
|
|
3454
|
+
scope.$apply("value = ' opt3 '");
|
|
3455
|
+
expect(inputElm[2].checked).toBe(true);
|
|
3456
|
+
scope.$apply("value = 'opt3'");
|
|
3457
|
+
expect(inputElm[2].checked).toBe(false);
|
|
3458
|
+
|
|
3459
|
+
scope.$apply("value = 'blue'");
|
|
3460
|
+
expect(inputElm[3].checked).toBe(true);
|
|
3461
|
+
expect(inputElm[4].checked).toBe(false);
|
|
3462
|
+
scope.$apply("value = ' blue '");
|
|
3463
|
+
expect(inputElm[3].checked).toBe(false);
|
|
3464
|
+
expect(inputElm[4].checked).toBe(true);
|
|
3465
|
+
});
|
|
3466
|
+
});
|
|
3467
|
+
|
|
3468
|
+
describe("checkbox", () => {
|
|
3469
|
+
it("should ignore checkbox without ngModel directive", () => {
|
|
3470
|
+
const inputElm = $compile(
|
|
3471
|
+
'<input type="checkbox" name="whatever" required />',
|
|
3472
|
+
)(scope);
|
|
3473
|
+
|
|
3474
|
+
inputElm[0].value = "";
|
|
3475
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3476
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBe(false);
|
|
3477
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBe(false);
|
|
3478
|
+
expect(inputElm[0].classList.contains("ng-pristine")).toBe(false);
|
|
3479
|
+
expect(inputElm[0].classList.contains("ng-dirty")).toBe(false);
|
|
3480
|
+
});
|
|
3481
|
+
|
|
3482
|
+
["click", "change"].forEach((event) => {
|
|
3483
|
+
it("should update the model on $prop event", () => {
|
|
3484
|
+
const inputElm = $compile(
|
|
3485
|
+
'<input type="checkbox" ng-model="checkbox" />',
|
|
3486
|
+
)(scope);
|
|
3487
|
+
|
|
3488
|
+
expect(inputElm[0].checked).toBe(false);
|
|
3489
|
+
|
|
3490
|
+
scope.$apply("checkbox = true");
|
|
3491
|
+
expect(inputElm[0].checked).toBe(true);
|
|
3492
|
+
|
|
3493
|
+
scope.$apply("checkbox = false");
|
|
3494
|
+
expect(inputElm[0].checked).toBe(false);
|
|
3495
|
+
|
|
3496
|
+
if (event === "change") inputElm[0].checked = true;
|
|
3497
|
+
if (event === "click") inputElm[0].click();
|
|
3498
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3499
|
+
expect(scope.checkbox).toBe(true);
|
|
3500
|
+
});
|
|
3501
|
+
});
|
|
3502
|
+
|
|
3503
|
+
it("should format booleans", () => {
|
|
3504
|
+
const inputElm = $compile('<input type="checkbox" ng-model="name" />')(
|
|
3505
|
+
scope,
|
|
3506
|
+
);
|
|
3507
|
+
|
|
3508
|
+
scope.$apply("name = false");
|
|
3509
|
+
expect(inputElm[0].checked).toBe(false);
|
|
3510
|
+
|
|
3511
|
+
scope.$apply("name = true");
|
|
3512
|
+
expect(inputElm[0].checked).toBe(true);
|
|
3513
|
+
});
|
|
3514
|
+
|
|
3515
|
+
it('should support type="checkbox" with non-standard capitalization', () => {
|
|
3516
|
+
const inputElm = $compile(
|
|
3517
|
+
'<input type="checkBox" ng-model="checkbox" />',
|
|
3518
|
+
)(scope);
|
|
3519
|
+
|
|
3520
|
+
inputElm[0].click();
|
|
3521
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3522
|
+
expect(scope.checkbox).toBe(true);
|
|
3523
|
+
|
|
3524
|
+
inputElm[0].click();
|
|
3525
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3526
|
+
expect(scope.checkbox).toBe(false);
|
|
3527
|
+
});
|
|
3528
|
+
|
|
3529
|
+
it("should allow custom enumeration", () => {
|
|
3530
|
+
const inputElm = $compile(
|
|
3531
|
+
'<input type="checkbox" ng-model="name" ng-true-value="\'y\'" ' +
|
|
3532
|
+
"ng-false-value=\"'n'\">",
|
|
3533
|
+
)(scope);
|
|
3534
|
+
|
|
3535
|
+
scope.$apply("name = 'y'");
|
|
3536
|
+
expect(inputElm[0].checked).toBe(true);
|
|
3537
|
+
|
|
3538
|
+
scope.$apply("name = 'n'");
|
|
3539
|
+
expect(inputElm[0].checked).toBe(false);
|
|
3540
|
+
|
|
3541
|
+
scope.$apply("name = 'something else'");
|
|
3542
|
+
expect(inputElm[0].checked).toBe(false);
|
|
3543
|
+
|
|
3544
|
+
inputElm[0].click();
|
|
3545
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3546
|
+
expect(scope.name).toEqual("y");
|
|
3547
|
+
|
|
3548
|
+
inputElm[0].click();
|
|
3549
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3550
|
+
expect(scope.name).toEqual("n");
|
|
3551
|
+
});
|
|
3552
|
+
|
|
3553
|
+
it("should throw if ngTrueValue is present and not a constant expression", () => {
|
|
3554
|
+
expect(() => {
|
|
3555
|
+
const inputElm = $compile(
|
|
3556
|
+
'<input type="checkbox" ng-model="value" ng-true-value="yes" />',
|
|
3557
|
+
)(scope);
|
|
3558
|
+
}).toThrowError(/constexpr/);
|
|
3559
|
+
});
|
|
3560
|
+
|
|
3561
|
+
it("should throw if ngFalseValue is present and not a constant expression", () => {
|
|
3562
|
+
expect(() => {
|
|
3563
|
+
const inputElm = $compile(
|
|
3564
|
+
'<input type="checkbox" ng-model="value" ng-false-value="no" />',
|
|
3565
|
+
)(scope);
|
|
3566
|
+
}).toThrowError(/constexpr/);
|
|
3567
|
+
});
|
|
3568
|
+
|
|
3569
|
+
it("should not throw if ngTrueValue or ngFalseValue are not present", () => {
|
|
3570
|
+
expect(() => {
|
|
3571
|
+
const inputElm = $compile(
|
|
3572
|
+
'<input type="checkbox" ng-model="value" />',
|
|
3573
|
+
)(scope);
|
|
3574
|
+
}).not.toThrow();
|
|
3575
|
+
});
|
|
3576
|
+
|
|
3577
|
+
it("should be required if false", () => {
|
|
3578
|
+
const inputElm = $compile(
|
|
3579
|
+
'<input type="checkbox" ng-model="value" required />',
|
|
3580
|
+
)(scope);
|
|
3581
|
+
|
|
3582
|
+
inputElm[0].click();
|
|
3583
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3584
|
+
expect(inputElm[0].checked).toBe(true);
|
|
3585
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
3586
|
+
|
|
3587
|
+
inputElm[0].click();
|
|
3588
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3589
|
+
expect(inputElm[0].checked).toBe(false);
|
|
3590
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
3591
|
+
});
|
|
3592
|
+
|
|
3593
|
+
it('should pass validation for "required" when trueValue is a string', () => {
|
|
3594
|
+
const formElm = $compile(
|
|
3595
|
+
'<form name="form">' +
|
|
3596
|
+
'<input type="checkbox" required name="cb" ng-model="value" ng-true-value="\'yes\'" />' +
|
|
3597
|
+
"</form>",
|
|
3598
|
+
)(scope);
|
|
3599
|
+
scope.$digest();
|
|
3600
|
+
|
|
3601
|
+
const inputElm = formElm.find("input");
|
|
3602
|
+
|
|
3603
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
|
|
3604
|
+
expect(scope.form.cb.$error.required).toBe(true);
|
|
3605
|
+
|
|
3606
|
+
inputElm[0].click();
|
|
3607
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3608
|
+
expect(inputElm[0].checked).toBe(true);
|
|
3609
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
|
|
3610
|
+
expect(scope.form.cb.$error.required).toBeUndefined();
|
|
3611
|
+
});
|
|
3612
|
+
});
|
|
3613
|
+
|
|
3614
|
+
describe("textarea", () => {
|
|
3615
|
+
it("should process textarea", () => {
|
|
3616
|
+
const inputElm = $compile('<textarea ng-model="name"></textarea>')(
|
|
3617
|
+
scope,
|
|
3618
|
+
);
|
|
3619
|
+
|
|
3620
|
+
scope.$apply("name = 'Adam'");
|
|
3621
|
+
expect(inputElm.val()).toEqual("Adam");
|
|
3622
|
+
|
|
3623
|
+
inputElm[0].value = "Shyam";
|
|
3624
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3625
|
+
expect(scope.name).toEqual("Shyam");
|
|
3626
|
+
|
|
3627
|
+
inputElm[0].value = "Kai";
|
|
3628
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3629
|
+
expect(scope.name).toEqual("Kai");
|
|
3630
|
+
});
|
|
3631
|
+
|
|
3632
|
+
it("should ignore textarea without ngModel directive", () => {
|
|
3633
|
+
const inputElm = $compile(
|
|
3634
|
+
'<textarea name="whatever" required></textarea>',
|
|
3635
|
+
)(scope);
|
|
3636
|
+
|
|
3637
|
+
inputElm[0].value = "";
|
|
3638
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3639
|
+
expect(inputElm[0].classList.contains("ng-valid")).toBe(false);
|
|
3640
|
+
expect(inputElm[0].classList.contains("ng-invalid")).toBe(false);
|
|
3641
|
+
expect(inputElm[0].classList.contains("ng-pristine")).toBe(false);
|
|
3642
|
+
expect(inputElm[0].classList.contains("ng-dirty")).toBe(false);
|
|
3643
|
+
});
|
|
3644
|
+
});
|
|
3645
|
+
|
|
3646
|
+
describe("ngValue", () => {
|
|
3647
|
+
it('should update the dom "value" property and attribute', () => {
|
|
3648
|
+
const inputElm = $compile('<input type="submit" ng-value="value">')(
|
|
3649
|
+
scope,
|
|
3650
|
+
);
|
|
3651
|
+
|
|
3652
|
+
scope.$apply("value = 'something'");
|
|
3653
|
+
|
|
3654
|
+
expect(inputElm[0].value).toBe("something");
|
|
3655
|
+
expect(inputElm[0].getAttribute("value")).toBe("something");
|
|
3656
|
+
});
|
|
3657
|
+
|
|
3658
|
+
it('should clear the "dom" value property and attribute when the value is undefined', () => {
|
|
3659
|
+
const inputElm = $compile('<input type="text" ng-value="value">')(
|
|
3660
|
+
scope,
|
|
3661
|
+
);
|
|
3662
|
+
|
|
3663
|
+
scope.$apply('value = "something"');
|
|
3664
|
+
|
|
3665
|
+
expect(inputElm[0].value).toBe("something");
|
|
3666
|
+
expect(inputElm[0].getAttribute("value")).toBe("something");
|
|
3667
|
+
|
|
3668
|
+
scope.$apply(() => {
|
|
3669
|
+
delete scope.value;
|
|
3670
|
+
});
|
|
3671
|
+
|
|
3672
|
+
expect(inputElm[0].value).toBe("");
|
|
3673
|
+
// Support: IE 9-11, Edge
|
|
3674
|
+
// In IE it is not possible to remove the `value` attribute from an input element.
|
|
3675
|
+
expect(inputElm[0].getAttribute("value")).toBeNull();
|
|
3676
|
+
// } else {
|
|
3677
|
+
// // Support: IE 9-11, Edge
|
|
3678
|
+
// // This will fail if the Edge bug gets fixed
|
|
3679
|
+
// expect(inputElm[0].getAttribute("value")).toBe("something");
|
|
3680
|
+
// }
|
|
3681
|
+
});
|
|
3682
|
+
|
|
3683
|
+
// they(
|
|
3684
|
+
// 'should update the $prop "value" property and attribute after the bound expression changes',
|
|
3685
|
+
// {
|
|
3686
|
+
// input: '<input type="text" ng-value="value">',
|
|
3687
|
+
// textarea: '<textarea ng-value="value"></textarea>',
|
|
3688
|
+
// },
|
|
3689
|
+
// (tmpl) => {
|
|
3690
|
+
// const element = $compile(tmpl)(scope);
|
|
3691
|
+
|
|
3692
|
+
// helper.changeInputValueTo("newValue");
|
|
3693
|
+
// expect(element[0].value).toBe("newValue");
|
|
3694
|
+
// expect(element[0].getAttribute("value")).toBeNull();
|
|
3695
|
+
|
|
3696
|
+
// scope.$apply(() => {
|
|
3697
|
+
// scope.value = "anotherValue";
|
|
3698
|
+
// });
|
|
3699
|
+
// expect(element[0].value).toBe("anotherValue");
|
|
3700
|
+
// expect(element[0].getAttribute("value")).toBe("anotherValue");
|
|
3701
|
+
// },
|
|
3702
|
+
// );
|
|
3703
|
+
|
|
3704
|
+
it("should evaluate and set constant expressions", () => {
|
|
3705
|
+
const inputElm = $compile(
|
|
3706
|
+
'<input type="radio" ng-model="selected" ng-value="true">' +
|
|
3707
|
+
'<input type="radio" ng-model="selected" ng-value="false">' +
|
|
3708
|
+
'<input type="radio" ng-model="selected" ng-value="1">',
|
|
3709
|
+
)(scope);
|
|
3710
|
+
|
|
3711
|
+
inputElm[0].click();
|
|
3712
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3713
|
+
expect(scope.selected).toBe(true);
|
|
3714
|
+
|
|
3715
|
+
inputElm[1].click();
|
|
3716
|
+
inputElm[1].dispatchEvent(new Event("change"));
|
|
3717
|
+
expect(scope.selected).toBe(false);
|
|
3718
|
+
|
|
3719
|
+
inputElm[2].click();
|
|
3720
|
+
inputElm[2].dispatchEvent(new Event("change"));
|
|
3721
|
+
expect(scope.selected).toBe(1);
|
|
3722
|
+
});
|
|
3723
|
+
|
|
3724
|
+
it("should use strict comparison between model and value", () => {
|
|
3725
|
+
scope.selected = false;
|
|
3726
|
+
const inputElm = $compile(
|
|
3727
|
+
'<input type="radio" ng-model="selected" ng-value="false">' +
|
|
3728
|
+
'<input type="radio" ng-model="selected" ng-value="\'\'">' +
|
|
3729
|
+
'<input type="radio" ng-model="selected" ng-value="0">',
|
|
3730
|
+
)(scope);
|
|
3731
|
+
scope.$digest();
|
|
3732
|
+
expect(inputElm[0].checked).toBe(true);
|
|
3733
|
+
expect(inputElm[1].checked).toBe(false);
|
|
3734
|
+
expect(inputElm[2].checked).toBe(false);
|
|
3735
|
+
});
|
|
3736
|
+
|
|
3737
|
+
it("should watch the expression", () => {
|
|
3738
|
+
const inputElm = $compile(
|
|
3739
|
+
'<input type="radio" ng-model="selected" ng-value="value">',
|
|
3740
|
+
)(scope);
|
|
3741
|
+
|
|
3742
|
+
scope.$apply(() => {
|
|
3743
|
+
scope.selected = scope.value = { some: "object" };
|
|
3744
|
+
});
|
|
3745
|
+
expect(inputElm[0].checked).toBe(true);
|
|
3746
|
+
|
|
3747
|
+
scope.$apply(() => {
|
|
3748
|
+
scope.value = { some: "other" };
|
|
3749
|
+
});
|
|
3750
|
+
expect(inputElm[0].checked).toBe(false);
|
|
3751
|
+
|
|
3752
|
+
inputElm[0].click();
|
|
3753
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3754
|
+
expect(scope.selected).toBe(scope.value);
|
|
3755
|
+
});
|
|
3756
|
+
|
|
3757
|
+
it("should work inside ngRepeat", () => {
|
|
3758
|
+
const inputElms = $compile(
|
|
3759
|
+
'<div><input type="radio" ng-repeat="i in items" ng-model="$parent.selected" ng-value="i.id"></div>',
|
|
3760
|
+
)(scope);
|
|
3761
|
+
|
|
3762
|
+
scope.$apply(() => {
|
|
3763
|
+
scope.items = [{ id: 1 }, { id: 2 }];
|
|
3764
|
+
scope.selected = 1;
|
|
3765
|
+
});
|
|
3766
|
+
|
|
3767
|
+
scope.$digest();
|
|
3768
|
+
|
|
3769
|
+
expect(inputElms[0].children[0].checked).toBe(true);
|
|
3770
|
+
expect(inputElms[0].children[1].checked).toBe(false);
|
|
3771
|
+
|
|
3772
|
+
inputElms[0].children[1].click();
|
|
3773
|
+
inputElms[0].children[1].dispatchEvent(new Event("change"));
|
|
3774
|
+
|
|
3775
|
+
expect(scope.selected).toBe(2);
|
|
3776
|
+
});
|
|
3777
|
+
});
|
|
3778
|
+
|
|
3779
|
+
describe("password", () => {
|
|
3780
|
+
// Under no circumstances should input[type=password] trim inputs
|
|
3781
|
+
it("should not trim if ngTrim is unspecified", () => {
|
|
3782
|
+
const inputElm = $compile(
|
|
3783
|
+
'<input type="password" ng-model="password">',
|
|
3784
|
+
)(scope);
|
|
3785
|
+
|
|
3786
|
+
inputElm[0].value = " - - untrimmed - - ";
|
|
3787
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3788
|
+
expect(scope.password.length).toBe(" - - untrimmed - - ".length);
|
|
3789
|
+
});
|
|
3790
|
+
|
|
3791
|
+
it("should not trim if ngTrim !== false", () => {
|
|
3792
|
+
const inputElm = $compile(
|
|
3793
|
+
'<input type="password" ng-model="password" ng-trim="true">',
|
|
3794
|
+
)(scope);
|
|
3795
|
+
inputElm[0].value = " - - untrimmed - - ";
|
|
3796
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3797
|
+
expect(scope.password.length).toBe(" - - untrimmed - - ".length);
|
|
3798
|
+
});
|
|
3799
|
+
|
|
3800
|
+
it("should not trim if ngTrim === false", () => {
|
|
3801
|
+
const inputElm = $compile(
|
|
3802
|
+
'<input type="password" ng-model="password" ng-trim="false">',
|
|
3803
|
+
)(scope);
|
|
3804
|
+
inputElm[0].value = " - - untrimmed - - ";
|
|
3805
|
+
inputElm[0].dispatchEvent(new Event("change"));
|
|
3806
|
+
expect(scope.password.length).toBe(" - - untrimmed - - ".length);
|
|
3807
|
+
});
|
|
3808
|
+
});
|
|
3809
|
+
});
|
|
3810
|
+
});
|