diff --git a/lib/checks/color/link-in-text-block-evaluate.js b/lib/checks/color/link-in-text-block-evaluate.js index be872cd71..0cb898936 100644 --- a/lib/checks/color/link-in-text-block-evaluate.js +++ b/lib/checks/color/link-in-text-block-evaluate.js @@ -136,6 +136,14 @@ function getColorContrast(node, parentBlock) { nodeColor = getForegroundColor(node); parentColor = getForegroundColor(parentBlock); + if (!nodeColor) { + nodeColor = new axe.commons.color.Color(0, 0, 0, 0); + } + + if (!parentColor) { + parentColor = new axe.commons.color.Color(0, 0, 0, 0); + } + if (!nodeColor || !parentColor) { return undefined; } diff --git a/lib/commons/aria/ariadescribedby-text.js b/lib/commons/aria/ariadescribedby-text.js new file mode 100644 index 000000000..d81252d5a --- /dev/null +++ b/lib/commons/aria/ariadescribedby-text.js @@ -0,0 +1,46 @@ +/** + * Get the accessible name based on aria-describedby + * + * @deprecated Do not use Element directly. Pass VirtualNode instead + * @param {VirtualNode|Element} element + * @param {Object} context + * @property {Bool} inLabelledByContext Whether or not the lookup is part of aria-describedby reference + * @property {Bool} inControlContext Whether or not the lookup is part of a native label reference + * @property {Element} startNode First node in accessible name computation + * @property {Bool} debug Enable logging for formControlValue + * @return {string} Concatenated text value for referenced elements + */ +function ariadescribedbyText(element, context = {}) { + const { vNode } = axe.utils.nodeLookup(element); + if (vNode?.props.nodeType !== 1) { + return ''; + } + + if ( + vNode.props.nodeType !== 1 || + context.inLabelledByContext || + context.inControlContext || + !vNode.attr('aria-describedby') + ) { + return ''; + } + + const refs = axe.commons.dom + .idrefs(vNode, 'aria-describedby') + .filter(elm => elm); + return refs.reduce((accessibleName, elm) => { + const accessibleNameAdd = axe.commons.text.accessibleText(elm, { + // Prevent the infinite reference loop: + inLabelledByContext: true, + startNode: context.startNode || vNode, + ...context + }); + + if (!accessibleName) { + return accessibleNameAdd; + } + return `${accessibleName} ${accessibleNameAdd}`; + }, ''); +} + +export default ariadescribedbyText; diff --git a/lib/commons/aria/index.js b/lib/commons/aria/index.js index 9eb5bb3a8..86d310ee8 100644 --- a/lib/commons/aria/index.js +++ b/lib/commons/aria/index.js @@ -6,6 +6,7 @@ export { default as allowedAttr } from './allowed-attr'; export { default as arialabelText } from './arialabel-text'; export { default as arialabelledbyText } from './arialabelledby-text'; +export { default as ariadescribedbyText } from './ariadescribedby-text'; export { default as getAccessibleRefs } from './get-accessible-refs'; export { default as getElementUnallowedRoles } from './get-element-unallowed-roles'; export { default as getExplicitRole } from './get-explicit-role'; diff --git a/lib/commons/dom/get-overflow-hidden-ancestors.js b/lib/commons/dom/get-overflow-hidden-ancestors.js index 79ca04cee..8ace42907 100644 --- a/lib/commons/dom/get-overflow-hidden-ancestors.js +++ b/lib/commons/dom/get-overflow-hidden-ancestors.js @@ -28,7 +28,7 @@ const getOverflowHiddenAncestors = memoize( ancestors.push(vNode); } } else { - if (overflow.includes('hidden')) { + if (overflow === 'hidden' || overflow.includes('clip')) { ancestors.push(vNode); } } diff --git a/lib/core/base/rule.js b/lib/core/base/rule.js index e42c74292..8432760a1 100644 --- a/lib/core/base/rule.js +++ b/lib/core/base/rule.js @@ -12,6 +12,7 @@ import { } from '../utils'; import { isVisibleToScreenReaders } from '../../commons/dom'; import constants from '../constants'; +import cache from './cache'; export default function Rule(spec, parentAudit) { this._audit = parentAudit; @@ -245,6 +246,7 @@ Rule.prototype.run = function run(context, options = {}, resolve, reject) { try { // Matches throws an error when it lacks support for document methods nodes = this.gatherAndMatchNodes(context, options); + cache.set(this.id, nodes.length); } catch (error) { // Exit the rule execution if matches fails reject(new SupportError({ cause: error, ruleId: this.id })); diff --git a/lib/core/utils/check-helper.js b/lib/core/utils/check-helper.js index fbedf3645..71960bac5 100644 --- a/lib/core/utils/check-helper.js +++ b/lib/core/utils/check-helper.js @@ -25,6 +25,9 @@ function checkHelper(checkResult, options, resolve, reject) { data(data) { checkResult.data = data; }, + getCheckData() { + return checkResult.data; + }, relatedNodes(nodes) { if (!window.Node) { return; diff --git a/lib/core/utils/dq-element.js b/lib/core/utils/dq-element.js index 3cf790fe1..47bcc64e8 100644 --- a/lib/core/utils/dq-element.js +++ b/lib/core/utils/dq-element.js @@ -26,7 +26,11 @@ function getSource(element) { if (!source && typeof window.XMLSerializer === 'function') { source = new window.XMLSerializer().serializeToString(element); } - return truncate(source || ''); + let htmlString = truncate(source || ''); + // Remove unwanted attributes + const regex = /\s*data-percy-[^=]+="[^"]*"/g; + htmlString = htmlString.replace(regex, ''); + return htmlString; } /**