@afixt/test-utils 1.1.2 → 1.1.4

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 (132) hide show
  1. package/.claude/settings.local.json +6 -2
  2. package/.github/workflows/test.yml +26 -0
  3. package/BROWSER_TESTING.md +109 -0
  4. package/CLAUDE.md +22 -0
  5. package/package.json +6 -8
  6. package/playwright.config.js +27 -0
  7. package/src/domUtils.js +1 -1
  8. package/src/getAccessibleName.js +8 -4
  9. package/src/getCSSGeneratedContent.js +9 -5
  10. package/src/getFocusableElements.js +13 -4
  11. package/src/getImageText.js +4 -1
  12. package/src/testContrast.js +5 -1
  13. package/test/__screenshots__/getImageText.test.js/getImageText-should-be-an-async-function-1.png +0 -0
  14. package/test/__screenshots__/getImageText.test.js/getImageText-should-be-defined-and-exported-from-the-module-1.png +0 -0
  15. package/test/__screenshots__/getImageText.test.js/getImageText-should-handle-empty-string-input-gracefully-1.png +0 -0
  16. package/test/__screenshots__/getImageText.test.js/getImageText-should-handle-invalid-image-paths-gracefully-1.png +0 -0
  17. package/test/__screenshots__/getImageText.test.js/getImageText-should-handle-null-or-undefined-input-gracefully-1.png +0 -0
  18. package/test/__screenshots__/getImageText.test.js/getImageText-should-log-errors-in-non-test-environments-1.png +0 -0
  19. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-addEventListener-override-should-call-original-addEventListener-1.png +0 -0
  20. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-addEventListener-override-should-track-added-event-listeners-1.png +0 -0
  21. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-addEventListener-override-should-track-listeners-for-different-event-types-1.png +0 -0
  22. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-addEventListener-override-should-track-multiple-listeners-for-the-same-event-1.png +0 -0
  23. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-addEventListener-override-should-track-options-parameter-1.png +0 -0
  24. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-getEventListeners-should-return-all-event-listeners-for-an-element-1.png +0 -0
  25. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-getEventListeners-should-return-empty-object-for-elements-without-listeners-1.png +0 -0
  26. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-getXPath-should-generate-XPath-for-elements-without-id-1.png +0 -0
  27. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-getXPath-should-handle-multiple-siblings-correctly-1.png +0 -0
  28. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-getXPath-should-return-XPath-for-element-with-id-1.png +0 -0
  29. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-listEventListeners-should-list-event-listeners-on-child-elements-1.png +0 -0
  30. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-listEventListeners-should-list-event-listeners-on-root-element-1.png +0 -0
  31. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-listEventListeners-should-list-listeners-from-multiple-elements-1.png +0 -0
  32. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-listEventListeners-should-list-multiple-event-types-on-same-element-1.png +0 -0
  33. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-listEventListeners-should-return-empty-array-when-no-event-listeners-exist-1.png +0 -0
  34. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-listEventListeners-should-use-document-as-default-root-element-1.png +0 -0
  35. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-listEventListeners-should-work-with-custom-root-element-1.png +0 -0
  36. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-removeEventListener-override-should-call-original-removeEventListener-1.png +0 -0
  37. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-removeEventListener-override-should-handle-removing-non-existent-listeners-gracefully-1.png +0 -0
  38. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-removeEventListener-override-should-only-remove-the-specified-listener-1.png +0 -0
  39. package/test/__screenshots__/listEventListeners.test.js/listEventListeners-removeEventListener-override-should-remove-tracked-event-listeners-1.png +0 -0
  40. package/test/arrayUtils.test.js +22 -0
  41. package/test/domUtils.test.js +241 -0
  42. package/test/getAccessibleName.test.js +182 -0
  43. package/test/getAccessibleText.test.js +350 -79
  44. package/test/getCSSGeneratedContent.test.js +175 -1
  45. package/test/getFocusableElements.test.js +106 -35
  46. package/test/getImageText.test.js +95 -12
  47. package/test/getStyleObject.test.js +19 -1
  48. package/test/hasCSSGeneratedContent.test.js +7 -2
  49. package/test/hasParent.test.js +116 -0
  50. package/test/hasValidAriaRole.test.js +64 -2
  51. package/test/index.test.js +165 -0
  52. package/test/interactiveRoles.test.js +60 -0
  53. package/test/isAriaAttributesValid.test.js +36 -0
  54. package/test/isDataTable.test.js +492 -0
  55. package/test/isFocusable.test.js +94 -1
  56. package/test/isValidUrl.test.js +31 -19
  57. package/test/isVisible.test.js +121 -3
  58. package/test/playwright/css-pseudo-elements.spec.js +155 -0
  59. package/test/playwright/fixtures/css-pseudo-elements.html +77 -0
  60. package/test/setup.js +9 -1
  61. package/test/stringUtils.test.js +277 -1
  62. package/test/testContrast.test.js +614 -9
  63. package/test/testLang.test.js +152 -11
  64. package/test/testOrder.integration.test.js +369 -0
  65. package/test/testOrder.test.js +756 -21
  66. package/todo.md +11 -1
  67. package/vitest.config.js +8 -1
  68. package/coverage/base.css +0 -224
  69. package/coverage/block-navigation.js +0 -87
  70. package/coverage/coverage-final.json +0 -51
  71. package/coverage/favicon.png +0 -0
  72. package/coverage/index.html +0 -161
  73. package/coverage/prettify.css +0 -1
  74. package/coverage/prettify.js +0 -2
  75. package/coverage/sort-arrow-sprite.png +0 -0
  76. package/coverage/sorter.js +0 -196
  77. package/coverage/test-utils/docs/scripts/core.js.html +0 -2263
  78. package/coverage/test-utils/docs/scripts/core.min.js.html +0 -151
  79. package/coverage/test-utils/docs/scripts/index.html +0 -176
  80. package/coverage/test-utils/docs/scripts/resize.js.html +0 -355
  81. package/coverage/test-utils/docs/scripts/search.js.html +0 -880
  82. package/coverage/test-utils/docs/scripts/search.min.js.html +0 -100
  83. package/coverage/test-utils/docs/scripts/third-party/fuse.js.html +0 -109
  84. package/coverage/test-utils/docs/scripts/third-party/hljs-line-num-original.js.html +0 -1192
  85. package/coverage/test-utils/docs/scripts/third-party/hljs-line-num.js.html +0 -85
  86. package/coverage/test-utils/docs/scripts/third-party/hljs-original.js.html +0 -15598
  87. package/coverage/test-utils/docs/scripts/third-party/hljs.js.html +0 -85
  88. package/coverage/test-utils/docs/scripts/third-party/index.html +0 -236
  89. package/coverage/test-utils/docs/scripts/third-party/popper.js.html +0 -100
  90. package/coverage/test-utils/docs/scripts/third-party/tippy.js.html +0 -88
  91. package/coverage/test-utils/docs/scripts/third-party/tocbot.js.html +0 -2098
  92. package/coverage/test-utils/docs/scripts/third-party/tocbot.min.js.html +0 -85
  93. package/coverage/test-utils/index.html +0 -131
  94. package/coverage/test-utils/src/arrayUtils.js.html +0 -283
  95. package/coverage/test-utils/src/domUtils.js.html +0 -622
  96. package/coverage/test-utils/src/getAccessibleName.js.html +0 -1444
  97. package/coverage/test-utils/src/getAccessibleText.js.html +0 -271
  98. package/coverage/test-utils/src/getAriaAttributesByElement.js.html +0 -142
  99. package/coverage/test-utils/src/getCSSGeneratedContent.js.html +0 -265
  100. package/coverage/test-utils/src/getComputedRole.js.html +0 -592
  101. package/coverage/test-utils/src/getFocusableElements.js.html +0 -163
  102. package/coverage/test-utils/src/getGeneratedContent.js.html +0 -130
  103. package/coverage/test-utils/src/getImageText.js.html +0 -160
  104. package/coverage/test-utils/src/getStyleObject.js.html +0 -220
  105. package/coverage/test-utils/src/hasAccessibleName.js.html +0 -166
  106. package/coverage/test-utils/src/hasAttribute.js.html +0 -130
  107. package/coverage/test-utils/src/hasCSSGeneratedContent.js.html +0 -145
  108. package/coverage/test-utils/src/hasHiddenParent.js.html +0 -172
  109. package/coverage/test-utils/src/hasParent.js.html +0 -247
  110. package/coverage/test-utils/src/hasValidAriaAttributes.js.html +0 -175
  111. package/coverage/test-utils/src/hasValidAriaRole.js.html +0 -172
  112. package/coverage/test-utils/src/index.html +0 -611
  113. package/coverage/test-utils/src/index.js.html +0 -274
  114. package/coverage/test-utils/src/interactiveRoles.js.html +0 -145
  115. package/coverage/test-utils/src/isAriaAttributesValid.js.html +0 -304
  116. package/coverage/test-utils/src/isComplexTable.js.html +0 -412
  117. package/coverage/test-utils/src/isDataTable.js.html +0 -799
  118. package/coverage/test-utils/src/isFocusable.js.html +0 -187
  119. package/coverage/test-utils/src/isHidden.js.html +0 -136
  120. package/coverage/test-utils/src/isOffScreen.js.html +0 -133
  121. package/coverage/test-utils/src/isValidUrl.js.html +0 -124
  122. package/coverage/test-utils/src/isVisible.js.html +0 -271
  123. package/coverage/test-utils/src/listEventListeners.js.html +0 -370
  124. package/coverage/test-utils/src/queryCache.js.html +0 -1156
  125. package/coverage/test-utils/src/stringUtils.js.html +0 -535
  126. package/coverage/test-utils/src/testContrast.js.html +0 -784
  127. package/coverage/test-utils/src/testLang.js.html +0 -1810
  128. package/coverage/test-utils/src/testOrder.js.html +0 -355
  129. package/coverage/test-utils/vitest.config.browser.js.html +0 -133
  130. package/coverage/test-utils/vitest.config.js.html +0 -157
  131. package/test/browser-setup.js +0 -68
  132. package/vitest.config.browser.js +0 -17
@@ -1,94 +1,365 @@
1
- import { describe, it, expect, beforeEach, vi } from 'vitest';
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
  import { getAccessibleText } from '../src/getAccessibleText.js';
3
3
 
4
- // Mock the isEmpty function since it's not imported
5
- const originalGetAccessibleText = getAccessibleText;
6
- vi.mock('../src/getAccessibleText.js', () => ({
7
- getAccessibleText: (el) => {
8
- if (!el) return "";
9
-
10
- let text = [];
11
-
12
- // Get text from aria-label attributes
13
- const elementsWithAriaLabel = el.querySelectorAll('[aria-label]');
14
- elementsWithAriaLabel.forEach(element => {
15
- text.push(element.getAttribute('aria-label'));
16
- });
17
-
18
- // Get text from alt attributes on img elements
19
- const imgElements = el.querySelectorAll('img[alt]');
20
- imgElements.forEach(img => {
21
- text.push(img.getAttribute('alt').trim());
22
- });
23
-
24
- // Also check the element itself for aria-label or alt
25
- if (el.hasAttribute && el.hasAttribute('aria-label')) {
26
- text.push(el.getAttribute('aria-label'));
27
- } else if (el.tagName && el.tagName.toLowerCase() === 'img' && el.hasAttribute('alt')) {
28
- text.push(el.getAttribute('alt').trim());
29
- }
30
-
31
- return text.join(' ').trim();
32
- }
33
- }));
34
-
35
4
  describe('getAccessibleText', () => {
36
5
  beforeEach(() => {
37
6
  document.body.innerHTML = '';
38
7
  });
39
8
 
40
- it('should return empty string when no element is provided', () => {
41
- expect(getAccessibleText(null)).toBe('');
42
- expect(getAccessibleText(undefined)).toBe('');
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('');
17
+ });
18
+
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
+ });
27
+
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
+ });
45
+
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
+ });
54
+
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
+ });
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
+ });
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
+ });
43
119
  });
44
120
 
45
- it('should get text from aria-label attribute', () => {
46
- // Arrange
47
- const div = document.createElement('div');
48
- div.setAttribute('aria-label', 'Accessible Label');
49
- document.body.appendChild(div);
50
-
51
- // Act
52
- const result = getAccessibleText(div);
53
-
54
- // Assert
55
- expect(result).toBe('Accessible Label');
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
+ <span>First part</span>
142
+ <span>Second part</span>
143
+ <span>Third part</span>
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
+ });
152
+
153
+ it('should handle nested elements', () => {
154
+ const container = document.createElement('div');
155
+ container.innerHTML = `
156
+ <div>
157
+ <span>Nested <strong>text</strong> content</span>
158
+ </div>
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
+ });
56
167
  });
57
168
 
58
- it('should get alt text from img elements', () => {
59
- // Arrange
60
- const parent = document.createElement('div');
61
- const img = document.createElement('img');
62
- img.setAttribute('alt', 'Image description');
63
- parent.appendChild(img);
64
- document.body.appendChild(parent);
65
-
66
- // Act
67
- const result = getAccessibleText(parent);
68
-
69
- // Assert
70
- expect(result).toBe('Image description');
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
+ <header>
214
+ <h1>Title</h1>
215
+ <nav>
216
+ <a href="#">Link 1</a>
217
+ <a href="#">Link 2</a>
218
+ </nav>
219
+ </header>
220
+ <main>
221
+ <p>Paragraph text</p>
222
+ <ul>
223
+ <li>Item 1</li>
224
+ <li>Item 2</li>
225
+ </ul>
226
+ </main>
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
+ });
71
238
  });
72
239
 
73
- it('should concatenate multiple text nodes', () => {
74
- // Arrange
75
- const container = document.createElement('div');
76
-
77
- const span1 = document.createElement('span');
78
- span1.setAttribute('aria-label', 'First label');
79
-
80
- const img = document.createElement('img');
81
- img.setAttribute('alt', 'Image alt');
82
-
83
- container.appendChild(span1);
84
- container.appendChild(img);
85
- document.body.appendChild(container);
86
-
87
- // Act
88
- const result = getAccessibleText(container);
89
-
90
- // Assert
91
- expect(result).toContain('First label');
92
- expect(result).toContain('Image alt');
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
+ });
252
+
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');
272
+ });
273
+
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
+ });
280
+
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
+ });
290
+
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
+ });
303
+
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
+ });
322
+
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');
342
+ });
343
+
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');
363
+ });
93
364
  });
94
365
  });
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
  import { getCSSGeneratedContent } from '../src/getCSSGeneratedContent';
3
3
 
4
4
  describe('getCSSGeneratedContent', () => {
@@ -41,7 +41,16 @@ describe('getCSSGeneratedContent', () => {
41
41
  const div = document.createElement('div');
42
42
  document.body.appendChild(div);
43
43
 
44
+ // Mock getComputedStyle for this test since JSDOM doesn't support it
45
+ const originalGetComputedStyle = window.getComputedStyle;
46
+ window.getComputedStyle = () => ({
47
+ getPropertyValue: () => 'none'
48
+ });
49
+
44
50
  expect(getCSSGeneratedContent(div)).toBe(false);
51
+
52
+ // Restore original
53
+ window.getComputedStyle = originalGetComputedStyle;
45
54
  });
46
55
 
47
56
  it('should get ::before content', () => {
@@ -89,7 +98,16 @@ describe('getCSSGeneratedContent', () => {
89
98
  div.className = 'empty-content';
90
99
  document.body.appendChild(div);
91
100
 
101
+ // Mock getComputedStyle to return empty quoted content
102
+ const originalGetComputedStyle = window.getComputedStyle;
103
+ window.getComputedStyle = () => ({
104
+ getPropertyValue: () => '""'
105
+ });
106
+
92
107
  expect(getCSSGeneratedContent(div)).toBe(false);
108
+
109
+ // Restore original
110
+ window.getComputedStyle = originalGetComputedStyle;
93
111
  });
94
112
 
95
113
  it('should return false for content: none', () => {
@@ -99,4 +117,160 @@ describe('getCSSGeneratedContent', () => {
99
117
 
100
118
  expect(getCSSGeneratedContent(div)).toBe(false);
101
119
  });
120
+
121
+ it('should handle url content', () => {
122
+ const div = document.createElement('div');
123
+ div.className = 'url-content';
124
+ document.body.appendChild(div);
125
+
126
+ expect(getCSSGeneratedContent(div, 'before')).toBe('url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")');
127
+ });
128
+
129
+ it('should handle undefined and non-string pseudoElement parameter', () => {
130
+ const div = document.createElement('div');
131
+ div.className = 'with-before';
132
+ document.body.appendChild(div);
133
+
134
+ // Should default to 'both' when no pseudoElement specified
135
+ expect(getCSSGeneratedContent(div, undefined)).toBe('Before Content');
136
+ });
137
+
138
+ describe('Browser implementation paths', () => {
139
+ let originalGetComputedStyle;
140
+
141
+ beforeEach(() => {
142
+ // Mock getComputedStyle to simulate real browser behavior
143
+ originalGetComputedStyle = window.getComputedStyle;
144
+ });
145
+
146
+ afterEach(() => {
147
+ // Restore original getComputedStyle
148
+ window.getComputedStyle = originalGetComputedStyle;
149
+ });
150
+
151
+ it('should handle quote removal in browser implementation', () => {
152
+ // Mock getComputedStyle to return quoted content
153
+ window.getComputedStyle = (el, pseudo) => ({
154
+ getPropertyValue: (prop) => {
155
+ if (prop === 'content' && pseudo === '::before') {
156
+ return '"Quoted Content"';
157
+ }
158
+ return 'none';
159
+ }
160
+ });
161
+
162
+ const div = document.createElement('div');
163
+ // Remove classList to bypass JSDOM check
164
+ Object.defineProperty(div, 'classList', { value: undefined });
165
+ document.body.appendChild(div);
166
+
167
+ expect(getCSSGeneratedContent(div, 'before')).toBe('Quoted Content');
168
+ });
169
+
170
+ it('should handle single quote removal in browser implementation', () => {
171
+ window.getComputedStyle = (el, pseudo) => ({
172
+ getPropertyValue: (prop) => {
173
+ if (prop === 'content' && pseudo === '::before') {
174
+ return "'Single Quoted'";
175
+ }
176
+ return 'none';
177
+ }
178
+ });
179
+
180
+ const div = document.createElement('div');
181
+ Object.defineProperty(div, 'classList', { value: undefined });
182
+ document.body.appendChild(div);
183
+
184
+ expect(getCSSGeneratedContent(div, 'before')).toBe('Single Quoted');
185
+ });
186
+
187
+ it('should handle both before and after content with quotes', () => {
188
+ window.getComputedStyle = (el, pseudo) => ({
189
+ getPropertyValue: (prop) => {
190
+ if (prop === 'content') {
191
+ if (pseudo === '::before') return '"Before"';
192
+ if (pseudo === '::after') return '"After"';
193
+ }
194
+ return 'none';
195
+ }
196
+ });
197
+
198
+ const div = document.createElement('div');
199
+ Object.defineProperty(div, 'classList', { value: undefined });
200
+ document.body.appendChild(div);
201
+
202
+ expect(getCSSGeneratedContent(div, 'both')).toBe('Before After');
203
+ });
204
+
205
+ it('should handle after content with quotes', () => {
206
+ window.getComputedStyle = (el, pseudo) => ({
207
+ getPropertyValue: (prop) => {
208
+ if (prop === 'content' && pseudo === '::after') {
209
+ return '"After Content"';
210
+ }
211
+ return 'none';
212
+ }
213
+ });
214
+
215
+ const div = document.createElement('div');
216
+ Object.defineProperty(div, 'classList', { value: undefined });
217
+ document.body.appendChild(div);
218
+
219
+ expect(getCSSGeneratedContent(div, 'after')).toBe('After Content');
220
+ });
221
+
222
+ it('should handle content: none in browser implementation', () => {
223
+ window.getComputedStyle = (el, pseudo) => ({
224
+ getPropertyValue: (prop) => 'none'
225
+ });
226
+
227
+ const div = document.createElement('div');
228
+ Object.defineProperty(div, 'classList', { value: undefined });
229
+ document.body.appendChild(div);
230
+
231
+ expect(getCSSGeneratedContent(div)).toBe(false);
232
+ });
233
+
234
+ it('should handle content: normal in browser implementation', () => {
235
+ window.getComputedStyle = (el, pseudo) => ({
236
+ getPropertyValue: (prop) => 'normal'
237
+ });
238
+
239
+ const div = document.createElement('div');
240
+ Object.defineProperty(div, 'classList', { value: undefined });
241
+ document.body.appendChild(div);
242
+
243
+ expect(getCSSGeneratedContent(div)).toBe(false);
244
+ });
245
+
246
+ it('should handle empty quoted content', () => {
247
+ window.getComputedStyle = (el, pseudo) => ({
248
+ getPropertyValue: (prop) => {
249
+ if (prop === 'content') {
250
+ if (pseudo === '::before') return '""';
251
+ }
252
+ return 'none';
253
+ }
254
+ });
255
+
256
+ const div = document.createElement('div');
257
+ Object.defineProperty(div, 'classList', { value: undefined });
258
+ document.body.appendChild(div);
259
+
260
+ expect(getCSSGeneratedContent(div, 'before')).toBe(false);
261
+ });
262
+
263
+ it('should handle getComputedStyle error', () => {
264
+ // Mock getComputedStyle to throw an error
265
+ window.getComputedStyle = () => {
266
+ throw new Error('getComputedStyle not supported');
267
+ };
268
+
269
+ const div = document.createElement('div');
270
+ Object.defineProperty(div, 'classList', { value: undefined });
271
+ document.body.appendChild(div);
272
+
273
+ expect(getCSSGeneratedContent(div)).toBe(false);
274
+ });
275
+ });
102
276
  });