@afixt/test-utils 1.1.1 → 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 (89) 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 +2 -2
  5. package/src/getAccessibleName.js +8 -4
  6. package/src/getAriaAttributesByElement.js +2 -2
  7. package/src/getFocusableElements.js +14 -5
  8. package/src/stringUtils.js +1 -1
  9. package/test/domUtils.test.js +117 -0
  10. package/test/getAccessibleName.test.js +182 -0
  11. package/test/getAccessibleText.test.js +350 -79
  12. package/test/getCSSGeneratedContent.test.js +175 -1
  13. package/test/getFocusableElements.test.js +106 -35
  14. package/test/getImageText.test.js +61 -12
  15. package/test/hasParent.test.js +116 -0
  16. package/test/index.test.js +165 -0
  17. package/test/interactiveRoles.test.js +60 -0
  18. package/test/isAriaAttributesValid.test.js +36 -0
  19. package/test/isDataTable.test.js +492 -0
  20. package/test/isValidUrl.test.js +31 -19
  21. package/test/stringUtils.test.js +235 -1
  22. package/test/testContrast.test.js +176 -8
  23. package/test/testOrder.integration.test.js +369 -0
  24. package/test/testOrder.test.js +756 -21
  25. package/todo.md +149 -1
  26. package/coverage/base.css +0 -224
  27. package/coverage/block-navigation.js +0 -87
  28. package/coverage/coverage-final.json +0 -51
  29. package/coverage/favicon.png +0 -0
  30. package/coverage/index.html +0 -161
  31. package/coverage/prettify.css +0 -1
  32. package/coverage/prettify.js +0 -2
  33. package/coverage/sort-arrow-sprite.png +0 -0
  34. package/coverage/sorter.js +0 -196
  35. package/coverage/test-utils/docs/scripts/core.js.html +0 -2263
  36. package/coverage/test-utils/docs/scripts/core.min.js.html +0 -151
  37. package/coverage/test-utils/docs/scripts/index.html +0 -176
  38. package/coverage/test-utils/docs/scripts/resize.js.html +0 -355
  39. package/coverage/test-utils/docs/scripts/search.js.html +0 -880
  40. package/coverage/test-utils/docs/scripts/search.min.js.html +0 -100
  41. package/coverage/test-utils/docs/scripts/third-party/fuse.js.html +0 -109
  42. package/coverage/test-utils/docs/scripts/third-party/hljs-line-num-original.js.html +0 -1192
  43. package/coverage/test-utils/docs/scripts/third-party/hljs-line-num.js.html +0 -85
  44. package/coverage/test-utils/docs/scripts/third-party/hljs-original.js.html +0 -15598
  45. package/coverage/test-utils/docs/scripts/third-party/hljs.js.html +0 -85
  46. package/coverage/test-utils/docs/scripts/third-party/index.html +0 -236
  47. package/coverage/test-utils/docs/scripts/third-party/popper.js.html +0 -100
  48. package/coverage/test-utils/docs/scripts/third-party/tippy.js.html +0 -88
  49. package/coverage/test-utils/docs/scripts/third-party/tocbot.js.html +0 -2098
  50. package/coverage/test-utils/docs/scripts/third-party/tocbot.min.js.html +0 -85
  51. package/coverage/test-utils/index.html +0 -131
  52. package/coverage/test-utils/src/arrayUtils.js.html +0 -283
  53. package/coverage/test-utils/src/domUtils.js.html +0 -622
  54. package/coverage/test-utils/src/getAccessibleName.js.html +0 -1444
  55. package/coverage/test-utils/src/getAccessibleText.js.html +0 -271
  56. package/coverage/test-utils/src/getAriaAttributesByElement.js.html +0 -142
  57. package/coverage/test-utils/src/getCSSGeneratedContent.js.html +0 -265
  58. package/coverage/test-utils/src/getComputedRole.js.html +0 -592
  59. package/coverage/test-utils/src/getFocusableElements.js.html +0 -163
  60. package/coverage/test-utils/src/getGeneratedContent.js.html +0 -130
  61. package/coverage/test-utils/src/getImageText.js.html +0 -160
  62. package/coverage/test-utils/src/getStyleObject.js.html +0 -220
  63. package/coverage/test-utils/src/hasAccessibleName.js.html +0 -166
  64. package/coverage/test-utils/src/hasAttribute.js.html +0 -130
  65. package/coverage/test-utils/src/hasCSSGeneratedContent.js.html +0 -145
  66. package/coverage/test-utils/src/hasHiddenParent.js.html +0 -172
  67. package/coverage/test-utils/src/hasParent.js.html +0 -247
  68. package/coverage/test-utils/src/hasValidAriaAttributes.js.html +0 -175
  69. package/coverage/test-utils/src/hasValidAriaRole.js.html +0 -172
  70. package/coverage/test-utils/src/index.html +0 -611
  71. package/coverage/test-utils/src/index.js.html +0 -274
  72. package/coverage/test-utils/src/interactiveRoles.js.html +0 -145
  73. package/coverage/test-utils/src/isAriaAttributesValid.js.html +0 -304
  74. package/coverage/test-utils/src/isComplexTable.js.html +0 -412
  75. package/coverage/test-utils/src/isDataTable.js.html +0 -799
  76. package/coverage/test-utils/src/isFocusable.js.html +0 -187
  77. package/coverage/test-utils/src/isHidden.js.html +0 -136
  78. package/coverage/test-utils/src/isOffScreen.js.html +0 -133
  79. package/coverage/test-utils/src/isValidUrl.js.html +0 -124
  80. package/coverage/test-utils/src/isVisible.js.html +0 -271
  81. package/coverage/test-utils/src/listEventListeners.js.html +0 -370
  82. package/coverage/test-utils/src/queryCache.js.html +0 -1156
  83. package/coverage/test-utils/src/stringUtils.js.html +0 -535
  84. package/coverage/test-utils/src/testContrast.js.html +0 -784
  85. package/coverage/test-utils/src/testLang.js.html +0 -1810
  86. package/coverage/test-utils/src/testOrder.js.html +0 -355
  87. package/coverage/test-utils/vitest.config.browser.js.html +0 -133
  88. package/coverage/test-utils/vitest.config.js.html +0 -157
  89. package/repairs-needed.md +0 -84
@@ -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
+ });