Reputation: 20646
I'm trying to click on an item of my BottomNavigationBar
but even if I record an Espresso test and then put it on my test it doesn't find the onView
.
What record an Espresso test is giving wrote for me is :
private fun childAtPosition(
parentMatcher: Matcher<View>, position: Int
): Matcher<View> {
return object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("Child at position $position in parent ")
parentMatcher.describeTo(description)
}
public override fun matchesSafely(view: View): Boolean {
val parent = view.parent
return parent is ViewGroup && parentMatcher.matches(parent)
&& view == parent.getChildAt(position)
}
}
}
val bottomNavigationItemView = onView(
allOf(
withContentDescription("Home"),
childAtPosition(
childAtPosition(
withId(R.id.navigation_bar),
0
),
1
),
isDisplayed()
)
)
bottomNavigationItemView.perform(click())
And even if I try this, it says :
androidx.test.espresso.PerformException: Error performing 'single click' on view '(with content description text: is "Home" and Child at position 1 in parent Child at position 0 in parent with id is <package_name:id/navigation_bar> and is displayed on the screen to the user)'.
I've tried several ways, with BoundedMatcher
but I couldn't get it work. What I'm missing?
fun withTitle(titleTested: String): Matcher<View?>? {
return object : BoundedMatcher<View?, BottomNavigationItemView>(
BottomNavigationItemView::class.java
) {
private var triedMatching = false
private var title: String? = null
override fun describeTo(description: Description) {
if (triedMatching) {
description.appendText("with title: $titleTested")
description.appendText("But was: " + title.toString())
}
}
override fun matchesSafely(item: BottomNavigationItemView): Boolean {
triedMatching = true
title = item.itemData.title.toString()
return title == titleTested
}
}
}
The hierarchy of my xml is :
CoordinatorLayout(id : mainCordinator)
RelativeLayout (no id)
com.google.android.material.bottomnavigation.BottomNavigationView(id : navigation_bar) />
Coordinatorlayout(id: anotherCoordinator)
FrameLayout(id: framelayout)
FloatActionButton(id: fab_test)
/>
/>
/>
I'd like to know the easiest way so I can call a function passing the index of the bottomNav and click on it, or even sending as a parameter the ContentDescription/Title/Text, whatever it is.
I'm creating the items dynamically as follows :
ArrayList<String> items...
....Create....
Menu menu = binding.navigationBar.getMenu();
menu.clear();
for (int i = 0; i < items.size(); i++) {
menu.add(
0,
items.get(i),
i,
bottomBarTitles.get(i));
menu.getItem(i).setIcon(bottomBarImages.getItemIcon(items.get(i)));
bottomMenu.add(items.get(i));
Items is a ArrayList<String>
with for instance "Home, Content, Images, More"
And then I have another ArrayList
with the images.
I could make it work using UiDevice
and UiSelector
but I would like to use Espresso for this one.
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
device.findObject(UiSelector().text("Home")).click()
Upvotes: 4
Views: 1440
Reputation: 1245
Can you try this one:
onView( allOf( withText("Home), isDescendantOfA(withId(R.id.navigation_bar)) ).perform( click() )
basically we are looking for a view with text "Home" which also happens to be descendant of a view with id of your bottom bar.
Upvotes: 1
Reputation: 1392
I would think using withID would for you, I know espresso recorded tests are flaky and it's best to write your own code.
onView(withId(R.id.home).perform(click())
I'm not sure what IDs you've assigned to navigation items, so I used home.
If the multiple views have the same ID you can use.
onView(withId(R.id.home), withText("Home")).performClick()
If you use withText()
by itself it will try and perform the click action on all Views that have the word "Home".
Upvotes: 2