/* * The MIT License (MIT) * * Copyright (c) 2015 NBCO Yandex.Money LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package ru.yandex.money.android.test; import android.content.Intent; import android.support.annotation.NonNull; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.LargeTest; import android.view.View; import com.robotium.solo.Condition; import com.robotium.solo.Solo; import com.yandex.money.api.methods.payment.params.PaymentParams; import java.util.concurrent.TimeUnit; import ru.yandex.money.android.PaymentActivity; import ru.yandex.money.android.test.espresso.ViewGroupInteraction; import ru.yandex.money.android.test.properties.LocalProperties; import ru.yandex.money.android.test.properties.TestProperties; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.Espresso.pressBack; 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.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 com.robotium.solo.By.id; import static org.hamcrest.Matchers.not; import static ru.yandex.money.android.test.espresso.MoreViewActions.waitView; import static ru.yandex.money.android.test.espresso.MoreViewActions.waitVisibilityChange; import static ru.yandex.money.android.test.espresso.ViewGroupInteraction.onViewGroup; /** * @author Slava Yasevich (vyasevich@yamoney.ru) */ @LargeTest public final class PaymentActivityTest extends ActivityInstrumentationTestCase2<PaymentActivity> { private static final String CARD_NUMBER_ID = "cardNumber"; private final LocalProperties localProperties = new LocalProperties(); private final TestProperties testProperties = new TestProperties(); private Solo solo; public PaymentActivityTest() { super(PaymentActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); AppData.clean(getInstrumentation().getTargetContext()); } public void testInvalidId() { new Test("invalid") { @Override protected void execute() { waitForFragment(); onView(withId(R.id.ym_error_title)) .check(matches(withText(R.string.ym_error_illegal_param_client_id_title))); onView(withId(R.id.ym_error_message)) .check(matches(withText(R.string.ym_error_illegal_param_client_id))); onView(withId(R.id.ym_error_action)) .check(matches(not(isDisplayed()))); } }.run(); } public void testHome() { new ClientTest() { @Override protected void execute() { waitForFragment(); onView(withId(android.R.id.home)) .perform(click()); } }.run(); } public void testNewCard() { new ClientTest() { @Override protected void execute() { waitForFragment(); // check that there was no error onView(withId(R.id.web_view)).check(matches(isDisplayed())); onView(withId(R.id.ym_error_title)) .check(matches(not(isDisplayed()))); payNewCard(); } }.run(); } public void testSavedCard() { new WithSavedCardTest() { @Override protected void execute() { paySavedCard(); } }.run(); } public void testSavedCardDeletion() { new WithSavedCardTest() { @Override protected void execute() { waitForCards(); deleteSavedCard(); clickOnListItem(0); waitForWebView(); } }.run(); } public void testSavedCardCanceledNewCard() { new WithSavedCardNewCardTest() { @Override protected void cancel() { onView(withId(R.id.ym_cancel)) .perform(click()); } }.run(); } public void testSavedCardCanceledBackNewCard() { new WithSavedCardNewCardTest() { @Override protected void cancel() { pressBack(); } }.run(); } public void testNewCardCanceledSavedCard() { new WithSavedCardTest() { @Override protected void execute() { waitForCards(); clickOnListItem(1); waitForWebView(); pressBack(); paySavedCard(); } }.run(); } public void testSavedCardDeletedNewCardCanceled() { new WithSavedCardTest() { @Override protected void execute() { waitForCards(); deleteSavedCard(); clickOnListItem(0); waitForWebView(); pressBack(); waitForCards(); } }.run(); } private void waitForFragment() { solo.waitForFragmentById(R.id.ym_container); } private void payNewCard() { waitForWebView(); // entering card details LocalProperties.Card card = localProperties.getCard(); solo.enterTextInWebElement(id(CARD_NUMBER_ID), card.number); solo.enterTextInWebElement(id("month"), card.month); solo.enterTextInWebElement(id("year"), card.year); solo.enterTextInWebElement(id("cardCvc"), card.csc); solo.clickOnWebElement(id("mobile-cps_submit-button")); // waiting for success payment, may require manual user input (3D Secure for instance) onView(isRoot()).perform(waitView(testProperties.getManualTimeout(), withId(R.id.ym_success))); // checking success page and trying to save a card onView(withId(R.id.ym_comment)) .check(matches(withText(getString(R.string.ym_success_comment, localProperties.getAmount())))); onView(withId(R.id.ym_description)) .check(matches(withText(R.string.ym_success_save_card_description))); onView(withId(R.id.ym_save_card)) .check(matches(withText(R.string.ym_success_save_card))) .perform(click()); // waiting for a card to be saved onView(withId(R.id.ym_save_card)) .perform(waitVisibilityChange(testProperties.getNetworkTimeout(), View.GONE)); // when a card is saved onView(withId(R.id.ym_success_marker)) .check(matches(isDisplayed())); } private void paySavedCard() { waitForCards(); clickOnListItem(0); initSavedCardPayment(); onView(isRoot()) .perform(waitView(testProperties.getNetworkTimeout(), withId(R.id.ym_success))); onView(withId(R.id.ym_card)) .check(matches(not(isDisplayed()))); onView(withId(R.id.ym_description)) .check(matches(not(isDisplayed()))); onView(withId(R.id.ym_success_marker)) .check(matches(not(isDisplayed()))); onView(withId(R.id.ym_save_card)) .check(matches(not(isDisplayed()))); } private void deleteSavedCard() { getItemAtPosition(0) .onChildView(withId(R.id.ym_card_container)) .onChildView(withId(R.id.ym_actions)) .perform(click()); onView(isRoot()) .perform(waitView(testProperties.getAnimationsTimeout(), withText(R.string.ym_cards_delete_card))) .perform(click()); } private void waitForWebView() { solo.waitForCondition(new Condition() { @Override public boolean isSatisfied() { return !solo.getWebElements(id(CARD_NUMBER_ID)).isEmpty(); } }, sleep(testProperties.getNetworkTimeout())); } private void waitForCards() { onView(isRoot()) .perform(waitView(testProperties.getNetworkTimeout(), withId(android.R.id.list))); onView(withId(R.id.ym_payment_sum)) .check(matches(withText(getString(R.string.ym_cards_payment_sum_value, localProperties.getAmount())))); } private void clickOnListItem(int position) { getItemAtPosition(position) .perform(click()); } private ViewGroupInteraction getItemAtPosition(int position) { return onViewGroup(withId(android.R.id.list)).atPosition(position); } private void initSavedCardPayment() { onView(withId(R.id.ym_csc)) .perform(typeText(localProperties.getCard().csc)); onView(withId(R.id.ym_pay)) .perform(click()); } private int sleep(int seconds) { return (int) TimeUnit.SECONDS.toMillis(seconds); } private String getString(int resId, Object... params) { return getActivity().getString(resId, params); } private abstract class Test implements Runnable { private final String clientId; public Test(String clientId) { this.clientId = clientId; } @Override public void run() { prepare(); setUp(); execute(); } protected void prepare() { } protected abstract void execute(); private void setUp() { Intent intent = PaymentActivity.getBuilder(getInstrumentation().getContext()) .setPaymentParams(createArguments()) .setClientId(clientId) .setHost(localProperties.getHostUrl()) .build(); setActivityIntent(intent); solo = new Solo(getInstrumentation(), getActivity()); } @NonNull private PaymentParams createArguments() { return localProperties.getPhoneParams(); } } private abstract class ClientTest extends Test { public ClientTest() { super(localProperties.getClientId()); } } private abstract class WithSavedCardTest extends ClientTest { @Override protected final void prepare() { AppData.addSavedCard(getInstrumentation().getContext(), localProperties.getInstanceId(), localProperties.getCard()); } } private abstract class WithSavedCardNewCardTest extends WithSavedCardTest { @Override protected final void execute() { waitForCards(); clickOnListItem(0); onView(withId(R.id.ym_pay)) .perform(click()); onView(withId(R.id.ym_error)) .check(matches(isDisplayed())); onView(withId(R.id.ym_error_title)) .check(matches(withText(R.string.ym_error_oops_title))); onView(withId(R.id.ym_error_message)) .check(matches(withText(R.string.ym_error_csc_invalid))); cancel(); waitForCards(); clickOnListItem(1); payNewCard(); } protected abstract void cancel(); } }