@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
@@ -1,52 +1,787 @@
1
- import { describe, it, expect, beforeEach, vi } from 'vitest';
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
2
  import { testOrder, sortByVisualOrder } from '../src/testOrder.js';
3
3
 
4
- // Note: Mocking CommonJS modules from ES6 test files in Vitest has compatibility issues
5
- // The actual implementation works correctly, but these tests are skipped due to mock limitations
4
+ // Use manual mock without vi.mock for simplicity
5
+ const mockedGetFocusableElements = vi.fn(() => []);
6
6
 
7
- describe('testOrder', () => {
7
+ // Manually replace the import
8
+ vi.doMock('../src/getFocusableElements.js', () => ({
9
+ getFocusableElements: mockedGetFocusableElements
10
+ }));
11
+
12
+ describe('sortByVisualOrder', () => {
8
13
  beforeEach(() => {
9
- document.body.innerHTML = '';
10
- document.head.innerHTML = '';
11
14
  vi.clearAllMocks();
12
15
  });
13
16
 
14
- it('should sort elements by visual order (top to bottom, left to right)', () => {
15
- // Arrange: Create elements with different positions
17
+ it('should sort elements by top position first', () => {
18
+ // Arrange: Create elements with different top positions
16
19
  const el1 = document.createElement('button');
17
20
  Object.defineProperty(el1, 'getBoundingClientRect', {
18
- value: () => ({ top: 10, left: 10 })
21
+ value: () => ({ top: 100, left: 10 })
19
22
  });
20
23
 
21
24
  const el2 = document.createElement('button');
22
25
  Object.defineProperty(el2, 'getBoundingClientRect', {
26
+ value: () => ({ top: 50, left: 10 })
27
+ });
28
+
29
+ const el3 = document.createElement('button');
30
+ Object.defineProperty(el3, 'getBoundingClientRect', {
31
+ value: () => ({ top: 10, left: 10 })
32
+ });
33
+
34
+ // Act: Sort the elements
35
+ const result = [el1, el2, el3].sort(sortByVisualOrder);
36
+
37
+ // Assert: Elements should be sorted by top position (ascending)
38
+ expect(result[0]).toBe(el3); // top: 10
39
+ expect(result[1]).toBe(el2); // top: 50
40
+ expect(result[2]).toBe(el1); // top: 100
41
+ });
42
+
43
+ it('should sort elements by left position when top positions are equal', () => {
44
+ // Arrange: Create elements with same top but different left positions
45
+ const el1 = document.createElement('button');
46
+ Object.defineProperty(el1, 'getBoundingClientRect', {
23
47
  value: () => ({ top: 10, left: 100 })
24
48
  });
25
49
 
50
+ const el2 = document.createElement('button');
51
+ Object.defineProperty(el2, 'getBoundingClientRect', {
52
+ value: () => ({ top: 10, left: 50 })
53
+ });
54
+
26
55
  const el3 = document.createElement('button');
27
56
  Object.defineProperty(el3, 'getBoundingClientRect', {
28
- value: () => ({ top: 100, left: 10 })
57
+ value: () => ({ top: 10, left: 10 })
29
58
  });
30
59
 
31
60
  // Act: Sort the elements
32
61
  const result = [el1, el2, el3].sort(sortByVisualOrder);
33
62
 
34
- // Assert: The elements should be sorted by visual order
35
- expect(result[0]).toBe(el1); // Top left
36
- expect(result[1]).toBe(el2); // Top right
37
- expect(result[2]).toBe(el3); // Bottom
63
+ // Assert: Elements should be sorted by left position (ascending)
64
+ expect(result[0]).toBe(el3); // left: 10
65
+ expect(result[1]).toBe(el2); // left: 50
66
+ expect(result[2]).toBe(el1); // left: 100
67
+ });
68
+
69
+ it('should handle mixed top and left positioning correctly', () => {
70
+ // Arrange: Create a realistic grid layout
71
+ const el1 = document.createElement('button');
72
+ Object.defineProperty(el1, 'getBoundingClientRect', {
73
+ value: () => ({ top: 10, left: 100 }) // top row, right
74
+ });
75
+
76
+ const el2 = document.createElement('button');
77
+ Object.defineProperty(el2, 'getBoundingClientRect', {
78
+ value: () => ({ top: 10, left: 10 }) // top row, left
79
+ });
80
+
81
+ const el3 = document.createElement('button');
82
+ Object.defineProperty(el3, 'getBoundingClientRect', {
83
+ value: () => ({ top: 50, left: 10 }) // bottom row, left
84
+ });
85
+
86
+ const el4 = document.createElement('button');
87
+ Object.defineProperty(el4, 'getBoundingClientRect', {
88
+ value: () => ({ top: 50, left: 100 }) // bottom row, right
89
+ });
90
+
91
+ // Act: Sort the elements
92
+ const result = [el1, el2, el3, el4].sort(sortByVisualOrder);
93
+
94
+ // Assert: Should be sorted by visual reading order (top-left to bottom-right)
95
+ expect(result[0]).toBe(el2); // top row, left
96
+ expect(result[1]).toBe(el1); // top row, right
97
+ expect(result[2]).toBe(el3); // bottom row, left
98
+ expect(result[3]).toBe(el4); // bottom row, right
99
+ });
100
+
101
+ it('should handle elements with negative positions', () => {
102
+ // Arrange: Create elements with negative positions
103
+ const el1 = document.createElement('button');
104
+ Object.defineProperty(el1, 'getBoundingClientRect', {
105
+ value: () => ({ top: -10, left: 10 })
106
+ });
107
+
108
+ const el2 = document.createElement('button');
109
+ Object.defineProperty(el2, 'getBoundingClientRect', {
110
+ value: () => ({ top: 10, left: -10 })
111
+ });
112
+
113
+ const el3 = document.createElement('button');
114
+ Object.defineProperty(el3, 'getBoundingClientRect', {
115
+ value: () => ({ top: 0, left: 0 })
116
+ });
117
+
118
+ // Act: Sort the elements
119
+ const result = [el1, el2, el3].sort(sortByVisualOrder);
120
+
121
+ // Assert: Should handle negative positions correctly
122
+ expect(result[0]).toBe(el1); // top: -10
123
+ expect(result[1]).toBe(el3); // top: 0
124
+ expect(result[2]).toBe(el2); // top: 10
125
+ });
126
+ });
127
+
128
+ describe('testOrder', () => {
129
+ beforeEach(() => {
130
+ document.body.innerHTML = '';
131
+ document.head.innerHTML = '';
132
+ vi.clearAllMocks();
133
+ });
134
+
135
+ it('should return true when focus order matches visual order', () => {
136
+ // Arrange: Create elements in proper visual order
137
+ const container = document.createElement('div');
138
+ const button1 = document.createElement('button');
139
+ const button2 = document.createElement('button');
140
+ const button3 = document.createElement('button');
141
+
142
+ // Mock getBoundingClientRect for visual ordering
143
+ Object.defineProperty(button1, 'getBoundingClientRect', {
144
+ value: () => ({ top: 10, left: 10 })
145
+ });
146
+ Object.defineProperty(button2, 'getBoundingClientRect', {
147
+ value: () => ({ top: 10, left: 100 })
148
+ });
149
+ Object.defineProperty(button3, 'getBoundingClientRect', {
150
+ value: () => ({ top: 100, left: 10 })
151
+ });
152
+
153
+ // Mock getFocusableElements to return elements in DOM order (which matches visual order)
154
+ mockedGetFocusableElements.mockReturnValue([button1, button2, button3]);
155
+
156
+ // Act
157
+ const result = testOrder(container);
158
+
159
+ // Assert
160
+ expect(result).toBe(true);
161
+ // Note: We can't easily test the mock call due to CommonJS/ES6 mocking complexity
162
+ // Instead we verify the function behavior through the result
163
+ });
164
+
165
+
166
+ it('should handle elements with positive tabindex correctly', () => {
167
+ // Arrange: Create elements with different tabindex values
168
+ const container = document.createElement('div');
169
+ const button1 = document.createElement('button');
170
+ const button2 = document.createElement('button');
171
+ const button3 = document.createElement('button');
172
+
173
+ button1.setAttribute('tabindex', '3');
174
+ button2.setAttribute('tabindex', '1');
175
+ button3.setAttribute('tabindex', '2');
176
+
177
+ // Mock getBoundingClientRect to match expected tabindex order
178
+ Object.defineProperty(button2, 'getBoundingClientRect', {
179
+ value: () => ({ top: 10, left: 10 }) // tabindex 1, should be first
180
+ });
181
+ Object.defineProperty(button3, 'getBoundingClientRect', {
182
+ value: () => ({ top: 10, left: 100 }) // tabindex 2, should be second
183
+ });
184
+ Object.defineProperty(button1, 'getBoundingClientRect', {
185
+ value: () => ({ top: 100, left: 10 }) // tabindex 3, should be third
186
+ });
187
+
188
+ // Mock getFocusableElements to return elements in DOM order
189
+ mockedGetFocusableElements.mockReturnValue([button1, button2, button3]);
190
+
191
+ // Act
192
+ const result = testOrder(container);
193
+
194
+ // Assert
195
+ expect(result).toBe(true);
196
+ });
197
+
198
+ it('should handle elements with mixed tabindex values (positive and default)', () => {
199
+ // Arrange: Create elements with mixed tabindex
200
+ const container = document.createElement('div');
201
+ const button1 = document.createElement('button'); // no tabindex (default 0)
202
+ const button2 = document.createElement('button');
203
+ const button3 = document.createElement('button'); // no tabindex (default 0)
204
+
205
+ button2.setAttribute('tabindex', '1'); // positive tabindex should come first
206
+
207
+ // Mock getBoundingClientRect - positive tabindex should come first in visual order
208
+ Object.defineProperty(button2, 'getBoundingClientRect', {
209
+ value: () => ({ top: 10, left: 10 }) // tabindex 1, should be first
210
+ });
211
+ Object.defineProperty(button1, 'getBoundingClientRect', {
212
+ value: () => ({ top: 10, left: 100 }) // default tabindex, should be second
213
+ });
214
+ Object.defineProperty(button3, 'getBoundingClientRect', {
215
+ value: () => ({ top: 100, left: 10 }) // default tabindex, should be third
216
+ });
217
+
218
+ // Mock getFocusableElements to return elements in DOM order
219
+ mockedGetFocusableElements.mockReturnValue([button1, button2, button3]);
220
+
221
+ // Act
222
+ const result = testOrder(container);
223
+
224
+ // Assert
225
+ expect(result).toBe(true);
226
+ });
227
+
228
+ it('should handle elements with zero tabindex correctly', () => {
229
+ // Arrange: Create elements with explicit tabindex="0"
230
+ const container = document.createElement('div');
231
+ const button1 = document.createElement('button');
232
+ const button2 = document.createElement('button');
233
+
234
+ button1.setAttribute('tabindex', '0');
235
+ button2.setAttribute('tabindex', '0');
236
+
237
+ // Mock getBoundingClientRect for visual ordering
238
+ Object.defineProperty(button1, 'getBoundingClientRect', {
239
+ value: () => ({ top: 10, left: 10 })
240
+ });
241
+ Object.defineProperty(button2, 'getBoundingClientRect', {
242
+ value: () => ({ top: 10, left: 100 })
243
+ });
244
+
245
+ // Mock getFocusableElements to return elements in DOM order
246
+ mockedGetFocusableElements.mockReturnValue([button1, button2]);
247
+
248
+ // Act
249
+ const result = testOrder(container);
250
+
251
+ // Assert
252
+ expect(result).toBe(true);
253
+ });
254
+
255
+ it('should prioritize positive tabindex over default tabindex', () => {
256
+ // Arrange: Create mixed scenario where positive tabindex should come first
257
+ const container = document.createElement('div');
258
+ const button1 = document.createElement('button'); // default tabindex
259
+ const button2 = document.createElement('button');
260
+
261
+ button2.setAttribute('tabindex', '1'); // positive tabindex
262
+
263
+ // Mock getBoundingClientRect where element with positive tabindex is visually first
264
+ // AND focus order matches visual order (positive tabindex first, then default)
265
+ Object.defineProperty(button2, 'getBoundingClientRect', {
266
+ value: () => ({ top: 10, left: 10 }) // visually first AND focus first (tabindex=1)
267
+ });
268
+ Object.defineProperty(button1, 'getBoundingClientRect', {
269
+ value: () => ({ top: 10, left: 100 }) // visually second AND focus second (default)
270
+ });
271
+
272
+ // Mock getFocusableElements
273
+ mockedGetFocusableElements.mockReturnValue([button1, button2]);
274
+
275
+ // Act
276
+ const result = testOrder(container);
277
+
278
+ // Assert: Should return true because visual order matches focus order
279
+ // Focus order: [button2 (tabindex=1), button1 (default)]
280
+ // Visual order: [button2, button1] (same)
281
+ expect(result).toBe(true);
282
+ });
283
+
284
+ it('should handle CSS style removal and restoration', () => {
285
+ // Arrange: Setup document with CSS styles
286
+ const container = document.createElement('div');
287
+ const button1 = document.createElement('button');
288
+ const button2 = document.createElement('button');
289
+
290
+ // Add CSS styles to document
291
+ const styleElement = document.createElement('style');
292
+ styleElement.textContent = 'button { position: absolute; }';
293
+ document.head.appendChild(styleElement);
294
+
295
+ const linkElement = document.createElement('link');
296
+ linkElement.rel = 'stylesheet';
297
+ linkElement.href = 'data:text/css,button%20%7B%20position%3A%20absolute%3B%20%7D';
298
+ document.head.appendChild(linkElement);
299
+
300
+ // Add inline styles
301
+ button1.setAttribute('style', 'top: 20px; left: 20px;');
302
+ button2.setAttribute('style', 'top: 10px; left: 10px;');
303
+
304
+ // Mock getBoundingClientRect to be consistent regardless of styles
305
+ Object.defineProperty(button1, 'getBoundingClientRect', {
306
+ value: () => ({ top: 10, left: 10 })
307
+ });
308
+ Object.defineProperty(button2, 'getBoundingClientRect', {
309
+ value: () => ({ top: 10, left: 100 })
310
+ });
311
+
312
+ // Mock getFocusableElements
313
+ mockedGetFocusableElements.mockReturnValue([button1, button2]);
314
+
315
+ // Act
316
+ const result = testOrder(container);
317
+
318
+ // Assert: CSS should be restored after test
319
+ expect(document.head.querySelector('style')).toBeTruthy();
320
+ expect(document.head.querySelector('link[rel="stylesheet"]')).toBeTruthy();
321
+ expect(button1.getAttribute('style')).toBe('top: 20px; left: 20px;');
322
+ expect(button2.getAttribute('style')).toBe('top: 10px; left: 10px;');
323
+ expect(result).toBe(true);
324
+ });
325
+
326
+ it('should handle elements without inline styles during CSS restoration', () => {
327
+ // Arrange: Create elements without inline styles
328
+ const container = document.createElement('div');
329
+ const button1 = document.createElement('button');
330
+ const button2 = document.createElement('button');
331
+
332
+ // Ensure no inline styles initially
333
+ expect(button1.getAttribute('style')).toBeNull();
334
+ expect(button2.getAttribute('style')).toBeNull();
335
+
336
+ // Mock getBoundingClientRect
337
+ Object.defineProperty(button1, 'getBoundingClientRect', {
338
+ value: () => ({ top: 10, left: 10 })
339
+ });
340
+ Object.defineProperty(button2, 'getBoundingClientRect', {
341
+ value: () => ({ top: 10, left: 100 })
342
+ });
343
+
344
+ // Mock getFocusableElements
345
+ mockedGetFocusableElements.mockReturnValue([button1, button2]);
346
+
347
+ // Act
348
+ const result = testOrder(container);
349
+
350
+ // Assert: Elements should not have style attribute after restoration
351
+ expect(button1.hasAttribute('style')).toBe(false);
352
+ expect(button2.hasAttribute('style')).toBe(false);
353
+ expect(result).toBe(true);
354
+ });
355
+
356
+ it('should return true when no focusable elements exist', () => {
357
+ // Arrange: Empty container
358
+ const container = document.createElement('div');
359
+
360
+ // Mock getFocusableElements to return empty array
361
+ mockedGetFocusableElements.mockReturnValue([]);
362
+
363
+ // Act
364
+ const result = testOrder(container);
365
+
366
+ // Assert
367
+ expect(result).toBe(true);
368
+ });
369
+
370
+ it('should return true for single focusable element', () => {
371
+ // Arrange: Container with single element
372
+ const container = document.createElement('div');
373
+ const button = document.createElement('button');
374
+
375
+ Object.defineProperty(button, 'getBoundingClientRect', {
376
+ value: () => ({ top: 10, left: 10 })
377
+ });
378
+
379
+ // Mock getFocusableElements
380
+ mockedGetFocusableElements.mockReturnValue([button]);
381
+
382
+ // Act
383
+ const result = testOrder(container);
384
+
385
+ // Assert
386
+ expect(result).toBe(true);
387
+ });
388
+
389
+ it('should handle complex tabindex ordering with multiple positive values', () => {
390
+ // Arrange: Create elements with various tabindex values
391
+ const container = document.createElement('div');
392
+ const button1 = document.createElement('button');
393
+ const button2 = document.createElement('button');
394
+ const button3 = document.createElement('button');
395
+ const button4 = document.createElement('button');
396
+
397
+ button1.setAttribute('tabindex', '5');
398
+ button2.setAttribute('tabindex', '2');
399
+ button3.setAttribute('tabindex', '10');
400
+ // button4 has no tabindex (default 0)
401
+
402
+ // Mock getBoundingClientRect to match expected tabindex order
403
+ Object.defineProperty(button2, 'getBoundingClientRect', {
404
+ value: () => ({ top: 10, left: 10 }) // tabindex 2, should be first
405
+ });
406
+ Object.defineProperty(button1, 'getBoundingClientRect', {
407
+ value: () => ({ top: 10, left: 100 }) // tabindex 5, should be second
408
+ });
409
+ Object.defineProperty(button3, 'getBoundingClientRect', {
410
+ value: () => ({ top: 20, left: 10 }) // tabindex 10, should be third
411
+ });
412
+ Object.defineProperty(button4, 'getBoundingClientRect', {
413
+ value: () => ({ top: 20, left: 100 }) // default, should be last
414
+ });
415
+
416
+ // Mock getFocusableElements
417
+ mockedGetFocusableElements.mockReturnValue([button1, button2, button3, button4]);
418
+
419
+ // Act
420
+ const result = testOrder(container);
421
+
422
+ // Assert
423
+ expect(result).toBe(true);
424
+ });
425
+
426
+ it('should handle elements with empty tabindex attributes', () => {
427
+ // Arrange: Create elements with empty tabindex
428
+ const container = document.createElement('div');
429
+ const button1 = document.createElement('button');
430
+ const button2 = document.createElement('button');
431
+
432
+ button1.setAttribute('tabindex', ''); // empty tabindex
433
+ button2.setAttribute('tabindex', '1'); // valid positive
434
+
435
+ // Mock getBoundingClientRect - positive tabindex should come first visually
436
+ Object.defineProperty(button2, 'getBoundingClientRect', {
437
+ value: () => ({ top: 10, left: 10 }) // tabindex 1, should be first
438
+ });
439
+ Object.defineProperty(button1, 'getBoundingClientRect', {
440
+ value: () => ({ top: 10, left: 100 }) // empty tabindex, should be last
441
+ });
442
+
443
+ // Mock getFocusableElements
444
+ mockedGetFocusableElements.mockReturnValue([button1, button2]);
445
+
446
+ // Act
447
+ const result = testOrder(container);
448
+
449
+ // Assert
450
+ expect(result).toBe(true);
451
+ });
452
+
453
+ it('should handle elements with negative tabindex', () => {
454
+ // Arrange: Create elements with negative tabindex (should be treated as 0)
455
+ const container = document.createElement('div');
456
+ const button1 = document.createElement('button');
457
+ const button2 = document.createElement('button');
458
+
459
+ button1.setAttribute('tabindex', '-1'); // negative tabindex
460
+ button2.setAttribute('tabindex', '1'); // positive tabindex
461
+
462
+ // Mock getBoundingClientRect - positive tabindex should come first visually
463
+ Object.defineProperty(button2, 'getBoundingClientRect', {
464
+ value: () => ({ top: 10, left: 10 }) // tabindex 1, should be first
465
+ });
466
+ Object.defineProperty(button1, 'getBoundingClientRect', {
467
+ value: () => ({ top: 10, left: 100 }) // tabindex -1, should be last
468
+ });
469
+
470
+ // Mock getFocusableElements
471
+ mockedGetFocusableElements.mockReturnValue([button1, button2]);
472
+
473
+ // Act
474
+ const result = testOrder(container);
475
+
476
+ // Assert
477
+ expect(result).toBe(true);
478
+ });
479
+
480
+ it('should handle elements with non-numeric tabindex values', () => {
481
+ // Arrange: Create elements with invalid tabindex values
482
+ const container = document.createElement('div');
483
+ const button1 = document.createElement('button');
484
+ const button2 = document.createElement('button');
485
+
486
+ button1.setAttribute('tabindex', 'invalid'); // non-numeric
487
+ button2.setAttribute('tabindex', '1'); // valid positive
488
+
489
+ // Mock getBoundingClientRect
490
+ Object.defineProperty(button2, 'getBoundingClientRect', {
491
+ value: () => ({ top: 10, left: 10 }) // valid tabindex first
492
+ });
493
+ Object.defineProperty(button1, 'getBoundingClientRect', {
494
+ value: () => ({ top: 10, left: 100 }) // invalid tabindex treated as 0
495
+ });
496
+
497
+ // Mock getFocusableElements
498
+ mockedGetFocusableElements.mockReturnValue([button1, button2]);
499
+
500
+ // Act
501
+ const result = testOrder(container);
502
+
503
+ // Assert
504
+ expect(result).toBe(true);
505
+ });
506
+
507
+ it('should handle elements where some have null style attributes', () => {
508
+ // Arrange: Create elements with mixed style situations
509
+ const container = document.createElement('div');
510
+ const button1 = document.createElement('button');
511
+ const button2 = document.createElement('button');
512
+
513
+ // One element has inline style, other doesn't
514
+ button1.setAttribute('style', 'color: red;');
515
+ // button2 has no style attribute
516
+
517
+ // Mock getBoundingClientRect
518
+ Object.defineProperty(button1, 'getBoundingClientRect', {
519
+ value: () => ({ top: 10, left: 10 })
520
+ });
521
+ Object.defineProperty(button2, 'getBoundingClientRect', {
522
+ value: () => ({ top: 10, left: 100 })
523
+ });
524
+
525
+ // Mock getFocusableElements
526
+ mockedGetFocusableElements.mockReturnValue([button1, button2]);
527
+
528
+ // Act
529
+ const result = testOrder(container);
530
+
531
+ // Assert: Should handle mixed style attributes correctly
532
+ expect(result).toBe(true);
533
+ // Verify original styles are preserved
534
+ expect(button1.getAttribute('style')).toBe('color: red;');
535
+ expect(button2.getAttribute('style')).toBeNull();
536
+ });
537
+
538
+ });
539
+
540
+ describe('testOrder - Integration Tests', () => {
541
+ beforeEach(() => {
542
+ // Clear all mocks to use real getFocusableElements
543
+ vi.clearAllMocks();
544
+ vi.resetModules();
545
+ document.body.innerHTML = '';
546
+ document.head.innerHTML = '';
547
+ });
548
+
549
+ it('should test with real getFocusableElements - visual order matches focus order', async () => {
550
+ // Restore real implementation
551
+ vi.unmock('../src/getFocusableElements.js');
552
+ const { testOrder } = await import('../src/testOrder.js');
553
+
554
+ // Create focusable elements in proper order
555
+ const container = document.createElement('div');
556
+ document.body.appendChild(container);
557
+
558
+ const button1 = document.createElement('button');
559
+ button1.textContent = 'Button 1';
560
+ container.appendChild(button1);
561
+
562
+ const button2 = document.createElement('button');
563
+ button2.textContent = 'Button 2';
564
+ container.appendChild(button2);
565
+
566
+ const button3 = document.createElement('button');
567
+ button3.textContent = 'Button 3';
568
+ container.appendChild(button3);
569
+
570
+ // Mock getBoundingClientRect to simulate proper visual order
571
+ Object.defineProperty(button1, 'getBoundingClientRect', {
572
+ value: () => ({ top: 10, left: 10 })
573
+ });
574
+ Object.defineProperty(button2, 'getBoundingClientRect', {
575
+ value: () => ({ top: 10, left: 100 })
576
+ });
577
+ Object.defineProperty(button3, 'getBoundingClientRect', {
578
+ value: () => ({ top: 100, left: 10 })
579
+ });
580
+
581
+ // Act
582
+ const result = testOrder(container);
583
+
584
+ // Assert
585
+ expect(result).toBe(true);
38
586
  });
39
587
 
40
- // Skip tests that require mocking due to CommonJS/ES6 module incompatibility in test environment
41
- it.skip('should return true when focus order matches visual order', () => {
42
- // Test implementation requires mocking getFocusableElements which has issues with CommonJS modules
588
+ it('should return false when focus order does not match visual order', async () => {
589
+ // Restore real implementation
590
+ vi.unmock('../src/getFocusableElements.js');
591
+ const { testOrder } = await import('../src/testOrder.js');
592
+
593
+ const container = document.createElement('div');
594
+ document.body.appendChild(container);
595
+
596
+ const button1 = document.createElement('button');
597
+ button1.textContent = 'Button 1';
598
+ container.appendChild(button1);
599
+
600
+ const button2 = document.createElement('button');
601
+ button2.textContent = 'Button 2';
602
+ container.appendChild(button2);
603
+
604
+ const button3 = document.createElement('button');
605
+ button3.textContent = 'Button 3';
606
+ container.appendChild(button3);
607
+
608
+ // Mock getBoundingClientRect to simulate mismatched visual order
609
+ // Visual order will be button3, button1, button2 (not matching DOM order)
610
+ Object.defineProperty(button1, 'getBoundingClientRect', {
611
+ value: () => ({ top: 50, left: 10 })
612
+ });
613
+ Object.defineProperty(button2, 'getBoundingClientRect', {
614
+ value: () => ({ top: 100, left: 10 })
615
+ });
616
+ Object.defineProperty(button3, 'getBoundingClientRect', {
617
+ value: () => ({ top: 10, left: 10 })
618
+ });
619
+
620
+ // Act
621
+ const result = testOrder(container);
622
+
623
+ // Assert
624
+ expect(result).toBe(false);
43
625
  });
44
626
 
45
- it.skip('should return false when focus order does not match visual order', () => {
46
- // Test implementation requires mocking getFocusableElements which has issues with CommonJS modules
627
+ it('should handle positive tabindex with real getFocusableElements', async () => {
628
+ // Restore real implementation
629
+ vi.unmock('../src/getFocusableElements.js');
630
+ const { testOrder } = await import('../src/testOrder.js');
631
+
632
+ const container = document.createElement('div');
633
+ document.body.appendChild(container);
634
+
635
+ const button1 = document.createElement('button');
636
+ button1.textContent = 'Button 1';
637
+ button1.setAttribute('tabindex', '2');
638
+ container.appendChild(button1);
639
+
640
+ const button2 = document.createElement('button');
641
+ button2.textContent = 'Button 2';
642
+ button2.setAttribute('tabindex', '1');
643
+ container.appendChild(button2);
644
+
645
+ const button3 = document.createElement('button');
646
+ button3.textContent = 'Button 3';
647
+ // no tabindex (default)
648
+ container.appendChild(button3);
649
+
650
+ // Mock getBoundingClientRect to match expected focus order
651
+ // Focus order: button2 (tabindex 1), button1 (tabindex 2), button3 (default)
652
+ Object.defineProperty(button2, 'getBoundingClientRect', {
653
+ value: () => ({ top: 10, left: 10 })
654
+ });
655
+ Object.defineProperty(button1, 'getBoundingClientRect', {
656
+ value: () => ({ top: 10, left: 100 })
657
+ });
658
+ Object.defineProperty(button3, 'getBoundingClientRect', {
659
+ value: () => ({ top: 100, left: 10 })
660
+ });
661
+
662
+ // Act
663
+ const result = testOrder(container);
664
+
665
+ // Assert
666
+ expect(result).toBe(true);
47
667
  });
48
668
 
49
- it.skip('should handle elements with tabindex correctly', () => {
50
- // Test implementation requires mocking getFocusableElements which has issues with CommonJS modules
669
+ it('should handle CSS removal and restoration with real implementation', async () => {
670
+ // Restore real implementation
671
+ vi.unmock('../src/getFocusableElements.js');
672
+ const { testOrder } = await import('../src/testOrder.js');
673
+
674
+ const container = document.createElement('div');
675
+ document.body.appendChild(container);
676
+
677
+ // Add CSS styles
678
+ const styleElement = document.createElement('style');
679
+ styleElement.textContent = 'button { position: relative; }';
680
+ document.head.appendChild(styleElement);
681
+
682
+ const button1 = document.createElement('button');
683
+ button1.textContent = 'Button 1';
684
+ button1.setAttribute('style', 'top: 0px;');
685
+ container.appendChild(button1);
686
+
687
+ const button2 = document.createElement('button');
688
+ button2.textContent = 'Button 2';
689
+ container.appendChild(button2);
690
+
691
+ // Mock getBoundingClientRect
692
+ Object.defineProperty(button1, 'getBoundingClientRect', {
693
+ value: () => ({ top: 10, left: 10 })
694
+ });
695
+ Object.defineProperty(button2, 'getBoundingClientRect', {
696
+ value: () => ({ top: 10, left: 100 })
697
+ });
698
+
699
+ // Act
700
+ const result = testOrder(container);
701
+
702
+ // Assert: Styles should be preserved
703
+ expect(document.head.querySelector('style')).toBeTruthy();
704
+ expect(button1.getAttribute('style')).toBe('top: 0px;');
705
+ expect(result).toBe(true);
706
+ });
707
+
708
+ it('should return false when visual order after CSS removal differs', async () => {
709
+ // Restore real implementation
710
+ vi.unmock('../src/getFocusableElements.js');
711
+ const { testOrder } = await import('../src/testOrder.js');
712
+
713
+ const container = document.createElement('div');
714
+ document.body.appendChild(container);
715
+
716
+ const button1 = document.createElement('button');
717
+ button1.textContent = 'Button 1';
718
+ container.appendChild(button1);
719
+
720
+ const button2 = document.createElement('button');
721
+ button2.textContent = 'Button 2';
722
+ container.appendChild(button2);
723
+
724
+ // Create a mock where visual order changes after CSS removal
725
+ let callCount = 0;
726
+ Object.defineProperty(button1, 'getBoundingClientRect', {
727
+ value: () => {
728
+ callCount++;
729
+ // First call: normal order
730
+ // Second call: after CSS removal, different order
731
+ // Third call: after restoration, back to normal
732
+ if (callCount === 2 || callCount === 4) {
733
+ return { top: 100, left: 10 }; // different position without CSS
734
+ }
735
+ return { top: 10, left: 10 };
736
+ }
737
+ });
738
+
739
+ Object.defineProperty(button2, 'getBoundingClientRect', {
740
+ value: () => ({ top: 10, left: 100 })
741
+ });
742
+
743
+ // Act
744
+ const result = testOrder(container);
745
+
746
+ // Assert
747
+ expect(result).toBe(false);
748
+ });
749
+
750
+ it('should handle input elements with real getFocusableElements', async () => {
751
+ // Restore real implementation
752
+ vi.unmock('../src/getFocusableElements.js');
753
+ const { testOrder } = await import('../src/testOrder.js');
754
+
755
+ const container = document.createElement('div');
756
+ document.body.appendChild(container);
757
+
758
+ const input1 = document.createElement('input');
759
+ input1.type = 'text';
760
+ container.appendChild(input1);
761
+
762
+ const input2 = document.createElement('input');
763
+ input2.type = 'checkbox';
764
+ container.appendChild(input2);
765
+
766
+ const textarea = document.createElement('textarea');
767
+ container.appendChild(textarea);
768
+
769
+ // Mock getBoundingClientRect
770
+ Object.defineProperty(input1, 'getBoundingClientRect', {
771
+ value: () => ({ top: 10, left: 10 })
772
+ });
773
+ Object.defineProperty(input2, 'getBoundingClientRect', {
774
+ value: () => ({ top: 10, left: 100 })
775
+ });
776
+ Object.defineProperty(textarea, 'getBoundingClientRect', {
777
+ value: () => ({ top: 50, left: 10 })
778
+ });
779
+
780
+ // Act
781
+ const result = testOrder(container);
782
+
783
+ // Assert
784
+ expect(result).toBe(true);
51
785
  });
786
+
52
787
  });