I have a list where every row contains a name and a button that invokes a Context Menu of options. I would like to write a test that verifies the following things
I would also like to test the contents of the actionBar and actionBar overflow menu when the item is long selected.
For both tests, I can write a check that ensures there is a view element with the correct "label" displayed (i.e finding the view using onView(withText(this.elementText)). However I have 2 actions which have the same label but different IDs and I need to ensure the correct action is in the context menu/long click menu.
I can not use the ID I specified in the XML for my context menu's menu because Android's Context Menu views do not have those IDs, instead they contain an internal Android ID (see the screenshot below).
When I wrote tests using Robotium, i had to get all current views of a certain type and the parse through them checking if they are the actionBar items, see sample code below.
public static List<MenuItemImpl> getLongClickMenuItems(String itemName) {
List<MenuItemImpl> menuItems = new ArrayList<>();
// long select the item
solo.clickLongOnText(itemName);
// get the children of the of the long click action bar
ArrayList<ActionMenuView> outViews = solo.getCurrentViews(ActionMenuView.class, solo.getView(R.id.action_mode_bar));
if (!outViews.isEmpty()) {
// get the first child which contains the action bar actions
ActionMenuView actionMenuView = outViews.get(0);
// loop over the children of the ActionMenuView which is the individual ActionMenuItemViews
// only a few fit will fit on the actionBar, others will be in the overflow menu
int count = actionMenuView.getChildCount();
for (int i = 0; i < count; i++) {
View child = actionMenuView.getChildAt(i);
if (child instanceof ActionMenuItemView) {
menuItems.add(((ActionMenuItemView) child).getItemData());
} else {
// this is the more button, click on it and wait for the popup window
// which will contain a list of ListMenuItemView
// As we are using the AppCompat the actionBar's menu items are the
// the AppCompat's ListMenuItemView (android.support.v7.view.menu.ListMenuItemView)
// In the context menu, the menu items are Android's native ListMenuItemView
// (com.android.internal.view.menu.ListMenuItemView)
solo.clickOnView(child);
solo.waitForView(ListMenuItemView.class);
ArrayList<ListMenuItemView> popupItems = solo.getCurrentViews(ListMenuItemView.class);
for (ListMenuItemView lvItem : popupItems) {
menuItems.add(lvItem.getItemData());
}
// close the more button actions menu
solo.goBack();
}
}
}
// get out of long click mode
solo.goBack();
return menuItems;
}
Does anyone know how I can get the list of Context Row menu items using Expresso.
Does anyone know how I can get the actionBar items (including all items in the overflow menu) using Expresso?
Thank you so much to dominicoder for pretty much giving me the answer to this question. working on their reply i have managed to get it working.
Instead of using "isAssignableFrom(AdapterView.class)" I use "isAssignableFrom(ListView.class)".
I then use the exact same matcher "dominicoder" provided to verify the context menu item count.
Using "dominicoder's" sample matchers I was able to code one myself that checks the MenuItem at a certain position in the ListView and I can compare the IDs to make sure its the ID that I am expecting.
public boolean verifyRowContextMenuContents(String name, MyActionObject[] actions){
// invoke the row context menu
clickRowActionButton(name);
// The Context Menu Popup contains a ListView
int expectedItemCount = actions.length;
// first check the Popup's listView contains the correct number of items
onView(isAssignableFrom(ListView.class))
.check(matches(correctNumberOfItems(expectedItemCount)));
// now check the order and the IDs of each action in the menu is the expected action
for (int i = 0; i < expectedItemCount; i++) {
onView(isAssignableFrom(ListView.class))
.check(matches(correctMenuId(i, actions[i].getId())));
}
// close the context menu
pressBack();
return true;
}
private static Matcher<View> correctNumberOfItems(final int itemsCount) {
return new BoundedMatcher<View, ListView>(ListView.class) {
@Override
public void describeTo(Description description) {
description.appendText("with number of items: " + itemsCount);
}
@Override
protected boolean matchesSafely(ListView listView) {
ListAdapter adapter = listView.getAdapter();
return adapter.getCount() == itemsCount;
}
};
}
private static Matcher<View> correctMenuId(final int position, final int expectedId) {
return new BoundedMatcher<View, ListView>(ListView.class) {
@Override
public void describeTo(Description description) {
description.appendText("with position : " + position + " expecting id: " + expectedId);
}
@Override
protected boolean matchesSafely(ListView listView) {
ListAdapter adapter = listView.getAdapter();
Object obj = adapter.getItem(position);
if (obj instanceof MenuItem){
MenuItem menuItem = (MenuItem)obj;
return menuItem.getItemId() == expectedId;
}
return false;
}
};
}
With this code I can check the Context Menu contains the correct number of menu items, and that the items in the menu are the ones i am expecting (using ID verification) and the order I am expecting.
Huge thank you to "dominicoder". Its a shame you can not mark both answers as correct as you did actually give me virtually the correct answer.