@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.
Files changed (66) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/.github/dependabot.yml +36 -0
  3. package/.github/workflows/ci.yml +71 -0
  4. package/.github/workflows/security.yml +142 -0
  5. package/.husky/pre-commit +1 -0
  6. package/.jscpd.json +27 -0
  7. package/.markdownlint.json +9 -0
  8. package/.prettierignore +13 -0
  9. package/.prettierrc +10 -0
  10. package/docs/arrayUtils.js.html +2 -2
  11. package/docs/domUtils.js.html +2 -2
  12. package/docs/getAccessibleName.js.html +2 -2
  13. package/docs/getAccessibleText.js.html +2 -2
  14. package/docs/getAriaAttributesByElement.js.html +2 -2
  15. package/docs/getCSSGeneratedContent.js.html +2 -2
  16. package/docs/getComputedRole.js.html +2 -2
  17. package/docs/getFocusableElements.js.html +2 -2
  18. package/docs/getGeneratedContent.js.html +2 -2
  19. package/docs/getImageText.js.html +2 -2
  20. package/docs/getStyleObject.js.html +2 -2
  21. package/docs/global.html +2 -2
  22. package/docs/hasAccessibleName.js.html +2 -2
  23. package/docs/hasAttribute.js.html +2 -2
  24. package/docs/hasCSSGeneratedContent.js.html +2 -2
  25. package/docs/hasHiddenParent.js.html +2 -2
  26. package/docs/hasParent.js.html +2 -2
  27. package/docs/hasValidAriaAttributes.js.html +2 -2
  28. package/docs/hasValidAriaRole.js.html +2 -2
  29. package/docs/index.html +2 -2
  30. package/docs/index.js.html +2 -2
  31. package/docs/isAriaAttributesValid.js.html +2 -2
  32. package/docs/isComplexTable.js.html +2 -2
  33. package/docs/isDataTable.js.html +2 -2
  34. package/docs/isFocusable.js.html +2 -2
  35. package/docs/isHidden.js.html +2 -2
  36. package/docs/isOffScreen.js.html +2 -2
  37. package/docs/isValidUrl.js.html +2 -2
  38. package/docs/isVisible.js.html +2 -2
  39. package/docs/module-afixt-test-utils.html +2 -2
  40. package/docs/scripts/core.js +726 -726
  41. package/docs/scripts/core.min.js +22 -22
  42. package/docs/scripts/resize.js +90 -90
  43. package/docs/scripts/search.js +265 -265
  44. package/docs/scripts/third-party/Apache-License-2.0.txt +202 -202
  45. package/docs/scripts/third-party/fuse.js +8 -8
  46. package/docs/scripts/third-party/hljs-line-num-original.js +369 -369
  47. package/docs/scripts/third-party/hljs-original.js +5171 -5171
  48. package/docs/scripts/third-party/popper.js +5 -5
  49. package/docs/scripts/third-party/tippy.js +1 -1
  50. package/docs/scripts/third-party/tocbot.js +671 -671
  51. package/docs/styles/clean-jsdoc-theme-base.css +1159 -1159
  52. package/docs/styles/clean-jsdoc-theme-dark.css +412 -412
  53. package/docs/styles/clean-jsdoc-theme-light.css +482 -482
  54. package/docs/styles/clean-jsdoc-theme-scrollbar.css +29 -29
  55. package/docs/testContrast.js.html +2 -2
  56. package/docs/testLang.js.html +2 -2
  57. package/docs/testOrder.js.html +2 -2
  58. package/eslint.config.mjs +84 -0
  59. package/package.json +68 -41
  60. package/scratchpads/issue-6-standardize-repo.md +109 -0
  61. package/src/getAccessibleName.js +156 -112
  62. package/src/getAccessibleText.js +71 -42
  63. package/test/getAccessibleName.test.js +379 -315
  64. package/test/getAccessibleText.test.js +375 -308
  65. package/.eslintrc +0 -78
  66. package/.github/workflows/test.yml +0 -26
@@ -2,214 +2,214 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
  import { getAccessibleText } from '../src/getAccessibleText.js';
3
3
 
4
4
  describe('getAccessibleText', () => {
5
- beforeEach(() => {
6
- document.body.innerHTML = '';
7
- });
8
-
9
- afterEach(() => {
10
- document.body.innerHTML = '';
11
- });
12
-
13
- describe('input validation', () => {
14
- it('should return empty string when no element is provided', () => {
15
- expect(getAccessibleText(null)).toBe('');
16
- expect(getAccessibleText(undefined)).toBe('');
5
+ beforeEach(() => {
6
+ document.body.innerHTML = '';
17
7
  });
18
8
 
19
- it('should return empty string for non-Element objects', () => {
20
- expect(getAccessibleText('string')).toBe('');
21
- expect(getAccessibleText(123)).toBe('');
22
- expect(getAccessibleText({})).toBe('');
23
- expect(getAccessibleText([])).toBe('');
24
- expect(getAccessibleText(document.createTextNode('text'))).toBe(''); // Text node, not Element
25
- expect(getAccessibleText(document.createComment('comment'))).toBe(''); // Comment node
9
+ afterEach(() => {
10
+ document.body.innerHTML = '';
26
11
  });
27
12
 
28
- it('should return empty string for disconnected elements', () => {
29
- const div = document.createElement('div');
30
- div.textContent = 'Test content';
31
- // Element is not connected to DOM
32
- expect(getAccessibleText(div)).toBe('');
33
- });
34
- });
35
-
36
- describe('aria-label handling', () => {
37
- it('should prioritize aria-label when present', () => {
38
- const div = document.createElement('div');
39
- div.setAttribute('aria-label', 'Accessible Label');
40
- div.textContent = 'Visual Text';
41
- document.body.appendChild(div);
42
-
43
- expect(getAccessibleText(div)).toBe('Accessible Label');
44
- });
13
+ describe('input validation', () => {
14
+ it('should return empty string when no element is provided', () => {
15
+ expect(getAccessibleText(null)).toBe('');
16
+ expect(getAccessibleText(undefined)).toBe('');
17
+ });
45
18
 
46
- it('should handle empty aria-label by falling back to text content', () => {
47
- const div = document.createElement('div');
48
- div.setAttribute('aria-label', '');
49
- div.textContent = 'Visual Text';
50
- document.body.appendChild(div);
51
-
52
- expect(getAccessibleText(div)).toBe('Visual Text');
53
- });
19
+ it('should return empty string for non-Element objects', () => {
20
+ expect(getAccessibleText('string')).toBe('');
21
+ expect(getAccessibleText(123)).toBe('');
22
+ expect(getAccessibleText({})).toBe('');
23
+ expect(getAccessibleText([])).toBe('');
24
+ expect(getAccessibleText(document.createTextNode('text'))).toBe(''); // Text node, not Element
25
+ expect(getAccessibleText(document.createComment('comment'))).toBe(''); // Comment node
26
+ });
54
27
 
55
- it('should handle aria-label with only whitespace', () => {
56
- const div = document.createElement('div');
57
- div.setAttribute('aria-label', ' ');
58
- div.textContent = 'Visual Text';
59
- document.body.appendChild(div);
60
-
61
- expect(getAccessibleText(div)).toBe('Visual Text');
28
+ it('should return empty string for disconnected elements', () => {
29
+ const div = document.createElement('div');
30
+ div.textContent = 'Test content';
31
+ // Element is not connected to DOM
32
+ expect(getAccessibleText(div)).toBe('');
33
+ });
62
34
  });
63
35
 
64
- it('should return empty aria-label when it exists but is empty after trim', () => {
65
- const div = document.createElement('div');
66
- div.setAttribute('aria-label', '');
67
- div.textContent = '';
68
- document.body.appendChild(div);
69
-
70
- expect(getAccessibleText(div)).toBe('');
71
- });
72
- });
73
-
74
- describe('img alt text handling', () => {
75
- it('should get alt text from img elements', () => {
76
- const img = document.createElement('img');
77
- img.setAttribute('alt', 'Image description');
78
- img.setAttribute('src', 'test.jpg');
79
- document.body.appendChild(img);
80
-
81
- expect(getAccessibleText(img)).toBe('Image description');
82
- });
36
+ describe('aria-label handling', () => {
37
+ it('should prioritize aria-label when present', () => {
38
+ const div = document.createElement('div');
39
+ div.setAttribute('aria-label', 'Accessible Label');
40
+ div.textContent = 'Visual Text';
41
+ document.body.appendChild(div);
83
42
 
84
- it('should handle img with empty alt attribute', () => {
85
- const img = document.createElement('img');
86
- img.setAttribute('alt', '');
87
- img.setAttribute('src', 'test.jpg');
88
- document.body.appendChild(img);
89
-
90
- expect(getAccessibleText(img)).toBe('');
91
- });
43
+ expect(getAccessibleText(div)).toBe('Accessible Label');
44
+ });
92
45
 
93
- it('should handle img without alt attribute', () => {
94
- const img = document.createElement('img');
95
- img.setAttribute('src', 'test.jpg');
96
- document.body.appendChild(img);
97
-
98
- expect(getAccessibleText(img)).toBe('');
99
- });
46
+ it('should handle empty aria-label by falling back to text content', () => {
47
+ const div = document.createElement('div');
48
+ div.setAttribute('aria-label', '');
49
+ div.textContent = 'Visual Text';
50
+ document.body.appendChild(div);
100
51
 
101
- it('should trim whitespace from img alt text', () => {
102
- const img = document.createElement('img');
103
- img.setAttribute('alt', ' Image description ');
104
- img.setAttribute('src', 'test.jpg');
105
- document.body.appendChild(img);
106
-
107
- expect(getAccessibleText(img)).toBe('Image description');
108
- });
52
+ expect(getAccessibleText(div)).toBe('Visual Text');
53
+ });
109
54
 
110
- it('should prioritize aria-label over alt text on img', () => {
111
- const img = document.createElement('img');
112
- img.setAttribute('alt', 'Alt text');
113
- img.setAttribute('aria-label', 'Aria label');
114
- img.setAttribute('src', 'test.jpg');
115
- document.body.appendChild(img);
116
-
117
- expect(getAccessibleText(img)).toBe('Aria label');
118
- });
119
- });
120
-
121
- describe('text content extraction', () => {
122
- it('should get text content from simple elements', () => {
123
- const div = document.createElement('div');
124
- div.textContent = 'Simple text content';
125
- document.body.appendChild(div);
126
-
127
- expect(getAccessibleText(div)).toBe('Simple text content');
55
+ it('should handle aria-label with only whitespace', () => {
56
+ const div = document.createElement('div');
57
+ div.setAttribute('aria-label', ' ');
58
+ div.textContent = 'Visual Text';
59
+ document.body.appendChild(div);
60
+
61
+ expect(getAccessibleText(div)).toBe('Visual Text');
62
+ });
63
+
64
+ it('should return empty aria-label when it exists but is empty after trim', () => {
65
+ const div = document.createElement('div');
66
+ div.setAttribute('aria-label', '');
67
+ div.textContent = '';
68
+ document.body.appendChild(div);
69
+
70
+ expect(getAccessibleText(div)).toBe('');
71
+ });
128
72
  });
129
73
 
130
- it('should trim whitespace from text content', () => {
131
- const div = document.createElement('div');
132
- div.textContent = ' Padded text ';
133
- document.body.appendChild(div);
134
-
135
- expect(getAccessibleText(div)).toBe('Padded text');
74
+ describe('img alt text handling', () => {
75
+ it('should get alt text from img elements', () => {
76
+ const img = document.createElement('img');
77
+ img.setAttribute('alt', 'Image description');
78
+ img.setAttribute('src', 'test.jpg');
79
+ document.body.appendChild(img);
80
+
81
+ expect(getAccessibleText(img)).toBe('Image description');
82
+ });
83
+
84
+ it('should handle img with empty alt attribute', () => {
85
+ const img = document.createElement('img');
86
+ img.setAttribute('alt', '');
87
+ img.setAttribute('src', 'test.jpg');
88
+ document.body.appendChild(img);
89
+
90
+ expect(getAccessibleText(img)).toBe('');
91
+ });
92
+
93
+ it('should handle img without alt attribute', () => {
94
+ const img = document.createElement('img');
95
+ img.setAttribute('src', 'test.jpg');
96
+ document.body.appendChild(img);
97
+
98
+ expect(getAccessibleText(img)).toBe('');
99
+ });
100
+
101
+ it('should trim whitespace from img alt text', () => {
102
+ const img = document.createElement('img');
103
+ img.setAttribute('alt', ' Image description ');
104
+ img.setAttribute('src', 'test.jpg');
105
+ document.body.appendChild(img);
106
+
107
+ expect(getAccessibleText(img)).toBe('Image description');
108
+ });
109
+
110
+ it('should prioritize aria-label over alt text on img', () => {
111
+ const img = document.createElement('img');
112
+ img.setAttribute('alt', 'Alt text');
113
+ img.setAttribute('aria-label', 'Aria label');
114
+ img.setAttribute('src', 'test.jpg');
115
+ document.body.appendChild(img);
116
+
117
+ expect(getAccessibleText(img)).toBe('Aria label');
118
+ });
136
119
  });
137
120
 
138
- it('should concatenate text from multiple child nodes', () => {
139
- const container = document.createElement('div');
140
- container.innerHTML = `
121
+ describe('text content extraction', () => {
122
+ it('should get text content from simple elements', () => {
123
+ const div = document.createElement('div');
124
+ div.textContent = 'Simple text content';
125
+ document.body.appendChild(div);
126
+
127
+ expect(getAccessibleText(div)).toBe('Simple text content');
128
+ });
129
+
130
+ it('should trim whitespace from text content', () => {
131
+ const div = document.createElement('div');
132
+ div.textContent = ' Padded text ';
133
+ document.body.appendChild(div);
134
+
135
+ expect(getAccessibleText(div)).toBe('Padded text');
136
+ });
137
+
138
+ it('should concatenate text from multiple child nodes', () => {
139
+ const container = document.createElement('div');
140
+ container.innerHTML = `
141
141
  <span>First part</span>
142
142
  <span>Second part</span>
143
143
  <span>Third part</span>
144
144
  `;
145
- document.body.appendChild(container);
146
-
147
- const result = getAccessibleText(container);
148
- expect(result).toContain('First part');
149
- expect(result).toContain('Second part');
150
- expect(result).toContain('Third part');
151
- });
145
+ document.body.appendChild(container);
152
146
 
153
- it('should handle nested elements', () => {
154
- const container = document.createElement('div');
155
- container.innerHTML = `
147
+ const result = getAccessibleText(container);
148
+ expect(result).toContain('First part');
149
+ expect(result).toContain('Second part');
150
+ expect(result).toContain('Third part');
151
+ });
152
+
153
+ it('should handle nested elements', () => {
154
+ const container = document.createElement('div');
155
+ container.innerHTML = `
156
156
  <div>
157
157
  <span>Nested <strong>text</strong> content</span>
158
158
  </div>
159
159
  `;
160
- document.body.appendChild(container);
161
-
162
- const result = getAccessibleText(container);
163
- expect(result).toContain('Nested');
164
- expect(result).toContain('text');
165
- expect(result).toContain('content');
166
- });
167
- });
168
-
169
- describe('TreeWalker edge cases', () => {
170
- it('should handle elements with no initial text content using TreeWalker', () => {
171
- const container = document.createElement('div');
172
- // Create element with no textContent initially
173
- container.textContent = '';
174
-
175
- const span1 = document.createElement('span');
176
- const span2 = document.createElement('span');
177
-
178
- // Create text nodes directly
179
- span1.appendChild(document.createTextNode('Text 1'));
180
- span2.appendChild(document.createTextNode('Text 2'));
181
-
182
- container.appendChild(span1);
183
- container.appendChild(document.createTextNode(' ')); // whitespace node
184
- container.appendChild(span2);
185
-
186
- document.body.appendChild(container);
187
-
188
- const result = getAccessibleText(container);
189
- expect(result).toContain('Text 1');
190
- expect(result).toContain('Text 2');
191
- });
160
+ document.body.appendChild(container);
192
161
 
193
- it('should filter empty text nodes using TreeWalker', () => {
194
- const container = document.createElement('div');
195
- container.innerHTML = ''; // Start with empty container
196
-
197
- // Add mix of empty and non-empty text nodes
198
- container.appendChild(document.createTextNode(''));
199
- container.appendChild(document.createTextNode('Valid text'));
200
- container.appendChild(document.createTextNode(' ')); // Only whitespace
201
- container.appendChild(document.createTextNode('More text'));
202
-
203
- document.body.appendChild(container);
204
-
205
- const result = getAccessibleText(container);
206
- expect(result).toContain('Valid text');
207
- expect(result).toContain('More text');
162
+ const result = getAccessibleText(container);
163
+ expect(result).toContain('Nested');
164
+ expect(result).toContain('text');
165
+ expect(result).toContain('content');
166
+ });
208
167
  });
209
168
 
210
- it('should handle complex DOM with mixed content', () => {
211
- const container = document.createElement('div');
212
- container.innerHTML = `
169
+ describe('TreeWalker edge cases', () => {
170
+ it('should handle elements with no initial text content using TreeWalker', () => {
171
+ const container = document.createElement('div');
172
+ // Create element with no textContent initially
173
+ container.textContent = '';
174
+
175
+ const span1 = document.createElement('span');
176
+ const span2 = document.createElement('span');
177
+
178
+ // Create text nodes directly
179
+ span1.appendChild(document.createTextNode('Text 1'));
180
+ span2.appendChild(document.createTextNode('Text 2'));
181
+
182
+ container.appendChild(span1);
183
+ container.appendChild(document.createTextNode(' ')); // whitespace node
184
+ container.appendChild(span2);
185
+
186
+ document.body.appendChild(container);
187
+
188
+ const result = getAccessibleText(container);
189
+ expect(result).toContain('Text 1');
190
+ expect(result).toContain('Text 2');
191
+ });
192
+
193
+ it('should filter empty text nodes using TreeWalker', () => {
194
+ const container = document.createElement('div');
195
+ container.innerHTML = ''; // Start with empty container
196
+
197
+ // Add mix of empty and non-empty text nodes
198
+ container.appendChild(document.createTextNode(''));
199
+ container.appendChild(document.createTextNode('Valid text'));
200
+ container.appendChild(document.createTextNode(' ')); // Only whitespace
201
+ container.appendChild(document.createTextNode('More text'));
202
+
203
+ document.body.appendChild(container);
204
+
205
+ const result = getAccessibleText(container);
206
+ expect(result).toContain('Valid text');
207
+ expect(result).toContain('More text');
208
+ });
209
+
210
+ it('should handle complex DOM with mixed content', () => {
211
+ const container = document.createElement('div');
212
+ container.innerHTML = `
213
213
  <header>
214
214
  <h1>Title</h1>
215
215
  <nav>
@@ -225,141 +225,208 @@ describe('getAccessibleText', () => {
225
225
  </ul>
226
226
  </main>
227
227
  `;
228
- document.body.appendChild(container);
229
-
230
- const result = getAccessibleText(container);
231
- expect(result).toContain('Title');
232
- expect(result).toContain('Link 1');
233
- expect(result).toContain('Link 2');
234
- expect(result).toContain('Paragraph text');
235
- expect(result).toContain('Item 1');
236
- expect(result).toContain('Item 2');
237
- });
238
- });
239
-
240
- describe('special cases', () => {
241
- it('should handle elements with only child elements and no text', () => {
242
- const container = document.createElement('div');
243
- const child1 = document.createElement('div');
244
- const child2 = document.createElement('div');
245
-
246
- container.appendChild(child1);
247
- container.appendChild(child2);
248
- document.body.appendChild(container);
249
-
250
- expect(getAccessibleText(container)).toBe('');
251
- });
228
+ document.body.appendChild(container);
252
229
 
253
- it('should use TreeWalker when textContent is empty but has text nodes', () => {
254
- const container = document.createElement('div');
255
- document.body.appendChild(container);
256
-
257
- // Create a scenario where textContent initially appears empty
258
- // but TreeWalker can find text nodes
259
- Object.defineProperty(container, 'textContent', {
260
- get: function() { return ''; },
261
- configurable: true
262
- });
263
-
264
- // Add actual text nodes that TreeWalker should find
265
- const text1 = document.createTextNode('Hidden text 1');
266
- const text2 = document.createTextNode('Hidden text 2');
267
- container.appendChild(text1);
268
- container.appendChild(text2);
269
-
270
- const result = getAccessibleText(container);
271
- expect(result).toBe('Hidden text 1 Hidden text 2');
230
+ const result = getAccessibleText(container);
231
+ expect(result).toContain('Title');
232
+ expect(result).toContain('Link 1');
233
+ expect(result).toContain('Link 2');
234
+ expect(result).toContain('Paragraph text');
235
+ expect(result).toContain('Item 1');
236
+ expect(result).toContain('Item 2');
237
+ });
272
238
  });
273
239
 
274
- it('should handle self-closing elements', () => {
275
- const br = document.createElement('br');
276
- document.body.appendChild(br);
277
-
278
- expect(getAccessibleText(br)).toBe('');
279
- });
240
+ describe('child img alt text in subtree', () => {
241
+ it('should get alt text from child img elements', () => {
242
+ const container = document.createElement('div');
243
+ container.innerHTML = '<img src="icon.png" alt="Submit">';
244
+ document.body.appendChild(container);
280
245
 
281
- it('should handle input elements with value', () => {
282
- const input = document.createElement('input');
283
- input.type = 'text';
284
- input.value = 'Input value';
285
- document.body.appendChild(input);
286
-
287
- // Input elements don't have textContent
288
- expect(getAccessibleText(input)).toBe('');
289
- });
246
+ expect(getAccessibleText(container)).toBe('Submit');
247
+ });
290
248
 
291
- it('should handle elements with comment nodes', () => {
292
- const container = document.createElement('div');
293
- container.appendChild(document.createTextNode('Before'));
294
- container.appendChild(document.createComment('This is a comment'));
295
- container.appendChild(document.createTextNode('After'));
296
- document.body.appendChild(container);
297
-
298
- const result = getAccessibleText(container);
299
- expect(result).toContain('Before');
300
- expect(result).toContain('After');
301
- expect(result).not.toContain('comment');
302
- });
249
+ it('should combine text nodes and child img alt text', () => {
250
+ const container = document.createElement('div');
251
+ container.innerHTML = 'Click <img src="icon.png" alt="here"> to continue';
252
+ document.body.appendChild(container);
303
253
 
304
- it('should handle TreeWalker acceptNode with element nodes that should be rejected', () => {
305
- const container = document.createElement('div');
306
- container.textContent = ''; // Ensure no textContent initially
307
-
308
- // Create an element node (should be rejected by acceptNode)
309
- const span = document.createElement('span');
310
- span.appendChild(document.createTextNode('Text inside span'));
311
- container.appendChild(span);
312
-
313
- // Create a direct text node (should be accepted)
314
- container.appendChild(document.createTextNode('Direct text'));
315
-
316
- document.body.appendChild(container);
317
-
318
- const result = getAccessibleText(container);
319
- expect(result).toContain('Text inside span');
320
- expect(result).toContain('Direct text');
321
- });
254
+ const result = getAccessibleText(container);
255
+ expect(result).toContain('Click');
256
+ expect(result).toContain('here');
257
+ expect(result).toContain('to continue');
258
+ });
259
+
260
+ it('should get alt text from deeply nested img elements', () => {
261
+ const container = document.createElement('div');
262
+ container.innerHTML = '<span><img src="icon.png" alt="Save"></span>';
263
+ document.body.appendChild(container);
264
+
265
+ expect(getAccessibleText(container)).toBe('Save');
266
+ });
267
+
268
+ it('should skip img elements with empty alt (decorative)', () => {
269
+ const container = document.createElement('div');
270
+ container.innerHTML = 'Label <img src="decorative.png" alt="">';
271
+ document.body.appendChild(container);
272
+
273
+ expect(getAccessibleText(container)).toBe('Label');
274
+ });
275
+
276
+ it('should skip img elements without alt attribute', () => {
277
+ const container = document.createElement('div');
278
+ container.innerHTML = 'Label <img src="decorative.png">';
279
+ document.body.appendChild(container);
280
+
281
+ expect(getAccessibleText(container)).toBe('Label');
282
+ });
322
283
 
323
- it('should handle TreeWalker acceptNode filtering and text node collection', () => {
324
- const container = document.createElement('div');
325
- container.textContent = ''; // Force TreeWalker path
326
-
327
- // Create child elements with text nodes to test TreeWalker
328
- const child1 = document.createElement('span');
329
- child1.appendChild(document.createTextNode('First'));
330
-
331
- const child2 = document.createElement('span');
332
- child2.appendChild(document.createTextNode('Second'));
333
-
334
- container.appendChild(child1);
335
- container.appendChild(document.createTextNode(' ')); // space between
336
- container.appendChild(child2);
337
-
338
- document.body.appendChild(container);
339
-
340
- const result = getAccessibleText(container);
341
- expect(result).toBe('First Second');
284
+ it('should handle multiple img elements with alt text', () => {
285
+ const container = document.createElement('div');
286
+ container.innerHTML = '<img src="a.png" alt="First"> <img src="b.png" alt="Second">';
287
+ document.body.appendChild(container);
288
+
289
+ const result = getAccessibleText(container);
290
+ expect(result).toContain('First');
291
+ expect(result).toContain('Second');
292
+ });
293
+
294
+ it('should handle input type="image" with alt in subtree', () => {
295
+ const container = document.createElement('div');
296
+ container.innerHTML = '<input type="image" alt="Search">';
297
+ document.body.appendChild(container);
298
+
299
+ expect(getAccessibleText(container)).toBe('Search');
300
+ });
342
301
  });
343
302
 
344
- it('should test TreeWalker acceptNode function with various node types', () => {
345
- const container = document.createElement('div');
346
- document.body.appendChild(container);
347
-
348
- // Override textContent to force TreeWalker path
349
- Object.defineProperty(container, 'textContent', {
350
- get: function() { return ''; },
351
- configurable: true
352
- });
353
-
354
- // Add mix of empty and non-empty text nodes
355
- container.appendChild(document.createTextNode('')); // Empty - should be rejected
356
- container.appendChild(document.createTextNode(' ')); // Whitespace only - should be rejected
357
- container.appendChild(document.createTextNode('Valid')); // Valid - should be accepted
358
- container.appendChild(document.createTextNode('\n\t')); // Whitespace - should be rejected
359
- container.appendChild(document.createTextNode('Text')); // Valid - should be accepted
360
-
361
- const result = getAccessibleText(container);
362
- expect(result).toBe('Valid Text');
303
+ describe('special cases', () => {
304
+ it('should handle elements with only child elements and no text', () => {
305
+ const container = document.createElement('div');
306
+ const child1 = document.createElement('div');
307
+ const child2 = document.createElement('div');
308
+
309
+ container.appendChild(child1);
310
+ container.appendChild(child2);
311
+ document.body.appendChild(container);
312
+
313
+ expect(getAccessibleText(container)).toBe('');
314
+ });
315
+
316
+ it('should use TreeWalker when textContent is empty but has text nodes', () => {
317
+ const container = document.createElement('div');
318
+ document.body.appendChild(container);
319
+
320
+ // Create a scenario where textContent initially appears empty
321
+ // but TreeWalker can find text nodes
322
+ Object.defineProperty(container, 'textContent', {
323
+ get: function () {
324
+ return '';
325
+ },
326
+ configurable: true,
327
+ });
328
+
329
+ // Add actual text nodes that TreeWalker should find
330
+ const text1 = document.createTextNode('Hidden text 1');
331
+ const text2 = document.createTextNode('Hidden text 2');
332
+ container.appendChild(text1);
333
+ container.appendChild(text2);
334
+
335
+ const result = getAccessibleText(container);
336
+ expect(result).toBe('Hidden text 1 Hidden text 2');
337
+ });
338
+
339
+ it('should handle self-closing elements', () => {
340
+ const br = document.createElement('br');
341
+ document.body.appendChild(br);
342
+
343
+ expect(getAccessibleText(br)).toBe('');
344
+ });
345
+
346
+ it('should handle input elements with value', () => {
347
+ const input = document.createElement('input');
348
+ input.type = 'text';
349
+ input.value = 'Input value';
350
+ document.body.appendChild(input);
351
+
352
+ // Input elements don't have textContent
353
+ expect(getAccessibleText(input)).toBe('');
354
+ });
355
+
356
+ it('should handle elements with comment nodes', () => {
357
+ const container = document.createElement('div');
358
+ container.appendChild(document.createTextNode('Before'));
359
+ container.appendChild(document.createComment('This is a comment'));
360
+ container.appendChild(document.createTextNode('After'));
361
+ document.body.appendChild(container);
362
+
363
+ const result = getAccessibleText(container);
364
+ expect(result).toContain('Before');
365
+ expect(result).toContain('After');
366
+ expect(result).not.toContain('comment');
367
+ });
368
+
369
+ it('should handle TreeWalker acceptNode with element nodes that should be rejected', () => {
370
+ const container = document.createElement('div');
371
+ container.textContent = ''; // Ensure no textContent initially
372
+
373
+ // Create an element node (should be rejected by acceptNode)
374
+ const span = document.createElement('span');
375
+ span.appendChild(document.createTextNode('Text inside span'));
376
+ container.appendChild(span);
377
+
378
+ // Create a direct text node (should be accepted)
379
+ container.appendChild(document.createTextNode('Direct text'));
380
+
381
+ document.body.appendChild(container);
382
+
383
+ const result = getAccessibleText(container);
384
+ expect(result).toContain('Text inside span');
385
+ expect(result).toContain('Direct text');
386
+ });
387
+
388
+ it('should handle TreeWalker acceptNode filtering and text node collection', () => {
389
+ const container = document.createElement('div');
390
+ container.textContent = ''; // Force TreeWalker path
391
+
392
+ // Create child elements with text nodes to test TreeWalker
393
+ const child1 = document.createElement('span');
394
+ child1.appendChild(document.createTextNode('First'));
395
+
396
+ const child2 = document.createElement('span');
397
+ child2.appendChild(document.createTextNode('Second'));
398
+
399
+ container.appendChild(child1);
400
+ container.appendChild(document.createTextNode(' ')); // space between
401
+ container.appendChild(child2);
402
+
403
+ document.body.appendChild(container);
404
+
405
+ const result = getAccessibleText(container);
406
+ expect(result).toBe('First Second');
407
+ });
408
+
409
+ it('should test TreeWalker acceptNode function with various node types', () => {
410
+ const container = document.createElement('div');
411
+ document.body.appendChild(container);
412
+
413
+ // Override textContent to force TreeWalker path
414
+ Object.defineProperty(container, 'textContent', {
415
+ get: function () {
416
+ return '';
417
+ },
418
+ configurable: true,
419
+ });
420
+
421
+ // Add mix of empty and non-empty text nodes
422
+ container.appendChild(document.createTextNode('')); // Empty - should be rejected
423
+ container.appendChild(document.createTextNode(' ')); // Whitespace only - should be rejected
424
+ container.appendChild(document.createTextNode('Valid')); // Valid - should be accepted
425
+ container.appendChild(document.createTextNode('\n\t')); // Whitespace - should be rejected
426
+ container.appendChild(document.createTextNode('Text')); // Valid - should be accepted
427
+
428
+ const result = getAccessibleText(container);
429
+ expect(result).toBe('Valid Text');
430
+ });
363
431
  });
364
- });
365
- });
432
+ });