-
Notifications
You must be signed in to change notification settings - Fork 467
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
Expressing user actions at a higher level #107
Comments
Skimming over the Cypress API, these seem to be some of the most useful APIs provided and an idea of how they would act: check |
Also have a look at https://github.com/getgauge/taiko whose goal is to provide such a high-level API on top of a browser and a REPL to test it and record scripts. I wish the very similar efforts dom-testing-library and cousins, Cypress, and now Taiko all put into making human-emulating APIs for webapp testing and automation could be united, but unfortunately Cypress is a not-so-modular-and-importable pile of CoffeeScript. |
Yeah, I think that someone should develop a package separate from dom-testing-library that's intended to do this. Then dom-testing-library can just re-export that library (like we do with Anyone want to do that? It'd probably be a lot of fun and I bet would get a lot of use. I just don't have time for it at the moment. |
The very part that I wish could be reuse from Cypress is all the funky and often-simply-impossible-without-devtools-protocol but still useful emulation of the browser default behavior via just the DOM APIs (what dom-testing-library does as an infant compared to Cypress). The part I wish could be reused from Taiko (although I read the code and it's not very robust yet) is the visually relative selectors by text (dom-testing-library kinda does by text, but not visually relative). I mean I agree with Kent that it should be a separate library. |
@sompylasar Taiko looks amazing, but if I understand correctly since jsdom doesn't support "getBoundingClientRects", or "the ability to calculate where elements will be visually laid out as a result of CSS", we won't be able to do things like near/above/below. :-( Is there something that I'm missing? |
Are you sure you don't want to pursue the monorepo idea in #98? The cross-dependency between these packages is a bit hairy and some of the wrappers are undoubtedly already falling behind, depending on implementation. |
Just for me to understand, would this "user" library work somehow like this? import React from "react";
import { render, fireEvent } from "react-testing-library";
import "jest-dom/extend-expect";
function App() {
return (
<React.Fragment>
<input data-testid="A" />
<input data-testid="B" />
</React.Fragment>
);
}
const userEvent = {
click(element) {
/* This is what really happens in my browser
* when I click on an element.
*
* Chrome 69.0.3497.92 (Official Build) (64-bit)
* OS X 10.11.6 (15G22010)
*
* mouseenter
* mouseover
* mousemove (a bunch of them)
* mousedown
* focus
* mouseup
* click
*
* If we fire the same events with fireEvent
* in the same exact order it doesn't work.
*
* This is because:
*
* 1. fireEvent.mouseOver also fires mouseenter
* although in the wrong order
* 2. fireEvent.focus does not set
* document.activeElement. I had to use
* element.focus()
*
* This is the best I could come up with
* to simulate the browser as closely as possible.
*/
const focusedElement = document.activeElement;
const wasAnotherElementFocused =
focusedElement !== document.body && focusedElement !== element;
if (wasAnotherElementFocused) {
fireEvent.mouseMove(focusedElement);
fireEvent.mouseLeave(focusedElement);
}
fireEvent.mouseOver(element);
fireEvent.mouseMove(element);
fireEvent.mouseDown(element);
element.focus();
fireEvent.mouseUp(element);
fireEvent.click(element);
wasAnotherElementFocused && focusedElement.blur();
}
};
test("events", () => {
const { getByTestId } = render(<App />);
userEvent.click(getByTestId("A"));
expect(getByTestId("A")).toHaveFocus();
userEvent.click(getByTestId("B"));
expect(getByTestId("B")).toHaveFocus();
expect(getByTestId("A")).not.toHaveFocus();
}); |
@alexkrolick, I'm going to be building a monorepo project at work (because I can't use semantic-release there) so we'll see how I enjoy it. My biggest hesitance for using monorepos is the semantic-release solution for monorepos seems slightly unmaintained and I'm worried about things that could come up. Dropping semantic-release from my workflow is kinda out of the question. I wouldn't have time to maintain the packages I do without semantic-release. That said, I'll let you know what I think of my experience using a monorepo. If it's positive, then I'll see about trying to get semantic-release to work for a monorepo. @Gpx, yes, I think that's exactly the kind of thing we're looking for 👍 |
Can I give it a shot? |
Go for it! |
@Gpx Not to discourage you from doing what you're going to do, but there's a lot of corner cases, so please reuse the experience Cypress has collected over time in how this should work. Also puppeteer has an extensive user-simulating API to look at. From my perspective the most accurate representation of user interactions with the UI is described in terms of the tools available to the user: 1) analyze the output: eyes or screenreaders to observe elements and move pointers on the screen, 2) produce the input: pointers, fingers, keyboards to interact with the screen. The input tools have state: the current coordinates and pressed/released for every button. The "click" action is actually a pointer click so it will be just optional focus of the element under the current coordinates and then click of the element under the current coordinates (also something may rerender on focus so click may hit a different element). Cypress has had a different approach last time I looked: to make as many shortcuts as possible (sacrificing the accuracy of representation), but still they try to emulate all events they can via the DOM. |
@sompylasar yep you're right there are going to be many edge cases. As you suggested I intend to start by reviewing what other projects do and what browsers do. Next, I'll take the new user events I create and put them in my test suite to see how it goes. We'll probably need to sacrifice some events (I'm thinking of I'll keep you posted and I'm happy if somebody wants to contribute. |
Hi everyone, a little update. I've started working on a library called I would much appreciate any feedback and especially bug reports. I believe many edge cases are not covered yet and at the moment I'm focusing only on the issues I find while working on my other projects. Let me know what you think, and if you want to contribute you're more than welcome! |
Fantastic! That's a great start 👍 once we get more user actions in there, then we can pull that in and re-export it 💯 Circular dependencies are fun 😅 I'm pretty sure it works though. |
I have had a look at the library and even when using that I am struggling to make onChange event handlers be called or the keydown events. I am not sure if something is broken or not. I have been able to make a reproducible sandbox which can be found at the link below. I have added some logs in my I am a bit worried that my testing approach is wrong. I am trying to test things which I should test? Sandbox URL: https://codesandbox.io/s/ol4qnvy669 |
Hey @weyert, your test is firing change events at a button, not the input field. I believe that's the problem. |
Amazing, thank you @kentcdodds. Clearly you are having a pair of fresh eyes!! |
I have created a PR for @Gpx library so that it will dispatch onKeyDown/onKeyUp and change correctly when you are using If this change is against the intended use of |
@kentcdodds do you think user-event is mature enough to include in the next release? |
I'm not sure. I'm actually a bit concerned about this. One of the things I didn't like about enzyme was that there were a million ways to do things. If we include this in dom-testing-library then we have two ways to do things and I feel like it could get confusing. What do you all think? |
I think there are inherently at least two ways to simulate user-originated events in the browser: 1) imitate them with variable precision via browser DOM API from within JS runtime (not all possible to implement, many shortcuts and hacks); 2) inject into browser user input queue as if an input device generated them, let the browser translate to DOM events (precise but requires browser automation API which either not available or is async). |
I was under the impression that this would be the new default behavior, and that if someone would want to fire events the old way, they would just do it manually. That seems consistent with how dom-testing-library handles selection - if you want to select a class, use DOM methods directly. |
I would back using user-events as the new default. The philosophy of dom-testing-library is to test "the way a user would". A user certainly doesn't trigger separate DOM events, of which the user is usually even unaware (think a simple "hover" vs. all the mouseIn, mouseOver and their chains). Instead, if a user clicks on an input field, he would expect the input field to become active, along with any side effects that normally take place in the browser. So, I would replace the "old" way with this. |
I would like to see it support more typical user events (drag and drop, hover, etc), then I'd like to see it used in real codebases for a while to see how it works in practice and work out any issues/common questions. I'm a little hesitant, particularly when someone changes from |
I agree with @kentcdodds user-event is not mature enough. It still lacks some basic actions and more real-world testing. As for including it in dom-testing-library, I'm not sure either. I think it shoud—at least in the beginning—be an external library that offers an alternative to |
Yes, I think it would be a good idea to mention the availability in the React and Dom testing library README that this helper library exists for some common user events :) |
Yes, I agree @weyert. Will help with adoption. I would like to suggest that we refer to it as experimental until it gets more features and is more proven. |
https://github.com/bigtestjs/interactor - an interesting library for simulating interactions, plus some Cypress like "interactability" detectors like isHidden, etc. Might be possible to get it to work with dom-testing-library somehow. |
Closing this as we now have user-event |
I think it would be great to expand on |
I'm happy to discuss this further, but let's open a new issue on user-event. |
Where is the new issue? :D we are looking forward to it. |
Describe the feature you'd like:
Following the discussion in testing-library/jest-dom#53 and testing-library/jest-dom#59, it is becoming apparent the need to express user actions on a web page using a higher-level abstraction than
fireEvent
.This issue is to bring the discussion to this repo, where the potential solution actually lies.
Suggested implementation
See this comment from @kentcdodds for details on how the api could look like.
I'd also like to add that we could take a look at similar cypress APIs. I wonder even if having something like what the above linked comment suggests, would be duplicating what cypress already provides. Of course this project is independent from cypress and it could well provide similar functionality.
The text was updated successfully, but these errors were encountered: