Commit 3ff64628 authored by Torsten Grote's avatar Torsten Grote

Refactor Espresso tests to use new AccountManager

parent 6f7d0c6f
app_package_name "org.briarproject.briar.android.screenshot.debug" app_package_name "org.briarproject.briar.android.screenshot.debug"
locales ['en-US'] 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" 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" tests_apk_path "build/outputs/apk/androidTest/screenshot/debug/briar-android-screenshot-debug-androidTest.apk"
test_instrumentation_runner "org.briarproject.briar.android.test.BriarTestRunner" test_instrumentation_runner "org.briarproject.briar.android.test.BriarTestRunner"
\ No newline at end of file
...@@ -12,4 +12,4 @@ ...@@ -12,4 +12,4 @@
-keep class junit.** { *; } -keep class junit.** { *; }
-dontwarn junit.** -dontwarn junit.**
-dontwarn org.briarproject.briar.android.TestBriarApplication -dontwarn org.briarproject.briar.android.BriarTestComponentApplication
\ No newline at end of file \ No newline at end of file
...@@ -3,7 +3,7 @@ package org.briarproject.briar.android; ...@@ -3,7 +3,7 @@ package org.briarproject.briar.android;
import org.briarproject.bramble.BrambleCoreModule; import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.briar.BriarCoreModule; import org.briarproject.briar.BriarCoreModule;
public class TestBriarApplication extends BriarApplicationImpl { public class BriarTestComponentApplication extends BriarApplicationImpl {
@Override @Override
protected AndroidComponent createApplicationComponent() { protected AndroidComponent createApplicationComponent() {
......
...@@ -2,7 +2,10 @@ package org.briarproject.briar.android; ...@@ -2,7 +2,10 @@ package org.briarproject.briar.android;
import org.briarproject.bramble.BrambleAndroidModule; import org.briarproject.bramble.BrambleAndroidModule;
import org.briarproject.bramble.BrambleCoreModule; import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.account.BriarAccountModule;
import org.briarproject.briar.BriarCoreModule; 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.navdrawer.NavDrawerActivityTest;
import org.briarproject.briar.android.settings.SettingsActivityTest; import org.briarproject.briar.android.settings.SettingsActivityTest;
...@@ -15,10 +18,13 @@ import dagger.Component; ...@@ -15,10 +18,13 @@ import dagger.Component;
AppModule.class, AppModule.class,
BriarCoreModule.class, BriarCoreModule.class,
BrambleAndroidModule.class, BrambleAndroidModule.class,
BriarAccountModule.class,
BrambleCoreModule.class BrambleCoreModule.class
}) })
public interface TestComponent extends AndroidComponent { public interface TestComponent extends AndroidComponent {
void inject(SetupActivityTest test);
void inject(PasswordActivityTest test);
void inject(NavDrawerActivityTest test); void inject(NavDrawerActivityTest test);
void inject(SettingsActivityTest test); void inject(SettingsActivityTest test);
......
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()));
}
}
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());
}
}
package org.briarproject.briar.android.navdrawer; package org.briarproject.briar.android.navdrawer;
import android.app.Activity;
import android.support.test.espresso.contrib.DrawerActions; import android.support.test.espresso.contrib.DrawerActions;
import android.support.test.espresso.intent.rule.IntentsTestRule;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.view.Gravity; import android.view.Gravity;
...@@ -28,19 +26,14 @@ import static android.support.test.espresso.matcher.ViewMatchers.withText; ...@@ -28,19 +26,14 @@ import static android.support.test.espresso.matcher.ViewMatchers.withText;
public class NavDrawerActivityTest extends ScreenshotTest { public class NavDrawerActivityTest extends ScreenshotTest {
@Rule @Rule
public IntentsTestRule<NavDrawerActivity> activityRule = public CleanAccountTestRule<NavDrawerActivity> testRule =
new IntentsTestRule<>(NavDrawerActivity.class); new CleanAccountTestRule<>(NavDrawerActivity.class);
@Override @Override
protected void inject(TestComponent component) { protected void inject(TestComponent component) {
component.inject(this); component.inject(this);
} }
@Override
protected Activity getActivity() {
return activityRule.getActivity();
}
@Test @Test
public void openSettings() { public void openSettings() {
onView(withId(R.id.drawer_layout)) onView(withId(R.id.drawer_layout))
......
package org.briarproject.briar.android.settings; package org.briarproject.briar.android.settings;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.support.test.espresso.contrib.DrawerActions; import android.support.test.espresso.contrib.DrawerActions;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.view.Gravity; import android.view.Gravity;
import junit.framework.AssertionFailedError;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.TestComponent; import org.briarproject.briar.android.TestComponent;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
...@@ -29,32 +25,22 @@ import static android.support.test.espresso.matcher.ViewMatchers.withText; ...@@ -29,32 +25,22 @@ import static android.support.test.espresso.matcher.ViewMatchers.withText;
public class SettingsActivityTest extends ScreenshotTest { public class SettingsActivityTest extends ScreenshotTest {
@Rule @Rule
public ActivityTestRule<SettingsActivity> activityRule = public CleanAccountTestRule<SettingsActivity> testRule =
new ActivityTestRule<>(SettingsActivity.class); new CleanAccountTestRule<>(SettingsActivity.class);
@Override @Override
protected void inject(TestComponent component) { protected void inject(TestComponent component) {
component.inject(this); component.inject(this);
} }
@Override
protected Activity getActivity() {
return activityRule.getActivity();
}
@Test @Test
public void changeTheme() { public void changeTheme() {
onView(withText(R.string.settings_button)) onView(withText(R.string.settings_button))
.check(matches(isDisplayed())); .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"); screenshot("manual_dark_theme_settings");
// switch to dark theme
onView(withText(R.string.pref_theme_title)) onView(withText(R.string.pref_theme_title))
.check(matches(isDisplayed())) .check(matches(isDisplayed()))
.perform(click()); .perform(click());
...@@ -62,22 +48,20 @@ public class SettingsActivityTest extends ScreenshotTest { ...@@ -62,22 +48,20 @@ public class SettingsActivityTest extends ScreenshotTest {
.check(matches(isDisplayed())) .check(matches(isDisplayed()))
.perform(click()); .perform(click());
// start main activity
Intent i = Intent i =
new Intent(activityRule.getActivity(), NavDrawerActivity.class); new Intent(testRule.getActivity(), NavDrawerActivity.class);
activityRule.getActivity().startActivity(i); testRule.getActivity().startActivity(i);
try { // close expiry warning
onView(withId(R.id.expiryWarningClose)) onView(withId(R.id.expiryWarningClose))
.check(matches(isDisplayed())); .check(matches(isDisplayed()));
onView(withId(R.id.expiryWarningClose)) onView(withId(R.id.expiryWarningClose))
.perform(click()); .perform(click());
} catch (AssertionFailedError e){
// TODO remove try block when starting with fresh account
// ignore since we already removed the expiry warning
}
// open navigation drawer
onView(withId(R.id.drawer_layout)) onView(withId(R.id.drawer_layout))
.check(matches(isClosed(Gravity.LEFT))) .check(matches(isClosed(Gravity.START)))
.perform(DrawerActions.open()); .perform(DrawerActions.open());
screenshot("manual_dark_theme_nav_drawer"); screenshot("manual_dark_theme_nav_drawer");
......
...@@ -4,7 +4,7 @@ import android.app.Application; ...@@ -4,7 +4,7 @@ import android.app.Application;
import android.content.Context; import android.content.Context;
import android.support.test.runner.AndroidJUnitRunner; import android.support.test.runner.AndroidJUnitRunner;
import org.briarproject.briar.android.TestBriarApplication; import org.briarproject.briar.android.BriarTestComponentApplication;
public class BriarTestRunner extends AndroidJUnitRunner { public class BriarTestRunner extends AndroidJUnitRunner {
...@@ -13,7 +13,7 @@ public class BriarTestRunner extends AndroidJUnitRunner { ...@@ -13,7 +13,7 @@ public class BriarTestRunner extends AndroidJUnitRunner {
Context context) Context context)
throws InstantiationException, IllegalAccessException, throws InstantiationException, IllegalAccessException,
ClassNotFoundException { ClassNotFoundException {
return super.newApplication(cl, TestBriarApplication.class.getName(), return super.newApplication(cl, BriarTestComponentApplication.class.getName(),
context); context);
} }
......
package org.briarproject.briar.android.test; package org.briarproject.briar.android.test;
import android.app.Activity; import android.app.Activity;
import android.support.test.espresso.NoMatchingViewException; import android.support.test.espresso.intent.rule.IntentsTestRule;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiSelector;
import android.util.Log; import android.util.Log;
import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.briar.R; import org.briarproject.briar.android.BriarTestComponentApplication;
import org.briarproject.briar.android.TestBriarApplication;
import org.briarproject.briar.android.TestComponent; import org.briarproject.briar.android.TestComponent;
import org.junit.Before;
import org.junit.ClassRule; import org.junit.ClassRule;
import javax.inject.Inject; import javax.inject.Inject;
...@@ -20,19 +16,7 @@ import tools.fastlane.screengrab.Screengrab; ...@@ -20,19 +16,7 @@ import tools.fastlane.screengrab.Screengrab;
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy; import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy;
import tools.fastlane.screengrab.locale.LocaleTestRule; 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.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; import static tools.fastlane.screengrab.Screengrab.setDefaultScreenshotStrategy;
public abstract class ScreenshotTest { public abstract class ScreenshotTest {
...@@ -40,86 +24,24 @@ public abstract class ScreenshotTest { ...@@ -40,86 +24,24 @@ public abstract class ScreenshotTest {
@ClassRule @ClassRule
public static final LocaleTestRule localeTestRule = new LocaleTestRule(); public static final LocaleTestRule localeTestRule = new LocaleTestRule();
private static final String USERNAME = "test"; protected static final String USERNAME = "Alice";
private static final String PASSWORD = "123456"; protected static final String PASSWORD = "123456";
private final TestBriarApplication app =
(TestBriarApplication) getTargetContext()
.getApplicationContext();
@Inject @Inject
LifecycleManager lifecycleManager; protected AccountManager accountManager;
@Inject
protected abstract void inject(TestComponent component); protected LifecycleManager lifecycleManager;
protected abstract Activity getActivity();
@Before public ScreenshotTest() {
public void setupScreenshots() { super();
setDefaultScreenshotStrategy(new UiAutomatorScreenshotStrategy()); setDefaultScreenshotStrategy(new UiAutomatorScreenshotStrategy());
} BriarTestComponentApplication app =
(BriarTestComponentApplication) getTargetContext()
@Before .getApplicationContext();
public void signIn() throws Exception {
inject((TestComponent) app.getApplicationComponent()); 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 { protected abstract void inject(TestComponent component);
// 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 void screenshot(String name) { protected void screenshot(String name) {
try { try {
...@@ -133,4 +55,19 @@ public abstract class ScreenshotTest { ...@@ -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);
}
}
} }
...@@ -6,16 +6,16 @@ import android.support.test.espresso.UiController; ...@@ -6,16 +6,16 @@ import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction; import android.support.test.espresso.ViewAction;
import android.support.test.runner.lifecycle.ActivityLifecycleMonitor; import android.support.test.runner.lifecycle.ActivityLifecycleMonitor;
import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import android.support.test.runner.lifecycle.Stage;
import android.view.View; import android.view.View;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import java.util.concurrent.TimeoutException; 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.HumanReadables.describe;
import static android.support.test.espresso.util.TreeIterables.breadthFirstViewTraversal; 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.lang.System.currentTimeMillis;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
...@@ -30,10 +30,13 @@ public class ViewActions { ...@@ -30,10 +30,13 @@ public class ViewActions {
private static ViewAction waitUntilMatches(Matcher<View> viewMatcher, private static ViewAction waitUntilMatches(Matcher<View> viewMatcher,
long timeout) { long timeout) {
return new ViewAction() { return new CustomViewAction() {
@Override @Override
public Matcher<View> getConstraints() { protected boolean exitConditionTrue(View view) {
return isRoot(); for (View child : breadthFirstViewTraversal(view)) {
if (viewMatcher.matches(child)) return true;
}
return false;
} }
@Override @Override
...@@ -41,35 +44,16 @@ public class ViewActions { ...@@ -41,35 +44,16 @@ public class ViewActions {
return "Wait for view matcher " + viewMatcher + return "Wait for view matcher " + viewMatcher +
" to match within " + timeout + " milliseconds."; " to match within " + timeout + " milliseconds.";
} }
@Override
public void perform(final UiController uiController,
final View view) {
uiController.loopMainThreadUntilIdle();