-
Notifications
You must be signed in to change notification settings - Fork 470
feat(byRole): Add name
filter
#408
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
Conversation
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 7a53e33:
|
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.
Very very cool. Really looking forward to this feature!
Also, I completely agree with your comment about moving things around a bit 👍 |
One thing I am wondering is why this shouldn't support combining the other matchers as well: // combine with displayValue
getByRole('textbox', {displayValue: 'username'})
// combine with aria-labelledby or htmlFor
getByRole('dialog', {label: 'uh oh'})
// Inverted? Anything that takes a `selector` option could take `role`?
// This actually seems like a more natural modifier - it increases specificity
// of content-based queries.
getByLabelText('uh oh', {role: 'dialog'}) Seems like a one off when we should be figuring out how to intersect queries in general. There have been a few proposals on how to do that. |
Not sure what this is supposed to query. A dialog doesn't have a label. It's important to note that
I would have to look into this more. I don't know how display value is handled currently but we need consider that one might include the actual But this will definitely be part of the docs since it's apparently not obvious when to use what. |
That was just an example, but this is from MDN: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/dialog_role <div role="dialog" aria-labelledby="dialog1Title" aria-describedby="dialog1Desc">
<h2 id="dialog1Title">Your personal details were successfully updated</h2>
<p id="dialog1Desc">You can change your details at any time in the user account section.</p>
<button>Close</button>
</div> |
Sure. But be careful to not mix a If you used |
Plan for this in the next week:
|
Codecov Report
@@ Coverage Diff @@
## master #408 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 22 22
Lines 385 397 +12
Branches 91 94 +3
=====================================
+ Hits 385 397 +12
Continue to review full report at Codecov.
|
Awesome! Let us know when you're ready for a review. Thanks a bunch! |
I'm also curious about the inversion that @alexkrolick mentioned. Maybe the example he gave wasn't applicable (finding a dialog by label text), but others would be. getByText(/my website/i, { role: 'heading' }) This seems a little more expressive, at least for this use case than the query added in this PR: getByRole('heading', { name: /my website/i }) I want to get something with specific text, and the fact that it's a heading is supplementary to that. I concede that this is nitpicky, though. |
I wouldn't agree with that since roles might be button, dialog, heading etc. These are not secondary at all. The point is to encourage writing out what kind of element you're dealing with. It's more important that the element is e.g. a button than that it reads "log in". The first one is part of the UI/UX. The second one is subject to localization. |
Fair enough. I honestly can see scenarios in which either the role or the text are the primary thing you're concerned with. |
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.
Looking awesome. I'm excited for this one :)
expect(deliveryForm).not.toBeNull() | ||
|
||
expect( | ||
// TODO: upstream bug in `aria-query`; should be `button` role |
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 we contribute a fix to aria-query before merging this?
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.
Right. They released a new major. Opening a separate PR that bumps aria-query and then we'll see if that worked.
|
||
heading: | ||
|
||
Name "Sign up": |
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.
The only difference between this and the query is the u
is not capitalized. Could we make this difference more stark so this makes more sense why it failed to find an element please? Thanks!
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 had trouble understanding it as well. I would like to keep the test to make sure that { name: string }
is always case-sensitive. I'll add a comment to the test explaining the issue.
</div>" | ||
`) | ||
|
||
expect(() => getByRole('heading', {name: /Login/})) |
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.
Not sure it's necessary to have all three of these because they're all testing the same codepath and their differences are covered in other 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.
It's more about showcasing how the name
argument is serialized. It may be too verbose in which case the codepath should change. And overall: Isn't that testing implementation details 😉
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.
expect(() => getByRole('heading', {name: /Login/})) | |
expect(() => getByRole('heading', {name: /something that does not match/})) |
I think this makes it more clear that it's not just a typo...
src/role-helpers.js
Outdated
.join('\n\n') | ||
|
||
return `${role}:\n\n${elementsString}\n\n${delimiterBar}` | ||
}) | ||
.join('\n') | ||
} | ||
|
||
const logRoles = (dom, {hidden = false} = {}) => | ||
console.log(prettyRoles(dom, {hidden})) | ||
const logRoles = (dom, {hidden = false, includeName = false} = {}) => |
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.
Why would we not want to always include the name?
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.
Haven't thought much about this. Just wanted to not change existing behavior.
Since this is only used by library consumers it might be a good idea to include the name by default to promote that we now support accessible name queries?
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.
Yes, since it's debug output, it seems fine to change.
Addressed #408 (comment) and #408 (comment) in 3c7dd0e. |
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.
Cool cool! Looking good :)
src/role-helpers.js
Outdated
.map( | ||
el => | ||
`${ | ||
includeName === true | ||
? `Name "${computeAccessibleName(el)}":\n` | ||
: '' | ||
}${prettyDOM(el.cloneNode(false))}`, | ||
) |
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.
Prettier makes this look funny. Why don't we do this:
.map( | |
el => | |
`${ | |
includeName === true | |
? `Name "${computeAccessibleName(el)}":\n` | |
: '' | |
}${prettyDOM(el.cloneNode(false))}`, | |
) | |
.map(el => | |
[ | |
includeName === true | |
? `Name "${computeAccessibleName(el)}":\n` | |
: '', | |
prettyDOM(el.cloneNode(false)), | |
].join(''), | |
) |
src/role-helpers.js
Outdated
.join('\n\n') | ||
|
||
return `${role}:\n\n${elementsString}\n\n${delimiterBar}` | ||
}) | ||
.join('\n') | ||
} | ||
|
||
const logRoles = (dom, {hidden = false} = {}) => | ||
console.log(prettyRoles(dom, {hidden})) | ||
const logRoles = (dom, {hidden = false, includeName = 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.
Why do we provide the option to not include the name?
I'm working on this now. I'll go ahead and remove the |
🎉 This PR is included in version 6.14.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Agreed with everything. Thanks for finishing this up! |
What:
Add a
name
option toByRole
queries. If aname
is provided then the error messages will include the accessible name of each element with a role.Why:
Better alternative to
byText
(especially for buttons) orbyLabelText
whenaria-label
oraria-labelledby
is used (e.g. for custom Select implementations) orbyTestId
for "semantic" elements e.g. menuitem.There's a bit more to write in https://testing-library.com/docs/guide-which-query to explain why you still might want to use byLabelText('street') instead of
byRole('textbox', { name: 'street' })
.How:
Using https://github.com/eps1lon/dom-accessibility-api#readme which implements https://www.w3.org/TR/accname-1.2/ and is tested against web-platform-tests
Checklist:
docs site. See feat(byRole): Add
name
option testing-library-docs#368 (Preview)DefinitelyTyped
I would hope that (after review and testing phase) we can move that package under the testing-library umbrella and move isInaccessible into dom-accessibility-api. In the end I'd like to add a single configuration that allows mocking
window.getComputedStyle
for the a11y context. That operation is quite expensive and I think allowing to pretend that every element is visible is sufficient for unit testing. The rationale being that we don't exclude certain groups of people by (accidentally) implementing inaccessible component but instead exclude everyone if we assert on elements that are actually visually hidden.