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(); } }
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(); }
@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(); } }
@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) { 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(); } }; }
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(); } }; }
/** * 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(); } }; }
public static ViewAction waitInvisibleGoneId(final int viewId, final long millis) { return new ViewAction() { @Override public Matcher<View> getConstraints() { return isRoot(); } @Override public String getDescription() { return "wait view 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); // Log.e("test", "waitInvisibleGoneId view id:" + viewId); uiController.loopMainThreadForAtLeast(200); do { View find = null; for (View child : TreeIterables.breadthFirstViewTraversal(view)) { // if (viewMatcher.matches(child) && child.getVisibility() == View.VISIBLE) { // Log.e("test","original view id:"+viewId+" child view id:"+child.getId()); if (viewMatcher.matches(child)) { find = child; Log.e("test", "macthch visible: " + child.getVisibility()); } } if (find != null && find.getVisibility() == View.VISIBLE) { // Log.e("test", "find startTime:" + startTime); // Log.e("test","find endTime:"+endTime); // Log.e("test","find System.currentTimeMillis():"+System.currentTimeMillis()); } else { Log.e("test", "waitInvisibleGoneId wait time :" + (System.currentTimeMillis() - startTime) / 1000 + " id:" + viewId); return; } // Log.e("test","startTime:"+startTime); // Log.e("test","endTime:"+endTime); uiController.loopMainThreadForAtLeast(50); } while (System.currentTimeMillis() < endTime); Log.e("test", "waitInvisibleGoneId time out :" + (System.currentTimeMillis() - startTime) / 1000 + " id:" + viewId); throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new TimeoutException()) .build(); } }; }
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 view 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); Log.e("test", "waitId original view id:" + viewId); do { for (View child : TreeIterables.breadthFirstViewTraversal(view)) { // if (viewMatcher.matches(child) && child.getVisibility() == View.VISIBLE) { // Log.e("test","original view id:"+viewId+" child view id:"+child.getId()); if (viewMatcher.matches(child) && child.getVisibility() == View.VISIBLE) { // Log.e("test", "find startTime:" + startTime); // Log.e("test","find endTime:"+endTime); // Log.e("test","find System.currentTimeMillis():"+System.currentTimeMillis()); Log.e("test", "waitId wait time :" + (System.currentTimeMillis() - startTime) / 1000 + " id:" + viewId); return; } } // Log.e("test","startTime:"+startTime); // Log.e("test","endTime:"+endTime); uiController.loopMainThreadForAtLeast(50); } while (System.currentTimeMillis() < endTime); Log.e("test", "waitId time out :" + (System.currentTimeMillis() - startTime) / 1000 + " id:" + viewId); throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new TimeoutException()) .build(); } }; }
/** * Perform action of waiting for a specific view id. * <p/> * E.g.: * onView(isRoot()).perform(waitId(R.id.dialogEditor, Sampling.SECONDS_15)); * * @param viewId * @param millis * @return */ 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 // seperate 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. Also the longpress behavour is app/widget specific. So if you have // a seperate long press behaviour from your short press, you can pass in a // 'RollBack' ViewAction which when executed will undo the effects of long press. while (status != Tapper.Status.SUCCESS && loopCount < 3) { try { status = tapper.sendTap(uiController, coordinates, precision); } catch (RuntimeException re) { throw new PerformException.Builder() .withActionDescription( String.format("%s - At Coordinates: %d, %d and precision: %d, %d", this.getDescription(), (int) coordinates[0], (int) coordinates[1], (int) precision[0], (int) precision[1])) .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. With Rollback? %s", coordinates[0], coordinates[1], precision[0], precision[1], tapper, coordinatesProvider, precisionDescriber, loopCount, false))) .build(); } if (tapper == Tap.SINGLE && view instanceof WebView) { // WebViews will not process click events until double tap // timeout. Not the best place for this - but good for now. uiController.loopMainThreadForAtLeast(ViewConfiguration.getDoubleTapTimeout()); } }
/** * Performs screenshot of activity with specified view * use like onView(isRoot()).perform(screenshot(R.id.MainActivity)); * @param viewId view to look at * @param testTag description of screenshoshot * @return */ public static ViewAction screenshot(final int viewId, final String testTag) { return new ViewAction() { @Override public Matcher<View> getConstraints() { return isRoot(); } @Override public String getDescription() { return "screenshoting current activity with root id <" + viewId + "> ."; } @Override public void perform(final UiController uiController, final View view) { uiController.loopMainThreadUntilIdle(); final Matcher<View> viewMatcher = withId(viewId); for (View child : TreeIterables.breadthFirstViewTraversal(view)) { // found view with required ID if (viewMatcher.matches(child)) { Activity host=(Activity) child.getContext(); try { Screenshot.capture(testTag+"_"+host.getLocalClassName().toString(),host); } catch (IOException e) { //something bad happened.no screenshot. Log.d(TAG,e.toString()); e.printStackTrace(); throw new RuntimeException(e); } return; } } // nothing found throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new NoRootViewFoundWithIdException()) .build(); } }; }
/** * Perform action of waiting for a specific view id * based off https://stackoverflow.com/questions/21417954/espresso-thread-sleep * @param viewId - view to look for * @param millis - how long to wait for in milliseconds */ 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(); } }; }