diff --git a/build.gradle b/build.gradle
index 1aeda0139..73835b378 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,6 +29,25 @@ dependencies {
compile 'com.beetstra.jutf7:jutf7:1.0.0'
compile 'com.android.support:support-v13:19.1.0'
compile 'net.sourceforge.htmlcleaner:htmlcleaner:2.2'
+
+ androidTestCompile ('com.jakewharton.espresso:espresso:1.1-r3' ) {
+ // Note: some of these exclusions may become necessary. See the
+ // github site https://github.com/JakeWharton/double-espresso
+// exclude group: 'com.squareup.dagger'
+// exclude group: 'javax.inject'
+// exclude group: 'javax.annotation'
+// exclude group: 'com.google.guava'
+ exclude group: 'com.google.code.findbugs'
+// exclude group: 'org.hamcrest'
+ }
+
+ androidTestCompile("com.icegreen:greenmail:1.3.1b") {
+ // Use a better, later version
+ exclude group: "javax.mail"
+ }
+
+ // this version avoids some "Ignoring InnerClasses attribute for an anonymous inner class" warnings
+ androidTestCompile "javax.mail:javax.mail-api:1.5.2"
}
project.ext.preDexLibs = !project.hasProperty('disablePreDex')
@@ -49,6 +68,7 @@ android {
defaultConfig {
minSdkVersion 15
targetSdkVersion 17
+ testInstrumentationRunner "com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner"
}
dexOptions {
@@ -89,6 +109,7 @@ android {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
+ exclude 'LICENSE.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 12121f1f8..05a7f8a90 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -5,18 +5,15 @@
android:versionCode="1"
android:versionName="1.0">
+
+
+
+
-
-
+
diff --git a/tests/src/com/fsck/k9/endtoend/A000_WelcomeAndSetupAccountIntegrationTest.java b/tests/src/com/fsck/k9/endtoend/A000_WelcomeAndSetupAccountIntegrationTest.java
new file mode 100644
index 000000000..c6a9187dc
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/A000_WelcomeAndSetupAccountIntegrationTest.java
@@ -0,0 +1,24 @@
+package com.fsck.k9.endtoend;
+
+import com.fsck.k9.activity.setup.WelcomeMessage;
+import com.fsck.k9.endtoend.pages.WelcomeMessagePage;
+
+/**
+ * Creates a new IMAP account via the getting started flow.
+ */
+public class A000_WelcomeAndSetupAccountIntegrationTest extends AbstractEndToEndTest {
+
+ public A000_WelcomeAndSetupAccountIntegrationTest() {
+ super(WelcomeMessage.class, false);
+ }
+
+ public void testCreateAccount() throws Exception {
+ new AccountSetupFlow(this).setupAccountFromWelcomePage(new WelcomeMessagePage());
+ }
+
+ public void testCreateSecondAccount() throws Exception {
+ new AccountSetupFlow(this).setupAccountFromWelcomePage(new WelcomeMessagePage());
+ }
+
+}
+
diff --git a/tests/src/com/fsck/k9/endtoend/A010_AccountIntegrationTest.java b/tests/src/com/fsck/k9/endtoend/A010_AccountIntegrationTest.java
new file mode 100644
index 000000000..d3fc8701c
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/A010_AccountIntegrationTest.java
@@ -0,0 +1,40 @@
+package com.fsck.k9.endtoend;
+
+import com.fsck.k9.activity.Accounts;
+import com.fsck.k9.endtoend.framework.AccountForTest;
+import com.fsck.k9.endtoend.framework.ApplicationState;
+import com.fsck.k9.endtoend.pages.AccountsPage;
+
+/**
+ * Creates and removes accounts.
+ *
+ * Because of the way K-9 shows the start page, there must already be two accounts
+ * in existence for this test to work.
+ */
+public class A010_AccountIntegrationTest extends AbstractEndToEndTest{
+
+ public A010_AccountIntegrationTest() {
+ super(Accounts.class);
+ }
+
+ public void testCreateAccountDirectly() throws Exception {
+ new AccountSetupFlow(this).setupAccountFromAccountsPage(new AccountsPage());
+ }
+
+ public void testDeleteAccount() {
+
+ AccountsPage accountsPage = new AccountsPage();
+
+ AccountForTest accountForTest = ApplicationState.getInstance().accounts.get(0);
+ accountsPage.assertAccountExists(accountForTest.description);
+
+ accountsPage.clickLongOnAccount(accountForTest);
+
+ accountsPage.clickRemoveInAccountMenu();
+
+ accountsPage.clickOK();
+
+ accountsPage.assertAccountDoesNotExist(accountForTest.description);
+
+ }
+}
diff --git a/tests/src/com/fsck/k9/endtoend/AbstractEndToEndTest.java b/tests/src/com/fsck/k9/endtoend/AbstractEndToEndTest.java
new file mode 100644
index 000000000..f1ed25a8c
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/AbstractEndToEndTest.java
@@ -0,0 +1,60 @@
+package com.fsck.k9.endtoend;
+
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import com.fsck.k9.R;
+import com.fsck.k9.endtoend.framework.ApplicationState;
+import com.fsck.k9.endtoend.framework.StubMailServer;
+import com.fsck.k9.endtoend.pages.WelcomeMessagePage;
+import com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions;
+
+import junit.framework.AssertionFailedError;
+
+import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
+
+public abstract class AbstractEndToEndTest extends ActivityInstrumentationTestCase2 {
+
+ private ApplicationState state = ApplicationState.getInstance();
+ private final boolean bypassWelcome;
+
+ public AbstractEndToEndTest(Class activityClass) {
+ this(activityClass, true);
+ }
+
+ public AbstractEndToEndTest(Class activityClass, boolean bypassWelcome) {
+ super(activityClass);
+ this.bypassWelcome = bypassWelcome;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ getActivity();
+
+ if (bypassWelcome) {
+ bypassWelcomeScreen();
+ }
+ }
+
+ private void bypassWelcomeScreen() {
+ try {
+ onView(withId(R.id.welcome_message)).check(ViewAssertions.doesNotExist());
+ } catch (AssertionFailedError ex) {
+ /*
+ * The view doesn't NOT exist == the view exists, and needs to be bypassed!
+ */
+ Log.d(getClass().getName(), "Bypassing welcome");
+ new AccountSetupFlow(this).setupAccountFromWelcomePage(new WelcomeMessagePage());
+ }
+ }
+
+ protected StubMailServer setupMailServer() {
+ if (null == state.stubMailServer) {
+ state.stubMailServer = new StubMailServer();
+ }
+ return state.stubMailServer;
+ }
+}
diff --git a/tests/src/com/fsck/k9/endtoend/AccountSetupFlow.java b/tests/src/com/fsck/k9/endtoend/AccountSetupFlow.java
new file mode 100644
index 000000000..978ffd332
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/AccountSetupFlow.java
@@ -0,0 +1,98 @@
+package com.fsck.k9.endtoend;
+
+import com.fsck.k9.endtoend.framework.AccountForTest;
+import com.fsck.k9.endtoend.framework.ApplicationState;
+import com.fsck.k9.endtoend.framework.StubMailServer;
+import com.fsck.k9.endtoend.framework.UserForImap;
+import com.fsck.k9.endtoend.pages.AccountOptionsPage;
+import com.fsck.k9.endtoend.pages.AccountSetupNamesPage;
+import com.fsck.k9.endtoend.pages.AccountSetupPage;
+import com.fsck.k9.endtoend.pages.AccountTypePage;
+import com.fsck.k9.endtoend.pages.AccountsPage;
+import com.fsck.k9.endtoend.pages.IncomingServerSettingsPage;
+import com.fsck.k9.endtoend.pages.OutgoingServerSettingsPage;
+import com.fsck.k9.endtoend.pages.WelcomeMessagePage;
+import com.fsck.k9.mail.ConnectionSecurity;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Encapsulated the steps required to set up a new mail account.
+ */
+public class AccountSetupFlow {
+
+ static final String ACCOUNT_NAME = "sendAndReceiveTestName";
+
+ private final AbstractEndToEndTest test;
+
+ public AccountSetupFlow(AbstractEndToEndTest test) {
+ this.test = test;
+ }
+
+ public AccountsPage setupAccountFromWelcomePage(WelcomeMessagePage welcomeMessagePage) {
+ AccountSetupPage accountSetupPage = welcomeMessagePage.clickNext();
+ return setupAccountFromSetupNewAccountActivity(accountSetupPage);
+ }
+
+ public AccountsPage setupAccountFromAccountsPage(AccountsPage accountPage) {
+ AccountSetupPage accountSetupPage = accountPage.clickAddNewAccount();
+ return setupAccountFromSetupNewAccountActivity(accountSetupPage);
+ }
+
+ public AccountsPage setupAccountFromSetupNewAccountActivity(AccountSetupPage accountSetupPage) {
+ AccountTypePage accountTypePage = fillInCredentialsAndClickManualSetup(accountSetupPage);
+
+ IncomingServerSettingsPage incoming = accountTypePage.clickImap();
+
+ StubMailServer stubMailServer = test.setupMailServer();
+
+ OutgoingServerSettingsPage outgoing = setupIncomingServerAndClickNext(incoming, stubMailServer);
+
+ AccountOptionsPage accountOptionsPage = setupOutgoingServerAndClickNext(outgoing, stubMailServer);
+
+ AccountSetupNamesPage accountSetupNamesPage = accountOptionsPage.clickNext();
+
+ String accountDescription = tempAccountName();
+ accountSetupNamesPage.inputAccountDescription(accountDescription);
+ accountSetupNamesPage.inputAccountName(ACCOUNT_NAME);
+
+ AccountsPage accountsPage = accountSetupNamesPage.clickDone();
+
+ accountsPage.assertAccountExists(accountDescription);
+
+ ApplicationState.getInstance().accounts.add(new AccountForTest(ACCOUNT_NAME, accountDescription, stubMailServer));
+
+ return accountsPage;
+ }
+
+
+ private String tempAccountName() {
+ return "sendAndReceiveTest-" + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new Date());
+ }
+
+ private AccountTypePage fillInCredentialsAndClickManualSetup(AccountSetupPage page) {
+ return page
+ .inputEmailAddress(UserForImap.TEST_USER.emailAddress)
+ .inputPassword(UserForImap.TEST_USER.password)
+ .clickManualSetup();
+ }
+
+ private AccountOptionsPage setupOutgoingServerAndClickNext(OutgoingServerSettingsPage page, StubMailServer stubMailServer) {
+ return page
+ .inputSmtpServer(stubMailServer.getSmtpBindAddress())
+ .inputSmtpSecurity(ConnectionSecurity.NONE)
+ .inputPort(stubMailServer.getSmtpPort())
+ .inputRequireSignIn(false)
+ .clickNext();
+ }
+
+ private OutgoingServerSettingsPage setupIncomingServerAndClickNext(IncomingServerSettingsPage page, StubMailServer stubMailServer) {
+ return page
+ .inputImapServer(stubMailServer.getImapBindAddress())
+ .inputImapSecurity(ConnectionSecurity.NONE)
+ .inputPort(stubMailServer.getImapPort())
+ .inputUsername(UserForImap.TEST_USER.loginUsername)
+ .clickNext();
+ }
+}
diff --git a/tests/src/com/fsck/k9/endtoend/framework/AccountForTest.java b/tests/src/com/fsck/k9/endtoend/framework/AccountForTest.java
new file mode 100644
index 000000000..6845efdbe
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/framework/AccountForTest.java
@@ -0,0 +1,17 @@
+package com.fsck.k9.endtoend.framework;
+
+/**
+ * An account that was added by a test.
+ */
+public class AccountForTest {
+
+ public final String name;
+ public final String description;
+ public final StubMailServer stubMailServer;
+
+ public AccountForTest(String name, String description, StubMailServer stubMailServer) {
+ this.name = name;
+ this.description = description;
+ this.stubMailServer = stubMailServer;
+ }
+}
diff --git a/tests/src/com/fsck/k9/endtoend/framework/ApplicationState.java b/tests/src/com/fsck/k9/endtoend/framework/ApplicationState.java
new file mode 100644
index 000000000..149a2a977
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/framework/ApplicationState.java
@@ -0,0 +1,22 @@
+package com.fsck.k9.endtoend.framework;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Stores the state of the application from the point of view of end-to-end tests.
+ */
+public class ApplicationState {
+
+ private static final ApplicationState state = new ApplicationState();
+
+ public final List accounts = new ArrayList();
+
+ public StubMailServer stubMailServer;
+
+ public static ApplicationState getInstance() {
+ return state;
+ }
+
+
+}
diff --git a/tests/src/com/fsck/k9/endtoend/framework/StubMailServer.java b/tests/src/com/fsck/k9/endtoend/framework/StubMailServer.java
new file mode 100644
index 000000000..d8d1df2a3
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/framework/StubMailServer.java
@@ -0,0 +1,41 @@
+package com.fsck.k9.endtoend.framework;
+
+import com.icegreen.greenmail.util.GreenMail;
+import com.icegreen.greenmail.util.ServerSetup;
+
+/**
+ * Configuration and management of a pair of stub servers for use by an account.
+ */
+public class StubMailServer {
+ private static final ServerSetup IMAP_SERVER_SETUP = new ServerSetup(10143, "127.0.0.2", ServerSetup.PROTOCOL_IMAP);
+ private static final ServerSetup SMTP_SERVER_SETUP = new ServerSetup(10587, "127.0.0.2", ServerSetup.PROTOCOL_SMTP);
+
+ /**
+ * Stub server that speaks SMTP, IMAP etc., that K-9 can talk to.
+ */
+ private GreenMail greenmail;
+
+ public StubMailServer() {
+
+ greenmail = new GreenMail(new ServerSetup[]{IMAP_SERVER_SETUP, SMTP_SERVER_SETUP});
+ greenmail.setUser(UserForImap.TEST_USER.emailAddress, UserForImap.TEST_USER.loginUsername, UserForImap.TEST_USER.password);
+ greenmail.start();
+ }
+
+ public String getSmtpBindAddress() {
+ return SMTP_SERVER_SETUP.getBindAddress();
+ }
+
+ public int getSmtpPort() {
+ return SMTP_SERVER_SETUP.getPort();
+ }
+
+ public String getImapBindAddress() {
+ return IMAP_SERVER_SETUP.getBindAddress();
+ }
+
+ public int getImapPort() {
+ return IMAP_SERVER_SETUP.getPort();
+ }
+}
+
diff --git a/tests/src/com/fsck/k9/endtoend/framework/UserForImap.java b/tests/src/com/fsck/k9/endtoend/framework/UserForImap.java
new file mode 100644
index 000000000..80c1c08f4
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/framework/UserForImap.java
@@ -0,0 +1,19 @@
+package com.fsck.k9.endtoend.framework;
+
+/**
+ * Credentials for the stub IMAP/SMTP server
+ */
+public class UserForImap {
+
+ public static final UserForImap TEST_USER = new UserForImap("test-username", "test-password", "test-email@example.com");
+
+ public final String loginUsername;
+ public final String password;
+ public final String emailAddress;
+
+ private UserForImap(String loginUsername, String password, String emailAddress) {
+ this.loginUsername = loginUsername;
+ this.password = password;
+ this.emailAddress = emailAddress;
+ }
+}
diff --git a/tests/src/com/fsck/k9/endtoend/pages/AbstractPage.java b/tests/src/com/fsck/k9/endtoend/pages/AbstractPage.java
new file mode 100644
index 000000000..bc308210e
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/pages/AbstractPage.java
@@ -0,0 +1,6 @@
+package com.fsck.k9.endtoend.pages;
+
+public class AbstractPage {
+
+ // used to have some content. Now a placeholder class
+}
diff --git a/tests/src/com/fsck/k9/endtoend/pages/AccountOptionsPage.java b/tests/src/com/fsck/k9/endtoend/pages/AccountOptionsPage.java
new file mode 100644
index 000000000..b2f400121
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/pages/AccountOptionsPage.java
@@ -0,0 +1,17 @@
+package com.fsck.k9.endtoend.pages;
+
+import com.fsck.k9.R;
+
+import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
+
+
+public class AccountOptionsPage extends AbstractPage {
+
+ public AccountSetupNamesPage clickNext() {
+ onView(withId(R.id.next)).perform(click());
+ return new AccountSetupNamesPage();
+ }
+
+}
diff --git a/tests/src/com/fsck/k9/endtoend/pages/AccountSetupNamesPage.java b/tests/src/com/fsck/k9/endtoend/pages/AccountSetupNamesPage.java
new file mode 100644
index 000000000..058f613b5
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/pages/AccountSetupNamesPage.java
@@ -0,0 +1,48 @@
+package com.fsck.k9.endtoend.pages;
+
+import com.fsck.k9.R;
+import com.google.android.apps.common.testing.ui.espresso.NoMatchingViewException;
+import com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers;
+
+import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.clearText;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.scrollTo;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.typeText;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
+
+
+public class AccountSetupNamesPage extends AbstractPage {
+
+ public AccountSetupNamesPage inputAccountName(String name) {
+ onView(withId(R.id.account_name))
+ .perform(scrollTo())
+ .perform(clearText())
+ .perform(typeText(name));
+ return this;
+ }
+
+ public AccountSetupNamesPage inputAccountDescription(String name) {
+ onView(withId(R.id.account_description))
+ .perform(scrollTo())
+ .perform(clearText())
+ .perform(typeText(name));
+ return this;
+ }
+
+ public AccountsPage clickDone() {
+ onView(withId(R.id.done))
+ .perform(click());
+ dismissChangelog();
+ return new AccountsPage();
+ }
+
+ private void dismissChangelog() {
+ try {
+ onView(ViewMatchers.withText("OK")).perform(click());
+ } catch (NoMatchingViewException ex) {
+ // Ignored. Not the best way of doing this, but Espresso rightly makes
+ // conditional flow difficult.
+ }
+ }
+}
diff --git a/tests/src/com/fsck/k9/endtoend/pages/AccountSetupPage.java b/tests/src/com/fsck/k9/endtoend/pages/AccountSetupPage.java
new file mode 100644
index 000000000..2fa659e90
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/pages/AccountSetupPage.java
@@ -0,0 +1,27 @@
+package com.fsck.k9.endtoend.pages;
+
+import com.fsck.k9.R;
+
+import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.typeText;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
+
+public class AccountSetupPage extends AbstractPage {
+
+ public AccountSetupPage inputEmailAddress(String emailAddress) {
+ onView(withId(R.id.account_email)).perform(typeText(emailAddress));
+ return this;
+ }
+
+ public AccountSetupPage inputPassword(String password) {
+ onView(withId(R.id.account_password)).perform(typeText(password));
+ return this;
+ }
+
+ public AccountTypePage clickManualSetup() {
+ onView(withId(R.id.manual_setup)).perform(click());
+ return new AccountTypePage();
+ }
+
+}
diff --git a/tests/src/com/fsck/k9/endtoend/pages/AccountTypePage.java b/tests/src/com/fsck/k9/endtoend/pages/AccountTypePage.java
new file mode 100644
index 000000000..386184161
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/pages/AccountTypePage.java
@@ -0,0 +1,14 @@
+package com.fsck.k9.endtoend.pages;
+
+import com.fsck.k9.R;
+
+import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
+public class AccountTypePage extends AbstractPage {
+
+ public IncomingServerSettingsPage clickImap() {
+ onView(withId(R.id.imap)).perform(click());
+ return new IncomingServerSettingsPage();
+ }
+}
diff --git a/tests/src/com/fsck/k9/endtoend/pages/AccountsPage.java b/tests/src/com/fsck/k9/endtoend/pages/AccountsPage.java
new file mode 100644
index 000000000..653281523
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/pages/AccountsPage.java
@@ -0,0 +1,56 @@
+package com.fsck.k9.endtoend.pages;
+
+import com.fsck.k9.R;
+import com.fsck.k9.endtoend.framework.AccountForTest;
+import com.google.android.apps.common.testing.ui.espresso.NoMatchingViewException;
+import com.google.android.apps.common.testing.ui.espresso.ViewAssertion;
+
+import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.longClick;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.scrollTo;
+import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.doesNotExist;
+import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.matches;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.isDisplayed;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withText;
+
+public class AccountsPage extends AbstractPage {
+
+ private void assertAccount(String accountDisplayName, boolean exists) {
+ ViewAssertion assertion = exists ? matches(isDisplayed()) : doesNotExist();
+ onView(withText(accountDisplayName)).check(assertion);
+ }
+
+ public AccountSetupPage clickAddNewAccount() {
+ // need to click twice for some reason?
+ onView(withId(R.id.add_new_account)).perform(click());
+ try {
+ onView(withId(R.id.add_new_account)).perform(click());
+ } catch (NoMatchingViewException ex) {
+ // Ignore
+ }
+ onView(withId(R.id.account_email)).perform(scrollTo());
+ return new AccountSetupPage();
+ }
+
+ public void assertAccountExists(String accountDisplayName) {
+ assertAccount(accountDisplayName, true);
+ }
+
+ public void assertAccountDoesNotExist(String accountDisplayName) {
+ assertAccount(accountDisplayName, false);
+ }
+
+ public void clickLongOnAccount(AccountForTest accountForTest) {
+ onView(withText(accountForTest.description)).perform(longClick());
+ }
+
+ public void clickRemoveInAccountMenu() {
+ onView(withText("Remove account")).perform(click());
+ }
+
+ public void clickOK() {
+ onView(withText("OK")).perform(click());
+ }
+}
diff --git a/tests/src/com/fsck/k9/endtoend/pages/IncomingServerSettingsPage.java b/tests/src/com/fsck/k9/endtoend/pages/IncomingServerSettingsPage.java
new file mode 100644
index 000000000..f0b07579c
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/pages/IncomingServerSettingsPage.java
@@ -0,0 +1,66 @@
+package com.fsck.k9.endtoend.pages;
+
+import com.fsck.k9.R;
+import com.fsck.k9.mail.ConnectionSecurity;
+
+import static com.google.android.apps.common.testing.ui.espresso.Espresso.onData;
+import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.clearText;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.scrollTo;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.typeText;
+import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.matches;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.isClickable;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+
+public class IncomingServerSettingsPage extends AbstractPage {
+
+ public IncomingServerSettingsPage inputImapServer(String imapServer) {
+ onView(withId(R.id.account_server))
+ .perform(scrollTo())
+ .perform(clearText())
+ .perform(typeText(imapServer));
+ return this;
+ }
+
+ public IncomingServerSettingsPage inputImapSecurity(ConnectionSecurity security) {
+ onView(withId(R.id.account_security_type))
+ .perform(scrollTo())
+ .perform(click());
+ onData(allOf(is(instanceOf(ConnectionSecurity.class)), is(security))).perform(click());
+ return this;
+ }
+
+ public IncomingServerSettingsPage inputPort(int port) {
+ onView(withId(R.id.account_port))
+ .perform(scrollTo())
+ .perform(clearText())
+ .perform(typeText(String.valueOf(port)));
+ return this;
+ }
+
+
+ public OutgoingServerSettingsPage clickNext() {
+ onView(withId(R.id.next))
+// .perform(scrollTo())
+ .check(matches(isClickable()))
+ .perform(click());
+
+ // We know this view is on the next page, this functions as a wait.
+ onView(withText("SMTP server")).perform(scrollTo());
+ return new OutgoingServerSettingsPage();
+ }
+
+ public IncomingServerSettingsPage inputUsername(String loginUsername) {
+ onView(withId(R.id.account_username))
+ .perform(scrollTo())
+ .perform(clearText())
+ .perform(typeText(loginUsername));
+ return this;
+ }
+
+}
diff --git a/tests/src/com/fsck/k9/endtoend/pages/OutgoingServerSettingsPage.java b/tests/src/com/fsck/k9/endtoend/pages/OutgoingServerSettingsPage.java
new file mode 100644
index 000000000..87ec9723f
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/pages/OutgoingServerSettingsPage.java
@@ -0,0 +1,69 @@
+package com.fsck.k9.endtoend.pages;
+
+import com.fsck.k9.R;
+import com.fsck.k9.mail.ConnectionSecurity;
+
+import static com.google.android.apps.common.testing.ui.espresso.Espresso.onData;
+import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.clearText;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.scrollTo;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.typeText;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+
+
+public class OutgoingServerSettingsPage extends AbstractPage {
+
+ public OutgoingServerSettingsPage inputSmtpServer(String serverAddress) {
+ onView(withId(R.id.account_server))
+ .perform(scrollTo())
+ .perform(clearText())
+ .perform(typeText(serverAddress));
+ return this;
+
+ }
+
+ public OutgoingServerSettingsPage inputSmtpSecurity(ConnectionSecurity security) {
+ onView(withId(R.id.account_security_type))
+ .perform(scrollTo())
+ .perform(click());
+ onData(allOf(is(instanceOf(ConnectionSecurity.class)), is(security))).perform(click());
+ return this;
+ }
+
+ public OutgoingServerSettingsPage inputPort(int port) {
+ onView(withId(R.id.account_port))
+ .perform(scrollTo())
+ .perform(clearText())
+ .perform(typeText(String.valueOf(port)));
+ return this;
+ }
+
+ public OutgoingServerSettingsPage inputRequireSignIn(boolean requireSignInInput) {
+ onView(withId(R.id.account_require_login))
+ .perform(scrollTo());
+ /*
+ * Make this smarter; click if necessary.
+ */
+ if (!requireSignInInput) {
+ onView(withId(R.id.account_require_login))
+ .perform(click());
+ }
+// Matcher checkedOrNot = requireSignInInput ? isChecked(): isNotChecked();
+// try {
+// onView(withId(R.id.account_require_login)).check((matches(checkedOrNot)));
+// } catch (AssertionFailedWithCauseError ex) {
+// onView(withId(R.id.account_require_login)).perform(click());
+// }
+ return this;
+ }
+
+ public AccountOptionsPage clickNext() {
+ onView(withId(R.id.next)).perform(click());
+ return new AccountOptionsPage();
+ }
+
+}
diff --git a/tests/src/com/fsck/k9/endtoend/pages/WelcomeMessagePage.java b/tests/src/com/fsck/k9/endtoend/pages/WelcomeMessagePage.java
new file mode 100644
index 000000000..723fb3645
--- /dev/null
+++ b/tests/src/com/fsck/k9/endtoend/pages/WelcomeMessagePage.java
@@ -0,0 +1,15 @@
+package com.fsck.k9.endtoend.pages;
+
+import com.fsck.k9.R;
+
+import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
+import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;
+import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
+
+public class WelcomeMessagePage extends AbstractPage {
+
+ public final AccountSetupPage clickNext() {
+ onView(withId(R.id.next)).perform(click());
+ return new AccountSetupPage();
+ }
+}