diff --git a/lib/checks/label/help-same-as-label-evaluate.js b/lib/checks/label/help-same-as-label-evaluate.js index 47b15769f6..8993d1f552 100644 --- a/lib/checks/label/help-same-as-label-evaluate.js +++ b/lib/checks/label/help-same-as-label-evaluate.js @@ -1,5 +1,10 @@ -import { labelVirtual, accessibleText, sanitize } from '../../commons/text'; -import { idrefs } from '../../commons/dom'; +import { + labelVirtual, + accessibleTextVirtual, + sanitize +} from '../../commons/text'; +import { getResolvedRefs } from '../../commons/dom'; +import { hasAriaValue } from '../../commons/aria'; function helpSameAsLabelEvaluate(node, options, virtualNode) { const labelText = labelVirtual(virtualNode); @@ -12,11 +17,11 @@ function helpSameAsLabelEvaluate(node, options, virtualNode) { if (!check) { check = ''; - if (node.getAttribute('aria-describedby')) { - const ref = idrefs(node, 'aria-describedby'); + if (hasAriaValue(virtualNode, 'aria-describedby')) { + const ref = getResolvedRefs(virtualNode, 'aria-describedby'); check = ref - .map(thing => { - return thing ? accessibleText(thing) : ''; + .map(vNode => { + return vNode ? accessibleTextVirtual(vNode) : ''; }) .join(''); } diff --git a/lib/checks/label/multiple-label-evaluate.js b/lib/checks/label/multiple-label-evaluate.js index 0a3de55dbd..a79543930a 100644 --- a/lib/checks/label/multiple-label-evaluate.js +++ b/lib/checks/label/multiple-label-evaluate.js @@ -2,7 +2,7 @@ import { getRootNode, isHiddenForEveryone, isVisibleToScreenReaders, - idrefs + getResolvedRefs } from '../../commons/dom'; import { escapeSelector } from '../../core/utils'; @@ -40,8 +40,10 @@ function multipleLabelEvaluate(node) { return undefined; } // make sure the ONE AT visible label is in the list of idRefs of aria-labelledby - const labelledby = idrefs(node, 'aria-labelledby'); - return !labelledby.includes(ATVisibleLabels[0]) ? undefined : false; + const labelledby = getResolvedRefs(node, 'aria-labelledby'); + return !labelledby.some(ref => ref?.actualNode === ATVisibleLabels[0]) + ? undefined + : false; } // only 1 CSS visible label diff --git a/lib/checks/label/title-only-evaluate.js b/lib/checks/label/title-only-evaluate.js index 573504c16f..9d3a7d5cab 100644 --- a/lib/checks/label/title-only-evaluate.js +++ b/lib/checks/label/title-only-evaluate.js @@ -1,11 +1,12 @@ import { labelVirtual, titleText } from '../../commons/text'; +import { getAriaValue } from '../../commons/aria'; function titleOnlyEvaluate(node, options, virtualNode) { const labelText = labelVirtual(virtualNode); const title = titleText(virtualNode); - const ariaDescribedBy = virtualNode.attr('aria-describedby'); + const ariaDescribedBy = getAriaValue(virtualNode, 'aria-describedby'); - return !labelText && !!(title || ariaDescribedBy); + return !labelText && !!(title || ariaDescribedBy?.value); } export default titleOnlyEvaluate; diff --git a/test/checks/label/help-same-as-label.js b/test/checks/label/help-same-as-label.js index e3ba0f8fe4..e5f57b7059 100644 --- a/test/checks/label/help-same-as-label.js +++ b/test/checks/label/help-same-as-label.js @@ -1,4 +1,6 @@ describe('help-same-as-label', () => { + const { checkSetup, html } = axe.testUtils; + const checkEvaluate = axe.testUtils.getCheckEvaluate('help-same-as-label'); const fixture = document.getElementById('fixture'); afterEach(() => { @@ -82,4 +84,18 @@ describe('help-same-as-label', () => { ) ); }); + + describe('ElementInternals', () => { + it('should return true if an element has a label and elementInternals aria-describedby with the same text', () => { + const params = checkSetup(html` +
Duplicate
+ + `); + assert.isTrue(checkEvaluate.apply(null, params)); + }); + }); }); diff --git a/test/checks/label/multiple-label.js b/test/checks/label/multiple-label.js index 8f7f6169d2..ac29af95a0 100644 --- a/test/checks/label/multiple-label.js +++ b/test/checks/label/multiple-label.js @@ -243,6 +243,38 @@ describe('multiple-label', () => { ); }); + // ariaLabelledByElements (reflected AOM property) coverage adapted from + // @jcfranco's work in #5187 (issue #4943) + it('should return false when ariaLabelledByElements overrides aria-labelledby', () => { + fixtureSetup(html` + + + + `); + const target = fixture.querySelector('#F'); + target.ariaLabelledByElements = [fixture.querySelector('#H')]; + assert.isFalse( + axe.testUtils + .getCheckEvaluate('multiple-label') + .call(checkContext, target) + ); + }); + + it('should return false when ariaLabelledByElements overrides aria-labelledby with aria-label present', () => { + fixtureSetup(html` + + + + `); + const target = fixture.querySelector('#F'); + target.ariaLabelledByElements = [fixture.querySelector('#H')]; + assert.isFalse( + axe.testUtils + .getCheckEvaluate('multiple-label') + .call(checkContext, target) + ); + }); + it('should return false given multiple labels, one label visible, and no aria-labelledby', () => { fixtureSetup(html` @@ -337,4 +369,19 @@ describe('multiple-label', () => { .call(checkContext, shadowTarget.firstElementChild) ); }); + + describe('ElementInternals', () => { + it('should return false given multiple labels, one label AT visible, and elementInternals aria-labelledby for AT visible', () => { + const checkEvaluate = axe.testUtils.getCheckEvaluate('multiple-label'); + const params = axe.testUtils.checkSetup(html` + + + + `); + assert.isFalse(checkEvaluate.call(checkContext, ...params)); + }); + }); }); diff --git a/test/checks/label/title-only.js b/test/checks/label/title-only.js index 46a607b5f6..4da7277e2a 100644 --- a/test/checks/label/title-only.js +++ b/test/checks/label/title-only.js @@ -1,4 +1,7 @@ describe('title-only', () => { + const checkSetup = axe.testUtils.checkSetup; + const html = axe.testUtils.html; + const checkEvaluate = axe.testUtils.getCheckEvaluate('title-only'); const fixture = document.getElementById('fixture'); afterEach(() => { @@ -61,4 +64,31 @@ describe('title-only', () => { ) ); }); + + it('should return false if aria-describedby is empty and there is no title or label', () => { + const node = document.createElement('input'); + node.type = 'text'; + node.setAttribute('aria-describedby', ''); + + fixture.appendChild(node); + + axe.testUtils.flatTreeSetup(fixture); + + assert.isFalse( + checkEvaluate(node, undefined, axe.utils.getNodeFromTree(node)) + ); + }); + + describe('ElementInternals', () => { + it('should return true if an element only has elementInternals aria-describedby', () => { + const params = checkSetup(html` +
description
+ + `); + assert.isTrue(checkEvaluate.apply(null, params)); + }); + }); });