Closed ardentum-c closed 2 months ago
@pierrezimmermannbam could you take a look, as you know press()
the best
Hmm yeah this is not an easy one, I'm not too sure how I would go about this. The reason we wait for 130ms is because onPressOut will never be called before that time and we wanted onPressOut to be called because it felt like what users would expect. Also I think it makes sense that the simulation of a press event involves some delay because it accounts for the user performing an action. The DEFAULT_MIN_PRESS_DURATION is implementation detail so you don't want it to leak in your tests for sure. I see a couple of approaches that could work here:
I hope this helps, let me know if anything is unclear or if you have suggestions that would help make this clearer for other users
I am not sure that trying to pinpoint timer + renders to a millisecond is a good approach, it's getting into area of testing RN/RNTL internal implementation and is prone to break if RN constant changes, etc.
Instead of this:
// this doesn't work
await act(() => jest.advanceTimersByTimeAsync(ASYNC_CALLBACK_DURATION - 1));
// and here is a workaround option:
// await act(() => jest.advanceTimersByTimeAsync(ASYNC_CALLBACK_DURATION - DEFAULT_MIN_PRESS_DURATION - 1));
expect(screen.getByText("loading...")).toBeVisible();
// Now the loading label should finally be hidden.
// So the overall time it was visible equals ASYNC_CALLBACK_DURATION.
await act(() => jest.advanceTimersByTimeAsync(1));
expect(screen.queryByText("loading...")).toBeNull();
Just do this:
await waitForElementToBeRemoved(() => screen.getByText("loading..."));
https://callstack.github.io/react-native-testing-library/docs/api#waitforelementtoberemoved
This does not rely on knowing min press duration implementation details, yet both conditions: that the loading element was there, and I stopped being there after some time (async call). Which would be correct from the users perspective.
Alternatively you can do:
// Loading just after render
expect(screen.getByText("loading...")).toBeOnTheScreen();
// Wait for something other to appear:
await screen.findByText("Loaded")
expect(screen.queryByText("loading...")).not.toBeOnTheScreen();
Thanks for the answers! Indeed, the example I provided is a bit far-fetched. There could be more realistic scenarios when the issue is relevant. I can think of an example of instagram-like stories component that requires precise slides durations to be tested along with press events.
I guess the fireEvent.press
is preferable in such cases.
I learned something new. Thank you again!
Closing as resolved.
I am trying to test a component that shows some loading state while the passed async onPress callback is executing.
<TestComponent onPress={ ... }/>
, which shows loading state while the provided asynconPress
callback is in progressrender(<TestComponent onPress={() => delay(ASYNC_CALLBACK_DURATION)} />)
and triggeringuserEvent.press()
ASYNC_CALLBACK_DURATION - 1
ms, advancing the timers withjest.advanceTimersByTimeAsync
expect
fails here. In my case the component shows the loading state forASYNC_CALLBACK_DURATION - 130 - 1
ms.As I understand, the mysterious 130ms is from DEFAULT_MIN_PRESS_DURATION constant, which is used to add delay to press events, mimicking the React Native Pressability behavior.
Here is a complete test case to reproduce:
So, the question:
Is there a better, more obvious way to test such a behavior and not depend on the internal DEFAULT_MIN_PRESS_DURATION constant? Is this the expected and correct behavior of
user.press()
?Initially I was planning to report this as a bug, but the more I'm thinking of it the more I am not sure.
Versions