Describe the bug
scrollTo continues to scroll after reaching the bottom of the ScrollView.
To Reproduce
ScrollView in an application.scrollTo in a test for android. I.e await element(by.id('someTestId')).scrollTo('bottom');Video
You can see after reaching the bottom of the screen, the driver continues to try and scroll down. I scroll back up in the video and it takes me back to the bottom, but just continues to scroll. Ultimately the tests just time out on this step.

Expected behavior
Move on to the next action after reaching the edge, in this case bottom, of the screen.
Environment (please complete the following information):
Device and Verbose Detox Logs
Here are the trace logs from a single test. The last invoke action hangs the entire time Detox is attempting to scroll. After the test times out the error action is logged.
detox-server wss 15:26:25: role=tester action=isReady (sessionId=Test)
detox-server wss 15:26:25: role=testee not connected, cannot fw action (sessionId=Test)
detox-server wss 15:26:29: role=testee login (sessionId=Test)
detox-server wss 15:26:29: role=testee action=loginSuccess (sessionId=Test)
detox-server wss 15:26:29: role=testee action=ready (sessionId=Test)
detox-server wss 15:26:30: role=testee disconnect (sessionId=Test)
detox-server wss 15:26:47: role=tester action=isReady (sessionId=Test)
detox-server wss 15:26:47: role=testee not connected, cannot fw action (sessionId=Test)
detox-server wss 15:26:51: role=testee login (sessionId=Test)
detox-server wss 15:26:51: role=testee action=loginSuccess (sessionId=Test)
detox-server wss 15:26:51: role=testee action=ready (sessionId=Test)
detox-server wss 15:26:51: role=tester action=invoke (sessionId=Test)
detox-server wss 15:27:02: role=testee action=invokeResult (sessionId=Test)
detox-server wss 15:27:02: role=tester action=invoke (sessionId=Test)
detox-server wss 15:27:02: role=testee action=invokeResult (sessionId=Test)
detox-server wss 15:27:02: role=tester action=invoke (sessionId=Test)
detox-server wss 15:27:02: role=testee action=invokeResult (sessionId=Test)
detox-server wss 15:27:02: role=tester action=invoke (sessionId=Test)
detox-server wss 15:27:02: role=testee action=invokeResult (sessionId=Test)
detox-server wss 15:27:02: role=tester action=invoke (sessionId=Test)
detox-server wss 15:27:02: role=testee action=invokeResult (sessionId=Test)
detox-server wss 15:27:02: role=tester action=invoke (sessionId=Test)
detox-server wss 15:29:49: role=testee action=error (sessionId=Test)
This functionality works as intended in 12.11.1, but does not work in 12.11.2 and up. I would guess it is related to #1464
@d4vidi More scrolling woes :( Can you pls take a look?
Yes, I'll give this high priority. @AlexK777 I assume that by ScrollView you're referring to react native's implementation, right?
Yes, that is correct.
@AlexK777 we have self-tests and numerous app scenarios where this works properly. Is there any other configuration you can think of that possibly makes your use case different? Or even better, could you share the primary JS-render code (with the ScrollView)?
@d4vidi none that I can think of, inside the ScrollView are a few container components, nothing strange. The only thing I am certain of is this functionality works if I revert my Detox version to 12.11.1, but it does not work for any version between 12.11.2 and 13.0.1. I will give 13.1.0, a shot later today. If I still see issues with this functionality I will see if I can get a repro put together.
@d4vidi I created a react-native init project and added a ScrollView. I am seeing the functionality work as intended there as well. I then swapped out our container components within the ScrollView of our actual application; for the Text components from the react-native init project, and it was still scrolling endlessly. This leads me to believe it has to be a specific prop on our ScrollView. I can report back if I figure it out, but I am also ok closing this issue since this seems specific to my app and not a Detox issue.
For now I am just using the await waitFor(element(by.id('id1'))).toBeVisible().whileElement(by.id('scrollId')).scroll(400, 'down'); command as a workaround.
hmm interesting - I think that suggests the problem could be with scrollTo(), specifically.
Anyways I think that you _should_ be using waitFor()+scroll() instead of scrollTo(). I don't consider that a workaround at all...
In any case I could really use more info re the scroll view, as you've mentioned. It would help a great deal in creating a reproduction scenario.
Anyways I think that you should be using waitFor()+scroll() instead of scrollTo(). I don't consider that a workaround at all...
I respectfully disagree. We have CMS driven data at the bottom of a ScrollView. Making an API call to get that data, then using waitFor() + scroll() is much slower than just using scrollTo('bottom') and tapping a designated testID. I don't even see the purpose of scrollTo() if you always suggest waitFor() + scroll().
It depends on what the intention is. waitFor+scroll is good when you don't know how much you have to scroll and don't want to rely on magic numbers. On the other hand, if you know your view is visible at the bottom of a scrollview, scrolling to the bottom is a perfectly fine and preferred solution.
Alright, I think I have the issue here pinned down. scrollTo('bottom') is not working correctly when a marginTop is set in the contentContainerStyle prop.
Will scroll endlessly:
<SafeAreaView style={{flex: 1}}>
<ScrollView
contentContainerStyle={{marginTop: 16}}
testID="dummyScroll"
>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
</ScrollView>
</SafeAreaView>
I changed the marginTop just for testing, and this works as expected.
<SafeAreaView style={{flex: 1}}>
<ScrollView
contentContainerStyle={{paddingTop: 16}}
testID="dummyScroll"
>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
<Text style={styles.instructions}>Making this long so we can scroll for testing</Text>
</ScrollView>
</SafeAreaView>
This was tested against Detox version 13.1.0.
BTW, just to make sure iOS works correctly, does this test, with the marginTop, work correctly on iOS?
I had to switch to Detox version 13.0.2 due to #1525, but can confirm iOS worked as expected, Android did not. This issue appears to only affect Android.
@AlexK777 'really appreciate your effort on this, thanks! This probably _does_ pin it down. If margins are the case here then my guess is that RN's impl for ScrollView has a bug in the canScrollVertically() method. I'll take a deeper look asap.
They have a custom ScrollView impl? 🤦♂️
The java equiv of the JS ScrolllView is a class called ReactScrollView that inherits Android's ScrollView. It is possible that they haven't taken into account that the changes require adaptations in canScrollVertically()
Looking at the source, they seem to use the padding for increasing the scroll window, but canScrollVertically() is not even implemented. 🙃
Indeed, it's possible that something is a bit off there. That could force us back into the older, more complex, implementation that I was trying to get rid of.
Can't we just calc the offset from the max scroll height and use that instead of canScrollVertically()?
@AlexK777 well I was easily able to reproduce this myself thanks to your findings, but I've actually found something even more interesting here (I think) - it seems that there's an RN bug where with a top margin set, the scroll effectively _does not reach its end_ (!)
Please try running your app again with an overly impactful margin (e.g. 32 or even 40): when scrolled to the bottom, you should be able to see the last list item clamped up at the bottom.
We are seeing sort of the same behavior on iOS after upgrading our app from RN 0.57.8 to 0.59.10 while using Detox 12.6.3 or newer versions.
In our case it doesn't even start scrolling:

We have tried and none work:
element(by.id('someTestId')).scrollTo('someEdge');
and
waitFor(element(someElement))
.toBeVisible()
.whileElement(by.id('ScrollView'))
.scroll(400, 'up')
We are working around the scrollTo not working by doing a swipe, but the waitFor + scroll not working really screws up since we are searching for an item in the scrollview.
Any ideas?
@santiagofm See the full scroll() api: https://github.com/wix/Detox/blob/master/docs/APIRef.ActionsOnElement.md#scrollpixels-direction-startpositionxnan-startpositionynan
Try adding a start position that is lower than the status bar and top bar.
@santiagofm See the full
scroll()api: https://github.com/wix/Detox/blob/master/docs/APIRef.ActionsOnElement.md#scrollpixels-direction-startpositionxnan-startpositionynanTry adding a start position that is lower than the status bar and top bar.
That worked! Thanks a lot!
@d4vidi Was this resolved?
No. It's in the backlog, please leave open.