@afixt/test-utils 1.1.8 → 1.2.1
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 +5 -1
- package/.github/workflows/pr-check.yml +88 -0
- package/.github/workflows/security.yml +0 -3
- package/eslint.config.mjs +1 -1
- package/package.json +2 -1
- package/src/constants.js +231 -0
- package/src/cssUtils.js +77 -0
- package/src/domUtils.js +268 -12
- package/src/formUtils.js +175 -0
- package/src/getCSSGeneratedContent.js +39 -17
- package/src/index.js +18 -2
- package/src/stringUtils.js +168 -21
- package/src/tableUtils.js +180 -0
- package/src/testContrast.js +137 -22
- package/src/testLang.js +514 -444
- package/test/cssUtils.test.js +248 -0
- package/test/domUtils.test.js +815 -297
- package/test/formUtils.test.js +389 -0
- package/test/getCSSGeneratedContent.test.js +187 -232
- package/test/hasCSSGeneratedContent.test.js +37 -147
- package/test/playwright/css-pseudo-elements.spec.js +224 -91
- package/test/playwright/fixtures/css-pseudo-elements.html +6 -0
- package/test/stringUtils.test.js +609 -343
- package/test/tableUtils.test.js +340 -0
- package/test/testContrast.test.js +801 -651
- package/vitest.config.js +28 -28
- package/.github/dependabot.yml +0 -36
- package/test/getCSSGeneratedContent.browser.test.js +0 -125
package/test/stringUtils.test.js
CHANGED
|
@@ -2,381 +2,647 @@ 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
|
-
|
|
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
|
-
});
|
|
257
|
+
p.textContent = 'Deep content';
|
|
258
|
+
article.appendChild(p);
|
|
259
|
+
section.appendChild(article);
|
|
260
|
+
div.appendChild(section);
|
|
116
261
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
expect(stringUtils.isAlphaNumeric('\t')).toBe(false); // tab
|
|
120
|
-
expect(stringUtils.isAlphaNumeric(' ')).toBe(false); // whitespace
|
|
121
|
-
});
|
|
122
|
-
});
|
|
262
|
+
expect(stringUtils.getAllText(div)).toBe('Deep content');
|
|
263
|
+
});
|
|
123
264
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
});
|
|
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
|
|
130
270
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
expect(stringUtils.getPathFromUrl('https://example.com:8080/path')).toBe('/path');
|
|
135
|
-
});
|
|
271
|
+
const text = document.createTextNode('Some text');
|
|
272
|
+
div.appendChild(text);
|
|
273
|
+
div.appendChild(img);
|
|
136
274
|
|
|
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
|
-
});
|
|
275
|
+
expect(stringUtils.getAllText(div)).toBe('Some text');
|
|
276
|
+
});
|
|
150
277
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
div.appendChild(span);
|
|
158
|
-
div.appendChild(text);
|
|
159
|
-
|
|
160
|
-
expect(stringUtils.getAllText(div)).toBe('Hello World');
|
|
161
|
-
});
|
|
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
|
|
162
283
|
|
|
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
|
-
});
|
|
284
|
+
div.appendChild(button);
|
|
175
285
|
|
|
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');
|
|
286
|
+
expect(stringUtils.getAllText(div)).toBe('Click me');
|
|
287
|
+
});
|
|
185
288
|
});
|
|
186
289
|
|
|
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
|
-
});
|
|
290
|
+
describe('hasText', () => {
|
|
291
|
+
it('should return true for elements with text content', () => {
|
|
292
|
+
const div = document.createElement('div');
|
|
293
|
+
div.textContent = 'Hello World';
|
|
216
294
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
expect(stringUtils.getAllText(div)).toBe('');
|
|
220
|
-
});
|
|
295
|
+
expect(stringUtils.hasText(div)).toBe(true);
|
|
296
|
+
});
|
|
221
297
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const textNode = document.createTextNode(' Hello World ');
|
|
225
|
-
div.appendChild(textNode);
|
|
226
|
-
|
|
227
|
-
expect(stringUtils.getAllText(div)).toBe('Hello World');
|
|
228
|
-
});
|
|
298
|
+
it('should return false for empty elements', () => {
|
|
299
|
+
const div = document.createElement('div');
|
|
229
300
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const textNode = document.createTextNode(' ');
|
|
233
|
-
div.appendChild(textNode);
|
|
234
|
-
|
|
235
|
-
// Should use textContent.trim() as fallback for empty text
|
|
236
|
-
expect(stringUtils.getAllText(div)).toBe('');
|
|
237
|
-
});
|
|
301
|
+
expect(stringUtils.hasText(div)).toBe(false);
|
|
302
|
+
});
|
|
238
303
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
});
|
|
304
|
+
it('should return false for elements with only whitespace', () => {
|
|
305
|
+
const div = document.createElement('div');
|
|
306
|
+
div.textContent = ' \n\t ';
|
|
252
307
|
|
|
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
|
-
});
|
|
308
|
+
expect(stringUtils.hasText(div)).toBe(false);
|
|
309
|
+
});
|
|
265
310
|
|
|
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
|
-
});
|
|
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');
|
|
285
315
|
|
|
286
|
-
|
|
287
|
-
const div = document.createElement('div');
|
|
288
|
-
|
|
289
|
-
expect(stringUtils.hasText(div)).toBe(false);
|
|
290
|
-
});
|
|
316
|
+
div.appendChild(button);
|
|
291
317
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
div.textContent = ' \n\t ';
|
|
295
|
-
|
|
296
|
-
expect(stringUtils.hasText(div)).toBe(false);
|
|
297
|
-
});
|
|
318
|
+
expect(stringUtils.hasText(div)).toBe(true);
|
|
319
|
+
});
|
|
298
320
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
div.appendChild(button);
|
|
305
|
-
|
|
306
|
-
expect(stringUtils.hasText(div)).toBe(true);
|
|
307
|
-
});
|
|
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');
|
|
308
325
|
|
|
309
|
-
|
|
310
|
-
const div = document.createElement('div');
|
|
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
|
-
});
|
|
326
|
+
div.appendChild(img);
|
|
318
327
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const span = document.createElement('span');
|
|
322
|
-
span.textContent = 'Nested text';
|
|
323
|
-
|
|
324
|
-
div.appendChild(span);
|
|
325
|
-
|
|
326
|
-
expect(stringUtils.hasText(div)).toBe(true);
|
|
327
|
-
});
|
|
328
|
+
expect(stringUtils.hasText(div)).toBe(true);
|
|
329
|
+
});
|
|
328
330
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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';
|
|
333
335
|
|
|
334
|
-
|
|
335
|
-
span.appendChild(p);
|
|
336
|
+
div.appendChild(span);
|
|
336
337
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
});
|
|
338
|
+
expect(stringUtils.hasText(div)).toBe(true);
|
|
339
|
+
});
|
|
340
340
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
div.appendChild(textNode);
|
|
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');
|
|
346
345
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
expect(typeof result).toBe('string');
|
|
350
|
-
});
|
|
346
|
+
div.appendChild(span);
|
|
347
|
+
span.appendChild(p);
|
|
351
348
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
div.innerHTML = ' <span>Text</span> ';
|
|
349
|
+
expect(stringUtils.hasText(div)).toBe(false);
|
|
350
|
+
});
|
|
355
351
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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';
|
|
359
356
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const button = document.createElement('button');
|
|
363
|
-
button.textContent = 'Visual Text';
|
|
364
|
-
button.setAttribute('aria-label', 'Accessible Label');
|
|
365
|
-
div.appendChild(button);
|
|
357
|
+
expect(stringUtils.hasText(input)).toBe(true);
|
|
358
|
+
});
|
|
366
359
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
360
|
+
it('should return false for input elements with empty value', () => {
|
|
361
|
+
const input = document.createElement('input');
|
|
362
|
+
input.type = 'text';
|
|
363
|
+
input.value = '';
|
|
371
364
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
365
|
+
expect(stringUtils.hasText(input)).toBe(false);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('should return true for textarea elements with a value', () => {
|
|
369
|
+
const textarea = document.createElement('textarea');
|
|
370
|
+
textarea.value = 'Some textarea text';
|
|
371
|
+
|
|
372
|
+
expect(stringUtils.hasText(textarea)).toBe(true);
|
|
373
|
+
});
|
|
377
374
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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);
|
|
380
|
+
|
|
381
|
+
expect(stringUtils.hasText(select)).toBe(true);
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
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);
|
|
390
|
+
|
|
391
|
+
// This tests the else branch where nodeValue.trim() is empty
|
|
392
|
+
const result = stringUtils.getAllText(div);
|
|
393
|
+
expect(typeof result).toBe('string');
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('should handle mixed content with whitespace text nodes', () => {
|
|
397
|
+
const div = document.createElement('div');
|
|
398
|
+
div.innerHTML = ' <span>Text</span> ';
|
|
399
|
+
|
|
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
|
+
});
|
|
415
|
+
|
|
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);
|
|
421
|
+
|
|
422
|
+
const result = stringUtils.getAllText(div);
|
|
423
|
+
expect(typeof result).toBe('string');
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
describe('isEmptyOrWhitespace', () => {
|
|
428
|
+
it('should return true for null/undefined/empty', () => {
|
|
429
|
+
expect(stringUtils.isEmptyOrWhitespace(null)).toBe(true);
|
|
430
|
+
expect(stringUtils.isEmptyOrWhitespace(undefined)).toBe(true);
|
|
431
|
+
expect(stringUtils.isEmptyOrWhitespace('')).toBe(true);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('should return true for whitespace-only strings', () => {
|
|
435
|
+
expect(stringUtils.isEmptyOrWhitespace(' ')).toBe(true);
|
|
436
|
+
expect(stringUtils.isEmptyOrWhitespace('\t\n')).toBe(true);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('should return true for strings with zero-width spaces', () => {
|
|
440
|
+
expect(stringUtils.isEmptyOrWhitespace('\u200B')).toBe(true);
|
|
441
|
+
expect(stringUtils.isEmptyOrWhitespace('\u200C')).toBe(true);
|
|
442
|
+
expect(stringUtils.isEmptyOrWhitespace('\u200D')).toBe(true);
|
|
443
|
+
expect(stringUtils.isEmptyOrWhitespace('\uFEFF')).toBe(true);
|
|
444
|
+
expect(stringUtils.isEmptyOrWhitespace('\u2060')).toBe(true);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it('should return true for non-breaking spaces only', () => {
|
|
448
|
+
expect(stringUtils.isEmptyOrWhitespace('\u00A0')).toBe(true);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('should return false for strings with visible characters', () => {
|
|
452
|
+
expect(stringUtils.isEmptyOrWhitespace('hello')).toBe(false);
|
|
453
|
+
expect(stringUtils.isEmptyOrWhitespace(' a ')).toBe(false);
|
|
454
|
+
expect(stringUtils.isEmptyOrWhitespace('\u200Bx')).toBe(false);
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
describe('isGenericTitle', () => {
|
|
459
|
+
it('should return true for known generic titles', () => {
|
|
460
|
+
expect(stringUtils.isGenericTitle('iframe')).toBe(true);
|
|
461
|
+
expect(stringUtils.isGenericTitle('frame')).toBe(true);
|
|
462
|
+
expect(stringUtils.isGenericTitle('untitled')).toBe(true);
|
|
463
|
+
expect(stringUtils.isGenericTitle('title')).toBe(true);
|
|
464
|
+
expect(stringUtils.isGenericTitle('content')).toBe(true);
|
|
465
|
+
expect(stringUtils.isGenericTitle('main')).toBe(true);
|
|
466
|
+
expect(stringUtils.isGenericTitle('page')).toBe(true);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it('should be case-insensitive', () => {
|
|
470
|
+
expect(stringUtils.isGenericTitle('IFRAME')).toBe(true);
|
|
471
|
+
expect(stringUtils.isGenericTitle('Untitled')).toBe(true);
|
|
472
|
+
expect(stringUtils.isGenericTitle('TITLE')).toBe(true);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('should match numbered variants', () => {
|
|
476
|
+
expect(stringUtils.isGenericTitle('frame1')).toBe(true);
|
|
477
|
+
expect(stringUtils.isGenericTitle('iframe2')).toBe(true);
|
|
478
|
+
expect(stringUtils.isGenericTitle('untitled3')).toBe(true);
|
|
479
|
+
expect(stringUtils.isGenericTitle('title42')).toBe(true);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it('should return false for descriptive titles', () => {
|
|
483
|
+
expect(stringUtils.isGenericTitle('Contact Form')).toBe(false);
|
|
484
|
+
expect(stringUtils.isGenericTitle('Product Details')).toBe(false);
|
|
485
|
+
expect(stringUtils.isGenericTitle('Navigation Menu')).toBe(false);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('should return false for null/undefined', () => {
|
|
489
|
+
expect(stringUtils.isGenericTitle(null)).toBe(false);
|
|
490
|
+
expect(stringUtils.isGenericTitle(undefined)).toBe(false);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('should trim whitespace', () => {
|
|
494
|
+
expect(stringUtils.isGenericTitle(' iframe ')).toBe(true);
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
describe('isGenericLinkText', () => {
|
|
499
|
+
it('should return true for common generic link text', () => {
|
|
500
|
+
expect(stringUtils.isGenericLinkText('click here')).toBe(true);
|
|
501
|
+
expect(stringUtils.isGenericLinkText('here')).toBe(true);
|
|
502
|
+
expect(stringUtils.isGenericLinkText('more')).toBe(true);
|
|
503
|
+
expect(stringUtils.isGenericLinkText('read more')).toBe(true);
|
|
504
|
+
expect(stringUtils.isGenericLinkText('learn more')).toBe(true);
|
|
505
|
+
expect(stringUtils.isGenericLinkText('link')).toBe(true);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it('should be case-insensitive', () => {
|
|
509
|
+
expect(stringUtils.isGenericLinkText('Click Here')).toBe(true);
|
|
510
|
+
expect(stringUtils.isGenericLinkText('READ MORE')).toBe(true);
|
|
511
|
+
expect(stringUtils.isGenericLinkText('LEARN MORE')).toBe(true);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('should return false for descriptive text', () => {
|
|
515
|
+
expect(stringUtils.isGenericLinkText('View product details')).toBe(false);
|
|
516
|
+
expect(stringUtils.isGenericLinkText('Download annual report')).toBe(false);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('should return false for null/undefined', () => {
|
|
520
|
+
expect(stringUtils.isGenericLinkText(null)).toBe(false);
|
|
521
|
+
expect(stringUtils.isGenericLinkText(undefined)).toBe(false);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it('should support custom generic list', () => {
|
|
525
|
+
const custom = ['foo', 'bar'];
|
|
526
|
+
expect(stringUtils.isGenericLinkText('foo', custom)).toBe(true);
|
|
527
|
+
expect(stringUtils.isGenericLinkText('bar', custom)).toBe(true);
|
|
528
|
+
expect(stringUtils.isGenericLinkText('click here', custom)).toBe(false);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('should trim whitespace', () => {
|
|
532
|
+
expect(stringUtils.isGenericLinkText(' here ')).toBe(true);
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
describe('getActualVisibleText', () => {
|
|
537
|
+
it('should return empty string for null/undefined', () => {
|
|
538
|
+
expect(stringUtils.getActualVisibleText(null)).toBe('');
|
|
539
|
+
expect(stringUtils.getActualVisibleText(undefined)).toBe('');
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('should return text content of element', () => {
|
|
543
|
+
const el = document.createElement('span');
|
|
544
|
+
el.textContent = 'Hello World';
|
|
545
|
+
expect(stringUtils.getActualVisibleText(el)).toBe('Hello World');
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it('should trim whitespace', () => {
|
|
549
|
+
const el = document.createElement('span');
|
|
550
|
+
el.textContent = ' Hello ';
|
|
551
|
+
expect(stringUtils.getActualVisibleText(el)).toBe('Hello');
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
it('should return empty string for empty element', () => {
|
|
555
|
+
const el = document.createElement('div');
|
|
556
|
+
expect(stringUtils.getActualVisibleText(el)).toBe('');
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
it('should include nested text content', () => {
|
|
560
|
+
const el = document.createElement('div');
|
|
561
|
+
el.innerHTML = '<span>Nested</span> text';
|
|
562
|
+
expect(stringUtils.getActualVisibleText(el)).toBe('Nested text');
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
describe('hasNewWindowWarning', () => {
|
|
567
|
+
it('should return true for text containing "new window"', () => {
|
|
568
|
+
expect(stringUtils.hasNewWindowWarning('Opens in a new window')).toBe(true);
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it('should return true for text containing "new tab"', () => {
|
|
572
|
+
expect(stringUtils.hasNewWindowWarning('Opens in new tab')).toBe(true);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it('should return true for "opens in new" variant', () => {
|
|
576
|
+
expect(stringUtils.hasNewWindowWarning('Link opens in new browser window')).toBe(true);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it('should return true for "external link"', () => {
|
|
580
|
+
expect(stringUtils.hasNewWindowWarning('External link to resource')).toBe(true);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it('should return true for "external site"', () => {
|
|
584
|
+
expect(stringUtils.hasNewWindowWarning('Goes to external site')).toBe(true);
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it('should be case-insensitive', () => {
|
|
588
|
+
expect(stringUtils.hasNewWindowWarning('Opens In A New Window')).toBe(true);
|
|
589
|
+
expect(stringUtils.hasNewWindowWarning('EXTERNAL LINK')).toBe(true);
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it('should return false for text without warning', () => {
|
|
593
|
+
expect(stringUtils.hasNewWindowWarning('Contact us')).toBe(false);
|
|
594
|
+
expect(stringUtils.hasNewWindowWarning('Read the article')).toBe(false);
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it('should return false for null/undefined', () => {
|
|
598
|
+
expect(stringUtils.hasNewWindowWarning(null)).toBe(false);
|
|
599
|
+
expect(stringUtils.hasNewWindowWarning(undefined)).toBe(false);
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
describe('textIncludingImgAlt', () => {
|
|
604
|
+
it('should return text content from text nodes', () => {
|
|
605
|
+
const el = document.createElement('div');
|
|
606
|
+
el.textContent = 'Hello World';
|
|
607
|
+
expect(stringUtils.textIncludingImgAlt(el).trim()).toBe('Hello World');
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it('should include img alt text', () => {
|
|
611
|
+
const el = document.createElement('div');
|
|
612
|
+
el.innerHTML = 'Text <img alt="photo"> more text';
|
|
613
|
+
const result = stringUtils.textIncludingImgAlt(el);
|
|
614
|
+
expect(result).toContain('Text');
|
|
615
|
+
expect(result).toContain('photo');
|
|
616
|
+
expect(result).toContain('more text');
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it('should handle img without alt', () => {
|
|
620
|
+
const el = document.createElement('div');
|
|
621
|
+
el.innerHTML = 'Text <img src="img.png"> more';
|
|
622
|
+
const result = stringUtils.textIncludingImgAlt(el);
|
|
623
|
+
expect(result).toContain('Text');
|
|
624
|
+
expect(result).toContain('more');
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
it('should not include aria-label text', () => {
|
|
628
|
+
const el = document.createElement('div');
|
|
629
|
+
el.innerHTML = '<button aria-label="Close">X</button>';
|
|
630
|
+
const result = stringUtils.textIncludingImgAlt(el);
|
|
631
|
+
expect(result).toContain('X');
|
|
632
|
+
expect(result).not.toContain('Close');
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('should handle empty element', () => {
|
|
636
|
+
const el = document.createElement('div');
|
|
637
|
+
expect(stringUtils.textIncludingImgAlt(el)).toBe('');
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it('should concatenate text from nested elements', () => {
|
|
641
|
+
const el = document.createElement('div');
|
|
642
|
+
el.innerHTML = '<span>First</span> <span>Second</span>';
|
|
643
|
+
const result = stringUtils.textIncludingImgAlt(el);
|
|
644
|
+
expect(result).toContain('First');
|
|
645
|
+
expect(result).toContain('Second');
|
|
646
|
+
});
|
|
647
|
+
});
|
|
648
|
+
});
|