@afixt/test-utils 1.2.3 → 2.0.0
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 +1 -6
- package/BROWSER_TESTING.md +42 -22
- package/CHANGELOG.md +40 -0
- package/CLAUDE.md +10 -9
- package/package.json +1 -1
- package/src/constants.js +438 -1
- package/src/domUtils.js +17 -38
- package/src/formUtils.js +7 -24
- package/src/getAccessibleName.js +20 -56
- package/src/getCSSGeneratedContent.js +2 -0
- package/src/getFocusableElements.js +12 -21
- package/src/getGeneratedContent.js +18 -11
- package/src/getImageText.js +22 -7
- package/src/hasValidAriaRole.js +11 -19
- package/src/index.js +4 -4
- package/src/interactiveRoles.js +2 -19
- package/src/isA11yVisible.js +95 -0
- package/src/isAriaAttributesValid.js +5 -64
- package/src/isFocusable.js +30 -10
- package/src/isHidden.js +44 -8
- package/src/listEventListeners.js +115 -10
- package/src/stringUtils.js +19 -98
- package/src/tableUtils.js +4 -36
- package/src/testContrast.js +54 -0
- package/test/domUtils.test.js +156 -0
- package/test/formUtils.test.js +0 -47
- package/test/getAccessibleName.test.js +39 -0
- package/test/getGeneratedContent.test.js +305 -241
- package/test/getImageText.test.js +158 -99
- package/test/index.test.js +54 -17
- package/test/{isVisible.test.js → isA11yVisible.test.js} +39 -33
- package/test/isFocusable.test.js +265 -272
- package/test/isHidden.test.js +257 -153
- package/test/listEventListeners.test.js +163 -44
- package/test/playwright/css-pseudo-elements.spec.js +3 -13
- package/test/stringUtils.test.js +55 -228
- package/test/testContrast.test.js +104 -2
- package/todo.md +2 -2
- package/src/isVisible.js +0 -103
package/test/isHidden.test.js
CHANGED
|
@@ -2,156 +2,260 @@ import { describe, it, expect, beforeEach } from 'vitest';
|
|
|
2
2
|
import isHidden from '../src/isHidden.js';
|
|
3
3
|
|
|
4
4
|
describe('isHidden', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
5
|
+
// Setup before each test
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
document.body.innerHTML = '';
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should return true for elements with display:none', () => {
|
|
11
|
+
// Arrange
|
|
12
|
+
const element = document.createElement('div');
|
|
13
|
+
element.style.display = 'none';
|
|
14
|
+
document.body.appendChild(element);
|
|
15
|
+
|
|
16
|
+
// Act
|
|
17
|
+
const result = isHidden(element);
|
|
18
|
+
|
|
19
|
+
// Assert
|
|
20
|
+
expect(result).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should return true for elements with hidden attribute', () => {
|
|
24
|
+
// Arrange
|
|
25
|
+
const element = document.createElement('div');
|
|
26
|
+
element.setAttribute('hidden', '');
|
|
27
|
+
document.body.appendChild(element);
|
|
28
|
+
|
|
29
|
+
// Act
|
|
30
|
+
const result = isHidden(element);
|
|
31
|
+
|
|
32
|
+
// Assert
|
|
33
|
+
expect(result).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should return true for elements with both display:none and hidden attribute', () => {
|
|
37
|
+
// Arrange
|
|
38
|
+
const element = document.createElement('div');
|
|
39
|
+
element.style.display = 'none';
|
|
40
|
+
element.setAttribute('hidden', '');
|
|
41
|
+
document.body.appendChild(element);
|
|
42
|
+
|
|
43
|
+
// Act
|
|
44
|
+
const result = isHidden(element);
|
|
45
|
+
|
|
46
|
+
// Assert
|
|
47
|
+
expect(result).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should return false for visible elements with no hidden attribute', () => {
|
|
51
|
+
// Arrange
|
|
52
|
+
const element = document.createElement('div');
|
|
53
|
+
element.textContent = 'Visible element';
|
|
54
|
+
document.body.appendChild(element);
|
|
55
|
+
|
|
56
|
+
// Act
|
|
57
|
+
const result = isHidden(element);
|
|
58
|
+
|
|
59
|
+
// Assert
|
|
60
|
+
expect(result).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should return false for elements with display:block', () => {
|
|
64
|
+
// Arrange
|
|
65
|
+
const element = document.createElement('div');
|
|
66
|
+
element.style.display = 'block';
|
|
67
|
+
document.body.appendChild(element);
|
|
68
|
+
|
|
69
|
+
// Act
|
|
70
|
+
const result = isHidden(element);
|
|
71
|
+
|
|
72
|
+
// Assert
|
|
73
|
+
expect(result).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should return false for elements with display:inline', () => {
|
|
77
|
+
// Arrange
|
|
78
|
+
const element = document.createElement('span');
|
|
79
|
+
element.style.display = 'inline';
|
|
80
|
+
document.body.appendChild(element);
|
|
81
|
+
|
|
82
|
+
// Act
|
|
83
|
+
const result = isHidden(element);
|
|
84
|
+
|
|
85
|
+
// Assert
|
|
86
|
+
expect(result).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should return false for elements with display:flex', () => {
|
|
90
|
+
// Arrange
|
|
91
|
+
const element = document.createElement('div');
|
|
92
|
+
element.style.display = 'flex';
|
|
93
|
+
document.body.appendChild(element);
|
|
94
|
+
|
|
95
|
+
// Act
|
|
96
|
+
const result = isHidden(element);
|
|
97
|
+
|
|
98
|
+
// Assert
|
|
99
|
+
expect(result).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should handle elements with visibility:hidden but not display:none', () => {
|
|
103
|
+
// Arrange
|
|
104
|
+
const element = document.createElement('div');
|
|
105
|
+
element.style.visibility = 'hidden';
|
|
106
|
+
document.body.appendChild(element);
|
|
107
|
+
|
|
108
|
+
// Act
|
|
109
|
+
const result = isHidden(element);
|
|
110
|
+
|
|
111
|
+
// Assert
|
|
112
|
+
// visibility:hidden is now detected as hidden via computed style
|
|
113
|
+
expect(result).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should handle elements with opacity:0 but not display:none', () => {
|
|
117
|
+
// Arrange
|
|
118
|
+
const element = document.createElement('div');
|
|
119
|
+
element.style.opacity = '0';
|
|
120
|
+
document.body.appendChild(element);
|
|
121
|
+
|
|
122
|
+
// Act
|
|
123
|
+
const result = isHidden(element);
|
|
124
|
+
|
|
125
|
+
// Assert
|
|
126
|
+
// Note: opacity:0 does not qualify as "hidden" according to this function
|
|
127
|
+
expect(result).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should return false for elements with removed hidden attribute', () => {
|
|
131
|
+
// Arrange
|
|
132
|
+
const element = document.createElement('div');
|
|
133
|
+
element.setAttribute('hidden', '');
|
|
134
|
+
element.removeAttribute('hidden');
|
|
135
|
+
document.body.appendChild(element);
|
|
136
|
+
|
|
137
|
+
// Act
|
|
138
|
+
const result = isHidden(element);
|
|
139
|
+
|
|
140
|
+
// Assert
|
|
141
|
+
expect(result).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should return false for elements with display changed from none to block', () => {
|
|
145
|
+
// Arrange
|
|
146
|
+
const element = document.createElement('div');
|
|
147
|
+
element.style.display = 'none';
|
|
148
|
+
element.style.display = 'block';
|
|
149
|
+
document.body.appendChild(element);
|
|
150
|
+
|
|
151
|
+
// Act
|
|
152
|
+
const result = isHidden(element);
|
|
153
|
+
|
|
154
|
+
// Assert
|
|
155
|
+
expect(result).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should return false for null or invalid input', () => {
|
|
159
|
+
expect(isHidden(null)).toBe(false);
|
|
160
|
+
expect(isHidden(undefined)).toBe(false);
|
|
161
|
+
expect(isHidden('not an element')).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should detect display:none applied via CSS class', () => {
|
|
165
|
+
// Arrange
|
|
166
|
+
const style = document.createElement('style');
|
|
167
|
+
style.textContent = '.sr-hidden { display: none; }';
|
|
168
|
+
document.head.appendChild(style);
|
|
169
|
+
const element = document.createElement('div');
|
|
170
|
+
element.className = 'sr-hidden';
|
|
171
|
+
document.body.appendChild(element);
|
|
172
|
+
|
|
173
|
+
// Act
|
|
174
|
+
const result = isHidden(element);
|
|
175
|
+
|
|
176
|
+
// Assert
|
|
177
|
+
expect(result).toBe(true);
|
|
178
|
+
|
|
179
|
+
// Cleanup
|
|
180
|
+
document.head.removeChild(style);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should return false for aria-hidden="true" by default', () => {
|
|
184
|
+
// Arrange
|
|
185
|
+
const element = document.createElement('div');
|
|
186
|
+
element.setAttribute('aria-hidden', 'true');
|
|
187
|
+
document.body.appendChild(element);
|
|
188
|
+
|
|
189
|
+
// Act & Assert
|
|
190
|
+
expect(isHidden(element)).toBe(false);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should return true for aria-hidden="true" with checkAriaHidden option', () => {
|
|
194
|
+
// Arrange
|
|
195
|
+
const element = document.createElement('div');
|
|
196
|
+
element.setAttribute('aria-hidden', 'true');
|
|
197
|
+
document.body.appendChild(element);
|
|
198
|
+
|
|
199
|
+
// Act & Assert
|
|
200
|
+
expect(isHidden(element, { checkAriaHidden: true })).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should return false for opacity:0 by default', () => {
|
|
204
|
+
// Arrange
|
|
205
|
+
const element = document.createElement('div');
|
|
206
|
+
element.style.opacity = '0';
|
|
207
|
+
document.body.appendChild(element);
|
|
208
|
+
|
|
209
|
+
// Act & Assert - already covered above, but explicit for options
|
|
210
|
+
expect(isHidden(element)).toBe(false);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should return true for opacity:0 with checkOpacity option', () => {
|
|
214
|
+
// Arrange
|
|
215
|
+
const element = document.createElement('div');
|
|
216
|
+
element.style.opacity = '0';
|
|
217
|
+
document.body.appendChild(element);
|
|
218
|
+
|
|
219
|
+
// Act & Assert
|
|
220
|
+
expect(isHidden(element, { checkOpacity: true })).toBe(true);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('should return false for zero dimensions by default', () => {
|
|
224
|
+
// Arrange
|
|
225
|
+
const element = document.createElement('div');
|
|
226
|
+
document.body.appendChild(element);
|
|
227
|
+
// JSDOM sets offsetWidth/offsetHeight to 0 by default
|
|
228
|
+
|
|
229
|
+
// Act & Assert
|
|
230
|
+
expect(isHidden(element)).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should return true for zero dimensions with checkDimensions option', () => {
|
|
234
|
+
// Arrange
|
|
235
|
+
const element = document.createElement('div');
|
|
236
|
+
document.body.appendChild(element);
|
|
237
|
+
// JSDOM sets offsetWidth/offsetHeight to 0 by default
|
|
238
|
+
|
|
239
|
+
// Act & Assert
|
|
240
|
+
expect(isHidden(element, { checkDimensions: true })).toBe(true);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('should detect visibility:hidden applied via CSS class', () => {
|
|
244
|
+
// Arrange
|
|
245
|
+
const style = document.createElement('style');
|
|
246
|
+
style.textContent = '.vis-hidden { visibility: hidden; }';
|
|
247
|
+
document.head.appendChild(style);
|
|
248
|
+
const element = document.createElement('div');
|
|
249
|
+
element.className = 'vis-hidden';
|
|
250
|
+
document.body.appendChild(element);
|
|
251
|
+
|
|
252
|
+
// Act
|
|
253
|
+
const result = isHidden(element);
|
|
254
|
+
|
|
255
|
+
// Assert
|
|
256
|
+
expect(result).toBe(true);
|
|
257
|
+
|
|
258
|
+
// Cleanup
|
|
259
|
+
document.head.removeChild(style);
|
|
260
|
+
});
|
|
261
|
+
});
|