diff --git a/ui/src/androidTest/java/com/hyperwallet/android/transfermethod/ui/ListTransferMethodTest.java b/ui/src/androidTest/java/com/hyperwallet/android/transfermethod/ui/ListTransferMethodTest.java index c9c491101..9c7038a6f 100644 --- a/ui/src/androidTest/java/com/hyperwallet/android/transfermethod/ui/ListTransferMethodTest.java +++ b/ui/src/androidTest/java/com/hyperwallet/android/transfermethod/ui/ListTransferMethodTest.java @@ -90,7 +90,6 @@ public void testListTransferMethod_userHasMultipleTransferMethods() { mActivityTestRule.launchActivity(null); // assert - onView(withId(R.id.toolbar)).check(matches(isDisplayed())); onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) .check(matches(withText(R.string.title_activity_list_transfer_method))); onView(withId(R.id.fab)).check(matches(isDisplayed())); @@ -146,7 +145,17 @@ public void testListTransferMethod_userHasMultipleTransferMethods() { onView(withId(R.id.list_transfer_method_item)).check( matches(atPosition(4, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); - onView(withId(R.id.list_transfer_method_item)).check(new RecyclerViewCountAssertion(5)); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(5, hasDescendant(withText(R.string.paypal_account_font_icon))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(5, hasDescendant(withText(R.string.paypal_account))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(5, hasDescendant(withText("United States"))))); + //TODO: Try to check for non existence of transfer_method_type_description_2 + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(5, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); + + onView(withId(R.id.list_transfer_method_item)).check(new RecyclerViewCountAssertion(6)); } @@ -337,6 +346,70 @@ public void onReceive(Context context, Intent intent) { } + @Test + public void testListTransferMethod_removePayPalAccount() throws InterruptedException { + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_list_single_paypal_account_response.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_deactivate_success.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_NO_CONTENT).withBody("").mock(); + + + final CountDownLatch gate = new CountDownLatch(1); + final BroadcastReceiver br = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + gate.countDown(); + + HyperwalletStatusTransition statusTransition = intent.getParcelableExtra( + "hyperwallet-local-broadcast-payload"); + assertThat("Transition is not valid", statusTransition.getTransition(), is(DE_ACTIVATED)); + } + }; + + // run test + mActivityTestRule.launchActivity(null); + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()) + .registerReceiver(br, new IntentFilter("ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED")); + + // assert + onView(withId(R.id.toolbar)).check(matches(isDisplayed())); + onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) + .check(matches(withText(R.string.title_activity_list_transfer_method))); + onView(withId(R.id.fab)).check(matches(isDisplayed())); + + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.paypal_account_font_icon))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.paypal_account))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText("United States"))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); + + onView(allOf(instanceOf(ImageButton.class), hasSibling(withText(R.string.paypal_account)))).perform(click()) + .inRoot(Matchers.instanceOf(MenuItem.class)); + onView(withDrawable(R.drawable.ic_trash)).check(matches(isDisplayed())); + onView(withText(R.string.menu_remove_account)).check(matches(isDisplayed())).perform(click()); + + // confirmation dialog is shown before deletion + onView(withText(R.string.transfer_method_remove_confirmation_title)).check(matches(isDisplayed())); + onView(withText(R.string.transfer_method_remove_confirmation)).check(matches(isDisplayed())); + onView(withId(android.R.id.button1)).check(matches(withText(R.string.remove_button_label))); + onView(withId(android.R.id.button2)).check(matches(withText(R.string.cancel_button_label))); + + onView(withId(android.R.id.button1)).perform(click()); + + gate.await(5, SECONDS); + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()).unregisterReceiver( + br); + assertThat("Action is not broadcasted", gate.getCount(), is(0L)); + + onView(withId(R.id.empty_transfer_method_list_layout)).check(matches(isDisplayed())); + onView(withText(R.string.empty_list_transfer_method_information)).check(matches(isDisplayed())); + + } + @Test public void testListTransferMethod_removeBankAccountClickCancel() throws InterruptedException { mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager diff --git a/ui/src/androidTest/java/com/hyperwallet/android/transfermethod/ui/PayPalTest.java b/ui/src/androidTest/java/com/hyperwallet/android/transfermethod/ui/PayPalTest.java new file mode 100644 index 000000000..40574449f --- /dev/null +++ b/ui/src/androidTest/java/com/hyperwallet/android/transfermethod/ui/PayPalTest.java @@ -0,0 +1,220 @@ +package com.hyperwallet.android.transfermethod.ui; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; +import static androidx.test.espresso.action.ViewActions.typeText; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.RootMatchers.isDialog; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withParent; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; + +import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; +import static java.net.HttpURLConnection.HTTP_CREATED; +import static java.net.HttpURLConnection.HTTP_OK; +import static java.util.concurrent.TimeUnit.SECONDS; + +import static com.hyperwallet.android.util.EspressoUtils.hasErrorText; +import static com.hyperwallet.android.util.EspressoUtils.nestedScrollTo; +import static com.hyperwallet.android.util.EspressoUtils.withHint; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.widget.TextView; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.espresso.IdlingRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import com.hyperwallet.android.Hyperwallet; +import com.hyperwallet.android.hyperwallet_ui.R; +import com.hyperwallet.android.model.HyperwalletTransferMethod; +import com.hyperwallet.android.rule.HyperwalletExternalResourceManager; +import com.hyperwallet.android.rule.HyperwalletMockWebServer; +import com.hyperwallet.android.ui.repository.RepositoryFactory; +import com.hyperwallet.android.ui.transfermethod.AddTransferMethodActivity; +import com.hyperwallet.android.ui.util.EspressoIdlingResource; +import com.hyperwallet.android.util.TestAuthenticationProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; + +@RunWith(AndroidJUnit4.class) +public class PayPalTest { + + @ClassRule + public static HyperwalletExternalResourceManager sResourceManager = new HyperwalletExternalResourceManager(); + @Rule + public HyperwalletMockWebServer mMockWebServer = new HyperwalletMockWebServer(8080); + @Rule + public ActivityTestRule mActivityTestRule = + new ActivityTestRule(AddTransferMethodActivity.class, true, false) { + @Override + protected Intent getActivityIntent() { + Intent intent = new Intent(ApplicationProvider.getApplicationContext(), + AddTransferMethodActivity.class); + intent.putExtra("TRANSFER_METHOD_TYPE", "PAYPAL_ACCOUNT"); + intent.putExtra("TRANSFER_METHOD_COUNTRY", "US"); + intent.putExtra("TRANSFER_METHOD_CURRENCY", "USD"); + return intent; + } + }; + + @Before + public void setup() { + Hyperwallet.getInstance(new TestAuthenticationProvider()); + + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("authentication_token_response.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("successful_tmc_paypal_fields_response.json")).mock(); + } + + @After + public void cleanup() { + RepositoryFactory.clearInstance(); + } + + @Before + public void registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.getIdlingResource()); + } + + @After + public void unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.getIdlingResource()); + } + + @Test + public void testAddTransferMethod_displaysElementsOnTmcResponse() { + mActivityTestRule.launchActivity(null); + + onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))).check( + matches(withText(R.string.paypal_account))); + + onView(withId(R.id.email)).check(matches(isDisplayed())); + onView(withId(R.id.emailLabel)).check(matches(withHint("Email"))); + + onView(withId(R.id.add_transfer_method_button)).perform(nestedScrollTo()).check( + matches(withText(R.string.button_create_transfer_method))); + } + + @Test + public void testAddTransferMethod_displaysFeeElementsOnTmcResponse() { + mActivityTestRule.launchActivity(null); + + onView(withId(R.id.add_transfer_method_fee_label)).check( + matches(withText(R.string.add_transfer_method_fee_label))); + onView(withId(R.id.add_transfer_method_processing_label)).check( + matches(withText(R.string.add_transfer_method_processing_time_label))); + onView(withId(R.id.add_transfer_method_fee_value)).check(matches(withText("USD 0.25"))); + onView(withId(R.id.add_transfer_method_processing_time_value)).check(matches(withText("IMMEDIATE"))); + } + + @Test + public void testAddTransferMethod_returnsTokenOnPaypalAccountCreation() throws InterruptedException { + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_CREATED).withBody(sResourceManager + .getResourceContent("paypal_response.json")).mock(); + + mActivityTestRule.launchActivity(null); + + final CountDownLatch gate = new CountDownLatch(1); + final BroadcastReceiver br = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + gate.countDown(); + + HyperwalletTransferMethod transferMethod = intent.getParcelableExtra( + "hyperwallet-local-broadcast-payload"); + assertThat("Bank Account Id is incorrect", transferMethod.getField( + HyperwalletTransferMethod.TransferMethodFields.EMAIL), is("sunshine.carreiro@hyperwallet.com")); + } + }; + + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()) + .registerReceiver(br, new IntentFilter("ACTION_HYPERWALLET_TRANSFER_METHOD_ADDED")); + + onView(withId(R.id.email)) + .perform(typeText("sunshine.carreiro@hyperwallet.com")) + .perform(closeSoftKeyboard()); + + onView(withId(R.id.add_transfer_method_button)).perform(nestedScrollTo(), click()); + + assertThat("Result code is incorrect", + mActivityTestRule.getActivityResult().getResultCode(), is(Activity.RESULT_OK)); + + gate.await(5, SECONDS); + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()).unregisterReceiver( + br); + assertThat("Action is not broadcasted", gate.getCount(), is(0L)); + } + + @Test + public void testAddTransferMethod_returnsErrorOnInvalidPattern() { + mActivityTestRule.launchActivity(null); + // Number input should not allow non numeric values + onView(withId(R.id.email)).perform(typeText("abc1test")); + onView(withId(R.id.add_transfer_method_button)).perform(nestedScrollTo(), click()); + + onView(withId(R.id.emailLabel)) + .check(matches(hasErrorText("accountNumber is invalid"))); + } + + @Test + public void testAddTransferMethod_returnsErrorOnInvalidPresence() { + mActivityTestRule.launchActivity(null); + + onView(withId(R.id.add_transfer_method_button)).perform(nestedScrollTo(), click()); + + onView(withId(R.id.emailLabel)) + .check(matches(hasErrorText("You must provide a value for this field"))); + } + + @Test + public void testAddTransferMethod_displaysErrorOnInvalidEmailAddress() { + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_BAD_REQUEST).withBody(sResourceManager + .getResourceContent("paypal_invalid_email_response.json")).mock(); + + mActivityTestRule.launchActivity(null); + + onView(withId(R.id.email)) + .perform(typeText("invalidEmail@gmail.com")).perform(closeSoftKeyboard()); + onView(withId(R.id.add_transfer_method_button)).perform(nestedScrollTo(), click()); + + // check dialog content + onView(withText(R.string.error_dialog_title)).inRoot(isDialog()).check(matches(isDisplayed())); + onView(withText(containsString( + "PayPal transfer method email address should be same as profile email address."))) + .inRoot(isDialog()).check(matches(isDisplayed())); + onView(withId(android.R.id.button1)).check(matches(withText(R.string.close_button_label))); + onView(withId(android.R.id.button1)).perform(click()); + + // should display the add tm form + onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))).check( + matches(withText(R.string.paypal_account))); + + // connectivity dialog should be dismissed and does not exist in ui + onView(withText(R.string.error_dialog_title)).check(doesNotExist()); + } + +} diff --git a/ui/src/main/java/com/hyperwallet/android/ui/repository/RepositoryFactory.java b/ui/src/main/java/com/hyperwallet/android/ui/repository/RepositoryFactory.java index ee5b44ea3..ec2a3a935 100644 --- a/ui/src/main/java/com/hyperwallet/android/ui/repository/RepositoryFactory.java +++ b/ui/src/main/java/com/hyperwallet/android/ui/repository/RepositoryFactory.java @@ -31,10 +31,12 @@ public class RepositoryFactory { private static RepositoryFactory sInstance; private TransferMethodConfigurationRepository mTransferMethodConfigurationRepository; private TransferMethodRepository mTransferMethodRepository; + private UserRepository mUserRepository; private RepositoryFactory() { mTransferMethodConfigurationRepository = new TransferMethodConfigurationRepositoryImpl(); mTransferMethodRepository = new TransferMethodRepositoryImpl(); + mUserRepository = new UserRepositoryImpl(); } public static synchronized RepositoryFactory getInstance() { @@ -52,6 +54,10 @@ public TransferMethodRepository getTransferMethodRepository() { return mTransferMethodRepository; } + public UserRepository getUserRepository() { + return mUserRepository; + } + public static void clearInstance() { sInstance = null; } diff --git a/ui/src/main/java/com/hyperwallet/android/ui/repository/TransferMethodRepositoryImpl.java b/ui/src/main/java/com/hyperwallet/android/ui/repository/TransferMethodRepositoryImpl.java index 0d43a7da7..7bc72f0c9 100644 --- a/ui/src/main/java/com/hyperwallet/android/ui/repository/TransferMethodRepositoryImpl.java +++ b/ui/src/main/java/com/hyperwallet/android/ui/repository/TransferMethodRepositoryImpl.java @@ -20,6 +20,7 @@ import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodFields.TYPE; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.BANK_ACCOUNT; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.BANK_CARD; +import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.PAYPAL_ACCOUNT; import android.os.Handler; @@ -34,6 +35,7 @@ import com.hyperwallet.android.model.HyperwalletBankCard; import com.hyperwallet.android.model.HyperwalletStatusTransition; import com.hyperwallet.android.model.HyperwalletTransferMethod; +import com.hyperwallet.android.model.PayPalAccount; import com.hyperwallet.android.model.paging.HyperwalletPageList; public class TransferMethodRepositoryImpl implements TransferMethodRepository { @@ -46,7 +48,7 @@ Hyperwallet getHyperwallet() { } @Override - public void createTransferMethod(final HyperwalletTransferMethod transferMethod, + public void createTransferMethod(@NonNull final HyperwalletTransferMethod transferMethod, LoadTransferMethodCallback callback) { switch (transferMethod.getField(TYPE)) { case BANK_ACCOUNT: @@ -55,6 +57,9 @@ public void createTransferMethod(final HyperwalletTransferMethod transferMethod, case BANK_CARD: createBankCard(transferMethod, callback); break; + case PAYPAL_ACCOUNT: + createPayPalAccount(transferMethod, callback); + break; default: //no default action } } @@ -89,6 +94,10 @@ public void deactivateTransferMethod(@NonNull final HyperwalletTransferMethod tr break; case BANK_CARD: deactivateBankCardAccount(transferMethod, callback); + break; + case PAYPAL_ACCOUNT: + deactivatePayPalAccount(transferMethod, callback); + break; default: //no default action } } @@ -135,6 +144,27 @@ public Handler getHandler() { }); } + private void deactivatePayPalAccount(@NonNull final HyperwalletTransferMethod transferMethod, + @NonNull final DeactivateTransferMethodCallback callback) { + getHyperwallet().deactivatePayPalAccount(transferMethod.getField(TOKEN), null, + new HyperwalletListener() { + @Override + public void onSuccess(@Nullable HyperwalletStatusTransition result) { + callback.onTransferMethodDeactivated(result); + } + + @Override + public void onFailure(HyperwalletException exception) { + callback.onError(exception.getHyperwalletErrors()); + } + + @Override + public Handler getHandler() { + return mHandler; + } + }); + } + private void createBankAccount(final HyperwalletTransferMethod transferMethod, final LoadTransferMethodCallback callback) { HyperwalletBankAccount bankAccount = (HyperwalletBankAccount) transferMethod; @@ -178,4 +208,26 @@ public Handler getHandler() { } }); } + + private void createPayPalAccount(@NonNull final HyperwalletTransferMethod transferMethod, + @NonNull final LoadTransferMethodCallback callback) { + PayPalAccount payPalAccount = (PayPalAccount) transferMethod; + + getHyperwallet().createPayPalAccount(payPalAccount, new HyperwalletListener() { + @Override + public void onSuccess(@Nullable PayPalAccount result) { + callback.onTransferMethodLoaded(result); + } + + @Override + public void onFailure(HyperwalletException exception) { + callback.onError(exception.getHyperwalletErrors()); + } + + @Override + public Handler getHandler() { + return mHandler; + } + }); + } } diff --git a/ui/src/main/java/com/hyperwallet/android/ui/repository/UserRepository.java b/ui/src/main/java/com/hyperwallet/android/ui/repository/UserRepository.java new file mode 100644 index 000000000..8ee119fa8 --- /dev/null +++ b/ui/src/main/java/com/hyperwallet/android/ui/repository/UserRepository.java @@ -0,0 +1,58 @@ +/* + * Copyright 2018 Hyperwallet + * + * 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 com.hyperwallet.android.ui.repository; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.hyperwallet.android.model.HyperwalletErrors; +import com.hyperwallet.android.model.HyperwalletUser; + +/** + * User Repository Contract + */ +public interface UserRepository { + /** + * Load user information + * + * @param callback @see {@link UserRepository.LoadUserCallback} + */ + void loadUser(@NonNull final LoadUserCallback callback); + + /** + * Set user to null + */ + void refreshUser(); + + /** + * Callback interface that responses to action when invoked to + * Load User information + *

+ * When User is properly loaded + * {@link UserRepository.LoadUserCallback#onUserLoaded(HyperwalletUser)} + * is invoked otherwise {@link UserRepository.LoadUserCallback#onError(HyperwalletErrors)} + * is called to further log or show error information + */ + interface LoadUserCallback { + + void onUserLoaded(@Nullable final HyperwalletUser user); + + void onError(@NonNull final HyperwalletErrors errors); + } +} diff --git a/ui/src/main/java/com/hyperwallet/android/ui/repository/UserRepositoryImpl.java b/ui/src/main/java/com/hyperwallet/android/ui/repository/UserRepositoryImpl.java new file mode 100644 index 000000000..561d4cf85 --- /dev/null +++ b/ui/src/main/java/com/hyperwallet/android/ui/repository/UserRepositoryImpl.java @@ -0,0 +1,71 @@ +/* + * Copyright 2018 Hyperwallet + * + * 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 com.hyperwallet.android.ui.repository; + +import android.os.Handler; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import com.hyperwallet.android.Hyperwallet; +import com.hyperwallet.android.exception.HyperwalletException; +import com.hyperwallet.android.listener.HyperwalletListener; +import com.hyperwallet.android.model.HyperwalletUser; + +public class UserRepositoryImpl implements UserRepository { + + private Handler mHandler = new Handler(); + private HyperwalletUser mUser; + + @VisibleForTesting + Hyperwallet getHyperwallet() { + return Hyperwallet.getDefault(); + } + + @Override + public void loadUser(@NonNull final LoadUserCallback callback) { + if (mUser == null) { + getHyperwallet().getUser(new HyperwalletListener() { + @Override + public void onSuccess(@Nullable HyperwalletUser result) { + mUser = result; + callback.onUserLoaded(mUser); + } + + @Override + public void onFailure(HyperwalletException exception) { + callback.onError(exception.getHyperwalletErrors()); + } + + @Override + public Handler getHandler() { + return mHandler; + } + }); + } else { + callback.onUserLoaded(mUser); + } + } + + @Override + public void refreshUser() { + mUser = null; + } +} diff --git a/ui/src/main/java/com/hyperwallet/android/ui/transfermethod/AddTransferMethodFragment.java b/ui/src/main/java/com/hyperwallet/android/ui/transfermethod/AddTransferMethodFragment.java index 131ec7e0e..28d977e7a 100644 --- a/ui/src/main/java/com/hyperwallet/android/ui/transfermethod/AddTransferMethodFragment.java +++ b/ui/src/main/java/com/hyperwallet/android/ui/transfermethod/AddTransferMethodFragment.java @@ -21,6 +21,7 @@ import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodFields.TYPE; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.BANK_ACCOUNT; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.BANK_CARD; +import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.PAYPAL_ACCOUNT; import android.app.Activity; import android.content.Context; @@ -48,6 +49,7 @@ import com.hyperwallet.android.model.HyperwalletBankCard; import com.hyperwallet.android.model.HyperwalletError; import com.hyperwallet.android.model.HyperwalletTransferMethod; +import com.hyperwallet.android.model.PayPalAccount; import com.hyperwallet.android.model.meta.Fee; import com.hyperwallet.android.model.meta.HyperwalletField; import com.hyperwallet.android.ui.HyperwalletLocalBroadcast; @@ -466,6 +468,12 @@ private void triggerSubmit() { mTransferMethod = new HyperwalletBankCard.Builder(). transferMethodCountry(mCountry).transferMethodCurrency(mCurrency).build(); break; + case PAYPAL_ACCOUNT: + mTransferMethod = new PayPalAccount.Builder() + .transferMethodCountry(mCountry) + .transferMethodCurrency(mCurrency) + .build(); + break; default: mTransferMethod = new HyperwalletTransferMethod(); mTransferMethod.setField(TRANSFER_METHOD_COUNTRY, mCountry); diff --git a/ui/src/main/java/com/hyperwallet/android/ui/transfermethod/TransferMethodUtils.java b/ui/src/main/java/com/hyperwallet/android/ui/transfermethod/TransferMethodUtils.java index 37444ba38..7eb96f386 100644 --- a/ui/src/main/java/com/hyperwallet/android/ui/transfermethod/TransferMethodUtils.java +++ b/ui/src/main/java/com/hyperwallet/android/ui/transfermethod/TransferMethodUtils.java @@ -20,6 +20,7 @@ import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.BANK_ACCOUNT; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.BANK_CARD; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.PAPER_CHECK; +import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.PAYPAL_ACCOUNT; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.PREPAID_CARD; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodTypes.WIRE_ACCOUNT; @@ -114,6 +115,9 @@ public static String getTransferMethodName(@NonNull final Context context, case WIRE_ACCOUNT: title = context.getString(R.string.wire_account); break; + case PAYPAL_ACCOUNT: + title = context.getString(R.string.paypal_account); + break; default: title = transferMethodType.toLowerCase(Locale.ROOT) + context.getString( R.string.not_translated_in_braces); diff --git a/ui/src/main/res/values/ids.xml b/ui/src/main/res/values/ids.xml index 64c8eb320..df6202824 100644 --- a/ui/src/main/res/values/ids.xml +++ b/ui/src/main/res/values/ids.xml @@ -12,4 +12,6 @@ + + \ No newline at end of file diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 4774946af..7e43ad98f 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -72,12 +72,14 @@ Wire Account Paper Check Prepaid Card + PayPal \uE000 \uE005 \uE00D \uE002 - \uE00E + \uE00A + \uE021 Search Hint Search Country diff --git a/ui/src/test/java/com/hyperwallet/android/ui/repository/TransferMethodRepositoryImplTest.java b/ui/src/test/java/com/hyperwallet/android/ui/repository/TransferMethodRepositoryImplTest.java index 3178ef652..77244db89 100644 --- a/ui/src/test/java/com/hyperwallet/android/ui/repository/TransferMethodRepositoryImplTest.java +++ b/ui/src/test/java/com/hyperwallet/android/ui/repository/TransferMethodRepositoryImplTest.java @@ -10,15 +10,15 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static com.hyperwallet.android.model.HyperwalletStatusTransition.StatusDefinition.ACTIVATED; import static com.hyperwallet.android.model.HyperwalletStatusTransition.StatusDefinition.DE_ACTIVATED; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodFields.BANK_ACCOUNT_ID; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodFields.BANK_NAME; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodFields.STATUS; +import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodFields.TOKEN; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodFields.TRANSFER_METHOD_COUNTRY; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodFields.TRANSFER_METHOD_CURRENCY; import static com.hyperwallet.android.model.HyperwalletTransferMethod.TransferMethodFields.TYPE; @@ -33,6 +33,7 @@ import com.hyperwallet.android.model.HyperwalletStatusTransition; import com.hyperwallet.android.model.HyperwalletTransferMethod; import com.hyperwallet.android.model.HyperwalletTransferMethodPagination; +import com.hyperwallet.android.model.PayPalAccount; import com.hyperwallet.android.model.paging.HyperwalletPageList; import org.junit.Before; @@ -59,6 +60,12 @@ public class TransferMethodRepositoryImplTest { @Mock private Hyperwallet mHyperwallet; + @Mock + private TransferMethodRepository.DeactivateTransferMethodCallback mDeactivateTransferMethodCallback; + @Mock + private TransferMethodRepository.LoadTransferMethodListCallback mLoadTransferMethodListCallback; + @Mock + private TransferMethodRepository.LoadTransferMethodCallback mLoadTransferMethodCallback; @Captor private ArgumentCaptor mErrorsArgumentCaptor; @Captor @@ -66,6 +73,8 @@ public class TransferMethodRepositoryImplTest { @Captor private ArgumentCaptor mBankCardArgumentCaptor; @Captor + private ArgumentCaptor mPayPalAccountArgumentCaptor; + @Captor private ArgumentCaptor mStatusTransitionArgumentCaptor; @Captor private ArgumentCaptor> mListTransferMethodCaptor; @@ -82,7 +91,7 @@ public void setup() { } @Test - public void testCreateTransferMethod_callsListenerWithBankAccountOnSuccess() { + public void testCreateTransferMethod_bankAccountWithSuccess() { HyperwalletBankAccount bankAccount = new HyperwalletBankAccount .Builder("CA", "CAD", "3423423432") .build(); @@ -100,14 +109,12 @@ public Object answer(InvocationOnMock invocation) { } }).when(mHyperwallet).createBankAccount(any(HyperwalletBankAccount.class), ArgumentMatchers.>any()); - TransferMethodRepository.LoadTransferMethodCallback mockCallback = mock( - TransferMethodRepository.LoadTransferMethodCallback.class); // test - mTransferMethodRepository.createTransferMethod(bankAccount, mockCallback); + mTransferMethodRepository.createTransferMethod(bankAccount, mLoadTransferMethodCallback); - verify(mockCallback).onTransferMethodLoaded(mBankAccountArgumentCaptor.capture()); - verify(mockCallback, never()).onError(any(HyperwalletErrors.class)); + verify(mLoadTransferMethodCallback).onTransferMethodLoaded(mBankAccountArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onError(any(HyperwalletErrors.class)); HyperwalletBankAccount transferMethod = mBankAccountArgumentCaptor.getValue(); assertThat(transferMethod, is(notNullValue())); @@ -119,7 +126,7 @@ public Object answer(InvocationOnMock invocation) { } @Test - public void testCreateTransferMethod_callsListenerWithErrorOnFailure() { + public void testCreateTransferMethod_bankAccountWithError() { HyperwalletBankAccount bankAccount = new HyperwalletBankAccount .Builder("US", "USD", "23432432") .build(); @@ -139,113 +146,86 @@ public Object answer(InvocationOnMock invocation) { } }).when(mHyperwallet).createBankAccount(any(HyperwalletBankAccount.class), ArgumentMatchers.>any()); - TransferMethodRepository.LoadTransferMethodCallback mockCallback = mock( - TransferMethodRepository.LoadTransferMethodCallback.class); // test - mTransferMethodRepository.createTransferMethod(bankAccount, mockCallback); + mTransferMethodRepository.createTransferMethod(bankAccount, mLoadTransferMethodCallback); - verify(mockCallback).onError(mErrorsArgumentCaptor.capture()); - verify(mockCallback, never()).onTransferMethodLoaded(any(HyperwalletTransferMethod.class)); + verify(mLoadTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onTransferMethodLoaded(any(HyperwalletTransferMethod.class)); assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(error)); } + @Test - public void testLoadTransferMethod_userContextHasBankAccount() { + public void testDeactivateTransferMethod_bankAccountWithSuccess() { HyperwalletBankAccount bankAccount = new HyperwalletBankAccount .Builder("CA", "CAD", "3423423432") + .token("trm-854c4ec1-9161-49d6-92e2-b8d15aa4bf56") .build(); - - List accounts = new ArrayList<>(); - accounts.add(bankAccount); - final HyperwalletPageList pageList = new HyperwalletPageList<>(accounts); - - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) { - HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; - listener.onSuccess(pageList); - return listener; - } - }).when(mHyperwallet).listTransferMethods((HyperwalletTransferMethodPagination) any(), - ArgumentMatchers.>>any()); - TransferMethodRepository.LoadTransferMethodListCallback mockCallback = mock( - TransferMethodRepository.LoadTransferMethodListCallback.class); - - // test - mTransferMethodRepository.loadTransferMethod(mockCallback); - - verify(mockCallback).onTransferMethodListLoaded(mListTransferMethodCaptor.capture()); - verify(mockCallback, never()).onError(any(HyperwalletErrors.class)); - - List transferMethods = mListTransferMethodCaptor.getValue(); - assertThat(transferMethods, hasSize(1)); - assertThat(transferMethods.get(0).getField(TRANSFER_METHOD_COUNTRY), is("CA")); - assertThat(transferMethods.get(0).getField(TRANSFER_METHOD_CURRENCY), is("CAD")); - assertThat(transferMethods.get(0).getField(BANK_ACCOUNT_ID), is("3423423432")); - } - - @Test - public void testLoadTransferMethod_userContextEmptyAccounts() { - + bankAccount.setField(STATUS, HyperwalletStatusTransition.StatusDefinition.ACTIVATED); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { - HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; - listener.onSuccess(null); + HyperwalletStatusTransition statusTransition = new HyperwalletStatusTransition(DE_ACTIVATED); + statusTransition.setNotes("Closing this account."); + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[2]; + listener.onSuccess(statusTransition); return listener; } - }).when(mHyperwallet).listTransferMethods((HyperwalletTransferMethodPagination) any(), - ArgumentMatchers.>>any()); - TransferMethodRepository.LoadTransferMethodListCallback mockCallback = mock( - TransferMethodRepository.LoadTransferMethodListCallback.class); + }).when(mHyperwallet).deactivateBankAccount(anyString(), ArgumentMatchers.isNull(), + ArgumentMatchers.>any()); // test - mTransferMethodRepository.loadTransferMethod(mockCallback); + mTransferMethodRepository.deactivateTransferMethod(bankAccount, mDeactivateTransferMethodCallback); - verify(mockCallback).onTransferMethodListLoaded(mListTransferMethodCaptor.capture()); - verify(mockCallback, never()).onError(any(HyperwalletErrors.class)); + verify(mDeactivateTransferMethodCallback).onTransferMethodDeactivated( + mStatusTransitionArgumentCaptor.capture()); + verify(mDeactivateTransferMethodCallback, never()).onError(any(HyperwalletErrors.class)); - List transferMethods = mListTransferMethodCaptor.getValue(); - assertThat(transferMethods, is(nullValue())); + HyperwalletStatusTransition statusTransition = mStatusTransitionArgumentCaptor.getValue(); + assertThat(statusTransition, is(notNullValue())); + assertThat(statusTransition.getTransition(), is(DE_ACTIVATED)); + assertThat(statusTransition.getNotes(), is("Closing this account.")); } @Test - public void testLoadTransferMethod_hasErrors() { - + public void testDeactivateTransferMethod_bankAccountWithError() { + HyperwalletBankAccount bankAccount = new HyperwalletBankAccount + .Builder("CA", "CAD", "3423423432") + .token("trm-854c4ec1-9161-49d6-92e2-b8d15aa4bf56") + .build(); + bankAccount.setField(STATUS, HyperwalletStatusTransition.StatusDefinition.ACTIVATED); final HyperwalletError error = new HyperwalletError("test message", "TEST_CODE"); - doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { - HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[2]; List errorList = new ArrayList<>(); errorList.add(error); HyperwalletErrors errors = new HyperwalletErrors(errorList); listener.onFailure(new HyperwalletException(errors)); return listener; } - }).when(mHyperwallet).listTransferMethods((HyperwalletTransferMethodPagination) any(), - ArgumentMatchers.>>any()); - TransferMethodRepository.LoadTransferMethodListCallback mockCallback = mock( - TransferMethodRepository.LoadTransferMethodListCallback.class); + }).when(mHyperwallet).deactivateBankAccount(anyString(), ArgumentMatchers.isNull(), + ArgumentMatchers.>any()); // test - mTransferMethodRepository.loadTransferMethod(mockCallback); + mTransferMethodRepository.deactivateTransferMethod(bankAccount, mDeactivateTransferMethodCallback); - verify(mockCallback, never()).onTransferMethodListLoaded(ArgumentMatchers.anyList()); - verify(mockCallback).onError(mErrorsArgumentCaptor.capture()); + verify(mDeactivateTransferMethodCallback, never()).onTransferMethodDeactivated( + any(HyperwalletStatusTransition.class)); + verify(mDeactivateTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(error)); } @Test - public void testDeactivateBankAccount_successfullyDeactivatedBankAccount() { - HyperwalletBankAccount bankAccount = new HyperwalletBankAccount - .Builder("CA", "CAD", "3423423432") - .token("some token") + public void testDeactivateTransferMethod_bankCardWithSuccess() { + HyperwalletBankCard bankCard = new HyperwalletBankCard + .Builder("CA", "CAD", "1232345456784", "2019-05", "234") + .token("trm-854c4ec1-9161-49d6-92e2-b8d15aa4bf56") .build(); - bankAccount.setField(STATUS, HyperwalletStatusTransition.StatusDefinition.ACTIVATED); + bankCard.setField(STATUS, HyperwalletStatusTransition.StatusDefinition.ACTIVATED); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { @@ -255,17 +235,15 @@ public Object answer(InvocationOnMock invocation) { listener.onSuccess(statusTransition); return listener; } - }).when(mHyperwallet).deactivateBankAccount(anyString(), ArgumentMatchers.isNull(), + }).when(mHyperwallet).deactivateBankCard(anyString(), ArgumentMatchers.isNull(), ArgumentMatchers.>any()); - TransferMethodRepository.DeactivateTransferMethodCallback mockCallback = mock( - TransferMethodRepository.DeactivateTransferMethodCallback.class); - // test - mTransferMethodRepository.deactivateTransferMethod(bankAccount, mockCallback); + mTransferMethodRepository.deactivateTransferMethod(bankCard, mDeactivateTransferMethodCallback); - verify(mockCallback).onTransferMethodDeactivated(mStatusTransitionArgumentCaptor.capture()); - verify(mockCallback, never()).onError(any(HyperwalletErrors.class)); + verify(mDeactivateTransferMethodCallback).onTransferMethodDeactivated( + mStatusTransitionArgumentCaptor.capture()); + verify(mDeactivateTransferMethodCallback, never()).onError(any(HyperwalletErrors.class)); HyperwalletStatusTransition statusTransition = mStatusTransitionArgumentCaptor.getValue(); assertThat(statusTransition, is(notNullValue())); @@ -274,15 +252,12 @@ public Object answer(InvocationOnMock invocation) { } @Test - public void testDeactivateBankAccount_hasErrorOnDeactivate() { - TransferMethodRepositoryImpl mTransferMethodRepository = spy(TransferMethodRepositoryImpl.class); - doReturn(mHyperwallet).when(mTransferMethodRepository).getHyperwallet(); - - HyperwalletBankAccount bankAccount = new HyperwalletBankAccount - .Builder("CA", "CAD", "3423423432") - .token("some token") + public void testDeactivateTransferMethod_bankCardWithError() { + HyperwalletBankCard bankCard = new HyperwalletBankCard + .Builder("CA", "CAD", "1232345456784", "2019-05", "234") + .token("trm-854c4ec1-9161-49d6-92e2-b8d15aa4bf56") .build(); - bankAccount.setField(STATUS, HyperwalletStatusTransition.StatusDefinition.ACTIVATED); + bankCard.setField(STATUS, HyperwalletStatusTransition.StatusDefinition.ACTIVATED); final HyperwalletError error = new HyperwalletError("test message", "TEST_CODE"); doAnswer(new Answer() { @Override @@ -294,27 +269,23 @@ public Object answer(InvocationOnMock invocation) { listener.onFailure(new HyperwalletException(errors)); return listener; } - }).when(mHyperwallet).deactivateBankAccount(anyString(), ArgumentMatchers.isNull(), + }).when(mHyperwallet).deactivateBankCard(anyString(), ArgumentMatchers.isNull(), ArgumentMatchers.>any()); - TransferMethodRepository.DeactivateTransferMethodCallback mockCallback = mock( - TransferMethodRepository.DeactivateTransferMethodCallback.class); // test - mTransferMethodRepository.deactivateTransferMethod(bankAccount, mockCallback); + mTransferMethodRepository.deactivateTransferMethod(bankCard, mDeactivateTransferMethodCallback); - verify(mockCallback, never()).onTransferMethodDeactivated(any(HyperwalletStatusTransition.class)); - verify(mockCallback).onError(mErrorsArgumentCaptor.capture()); + verify(mDeactivateTransferMethodCallback, never()).onTransferMethodDeactivated( + any(HyperwalletStatusTransition.class)); + verify(mDeactivateTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(error)); } @Test - public void testDeactivateBankCard_successfullyDeactivatedBankCard() { - HyperwalletBankCard bankCard = new HyperwalletBankCard - .Builder("CA", "CAD", "1232345456784", "2019-05", "234") - .token("some token") - .build(); - bankCard.setField(STATUS, HyperwalletStatusTransition.StatusDefinition.ACTIVATED); + public void testDeactivateTransferMethod_payPalAccountWithSuccess() { + PayPalAccount payPalAccount = new PayPalAccount.Builder("US", "US", "jsmith4@hyperwallet.com") + .token("trm-854c4ec1-9161-49d6-92e2-b8d15aa4bf56").build(); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { @@ -324,17 +295,15 @@ public Object answer(InvocationOnMock invocation) { listener.onSuccess(statusTransition); return listener; } - }).when(mHyperwallet).deactivateBankCard(anyString(), ArgumentMatchers.isNull(), + }).when(mHyperwallet).deactivatePayPalAccount(anyString(), ArgumentMatchers.isNull(), ArgumentMatchers.>any()); - TransferMethodRepository.DeactivateTransferMethodCallback mockCallback = mock( - TransferMethodRepository.DeactivateTransferMethodCallback.class); - // test - mTransferMethodRepository.deactivateTransferMethod(bankCard, mockCallback); + mTransferMethodRepository.deactivateTransferMethod(payPalAccount, mDeactivateTransferMethodCallback); - verify(mockCallback).onTransferMethodDeactivated(mStatusTransitionArgumentCaptor.capture()); - verify(mockCallback, never()).onError(any(HyperwalletErrors.class)); + verify(mDeactivateTransferMethodCallback).onTransferMethodDeactivated( + mStatusTransitionArgumentCaptor.capture()); + verify(mDeactivateTransferMethodCallback, never()).onError(any(HyperwalletErrors.class)); HyperwalletStatusTransition statusTransition = mStatusTransitionArgumentCaptor.getValue(); assertThat(statusTransition, is(notNullValue())); @@ -343,15 +312,10 @@ public Object answer(InvocationOnMock invocation) { } @Test - public void testDeactivateBankCard_hasErrorOnDeactivate() { - TransferMethodRepositoryImpl mTransferMethodRepository = spy(TransferMethodRepositoryImpl.class); - doReturn(mHyperwallet).when(mTransferMethodRepository).getHyperwallet(); - - HyperwalletBankCard bankCard = new HyperwalletBankCard - .Builder("CA", "CAD", "1232345456784", "2019-05", "234") - .token("some token") - .build(); - bankCard.setField(STATUS, HyperwalletStatusTransition.StatusDefinition.ACTIVATED); + public void testDeactivateTransferMethod_payPalAccountWithError() { + PayPalAccount payPalAccount = new PayPalAccount.Builder("US", "US", "jsmith4@hyperwallet.com") + .token("trm-854c4ec1-9161-49d6-92e2-b8d15aa4bf56").build(); + payPalAccount.setField(STATUS, HyperwalletStatusTransition.StatusDefinition.ACTIVATED); final HyperwalletError error = new HyperwalletError("test message", "TEST_CODE"); doAnswer(new Answer() { @Override @@ -363,23 +327,22 @@ public Object answer(InvocationOnMock invocation) { listener.onFailure(new HyperwalletException(errors)); return listener; } - }).when(mHyperwallet).deactivateBankCard(anyString(), ArgumentMatchers.isNull(), + }).when(mHyperwallet).deactivatePayPalAccount(anyString(), ArgumentMatchers.isNull(), ArgumentMatchers.>any()); - TransferMethodRepository.DeactivateTransferMethodCallback mockCallback = mock( - TransferMethodRepository.DeactivateTransferMethodCallback.class); // test - mTransferMethodRepository.deactivateTransferMethod(bankCard, mockCallback); + mTransferMethodRepository.deactivateTransferMethod(payPalAccount, mDeactivateTransferMethodCallback); - verify(mockCallback, never()).onTransferMethodDeactivated(any(HyperwalletStatusTransition.class)); - verify(mockCallback).onError(mErrorsArgumentCaptor.capture()); + verify(mDeactivateTransferMethodCallback, never()).onTransferMethodDeactivated( + any(HyperwalletStatusTransition.class)); + verify(mDeactivateTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(error)); } @Test - public void testCreateTransferMethod_callsListenerWithBankCardOnSuccess() { + public void testCreateTransferMethod_bankCardWithSuccess() { HyperwalletBankCard bankCard = new HyperwalletBankCard .Builder("CA", "CAD", "1232345456784", "2019-05", "234") .build(); @@ -398,14 +361,12 @@ public Object answer(InvocationOnMock invocation) { } }).when(mHyperwallet).createBankCard(any(HyperwalletBankCard.class), ArgumentMatchers.>any()); - TransferMethodRepository.LoadTransferMethodCallback mockCallback = mock( - TransferMethodRepository.LoadTransferMethodCallback.class); // test - mTransferMethodRepository.createTransferMethod(bankCard, mockCallback); + mTransferMethodRepository.createTransferMethod(bankCard, mLoadTransferMethodCallback); - verify(mockCallback).onTransferMethodLoaded(mBankCardArgumentCaptor.capture()); - verify(mockCallback, never()).onError(any(HyperwalletErrors.class)); + verify(mLoadTransferMethodCallback).onTransferMethodLoaded(mBankCardArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onError(any(HyperwalletErrors.class)); HyperwalletBankCard transferMethod = mBankCardArgumentCaptor.getValue(); assertThat(transferMethod, is(notNullValue())); @@ -415,12 +376,11 @@ public Object answer(InvocationOnMock invocation) { } @Test - public void testCreateTransferMethod_callsListenerWithErrorOnBankCardFailure() { + public void testCreateTransferMethod_bankCardWithError() { HyperwalletBankCard bankCard = new HyperwalletBankCard .Builder("CA", "CAD", "1232345456784", "2019-05", "234") .build(); - final HyperwalletError error = new HyperwalletError("bank card test message", "BANK_CARD_TEST_CODE"); doAnswer(new Answer() { @@ -436,14 +396,166 @@ public Object answer(InvocationOnMock invocation) { } }).when(mHyperwallet).createBankCard(any(HyperwalletBankCard.class), ArgumentMatchers.>any()); - TransferMethodRepository.LoadTransferMethodCallback mockCallback = mock( - TransferMethodRepository.LoadTransferMethodCallback.class); // test - mTransferMethodRepository.createTransferMethod(bankCard, mockCallback); + mTransferMethodRepository.createTransferMethod(bankCard, mLoadTransferMethodCallback); - verify(mockCallback).onError(mErrorsArgumentCaptor.capture()); - verify(mockCallback, never()).onTransferMethodLoaded(any(HyperwalletTransferMethod.class)); + verify(mLoadTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onTransferMethodLoaded(any(HyperwalletTransferMethod.class)); assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(error)); } + + + @Test + public void testLoadTransferMethod_returnsBankAccount() { + HyperwalletBankAccount bankAccount = new HyperwalletBankAccount + .Builder("CA", "CAD", "3423423432") + .build(); + + List accounts = new ArrayList<>(); + accounts.add(bankAccount); + final HyperwalletPageList pageList = new HyperwalletPageList<>(accounts); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + listener.onSuccess(pageList); + return listener; + } + }).when(mHyperwallet).listTransferMethods((HyperwalletTransferMethodPagination) any(), + ArgumentMatchers.>>any()); + + // test + mTransferMethodRepository.loadTransferMethod(mLoadTransferMethodListCallback); + + verify(mLoadTransferMethodListCallback).onTransferMethodListLoaded(mListTransferMethodCaptor.capture()); + verify(mLoadTransferMethodListCallback, never()).onError(any(HyperwalletErrors.class)); + + List transferMethods = mListTransferMethodCaptor.getValue(); + assertThat(transferMethods, hasSize(1)); + assertThat(transferMethods.get(0).getField(TRANSFER_METHOD_COUNTRY), is("CA")); + assertThat(transferMethods.get(0).getField(TRANSFER_METHOD_CURRENCY), is("CAD")); + assertThat(transferMethods.get(0).getField(BANK_ACCOUNT_ID), is("3423423432")); + } + + @Test + public void testLoadTransferMethod_returnsNoAccounts() { + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + listener.onSuccess(null); + return listener; + } + }).when(mHyperwallet).listTransferMethods((HyperwalletTransferMethodPagination) any(), + ArgumentMatchers.>>any()); + + // test + mTransferMethodRepository.loadTransferMethod(mLoadTransferMethodListCallback); + + verify(mLoadTransferMethodListCallback).onTransferMethodListLoaded(mListTransferMethodCaptor.capture()); + verify(mLoadTransferMethodListCallback, never()).onError(any(HyperwalletErrors.class)); + + List transferMethods = mListTransferMethodCaptor.getValue(); + assertThat(transferMethods, is(nullValue())); + } + + @Test + public void testLoadTransferMethod_withError() { + final HyperwalletError error = new HyperwalletError("test message", "TEST_CODE"); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + List errorList = new ArrayList<>(); + errorList.add(error); + HyperwalletErrors errors = new HyperwalletErrors(errorList); + listener.onFailure(new HyperwalletException(errors)); + return listener; + } + }).when(mHyperwallet).listTransferMethods((HyperwalletTransferMethodPagination) any(), + ArgumentMatchers.>>any()); + + // test + mTransferMethodRepository.loadTransferMethod(mLoadTransferMethodListCallback); + + verify(mLoadTransferMethodListCallback, never()).onTransferMethodListLoaded( + ArgumentMatchers.anyList()); + verify(mLoadTransferMethodListCallback).onError(mErrorsArgumentCaptor.capture()); + + assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(error)); + } + + @Test + public void testCreateTransferMethod_payPalAccountWithSuccess() { + // prepare + final PayPalAccount returnedPayPalAccount = new PayPalAccount.Builder() + .transferMethodCurrency("USD") + .transferMethodCountry("US") + .email("money@mail.com") + .token("trm-token-1342242314") + .build(); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + returnedPayPalAccount.setField(STATUS, ACTIVATED); + listener.onSuccess(returnedPayPalAccount); + return listener; + } + }).when(mHyperwallet).createPayPalAccount(any(PayPalAccount.class), + ArgumentMatchers.>any()); + + PayPalAccount parameter = new PayPalAccount.Builder().build(); + + // test + mTransferMethodRepository.createTransferMethod(parameter, mLoadTransferMethodCallback); + + // verify + verify(mLoadTransferMethodCallback).onTransferMethodLoaded(mPayPalAccountArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onError(any(HyperwalletErrors.class)); + + // assert + PayPalAccount payPalAccount = mPayPalAccountArgumentCaptor.getValue(); + assertThat(payPalAccount, is(notNullValue())); + assertThat(payPalAccount.getCountry(), is("US")); + assertThat(payPalAccount.getCurrency(), is("USD")); + assertThat(payPalAccount.getEmail(), is("money@mail.com")); + assertThat(payPalAccount.getField(STATUS), is(ACTIVATED)); + assertThat(payPalAccount.getField(TOKEN), is("trm-token-1342242314")); + } + + @Test + public void testCreateTransferMethod_payPalAccountWithError() { + // prepare + final HyperwalletError returnedError = new HyperwalletError("PayPal test message", "PAYPAL_TEST_CODE"); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + + List errorList = new ArrayList<>(); + errorList.add(returnedError); + + listener.onFailure(new HyperwalletException(new HyperwalletErrors(errorList))); + return listener; + } + }).when(mHyperwallet).createPayPalAccount(any(PayPalAccount.class), + ArgumentMatchers.>any()); + PayPalAccount parameter = new PayPalAccount.Builder().build(); + + // test + mTransferMethodRepository.createTransferMethod(parameter, mLoadTransferMethodCallback); + + // verify + verify(mLoadTransferMethodCallback, never()).onTransferMethodLoaded(any(HyperwalletTransferMethod.class)); + verify(mLoadTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); + + // assert + assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(returnedError)); + } } \ No newline at end of file diff --git a/ui/src/test/java/com/hyperwallet/android/ui/repository/UserRepositoryImplTest.java b/ui/src/test/java/com/hyperwallet/android/ui/repository/UserRepositoryImplTest.java new file mode 100644 index 000000000..5d97211f8 --- /dev/null +++ b/ui/src/test/java/com/hyperwallet/android/ui/repository/UserRepositoryImplTest.java @@ -0,0 +1,200 @@ +package com.hyperwallet.android.ui.repository; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import static com.hyperwallet.android.model.HyperwalletUser.ProfileTypes.INDIVIDUAL; +import static com.hyperwallet.android.model.HyperwalletUser.UserStatuses.PRE_ACTIVATED; +import static com.hyperwallet.android.model.HyperwalletUser.VerificationStatuses.NOT_REQUIRED; + +import com.hyperwallet.android.Hyperwallet; +import com.hyperwallet.android.exception.HyperwalletException; +import com.hyperwallet.android.listener.HyperwalletListener; +import com.hyperwallet.android.model.HyperwalletError; +import com.hyperwallet.android.model.HyperwalletErrors; +import com.hyperwallet.android.model.HyperwalletUser; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class UserRepositoryImplTest { + @Mock + private Hyperwallet mHyperwallet; + @Captor + private ArgumentCaptor mErrorCaptor; + @Captor + private ArgumentCaptor mUserCaptor; + @Rule + public MockitoRule mMockito = MockitoJUnit.rule(); + @Spy + UserRepositoryImpl mUserRepository; + + @Before + public void setup() { + doReturn(mHyperwallet).when(mUserRepository).getHyperwallet(); + } + + @Test + public void testLoadUser_returnsUser() { + HyperwalletUser.Builder builder = new HyperwalletUser.Builder(); + final HyperwalletUser user = builder + .token("usr-f9154016-94e8-4686-a840-075688ac07b5") + .status(PRE_ACTIVATED) + .verificationStatus(NOT_REQUIRED) + .createdOn("2017-10-30T22:15:45") + .clientUserId("123456") + .profileType(INDIVIDUAL) + .firstName("Some") + .lastName("Guy") + .dateOfBirth("1991-01-01") + .email("testUser@hyperwallet.com") + .addressLine1("575 Market Street") + .city("San Francisco") + .stateProvince("CA") + .country("US") + .postalCode("94105") + .language("en") + .programToken("prg-83836cdf-2ce2-4696-8bc5-f1b86077238c") + .build(); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[0]; + listener.onSuccess(user); + return listener; + } + }).when(mHyperwallet).getUser(ArgumentMatchers.>any()); + UserRepository.LoadUserCallback mockCallback = mock(UserRepository.LoadUserCallback.class); + + mUserRepository.loadUser(mockCallback); + + verify(mockCallback).onUserLoaded(mUserCaptor.capture()); + verify(mockCallback, never()).onError(any(HyperwalletErrors.class)); + + HyperwalletUser resultUser = mUserCaptor.getValue(); + assertThat(resultUser.getToken(), is("usr-f9154016-94e8-4686-a840-075688ac07b5")); + assertThat(resultUser.getStatus(), is(PRE_ACTIVATED)); + assertThat(resultUser.getVerificationStatus(), is(NOT_REQUIRED)); + assertThat(resultUser.getCreatedOn(), is("2017-10-30T22:15:45")); + assertThat(resultUser.getClientUserId(), is("123456")); + assertThat(resultUser.getProfileType(), is(INDIVIDUAL)); + assertThat(resultUser.getFirstName(), is("Some")); + assertThat(resultUser.getLastName(), is("Guy")); + assertThat(resultUser.getDateOfBirth(), is("1991-01-01")); + assertThat(resultUser.getEmail(), is("testUser@hyperwallet.com")); + assertThat(resultUser.getAddressLine1(), is("575 Market Street")); + assertThat(resultUser.getCity(), is("San Francisco")); + assertThat(resultUser.getStateProvince(), is("CA")); + assertThat(resultUser.getCountry(), is("US")); + assertThat(resultUser.getPostalCode(), is("94105")); + assertThat(resultUser.getLanguage(), is("en")); + assertThat(resultUser.getProgramToken(), is("prg-83836cdf-2ce2-4696-8bc5-f1b86077238c")); + } + + @Test + public void testLoadUser_returnsNoUser() { + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[0]; + listener.onSuccess(null); + return listener; + } + }).when(mHyperwallet).getUser(ArgumentMatchers.>any()); + UserRepository.LoadUserCallback mockCallback = mock(UserRepository.LoadUserCallback.class); + + mUserRepository.loadUser(mockCallback); + + verify(mockCallback).onUserLoaded(mUserCaptor.capture()); + verify(mockCallback, never()).onError(any(HyperwalletErrors.class)); + + HyperwalletUser user = mUserCaptor.getValue(); + assertThat(user, is(nullValue())); + } + + + @Test + public void testLoadUser_withError() { + + final HyperwalletError error = new HyperwalletError("test message", "TEST_CODE"); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[0]; + List errorList = new ArrayList<>(); + errorList.add(error); + HyperwalletErrors errors = new HyperwalletErrors(errorList); + listener.onFailure(new HyperwalletException(errors)); + return listener; + } + }).when(mHyperwallet).getUser(ArgumentMatchers.>any()); + UserRepository.LoadUserCallback mockCallback = mock( + UserRepository.LoadUserCallback.class); + + mUserRepository.loadUser(mockCallback); + + verify(mockCallback, never()).onUserLoaded(ArgumentMatchers.any()); + verify(mockCallback).onError(mErrorCaptor.capture()); + + assertThat(mErrorCaptor.getValue().getErrors(), hasItem(error)); + } + + @Test + public void testRefreshUser_verifyHyperwalletCallGetUser() { + HyperwalletUser.Builder builder = new HyperwalletUser.Builder(); + final HyperwalletUser user = builder + .token("usr-f9154016-94e8-4686-a840-075688ac07b5") + .profileType(INDIVIDUAL) + .build(); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[0]; + listener.onSuccess(user); + return listener; + } + }).when(mHyperwallet).getUser(ArgumentMatchers.>any()); + UserRepository.LoadUserCallback mockCallback = mock(UserRepository.LoadUserCallback.class); + + mUserRepository.loadUser(mockCallback); + + verify(mHyperwallet).getUser(ArgumentMatchers.>any()); + + mUserRepository.loadUser(mockCallback); + verify(mHyperwallet).getUser(ArgumentMatchers.>any()); + + mUserRepository.refreshUser(); + mUserRepository.loadUser(mockCallback); + verify(mHyperwallet, times(2)).getUser(ArgumentMatchers.>any()); + + } +} diff --git a/ui/src/test/resources/paypal_invalid_email_response.json b/ui/src/test/resources/paypal_invalid_email_response.json new file mode 100644 index 000000000..bb112d852 --- /dev/null +++ b/ui/src/test/resources/paypal_invalid_email_response.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "message": "PayPal transfer method email address should be same as profile email address.", + "code": "CONSTRAINT_VIOLATIONS" + } + ] +} \ No newline at end of file diff --git a/ui/src/test/resources/paypal_response.json b/ui/src/test/resources/paypal_response.json new file mode 100644 index 000000000..e86bbde93 --- /dev/null +++ b/ui/src/test/resources/paypal_response.json @@ -0,0 +1,17 @@ +{ + "token": "trm-6a29ddd5-6913-479e-a60c-24dc6bf14cd4", + "type": "PAYPAL_ACCOUNT", + "status": "ACTIVATED", + "createdOn": "2019-05-08T00:25:23", + "transferMethodCountry": "US", + "transferMethodCurrency": "USD", + "email": "sunshine.carreiro@hyperwallet.com", + "links": [ + { + "params": { + "rel": "self" + }, + "href": "https://localhost:8181/rest/v3/users/usr-d702cfa0-7cb1-491d-8f25-9b896003e872/paypal-accounts/trm-6a29ddd5-6913-479e-a60c-24dc6bf14cd4" + } + ] +} \ No newline at end of file diff --git a/ui/src/test/resources/successful_tmc_paypal_fields_response.json b/ui/src/test/resources/successful_tmc_paypal_fields_response.json new file mode 100644 index 000000000..06ec0fe45 --- /dev/null +++ b/ui/src/test/resources/successful_tmc_paypal_fields_response.json @@ -0,0 +1,46 @@ +{ + "data": { + "transferMethodConfigurations": { + "nodes": [ + { + "countries": [ + "US" + ], + "currencies": [ + "USD" + ], + "transferMethodType": "PAYPAL_ACCOUNT", + "profile": "INDIVIDUAL", + "processingTime": "IMMEDIATE", + "fees": { + "nodes": [ + { + "transferMethodType": "PAYPAL_ACCOUNT", + "country": "US", + "currency": "USD", + "feeRateType": "FLAT", + "value": "0.25" + } + ] + }, + "fields": [ + { + "category": "ACCOUNT", + "dataType": "TEXT", + "isRequired": true, + "label": "Email", + "name": "email", + "placeholder": "", + "regularExpression": "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$", + "validationMessage": { + "length": "", + "pattern": "accountNumber is invalid", + "empty": "You must provide a value for this field" + } + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/ui/src/test/resources/transfer_method_list_response.json b/ui/src/test/resources/transfer_method_list_response.json index a83276744..221efcdc8 100644 --- a/ui/src/test/resources/transfer_method_list_response.json +++ b/ui/src/test/resources/transfer_method_list_response.json @@ -146,6 +146,23 @@ "href": "https://localhost:8181/rest/v3/users/usr-a51c7522-ccba-4bcf-a6a7-bc59dae8f9b0/bank-accounts/trm-13141cde-56a1-4bde-ab82-534fde4d1cdb" } ] + }, + { + "token": "trm-f3f3d205-b7fb-4292-bd40-fbe814c7efd3", + "type": "PAYPAL_ACCOUNT", + "status": "ACTIVATED", + "createdOn": "2019-05-08T20:28:38", + "transferMethodCountry": "US", + "transferMethodCurrency": "USD", + "email": "honey.thigpen@ukbuilder.com", + "links": [ + { + "params": { + "rel": "self" + }, + "href": "https://localhost:8181/rest/v3/users/usr-fa76a738-f43d-48b9-9a7a-7048d44a5d2d/transfer-methods/trm-f3f3d205-b7fb-4292-bd40-fbe814c7efd3" + } + ] } ], "links": [ diff --git a/ui/src/test/resources/transfer_method_list_single_paypal_account_response.json b/ui/src/test/resources/transfer_method_list_single_paypal_account_response.json new file mode 100644 index 000000000..14837f4d3 --- /dev/null +++ b/ui/src/test/resources/transfer_method_list_single_paypal_account_response.json @@ -0,0 +1,32 @@ +{ + "count": 1, + "offset": 0, + "limit": 10, + "data": [ + { + "token": "trm-f3f3d205-b7fb-4292-bd40-fbe814c7efd3", + "type": "PAYPAL_ACCOUNT", + "status": "ACTIVATED", + "createdOn": "2019-05-08T20:28:38", + "transferMethodCountry": "US", + "transferMethodCurrency": "USD", + "email": "honey.thigpen@ukbuilder.com", + "links": [ + { + "params": { + "rel": "self" + }, + "href": "https://localhost:8181/rest/v3/users/usr-fa76a738-f43d-48b9-9a7a-7048d44a5d2d/transfer-methods/trm-f3f3d205-b7fb-4292-bd40-fbe814c7efd3" + } + ] + } + ], + "links": [ + { + "params": { + "rel": "self" + }, + "href": "https://localhost:8181/rest/v3/users/usr-fa76a738-f43d-48b9-9a7a-7048d44a5d2d/transfer-methods?offset=0&limit=10&sortBy=-createdOn&status=ACTIVATED" + } + ] +} \ No newline at end of file