How do you click on a menuItem in a NavigationView from the design library using Espresso? It works fine on a larger devices where the whole menu is visible but in landscape or smaller devices the bottom of my menu is off the screen and I can not figure out a way to scroll/swipe to these menuItems so I can click on them.
Calling:
onView(withText(R.string.contact_us)).perform(scrollTo()).check(matches(isDisplayed())).perform(click());
Generates the following:
Caused by: java.lang.RuntimeException: Action will not be performed because the target view does not match one or more of the following constraints: (view has effective visibility=VISIBLE and is descendant of a: (is assignable from class: class android.widget.ScrollView or is assignable from class: class android.widget.HorizontalScrollView)) Target view: "NavigationMenuItemView{id=-1, visibility=VISIBLE, width=888, height=144, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=1653.0, text=Contact Us, input-type=0, ime-target=false, has-links=false}"
NavigationView inherits from FrameLayout and are doing a canvas shift to scroll. https://developer.android.com/reference/android/support/design/widget/NavigationView.html
I tried to write a customViewAction to slowly scroll the screen using the below action and check if the menuItem is visible and if not keep scrolling, but I could not get it to work.
CoordinatesProvider coordinatesProvider = new CoordinatesProvider() {
public float[] calculateCoordinates(View view) {
float[] xy = GeneralLocation.BOTTOM_CENTER.calculateCoordinates(view);
xy[0] += 0.0F * (float)view.getWidth();
xy[1] += -0.083F * (float)view.getHeight();
return xy;
}
};
onView(withId(R.id.navigation_view)).perform(new GeneralSwipeAction(Swipe.SLOW,
coordinatesProvider, GeneralLocation.TOP_CENTER, Press.FINGER));
onView(withText(R.string.contact_us)).check(matches(isDisplayed())).perform(click());
Has anyone figured out how to test navigationView on Espresso yet? I am pretty sure my logic behind the customViewAction would work but I can not get it to work.
I figured it out, I used a custom viewAction
public static ViewAction swipeForTextToBeVisible(final String text, final long millis, final View navigationView) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isRoot();
}
@Override
public String getDescription() {
return "swipe for a specific view with text <" + text + "> during " + millis + " millis.";
}
@Override
public void perform(final UiController uiController, final View view) {
uiController.loopMainThreadUntilIdle();
final long startTime = System.currentTimeMillis();
final long endTime = startTime + millis;
final Matcher<View> viewMatcher = withText(text);
do {
boolean returnAfterOneMoreSwipe = false; //not sure why I have to do one more swipe but it works
for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
// found view with required ID
if (viewMatcher.matches(child) && child.getVisibility() == View.VISIBLE) {
returnAfterOneMoreSwipe = true;
}
}
CoordinatesProvider coordinatesProvider = new CoordinatesProvider() {
public float[] calculateCoordinates(View view) {
float[] xy = GeneralLocation.BOTTOM_CENTER.calculateCoordinates(view);
xy[0] += 0.0F * (float)view.getWidth();
xy[1] += -0.083F * (float)view.getHeight();
return xy;
}
};
//Below code is c/p from perform in android/support/test/espresso/action/GeneralSwipeAction.class
float[] startCoordinates = coordinatesProvider.calculateCoordinates(navigationView);
float[] endCoordinates = GeneralLocation.TOP_CENTER.calculateCoordinates(navigationView);
float[] precision = Press.FINGER.describePrecision();
Swiper.Status status = Swiper.Status.FAILURE;
for(int tries = 0; tries < 3 && status != Swiper.Status.SUCCESS; ++tries) {
try {
status = Swipe.SLOW.sendSwipe(uiController, startCoordinates, endCoordinates, precision);
} catch (RuntimeException var9) {
throw (new PerformException.Builder()).withActionDescription(this.getDescription()).withViewDescription(HumanReadables.describe(navigationView)).withCause(var9).build();
}
int duration = ViewConfiguration.getPressedStateDuration();
if(duration > 0) {
uiController.loopMainThreadForAtLeast((long)duration);
}
}
//End c/p from android/support/test/espresso/action/GeneralSwipeAction.class
if (returnAfterOneMoreSwipe) {
return;
}
uiController.loopMainThreadForAtLeast(50);
}
while (System.currentTimeMillis() < endTime);
// timeout happens
throw new PerformException.Builder()
.withActionDescription(this.getDescription())
.withViewDescription(HumanReadables.describe(view))
.withCause(new TimeoutException())
.build();
}
};
}
I do not love this solution, it is not the cleanest since for some reason I have to swipe again after it says its visible but it works.
To use it you call it with the following:
onView(isRoot()).perform(CustomViewActions.swipeForTextToBeVisible(getActivity().getString(R.string.contact_us),2000, getActivity().findViewById(R.id.navigation_view)));
This worked for me even when menu item was not visible (somewhere down in the list)
onView(withId(R.id.nav_view)).perform(NavigationViewActions.navigateTo(R.id.nav_login));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With