Skip to content

Conversation

mdjastrzebski
Copy link
Member

@mdjastrzebski mdjastrzebski commented Oct 25, 2024

Summary

Resolves: #1687

Reverses pressOut and press event order so that it matches experimental data for regular presses:

  1. pressIn,
  2. (press duration)
  3. pressOut
  4. (no waiting) press.

The previous setup matched edge case for very short (< 130ms) presses, when order was:

  1. pressIn
  2. (short press duration < 130 ms)
  3. press
  4. (wait till 130 ms - short press duration)
  5. pressOut

Scope

  • Fix event order
  • Prevent triggering events on unmounted components

See experiments: https://github.com/callstack/react-native-testing-library/wiki/Press-Events

Test plan

All tests pass

@mdjastrzebski mdjastrzebski changed the title fix: press event order fix: press event order [WIP] Oct 25, 2024
@mdjastrzebski
Copy link
Member Author

@pierrezimmermannbam I'm messing with User Event press / longPress implementation. You might want to take a look,

@winghouchan
Copy link

@mdjastrzebski: interesting observation in regards to 'short' presses. I was able to reproduce this in my own experimentation too. Do you know if this phenomenon is documented somewhere (whether that be actual documentation or a reference to source code)?

@mdjastrzebski
Copy link
Member Author

@mdjastrzebski mdjastrzebski changed the title fix: press event order [WIP] fix: press event order Oct 26, 2024
Copy link
Collaborator

@pierrezimmermannbam pierrezimmermannbam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work investigating the issue! The fix is correct but I suggested a slightly different approach

@mdjastrzebski mdjastrzebski merged commit 5de8790 into main Oct 30, 2024
8 checks passed
@mdjastrzebski mdjastrzebski deleted the fix/press-event-order branch October 30, 2024 20:19
@mdjastrzebski
Copy link
Member Author

This PR has been released in v12.8.1 🎉

@cartok
Copy link

cartok commented May 21, 2025

Since these changes, I have issues on some tests when using userEvent() i'd have to setup userEvent() with the delay option, like { delay: 100 }. Was it expected from these changes? The fireEvent() still works as before.

@mdjastrzebski
Copy link
Member Author

@cartok What kind of issues are you having with user event?

@cartok
Copy link

cartok commented May 26, 2025

@mdjastrzebski Thanks for reply. I can no longer reproduce it. Must've been something with my test environment. Maybe related to the jest vscode extension.

@friederbluemle
Copy link
Contributor

I also have a test case which is failing with 12.8.1 and passing with 12.8.0 - A bit unexpected that a patch increment can break something on the consumer side.

Simplified test looks like this - The screen makes an API call on first render, and then again after pull to refresh (using standard ScrollView/RefreshControl from RN. While the API call is in progress (async), a loading skeleton is shown on screen. As mentioned, up until 12.8.0 everything succeeds, but breaks with 12.8.1 - The API is never called, and loading-skeleton never shows up again (i.e. calling "onRefresh" with fireEvent no longer works as before).

  it('clears loading state after loading is done and pull to refresh', async () => {
    render(<TestScreen />);

    await waitForElementToBeRemoved(() => screen.getByTestId('loading-skeleton'));

    const scrollView = screen.getByTestId('scroll-view');
    fireEvent(scrollView.props.refreshControl, 'onRefresh');

    await waitForElementToBeRemoved(() => screen.getByTestId('loading-skeleton'));

    expect(Api.getFooBar).toHaveBeenCalledTimes(2);
  });

Is there a better way to interact with RefreshControl/onRefresh in a pull to refresh scenario? Any idea why 12.8.1 breaks this? Thank you!

@friederbluemle
Copy link
Contributor

Ah interesting, after doing a bit more investigation, I found the root cause is related to those 3 added lines that check if the element is mounted:

function fireEvent(element: ReactTestInstance, eventName: EventName, ...data: unknown[]) {
  if (!isElementMounted(element)) {
    return;
  }
// ...

Maybe there is something wrong with the way I query the element? Why would scrollView.props.refreshControl not be mounted when I call fireEvent?

@mdjastrzebski
Copy link
Member Author

mdjastrzebski commented Sep 15, 2025

Looks like you are trying to fire event on a prop (refresh control), which is a React.Element type (description of React component to be rendered),

Fire Event, and most other RNTL APIs expect to be called on ReactTestInstance, rendered React component.

As a workaround, you can try to either:

  • assign your Refresh Control
    a test id, and use it to find the element
  • manually call: refreshControl.onRefresh without using fire event

@friederbluemle
Copy link
Contributor

Thanks for the quick reply @mdjastrzebski!

Fire Event, and most other RNTL APIs expect to be called on ReactTestInstance, rendered React component.

Yes, that makes sense.

Regarding the workarounds, assigning a testID also does not work, it is not able to find the element (I assume it is never "really" rendered).
But - workaround #2 does work! Calling onRefresh() directly via scrollView.props.refreshControl.props.onRefresh() makes the test pass like before.

Using the chained props access really doesn't look good which is why I would prefer fireEvent syntax. I understand it doesn't make sense to use it on an "unmounted" element. The next and real question is why is RefreshControl never rendered/mounted.

@friederbluemle
Copy link
Contributor

So, is there more "RNTL way" to interact with RefreshControl and avoid accessing props directly?

@mdjastrzebski
Copy link
Member Author

WIP #1822

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

React Native Testing Library + Expo Router Testing Library: Unable to find node on an unmounted component after press event simulation

6 participants