Split up UI and Screenshot tests

Closes #1377
This commit is contained in:
Torsten Grote
2018-09-06 16:00:29 -03:00
parent f0e2d5281f
commit 1c90e64894
12 changed files with 154 additions and 126 deletions

View File

@@ -1,11 +1,9 @@
package org.briarproject.briar.android.test;
package org.briarproject.briar.android;
import android.app.Application;
import android.content.Context;
import android.support.test.runner.AndroidJUnitRunner;
import org.briarproject.briar.android.BriarTestComponentApplication;
public class BriarTestRunner extends AndroidJUnitRunner {
@Override
@@ -13,8 +11,8 @@ public class BriarTestRunner extends AndroidJUnitRunner {
Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return super.newApplication(cl, BriarTestComponentApplication.class.getName(),
context);
return super.newApplication(cl,
BriarTestComponentApplication.class.getName(), context);
}
}

View File

@@ -1,31 +0,0 @@
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.contact.ConversationActivityScreenshotTest;
import org.briarproject.briar.android.login.SetupActivityScreenshotTest;
import org.briarproject.briar.android.navdrawer.NavDrawerActivityTest;
import org.briarproject.briar.android.settings.SettingsActivityScreenshotTest;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {
AppModule.class,
BriarCoreModule.class,
BrambleAndroidModule.class,
BriarAccountModule.class,
BrambleCoreModule.class
})
public interface BriarUiTestComponent extends AndroidComponent {
void inject(ConversationActivityScreenshotTest test);
void inject(SetupActivityScreenshotTest test);
void inject(NavDrawerActivityTest test);
void inject(SettingsActivityScreenshotTest test);
}

View File

@@ -1,35 +1,20 @@
package org.briarproject.briar.android.test;
package org.briarproject.briar.android;
import android.app.Activity;
import android.content.Intent;
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.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.android.BriarService;
import org.briarproject.briar.android.BriarTestComponentApplication;
import org.briarproject.briar.android.BriarUiTestComponent;
import org.briarproject.briar.api.test.TestDataCreator;
import org.junit.ClassRule;
import javax.annotation.Nullable;
import javax.inject.Inject;
import tools.fastlane.screengrab.Screengrab;
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy;
import tools.fastlane.screengrab.locale.LocaleTestRule;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static tools.fastlane.screengrab.Screengrab.setDefaultScreenshotStrategy;
public abstract class ScreenshotTest {
@ClassRule
public static final LocaleTestRule localeTestRule = new LocaleTestRule();
@SuppressWarnings("WeakerAccess")
public abstract class UiTest {
protected static final String USERNAME = "Alice";
protected static final String PASSWORD = "123456";
@@ -38,16 +23,8 @@ public abstract class ScreenshotTest {
protected AccountManager accountManager;
@Inject
protected LifecycleManager lifecycleManager;
@Inject
protected TestDataCreator testDataCreator;
@Inject
protected ConnectionRegistry connectionRegistry;
@Inject
protected Clock clock;
public ScreenshotTest() {
super();
setDefaultScreenshotStrategy(new UiAutomatorScreenshotStrategy());
public UiTest() {
BriarTestComponentApplication app =
(BriarTestComponentApplication) getTargetContext()
.getApplicationContext();
@@ -56,22 +33,6 @@ public abstract class ScreenshotTest {
protected abstract void inject(BriarUiTestComponent component);
protected void screenshot(String name) {
try {
Screengrab.screenshot(name);
} catch (RuntimeException e) {
if (!e.getMessage().equals("Unable to capture screenshot."))
throw e;
// The tests should still pass when run from AndroidStudio
// without manually granting permissions like fastlane does.
Log.w("Screengrab", "Permission to write screenshot is missing.");
}
}
protected long getMinutesAgo(int minutes) {
return clock.currentTimeMillis() - minutes * 60 * 1000;
}
@NotNullByDefault
protected class CleanAccountTestRule<A extends Activity>
extends IntentsTestRule<A> {

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.test;
package org.briarproject.briar.android;
import android.app.Activity;
import android.support.test.espresso.PerformException;

View File

@@ -1,86 +0,0 @@
package org.briarproject.briar.android.contact;
import android.content.Context;
import android.content.Intent;
import android.support.test.runner.AndroidJUnit4;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.briar.R;
import org.briarproject.briar.android.BriarUiTestComponent;
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.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
import static org.briarproject.briar.android.contact.ConversationActivity.CONTACT_ID;
import static org.briarproject.briar.android.test.ViewActions.waitUntilMatches;
import static org.hamcrest.Matchers.allOf;
@RunWith(AndroidJUnit4.class)
public class ConversationActivityScreenshotTest extends ScreenshotTest {
@Rule
public CleanAccountTestRule<ConversationActivity> testRule =
new CleanAccountTestRule<>(ConversationActivity.class,
this::createTestData);
@Override
protected void inject(BriarUiTestComponent component) {
component.inject(this);
}
@Test
public void messaging() {
Context targetContext = getInstrumentation().getTargetContext();
Intent intent = new Intent(targetContext, ConversationActivity.class);
intent.putExtra(CONTACT_ID, 1);
testRule.launchActivity(intent);
onView(withId(R.id.conversationView)).perform(waitUntilMatches(
allOf(withText(R.string.screenshot_message_3), isDisplayed())));
screenshot("manual_messaging");
}
private void createTestData() {
try {
createTestDataExceptions();
} catch (DbException | FormatException e) {
throw new AssertionError(e);
}
}
private void createTestDataExceptions()
throws DbException, FormatException {
String bobName =
getTargetContext().getString(R.string.screenshot_bob);
Contact bob = testDataCreator.addContact(bobName);
String bobHi = getTargetContext()
.getString(R.string.screenshot_message_1);
long bobTime = getMinutesAgo(2);
testDataCreator.addPrivateMessage(bob, bobHi, bobTime, true);
String aliceHi = getTargetContext()
.getString(R.string.screenshot_message_2);
long aliceTime = getMinutesAgo(1);
testDataCreator.addPrivateMessage(bob, aliceHi, aliceTime, false);
String bobHi2 = getTargetContext()
.getString(R.string.screenshot_message_3);
long bobTime2 = getMinutesAgo(0);
testDataCreator.addPrivateMessage(bob, bobHi2, bobTime2, true);
connectionRegistry.registerConnection(bob.getId(), ID, true);
}
}

View File

@@ -1,106 +0,0 @@
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.BriarUiTestComponent;
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 SetupActivityScreenshotTest extends ScreenshotTest {
@Rule
public IntentsTestRule<SetupActivity> testRule =
new IntentsTestRule<SetupActivity>(SetupActivity.class) {
@Override
protected void beforeActivityLaunched() {
super.beforeActivityLaunched();
accountManager.deleteAccount();
}
};
@Override
protected void inject(BriarUiTestComponent 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());
}
}

View File

@@ -1,48 +0,0 @@
package org.briarproject.briar.android.navdrawer;
import android.support.test.espresso.contrib.DrawerActions;
import android.support.test.runner.AndroidJUnit4;
import android.view.Gravity;
import org.briarproject.briar.R;
import org.briarproject.briar.android.BriarUiTestComponent;
import org.briarproject.briar.android.settings.SettingsActivity;
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.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.contrib.DrawerMatchers.isClosed;
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;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
@RunWith(AndroidJUnit4.class)
public class NavDrawerActivityTest extends ScreenshotTest {
@Rule
public CleanAccountTestRule<NavDrawerActivity> testRule =
new CleanAccountTestRule<>(NavDrawerActivity.class);
@Override
protected void inject(BriarUiTestComponent component) {
component.inject(this);
}
@Test
public void openSettings() {
onView(withId(R.id.drawer_layout))
.check(matches(isClosed(Gravity.START)))
.perform(DrawerActions.open());
onView(withText(R.string.settings_button))
.check(matches(isDisplayed()))
.perform(click());
intended(hasComponent(SettingsActivity.class.getName()));
}
}

View File

@@ -1,130 +0,0 @@
package org.briarproject.briar.android.settings;
import android.content.Intent;
import android.support.test.espresso.contrib.DrawerActions;
import android.support.test.runner.AndroidJUnit4;
import android.support.v7.widget.RecyclerView;
import android.view.Gravity;
import org.briarproject.briar.R;
import org.briarproject.briar.android.BriarUiTestComponent;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
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.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.contrib.DrawerMatchers.isClosed;
import static android.support.test.espresso.contrib.RecyclerViewActions.scrollTo;
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
import static android.support.test.espresso.matcher.ViewMatchers.withChild;
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.briarproject.briar.android.test.ViewActions.waitUntilMatches;
import static org.hamcrest.CoreMatchers.is;
@RunWith(AndroidJUnit4.class)
public class SettingsActivityScreenshotTest extends ScreenshotTest {
@Rule
public CleanAccountTestRule<SettingsActivity> testRule =
new CleanAccountTestRule<>(SettingsActivity.class);
@Override
protected void inject(BriarUiTestComponent component) {
component.inject(this);
}
@Test
public void changeTheme() {
onView(withText(R.string.settings_button))
.check(matches(isDisplayed()));
screenshot("manual_dark_theme_settings");
// switch to dark theme
onView(withText(R.string.pref_theme_title))
.check(matches(isDisplayed()))
.perform(click());
onView(withText(R.string.pref_theme_dark))
.check(matches(isDisplayed()))
.perform(click());
// open nav drawer and remove expiry warning
openNavDrawer(true);
screenshot("manual_dark_theme_nav_drawer");
}
@Test
public void appLock() {
// scroll down
onView(withClassName(is(RecyclerView.class.getName())))
.perform(scrollTo(hasDescendant(
// scroll down a bit more to have settings in the middle
withText(R.string.panic_setting))));
// wait for settings to get loaded and enabled
onView(withText(R.string.tor_mobile_data_title))
.perform(waitUntilMatches(isEnabled()));
// ensure app lock is displayed and enabled
onView(withText(R.string.pref_lock_title))
.check(matches(isDisplayed()))
.check(matches(isEnabled()))
.perform(click());
onView(withChild(withText(R.string.pref_lock_timeout_title)))
.check(matches(isDisplayed()))
.check(matches(isEnabled()));
screenshot("manual_app_lock");
// no more expiry warning to remove, because sharedprefs cached?
openNavDrawer(false);
screenshot("manual_app_lock_nav_drawer");
}
@Test
public void torSettings() {
// scroll down
onView(withClassName(is(RecyclerView.class.getName())))
.perform(scrollTo(hasDescendant(
// scroll down a bit more to have settings in the middle
withText(R.string.pref_lock_timeout_title))));
// wait for settings to get loaded and enabled
onView(withText(R.string.tor_network_setting))
.check(matches(isDisplayed()))
.perform(waitUntilMatches(isEnabled()));
screenshot("manual_tor_settings");
}
private void openNavDrawer(boolean expiry) {
// start main activity
Intent i =
new Intent(testRule.getActivity(), NavDrawerActivity.class);
testRule.getActivity().startActivity(i);
// close expiry warning
if (expiry) {
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.START)))
.perform(DrawerActions.open());
}
}