diff --git a/app/src/androidTest/java/com/orgzly/android/espresso/NoteEventsTest.kt b/app/src/androidTest/java/com/orgzly/android/espresso/NoteEventsTest.kt index a8b622aae..eb3cf520d 100644 --- a/app/src/androidTest/java/com/orgzly/android/espresso/NoteEventsTest.kt +++ b/app/src/androidTest/java/com/orgzly/android/espresso/NoteEventsTest.kt @@ -154,8 +154,6 @@ class NoteEventsTest : OrgzlyTest() { onNotesInAgenda().check(matches(recyclerViewItemCount(10))) - SystemClock.sleep(2000) - // Today: deadline onItemInAgenda(1, R.id.item_head_scheduled_text).check(matches(not(isDisplayed()))) onItemInAgenda(1, R.id.item_head_deadline_text).check(matches(isDisplayed())) diff --git a/app/src/androidTest/java/com/orgzly/android/espresso/QueryFragmentTest.java b/app/src/androidTest/java/com/orgzly/android/espresso/QueryFragmentTest.java index 52422197a..68033ec49 100644 --- a/app/src/androidTest/java/com/orgzly/android/espresso/QueryFragmentTest.java +++ b/app/src/androidTest/java/com/orgzly/android/espresso/QueryFragmentTest.java @@ -14,6 +14,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withParent; import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; import static com.orgzly.android.espresso.util.EspressoUtils.contextualToolbarOverflowMenu; import static com.orgzly.android.espresso.util.EspressoUtils.onActionItemClick; import static com.orgzly.android.espresso.util.EspressoUtils.onBook; @@ -24,6 +25,7 @@ import static com.orgzly.android.espresso.util.EspressoUtils.replaceTextCloseKeyboard; import static com.orgzly.android.espresso.util.EspressoUtils.scroll; import static com.orgzly.android.espresso.util.EspressoUtils.searchForTextCloseKeyboard; +import static com.orgzly.android.espresso.util.EspressoUtils.waitId; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.endsWith; @@ -264,6 +266,7 @@ public void testSchedulingNote() { onView(withId(R.id.date_picker_button)).perform(click()); onView(withClassName(equalTo(DatePicker.class.getName()))).perform(setDate(2014, 4, 1)); onView(withText(android.R.string.ok)).perform(click()); + onView(isRoot()).perform(waitId(R.id.time_picker_button, 5000)); onView(withId(R.id.time_picker_button)).perform(scroll(), click()); onView(withClassName(equalTo(TimePicker.class.getName()))).perform(setTime(9, 15)); onView(withText(android.R.string.ok)).perform(click()); diff --git a/app/src/androidTest/java/com/orgzly/android/espresso/ReposActivityTest.java b/app/src/androidTest/java/com/orgzly/android/espresso/ReposActivityTest.java index cfc1c168f..acf5c3f1a 100644 --- a/app/src/androidTest/java/com/orgzly/android/espresso/ReposActivityTest.java +++ b/app/src/androidTest/java/com/orgzly/android/espresso/ReposActivityTest.java @@ -4,12 +4,14 @@ import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; import static com.orgzly.android.espresso.util.EspressoUtils.onListItem; import static com.orgzly.android.espresso.util.EspressoUtils.onSnackbar; import static com.orgzly.android.espresso.util.EspressoUtils.replaceTextCloseKeyboard; import static com.orgzly.android.espresso.util.EspressoUtils.scroll; +import static com.orgzly.android.espresso.util.EspressoUtils.waitId; import androidx.test.core.app.ActivityScenario; @@ -46,6 +48,7 @@ public void testDirectoryRepoWithPercentCharacter() { ActivityScenario.launch(ReposActivity.class); onView(withId(R.id.activity_repos_flipper)).check(matches(isDisplayed())); + onView(isRoot()).perform(waitId(R.id.activity_repos_directory, 5000)); onView(withId(R.id.activity_repos_directory)).perform(scroll(), click()); onView(withId(R.id.activity_repo_directory)).perform(replaceTextCloseKeyboard(repoUri)); onView(withId(R.id.fab)).perform(click()); // Repo done diff --git a/app/src/androidTest/java/com/orgzly/android/espresso/util/EspressoUtils.java b/app/src/androidTest/java/com/orgzly/android/espresso/util/EspressoUtils.java index b474274c1..66e0b46db 100644 --- a/app/src/androidTest/java/com/orgzly/android/espresso/util/EspressoUtils.java +++ b/app/src/androidTest/java/com/orgzly/android/espresso/util/EspressoUtils.java @@ -10,6 +10,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; import static androidx.test.espresso.matcher.ViewMatchers.withClassName; import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription; import static androidx.test.espresso.matcher.ViewMatchers.withId; @@ -33,12 +34,15 @@ import androidx.appcompat.widget.Toolbar; import androidx.recyclerview.widget.RecyclerView; import androidx.test.espresso.DataInteraction; +import androidx.test.espresso.PerformException; import androidx.test.espresso.UiController; import androidx.test.espresso.ViewAction; import androidx.test.espresso.ViewInteraction; import androidx.test.espresso.action.CloseKeyboardAction; import androidx.test.espresso.contrib.RecyclerViewActions; import androidx.test.espresso.matcher.ViewMatchers; +import androidx.test.espresso.util.HumanReadables; +import androidx.test.espresso.util.TreeIterables; import androidx.test.platform.app.InstrumentationRegistry; import com.orgzly.R; @@ -49,6 +53,8 @@ import org.hamcrest.Matchers; import org.hamcrest.TypeSafeMatcher; +import java.util.concurrent.TimeoutException; + /* * Few espresso-related notes: * @@ -179,8 +185,8 @@ public static ViewInteraction onSavedSearch(int position) { } public static ViewInteraction onRecyclerViewItem(@IdRes int recyclerView, int position, @IdRes int childView) { + onView(isRoot()).perform(waitId(recyclerView, 5000)); onView(withId(recyclerView)).perform(RecyclerViewActions.scrollToPosition(position)); - SystemClock.sleep(500); return onView(new EspressoRecyclerViewMatcher(recyclerView) .atPositionOnView(position, childView)); } @@ -447,4 +453,50 @@ public void perform(UiController uiController, View view) { public static ViewAction scroll() { return new NestedScrollViewExtension(); } + + /** + * Perform action of waiting for a specific view id. Copied from https://stackoverflow.com/a/49814995. + * @param viewId The id of the view to wait for. + * @param millis The timeout of until when to wait for. + */ + public static ViewAction waitId(final int viewId, final long millis) { + return new ViewAction() { + @Override + public Matcher 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 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(); + } + }; + } } \ No newline at end of file