From 854b1b3ffc57ae2978e5b629100e91c88cd8b6a6 Mon Sep 17 00:00:00 2001 From: Art O Cathain Date: Sun, 9 Nov 2014 21:14:34 +0000 Subject: [PATCH] Add end-to-end tests using Espresso --- build.gradle | 21 ++++ tests/AndroidManifest.xml | 13 +-- ...WelcomeAndSetupAccountIntegrationTest.java | 24 +++++ .../endtoend/A010_AccountIntegrationTest.java | 40 ++++++++ .../k9/endtoend/AbstractEndToEndTest.java | 60 ++++++++++++ .../fsck/k9/endtoend/AccountSetupFlow.java | 98 +++++++++++++++++++ .../k9/endtoend/framework/AccountForTest.java | 17 ++++ .../endtoend/framework/ApplicationState.java | 22 +++++ .../k9/endtoend/framework/StubMailServer.java | 41 ++++++++ .../k9/endtoend/framework/UserForImap.java | 19 ++++ .../fsck/k9/endtoend/pages/AbstractPage.java | 6 ++ .../k9/endtoend/pages/AccountOptionsPage.java | 17 ++++ .../endtoend/pages/AccountSetupNamesPage.java | 48 +++++++++ .../k9/endtoend/pages/AccountSetupPage.java | 27 +++++ .../k9/endtoend/pages/AccountTypePage.java | 14 +++ .../fsck/k9/endtoend/pages/AccountsPage.java | 56 +++++++++++ .../pages/IncomingServerSettingsPage.java | 66 +++++++++++++ .../pages/OutgoingServerSettingsPage.java | 69 +++++++++++++ .../k9/endtoend/pages/WelcomeMessagePage.java | 15 +++ 19 files changed, 665 insertions(+), 8 deletions(-) create mode 100644 tests/src/com/fsck/k9/endtoend/A000_WelcomeAndSetupAccountIntegrationTest.java create mode 100644 tests/src/com/fsck/k9/endtoend/A010_AccountIntegrationTest.java create mode 100644 tests/src/com/fsck/k9/endtoend/AbstractEndToEndTest.java create mode 100644 tests/src/com/fsck/k9/endtoend/AccountSetupFlow.java create mode 100644 tests/src/com/fsck/k9/endtoend/framework/AccountForTest.java create mode 100644 tests/src/com/fsck/k9/endtoend/framework/ApplicationState.java create mode 100644 tests/src/com/fsck/k9/endtoend/framework/StubMailServer.java create mode 100644 tests/src/com/fsck/k9/endtoend/framework/UserForImap.java create mode 100644 tests/src/com/fsck/k9/endtoend/pages/AbstractPage.java create mode 100644 tests/src/com/fsck/k9/endtoend/pages/AccountOptionsPage.java create mode 100644 tests/src/com/fsck/k9/endtoend/pages/AccountSetupNamesPage.java create mode 100644 tests/src/com/fsck/k9/endtoend/pages/AccountSetupPage.java create mode 100644 tests/src/com/fsck/k9/endtoend/pages/AccountTypePage.java create mode 100644 tests/src/com/fsck/k9/endtoend/pages/AccountsPage.java create mode 100644 tests/src/com/fsck/k9/endtoend/pages/IncomingServerSettingsPage.java create mode 100644 tests/src/com/fsck/k9/endtoend/pages/OutgoingServerSettingsPage.java create mode 100644 tests/src/com/fsck/k9/endtoend/pages/WelcomeMessagePage.java 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(); + } +}