@afixt/test-utils 1.1.7 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +6 -1
- package/.github/workflows/ci.yml +71 -0
- package/.github/workflows/pr-check.yml +88 -0
- package/.github/workflows/security.yml +139 -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/src/stringUtils.js +19 -21
- package/src/testContrast.js +103 -22
- package/test/getAccessibleName.test.js +379 -315
- package/test/getAccessibleText.test.js +375 -308
- package/test/stringUtils.test.js +376 -332
- package/test/testContrast.test.js +801 -651
- package/.eslintrc +0 -78
- package/.github/workflows/test.yml +0 -26
package/test/stringUtils.test.js
CHANGED
|
@@ -2,381 +2,425 @@ import { describe, it, expect } from 'vitest';
|
|
|
2
2
|
import stringUtils from '../src/stringUtils.js';
|
|
3
3
|
|
|
4
4
|
describe('stringUtils', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
describe('isEmpty', () => {
|
|
6
|
+
it('should return true for empty strings', () => {
|
|
7
|
+
expect(stringUtils.isEmpty('')).toBe(true);
|
|
8
|
+
expect(stringUtils.isEmpty(' ')).toBe(true);
|
|
9
|
+
expect(stringUtils.isEmpty('\n\t')).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should return true for null or undefined', () => {
|
|
13
|
+
expect(stringUtils.isEmpty(null)).toBe(true);
|
|
14
|
+
expect(stringUtils.isEmpty(undefined)).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return false for non-empty strings', () => {
|
|
18
|
+
expect(stringUtils.isEmpty('hello')).toBe(false);
|
|
19
|
+
expect(stringUtils.isEmpty(' hello ')).toBe(false);
|
|
20
|
+
expect(stringUtils.isEmpty('0')).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('isString', () => {
|
|
25
|
+
it('should return true for string literals', () => {
|
|
26
|
+
expect(stringUtils.isString('')).toBe(true);
|
|
27
|
+
expect(stringUtils.isString('hello')).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return true for String objects', () => {
|
|
31
|
+
expect(stringUtils.isString(new String('hello'))).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should return false for non-strings', () => {
|
|
35
|
+
expect(stringUtils.isString(123)).toBe(false);
|
|
36
|
+
expect(stringUtils.isString({})).toBe(false);
|
|
37
|
+
expect(stringUtils.isString([])).toBe(false);
|
|
38
|
+
expect(stringUtils.isString(null)).toBe(false);
|
|
39
|
+
expect(stringUtils.isString(undefined)).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('strlen', () => {
|
|
44
|
+
it('should return correct length for trimmed strings', () => {
|
|
45
|
+
expect(stringUtils.strlen('hello')).toBe(5);
|
|
46
|
+
expect(stringUtils.strlen(' hello ')).toBe(5);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should return 0 for empty strings', () => {
|
|
50
|
+
expect(stringUtils.strlen('')).toBe(0);
|
|
51
|
+
expect(stringUtils.strlen(' ')).toBe(0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should return 0 for non-strings', () => {
|
|
55
|
+
expect(stringUtils.strlen(null)).toBe(0);
|
|
56
|
+
expect(stringUtils.strlen(undefined)).toBe(0);
|
|
57
|
+
expect(stringUtils.strlen(123)).toBe(0);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('isNormalInteger', () => {
|
|
62
|
+
it('should return true for valid integer strings', () => {
|
|
63
|
+
expect(stringUtils.isNormalInteger('0')).toBe(true);
|
|
64
|
+
expect(stringUtils.isNormalInteger('1')).toBe(true);
|
|
65
|
+
expect(stringUtils.isNormalInteger('123')).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should return false for non-integer strings', () => {
|
|
69
|
+
expect(stringUtils.isNormalInteger('1.5')).toBe(false);
|
|
70
|
+
expect(stringUtils.isNormalInteger('-1')).toBe(false);
|
|
71
|
+
expect(stringUtils.isNormalInteger('abc')).toBe(false);
|
|
72
|
+
expect(stringUtils.isNormalInteger('')).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('isUpperCase', () => {
|
|
77
|
+
it('should return true for uppercase strings', () => {
|
|
78
|
+
expect(stringUtils.isUpperCase('HELLO')).toBe(true);
|
|
79
|
+
expect(stringUtils.isUpperCase('HELLO WORLD')).toBe(true);
|
|
80
|
+
expect(stringUtils.isUpperCase('HELLO123')).toBe(true);
|
|
81
|
+
expect(stringUtils.isUpperCase('HELLO!')).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should return false for strings with lowercase characters', () => {
|
|
85
|
+
expect(stringUtils.isUpperCase('Hello')).toBe(false);
|
|
86
|
+
expect(stringUtils.isUpperCase('HELLO world')).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should handle edge cases', () => {
|
|
90
|
+
expect(stringUtils.isUpperCase('')).toBe(true); // Empty string has no lowercase letters
|
|
91
|
+
expect(stringUtils.isUpperCase('123')).toBe(true); // Numbers only
|
|
92
|
+
expect(stringUtils.isUpperCase('!@#')).toBe(true); // Symbols only
|
|
93
|
+
expect(stringUtils.isUpperCase(undefined)).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('isAlphaNumeric', () => {
|
|
98
|
+
it('should return true for strings containing only alphanumeric characters', () => {
|
|
99
|
+
expect(stringUtils.isAlphaNumeric('abc123')).toBe(true);
|
|
100
|
+
expect(stringUtils.isAlphaNumeric('ABC')).toBe(true);
|
|
101
|
+
expect(stringUtils.isAlphaNumeric('123')).toBe(true);
|
|
102
|
+
expect(stringUtils.isAlphaNumeric('Test123')).toBe(true);
|
|
103
|
+
expect(stringUtils.isAlphaNumeric('a')).toBe(true);
|
|
104
|
+
expect(stringUtils.isAlphaNumeric('1')).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should return false for strings containing non-alphanumeric characters', () => {
|
|
108
|
+
expect(stringUtils.isAlphaNumeric('abc 123')).toBe(false); // space
|
|
109
|
+
expect(stringUtils.isAlphaNumeric('abc-123')).toBe(false); // hyphen
|
|
110
|
+
expect(stringUtils.isAlphaNumeric('abc_123')).toBe(false); // underscore
|
|
111
|
+
expect(stringUtils.isAlphaNumeric('abc@123')).toBe(false); // special character
|
|
112
|
+
expect(stringUtils.isAlphaNumeric('abc.123')).toBe(false); // period
|
|
113
|
+
expect(stringUtils.isAlphaNumeric('abc!123')).toBe(false); // exclamation
|
|
114
|
+
expect(stringUtils.isAlphaNumeric('')).toBe(false); // empty string
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should return false for special edge cases', () => {
|
|
118
|
+
expect(stringUtils.isAlphaNumeric('\n')).toBe(false); // newline
|
|
119
|
+
expect(stringUtils.isAlphaNumeric('\t')).toBe(false); // tab
|
|
120
|
+
expect(stringUtils.isAlphaNumeric(' ')).toBe(false); // whitespace
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('getPathFromUrl', () => {
|
|
125
|
+
it('should extract the pathname correctly', () => {
|
|
126
|
+
expect(stringUtils.getPathFromUrl('https://example.com/path/to/resource')).toBe(
|
|
127
|
+
'/path/to/resource'
|
|
128
|
+
);
|
|
129
|
+
expect(stringUtils.getPathFromUrl('https://example.com/')).toBe('/');
|
|
130
|
+
expect(stringUtils.getPathFromUrl('https://example.com/path?query=string')).toBe(
|
|
131
|
+
'/path'
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should handle complex URLs', () => {
|
|
136
|
+
expect(
|
|
137
|
+
stringUtils.getPathFromUrl('https://subdomain.example.com/path/to/resource')
|
|
138
|
+
).toBe('/path/to/resource');
|
|
139
|
+
expect(stringUtils.getPathFromUrl('http://example.com/path/to/resource#hash')).toBe(
|
|
140
|
+
'/path/to/resource'
|
|
141
|
+
);
|
|
142
|
+
expect(stringUtils.getPathFromUrl('https://example.com:8080/path')).toBe('/path');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should handle URLs with encoded characters', () => {
|
|
146
|
+
expect(stringUtils.getPathFromUrl('https://example.com/path%20with%20spaces')).toBe(
|
|
147
|
+
'/path%20with%20spaces'
|
|
148
|
+
);
|
|
149
|
+
expect(
|
|
150
|
+
stringUtils.getPathFromUrl('https://example.com/path/with/special%20chars')
|
|
151
|
+
).toBe('/path/with/special%20chars');
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('getAllText', () => {
|
|
156
|
+
it('should extract text from simple text nodes', () => {
|
|
157
|
+
const div = document.createElement('div');
|
|
158
|
+
div.textContent = 'Hello World';
|
|
159
|
+
|
|
160
|
+
expect(stringUtils.getAllText(div)).toBe('Hello World');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should extract text from nested elements', () => {
|
|
164
|
+
const div = document.createElement('div');
|
|
165
|
+
const span = document.createElement('span');
|
|
166
|
+
span.textContent = 'Hello';
|
|
167
|
+
const text = document.createTextNode(' World');
|
|
168
|
+
|
|
169
|
+
div.appendChild(span);
|
|
170
|
+
div.appendChild(text);
|
|
171
|
+
|
|
172
|
+
expect(stringUtils.getAllText(div)).toBe('Hello World');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should extract aria-label attributes', () => {
|
|
176
|
+
const div = document.createElement('div');
|
|
177
|
+
const button = document.createElement('button');
|
|
178
|
+
button.setAttribute('aria-label', 'Close dialog');
|
|
179
|
+
button.textContent = 'X';
|
|
180
|
+
|
|
181
|
+
div.appendChild(button);
|
|
182
|
+
|
|
183
|
+
const result = stringUtils.getAllText(div);
|
|
184
|
+
expect(result).toContain('Close dialog');
|
|
185
|
+
expect(result).toContain('X');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should extract alt attributes from images', () => {
|
|
189
|
+
const div = document.createElement('div');
|
|
190
|
+
const img = document.createElement('img');
|
|
191
|
+
img.setAttribute('alt', 'Profile picture');
|
|
192
|
+
img.setAttribute('src', 'profile.jpg');
|
|
193
|
+
|
|
194
|
+
div.appendChild(img);
|
|
195
|
+
|
|
196
|
+
expect(stringUtils.getAllText(div)).toBe('Profile picture');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should handle mixed content with text, aria-labels, and alt text', () => {
|
|
200
|
+
const div = document.createElement('div');
|
|
201
|
+
|
|
202
|
+
// Add some text
|
|
203
|
+
const textNode = document.createTextNode('Welcome ');
|
|
204
|
+
div.appendChild(textNode);
|
|
205
|
+
|
|
206
|
+
// Add element with aria-label
|
|
207
|
+
const button = document.createElement('button');
|
|
208
|
+
button.setAttribute('aria-label', 'Help');
|
|
209
|
+
button.textContent = '?';
|
|
210
|
+
div.appendChild(button);
|
|
211
|
+
|
|
212
|
+
// Add some more text
|
|
213
|
+
const moreText = document.createTextNode(' to our site ');
|
|
214
|
+
div.appendChild(moreText);
|
|
215
|
+
|
|
216
|
+
// Add image with alt text
|
|
217
|
+
const img = document.createElement('img');
|
|
218
|
+
img.setAttribute('alt', 'Company logo');
|
|
219
|
+
div.appendChild(img);
|
|
220
|
+
|
|
221
|
+
const result = stringUtils.getAllText(div);
|
|
222
|
+
expect(result).toContain('Welcome');
|
|
223
|
+
expect(result).toContain('Help');
|
|
224
|
+
expect(result).toContain('?');
|
|
225
|
+
expect(result).toContain('to our site');
|
|
226
|
+
expect(result).toContain('Company logo');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should handle empty elements', () => {
|
|
230
|
+
const div = document.createElement('div');
|
|
231
|
+
expect(stringUtils.getAllText(div)).toBe('');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should trim whitespace from text nodes', () => {
|
|
235
|
+
const div = document.createElement('div');
|
|
236
|
+
const textNode = document.createTextNode(' Hello World ');
|
|
237
|
+
div.appendChild(textNode);
|
|
238
|
+
|
|
239
|
+
expect(stringUtils.getAllText(div)).toBe('Hello World');
|
|
240
|
+
});
|
|
11
241
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
242
|
+
it('should handle elements with only whitespace', () => {
|
|
243
|
+
const div = document.createElement('div');
|
|
244
|
+
const textNode = document.createTextNode(' ');
|
|
245
|
+
div.appendChild(textNode);
|
|
16
246
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
expect(stringUtils.isEmpty('0')).toBe(false);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
247
|
+
// Should use textContent.trim() as fallback for empty text
|
|
248
|
+
expect(stringUtils.getAllText(div)).toBe('');
|
|
249
|
+
});
|
|
23
250
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
251
|
+
it('should handle deeply nested structures', () => {
|
|
252
|
+
const div = document.createElement('div');
|
|
253
|
+
const section = document.createElement('section');
|
|
254
|
+
const article = document.createElement('article');
|
|
255
|
+
const p = document.createElement('p');
|
|
29
256
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
257
|
+
p.textContent = 'Deep content';
|
|
258
|
+
article.appendChild(p);
|
|
259
|
+
section.appendChild(article);
|
|
260
|
+
div.appendChild(section);
|
|
33
261
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
expect(stringUtils.isString({})).toBe(false);
|
|
37
|
-
expect(stringUtils.isString([])).toBe(false);
|
|
38
|
-
expect(stringUtils.isString(null)).toBe(false);
|
|
39
|
-
expect(stringUtils.isString(undefined)).toBe(false);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
262
|
+
expect(stringUtils.getAllText(div)).toBe('Deep content');
|
|
263
|
+
});
|
|
42
264
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
265
|
+
it('should handle images without alt attributes', () => {
|
|
266
|
+
const div = document.createElement('div');
|
|
267
|
+
const img = document.createElement('img');
|
|
268
|
+
img.setAttribute('src', 'image.jpg');
|
|
269
|
+
// No alt attribute
|
|
48
270
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
});
|
|
271
|
+
const text = document.createTextNode('Some text');
|
|
272
|
+
div.appendChild(text);
|
|
273
|
+
div.appendChild(img);
|
|
53
274
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
expect(stringUtils.strlen(undefined)).toBe(0);
|
|
57
|
-
expect(stringUtils.strlen(123)).toBe(0);
|
|
58
|
-
});
|
|
59
|
-
});
|
|
275
|
+
expect(stringUtils.getAllText(div)).toBe('Some text');
|
|
276
|
+
});
|
|
60
277
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
});
|
|
278
|
+
it('should handle elements without aria-label', () => {
|
|
279
|
+
const div = document.createElement('div');
|
|
280
|
+
const button = document.createElement('button');
|
|
281
|
+
button.textContent = 'Click me';
|
|
282
|
+
// No aria-label attribute
|
|
67
283
|
|
|
68
|
-
|
|
69
|
-
expect(stringUtils.isNormalInteger('1.5')).toBe(false);
|
|
70
|
-
expect(stringUtils.isNormalInteger('-1')).toBe(false);
|
|
71
|
-
expect(stringUtils.isNormalInteger('abc')).toBe(false);
|
|
72
|
-
expect(stringUtils.isNormalInteger('')).toBe(false);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe('isUpperCase', () => {
|
|
77
|
-
it('should return true for uppercase strings', () => {
|
|
78
|
-
expect(stringUtils.isUpperCase('HELLO')).toBe(true);
|
|
79
|
-
expect(stringUtils.isUpperCase('HELLO WORLD')).toBe(true);
|
|
80
|
-
expect(stringUtils.isUpperCase('HELLO123')).toBe(true);
|
|
81
|
-
expect(stringUtils.isUpperCase('HELLO!')).toBe(true);
|
|
82
|
-
});
|
|
284
|
+
div.appendChild(button);
|
|
83
285
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
expect(stringUtils.isUpperCase('HELLO world')).toBe(false);
|
|
286
|
+
expect(stringUtils.getAllText(div)).toBe('Click me');
|
|
287
|
+
});
|
|
87
288
|
});
|
|
88
289
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
expect(stringUtils.isUpperCase(undefined)).toBe(false);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe('isAlphaNumeric', () => {
|
|
98
|
-
it('should return true for strings containing only alphanumeric characters', () => {
|
|
99
|
-
expect(stringUtils.isAlphaNumeric('abc123')).toBe(true);
|
|
100
|
-
expect(stringUtils.isAlphaNumeric('ABC')).toBe(true);
|
|
101
|
-
expect(stringUtils.isAlphaNumeric('123')).toBe(true);
|
|
102
|
-
expect(stringUtils.isAlphaNumeric('Test123')).toBe(true);
|
|
103
|
-
expect(stringUtils.isAlphaNumeric('a')).toBe(true);
|
|
104
|
-
expect(stringUtils.isAlphaNumeric('1')).toBe(true);
|
|
105
|
-
});
|
|
290
|
+
describe('hasText', () => {
|
|
291
|
+
it('should return true for elements with text content', () => {
|
|
292
|
+
const div = document.createElement('div');
|
|
293
|
+
div.textContent = 'Hello World';
|
|
106
294
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
expect(stringUtils.isAlphaNumeric('abc-123')).toBe(false); // hyphen
|
|
110
|
-
expect(stringUtils.isAlphaNumeric('abc_123')).toBe(false); // underscore
|
|
111
|
-
expect(stringUtils.isAlphaNumeric('abc@123')).toBe(false); // special character
|
|
112
|
-
expect(stringUtils.isAlphaNumeric('abc.123')).toBe(false); // period
|
|
113
|
-
expect(stringUtils.isAlphaNumeric('abc!123')).toBe(false); // exclamation
|
|
114
|
-
expect(stringUtils.isAlphaNumeric('')).toBe(false); // empty string
|
|
115
|
-
});
|
|
295
|
+
expect(stringUtils.hasText(div)).toBe(true);
|
|
296
|
+
});
|
|
116
297
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
expect(stringUtils.isAlphaNumeric('\t')).toBe(false); // tab
|
|
120
|
-
expect(stringUtils.isAlphaNumeric(' ')).toBe(false); // whitespace
|
|
121
|
-
});
|
|
122
|
-
});
|
|
298
|
+
it('should return false for empty elements', () => {
|
|
299
|
+
const div = document.createElement('div');
|
|
123
300
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
expect(stringUtils.getPathFromUrl('https://example.com/path/to/resource')).toBe('/path/to/resource');
|
|
127
|
-
expect(stringUtils.getPathFromUrl('https://example.com/')).toBe('/');
|
|
128
|
-
expect(stringUtils.getPathFromUrl('https://example.com/path?query=string')).toBe('/path');
|
|
129
|
-
});
|
|
301
|
+
expect(stringUtils.hasText(div)).toBe(false);
|
|
302
|
+
});
|
|
130
303
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
expect(stringUtils.getPathFromUrl('https://example.com:8080/path')).toBe('/path');
|
|
135
|
-
});
|
|
304
|
+
it('should return false for elements with only whitespace', () => {
|
|
305
|
+
const div = document.createElement('div');
|
|
306
|
+
div.textContent = ' \n\t ';
|
|
136
307
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
expect(stringUtils.getPathFromUrl('https://example.com/path/with/special%20chars')).toBe('/path/with/special%20chars');
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
describe('getAllText', () => {
|
|
144
|
-
it('should extract text from simple text nodes', () => {
|
|
145
|
-
const div = document.createElement('div');
|
|
146
|
-
div.textContent = 'Hello World';
|
|
147
|
-
|
|
148
|
-
expect(stringUtils.getAllText(div)).toBe('Hello World');
|
|
149
|
-
});
|
|
308
|
+
expect(stringUtils.hasText(div)).toBe(false);
|
|
309
|
+
});
|
|
150
310
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const text = document.createTextNode(' World');
|
|
156
|
-
|
|
157
|
-
div.appendChild(span);
|
|
158
|
-
div.appendChild(text);
|
|
159
|
-
|
|
160
|
-
expect(stringUtils.getAllText(div)).toBe('Hello World');
|
|
161
|
-
});
|
|
311
|
+
it('should return true for elements with aria-label', () => {
|
|
312
|
+
const div = document.createElement('div');
|
|
313
|
+
const button = document.createElement('button');
|
|
314
|
+
button.setAttribute('aria-label', 'Close');
|
|
162
315
|
|
|
163
|
-
|
|
164
|
-
const div = document.createElement('div');
|
|
165
|
-
const button = document.createElement('button');
|
|
166
|
-
button.setAttribute('aria-label', 'Close dialog');
|
|
167
|
-
button.textContent = 'X';
|
|
168
|
-
|
|
169
|
-
div.appendChild(button);
|
|
170
|
-
|
|
171
|
-
const result = stringUtils.getAllText(div);
|
|
172
|
-
expect(result).toContain('Close dialog');
|
|
173
|
-
expect(result).toContain('X');
|
|
174
|
-
});
|
|
316
|
+
div.appendChild(button);
|
|
175
317
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const img = document.createElement('img');
|
|
179
|
-
img.setAttribute('alt', 'Profile picture');
|
|
180
|
-
img.setAttribute('src', 'profile.jpg');
|
|
181
|
-
|
|
182
|
-
div.appendChild(img);
|
|
183
|
-
|
|
184
|
-
expect(stringUtils.getAllText(div)).toBe('Profile picture');
|
|
185
|
-
});
|
|
318
|
+
expect(stringUtils.hasText(div)).toBe(true);
|
|
319
|
+
});
|
|
186
320
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const textNode = document.createTextNode('Welcome ');
|
|
192
|
-
div.appendChild(textNode);
|
|
193
|
-
|
|
194
|
-
// Add element with aria-label
|
|
195
|
-
const button = document.createElement('button');
|
|
196
|
-
button.setAttribute('aria-label', 'Help');
|
|
197
|
-
button.textContent = '?';
|
|
198
|
-
div.appendChild(button);
|
|
199
|
-
|
|
200
|
-
// Add some more text
|
|
201
|
-
const moreText = document.createTextNode(' to our site ');
|
|
202
|
-
div.appendChild(moreText);
|
|
203
|
-
|
|
204
|
-
// Add image with alt text
|
|
205
|
-
const img = document.createElement('img');
|
|
206
|
-
img.setAttribute('alt', 'Company logo');
|
|
207
|
-
div.appendChild(img);
|
|
208
|
-
|
|
209
|
-
const result = stringUtils.getAllText(div);
|
|
210
|
-
expect(result).toContain('Welcome');
|
|
211
|
-
expect(result).toContain('Help');
|
|
212
|
-
expect(result).toContain('?');
|
|
213
|
-
expect(result).toContain('to our site');
|
|
214
|
-
expect(result).toContain('Company logo');
|
|
215
|
-
});
|
|
321
|
+
it('should return true for elements with image alt text', () => {
|
|
322
|
+
const div = document.createElement('div');
|
|
323
|
+
const img = document.createElement('img');
|
|
324
|
+
img.setAttribute('alt', 'Profile picture');
|
|
216
325
|
|
|
217
|
-
|
|
218
|
-
const div = document.createElement('div');
|
|
219
|
-
expect(stringUtils.getAllText(div)).toBe('');
|
|
220
|
-
});
|
|
326
|
+
div.appendChild(img);
|
|
221
327
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const textNode = document.createTextNode(' Hello World ');
|
|
225
|
-
div.appendChild(textNode);
|
|
226
|
-
|
|
227
|
-
expect(stringUtils.getAllText(div)).toBe('Hello World');
|
|
228
|
-
});
|
|
328
|
+
expect(stringUtils.hasText(div)).toBe(true);
|
|
329
|
+
});
|
|
229
330
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
// Should use textContent.trim() as fallback for empty text
|
|
236
|
-
expect(stringUtils.getAllText(div)).toBe('');
|
|
237
|
-
});
|
|
331
|
+
it('should return true for nested elements with text', () => {
|
|
332
|
+
const div = document.createElement('div');
|
|
333
|
+
const span = document.createElement('span');
|
|
334
|
+
span.textContent = 'Nested text';
|
|
238
335
|
|
|
239
|
-
|
|
240
|
-
const div = document.createElement('div');
|
|
241
|
-
const section = document.createElement('section');
|
|
242
|
-
const article = document.createElement('article');
|
|
243
|
-
const p = document.createElement('p');
|
|
244
|
-
|
|
245
|
-
p.textContent = 'Deep content';
|
|
246
|
-
article.appendChild(p);
|
|
247
|
-
section.appendChild(article);
|
|
248
|
-
div.appendChild(section);
|
|
249
|
-
|
|
250
|
-
expect(stringUtils.getAllText(div)).toBe('Deep content');
|
|
251
|
-
});
|
|
336
|
+
div.appendChild(span);
|
|
252
337
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const img = document.createElement('img');
|
|
256
|
-
img.setAttribute('src', 'image.jpg');
|
|
257
|
-
// No alt attribute
|
|
258
|
-
|
|
259
|
-
const text = document.createTextNode('Some text');
|
|
260
|
-
div.appendChild(text);
|
|
261
|
-
div.appendChild(img);
|
|
262
|
-
|
|
263
|
-
expect(stringUtils.getAllText(div)).toBe('Some text');
|
|
264
|
-
});
|
|
338
|
+
expect(stringUtils.hasText(div)).toBe(true);
|
|
339
|
+
});
|
|
265
340
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
// No aria-label attribute
|
|
271
|
-
|
|
272
|
-
div.appendChild(button);
|
|
273
|
-
|
|
274
|
-
expect(stringUtils.getAllText(div)).toBe('Click me');
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
describe('hasText', () => {
|
|
279
|
-
it('should return true for elements with text content', () => {
|
|
280
|
-
const div = document.createElement('div');
|
|
281
|
-
div.textContent = 'Hello World';
|
|
282
|
-
|
|
283
|
-
expect(stringUtils.hasText(div)).toBe(true);
|
|
284
|
-
});
|
|
341
|
+
it('should return false for elements with empty nested structure', () => {
|
|
342
|
+
const div = document.createElement('div');
|
|
343
|
+
const span = document.createElement('span');
|
|
344
|
+
const p = document.createElement('p');
|
|
285
345
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
expect(stringUtils.hasText(div)).toBe(false);
|
|
290
|
-
});
|
|
346
|
+
div.appendChild(span);
|
|
347
|
+
span.appendChild(p);
|
|
291
348
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
div.textContent = ' \n\t ';
|
|
295
|
-
|
|
296
|
-
expect(stringUtils.hasText(div)).toBe(false);
|
|
297
|
-
});
|
|
349
|
+
expect(stringUtils.hasText(div)).toBe(false);
|
|
350
|
+
});
|
|
298
351
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
div.appendChild(button);
|
|
305
|
-
|
|
306
|
-
expect(stringUtils.hasText(div)).toBe(true);
|
|
307
|
-
});
|
|
352
|
+
it('should return true for input elements with a value', () => {
|
|
353
|
+
const input = document.createElement('input');
|
|
354
|
+
input.type = 'text';
|
|
355
|
+
input.value = 'Some input text';
|
|
308
356
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
const img = document.createElement('img');
|
|
312
|
-
img.setAttribute('alt', 'Profile picture');
|
|
313
|
-
|
|
314
|
-
div.appendChild(img);
|
|
315
|
-
|
|
316
|
-
expect(stringUtils.hasText(div)).toBe(true);
|
|
317
|
-
});
|
|
357
|
+
expect(stringUtils.hasText(input)).toBe(true);
|
|
358
|
+
});
|
|
318
359
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
div.appendChild(span);
|
|
325
|
-
|
|
326
|
-
expect(stringUtils.hasText(div)).toBe(true);
|
|
327
|
-
});
|
|
360
|
+
it('should return false for input elements with empty value', () => {
|
|
361
|
+
const input = document.createElement('input');
|
|
362
|
+
input.type = 'text';
|
|
363
|
+
input.value = '';
|
|
328
364
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const span = document.createElement('span');
|
|
332
|
-
const p = document.createElement('p');
|
|
365
|
+
expect(stringUtils.hasText(input)).toBe(false);
|
|
366
|
+
});
|
|
333
367
|
|
|
334
|
-
|
|
335
|
-
|
|
368
|
+
it('should return true for textarea elements with a value', () => {
|
|
369
|
+
const textarea = document.createElement('textarea');
|
|
370
|
+
textarea.value = 'Some textarea text';
|
|
336
371
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
});
|
|
372
|
+
expect(stringUtils.hasText(textarea)).toBe(true);
|
|
373
|
+
});
|
|
340
374
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
375
|
+
it('should return true for select elements with selected option text', () => {
|
|
376
|
+
const select = document.createElement('select');
|
|
377
|
+
const option = document.createElement('option');
|
|
378
|
+
option.textContent = 'Option 1';
|
|
379
|
+
select.appendChild(option);
|
|
346
380
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
expect(typeof result).toBe('string');
|
|
381
|
+
expect(stringUtils.hasText(select)).toBe(true);
|
|
382
|
+
});
|
|
350
383
|
});
|
|
351
384
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
385
|
+
describe('getAllText edge cases', () => {
|
|
386
|
+
it('should handle text nodes with empty nodeValue but non-empty textContent', () => {
|
|
387
|
+
const div = document.createElement('div');
|
|
388
|
+
const textNode = document.createTextNode(' ');
|
|
389
|
+
div.appendChild(textNode);
|
|
355
390
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
391
|
+
// This tests the else branch where nodeValue.trim() is empty
|
|
392
|
+
const result = stringUtils.getAllText(div);
|
|
393
|
+
expect(typeof result).toBe('string');
|
|
394
|
+
});
|
|
359
395
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
button.textContent = 'Visual Text';
|
|
364
|
-
button.setAttribute('aria-label', 'Accessible Label');
|
|
365
|
-
div.appendChild(button);
|
|
396
|
+
it('should handle mixed content with whitespace text nodes', () => {
|
|
397
|
+
const div = document.createElement('div');
|
|
398
|
+
div.innerHTML = ' <span>Text</span> ';
|
|
366
399
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
400
|
+
const result = stringUtils.getAllText(div);
|
|
401
|
+
expect(result).toContain('Text');
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('should handle elements with both aria-label and text content', () => {
|
|
405
|
+
const div = document.createElement('div');
|
|
406
|
+
const button = document.createElement('button');
|
|
407
|
+
button.textContent = 'Visual Text';
|
|
408
|
+
button.setAttribute('aria-label', 'Accessible Label');
|
|
409
|
+
div.appendChild(button);
|
|
410
|
+
|
|
411
|
+
const result = stringUtils.getAllText(div);
|
|
412
|
+
expect(result).toContain('Accessible Label');
|
|
413
|
+
expect(result).toContain('Visual Text');
|
|
414
|
+
});
|
|
371
415
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
416
|
+
it('should handle img without alt attribute', () => {
|
|
417
|
+
const div = document.createElement('div');
|
|
418
|
+
const img = document.createElement('img');
|
|
419
|
+
// No alt attribute
|
|
420
|
+
div.appendChild(img);
|
|
377
421
|
|
|
378
|
-
|
|
379
|
-
|
|
422
|
+
const result = stringUtils.getAllText(div);
|
|
423
|
+
expect(typeof result).toBe('string');
|
|
424
|
+
});
|
|
380
425
|
});
|
|
381
|
-
|
|
382
|
-
});
|
|
426
|
+
});
|