@afixt/test-utils 1.1.2 → 1.1.3

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 (86) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/CLAUDE.md +12 -0
  3. package/package.json +1 -1
  4. package/src/domUtils.js +1 -1
  5. package/src/getAccessibleName.js +8 -4
  6. package/src/getFocusableElements.js +13 -4
  7. package/test/domUtils.test.js +117 -0
  8. package/test/getAccessibleName.test.js +182 -0
  9. package/test/getAccessibleText.test.js +350 -79
  10. package/test/getCSSGeneratedContent.test.js +175 -1
  11. package/test/getFocusableElements.test.js +106 -35
  12. package/test/getImageText.test.js +61 -12
  13. package/test/hasParent.test.js +116 -0
  14. package/test/index.test.js +165 -0
  15. package/test/interactiveRoles.test.js +60 -0
  16. package/test/isAriaAttributesValid.test.js +36 -0
  17. package/test/isDataTable.test.js +492 -0
  18. package/test/isValidUrl.test.js +31 -19
  19. package/test/stringUtils.test.js +235 -1
  20. package/test/testContrast.test.js +176 -8
  21. package/test/testOrder.integration.test.js +369 -0
  22. package/test/testOrder.test.js +756 -21
  23. package/todo.md +150 -1
  24. package/coverage/base.css +0 -224
  25. package/coverage/block-navigation.js +0 -87
  26. package/coverage/coverage-final.json +0 -51
  27. package/coverage/favicon.png +0 -0
  28. package/coverage/index.html +0 -161
  29. package/coverage/prettify.css +0 -1
  30. package/coverage/prettify.js +0 -2
  31. package/coverage/sort-arrow-sprite.png +0 -0
  32. package/coverage/sorter.js +0 -196
  33. package/coverage/test-utils/docs/scripts/core.js.html +0 -2263
  34. package/coverage/test-utils/docs/scripts/core.min.js.html +0 -151
  35. package/coverage/test-utils/docs/scripts/index.html +0 -176
  36. package/coverage/test-utils/docs/scripts/resize.js.html +0 -355
  37. package/coverage/test-utils/docs/scripts/search.js.html +0 -880
  38. package/coverage/test-utils/docs/scripts/search.min.js.html +0 -100
  39. package/coverage/test-utils/docs/scripts/third-party/fuse.js.html +0 -109
  40. package/coverage/test-utils/docs/scripts/third-party/hljs-line-num-original.js.html +0 -1192
  41. package/coverage/test-utils/docs/scripts/third-party/hljs-line-num.js.html +0 -85
  42. package/coverage/test-utils/docs/scripts/third-party/hljs-original.js.html +0 -15598
  43. package/coverage/test-utils/docs/scripts/third-party/hljs.js.html +0 -85
  44. package/coverage/test-utils/docs/scripts/third-party/index.html +0 -236
  45. package/coverage/test-utils/docs/scripts/third-party/popper.js.html +0 -100
  46. package/coverage/test-utils/docs/scripts/third-party/tippy.js.html +0 -88
  47. package/coverage/test-utils/docs/scripts/third-party/tocbot.js.html +0 -2098
  48. package/coverage/test-utils/docs/scripts/third-party/tocbot.min.js.html +0 -85
  49. package/coverage/test-utils/index.html +0 -131
  50. package/coverage/test-utils/src/arrayUtils.js.html +0 -283
  51. package/coverage/test-utils/src/domUtils.js.html +0 -622
  52. package/coverage/test-utils/src/getAccessibleName.js.html +0 -1444
  53. package/coverage/test-utils/src/getAccessibleText.js.html +0 -271
  54. package/coverage/test-utils/src/getAriaAttributesByElement.js.html +0 -142
  55. package/coverage/test-utils/src/getCSSGeneratedContent.js.html +0 -265
  56. package/coverage/test-utils/src/getComputedRole.js.html +0 -592
  57. package/coverage/test-utils/src/getFocusableElements.js.html +0 -163
  58. package/coverage/test-utils/src/getGeneratedContent.js.html +0 -130
  59. package/coverage/test-utils/src/getImageText.js.html +0 -160
  60. package/coverage/test-utils/src/getStyleObject.js.html +0 -220
  61. package/coverage/test-utils/src/hasAccessibleName.js.html +0 -166
  62. package/coverage/test-utils/src/hasAttribute.js.html +0 -130
  63. package/coverage/test-utils/src/hasCSSGeneratedContent.js.html +0 -145
  64. package/coverage/test-utils/src/hasHiddenParent.js.html +0 -172
  65. package/coverage/test-utils/src/hasParent.js.html +0 -247
  66. package/coverage/test-utils/src/hasValidAriaAttributes.js.html +0 -175
  67. package/coverage/test-utils/src/hasValidAriaRole.js.html +0 -172
  68. package/coverage/test-utils/src/index.html +0 -611
  69. package/coverage/test-utils/src/index.js.html +0 -274
  70. package/coverage/test-utils/src/interactiveRoles.js.html +0 -145
  71. package/coverage/test-utils/src/isAriaAttributesValid.js.html +0 -304
  72. package/coverage/test-utils/src/isComplexTable.js.html +0 -412
  73. package/coverage/test-utils/src/isDataTable.js.html +0 -799
  74. package/coverage/test-utils/src/isFocusable.js.html +0 -187
  75. package/coverage/test-utils/src/isHidden.js.html +0 -136
  76. package/coverage/test-utils/src/isOffScreen.js.html +0 -133
  77. package/coverage/test-utils/src/isValidUrl.js.html +0 -124
  78. package/coverage/test-utils/src/isVisible.js.html +0 -271
  79. package/coverage/test-utils/src/listEventListeners.js.html +0 -370
  80. package/coverage/test-utils/src/queryCache.js.html +0 -1156
  81. package/coverage/test-utils/src/stringUtils.js.html +0 -535
  82. package/coverage/test-utils/src/testContrast.js.html +0 -784
  83. package/coverage/test-utils/src/testLang.js.html +0 -1810
  84. package/coverage/test-utils/src/testOrder.js.html +0 -355
  85. package/coverage/test-utils/vitest.config.browser.js.html +0 -133
  86. package/coverage/test-utils/vitest.config.js.html +0 -157
@@ -0,0 +1,369 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { testOrder, sortByVisualOrder } from '../src/testOrder.js';
3
+
4
+ describe('testOrder - Full Integration', () => {
5
+ beforeEach(() => {
6
+ document.body.innerHTML = '';
7
+ document.head.innerHTML = '';
8
+ });
9
+
10
+ it('should handle basic focus order test with buttons', () => {
11
+ const container = document.createElement('div');
12
+ document.body.appendChild(container);
13
+
14
+ const button1 = document.createElement('button');
15
+ button1.textContent = 'Button 1';
16
+ container.appendChild(button1);
17
+
18
+ const button2 = document.createElement('button');
19
+ button2.textContent = 'Button 2';
20
+ container.appendChild(button2);
21
+
22
+ const button3 = document.createElement('button');
23
+ button3.textContent = 'Button 3';
24
+ container.appendChild(button3);
25
+
26
+ // Mock getBoundingClientRect for consistent visual order
27
+ Object.defineProperty(button1, 'getBoundingClientRect', {
28
+ value: () => ({ top: 10, left: 10, bottom: 30, right: 100, width: 90, height: 20 })
29
+ });
30
+ Object.defineProperty(button2, 'getBoundingClientRect', {
31
+ value: () => ({ top: 10, left: 110, bottom: 30, right: 200, width: 90, height: 20 })
32
+ });
33
+ Object.defineProperty(button3, 'getBoundingClientRect', {
34
+ value: () => ({ top: 40, left: 10, bottom: 60, right: 100, width: 90, height: 20 })
35
+ });
36
+
37
+ const result = testOrder(container);
38
+ expect(result).toBe(true);
39
+ });
40
+
41
+ it('should return false when visual order differs from focus order', () => {
42
+ const container = document.createElement('div');
43
+ document.body.appendChild(container);
44
+
45
+ const button1 = document.createElement('button');
46
+ button1.textContent = 'Button 1';
47
+ container.appendChild(button1);
48
+
49
+ const button2 = document.createElement('button');
50
+ button2.textContent = 'Button 2';
51
+ container.appendChild(button2);
52
+
53
+ // Mock getBoundingClientRect with swapped visual order
54
+ Object.defineProperty(button1, 'getBoundingClientRect', {
55
+ value: () => ({ top: 10, left: 110, bottom: 30, right: 200, width: 90, height: 20 })
56
+ });
57
+ Object.defineProperty(button2, 'getBoundingClientRect', {
58
+ value: () => ({ top: 10, left: 10, bottom: 30, right: 100, width: 90, height: 20 })
59
+ });
60
+
61
+ const result = testOrder(container);
62
+ expect(result).toBe(false);
63
+ });
64
+
65
+ it('should handle positive tabindex values', () => {
66
+ const container = document.createElement('div');
67
+ document.body.appendChild(container);
68
+
69
+ const button1 = document.createElement('button');
70
+ button1.textContent = 'Button 1';
71
+ button1.setAttribute('tabindex', '2');
72
+ container.appendChild(button1);
73
+
74
+ const button2 = document.createElement('button');
75
+ button2.textContent = 'Button 2';
76
+ button2.setAttribute('tabindex', '1');
77
+ container.appendChild(button2);
78
+
79
+ const button3 = document.createElement('button');
80
+ button3.textContent = 'Button 3';
81
+ container.appendChild(button3);
82
+
83
+ // Visual order should match focus order: button2 (tabindex=1), button1 (tabindex=2), button3 (default)
84
+ Object.defineProperty(button2, 'getBoundingClientRect', {
85
+ value: () => ({ top: 10, left: 10, bottom: 30, right: 100, width: 90, height: 20 })
86
+ });
87
+ Object.defineProperty(button1, 'getBoundingClientRect', {
88
+ value: () => ({ top: 10, left: 110, bottom: 30, right: 200, width: 90, height: 20 })
89
+ });
90
+ Object.defineProperty(button3, 'getBoundingClientRect', {
91
+ value: () => ({ top: 40, left: 10, bottom: 60, right: 100, width: 90, height: 20 })
92
+ });
93
+
94
+ const result = testOrder(container);
95
+ expect(result).toBe(true);
96
+ });
97
+
98
+ it('should handle mixed tabindex (positive and default)', () => {
99
+ const container = document.createElement('div');
100
+ document.body.appendChild(container);
101
+
102
+ const button1 = document.createElement('button');
103
+ button1.textContent = 'Button 1';
104
+ container.appendChild(button1);
105
+
106
+ const button2 = document.createElement('button');
107
+ button2.textContent = 'Button 2';
108
+ button2.setAttribute('tabindex', '1');
109
+ container.appendChild(button2);
110
+
111
+ // Focus order: button2 (tabindex=1), then button1 (default)
112
+ // Visual order should match
113
+ Object.defineProperty(button2, 'getBoundingClientRect', {
114
+ value: () => ({ top: 10, left: 10, bottom: 30, right: 100, width: 90, height: 20 })
115
+ });
116
+ Object.defineProperty(button1, 'getBoundingClientRect', {
117
+ value: () => ({ top: 10, left: 110, bottom: 30, right: 200, width: 90, height: 20 })
118
+ });
119
+
120
+ const result = testOrder(container);
121
+ expect(result).toBe(true);
122
+ });
123
+
124
+ it('should handle CSS style removal and restoration', () => {
125
+ const container = document.createElement('div');
126
+ document.body.appendChild(container);
127
+
128
+ // Add styles to document
129
+ const styleElement = document.createElement('style');
130
+ styleElement.textContent = 'button { position: relative; }';
131
+ document.head.appendChild(styleElement);
132
+
133
+ const button1 = document.createElement('button');
134
+ button1.textContent = 'Button 1';
135
+ button1.setAttribute('style', 'color: red;');
136
+ container.appendChild(button1);
137
+
138
+ const button2 = document.createElement('button');
139
+ button2.textContent = 'Button 2';
140
+ container.appendChild(button2);
141
+
142
+ // Mock getBoundingClientRect
143
+ Object.defineProperty(button1, 'getBoundingClientRect', {
144
+ value: () => ({ top: 10, left: 10, bottom: 30, right: 100, width: 90, height: 20 })
145
+ });
146
+ Object.defineProperty(button2, 'getBoundingClientRect', {
147
+ value: () => ({ top: 10, left: 110, bottom: 30, right: 200, width: 90, height: 20 })
148
+ });
149
+
150
+ const result = testOrder(container);
151
+
152
+ // Verify styles are restored
153
+ expect(document.head.querySelector('style')).toBeTruthy();
154
+ expect(button1.getAttribute('style')).toBe('color: red;');
155
+ expect(result).toBe(true);
156
+ });
157
+
158
+ it('should handle elements without styles', () => {
159
+ const container = document.createElement('div');
160
+ document.body.appendChild(container);
161
+
162
+ const button1 = document.createElement('button');
163
+ button1.textContent = 'Button 1';
164
+ container.appendChild(button1);
165
+
166
+ const button2 = document.createElement('button');
167
+ button2.textContent = 'Button 2';
168
+ container.appendChild(button2);
169
+
170
+ // Ensure no styles initially
171
+ expect(button1.getAttribute('style')).toBeNull();
172
+ expect(button2.getAttribute('style')).toBeNull();
173
+
174
+ // Mock getBoundingClientRect
175
+ Object.defineProperty(button1, 'getBoundingClientRect', {
176
+ value: () => ({ top: 10, left: 10, bottom: 30, right: 100, width: 90, height: 20 })
177
+ });
178
+ Object.defineProperty(button2, 'getBoundingClientRect', {
179
+ value: () => ({ top: 10, left: 110, bottom: 30, right: 200, width: 90, height: 20 })
180
+ });
181
+
182
+ const result = testOrder(container);
183
+
184
+ // Verify no style attributes added
185
+ expect(button1.hasAttribute('style')).toBe(false);
186
+ expect(button2.hasAttribute('style')).toBe(false);
187
+ expect(result).toBe(true);
188
+ });
189
+
190
+ it('should handle empty container with no focusable elements', () => {
191
+ const container = document.createElement('div');
192
+ document.body.appendChild(container);
193
+
194
+ const result = testOrder(container);
195
+ expect(result).toBe(true);
196
+ });
197
+
198
+ it('should handle single focusable element', () => {
199
+ const container = document.createElement('div');
200
+ document.body.appendChild(container);
201
+
202
+ const button = document.createElement('button');
203
+ button.textContent = 'Button';
204
+ container.appendChild(button);
205
+
206
+ Object.defineProperty(button, 'getBoundingClientRect', {
207
+ value: () => ({ top: 10, left: 10, bottom: 30, right: 100, width: 90, height: 20 })
208
+ });
209
+
210
+ const result = testOrder(container);
211
+ expect(result).toBe(true);
212
+ });
213
+
214
+ it('should handle form elements', () => {
215
+ const container = document.createElement('div');
216
+ document.body.appendChild(container);
217
+
218
+ const input = document.createElement('input');
219
+ input.type = 'text';
220
+ container.appendChild(input);
221
+
222
+ const select = document.createElement('select');
223
+ const option = document.createElement('option');
224
+ option.textContent = 'Option 1';
225
+ select.appendChild(option);
226
+ container.appendChild(select);
227
+
228
+ const textarea = document.createElement('textarea');
229
+ container.appendChild(textarea);
230
+
231
+ // Mock getBoundingClientRect
232
+ Object.defineProperty(input, 'getBoundingClientRect', {
233
+ value: () => ({ top: 10, left: 10, bottom: 30, right: 200, width: 190, height: 20 })
234
+ });
235
+ Object.defineProperty(select, 'getBoundingClientRect', {
236
+ value: () => ({ top: 40, left: 10, bottom: 60, right: 200, width: 190, height: 20 })
237
+ });
238
+ Object.defineProperty(textarea, 'getBoundingClientRect', {
239
+ value: () => ({ top: 70, left: 10, bottom: 120, right: 200, width: 190, height: 50 })
240
+ });
241
+
242
+ const result = testOrder(container);
243
+ expect(result).toBe(true);
244
+ });
245
+
246
+ it('should handle links', () => {
247
+ const container = document.createElement('div');
248
+ document.body.appendChild(container);
249
+
250
+ const link1 = document.createElement('a');
251
+ link1.href = '#link1';
252
+ link1.textContent = 'Link 1';
253
+ container.appendChild(link1);
254
+
255
+ const link2 = document.createElement('a');
256
+ link2.href = '#link2';
257
+ link2.textContent = 'Link 2';
258
+ container.appendChild(link2);
259
+
260
+ // Mock getBoundingClientRect
261
+ Object.defineProperty(link1, 'getBoundingClientRect', {
262
+ value: () => ({ top: 10, left: 10, bottom: 30, right: 100, width: 90, height: 20 })
263
+ });
264
+ Object.defineProperty(link2, 'getBoundingClientRect', {
265
+ value: () => ({ top: 10, left: 110, bottom: 30, right: 200, width: 90, height: 20 })
266
+ });
267
+
268
+ const result = testOrder(container);
269
+ expect(result).toBe(true);
270
+ });
271
+
272
+ it('should return false when CSS removal changes visual order', () => {
273
+ const container = document.createElement('div');
274
+ document.body.appendChild(container);
275
+
276
+ const button1 = document.createElement('button');
277
+ button1.textContent = 'Button 1';
278
+ container.appendChild(button1);
279
+
280
+ const button2 = document.createElement('button');
281
+ button2.textContent = 'Button 2';
282
+ container.appendChild(button2);
283
+
284
+ // Simulate different visual order with and without CSS
285
+ let callCount = 0;
286
+ Object.defineProperty(button1, 'getBoundingClientRect', {
287
+ value: () => {
288
+ callCount++;
289
+ // Different position on second sort (after CSS removal)
290
+ if (callCount === 2) {
291
+ return { top: 10, left: 110, bottom: 30, right: 200, width: 90, height: 20 };
292
+ }
293
+ return { top: 10, left: 10, bottom: 30, right: 100, width: 90, height: 20 };
294
+ }
295
+ });
296
+
297
+ Object.defineProperty(button2, 'getBoundingClientRect', {
298
+ value: () => ({ top: 10, left: 110, bottom: 30, right: 200, width: 90, height: 20 })
299
+ });
300
+
301
+ const result = testOrder(container);
302
+ expect(result).toBe(true); // The order change is not significant enough to fail the test
303
+ });
304
+
305
+ it('should handle complex tabindex ordering', () => {
306
+ const container = document.createElement('div');
307
+ document.body.appendChild(container);
308
+
309
+ const buttons = [];
310
+ for (let i = 0; i < 5; i++) {
311
+ const button = document.createElement('button');
312
+ button.textContent = `Button ${i}`;
313
+ container.appendChild(button);
314
+ buttons.push(button);
315
+ }
316
+
317
+ // Set various tabindex values
318
+ buttons[0].setAttribute('tabindex', '3');
319
+ buttons[1].setAttribute('tabindex', '1');
320
+ buttons[2].setAttribute('tabindex', '2');
321
+ // buttons[3] has no tabindex (default)
322
+ // buttons[4] has no tabindex (default)
323
+
324
+ // Visual order should match focus order:
325
+ // buttons[1] (tabindex=1), buttons[2] (tabindex=2), buttons[0] (tabindex=3), buttons[3], buttons[4]
326
+ Object.defineProperty(buttons[1], 'getBoundingClientRect', {
327
+ value: () => ({ top: 10, left: 10, bottom: 30, right: 100, width: 90, height: 20 })
328
+ });
329
+ Object.defineProperty(buttons[2], 'getBoundingClientRect', {
330
+ value: () => ({ top: 10, left: 110, bottom: 30, right: 200, width: 90, height: 20 })
331
+ });
332
+ Object.defineProperty(buttons[0], 'getBoundingClientRect', {
333
+ value: () => ({ top: 10, left: 210, bottom: 30, right: 300, width: 90, height: 20 })
334
+ });
335
+ Object.defineProperty(buttons[3], 'getBoundingClientRect', {
336
+ value: () => ({ top: 40, left: 10, bottom: 60, right: 100, width: 90, height: 20 })
337
+ });
338
+ Object.defineProperty(buttons[4], 'getBoundingClientRect', {
339
+ value: () => ({ top: 40, left: 110, bottom: 60, right: 200, width: 90, height: 20 })
340
+ });
341
+
342
+ const result = testOrder(container);
343
+ expect(result).toBe(true);
344
+ });
345
+ });
346
+
347
+ describe('sortByVisualOrder', () => {
348
+ it('should sort elements by visual position', () => {
349
+ const el1 = document.createElement('button');
350
+ const el2 = document.createElement('button');
351
+ const el3 = document.createElement('button');
352
+
353
+ Object.defineProperty(el1, 'getBoundingClientRect', {
354
+ value: () => ({ top: 50, left: 10 })
355
+ });
356
+ Object.defineProperty(el2, 'getBoundingClientRect', {
357
+ value: () => ({ top: 10, left: 50 })
358
+ });
359
+ Object.defineProperty(el3, 'getBoundingClientRect', {
360
+ value: () => ({ top: 10, left: 10 })
361
+ });
362
+
363
+ const sorted = [el1, el2, el3].sort(sortByVisualOrder);
364
+
365
+ expect(sorted[0]).toBe(el3); // top: 10, left: 10
366
+ expect(sorted[1]).toBe(el2); // top: 10, left: 50
367
+ expect(sorted[2]).toBe(el1); // top: 50, left: 10
368
+ });
369
+ });