I have a tab layout with view pager .I am using Espresso for testing my android app. In my previous projects I use the tab title to perform click for selecting a tab Position like follows.
Espresso.onView(ViewMatchers.withText("MAP"))
.perform(ViewActions.click());
Right now I a have 114 tabs. So I can't use above method for randomly select these tabs for testing. Is there any way I can select tabs by its position. I already check other solutions but none of those helped me.
Should be doable with a custom ViewAction
. Something like this:
fun selectTabAtPosition(tabIndex: Int): ViewAction {
return object : ViewAction {
override fun getDescription() = "with tab at index $tabIndex"
override fun getConstraints() = allOf(isDisplayed(), isAssignableFrom(TabLayout::class.java))
override fun perform(uiController: UiController, view: View) {
val tabLayout = view as TabLayout
val tabAtIndex: TabLayout.Tab = tabLayout.getTabAt(tabIndex)
?: throw PerformException.Builder()
.withCause(Throwable("No tab at index $tabIndex"))
.build()
tabAtIndex.select()
}
}
}
and the usage:
onView(withId(R.id.tab_layout)).perform(selectTabAtPosition(99))
As someone who hasn't switched/used Kotlin, here is it in Java. It's basically the same as @Be_Negative so just use it in the same way.
@NonNull
private static ViewAction selectTabAtPosition(final int position) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return allOf(isDisplayed(), isAssignableFrom(TabLayout.class));
}
@Override
public String getDescription() {
return "with tab at index" + String.valueOf(position);
}
@Override
public void perform(UiController uiController, View view) {
if (view instanceof TabLayout) {
TabLayout tabLayout = (TabLayout) view;
TabLayout.Tab tab = tabLayout.getTabAt(position);
if (tab != null) {
tab.select();
}
}
}
};
}
Both of the above answers are good. For me, after selecting the tab I also wanted to verify that it is the correct tab by matching with the title. Since, this post is about espresso testing, I will share the code snippet here as it may be helpful to someone.
fun matchCurrentTabTitle(tabTitle: String): Matcher<View> {
return object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description?) {
description?.appendText("unable to match title of current selected tab with $tabTitle")
}
override fun matchesSafely(item: View?): Boolean {
val tabLayout = item as TabLayout
val tabAtIndex: TabLayout.Tab = tabLayout.getTabAt(tabLayout.selectedTabPosition)
?: throw PerformException.Builder()
.withCause(Throwable("No tab at index ${tabLayout.selectedTabPosition}"))
.build()
return tabAtIndex.text.toString().contains(tabTitle, true)
}
}
}
fun matchTabTitleAtPosition(tabTitle: String, tabIndex: Int): Matcher<View> {
return object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description?) {
description?.appendText("unable to select tab at index $tabIndex and match title with $tabTitle")
}
override fun matchesSafely(item: View?): Boolean {
val tabLayout = item as TabLayout
val tabAtIndex: TabLayout.Tab = tabLayout.getTabAt(tabIndex)
?: throw PerformException.Builder()
.withCause(Throwable("No tab at index $tabIndex"))
.build()
return tabAtIndex.text.toString().contains(tabTitle, true)
}
}
}