@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.
- package/.claude/settings.local.json +4 -1
- package/CLAUDE.md +12 -0
- package/package.json +1 -1
- package/src/domUtils.js +1 -1
- package/src/getAccessibleName.js +8 -4
- package/src/getFocusableElements.js +13 -4
- package/test/domUtils.test.js +117 -0
- package/test/getAccessibleName.test.js +182 -0
- package/test/getAccessibleText.test.js +350 -79
- package/test/getCSSGeneratedContent.test.js +175 -1
- package/test/getFocusableElements.test.js +106 -35
- package/test/getImageText.test.js +61 -12
- package/test/hasParent.test.js +116 -0
- package/test/index.test.js +165 -0
- package/test/interactiveRoles.test.js +60 -0
- package/test/isAriaAttributesValid.test.js +36 -0
- package/test/isDataTable.test.js +492 -0
- package/test/isValidUrl.test.js +31 -19
- package/test/stringUtils.test.js +235 -1
- package/test/testContrast.test.js +176 -8
- package/test/testOrder.integration.test.js +369 -0
- package/test/testOrder.test.js +756 -21
- package/todo.md +150 -1
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/coverage-final.json +0 -51
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -161
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -196
- package/coverage/test-utils/docs/scripts/core.js.html +0 -2263
- package/coverage/test-utils/docs/scripts/core.min.js.html +0 -151
- package/coverage/test-utils/docs/scripts/index.html +0 -176
- package/coverage/test-utils/docs/scripts/resize.js.html +0 -355
- package/coverage/test-utils/docs/scripts/search.js.html +0 -880
- package/coverage/test-utils/docs/scripts/search.min.js.html +0 -100
- package/coverage/test-utils/docs/scripts/third-party/fuse.js.html +0 -109
- package/coverage/test-utils/docs/scripts/third-party/hljs-line-num-original.js.html +0 -1192
- package/coverage/test-utils/docs/scripts/third-party/hljs-line-num.js.html +0 -85
- package/coverage/test-utils/docs/scripts/third-party/hljs-original.js.html +0 -15598
- package/coverage/test-utils/docs/scripts/third-party/hljs.js.html +0 -85
- package/coverage/test-utils/docs/scripts/third-party/index.html +0 -236
- package/coverage/test-utils/docs/scripts/third-party/popper.js.html +0 -100
- package/coverage/test-utils/docs/scripts/third-party/tippy.js.html +0 -88
- package/coverage/test-utils/docs/scripts/third-party/tocbot.js.html +0 -2098
- package/coverage/test-utils/docs/scripts/third-party/tocbot.min.js.html +0 -85
- package/coverage/test-utils/index.html +0 -131
- package/coverage/test-utils/src/arrayUtils.js.html +0 -283
- package/coverage/test-utils/src/domUtils.js.html +0 -622
- package/coverage/test-utils/src/getAccessibleName.js.html +0 -1444
- package/coverage/test-utils/src/getAccessibleText.js.html +0 -271
- package/coverage/test-utils/src/getAriaAttributesByElement.js.html +0 -142
- package/coverage/test-utils/src/getCSSGeneratedContent.js.html +0 -265
- package/coverage/test-utils/src/getComputedRole.js.html +0 -592
- package/coverage/test-utils/src/getFocusableElements.js.html +0 -163
- package/coverage/test-utils/src/getGeneratedContent.js.html +0 -130
- package/coverage/test-utils/src/getImageText.js.html +0 -160
- package/coverage/test-utils/src/getStyleObject.js.html +0 -220
- package/coverage/test-utils/src/hasAccessibleName.js.html +0 -166
- package/coverage/test-utils/src/hasAttribute.js.html +0 -130
- package/coverage/test-utils/src/hasCSSGeneratedContent.js.html +0 -145
- package/coverage/test-utils/src/hasHiddenParent.js.html +0 -172
- package/coverage/test-utils/src/hasParent.js.html +0 -247
- package/coverage/test-utils/src/hasValidAriaAttributes.js.html +0 -175
- package/coverage/test-utils/src/hasValidAriaRole.js.html +0 -172
- package/coverage/test-utils/src/index.html +0 -611
- package/coverage/test-utils/src/index.js.html +0 -274
- package/coverage/test-utils/src/interactiveRoles.js.html +0 -145
- package/coverage/test-utils/src/isAriaAttributesValid.js.html +0 -304
- package/coverage/test-utils/src/isComplexTable.js.html +0 -412
- package/coverage/test-utils/src/isDataTable.js.html +0 -799
- package/coverage/test-utils/src/isFocusable.js.html +0 -187
- package/coverage/test-utils/src/isHidden.js.html +0 -136
- package/coverage/test-utils/src/isOffScreen.js.html +0 -133
- package/coverage/test-utils/src/isValidUrl.js.html +0 -124
- package/coverage/test-utils/src/isVisible.js.html +0 -271
- package/coverage/test-utils/src/listEventListeners.js.html +0 -370
- package/coverage/test-utils/src/queryCache.js.html +0 -1156
- package/coverage/test-utils/src/stringUtils.js.html +0 -535
- package/coverage/test-utils/src/testContrast.js.html +0 -784
- package/coverage/test-utils/src/testLang.js.html +0 -1810
- package/coverage/test-utils/src/testOrder.js.html +0 -355
- package/coverage/test-utils/vitest.config.browser.js.html +0 -133
- package/coverage/test-utils/vitest.config.js.html +0 -157
package/test/testOrder.test.js
CHANGED
|
@@ -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
|
-
//
|
|
5
|
-
|
|
4
|
+
// Use manual mock without vi.mock for simplicity
|
|
5
|
+
const mockedGetFocusableElements = vi.fn(() => []);
|
|
6
6
|
|
|
7
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
35
|
-
expect(result[0]).toBe(
|
|
36
|
-
expect(result[1]).toBe(el2); //
|
|
37
|
-
expect(result[2]).toBe(
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
46
|
-
//
|
|
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
|
|
50
|
-
//
|
|
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
|
});
|