@afixt/test-utils 1.1.6 → 1.1.7
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/package.json +1 -1
- package/src/getAccessibleName.js +24 -1
- package/src/getCSSGeneratedContent.js +23 -48
- package/src/hasCSSGeneratedContent.js +11 -11
package/package.json
CHANGED
package/src/getAccessibleName.js
CHANGED
|
@@ -436,6 +436,27 @@ function getAccessibleName(element) {
|
|
|
436
436
|
}
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
+
// STEP 15: Landmark elements that require explicit accessible names
|
|
440
|
+
// Navigation landmarks (nav elements and role="navigation") should NOT get
|
|
441
|
+
// their accessible name from text content - they require explicit labeling
|
|
442
|
+
// via aria-labelledby, aria-label, or title attribute.
|
|
443
|
+
// Since steps 1 & 2 already checked aria-labelledby and aria-label,
|
|
444
|
+
// we only need to check title here, then return false.
|
|
445
|
+
const isNavigation = element.tagName.toLowerCase() === "nav" ||
|
|
446
|
+
element.getAttribute("role") === "navigation";
|
|
447
|
+
|
|
448
|
+
if (isNavigation) {
|
|
449
|
+
// Title attribute is valid for navigation landmarks
|
|
450
|
+
if (element.hasAttribute("title")) {
|
|
451
|
+
const titleValue = element.getAttribute("title");
|
|
452
|
+
if (strlen(titleValue) > 0) {
|
|
453
|
+
return titleValue;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// Navigation landmarks do not get accessible name from text content
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
|
|
439
460
|
// Absolute last ditch for the whole plugin:
|
|
440
461
|
// use the accessible text from the element itself.
|
|
441
462
|
if (strlen(getAccessibleText(element)) > 0) {
|
|
@@ -458,9 +479,11 @@ function isNotVisible(element) {
|
|
|
458
479
|
if (!element) return true;
|
|
459
480
|
|
|
460
481
|
// These elements are inherently not visible
|
|
482
|
+
// Note: 'area' is NOT included here because area elements DO have accessible names
|
|
483
|
+
// via the alt attribute and should participate in accessible name calculation
|
|
461
484
|
const nonVisibleSelectors = [
|
|
462
485
|
'base', 'head', 'meta', 'title', 'link', 'style', 'script', 'br', 'nobr', 'col', 'embed',
|
|
463
|
-
'input[type="hidden"]', 'keygen', 'source', 'track', 'wbr', 'datalist', '
|
|
486
|
+
'input[type="hidden"]', 'keygen', 'source', 'track', 'wbr', 'datalist', 'param', 'noframes', 'ruby > rp'
|
|
464
487
|
];
|
|
465
488
|
|
|
466
489
|
if (nonVisibleSelectors.some(selector => matchesSelector(element, selector))) {
|
|
@@ -1,67 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Gets the CSS generated content for an element's ::before or ::after pseudo-elements.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* This function only checks for content added via the CSS `content` property,
|
|
4
|
+
* not the element's own text content.
|
|
5
|
+
*
|
|
6
6
|
* @param {Element} el - The DOM element to check
|
|
7
7
|
* @param {string} [pseudoElement='both'] - Which pseudo-element to check ('before', 'after', or 'both')
|
|
8
8
|
* @returns {string|boolean} The generated content as a string or false if none exists
|
|
9
9
|
*/
|
|
10
10
|
function getCSSGeneratedContent(el, pseudoElement = 'both') {
|
|
11
11
|
if (!el) return false;
|
|
12
|
-
|
|
13
|
-
// jsdom doesn't fully support getComputedStyle for pseudo-elements
|
|
14
|
-
// This is test code to make the tests pass in the JSDOM environment
|
|
15
|
-
if (typeof window !== 'undefined' && window.document && el.classList) {
|
|
16
|
-
if (pseudoElement === 'before' || pseudoElement === 'both') {
|
|
17
|
-
if (el.classList.contains('with-before')) return 'Before Content';
|
|
18
|
-
if (el.classList.contains('with-both')) return pseudoElement === 'both' ? 'Before Text After Text' : 'Before Text';
|
|
19
|
-
if (el.classList.contains('with-quotes')) return 'Quoted Text';
|
|
20
|
-
if (el.classList.contains('url-content')) return 'url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (pseudoElement === 'after' || pseudoElement === 'both') {
|
|
24
|
-
if (el.classList.contains('with-after')) return 'After Content';
|
|
25
|
-
if (el.classList.contains('with-both') && pseudoElement === 'after') return 'After Text';
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// If element has classList but no special test classes, we're in JSDOM without mocked getComputedStyle
|
|
29
|
-
// Return false early to avoid JSDOM errors being logged
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
12
|
|
|
33
|
-
|
|
34
|
-
// This would be the actual implementation for browsers
|
|
35
|
-
let content = '';
|
|
13
|
+
let content = '';
|
|
36
14
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
15
|
+
if (pseudoElement === 'before' || pseudoElement === 'both') {
|
|
16
|
+
const style = window.getComputedStyle(el, '::before');
|
|
17
|
+
const before = style.getPropertyValue('content');
|
|
18
|
+
if (before && before !== 'none' && before !== 'normal' && before !== '""' && before !== "''") {
|
|
19
|
+
// Remove surrounding quotes if present
|
|
20
|
+
const cleanBefore = before.replace(/^["'](.*)["']$/, '$1');
|
|
21
|
+
if (cleanBefore) {
|
|
22
|
+
content += cleanBefore;
|
|
46
23
|
}
|
|
47
24
|
}
|
|
25
|
+
}
|
|
48
26
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
27
|
+
if (pseudoElement === 'after' || pseudoElement === 'both') {
|
|
28
|
+
const style = window.getComputedStyle(el, '::after');
|
|
29
|
+
const after = style.getPropertyValue('content');
|
|
30
|
+
if (after && after !== 'none' && after !== 'normal' && after !== '""' && after !== "''") {
|
|
31
|
+
// Remove surrounding quotes if present
|
|
32
|
+
const cleanAfter = after.replace(/^["'](.*)["']$/, '$1');
|
|
33
|
+
if (cleanAfter) {
|
|
34
|
+
content += (content ? ' ' : '') + cleanAfter;
|
|
58
35
|
}
|
|
59
36
|
}
|
|
60
|
-
|
|
61
|
-
return content ? content.trim() : false;
|
|
62
|
-
} catch (error) {
|
|
63
|
-
return false;
|
|
64
37
|
}
|
|
38
|
+
|
|
39
|
+
return content ? content.trim() : false;
|
|
65
40
|
}
|
|
66
41
|
|
|
67
42
|
module.exports = {
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
|
|
2
|
-
const {
|
|
2
|
+
const { getCSSGeneratedContent } = require('./getCSSGeneratedContent.js');
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Checks if an element has CSS generated content in its ::before or ::after pseudo-elements
|
|
6
|
-
*
|
|
5
|
+
* Checks if an element has CSS generated content in its ::before or ::after pseudo-elements.
|
|
6
|
+
* This only checks for content added via the CSS `content` property, not regular HTML text content.
|
|
7
7
|
*
|
|
8
|
-
* @param {HTMLElement} el - The element to check for generated content.
|
|
9
|
-
* @returns {boolean} True if the element has
|
|
8
|
+
* @param {HTMLElement} el - The element to check for CSS-generated content.
|
|
9
|
+
* @returns {boolean} True if the element has CSS-generated content, otherwise false.
|
|
10
10
|
*/
|
|
11
11
|
function hasCSSGeneratedContent(el) {
|
|
12
12
|
if (!el) return false;
|
|
13
|
-
|
|
14
|
-
// Use
|
|
15
|
-
const content =
|
|
16
|
-
|
|
17
|
-
//
|
|
18
|
-
// We want to return true if content exists, false otherwise
|
|
13
|
+
|
|
14
|
+
// Use getCSSGeneratedContent which only checks ::before and ::after pseudo-elements
|
|
15
|
+
const content = getCSSGeneratedContent(el);
|
|
16
|
+
|
|
17
|
+
// getCSSGeneratedContent returns either a string or false
|
|
18
|
+
// We want to return true if CSS-generated content exists, false otherwise
|
|
19
19
|
return content !== false;
|
|
20
20
|
}
|
|
21
21
|
|