@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.
Files changed (231) hide show
  1. package/.eslintignore +1 -0
  2. package/.eslintrc.cjs +29 -0
  3. package/.github/workflows/playwright.yml +27 -0
  4. package/CHANGELOG.md +17974 -0
  5. package/CODE_OF_CONDUCT.md +3 -0
  6. package/CONTRIBUTING.md +246 -0
  7. package/DEVELOPERS.md +488 -0
  8. package/LICENSE +22 -0
  9. package/Makefile +31 -0
  10. package/README.md +115 -0
  11. package/RELEASE.md +98 -0
  12. package/SECURITY.md +16 -0
  13. package/TRIAGING.md +135 -0
  14. package/css/angular.css +22 -0
  15. package/dist/angular-ts.cjs.js +36843 -0
  16. package/dist/angular-ts.esm.js +36841 -0
  17. package/dist/angular-ts.umd.js +36848 -0
  18. package/dist/build/angular-animate.js +4272 -0
  19. package/dist/build/angular-aria.js +426 -0
  20. package/dist/build/angular-message-format.js +1072 -0
  21. package/dist/build/angular-messages.js +829 -0
  22. package/dist/build/angular-mocks.js +3757 -0
  23. package/dist/build/angular-parse-ext.js +1275 -0
  24. package/dist/build/angular-resource.js +911 -0
  25. package/dist/build/angular-route.js +1266 -0
  26. package/dist/build/angular-sanitize.js +891 -0
  27. package/dist/build/angular-touch.js +368 -0
  28. package/dist/build/angular.js +36600 -0
  29. package/e2e/unit.spec.ts +15 -0
  30. package/images/android-chrome-192x192.png +0 -0
  31. package/images/android-chrome-512x512.png +0 -0
  32. package/images/apple-touch-icon.png +0 -0
  33. package/images/favicon-16x16.png +0 -0
  34. package/images/favicon-32x32.png +0 -0
  35. package/images/favicon.ico +0 -0
  36. package/images/site.webmanifest +1 -0
  37. package/index.html +104 -0
  38. package/package.json +47 -0
  39. package/playwright.config.ts +78 -0
  40. package/public/circle.html +1 -0
  41. package/public/my_child_directive.html +1 -0
  42. package/public/my_directive.html +1 -0
  43. package/public/my_other_directive.html +1 -0
  44. package/public/test.html +1 -0
  45. package/rollup.config.js +31 -0
  46. package/src/animations/animateCache.js +55 -0
  47. package/src/animations/animateChildrenDirective.js +105 -0
  48. package/src/animations/animateCss.js +1139 -0
  49. package/src/animations/animateCssDriver.js +291 -0
  50. package/src/animations/animateJs.js +367 -0
  51. package/src/animations/animateJsDriver.js +67 -0
  52. package/src/animations/animateQueue.js +851 -0
  53. package/src/animations/animation.js +506 -0
  54. package/src/animations/module.js +779 -0
  55. package/src/animations/ngAnimateSwap.js +119 -0
  56. package/src/animations/rafScheduler.js +50 -0
  57. package/src/animations/shared.js +378 -0
  58. package/src/constants.js +20 -0
  59. package/src/core/animate.js +845 -0
  60. package/src/core/animateCss.js +73 -0
  61. package/src/core/animateRunner.js +195 -0
  62. package/src/core/attributes.js +199 -0
  63. package/src/core/cache.js +45 -0
  64. package/src/core/compile.js +4727 -0
  65. package/src/core/controller.js +225 -0
  66. package/src/core/exceptionHandler.js +63 -0
  67. package/src/core/filter.js +146 -0
  68. package/src/core/interpolate.js +442 -0
  69. package/src/core/interval.js +188 -0
  70. package/src/core/intervalFactory.js +57 -0
  71. package/src/core/location.js +1086 -0
  72. package/src/core/parser/parse.js +2562 -0
  73. package/src/core/parser/parse.md +13 -0
  74. package/src/core/q.js +746 -0
  75. package/src/core/rootScope.js +1596 -0
  76. package/src/core/sanitizeUri.js +85 -0
  77. package/src/core/sce.js +1161 -0
  78. package/src/core/taskTrackerFactory.js +125 -0
  79. package/src/core/timeout.js +121 -0
  80. package/src/core/urlUtils.js +187 -0
  81. package/src/core/utils.js +1349 -0
  82. package/src/directive/a.js +37 -0
  83. package/src/directive/attrs.js +283 -0
  84. package/src/directive/bind.js +51 -0
  85. package/src/directive/bind.md +142 -0
  86. package/src/directive/change.js +12 -0
  87. package/src/directive/change.md +25 -0
  88. package/src/directive/cloak.js +12 -0
  89. package/src/directive/cloak.md +24 -0
  90. package/src/directive/events.js +75 -0
  91. package/src/directive/events.md +166 -0
  92. package/src/directive/form.js +725 -0
  93. package/src/directive/init.js +15 -0
  94. package/src/directive/init.md +41 -0
  95. package/src/directive/input.js +1783 -0
  96. package/src/directive/list.js +46 -0
  97. package/src/directive/list.md +22 -0
  98. package/src/directive/ngClass.js +249 -0
  99. package/src/directive/ngController.js +64 -0
  100. package/src/directive/ngCsp.js +82 -0
  101. package/src/directive/ngIf.js +134 -0
  102. package/src/directive/ngInclude.js +217 -0
  103. package/src/directive/ngModel.js +1356 -0
  104. package/src/directive/ngModelOptions.js +509 -0
  105. package/src/directive/ngOptions.js +670 -0
  106. package/src/directive/ngRef.js +90 -0
  107. package/src/directive/ngRepeat.js +650 -0
  108. package/src/directive/ngShowHide.js +255 -0
  109. package/src/directive/ngSwitch.js +178 -0
  110. package/src/directive/ngTransclude.js +98 -0
  111. package/src/directive/non-bindable.js +11 -0
  112. package/src/directive/non-bindable.md +17 -0
  113. package/src/directive/script.js +30 -0
  114. package/src/directive/select.js +624 -0
  115. package/src/directive/style.js +25 -0
  116. package/src/directive/style.md +23 -0
  117. package/src/directive/validators.js +329 -0
  118. package/src/exts/aria.js +544 -0
  119. package/src/exts/messages.js +852 -0
  120. package/src/filters/filter.js +207 -0
  121. package/src/filters/filter.md +69 -0
  122. package/src/filters/filters.js +239 -0
  123. package/src/filters/json.md +16 -0
  124. package/src/filters/limit-to.js +43 -0
  125. package/src/filters/limit-to.md +19 -0
  126. package/src/filters/order-by.js +183 -0
  127. package/src/filters/order-by.md +83 -0
  128. package/src/index.js +13 -0
  129. package/src/injector.js +1034 -0
  130. package/src/jqLite.js +1117 -0
  131. package/src/loader.js +1320 -0
  132. package/src/public.js +215 -0
  133. package/src/routeToRegExp.js +41 -0
  134. package/src/services/anchorScroll.js +135 -0
  135. package/src/services/browser.js +321 -0
  136. package/src/services/cacheFactory.js +398 -0
  137. package/src/services/cookieReader.js +72 -0
  138. package/src/services/document.js +64 -0
  139. package/src/services/http.js +1537 -0
  140. package/src/services/httpBackend.js +206 -0
  141. package/src/services/log.js +160 -0
  142. package/src/services/templateRequest.js +139 -0
  143. package/test/angular.spec.js +2153 -0
  144. package/test/aria/aria.spec.js +1245 -0
  145. package/test/binding.spec.js +504 -0
  146. package/test/build-test.html +14 -0
  147. package/test/injector.spec.js +2327 -0
  148. package/test/jasmine/jasmine-5.1.2/boot0.js +65 -0
  149. package/test/jasmine/jasmine-5.1.2/boot1.js +133 -0
  150. package/test/jasmine/jasmine-5.1.2/jasmine-html.js +963 -0
  151. package/test/jasmine/jasmine-5.1.2/jasmine.css +320 -0
  152. package/test/jasmine/jasmine-5.1.2/jasmine.js +10824 -0
  153. package/test/jasmine/jasmine-5.1.2/jasmine_favicon.png +0 -0
  154. package/test/jasmine/jasmine-browser.json +17 -0
  155. package/test/jasmine/jasmine.json +9 -0
  156. package/test/jqlite.spec.js +2133 -0
  157. package/test/loader.spec.js +219 -0
  158. package/test/messages/messages.spec.js +1146 -0
  159. package/test/min-err.spec.js +174 -0
  160. package/test/mock-test.html +13 -0
  161. package/test/module-test.html +15 -0
  162. package/test/ng/anomate.spec.js +606 -0
  163. package/test/ng/cache-factor.spec.js +334 -0
  164. package/test/ng/compile.spec.js +17956 -0
  165. package/test/ng/controller-provider.spec.js +227 -0
  166. package/test/ng/cookie-reader.spec.js +98 -0
  167. package/test/ng/directive/a.spec.js +192 -0
  168. package/test/ng/directive/bind.spec.js +334 -0
  169. package/test/ng/directive/boolean.spec.js +136 -0
  170. package/test/ng/directive/change.spec.js +71 -0
  171. package/test/ng/directive/class.spec.js +858 -0
  172. package/test/ng/directive/click.spec.js +38 -0
  173. package/test/ng/directive/cloak.spec.js +44 -0
  174. package/test/ng/directive/constoller.spec.js +194 -0
  175. package/test/ng/directive/element-style.spec.js +92 -0
  176. package/test/ng/directive/event.spec.js +282 -0
  177. package/test/ng/directive/form.spec.js +1518 -0
  178. package/test/ng/directive/href.spec.js +143 -0
  179. package/test/ng/directive/if.spec.js +402 -0
  180. package/test/ng/directive/include.spec.js +828 -0
  181. package/test/ng/directive/init.spec.js +68 -0
  182. package/test/ng/directive/input.spec.js +3810 -0
  183. package/test/ng/directive/list.spec.js +170 -0
  184. package/test/ng/directive/model-options.spec.js +1008 -0
  185. package/test/ng/directive/model.spec.js +1905 -0
  186. package/test/ng/directive/non-bindable.spec.js +55 -0
  187. package/test/ng/directive/options.spec.js +3583 -0
  188. package/test/ng/directive/ref.spec.js +575 -0
  189. package/test/ng/directive/repeat.spec.js +1675 -0
  190. package/test/ng/directive/script.spec.js +52 -0
  191. package/test/ng/directive/scrset.spec.js +67 -0
  192. package/test/ng/directive/select.spec.js +2541 -0
  193. package/test/ng/directive/show-hide.spec.js +253 -0
  194. package/test/ng/directive/src.spec.js +157 -0
  195. package/test/ng/directive/style.spec.js +178 -0
  196. package/test/ng/directive/switch.spec.js +647 -0
  197. package/test/ng/directive/validators.spec.js +717 -0
  198. package/test/ng/document.spec.js +52 -0
  199. package/test/ng/filter/filter.spec.js +714 -0
  200. package/test/ng/filter/filters.spec.js +35 -0
  201. package/test/ng/filter/limit-to.spec.js +251 -0
  202. package/test/ng/filter/order-by.spec.js +891 -0
  203. package/test/ng/filter.spec.js +149 -0
  204. package/test/ng/http-backend.spec.js +398 -0
  205. package/test/ng/http.spec.js +4071 -0
  206. package/test/ng/interpolate.spec.js +642 -0
  207. package/test/ng/interval.spec.js +343 -0
  208. package/test/ng/location.spec.js +3488 -0
  209. package/test/ng/on.spec.js +229 -0
  210. package/test/ng/parse.spec.js +4655 -0
  211. package/test/ng/prop.spec.js +805 -0
  212. package/test/ng/q.spec.js +2904 -0
  213. package/test/ng/root-element.spec.js +16 -0
  214. package/test/ng/sanitize-uri.spec.js +249 -0
  215. package/test/ng/sce.spec.js +660 -0
  216. package/test/ng/scope.spec.js +3442 -0
  217. package/test/ng/template-request.spec.js +236 -0
  218. package/test/ng/timeout.spec.js +351 -0
  219. package/test/ng/url-utils.spec.js +156 -0
  220. package/test/ng/utils.spec.js +144 -0
  221. package/test/original-test.html +21 -0
  222. package/test/public.spec.js +34 -0
  223. package/test/sanitize/bing-html.spec.js +36 -0
  224. package/test/server/express.js +158 -0
  225. package/test/test-utils.js +11 -0
  226. package/tsconfig.json +17 -0
  227. package/types/angular.d.ts +138 -0
  228. package/types/global.d.ts +9 -0
  229. package/types/index.d.ts +2357 -0
  230. package/types/jqlite.d.ts +558 -0
  231. package/vite.config.js +14 -0
@@ -0,0 +1,717 @@
1
+ import { publishExternalAPI } from "../../../src/public";
2
+ import { createInjector } from "../../../src/injector";
3
+ import { dealoc } from "../../../src/jqLite";
4
+
5
+ describe("validators", () => {
6
+ let $rootScope;
7
+ let $compile;
8
+ let inputElm;
9
+
10
+ beforeEach(() => {
11
+ publishExternalAPI().decorator("$exceptionHandler", function () {
12
+ return (exception, cause) => {
13
+ console.error(exception);
14
+ throw new Error(exception);
15
+ };
16
+ });
17
+ createInjector(["ng"]).invoke((_$compile_, _$rootScope_) => {
18
+ $compile = _$compile_;
19
+ $rootScope = _$rootScope_;
20
+ });
21
+ });
22
+
23
+ afterEach(() => {
24
+ dealoc(inputElm);
25
+ });
26
+
27
+ describe("pattern", () => {
28
+ it("should validate in-lined pattern", () => {
29
+ inputElm = $compile(
30
+ '<input type="text" ng-model="value" ng-pattern="/^\\d\\d\\d-\\d\\d-\\d\\d\\d\\d$/" />',
31
+ )($rootScope);
32
+
33
+ inputElm[0].setAttribute("value", "x000-00-0000x");
34
+ inputElm[0].dispatchEvent(new Event("change"));
35
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
36
+
37
+ inputElm[0].setAttribute("value", "000-00-0000");
38
+ inputElm[0].dispatchEvent(new Event("change"));
39
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
40
+
41
+ inputElm[0].setAttribute("value", "000-00-0000x");
42
+ inputElm[0].dispatchEvent(new Event("change"));
43
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
44
+
45
+ inputElm[0].setAttribute("value", "123-45-6789");
46
+ inputElm[0].dispatchEvent(new Event("change"));
47
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
48
+
49
+ inputElm[0].setAttribute("value", "x");
50
+ inputElm[0].dispatchEvent(new Event("change"));
51
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
52
+ });
53
+
54
+ it("should listen on ng-pattern when pattern is changed", () => {
55
+ const patternVal = /^\w+$/;
56
+ inputElm = $compile(
57
+ '<input type="text" ng-model="value" ng-pattern="pat" />',
58
+ )($rootScope);
59
+
60
+ $rootScope.pat = patternVal;
61
+ $rootScope.$apply();
62
+ expect(inputElm[0].getAttribute("ng-pattern")).toEqual("/^\\w+$/");
63
+ });
64
+
65
+ it("should validate in-lined pattern with modifiers", () => {
66
+ inputElm = $compile(
67
+ '<input type="text" ng-model="value" ng-pattern="/^abc?$/i" />',
68
+ )($rootScope);
69
+
70
+ inputElm[0].setAttribute("value", "aB");
71
+ inputElm[0].dispatchEvent(new Event("change"));
72
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
73
+
74
+ inputElm[0].setAttribute("value", "xx");
75
+ inputElm[0].dispatchEvent(new Event("change"));
76
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
77
+ });
78
+
79
+ it("should validate pattern from scope", () => {
80
+ $rootScope.regexp = /^\d\d\d-\d\d-\d\d\d\d$/;
81
+ inputElm = $compile(
82
+ '<input type="text" ng-model="value" ng-pattern="regexp" />',
83
+ )($rootScope);
84
+
85
+ inputElm[0].setAttribute("value", "x000-00-0000x");
86
+ inputElm[0].dispatchEvent(new Event("change"));
87
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
88
+
89
+ inputElm[0].setAttribute("value", "000-00-0000");
90
+ inputElm[0].dispatchEvent(new Event("change"));
91
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
92
+
93
+ inputElm[0].setAttribute("value", "000-00-0000x");
94
+ inputElm[0].dispatchEvent(new Event("change"));
95
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
96
+
97
+ inputElm[0].setAttribute("value", "123-45-6789");
98
+ inputElm[0].dispatchEvent(new Event("change"));
99
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
100
+
101
+ inputElm[0].setAttribute("value", "x");
102
+ inputElm[0].dispatchEvent(new Event("change"));
103
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
104
+
105
+ $rootScope.$apply(() => {
106
+ $rootScope.regexp = /abc?/;
107
+ });
108
+
109
+ inputElm[0].setAttribute("value", "ab");
110
+ inputElm[0].dispatchEvent(new Event("change"));
111
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
112
+
113
+ inputElm[0].setAttribute("value", "xx");
114
+ inputElm[0].dispatchEvent(new Event("change"));
115
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
116
+ });
117
+
118
+ it("should perform validations when the ngPattern scope value changes", () => {
119
+ $rootScope.regexp = /^[a-z]+$/;
120
+ inputElm = $compile(
121
+ '<input type="text" ng-model="value" ng-pattern="regexp" />',
122
+ )($rootScope);
123
+
124
+ inputElm[0].setAttribute("value", "abcdef");
125
+ inputElm[0].dispatchEvent(new Event("change"));
126
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
127
+
128
+ inputElm[0].setAttribute("value", "123");
129
+ inputElm[0].dispatchEvent(new Event("change"));
130
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
131
+
132
+ $rootScope.$apply(() => {
133
+ $rootScope.regexp = /^\d+$/;
134
+ });
135
+
136
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
137
+
138
+ inputElm[0].setAttribute("value", "abcdef");
139
+ inputElm[0].dispatchEvent(new Event("change"));
140
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
141
+
142
+ $rootScope.$apply(() => {
143
+ $rootScope.regexp = "";
144
+ });
145
+
146
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
147
+ });
148
+
149
+ it('should register "pattern" with the model validations when the pattern attribute is used', () => {
150
+ const formElm = $compile(
151
+ '<form name="form"><input type="text" name="input" ng-model="value" pattern="^\\d+$" /></form>',
152
+ )($rootScope);
153
+ inputElm = formElm.find("input");
154
+
155
+ inputElm[0].value = "abcd";
156
+ inputElm[0].dispatchEvent(new Event("change"));
157
+ $rootScope.$apply();
158
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
159
+ expect($rootScope.form.input.$error.pattern).toBe(true);
160
+
161
+ inputElm[0].value = "12345";
162
+ inputElm[0].dispatchEvent(new Event("change"));
163
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
164
+ expect($rootScope.form.input.$error.pattern).not.toBe(true);
165
+ });
166
+
167
+ it("should not throw an error when scope pattern can't be found", () => {
168
+ expect(() => {
169
+ inputElm = $compile(
170
+ '<input type="text" ng-model="foo" ng-pattern="fooRegexp" />',
171
+ )($rootScope);
172
+ $rootScope.$apply("foo = 'bar'");
173
+ }).not.toThrow();
174
+ });
175
+
176
+ it("should throw an error when the scope pattern is not a regular expression", () => {
177
+ expect(() => {
178
+ inputElm = $compile(
179
+ '<input type="text" ng-model="foo" ng-pattern="fooRegexp" />',
180
+ )($rootScope);
181
+ $rootScope.$apply(() => {
182
+ $rootScope.fooRegexp = {};
183
+ $rootScope.foo = "bar";
184
+ });
185
+ }).toThrowError(/Expected fooRegexp/);
186
+ });
187
+
188
+ it("should be invalid if entire string does not match pattern", () => {
189
+ let formElm = $compile(
190
+ '<form name="form"><input type="text" name="test" ng-model="value" pattern="\\d{4}"></form>',
191
+ )($rootScope);
192
+ inputElm = formElm.find("input");
193
+ inputElm[0].setAttribute("value", "1234");
194
+ inputElm[0].dispatchEvent(new Event("change"));
195
+ expect($rootScope.form.test.$error.pattern).not.toBe(true);
196
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
197
+
198
+ inputElm[0].setAttribute("value", "123");
199
+ inputElm[0].dispatchEvent(new Event("change"));
200
+ expect($rootScope.form.test.$error.pattern).toBe(true);
201
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
202
+
203
+ inputElm[0].setAttribute("value", "12345");
204
+ inputElm[0].dispatchEvent(new Event("change"));
205
+ expect($rootScope.form.test.$error.pattern).toBe(true);
206
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
207
+ });
208
+
209
+ it("should be cope with patterns that start with ^", () => {
210
+ let formElm = $compile(
211
+ '<form name="form"><input type="text" name="test" ng-model="value" pattern="^\\d{4}"></form>',
212
+ )($rootScope);
213
+ inputElm = formElm.find("input");
214
+ inputElm[0].setAttribute("value", "1234");
215
+ inputElm[0].dispatchEvent(new Event("change"));
216
+ expect($rootScope.form.test.$error.pattern).not.toBe(true);
217
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
218
+
219
+ inputElm[0].setAttribute("value", "123");
220
+ inputElm[0].dispatchEvent(new Event("change"));
221
+ expect($rootScope.form.test.$error.pattern).toBe(true);
222
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
223
+
224
+ inputElm[0].setAttribute("value", "12345");
225
+ inputElm[0].dispatchEvent(new Event("change"));
226
+ expect($rootScope.form.test.$error.pattern).toBe(true);
227
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
228
+ });
229
+
230
+ it("should be cope with patterns that end with $", () => {
231
+ let formElm = $compile(
232
+ '<form name="form"><input type="text" name="test" ng-model="value" pattern="\\d{4}$"></form>',
233
+ )($rootScope);
234
+ inputElm = formElm.find("input");
235
+ inputElm[0].setAttribute("value", "1234");
236
+ inputElm[0].dispatchEvent(new Event("change"));
237
+ expect($rootScope.form.test.$error.pattern).not.toBe(true);
238
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
239
+
240
+ inputElm[0].setAttribute("value", "123");
241
+ inputElm[0].dispatchEvent(new Event("change"));
242
+ expect($rootScope.form.test.$error.pattern).toBe(true);
243
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
244
+
245
+ inputElm[0].setAttribute("value", "12345");
246
+ inputElm[0].dispatchEvent(new Event("change"));
247
+ expect($rootScope.form.test.$error.pattern).toBe(true);
248
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
249
+ });
250
+
251
+ it("should validate the viewValue and not the modelValue", () => {
252
+ let formElm = $compile(
253
+ '<form name="form"><input type="text" name="test" ng-model="value" pattern="\\d{4}"></form>',
254
+ )($rootScope);
255
+ inputElm = formElm.find("input");
256
+ const ctrl = inputElm.controller("ngModel");
257
+
258
+ ctrl.$parsers.push((value) => `${value * 10}`);
259
+
260
+ inputElm[0].setAttribute("value", "1234");
261
+ inputElm[0].dispatchEvent(new Event("change"));
262
+ expect($rootScope.form.test.$error.pattern).not.toBe(true);
263
+ expect($rootScope.form.test.$modelValue).toBe("12340");
264
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
265
+ });
266
+
267
+ it("should validate on non-input elements", () => {
268
+ $rootScope.pattern = "\\d{4}";
269
+ const elm = $compile('<span ng-model="value" pattern="\\d{4}"></span>')(
270
+ $rootScope,
271
+ );
272
+ const elmNg = $compile(
273
+ '<span ng-model="value" ng-pattern="pattern"></span>',
274
+ )($rootScope);
275
+ const ctrl = elm.controller("ngModel");
276
+ const ctrlNg = elmNg.controller("ngModel");
277
+
278
+ expect(ctrl.$error.pattern).not.toBe(true);
279
+ expect(ctrlNg.$error.pattern).not.toBe(true);
280
+
281
+ ctrl.$setViewValue("12");
282
+ ctrlNg.$setViewValue("12");
283
+
284
+ expect(ctrl.$error.pattern).toBe(true);
285
+ expect(ctrlNg.$error.pattern).toBe(true);
286
+ });
287
+ });
288
+
289
+ describe("minlength", () => {
290
+ it("should invalidate values that are shorter than the given minlength", () => {
291
+ inputElm = $compile(
292
+ '<input type="text" ng-model="value" ng-minlength="3" />',
293
+ )($rootScope);
294
+
295
+ inputElm[0].setAttribute("value", "aa");
296
+ inputElm[0].dispatchEvent(new Event("change"));
297
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
298
+
299
+ inputElm[0].setAttribute("value", "aaa");
300
+ inputElm[0].dispatchEvent(new Event("change"));
301
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
302
+ });
303
+
304
+ it("should observe the standard minlength attribute and register it as a validator on the model", () => {
305
+ let formElm = $compile(
306
+ '<form name="form"><input type="text" name="input" ng-model="value" minlength="{{ min }}" /></form>',
307
+ )($rootScope);
308
+ inputElm = formElm.find("input");
309
+ $rootScope.$apply("min = 10");
310
+
311
+ inputElm[0].setAttribute("value", "12345");
312
+ inputElm[0].dispatchEvent(new Event("change"));
313
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
314
+ expect($rootScope.form.input.$error.minlength).toBe(true);
315
+
316
+ $rootScope.$apply("min = 5");
317
+
318
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
319
+ expect($rootScope.form.input.$error.minlength).not.toBe(true);
320
+ });
321
+
322
+ it("should validate when the model is initialized as a number", () => {
323
+ $rootScope.value = 12345;
324
+ let formElm = $compile(
325
+ '<form name="form"><input type="text" name="input" ng-model="value" minlength="3" /></form>',
326
+ )($rootScope);
327
+ inputElm = formElm.find("input");
328
+ expect($rootScope.value).toBe(12345);
329
+ expect($rootScope.form.input.$error.minlength).toBeUndefined();
330
+ });
331
+
332
+ it("should validate emptiness against the viewValue", () => {
333
+ inputElm = $compile(
334
+ '<input type="text" name="input" ng-model="value" minlength="3" />',
335
+ )($rootScope);
336
+
337
+ const ctrl = inputElm.controller("ngModel");
338
+ spyOn(ctrl, "$isEmpty").and.callThrough();
339
+
340
+ ctrl.$parsers.push((value) => `${value}678`);
341
+
342
+ inputElm[0].setAttribute("value", "12345");
343
+ inputElm[0].dispatchEvent(new Event("change"));
344
+ expect(ctrl.$isEmpty).toHaveBeenCalledWith("12345");
345
+ });
346
+
347
+ it("should validate on non-input elements", () => {
348
+ $rootScope.min = 3;
349
+ const elm = $compile(
350
+ '<span ng-model="value" minlength="{{min}}"></span>',
351
+ )($rootScope);
352
+ const elmNg = $compile(
353
+ '<span ng-model="value" ng-minlength="min"></span>',
354
+ )($rootScope);
355
+ const ctrl = elm.controller("ngModel");
356
+ const ctrlNg = elmNg.controller("ngModel");
357
+
358
+ expect(ctrl.$error.minlength).not.toBe(true);
359
+ expect(ctrlNg.$error.minlength).not.toBe(true);
360
+
361
+ ctrl.$setViewValue("12");
362
+ ctrlNg.$setViewValue("12");
363
+
364
+ expect(ctrl.$error.minlength).toBe(true);
365
+ expect(ctrlNg.$error.minlength).toBe(true);
366
+ });
367
+ });
368
+
369
+ describe("maxlength", () => {
370
+ it("should invalidate values that are longer than the given maxlength", () => {
371
+ inputElm = $compile(
372
+ '<input type="text" ng-model="value" ng-maxlength="5" />',
373
+ )($rootScope);
374
+
375
+ inputElm[0].setAttribute("value", "aaaaaaaa");
376
+ inputElm[0].dispatchEvent(new Event("change"));
377
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
378
+
379
+ inputElm[0].setAttribute("value", "aaa");
380
+ inputElm[0].dispatchEvent(new Event("change"));
381
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
382
+ });
383
+
384
+ it("should only accept empty values when maxlength is 0", () => {
385
+ inputElm = $compile(
386
+ '<input type="text" ng-model="value" ng-maxlength="0" />',
387
+ )($rootScope);
388
+
389
+ inputElm[0].setAttribute("value", "");
390
+ inputElm[0].dispatchEvent(new Event("change"));
391
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
392
+
393
+ inputElm[0].setAttribute("value", "a");
394
+ inputElm[0].dispatchEvent(new Event("change"));
395
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
396
+ });
397
+
398
+ it("should accept values of any length when maxlength is negative", () => {
399
+ inputElm = $compile(
400
+ '<input type="text" ng-model="value" ng-maxlength="-1" />',
401
+ )($rootScope);
402
+
403
+ inputElm[0].setAttribute("value", "");
404
+ inputElm[0].dispatchEvent(new Event("change"));
405
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
406
+
407
+ inputElm[0].setAttribute("value", "aaaaaaaaaa");
408
+ inputElm[0].dispatchEvent(new Event("change"));
409
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
410
+ });
411
+
412
+ it("should accept values of any length when maxlength is non-numeric", () => {
413
+ inputElm = $compile(
414
+ '<input type="text" ng-model="value" ng-maxlength="maxlength" />',
415
+ )($rootScope);
416
+ inputElm[0].setAttribute("value", "aaaaaaaaaa");
417
+ inputElm[0].dispatchEvent(new Event("change"));
418
+
419
+ $rootScope.$apply('maxlength = "5"');
420
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
421
+
422
+ $rootScope.$apply('maxlength = "abc"');
423
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
424
+
425
+ $rootScope.$apply('maxlength = ""');
426
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
427
+
428
+ $rootScope.$apply("maxlength = null");
429
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
430
+
431
+ $rootScope.someObj = {};
432
+ $rootScope.$apply("maxlength = someObj");
433
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
434
+ });
435
+
436
+ it("should observe the standard maxlength attribute and register it as a validator on the model", () => {
437
+ let formElm = $compile(
438
+ '<form name="form"><input type="text" name="input" ng-model="value" maxlength="{{ max }}" /></form>',
439
+ )($rootScope);
440
+ inputElm = formElm.find("input");
441
+ $rootScope.$apply("max = 1");
442
+
443
+ inputElm[0].setAttribute("value", "12345");
444
+ inputElm[0].dispatchEvent(new Event("change"));
445
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
446
+ expect($rootScope.form.input.$error.maxlength).toBe(true);
447
+
448
+ $rootScope.$apply("max = 6");
449
+
450
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
451
+ expect($rootScope.form.input.$error.maxlength).not.toBe(true);
452
+ });
453
+
454
+ it("should assign the correct model after an observed validator became valid", () => {
455
+ inputElm = $compile(
456
+ '<input type="text" name="input" ng-model="value" maxlength="{{ max }}" />',
457
+ )($rootScope);
458
+
459
+ $rootScope.$apply("max = 1");
460
+ inputElm[0].setAttribute("value", "12345");
461
+ inputElm[0].dispatchEvent(new Event("change"));
462
+ expect($rootScope.value).toBeUndefined();
463
+
464
+ $rootScope.$apply("max = 6");
465
+ expect($rootScope.value).toBe("12345");
466
+ });
467
+
468
+ it("should assign the correct model after an observed validator became invalid", () => {
469
+ inputElm = $compile(
470
+ '<input type="text" name="input" ng-model="value" maxlength="{{ max }}" />',
471
+ )($rootScope);
472
+
473
+ $rootScope.$apply("max = 6");
474
+ inputElm[0].setAttribute("value", "12345");
475
+ inputElm[0].dispatchEvent(new Event("change"));
476
+ expect($rootScope.value).toBe("12345");
477
+
478
+ $rootScope.$apply("max = 1");
479
+ expect($rootScope.value).toBeUndefined();
480
+ });
481
+
482
+ it("should leave the value as invalid if observed maxlength changed, but is still invalid", () => {
483
+ let formElm = $compile(
484
+ '<form name="form"><input type="text" name="input" ng-model="value" maxlength="{{ max }}" /></form>',
485
+ )($rootScope);
486
+ inputElm = formElm.find("input");
487
+ $rootScope.$apply("max = 1");
488
+
489
+ inputElm[0].setAttribute("value", "12345");
490
+ inputElm[0].dispatchEvent(new Event("change"));
491
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
492
+ expect($rootScope.form.input.$error.maxlength).toBe(true);
493
+ expect($rootScope.value).toBeUndefined();
494
+
495
+ $rootScope.$apply("max = 3");
496
+
497
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
498
+ expect($rootScope.form.input.$error.maxlength).toBe(true);
499
+ expect($rootScope.value).toBeUndefined();
500
+ });
501
+
502
+ it("should not notify if observed maxlength changed, but is still invalid", () => {
503
+ inputElm = $compile(
504
+ '<input type="text" name="input" ng-model="value" ng-change="ngChangeSpy()" ' +
505
+ 'maxlength="{{ max }}" />',
506
+ )($rootScope);
507
+
508
+ $rootScope.$apply("max = 1");
509
+ inputElm[0].setAttribute("value", "12345");
510
+ inputElm[0].dispatchEvent(new Event("change"));
511
+
512
+ $rootScope.ngChangeSpy = jasmine.createSpy();
513
+ $rootScope.$apply("max = 3");
514
+
515
+ expect($rootScope.ngChangeSpy).not.toHaveBeenCalled();
516
+ });
517
+
518
+ it("should leave the model untouched when validating before model initialization", () => {
519
+ $rootScope.value = "12345";
520
+ inputElm = $compile(
521
+ '<input type="text" name="input" ng-model="value" minlength="3" />',
522
+ )($rootScope);
523
+ expect($rootScope.value).toBe("12345");
524
+ });
525
+
526
+ it("should validate when the model is initialized as a number", () => {
527
+ $rootScope.value = 12345;
528
+ $compile(
529
+ '<form name="form"><input type="text" name="input" ng-model="value" maxlength="10" /></form>',
530
+ )($rootScope);
531
+
532
+ expect($rootScope.value).toBe(12345);
533
+ expect($rootScope.form.input.$error.maxlength).toBeUndefined();
534
+ });
535
+
536
+ it("should validate emptiness against the viewValue", () => {
537
+ inputElm = $compile(
538
+ '<input type="text" name="input" ng-model="value" maxlength="10" />',
539
+ )($rootScope);
540
+
541
+ const ctrl = inputElm.controller("ngModel");
542
+ spyOn(ctrl, "$isEmpty").and.callThrough();
543
+
544
+ ctrl.$parsers.push((value) => `${value}678`);
545
+
546
+ inputElm[0].setAttribute("value", "12345");
547
+ inputElm[0].dispatchEvent(new Event("change"));
548
+ expect(ctrl.$isEmpty).toHaveBeenCalledWith("12345");
549
+ });
550
+
551
+ it("should validate on non-input elements", () => {
552
+ $rootScope.max = 3;
553
+ const elm = $compile(
554
+ '<span ng-model="value" maxlength="{{max}}"></span>',
555
+ )($rootScope);
556
+ const elmNg = $compile(
557
+ '<span ng-model="value" ng-maxlength="max"></span>',
558
+ )($rootScope);
559
+ const ctrl = elm.controller("ngModel");
560
+ const ctrlNg = elmNg.controller("ngModel");
561
+
562
+ expect(ctrl.$error.maxlength).not.toBe(true);
563
+ expect(ctrlNg.$error.maxlength).not.toBe(true);
564
+
565
+ ctrl.$setViewValue("1234");
566
+ ctrlNg.$setViewValue("1234");
567
+
568
+ expect(ctrl.$error.maxlength).toBe(true);
569
+ expect(ctrlNg.$error.maxlength).toBe(true);
570
+ });
571
+ });
572
+
573
+ describe("required", () => {
574
+ it("should allow bindings via ngRequired", () => {
575
+ inputElm = $compile(
576
+ '<input type="text" ng-model="value" ng-required="required" />',
577
+ )($rootScope);
578
+
579
+ $rootScope.$apply("required = false");
580
+
581
+ inputElm[0].setAttribute("value", "");
582
+ inputElm[0].dispatchEvent(new Event("change"));
583
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
584
+
585
+ $rootScope.$apply("required = true");
586
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
587
+
588
+ $rootScope.$apply("value = 'some'");
589
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
590
+
591
+ inputElm[0].value = "";
592
+ inputElm[0].dispatchEvent(new Event("change"));
593
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
594
+
595
+ $rootScope.$apply("required = false");
596
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
597
+ });
598
+
599
+ it("should invalid initial value with bound required", () => {
600
+ inputElm = $compile(
601
+ '<input type="text" ng-model="value" required="{{required}}" />',
602
+ )($rootScope);
603
+
604
+ $rootScope.$apply("required = true");
605
+
606
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
607
+ });
608
+
609
+ it("should be $invalid but $pristine if not touched", () => {
610
+ inputElm = $compile(
611
+ '<input type="text" ng-model="name" name="alias" required />',
612
+ )($rootScope);
613
+
614
+ $rootScope.$apply("name = null");
615
+
616
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
617
+ expect(inputElm[0].classList.contains("ng-pristine")).toBeTrue();
618
+
619
+ inputElm[0].setAttribute("value", "");
620
+ inputElm[0].dispatchEvent(new Event("change"));
621
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
622
+ expect(inputElm[0].classList.contains("ng-dirty")).toBeTrue();
623
+ });
624
+
625
+ it("should allow empty string if not required", () => {
626
+ inputElm = $compile('<input type="text" ng-model="foo" />')($rootScope);
627
+ inputElm[0].setAttribute("value", "a");
628
+ inputElm[0].dispatchEvent(new Event("change"));
629
+ inputElm[0].setAttribute("value", "");
630
+ inputElm[0].dispatchEvent(new Event("change"));
631
+ expect($rootScope.foo).toBe("");
632
+ });
633
+
634
+ it("should set $invalid when model undefined", () => {
635
+ inputElm = $compile(
636
+ '<input type="text" ng-model="notDefined" required />',
637
+ )($rootScope);
638
+ $rootScope.$digest();
639
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
640
+ });
641
+
642
+ it("should consider bad input as an error before any other errors are considered", () => {
643
+ inputElm = $compile('<input type="text" ng-model="value" required />', {
644
+ badInput: true,
645
+ })($rootScope);
646
+ const ctrl = inputElm.controller("ngModel");
647
+ ctrl.$parsers.push(() => undefined);
648
+
649
+ inputElm[0].setAttribute("value", "abc123");
650
+ inputElm[0].dispatchEvent(new Event("change"));
651
+
652
+ expect(ctrl.$error.parse).toBe(true);
653
+ expect(inputElm[0].classList.contains("ng-invalid-parse")).toBeTrue();
654
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue(); // invalid because of the number validator
655
+ });
656
+
657
+ it('should allow `false` as a valid value when the input type is not "checkbox"', () => {
658
+ inputElm = $compile(
659
+ '<input type="radio" ng-value="true" ng-model="answer" required />' +
660
+ '<input type="radio" ng-value="false" ng-model="answer" required />',
661
+ )($rootScope);
662
+
663
+ $rootScope.$apply();
664
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
665
+
666
+ $rootScope.$apply("answer = true");
667
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
668
+
669
+ $rootScope.$apply("answer = false");
670
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
671
+ });
672
+
673
+ it("should validate emptiness against the viewValue", () => {
674
+ inputElm = $compile(
675
+ '<input type="text" name="input" ng-model="value" required />',
676
+ )($rootScope);
677
+
678
+ const ctrl = inputElm.controller("ngModel");
679
+ spyOn(ctrl, "$isEmpty").and.callThrough();
680
+
681
+ ctrl.$parsers.push((value) => `${value}678`);
682
+
683
+ inputElm[0].setAttribute("value", "12345");
684
+ inputElm[0].dispatchEvent(new Event("change"));
685
+ expect(ctrl.$isEmpty).toHaveBeenCalledWith("12345");
686
+ });
687
+
688
+ it("should validate on non-input elements", () => {
689
+ $rootScope.value = "12";
690
+ const elm = $compile('<span ng-model="value" required></span>')(
691
+ $rootScope,
692
+ );
693
+ const elmNg = $compile(
694
+ '<span ng-model="value" ng-required="true"></span>',
695
+ )($rootScope);
696
+ const ctrl = elm.controller("ngModel");
697
+ const ctrlNg = elmNg.controller("ngModel");
698
+
699
+ expect(ctrl.$error.required).not.toBe(true);
700
+ expect(ctrlNg.$error.required).not.toBe(true);
701
+
702
+ ctrl.$setViewValue("");
703
+ ctrlNg.$setViewValue("");
704
+
705
+ expect(ctrl.$error.required).toBe(true);
706
+ expect(ctrlNg.$error.required).toBe(true);
707
+ });
708
+
709
+ it('should override "required" when ng-required="false" is set', () => {
710
+ inputElm = $compile(
711
+ '<input type="text" ng-model="notDefined" required ng-required="false" />',
712
+ )($rootScope);
713
+
714
+ expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
715
+ });
716
+ });
717
+ });