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