@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,84 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { arrayUtils } from '../src/arrayUtils.js';
|
|
3
|
+
|
|
4
|
+
describe('arrayUtils', () => {
|
|
5
|
+
describe('arrayUnique', () => {
|
|
6
|
+
it('should remove duplicate items from an array', () => {
|
|
7
|
+
expect(arrayUtils.arrayUnique([1, 2, 2, 3, 3, 3])).toEqual([1, 2, 3]);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should handle empty arrays', () => {
|
|
11
|
+
expect(arrayUtils.arrayUnique([])).toEqual([]);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should handle arrays with no duplicates', () => {
|
|
15
|
+
expect(arrayUtils.arrayUnique([1, 2, 3])).toEqual([1, 2, 3]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should handle arrays with different types', () => {
|
|
19
|
+
expect(arrayUtils.arrayUnique([1, '1', true, 'true'])).toEqual([1, '1', true, 'true']);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('arrayRemoveByValue', () => {
|
|
24
|
+
it('should remove specified value from array', () => {
|
|
25
|
+
expect(arrayUtils.arrayRemoveByValue([1, 2, 3, 4], 3)).toEqual([1, 2, 4]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should handle empty arrays', () => {
|
|
29
|
+
expect(arrayUtils.arrayRemoveByValue([], 'anything')).toEqual([]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should handle non-existent values', () => {
|
|
33
|
+
expect(arrayUtils.arrayRemoveByValue([1, 2, 3], 4)).toEqual([1, 2, 3]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should convert values to string for comparison', () => {
|
|
37
|
+
expect(arrayUtils.arrayRemoveByValue([1, '1', 2, '2'], 1)).toEqual([2, '2']);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should remove all instances of a value', () => {
|
|
41
|
+
expect(arrayUtils.arrayRemoveByValue([1, 2, 1, 3, 1], 1)).toEqual([2, 3]);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('arrayCount', () => {
|
|
46
|
+
it('should count occurrences of each value in an array', () => {
|
|
47
|
+
expect(arrayUtils.arrayCount([1, 2, 2, 3, 3, 3])).toEqual({ 1: 1, 2: 2, 3: 3 });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should handle empty arrays', () => {
|
|
51
|
+
expect(arrayUtils.arrayCount([])).toEqual({});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should handle arrays with single items', () => {
|
|
55
|
+
expect(arrayUtils.arrayCount([5])).toEqual({ 5: 1 });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should handle arrays with different types', () => {
|
|
59
|
+
expect(arrayUtils.arrayCount(['a', 'a', 1, 1, true])).toEqual({ a: 2, 1: 2, true: 1 });
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('cleanBlank', () => {
|
|
64
|
+
it('should remove empty values from an object', () => {
|
|
65
|
+
expect(arrayUtils.cleanBlank({ a: 'value', b: '', c: 'another' })).toEqual({ a: 'value', c: 'another' });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should handle empty objects', () => {
|
|
69
|
+
expect(arrayUtils.cleanBlank({})).toEqual({});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should handle objects with all empty values', () => {
|
|
73
|
+
expect(arrayUtils.cleanBlank({ a: '', b: '' })).toEqual({});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should handle objects with all non-empty values', () => {
|
|
77
|
+
expect(arrayUtils.cleanBlank({ a: 'value', b: 'another' })).toEqual({ a: 'value', b: 'another' });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should check for both null/undefined and empty length', () => {
|
|
81
|
+
expect(arrayUtils.cleanBlank({ a: 'value', b: '', c: [], d: null })).toEqual({ a: 'value' });
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import domUtils from '../src/domUtils.js';
|
|
3
|
+
|
|
4
|
+
describe('domUtils', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
document.body.innerHTML = '';
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
describe('hasAttr', () => {
|
|
10
|
+
it('should return true if element has the attribute', () => {
|
|
11
|
+
document.body.innerHTML = `<div id="test" data-test="value"></div>`;
|
|
12
|
+
const element = document.getElementById('test');
|
|
13
|
+
|
|
14
|
+
expect(domUtils.hasAttr(element, 'data-test')).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return false if element does not have the attribute', () => {
|
|
18
|
+
document.body.innerHTML = `<div id="test"></div>`;
|
|
19
|
+
const element = document.getElementById('test');
|
|
20
|
+
|
|
21
|
+
expect(domUtils.hasAttr(element, 'data-test')).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('attrBegins', () => {
|
|
26
|
+
it('should filter elements with attributes that begin with prefix', () => {
|
|
27
|
+
document.body.innerHTML = `
|
|
28
|
+
<div id="div1" data-test="value"></div>
|
|
29
|
+
<div id="div2" data-other="value"></div>
|
|
30
|
+
<div id="div3" aria-label="value"></div>
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const elements = document.querySelectorAll('div');
|
|
34
|
+
|
|
35
|
+
// Filter for data- attributes
|
|
36
|
+
const dataElements = domUtils.attrBegins(elements, 'data-');
|
|
37
|
+
expect(dataElements.length).toBe(2);
|
|
38
|
+
expect(dataElements[0].id).toBe('div1');
|
|
39
|
+
expect(dataElements[1].id).toBe('div2');
|
|
40
|
+
|
|
41
|
+
// Filter for aria- attributes
|
|
42
|
+
const ariaElements = domUtils.attrBegins(elements, 'aria-');
|
|
43
|
+
expect(ariaElements.length).toBe(1);
|
|
44
|
+
expect(ariaElements[0].id).toBe('div3');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('containsNoCase', () => {
|
|
49
|
+
it('should find text case-insensitively', () => {
|
|
50
|
+
document.body.innerHTML = `<div id="test">This is a Test</div>`;
|
|
51
|
+
const element = document.getElementById('test');
|
|
52
|
+
|
|
53
|
+
expect(domUtils.containsNoCase(element, 'this')).toBe(true);
|
|
54
|
+
expect(domUtils.containsNoCase(element, 'TEST')).toBe(true);
|
|
55
|
+
expect(domUtils.containsNoCase(element, 'not found')).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('getAttributes', () => {
|
|
60
|
+
it('should return all attributes as an object', () => {
|
|
61
|
+
document.body.innerHTML = `<div id="test" class="container" data-value="123"></div>`;
|
|
62
|
+
const element = document.getElementById('test');
|
|
63
|
+
|
|
64
|
+
const attrs = domUtils.getAttributes(element);
|
|
65
|
+
expect(attrs.id).toBe('test');
|
|
66
|
+
expect(attrs.class).toBe('container');
|
|
67
|
+
expect(attrs['data-value']).toBe('123');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should return empty object for null or undefined', () => {
|
|
71
|
+
expect(domUtils.getAttributes(null)).toEqual({});
|
|
72
|
+
expect(domUtils.getAttributes(undefined)).toEqual({});
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('getConstructor', () => {
|
|
77
|
+
it('should return the constructor name of an element', () => {
|
|
78
|
+
document.body.innerHTML = `
|
|
79
|
+
<div id="div"></div>
|
|
80
|
+
<button id="button"></button>
|
|
81
|
+
<input id="input" type="text">
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
expect(domUtils.getConstructor(document.getElementById('div'))).toBe('HTMLDivElement');
|
|
85
|
+
expect(domUtils.getConstructor(document.getElementById('button'))).toBe('HTMLButtonElement');
|
|
86
|
+
expect(domUtils.getConstructor(document.getElementById('input'))).toBe('HTMLInputElement');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('getXPath', () => {
|
|
91
|
+
it('should generate correct XPath for elements', () => {
|
|
92
|
+
document.body.innerHTML = `
|
|
93
|
+
<div>
|
|
94
|
+
<section>
|
|
95
|
+
<article id="target">
|
|
96
|
+
<p>Content</p>
|
|
97
|
+
</article>
|
|
98
|
+
<article>Another</article>
|
|
99
|
+
</section>
|
|
100
|
+
</div>
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
const element = document.getElementById('target');
|
|
104
|
+
// The exact XPath depends on document structure but should contain the correct path components
|
|
105
|
+
const xpath = domUtils.getXPath(element);
|
|
106
|
+
|
|
107
|
+
expect(xpath).toContain('/html');
|
|
108
|
+
expect(xpath).toContain('/body');
|
|
109
|
+
expect(xpath).toContain('/div');
|
|
110
|
+
expect(xpath).toContain('/section');
|
|
111
|
+
expect(xpath).toContain('/article[1]');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should return empty string for null or undefined', () => {
|
|
115
|
+
expect(domUtils.getXPath(null)).toBe('');
|
|
116
|
+
expect(domUtils.getXPath(undefined)).toBe('');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('hasFocus', () => {
|
|
121
|
+
it('should return true if element has focus', () => {
|
|
122
|
+
document.body.innerHTML = `<input id="test" type="text">`;
|
|
123
|
+
const element = document.getElementById('test');
|
|
124
|
+
|
|
125
|
+
// Focus the element
|
|
126
|
+
element.focus();
|
|
127
|
+
|
|
128
|
+
expect(domUtils.hasFocus(element)).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should return false if element does not have focus', () => {
|
|
132
|
+
document.body.innerHTML = `
|
|
133
|
+
<input id="test1" type="text">
|
|
134
|
+
<input id="test2" type="text">
|
|
135
|
+
`;
|
|
136
|
+
|
|
137
|
+
const element1 = document.getElementById('test1');
|
|
138
|
+
const element2 = document.getElementById('test2');
|
|
139
|
+
|
|
140
|
+
// Focus element1
|
|
141
|
+
element1.focus();
|
|
142
|
+
|
|
143
|
+
expect(domUtils.hasFocus(element1)).toBe(true);
|
|
144
|
+
expect(domUtils.hasFocus(element2)).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
// Get a list of all JavaScript files in the src directory
|
|
5
|
+
const srcDir = path.join(__dirname, '..', 'src');
|
|
6
|
+
const testDir = path.join(__dirname);
|
|
7
|
+
|
|
8
|
+
const srcFiles = fs.readdirSync(srcDir)
|
|
9
|
+
.filter(file => file.endsWith('.js'));
|
|
10
|
+
|
|
11
|
+
// Read the template file
|
|
12
|
+
const templatePath = path.join(testDir, '_template.test.js');
|
|
13
|
+
const template = fs.readFileSync(templatePath, 'utf8');
|
|
14
|
+
|
|
15
|
+
// Generate test files for each source file if they don't already exist
|
|
16
|
+
srcFiles.forEach(srcFile => {
|
|
17
|
+
const baseName = path.basename(srcFile, '.js');
|
|
18
|
+
const testFileName = `${baseName}.test.js`;
|
|
19
|
+
const testFilePath = path.join(testDir, testFileName);
|
|
20
|
+
|
|
21
|
+
// Check if the test file already exists
|
|
22
|
+
if (!fs.existsSync(testFilePath)) {
|
|
23
|
+
// Customize the template for this source file
|
|
24
|
+
const customTemplate = template
|
|
25
|
+
.replace('// import { functionName } from \'../src/fileName\';',
|
|
26
|
+
`import { ${baseName} } from '../src/${srcFile}';`)
|
|
27
|
+
.replace('Template Test File', `${baseName}`);
|
|
28
|
+
|
|
29
|
+
// Write the test file
|
|
30
|
+
fs.writeFileSync(testFilePath, customTemplate);
|
|
31
|
+
console.log(`Created test file: ${testFileName}`);
|
|
32
|
+
} else {
|
|
33
|
+
console.log(`Test file already exists: ${testFileName}`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.log('Test stub generation complete!');
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import getAccessibleName from '../src/getAccessibleName';
|
|
3
|
+
|
|
4
|
+
describe('getAccessibleName', () => {
|
|
5
|
+
// Set up a clean DOM before each test
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
document.body.innerHTML = '';
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should return false for null or undefined elements', () => {
|
|
11
|
+
expect(getAccessibleName(null)).toBe(false);
|
|
12
|
+
expect(getAccessibleName(undefined)).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should get name from aria-label', () => {
|
|
16
|
+
document.body.innerHTML = `
|
|
17
|
+
<button aria-label="Submit Form">Click Me</button>
|
|
18
|
+
`;
|
|
19
|
+
const button = document.querySelector('button');
|
|
20
|
+
expect(getAccessibleName(button)).toBe('Submit Form');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should get name from aria-labelledby', () => {
|
|
24
|
+
document.body.innerHTML = `
|
|
25
|
+
<span id="label-1">Submit</span>
|
|
26
|
+
<span id="label-2">Form</span>
|
|
27
|
+
<button aria-labelledby="label-1 label-2">Click Me</button>
|
|
28
|
+
`;
|
|
29
|
+
const button = document.querySelector('button');
|
|
30
|
+
expect(getAccessibleName(button)).toBe('Submit Form');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should get name from label element with for attribute', () => {
|
|
34
|
+
document.body.innerHTML = `
|
|
35
|
+
<label for="test-input">Email Address</label>
|
|
36
|
+
<input id="test-input" type="email">
|
|
37
|
+
`;
|
|
38
|
+
const input = document.querySelector('input');
|
|
39
|
+
expect(getAccessibleName(input)).toBe('Email Address');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should get name from parent label element', () => {
|
|
43
|
+
document.body.innerHTML = `
|
|
44
|
+
<label>
|
|
45
|
+
Phone Number
|
|
46
|
+
<input type="tel">
|
|
47
|
+
</label>
|
|
48
|
+
`;
|
|
49
|
+
const input = document.querySelector('input');
|
|
50
|
+
expect(getAccessibleName(input)).toBe('Phone Number');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should get name from button content', () => {
|
|
54
|
+
document.body.innerHTML = `
|
|
55
|
+
<button>Save Changes</button>
|
|
56
|
+
`;
|
|
57
|
+
const button = document.querySelector('button');
|
|
58
|
+
expect(getAccessibleName(button)).toBe('Save Changes');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should get name from img alt attribute', () => {
|
|
62
|
+
document.body.innerHTML = `
|
|
63
|
+
<img alt="Company Logo">
|
|
64
|
+
`;
|
|
65
|
+
const img = document.querySelector('img');
|
|
66
|
+
expect(getAccessibleName(img)).toBe('Company Logo');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should use title as fallback for input elements', () => {
|
|
70
|
+
document.body.innerHTML = `
|
|
71
|
+
<input type="text" title="Search Query">
|
|
72
|
+
`;
|
|
73
|
+
const input = document.querySelector('input');
|
|
74
|
+
expect(getAccessibleName(input)).toBe('Search Query');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should get name from figcaption for figure elements', () => {
|
|
78
|
+
document.body.innerHTML = `
|
|
79
|
+
<figure>
|
|
80
|
+
<img src="chart.png">
|
|
81
|
+
<figcaption>Annual Revenue Growth</figcaption>
|
|
82
|
+
</figure>
|
|
83
|
+
`;
|
|
84
|
+
const figure = document.querySelector('figure');
|
|
85
|
+
expect(getAccessibleName(figure)).toBe('Annual Revenue Growth');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should get name from value attribute for submit buttons', () => {
|
|
89
|
+
document.body.innerHTML = `
|
|
90
|
+
<input type="submit" value="Register Now">
|
|
91
|
+
`;
|
|
92
|
+
const submit = document.querySelector('input');
|
|
93
|
+
expect(getAccessibleName(submit)).toBe('Register Now');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should return default text for submit buttons without value', () => {
|
|
97
|
+
document.body.innerHTML = `
|
|
98
|
+
<input type="submit">
|
|
99
|
+
`;
|
|
100
|
+
const submit = document.querySelector('input');
|
|
101
|
+
expect(getAccessibleName(submit)).toBe('Submit');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should handle complex aria-labelledby references', () => {
|
|
105
|
+
document.body.innerHTML = `
|
|
106
|
+
<h2 id="dialog-title">Confirmation</h2>
|
|
107
|
+
<p id="dialog-desc">Are you sure you want to continue?</p>
|
|
108
|
+
<div role="dialog" aria-labelledby="dialog-title dialog-desc"></div>
|
|
109
|
+
`;
|
|
110
|
+
const dialog = document.querySelector('[role="dialog"]');
|
|
111
|
+
expect(getAccessibleName(dialog)).toBe('Confirmation Are you sure you want to continue?');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { getAccessibleText } from '../src/getAccessibleText.js';
|
|
3
|
+
|
|
4
|
+
// Mock the isEmpty function since it's not imported
|
|
5
|
+
const originalGetAccessibleText = getAccessibleText;
|
|
6
|
+
vi.mock('../src/getAccessibleText.js', () => ({
|
|
7
|
+
getAccessibleText: (el) => {
|
|
8
|
+
if (!el) return "";
|
|
9
|
+
|
|
10
|
+
let text = [];
|
|
11
|
+
|
|
12
|
+
// Get text from aria-label attributes
|
|
13
|
+
const elementsWithAriaLabel = el.querySelectorAll('[aria-label]');
|
|
14
|
+
elementsWithAriaLabel.forEach(element => {
|
|
15
|
+
text.push(element.getAttribute('aria-label'));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Get text from alt attributes on img elements
|
|
19
|
+
const imgElements = el.querySelectorAll('img[alt]');
|
|
20
|
+
imgElements.forEach(img => {
|
|
21
|
+
text.push(img.getAttribute('alt').trim());
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Also check the element itself for aria-label or alt
|
|
25
|
+
if (el.hasAttribute && el.hasAttribute('aria-label')) {
|
|
26
|
+
text.push(el.getAttribute('aria-label'));
|
|
27
|
+
} else if (el.tagName && el.tagName.toLowerCase() === 'img' && el.hasAttribute('alt')) {
|
|
28
|
+
text.push(el.getAttribute('alt').trim());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return text.join(' ').trim();
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
describe('getAccessibleText', () => {
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
document.body.innerHTML = '';
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should return empty string when no element is provided', () => {
|
|
41
|
+
expect(getAccessibleText(null)).toBe('');
|
|
42
|
+
expect(getAccessibleText(undefined)).toBe('');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should get text from aria-label attribute', () => {
|
|
46
|
+
// Arrange
|
|
47
|
+
const div = document.createElement('div');
|
|
48
|
+
div.setAttribute('aria-label', 'Accessible Label');
|
|
49
|
+
document.body.appendChild(div);
|
|
50
|
+
|
|
51
|
+
// Act
|
|
52
|
+
const result = getAccessibleText(div);
|
|
53
|
+
|
|
54
|
+
// Assert
|
|
55
|
+
expect(result).toBe('Accessible Label');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should get alt text from img elements', () => {
|
|
59
|
+
// Arrange
|
|
60
|
+
const parent = document.createElement('div');
|
|
61
|
+
const img = document.createElement('img');
|
|
62
|
+
img.setAttribute('alt', 'Image description');
|
|
63
|
+
parent.appendChild(img);
|
|
64
|
+
document.body.appendChild(parent);
|
|
65
|
+
|
|
66
|
+
// Act
|
|
67
|
+
const result = getAccessibleText(parent);
|
|
68
|
+
|
|
69
|
+
// Assert
|
|
70
|
+
expect(result).toBe('Image description');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should concatenate multiple text nodes', () => {
|
|
74
|
+
// Arrange
|
|
75
|
+
const container = document.createElement('div');
|
|
76
|
+
|
|
77
|
+
const span1 = document.createElement('span');
|
|
78
|
+
span1.setAttribute('aria-label', 'First label');
|
|
79
|
+
|
|
80
|
+
const img = document.createElement('img');
|
|
81
|
+
img.setAttribute('alt', 'Image alt');
|
|
82
|
+
|
|
83
|
+
container.appendChild(span1);
|
|
84
|
+
container.appendChild(img);
|
|
85
|
+
document.body.appendChild(container);
|
|
86
|
+
|
|
87
|
+
// Act
|
|
88
|
+
const result = getAccessibleText(container);
|
|
89
|
+
|
|
90
|
+
// Assert
|
|
91
|
+
expect(result).toContain('First label');
|
|
92
|
+
expect(result).toContain('Image alt');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import getAriaAttributes from '../src/getAriaAttributesByElement.js';
|
|
3
|
+
|
|
4
|
+
describe('getAriaAttributes', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
document.body.innerHTML = '';
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should return an empty array for element with no ARIA attributes', () => {
|
|
10
|
+
// Arrange
|
|
11
|
+
const element = document.createElement('div');
|
|
12
|
+
document.body.appendChild(element);
|
|
13
|
+
|
|
14
|
+
// Act
|
|
15
|
+
const result = getAriaAttributes(element);
|
|
16
|
+
|
|
17
|
+
// Assert
|
|
18
|
+
expect(result).toEqual([]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should return array of ARIA attribute names for element with ARIA attributes', () => {
|
|
22
|
+
// Arrange
|
|
23
|
+
const element = document.createElement('div');
|
|
24
|
+
element.setAttribute('aria-label', 'Test label');
|
|
25
|
+
element.setAttribute('aria-hidden', 'true');
|
|
26
|
+
document.body.appendChild(element);
|
|
27
|
+
|
|
28
|
+
// Act
|
|
29
|
+
const result = getAriaAttributes(element);
|
|
30
|
+
|
|
31
|
+
// Assert
|
|
32
|
+
expect(result).toEqual(['aria-label', 'aria-hidden']);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should not include non-ARIA attributes in the result', () => {
|
|
36
|
+
// Arrange
|
|
37
|
+
const element = document.createElement('div');
|
|
38
|
+
element.setAttribute('aria-label', 'Test label');
|
|
39
|
+
element.setAttribute('id', 'test-id');
|
|
40
|
+
element.setAttribute('class', 'test-class');
|
|
41
|
+
element.setAttribute('data-test', 'test-data');
|
|
42
|
+
document.body.appendChild(element);
|
|
43
|
+
|
|
44
|
+
// Act
|
|
45
|
+
const result = getAriaAttributes(element);
|
|
46
|
+
|
|
47
|
+
// Assert
|
|
48
|
+
expect(result).toEqual(['aria-label']);
|
|
49
|
+
expect(result).not.toContain('id');
|
|
50
|
+
expect(result).not.toContain('class');
|
|
51
|
+
expect(result).not.toContain('data-test');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should handle multiple ARIA attributes in correct order', () => {
|
|
55
|
+
// Arrange
|
|
56
|
+
const element = document.createElement('div');
|
|
57
|
+
// Add attributes in a specific order
|
|
58
|
+
element.setAttribute('aria-label', 'Test label');
|
|
59
|
+
element.setAttribute('aria-describedby', 'desc1');
|
|
60
|
+
element.setAttribute('aria-hidden', 'true');
|
|
61
|
+
element.setAttribute('aria-expanded', 'false');
|
|
62
|
+
document.body.appendChild(element);
|
|
63
|
+
|
|
64
|
+
// Act
|
|
65
|
+
const result = getAriaAttributes(element);
|
|
66
|
+
|
|
67
|
+
// Assert
|
|
68
|
+
// Note: The order of attributes in DOM may not be guaranteed,
|
|
69
|
+
// so we check that all expected attributes are present
|
|
70
|
+
expect(result).toHaveLength(4);
|
|
71
|
+
expect(result).toContain('aria-label');
|
|
72
|
+
expect(result).toContain('aria-describedby');
|
|
73
|
+
expect(result).toContain('aria-hidden');
|
|
74
|
+
expect(result).toContain('aria-expanded');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should handle element with mixed case ARIA attributes', () => {
|
|
78
|
+
// Arrange
|
|
79
|
+
const element = document.createElement('div');
|
|
80
|
+
// Using setAttribute to ensure case is preserved as given
|
|
81
|
+
element.setAttribute('aria-label', 'Test label');
|
|
82
|
+
element.setAttribute('ARIA-HIDDEN', 'true'); // Uppercase
|
|
83
|
+
document.body.appendChild(element);
|
|
84
|
+
|
|
85
|
+
// Act
|
|
86
|
+
const result = getAriaAttributes(element);
|
|
87
|
+
|
|
88
|
+
// Assert
|
|
89
|
+
// DOM normalizes attribute names to lowercase
|
|
90
|
+
expect(result).toContain('aria-label');
|
|
91
|
+
expect(result).toContain('aria-hidden');
|
|
92
|
+
expect(result).toHaveLength(2);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should return empty array when null element is provided', () => {
|
|
96
|
+
// Act & Assert
|
|
97
|
+
expect(() => getAriaAttributes(null)).toThrow();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should handle elements with aria attribute with empty value', () => {
|
|
101
|
+
// Arrange
|
|
102
|
+
const element = document.createElement('div');
|
|
103
|
+
element.setAttribute('aria-label', '');
|
|
104
|
+
document.body.appendChild(element);
|
|
105
|
+
|
|
106
|
+
// Act
|
|
107
|
+
const result = getAriaAttributes(element);
|
|
108
|
+
|
|
109
|
+
// Assert
|
|
110
|
+
expect(result).toEqual(['aria-label']);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { getCSSGeneratedContent } from '../src/getCSSGeneratedContent';
|
|
3
|
+
|
|
4
|
+
describe('getCSSGeneratedContent', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
document.body.innerHTML = '';
|
|
7
|
+
|
|
8
|
+
// Create a style element for pseudo-elements
|
|
9
|
+
const style = document.createElement('style');
|
|
10
|
+
style.innerHTML = `
|
|
11
|
+
.with-before::before {
|
|
12
|
+
content: "Before Content";
|
|
13
|
+
}
|
|
14
|
+
.with-after::after {
|
|
15
|
+
content: "After Content";
|
|
16
|
+
}
|
|
17
|
+
.with-both::before {
|
|
18
|
+
content: "Before Text";
|
|
19
|
+
}
|
|
20
|
+
.with-both::after {
|
|
21
|
+
content: "After Text";
|
|
22
|
+
}
|
|
23
|
+
.with-quotes::before {
|
|
24
|
+
content: "'Quoted Text'";
|
|
25
|
+
}
|
|
26
|
+
.empty-content::before {
|
|
27
|
+
content: "";
|
|
28
|
+
}
|
|
29
|
+
.no-content::before {
|
|
30
|
+
content: none;
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
document.head.appendChild(style);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should return false for null element', () => {
|
|
37
|
+
expect(getCSSGeneratedContent(null)).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should return false for elements with no generated content', () => {
|
|
41
|
+
const div = document.createElement('div');
|
|
42
|
+
document.body.appendChild(div);
|
|
43
|
+
|
|
44
|
+
expect(getCSSGeneratedContent(div)).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should get ::before content', () => {
|
|
48
|
+
const div = document.createElement('div');
|
|
49
|
+
div.className = 'with-before';
|
|
50
|
+
document.body.appendChild(div);
|
|
51
|
+
|
|
52
|
+
expect(getCSSGeneratedContent(div, 'before')).toBe('Before Content');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should get ::after content', () => {
|
|
56
|
+
const div = document.createElement('div');
|
|
57
|
+
div.className = 'with-after';
|
|
58
|
+
document.body.appendChild(div);
|
|
59
|
+
|
|
60
|
+
expect(getCSSGeneratedContent(div, 'after')).toBe('After Content');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should get both ::before and ::after content', () => {
|
|
64
|
+
const div = document.createElement('div');
|
|
65
|
+
div.className = 'with-both';
|
|
66
|
+
document.body.appendChild(div);
|
|
67
|
+
|
|
68
|
+
expect(getCSSGeneratedContent(div)).toBe('Before Text After Text');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should get both content by default', () => {
|
|
72
|
+
const div = document.createElement('div');
|
|
73
|
+
div.className = 'with-both';
|
|
74
|
+
document.body.appendChild(div);
|
|
75
|
+
|
|
76
|
+
expect(getCSSGeneratedContent(div)).toBe(getCSSGeneratedContent(div, 'both'));
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should handle quoted content', () => {
|
|
80
|
+
const div = document.createElement('div');
|
|
81
|
+
div.className = 'with-quotes';
|
|
82
|
+
document.body.appendChild(div);
|
|
83
|
+
|
|
84
|
+
expect(getCSSGeneratedContent(div, 'before')).toBe('Quoted Text');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should return false for empty content', () => {
|
|
88
|
+
const div = document.createElement('div');
|
|
89
|
+
div.className = 'empty-content';
|
|
90
|
+
document.body.appendChild(div);
|
|
91
|
+
|
|
92
|
+
expect(getCSSGeneratedContent(div)).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should return false for content: none', () => {
|
|
96
|
+
const div = document.createElement('div');
|
|
97
|
+
div.className = 'no-content';
|
|
98
|
+
document.body.appendChild(div);
|
|
99
|
+
|
|
100
|
+
expect(getCSSGeneratedContent(div)).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
});
|