I have the following list item that is used within a RecyclerView that has a vertical LinearLayout Manager. I am using espresso, and I would like to simulate clicking the id/action_save menu item. Below is my latest attempts at figuring this one out. I have tried using onData method combined with onChildView, but a MenuItem is not a View. The onView and withId methods works well if the menu item is located in a toolbar that is in the action bar, but while being embedded inside of a RecyclerView.
I have tried recording using the espresso recording feature in android studio, however, it inject code similar to this Espresso click menu item. When running the test case again causes the test to raise an exception.
List Item
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="0dp"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimaryDark"
android:elevation="4dp"
android:minHeight="?attr/actionBarSize"
app:popupTheme="@style/MyDarkToolbarStyle"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<ProgressBar
android:id="@+id/progress"
style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true" />
<android.support.v7.widget.AppCompatImageView
android:id="@+id/imgPalette"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="250dp" />
</RelativeLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_marginTop="-2dp"
android:elevation="4dp"
android:outlineProvider="bounds"
android:paddingTop="2dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimaryDark">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</FrameLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
Menu
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_save"
android:title="@string/save"
app:showAsAction="never" />
</menu>
Main Layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/banner" />
...
</RelativeLayout>
Java (Espresso / JUnit Test Case)
...
// mProducts is a list that is backed by the adapter and is of Type Product
for (int i = 0; i < mProducts.size(); i++) {
onView(allOf(withId(R.id.recyclerView), withParent(withId(R.id.container))))
.perform(actionOnItemAtPosition(i, clickOverflow(R.id.toolbar)));
// Add delay for system to respond to overflow being opened
SystemClock.sleep(1000);
onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition(i, clickMenuItem(R.id.toolbar, R.id.action_save)));
}
...
public static ViewAction clickMenuItem(final int toolbarId, final int id) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return null;
}
@Override
public String getDescription() {
return "Click on a child view with specified id.";
}
@Override
public void perform(UiController uiController, View view) {
Toolbar toolbar = (Toolbar) view.findViewById(toolbarId);
for (int i = 0; i < toolbar.getChildCount(); i++) {
if (toolbar.getChildAt(i) instanceof ActionMenuView) {
ViewGroup viewGroup = ((ViewGroup) toolbar.getChildAt(i));
for (int j = 0; j < viewGroup.getChildCount(); j++) {
if (viewGroup.getChildAt(j).getId() == id) {
viewGroup.getChildAt(j).performClick();
break;
}
}
}
}
}
};
}
public static ViewAction clickOverflow(final int toolbarId) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return null;
}
@Override
public String getDescription() {
return "Click on a child view with specified id.";
}
@Override
public void perform(UiController uiController, View view) {
Toolbar toolbar = (Toolbar) view.findViewById(toolbarId);
((ViewGroup) toolbar.getChildAt(0)).getChildAt(0).performClick();
}
};
}
Error
android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: with id: com.variable.inspire:id/recyclerView
If the target view is not part of the view hierarchy, you may need to use Espresso.onData to load it from one of the following AdapterViews:android.support.v7.widget.MenuPopupWindow$MenuDropDownListView{92d26ac VFED.VC.. .F...... 0,0-515,252}
Update #1
This does not solve my question, but provides a workaround by displaying the menu item directly and not inside the overflow menu. My question still is using espresso can you open the overflow menu and click an item in it.
onView(allOf(withId(R.id.recyclerView), withParent(withId(R.id.container)))).perform(actionOnItemAtPosition(i, clickMenuItem(R.id.toolbar, R.id.action_save)));