-
-
Notifications
You must be signed in to change notification settings - Fork 257
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
Error when interacting with disabled form controls #778
Conversation
7468957
to
b659632
Compare
b3b0a87
to
714f0aa
Compare
if (!isDisabledFormControl) { | ||
__click__(element, options); | ||
if (isFormControl(element) && element.disabled) { | ||
throw new Error(`Can not \`click\` disabled ${element}`); |
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'm not sure if we should throw an error for this. A real user is absolutely able to click on a disabled element, the difference though is that nothing will/should happen because the element is disabled.
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 intention of the PR is to disallow any interactions over disabled form elements. It seems the most effective to achieve it via throwing in the focus. This throw
statement in click
has been put here just for a better error message. If remove it, it'd fail later in the __focus__
util anyways.
A real user is absolutely able to click on a disabled element
yes, I agree about the user perspective. However, test env seems to be a bit different. If we don't fail as fast as we can, tracking the source of invalid test results can be more difficult then it could be if we were more strict.
In tests, we usually have all the state under control, and before clicking smth, we, as test authors, should likely know a desirable state for the button.
For instance, if we have a button which is responsible for some computation, and we click it while it's disabled(because of some bug), we may have a failing assert
for the expected computation value. And since we don't fail fast, the more distance between the action and assert is, the more difficult to track the source of the issue. I believe test helpers could become more helpful here, by letting the user know if he tries to interact w/ something which is not supposed to be interactable.
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.
There are 2 possible reasons I can see, why someone clicks on a disabled element:
- they intentionally did want to check if smth doesn't occur after click. In this case, the question is why not to just check for
disabled
value? And it also encourages the fail-late approach which I've tried to describe in the previous comment. - the button should not have been disabled, but disabled because of some code issue. In this case we fall again in a "fail late" thing. And there is even a probability to have a false-positive test results if some assert is missing.
Curious if there are any other use cases for clicking disabled elements in 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.
I agree with @Turbo87 - it shouldn't throw, just do nothing.
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'd be happy to agree as well. But don't see a good reason for that. The only one which is raised:
A real user is absolutely able to click on a disabled element, the difference though is that nothing will/should happen because the element is disabled.
however, in addition to my previous arguments, I don't find this aligned with some of the current behaviors. For instance, we currently fail on attempt to focus non-focusable elements
if (!isFocusable(element)) { | |
throw new Error(`${target} is not focusable`); | |
} |
while in reality a user can absolutely try to focus on a disabled element or smth else w/o any success or exceptions in UI.
Or, we fail on attempt to fill-in non-editable elements
if (!isControl && !element.isContentEditable) { | |
throw new Error('`fillIn` is only usable on form controls or contenteditable elements.'); | |
} |
As far as I see, these all are just examples of fast feedback for the test helpers user, which allows restrict he/she from doing pointless things.
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.
In general, I agree with @ro0gr here. The general purpose helpers we provide here are intended to be demonstrating the functionality of the application, allowing click('.some-disabled-thing')
to silently do nothing seems almost certainly to be an actual logic / error condition.
@ro0gr - Would you mind rebasing (I think the conflicts are likely due to prettier changes)? |
714f0aa
to
900925f
Compare
900925f
to
ea99bab
Compare
How can we migrate our tests to support this new behaviour? Take the following scenario: await triggerEvent('button[disabled]', 'mouseenter');
assert.dom('.tooltip').hasText("You can't do this because reason") This used to work, but now it throws. And, even if caught, the mouseenter event never fires. |
I replied on Discord:
Curious to learn what other approaches can be taken. |
if the browser fires one then we should do it here too |
rwjblue pointed out that even though the user can 'interact' with a disabled button, the browser won't fire the mouseenter event anyway. So I think this PR was correct - and my above code snippet is wrong - it just worked incorrectly. |
I think @rwjblue only pointed out that we should follow what the real browsers are doing, but if you're referring to the Discord conversation, then he didn't say that he knew exactly what the browsers were doing in this case. In other words: this will need to be investigated before taking any decision whether this is correct or not :) |
Great finding! Ya, this seems to be a bug. Here I've built a quick playground to found out events, which should be triggered for disabled buttons https://codepen.io/ro0gr/pen/MWjEjRE. Hope I haven't missed any events, just copy pasted all the events known to the According to the playground we do currently miss to trigger at least the following events for the disabled elements:
Unfortunately, I'm a bit busy with some other activities right now. Will try to find some time to look at it this weekend, if someone else would not do it earlier. |
oh.. seems like events mentioned above are triggered in FF only on my machine. Chrome doesn't react at all 🤔 |
maybe a "good" solution could be to leave the |
I think logically being disabled means no events (e.g. the behavior added in this PR) and that matches Chrome / Edge / Safari behavior. I don't really want to have divergent by browser here in the helpers, so I think I'd prefer to take a more conservative approach (which this PR does). I do think it would be fine to add an explicit option that could be used to force an event interaction (e.g. to override this limitation), but we should have that discussion separately from this PR thread. Overall, I think this PR did the right thing and it points out logical issues in folks' test suites (which was the point). |
Oh, also, I inferred it above but I also tested this in Safari and IE11. IE11 fires events for everything regardless of disabled status, Safari fires no events on the disabled button, and FireFox only fires mousemove events (not click, dblclick, etc). |
With this, any action helper(
click
,triggerEvent
,...) would fail on attempt to be invoked againstinput
,textarea
,select
indisabled
state.I've extracted it from #741, cause
fillIn
andtypeIn
seem to be a bit different cases, and it should be easier to review this way. Also this is possibly a breaking change, so we may want to postpone merging it, whilefillIn
andtypeIn
seem to be more critical to fix.