KRecyclerView: RecyclerActions::scrollToEnd() throws NullPointerException
cee-dee opened this issue · comments
Steps to reproduce:
- Use long-loading RecyclerView items in a standard RecyclerView
- call
scrollToEnd()
in a UI test
Observed Results:
The code
val lastView = view.findViewHolderForLayoutPosition(position)!!.itemView
view.scrollBy(0, lastView.height)
throws a NullPointerException
because lastView
is null
.
Expected Results:
I expected the RecyclerView just to scroll down to have the last item fully visible.
Relevant Code:
override fun scrollToEnd() {
view.perform(object : ViewAction {
override fun getDescription() = "Scroll RecyclerView to the bottom"
override fun getConstraints() = ViewMatchers.isAssignableFrom(RecyclerView::class.java)
override fun perform(controller: UiController, view: View) {
if (view is RecyclerView) {
val position = view.adapter!!.itemCount - 1
view.scrollToPosition(position)
controller.loopMainThreadUntilIdle()
val lastView = view.findViewHolderForLayoutPosition(position)!!.itemView
view.scrollBy(0, lastView.height)
controller.loopMainThreadUntilIdle()
}
}
})
}
Workaround:
I've created an extension function to still be able to do what I'd like to do:
fun KRecyclerView.scrollToEndRepeatedly(repetitions: Int) {
view.perform(
object : ViewAction {
override fun getDescription() =
"Scroll RecyclerView to the bottom"
override fun getConstraints() =
ViewMatchers.isAssignableFrom(
RecyclerView::class.java
)
override fun perform(controller: UiController, view: View) {
if (view is RecyclerView) {
var lastViewFound = false
var tryCount = 0
do {
tryCount++
val position = view.adapter!!.itemCount - 1
view.scrollToPosition(position)
controller.loopMainThreadUntilIdle()
val lastView =
view.findViewHolderForLayoutPosition(
position
)
lastView?.let {
view.scrollBy(0, lastView.itemView.height)
lastViewFound = false
}
controller.loopMainThreadUntilIdle()
} while ((!lastViewFound) && (tryCount < repetitions))
}
}
}
)
}
While this does what it's supposed to do, I think, there must be a better solution using interceptors which fit's more naturally into Kakaos concepts, i.e. making the repetions
parameter superfluous.
Same problem
@cee-dee @AlexeyRybakov what do you mean under "long-loading items"?
If it some view what related on async calls - you may use IdleResources for it. Otherwise it those views is "long-loaded" because of device performance it should't be a problem because it will block UI thread.
@Unlimity please leave you comments
If your RecyclerView's adapter and layout manager cannot layout all children in a single layout pass - there is not much Espresso and Kakao can do for you. You either need to optimize your RecyclerView to be able to layout all items in adapter as it is expected by the system, or use your own extension or try/catch with retry blocks in the test itself.
This is a very specific corner case and is not system expected behavior, so I don't see a lot of value into supporting it as part of the library.