public void perform(UiController uiController, View view) { RecyclerView recyclerView = (RecyclerView) view; (new ScrollToPositionViewAction(this.position)).perform(uiController, view); uiController.loopMainThreadUntilIdle(); View targetView = recyclerView.getChildAt(this.position).findViewById(this.viewId); if (targetView == null) { throw (new PerformException.Builder()).withActionDescription(this.toString()) .withViewDescription(HumanReadables.describe(view)) .withCause(new IllegalStateException( "No view with id " + this.viewId + " found at position: " + this.position) ) .build(); } else { this.viewAction.perform(uiController, targetView); } }
@Override public void perform(UiController uiController, View view) { if (isDisplayingAtLeast(90).matches(view)) { Log.i(TAG, "View is already displayed. Returning."); return; } Rect rect = new Rect(); view.getDrawingRect(rect); if (!view.requestRectangleOnScreen(rect, true /* immediate */)) { Log.w(TAG, "Scrolling to view was requested, but none of the parents scrolled."); } uiController.loopMainThreadUntilIdle(); if (!isDisplayingAtLeast(90).matches(view)) { throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new RuntimeException( "Scrolling to view was attempted, but the view is not displayed")) .build(); } }
@Override public void perform(UiController uiController, View view) { if (isDisplayingAtLeast(90).matches(view)) { Log.i(TAG, "View is already displayed. Returning."); return; } Rect rect = new Rect(); view.getDrawingRect(rect); if (!view.requestRectangleOnScreen(rect, true /* immediate */)) { Log.w(TAG, "Scrolling to view was requested, but none of the parents scrolled."); } uiController.loopMainThreadUntilIdle(); if (!isDisplayingAtLeast(90).matches(view)) { throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new RuntimeException("Scrolling to view was attempted, but the view is not displayed")) .build(); } }
/** * When the error is coming from espresso, it is more user friendly to: * 1. propagate assertions as assertions * 2. swap the stack trace of the error to that of current thread (which will show * directly where the actual problem is) */ private Throwable getUserFriendlyError(Throwable error, Matcher<View> viewMatcher, String screenShotLocation) { String formattedScreenLocation = ""; if (screenShotLocation != null) { formattedScreenLocation = " \n. Location of Screenshot: \n " + screenShotLocation; } if (error instanceof PerformException) { // Re-throw the exception with the viewMatcher (used to locate the view) as the view // description (makes the error more readable). The reason we do this here: not all creators // of PerformException have access to the viewMatcher. throw new PerformException.Builder() .from((PerformException) error) .withViewDescription(viewMatcher.toString() + formattedScreenLocation) .build(); } if (error instanceof AssertionError) { // reports Failure instead of Error. // assertThat(...) throws an AssertionFailedError. error = new AssertionFailedWithCauseError(error.getMessage() + formattedScreenLocation, error); } error.setStackTrace(Thread.currentThread().getStackTrace()); return error; }
public void perform(UiController uiController, View view) { RecyclerView recyclerView = (RecyclerView) view; (new ScrollToPositionViewAction(this.position)).perform(uiController, view); uiController.loopMainThreadUntilIdle(); View targetView = recyclerView.getChildAt(this.position).findViewById(this.viewId); if (targetView == null) { throw (new PerformException.Builder()).withActionDescription(this.toString()) .withViewDescription( HumanReadables.describe(view)) .withCause(new IllegalStateException( "No view with id " + this.viewId + " found at position: " + this.position)) .build(); } else { this.viewAction.perform(uiController, targetView); } }
public RecyclerViewInteraction checkView(final @IdRes int id, final ViewAssertion itemViewAssertion) { viewInteraction.check(new ViewAssertion() { @Override public void check(View view, NoMatchingViewException ex) { RecyclerView recyclerView = (RecyclerView) view; RecyclerView.ViewHolder viewHolderForPosition = recyclerView.findViewHolderForLayoutPosition(position); if (viewHolderForPosition == null) { throw (new PerformException.Builder()) .withActionDescription(toString()) .withViewDescription(HumanReadables.describe(view)) .withCause(new IllegalStateException("No view holder at position: " + position)) .build(); } else { View viewAtPosition = viewHolderForPosition.itemView.findViewById(id); itemViewAssertion.check(viewAtPosition, ex); } } }); return this; }
@Override public final void perform(UiController uiController, View view) { uiController.loopMainThreadUntilIdle(); long finishTime = System.currentTimeMillis() + duration; while (System.currentTimeMillis() < finishTime) { if (isConditionMet(view)) { return; } uiController.loopMainThreadForAtLeast(50L); } throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new TimeoutException()) .build(); }
public Act performWildCardAction(Act origin) { Espresso.onView(isRoot()).perform( new ChimpStagingAction() ); chimpDriver.preemptiveTraceReport(); try { ArrayList<UiObject2> uiObject2s = wildCardManager.retrieveUiObject2s(wildCardSelector, wildCardChildSelector, 10); ArrayList<WildCardTarget> matchers = MatcherManager.getViewMatchers(uiObject2s, userDefinedMatcher); while(matchers.size() > 0) { Log.i(tag("wildcard"), Integer.toString(matchers.size())); WildCardTarget target = wildCardManager.popOne(matchers); try { Log.i(tag("wildcard"), "Attempting to perform action on UiObject"); Act result = performWildCardTargetAction(origin, target); return result; } catch (AmbiguousViewMatcherException avme){ Log.e(tag("wildcard"), avme.toString()); } catch (NoMatchingViewException nmve){ Log.e(tag("wildcard"), nmve.toString()); } catch (PerformException pe){ Log.e(tag("wildcard"), pe.toString()); } } } catch (Exception ee) { Log.e(tag("wildcard"), "Error occurred at wild card top-level"); ee.printStackTrace(); } Log.e(tag("wildcard"), "Exhausted all wild card options."); return null; }
@Override public final void check(View view, NoMatchingViewException e) { RecyclerView recyclerView = (RecyclerView) view; RecyclerView.ViewHolder viewHolderForPosition = recyclerView.findViewHolderForLayoutPosition(position); if (viewHolderForPosition == null) { throw (new PerformException.Builder()) .withActionDescription(toString()) .withViewDescription(HumanReadables.describe(view)) .withCause(new IllegalStateException("No view holder at position: " + position)) .build(); } else { View viewAtPosition = viewHolderForPosition.itemView; itemViewAssertion.check(item, viewAtPosition, e); } }
@Override public void perform(UiController uiController, View view) { ViewMatcherIdlingResource idlingResource = new ViewMatcherIdlingResource(timeout, viewMatcher, view); registerIdlingResources(idlingResource); uiController.loopMainThreadUntilIdle(); unregisterIdlingResources(idlingResource); if(!idlingResource.isMatched()) { throw new PerformException.Builder() .withActionDescription(getDescription()) .withViewDescription(HumanReadables.getViewHierarchyErrorMessage(view, null, "Action timed out : " + getDescription(), null)) .build(); } }
public static ViewAction waitForMatch(final Matcher<View> aViewMatcher, final long timeout) { return new ViewAction() { @Override public Matcher<View> getConstraints() { return isRoot(); } @Override public String getDescription() { return "Waiting for view matching " + aViewMatcher; } @Override public void perform(UiController uiController, View view) { uiController.loopMainThreadUntilIdle(); final long startTime = System.currentTimeMillis(); final long endTime = startTime + timeout; do { for (View child : TreeIterables.breadthFirstViewTraversal(view)) { if (aViewMatcher.matches(child)) { // found return; } } uiController.loopMainThreadForAtLeast(50); } while (System.currentTimeMillis() < endTime); //The action has timed out. throw new PerformException.Builder() .withActionDescription(getDescription()) .withViewDescription("") .withCause(new TimeoutException()) .build(); } }; }
@Override public final void check(View view, NoMatchingViewException e) { RecyclerView recyclerView = (RecyclerView) view; RecyclerView.ViewHolder viewHolderForPosition = recyclerView.findViewHolderForLayoutPosition(position); if (viewHolderForPosition == null) { throw (new PerformException.Builder()).withActionDescription(toString()) .withViewDescription(HumanReadables.describe(view)) .withCause(new IllegalStateException("No view holder at position: " + position)) .build(); } else { View viewAtPosition = viewHolderForPosition.itemView; itemViewAssertion.check(item, viewAtPosition, e); } }
@Test public void testFaultyClickArgumentsView() { expectedException.expect(PerformException.class); expectedException.expectCause(allOf( isA(SporkRuntimeException.class), hasMessage(containsString("onClick() failed because the method arguments are invalid")) )); TestFaultyClickArgumentsView view = activityRule.getActivity().getTestFaultyClickArgumentsView(); assertNotNull(view); onView(withId(view.getId())).perform(click()); }
@Override public void perform(UiController uiController, View view) { if (isDisplayingAtLeast(90).matches(view)) { Log.i(TAG, "View is already displayed. Returning."); return; } View parentScrollView = findScrollView(view); CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) parentScrollView.getLayoutParams(); params.setBehavior(null); parentScrollView.requestLayout(); uiController.loopMainThreadUntilIdle(); Rect rect = new Rect(); view.getDrawingRect(rect); if (!view.requestRectangleOnScreen(rect, true /* immediate */)) { Log.w(TAG, "Scrolling to view was requested, but none of the parents scrolled."); } uiController.loopMainThreadUntilIdle(); if (!isDisplayingAtLeast(90).matches(view)) { throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new RuntimeException( "Scrolling to view was attempted, but the view is not displayed")) .build(); } }
private View findScrollView(View view) { View parent = (View) view.getParent(); if (parent != null) { if (parent instanceof NestedScrollView) { return parent; } return findScrollView(parent); } throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new RuntimeException( "Scrolling aborted due to not being NestedScrollView child")) .build(); }
private void checkIsNotEmpty(View view, StringDescription description) { if (sortedList.isEmpty()) { description.appendText("The list must be not empty"); throw (new PerformException.Builder()) .withActionDescription(description.toString()) .withViewDescription(HumanReadables.describe(view)) .withCause(new IllegalStateException("The list is empty")) .build(); } }
@Override public void perform(UiController uiController, View view) { checkNotNull(viewAction); ViewFinder viewFinder = ViewFinderHelper.buildViewFinder(viewMatcher, view); View descendantView = viewFinder.getView(); if (descendantView == null) { throw new PerformException.Builder() .withActionDescription(getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new RuntimeException("Descendant view not found")) .build(); } try { viewAction.perform(uiController, descendantView); } catch (Throwable t) { throw new PerformException.Builder() .withActionDescription(getDescription()) .withViewDescription(HumanReadables.describe(descendantView)) .withCause(t) .build(); } }
@Override public void perform(UiController uiController, View view) { checkNotNull(viewAssertion); try { viewAssertion.check(view, null); } catch (Throwable e) { throw new PerformException.Builder() .withActionDescription(getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(e) .build(); } }
/** * Looks for a view with the given ID for a given millis * @param snippet ID of the view you are looking for * @param millis Millis trying to find it * @return ViewAction so you can use like any other EspressoAction */ public static ViewAction waitSnippet(final EspressoSnippet snippet, final long millis) { return new ViewAction() { @Override public Matcher<View> getConstraints() { return isRoot(); } @Override public String getDescription() { return "wait for snippet<" + snippet.getDescription() + "> 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; do { try{ snippet.attempt(); return; }catch(Exception ex){ Log.w(".MalariaEspressoActions", ex.getMessage()); } uiController.loopMainThreadForAtLeast(50); } while (System.currentTimeMillis() < endTime); // timeout happens throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new TimeoutException()) .build(); } }; }
@Test(expected = PerformException.class) public void Verify_Throw_Next_Step_When_Steps_Left() { onView(withId(R.id.bt_next)).perform(click()); onView(withId(R.id.bt_next)).perform(click()); onView(withId(R.id.bt_next)).perform(click()); onView(withId(R.id.bt_next)).perform(click()); onView(withId(R.id.bt_next)).perform(click()); }
private void checkIsNotEmpty(View view, StringDescription description) { if (sortedList.isEmpty()) { description.appendText("The list must be not null"); throw (new PerformException.Builder()) .withActionDescription(description.toString()) .withViewDescription(HumanReadables.describe(view)) .withCause(new IllegalStateException("The list is empty")) .build(); } }
@Override public final void check(final View view, final NoMatchingViewException e) { final RecyclerView recyclerView = (RecyclerView) view; final RecyclerView.ViewHolder viewHolderForPosition = recyclerView.findViewHolderForLayoutPosition(position); if (viewHolderForPosition == null) { throw (new PerformException.Builder()).withActionDescription(toString()) .withViewDescription(HumanReadables.describe(view)) .withCause(new IllegalStateException("No view holder at position: " + position)) .build(); } else { final View viewAtPosition = viewHolderForPosition.itemView; itemViewAssertion.check(item, viewAtPosition, e); } }
public static ViewAction waitId(final int viewId, final long millis) { return new ViewAction() { @Override public Matcher<View> getConstraints() { return isRoot(); } @Override public String getDescription() { return "wait for a specific view with id <" + viewId + "> 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 = withId(viewId); do { for (View child : TreeIterables.breadthFirstViewTraversal(view)) { // found view with required ID if (viewMatcher.matches(child)) { 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(); } }; }
/** * Tests if the app crashes when the crash button is pressed */ @Test(expected = PerformException.class) public void testVerifyCrash(){ checkIfIdIsDisplayedWithText(R.id.bug_fragment_message, R.string.crash_headline); try { clickId(R.id.crash_button); fail("App did not crash"); } catch (PerformException e ){} }
private static PerformException getPerformException(String description, Throwable cause) { return new PerformException.Builder() .withActionDescription(description) .withViewDescription("unknown") // likely to be replaced by FailureHandler .withCause(cause) .build(); }
/** * Perform action of waiting for a specific view id. */ public static ViewAction waitId(final int viewId, final long millis) { return new ViewAction() { @Override public Matcher<View> getConstraints() { return isRoot(); } @Override public String getDescription() { return "wait for a specific view with id <" + viewId + "> 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 = withId(viewId); do { for (View child : TreeIterables.breadthFirstViewTraversal(view)) { // found view with required ID if (viewMatcher.matches(child)) { 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(); } }; }
/** Perform action of waiting for a specific view id. */ public static ViewAction waitId(final int viewId, final long millis) { return new ViewAction() { @Override public Matcher<View> getConstraints() { return isRoot(); } @Override public String getDescription() { return "wait for a specific view with id <" + viewId + "> 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 = withId(viewId); do { for (View child : TreeIterables.breadthFirstViewTraversal(view)) { // found view with required ID if (viewMatcher.matches(child)) { 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(); } }; }
@Override public void perform(UiController uiController, View view) { float[] coordinates = coordinatesProvider.calculateCoordinates(view); float[] precision = precisionDescriber.describePrecision(); Tapper.Status status = Tapper.Status.FAILURE; int loopCount = 0; // Native event injection is quite a tricky process. A tap is actually 2 // separate motion events which need to get injected into the system. Injection // makes an RPC call from our app under test to the Android system server, the // system server decides which window layer to deliver the event to, the system // server makes an RPC to that window layer, that window layer delivers the event // to the correct UI element, activity, or window object. Now we need to repeat // that 2x. for a simple down and up. Oh and the down event triggers timers to // detect whether or not the event is a long vs. short press. The timers are // removed the moment the up event is received (NOTE: the possibility of eventTime // being in the future is totally ignored by most motion event processors). // // Phew. // // The net result of this is sometimes we'll want to do a regular tap, and for // whatever reason the up event (last half) of the tap is delivered after long // press timeout (depending on system load) and the long press behaviour is // displayed (EG: show a context menu). There is no way to avoid or handle this more // gracefully. while (status != Tapper.Status.SUCCESS && loopCount < 3) { try { status = tapper.sendTap(uiController, coordinates, precision); } catch (RuntimeException re) { throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(re) .build(); } int duration = ViewConfiguration.getPressedStateDuration(); // ensures that all work enqueued to process the tap has been run. if (duration > 0) { uiController.loopMainThreadForAtLeast(duration); } if (status == Tapper.Status.WARNING) { break; } loopCount++; } if (status == Tapper.Status.FAILURE) { throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause( new RuntimeException( String.format( "Couldn't click at: %s,%s precision: %s, %s . Tapper: %s" + " coordinate provider: %s precision " + "describer: %s. Tried %s times.", coordinates[0], coordinates[1], precision[0], precision[1], tapper, coordinatesProvider, precisionDescriber, loopCount))) .build(); } }
@Override public void perform(UiController uiController, View view) { float[] startCoordinates = startCoordinatesProvider.calculateCoordinates(view); float[] endCoordinates = endCoordinatesProvider.calculateCoordinates(view); float[] precision = precisionDescriber.describePrecision(); Swiper.Status status = Swiper.Status.FAILURE; for (int tries = 0; tries < MAX_TRIES && status != Swiper.Status.SUCCESS; tries++) { try { status = swiper.sendSwipe(uiController, startCoordinates, endCoordinates, precision); } catch (RuntimeException re) { throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(re) .build(); } int duration = ViewConfiguration.getPressedStateDuration(); // ensures that all work enqueued to process the swipe has been run. if (duration > 0) { uiController.loopMainThreadForAtLeast(duration); } } if (status == Swiper.Status.FAILURE) { throw new PerformException.Builder() .withActionDescription(getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new RuntimeException(String.format( "Couldn't swipe from: %s,%s to: %s,%s precision: %s, %s . Swiper: %s " + "start coordinate provider: %s precision describer: %s. Tried %s times", startCoordinates[0], startCoordinates[1], endCoordinates[0], endCoordinates[1], precision[0], precision[1], swiper, startCoordinatesProvider, precisionDescriber, MAX_TRIES))) .build(); } }
/** * Looks for a view with the given ID for a given millis * @param viewId ID of the view you are looking for * @param millis Millis trying to find it * @return ViewAction so you can use like any other EspressoAction */ public static ViewAction waitId(final int viewId, final long millis) { return new ViewAction() { @Override public Matcher<View> getConstraints() { return isRoot(); } @Override public String getDescription() { return "wait for a specific view with id <" + viewId + "> 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 = withId(viewId); do { for (View child : TreeIterables.breadthFirstViewTraversal(view)) { // found view with required ID if (viewMatcher.matches(child)) { 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(); } }; }
@Test(expected = PerformException.class) public void Verify_Throw_Prev_Step_When_Steps_Left() { onView(withId(R.id.bt_prev)).perform(click()); }