-
Notifications
You must be signed in to change notification settings - Fork 789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't have used sort. We only need the last item in the array.
@@ -12,29 +12,20 @@ export function parseSelectorArray(context, type) { | |||
const result = []; | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might just as well be "else". If this isn't an array, normalization has a bug. Open to suggestions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this just a short circuit so you don't have to navigate up the entire parent tree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jup
test/core/utils/contains.js
Outdated
assert.isFalse(axe.utils.contains(node2, node1)); | ||
}); | ||
|
||
it('should work', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should work exactly?
test/core/utils/contains.js
Outdated
assert.isFalse(axe.utils.contains(node2, node1)); | ||
}); | ||
|
||
describe.skip('using fallbacks', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this skip be here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// axe.run with frameContext context | |
// axe.run with shadow DOM context |
@@ -29,6 +29,38 @@ axe.run( | |||
console.log(error || results); | |||
} | |||
); | |||
export async function runAsync() { | |||
await axe.run('main'); // Single selector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll also want to add tests for unlabeled frame and shadow DOM selectors.
Closes #3793, closes #3765, and closes #3799