@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,1245 @@
1
+ import { createInjector } from "../../src/injector";
2
+ import { publishExternalAPI } from "../../src/public";
3
+ import { dealoc } from "../../src/jqLite";
4
+
5
+ describe("$aria", () => {
6
+ let scope;
7
+ let $compile;
8
+ let element;
9
+
10
+ beforeEach(() => {
11
+ publishExternalAPI();
12
+ window.angular.module("test", ["ngAria"]);
13
+ let injector = createInjector(["test"]);
14
+ scope = injector.get("$rootScope");
15
+ $compile = injector.get("$compile");
16
+ });
17
+
18
+ afterEach(() => {
19
+ dealoc(element);
20
+ });
21
+
22
+ describe("with `ngAriaDisable`", () => {
23
+ // ariaChecked
24
+ it("should not attach aria-checked to custom checkbox", () => {
25
+ element = $compile(
26
+ '<div role="checkbox" ng-model="val" ng-aria-disable></div>',
27
+ )(scope);
28
+
29
+ scope.$apply("val = false");
30
+ expect(element[0].hasAttribute("aria-checked")).toBeFalse();
31
+
32
+ scope.$apply("val = true");
33
+ expect(element[0].hasAttribute("aria-checked")).toBeFalse();
34
+ });
35
+
36
+ it("should not attach aria-checked to custom radio controls", () => {
37
+ element = $compile(
38
+ '<div role="radio" ng-model="val" value="one" ng-aria-disable></div>' +
39
+ '<div role="radio" ng-model="val" value="two" ng-aria-disable></div>',
40
+ )(scope);
41
+ scope.$digest();
42
+
43
+ const radio1 = element.eq(0);
44
+ const radio2 = element.eq(1);
45
+
46
+ scope.$apply('val = "one"');
47
+ expect(radio1[0].hasAttribute("aria-checked")).toBeFalse();
48
+ expect(radio2[0].hasAttribute("aria-checked")).toBeFalse();
49
+
50
+ scope.$apply('val = "two"');
51
+ expect(radio1[0].hasAttribute("aria-checked")).toBeFalse();
52
+ expect(radio2[0].hasAttribute("aria-checked")).toBeFalse();
53
+ });
54
+
55
+ // ariaDisabled
56
+ it("should not attach aria-disabled to custom controls", () => {
57
+ element = $compile('<div ng-disabled="val" ng-aria-disable></div>')(
58
+ scope,
59
+ );
60
+ scope.$digest();
61
+
62
+ scope.$apply("val = false");
63
+ expect(element[0].hasAttribute("aria-disabled")).toBeFalse();
64
+
65
+ scope.$apply("val = true");
66
+ expect(element[0].hasAttribute("aria-disabled")).toBeFalse();
67
+ });
68
+
69
+ // ariaHidden
70
+ it("should not attach aria-hidden to `ngShow`", () => {
71
+ element = $compile('<div ng-show="val" ng-aria-disable></div>')(scope);
72
+ scope.$digest();
73
+
74
+ scope.$apply("val = false");
75
+ expect(element[0].hasAttribute("aria-hidden")).toBeFalse();
76
+
77
+ scope.$apply("val = true");
78
+ expect(element[0].hasAttribute("aria-hidden")).toBeFalse();
79
+ });
80
+
81
+ it("should not attach aria-hidden to `ngHide`", () => {
82
+ element = $compile('<div ng-hide="val" ng-aria-disable></div>')(scope);
83
+ scope.$digest();
84
+
85
+ scope.$apply("val = false");
86
+ expect(element[0].hasAttribute("aria-hidden")).toBeFalse();
87
+
88
+ scope.$apply("val = true");
89
+ expect(element[0].hasAttribute("aria-hidden")).toBeFalse();
90
+ });
91
+
92
+ // ariaInvalid
93
+ it("should not attach aria-invalid to input", () => {
94
+ element = $compile(
95
+ '<input ng-model="val" ng-minlength="10" ng-aria-disable />',
96
+ )(scope);
97
+ scope.$digest();
98
+
99
+ scope.$apply('val = "lt 10"');
100
+ expect(element[0].hasAttribute("aria-invalid")).toBeFalse();
101
+
102
+ scope.$apply('val = "gt 10 characters"');
103
+ expect(element[0].hasAttribute("aria-invalid")).toBeFalse();
104
+ });
105
+
106
+ it("should not attach aria-invalid to custom controls", () => {
107
+ element = $compile(
108
+ '<div role="textbox" ng-model="val" ng-minlength="10" ng-aria-disable></div>',
109
+ )(scope);
110
+ scope.$digest();
111
+
112
+ scope.$apply('val = "lt 10"');
113
+ expect(element[0].hasAttribute("aria-invalid")).toBeFalse();
114
+
115
+ scope.$apply('val = "gt 10 characters"');
116
+ expect(element[0].hasAttribute("aria-invalid")).toBeFalse();
117
+ });
118
+
119
+ // ariaLive
120
+ it("should not attach aria-live to `ngMessages`", () => {
121
+ element = $compile('<div ng-messages="val" ng-aria-disable>')(scope);
122
+ scope.$digest();
123
+ expect(element[0].hasAttribute("aria-live")).toBeFalse();
124
+ });
125
+
126
+ // ariaReadonly
127
+ it("should not attach aria-readonly to custom controls", () => {
128
+ element = $compile('<div ng-readonly="val" ng-aria-disable></div>')(
129
+ scope,
130
+ );
131
+ scope.$digest();
132
+
133
+ scope.$apply("val = false");
134
+ expect(element[0].hasAttribute("aria-readonly")).toBeFalse();
135
+
136
+ scope.$apply("val = true");
137
+ expect(element[0].hasAttribute("aria-readonly")).toBeFalse();
138
+ });
139
+
140
+ // ariaRequired
141
+ it("should not attach aria-required to custom controls with `required`", () => {
142
+ element = $compile('<div ng-model="val" required ng-aria-disable></div>')(
143
+ scope,
144
+ );
145
+ scope.$digest();
146
+ expect(element[0].hasAttribute("aria-required")).toBeFalse();
147
+ });
148
+
149
+ it("should not attach aria-required to custom controls with `ngRequired`", () => {
150
+ element = $compile(
151
+ '<div ng-model="val" ng-required="val" ng-aria-disable></div>',
152
+ )(scope);
153
+ scope.$digest();
154
+
155
+ scope.$apply("val = false");
156
+ expect(element[0].hasAttribute("aria-required")).toBeFalse();
157
+
158
+ scope.$apply("val = true");
159
+ expect(element[0].hasAttribute("aria-required")).toBeFalse();
160
+ });
161
+
162
+ // ariaValue
163
+ it("should not attach aria-value* to input[range]", () => {
164
+ element = $compile(
165
+ '<input type="range" ng-model="val" min="0" max="100" ng-aria-disable />',
166
+ )(scope);
167
+ scope.$digest();
168
+
169
+ expect(element[0].hasAttribute("aria-valuemax")).toBeFalse();
170
+ expect(element[0].hasAttribute("aria-valuemin")).toBeFalse();
171
+ expect(element[0].hasAttribute("aria-valuenow")).toBeFalse();
172
+
173
+ scope.$apply("val = 50");
174
+ expect(element[0].hasAttribute("aria-valuemax")).toBeFalse();
175
+ expect(element[0].hasAttribute("aria-valuemin")).toBeFalse();
176
+ expect(element[0].hasAttribute("aria-valuenow")).toBeFalse();
177
+
178
+ scope.$apply("val = 150");
179
+ expect(element[0].hasAttribute("aria-valuemax")).toBeFalse();
180
+ expect(element[0].hasAttribute("aria-valuemin")).toBeFalse();
181
+ expect(element[0].hasAttribute("aria-valuenow")).toBeFalse();
182
+ });
183
+
184
+ it("should not attach aria-value* to custom controls", () => {
185
+ element = $compile(
186
+ '<div role="progressbar" ng-model="val" min="0" max="100" ng-aria-disable></div>' +
187
+ '<div role="slider" ng-model="val" min="0" max="100" ng-aria-disable></div>',
188
+ )(scope);
189
+ scope.$digest();
190
+
191
+ const progressbar = element.eq(0);
192
+ const slider = element.eq(1);
193
+
194
+ ["aria-valuemax", "aria-valuemin", "aria-valuenow"].forEach((attr) => {
195
+ expect(progressbar[0].hasAttribute(attr)).toBeFalse();
196
+ expect(slider[0].hasAttribute(attr)).toBeFalse();
197
+ });
198
+
199
+ scope.$apply("val = 50");
200
+ ["aria-valuemax", "aria-valuemin", "aria-valuenow"].forEach((attr) => {
201
+ expect(progressbar[0].hasAttribute(attr)).toBeFalse();
202
+ expect(slider[0].hasAttribute(attr)).toBeFalse();
203
+ });
204
+
205
+ scope.$apply("val = 150");
206
+ ["aria-valuemax", "aria-valuemin", "aria-valuenow"].forEach((attr) => {
207
+ expect(progressbar[0].hasAttribute(attr)).toBeFalse();
208
+ expect(slider[0].hasAttribute(attr)).toBeFalse();
209
+ });
210
+ });
211
+
212
+ // bindKeypress
213
+ it("should not bind keypress to `ngClick`", () => {
214
+ scope.onClick = jasmine.createSpy("onClick");
215
+ element = $compile(
216
+ '<div ng-click="onClick()" tabindex="0" ng-aria-disable></div>' +
217
+ '<ul><li ng-click="onClick()" tabindex="0" ng-aria-disable></li></ul>',
218
+ )(scope);
219
+ scope.$digest();
220
+
221
+ const div = element.find("div");
222
+ const li = element.find("li");
223
+
224
+ div.triggerHandler({ type: "keypress", keyCode: 32 });
225
+ li.triggerHandler({ type: "keypress", keyCode: 32 });
226
+
227
+ expect(scope.onClick).not.toHaveBeenCalled();
228
+ });
229
+
230
+ // bindRoleForClick
231
+ it("should not attach role to custom controls", () => {
232
+ element = $compile(
233
+ '<div ng-click="onClick()" ng-aria-disable></div>' +
234
+ '<div type="checkbox" ng-model="val" ng-aria-disable></div>' +
235
+ '<div type="radio" ng-model="val" ng-aria-disable></div>' +
236
+ '<div type="range" ng-model="val" ng-aria-disable></div>',
237
+ )(scope);
238
+ scope.$digest();
239
+
240
+ expect(element.eq(0)[0].hasAttribute("role")).toBeFalse();
241
+ expect(element.eq(1)[0].hasAttribute("role")).toBeFalse();
242
+ expect(element.eq(2)[0].hasAttribute("role")).toBeFalse();
243
+ expect(element.eq(3)[0].hasAttribute("role")).toBeFalse();
244
+ });
245
+
246
+ // tabindex
247
+ it("should not attach tabindex to custom controls", () => {
248
+ element = $compile(
249
+ '<div role="checkbox" ng-model="val" ng-aria-disable></div>' +
250
+ '<div role="slider" ng-model="val" ng-aria-disable></div>',
251
+ )(scope);
252
+ scope.$digest();
253
+
254
+ expect(element.eq(0)[0].hasAttribute("tabindex")).toBeFalse();
255
+ expect(element.eq(1)[0].hasAttribute("tabindex")).toBeFalse();
256
+ });
257
+
258
+ it("should not attach tabindex to `ngClick` or `ngDblclick`", () => {
259
+ element = $compile(
260
+ '<div ng-click="onClick()" ng-aria-disable></div>' +
261
+ '<div ng-dblclick="onDblclick()" ng-aria-disable></div>',
262
+ )(scope);
263
+ scope.$digest();
264
+
265
+ expect(element.eq(0)[0].hasAttribute("tabindex")).toBeFalse();
266
+ expect(element.eq(1)[0].hasAttribute("tabindex")).toBeFalse();
267
+ });
268
+ });
269
+
270
+ describe("aria-hidden", () => {
271
+ it("should attach aria-hidden to ng-show", () => {
272
+ element = $compile('<div ng-show="val"></div>')(scope);
273
+ scope.$digest();
274
+ scope.$apply("val = false");
275
+ expect(element.attr("aria-hidden")).toBe("true");
276
+
277
+ scope.$apply("val = true");
278
+ expect(element.attr("aria-hidden")).toBe("false");
279
+ });
280
+
281
+ it("should attach aria-hidden to ng-hide", () => {
282
+ element = $compile('<div ng-hide="val"></div>')(scope);
283
+ scope.$digest();
284
+ scope.$apply("val = false");
285
+ expect(element.attr("aria-hidden")).toBe("false");
286
+
287
+ scope.$apply("val = true");
288
+ expect(element.attr("aria-hidden")).toBe("true");
289
+ });
290
+
291
+ it("should not change aria-hidden if it is already present on ng-show", () => {
292
+ element = $compile(
293
+ '<div ng-show="val" aria-hidden="userSetValue"></div>',
294
+ )(scope);
295
+ scope.$digest();
296
+ expect(element.attr("aria-hidden")).toBe("userSetValue");
297
+
298
+ scope.$apply("val = true");
299
+ expect(element.attr("aria-hidden")).toBe("userSetValue");
300
+ });
301
+
302
+ it("should not change aria-hidden if it is already present on ng-hide", () => {
303
+ element = $compile(
304
+ '<div ng-hide="val" aria-hidden="userSetValue"></div>',
305
+ )(scope);
306
+ scope.$digest();
307
+ expect(element.attr("aria-hidden")).toBe("userSetValue");
308
+
309
+ scope.$apply("val = true");
310
+ expect(element.attr("aria-hidden")).toBe("userSetValue");
311
+ });
312
+
313
+ it("should always set aria-hidden to a boolean value", () => {
314
+ element = $compile('<div ng-hide="val"></div>')(scope);
315
+ scope.$digest();
316
+
317
+ scope.$apply('val = "test angular"');
318
+ expect(element.attr("aria-hidden")).toBe("true");
319
+
320
+ scope.$apply("val = null");
321
+ expect(element.attr("aria-hidden")).toBe("false");
322
+
323
+ scope.$apply("val = {}");
324
+ expect(element.attr("aria-hidden")).toBe("true");
325
+
326
+ element = $compile('<div ng-show="val"></div>')(scope);
327
+ scope.$digest();
328
+
329
+ scope.$apply('val = "test angular"');
330
+ expect(element.attr("aria-hidden")).toBe("false");
331
+
332
+ scope.$apply("val = null");
333
+ expect(element.attr("aria-hidden")).toBe("true");
334
+
335
+ scope.$apply("val = {}");
336
+ expect(element.attr("aria-hidden")).toBe("false");
337
+ });
338
+ });
339
+
340
+ describe("aria-hidden when disabled", () => {
341
+ beforeEach(() => {
342
+ window.angular.module("test", [
343
+ "ngAria",
344
+ ($ariaProvider) => {
345
+ $ariaProvider.config({
346
+ ariaHidden: false,
347
+ });
348
+ },
349
+ ]);
350
+ let injector = createInjector(["test"]);
351
+ scope = injector.get("$rootScope");
352
+ $compile = injector.get("$compile");
353
+ });
354
+
355
+ it("should not attach aria-hidden", () => {
356
+ scope.$apply("val = false");
357
+ element = $compile('<div ng-show="val"></div>')(scope);
358
+ scope.$digest();
359
+ expect(element[0].hasAttribute("aria-hidden")).toBeFalse();
360
+
361
+ element = $compile('<div ng-hide="val"></div>')(scope);
362
+ scope.$digest();
363
+ expect(element[0].hasAttribute("aria-hidden")).toBeFalse();
364
+ });
365
+ });
366
+
367
+ describe("aria-checked", () => {
368
+ it('should not attach itself to native input type="checkbox"', () => {
369
+ element = $compile('<input type="checkbox" ng-model="val">')(scope);
370
+ scope.$digest();
371
+
372
+ scope.$apply("val = true");
373
+ expect(element.attr("aria-checked")).toBeUndefined();
374
+
375
+ scope.$apply("val = false");
376
+ expect(element.attr("aria-checked")).toBeUndefined();
377
+ });
378
+
379
+ it("should attach itself to custom checkbox", () => {
380
+ element = $compile('<div role="checkbox" ng-model="val"></div>')(scope);
381
+ scope.$digest();
382
+
383
+ scope.$apply('val = "checked"');
384
+ expect(element.attr("aria-checked")).toBe("true");
385
+
386
+ scope.$apply("val = null");
387
+ expect(element.attr("aria-checked")).toBe("false");
388
+ });
389
+
390
+ it("should use `$isEmpty()` to determine if the checkbox is checked", () => {
391
+ element = $compile('<div role="checkbox" ng-model="val"></div>')(scope);
392
+ scope.$digest();
393
+ const ctrl = element.controller("ngModel");
394
+ ctrl.$isEmpty = function (value) {
395
+ return value === "not-checked";
396
+ };
397
+
398
+ scope.$apply("val = true");
399
+ expect(ctrl.$modelValue).toBe(true);
400
+ expect(element.attr("aria-checked")).toBe("true");
401
+
402
+ scope.$apply("val = false");
403
+ expect(ctrl.$modelValue).toBe(false);
404
+ expect(element.attr("aria-checked")).toBe("true");
405
+
406
+ scope.$apply('val = "not-checked"');
407
+ expect(ctrl.$modelValue).toBe("not-checked");
408
+ expect(element.attr("aria-checked")).toBe("false");
409
+
410
+ scope.$apply('val = "checked"');
411
+ expect(ctrl.$modelValue).toBe("checked");
412
+ expect(element.attr("aria-checked")).toBe("true");
413
+ });
414
+
415
+ it("should not handle native checkbox with ngChecked", () => {
416
+ const element = $compile('<input type="checkbox" ng-checked="val">')(
417
+ scope,
418
+ );
419
+ scope.$digest();
420
+
421
+ scope.$apply("val = true");
422
+ expect(element.attr("aria-checked")).toBeUndefined();
423
+
424
+ scope.$apply("val = false");
425
+ expect(element.attr("aria-checked")).toBeUndefined();
426
+ });
427
+
428
+ it("should handle custom checkbox with ngChecked", () => {
429
+ const element = $compile('<div role="checkbox" ng-checked="val">')(scope);
430
+
431
+ scope.$apply("val = true");
432
+ expect(element[0].getAttribute("aria-checked")).toBe("true");
433
+
434
+ scope.$apply("val = false");
435
+ expect(element[0].getAttribute("aria-checked")).toBe("false");
436
+ });
437
+
438
+ it('should not attach to native input type="radio"', () => {
439
+ const element = $compile(
440
+ '<input type="radio" ng-model="val" value="one">' +
441
+ '<input type="radio" ng-model="val" value="two">',
442
+ )(scope);
443
+
444
+ scope.$apply("val='one'");
445
+ expect(element.eq(0).attr("aria-checked")).toBeUndefined();
446
+ expect(element.eq(1).attr("aria-checked")).toBeUndefined();
447
+
448
+ scope.$apply("val='two'");
449
+ expect(element.eq(0).attr("aria-checked")).toBeUndefined();
450
+ expect(element.eq(1).attr("aria-checked")).toBeUndefined();
451
+ });
452
+
453
+ it("should attach to custom radio controls", () => {
454
+ const element = $compile(
455
+ '<div role="radio" ng-model="val" value="one"></div>' +
456
+ '<div role="radio" ng-model="val" value="two"></div>',
457
+ )(scope);
458
+
459
+ scope.$apply("val='one'");
460
+ expect(element.eq(0).attr("aria-checked")).toBe("true");
461
+ expect(element.eq(1).attr("aria-checked")).toBe("false");
462
+
463
+ scope.$apply("val='two'");
464
+ expect(element.eq(0).attr("aria-checked")).toBe("false");
465
+ expect(element.eq(1).attr("aria-checked")).toBe("true");
466
+ });
467
+
468
+ it("should handle custom radios with integer model values", () => {
469
+ const element = $compile(
470
+ '<div role="radio" ng-model="val" value="0"></div>' +
471
+ '<div role="radio" ng-model="val" value="1"></div>',
472
+ )(scope);
473
+
474
+ scope.$apply("val=0");
475
+ expect(element.eq(0).attr("aria-checked")).toBe("true");
476
+ expect(element.eq(1).attr("aria-checked")).toBe("false");
477
+
478
+ scope.$apply("val=1");
479
+ expect(element.eq(0).attr("aria-checked")).toBe("false");
480
+ expect(element.eq(1).attr("aria-checked")).toBe("true");
481
+ });
482
+
483
+ it("should handle radios with boolean model values using ngValue", () => {
484
+ const element = $compile(
485
+ '<div role="radio" ng-model="val" ng-value="valExp"></div>' +
486
+ '<div role="radio" ng-model="val" ng-value="valExp2"></div>',
487
+ )(scope);
488
+
489
+ scope.$apply(() => {
490
+ scope.valExp = true;
491
+ scope.valExp2 = false;
492
+ scope.val = true;
493
+ });
494
+ expect(element.eq(0).attr("aria-checked")).toBe("true");
495
+ expect(element.eq(1).attr("aria-checked")).toBe("false");
496
+
497
+ scope.$apply("val = false");
498
+ expect(element.eq(0).attr("aria-checked")).toBe("false");
499
+ expect(element.eq(1).attr("aria-checked")).toBe("true");
500
+ });
501
+
502
+ it('should attach itself to role="menuitemradio"', () => {
503
+ scope.val = "one";
504
+ element = $compile(
505
+ '<div role="menuitemradio" ng-model="val" value="one"></div>',
506
+ )(scope);
507
+ scope.$digest();
508
+ expect(element.attr("aria-checked")).toBe("true");
509
+
510
+ scope.$apply("val = 'two'");
511
+ expect(element.attr("aria-checked")).toBe("false");
512
+ });
513
+
514
+ it('should attach itself to role="menuitemcheckbox"', () => {
515
+ element = $compile('<div role="menuitemcheckbox" ng-model="val"></div>')(
516
+ scope,
517
+ );
518
+ scope.$digest();
519
+
520
+ scope.$apply('val = "checked"');
521
+ expect(element.attr("aria-checked")).toBe("true");
522
+
523
+ scope.$apply("val = null");
524
+ expect(element.attr("aria-checked")).toBe("false");
525
+ });
526
+
527
+ it("should not attach itself if an aria-checked value is already present", () => {
528
+ const element = [
529
+ $compile(
530
+ "<div role='radio' ng-model='val' value='{{val3}}' aria-checked='userSetValue'></div>",
531
+ )(scope),
532
+ $compile(
533
+ "<div role='menuitemradio' ng-model='val' value='{{val3}}' aria-checked='userSetValue'></div>",
534
+ )(scope),
535
+ $compile(
536
+ "<div role='checkbox' checked='checked' aria-checked='userSetValue'></div>",
537
+ )(scope),
538
+ $compile(
539
+ "<div role='menuitemcheckbox' checked='checked' aria-checked='userSetValue'></div>",
540
+ )(scope),
541
+ ];
542
+ scope.$apply("val1=true;val2='one';val3='1'");
543
+ expectAriaAttrOnEachElement(element, "aria-checked", "userSetValue");
544
+ });
545
+ });
546
+
547
+ describe("roles for custom inputs", () => {
548
+ it('should add missing role="button" to custom input', () => {
549
+ element = $compile('<div ng-click="someFunction()"></div>')(scope);
550
+ scope.$digest();
551
+ expect(element.attr("role")).toBe("button");
552
+ });
553
+
554
+ it('should not add role="button" to anchor', () => {
555
+ element = $compile('<a ng-click="someFunction()"></a>')(scope);
556
+ expect(element.attr("role")).not.toBe("button");
557
+ });
558
+
559
+ it('should add missing role="checkbox" to custom input', () => {
560
+ element = $compile('<div type="checkbox" ng-model="val"></div>')(scope);
561
+ scope.$digest();
562
+ expect(element.attr("role")).toBe("checkbox");
563
+ });
564
+
565
+ it("should not add a role to a native checkbox", () => {
566
+ element = $compile('<input type="checkbox" ng-model="val"/>')(scope);
567
+ scope.$digest();
568
+ expect(element.attr("role")).toBeUndefined();
569
+ });
570
+
571
+ it('should add missing role="radio" to custom input', () => {
572
+ element = $compile('<div type="radio" ng-model="val"></div>')(scope);
573
+ scope.$digest();
574
+ expect(element.attr("role")).toBe("radio");
575
+ });
576
+
577
+ it("should not add a role to a native radio button", () => {
578
+ element = $compile('<input type="radio" ng-model="val"/>')(scope);
579
+ scope.$digest();
580
+ expect(element.attr("role")).toBeUndefined();
581
+ });
582
+
583
+ it('should add missing role="slider" to custom input', () => {
584
+ element = $compile('<div type="range" ng-model="val"></div>')(scope);
585
+ scope.$digest();
586
+ expect(element.attr("role")).toBe("slider");
587
+ });
588
+
589
+ it("should not add a role to a native range input", () => {
590
+ element = $compile('<input type="range" ng-model="val"/>')(scope);
591
+ scope.$digest();
592
+ expect(element.attr("role")).toBeUndefined();
593
+ });
594
+
595
+ it("should not add role to native $prop controls", () => {
596
+ [
597
+ '<input type="text" ng-model="val">',
598
+ '<select type="checkbox" ng-model="val"></select>',
599
+ '<textarea type="checkbox" ng-model="val"></textarea>',
600
+ '<button ng-click="doClick()"></button>',
601
+ '<summary ng-click="doClick()"></summary>',
602
+ '<details ng-click="doClick()"></details>',
603
+ '<a ng-click="doClick()"></a>',
604
+ ].forEach((tmpl) => {
605
+ const element = $compile(tmpl)(scope);
606
+ expect(element.attr("role")).toBeUndefined();
607
+ });
608
+ });
609
+ });
610
+
611
+ describe("aria-checked when disabled", () => {
612
+ beforeEach(() => {
613
+ window.angular.module("test", [
614
+ "ngAria",
615
+ ($ariaProvider) => {
616
+ $ariaProvider.config({
617
+ ariaChecked: false,
618
+ });
619
+ },
620
+ ]);
621
+ let injector = createInjector(["test"]);
622
+ scope = injector.get("$rootScope");
623
+ $compile = injector.get("$compile");
624
+ });
625
+
626
+ it("should not attach aria-checked", () => {
627
+ element = $compile(
628
+ "<div role='radio' ng-model='val' value='{{val}}'></div>",
629
+ )(scope);
630
+ scope.$digest();
631
+ expect(element.attr("aria-checked")).toBeUndefined();
632
+
633
+ element = $compile(
634
+ "<div role='menuitemradio' ng-model='val' value='{{val}}'></div>",
635
+ )(scope);
636
+ scope.$digest();
637
+ expect(element.attr("aria-checked")).toBeUndefined();
638
+
639
+ element = $compile("<div role='checkbox' checked='checked'></div>")(
640
+ scope,
641
+ );
642
+ scope.$digest();
643
+ expect(element.attr("aria-checked")).toBeUndefined();
644
+
645
+ element = $compile(
646
+ "<div role='menuitemcheckbox' checked='checked'></div>",
647
+ )(scope);
648
+ scope.$digest();
649
+ expect(element.attr("aria-checked")).toBeUndefined();
650
+ });
651
+ });
652
+
653
+ describe("aria-disabled", () => {
654
+ it("should not attach itself to native $prop controls", () => {
655
+ [
656
+ '<input ng-disabled="val">',
657
+ '<textarea ng-disabled="val"></textarea>',
658
+ '<select ng-disabled="val"></select>',
659
+ '<button ng-disabled="val"></button>',
660
+ ].forEach((tmpl) => {
661
+ const element = $compile(tmpl)(scope);
662
+ scope.$apply("val = true");
663
+
664
+ expect(element.attr("disabled")).toBeDefined();
665
+ expect(element.attr("aria-disabled")).toBeUndefined();
666
+ });
667
+ });
668
+
669
+ it("should attach itself to custom controls", () => {
670
+ element = $compile('<div ng-disabled="val"></div>')(scope);
671
+ scope.$digest();
672
+ expect(element.attr("aria-disabled")).toBe("false");
673
+
674
+ scope.$apply("val = true");
675
+ expect(element.attr("aria-disabled")).toBe("true");
676
+ });
677
+
678
+ it("should not attach itself if an aria-disabled attribute is already present", () => {
679
+ element = $compile(
680
+ '<div ng-disabled="val" aria-disabled="userSetValue"></div>',
681
+ )(scope);
682
+ scope.$digest();
683
+
684
+ expect(element.attr("aria-disabled")).toBe("userSetValue");
685
+ });
686
+
687
+ it("should always set aria-disabled to a boolean value", () => {
688
+ element = $compile('<div ng-disabled="val"></div>')(scope);
689
+
690
+ scope.$apply('val = "test angular"');
691
+ expect(element.attr("aria-disabled")).toBe("true");
692
+
693
+ scope.$apply("val = null");
694
+ expect(element.attr("aria-disabled")).toBe("false");
695
+
696
+ scope.$apply("val = {}");
697
+ expect(element.attr("aria-disabled")).toBe("true");
698
+ });
699
+ });
700
+
701
+ describe("aria-disabled when disabled", () => {
702
+ beforeEach(() => {
703
+ window.angular.module("test", [
704
+ "ngAria",
705
+ ($ariaProvider) => {
706
+ $ariaProvider.config({
707
+ ariaDisabled: false,
708
+ });
709
+ },
710
+ ]);
711
+ let injector = createInjector(["test"]);
712
+ scope = injector.get("$rootScope");
713
+ $compile = injector.get("$compile");
714
+ });
715
+
716
+ it("should not attach aria-disabled", () => {
717
+ element = $compile('<div ng-disabled="val"></div>')(scope);
718
+
719
+ scope.$apply("val = true");
720
+ expect(element.attr("aria-disabled")).toBeUndefined();
721
+ });
722
+ });
723
+
724
+ describe("aria-invalid", () => {
725
+ it("should attach aria-invalid to input", () => {
726
+ element = $compile('<input ng-model="txtInput" ng-minlength="10">')(
727
+ scope,
728
+ );
729
+ scope.$apply("txtInput='LTten'");
730
+ expect(element.attr("aria-invalid")).toBe("true");
731
+
732
+ scope.$apply("txtInput='morethantencharacters'");
733
+ expect(element.attr("aria-invalid")).toBe("false");
734
+ });
735
+
736
+ it("should attach aria-invalid to custom controls", () => {
737
+ element = $compile(
738
+ '<div ng-model="txtInput" role="textbox" ng-minlength="10"></div>',
739
+ )(scope);
740
+ scope.$apply("txtInput='LTten'");
741
+ expect(element.attr("aria-invalid")).toBe("true");
742
+
743
+ scope.$apply("txtInput='morethantencharacters'");
744
+ expect(element.attr("aria-invalid")).toBe("false");
745
+ });
746
+
747
+ it("should not attach itself if aria-invalid is already present", () => {
748
+ element = $compile(
749
+ '<input ng-model="txtInput" ng-minlength="10" aria-invalid="userSetValue">',
750
+ )(scope);
751
+ scope.$apply("txtInput='LTten'");
752
+ expect(element.attr("aria-invalid")).toBe("userSetValue");
753
+ });
754
+
755
+ it('should not attach if input is type="hidden"', () => {
756
+ element = $compile('<input type="hidden" ng-model="txtInput">')(scope);
757
+ expect(element.attr("aria-invalid")).toBeUndefined();
758
+ });
759
+
760
+ it('should attach aria-invalid to custom control that is type="hidden"', () => {
761
+ element = $compile(
762
+ '<div ng-model="txtInput" type="hidden" role="textbox" ng-minlength="10"></div>',
763
+ )(scope);
764
+ scope.$apply("txtInput='LTten'");
765
+ expect(element.attr("aria-invalid")).toBe("true");
766
+
767
+ scope.$apply("txtInput='morethantencharacters'");
768
+ expect(element.attr("aria-invalid")).toBe("false");
769
+ });
770
+ });
771
+
772
+ describe("aria-invalid when disabled", () => {
773
+ beforeEach(() => {
774
+ window.angular.module("test", [
775
+ "ngAria",
776
+ ($ariaProvider) => {
777
+ $ariaProvider.config({
778
+ ariaInvalid: false,
779
+ });
780
+ },
781
+ ]);
782
+ let injector = createInjector(["test"]);
783
+ scope = injector.get("$rootScope");
784
+ $compile = injector.get("$compile");
785
+ });
786
+
787
+ it("should not attach aria-invalid if the option is disabled", () => {
788
+ scope.$apply("txtInput='LTten'");
789
+ element = $compile('<input ng-model="txtInput" ng-minlength="10">')(
790
+ scope,
791
+ );
792
+ expect(element.attr("aria-invalid")).toBeUndefined();
793
+ });
794
+ });
795
+
796
+ describe("aria-readonly", () => {
797
+ it("should not attach itself to native $prop controls", () => {
798
+ [
799
+ '<input ng-readonly="val">',
800
+ '<textarea ng-readonly="val"></textarea>',
801
+ '<select ng-readonly="val"></select>',
802
+ '<button ng-readonly="val"></button>',
803
+ ].forEach((tmpl) => {
804
+ const element = $compile(tmpl)(scope);
805
+ scope.$apply("val = true");
806
+
807
+ expect(element.attr("readonly")).toBeDefined();
808
+ expect(element.attr("aria-readonly")).toBeUndefined();
809
+ });
810
+ });
811
+
812
+ it("should attach itself to custom controls", () => {
813
+ element = $compile('<div ng-readonly="val"></div>')(scope);
814
+ scope.$digest();
815
+ expect(element.attr("aria-readonly")).toBe("false");
816
+
817
+ scope.$apply("val = true");
818
+ expect(element.attr("aria-readonly")).toBe("true");
819
+ });
820
+
821
+ it("should not attach itself if an aria-readonly attribute is already present", () => {
822
+ element = $compile(
823
+ '<div ng-readonly="val" aria-readonly="userSetValue"></div>',
824
+ )(scope);
825
+ scope.$digest();
826
+ expect(element.attr("aria-readonly")).toBe("userSetValue");
827
+ });
828
+
829
+ it("should always set aria-readonly to a boolean value", () => {
830
+ element = $compile('<div ng-readonly="val"></div>')(scope);
831
+
832
+ scope.$apply('val = "test angular"');
833
+ expect(element.attr("aria-readonly")).toBe("true");
834
+
835
+ scope.$apply("val = null");
836
+ expect(element.attr("aria-readonly")).toBe("false");
837
+
838
+ scope.$apply("val = {}");
839
+ expect(element.attr("aria-readonly")).toBe("true");
840
+ });
841
+ });
842
+
843
+ describe("aria-readonly when disabled", () => {
844
+ beforeEach(() => {
845
+ window.angular.module("test", [
846
+ "ngAria",
847
+ ($ariaProvider) => {
848
+ $ariaProvider.config({
849
+ ariaReadonly: false,
850
+ });
851
+ },
852
+ ]);
853
+ let injector = createInjector(["test"]);
854
+ scope = injector.get("$rootScope");
855
+ $compile = injector.get("$compile");
856
+ });
857
+
858
+ it("should not add the aria-readonly attribute", () => {
859
+ element = $compile("<input ng-model='val' readonly>")(scope);
860
+ expect(element.attr("aria-readonly")).toBeUndefined();
861
+
862
+ element = $compile("<div ng-model='val' ng-readonly='true'></div>")(
863
+ scope,
864
+ );
865
+ expect(element.attr("aria-readonly")).toBeUndefined();
866
+ });
867
+ });
868
+
869
+ describe("aria-required", () => {
870
+ it("should not attach to input", () => {
871
+ element = $compile('<input ng-model="val" required>')(scope);
872
+ scope.$digest();
873
+ expect(element.attr("aria-required")).toBeUndefined();
874
+ });
875
+
876
+ it("should attach to custom controls with ngModel and required", () => {
877
+ element = $compile('<div ng-model="val" role="checkbox" required></div>')(
878
+ scope,
879
+ );
880
+ scope.$digest();
881
+ expect(element.attr("aria-required")).toBe("true");
882
+ });
883
+
884
+ it("should set aria-required to false when ng-required is false", () => {
885
+ element = $compile(
886
+ "<div role='checkbox' ng-required='false' ng-model='val'></div>",
887
+ )(scope);
888
+ scope.$digest();
889
+ expect(element.attr("aria-required")).toBe("false");
890
+ });
891
+
892
+ it("should attach to custom controls with ngRequired", () => {
893
+ element = $compile(
894
+ '<div role="checkbox" ng-model="val" ng-required="true"></div>',
895
+ )(scope);
896
+ scope.$digest();
897
+ expect(element.attr("aria-required")).toBe("true");
898
+ });
899
+
900
+ it("should not attach itself if aria-required is already present", () => {
901
+ element = $compile(
902
+ "<div role='checkbox' ng-model='val' ng-required='true' aria-required='userSetValue'></div>",
903
+ )(scope);
904
+ expect(element.attr("aria-required")).toBe("userSetValue");
905
+ });
906
+ });
907
+
908
+ describe("aria-required when disabled", () => {
909
+ beforeEach(() => {
910
+ window.angular.module("test", [
911
+ "ngAria",
912
+ ($ariaProvider) => {
913
+ $ariaProvider.config({
914
+ ariaRequired: false,
915
+ });
916
+ },
917
+ ]);
918
+ let injector = createInjector(["test"]);
919
+ scope = injector.get("$rootScope");
920
+ $compile = injector.get("$compile");
921
+ });
922
+
923
+ it("should not add the aria-required attribute", () => {
924
+ element = $compile("<input ng-model='val' required>")(scope);
925
+ scope.$digest();
926
+ expect(element.attr("aria-required")).toBeUndefined();
927
+
928
+ element = $compile("<div ng-model='val' ng-required='true'></div>")(
929
+ scope,
930
+ );
931
+ scope.$digest();
932
+ expect(element.attr("aria-required")).toBeUndefined();
933
+ });
934
+ });
935
+
936
+ describe("aria-value", () => {
937
+ it('should attach to input type="range"', () => {
938
+ const element = [
939
+ $compile('<input type="range" ng-model="val" min="0" max="100">')(
940
+ scope,
941
+ ),
942
+ $compile('<div role="progressbar" min="0" max="100" ng-model="val">')(
943
+ scope,
944
+ ),
945
+ $compile('<div role="slider" min="0" max="100" ng-model="val">')(scope),
946
+ ];
947
+
948
+ scope.$apply("val = 50");
949
+ expectAriaAttrOnEachElement(element, "aria-valuenow", "50");
950
+ expectAriaAttrOnEachElement(element, "aria-valuemin", "0");
951
+ expectAriaAttrOnEachElement(element, "aria-valuemax", "100");
952
+
953
+ scope.$apply("val = 90");
954
+ expectAriaAttrOnEachElement(element, "aria-valuenow", "90");
955
+ });
956
+
957
+ it("should not attach if aria-value* is already present", () => {
958
+ const element = [
959
+ $compile(
960
+ '<input type="range" ng-model="val" min="0" max="100" aria-valuenow="userSetValue1" aria-valuemin="userSetValue2" aria-valuemax="userSetValue3">',
961
+ )(scope),
962
+ $compile(
963
+ '<div role="progressbar" min="0" max="100" ng-model="val" aria-valuenow="userSetValue1" aria-valuemin="userSetValue2" aria-valuemax="userSetValue3">',
964
+ )(scope),
965
+ $compile(
966
+ '<div role="slider" min="0" max="100" ng-model="val" aria-valuenow="userSetValue1" aria-valuemin="userSetValue2" aria-valuemax="userSetValue3">',
967
+ )(scope),
968
+ ];
969
+
970
+ scope.$apply("val = 50");
971
+ expectAriaAttrOnEachElement(element, "aria-valuenow", "userSetValue1");
972
+ expectAriaAttrOnEachElement(element, "aria-valuemin", "userSetValue2");
973
+ expectAriaAttrOnEachElement(element, "aria-valuemax", "userSetValue3");
974
+ });
975
+
976
+ it("should update `aria-valuemin/max` when `min/max` changes dynamically", () => {
977
+ scope.$apply("min = 25; max = 75");
978
+ element = $compile(
979
+ '<input type="range" ng-model="val" min="{{min}}" max="{{max}}" />',
980
+ )(scope);
981
+ scope.$digest();
982
+ expect(element.attr("aria-valuemin")).toBe("25");
983
+ expect(element.attr("aria-valuemax")).toBe("75");
984
+
985
+ scope.$apply("min = 0");
986
+ expect(element.attr("aria-valuemin")).toBe("0");
987
+
988
+ scope.$apply("max = 100");
989
+ expect(element.attr("aria-valuemax")).toBe("100");
990
+ });
991
+
992
+ it("should update `aria-valuemin/max` when `ng-min/ng-max` changes dynamically", () => {
993
+ scope.$apply("min = 25; max = 75");
994
+ element = $compile(
995
+ '<input type="range" ng-model="val" ng-min="min" ng-max="max" />',
996
+ )(scope);
997
+ scope.$digest();
998
+ expect(element.attr("aria-valuemin")).toBe("25");
999
+ expect(element.attr("aria-valuemax")).toBe("75");
1000
+
1001
+ scope.$apply("min = 0");
1002
+ expect(element.attr("aria-valuemin")).toBe("0");
1003
+
1004
+ scope.$apply("max = 100");
1005
+ expect(element.attr("aria-valuemax")).toBe("100");
1006
+ });
1007
+ });
1008
+
1009
+ describe("announcing ngMessages", () => {
1010
+ it("should attach aria-live", () => {
1011
+ const element = [
1012
+ $compile('<div ng-messages="myForm.myName.$error">')(scope),
1013
+ ];
1014
+ scope.$digest();
1015
+ expectAriaAttrOnEachElement(element, "aria-live", "assertive");
1016
+ });
1017
+ });
1018
+
1019
+ describe("aria-value when disabled", () => {
1020
+ beforeEach(() => {
1021
+ window.angular.module("test", [
1022
+ "ngAria",
1023
+ ($ariaProvider) => {
1024
+ $ariaProvider.config({
1025
+ ariaValue: false,
1026
+ });
1027
+ },
1028
+ ]);
1029
+ let injector = createInjector(["test"]);
1030
+ scope = injector.get("$rootScope");
1031
+ $compile = injector.get("$compile");
1032
+ });
1033
+
1034
+ it("should not attach itself", () => {
1035
+ scope.$apply("val = 50");
1036
+
1037
+ element = $compile(
1038
+ '<input type="range" ng-model="val" min="0" max="100">',
1039
+ )(scope);
1040
+ scope.$digest();
1041
+ expect(element.attr("aria-valuenow")).toBeUndefined();
1042
+ expect(element.attr("aria-valuemin")).toBeUndefined();
1043
+ expect(element.attr("aria-valuemax")).toBeUndefined();
1044
+
1045
+ element = $compile(
1046
+ '<div role="progressbar" min="0" max="100" ng-model="val">',
1047
+ )(scope);
1048
+ scope.$digest();
1049
+ expect(element.attr("aria-valuenow")).toBeUndefined();
1050
+ expect(element.attr("aria-valuemin")).toBeUndefined();
1051
+ expect(element.attr("aria-valuemax")).toBeUndefined();
1052
+ });
1053
+ });
1054
+
1055
+ describe("tabindex", () => {
1056
+ it("should not attach to native control $prop", () => {
1057
+ [
1058
+ "<button ng-click='something'></button>",
1059
+ "<a ng-href='#/something'>",
1060
+ "<input type='text' ng-model='val'>",
1061
+ "<input type='radio' ng-model='val'>",
1062
+ "<input type='checkbox' ng-model='val'>",
1063
+ "<textarea ng-model='val'></textarea>",
1064
+ "<select ng-model='val'></select>",
1065
+ "<details ng-model='val'></details>",
1066
+ ].forEach((html) => {
1067
+ element = $compile(html)(scope);
1068
+ scope.$digest();
1069
+ expect(element.attr("tabindex")).toBeUndefined();
1070
+ });
1071
+ });
1072
+
1073
+ it("should not attach to random ng-model elements", () => {
1074
+ element = $compile('<div ng-model="val"></div>')(scope);
1075
+ scope.$digest();
1076
+ expect(element.attr("tabindex")).toBeUndefined();
1077
+ });
1078
+
1079
+ it("should attach tabindex to custom inputs", () => {
1080
+ element = $compile('<div role="checkbox" ng-model="val"></div>')(scope);
1081
+ scope.$digest();
1082
+ expect(element.attr("tabindex")).toBe("0");
1083
+
1084
+ element = $compile('<div role="slider" ng-model="val"></div>')(scope);
1085
+ scope.$digest();
1086
+ expect(element.attr("tabindex")).toBe("0");
1087
+ });
1088
+
1089
+ it("should attach to ng-click and ng-dblclick", () => {
1090
+ element = $compile('<div ng-click="someAction()"></div>')(scope);
1091
+ scope.$digest();
1092
+ expect(element.attr("tabindex")).toBe("0");
1093
+
1094
+ element = $compile('<div ng-dblclick="someAction()"></div>')(scope);
1095
+ scope.$digest();
1096
+ expect(element.attr("tabindex")).toBe("0");
1097
+ });
1098
+
1099
+ it("should not attach tabindex if it is already on an element", () => {
1100
+ element = $compile('<div role="button" tabindex="userSetValue"></div>')(
1101
+ scope,
1102
+ );
1103
+ scope.$digest();
1104
+ expect(element.attr("tabindex")).toBe("userSetValue");
1105
+
1106
+ element = $compile('<div role="checkbox" tabindex="userSetValue"></div>')(
1107
+ scope,
1108
+ );
1109
+ scope.$digest();
1110
+ expect(element.attr("tabindex")).toBe("userSetValue");
1111
+
1112
+ element = $compile(
1113
+ '<div ng-click="someAction()" tabindex="userSetValue"></div>',
1114
+ )(scope);
1115
+ scope.$digest();
1116
+ expect(element.attr("tabindex")).toBe("userSetValue");
1117
+
1118
+ element = $compile(
1119
+ '<div ng-dblclick="someAction()" tabindex="userSetValue"></div>',
1120
+ )(scope);
1121
+ scope.$digest();
1122
+ expect(element.attr("tabindex")).toBe("userSetValue");
1123
+ });
1124
+ });
1125
+
1126
+ describe("actions when bindRoleForClick is set to false", () => {
1127
+ beforeEach(() => {
1128
+ window.angular.module("test", [
1129
+ "ngAria",
1130
+ ($ariaProvider) => {
1131
+ $ariaProvider.config({
1132
+ bindRoleForClick: false,
1133
+ });
1134
+ },
1135
+ ]);
1136
+ let injector = createInjector(["test"]);
1137
+ scope = injector.get("$rootScope");
1138
+ $compile = injector.get("$compile");
1139
+ });
1140
+
1141
+ it("should not add a button role", () => {
1142
+ element = $compile('<radio-group ng-click="something"></radio-group>')(
1143
+ scope,
1144
+ );
1145
+ expect(element.attr("role")).toBeUndefined();
1146
+ });
1147
+ });
1148
+
1149
+ describe("actions when bindKeydown is set to false", () => {
1150
+ beforeEach(() => {
1151
+ window.angular.module("test", [
1152
+ "ngAria",
1153
+ ($ariaProvider) => {
1154
+ $ariaProvider.config({
1155
+ bindKeydown: false,
1156
+ });
1157
+ },
1158
+ ]);
1159
+ let injector = createInjector(["test"]);
1160
+ scope = injector.get("$rootScope");
1161
+ $compile = injector.get("$compile");
1162
+ });
1163
+
1164
+ it("should not trigger click", () => {
1165
+ scope.someAction = jasmine.createSpy("someAction");
1166
+
1167
+ element = $compile('<div ng-click="someAction()" tabindex="0"></div>')(
1168
+ scope,
1169
+ );
1170
+
1171
+ element.triggerHandler({ type: "keydown", keyCode: 13 });
1172
+ element.triggerHandler({ type: "keydown", keyCode: 32 });
1173
+ element.triggerHandler({ type: "keypress", keyCode: 13 });
1174
+ element.triggerHandler({ type: "keypress", keyCode: 32 });
1175
+ element.triggerHandler({ type: "keyup", keyCode: 13 });
1176
+ element.triggerHandler({ type: "keyup", keyCode: 32 });
1177
+
1178
+ expect(scope.someAction).not.toHaveBeenCalled();
1179
+
1180
+ element.triggerHandler({ type: "click", keyCode: 32 });
1181
+
1182
+ expect(scope.someAction).toHaveBeenCalled();
1183
+ });
1184
+ });
1185
+
1186
+ describe("tabindex when disabled", () => {
1187
+ beforeEach(() => {
1188
+ window.angular.module("test", [
1189
+ "ngAria",
1190
+ ($ariaProvider) => {
1191
+ $ariaProvider.config({
1192
+ tabindex: false,
1193
+ });
1194
+ },
1195
+ ]);
1196
+ let injector = createInjector(["test"]);
1197
+ scope = injector.get("$rootScope");
1198
+ $compile = injector.get("$compile");
1199
+ });
1200
+
1201
+ it("should not add a tabindex attribute", () => {
1202
+ element = $compile('<div role="button"></div>')(scope);
1203
+ expect(element.attr("tabindex")).toBeUndefined();
1204
+
1205
+ element = $compile('<div role="checkbox"></div>')(scope);
1206
+ expect(element.attr("tabindex")).toBeUndefined();
1207
+
1208
+ element = $compile('<div ng-click="someAction()"></div>')(scope);
1209
+ expect(element.attr("tabindex")).toBeUndefined();
1210
+
1211
+ element = $compile('<div ng-dblclick="someAction()"></div>')(scope);
1212
+ expect(element.attr("tabindex")).toBeUndefined();
1213
+ });
1214
+ });
1215
+
1216
+ describe("ngModel", () => {
1217
+ it("should not break when manually compiling", () => {
1218
+ window.angular.module("test", [
1219
+ "ngAria",
1220
+ ($compileProvider) => {
1221
+ $compileProvider.directive("foo", () => ({
1222
+ priority: 10,
1223
+ terminal: true,
1224
+ link(scope, elem) {
1225
+ $compile(elem, null, 10)(scope);
1226
+ },
1227
+ }));
1228
+ },
1229
+ ]);
1230
+ let injector = createInjector(["test"]);
1231
+ scope = injector.get("$rootScope");
1232
+ $compile = injector.get("$compile");
1233
+ element = $compile('<div role="checkbox" ng-model="value" foo />')(scope);
1234
+
1235
+ // Just check an arbitrary feature to make sure it worked
1236
+ expect(element.attr("tabindex")).toBe("0");
1237
+ });
1238
+ });
1239
+
1240
+ function expectAriaAttrOnEachElement(elem, ariaAttr, expected) {
1241
+ elem.forEach((val) => {
1242
+ expect(val[0].getAttribute(ariaAttr)).toBe(expected);
1243
+ });
1244
+ }
1245
+ });