From d21d27868b7c207df5365ac6613cd4af289069d0 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Tue, 24 Jan 2023 16:27:06 +0100 Subject: [PATCH 1/6] fix(aria-input-field-name): skip combobox popups --- lib/commons/aria/is-combobox-popup.js | 40 +++++++++ .../scrollable-region-focusable-matches.js | 89 ++++--------------- .../aria-input-field-name.html | 17 +++- 3 files changed, 73 insertions(+), 73 deletions(-) create mode 100644 lib/commons/aria/is-combobox-popup.js diff --git a/lib/commons/aria/is-combobox-popup.js b/lib/commons/aria/is-combobox-popup.js new file mode 100644 index 0000000000..c8ffb8bbd5 --- /dev/null +++ b/lib/commons/aria/is-combobox-popup.js @@ -0,0 +1,40 @@ +import getExplicitRole from './get-explicit-role'; +import ariaAttrs from '../standards/aria-attrs'; +import closest from '../core/utils/closest'; +import getRootNode from '../core/utils/get-root-node'; +import tokenList from '../core/utils/token-list'; + +export default function isComboboxPopup(virtualNode) { + const popupRoles = ariaAttrs['aria-haspopup'].values; + const node = virtualNode.actualNode; + + const role = getExplicitRole(virtualNode); + if (!popupRoles.includes(role)) { + return false; + } + + // in ARIA 1.1 the container has role=combobox + if (closest(virtualNode, '[role~="combobox"]')) { + return true; + } + + // in ARIA 1.0 and 1.2 the combobox owns (1.0) or controls (1.2) the listbox + const id = virtualNode.attr('id'); + if (!id) { + return false; + } + + const doc = getRootNode(node); + const owned = Array.from( + doc.querySelectorAll(`[aria-owns~="${id}"], [aria-controls~="${id}"]`) + ); + const comboboxOwned = owned.some(el => { + const roles = tokenList(el.getAttribute('role')); + return roles.includes('combobox'); + }); + if (!comboboxOwned) { + return false; + } + + return true; +} diff --git a/lib/rules/scrollable-region-focusable-matches.js b/lib/rules/scrollable-region-focusable-matches.js index 71acf09d24..4141dfc7e1 100644 --- a/lib/rules/scrollable-region-focusable-matches.js +++ b/lib/rules/scrollable-region-focusable-matches.js @@ -1,75 +1,20 @@ -import { hasContentVirtual } from '../commons/dom'; -import { getExplicitRole } from '../commons/aria'; -import { - querySelectorAll, - getScroll, - closest, - getRootNode, - tokenList -} from '../core/utils'; -import ariaAttrs from '../standards/aria-attrs'; - -function scrollableRegionFocusableMatches(node, virtualNode) { - /** - * Note: - * `excludeHidden=true` for this rule, thus considering only elements in the accessibility tree. - */ - - /** - * if not scrollable -> `return` - */ - if (!!getScroll(node, 13) === false) { - return false; - } - - /** - * ignore scrollable regions owned by combobox. limit to roles - * ownable by combobox so we don't keep calling closest for every - * node (which would be slow) - * @see https://github.com/dequelabs/axe-core/issues/1763 - */ - const role = getExplicitRole(virtualNode); - if (ariaAttrs['aria-haspopup'].values.includes(role)) { - // in ARIA 1.1 the container has role=combobox - if (closest(virtualNode, '[role~="combobox"]')) { - return false; - } - - // in ARIA 1.0 and 1.2 the combobox owns (1.0) or controls (1.2) - // the listbox - const id = virtualNode.attr('id'); - if (id) { - const doc = getRootNode(node); - const owned = Array.from( - doc.querySelectorAll(`[aria-owns~="${id}"], [aria-controls~="${id}"]`) - ); - const comboboxOwned = owned.some(el => { - const roles = tokenList(el.getAttribute('role')); - return roles.includes('combobox'); - }); - - if (comboboxOwned) { - return false; - } - } - } - - /** - * check if node has visible contents - */ - const nodeAndDescendents = querySelectorAll(virtualNode, '*'); - const hasVisibleChildren = nodeAndDescendents.some(elm => - hasContentVirtual( - elm, - true, // noRecursion - true // ignoreAria - ) +import hasContentVirtual from '../commons/dom/has-content-virtual'; +import isComboboxPopup from '../commons/aria/is-combobox-popup'; +import querySelectorAll from '../core/utils/query-selector-all'; +import getScroll from '../core/utils/get-scroll'; + +export default function scrollableRegionFocusableMatches(node, virtualNode) { + return ( + getScroll(node, 13) && + isComboboxPopup(virtualNode) === false && + hasVisibleChildren(virtualNode) ); - if (!hasVisibleChildren) { - return false; - } - - return true; } -export default scrollableRegionFocusableMatches; +function hasVisibleChildren(vNode) { + const nodeAndDescendents = querySelectorAll(vNode, '*'); + return nodeAndDescendents.some(elm => + // (elm, noRecursion, ignoreAria) + hasContentVirtual(elm, true, true) + ); +} diff --git a/test/integration/rules/aria-input-field-name/aria-input-field-name.html b/test/integration/rules/aria-input-field-name/aria-input-field-name.html index bfd15eb387..7ca4b29858 100644 --- a/test/integration/rules/aria-input-field-name/aria-input-field-name.html +++ b/test/integration/rules/aria-input-field-name/aria-input-field-name.html @@ -1,6 +1,15 @@ -
England
+
+ England +
+

Select a color:

@@ -105,3 +114,9 @@
+ + + From d735b52b9f7c76bfe21617e91edf09befea10eae Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Tue, 24 Jan 2023 19:16:04 +0100 Subject: [PATCH 2/6] skip popups on no-naming-method-matches --- lib/commons/aria/index.js | 1 + lib/commons/aria/is-combobox-popup.js | 6 ++---- lib/rules/no-naming-method-matches.js | 10 +++++++--- .../scrollable-region-focusable-matches.js | 15 +++++++------- .../aria-input-field-name.html | 20 +++++++++---------- test/rule-matches/no-naming-method-matches.js | 9 +++++++++ 6 files changed, 37 insertions(+), 24 deletions(-) diff --git a/lib/commons/aria/index.js b/lib/commons/aria/index.js index d9f7bd4805..9eb5bb3a88 100644 --- a/lib/commons/aria/index.js +++ b/lib/commons/aria/index.js @@ -19,6 +19,7 @@ export { default as implicitNodes } from './implicit-nodes'; export { default as implicitRole } from './implicit-role'; export { default as isAccessibleRef } from './is-accessible-ref'; export { default as isAriaRoleAllowedOnElement } from './is-aria-role-allowed-on-element'; +export { default as isComboboxPopup } from './is-combobox-popup'; export { default as isUnsupportedRole } from './is-unsupported-role'; export { default as isValidRole } from './is-valid-role'; export { default as labelVirtual } from './label-virtual'; diff --git a/lib/commons/aria/is-combobox-popup.js b/lib/commons/aria/is-combobox-popup.js index c8ffb8bbd5..2c0dc3bb12 100644 --- a/lib/commons/aria/is-combobox-popup.js +++ b/lib/commons/aria/is-combobox-popup.js @@ -1,8 +1,6 @@ import getExplicitRole from './get-explicit-role'; -import ariaAttrs from '../standards/aria-attrs'; -import closest from '../core/utils/closest'; -import getRootNode from '../core/utils/get-root-node'; -import tokenList from '../core/utils/token-list'; +import ariaAttrs from '../../standards/aria-attrs'; +import { closest, getRootNode, tokenList } from '../../core/utils'; export default function isComboboxPopup(virtualNode) { const popupRoles = ariaAttrs['aria-haspopup'].values; diff --git a/lib/rules/no-naming-method-matches.js b/lib/rules/no-naming-method-matches.js index 2986f8247b..33404aaf86 100644 --- a/lib/rules/no-naming-method-matches.js +++ b/lib/rules/no-naming-method-matches.js @@ -1,6 +1,7 @@ -import { getExplicitRole } from '../commons/aria'; -import { querySelectorAll } from '../core/utils'; +import getExplicitRole from '../commons/aria/get-explicit-role'; +import isComboboxPopup from '../commons/aria/is-combobox-popup'; import getElementSpec from '../commons/standards/get-element-spec'; +import querySelectorAll from '../core/utils/query-selector-all'; /** * Filter out elements that have a naming method (i.e. img[alt], table > caption, etc.) @@ -10,7 +11,6 @@ function noNamingMethodMatches(node, virtualNode) { if (namingMethods && namingMethods.length !== 0) { return false; } - // Additionally, ignore combobox that get their name from a descendant input: if ( getExplicitRole(virtualNode) === 'combobox' && @@ -18,6 +18,10 @@ function noNamingMethodMatches(node, virtualNode) { ) { return false; } + // Ignore listboxes that are referenced by a combobox + if (isComboboxPopup(virtualNode)) { + return false; + } return true; } diff --git a/lib/rules/scrollable-region-focusable-matches.js b/lib/rules/scrollable-region-focusable-matches.js index 4141dfc7e1..34c4b08d77 100644 --- a/lib/rules/scrollable-region-focusable-matches.js +++ b/lib/rules/scrollable-region-focusable-matches.js @@ -1,19 +1,20 @@ import hasContentVirtual from '../commons/dom/has-content-virtual'; import isComboboxPopup from '../commons/aria/is-combobox-popup'; -import querySelectorAll from '../core/utils/query-selector-all'; -import getScroll from '../core/utils/get-scroll'; +import { querySelectorAll, getScroll } from '../core/utils'; export default function scrollableRegionFocusableMatches(node, virtualNode) { return ( - getScroll(node, 13) && + // The element scrolls + getScroll(node, 13) !== undefined && + // It's not a combobox popup, which commonly has keyboard focus added isComboboxPopup(virtualNode) === false && - hasVisibleChildren(virtualNode) + // And there's something actually worth scrolling to + isNoneEmptyElement(virtualNode) ); } -function hasVisibleChildren(vNode) { - const nodeAndDescendents = querySelectorAll(vNode, '*'); - return nodeAndDescendents.some(elm => +function isNoneEmptyElement(vNode) { + return querySelectorAll(vNode, '*').some(elm => // (elm, noRecursion, ignoreAria) hasContentVirtual(elm, true, true) ); diff --git a/test/integration/rules/aria-input-field-name/aria-input-field-name.html b/test/integration/rules/aria-input-field-name/aria-input-field-name.html index 7ca4b29858..a83a78cd27 100644 --- a/test/integration/rules/aria-input-field-name/aria-input-field-name.html +++ b/test/integration/rules/aria-input-field-name/aria-input-field-name.html @@ -5,11 +5,17 @@ aria-label="country" role="combobox" aria-expanded="true" - aria-controls="inapplicable4" + aria-controls="inapplicable1" > England
+ + +

Select a color:

@@ -96,13 +102,13 @@ - - + - + @@ -114,9 +120,3 @@
- - - diff --git a/test/rule-matches/no-naming-method-matches.js b/test/rule-matches/no-naming-method-matches.js index fd00c1eed8..9d37ad9d49 100644 --- a/test/rule-matches/no-naming-method-matches.js +++ b/test/rule-matches/no-naming-method-matches.js @@ -69,6 +69,15 @@ describe('no-naming-method-matches', function () { assert.isFalse(actual); }); + it('returns false for the popup of a role=`combobox`', function () { + var vNode = queryFixture( + '
' + + '
' + ); + var actual = rule.matches(null, vNode); + assert.isFalse(actual); + }); + it('returns true for a div with role=`button`', function () { var vNode = queryFixture('
'); var actual = rule.matches(null, vNode); From a92c6af381c13cc684a975d9ce6eb0634bad69d9 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Tue, 24 Jan 2023 19:32:35 +0100 Subject: [PATCH 3/6] Test isComboboxPopup --- test/commons/aria/is-combobox-popup.js | 52 ++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 test/commons/aria/is-combobox-popup.js diff --git a/test/commons/aria/is-combobox-popup.js b/test/commons/aria/is-combobox-popup.js new file mode 100644 index 0000000000..0dc227f6d6 --- /dev/null +++ b/test/commons/aria/is-combobox-popup.js @@ -0,0 +1,52 @@ +describe('isComboboxPopup', () => { + const { isComboboxPopup } = axe.commons.aria; + const { queryFixture } = axe.testUtils; + + it('does not match non-popup roles', () => { + const roles = ['main', 'combobox', 'textbox', 'button']; + for (const role of roles) { + const vNode = queryFixture( + `
+
` + ); + assert.isFalse(isComboboxPopup(vNode)); + } + }); + + for (const role of ['menu', 'listbox', 'tree', 'grid', 'dialog']) { + describe(role, () => { + it('is true when referenced with aria-controls (WAI-ARIA 1.2)', () => { + const vNode = queryFixture( + `
+
` + ); + assert.isTrue(isComboboxPopup(vNode)); + }); + + it('is true when its a child of the combobox (WAI-ARIA 1.1)', () => { + const vNode = queryFixture( + `
+
+
` + ); + assert.isTrue(isComboboxPopup(vNode)); + }); + + it('is true when referenced with aria-owned (WAI-ARIA 1.0)', () => { + const vNode = queryFixture( + `
+
` + ); + assert.isTrue(isComboboxPopup(vNode)); + }); + + it('is false when not related to the combobox', () => { + const vNode = queryFixture( + `
+
` + ); + assert.isFalse(isComboboxPopup(vNode)); + }); + }); + } +}); From 427c9c1bee22ac44d37c0129341a46dd4b822ed3 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Wed, 25 Jan 2023 12:23:45 +0100 Subject: [PATCH 4/6] Improve to run better on virtual trees --- lib/commons/aria/get-role.js | 4 +- lib/commons/aria/is-combobox-popup.js | 59 +++++--- test/commons/aria/is-combobox-popup.js | 137 +++++++++++++++--- test/rule-matches/no-naming-method-matches.js | 11 +- 4 files changed, 165 insertions(+), 46 deletions(-) diff --git a/lib/commons/aria/get-role.js b/lib/commons/aria/get-role.js index fb5fc7347f..3a3cc1a79b 100644 --- a/lib/commons/aria/get-role.js +++ b/lib/commons/aria/get-role.js @@ -3,7 +3,7 @@ import getImplicitRole from './implicit-role'; import getGlobalAriaAttrs from '../standards/get-global-aria-attrs'; import isFocusable from '../dom/is-focusable'; import { getNodeFromTree } from '../../core/utils'; -import AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node'; +import AbstractVirtualNode from '../../core/base/virtual-node/abstract-virtual-node'; // when an element inherits the presentational role from a parent // is not defined in the spec, but through testing it seems to be @@ -126,7 +126,7 @@ function hasConflictResolution(vNode) { */ function resolveRole(node, { noImplicit, ...roleOptions } = {}) { const vNode = - node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node); + node instanceof AbstractVirtualNode ? node : getNodeFromTree(node); if (vNode.props.nodeType !== 1) { return null; } diff --git a/lib/commons/aria/is-combobox-popup.js b/lib/commons/aria/is-combobox-popup.js index 2c0dc3bb12..b1c787ed7e 100644 --- a/lib/commons/aria/is-combobox-popup.js +++ b/lib/commons/aria/is-combobox-popup.js @@ -1,38 +1,55 @@ -import getExplicitRole from './get-explicit-role'; +import getRole from './get-role'; import ariaAttrs from '../../standards/aria-attrs'; -import { closest, getRootNode, tokenList } from '../../core/utils'; +import { getRootNode } from '../../core/utils'; -export default function isComboboxPopup(virtualNode) { - const popupRoles = ariaAttrs['aria-haspopup'].values; - const node = virtualNode.actualNode; - - const role = getExplicitRole(virtualNode); +/** + * Whether an element is the popup for a combobox + * @method isComboboxPopup + * @memberof axe.commons.aria + * @instance + * @param {VirtualNode} virtualNode + * @param {Object} optionsw + * @property {String[]} popupRoles Overrides which roles can be popup. Defaults to aria-haspopup values + * @returns {boolean} + */ +export default function isComboboxPopup(virtualNode, { popupRoles } = {}) { + const role = getRole(virtualNode); + popupRoles ??= ariaAttrs['aria-haspopup'].values; if (!popupRoles.includes(role)) { return false; } // in ARIA 1.1 the container has role=combobox - if (closest(virtualNode, '[role~="combobox"]')) { + const vParent = nearestParentWithRole(virtualNode); + if (isCombobox(vParent)) { return true; } - // in ARIA 1.0 and 1.2 the combobox owns (1.0) or controls (1.2) the listbox - const id = virtualNode.attr('id'); + const { id } = virtualNode.props; if (!id) { return false; } - const doc = getRootNode(node); - const owned = Array.from( - doc.querySelectorAll(`[aria-owns~="${id}"], [aria-controls~="${id}"]`) - ); - const comboboxOwned = owned.some(el => { - const roles = tokenList(el.getAttribute('role')); - return roles.includes('combobox'); - }); - if (!comboboxOwned) { - return false; + if (!virtualNode.actualNode) { + throw new Error('Unable to determine combobox popup without an actualNode'); } + const root = getRootNode(virtualNode.actualNode); + const ownedCombobox = root.querySelectorAll( + // aria-owns was from ARIA 1.0, aria-controls was from ARIA 1.2 + `[aria-owns~="${id}"][role~="combobox"]:not(select), + [aria-controls~="${id}"][role~="combobox"]:not(select)` + ); - return true; + return Array.from(ownedCombobox).some(isCombobox); +} + +const isCombobox = node => node && getRole(node) === 'combobox'; + +function nearestParentWithRole(vNode) { + while ((vNode = vNode.parent)) { + if (getRole(vNode, { noPresentational: true }) !== null) { + return vNode; + } + } + return null; } diff --git a/test/commons/aria/is-combobox-popup.js b/test/commons/aria/is-combobox-popup.js index 0dc227f6d6..65ff04ac2b 100644 --- a/test/commons/aria/is-combobox-popup.js +++ b/test/commons/aria/is-combobox-popup.js @@ -15,38 +15,131 @@ describe('isComboboxPopup', () => { for (const role of ['menu', 'listbox', 'tree', 'grid', 'dialog']) { describe(role, () => { - it('is true when referenced with aria-controls (WAI-ARIA 1.2)', () => { + it('is false when not related to the combobox', () => { const vNode = queryFixture( - `
+ `
` ); - assert.isTrue(isComboboxPopup(vNode)); + assert.isFalse(isComboboxPopup(vNode)); }); - it('is true when its a child of the combobox (WAI-ARIA 1.1)', () => { - const vNode = queryFixture( - `
-
-
` - ); - assert.isTrue(isComboboxPopup(vNode)); + describe('using aria-controls (ARIA 1.2 pattern)', () => { + it('is true when referenced', () => { + const vNode = queryFixture( + `
+
` + ); + assert.isTrue(isComboboxPopup(vNode)); + }); + + it('is false when controlled by a select element', () => { + const vNode = queryFixture( + ` +
` + ); + assert.isFalse(isComboboxPopup(vNode)); + }); + + it('is false when not controlled by a combobox', () => { + const vNode = queryFixture( + `
+
` + ); + assert.isFalse(isComboboxPopup(vNode)); + }); }); - it('is true when referenced with aria-owned (WAI-ARIA 1.0)', () => { - const vNode = queryFixture( - `
-
` - ); - assert.isTrue(isComboboxPopup(vNode)); + describe('using parent owned (ARIA 1.1 pattern)', () => { + it('is true when its a child of the combobox', () => { + const vNode = queryFixture( + `
+
+
` + ); + assert.isTrue(isComboboxPopup(vNode)); + }); + + it('is false when its not a child of a real combobox', () => { + const vNode = queryFixture( + `
+
+
` + ); + assert.isFalse(isComboboxPopup(vNode)); + }); + + it('is false when its nearest parent with a role is not a combobox', () => { + const vNode = queryFixture( + `
+
+
+
+
` + ); + assert.isFalse(isComboboxPopup(vNode)); + }); + + it('is true when its nearest parent with a role is not a combobox', () => { + const vNode = queryFixture( + `
+
+
+
+
+
+
+
+
` + ); + assert.isTrue(isComboboxPopup(vNode)); + }); }); - it('is false when not related to the combobox', () => { - const vNode = queryFixture( - `
-
` - ); - assert.isFalse(isComboboxPopup(vNode)); + describe('when using aria-owns (ARIA 1.0 pattern)', () => { + it('is true when referenced', () => { + const vNode = queryFixture( + `
+
` + ); + assert.isTrue(isComboboxPopup(vNode)); + }); + + it('is false when owned by a select element', () => { + const vNode = queryFixture( + ` +
` + ); + assert.isFalse(isComboboxPopup(vNode)); + }); + + it('is false when not owned by a combobox', () => { + const vNode = queryFixture( + `
+
` + ); + assert.isFalse(isComboboxPopup(vNode)); + }); }); }); } + + describe('options.popupRoles', () => { + it('allows custom popup roles', () => { + const vNode = queryFixture( + `
+
` + ); + assert.isFalse(isComboboxPopup(vNode)); + assert.isTrue(isComboboxPopup(vNode, { popupRoles: ['button'] })); + }); + + it('overrides the default popup roles', () => { + const vNode = queryFixture( + `
+
` + ); + assert.isTrue(isComboboxPopup(vNode)); + assert.isFalse(isComboboxPopup(vNode, { popupRoles: ['button'] })); + }); + }); }); diff --git a/test/rule-matches/no-naming-method-matches.js b/test/rule-matches/no-naming-method-matches.js index 9d37ad9d49..5648990703 100644 --- a/test/rule-matches/no-naming-method-matches.js +++ b/test/rule-matches/no-naming-method-matches.js @@ -69,7 +69,7 @@ describe('no-naming-method-matches', function () { assert.isFalse(actual); }); - it('returns false for the popup of a role=`combobox`', function () { + it('returns false for the listbox popup of a role=`combobox`', function () { var vNode = queryFixture( '
' + '
' @@ -78,6 +78,15 @@ describe('no-naming-method-matches', function () { assert.isFalse(actual); }); + it('returns true for the modal popup of a role=`combobox`', function () { + var vNode = queryFixture( + '
' + + '
' + ); + var actual = rule.matches(null, vNode); + assert.isTrue(actual); + }); + it('returns true for a div with role=`button`', function () { var vNode = queryFixture('
'); var actual = rule.matches(null, vNode); From d3c06641ddb6b5733306896705914e62b9294fd8 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Wed, 25 Jan 2023 13:41:59 +0100 Subject: [PATCH 5/6] modals require a name though --- lib/rules/no-naming-method-matches.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rules/no-naming-method-matches.js b/lib/rules/no-naming-method-matches.js index 33404aaf86..6f56f8e88f 100644 --- a/lib/rules/no-naming-method-matches.js +++ b/lib/rules/no-naming-method-matches.js @@ -19,7 +19,8 @@ function noNamingMethodMatches(node, virtualNode) { return false; } // Ignore listboxes that are referenced by a combobox - if (isComboboxPopup(virtualNode)) { + // Other roles don't require a name at all, or require one anyway + if (isComboboxPopup(virtualNode, ['listbox'])) { return false; } return true; From 163911da03048304dfb341cce862a9824a439970 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Fri, 27 Jan 2023 12:24:15 +0100 Subject: [PATCH 6/6] Resolve feedback --- lib/commons/aria/is-combobox-popup.js | 2 +- lib/rules/no-naming-method-matches.js | 2 +- test/rule-matches/no-naming-method-matches.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/commons/aria/is-combobox-popup.js b/lib/commons/aria/is-combobox-popup.js index b1c787ed7e..bef80f83be 100644 --- a/lib/commons/aria/is-combobox-popup.js +++ b/lib/commons/aria/is-combobox-popup.js @@ -8,7 +8,7 @@ import { getRootNode } from '../../core/utils'; * @memberof axe.commons.aria * @instance * @param {VirtualNode} virtualNode - * @param {Object} optionsw + * @param {Object} options * @property {String[]} popupRoles Overrides which roles can be popup. Defaults to aria-haspopup values * @returns {boolean} */ diff --git a/lib/rules/no-naming-method-matches.js b/lib/rules/no-naming-method-matches.js index 6f56f8e88f..cd8c973f3b 100644 --- a/lib/rules/no-naming-method-matches.js +++ b/lib/rules/no-naming-method-matches.js @@ -20,7 +20,7 @@ function noNamingMethodMatches(node, virtualNode) { } // Ignore listboxes that are referenced by a combobox // Other roles don't require a name at all, or require one anyway - if (isComboboxPopup(virtualNode, ['listbox'])) { + if (isComboboxPopup(virtualNode, { popupRoles: ['listbox'] })) { return false; } return true; diff --git a/test/rule-matches/no-naming-method-matches.js b/test/rule-matches/no-naming-method-matches.js index 5648990703..c92213817e 100644 --- a/test/rule-matches/no-naming-method-matches.js +++ b/test/rule-matches/no-naming-method-matches.js @@ -78,10 +78,10 @@ describe('no-naming-method-matches', function () { assert.isFalse(actual); }); - it('returns true for the modal popup of a role=`combobox`', function () { + it('returns true for the dialog popup of a role=`combobox`', function () { var vNode = queryFixture( '
' + - '
' + '' ); var actual = rule.matches(null, vNode); assert.isTrue(actual);