user-event icon indicating copy to clipboard operation
user-event copied to clipboard

Support Scroll User Event

Open svadali2 opened this issue 3 years ago • 13 comments

Problem description: When we have an area to scroll, it'd be good to account for this user event

Suggested solution: Add a scroll handler

svadali2 avatar Oct 22 '20 12:10 svadali2

We assume users use JSDOM, which doesn't implement most CSS layout features, so I'm not sure how useful it would be for a test to depend on what is scrollable/visible. Could you give a use case where a test (either using fireEvent or a proposed new API for userEvent) adds value for verifying scrolling behavior that isn't dependent on layout?

For example if you have an infinite scroll component, the test may or may not work properly depending on the size of scrolled elements. I may be missing other use cases that don't have this issue though.

nickmccurdy avatar Nov 08 '20 14:11 nickmccurdy

Not everyone using this package is using jsdom (though most are). So I'm not opposed to adding things that really on layout. It just means we'd have to add a tool that supports layout for our own tests.

Personally, if I had sobering like this to test, I would just use cypress instead 🤷‍♂️

kentcdodds avatar Nov 08 '20 15:11 kentcdodds

Does cypress-testing-library handle events using dom-testing-library or is it assumed that users will use Cypress commands directly?

nickmccurdy avatar Nov 08 '20 15:11 nickmccurdy

It assumes people will interact with the DOM using cypress. No reason to implement that ourselves when cypress already did a great job of that. User event is a less good version of what cypress does.

kentcdodds avatar Nov 08 '20 15:11 kentcdodds

In that case, I guess it comes down to if this is worth the effort to support without Cypress. If you have a test like this it's entirely possible that JSDOM isn't good enough and you'd need Cypress anyway, and I imagine cypress-testing-library should make it easier to migrate.

nickmccurdy avatar Nov 08 '20 15:11 nickmccurdy

I am in a situation where I cannot use cypress to test this out - it would bloat the codebase if only a single component uses the library. It would be highly preferred to be able to use jest, enzyme or rtl. How would you recommend someone be able to test out, say, an event that triggers at the end of a scroll action? With or without the given libraries as options.

svadali2 avatar Nov 09 '20 10:11 svadali2

You could mock whatever your code uses to detect scroll position. Or if that logic is in its own function you can replace the entire thing with a mock.

nickmccurdy avatar Nov 09 '20 11:11 nickmccurdy

Hi. Actually, I've met a case when a user wheels a page under the focused input[type=number] and its value has changed. I added some code to avoid such behaviour, but I also wanted to cover it with the test but I couldn't. I hope, it would be useful

Anatoliy13Kravchenko avatar Dec 25 '22 17:12 Anatoliy13Kravchenko

+1 for above, encountering a use case of scrolling on <input type="number">

More specifically, I want to test the onWheel event but seems like I've no way to do it at the moment.

xsjcTony avatar Jan 21 '23 15:01 xsjcTony

Hi! Another use case: A UI where a Continue button is disabled, waiting for the user to scroll to the bottom of the list (within a fixed-height container).

julianCast avatar Feb 06 '23 11:02 julianCast

For a scrollbar component with an event for scrollToBottom, I also ran into the issue of layouting. We're not yet using Cypress within the repo, so I wanted to solve it with jsdom.

I found a StackOverlow answer by kamlesh suggesting using spyOn(element, 'scrollHeight', 'get').mockImplementation(() => scrollHeight) to mock the getter function. That way, you can manually mock the layouting values used by the scroll position calculations.

We also have a threshold, so you don't have to (necessarily) scroll all the way to the bottom, but within a range of it. Trying to test this behavior made the limitation of fireEvent pretty clear. I wrote a function to simulate the scroll event happening many times to scroll from one position to the next.

const scroll = (scrollBar, scrollTop) => {
  fireEvent.scroll(scrollBar, { target: { scrollTop } });
};

const simulateScroll = (scrollBar, scrollTop) => {
  let currentScrollTop = scrollBar.scrollTop;
  let change = scrollBar.scrollTop > scrollTop ? -10 : 10;
  if (Math.abs(scrollBar.scrollTop - scrollTop) % 100 === 0) {
    // performance optimization. Go 50/-50 at once when difference is a couple 100.
    change *= 5;
  }

  while (
    currentScrollTop !== scrollTop &&
    !(
      currentScrollTop < 0 ||
      currentScrollTop > scrollBar.scrollHeight - scrollBar.clientHeight
    )
  ) {
    currentScrollTop += change;
    scroll(scrollBar, currentScrollTop);
  }
};

Since this issue has the tag "needs specification", I'm wondering whether the above is something that this package wants to include. Then in the docs, we could give caveats and advice to use cypress, but still provide a helper if someone wants it. Could save some people some time.

npeterkamps avatar Mar 02 '23 12:03 npeterkamps

+1 for scrolling on <input type="number">, also more specifically testing the disabling of 'wheel' event.

yaboi avatar May 16 '24 22:05 yaboi