@afixt/test-utils 1.1.7 → 1.1.8
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 +2 -1
- package/.github/dependabot.yml +36 -0
- package/.github/workflows/ci.yml +71 -0
- package/.github/workflows/security.yml +142 -0
- package/.husky/pre-commit +1 -0
- package/.jscpd.json +27 -0
- package/.markdownlint.json +9 -0
- package/.prettierignore +13 -0
- package/.prettierrc +10 -0
- package/docs/arrayUtils.js.html +2 -2
- package/docs/domUtils.js.html +2 -2
- package/docs/getAccessibleName.js.html +2 -2
- package/docs/getAccessibleText.js.html +2 -2
- package/docs/getAriaAttributesByElement.js.html +2 -2
- package/docs/getCSSGeneratedContent.js.html +2 -2
- package/docs/getComputedRole.js.html +2 -2
- package/docs/getFocusableElements.js.html +2 -2
- package/docs/getGeneratedContent.js.html +2 -2
- package/docs/getImageText.js.html +2 -2
- package/docs/getStyleObject.js.html +2 -2
- package/docs/global.html +2 -2
- package/docs/hasAccessibleName.js.html +2 -2
- package/docs/hasAttribute.js.html +2 -2
- package/docs/hasCSSGeneratedContent.js.html +2 -2
- package/docs/hasHiddenParent.js.html +2 -2
- package/docs/hasParent.js.html +2 -2
- package/docs/hasValidAriaAttributes.js.html +2 -2
- package/docs/hasValidAriaRole.js.html +2 -2
- package/docs/index.html +2 -2
- package/docs/index.js.html +2 -2
- package/docs/isAriaAttributesValid.js.html +2 -2
- package/docs/isComplexTable.js.html +2 -2
- package/docs/isDataTable.js.html +2 -2
- package/docs/isFocusable.js.html +2 -2
- package/docs/isHidden.js.html +2 -2
- package/docs/isOffScreen.js.html +2 -2
- package/docs/isValidUrl.js.html +2 -2
- package/docs/isVisible.js.html +2 -2
- package/docs/module-afixt-test-utils.html +2 -2
- package/docs/scripts/core.js +726 -726
- package/docs/scripts/core.min.js +22 -22
- package/docs/scripts/resize.js +90 -90
- package/docs/scripts/search.js +265 -265
- package/docs/scripts/third-party/Apache-License-2.0.txt +202 -202
- package/docs/scripts/third-party/fuse.js +8 -8
- package/docs/scripts/third-party/hljs-line-num-original.js +369 -369
- package/docs/scripts/third-party/hljs-original.js +5171 -5171
- package/docs/scripts/third-party/popper.js +5 -5
- package/docs/scripts/third-party/tippy.js +1 -1
- package/docs/scripts/third-party/tocbot.js +671 -671
- package/docs/styles/clean-jsdoc-theme-base.css +1159 -1159
- package/docs/styles/clean-jsdoc-theme-dark.css +412 -412
- package/docs/styles/clean-jsdoc-theme-light.css +482 -482
- package/docs/styles/clean-jsdoc-theme-scrollbar.css +29 -29
- package/docs/testContrast.js.html +2 -2
- package/docs/testLang.js.html +2 -2
- package/docs/testOrder.js.html +2 -2
- package/eslint.config.mjs +84 -0
- package/package.json +68 -41
- package/scratchpads/issue-6-standardize-repo.md +109 -0
- package/src/getAccessibleName.js +156 -112
- package/src/getAccessibleText.js +71 -42
- package/test/getAccessibleName.test.js +379 -315
- package/test/getAccessibleText.test.js +375 -308
- package/.eslintrc +0 -78
- package/.github/workflows/test.yml +0 -26
|
@@ -2,366 +2,430 @@ import { describe, it, expect, beforeEach } from 'vitest';
|
|
|
2
2
|
import getAccessibleName from '../src/getAccessibleName';
|
|
3
3
|
|
|
4
4
|
describe('getAccessibleName', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
// Set up a clean DOM before each test
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
document.body.innerHTML = '';
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should return false for null or undefined elements', () => {
|
|
11
|
+
expect(getAccessibleName(null)).toBe(false);
|
|
12
|
+
expect(getAccessibleName(undefined)).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should get name from aria-label', () => {
|
|
16
|
+
document.body.innerHTML = `
|
|
17
17
|
<button aria-label="Submit Form">Click Me</button>
|
|
18
18
|
`;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const button = document.querySelector('button');
|
|
20
|
+
expect(getAccessibleName(button)).toBe('Submit Form');
|
|
21
|
+
});
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
it('should get name from aria-labelledby', () => {
|
|
24
|
+
document.body.innerHTML = `
|
|
25
25
|
<span id="label-1">Submit</span>
|
|
26
26
|
<span id="label-2">Form</span>
|
|
27
27
|
<button aria-labelledby="label-1 label-2">Click Me</button>
|
|
28
28
|
`;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const button = document.querySelector('button');
|
|
30
|
+
expect(getAccessibleName(button)).toBe('Submit Form');
|
|
31
|
+
});
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
it('should get name from label element with for attribute', () => {
|
|
34
|
+
document.body.innerHTML = `
|
|
35
35
|
<label for="test-input">Email Address</label>
|
|
36
36
|
<input id="test-input" type="email">
|
|
37
37
|
`;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
const input = document.querySelector('input');
|
|
39
|
+
expect(getAccessibleName(input)).toBe('Email Address');
|
|
40
|
+
});
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
it('should get name from parent label element', () => {
|
|
43
|
+
document.body.innerHTML = `
|
|
44
44
|
<label>
|
|
45
45
|
Phone Number
|
|
46
46
|
<input type="tel">
|
|
47
47
|
</label>
|
|
48
48
|
`;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
const input = document.querySelector('input');
|
|
50
|
+
expect(getAccessibleName(input)).toBe('Phone Number');
|
|
51
|
+
});
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
it('should get name from button content', () => {
|
|
54
|
+
document.body.innerHTML = `
|
|
55
55
|
<button>Save Changes</button>
|
|
56
56
|
`;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
const button = document.querySelector('button');
|
|
58
|
+
expect(getAccessibleName(button)).toBe('Save Changes');
|
|
59
|
+
});
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
it('should get name from img alt attribute', () => {
|
|
62
|
+
document.body.innerHTML = `
|
|
63
63
|
<img alt="Company Logo">
|
|
64
64
|
`;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
const img = document.querySelector('img');
|
|
66
|
+
expect(getAccessibleName(img)).toBe('Company Logo');
|
|
67
|
+
});
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
it('should use title as fallback for input elements', () => {
|
|
70
|
+
document.body.innerHTML = `
|
|
71
71
|
<input type="text" title="Search Query">
|
|
72
72
|
`;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
const input = document.querySelector('input');
|
|
74
|
+
expect(getAccessibleName(input)).toBe('Search Query');
|
|
75
|
+
});
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
it('should get name from figcaption for figure elements', () => {
|
|
78
|
+
document.body.innerHTML = `
|
|
79
79
|
<figure>
|
|
80
80
|
<img src="chart.png">
|
|
81
81
|
<figcaption>Annual Revenue Growth</figcaption>
|
|
82
82
|
</figure>
|
|
83
83
|
`;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
const figure = document.querySelector('figure');
|
|
85
|
+
expect(getAccessibleName(figure)).toBe('Annual Revenue Growth');
|
|
86
|
+
});
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
it('should get name from value attribute for submit buttons', () => {
|
|
89
|
+
document.body.innerHTML = `
|
|
90
90
|
<input type="submit" value="Register Now">
|
|
91
91
|
`;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
const submit = document.querySelector('input');
|
|
93
|
+
expect(getAccessibleName(submit)).toBe('Register Now');
|
|
94
|
+
});
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
it('should return default text for submit buttons without value', () => {
|
|
97
|
+
document.body.innerHTML = `
|
|
98
98
|
<input type="submit">
|
|
99
99
|
`;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
const submit = document.querySelector('input');
|
|
101
|
+
expect(getAccessibleName(submit)).toBe('Submit');
|
|
102
|
+
});
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
it('should handle complex aria-labelledby references', () => {
|
|
105
|
+
document.body.innerHTML = `
|
|
106
106
|
<h2 id="dialog-title">Confirmation</h2>
|
|
107
107
|
<p id="dialog-desc">Are you sure you want to continue?</p>
|
|
108
108
|
<div role="dialog" aria-labelledby="dialog-title dialog-desc"></div>
|
|
109
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
110
|
+
const dialog = document.querySelector('[role="dialog"]');
|
|
111
|
+
expect(getAccessibleName(dialog)).toBe('Confirmation Are you sure you want to continue?');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should handle text-level elements with content', () => {
|
|
115
|
+
// Test various text-level elements
|
|
116
|
+
const textLevelElements = [
|
|
117
|
+
'em',
|
|
118
|
+
'strong',
|
|
119
|
+
'small',
|
|
120
|
+
'cite',
|
|
121
|
+
'code',
|
|
122
|
+
'var',
|
|
123
|
+
'kbd',
|
|
124
|
+
'sub',
|
|
125
|
+
'sup',
|
|
126
|
+
'i',
|
|
127
|
+
'b',
|
|
128
|
+
'u',
|
|
129
|
+
'mark',
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
textLevelElements.forEach(tagName => {
|
|
133
|
+
document.body.innerHTML = `<${tagName}>Important text</${tagName}>`;
|
|
134
|
+
const element = document.querySelector(tagName);
|
|
135
|
+
expect(getAccessibleName(element)).toBe('Important text');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should handle text-level elements with title fallback', () => {
|
|
140
|
+
document.body.innerHTML = '<em title="Emphasis title"></em>';
|
|
141
|
+
const em = document.querySelector('em');
|
|
142
|
+
expect(getAccessibleName(em)).toBe('Emphasis title');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should return false for text-level elements without content or title', () => {
|
|
146
|
+
document.body.innerHTML = '<strong></strong>';
|
|
147
|
+
const strong = document.querySelector('strong');
|
|
148
|
+
expect(getAccessibleName(strong)).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should handle elements with role-based text content', () => {
|
|
152
|
+
const textRoles = ['button', 'checkbox', 'link', 'menuitem', 'option', 'tab'];
|
|
153
|
+
|
|
154
|
+
textRoles.forEach(role => {
|
|
155
|
+
document.body.innerHTML = `<div role="${role}">Role content</div>`;
|
|
156
|
+
const element = document.querySelector('div');
|
|
157
|
+
expect(getAccessibleName(element)).toBe('Role content');
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should return false for elements with roles but no text content', () => {
|
|
162
|
+
document.body.innerHTML = '<div role="button"></div>';
|
|
163
|
+
const element = document.querySelector('div');
|
|
164
|
+
expect(getAccessibleName(element)).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should handle input type="button" with value', () => {
|
|
168
|
+
document.body.innerHTML = '<input type="button" value="Cancel">';
|
|
169
|
+
const button = document.querySelector('input');
|
|
170
|
+
expect(getAccessibleName(button)).toBe('Cancel');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should handle input type="button" with title fallback', () => {
|
|
174
|
+
document.body.innerHTML = '<input type="button" title="Close Dialog">';
|
|
175
|
+
const button = document.querySelector('input');
|
|
176
|
+
expect(getAccessibleName(button)).toBe('Close Dialog');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should return false for input type="button" without value or title', () => {
|
|
180
|
+
document.body.innerHTML = '<input type="button">';
|
|
181
|
+
const button = document.querySelector('input');
|
|
182
|
+
expect(getAccessibleName(button)).toBe(false);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should handle input type="reset" with title', () => {
|
|
186
|
+
document.body.innerHTML = '<input type="reset" title="Clear Form">';
|
|
187
|
+
const reset = document.querySelector('input');
|
|
188
|
+
expect(getAccessibleName(reset)).toBe('Clear Form');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should return default "Reset" for input type="reset" without title', () => {
|
|
192
|
+
document.body.innerHTML = '<input type="reset">';
|
|
193
|
+
const reset = document.querySelector('input');
|
|
194
|
+
expect(getAccessibleName(reset)).toBe('Reset');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should handle input type="image" with alt attribute', () => {
|
|
198
|
+
document.body.innerHTML = '<input type="image" alt="Submit Button">';
|
|
199
|
+
const image = document.querySelector('input');
|
|
200
|
+
expect(getAccessibleName(image)).toBe('Submit Button');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should handle input type="image" with value fallback', () => {
|
|
204
|
+
document.body.innerHTML = '<input type="image" value="Send Message">';
|
|
205
|
+
const image = document.querySelector('input');
|
|
206
|
+
expect(getAccessibleName(image)).toBe('Send Message');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should handle input type="image" with title fallback', () => {
|
|
210
|
+
document.body.innerHTML = '<input type="image" title="Upload File">';
|
|
211
|
+
const image = document.querySelector('input');
|
|
212
|
+
expect(getAccessibleName(image)).toBe('Upload File');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should return false for input type="image" without alt, value, or title', () => {
|
|
216
|
+
document.body.innerHTML = '<input type="image">';
|
|
217
|
+
const image = document.querySelector('input');
|
|
218
|
+
expect(getAccessibleName(image)).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should handle button element with title fallback', () => {
|
|
222
|
+
document.body.innerHTML = '<button title="Submit Form"></button>';
|
|
223
|
+
const button = document.querySelector('button');
|
|
224
|
+
expect(getAccessibleName(button)).toBe('Submit Form');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should return false for button element without content or title', () => {
|
|
228
|
+
document.body.innerHTML = '<button></button>';
|
|
229
|
+
const button = document.querySelector('button');
|
|
230
|
+
expect(getAccessibleName(button)).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should handle empty aria-label gracefully', () => {
|
|
234
|
+
document.body.innerHTML = '<button aria-label="">Click Me</button>';
|
|
235
|
+
const button = document.querySelector('button');
|
|
236
|
+
expect(getAccessibleName(button)).toBe('Click Me');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should return false for aria-labelledby with non-existent reference', () => {
|
|
240
|
+
document.body.innerHTML = '<button aria-labelledby="non-existent">Click Me</button>';
|
|
241
|
+
const button = document.querySelector('button');
|
|
242
|
+
expect(getAccessibleName(button)).toBe(false);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should handle disconnected elements', () => {
|
|
246
|
+
const div = document.createElement('div');
|
|
247
|
+
div.textContent = 'Not connected';
|
|
248
|
+
expect(getAccessibleName(div)).toBe(false);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should handle invisible elements', () => {
|
|
252
|
+
document.body.innerHTML = '<div style="display: none;">Hidden content</div>';
|
|
253
|
+
const div = document.querySelector('div');
|
|
254
|
+
expect(getAccessibleName(div)).toBe(false);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should handle elements with aria-hidden="true"', () => {
|
|
258
|
+
document.body.innerHTML = '<div aria-hidden="true">Hidden from AT</div>';
|
|
259
|
+
const div = document.querySelector('div');
|
|
260
|
+
expect(getAccessibleName(div)).toBe(false);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should handle unlabellable elements', () => {
|
|
264
|
+
document.body.innerHTML = '<hr>';
|
|
265
|
+
const hr = document.querySelector('hr');
|
|
266
|
+
expect(getAccessibleName(hr)).toBe(false);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should use fallback accessible text for general elements', () => {
|
|
270
|
+
document.body.innerHTML = '<span>Fallback text content</span>';
|
|
271
|
+
const span = document.querySelector('span');
|
|
272
|
+
expect(getAccessibleName(span)).toBe('Fallback text content');
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should return false when no accessible name can be determined', () => {
|
|
276
|
+
document.body.innerHTML = '<div></div>';
|
|
277
|
+
const div = document.querySelector('div');
|
|
278
|
+
expect(getAccessibleName(div)).toBe(false);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should handle input element with empty title', () => {
|
|
282
|
+
document.body.innerHTML = '<input type="text" title="">';
|
|
283
|
+
const input = document.querySelector('input');
|
|
284
|
+
expect(getAccessibleName(input)).toBe(false);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should handle form elements with label', () => {
|
|
288
|
+
document.body.innerHTML = `
|
|
275
289
|
<label for="select-id">Choose option</label>
|
|
276
290
|
<select id="select-id">
|
|
277
291
|
<option>Option 1</option>
|
|
278
292
|
</select>
|
|
279
293
|
`;
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
});
|
|
294
|
+
const select = document.querySelector('select');
|
|
295
|
+
expect(getAccessibleName(select)).toBe('Choose option');
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('should handle form elements without label but with title', () => {
|
|
299
|
+
document.body.innerHTML =
|
|
300
|
+
'<select title="Dropdown menu"><option>Option 1</option></select>';
|
|
301
|
+
const select = document.querySelector('select');
|
|
302
|
+
expect(getAccessibleName(select)).toBe('Dropdown menu');
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('should return false for form elements without label or title', () => {
|
|
306
|
+
document.body.innerHTML = '<select><option>Option 1</option></select>';
|
|
307
|
+
const select = document.querySelector('select');
|
|
308
|
+
expect(getAccessibleName(select)).toBe(false);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should handle object element with title attribute', () => {
|
|
312
|
+
document.body.innerHTML =
|
|
313
|
+
'<object data="chart.pdf" type="application/pdf" title="Q4 Sales Report"></object>';
|
|
314
|
+
const obj = document.querySelector('object');
|
|
315
|
+
expect(getAccessibleName(obj)).toBe('Q4 Sales Report');
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should handle object element with fallback content', () => {
|
|
319
|
+
document.body.innerHTML =
|
|
320
|
+
'<object data="diagram.svg" type="image/svg+xml"><p>Network architecture diagram</p></object>';
|
|
321
|
+
const obj = document.querySelector('object');
|
|
322
|
+
expect(getAccessibleName(obj)).toBe('Network architecture diagram');
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('should handle object element with aria-label', () => {
|
|
326
|
+
document.body.innerHTML =
|
|
327
|
+
'<object data="chart.pdf" aria-label="Annual Revenue Chart"></object>';
|
|
328
|
+
const obj = document.querySelector('object');
|
|
329
|
+
expect(getAccessibleName(obj)).toBe('Annual Revenue Chart');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('should return false for object element without accessible name', () => {
|
|
333
|
+
document.body.innerHTML = '<object data="chart.pdf" type="application/pdf"></object>';
|
|
334
|
+
const obj = document.querySelector('object');
|
|
335
|
+
expect(getAccessibleName(obj)).toBe(false);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('should handle object element with empty title', () => {
|
|
339
|
+
document.body.innerHTML = '<object data="chart.pdf" title=""></object>';
|
|
340
|
+
const obj = document.querySelector('object');
|
|
341
|
+
expect(getAccessibleName(obj)).toBe(false);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('should handle ARIA meter with aria-label', () => {
|
|
345
|
+
document.body.innerHTML =
|
|
346
|
+
'<div role="meter" aria-label="Disk usage" aria-valuenow="75"></div>';
|
|
347
|
+
const meter = document.querySelector('[role="meter"]');
|
|
348
|
+
expect(getAccessibleName(meter)).toBe('Disk usage');
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should handle ARIA meter with title attribute', () => {
|
|
352
|
+
document.body.innerHTML =
|
|
353
|
+
'<div role="meter" title="Battery level" aria-valuenow="50"></div>';
|
|
354
|
+
const meter = document.querySelector('[role="meter"]');
|
|
355
|
+
expect(getAccessibleName(meter)).toBe('Battery level');
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('should handle native meter with associated label', () => {
|
|
359
|
+
document.body.innerHTML =
|
|
360
|
+
'<label for="cpu">CPU usage</label><meter id="cpu" value="0.6">60%</meter>';
|
|
361
|
+
const meter = document.querySelector('meter');
|
|
362
|
+
expect(getAccessibleName(meter)).toBe('CPU usage');
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should handle native meter wrapped in label', () => {
|
|
366
|
+
document.body.innerHTML = '<label>Memory: <meter value="0.8">80%</meter></label>';
|
|
367
|
+
const meter = document.querySelector('meter');
|
|
368
|
+
expect(getAccessibleName(meter)).toBe('Memory:');
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should return false for meter without accessible name', () => {
|
|
372
|
+
document.body.innerHTML = '<div role="meter" aria-valuenow="50">50%</div>';
|
|
373
|
+
const meter = document.querySelector('[role="meter"]');
|
|
374
|
+
expect(getAccessibleName(meter)).toBe(false);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it('should return false for native meter without label', () => {
|
|
378
|
+
document.body.innerHTML = '<meter value="0.5">50%</meter>';
|
|
379
|
+
const meter = document.querySelector('meter');
|
|
380
|
+
expect(getAccessibleName(meter)).toBe(false);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// Subtree image-based accessible name tests
|
|
384
|
+
it('should get name from img alt text inside a button', () => {
|
|
385
|
+
document.body.innerHTML = '<button><img src="foo.png" alt="Submit"></button>';
|
|
386
|
+
const button = document.querySelector('button');
|
|
387
|
+
expect(getAccessibleName(button)).toBe('Submit');
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('should get name from img alt text inside a link', () => {
|
|
391
|
+
document.body.innerHTML = '<a href="/home"><img src="logo.png" alt="Home"></a>';
|
|
392
|
+
const link = document.querySelector('a');
|
|
393
|
+
expect(getAccessibleName(link)).toBe('Home');
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('should combine text and img alt text inside a button', () => {
|
|
397
|
+
document.body.innerHTML = '<button><img src="icon.png" alt="Save"> Document</button>';
|
|
398
|
+
const button = document.querySelector('button');
|
|
399
|
+
const name = getAccessibleName(button);
|
|
400
|
+
expect(name).toContain('Save');
|
|
401
|
+
expect(name).toContain('Document');
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('should combine text and img alt text inside a link', () => {
|
|
405
|
+
document.body.innerHTML =
|
|
406
|
+
'<a href="/profile"><img src="avatar.png" alt="User"> Profile</a>';
|
|
407
|
+
const link = document.querySelector('a');
|
|
408
|
+
const name = getAccessibleName(link);
|
|
409
|
+
expect(name).toContain('User');
|
|
410
|
+
expect(name).toContain('Profile');
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('should get name from img alt text inside a role="button" element', () => {
|
|
414
|
+
document.body.innerHTML = '<div role="button"><img src="play.png" alt="Play"></div>';
|
|
415
|
+
const div = document.querySelector('div');
|
|
416
|
+
expect(getAccessibleName(div)).toBe('Play');
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('should get name from img alt text inside a role="link" element', () => {
|
|
420
|
+
document.body.innerHTML = '<div role="link"><img src="ext.png" alt="External Link"></div>';
|
|
421
|
+
const div = document.querySelector('div');
|
|
422
|
+
expect(getAccessibleName(div)).toBe('External Link');
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('should handle meter with aria-labelledby', () => {
|
|
426
|
+
document.body.innerHTML =
|
|
427
|
+
'<span id="signal-label">Signal strength</span><div role="meter" aria-labelledby="signal-label" aria-valuenow="4"></div>';
|
|
428
|
+
const meter = document.querySelector('[role="meter"]');
|
|
429
|
+
expect(getAccessibleName(meter)).toBe('Signal strength');
|
|
430
|
+
});
|
|
431
|
+
});
|