skbrhmn
skbrhmn

Reputation: 1199

Finding views in a ViewPager using Espresso (AmbiguousViewMatcherException)

I have a ViewPager which shows only one fragment on the screen at a time. This fragment has a recyclerView which is filled with list items that consist of one image and two textViews. I need to select the item and perform a click based on the text displayed.

My viewPage has the Id pager, the recyclerView is listview, and I am using the cardView widget which has a relative layout called cardContainer which has a textView nickNname that I am interested in.

Espresso only seems to recognize pager but it cannot find any of the other Id's in the view hierarchy. How can I access nickName or basically any other view inside the viewPager or fragment?

I have tried the following:

        ViewInteraction listviewInteraction = onView (allOf(withId(R.id.pager)));
    listviewInteraction.check(matches(hasDescendant(withId(R.id.listview))));

I get the following error as pager does not have any child

    Expected: has descendant: with id: 2131689666
Got: "ViewPager{id=2131689659, res-name=pager, visibility=VISIBLE, width=1080, height=1533, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=144.0, child-count=0}"

I have also tried out a few different methods similar to what was posted here but Espresso cannot find listview or nickname.

How can I find the view nickName?

UPDATE:

I have run a few more tests and it seems the viewPager never loads the fragments when Espresso runs its tests. I have tried using Thread.sleep() to wait for a while but no luck. I see the listView populated for only a while and then the pager goes blank and the test fails because it cannot find any other views. Any idea on what could be causing this?

UPDATE 2:

The pager loads as normal now but Espresso cannot recognize listview or cardContainer but only pager because of AmbiguousViewMatcherException. There are four fragments in the viewPager but only one fragment is being shown on the screen. My code is as follows:

ViewInteraction listviewInteraction = onView (allOf(withId(R.id.pager)));
    listviewInteraction.check(matches(isDisplayed()));
    onView(allOf(withId(R.id.listview), isDisplayed())).check(matches(isDisplayed()));

There are two matches in the view hierarchy. If I don't use the isDisplayed() method in the viewMatcher, then I get four matches for the four fragments. How do I solve this?

Upvotes: 2

Views: 3410

Answers (2)

user2237529
user2237529

Reputation: 81

If anyone is interested in selectingTabByText below mechanism works

/**
 * Select Tab by [tabName] if it's available. Otherwise Throw PerformException
 */
fun selectTabByText(tabName: String, contains: Boolean = false, ignoreCase: Boolean = false): ViewAction {
    return object : ViewAction {
        override fun getDescription() = "With tab name $tabName"

        override fun getConstraints() = Matchers.allOf(isDisplayed(), ViewMatchers.isAssignableFrom(TabLayout::class.java))

        override fun perform(uiController: UiController, view: View) {
            val tabLayout = view as TabLayout
            val tabCount = tabLayout.tabCount
            for (tabIndex in 0 until tabCount) {
                /**
                 * Get the text of each available tab and if it's the same as [tabName]
                 * then select it
                 */
                val tabAtIndex: TabLayout.Tab = tabLayout.getTabAt(tabIndex)
                    ?: throw PerformException.Builder()
                        .withCause(Throwable("Tab at Index $tabIndex failed to be resolved to a Tab Object"))
                        .build()
                val tabText = tabAtIndex.text.toString()
                if (tabText.compare(tabName, ignoreCase, contains)) {
                    tabAtIndex.select()
                    return
                }
            }
            throw PerformException.Builder()
                .withCause(Throwable("Tab with name: $tabName with params: contains = $contains and ignoreCase = $ignoreCase is not found"))
                .build()
        }
    }
}

/**
 * Returns true if [contains] is true and this String contains the specified [other] String as a substring, optionally ignoring character case.
 * Returns true if [contains] is false and this string is equal to other, optionally ignoring character case.
 * @param ignoreCase `true` to ignore character case when comparing strings. By default `false`.
 */
fun String.compare(other: String, ignoreCase: Boolean = false, contains: Boolean = false): Boolean =
    if (contains) {
        this.contains(other, ignoreCase = ignoreCase)
    } else

Upvotes: 0

skbrhmn
skbrhmn

Reputation: 1199

There was apparently a listview offscreen even though it was not visible. The matcher isDisplayed() also takes into account views that are off screen. Using isCompletelyDisplayed() fixed the AmbiguousViewMatcherException.

Upvotes: 7

Related Questions