@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,1008 @@
1
+ import { defaultModelOptions } from "../../../src/directive/ngModelOptions";
2
+ import { dealoc, jqLite } from "../../../src/jqLite";
3
+ import { publishExternalAPI } from "../../../src/public";
4
+ import { createInjector } from "../../../src/injector";
5
+ import { valueFn } from "../../../src/core/utils";
6
+ import { Angular } from "../../../src/loader";
7
+
8
+ function changeGivenInputTo(inputElm, val) {
9
+ inputElm[0].value = val;
10
+ inputElm[0].dispatchEvent(new Event("change"));
11
+ }
12
+
13
+ function browserTrigger(inputElm, event) {
14
+ inputElm[0].dispatchEvent(new Event(event));
15
+ }
16
+
17
+ describe("ngModelOptions", () => {
18
+ describe("defaultModelOptions", () => {
19
+ it("should provide default values", () => {
20
+ expect(defaultModelOptions.getOption("updateOn")).toEqual("");
21
+ expect(defaultModelOptions.getOption("updateOnDefault")).toEqual(true);
22
+ expect(defaultModelOptions.getOption("debounce")).toBe(0);
23
+ expect(defaultModelOptions.getOption("getterSetter")).toBe(false);
24
+ expect(defaultModelOptions.getOption("allowInvalid")).toBe(false);
25
+ expect(defaultModelOptions.getOption("timezone")).toBe(null);
26
+ });
27
+ });
28
+
29
+ describe("directive", () => {
30
+ describe("basic usage", () => {
31
+ let $rootScope;
32
+ let $compile;
33
+ let $timeout;
34
+ let $q;
35
+ let inputElm;
36
+ let formElm;
37
+ let injector;
38
+
39
+ beforeEach(() => {
40
+ publishExternalAPI().decorator("$exceptionHandler", function () {
41
+ return (exception, cause) => {
42
+ throw new Error(exception.message);
43
+ };
44
+ });
45
+ injector = createInjector(["ng"]);
46
+ $compile = injector.get("$compile");
47
+ $rootScope = injector.get("$rootScope");
48
+ $q = injector.get("$q");
49
+ $timeout = injector.get("$timeout");
50
+ });
51
+
52
+ describe("should fall back to `defaultModelOptions`", () => {
53
+ it("if there is no `ngModelOptions` directive", () => {
54
+ formElm = $compile(
55
+ '<form name="form"><input type="text" ng-model="name" name="alias" /></form>',
56
+ )($rootScope);
57
+ inputElm = formElm.find("input");
58
+
59
+ const inputOptions = $rootScope.form.alias.$options;
60
+ expect(inputOptions.getOption("updateOn")).toEqual(
61
+ defaultModelOptions.getOption("updateOn"),
62
+ );
63
+ expect(inputOptions.getOption("updateOnDefault")).toEqual(
64
+ defaultModelOptions.getOption("updateOnDefault"),
65
+ );
66
+ expect(inputOptions.getOption("debounce")).toEqual(
67
+ defaultModelOptions.getOption("debounce"),
68
+ );
69
+ expect(inputOptions.getOption("getterSetter")).toEqual(
70
+ defaultModelOptions.getOption("getterSetter"),
71
+ );
72
+ expect(inputOptions.getOption("allowInvalid")).toEqual(
73
+ defaultModelOptions.getOption("allowInvalid"),
74
+ );
75
+ expect(inputOptions.getOption("timezone")).toEqual(
76
+ defaultModelOptions.getOption("timezone"),
77
+ );
78
+ });
79
+
80
+ it("if `ngModelOptions` on the same element does not specify the option", () => {
81
+ formElm = $compile(
82
+ '<form name="form"><input type="text" ng-model="name" name="alias" ng-model-options="{ updateOn: \'blur\' }"/></form>',
83
+ )($rootScope);
84
+ inputElm = formElm.find("input");
85
+
86
+ const inputOptions = $rootScope.form.alias.$options;
87
+ expect(inputOptions.getOption("debounce")).toEqual(
88
+ defaultModelOptions.getOption("debounce"),
89
+ );
90
+ expect(inputOptions.getOption("updateOnDefault")).toBe(false);
91
+ expect(inputOptions.getOption("updateOnDefault")).not.toEqual(
92
+ defaultModelOptions.getOption("updateOnDefault"),
93
+ );
94
+ });
95
+
96
+ it("if the first `ngModelOptions` ancestor does not specify the option", () => {
97
+ const form = $compile(
98
+ '<form name="form" ng-model-options="{ updateOn: \'blur\' }">' +
99
+ '<input name="alias" ng-model="x">' +
100
+ "</form>",
101
+ )($rootScope);
102
+ const inputOptions = $rootScope.form.alias.$options;
103
+
104
+ expect(inputOptions.getOption("debounce")).toEqual(
105
+ defaultModelOptions.getOption("debounce"),
106
+ );
107
+ expect(inputOptions.getOption("updateOnDefault")).toBe(false);
108
+ expect(inputOptions.getOption("updateOnDefault")).not.toEqual(
109
+ defaultModelOptions.getOption("updateOnDefault"),
110
+ );
111
+ dealoc(form);
112
+ });
113
+ });
114
+
115
+ describe("sharing and inheritance", () => {
116
+ it("should not inherit options from ancestor `ngModelOptions` directives by default", () => {
117
+ const container = $compile(
118
+ '<div ng-model-options="{ allowInvalid: true }">' +
119
+ "<form ng-model-options=\"{ updateOn: 'blur' }\">" +
120
+ "<input ng-model-options=\"{ updateOn: 'default' }\">" +
121
+ "</form>" +
122
+ "</div>",
123
+ )($rootScope);
124
+
125
+ const form = container.find("form");
126
+ const input = container.find("input");
127
+
128
+ const containerOptions =
129
+ container.controller("ngModelOptions").$options;
130
+ const formOptions = form.controller("ngModelOptions").$options;
131
+ const inputOptions = input.controller("ngModelOptions").$options;
132
+
133
+ expect(containerOptions.getOption("allowInvalid")).toEqual(true);
134
+ expect(formOptions.getOption("allowInvalid")).toEqual(false);
135
+ expect(inputOptions.getOption("allowInvalid")).toEqual(false);
136
+
137
+ expect(containerOptions.getOption("updateOn")).toEqual("");
138
+ expect(containerOptions.getOption("updateOnDefault")).toEqual(true);
139
+ expect(formOptions.getOption("updateOn")).toEqual("blur");
140
+ expect(formOptions.getOption("updateOnDefault")).toEqual(false);
141
+ expect(inputOptions.getOption("updateOn")).toEqual("");
142
+ expect(inputOptions.getOption("updateOnDefault")).toEqual(true);
143
+
144
+ dealoc(container);
145
+ });
146
+
147
+ it('should inherit options that are marked with "$inherit" from the nearest ancestor `ngModelOptions` directive', () => {
148
+ const container = $compile(
149
+ '<div ng-model-options="{ allowInvalid: true }">' +
150
+ "<form ng-model-options=\"{ updateOn: 'blur', allowInvalid: '$inherit' }\">" +
151
+ "<input ng-model-options=\"{ updateOn: 'default' }\">" +
152
+ "</form>" +
153
+ "</div>",
154
+ )($rootScope);
155
+
156
+ const form = container.find("form");
157
+ const input = container.find("input");
158
+
159
+ const containerOptions =
160
+ container.controller("ngModelOptions").$options;
161
+ const formOptions = form.controller("ngModelOptions").$options;
162
+ const inputOptions = input.controller("ngModelOptions").$options;
163
+
164
+ expect(containerOptions.getOption("allowInvalid")).toEqual(true);
165
+ expect(formOptions.getOption("allowInvalid")).toEqual(true);
166
+ expect(inputOptions.getOption("allowInvalid")).toEqual(false);
167
+
168
+ expect(containerOptions.getOption("updateOn")).toEqual("");
169
+ expect(containerOptions.getOption("updateOnDefault")).toEqual(true);
170
+ expect(formOptions.getOption("updateOn")).toEqual("blur");
171
+ expect(formOptions.getOption("updateOnDefault")).toEqual(false);
172
+ expect(inputOptions.getOption("updateOn")).toEqual("");
173
+ expect(inputOptions.getOption("updateOnDefault")).toEqual(true);
174
+
175
+ dealoc(container);
176
+ });
177
+
178
+ it('should inherit all unspecified options if the options object contains a `"*"` property with value "$inherit"', () => {
179
+ const container = $compile(
180
+ "<div ng-model-options=\"{ allowInvalid: true, debounce: 100, updateOn: 'keyup' }\">" +
181
+ "<form ng-model-options=\"{ updateOn: 'blur', '*': '$inherit' }\">" +
182
+ "<input ng-model-options=\"{ updateOn: 'default' }\">" +
183
+ "</form>" +
184
+ "</div>",
185
+ )($rootScope);
186
+
187
+ const form = container.find("form");
188
+ const input = container.find("input");
189
+
190
+ const containerOptions =
191
+ container.controller("ngModelOptions").$options;
192
+ const formOptions = form.controller("ngModelOptions").$options;
193
+ const inputOptions = input.controller("ngModelOptions").$options;
194
+
195
+ expect(containerOptions.getOption("allowInvalid")).toEqual(true);
196
+ expect(formOptions.getOption("allowInvalid")).toEqual(true);
197
+ expect(inputOptions.getOption("allowInvalid")).toEqual(false);
198
+
199
+ expect(containerOptions.getOption("debounce")).toEqual(100);
200
+ expect(formOptions.getOption("debounce")).toEqual(100);
201
+ expect(inputOptions.getOption("debounce")).toEqual(0);
202
+
203
+ expect(containerOptions.getOption("updateOn")).toEqual("keyup");
204
+ expect(containerOptions.getOption("updateOnDefault")).toEqual(false);
205
+ expect(formOptions.getOption("updateOn")).toEqual("blur");
206
+ expect(formOptions.getOption("updateOnDefault")).toEqual(false);
207
+ expect(inputOptions.getOption("updateOn")).toEqual("");
208
+ expect(inputOptions.getOption("updateOnDefault")).toEqual(true);
209
+
210
+ dealoc(container);
211
+ });
212
+
213
+ it("should correctly inherit default and another specified event for `updateOn`", () => {
214
+ const container = $compile(
215
+ "<div ng-model-options=\"{updateOn: 'default blur'}\">" +
216
+ "<input ng-model-options=\"{'*': '$inherit'}\">" +
217
+ "</div>",
218
+ )($rootScope);
219
+
220
+ const input = container.find("input");
221
+ const inputOptions = input.controller("ngModelOptions").$options;
222
+
223
+ expect(inputOptions.getOption("updateOn")).toEqual("blur");
224
+ expect(inputOptions.getOption("updateOnDefault")).toEqual(true);
225
+
226
+ dealoc(container);
227
+ });
228
+
229
+ it('should `updateOnDefault` as well if we have `updateOn: "$inherit"`', () => {
230
+ const container = $compile(
231
+ "<div ng-model-options=\"{updateOn: 'keyup'}\">" +
232
+ "<input ng-model-options=\"{updateOn: '$inherit'}\">" +
233
+ "<div ng-model-options=\"{updateOn: 'default blur'}\">" +
234
+ "<input ng-model-options=\"{updateOn: '$inherit'}\">" +
235
+ "</div>" +
236
+ "</div>",
237
+ )($rootScope);
238
+
239
+ const input1 = container.find("input").eq(0);
240
+ const inputOptions1 = input1.controller("ngModelOptions").$options;
241
+
242
+ expect(inputOptions1.getOption("updateOn")).toEqual("keyup");
243
+ expect(inputOptions1.getOption("updateOnDefault")).toEqual(false);
244
+
245
+ const input2 = container.find("input").eq(1);
246
+ const inputOptions2 = input2.controller("ngModelOptions").$options;
247
+
248
+ expect(inputOptions2.getOption("updateOn")).toEqual("blur");
249
+ expect(inputOptions2.getOption("updateOnDefault")).toEqual(true);
250
+
251
+ dealoc(container);
252
+ });
253
+
254
+ it("should make a copy of the options object", () => {
255
+ $rootScope.options = { updateOn: "default" };
256
+ inputElm = $compile(
257
+ '<form name="form"><input type="text" ng-model="name" name="alias" ' +
258
+ 'ng-model-options="options"' +
259
+ "/></from>",
260
+ )($rootScope);
261
+ expect($rootScope.options).toEqual({ updateOn: "default" });
262
+ expect($rootScope.form.alias.$options).not.toBe($rootScope.options);
263
+ });
264
+
265
+ it("should be retrieved from an ancestor element containing an `ngModelOptions` directive", (done) => {
266
+ const doc = $compile(
267
+ '<form name="test" ' +
268
+ "ng-model-options=\"{ debounce: 100, updateOn: 'blur' }\" >" +
269
+ '<input type="text" ng-model="name" name="alias" />' +
270
+ "</form>",
271
+ )($rootScope);
272
+ $rootScope.$digest();
273
+
274
+ inputElm = doc.find("input");
275
+ changeGivenInputTo(inputElm, "a");
276
+ expect($rootScope.name).toEqual(undefined);
277
+ browserTrigger(inputElm, "blur");
278
+ expect($rootScope.name).toBeUndefined();
279
+ setTimeout(() => {
280
+ expect($rootScope.name).toBeUndefined();
281
+ }, 50);
282
+ setTimeout(() => {
283
+ expect($rootScope.name).toEqual("a");
284
+ done();
285
+ }, 200);
286
+ });
287
+
288
+ it("should allow sharing options between multiple inputs", () => {
289
+ $rootScope.options = { updateOn: "default" };
290
+ inputElm = $compile(
291
+ '<input type="text" ng-model="name1" name="alias1" ' +
292
+ 'ng-model-options="options"' +
293
+ "/>" +
294
+ '<input type="text" ng-model="name2" name="alias2" ' +
295
+ 'ng-model-options="options"' +
296
+ "/>",
297
+ )($rootScope);
298
+
299
+ changeGivenInputTo(inputElm.eq(0), "a");
300
+ changeGivenInputTo(inputElm.eq(1), "b");
301
+ expect($rootScope.name1).toEqual("a");
302
+ expect($rootScope.name2).toEqual("b");
303
+ });
304
+ });
305
+
306
+ describe("updateOn", () => {
307
+ it("should allow overriding the model update trigger event on text inputs", () => {
308
+ inputElm = $compile(
309
+ '<input type="text" ng-model="name" name="alias" ' +
310
+ "ng-model-options=\"{ updateOn: 'blur' }\"" +
311
+ "/>",
312
+ )($rootScope);
313
+
314
+ changeGivenInputTo(inputElm, "a");
315
+ expect($rootScope.name).toBeUndefined();
316
+ browserTrigger(inputElm, "blur");
317
+ expect($rootScope.name).toEqual("a");
318
+ });
319
+
320
+ it("should not dirty the input if nothing was changed before updateOn trigger", () => {
321
+ formElm = $compile(
322
+ '<form name="form"><input type="text" ng-model="name" name="alias" ' +
323
+ "ng-model-options=\"{ updateOn: 'blur' }\"" +
324
+ "/></form>",
325
+ )($rootScope);
326
+ inputElm = formElm.find("input");
327
+ expect($rootScope.form.alias.$pristine).toBeTruthy();
328
+ browserTrigger(inputElm, "blur");
329
+
330
+ expect($rootScope.form.alias.$pristine).toBeTruthy();
331
+ });
332
+
333
+ it("should allow overriding the model update trigger event on text areas", () => {
334
+ inputElm = $compile(
335
+ '<textarea ng-model="name" name="alias" ' +
336
+ "ng-model-options=\"{ updateOn: 'blur' }\"" +
337
+ "/>",
338
+ )($rootScope);
339
+
340
+ changeGivenInputTo(inputElm, "a");
341
+ expect($rootScope.name).toBeUndefined();
342
+ browserTrigger(inputElm, "blur");
343
+ expect($rootScope.name).toEqual("a");
344
+ });
345
+
346
+ it("should bind the element to a list of events", () => {
347
+ inputElm = $compile(
348
+ '<input type="text" ng-model="name" name="alias" ' +
349
+ "ng-model-options=\"{ updateOn: 'blur mousemove' }\"" +
350
+ "/>",
351
+ )($rootScope);
352
+
353
+ changeGivenInputTo(inputElm, "a");
354
+ expect($rootScope.name).toBeUndefined();
355
+ browserTrigger(inputElm, "blur");
356
+ expect($rootScope.name).toEqual("a");
357
+
358
+ changeGivenInputTo(inputElm, "b");
359
+ expect($rootScope.name).toEqual("a");
360
+ browserTrigger(inputElm, "mousemove");
361
+ expect($rootScope.name).toEqual("b");
362
+ });
363
+
364
+ it("should allow keeping the default update behavior on text inputs", () => {
365
+ inputElm = $compile(
366
+ '<input type="text" ng-model="name" name="alias" ' +
367
+ "ng-model-options=\"{ updateOn: 'default' }\"" +
368
+ "/>",
369
+ )($rootScope);
370
+
371
+ changeGivenInputTo(inputElm, "a");
372
+ expect($rootScope.name).toEqual("a");
373
+ });
374
+
375
+ // TODO: This doesnt event work in the original
376
+ // it("should allow overriding the model update trigger event on checkboxes", () => {
377
+ // inputElm = $compile(
378
+ // '<input type="checkbox" ng-model="checkbox" ' +
379
+ // "ng-model-options=\"{ updateOn: 'blur' }\"" +
380
+ // "/>",
381
+ // )($rootScope);
382
+
383
+ // browserTrigger(inputElm, "click");
384
+ // expect($rootScope.checkbox).toBeUndefined();
385
+
386
+ // browserTrigger(inputElm, "blur");
387
+ // expect($rootScope.checkbox).toBe(true);
388
+
389
+ // browserTrigger(inputElm, "click");
390
+ // expect($rootScope.checkbox).toBe(true);
391
+ // });
392
+
393
+ // it("should allow keeping the default update behavior on checkboxes", () => {
394
+ // inputElm = $compile(
395
+ // '<input type="checkbox" ng-model="checkbox" ' +
396
+ // "ng-model-options=\"{ updateOn: 'blur default' }\"" +
397
+ // "/>",
398
+ // )($rootScope);
399
+
400
+ // browserTrigger(inputElm, "click");
401
+ // expect($rootScope.checkbox).toBe(true);
402
+
403
+ // browserTrigger(inputElm, "click");
404
+ // expect($rootScope.checkbox).toBe(false);
405
+ // });
406
+
407
+ // it("should allow overriding the model update trigger event on radio buttons", () => {
408
+ // inputElm = $compile(
409
+ // '<input type="radio" ng-model="color" value="white" ' +
410
+ // "ng-model-options=\"{ updateOn: 'blur'}\"" +
411
+ // "/>" +
412
+ // '<input type="radio" ng-model="color" value="red" ' +
413
+ // "ng-model-options=\"{ updateOn: 'blur'}\"" +
414
+ // "/>" +
415
+ // '<input type="radio" ng-model="color" value="blue" ' +
416
+ // "ng-model-options=\"{ updateOn: 'blur'}\"" +
417
+ // "/>",
418
+ // )($rootScope);
419
+
420
+ // $rootScope.$apply("color = 'white'");
421
+ // browserTrigger(inputElm[2], "click");
422
+ // expect($rootScope.color).toBe("white");
423
+
424
+ // browserTrigger(inputElm[2], "blur");
425
+ // expect($rootScope.color).toBe("blue");
426
+ // });
427
+
428
+ // it("should allow keeping the default update behavior on radio buttons", () => {
429
+ // inputElm = $compile(
430
+ // '<input type="radio" ng-model="color" value="white" ' +
431
+ // "ng-model-options=\"{ updateOn: 'blur default' }\"" +
432
+ // "/>" +
433
+ // '<input type="radio" ng-model="color" value="red" ' +
434
+ // "ng-model-options=\"{ updateOn: 'blur default' }\"" +
435
+ // "/>" +
436
+ // '<input type="radio" ng-model="color" value="blue" ' +
437
+ // "ng-model-options=\"{ updateOn: 'blur default' }\"" +
438
+ // "/>",
439
+ // )($rootScope);
440
+
441
+ // $rootScope.$apply("color = 'white'");
442
+ // browserTrigger(jqLite(inputElm[2]), "click");
443
+ // expect($rootScope.color).toBe("blue");
444
+ // });
445
+
446
+ it("should re-set the trigger events when overridden with $overrideModelOptions", () => {
447
+ inputElm = $compile(
448
+ '<input type="text" ng-model="name" name="alias" ' +
449
+ "ng-model-options=\"{ updateOn: 'blur click' }\"" +
450
+ "/>",
451
+ )($rootScope);
452
+
453
+ const ctrl = inputElm.controller("ngModel");
454
+
455
+ changeGivenInputTo(inputElm, "a");
456
+ expect($rootScope.name).toBeUndefined();
457
+ browserTrigger(inputElm, "blur");
458
+ expect($rootScope.name).toEqual("a");
459
+
460
+ changeGivenInputTo(inputElm, "b");
461
+ expect($rootScope.name).toBe("a");
462
+ browserTrigger(inputElm, "click");
463
+ expect($rootScope.name).toEqual("b");
464
+
465
+ $rootScope.$apply("name = undefined");
466
+ expect(inputElm.val()).toBe("");
467
+ ctrl.$overrideModelOptions({ updateOn: "blur mousedown" });
468
+
469
+ changeGivenInputTo(inputElm, "a");
470
+ expect($rootScope.name).toBeUndefined();
471
+ browserTrigger(inputElm, "blur");
472
+ expect($rootScope.name).toEqual("a");
473
+
474
+ changeGivenInputTo(inputElm, "b");
475
+ expect($rootScope.name).toBe("a");
476
+ browserTrigger(inputElm, "click");
477
+ expect($rootScope.name).toBe("a");
478
+
479
+ browserTrigger(inputElm, "mousedown");
480
+ expect($rootScope.name).toEqual("b");
481
+ });
482
+ });
483
+
484
+ //describe("debounce", () => {
485
+ // it("should trigger only after timeout in text inputs", (done) => {
486
+ // inputElm = $compile(
487
+ // '<input type="text" ng-model="name" name="alias" ' +
488
+ // 'ng-model-options="{ debounce: 100 }"' +
489
+ // "/>",
490
+ // )($rootScope);
491
+
492
+ // changeGivenInputTo(inputElm, "a");
493
+ // changeGivenInputTo(inputElm, "b");
494
+ // changeGivenInputTo(inputElm, "c");
495
+ // //expect($rootScope.name).toEqual(undefined);
496
+ // //$timeout.flush(2000);
497
+ // //expect($rootScope.name).toEqual(undefined);
498
+ // //$timeout.flush(9000);
499
+ // setTimeout(() => {
500
+ // expect($rootScope.name).toEqual("c");
501
+ // done();
502
+ // }, 2000);
503
+
504
+ // });
505
+
506
+ // it("should trigger only after timeout in checkboxes", () => {
507
+ // inputElm = $compile(
508
+ // '<input type="checkbox" ng-model="checkbox" ' +
509
+ // 'ng-model-options="{ debounce: 10000 }"' +
510
+ // "/>",
511
+ // )($rootScope);
512
+
513
+ // browserTrigger(inputElm, "click");
514
+ // expect($rootScope.checkbox).toBeUndefined();
515
+ // //$timeout.flush(2000);
516
+ // expect($rootScope.checkbox).toBeUndefined();
517
+ // //$timeout.flush(9000);
518
+ // expect($rootScope.checkbox).toBe(true);
519
+ // });
520
+
521
+ // it("should trigger only after timeout in radio buttons", () => {
522
+ // inputElm = $compile(
523
+ // '<input type="radio" ng-model="color" value="white" />' +
524
+ // '<input type="radio" ng-model="color" value="red" ' +
525
+ // 'ng-model-options="{ debounce: 20000 }"' +
526
+ // "/>" +
527
+ // '<input type="radio" ng-model="color" value="blue" ' +
528
+ // 'ng-model-options="{ debounce: 30000 }"' +
529
+ // "/>",
530
+ // )($rootScope);
531
+
532
+ // browserTrigger(inputElm[0], "click");
533
+ // expect($rootScope.color).toBe("white");
534
+ // browserTrigger(inputElm[1], "click");
535
+ // expect($rootScope.color).toBe("white");
536
+ // //$timeout.flush(12000);
537
+ // expect($rootScope.color).toBe("white");
538
+ // //$timeout.flush(10000);
539
+ // expect($rootScope.color).toBe("red");
540
+ // });
541
+
542
+ // it("should not trigger digest while debouncing", () => {
543
+ // inputElm = $compile(
544
+ // '<input type="text" ng-model="name" name="alias" ' +
545
+ // 'ng-model-options="{ debounce: 10000 }"' +
546
+ // "/>",
547
+ // )($rootScope);
548
+
549
+ // const watchSpy = jasmine.createSpy("watchSpy");
550
+ // $rootScope.$watch(watchSpy);
551
+
552
+ // changeGivenInputTo(inputElm, "a");
553
+ // $timeout.flush(2000);
554
+ // expect(watchSpy).not.toHaveBeenCalled();
555
+
556
+ // changeGivenInputTo(inputElm, "b");
557
+ // $timeout.flush(2000);
558
+ // expect(watchSpy).not.toHaveBeenCalled();
559
+
560
+ // changeGivenInputTo(inputElm, "c");
561
+ // $timeout.flush(10000);
562
+ // expect(watchSpy).toHaveBeenCalled();
563
+ // });
564
+
565
+ // it("should allow selecting different debounce timeouts for each event", () => {
566
+ // inputElm = $compile(
567
+ // '<input type="text" ng-model="name" name="alias" ' +
568
+ // 'ng-model-options="{' +
569
+ // "updateOn: 'default blur mouseup', " +
570
+ // "debounce: { default: 10000, blur: 5000 }" +
571
+ // '}"' +
572
+ // "/>",
573
+ // )($rootScope);
574
+
575
+ // changeGivenInputTo(inputElm, "a");
576
+ // expect($rootScope.name).toBeUndefined();
577
+ // //$timeout.flush(6000);
578
+ // expect($rootScope.name).toBeUndefined();
579
+ // //$timeout.flush(4000);
580
+ // expect($rootScope.name).toEqual("a");
581
+
582
+ // changeGivenInputTo(inputElm, "b");
583
+ // browserTrigger(inputElm, "blur");
584
+ // //$timeout.flush(4000);
585
+ // expect($rootScope.name).toEqual("a");
586
+ // //$timeout.flush(2000);
587
+ // expect($rootScope.name).toEqual("b");
588
+
589
+ // changeGivenInputTo(inputElm, "c");
590
+ // browserTrigger(helper.inputElm, "mouseup");
591
+ // // `default` in `debounce` only affects the event triggers that are not defined in updateOn
592
+ // expect($rootScope.name).toEqual("c");
593
+ // });
594
+
595
+ // it("should use the value of * to debounce all unspecified events", () => {
596
+ // inputElm = $compile(
597
+ // '<input type="text" ng-model="name" name="alias" ' +
598
+ // 'ng-model-options="{' +
599
+ // "updateOn: 'default blur mouseup', " +
600
+ // "debounce: { default: 10000, blur: 5000, '*': 15000 }" +
601
+ // '}"' +
602
+ // "/>",
603
+ // )($rootScope);
604
+
605
+ // changeGivenInputTo(inputElm, "a");
606
+ // expect($rootScope.name).toBeUndefined();
607
+ // $timeout.flush(6000);
608
+ // expect($rootScope.name).toBeUndefined();
609
+ // $timeout.flush(4000);
610
+ // expect($rootScope.name).toEqual("a");
611
+
612
+ // changeGivenInputTo(inputElm, "b");
613
+ // browserTrigger(inputElm, "blur");
614
+ // $timeout.flush(4000);
615
+ // expect($rootScope.name).toEqual("a");
616
+ // $timeout.flush(2000);
617
+ // expect($rootScope.name).toEqual("b");
618
+
619
+ // changeGivenInputTo(inputElm, "c");
620
+ // browserTrigger(helper.inputElm, "mouseup");
621
+ // expect($rootScope.name).toEqual("b");
622
+ // $timeout.flush(10000); // flush default
623
+ // expect($rootScope.name).toEqual("b");
624
+ // $timeout.flush(5000);
625
+ // expect($rootScope.name).toEqual("c");
626
+ // });
627
+
628
+ // it("should trigger immediately for the event if not listed in the debounce list", () => {
629
+ // inputElm = $compile(
630
+ // '<input type="text" ng-model="name" name="alias" ' +
631
+ // 'ng-model-options="{' +
632
+ // "updateOn: 'default blur foo', " +
633
+ // "debounce: { blur: 5000 }" +
634
+ // '}"' +
635
+ // "/>",
636
+ // )($rootScope);
637
+
638
+ // changeGivenInputTo(inputElm, "a");
639
+ // expect($rootScope.name).toEqual("a");
640
+
641
+ // changeGivenInputTo(inputElm, "b");
642
+ // browserTrigger(inputElm, "foo");
643
+ // expect($rootScope.name).toEqual("b");
644
+ // });
645
+
646
+ // it("should allow selecting different debounce timeouts for each event on checkboxes", () => {
647
+ // inputElm = $compile(
648
+ // '<input type="checkbox" ng-model="checkbox" ' +
649
+ // 'ng-model-options="{ ' +
650
+ // "updateOn: 'default blur', debounce: { default: 10000, blur: 5000 } }\"" +
651
+ // "/>",
652
+ // )($rootScope);
653
+
654
+ // inputElm[0].checked = false;
655
+ // browserTrigger(inputElm, "click");
656
+ // expect($rootScope.checkbox).toBeUndefined();
657
+ // $timeout.flush(8000);
658
+ // expect($rootScope.checkbox).toBeUndefined();
659
+ // $timeout.flush(3000);
660
+ // expect($rootScope.checkbox).toBe(true);
661
+ // inputElm[0].checked = true;
662
+ // browserTrigger(inputElm, "click");
663
+ // browserTrigger(inputElm, "blur");
664
+ // $timeout.flush(3000);
665
+ // expect($rootScope.checkbox).toBe(true);
666
+ // $timeout.flush(3000);
667
+ // expect($rootScope.checkbox).toBe(false);
668
+ // });
669
+
670
+ // it("should allow selecting 0 for non-default debounce timeouts for each event on checkboxes", () => {
671
+ // inputElm = $compile(
672
+ // '<input type="checkbox" ng-model="checkbox" ' +
673
+ // 'ng-model-options="{ ' +
674
+ // "updateOn: 'default blur', debounce: { default: 10000, blur: 0 } }\"" +
675
+ // "/>",
676
+ // )($rootScope);
677
+
678
+ // inputElm[0].checked = false;
679
+ // browserTrigger(inputElm, "click");
680
+ // expect($rootScope.checkbox).toBeUndefined();
681
+ // $timeout.flush(8000);
682
+ // expect($rootScope.checkbox).toBeUndefined();
683
+ // $timeout.flush(3000);
684
+ // expect($rootScope.checkbox).toBe(true);
685
+ // inputElm[0].checked = true;
686
+ // browserTrigger(inputElm, "click");
687
+ // browserTrigger(inputElm, "blur");
688
+ // $timeout.flush(0);
689
+ // expect($rootScope.checkbox).toBe(false);
690
+ // });
691
+
692
+ // it("should flush debounced events when calling $commitViewValue directly", () => {
693
+ // inputElm = $compile(
694
+ // '<input type="text" ng-model="name" name="alias" ' +
695
+ // 'ng-model-options="{ debounce: 1000 }" />',
696
+ // )($rootScope);
697
+
698
+ // changeGivenInputTo(inputElm, "a");
699
+ // expect($rootScope.name).toEqual(undefined);
700
+ // $rootScope.form.alias.$commitViewValue();
701
+ // expect($rootScope.name).toEqual("a");
702
+ // });
703
+
704
+ // it("should cancel debounced events when calling $commitViewValue", () => {
705
+ // inputElm = $compile(
706
+ // '<input type="text" ng-model="name" name="alias" ' +
707
+ // 'ng-model-options="{ debounce: 1000 }"/>',
708
+ // )($rootScope);
709
+
710
+ // changeGivenInputTo(inputElm, "a");
711
+ // $rootScope.form.alias.$commitViewValue();
712
+ // expect($rootScope.name).toEqual("a");
713
+
714
+ // $rootScope.form.alias.$setPristine();
715
+ // $timeout.flush(1000);
716
+ // expect($rootScope.form.alias.$pristine).toBeTruthy();
717
+ // });
718
+
719
+ // it("should reset input val if rollbackViewValue called during pending update", () => {
720
+ // inputElm = $compile(
721
+ // '<input type="text" ng-model="name" name="alias" ' +
722
+ // "ng-model-options=\"{ updateOn: 'blur' }\" />",
723
+ // )($rootScope);
724
+
725
+ // changeGivenInputTo(inputElm, "a");
726
+ // expect(inputElm.val()).toBe("a");
727
+ // $rootScope.form.alias.$rollbackViewValue();
728
+ // expect(inputElm.val()).toBe("");
729
+ // browserTrigger(inputElm, "blur");
730
+ // expect(inputElm.val()).toBe("");
731
+ // });
732
+
733
+ // it("should allow canceling pending updates", () => {
734
+ // inputElm = $compile(
735
+ // '<input type="text" ng-model="name" name="alias" ' +
736
+ // "ng-model-options=\"{ updateOn: 'blur' }\" />",
737
+ // )($rootScope);
738
+
739
+ // changeGivenInputTo(inputElm, "a");
740
+ // expect($rootScope.name).toEqual(undefined);
741
+ // $rootScope.form.alias.$rollbackViewValue();
742
+ // expect($rootScope.name).toEqual(undefined);
743
+ // browserTrigger(inputElm, "blur");
744
+ // expect($rootScope.name).toEqual(undefined);
745
+ // });
746
+
747
+ // it("should allow canceling debounced updates", () => {
748
+ // inputElm = $compile(
749
+ // '<input type="text" ng-model="name" name="alias" ' +
750
+ // 'ng-model-options="{ debounce: 10000 }" />',
751
+ // )($rootScope);
752
+
753
+ // changeGivenInputTo(inputElm, "a");
754
+ // expect($rootScope.name).toEqual(undefined);
755
+ // $timeout.flush(2000);
756
+ // $rootScope.form.alias.$rollbackViewValue();
757
+ // expect($rootScope.name).toEqual(undefined);
758
+ // $timeout.flush(10000);
759
+ // expect($rootScope.name).toEqual(undefined);
760
+ // });
761
+
762
+ // it("should handle model updates correctly even if rollbackViewValue is not invoked", () => {
763
+ // inputElm = $compile(
764
+ // '<input type="text" ng-model="name" name="alias" ' +
765
+ // "ng-model-options=\"{ updateOn: 'blur' }\" />",
766
+ // )($rootScope);
767
+
768
+ // changeGivenInputTo(inputElm, "a");
769
+ // $rootScope.$apply("name = 'b'");
770
+ // browserTrigger(inputElm, "blur");
771
+ // expect($rootScope.name).toBe("b");
772
+ // });
773
+
774
+ // it("should reset input val if rollbackViewValue called during debounce", () => {
775
+ // inputElm = $compile(
776
+ // '<input type="text" ng-model="name" name="alias" ' +
777
+ // 'ng-model-options="{ debounce: 2000 }" />',
778
+ // )($rootScope);
779
+
780
+ // changeGivenInputTo(inputElm, "a");
781
+ // expect(inputElm.val()).toBe("a");
782
+ // $rootScope.form.alias.$rollbackViewValue();
783
+ // expect(inputElm.val()).toBe("");
784
+ // $timeout.flush(3000);
785
+ // expect(inputElm.val()).toBe("");
786
+ // });
787
+ //});
788
+
789
+ describe("getterSetter", () => {
790
+ it("should not try to invoke a model if getterSetter is false", () => {
791
+ inputElm = $compile(
792
+ '<input type="text" ng-model="name" ' +
793
+ 'ng-model-options="{ getterSetter: false }" />',
794
+ )($rootScope);
795
+
796
+ const spy = ($rootScope.name = jasmine.createSpy("setterSpy"));
797
+ changeGivenInputTo(inputElm, "a");
798
+ expect(spy).not.toHaveBeenCalled();
799
+ expect(inputElm.val()).toBe("a");
800
+ });
801
+
802
+ it("should not try to invoke a model if getterSetter is not set", () => {
803
+ inputElm = $compile('<input type="text" ng-model="name" />')(
804
+ $rootScope,
805
+ );
806
+
807
+ const spy = ($rootScope.name = jasmine.createSpy("setterSpy"));
808
+ changeGivenInputTo(inputElm, "a");
809
+ expect(spy).not.toHaveBeenCalled();
810
+ expect(inputElm.val()).toBe("a");
811
+ });
812
+
813
+ it("should try to invoke a function model if getterSetter is true", () => {
814
+ inputElm = $compile(
815
+ '<input type="text" ng-model="name" ' +
816
+ 'ng-model-options="{ getterSetter: true }" />',
817
+ )($rootScope);
818
+
819
+ const spy = ($rootScope.name = jasmine
820
+ .createSpy("setterSpy")
821
+ .and.callFake(() => "b"));
822
+ $rootScope.$apply();
823
+ expect(inputElm.val()).toBe("b");
824
+
825
+ changeGivenInputTo(inputElm, "a");
826
+ expect(inputElm.val()).toBe("b");
827
+ expect(spy).toHaveBeenCalledWith("a");
828
+ expect($rootScope.name).toBe(spy);
829
+ });
830
+
831
+ it("should assign to non-function models if getterSetter is true", () => {
832
+ inputElm = $compile(
833
+ '<input type="text" ng-model="name" ' +
834
+ 'ng-model-options="{ getterSetter: true }" />',
835
+ )($rootScope);
836
+
837
+ $rootScope.name = "c";
838
+ changeGivenInputTo(inputElm, "d");
839
+ expect(inputElm.val()).toBe("d");
840
+ expect($rootScope.name).toBe("d");
841
+ });
842
+
843
+ it("should fail on non-assignable model binding if getterSetter is false", () => {
844
+ expect(() => {
845
+ inputElm = $compile(
846
+ '<input type="text" ng-model="accessor(user, \'name\')" />',
847
+ )($rootScope);
848
+ }).toThrowError(/nonassign/);
849
+ });
850
+
851
+ it("should not fail on non-assignable model binding if getterSetter is true", () => {
852
+ expect(() => {
853
+ inputElm = $compile(
854
+ '<input type="text" ng-model="accessor(user, \'name\')" ' +
855
+ 'ng-model-options="{ getterSetter: true }" />',
856
+ )($rootScope);
857
+ }).not.toThrow();
858
+ });
859
+
860
+ it("should invoke a model in the correct context if getterSetter is true", () => {
861
+ inputElm = $compile(
862
+ '<input type="text" ng-model="someService.getterSetter" ' +
863
+ 'ng-model-options="{ getterSetter: true }" />',
864
+ )($rootScope);
865
+
866
+ $rootScope.someService = {
867
+ value: "a",
868
+ getterSetter(newValue) {
869
+ this.value = newValue || this.value;
870
+ return this.value;
871
+ },
872
+ };
873
+ spyOn($rootScope.someService, "getterSetter").and.callThrough();
874
+ $rootScope.$apply();
875
+
876
+ expect(inputElm.val()).toBe("a");
877
+ expect($rootScope.someService.getterSetter).toHaveBeenCalledWith();
878
+ expect($rootScope.someService.value).toBe("a");
879
+
880
+ changeGivenInputTo(inputElm, "b");
881
+ expect($rootScope.someService.getterSetter).toHaveBeenCalledWith("b");
882
+ expect($rootScope.someService.value).toBe("b");
883
+
884
+ $rootScope.someService.value = "c";
885
+ $rootScope.$apply();
886
+ expect(inputElm.val()).toBe("c");
887
+ expect($rootScope.someService.getterSetter).toHaveBeenCalledWith();
888
+ });
889
+ });
890
+
891
+ describe("allowInvalid", () => {
892
+ it("should assign invalid values to the scope if allowInvalid is true", () => {
893
+ inputElm = $compile(
894
+ '<input type="text" name="input" ng-model="value" maxlength="1" ' +
895
+ 'ng-model-options="{allowInvalid: true}" />',
896
+ )($rootScope);
897
+ changeGivenInputTo(inputElm, "12345");
898
+
899
+ expect($rootScope.value).toBe("12345");
900
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
901
+ });
902
+
903
+ it("should not assign not parsable values to the scope if allowInvalid is true", () => {
904
+ inputElm = $compile(
905
+ '<input type="number" name="input" ng-model="value" ' +
906
+ 'ng-model-options="{allowInvalid: true}" />',
907
+ )($rootScope);
908
+ changeGivenInputTo(inputElm, "abcd");
909
+
910
+ expect($rootScope.value).toBeNull();
911
+ // TODO: -> expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
912
+ });
913
+
914
+ it("should update the scope before async validators execute if allowInvalid is true", () => {
915
+ formElm = $compile(
916
+ '<form name="form"><input type="text" name="input" ng-model="value" ' +
917
+ 'ng-model-options="{allowInvalid: true}" /></form>',
918
+ )($rootScope);
919
+
920
+ inputElm = formElm.find("input");
921
+
922
+ let defer;
923
+ $rootScope.form.input.$asyncValidators.promiseValidator = function (
924
+ value,
925
+ ) {
926
+ defer = $q.defer();
927
+ return defer.promise;
928
+ };
929
+ changeGivenInputTo(inputElm, "12345");
930
+
931
+ expect($rootScope.value).toBe("12345");
932
+ expect($rootScope.form.input.$pending.promiseValidator).toBe(true);
933
+ defer.reject();
934
+ $rootScope.$digest();
935
+ expect($rootScope.value).toBe("12345");
936
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
937
+ });
938
+
939
+ it("should update the view before async validators execute if allowInvalid is true", () => {
940
+ formElm = $compile(
941
+ '<form name="form"><input type="text" name="input" ng-model="value" ' +
942
+ 'ng-model-options="{allowInvalid: true}" /></form>',
943
+ )($rootScope);
944
+ inputElm = formElm.find("input");
945
+ let defer;
946
+ $rootScope.form.input.$asyncValidators.promiseValidator = function (
947
+ value,
948
+ ) {
949
+ defer = $q.defer();
950
+ return defer.promise;
951
+ };
952
+ $rootScope.$apply("value = '12345'");
953
+
954
+ expect(inputElm.val()).toBe("12345");
955
+ expect($rootScope.form.input.$pending.promiseValidator).toBe(true);
956
+ defer.reject();
957
+ $rootScope.$digest();
958
+ expect(inputElm.val()).toBe("12345");
959
+ expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
960
+ });
961
+
962
+ it("should not call ng-change listeners twice if the model did not change with allowInvalid", () => {
963
+ formElm = $compile(
964
+ '<form name="form"><input type="text" name="input" ng-model="value" ' +
965
+ 'ng-model-options="{allowInvalid: true}" ng-change="changed()" /></form>',
966
+ )($rootScope);
967
+ inputElm = formElm.find("input");
968
+ $rootScope.changed = jasmine.createSpy("changed");
969
+ $rootScope.form.input.$parsers.push((value) => "modelValue");
970
+
971
+ changeGivenInputTo(inputElm, "input1");
972
+ expect($rootScope.value).toBe("modelValue");
973
+ expect($rootScope.changed).toHaveBeenCalled();
974
+
975
+ changeGivenInputTo(inputElm, "input2");
976
+ expect($rootScope.value).toBe("modelValue");
977
+ expect($rootScope.changed).toHaveBeenCalled();
978
+ });
979
+ });
980
+ });
981
+
982
+ describe("on directives with `replace: true`", () => {
983
+ let inputElm, module, $rootScope;
984
+
985
+ beforeEach(() => {
986
+ angular = new Angular();
987
+ publishExternalAPI();
988
+ module = window.angular.module("myModule", []).directive(
989
+ "foo",
990
+ valueFn({
991
+ replace: true,
992
+ template:
993
+ '<input type="text" ng-model-options="{debounce: 1000}" />',
994
+ }),
995
+ );
996
+ });
997
+
998
+ it("should get initialized in time for `ngModel` on the original element", () => {
999
+ inputElm = jqLite('<foo ng-model="value"></foo>');
1000
+ const injector = angular.bootstrap(inputElm, ["myModule"]);
1001
+ $rootScope = injector.get("$rootScope");
1002
+ const ngModelCtrl = inputElm.controller("ngModel");
1003
+
1004
+ expect(ngModelCtrl.$options.getOption("debounce")).toBe(1000);
1005
+ });
1006
+ });
1007
+ });
1008
+ });