mercredi 29 mars 2017

Using Espresso with Embedded Toolbar

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.

List Item

<android.support.v7.widget.CardView xmlns:android="http://ift.tt/nIICcg"
    xmlns:app="http://ift.tt/GEGVYd"
    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://ift.tt/nIICcg"
xmlns:app="http://ift.tt/GEGVYd">
    <item
       android:id="@+id/action_save"
       android:title="@string/save"
       app:showAsAction="never" />
</menu>

Main Layout

 <RelativeLayout xmlns:android="http://ift.tt/nIICcg"
    xmlns:app="http://ift.tt/GEGVYd"
    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}

Aucun commentaire:

Enregistrer un commentaire