@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,1518 @@
1
+ import { publishExternalAPI } from "../../../src/public";
2
+ import { createInjector } from "../../../src/injector";
3
+ import { dealoc, jqLite } from "../../../src/jqLite";
4
+ import { FormController } from "../../../src/directive/form";
5
+ import { Angular } from "../../../src/loader";
6
+
7
+ describe("form", () => {
8
+ let doc;
9
+ let control;
10
+ let scope;
11
+ let $compile;
12
+ let injector;
13
+
14
+ beforeEach(() => {
15
+ publishExternalAPI().decorator("$exceptionHandler", function () {
16
+ return (exception, cause) => {
17
+ throw new Error(exception);
18
+ };
19
+ });
20
+ injector = createInjector([
21
+ "ng",
22
+ ($compileProvider) => {
23
+ $compileProvider.directive("storeModelCtrl", () => ({
24
+ require: "ngModel",
25
+ link(scope, elm, attr, ctrl) {
26
+ control = ctrl;
27
+ },
28
+ }));
29
+ },
30
+ ]);
31
+
32
+ injector.invoke((_$compile_, $rootScope) => {
33
+ $compile = _$compile_;
34
+ scope = $rootScope.$new();
35
+ });
36
+ });
37
+
38
+ afterEach(() => {
39
+ dealoc(doc);
40
+ });
41
+
42
+ it("should instantiate form and attach it to DOM", () => {
43
+ doc = $compile("<form>")(scope);
44
+ expect(doc.data("$formController")).toBeTruthy();
45
+ expect(doc.data("$formController") instanceof FormController).toBe(true);
46
+ });
47
+
48
+ it("should remove form control references from the form when nested control is removed from the DOM", () => {
49
+ doc = $compile(
50
+ '<form name="myForm">' +
51
+ '<input ng-if="inputPresent" name="alias" ng-model="value" store-model-ctrl/>' +
52
+ "</form>",
53
+ )(scope);
54
+ scope.inputPresent = true;
55
+ scope.$digest();
56
+
57
+ const form = scope.myForm;
58
+ control.$setValidity("required", false);
59
+
60
+ expect(form.alias).toBe(control);
61
+ expect(form.$error.required).toEqual([control]);
62
+
63
+ // remove nested control
64
+ scope.inputPresent = false;
65
+ scope.$apply();
66
+
67
+ expect(form.$error.required).toBeFalsy();
68
+ expect(form.alias).toBeUndefined();
69
+ });
70
+
71
+ it("should ignore changes in manually removed controls", () => {
72
+ doc = $compile(
73
+ '<form name="myForm">' +
74
+ '<input name="control" ng-maxlength="1" ng-model="value" store-model-ctrl/>' +
75
+ "</form>",
76
+ )(scope);
77
+
78
+ const form = scope.myForm;
79
+
80
+ const input = doc.find("input").eq(0);
81
+ const inputController = input.controller("ngModel");
82
+
83
+ input[0].setAttribute("value", "ab");
84
+ input[0].dispatchEvent(new Event("change"));
85
+
86
+ scope.$apply();
87
+
88
+ expect(form.$error.maxlength).toBeTruthy();
89
+ expect(form.$dirty).toBe(true);
90
+ expect(form.$error.maxlength[0].$name).toBe("control");
91
+
92
+ // remove control
93
+ form.$removeControl(form.control);
94
+ expect(form.control).toBeUndefined();
95
+ expect(form.$error.maxlength).toBeFalsy();
96
+
97
+ inputController.$setPristine();
98
+ expect(form.$dirty).toBe(true);
99
+
100
+ form.$setPristine();
101
+
102
+ input[0].setAttribute("value", "ab");
103
+ input[0].dispatchEvent(new Event("change"));
104
+ scope.$apply();
105
+
106
+ expect(form.$error.maxlength).toBeFalsy();
107
+ expect(form.$dirty).toBe(false);
108
+ });
109
+
110
+ it("should react to validation changes in manually added controls", () => {
111
+ doc = $compile(
112
+ '<form name="myForm">' +
113
+ '<input name="control" ng-maxlength="1" ng-model="value" store-model-ctrl/>' +
114
+ "</form>",
115
+ )(scope);
116
+
117
+ scope.$digest();
118
+
119
+ const form = scope.myForm;
120
+
121
+ const input = doc.find("input").eq(0);
122
+
123
+ // remove control and invalidate it
124
+ form.$removeControl(control);
125
+ expect(form.control).toBeUndefined();
126
+
127
+ input[0].setAttribute("value", "abc");
128
+ input[0].dispatchEvent(new Event("change"));
129
+ expect(control.$error.maxlength).toBe(true);
130
+ expect(control.$dirty).toBe(true);
131
+ expect(form.$error.maxlength).toBeFalsy();
132
+ expect(form.$dirty).toBe(false);
133
+
134
+ // re-add the control; its current validation state is not propagated
135
+ form.$addControl(control);
136
+ expect(form.control).toBe(control);
137
+ expect(form.$error.maxlength).toBeFalsy();
138
+ expect(form.$dirty).toBe(false);
139
+
140
+ // Only when the input changes again its validation state is propagated
141
+ input[0].setAttribute("value", "abcd");
142
+ input[0].dispatchEvent(new Event("change"));
143
+ expect(form.$error.maxlength[0]).toBe(control);
144
+ expect(form.$dirty).toBe(false);
145
+ });
146
+
147
+ it("should use the correct parent when renaming and removing dynamically added controls", () => {
148
+ scope.controlName = "childControl";
149
+ scope.hasChildControl = true;
150
+
151
+ doc = $compile(
152
+ '<form name="myForm">' +
153
+ '<div ng-if="hasChildControl">' +
154
+ '<input name="{{controlName}}" ng-maxlength="1" ng-model="value"/>' +
155
+ "</div>" +
156
+ "</form>" +
157
+ '<form name="otherForm"></form>',
158
+ )(scope);
159
+
160
+ scope.$digest();
161
+
162
+ const form = scope.myForm;
163
+ const { otherForm } = scope;
164
+ const { childControl } = form;
165
+
166
+ // remove child form and add it to another form
167
+ form.$removeControl(childControl);
168
+ otherForm.$addControl(childControl);
169
+
170
+ expect(form.childControl).toBeUndefined();
171
+ expect(otherForm.childControl).toBe(childControl);
172
+
173
+ // rename the childControl
174
+ scope.controlName = "childControlMoved";
175
+ scope.$digest();
176
+
177
+ expect(form.childControlMoved).toBeUndefined();
178
+ expect(otherForm.childControl).toBeUndefined();
179
+ expect(otherForm.childControlMoved).toBe(childControl);
180
+
181
+ scope.hasChildControl = false;
182
+ scope.$digest();
183
+
184
+ expect(form.childControlMoved).toBeUndefined();
185
+ expect(otherForm.childControlMoved).toBeUndefined();
186
+ });
187
+
188
+ it("should remove scope reference when form with no parent form is removed from the DOM", () => {
189
+ let formController;
190
+ scope.ctrl = {};
191
+ doc = $compile(
192
+ '<div><form name="ctrl.myForm" ng-if="formPresent">' +
193
+ '<input name="alias" ng-model="value" />' +
194
+ "</form></div>",
195
+ )(scope);
196
+
197
+ scope.$digest();
198
+ expect(scope.ctrl.myForm).toBeUndefined();
199
+
200
+ scope.$apply("formPresent = true");
201
+ expect(scope.ctrl.myForm).toBeDefined();
202
+
203
+ formController = doc.find("form").controller("form");
204
+ expect(scope.ctrl.myForm == formController).toBeTrue();
205
+
206
+ scope.$apply("formPresent = false");
207
+ expect(doc[0].innerText).toBe("");
208
+ });
209
+
210
+ it("should use ngForm value as form name", () => {
211
+ doc = $compile(
212
+ '<div ng-form="myForm">' +
213
+ '<input type="text" name="alias" ng-model="value"/>' +
214
+ "</div>",
215
+ )(scope);
216
+
217
+ expect(scope.myForm).toBeDefined();
218
+ expect(scope.myForm.alias).toBeDefined();
219
+ });
220
+
221
+ it("should use ngForm value as form name when nested inside form", () => {
222
+ doc = $compile(
223
+ '<form name="myForm">' +
224
+ '<div ng-form="nestedForm"><input type="text" name="alias" ng-model="value"/></div>' +
225
+ "</form>",
226
+ )(scope);
227
+
228
+ expect(scope.myForm).toBeDefined();
229
+ expect(scope.myForm.nestedForm).toBeDefined();
230
+ expect(scope.myForm.nestedForm.alias).toBeDefined();
231
+ });
232
+
233
+ it("should publish form to scope when name attr is defined", () => {
234
+ doc = $compile('<form name="myForm"></form>')(scope);
235
+ expect(scope.myForm).toBeTruthy();
236
+ expect(doc.data("$formController")).toBeTruthy();
237
+ expect(doc.data("$formController")).toEqual(scope.myForm);
238
+ });
239
+
240
+ it("should support expression in form name", () => {
241
+ doc = $compile('<form name="obj.myForm"></form>')(scope);
242
+
243
+ expect(scope.obj).toBeDefined();
244
+ expect(scope.obj.myForm).toBeTruthy();
245
+ });
246
+
247
+ it("should support two forms on a single scope", () => {
248
+ doc = $compile(`
249
+ <div>
250
+ <form name="formA">
251
+ <input name="firstName" ng-model="firstName" required>
252
+ </form>
253
+ <form name="formB">
254
+ <input name="lastName" ng-model="lastName" required>
255
+ </form>
256
+ </div>
257
+ `)(scope);
258
+ scope.$digest();
259
+ expect(scope.formA.$error.required.length).toBe(1);
260
+ expect(scope.formA.$error.required).toEqual([scope.formA.firstName]);
261
+ expect(scope.formB.$error.required.length).toBe(1);
262
+ expect(scope.formB.$error.required).toEqual([scope.formB.lastName]);
263
+
264
+ const inputA = doc.find("input").eq(0);
265
+ const inputB = doc.find("input").eq(1);
266
+
267
+ inputA[0].setAttribute("value", "val1");
268
+ inputA[0].dispatchEvent(new Event("change"));
269
+ inputB[0].setAttribute("value", "val2");
270
+ inputB[0].dispatchEvent(new Event("change"));
271
+
272
+ expect(scope.firstName).toBe("val1");
273
+ expect(scope.lastName).toBe("val2");
274
+
275
+ expect(scope.formA.$error.required).toBeFalsy();
276
+ expect(scope.formB.$error.required).toBeFalsy();
277
+ });
278
+
279
+ it("should publish widgets", () => {
280
+ doc = jqLite(
281
+ '<form name="form"><input type="text" name="w1" ng-model="some" /></form>',
282
+ );
283
+ $compile(doc)(scope);
284
+
285
+ const widget = scope.form.w1;
286
+ expect(widget).toBeDefined();
287
+ expect(widget.$pristine).toBe(true);
288
+ expect(widget.$dirty).toBe(false);
289
+ expect(widget.$valid).toBe(true);
290
+ expect(widget.$invalid).toBe(false);
291
+ });
292
+
293
+ it('should throw an exception if an input has name="hasOwnProperty"', () => {
294
+ doc = jqLite(
295
+ '<form name="form">' +
296
+ '<input name="hasOwnProperty" ng-model="some" />' +
297
+ '<input name="other" ng-model="someOther" />' +
298
+ "</form>",
299
+ );
300
+ expect(() => {
301
+ $compile(doc)(scope);
302
+ }).toThrowError();
303
+ });
304
+
305
+ describe("triggering commit value on submit", () => {
306
+ it("should trigger update on form submit", () => {
307
+ const form = $compile(
308
+ '<form name="test" ng-model-options="{ updateOn: \'submit\' }" >' +
309
+ '<input type="text" ng-model="name" />' +
310
+ "</form>",
311
+ )(scope);
312
+ scope.$digest();
313
+
314
+ const inputElm = form.find("input").eq(0);
315
+
316
+ inputElm[0].setAttribute("value", "a");
317
+ inputElm[0].dispatchEvent(new Event("change"));
318
+ expect(scope.name).toEqual(undefined);
319
+
320
+ form[0].dispatchEvent(new Event("submit"));
321
+ expect(scope.name).toEqual("a");
322
+ dealoc(form);
323
+ });
324
+
325
+ it("should trigger update on form submit with nested forms", () => {
326
+ const form = $compile(
327
+ '<form name="test" ng-model-options="{ updateOn: \'submit\' }" >' +
328
+ '<div class="ng-form" name="child">' +
329
+ '<input type="text" ng-model="name" />' +
330
+ "</div>" +
331
+ "</form>",
332
+ )(scope);
333
+ scope.$digest();
334
+
335
+ const inputElm = form.find("input").eq(0);
336
+ inputElm[0].setAttribute("value", "a");
337
+ inputElm[0].dispatchEvent(new Event("change"));
338
+ expect(scope.name).toEqual(undefined);
339
+ // browserTrigger(form, "submit");
340
+ form[0].dispatchEvent(new Event("submit"));
341
+ expect(scope.name).toEqual("a");
342
+ dealoc(form);
343
+ });
344
+
345
+ it("should trigger update before ng-submit is invoked", () => {
346
+ const form = $compile(
347
+ '<form name="test" ng-submit="submit()" ' +
348
+ "ng-model-options=\"{ updateOn: 'submit' }\" >" +
349
+ '<input type="text" ng-model="name" />' +
350
+ "</form>",
351
+ )(scope);
352
+ scope.$digest();
353
+
354
+ const inputElm = form.find("input").eq(0);
355
+ inputElm[0].setAttribute("value", "a");
356
+ inputElm[0].dispatchEvent(new Event("change"));
357
+ scope.submit = jasmine.createSpy("submit").and.callFake(() => {
358
+ expect(scope.name).toEqual("a");
359
+ });
360
+ // browserTrigger(form, "submit");
361
+ form[0].dispatchEvent(new Event("submit"));
362
+ expect(scope.submit).toHaveBeenCalled();
363
+ dealoc(form);
364
+ });
365
+ });
366
+
367
+ describe("rollback view value", () => {
368
+ it("should trigger rollback on form controls", () => {
369
+ const form = $compile(
370
+ '<form name="test" ng-model-options="{ updateOn: \'submit\' }" >' +
371
+ '<input type="text" ng-model="name" />' +
372
+ '<button ng-click="test.$rollbackViewValue()" />' +
373
+ "</form>",
374
+ )(scope);
375
+ scope.$digest();
376
+
377
+ const inputElm = form.find("input").eq(0);
378
+ inputElm[0].setAttribute("value", "a");
379
+ inputElm[0].dispatchEvent(new Event("click"));
380
+ expect(inputElm.val()).toBe("a");
381
+ // browserTrigger(form.find("button"), "click");
382
+ form.find("button")[0].click();
383
+ expect(inputElm.val()).toBe("");
384
+ dealoc(form);
385
+ });
386
+
387
+ it("should trigger rollback on form controls with nested forms", () => {
388
+ const form = $compile(
389
+ '<form name="test" ng-model-options="{ updateOn: \'submit\' }" >' +
390
+ '<div class="ng-form" name="child">' +
391
+ '<input type="text" ng-model="name" />' +
392
+ "</div>" +
393
+ '<button ng-click="test.$rollbackViewValue()" />' +
394
+ "</form>",
395
+ )(scope);
396
+ scope.$digest();
397
+
398
+ const inputElm = form.find("input").eq(0);
399
+ inputElm[0].setAttribute("value", "a");
400
+ inputElm[0].dispatchEvent(new Event("click"));
401
+ expect(inputElm.val()).toBe("a");
402
+ form.find("button")[0].click();
403
+ expect(inputElm.val()).toBe("");
404
+ dealoc(form);
405
+ });
406
+ });
407
+
408
+ describe("preventing default submission", () => {
409
+ it("should prevent form submission", (done) => {
410
+ let nextTurn = false;
411
+ let submitted = false;
412
+ let reloadPrevented;
413
+
414
+ doc = jqLite(
415
+ '<form ng-submit="submitMe()">' +
416
+ '<input type="submit" value="submit">' +
417
+ "</form>",
418
+ );
419
+ // Support: Chrome 60+ (on Windows)
420
+ // We need to add the form to the DOM in order for `submit` events to be properly fired.
421
+ window.document.body.appendChild(doc[0]);
422
+
423
+ const assertPreventDefaultListener = function (e) {
424
+ reloadPrevented = e.defaultPrevented || e.returnValue === false;
425
+ };
426
+
427
+ $compile(doc)(scope);
428
+
429
+ scope.submitMe = function () {
430
+ submitted = true;
431
+ };
432
+
433
+ doc[0].addEventListener("submit", assertPreventDefaultListener);
434
+ doc.find("input")[0].click();
435
+
436
+ // let the browser process all events (and potentially reload the page)
437
+ window.setTimeout(() => {
438
+ expect(reloadPrevented).toBe(true);
439
+ expect(submitted).toBe(true);
440
+
441
+ // prevent mem leak in test
442
+ doc[0].removeEventListener("submit", assertPreventDefaultListener);
443
+ done();
444
+ }, 100);
445
+ });
446
+
447
+ it("should prevent the default when the form is destroyed by a submission via a click event", (done) => {
448
+ doc = jqLite(
449
+ "<div>" +
450
+ '<form ng-submit="submitMe()">' +
451
+ '<button type="submit" ng-click="destroy()"></button>' +
452
+ "</form>" +
453
+ "</div>",
454
+ );
455
+
456
+ const form = doc.find("form");
457
+ let destroyed = false;
458
+ let nextTurn = false;
459
+ let submitted = false;
460
+ let reloadPrevented = "never called";
461
+
462
+ scope.destroy = function () {
463
+ // yes, I know, scope methods should not do direct DOM manipulation, but I wanted to keep
464
+ // this test small. Imagine that the destroy action will cause a model change (e.g.
465
+ // $location change) that will cause some directive to destroy the dom (e.g. ngView+$route)
466
+ doc.empty();
467
+ destroyed = true;
468
+ };
469
+
470
+ scope.submitMe = function () {
471
+ submitted = true;
472
+ };
473
+
474
+ const assertPreventDefaultListener = function (e) {
475
+ reloadPrevented = e.defaultPrevented || e.returnValue === false;
476
+ };
477
+
478
+ $compile(doc)(scope);
479
+
480
+ form[0].addEventListener("submit", assertPreventDefaultListener);
481
+
482
+ form.find("button")[0].click();
483
+
484
+ // let the browser process all events (and potentially reload the page)
485
+ window.setTimeout(() => {
486
+ nextTurn = true;
487
+ expect(doc.html()).toBe("");
488
+ expect(destroyed).toBe(true);
489
+ expect(submitted).toBe(false);
490
+ // this is known corner-case that is not currently handled
491
+ // the issue is that the submit listener is destroyed before
492
+ // the event propagates there. we can fix this if we see
493
+ // the issue in the wild, I'm not going to bother to do it
494
+ // now. (i)
495
+
496
+ // Support: Chrome 60+ (on Windows)
497
+ // Chrome 60+ on Windows does not fire `submit` events when the form is not attached to
498
+ // the DOM. Verify that the `submit` listener was either never fired or (if fired) the
499
+ // reload was prevented.
500
+ expect(reloadPrevented).not.toBe(false);
501
+
502
+ // prevent mem leak in test
503
+ form[0].removeEventListener("submit", assertPreventDefaultListener);
504
+ done();
505
+ }, 100);
506
+ });
507
+
508
+ it("should NOT prevent form submission if action attribute present", () => {
509
+ const callback = jasmine.createSpy("submit").and.callFake((event) => {
510
+ expect(event.isDefaultPrevented()).toBe(false);
511
+ event.preventDefault();
512
+ });
513
+
514
+ doc = $compile('<form action="some.py"></form>')(scope);
515
+ doc.on("submit", callback);
516
+
517
+ //browserTrigger(doc, "submit");
518
+ doc[0].dispatchEvent(new Event("submit"));
519
+ expect(callback).toHaveBeenCalled();
520
+ });
521
+ });
522
+
523
+ describe("nested forms", () => {
524
+ it("should chain nested forms", () => {
525
+ doc = jqLite(
526
+ '<ng:form name="parent">' +
527
+ '<ng:form name="child">' +
528
+ '<input ng:model="modelA" name="inputA">' +
529
+ '<input ng:model="modelB" name="inputB">' +
530
+ "</ng:form>" +
531
+ "</ng:form>",
532
+ );
533
+ $compile(doc)(scope);
534
+
535
+ const { parent } = scope;
536
+ const { child } = scope;
537
+ const { inputA } = child;
538
+ const { inputB } = child;
539
+
540
+ inputA.$setValidity("MyError", false);
541
+ inputB.$setValidity("MyError", false);
542
+ expect(parent.$error.MyError).toEqual([child]);
543
+ expect(child.$error.MyError).toEqual([inputA, inputB]);
544
+
545
+ inputA.$setValidity("MyError", true);
546
+ expect(parent.$error.MyError).toEqual([child]);
547
+ expect(child.$error.MyError).toEqual([inputB]);
548
+
549
+ inputB.$setValidity("MyError", true);
550
+ expect(parent.$error.MyError).toBeFalsy();
551
+ expect(child.$error.MyError).toBeFalsy();
552
+
553
+ child.$setDirty();
554
+ expect(parent.$dirty).toBeTruthy();
555
+
556
+ child.$setSubmitted();
557
+ expect(parent.$submitted).toBeTruthy();
558
+ });
559
+
560
+ it("should set $submitted to true on child forms when parent is submitted", () => {
561
+ doc = jqLite(
562
+ '<ng-form name="parent">' +
563
+ '<ng-form name="child">' +
564
+ '<input ng:model="modelA" name="inputA">' +
565
+ '<input ng:model="modelB" name="inputB">' +
566
+ "</ng-form>" +
567
+ "</ng-form>",
568
+ );
569
+ $compile(doc)(scope);
570
+
571
+ const { parent } = scope;
572
+ const { child } = scope;
573
+
574
+ parent.$setSubmitted();
575
+ expect(parent.$submitted).toBeTruthy();
576
+ expect(child.$submitted).toBeTruthy();
577
+ });
578
+
579
+ it("should not propagate $submitted state on removed child forms when parent is submitted", () => {
580
+ doc = jqLite(
581
+ '<ng-form name="parent">' +
582
+ '<ng-form name="child">' +
583
+ '<ng-form name="grandchild">' +
584
+ '<input ng:model="modelA" name="inputA">' +
585
+ "</ng-form>" +
586
+ "</ng-form>" +
587
+ "</ng-form>",
588
+ );
589
+ $compile(doc)(scope);
590
+
591
+ const { parent } = scope;
592
+ const { child } = scope;
593
+ const { grandchild } = scope;
594
+ const ggchild = scope.greatgrandchild;
595
+
596
+ parent.$removeControl(child);
597
+
598
+ parent.$setSubmitted();
599
+ expect(parent.$submitted).toBeTruthy();
600
+ expect(child.$submitted).not.toBeTruthy();
601
+ expect(grandchild.$submitted).not.toBeTruthy();
602
+
603
+ parent.$addControl(child);
604
+
605
+ expect(parent.$submitted).toBeTruthy();
606
+ expect(child.$submitted).not.toBeTruthy();
607
+ expect(grandchild.$submitted).not.toBeTruthy();
608
+
609
+ parent.$setSubmitted();
610
+ expect(parent.$submitted).toBeTruthy();
611
+ expect(child.$submitted).toBeTruthy();
612
+ expect(grandchild.$submitted).toBeTruthy();
613
+
614
+ parent.$removeControl(child);
615
+
616
+ expect(parent.$submitted).toBeTruthy();
617
+ expect(child.$submitted).toBeTruthy();
618
+ expect(grandchild.$submitted).toBeTruthy();
619
+
620
+ parent.$setPristine(); // sets $submitted to false
621
+ expect(parent.$submitted).not.toBeTruthy();
622
+ expect(child.$submitted).toBeTruthy();
623
+ expect(grandchild.$submitted).toBeTruthy();
624
+
625
+ grandchild.$setPristine();
626
+ expect(grandchild.$submitted).not.toBeTruthy();
627
+
628
+ child.$setSubmitted();
629
+ expect(parent.$submitted).not.toBeTruthy();
630
+ expect(child.$submitted).toBeTruthy();
631
+ expect(grandchild.$submitted).toBeTruthy();
632
+
633
+ child.$setPristine();
634
+ expect(parent.$submitted).not.toBeTruthy();
635
+ expect(child.$submitted).not.toBeTruthy();
636
+ expect(grandchild.$submitted).not.toBeTruthy();
637
+
638
+ // Test upwards submission setting
639
+ grandchild.$setSubmitted();
640
+ expect(parent.$submitted).not.toBeTruthy();
641
+ expect(child.$submitted).toBeTruthy();
642
+ expect(grandchild.$submitted).toBeTruthy();
643
+ });
644
+
645
+ it("should set $submitted to true on child and parent forms when form is submitted", () => {
646
+ doc = jqLite(
647
+ '<ng-form name="parent">' +
648
+ '<ng-form name="child">' +
649
+ '<ng-form name="grandchild">' +
650
+ '<input ng:model="modelA" name="inputA">' +
651
+ '<input ng:model="modelB" name="inputB">' +
652
+ "</ng-form>" +
653
+ "</ng-form>" +
654
+ "</ng-form>",
655
+ );
656
+ $compile(doc)(scope);
657
+
658
+ const { parent } = scope;
659
+ const { child } = scope;
660
+ const { grandchild } = scope;
661
+
662
+ child.$setSubmitted();
663
+
664
+ expect(parent.$submitted).toBeTruthy();
665
+ expect(child.$submitted).toBeTruthy();
666
+ expect(grandchild.$submitted).toBeTruthy();
667
+ });
668
+
669
+ it("should deregister a child form when its DOM is removed", function () {
670
+ doc = jqLite(
671
+ '<form name="parent">' +
672
+ '<div class="ng-form" name="child">' +
673
+ '<input ng:model="modelA" name="inputA" required>' +
674
+ "</div>" +
675
+ "</form>",
676
+ );
677
+ $compile(doc)(scope);
678
+ scope.$apply();
679
+
680
+ var parent = scope.parent,
681
+ child = scope.child;
682
+
683
+ expect(parent).toBeDefined();
684
+ expect(child).toBeDefined();
685
+ expect(parent.$error.required).toEqual([child]);
686
+ doc.children().remove(); //remove child
687
+
688
+ expect(parent.child).toBeUndefined();
689
+ expect(scope.child).toBeUndefined();
690
+ expect(parent.$error.required).toBeFalsy();
691
+ });
692
+
693
+ it("should deregister a child form whose name is an expression when its DOM is removed", () => {
694
+ doc = jqLite(
695
+ '<form name="parent">' +
696
+ '<div class="ng-form" name="child.form">' +
697
+ '<input ng:model="modelA" name="inputA" required>' +
698
+ "</div>" +
699
+ "</form>",
700
+ );
701
+ $compile(doc)(scope);
702
+ scope.$apply();
703
+
704
+ const { parent } = scope;
705
+ const child = scope.child.form;
706
+
707
+ expect(parent).toBeDefined();
708
+ expect(child).toBeDefined();
709
+ expect(parent.$error.required).toEqual([child]);
710
+ doc.children().remove(); // remove child
711
+
712
+ expect(parent.child).toBeUndefined();
713
+ expect(scope.child.form).toBeUndefined();
714
+ expect(parent.$error.required).toBeFalsy();
715
+ });
716
+
717
+ it("should deregister a input when it is removed from DOM", () => {
718
+ doc = jqLite(
719
+ '<form name="parent">' +
720
+ '<div class="ng-form" name="child">' +
721
+ '<input ng-if="inputPresent" ng-model="modelA" name="inputA" required maxlength="10">' +
722
+ "</div>" +
723
+ "</form>",
724
+ );
725
+ $compile(doc)(scope);
726
+ scope.inputPresent = true;
727
+ scope.$apply();
728
+
729
+ const { parent } = scope;
730
+ const { child } = scope;
731
+ const input = child.inputA;
732
+
733
+ expect(parent).toBeDefined();
734
+ expect(child).toBeDefined();
735
+
736
+ expect(parent.$error.required).toEqual([child]);
737
+ expect(parent.$$success.maxlength).toEqual([child]);
738
+
739
+ expect(child.$error.required).toEqual([input]);
740
+ expect(child.$$success.maxlength).toEqual([input]);
741
+
742
+ expect(doc[0].classList.contains("ng-invalid")).toBe(true);
743
+ expect(doc[0].classList.contains("ng-invalid-required")).toBe(true);
744
+ expect(doc[0].classList.contains("ng-valid-maxlength")).toBe(true);
745
+ expect(doc.find("div")[0].classList.contains("ng-invalid")).toBe(true);
746
+ expect(doc.find("div")[0].classList.contains("ng-invalid-required")).toBe(
747
+ true,
748
+ );
749
+ expect(doc.find("div")[0].classList.contains("ng-valid-maxlength")).toBe(
750
+ true,
751
+ );
752
+
753
+ // remove child input
754
+ scope.$apply("inputPresent = false");
755
+
756
+ expect(parent.$error.required).toBeFalsy();
757
+ expect(parent.$$success.maxlength).toBeFalsy();
758
+
759
+ expect(child.$error.required).toBeFalsy();
760
+ expect(child.$$success.maxlength).toBeFalsy();
761
+
762
+ expect(doc[0].classList.contains("ng-valid")).toBe(true);
763
+ expect(doc[0].classList.contains("ng-valid-required")).toBe(false);
764
+ expect(doc[0].classList.contains("ng-invalid-required")).toBe(false);
765
+ expect(doc[0].classList.contains("ng-valid-maxlength")).toBe(false);
766
+ expect(doc[0].classList.contains("ng-invalid-maxlength")).toBe(false);
767
+
768
+ expect(doc.find("div")[0].classList.contains("ng-valid")).toBe(true);
769
+ expect(doc.find("div")[0].classList.contains("ng-valid-required")).toBe(
770
+ false,
771
+ );
772
+ expect(doc.find("div")[0].classList.contains("ng-invalid-required")).toBe(
773
+ false,
774
+ );
775
+ expect(doc.find("div")[0].classList.contains("ng-valid-maxlength")).toBe(
776
+ false,
777
+ );
778
+ expect(
779
+ doc.find("div")[0].classList.contains("ng-invalid-maxlength"),
780
+ ).toBe(false);
781
+ });
782
+
783
+ it("should deregister a input that is $pending when it is removed from DOM", () => {
784
+ doc = jqLite(
785
+ '<form name="parent">' +
786
+ '<div class="ng-form" name="child">' +
787
+ '<input ng-if="inputPresent" ng-model="modelA" name="inputA">' +
788
+ "</div>" +
789
+ "</form>",
790
+ );
791
+ $compile(doc)(scope);
792
+ scope.$apply("inputPresent = true");
793
+
794
+ const { parent } = scope;
795
+ const { child } = scope;
796
+ const input = child.inputA;
797
+
798
+ scope.$apply(child.inputA.$setValidity("fake", undefined));
799
+
800
+ expect(parent).toBeDefined();
801
+ expect(child).toBeDefined();
802
+
803
+ expect(parent.$pending.fake).toEqual([child]);
804
+ expect(child.$pending.fake).toEqual([input]);
805
+
806
+ expect(doc[0].classList.contains("ng-pending")).toBe(true);
807
+ expect(doc.find("div")[0].classList.contains("ng-pending")).toBe(true);
808
+
809
+ // remove child input
810
+ scope.$apply("inputPresent = false");
811
+
812
+ expect(parent.$pending).toBeUndefined();
813
+ expect(child.$pending).toBeUndefined();
814
+
815
+ expect(doc[0].classList.contains("ng-pending")).toBe(false);
816
+ expect(doc.find("div")[0].classList.contains("ng-pending")).toBe(false);
817
+ });
818
+
819
+ it("should leave the parent form invalid when deregister a removed input", () => {
820
+ doc = jqLite(
821
+ '<form name="parent">' +
822
+ '<div class="ng-form" name="child">' +
823
+ '<input ng-if="inputPresent" ng-model="modelA" name="inputA" required>' +
824
+ '<input ng-model="modelB" name="inputB" required>' +
825
+ "</div>" +
826
+ "</form>",
827
+ );
828
+ $compile(doc)(scope);
829
+ scope.inputPresent = true;
830
+ scope.$apply();
831
+
832
+ const { parent } = scope;
833
+ const { child } = scope;
834
+ const { inputA } = child;
835
+ const { inputB } = child;
836
+
837
+ expect(parent).toBeDefined();
838
+ expect(child).toBeDefined();
839
+ expect(parent.$error.required).toEqual([child]);
840
+ expect(child.$error.required).toEqual([inputB, inputA]);
841
+
842
+ // remove child input
843
+ scope.inputPresent = false;
844
+ scope.$apply();
845
+
846
+ expect(parent.$error.required).toEqual([child]);
847
+ expect(child.$error.required).toEqual([inputB]);
848
+ });
849
+
850
+ it("should ignore changes in manually removed child forms", () => {
851
+ doc = $compile(
852
+ '<form name="myForm">' +
853
+ '<ng-form name="childform">' +
854
+ '<input name="childformcontrol" ng-maxlength="1" ng-model="value"/>' +
855
+ "</ng-form>" +
856
+ "</form>",
857
+ )(scope);
858
+
859
+ const form = scope.myForm;
860
+ const childformController = doc.find("ng-form").eq(0).controller("form");
861
+
862
+ const input = doc.find("input").eq(0);
863
+ const inputController = input.controller("ngModel");
864
+
865
+ // changeInputValue(input, "ab");
866
+ input[0].setAttribute("value", "ab");
867
+ input[0].dispatchEvent(new Event("change"));
868
+
869
+ scope.$apply();
870
+
871
+ expect(form.$dirty).toBe(true);
872
+ expect(form.$error.maxlength).toBeTruthy();
873
+ expect(form.$error.maxlength[0].$name).toBe("childform");
874
+
875
+ inputController.$setPristine();
876
+ expect(form.$dirty).toBe(true);
877
+
878
+ form.$setPristine();
879
+
880
+ // remove child form
881
+ form.$removeControl(childformController);
882
+ expect(form.childform).toBeUndefined();
883
+ expect(form.$error.maxlength).toBeFalsy();
884
+
885
+ // changeInputValue(input, "abc");
886
+ input[0].setAttribute("value", "abc");
887
+ input[0].dispatchEvent(new Event("change"));
888
+ scope.$apply();
889
+
890
+ expect(form.$error.maxlength).toBeFalsy();
891
+ expect(form.$dirty).toBe(false);
892
+ });
893
+
894
+ it("should react to changes in manually added child forms", () => {
895
+ doc = $compile(
896
+ '<form name="myForm">' +
897
+ '<ng-form name="childForm">' +
898
+ '<input name="childformcontrol" ng-maxlength="1" ng-model="value" />' +
899
+ "</ng-form>" +
900
+ "</form>",
901
+ )(scope);
902
+
903
+ const form = scope.myForm;
904
+ const childFormController = doc.find("ng-form").eq(0).controller("form");
905
+
906
+ const input = doc.find("input").eq(0);
907
+
908
+ // remove child form so we can add it manually
909
+ form.$removeControl(childFormController);
910
+ // changeInputValue(input, "ab");
911
+ input[0].setAttribute("value", "ab");
912
+ input[0].dispatchEvent(new Event("change"));
913
+
914
+ expect(form.childForm).toBeUndefined();
915
+ expect(form.$dirty).toBe(false);
916
+ expect(form.$error.maxlength).toBeFalsy();
917
+
918
+ // re-add the child form; its current validation state is not propagated
919
+ form.$addControl(childFormController);
920
+ expect(form.childForm).toBe(childFormController);
921
+ expect(form.$error.maxlength).toBeFalsy();
922
+ expect(form.$dirty).toBe(false);
923
+
924
+ // Only when the input inside the child form changes, the validation state is propagated
925
+ // changeInputValue(input, "abc");
926
+ input[0].setAttribute("value", "abc");
927
+ input[0].dispatchEvent(new Event("change"));
928
+ expect(form.$error.maxlength[0]).toBe(childFormController);
929
+ expect(form.$dirty).toBe(false);
930
+ });
931
+
932
+ it("should use the correct parent when renaming and removing dynamically added forms", () => {
933
+ scope.formName = "childForm";
934
+ scope.hasChildForm = true;
935
+
936
+ doc = $compile(
937
+ '<form name="myForm">' +
938
+ '<div ng-if="hasChildForm">' +
939
+ '<ng-form name="{{formName}}">' +
940
+ '<input name="childformcontrol" ng-maxlength="1" ng-model="value"/>' +
941
+ "</ng-form>" +
942
+ "</div>" +
943
+ "</form>" +
944
+ '<form name="otherForm"></form>',
945
+ )(scope);
946
+
947
+ scope.$digest();
948
+
949
+ const form = scope.myForm;
950
+ const { otherForm } = scope;
951
+ const { childForm } = form;
952
+
953
+ // remove child form and add it to another form
954
+ form.$removeControl(childForm);
955
+ otherForm.$addControl(childForm);
956
+
957
+ expect(form.childForm).toBeUndefined();
958
+ expect(otherForm.childForm).toBe(childForm);
959
+
960
+ // rename the childForm
961
+ scope.formName = "childFormMoved";
962
+ scope.$digest();
963
+
964
+ expect(form.childFormMoved).toBeUndefined();
965
+ expect(otherForm.childForm).toBeUndefined();
966
+ expect(otherForm.childFormMoved).toBe(childForm);
967
+
968
+ scope.hasChildForm = false;
969
+ scope.$digest();
970
+
971
+ expect(form.childFormMoved).toBeUndefined();
972
+ expect(otherForm.childFormMoved).toBeUndefined();
973
+ });
974
+
975
+ it("should chain nested forms in repeater", () => {
976
+ doc = jqLite(
977
+ "<ng:form name=parent>" +
978
+ '<ng:form ng:repeat="f in forms" name=child>' +
979
+ "<input type=text ng:model=text name=text>" +
980
+ "</ng:form>" +
981
+ "</ng:form>",
982
+ );
983
+ $compile(doc)(scope);
984
+
985
+ scope.$apply(() => {
986
+ scope.forms = [1];
987
+ });
988
+
989
+ const { parent } = scope;
990
+ const { child } = doc.find("input").scope();
991
+ const input = child.text;
992
+
993
+ expect(parent).toBeDefined();
994
+ expect(child).toBeDefined();
995
+ expect(input).toBeDefined();
996
+
997
+ input.$setValidity("myRule", false);
998
+ expect(input.$error.myRule).toEqual(true);
999
+ expect(child.$error.myRule).toEqual([input]);
1000
+ expect(parent.$error.myRule).toEqual([child]);
1001
+
1002
+ input.$setValidity("myRule", true);
1003
+ expect(parent.$error.myRule).toBeFalsy();
1004
+ expect(child.$error.myRule).toBeFalsy();
1005
+ });
1006
+ });
1007
+
1008
+ describe("validation", () => {
1009
+ beforeEach(() => {
1010
+ doc = $compile(
1011
+ '<form name="form">' +
1012
+ '<input ng-model="name" name="name" store-model-ctrl/>' +
1013
+ "</form>",
1014
+ )(scope);
1015
+
1016
+ scope.$digest();
1017
+ });
1018
+
1019
+ it("should have ng-valid/ng-invalid css class", () => {
1020
+ expect(doc[0].classList.contains("ng-valid")).toBeTrue();
1021
+
1022
+ control.$setValidity("error", false);
1023
+ scope.$digest();
1024
+ expect(doc[0].classList.contains("ng-invalid")).toBeTrue();
1025
+ expect(doc[0].classList.contains("ng-valid-error")).toBe(false);
1026
+ expect(doc[0].classList.contains("ng-invalid-error")).toBe(true);
1027
+
1028
+ control.$setValidity("another", false);
1029
+ scope.$digest();
1030
+ expect(doc[0].classList.contains("ng-valid-error")).toBe(false);
1031
+ expect(doc[0].classList.contains("ng-invalid-error")).toBe(true);
1032
+ expect(doc[0].classList.contains("ng-valid-another")).toBe(false);
1033
+ expect(doc[0].classList.contains("ng-invalid-another")).toBe(true);
1034
+
1035
+ control.$setValidity("error", true);
1036
+ scope.$digest();
1037
+ expect(doc[0].classList.contains("ng-invalid")).toBeTrue();
1038
+ expect(doc[0].classList.contains("ng-valid-error")).toBe(true);
1039
+ expect(doc[0].classList.contains("ng-invalid-error")).toBe(false);
1040
+ expect(doc[0].classList.contains("ng-valid-another")).toBe(false);
1041
+ expect(doc[0].classList.contains("ng-invalid-another")).toBe(true);
1042
+
1043
+ control.$setValidity("another", true);
1044
+ scope.$digest();
1045
+ expect(doc[0].classList.contains("ng-valid")).toBeTrue();
1046
+ expect(doc[0].classList.contains("ng-valid-error")).toBe(true);
1047
+ expect(doc[0].classList.contains("ng-invalid-error")).toBe(false);
1048
+ expect(doc[0].classList.contains("ng-valid-another")).toBe(true);
1049
+ expect(doc[0].classList.contains("ng-invalid-another")).toBe(false);
1050
+
1051
+ // validators are skipped, e.g. because of a parser error
1052
+ control.$setValidity("error", null);
1053
+ control.$setValidity("another", null);
1054
+ scope.$digest();
1055
+ expect(doc[0].classList.contains("ng-valid-error")).toBe(false);
1056
+ expect(doc[0].classList.contains("ng-invalid-error")).toBe(false);
1057
+ expect(doc[0].classList.contains("ng-valid-another")).toBe(false);
1058
+ expect(doc[0].classList.contains("ng-invalid-another")).toBe(false);
1059
+ });
1060
+
1061
+ it("should have ng-pristine/ng-dirty css class", () => {
1062
+ expect(doc[0].classList.contains("ng-pristine")).toBeTrue();
1063
+ expect(doc[0].classList.contains("ng-dirty")).toBeFalse();
1064
+
1065
+ control.$setViewValue("");
1066
+ scope.$apply();
1067
+ expect(doc[0].classList.contains("ng-pristine")).toBeFalse();
1068
+ expect(doc[0].classList.contains("ng-dirty")).toBeTrue();
1069
+ });
1070
+ });
1071
+
1072
+ describe("$pending", () => {
1073
+ beforeEach(() => {
1074
+ doc = $compile('<form name="form"></form>')(scope);
1075
+ scope.$digest();
1076
+ });
1077
+
1078
+ it("should set valid and invalid to undefined when a validation error state is set as pending", () => {
1079
+ let defer;
1080
+ const form = doc.data("$formController");
1081
+
1082
+ const ctrl = {};
1083
+ form.$setValidity("matias", undefined, ctrl);
1084
+
1085
+ expect(form.$valid).toBeUndefined();
1086
+ expect(form.$invalid).toBeUndefined();
1087
+ expect(form.$pending.matias).toEqual([ctrl]);
1088
+
1089
+ form.$setValidity("matias", true, ctrl);
1090
+
1091
+ expect(form.$valid).toBe(true);
1092
+ expect(form.$invalid).toBe(false);
1093
+ expect(form.$pending).toBeUndefined();
1094
+
1095
+ form.$setValidity("matias", false, ctrl);
1096
+
1097
+ expect(form.$valid).toBe(false);
1098
+ expect(form.$invalid).toBe(true);
1099
+ expect(form.$pending).toBeUndefined();
1100
+ });
1101
+ });
1102
+
1103
+ describe("$setPristine", () => {
1104
+ it("should reset pristine state of form and controls", () => {
1105
+ doc = $compile(
1106
+ '<form name="testForm">' +
1107
+ '<input ng-model="named1" name="foo">' +
1108
+ '<input ng-model="named2" name="bar">' +
1109
+ "</form>",
1110
+ )(scope);
1111
+
1112
+ scope.$digest();
1113
+
1114
+ const form = doc;
1115
+ const formCtrl = scope.testForm;
1116
+ const input1 = form.find("input").eq(0);
1117
+ const input1Ctrl = input1.controller("ngModel");
1118
+ const input2 = form.find("input").eq(1);
1119
+ const input2Ctrl = input2.controller("ngModel");
1120
+
1121
+ input1Ctrl.$setViewValue("xx");
1122
+ input2Ctrl.$setViewValue("yy");
1123
+ scope.$apply();
1124
+ expect(form[0].classList.contains("ng-dirty")).toBeTrue();
1125
+ expect(input1[0].classList.contains("ng-dirty")).toBeTrue();
1126
+ expect(input2[0].classList.contains("ng-dirty")).toBeTrue();
1127
+
1128
+ formCtrl.$setPristine();
1129
+ scope.$digest();
1130
+
1131
+ expect(form[0].classList.contains("ng-pristine")).toBeTrue();
1132
+ expect(form[0].classList.contains("ng-dirty")).toBeFalse();
1133
+ expect(formCtrl.$pristine).toBe(true);
1134
+ expect(formCtrl.$dirty).toBe(false);
1135
+
1136
+ expect(input1[0].classList.contains("ng-pristine")).toBeTrue();
1137
+ expect(input1Ctrl.$pristine).toBe(true);
1138
+ expect(input1Ctrl.$dirty).toBe(false);
1139
+ expect(input2[0].classList.contains("ng-pristine")).toBeTrue();
1140
+ expect(input2Ctrl.$pristine).toBe(true);
1141
+ expect(input2Ctrl.$dirty).toBe(false);
1142
+ });
1143
+
1144
+ it("should reset pristine state of anonymous form controls", () => {
1145
+ doc = $compile(
1146
+ '<form name="testForm">' + '<input ng-model="anonymous">' + "</form>",
1147
+ )(scope);
1148
+
1149
+ scope.$digest();
1150
+
1151
+ const form = doc;
1152
+ const formCtrl = scope.testForm;
1153
+ const input = form.find("input").eq(0);
1154
+ const inputCtrl = input.controller("ngModel");
1155
+
1156
+ inputCtrl.$setViewValue("xx");
1157
+ scope.$apply();
1158
+ expect(form[0].classList.contains("ng-dirty")).toBeTrue();
1159
+ expect(input[0].classList.contains("ng-dirty")).toBeTrue();
1160
+
1161
+ formCtrl.$setPristine();
1162
+ scope.$digest();
1163
+ expect(form[0].classList.contains("ng-pristine")).toBeTrue();
1164
+ expect(formCtrl.$pristine).toBe(true);
1165
+ expect(formCtrl.$dirty).toBe(false);
1166
+ expect(input[0].classList.contains("ng-pristine")).toBeTrue();
1167
+ expect(inputCtrl.$pristine).toBe(true);
1168
+ expect(inputCtrl.$dirty).toBe(false);
1169
+ });
1170
+
1171
+ it("should reset pristine state of nested forms", () => {
1172
+ doc = $compile(
1173
+ '<form name="testForm">' +
1174
+ "<div ng-form>" +
1175
+ '<input ng-model="named" name="foo">' +
1176
+ "</div>" +
1177
+ "</form>",
1178
+ )(scope);
1179
+
1180
+ scope.$digest();
1181
+
1182
+ const form = doc;
1183
+ const formCtrl = scope.testForm;
1184
+ const nestedForm = form.find("div");
1185
+ const nestedFormCtrl = nestedForm.controller("form");
1186
+ const nestedInput = form.find("input").eq(0);
1187
+ const nestedInputCtrl = nestedInput.controller("ngModel");
1188
+
1189
+ nestedInputCtrl.$setViewValue("xx");
1190
+ scope.$apply();
1191
+ expect(form[0].classList.contains("ng-dirty")).toBeTrue();
1192
+ expect(nestedForm[0].classList.contains("ng-dirty")).toBeTrue();
1193
+ expect(nestedInput[0].classList.contains("ng-dirty")).toBeTrue();
1194
+
1195
+ formCtrl.$setPristine();
1196
+ scope.$digest();
1197
+ expect(form[0].classList.contains("ng-pristine")).toBeTrue();
1198
+ scope.$digest();
1199
+
1200
+ expect(formCtrl.$pristine).toBe(true);
1201
+ expect(formCtrl.$dirty).toBe(false);
1202
+ expect(nestedForm[0].classList.contains("ng-pristine")).toBeTrue();
1203
+ expect(nestedFormCtrl.$pristine).toBe(true);
1204
+ expect(nestedFormCtrl.$dirty).toBe(false);
1205
+ expect(nestedInput[0].classList.contains("ng-pristine")).toBeTrue();
1206
+ expect(nestedInputCtrl.$pristine).toBe(true);
1207
+ expect(nestedInputCtrl.$dirty).toBe(false);
1208
+ });
1209
+ });
1210
+
1211
+ describe("$setUntouched", () => {
1212
+ it("should trigger setUntouched on form controls", () => {
1213
+ const form = $compile(
1214
+ '<form name="myForm">' +
1215
+ '<input name="alias" type="text" ng-model="name" />' +
1216
+ "</form>",
1217
+ )(scope);
1218
+ scope.$digest();
1219
+
1220
+ scope.myForm.alias.$setTouched();
1221
+ expect(scope.myForm.alias.$touched).toBe(true);
1222
+ scope.myForm.$setUntouched();
1223
+ expect(scope.myForm.alias.$touched).toBe(false);
1224
+ dealoc(form);
1225
+ });
1226
+
1227
+ it("should trigger setUntouched on form controls with nested forms", () => {
1228
+ const form = $compile(
1229
+ '<form name="myForm">' +
1230
+ '<div class="ng-form" name="childForm">' +
1231
+ '<input name="alias" type="text" ng-model="name" />' +
1232
+ "</div>" +
1233
+ "</form>",
1234
+ )(scope);
1235
+ scope.$digest();
1236
+
1237
+ scope.myForm.childForm.alias.$setTouched();
1238
+ expect(scope.myForm.childForm.alias.$touched).toBe(true);
1239
+ scope.myForm.$setUntouched();
1240
+ expect(scope.myForm.childForm.alias.$touched).toBe(false);
1241
+ dealoc(form);
1242
+ });
1243
+ });
1244
+
1245
+ describe("$getControls", () => {
1246
+ it("should return an empty array if the controller has no controls", () => {
1247
+ doc = $compile('<form name="testForm"></form>')(scope);
1248
+
1249
+ scope.$digest();
1250
+
1251
+ const formCtrl = scope.testForm;
1252
+
1253
+ expect(formCtrl.$getControls()).toEqual([]);
1254
+ });
1255
+
1256
+ it("should return a shallow copy of the form controls", () => {
1257
+ doc = $compile(
1258
+ '<form name="testForm">' +
1259
+ '<input ng-model="named" name="foo">' +
1260
+ "<div ng-form>" +
1261
+ '<input ng-model="named" name="foo">' +
1262
+ "</div>" +
1263
+ "</form>",
1264
+ )(scope);
1265
+
1266
+ scope.$digest();
1267
+
1268
+ const form = doc;
1269
+ const formCtrl = scope.testForm;
1270
+ const formInput = form.children("input").eq(0);
1271
+ const formInputCtrl = formInput.controller("ngModel");
1272
+ const nestedForm = form.find("div");
1273
+ const nestedFormCtrl = nestedForm.controller("form");
1274
+ const nestedInput = nestedForm.children("input").eq(0);
1275
+ const nestedInputCtrl = nestedInput.controller("ngModel");
1276
+
1277
+ const controls = formCtrl.$getControls();
1278
+
1279
+ expect(controls).not.toBe(formCtrl.$$controls);
1280
+
1281
+ controls.push("something");
1282
+ expect(formCtrl.$$controls).not.toContain("something");
1283
+
1284
+ expect(controls[0]).toBe(formInputCtrl);
1285
+ expect(controls[1]).toBe(nestedFormCtrl);
1286
+
1287
+ const nestedControls = controls[1].$getControls();
1288
+
1289
+ expect(nestedControls[0]).toBe(nestedInputCtrl);
1290
+ });
1291
+ });
1292
+
1293
+ it("should rename nested form controls when interpolated name changes", () => {
1294
+ scope.idA = "A";
1295
+ scope.idB = "X";
1296
+
1297
+ doc = $compile(
1298
+ '<form name="form">' +
1299
+ '<div ng-form="nested{{idA}}">' +
1300
+ '<div ng-form name="nested{{idB}}"' +
1301
+ "</div>" +
1302
+ "</div>" +
1303
+ "</form>",
1304
+ )(scope);
1305
+
1306
+ scope.$digest();
1307
+ const formA = scope.form.nestedA;
1308
+ expect(formA).toBeDefined();
1309
+ expect(formA.$name).toBe("nestedA");
1310
+
1311
+ const formX = formA.nestedX;
1312
+ expect(formX).toBeDefined();
1313
+ expect(formX.$name).toBe("nestedX");
1314
+
1315
+ scope.idA = "B";
1316
+ scope.idB = "Y";
1317
+ scope.$digest();
1318
+
1319
+ expect(scope.form.nestedA).toBeUndefined();
1320
+ expect(scope.form.nestedB).toBe(formA);
1321
+ expect(formA.nestedX).toBeUndefined();
1322
+ expect(formA.nestedY).toBe(formX);
1323
+ });
1324
+
1325
+ it("should rename forms with no parent when interpolated name changes", () => {
1326
+ const element = $compile('<form name="name{{nameID}}"></form>')(scope);
1327
+ const element2 = $compile('<div ng-form="ngform{{nameID}}"></div>')(scope);
1328
+ scope.nameID = "A";
1329
+ scope.$digest();
1330
+ const form = element.controller("form");
1331
+ const form2 = element2.controller("form");
1332
+ expect(scope.nameA).toBe(form);
1333
+ expect(scope.ngformA).toBe(form2);
1334
+ expect(form.$name).toBe("nameA");
1335
+ expect(form2.$name).toBe("ngformA");
1336
+
1337
+ scope.nameID = "B";
1338
+ scope.$digest();
1339
+ expect(scope.nameA).toBeUndefined();
1340
+ expect(scope.ngformA).toBeUndefined();
1341
+ expect(scope.nameB).toBe(form);
1342
+ expect(scope.ngformB).toBe(form2);
1343
+ expect(form.$name).toBe("nameB");
1344
+ expect(form2.$name).toBe("ngformB");
1345
+ });
1346
+
1347
+ it("should rename forms with an initially blank name", () => {
1348
+ const element = $compile('<form name="{{name}}"></form>')(scope);
1349
+ scope.$digest();
1350
+ const form = element.controller("form");
1351
+ expect(scope[""]).toBe(form);
1352
+ expect(form.$name).toBe("");
1353
+ scope.name = "foo";
1354
+ scope.$digest();
1355
+ expect(scope.foo).toBe(form);
1356
+ expect(form.$name).toBe("foo");
1357
+ expect(scope.foo).toBe(form);
1358
+ });
1359
+
1360
+ describe("$setSubmitted", () => {
1361
+ beforeEach(() => {
1362
+ doc = $compile(
1363
+ '<form name="form" ng-submit="submitted = true">' +
1364
+ '<input type="text" ng-model="name" required />' +
1365
+ '<input type="submit" />' +
1366
+ "</form>",
1367
+ )(scope);
1368
+
1369
+ scope.$digest();
1370
+ });
1371
+
1372
+ it("should not init in submitted state", () => {
1373
+ expect(scope.form.$submitted).toBe(false);
1374
+ });
1375
+
1376
+ it("should be in submitted state when submitted", () => {
1377
+ // browserTrigger(doc, "submit");
1378
+ doc[0].dispatchEvent(new Event("submit"));
1379
+ expect(scope.form.$submitted).toBe(true);
1380
+ });
1381
+
1382
+ it("should revert submitted back to false when $setPristine is called on the form", () => {
1383
+ scope.form.$submitted = true;
1384
+ scope.form.$setPristine();
1385
+ expect(scope.form.$submitted).toBe(false);
1386
+ });
1387
+ });
1388
+
1389
+ describe("form animations", () => {
1390
+ function assertValidAnimation(
1391
+ animation,
1392
+ event,
1393
+ classNameAdded,
1394
+ classNameRemoved,
1395
+ ) {
1396
+ expect(animation.event).toBe(event);
1397
+ expect(animation.args[1]).toBe(classNameAdded);
1398
+ expect(animation.args[2]).toBe(classNameRemoved);
1399
+ }
1400
+
1401
+ let form;
1402
+ let $animate;
1403
+ let myModule;
1404
+
1405
+ beforeEach(() => {
1406
+ let dummy = window.document.getElementById("dummy");
1407
+ doc = jqLite('<form name="myForm"></form>');
1408
+ jqLite(dummy).append(doc);
1409
+ let angular = new Angular();
1410
+ publishExternalAPI();
1411
+ myModule = window.angular.module("myModule", ["ngAnimate"]);
1412
+
1413
+ injector = angular.bootstrap(dummy, ["myModule"]);
1414
+ injector.invoke((_$compile_, $rootScope, _$animate_) => {
1415
+ $compile = _$compile_;
1416
+ scope = $rootScope.$new();
1417
+ $animate = _$animate_;
1418
+ });
1419
+ form = scope.myForm;
1420
+ });
1421
+
1422
+ afterEach(() => {
1423
+ dealoc(doc);
1424
+ dealoc(dummy);
1425
+ window.document.getElementById("dummy").innerHTML = "";
1426
+ });
1427
+
1428
+ it("should trigger an animation when invalid", (done) => {
1429
+ form.$setValidity("required", false);
1430
+ setTimeout(() => {
1431
+ // assertValidAnimation($animate.queue[0], "removeClass", "ng-valid");
1432
+ // assertValidAnimation($animate.queue[1], "addClass", "ng-invalid");
1433
+ // assertValidAnimation($animate.queue[2], "addClass", "ng-invalid-required");
1434
+ expect(doc[0].classList.contains("ng-valid")).toBeTrue();
1435
+ expect(doc[0].classList.contains("ng-invalid-add")).toBeTrue();
1436
+ expect(doc[0].classList.contains("ng-invalid-required-add")).toBeTrue();
1437
+ done();
1438
+ }, 100);
1439
+ });
1440
+
1441
+ it("should trigger an animation when valid", (done) => {
1442
+ form.$setValidity("required", false);
1443
+
1444
+ form.$setValidity("required", true);
1445
+
1446
+ setTimeout(() => {
1447
+ // assertValidAnimation($animate.queue[0], "addClass", "ng-valid");
1448
+ // assertValidAnimation($animate.queue[1], "removeClass", "ng-invalid");
1449
+ // assertValidAnimation($animate.queue[2], "addClass", "ng-valid-required");
1450
+ expect(doc[0].classList.contains("ng-valid")).toBeTrue();
1451
+ expect(doc[0].classList.contains("ng-invalid-add")).toBeTrue();
1452
+ expect(doc[0].classList.contains("ng-invalid-required-add")).toBeTrue();
1453
+ done();
1454
+ }, 100);
1455
+ });
1456
+
1457
+ it("should trigger an animation when dirty", (done) => {
1458
+ form.$setDirty();
1459
+ setTimeout(() => {
1460
+ // assertValidAnimation($animate.queue[0], "removeClass", "ng-pristine");
1461
+ // assertValidAnimation($animate.queue[1], "addClass", "ng-dirty");
1462
+ expect(doc[0].classList.contains("ng-pristine")).toBeTrue();
1463
+ expect(doc[0].classList.contains("ng-dirty-add")).toBeTrue();
1464
+ done();
1465
+ }, 100);
1466
+ });
1467
+
1468
+ it("should trigger an animation when pristine", (done) => {
1469
+ form.$setDirty();
1470
+ form.$setPristine();
1471
+ setTimeout(() => {
1472
+ // assertValidAnimation($animate.queue[0], "removeClass", "ng-pristine");
1473
+ // assertValidAnimation($animate.queue[1], "addClass", "ng-dirty");
1474
+ expect(doc[0].classList.contains("ng-pristine")).toBeTrue();
1475
+ expect(doc[0].classList.contains("ng-dirty-add")).toBeTrue();
1476
+ done();
1477
+ }, 100);
1478
+ });
1479
+
1480
+ it("should trigger custom errors as addClass/removeClass when invalid/valid", (done) => {
1481
+ form.$setValidity("custom-error", false);
1482
+
1483
+ setTimeout(() => {
1484
+ // assertValidAnimation($animate.queue[0], "removeClass", "ng-valid");
1485
+ // assertValidAnimation($animate.queue[1], "addClass", "ng-invalid");
1486
+ // assertValidAnimation(
1487
+ // $animate.queue[2],
1488
+ // "addClass",
1489
+ // "ng-invalid-custom-error",
1490
+ // );
1491
+ expect(doc[0].classList.contains("ng-valid")).toBeTrue();
1492
+ expect(doc[0].classList.contains("ng-invalid-add")).toBeTrue();
1493
+ expect(
1494
+ doc[0].classList.contains("ng-invalid-custom-error-add"),
1495
+ ).toBeTrue();
1496
+ }, 100);
1497
+
1498
+ // $animate.queue = [];
1499
+ form.$setValidity("custom-error", true);
1500
+
1501
+ setTimeout(() => {
1502
+ // assertValidAnimation($animate.queue[0], "removeClass", "ng-valid");
1503
+ // assertValidAnimation($animate.queue[1], "addClass", "ng-invalid");
1504
+ // assertValidAnimation(
1505
+ // $animate.queue[2],
1506
+ // "addClass",
1507
+ // "ng-invalid-custom-error",
1508
+ // );
1509
+ expect(doc[0].classList.contains("ng-valid")).toBeTrue();
1510
+ expect(doc[0].classList.contains("ng-invalid-add")).toBeTrue();
1511
+ expect(
1512
+ doc[0].classList.contains("ng-valid-custom-error-add"),
1513
+ ).toBeTrue();
1514
+ done();
1515
+ }, 300);
1516
+ });
1517
+ });
1518
+ });