feat(context): allow selecting shadow DOM nodes#3798
Conversation
| if (vNode.shadowId || otherVNode.shadowId) { | ||
| do { | ||
| if (vNode.shadowId === otherVNode.shadowId) { | ||
| return true; |
There was a problem hiding this comment.
This was wrong. We never caught it because contains is never called with vNode as a node in the shadow DOM tree.
| include: [vNode], | ||
| exclude: [], | ||
| frames: [], | ||
| page: false, | ||
| focusable: true, | ||
| size: {}, | ||
| flatTree: [] |
There was a problem hiding this comment.
Spotted some missing props. Don't know if it can cause a problem, but I figured I'd fix it and add a test.
| * @return {Node} The deepest node | ||
| */ | ||
| function getDeepest(collection) { | ||
| return collection.sort((a, b) => { |
There was a problem hiding this comment.
This shouldn't have used sort. We only need the last item in the array.
| for (let i = 0, l = context[type].length; i < l; i++) { | ||
| const item = context[type][i]; | ||
| // selector | ||
| if (typeof item === 'string') { |
There was a problem hiding this comment.
This cannot happen. After normalising, this is always either a node, or an array..
| // Handle Iframe selection | ||
| if (item && item.length) { | ||
| // Handle Iframe selection | ||
| } else if (item && item.length) { |
There was a problem hiding this comment.
This might just as well be "else". If this isn't an array, normalization has a bug. Open to suggestions.
straker
left a comment
There was a problem hiding this comment.
This should include tests for the types in typings/axe-core-tests.ts.
Also, when a shadow DOM selector targets an element that is not a shadow host, axe-core should call axe.log() to log the issue. Right now it's throwing with "No elements found for include in page Context"
| do { | ||
| if (vNode === otherVNode) { | ||
| return true; | ||
| } else if (otherVNode.nodeIndex < vNode.nodeIndex) { |
There was a problem hiding this comment.
Is this just a short circuit so you don't have to navigate up the entire parent tree?
test/core/utils/contains.js
Outdated
| assert.isFalse(axe.utils.contains(node2, node1)); | ||
| }); | ||
|
|
||
| it('should work', () => { |
test/core/utils/contains.js
Outdated
| assert.isFalse(axe.utils.contains(node2, node1)); | ||
| }); | ||
|
|
||
| describe.skip('using fallbacks', () => { |
There was a problem hiding this comment.
Ahh yes. I got rid of .compareDocumentPosition. Don't care for maintaining that code path, but I can keep the other two tests.
test/core/utils/contains.js
Outdated
| `<section id="target"></section>` | ||
| ); | ||
| createNestedShadowDom(section, `<img>`); | ||
| createNestedShadowDom(section, `<input>`); |
There was a problem hiding this comment.
Did you mean for this to not have any shadow trees? Passing a single html code to createNestedShadowDom just appends the code to innerHTML and never creates a shadow tree, so the end result is just flat nodes:
<div id="fixture">
<section id="target">
<img>
<input>
</section>
</div>There was a problem hiding this comment.
Ooff! Good catch. This test is completely broken. Will fix.
doc/context.md
Outdated
|
|
||
| **Note**: The `fromShadowDom` property cannot be used on the same object as `include` and `exclude`. | ||
|
|
||
| ### Slotted elements |
There was a problem hiding this comment.
I need to add tests for this. I think this will work, but I didn't try it.
Co-authored-by: Steven Lambert <2433219+straker@users.noreply.github.com>
doc/context.md
Outdated
|
|
||
| 1. [Test specific elements](#test-specific-elements) | ||
| 1. [Test DOM nodes](#test-dom-nodes) | ||
| 1. [Excludes elements from test](#exclude-elements-from-test) |
There was a problem hiding this comment.
| 1. [Excludes elements from test](#exclude-elements-from-test) | |
| 1. [Exclude elements from test](#exclude-elements-from-test). |
| 1. [Limit frame testing](#limit-frame-testing) | ||
| 1. [Limit shadow DOM testing](#limit-shadow-dom-testing) | ||
| 1. [Combine shadow DOM and frame context](#combine-shadow-dom-and-frame-context) | ||
| 1. [Implicit frame and shadow DOM selection](#implicit-frame-and-shadow-dom-selection) |
There was a problem hiding this comment.
| 1. [Implicit frame and shadow DOM selection](#implicit-frame-and-shadow-dom-selection) | |
| 1. [Select frame and shadow DOM implicitly](#implicit-frame-and-shadow-dom-selection). |
There was a problem hiding this comment.
I see where you're coming from, but this isn't a call to action thing. I don't want people to do this. It's more about explaining some background.
| Axe-core's `context` argument is a powerful tool for controlling precisely which elements are tested and which are ignored. The context lets you do many things, including: | ||
|
|
||
| 1. [Test specific elements](#test-specific-elements) | ||
| 1. [Test DOM nodes](#test-dom-nodes) |
There was a problem hiding this comment.
| 1. [Test DOM nodes](#test-dom-nodes) | |
| 1. [Test DOM nodes](#test-dom-nodes). |
| 1. [Test specific elements](#test-specific-elements) | ||
| 1. [Test DOM nodes](#test-dom-nodes) | ||
| 1. [Excludes elements from test](#exclude-elements-from-test) | ||
| 1. [Select from prior tests](#select-from-prior-tests) |
There was a problem hiding this comment.
| 1. [Select from prior tests](#select-from-prior-tests) | |
| 1. [Select from prior tests](#select-from-prior-tests). |
| 1. [Test DOM nodes](#test-dom-nodes) | ||
| 1. [Excludes elements from test](#exclude-elements-from-test) | ||
| 1. [Select from prior tests](#select-from-prior-tests) | ||
| 1. [Limit frame testing](#limit-frame-testing) |
There was a problem hiding this comment.
| 1. [Limit frame testing](#limit-frame-testing) | |
| 1. [Limit frame testing](#limit-frame-testing). |
| 1. [Excludes elements from test](#exclude-elements-from-test) | ||
| 1. [Select from prior tests](#select-from-prior-tests) | ||
| 1. [Limit frame testing](#limit-frame-testing) | ||
| 1. [Limit shadow DOM testing](#limit-shadow-dom-testing) |
There was a problem hiding this comment.
| 1. [Limit shadow DOM testing](#limit-shadow-dom-testing) | |
| 1. [Limit shadow DOM testing](#limit-shadow-dom-testing). |
| 1. [Select from prior tests](#select-from-prior-tests) | ||
| 1. [Limit frame testing](#limit-frame-testing) | ||
| 1. [Limit shadow DOM testing](#limit-shadow-dom-testing) | ||
| 1. [Combine shadow DOM and frame context](#combine-shadow-dom-and-frame-context) |
There was a problem hiding this comment.
| 1. [Combine shadow DOM and frame context](#combine-shadow-dom-and-frame-context) | |
| 1. [Combine shadow DOM and frame context](#combine-shadow-dom-and-frame-context). |
| await axe.run([navbar, cookiePopup]); | ||
| ``` | ||
|
|
||
| ### Component Frameworks |
There was a problem hiding this comment.
If we're listing component frameworks, could we also list a small section for testing frameworks like Jest? Then we could add a quick example showing that you need to mount instead of shallowMount a component for testing
There was a problem hiding this comment.
Good point, but I'm going for a note instead.
Co-authored-by: Erik Larsen <enlarsen@users.noreply.github.com> Co-authored-by: Steven Lambert <2433219+straker@users.noreply.github.com>
| await axe.run(document.querySelectorAll('main')); | ||
| // axe.run with frameContext context | ||
| await axe.run({ fromShadowDom: ['#app', '#main', '#inner'] }); | ||
| // @ts-expect-error // Must be two long: |
There was a problem hiding this comment.
I did not know about @ts-expect-error. That's really cool.
| await axe.run({ include: ['main', document.head] }); | ||
| await axe.run(document.querySelector('main')); | ||
| await axe.run(document.querySelectorAll('main')); | ||
| // axe.run with frameContext context |
There was a problem hiding this comment.
| // axe.run with frameContext context | |
| // axe.run with shadow DOM context |
| } | ||
| ); | ||
| export async function runAsync() { | ||
| await axe.run('main'); // Single selector |
There was a problem hiding this comment.
We'll also want to add tests for unlabeled frame and shadow DOM selectors.
Closes #3793, closes #3765, and closes #3799