diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/ContactUsTests.java b/WordPress/src/androidTest/java/org/wordpress/android/e2e/ContactUsTests.java new file mode 100644 index 000000000000..d0e464f8bc10 --- /dev/null +++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/ContactUsTests.java @@ -0,0 +1,84 @@ +package org.wordpress.android.e2e; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.wordpress.android.R; +import org.wordpress.android.e2e.flows.LoginFlow; +import org.wordpress.android.e2e.pages.ContactSupportScreen; +import org.wordpress.android.support.BaseTest; + +import static org.wordpress.android.BuildConfig.E2E_WP_COM_USER_EMAIL; +import static org.wordpress.android.support.WPSupportUtils.pressBackUntilElementIsDisplayed; + +public class ContactUsTests extends BaseTest { + @Before + public void setUp() { + logoutIfNecessary(); + } + + @After + public void tearDown() { + pressBackUntilElementIsDisplayed(R.id.continue_with_wpcom_button); + } + + @Test + public void sendButtonEnabledWhenTextIsEntered() { + try { + new LoginFlow() + .chooseContinueWithWpCom() + .tapHelp() + .assertHelpAndSupportScreenLoaded() + .openContactUs() + .assertContactSupportScreenLoaded() + .assertSendButtonDisabled() + .setMessageText("Hello") + .assertSendButtonEnabled() + .setMessageText("") + .assertSendButtonDisabled(); + } finally { + new ContactSupportScreen().goBackAndDeleteUnsentMessageIfNeeded(); + } + } + + @Ignore("As long as CI does not use gradle.properties from MobileSecrets") + @Test + public void messageCanBeSent() { + String userMessageText = "Please ignore, this is an automated test."; + String automatedReplyText = "Mobile support will respond as soon as possible, " + + "generally within 48-96 hours. " + + "Please reply with your site address (URL) " + + "and any additional details we should know."; + + try { + new LoginFlow() + .chooseContinueWithWpCom() + .tapHelp() + .openContactUs() + .setMessageText(userMessageText) + .tapSendButton() + .assertUserMessageDelivered(userMessageText) + .assertSystemMessageReceived(automatedReplyText); + } finally { + new ContactSupportScreen().goBackAndDeleteUnsentMessageIfNeeded(); + } + } + + @Test + public void helpCanBeOpenedWhileEnteringEmail() { + new LoginFlow() + .chooseContinueWithWpCom() + .tapHelp() + .assertHelpAndSupportScreenLoaded(); + } + + @Test + public void helpCanBeOpenedWhileEnteringPassword() { + new LoginFlow() + .chooseContinueWithWpCom() + .enterEmailAddress(E2E_WP_COM_USER_EMAIL) + .tapHelp() + .assertHelpAndSupportScreenLoaded(); + } +} diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.java b/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.java index 1b8b5b63823e..6214c1ae16d8 100644 --- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.java +++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.java @@ -10,6 +10,7 @@ import org.hamcrest.Matchers; import org.wordpress.android.BuildConfig; import org.wordpress.android.R; +import org.wordpress.android.e2e.pages.HelpAndSupportScreen; import org.wordpress.android.ui.accounts.LoginMagicLinkInterceptActivity; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; @@ -128,4 +129,9 @@ public LoginFlow enterSiteAddress(String siteAddress) { clickOn(R.id.bottom_button); return this; } + + public HelpAndSupportScreen tapHelp() { + clickOn(onView(withId(R.id.help))); + return new HelpAndSupportScreen(); + } } diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/pages/ContactSupportScreen.java b/WordPress/src/androidTest/java/org/wordpress/android/e2e/pages/ContactSupportScreen.java new file mode 100644 index 000000000000..dcb44bb54a8b --- /dev/null +++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/pages/ContactSupportScreen.java @@ -0,0 +1,119 @@ +package org.wordpress.android.e2e.pages; + +import androidx.test.espresso.Espresso; +import androidx.test.espresso.ViewInteraction; +import androidx.test.espresso.action.ViewActions; + +import org.wordpress.android.R; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isEnabled; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.not; +import static org.wordpress.android.support.WPSupportUtils.populateTextField; +import static org.wordpress.android.support.WPSupportUtils.sleep; +import static org.wordpress.android.support.WPSupportUtils.waitForElementToBeDisplayed; +import static org.wordpress.android.support.WPSupportUtils.waitForElementToBeDisplayedWithoutFailure; + +public class ContactSupportScreen { + // "Contact Support" screen looks differently depending on + // "gradle.properties" content (default or from Mobile Secrets). + // But the elements tree always contains all elements, some are + // just hidden. Locators below attempt to support both variants. + static ViewInteraction textInput = onView(allOf( + isCompletelyDisplayed(), + anyOf( + withId(R.id.message_composer_input_text), + withId(R.id.request_message_field) + ) + )); + static ViewInteraction sendButton = onView(allOf( + isCompletelyDisplayed(), + anyOf( + withId(R.id.message_composer_send_btn), + withId(R.id.request_conversations_disabled_menu_ic_send) + ) + )); + + // Actions: + public ContactSupportScreen tapSendButton() { + sendButton.perform(ViewActions.click()); + return this; + } + + public ContactSupportScreen setMessageText(String text) { + populateTextField(textInput, text); + // This sleep serves only one purpose: allowing human to notice + // that text was really entered. Especially matters when watching + // low-fps test video recordings from CI. + sleep(); + return this; + } + + public HelpAndSupportScreen goBackAndDeleteUnsentMessageIfNeeded() { + Espresso.pressBack(); + + ViewInteraction unsentMessageAlert = onView( + withText("Going back will delete your message. " + + "Are you sure you want to delete it?" + )); + + if (waitForElementToBeDisplayedWithoutFailure(unsentMessageAlert)) { + onView(withText("Delete")) + .perform(ViewActions.click()); + } + + return new HelpAndSupportScreen(); + } + + // Assertions: + public ContactSupportScreen assertContactSupportScreenLoaded() { + textInput.check(matches(isCompletelyDisplayed())); + sendButton.check(matches(isCompletelyDisplayed())); + return this; + } + + public ContactSupportScreen assertSendButtonDisabled() { + sendButton.check(matches(not(isEnabled()))); + return this; + } + + public ContactSupportScreen assertSendButtonEnabled() { + sendButton.check(matches(isEnabled())); + return this; + } + + public ContactSupportScreen assertUserMessageDelivered(String messageText) { + ViewInteraction userMessageContainer = onView(allOf( + withId(R.id.request_user_message_container), + hasDescendant(allOf( + withId(R.id.request_user_message_text), + withText(messageText) + )), + hasDescendant(allOf( + withId(R.id.request_user_message_status), + withText("Delivered") + )) + )); + + waitForElementToBeDisplayed(userMessageContainer); + userMessageContainer.check(matches(isCompletelyDisplayed())); + return this; + } + + public ContactSupportScreen assertSystemMessageReceived(String messageText) { + ViewInteraction systemResponseBubble = onView(allOf( + withId(R.id.request_system_message_text), + withText(messageText))); + + waitForElementToBeDisplayed(systemResponseBubble); + systemResponseBubble.check(matches(isCompletelyDisplayed())); + return this; + } +} diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/pages/HelpAndSupportScreen.java b/WordPress/src/androidTest/java/org/wordpress/android/e2e/pages/HelpAndSupportScreen.java new file mode 100644 index 000000000000..f6bf54ed40e6 --- /dev/null +++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/pages/HelpAndSupportScreen.java @@ -0,0 +1,60 @@ +package org.wordpress.android.e2e.pages; + +import androidx.test.espresso.ViewInteraction; +import androidx.test.espresso.action.ViewActions; + +import org.wordpress.android.R; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static org.wordpress.android.support.WPSupportUtils.populateTextField; +import static org.hamcrest.Matchers.anyOf; +import static org.wordpress.android.support.WPSupportUtils.waitForElementToBeDisplayedWithoutFailure; + + +public class HelpAndSupportScreen { + static ViewInteraction contactUsButton = onView(withId(R.id.contact_us_button)); + static ViewInteraction faqButton = onView(withId(R.id.faq_button)); + static ViewInteraction myTicketsButton = onView(withId(R.id.my_tickets_button)); + static ViewInteraction applicationLogButton = onView(withId(R.id.application_log_button)); + static ViewInteraction applicationVersionText = onView(withId(R.id.applicationVersion)); + static ViewInteraction emailAddressText = onView(withId(R.id.contactEmailAddress)); + + + public HelpAndSupportScreen assertHelpAndSupportScreenLoaded() { + contactUsButton.check(matches(isCompletelyDisplayed())); + faqButton.check(matches(isCompletelyDisplayed())); + myTicketsButton.check(matches(isCompletelyDisplayed())); + applicationLogButton.check(matches(isCompletelyDisplayed())); + applicationVersionText.check(matches(isCompletelyDisplayed())); + emailAddressText.check(matches(isCompletelyDisplayed())); + return this; + } + + public ContactSupportScreen openContactUs() { + contactUsButton.perform(ViewActions.click()); + setEmailIfNeeded("WPcomTest@test.com", "TestUser"); + return new ContactSupportScreen(); + } + + public void setEmailIfNeeded(String emailAddress, String userName) { + ViewInteraction emailInput = onView(withId(R.id.support_identity_input_dialog_email_edit_text)); + + if (!waitForElementToBeDisplayedWithoutFailure(emailInput)) { + return; + } + + populateTextField(emailInput, emailAddress); + ViewInteraction nameInput = onView(withId(R.id.support_identity_input_dialog_name_edit_text)); + populateTextField(nameInput, userName); + + onView(anyOf( + withText(android.R.string.ok), + withId(android.R.id.button1) + )) + .perform(ViewActions.click()); + } +} diff --git a/WordPress/src/androidTest/java/org/wordpress/android/support/WPSupportUtils.java b/WordPress/src/androidTest/java/org/wordpress/android/support/WPSupportUtils.java index 725951f087b1..4b61a83cf224 100644 --- a/WordPress/src/androidTest/java/org/wordpress/android/support/WPSupportUtils.java +++ b/WordPress/src/androidTest/java/org/wordpress/android/support/WPSupportUtils.java @@ -475,6 +475,20 @@ public Boolean get() { return isElementDisplayed(elementID); } + public static boolean waitForElementToBeDisplayedWithoutFailure(final ViewInteraction element) { + try { + waitForConditionToBeTrueWithoutFailure(new Supplier() { + @Override + public Boolean get() { + return isElementDisplayed(element); + } + }); + } catch (Exception e) { + // ignore the failure + } + return isElementDisplayed(element); + } + public static void waitForConditionToBeTrue(Supplier supplier) { if (supplier.get()) { return;