Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KRecyclerView: RecyclerActions::scrollToEnd() throws NullPointerException #60

Open
cee-dee opened this issue Jul 18, 2022 · 3 comments
Open

Comments

@cee-dee
Copy link

cee-dee commented Jul 18, 2022

Steps to reproduce:

  1. Use long-loading RecyclerView items in a standard RecyclerView
  2. 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.

@AlexeyRybakov
Copy link

Same problem

@Vacxe
Copy link
Member

Vacxe commented Jul 7, 2023

@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

@Unlimity
Copy link
Contributor

Unlimity commented Jul 7, 2023

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.

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

No branches or pull requests

4 participants