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
isDisabled doesn't match the toBeDisabled() implementation in testing-library/jest-dom for custom elements #1042
Comments
Could you break this down to a minimal example without third-party utils? The implementation in user-event/tests/utils/misc/isDisabled.ts Lines 67 to 71 in 1aa2027
|
It would take a lot of code to create a reproduction of our button element - especially because we use Lit to create our components, which I'm sure you also wouldn't want in a repro case. Let me see what I can come up with. I saw your tests, but in practice, it's so much more complicated than simply setting Browsers don't follow that same behavior though - there are so many ways to prevent an element from being clickable that aren't simply setting |
Ok, this is a pretty contrived example, but I've found a lot of design systems use a In a perfect world, would every custom button have However, I've realized that I can give our react consumers the following workaround. In their jest setup file (where we have to mock all the things jsdom doesn't implement), they can put the following:
It would be nice if they didn't have to do that though. |
Before this issue sounds as being easily dismissed: Thanks for raising this issue! ❤️ Our goal is to provide a testing tool that helps to ensure code works and follows best practice. If something works in all major browsers, we generally will try to support it. If something doesn't work across different browsers, we'll most likely follow the spec (if it exists) or try to come up with a reasonable default.
I don't know how much code or what code it takes to make the browser consider this element disabled.
While I see your point, this is something we can't consider. What if we break someone's test who does exactly this? Their component would match specs and work in the browser and our test would fail. |
The previous implementation of I tried checking with a few other web component components libraries, but was having trouble getting the tests to run. The Shoelace.Style button now clicks when disabled as well. I forgot to link the sandbox with a repro case in my previous comment, oops: https://codesandbox.io/s/hopeful-hugle-c5xqcg |
We won't consider breaking with Semver rules here. If something is released and isn't considered a bug, it won't change. We can't introduce a potentially breaking change just because someone relied on it without filing an issue. You've got a case that your component seems to work in browsers as described. If this behavior is consistent, we'll try to support it. But we need to know what exactly triggers this behavior or we'd just be (re)introducing a bug. |
I'm not sure if you saw the link I posted above, but I didn't realize I forgot to include a stackblitz in my second comment above. There are a couple of ways of creating custom buttons to get them to interact and submit forms.
There's probably other ways of doing it as well. For #1, you could attempt to look for a button in the shadowDOM, I suppose. But that's a hack and you obviously shouldn't be browsing the shadowDOM, it's supposed to be private. I don't know how you would catch #2. I've looked through the implementation for 4 different web-component design system buttons - VMware's Clarity, Adobe's Spectrum, Shoelace.style, and Alaska Airline's Aurora and none of them set |
I'm not sure I can follow. Is the custom element the event target or not? If is isn't,
Again I can't follow. For capturing and stopping propagation there has to be an event. Why should this be broken by dispatching the event that is supposed to be captured? |
It uses event bubbling to propagate the event. You can't target the inner button because it's essentially part of a private API. For all intents and purposes, the custom element is the button, it has role="button" and that's where you apply the click handler. The wrapped button is an implementation detail, used to get form submission (since Safari doesn't implement attachInternals() yet)
In this second use case, imagine the custom element is simply a div with an attribute called As a third option, there's also In any case, I'm not sure how to recommend supporting this use case since there are so many workarounds to prevent an element from being clicked. Perhaps you'll be willing to consider a breaking change for v15 that isn't so restrictive, or perhaps Safari will have it implemented by then and all of the custom element authors will be using |
I think this might be a misconception. If there are two elements in the document, they are two different event targets. If the wrapped button is the uppermost layer that can receive pointer events it is the event target for a pointer event. If this is the case, the wrapped button is the correct target for There might be a nicer way to describe such interaction in the future when #846 is implemented, but this will require running the test in an environment with layout - e.g. a real browser.
The event dispatched by this library can be handled like any other.
I'll consider any change if we can work out what exactly should be handled differently.
If the function getButtonTarget(customEl) {
return customEl.shadowRoot.querySelector('button')
}
await user.click(getButtonTarget(screen.getByRole('button'))) |
It's not any different. The problem is that you dispatch it even the custom element is disabled because the custom element doesn't have
I'll reduce technical debt when the major browsers support
I'm not providing a helper, I don't provide any helpers. The custom element is the correct target, it is the button, it has |
If the custom element isn't |
I took your first example to verify this and Chrome thinks you're wrong. https://codesandbox.io/s/keen-panini-g0vwmg?file=/src/index.js |
Reproduction example
https://codesandbox.io/s/hopeful-hugle-c5xqcg?file=/src/App.test.js
Prerequisites
I can't get the tests to run in codesandbox, but in a repo, they behave as follows:
expect().toBeDisabled()
passes, despite the click going through.Expected behavior
The click shouldn't happen because the button is disabled
Actual behavior
The click goes through, even though the button passes the
toBeDisabled()
test.User-event version
14.0.0
Environment
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^14.4.3",
Jest, using react-script v5
A sample here: vmware-clarity/starters@main...ashleyryan:starters:asryan/tl-userevent
Additional context
The reason this is happening is because in v14 of user-event, the implementation of isDisabled was changed to add a check for
formAssociated
for custom elements. This isn't wrong, the spec says a custom-element is disabled if it's form-associated.However,
formAssociated
is intended to be used with ElementInternals, which doesn't have full browser support (Safari doesn't have it) - and is absolutely not supported in jsdom. Most custom element authors have to find workarounds for custom form elements, and in our case, we use a hiddenbutton
element in the light DOM for form submission and other form-associated behavior.So yes, we could (theoretically) add
static formAssociated = true
to our elements and these tests would work, but that's not entirely correct withoutattachInternals()
and its API being implemented. It's confusing and misleading to see that in the source code.I helped a colleague add custom-element support to
toBeDisabled()
in@testing-library/jest-dom
- that implementation only checks that it's a custom element, named with a -. At the time, I noticed the spec said the "form-associated" part and looked into adding the check for the static attribute, but noticed that jsdom didn't supportattachInternals()
, so we left it out. PR: testing-library/jest-dom#368It's confusing to have a test pass that says the button is disabled, while also allowing the click event go through.
Original issue in our repo: vmware-clarity/core#153
The text was updated successfully, but these errors were encountered: