mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Add a test for sign-in
This requires an account to exist before as we can't restart our lifecycle. So we don't automatically clear app data after each test, but rather need to delete an existing account manually before each test.
This commit is contained in:
@@ -37,7 +37,7 @@ android {
|
||||
buildConfigField "Long", "BuildTimestamp",
|
||||
"${getStdout(['git', 'log', '-n', '1', '--format=%ct'], now)}000L"
|
||||
testInstrumentationRunner 'org.briarproject.briar.android.BriarTestRunner'
|
||||
testInstrumentationRunnerArguments disableAnalytics: 'true', clearPackageData: 'true'
|
||||
testInstrumentationRunnerArguments disableAnalytics: 'true'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
@@ -4,10 +4,8 @@ import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||
import org.briarproject.briar.R;
|
||||
import org.junit.ClassRule;
|
||||
@@ -16,9 +14,8 @@ import javax.inject.Inject;
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
||||
import static org.briarproject.briar.android.controller.BriarControllerImpl.DOZE_ASK_AGAIN;
|
||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@@ -46,6 +43,12 @@ public abstract class UiTest {
|
||||
|
||||
protected abstract void inject(BriarUiTestComponent component);
|
||||
|
||||
protected void startActivity(Class<? extends Activity> clazz) {
|
||||
Intent i = new Intent(getApplicationContext(), clazz);
|
||||
i.addFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
getApplicationContext().startActivity(i);
|
||||
}
|
||||
|
||||
@NotNullByDefault
|
||||
protected class CleanAccountTestRule<A extends Activity>
|
||||
extends IntentsTestRule<A> {
|
||||
@@ -57,18 +60,14 @@ public abstract class UiTest {
|
||||
@Override
|
||||
protected void beforeActivityLaunched() {
|
||||
super.beforeActivityLaunched();
|
||||
// Android Test Orchestrator already clears existing accounts
|
||||
accountManager.deleteAccount();
|
||||
accountManager.createAccount(USERNAME, PASSWORD);
|
||||
Intent serviceIntent =
|
||||
new Intent(getApplicationContext(), BriarService.class);
|
||||
getApplicationContext().startService(serviceIntent);
|
||||
try {
|
||||
lifecycleManager.waitForStartup();
|
||||
// do not show doze white-listing dialog
|
||||
Settings settings = new Settings();
|
||||
settings.putBoolean(DOZE_ASK_AGAIN, false);
|
||||
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||
} catch (InterruptedException | DbException e) {
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
@@ -20,6 +21,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static androidx.test.espresso.util.HumanReadables.describe;
|
||||
import static androidx.test.espresso.util.TreeIterables.breadthFirstViewTraversal;
|
||||
import static androidx.test.runner.lifecycle.Stage.RESUMED;
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
@@ -32,13 +34,22 @@ public class ViewActions {
|
||||
onView(isRoot()).perform(waitUntilMatches(hasDescendant(viewMatcher)));
|
||||
}
|
||||
|
||||
public static void waitFor(final Class<? extends Activity> clazz) {
|
||||
onView(isRoot()).perform(waitForActivity(clazz, RESUMED, TIMEOUT_MS));
|
||||
}
|
||||
|
||||
public static void waitFor(final Class<? extends Activity> clazz,
|
||||
long timeout) {
|
||||
onView(isRoot()).perform(waitForActivity(clazz, RESUMED, timeout));
|
||||
}
|
||||
|
||||
public static ViewAction waitUntilMatches(Matcher<View> viewMatcher) {
|
||||
return waitUntilMatches(viewMatcher, TIMEOUT_MS);
|
||||
}
|
||||
|
||||
private static ViewAction waitUntilMatches(Matcher<View> viewMatcher,
|
||||
long timeout) {
|
||||
return new CustomViewAction() {
|
||||
return new CustomViewAction(timeout) {
|
||||
@Override
|
||||
protected boolean exitConditionTrue(View view) {
|
||||
for (View child : breadthFirstViewTraversal(view)) {
|
||||
@@ -55,24 +66,43 @@ public class ViewActions {
|
||||
};
|
||||
}
|
||||
|
||||
public static ViewAction waitForActivity(Activity activity, Stage stage) {
|
||||
public static ViewAction waitForActivity(Class<? extends Activity> clazz,
|
||||
Stage stage, long timeout) {
|
||||
return new CustomViewAction() {
|
||||
@Override
|
||||
protected boolean exitConditionTrue(View view) {
|
||||
boolean found = false;
|
||||
ActivityLifecycleMonitor lifecycleMonitor =
|
||||
ActivityLifecycleMonitorRegistry.getInstance();
|
||||
return lifecycleMonitor.getLifecycleStageOf(activity) == stage;
|
||||
for (Activity a : lifecycleMonitor
|
||||
.getActivitiesInStage(stage)) {
|
||||
Log.e("TEST", a.getClass().getSimpleName() +
|
||||
" is in state " + stage);
|
||||
if (a.getClass().equals(clazz)) found = true;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Wait for activity " + activity.getClass().getName() +
|
||||
" to resume within " + TIMEOUT_MS + " milliseconds.";
|
||||
return "Wait for activity " + clazz.getName() + " in stage " +
|
||||
stage.name() + " within " + timeout +
|
||||
" milliseconds.";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static abstract class CustomViewAction implements ViewAction {
|
||||
private final long timeout;
|
||||
|
||||
public CustomViewAction() {
|
||||
this(TIMEOUT_MS);
|
||||
}
|
||||
|
||||
public CustomViewAction(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
@@ -81,7 +111,7 @@ public class ViewActions {
|
||||
@Override
|
||||
public void perform(UiController uiController, View view) {
|
||||
uiController.loopMainThreadUntilIdle();
|
||||
long endTime = currentTimeMillis() + TIMEOUT_MS;
|
||||
long endTime = currentTimeMillis() + timeout;
|
||||
do {
|
||||
if (exitConditionTrue(view)) return;
|
||||
uiController.loopMainThreadForAtLeast(WAIT_MS);
|
||||
|
||||
@@ -4,6 +4,8 @@ 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.account.SignInTestCreateAccount;
|
||||
import org.briarproject.briar.android.account.SignInTestSignIn;
|
||||
import org.briarproject.briar.android.attachment.AttachmentModule;
|
||||
import org.briarproject.briar.android.attachment.media.MediaModule;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivityTest;
|
||||
@@ -26,4 +28,8 @@ public interface BriarUiTestComponent extends AndroidComponent {
|
||||
|
||||
void inject(NavDrawerActivityTest test);
|
||||
|
||||
void inject(SignInTestCreateAccount test);
|
||||
|
||||
void inject(SignInTestSignIn test);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package org.briarproject.briar.android.account;
|
||||
|
||||
import android.view.Gravity;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.BriarUiTestComponent;
|
||||
import org.briarproject.briar.android.UiTest;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import androidx.test.espresso.contrib.DrawerActions;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.contrib.DrawerMatchers.isClosed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
|
||||
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.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.briarproject.briar.android.ViewActions.waitFor;
|
||||
import static org.briarproject.briar.android.ViewActions.waitUntilMatches;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SignInTestCreateAccount extends UiTest {
|
||||
|
||||
@Override
|
||||
protected void inject(BriarUiTestComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createAccount() throws Exception {
|
||||
accountManager.deleteAccount();
|
||||
accountManager.createAccount(USERNAME, PASSWORD);
|
||||
|
||||
startActivity(SplashScreenActivity.class);
|
||||
lifecycleManager.waitForStartup();
|
||||
waitFor(NavDrawerActivity.class);
|
||||
|
||||
// open nav drawer
|
||||
onView(withId(R.id.drawer_layout))
|
||||
.check(matches(isClosed(Gravity.START)))
|
||||
.perform(DrawerActions.open());
|
||||
|
||||
// click onboarding away (once shown)
|
||||
onView(isRoot()).perform(waitUntilMatches(hasDescendant(
|
||||
withClassName(endsWith("PromptView")))));
|
||||
onView(withClassName(endsWith("PromptView")))
|
||||
.perform(click());
|
||||
|
||||
// sign-out manually
|
||||
onView(withText(R.string.sign_out_button))
|
||||
.check(matches(isDisplayed()))
|
||||
.perform(click());
|
||||
lifecycleManager.waitForShutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.briarproject.briar.android.account;
|
||||
|
||||
import android.view.Gravity;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.BriarUiTestComponent;
|
||||
import org.briarproject.briar.android.UiTest;
|
||||
import org.briarproject.briar.android.login.StartupActivity;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.contrib.DrawerMatchers.isClosed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static org.briarproject.briar.android.ViewActions.waitFor;
|
||||
import static org.hamcrest.CoreMatchers.allOf;
|
||||
|
||||
/**
|
||||
* This relies on class sorting to run after {@link SignInTestCreateAccount}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SignInTestSignIn extends UiTest {
|
||||
|
||||
@Override
|
||||
protected void inject(BriarUiTestComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signIn() throws Exception {
|
||||
startActivity(SplashScreenActivity.class);
|
||||
|
||||
waitFor(StartupActivity.class);
|
||||
|
||||
// enter password
|
||||
onView(withId(R.id.edit_password))
|
||||
.check(matches(isDisplayed()))
|
||||
.perform(replaceText(PASSWORD));
|
||||
onView(withId(R.id.btn_sign_in))
|
||||
.check(matches(allOf(isDisplayed(), isEnabled())))
|
||||
.perform(click());
|
||||
|
||||
lifecycleManager.waitForStartup();
|
||||
waitFor(NavDrawerActivity.class);
|
||||
|
||||
// ensure nav drawer is visible
|
||||
onView(withId(R.id.drawer_layout))
|
||||
.check(matches(isClosed(Gravity.START)));
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@ import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.contact.PendingContact;
|
||||
import org.briarproject.bramble.api.contact.PendingContactState;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.account.SetupActivity;
|
||||
import org.briarproject.briar.android.contact.add.remote.PendingContactListActivity;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Rule;
|
||||
@@ -66,6 +69,7 @@ public class PromoVideoTest extends ScreenshotTest {
|
||||
@Override
|
||||
protected void inject(BriarUiTestComponent component) {
|
||||
component.inject(this);
|
||||
accountManager.deleteAccount();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,9 +84,8 @@ public class PromoVideoTest extends ScreenshotTest {
|
||||
onView(withId(R.id.logoView))
|
||||
.perform(waitUntilMatches(isDisplayed()));
|
||||
|
||||
int duration = getApplicationContext().getResources()
|
||||
.getInteger(R.integer.splashScreenDuration);
|
||||
sleep(Math.max(DELAY_LONG, duration));
|
||||
if (debug) waitFor(SetupActivity.class, 25_000);
|
||||
sleep(DELAY_LONG);
|
||||
|
||||
// Enter username
|
||||
onView(withText(R.string.setup_title))
|
||||
@@ -132,7 +135,8 @@ public class PromoVideoTest extends ScreenshotTest {
|
||||
|
||||
sleep(DELAY_SMALL);
|
||||
|
||||
waitFor(allOf(withId(R.id.speedDial), isDisplayed()));
|
||||
// wait for contact list to be shown
|
||||
if (debug) waitFor(NavDrawerActivity.class);
|
||||
|
||||
// clicking the FAB doesn't work, so we click its inner FAB as well
|
||||
onView(withId(R.id.speedDial))
|
||||
@@ -173,8 +177,11 @@ public class PromoVideoTest extends ScreenshotTest {
|
||||
sleep(DELAY_LONG);
|
||||
|
||||
// wait for pending contact list activity to be shown
|
||||
waitFor(allOf(withText(R.string.pending_contact_requests),
|
||||
isDisplayed()));
|
||||
if (debug) {
|
||||
waitFor(PendingContactListActivity.class);
|
||||
waitFor(allOf(withText(R.string.pending_contact_requests),
|
||||
isDisplayed()));
|
||||
}
|
||||
|
||||
// remove pending contact
|
||||
for (Pair<PendingContact, PendingContactState> p : contactManager
|
||||
@@ -187,8 +194,14 @@ public class PromoVideoTest extends ScreenshotTest {
|
||||
connectionRegistry.registerIncomingConnection(bob.getId(), ID, () -> {
|
||||
});
|
||||
|
||||
sleep(DELAY_LONG);
|
||||
|
||||
// wait for contact list to be shown
|
||||
waitFor(allOf(withText(R.string.contact_list_button), isDisplayed()));
|
||||
if (debug) {
|
||||
waitFor(NavDrawerActivity.class);
|
||||
waitFor(allOf(withText(R.string.contact_list_button),
|
||||
isDisplayed()));
|
||||
}
|
||||
|
||||
// click on new contact
|
||||
doItemClick(withId(R.id.recyclerView), 0);
|
||||
|
||||
@@ -42,6 +42,7 @@ public class SetupDataTest extends ScreenshotTest {
|
||||
@Override
|
||||
protected void inject(BriarUiTestComponent component) {
|
||||
component.inject(this);
|
||||
accountManager.deleteAccount();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user