@afixt/test-utils 1.1.6 → 1.1.8

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.
Files changed (68) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/.github/dependabot.yml +36 -0
  3. package/.github/workflows/ci.yml +71 -0
  4. package/.github/workflows/security.yml +142 -0
  5. package/.husky/pre-commit +1 -0
  6. package/.jscpd.json +27 -0
  7. package/.markdownlint.json +9 -0
  8. package/.prettierignore +13 -0
  9. package/.prettierrc +10 -0
  10. package/docs/arrayUtils.js.html +2 -2
  11. package/docs/domUtils.js.html +2 -2
  12. package/docs/getAccessibleName.js.html +2 -2
  13. package/docs/getAccessibleText.js.html +2 -2
  14. package/docs/getAriaAttributesByElement.js.html +2 -2
  15. package/docs/getCSSGeneratedContent.js.html +2 -2
  16. package/docs/getComputedRole.js.html +2 -2
  17. package/docs/getFocusableElements.js.html +2 -2
  18. package/docs/getGeneratedContent.js.html +2 -2
  19. package/docs/getImageText.js.html +2 -2
  20. package/docs/getStyleObject.js.html +2 -2
  21. package/docs/global.html +2 -2
  22. package/docs/hasAccessibleName.js.html +2 -2
  23. package/docs/hasAttribute.js.html +2 -2
  24. package/docs/hasCSSGeneratedContent.js.html +2 -2
  25. package/docs/hasHiddenParent.js.html +2 -2
  26. package/docs/hasParent.js.html +2 -2
  27. package/docs/hasValidAriaAttributes.js.html +2 -2
  28. package/docs/hasValidAriaRole.js.html +2 -2
  29. package/docs/index.html +2 -2
  30. package/docs/index.js.html +2 -2
  31. package/docs/isAriaAttributesValid.js.html +2 -2
  32. package/docs/isComplexTable.js.html +2 -2
  33. package/docs/isDataTable.js.html +2 -2
  34. package/docs/isFocusable.js.html +2 -2
  35. package/docs/isHidden.js.html +2 -2
  36. package/docs/isOffScreen.js.html +2 -2
  37. package/docs/isValidUrl.js.html +2 -2
  38. package/docs/isVisible.js.html +2 -2
  39. package/docs/module-afixt-test-utils.html +2 -2
  40. package/docs/scripts/core.js +726 -726
  41. package/docs/scripts/core.min.js +22 -22
  42. package/docs/scripts/resize.js +90 -90
  43. package/docs/scripts/search.js +265 -265
  44. package/docs/scripts/third-party/Apache-License-2.0.txt +202 -202
  45. package/docs/scripts/third-party/fuse.js +8 -8
  46. package/docs/scripts/third-party/hljs-line-num-original.js +369 -369
  47. package/docs/scripts/third-party/hljs-original.js +5171 -5171
  48. package/docs/scripts/third-party/popper.js +5 -5
  49. package/docs/scripts/third-party/tippy.js +1 -1
  50. package/docs/scripts/third-party/tocbot.js +671 -671
  51. package/docs/styles/clean-jsdoc-theme-base.css +1159 -1159
  52. package/docs/styles/clean-jsdoc-theme-dark.css +412 -412
  53. package/docs/styles/clean-jsdoc-theme-light.css +482 -482
  54. package/docs/styles/clean-jsdoc-theme-scrollbar.css +29 -29
  55. package/docs/testContrast.js.html +2 -2
  56. package/docs/testLang.js.html +2 -2
  57. package/docs/testOrder.js.html +2 -2
  58. package/eslint.config.mjs +84 -0
  59. package/package.json +68 -41
  60. package/scratchpads/issue-6-standardize-repo.md +109 -0
  61. package/src/getAccessibleName.js +175 -108
  62. package/src/getAccessibleText.js +71 -42
  63. package/src/getCSSGeneratedContent.js +23 -48
  64. package/src/hasCSSGeneratedContent.js +11 -11
  65. package/test/getAccessibleName.test.js +379 -315
  66. package/test/getAccessibleText.test.js +375 -308
  67. package/.eslintrc +0 -78
  68. package/.github/workflows/test.yml +0 -26
@@ -1,7 +1,9 @@
1
- const { isEmpty } = require("./stringUtils.js");
1
+ const { isEmpty } = require('./stringUtils.js');
2
2
 
3
3
  /**
4
4
  * Get all accessible text for an element, including aria-labels and content from children.
5
+ * Traverses the DOM subtree collecting text from text nodes, img alt attributes,
6
+ * and input[type="image"] alt attributes.
5
7
  * @param {Element} el - The DOM element.
6
8
  * @returns {string} The accessible text.
7
9
  */
@@ -13,55 +15,82 @@ function getAccessibleText(el) {
13
15
  if (!el.isConnected) {
14
16
  return '';
15
17
  }
16
-
17
- let textContent = "";
18
-
19
- // Check for element's own text content
20
- if (el.textContent) {
21
- textContent = el.textContent.trim();
22
- }
23
-
24
- // Check for aria-label
25
- if (el.hasAttribute("aria-label")) {
26
- const ariaLabel = el.getAttribute("aria-label").trim();
18
+
19
+ // Check for aria-label first (highest priority)
20
+ if (el.hasAttribute('aria-label')) {
21
+ const ariaLabel = el.getAttribute('aria-label').trim();
27
22
  if (ariaLabel) {
28
- // Prioritize aria-label if present
29
23
  return ariaLabel;
30
24
  }
31
25
  }
32
-
33
- // Check for img alt text
34
- if (el.tagName.toLowerCase() === "img" && el.hasAttribute("alt")) {
35
- return el.getAttribute("alt").trim();
26
+
27
+ // Check for img alt text when the element itself is an img
28
+ if (el.tagName.toLowerCase() === 'img' && el.hasAttribute('alt')) {
29
+ return el.getAttribute('alt').trim();
30
+ }
31
+
32
+ // Check for input[type="image"] alt text when the element itself is one
33
+ if (
34
+ el.tagName.toLowerCase() === 'input' &&
35
+ el.getAttribute('type') === 'image' &&
36
+ el.hasAttribute('alt')
37
+ ) {
38
+ return el.getAttribute('alt').trim();
36
39
  }
37
-
38
- // Handle other elements by getting visible text
39
- if (!textContent) {
40
- const walker = document.createTreeWalker(
41
- el,
42
- NodeFilter.SHOW_TEXT,
43
- {
44
- acceptNode: function (node) {
45
- // Only accept non-empty text nodes
46
- return node.nodeType === Node.TEXT_NODE && !isEmpty(node.nodeValue)
47
- ? NodeFilter.FILTER_ACCEPT
48
- : NodeFilter.FILTER_REJECT;
49
- },
40
+
41
+ // Collect accessible text from the subtree, including text nodes
42
+ // and alt text from embedded images
43
+ const parts = collectSubtreeText(el);
44
+ return parts.join(' ').replace(/\s+/g, ' ').trim();
45
+ }
46
+
47
+ /**
48
+ * Recursively collect accessible text parts from an element's subtree.
49
+ * Handles text nodes, img alt text, and input[type="image"] alt text.
50
+ * @param {Node} node - The DOM node to traverse.
51
+ * @returns {string[]} Array of text parts found in the subtree.
52
+ */
53
+ function collectSubtreeText(node) {
54
+ const parts = [];
55
+
56
+ for (let child = node.firstChild; child; child = child.nextSibling) {
57
+ if (child.nodeType === Node.TEXT_NODE) {
58
+ const text = child.nodeValue.trim();
59
+ if (!isEmpty(text)) {
60
+ parts.push(text);
61
+ }
62
+ } else if (child.nodeType === Node.ELEMENT_NODE) {
63
+ const tag = child.tagName.toLowerCase();
64
+
65
+ // img with non-empty alt contributes its alt text
66
+ if (tag === 'img' && child.hasAttribute('alt')) {
67
+ const alt = child.getAttribute('alt').trim();
68
+ if (alt) {
69
+ parts.push(alt);
70
+ }
71
+ continue;
50
72
  }
51
- );
52
-
53
- let textNodes = [];
54
- let node;
55
-
56
- while ((node = walker.nextNode())) {
57
- textNodes.push(node.nodeValue.trim());
73
+
74
+ // input[type="image"] with non-empty alt contributes its alt text
75
+ if (
76
+ tag === 'input' &&
77
+ child.getAttribute('type') === 'image' &&
78
+ child.hasAttribute('alt')
79
+ ) {
80
+ const alt = child.getAttribute('alt').trim();
81
+ if (alt) {
82
+ parts.push(alt);
83
+ }
84
+ continue;
85
+ }
86
+
87
+ // Recurse into other element children
88
+ parts.push(...collectSubtreeText(child));
58
89
  }
59
-
60
- textContent = textNodes.join(" ").trim();
61
90
  }
62
-
63
- return textContent;
91
+
92
+ return parts;
64
93
  }
65
94
 
66
95
  // Export for CommonJS module usage
67
- module.exports = { getAccessibleText };
96
+ module.exports = { getAccessibleText };
@@ -1,67 +1,42 @@
1
1
  /**
2
2
  * Gets the CSS generated content for an element's ::before or ::after pseudo-elements.
3
- * Unlike getGeneratedContent, this function focuses only on CSS-generated content
4
- * and does not include the element's own text content.
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
- try {
34
- // This would be the actual implementation for browsers
35
- let content = '';
13
+ let content = '';
36
14
 
37
- if (pseudoElement === 'before' || pseudoElement === 'both') {
38
- const style = window.getComputedStyle(el, '::before');
39
- const before = style.getPropertyValue('content');
40
- if (before && before !== 'none' && before !== 'normal') {
41
- // Remove quotes if present
42
- const cleanBefore = before.replace(/^["'](.*)["']$/, '$1');
43
- if (cleanBefore) {
44
- content += cleanBefore;
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
- if (pseudoElement === 'after' || pseudoElement === 'both') {
50
- const style = window.getComputedStyle(el, '::after');
51
- const after = style.getPropertyValue('content');
52
- if (after && after !== 'none' && after !== 'normal') {
53
- // Remove quotes if present
54
- const cleanAfter = after.replace(/^["'](.*)["']$/, '$1');
55
- if (cleanAfter) {
56
- content += (content ? ' ' : '') + cleanAfter;
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 { getGeneratedContent } = require('./getGeneratedContent.js');
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
- * or text content.
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 any generated content, otherwise false.
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 getGeneratedContent and convert its result to a boolean
15
- const content = getGeneratedContent(el);
16
-
17
- // getGeneratedContent returns either a string or false
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