From 3ff64628991c2caea085ba61a30ddd53eeb7fb40 Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Mon, 30 Jul 2018 16:00:13 -0300 Subject: [PATCH] Refactor Espresso tests to use new AccountManager --- briar-android/fastlane/Screengrabfile | 4 + briar-android/proguard-test.txt | 2 +- ...ava => BriarTestComponentApplication.java} | 2 +- .../briar/android/TestComponent.java | 6 + .../android/login/PasswordActivityTest.java | 50 ++++++++ .../android/login/SetupActivityTest.java | 106 +++++++++++++++ .../navdrawer/NavDrawerActivityTest.java | 11 +- .../settings/SettingsActivityTest.java | 42 ++---- .../briar/android/test/BriarTestRunner.java | 4 +- .../briar/android/test/ScreenshotTest.java | 121 +++++------------- .../briar/android/test/ViewActions.java | 89 ++++++------- 11 files changed, 253 insertions(+), 184 deletions(-) rename briar-android/src/androidTest/java/org/briarproject/briar/android/{TestBriarApplication.java => BriarTestComponentApplication.java} (88%) create mode 100644 briar-android/src/androidTest/java/org/briarproject/briar/android/login/PasswordActivityTest.java create mode 100644 briar-android/src/androidTest/java/org/briarproject/briar/android/login/SetupActivityTest.java diff --git a/briar-android/fastlane/Screengrabfile b/briar-android/fastlane/Screengrabfile index f69c2238aa..3afd283cd1 100644 --- a/briar-android/fastlane/Screengrabfile +++ b/briar-android/fastlane/Screengrabfile @@ -1,5 +1,9 @@ app_package_name "org.briarproject.briar.android.screenshot.debug" locales ['en-US'] +use_tests_in_classes([ + 'org.briarproject.briar.android.login.SetupActivityTest', + 'org.briarproject.briar.android.settings.SettingsActivityTest', +]) app_apk_path "build/outputs/apk/screenshot/debug/briar-android-screenshot-debug.apk" tests_apk_path "build/outputs/apk/androidTest/screenshot/debug/briar-android-screenshot-debug-androidTest.apk" test_instrumentation_runner "org.briarproject.briar.android.test.BriarTestRunner" \ No newline at end of file diff --git a/briar-android/proguard-test.txt b/briar-android/proguard-test.txt index 652f46e58c..df4eb272e3 100644 --- a/briar-android/proguard-test.txt +++ b/briar-android/proguard-test.txt @@ -12,4 +12,4 @@ -keep class junit.** { *; } -dontwarn junit.** --dontwarn org.briarproject.briar.android.TestBriarApplication \ No newline at end of file +-dontwarn org.briarproject.briar.android.BriarTestComponentApplication \ No newline at end of file diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/TestBriarApplication.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/BriarTestComponentApplication.java similarity index 88% rename from briar-android/src/androidTest/java/org/briarproject/briar/android/TestBriarApplication.java rename to briar-android/src/androidTest/java/org/briarproject/briar/android/BriarTestComponentApplication.java index eb6d86057f..6634d5bfcf 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/TestBriarApplication.java +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/BriarTestComponentApplication.java @@ -3,7 +3,7 @@ package org.briarproject.briar.android; import org.briarproject.bramble.BrambleCoreModule; import org.briarproject.briar.BriarCoreModule; -public class TestBriarApplication extends BriarApplicationImpl { +public class BriarTestComponentApplication extends BriarApplicationImpl { @Override protected AndroidComponent createApplicationComponent() { diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/TestComponent.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/TestComponent.java index a42980f101..240eb6f14d 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/TestComponent.java +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/TestComponent.java @@ -2,7 +2,10 @@ package org.briarproject.briar.android; import org.briarproject.bramble.BrambleAndroidModule; import org.briarproject.bramble.BrambleCoreModule; +import org.briarproject.bramble.account.BriarAccountModule; import org.briarproject.briar.BriarCoreModule; +import org.briarproject.briar.android.login.PasswordActivityTest; +import org.briarproject.briar.android.login.SetupActivityTest; import org.briarproject.briar.android.navdrawer.NavDrawerActivityTest; import org.briarproject.briar.android.settings.SettingsActivityTest; @@ -15,10 +18,13 @@ import dagger.Component; AppModule.class, BriarCoreModule.class, BrambleAndroidModule.class, + BriarAccountModule.class, BrambleCoreModule.class }) public interface TestComponent extends AndroidComponent { + void inject(SetupActivityTest test); + void inject(PasswordActivityTest test); void inject(NavDrawerActivityTest test); void inject(SettingsActivityTest test); diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/login/PasswordActivityTest.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/login/PasswordActivityTest.java new file mode 100644 index 0000000000..0f7ce89b97 --- /dev/null +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/login/PasswordActivityTest.java @@ -0,0 +1,50 @@ +package org.briarproject.briar.android.login; + +import android.support.test.runner.AndroidJUnit4; + +import org.briarproject.briar.R; +import org.briarproject.briar.android.TestComponent; +import org.briarproject.briar.android.test.ScreenshotTest; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.action.ViewActions.typeText; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.intent.Intents.intended; +import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withId; + + +@RunWith(AndroidJUnit4.class) +public class PasswordActivityTest extends ScreenshotTest { + + @Rule + public CleanAccountTestRule<PasswordActivity> testRule = + new CleanAccountTestRule<>(PasswordActivity.class); + + @Override + protected void inject(TestComponent component) { + component.inject(this); + } + + // FIXME + @Ignore("Need to find a way to sign-out after creating fresh account") + @Test + public void successfulLogin() { + onView(withId(R.id.edit_password)) + .check(matches(isDisplayed())) + .perform(typeText(PASSWORD)); + onView(withId(R.id.btn_sign_in)) + .check(matches(isDisplayed())) + .perform(click()); + onView(withId(R.id.progress)) + .check(matches(isDisplayed())); + intended(hasComponent(OpenDatabaseActivity.class.getName())); + } + +} diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/login/SetupActivityTest.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/login/SetupActivityTest.java new file mode 100644 index 0000000000..922cad6553 --- /dev/null +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/login/SetupActivityTest.java @@ -0,0 +1,106 @@ +package org.briarproject.briar.android.login; + +import android.support.test.espresso.intent.rule.IntentsTestRule; +import android.support.test.runner.AndroidJUnit4; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject; +import android.support.test.uiautomator.UiSelector; + +import org.briarproject.briar.R; +import org.briarproject.briar.android.TestComponent; +import org.briarproject.briar.android.test.ScreenshotTest; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.InstrumentationRegistry.getInstrumentation; +import static android.support.test.InstrumentationRegistry.getTargetContext; +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.action.ViewActions.typeText; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.intent.Intents.intended; +import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.isRoot; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static android.support.test.runner.lifecycle.Stage.PAUSED; +import static junit.framework.Assert.assertTrue; +import static org.briarproject.briar.android.test.ViewActions.waitForActivity; +import static org.briarproject.briar.android.test.ViewActions.waitUntilMatches; +import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting; + + +@RunWith(AndroidJUnit4.class) +public class SetupActivityTest extends ScreenshotTest { + + @Rule + public IntentsTestRule<SetupActivity> testRule = + new IntentsTestRule<SetupActivity>(SetupActivity.class) { + @Override + protected void beforeActivityLaunched() { + super.beforeActivityLaunched(); + accountManager.deleteAccount(); + } + }; + + @Override + protected void inject(TestComponent component) { + component.inject(this); + } + + @Test + public void createAccount() throws Exception { + // Enter username + onView(withText(R.string.setup_title)) + .check(matches(isDisplayed())); + onView(withId(R.id.nickname_entry)) + .check(matches(isDisplayed())) + .perform(typeText(USERNAME)); + onView(withId(R.id.nickname_entry)) + .perform(waitUntilMatches(withText(USERNAME))); + + screenshot("manual_create_account"); + + onView(withId(R.id.next)) + .check(matches(isDisplayed())) + .perform(click()); + + // Enter password + onView(withId(R.id.password_entry)) + .check(matches(isDisplayed())) + .perform(typeText(PASSWORD)); + onView(withId(R.id.password_confirm)) + .check(matches(isDisplayed())) + .perform(typeText(PASSWORD)); + onView(withId(R.id.next)) + .check(matches(isDisplayed())) + .perform(click()); + + // White-list Doze if needed + if (needsDozeWhitelisting(getTargetContext())) { + onView(withText(R.string.setup_doze_button)) + .check(matches(isDisplayed())) + .perform(click()); + UiDevice device = UiDevice.getInstance(getInstrumentation()); + UiObject allowButton = device.findObject( + new UiSelector().className("android.widget.Button") + .index(1)); + allowButton.click(); + onView(withId(R.id.next)) + .check(matches(isDisplayed())) + .perform(click()); + } + + // wait for OpenDatabaseActivity to show up + onView(withId(R.id.progress)) + .check(matches(isDisplayed())); + onView(isRoot()) + .perform(waitForActivity(testRule.getActivity(), PAUSED)); + intended(hasComponent(OpenDatabaseActivity.class.getName())); + + assertTrue(accountManager.hasDatabaseKey()); + } + +} diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/navdrawer/NavDrawerActivityTest.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/navdrawer/NavDrawerActivityTest.java index 4f8b03574e..395ed8c48f 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/navdrawer/NavDrawerActivityTest.java +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/navdrawer/NavDrawerActivityTest.java @@ -1,8 +1,6 @@ package org.briarproject.briar.android.navdrawer; -import android.app.Activity; import android.support.test.espresso.contrib.DrawerActions; -import android.support.test.espresso.intent.rule.IntentsTestRule; import android.support.test.runner.AndroidJUnit4; import android.view.Gravity; @@ -28,19 +26,14 @@ import static android.support.test.espresso.matcher.ViewMatchers.withText; public class NavDrawerActivityTest extends ScreenshotTest { @Rule - public IntentsTestRule<NavDrawerActivity> activityRule = - new IntentsTestRule<>(NavDrawerActivity.class); + public CleanAccountTestRule<NavDrawerActivity> testRule = + new CleanAccountTestRule<>(NavDrawerActivity.class); @Override protected void inject(TestComponent component) { component.inject(this); } - @Override - protected Activity getActivity() { - return activityRule.getActivity(); - } - @Test public void openSettings() { onView(withId(R.id.drawer_layout)) diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/settings/SettingsActivityTest.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/settings/SettingsActivityTest.java index 5817d732f2..b5c32f46f2 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/settings/SettingsActivityTest.java +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/settings/SettingsActivityTest.java @@ -1,14 +1,10 @@ package org.briarproject.briar.android.settings; -import android.app.Activity; import android.content.Intent; import android.support.test.espresso.contrib.DrawerActions; -import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.view.Gravity; -import junit.framework.AssertionFailedError; - import org.briarproject.briar.R; import org.briarproject.briar.android.TestComponent; import org.briarproject.briar.android.navdrawer.NavDrawerActivity; @@ -29,32 +25,22 @@ import static android.support.test.espresso.matcher.ViewMatchers.withText; public class SettingsActivityTest extends ScreenshotTest { @Rule - public ActivityTestRule<SettingsActivity> activityRule = - new ActivityTestRule<>(SettingsActivity.class); + public CleanAccountTestRule<SettingsActivity> testRule = + new CleanAccountTestRule<>(SettingsActivity.class); @Override protected void inject(TestComponent component) { component.inject(this); } - @Override - protected Activity getActivity() { - return activityRule.getActivity(); - } - @Test public void changeTheme() { onView(withText(R.string.settings_button)) .check(matches(isDisplayed())); - onView(withText(R.string.pref_theme_title)) - .check(matches(isDisplayed())) - .perform(click()); - onView(withText(R.string.pref_theme_light)) - .check(matches(isDisplayed())) - .perform(click()); screenshot("manual_dark_theme_settings"); + // switch to dark theme onView(withText(R.string.pref_theme_title)) .check(matches(isDisplayed())) .perform(click()); @@ -62,22 +48,20 @@ public class SettingsActivityTest extends ScreenshotTest { .check(matches(isDisplayed())) .perform(click()); + // start main activity Intent i = - new Intent(activityRule.getActivity(), NavDrawerActivity.class); - activityRule.getActivity().startActivity(i); + new Intent(testRule.getActivity(), NavDrawerActivity.class); + testRule.getActivity().startActivity(i); - try { - onView(withId(R.id.expiryWarningClose)) - .check(matches(isDisplayed())); - onView(withId(R.id.expiryWarningClose)) - .perform(click()); - } catch (AssertionFailedError e){ - // TODO remove try block when starting with fresh account - // ignore since we already removed the expiry warning - } + // close expiry warning + onView(withId(R.id.expiryWarningClose)) + .check(matches(isDisplayed())); + onView(withId(R.id.expiryWarningClose)) + .perform(click()); + // open navigation drawer onView(withId(R.id.drawer_layout)) - .check(matches(isClosed(Gravity.LEFT))) + .check(matches(isClosed(Gravity.START))) .perform(DrawerActions.open()); screenshot("manual_dark_theme_nav_drawer"); diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/BriarTestRunner.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/test/BriarTestRunner.java index 655582242e..bf520102d7 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/BriarTestRunner.java +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/test/BriarTestRunner.java @@ -4,7 +4,7 @@ import android.app.Application; import android.content.Context; import android.support.test.runner.AndroidJUnitRunner; -import org.briarproject.briar.android.TestBriarApplication; +import org.briarproject.briar.android.BriarTestComponentApplication; public class BriarTestRunner extends AndroidJUnitRunner { @@ -13,7 +13,7 @@ public class BriarTestRunner extends AndroidJUnitRunner { Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { - return super.newApplication(cl, TestBriarApplication.class.getName(), + return super.newApplication(cl, BriarTestComponentApplication.class.getName(), context); } diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ScreenshotTest.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ScreenshotTest.java index ef11475a9c..77ed5faac4 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ScreenshotTest.java +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ScreenshotTest.java @@ -1,17 +1,13 @@ package org.briarproject.briar.android.test; import android.app.Activity; -import android.support.test.espresso.NoMatchingViewException; -import android.support.test.uiautomator.UiDevice; -import android.support.test.uiautomator.UiObject; -import android.support.test.uiautomator.UiSelector; +import android.support.test.espresso.intent.rule.IntentsTestRule; import android.util.Log; +import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager; -import org.briarproject.briar.R; -import org.briarproject.briar.android.TestBriarApplication; +import org.briarproject.briar.android.BriarTestComponentApplication; import org.briarproject.briar.android.TestComponent; -import org.junit.Before; import org.junit.ClassRule; import javax.inject.Inject; @@ -20,19 +16,7 @@ import tools.fastlane.screengrab.Screengrab; import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy; import tools.fastlane.screengrab.locale.LocaleTestRule; -import static android.support.test.InstrumentationRegistry.getInstrumentation; import static android.support.test.InstrumentationRegistry.getTargetContext; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.action.ViewActions.click; -import static android.support.test.espresso.action.ViewActions.typeText; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.isRoot; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withText; -import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING; -import static org.briarproject.briar.android.test.ViewActions.waitForActivityToResume; -import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting; import static tools.fastlane.screengrab.Screengrab.setDefaultScreenshotStrategy; public abstract class ScreenshotTest { @@ -40,86 +24,24 @@ public abstract class ScreenshotTest { @ClassRule public static final LocaleTestRule localeTestRule = new LocaleTestRule(); - private static final String USERNAME = "test"; - private static final String PASSWORD = "123456"; + protected static final String USERNAME = "Alice"; + protected static final String PASSWORD = "123456"; - private final TestBriarApplication app = - (TestBriarApplication) getTargetContext() - .getApplicationContext(); @Inject - LifecycleManager lifecycleManager; - - protected abstract void inject(TestComponent component); - protected abstract Activity getActivity(); + protected AccountManager accountManager; + @Inject + protected LifecycleManager lifecycleManager; - @Before - public void setupScreenshots() { + public ScreenshotTest() { + super(); setDefaultScreenshotStrategy(new UiAutomatorScreenshotStrategy()); - } - - @Before - public void signIn() throws Exception { + BriarTestComponentApplication app = + (BriarTestComponentApplication) getTargetContext() + .getApplicationContext(); inject((TestComponent) app.getApplicationComponent()); - if (lifecycleManager.getLifecycleState() == RUNNING) return; - - try { - onView(withId(R.id.edit_password)) - .check(matches(isDisplayed())) - .perform(typeText(PASSWORD)); - onView(withId(R.id.btn_sign_in)) - .check(matches(isDisplayed())) - .perform(click()); - } catch (NoMatchingViewException e) { - // we start from a blank state and have no account, yet - createAccount(); - } - onView(isRoot()) - .perform(waitForActivityToResume(getActivity())); } - private void createAccount() throws Exception { - // TODO use AccountManager to start with fresh account - // TODO move this below into a dedicated test for SetupActivity - - // Enter username - onView(withText(R.string.setup_title)) - .check(matches(isDisplayed())); - onView(withId(R.id.nickname_entry)) - .check(matches(isDisplayed())) - .perform(typeText(USERNAME)); - onView(withId(R.id.next)) - .check(matches(isDisplayed())) - .perform(click()); - - // Enter password - onView(withId(R.id.password_entry)) - .check(matches(isDisplayed())) - .perform(typeText(PASSWORD)); - onView(withId(R.id.password_confirm)) - .check(matches(isDisplayed())) - .perform(typeText(PASSWORD)); - onView(withId(R.id.next)) - .check(matches(isDisplayed())) - .perform(click()); - - // White-list Doze if needed - if (needsDozeWhitelisting(getTargetContext())) { - onView(withText(R.string.setup_doze_button)) - .check(matches(isDisplayed())) - .perform(click()); - UiDevice device = UiDevice.getInstance(getInstrumentation()); - UiObject allowButton = device.findObject( - new UiSelector().className("android.widget.Button") - .index(1)); - allowButton.click(); - onView(withId(R.id.next)) - .check(matches(isDisplayed())) - .perform(click()); - } - - onView(withId(R.id.progress)) - .check(matches(isDisplayed())); - } + protected abstract void inject(TestComponent component); protected void screenshot(String name) { try { @@ -133,4 +55,19 @@ public abstract class ScreenshotTest { } } + protected class CleanAccountTestRule<A extends Activity> + extends IntentsTestRule<A> { + + public CleanAccountTestRule(Class<A> activityClass) { + super(activityClass); + } + + @Override + protected void beforeActivityLaunched() { + super.beforeActivityLaunched(); + accountManager.deleteAccount(); + accountManager.createAccount(USERNAME, PASSWORD); + } + } + } diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ViewActions.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ViewActions.java index 1dc07c8c78..0d1aceba53 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ViewActions.java +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ViewActions.java @@ -6,16 +6,16 @@ import android.support.test.espresso.UiController; import android.support.test.espresso.ViewAction; import android.support.test.runner.lifecycle.ActivityLifecycleMonitor; import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; +import android.support.test.runner.lifecycle.Stage; import android.view.View; import org.hamcrest.Matcher; import java.util.concurrent.TimeoutException; -import static android.support.test.espresso.matcher.ViewMatchers.isRoot; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.util.HumanReadables.describe; import static android.support.test.espresso.util.TreeIterables.breadthFirstViewTraversal; -import static android.support.test.runner.lifecycle.Stage.RESUMED; import static java.lang.System.currentTimeMillis; import static java.util.concurrent.TimeUnit.SECONDS; @@ -30,10 +30,13 @@ public class ViewActions { private static ViewAction waitUntilMatches(Matcher<View> viewMatcher, long timeout) { - return new ViewAction() { + return new CustomViewAction() { @Override - public Matcher<View> getConstraints() { - return isRoot(); + protected boolean exitConditionTrue(View view) { + for (View child : breadthFirstViewTraversal(view)) { + if (viewMatcher.matches(child)) return true; + } + return false; } @Override @@ -41,35 +44,16 @@ public class ViewActions { return "Wait for view matcher " + viewMatcher + " to match within " + timeout + " milliseconds."; } - - @Override - public void perform(final UiController uiController, - final View view) { - uiController.loopMainThreadUntilIdle(); - long endTime = currentTimeMillis() + timeout; - - do { - for (View child : breadthFirstViewTraversal(view)) { - if (viewMatcher.matches(child)) return; - } - uiController.loopMainThreadForAtLeast(WAIT_MS); - } - while (currentTimeMillis() < endTime); - - throw new PerformException.Builder() - .withActionDescription(getDescription()) - .withViewDescription(describe(view)) - .withCause(new TimeoutException()) - .build(); - } }; } - public static ViewAction waitForActivityToResume(Activity activity) { - return new ViewAction() { + public static ViewAction waitForActivity(Activity activity, Stage stage) { + return new CustomViewAction() { @Override - public Matcher<View> getConstraints() { - return isRoot(); + protected boolean exitConditionTrue(View view) { + ActivityLifecycleMonitor lifecycleMonitor = + ActivityLifecycleMonitorRegistry.getInstance(); + return lifecycleMonitor.getLifecycleStageOf(activity) == stage; } @Override @@ -77,28 +61,33 @@ public class ViewActions { return "Wait for activity " + activity.getClass().getName() + " to resume within " + TIMEOUT_MS + " milliseconds."; } + }; + } - @Override - public void perform(final UiController uiController, - final View view) { - uiController.loopMainThreadUntilIdle(); - long endTime = currentTimeMillis() + TIMEOUT_MS; - ActivityLifecycleMonitor lifecycleMonitor = - ActivityLifecycleMonitorRegistry.getInstance(); - do { - if (lifecycleMonitor.getLifecycleStageOf(activity) == - RESUMED) return; - uiController.loopMainThreadForAtLeast(WAIT_MS); - } - while (currentTimeMillis() < endTime); - - throw new PerformException.Builder() - .withActionDescription(getDescription()) - .withViewDescription(describe(view)) - .withCause(new TimeoutException()) - .build(); + private static abstract class CustomViewAction implements ViewAction { + @Override + public Matcher<View> getConstraints() { + return isDisplayed(); + } + + @Override + public void perform(UiController uiController, View view) { + uiController.loopMainThreadUntilIdle(); + long endTime = currentTimeMillis() + TIMEOUT_MS; + do { + if (exitConditionTrue(view)) return; + uiController.loopMainThreadForAtLeast(WAIT_MS); } - }; + while (currentTimeMillis() < endTime); + + throw new PerformException.Builder() + .withActionDescription(getDescription()) + .withViewDescription(describe(view)) + .withCause(new TimeoutException()) + .build(); + } + + protected abstract boolean exitConditionTrue(View view); } } -- GitLab