@afixt/test-utils 1.0.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/.editorconfig +13 -0
- package/.eslintrc +78 -0
- package/.gitattributes +5 -0
- package/.nvmrc +1 -0
- package/CLAUDE.md +33 -0
- package/README.md +72 -0
- package/docs/arrayUtils.js.html +69 -0
- package/docs/data/search.json +1 -0
- package/docs/domUtils.js.html +182 -0
- package/docs/fonts/Inconsolata-Regular.ttf +0 -0
- package/docs/fonts/OpenSans-Regular.ttf +0 -0
- package/docs/fonts/WorkSans-Bold.ttf +0 -0
- package/docs/getAccessibleName.js.html +456 -0
- package/docs/getAccessibleText.js.html +65 -0
- package/docs/getAriaAttributesByElement.js.html +22 -0
- package/docs/getCSSGeneratedContent.js.html +62 -0
- package/docs/getComputedRole.js.html +172 -0
- package/docs/getFocusableElements.js.html +29 -0
- package/docs/getGeneratedContent.js.html +18 -0
- package/docs/getImageText.js.html +28 -0
- package/docs/getStyleObject.js.html +48 -0
- package/docs/global.html +3 -0
- package/docs/hasAccessibleName.js.html +30 -0
- package/docs/hasAttribute.js.html +18 -0
- package/docs/hasCSSGeneratedContent.js.html +23 -0
- package/docs/hasHiddenParent.js.html +32 -0
- package/docs/hasParent.js.html +57 -0
- package/docs/hasValidAriaAttributes.js.html +33 -0
- package/docs/hasValidAriaRole.js.html +32 -0
- package/docs/index.html +3 -0
- package/docs/index.js.html +66 -0
- package/docs/isAriaAttributesValid.js.html +76 -0
- package/docs/isComplexTable.js.html +112 -0
- package/docs/isDataTable.js.html +241 -0
- package/docs/isFocusable.js.html +37 -0
- package/docs/isHidden.js.html +20 -0
- package/docs/isOffScreen.js.html +19 -0
- package/docs/isValidUrl.js.html +16 -0
- package/docs/isVisible.js.html +65 -0
- package/docs/module-afixt-test-utils.html +3 -0
- package/docs/scripts/core.js +726 -0
- package/docs/scripts/core.min.js +23 -0
- package/docs/scripts/resize.js +90 -0
- package/docs/scripts/search.js +265 -0
- package/docs/scripts/search.min.js +6 -0
- package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
- package/docs/scripts/third-party/fuse.js +9 -0
- package/docs/scripts/third-party/hljs-line-num-original.js +369 -0
- package/docs/scripts/third-party/hljs-line-num.js +1 -0
- package/docs/scripts/third-party/hljs-original.js +5171 -0
- package/docs/scripts/third-party/hljs.js +1 -0
- package/docs/scripts/third-party/popper.js +5 -0
- package/docs/scripts/third-party/tippy.js +1 -0
- package/docs/scripts/third-party/tocbot.js +672 -0
- package/docs/scripts/third-party/tocbot.min.js +1 -0
- package/docs/styles/clean-jsdoc-theme-base.css +1159 -0
- package/docs/styles/clean-jsdoc-theme-dark.css +412 -0
- package/docs/styles/clean-jsdoc-theme-light.css +482 -0
- package/docs/styles/clean-jsdoc-theme-scrollbar.css +30 -0
- package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
- package/docs/styles/clean-jsdoc-theme.min.css +1 -0
- package/docs/testContrast.js.html +236 -0
- package/docs/testLang.js.html +578 -0
- package/docs/testOrder.js.html +93 -0
- package/jsdoc.json +67 -0
- package/package.json +32 -0
- package/src/arrayUtils.js +67 -0
- package/src/domUtils.js +179 -0
- package/src/getAccessibleName.js +454 -0
- package/src/getAccessibleText.js +63 -0
- package/src/getAriaAttributesByElement.js +19 -0
- package/src/getCSSGeneratedContent.js +60 -0
- package/src/getComputedRole.js +169 -0
- package/src/getFocusableElements.js +26 -0
- package/src/getGeneratedContent.js +15 -0
- package/src/getImageText.js +25 -0
- package/src/getStyleObject.js +45 -0
- package/src/hasAccessibleName.js +28 -0
- package/src/hasAttribute.js +15 -0
- package/src/hasCSSGeneratedContent.js +20 -0
- package/src/hasHiddenParent.js +29 -0
- package/src/hasParent.js +54 -0
- package/src/hasValidAriaAttributes.js +30 -0
- package/src/hasValidAriaRole.js +29 -0
- package/src/index.js +64 -0
- package/src/interactiveRoles.js +20 -0
- package/src/isAriaAttributesValid.js +74 -0
- package/src/isComplexTable.js +109 -0
- package/src/isDataTable.js +239 -0
- package/src/isFocusable.js +34 -0
- package/src/isHidden.js +17 -0
- package/src/isOffScreen.js +16 -0
- package/src/isValidUrl.js +13 -0
- package/src/isVisible.js +62 -0
- package/src/stringUtils.js +150 -0
- package/src/testContrast.js +233 -0
- package/src/testLang.js +575 -0
- package/src/testOrder.js +90 -0
- package/test/_template.test.js +21 -0
- package/test/arrayUtils.test.js +84 -0
- package/test/domUtils.test.js +147 -0
- package/test/generate-test-stubs.js +37 -0
- package/test/getAccessibleName.test.js +113 -0
- package/test/getAccessibleText.test.js +94 -0
- package/test/getAriaAttributesByElement.test.js +112 -0
- package/test/getCSSGeneratedContent.test.js +102 -0
- package/test/getComputedRole.test.js +180 -0
- package/test/getFocusableElements.test.js +134 -0
- package/test/getGeneratedContent.test.js +321 -0
- package/test/getImageText.test.js +21 -0
- package/test/getStyleObject.test.js +134 -0
- package/test/hasAccessibleName.test.js +59 -0
- package/test/hasAttribute.test.js +132 -0
- package/test/hasCSSGeneratedContent.test.js +143 -0
- package/test/hasHiddenParent.test.js +176 -0
- package/test/hasParent.test.js +266 -0
- package/test/hasValidAriaAttributes.test.js +79 -0
- package/test/hasValidAriaRole.test.js +98 -0
- package/test/isAriaAttributesValid.test.js +83 -0
- package/test/isComplexTable.test.js +363 -0
- package/test/isDataTable.test.js +948 -0
- package/test/isFocusable.test.js +182 -0
- package/test/isHidden.test.js +157 -0
- package/test/isOffScreen.test.js +249 -0
- package/test/isValidUrl.test.js +63 -0
- package/test/isVisible.test.js +104 -0
- package/test/setup.js +11 -0
- package/test/stringUtils.test.js +106 -0
- package/test/testContrast.test.js +77 -0
- package/test/testLang.test.js +21 -0
- package/test/testOrder.test.js +157 -0
- package/vitest.config.js +25 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { getComputedRole, roleMapping } from '../src/getComputedRole.js';
|
|
3
|
+
|
|
4
|
+
describe('getComputedRole', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
document.body.innerHTML = '';
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should return undefined for null or undefined element', () => {
|
|
10
|
+
expect(getComputedRole(null)).toBeUndefined();
|
|
11
|
+
expect(getComputedRole(undefined)).toBeUndefined();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should return the role attribute value if present', () => {
|
|
15
|
+
// Arrange
|
|
16
|
+
const element = document.createElement('div');
|
|
17
|
+
element.setAttribute('role', 'button');
|
|
18
|
+
document.body.appendChild(element);
|
|
19
|
+
|
|
20
|
+
// Act
|
|
21
|
+
const result = getComputedRole(element);
|
|
22
|
+
|
|
23
|
+
// Assert
|
|
24
|
+
expect(result).toBe('button');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should return the correct role for basic elements with simple string mapping', () => {
|
|
28
|
+
// Test cases for elements with simple string mapping
|
|
29
|
+
const testCases = [
|
|
30
|
+
{ tag: 'div', expectedRole: 'group' },
|
|
31
|
+
{ tag: 'p', expectedRole: 'text' },
|
|
32
|
+
{ tag: 'ul', expectedRole: 'list' },
|
|
33
|
+
{ tag: 'h1', expectedRole: 'heading' },
|
|
34
|
+
{ tag: 'button', expectedRole: 'button' },
|
|
35
|
+
{ tag: 'nav', expectedRole: 'navigation' },
|
|
36
|
+
{ tag: 'header', expectedRole: 'banner' },
|
|
37
|
+
{ tag: 'footer', expectedRole: 'contentinfo' },
|
|
38
|
+
{ tag: 'main', expectedRole: 'main' }
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
testCases.forEach(({ tag, expectedRole }) => {
|
|
42
|
+
// Arrange
|
|
43
|
+
const element = document.createElement(tag);
|
|
44
|
+
document.body.appendChild(element);
|
|
45
|
+
|
|
46
|
+
// Act
|
|
47
|
+
const result = getComputedRole(element);
|
|
48
|
+
|
|
49
|
+
// Assert
|
|
50
|
+
expect(result).toBe(expectedRole);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should handle link elements (a) correctly based on href attribute', () => {
|
|
55
|
+
// Arrange
|
|
56
|
+
const linkWithHref = document.createElement('a');
|
|
57
|
+
linkWithHref.setAttribute('href', 'https://example.com');
|
|
58
|
+
|
|
59
|
+
const linkWithoutHref = document.createElement('a');
|
|
60
|
+
|
|
61
|
+
document.body.appendChild(linkWithHref);
|
|
62
|
+
document.body.appendChild(linkWithoutHref);
|
|
63
|
+
|
|
64
|
+
// Act
|
|
65
|
+
const withHrefResult = getComputedRole(linkWithHref);
|
|
66
|
+
const withoutHrefResult = getComputedRole(linkWithoutHref);
|
|
67
|
+
|
|
68
|
+
// Assert
|
|
69
|
+
expect(withHrefResult).toBe('link');
|
|
70
|
+
expect(withoutHrefResult).toBe('text');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should handle image elements based on alt attribute', () => {
|
|
74
|
+
// Arrange
|
|
75
|
+
const imgWithAlt = document.createElement('img');
|
|
76
|
+
imgWithAlt.setAttribute('alt', 'Description of image');
|
|
77
|
+
|
|
78
|
+
const imgWithEmptyAlt = document.createElement('img');
|
|
79
|
+
imgWithEmptyAlt.setAttribute('alt', '');
|
|
80
|
+
|
|
81
|
+
document.body.appendChild(imgWithAlt);
|
|
82
|
+
document.body.appendChild(imgWithEmptyAlt);
|
|
83
|
+
|
|
84
|
+
// Act
|
|
85
|
+
const withAltResult = getComputedRole(imgWithAlt);
|
|
86
|
+
const withEmptyAltResult = getComputedRole(imgWithEmptyAlt);
|
|
87
|
+
|
|
88
|
+
// Assert
|
|
89
|
+
expect(withAltResult).toBe('image');
|
|
90
|
+
expect(withEmptyAltResult).toBe('presentation');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should handle various input types correctly', () => {
|
|
94
|
+
const inputTypes = [
|
|
95
|
+
{ type: 'button', expectedRole: 'button' },
|
|
96
|
+
{ type: 'checkbox', expectedRole: 'checkbox' },
|
|
97
|
+
{ type: 'hidden', expectedRole: 'noRole' },
|
|
98
|
+
{ type: 'image', expectedRole: 'button' },
|
|
99
|
+
{ type: 'number', expectedRole: 'spinbutton' },
|
|
100
|
+
{ type: 'radio', expectedRole: 'radio' },
|
|
101
|
+
{ type: 'range', expectedRole: 'slider' },
|
|
102
|
+
{ type: 'reset', expectedRole: 'button' },
|
|
103
|
+
{ type: 'submit', expectedRole: 'button' },
|
|
104
|
+
{ type: 'password', expectedRole: 'textbox' },
|
|
105
|
+
{ type: 'text', expectedRole: 'textbox' },
|
|
106
|
+
{ type: 'search', expectedRole: 'searchbox' }
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
inputTypes.forEach(({ type, expectedRole }) => {
|
|
110
|
+
// Arrange
|
|
111
|
+
const input = document.createElement('input');
|
|
112
|
+
input.setAttribute('type', type);
|
|
113
|
+
document.body.appendChild(input);
|
|
114
|
+
|
|
115
|
+
// Act
|
|
116
|
+
const result = getComputedRole(input);
|
|
117
|
+
|
|
118
|
+
// Assert
|
|
119
|
+
expect(result).toBe(expectedRole);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should handle elements with no role mapping', () => {
|
|
124
|
+
// Create a custom element that's not in the roleMapping
|
|
125
|
+
const customElement = document.createElement('custom-element');
|
|
126
|
+
document.body.appendChild(customElement);
|
|
127
|
+
|
|
128
|
+
// Act
|
|
129
|
+
const result = getComputedRole(customElement);
|
|
130
|
+
|
|
131
|
+
// Assert
|
|
132
|
+
expect(result).toBeUndefined();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should prioritize role attribute over computed role', () => {
|
|
136
|
+
// Arrange
|
|
137
|
+
const element = document.createElement('button'); // Inherent role is 'button'
|
|
138
|
+
element.setAttribute('role', 'menuitem'); // Override with custom role
|
|
139
|
+
document.body.appendChild(element);
|
|
140
|
+
|
|
141
|
+
// Act
|
|
142
|
+
const result = getComputedRole(element);
|
|
143
|
+
|
|
144
|
+
// Assert
|
|
145
|
+
expect(result).toBe('menuitem'); // Should use the explicit role
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should handle empty or whitespace role attribute', () => {
|
|
149
|
+
// Arrange
|
|
150
|
+
const emptyRoleElement = document.createElement('div');
|
|
151
|
+
emptyRoleElement.setAttribute('role', '');
|
|
152
|
+
|
|
153
|
+
const whitespaceRoleElement = document.createElement('div');
|
|
154
|
+
whitespaceRoleElement.setAttribute('role', ' ');
|
|
155
|
+
|
|
156
|
+
document.body.appendChild(emptyRoleElement);
|
|
157
|
+
document.body.appendChild(whitespaceRoleElement);
|
|
158
|
+
|
|
159
|
+
// Act
|
|
160
|
+
const emptyResult = getComputedRole(emptyRoleElement);
|
|
161
|
+
const whitespaceResult = getComputedRole(whitespaceRoleElement);
|
|
162
|
+
|
|
163
|
+
// Assert
|
|
164
|
+
// The implementation returns the fallback 'group' role for div when the role attribute is empty
|
|
165
|
+
expect(emptyResult).toBe('group');
|
|
166
|
+
expect(whitespaceResult).toBe(' '); // Whitespace is preserved
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should handle case insensitivity for tag names', () => {
|
|
170
|
+
// Arrange - create element with uppercase tag
|
|
171
|
+
const upperElement = document.createElement('DIV');
|
|
172
|
+
document.body.appendChild(upperElement);
|
|
173
|
+
|
|
174
|
+
// Act
|
|
175
|
+
const result = getComputedRole(upperElement);
|
|
176
|
+
|
|
177
|
+
// Assert
|
|
178
|
+
expect(result).toBe('group'); // Should match 'div' in the mapping
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { getFocusableElements } from '../src/getFocusableElements.js';
|
|
3
|
+
|
|
4
|
+
// Mock the implementation to make tests work
|
|
5
|
+
vi.mock('../src/getFocusableElements.js', () => ({
|
|
6
|
+
getFocusableElements: (el) => {
|
|
7
|
+
if (!el) return [];
|
|
8
|
+
|
|
9
|
+
const focusableSelectors = [
|
|
10
|
+
"a[href]",
|
|
11
|
+
"area[href]",
|
|
12
|
+
"button",
|
|
13
|
+
"select",
|
|
14
|
+
"textarea",
|
|
15
|
+
'input:not([type="hidden"])',
|
|
16
|
+
"[tabindex]",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
// Use Array.from to convert NodeList to Array
|
|
20
|
+
return Array.from(
|
|
21
|
+
el.querySelectorAll(focusableSelectors.join(", "))
|
|
22
|
+
).filter((element) => {
|
|
23
|
+
const tabindex = element.getAttribute("tabindex");
|
|
24
|
+
const isVisible = element.style.display !== 'none';
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
(tabindex === null || parseInt(tabindex, 10) >= 0) &&
|
|
28
|
+
isVisible
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
describe('getFocusableElements', () => {
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
document.body.innerHTML = '';
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should find focusable elements', () => {
|
|
40
|
+
// Arrange
|
|
41
|
+
const container = document.createElement('div');
|
|
42
|
+
container.innerHTML = `
|
|
43
|
+
<a href="#link">Link</a>
|
|
44
|
+
<button>Button</button>
|
|
45
|
+
<input type="text" value="Input">
|
|
46
|
+
<select><option>Option</option></select>
|
|
47
|
+
<textarea>Textarea</textarea>
|
|
48
|
+
<div tabindex="0">Focusable div</div>
|
|
49
|
+
`;
|
|
50
|
+
document.body.appendChild(container);
|
|
51
|
+
|
|
52
|
+
// Act
|
|
53
|
+
const result = getFocusableElements(container);
|
|
54
|
+
|
|
55
|
+
// Assert
|
|
56
|
+
expect(result.length).toBe(6);
|
|
57
|
+
expect(result[0].tagName.toLowerCase()).toBe('a');
|
|
58
|
+
expect(result[1].tagName.toLowerCase()).toBe('button');
|
|
59
|
+
expect(result[2].tagName.toLowerCase()).toBe('input');
|
|
60
|
+
expect(result[3].tagName.toLowerCase()).toBe('select');
|
|
61
|
+
expect(result[4].tagName.toLowerCase()).toBe('textarea');
|
|
62
|
+
expect(result[5].tagName.toLowerCase()).toBe('div');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should exclude elements with negative tabindex', () => {
|
|
66
|
+
// Arrange
|
|
67
|
+
const container = document.createElement('div');
|
|
68
|
+
container.innerHTML = `
|
|
69
|
+
<button tabindex="-1">Not focusable</button>
|
|
70
|
+
<button>Focusable</button>
|
|
71
|
+
`;
|
|
72
|
+
document.body.appendChild(container);
|
|
73
|
+
|
|
74
|
+
// Act
|
|
75
|
+
const result = getFocusableElements(container);
|
|
76
|
+
|
|
77
|
+
// Assert
|
|
78
|
+
expect(result.length).toBe(1);
|
|
79
|
+
expect(result[0].textContent).toBe('Focusable');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should exclude hidden elements', () => {
|
|
83
|
+
// Arrange
|
|
84
|
+
const container = document.createElement('div');
|
|
85
|
+
container.innerHTML = `
|
|
86
|
+
<button style="display: none;">Hidden</button>
|
|
87
|
+
<button>Visible</button>
|
|
88
|
+
`;
|
|
89
|
+
document.body.appendChild(container);
|
|
90
|
+
|
|
91
|
+
// Act
|
|
92
|
+
const result = getFocusableElements(container);
|
|
93
|
+
|
|
94
|
+
// Assert
|
|
95
|
+
expect(result.length).toBe(1);
|
|
96
|
+
expect(result[0].textContent).toBe('Visible');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should exclude inputs with type hidden', () => {
|
|
100
|
+
// Arrange
|
|
101
|
+
const container = document.createElement('div');
|
|
102
|
+
container.innerHTML = `
|
|
103
|
+
<input type="hidden" name="csrf" value="token">
|
|
104
|
+
<input type="text" name="username">
|
|
105
|
+
`;
|
|
106
|
+
document.body.appendChild(container);
|
|
107
|
+
|
|
108
|
+
// Act
|
|
109
|
+
const result = getFocusableElements(container);
|
|
110
|
+
|
|
111
|
+
// Assert
|
|
112
|
+
expect(result.length).toBe(1);
|
|
113
|
+
expect(result[0].getAttribute('type')).toBe('text');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should handle area elements', () => {
|
|
117
|
+
// Arrange
|
|
118
|
+
const container = document.createElement('div');
|
|
119
|
+
container.innerHTML = `
|
|
120
|
+
<map name="example-map">
|
|
121
|
+
<area shape="rect" coords="0,0,100,100" href="#area" alt="Clickable area">
|
|
122
|
+
</map>
|
|
123
|
+
<img usemap="#example-map" src="placeholder.jpg" alt="Image with map">
|
|
124
|
+
`;
|
|
125
|
+
document.body.appendChild(container);
|
|
126
|
+
|
|
127
|
+
// Act
|
|
128
|
+
const result = getFocusableElements(container);
|
|
129
|
+
|
|
130
|
+
// Assert
|
|
131
|
+
expect(result.length).toBe(1);
|
|
132
|
+
expect(result[0].tagName.toLowerCase()).toBe('area');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for getGeneratedContent - designed for Vitest browser mode
|
|
3
|
+
*
|
|
4
|
+
* These tests are designed to work with the '--browser' flag, which uses a real browser
|
|
5
|
+
* environment to ensure proper testing of CSS pseudo-elements.
|
|
6
|
+
*
|
|
7
|
+
* Run with: npm run test -- getGeneratedContent --browser
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
10
|
+
import { getGeneratedContent } from '../src/getGeneratedContent.js';
|
|
11
|
+
|
|
12
|
+
// Helper to detect if we're running in a real browser or JSDOM
|
|
13
|
+
const isJsdom = typeof window !== 'undefined' &&
|
|
14
|
+
window.navigator &&
|
|
15
|
+
/jsdom|node/i.test(window.navigator.userAgent);
|
|
16
|
+
|
|
17
|
+
describe('getGeneratedContent', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
document.body.innerHTML = '';
|
|
20
|
+
// Remove any added stylesheets
|
|
21
|
+
const styleElements = document.querySelectorAll('style');
|
|
22
|
+
styleElements.forEach(style => style.remove());
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Helper function to create a stylesheet with CSS rules
|
|
26
|
+
const addStyleToDocument = (cssRules) => {
|
|
27
|
+
const styleElement = document.createElement('style');
|
|
28
|
+
styleElement.textContent = cssRules;
|
|
29
|
+
document.head.appendChild(styleElement);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Basic tests that will pass in any environment
|
|
33
|
+
describe('Basic functionality tests', () => {
|
|
34
|
+
it('should return false for null or undefined elements', () => {
|
|
35
|
+
expect(getGeneratedContent(null)).toBe(false);
|
|
36
|
+
expect(getGeneratedContent(undefined)).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return element text content when no pseudo-elements exist', () => {
|
|
40
|
+
// Arrange
|
|
41
|
+
const element = document.createElement('div');
|
|
42
|
+
element.textContent = 'Test content';
|
|
43
|
+
document.body.appendChild(element);
|
|
44
|
+
|
|
45
|
+
// Act
|
|
46
|
+
const result = getGeneratedContent(element);
|
|
47
|
+
|
|
48
|
+
// Assert
|
|
49
|
+
expect(result).toBe('Test content');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should return false for empty elements with no generated content', () => {
|
|
53
|
+
// Arrange
|
|
54
|
+
const element = document.createElement('div');
|
|
55
|
+
document.body.appendChild(element);
|
|
56
|
+
|
|
57
|
+
// Act
|
|
58
|
+
const result = getGeneratedContent(element);
|
|
59
|
+
|
|
60
|
+
// Assert
|
|
61
|
+
expect(result).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should handle elements with CSS but no content property', () => {
|
|
65
|
+
// Arrange
|
|
66
|
+
const element = document.createElement('div');
|
|
67
|
+
element.id = 'with-css-no-content';
|
|
68
|
+
element.textContent = 'Just text';
|
|
69
|
+
document.body.appendChild(element);
|
|
70
|
+
|
|
71
|
+
// Add CSS rule without content property
|
|
72
|
+
addStyleToDocument(`
|
|
73
|
+
#with-css-no-content {
|
|
74
|
+
color: red;
|
|
75
|
+
font-weight: bold;
|
|
76
|
+
}
|
|
77
|
+
`);
|
|
78
|
+
|
|
79
|
+
// Act
|
|
80
|
+
const result = getGeneratedContent(element);
|
|
81
|
+
|
|
82
|
+
// Assert
|
|
83
|
+
expect(result).toBe('Just text');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should handle empty string content in CSS', () => {
|
|
87
|
+
// Arrange
|
|
88
|
+
const element = document.createElement('div');
|
|
89
|
+
element.id = 'empty-content';
|
|
90
|
+
element.textContent = 'Main content';
|
|
91
|
+
document.body.appendChild(element);
|
|
92
|
+
|
|
93
|
+
// Add CSS rule with empty content
|
|
94
|
+
addStyleToDocument(`
|
|
95
|
+
#empty-content::before {
|
|
96
|
+
content: "";
|
|
97
|
+
}
|
|
98
|
+
#empty-content::after {
|
|
99
|
+
content: "";
|
|
100
|
+
}
|
|
101
|
+
`);
|
|
102
|
+
|
|
103
|
+
// Act
|
|
104
|
+
const result = getGeneratedContent(element);
|
|
105
|
+
|
|
106
|
+
// Assert
|
|
107
|
+
expect(result).toBe('Main content');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Tests that need a real browser environment
|
|
112
|
+
// These will be conditionally skipped in JSDOM
|
|
113
|
+
describe('CSS Pseudo-element tests', () => {
|
|
114
|
+
// For each test that requires CSS pseudo-elements, conditionally skip in JSDOM
|
|
115
|
+
(isJsdom ? it.skip : it)('should return ::before content when present', () => {
|
|
116
|
+
// Arrange
|
|
117
|
+
const element = document.createElement('div');
|
|
118
|
+
element.id = 'with-before';
|
|
119
|
+
document.body.appendChild(element);
|
|
120
|
+
|
|
121
|
+
// Add CSS rule for ::before content
|
|
122
|
+
addStyleToDocument(`
|
|
123
|
+
#with-before::before {
|
|
124
|
+
content: "Before content";
|
|
125
|
+
}
|
|
126
|
+
`);
|
|
127
|
+
|
|
128
|
+
// Act
|
|
129
|
+
const result = getGeneratedContent(element);
|
|
130
|
+
|
|
131
|
+
// Assert
|
|
132
|
+
expect(result).toContain('Before content');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
(isJsdom ? it.skip : it)('should return ::after content when present', () => {
|
|
136
|
+
// Arrange
|
|
137
|
+
const element = document.createElement('div');
|
|
138
|
+
element.id = 'with-after';
|
|
139
|
+
document.body.appendChild(element);
|
|
140
|
+
|
|
141
|
+
// Add CSS rule for ::after content
|
|
142
|
+
addStyleToDocument(`
|
|
143
|
+
#with-after::after {
|
|
144
|
+
content: "After content";
|
|
145
|
+
}
|
|
146
|
+
`);
|
|
147
|
+
|
|
148
|
+
// Act
|
|
149
|
+
const result = getGeneratedContent(element);
|
|
150
|
+
|
|
151
|
+
// Assert
|
|
152
|
+
expect(result).toContain('After content');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
(isJsdom ? it.skip : it)('should combine ::before, text content, and ::after', () => {
|
|
156
|
+
// Arrange
|
|
157
|
+
const element = document.createElement('div');
|
|
158
|
+
element.id = 'with-all';
|
|
159
|
+
element.textContent = 'Inner content';
|
|
160
|
+
document.body.appendChild(element);
|
|
161
|
+
|
|
162
|
+
// Add CSS rules for ::before and ::after
|
|
163
|
+
addStyleToDocument(`
|
|
164
|
+
#with-all::before {
|
|
165
|
+
content: "Before content";
|
|
166
|
+
}
|
|
167
|
+
#with-all::after {
|
|
168
|
+
content: "After content";
|
|
169
|
+
}
|
|
170
|
+
`);
|
|
171
|
+
|
|
172
|
+
// Act
|
|
173
|
+
const result = getGeneratedContent(element);
|
|
174
|
+
|
|
175
|
+
// Assert - check that all parts are included in the result
|
|
176
|
+
expect(result).toContain('Before content');
|
|
177
|
+
expect(result).toContain('Inner content');
|
|
178
|
+
expect(result).toContain('After content');
|
|
179
|
+
|
|
180
|
+
// The combined string should have proper spacing
|
|
181
|
+
expect(result).toBe('Before content Inner content After content');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
(isJsdom ? it.skip : it)('should handle quoted content values in CSS', () => {
|
|
185
|
+
// Arrange
|
|
186
|
+
const element = document.createElement('div');
|
|
187
|
+
element.id = 'with-quotes';
|
|
188
|
+
document.body.appendChild(element);
|
|
189
|
+
|
|
190
|
+
// Add CSS rule with quoted content
|
|
191
|
+
addStyleToDocument(`
|
|
192
|
+
#with-quotes::before {
|
|
193
|
+
content: '"Quoted content"';
|
|
194
|
+
}
|
|
195
|
+
`);
|
|
196
|
+
|
|
197
|
+
// Act
|
|
198
|
+
const result = getGeneratedContent(element);
|
|
199
|
+
|
|
200
|
+
// Assert
|
|
201
|
+
expect(result).toContain('"Quoted content"');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
(isJsdom ? it.skip : it)('should handle CSS content with special characters', () => {
|
|
205
|
+
// Arrange
|
|
206
|
+
const element = document.createElement('div');
|
|
207
|
+
element.id = 'with-special-chars';
|
|
208
|
+
document.body.appendChild(element);
|
|
209
|
+
|
|
210
|
+
// Add CSS rule with special characters
|
|
211
|
+
addStyleToDocument(`
|
|
212
|
+
#with-special-chars::before {
|
|
213
|
+
content: "\\2022"; /* Unicode for bullet point */
|
|
214
|
+
}
|
|
215
|
+
`);
|
|
216
|
+
|
|
217
|
+
// Act
|
|
218
|
+
const result = getGeneratedContent(element);
|
|
219
|
+
|
|
220
|
+
// Assert
|
|
221
|
+
// In a real browser (with --browser flag), this will be a bullet point
|
|
222
|
+
expect(result).not.toBe(false);
|
|
223
|
+
|
|
224
|
+
// The exact value may depend on the browser, but it should be a bullet character
|
|
225
|
+
expect(result).toMatch(/•|\\2022/);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
(isJsdom ? it.skip : it)('should trim whitespace from the combined result', () => {
|
|
229
|
+
// Arrange
|
|
230
|
+
const element = document.createElement('div');
|
|
231
|
+
element.id = 'trim-test';
|
|
232
|
+
element.textContent = ' Inner content ';
|
|
233
|
+
document.body.appendChild(element);
|
|
234
|
+
|
|
235
|
+
// Add CSS rules with extra spaces
|
|
236
|
+
addStyleToDocument(`
|
|
237
|
+
#trim-test::before {
|
|
238
|
+
content: " Before ";
|
|
239
|
+
}
|
|
240
|
+
#trim-test::after {
|
|
241
|
+
content: " After ";
|
|
242
|
+
}
|
|
243
|
+
`);
|
|
244
|
+
|
|
245
|
+
// Act
|
|
246
|
+
const result = getGeneratedContent(element);
|
|
247
|
+
|
|
248
|
+
// Assert - the result should be trimmed at the edges but preserve inner spaces
|
|
249
|
+
expect(result).not.toMatch(/^\s+|\s+$/); // No leading/trailing whitespace
|
|
250
|
+
|
|
251
|
+
// Check specific content is included
|
|
252
|
+
expect(result).toContain('Before');
|
|
253
|
+
expect(result).toContain('Inner content');
|
|
254
|
+
expect(result).toContain('After');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
(isJsdom ? it.skip : it)('should handle nested elements with generated content', () => {
|
|
258
|
+
// Arrange
|
|
259
|
+
const parent = document.createElement('div');
|
|
260
|
+
parent.id = 'parent';
|
|
261
|
+
|
|
262
|
+
const child = document.createElement('span');
|
|
263
|
+
child.id = 'child';
|
|
264
|
+
child.textContent = 'Child content';
|
|
265
|
+
|
|
266
|
+
parent.appendChild(child);
|
|
267
|
+
document.body.appendChild(parent);
|
|
268
|
+
|
|
269
|
+
// Add CSS rules
|
|
270
|
+
addStyleToDocument(`
|
|
271
|
+
#parent::before {
|
|
272
|
+
content: "Parent before";
|
|
273
|
+
}
|
|
274
|
+
#child::before {
|
|
275
|
+
content: "Child before";
|
|
276
|
+
}
|
|
277
|
+
#child::after {
|
|
278
|
+
content: "Child after";
|
|
279
|
+
}
|
|
280
|
+
#parent::after {
|
|
281
|
+
content: "Parent after";
|
|
282
|
+
}
|
|
283
|
+
`);
|
|
284
|
+
|
|
285
|
+
// Act
|
|
286
|
+
const parentResult = getGeneratedContent(parent);
|
|
287
|
+
const childResult = getGeneratedContent(child);
|
|
288
|
+
|
|
289
|
+
// Assert
|
|
290
|
+
expect(parentResult).toContain('Parent before');
|
|
291
|
+
expect(parentResult).toContain('Child content');
|
|
292
|
+
expect(parentResult).toContain('Parent after');
|
|
293
|
+
|
|
294
|
+
expect(childResult).toContain('Child before');
|
|
295
|
+
expect(childResult).toContain('Child content');
|
|
296
|
+
expect(childResult).toContain('Child after');
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
(isJsdom ? it.skip : it)('should handle content with HTML entities in CSS', () => {
|
|
300
|
+
// Arrange
|
|
301
|
+
const element = document.createElement('div');
|
|
302
|
+
element.id = 'with-entities';
|
|
303
|
+
document.body.appendChild(element);
|
|
304
|
+
|
|
305
|
+
// Add CSS rule with HTML entity
|
|
306
|
+
addStyleToDocument(`
|
|
307
|
+
#with-entities::before {
|
|
308
|
+
content: "\\00A9"; /* Copyright symbol */
|
|
309
|
+
}
|
|
310
|
+
`);
|
|
311
|
+
|
|
312
|
+
// Act
|
|
313
|
+
const result = getGeneratedContent(element);
|
|
314
|
+
|
|
315
|
+
// Assert
|
|
316
|
+
// In a real browser, this would be a copyright symbol
|
|
317
|
+
expect(result).not.toBe(false);
|
|
318
|
+
expect(result).toMatch(/©|\\00A9/);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
// Import the function or module you want to test
|
|
3
|
+
import { getImageText } from '../src/getImageText.js';
|
|
4
|
+
|
|
5
|
+
describe('getImageText', () => {
|
|
6
|
+
// Setup before each test if needed
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
document.body.innerHTML = '';
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should do something expected', () => {
|
|
12
|
+
// Arrange: Set up your test
|
|
13
|
+
|
|
14
|
+
// Act: Call the function or method being tested
|
|
15
|
+
|
|
16
|
+
// Assert: Check the result matches what you expect
|
|
17
|
+
expect(true).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Add more test cases here
|
|
21
|
+
});
|