diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java index 35b332fb6b..fb0fe6b014 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java @@ -128,6 +128,7 @@ public void onBrowserSwitchResult(@NonNull SEPADirectDebitBrowserSwitchResult se if (browserSwitchResult == null) { callback.onResult(null, new BraintreeException("An unexpected error occurred.")); + return; } int result = browserSwitchResult.getStatus(); diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java index 00094c44b5..442487e970 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java @@ -16,5 +16,5 @@ public interface SEPADirectDebitFlowStartedCallback { * {@link SEPADirectDebitLauncher#launch(FragmentActivity, SEPADirectDebitResponse)} * @param error an exception that occurred while initiating the SEPA transaction */ - void onResult(SEPADirectDebitResponse sepaDirectDebitResponse, @Nullable Exception error); + void onResult(@Nullable SEPADirectDebitResponse sepaDirectDebitResponse, @Nullable Exception error); } diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java index 76184b8946..6e4ddf15c8 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java @@ -1,5 +1,6 @@ package com.braintreepayments.api; +import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; /** @@ -31,7 +32,7 @@ BrowserSwitchOptions getBrowserSwitchOptions() { * * @return {@link SEPADirectDebitNonce} */ - public SEPADirectDebitNonce getNonce() { + public @Nullable SEPADirectDebitNonce getNonce() { return this.sepaDirectDebitNonce; } } diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java index 3639f7cee2..feeea7aefa 100644 --- a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java +++ b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java @@ -1,12 +1,14 @@ package com.braintreepayments.api; -import static junit.framework.TestCase.assertSame; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.doThrow; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -14,9 +16,7 @@ import android.net.Uri; -import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; -import androidx.lifecycle.Lifecycle; import org.json.JSONException; import org.json.JSONObject; @@ -29,21 +29,17 @@ @RunWith(RobolectricTestRunner.class) public class SEPADirectDebitClientUnitTest { - private FragmentActivity activity; - private Lifecycle lifecycle; private BraintreeClient braintreeClient; - private SEPADirectDebitListener listener; private CreateMandateResult createMandateResult; private SEPADirectDebitRequest sepaDirectDebitRequest; + private SEPADirectDebitBrowserSwitchResultCallback sepaBrowserSwitchResultCallback; + private SEPADirectDebitFlowStartedCallback sepaFlowStartedCallback; @Before public void beforeEach() { - activity = mock(FragmentActivity.class); - lifecycle = mock(Lifecycle.class); braintreeClient = new MockBraintreeClientBuilder() .returnUrlScheme("com.example") .build(); - listener = mock(SEPADirectDebitListener.class); createMandateResult = new CreateMandateResult( "http://www.example.com", @@ -53,92 +49,34 @@ public void beforeEach() { "ONE_OFF" ); sepaDirectDebitRequest = new SEPADirectDebitRequest(); - } - - @Test - public void constructor_setsLifecycleObserver() { - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); - BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(SEPADirectDebitLifecycleObserver.class); - verify(lifecycle).addObserver(captor.capture()); - - SEPADirectDebitLifecycleObserver observer = captor.getValue(); - assertSame(sut, observer.sepaDirectDebitClient); - } - - @Test - public void constructor_withFragment_passesFragmentLifecycleAndActivityToObserver() { - BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - - Fragment fragment = mock(Fragment.class); - when(fragment.getActivity()).thenReturn(activity); - when(fragment.getLifecycle()).thenReturn(lifecycle); - - SEPADirectDebitClient sut = new SEPADirectDebitClient(fragment, braintreeClient); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(SEPADirectDebitLifecycleObserver.class); - verify(lifecycle).addObserver(captor.capture()); - SEPADirectDebitLifecycleObserver observer = captor.getValue(); - assertSame(sut, observer.sepaDirectDebitClient); + sepaBrowserSwitchResultCallback = mock(SEPADirectDebitBrowserSwitchResultCallback.class); + sepaFlowStartedCallback = mock(SEPADirectDebitFlowStartedCallback.class); } @Test - public void constructor_withFragmentActivity_passesActivityLifecycleAndActivityToObserver() { - BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - - FragmentActivity activity = mock(FragmentActivity.class); - when(activity.getLifecycle()).thenReturn(lifecycle); - - SEPADirectDebitClient sut = new SEPADirectDebitClient(activity, braintreeClient); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(SEPADirectDebitLifecycleObserver.class); - verify(lifecycle).addObserver(captor.capture()); - - SEPADirectDebitLifecycleObserver observer = captor.getValue(); - assertSame(sut, observer.sepaDirectDebitClient); - } - - @Test - public void constructor_withoutFragmentOrActivity_doesNotSetObserver() { - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); - BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(null, null, braintreeClient, sepaDirectDebitApi); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(SEPADirectDebitLifecycleObserver.class); - verify(lifecycle, never()).addObserver(captor.capture()); - } - - @Test - public void tokenize_onCreateMandateRequestSuccess_launchesBrowserSwitch_andSendsAnalytics() - throws BrowserSwitchException, JSONException { + public void tokenize_onCreateMandateRequestSuccess_callsBackSEPAResponse_andSendsAnalytics() + throws JSONException { SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() .createMandateResultSuccess(createMandateResult) .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(activity, sepaDirectDebitRequest); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.selected.started"); - ArgumentCaptor captor = - ArgumentCaptor.forClass(BrowserSwitchOptions.class); - verify(braintreeClient).startBrowserSwitch(same(activity), captor.capture()); + ArgumentCaptor captor = + ArgumentCaptor.forClass(SEPADirectDebitResponse.class); + verify(sepaFlowStartedCallback).onResult(captor.capture(), isNull()); + + SEPADirectDebitResponse sepaResponseResult = captor.getValue(); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.requested"); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.success"); - BrowserSwitchOptions browserSwitchOptions = captor.getValue(); + BrowserSwitchOptions browserSwitchOptions = sepaResponseResult.getBrowserSwitchOptions(); assertEquals(Uri.parse("http://www.example.com"), browserSwitchOptions.getUrl()); assertEquals("com.example", browserSwitchOptions.getReturnUrlScheme()); assertEquals(BraintreeRequestCodes.SEPA_DEBIT, browserSwitchOptions.getRequestCode()); @@ -150,7 +88,7 @@ public void tokenize_onCreateMandateRequestSuccess_launchesBrowserSwitch_andSend } @Test - public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_onTokenizeSuccess_forwardsResultToListener_andSendsAnalytics() + public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_onTokenizeSuccess_callsBackResultWithNonce_andSendsAnalytics() throws JSONException { // null approval URL indicates mandate approved createMandateResult = new CreateMandateResult( @@ -169,16 +107,18 @@ public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_on .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(activity, sepaDirectDebitRequest); - verify(listener).onSEPADirectDebitSuccess(nonce); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SEPADirectDebitResponse.class); + verify(sepaFlowStartedCallback).onResult(captor.capture(), isNull()); + assertEquals(captor.getValue().getNonce(), nonce); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.success"); } @Test - public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_onTokenizeFailure_forwardsErrorToListener_andSendsAnalytics() { + public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_onTokenizeFailure_callsBackError_andSendsAnalytics() { // null approval URL indicates mandate approved createMandateResult = new CreateMandateResult( "null", @@ -195,16 +135,15 @@ public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_on .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(activity, sepaDirectDebitRequest); - verify(listener).onSEPADirectDebitFailure(exception); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); + verify(sepaFlowStartedCallback).onResult(isNull(), eq(exception)); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); } @Test - public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLInvalid_returnsErrorToListener() { + public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLInvalid_callsBackError() { createMandateResult = new CreateMandateResult( "", "1234", @@ -218,18 +157,15 @@ public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLInvalid_return .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(activity, sepaDirectDebitRequest); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); - verify(listener).onSEPADirectDebitFailure(captor.capture()); + verify(sepaFlowStartedCallback).onResult(isNull(), captor.capture()); + assertTrue(captor.getValue() instanceof BraintreeException); + assertEquals("An unexpected error occurred.", captor.getValue().getMessage()); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.failure"); - - Exception exception = captor.getValue(); - assertTrue(exception instanceof BraintreeException); - assertEquals("An unexpected error occurred.", exception.getMessage()); } @Test @@ -248,10 +184,9 @@ public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLNull_callsSEPA .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(activity, sepaDirectDebitRequest); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); verify(braintreeClient, never()).startBrowserSwitch(any(FragmentActivity.class), any(BrowserSwitchOptions.class)); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.success"); @@ -262,48 +197,61 @@ public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLNull_callsSEPA } @Test - public void tokenize_onCreateMandateRequestSuccess_whenStartBrowserSwitchFails_returnsErrorToListener_andSendsAnalytics() - throws BrowserSwitchException { - doThrow(BrowserSwitchException.class).when(braintreeClient) - .startBrowserSwitch(any(FragmentActivity.class), any(BrowserSwitchOptions.class)); - + public void tokenize_onCreateMandateError_returnsErrorToListener_andSendsAnalytics() { + Exception error = new Exception("error"); SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() - .createMandateResultSuccess(createMandateResult) + .createMandateError(error) .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(activity, sepaDirectDebitRequest); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); + verify(sepaFlowStartedCallback).onResult(isNull(), eq(error)); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.failure"); + } + + @Test + public void onBrowserSwitchResult_whenErrorNotNull_callsBackError() { + SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); + + braintreeClient = new MockBraintreeClientBuilder().build(); + + SEPADirectDebitClient sut = + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + Exception expectedError = new Exception("error"); + SEPADirectDebitBrowserSwitchResult payPalBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(expectedError); + sut.onBrowserSwitchResult(payPalBrowserSwitchResult, sepaBrowserSwitchResultCallback); ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); - verify(listener).onSEPADirectDebitFailure(captor.capture()); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), captor.capture()); - Exception exception = captor.getValue(); - assertTrue(exception instanceof BrowserSwitchException); + assertNotNull(captor.getValue()); + assertEquals(expectedError, captor.getValue()); } @Test - public void tokenize_onCreateMandateError_returnsErrorToListener_andSendsAnalytics() { - Exception error = new Exception("error"); - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() - .createMandateError(error) - .build(); + public void onBrowserSwitchResult_whenResultAndErrorNull_callsBackUnexpectedError() { + SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); + + braintreeClient = new MockBraintreeClientBuilder().build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(activity, sepaDirectDebitRequest); + SEPADirectDebitBrowserSwitchResult payPalBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult((BrowserSwitchResult) null); + sut.onBrowserSwitchResult(payPalBrowserSwitchResult, sepaBrowserSwitchResultCallback); - verify(listener).onSEPADirectDebitFailure(same(error)); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.failure"); + ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), captor.capture()); + + assertNotNull(captor.getValue()); + assertEquals("An unexpected error occurred.", captor.getValue().getMessage()); } @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusCanceled_returnsUserCanceledExceptionToListener_andSendsAnalytics() { + public void onBrowserSwitchResult_whenBrowserSwitchStatusCanceled_callsBackUserCanceledException_andSendsAnalytics() { SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); @@ -313,13 +261,14 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusCanceled_returnsUserCan .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); - sut.onBrowserSwitchResult(activity); + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); - verify(listener).onSEPADirectDebitFailure(captor.capture()); + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), captor.capture()); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.canceled"); Exception exception = captor.getValue(); @@ -349,10 +298,12 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkCon .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); + + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); - sut.onBrowserSwitchResult(activity); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.success"); verify(sepaDirectDebitApi).tokenize(eq("1234"), eq("customer-id"), eq("bank-reference-token"), eq("ONE_OFF"), @@ -361,7 +312,7 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkCon } @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSuccess_forwardsResultToListener_andSendsAnalytics() + public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSuccess_callsBackNonce_andSendsAnalytics() throws JSONException { SEPADirectDebitNonce nonce = SEPADirectDebitNonce.fromJSON( new JSONObject(Fixtures.SEPA_DEBIT_TOKENIZE_RESPONSE)); @@ -386,17 +337,18 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSucce .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); - sut.onBrowserSwitchResult(activity); + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); - verify(listener).onSEPADirectDebitSuccess(nonce); + verify(sepaBrowserSwitchResultCallback).onResult(eq(nonce), isNull()); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.success"); } @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailure_forwardsErrorToListener_andSendsAnalytics() + public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailure_callsBackError_andSendsAnalytics() throws JSONException { Exception exception = new Exception("tokenize error"); SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() @@ -420,12 +372,13 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailu .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.onBrowserSwitchResult(activity); + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); - verify(listener).onSEPADirectDebitFailure(exception); + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); + + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), eq(exception)); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); verify(sepaDirectDebitApi).tokenize(eq("1234"), eq("customer-id"), eq("bank-reference-token"), eq("ONE_OFF"), @@ -433,7 +386,7 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailu } @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSuccess_forwardsResultToListener() + public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSuccess_callsBackResult() throws JSONException { SEPADirectDebitNonce nonce = SEPADirectDebitNonce.fromJSON( new JSONObject(Fixtures.SEPA_DEBIT_TOKENIZE_RESPONSE)); @@ -458,45 +411,13 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSucce .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.onBrowserSwitchResult(activity); + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); - verify(listener).onSEPADirectDebitSuccess(nonce); - } + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); - @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailure_forwardsErrorToListener() - throws JSONException { - Exception exception = new Exception("tokenize error"); - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() - .tokenizeError(exception) - .build(); - - JSONObject metadata = new JSONObject() - .put("ibanLastFour", "1234") - .put("customerId", "customer-id") - .put("bankReferenceToken", "bank-reference-token") - .put("mandateType", "ONE_OFF"); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); - when(browserSwitchResult.getDeepLinkUrl()).thenReturn( - Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); - when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); - - braintreeClient = new MockBraintreeClientBuilder() - .deliverBrowserSwitchResult(browserSwitchResult) - .build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.onBrowserSwitchResult(activity); - - verify(listener).onSEPADirectDebitFailure(exception); + verify(sepaBrowserSwitchResultCallback).onResult(eq(nonce), isNull()); } @Test @@ -513,13 +434,14 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkCon .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.onBrowserSwitchResult(activity); + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); + + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); - verify(listener).onSEPADirectDebitFailure(captor.capture()); + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), captor.capture()); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); Exception exception = captor.getValue(); @@ -540,30 +462,18 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkURL .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); - sut.onBrowserSwitchResult(activity); + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); - verify(listener).onSEPADirectDebitFailure(captor.capture()); + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), captor.capture()); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); Exception exception = captor.getValue(); assertTrue(exception instanceof BraintreeException); assertEquals("Unknown error", exception.getMessage()); } - - @Test - public void getBrowserSwitchResult_getBrowserSwitchResultFromBraintreeClient() { - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); - - braintreeClient = new MockBraintreeClientBuilder() - .build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.getBrowserSwitchResult(activity); - verify(braintreeClient).getBrowserSwitchResult(activity); - } -} \ No newline at end of file +} diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLauncherUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLauncherUnitTest.java new file mode 100644 index 0000000000..f72e38314d --- /dev/null +++ b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLauncherUnitTest.java @@ -0,0 +1,96 @@ +package com.braintreepayments.api; + +import static com.braintreepayments.api.BraintreeRequestCodes.SEPA_DEBIT; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Intent; + +import androidx.fragment.app.FragmentActivity; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class SEPADirectDebitLauncherUnitTest { + + private BrowserSwitchClient browserSwitchClient; + private FragmentActivity activity; + private Intent intent; + private SEPADirectDebitLauncherCallback sepaLauncherCallback; + + @Before + public void beforeEach() { + browserSwitchClient = mock(BrowserSwitchClient.class); + activity = mock(FragmentActivity.class); + intent = new Intent(); + sepaLauncherCallback = mock(SEPADirectDebitLauncherCallback.class); + } + + @Test + public void launch_startsBrowserSwitch() throws BrowserSwitchException { + SEPADirectDebitResponse sepaResponse = mock(SEPADirectDebitResponse.class); + BrowserSwitchOptions options = mock(BrowserSwitchOptions.class); + when(sepaResponse.getBrowserSwitchOptions()).thenReturn(options); + SEPADirectDebitLauncher sut = new SEPADirectDebitLauncher(browserSwitchClient, sepaLauncherCallback); + + sut.launch(activity, sepaResponse); + + verify(browserSwitchClient).start(same(activity), same(options)); + } + + @Test + public void launch_onError_callsBackError() throws BrowserSwitchException { + SEPADirectDebitResponse sepaResponse = mock(SEPADirectDebitResponse.class); + BrowserSwitchOptions options = mock(BrowserSwitchOptions.class); + when(sepaResponse.getBrowserSwitchOptions()).thenReturn(options); + BrowserSwitchException exception = new BrowserSwitchException("error"); + doThrow(exception).when(browserSwitchClient).start(same(activity), same(options)); + SEPADirectDebitLauncher sut = new SEPADirectDebitLauncher(browserSwitchClient, sepaLauncherCallback); + + sut.launch(activity, sepaResponse); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(SEPADirectDebitBrowserSwitchResult.class); + verify(sepaLauncherCallback).onResult(captor.capture()); + assertSame(exception, captor.getValue().getError()); + assertNull(captor.getValue().getBrowserSwitchResult()); + } + + @Test + public void handleReturnToAppFromBrowser_deliversResultToLauncherCallback() { + BrowserSwitchResult result = mock(BrowserSwitchResult.class); + when(browserSwitchClient.parseResult(eq(activity), eq(SEPA_DEBIT), eq(intent))).thenReturn( + result); + SEPADirectDebitLauncher sut = new SEPADirectDebitLauncher(browserSwitchClient, sepaLauncherCallback); + + sut.handleReturnToAppFromBrowser(activity, intent); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(SEPADirectDebitBrowserSwitchResult.class); + verify(sepaLauncherCallback).onResult(captor.capture()); + assertSame(result, captor.getValue().getBrowserSwitchResult()); + assertNull(captor.getValue().getError()); + } + + @Test + public void handleReturnToAppFromBrowser_clearsActiveBrowserSwitchRequests() { + BrowserSwitchResult result = mock(BrowserSwitchResult.class); + when(browserSwitchClient.parseResult(eq(activity), eq(SEPA_DEBIT), eq(intent))).thenReturn( + result); + SEPADirectDebitLauncher sut = new SEPADirectDebitLauncher(browserSwitchClient, sepaLauncherCallback); + + sut.handleReturnToAppFromBrowser(activity, intent); + + verify(browserSwitchClient).clearActiveRequests(same(activity)); + } +} diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserverUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserverUnitTest.java deleted file mode 100644 index ca356bc0ab..0000000000 --- a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserverUnitTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.braintreepayments.api; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.lifecycle.Lifecycle; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -@RunWith(RobolectricTestRunner.class) -public class SEPADirectDebitLifecycleObserverUnitTest { - - @Test - public void onResume_whenLifeCycleObserverIsFragment_payPalClientDeliversResultWithFragmentActivity() { - Fragment fragment = mock(Fragment.class); - FragmentActivity activity = mock(FragmentActivity.class); - when(fragment.getActivity()).thenReturn(activity); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getRequestCode()).thenReturn(BraintreeRequestCodes.SEPA_DEBIT); - - SEPADirectDebitClient sepaDirectDebitClient = mock(SEPADirectDebitClient.class); - when(sepaDirectDebitClient.getBrowserSwitchResult(activity)).thenReturn( - browserSwitchResult); - - SEPADirectDebitLifecycleObserver sut = - new SEPADirectDebitLifecycleObserver(sepaDirectDebitClient); - - sut.onStateChanged(fragment, Lifecycle.Event.ON_RESUME); - - verify(sepaDirectDebitClient).onBrowserSwitchResult(same(activity)); - } - - @Test - public void onResume_whenLifeCycleObserverIsActivity_payPalClientDeliversResultWithSameActivity() { - FragmentActivity activity = mock(FragmentActivity.class); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getRequestCode()).thenReturn(BraintreeRequestCodes.SEPA_DEBIT); - - SEPADirectDebitClient sepaDirectDebitClient = mock(SEPADirectDebitClient.class); - when(sepaDirectDebitClient.getBrowserSwitchResult(activity)).thenReturn( - browserSwitchResult); - - SEPADirectDebitLifecycleObserver sut = - new SEPADirectDebitLifecycleObserver(sepaDirectDebitClient); - - sut.onStateChanged(activity, Lifecycle.Event.ON_RESUME); - - verify(sepaDirectDebitClient).onBrowserSwitchResult(same(activity)); - } - - @Test - public void onResume_whenPendingBrowserSwitchResultExists_andRequestCodeNotPayPal_doesNothing() { - FragmentActivity activity = mock(FragmentActivity.class); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getRequestCode()).thenReturn(BraintreeRequestCodes.PAYPAL); - - SEPADirectDebitClient sepaDirectDebitClient = mock(SEPADirectDebitClient.class); - when(sepaDirectDebitClient.getBrowserSwitchResult(activity)).thenReturn( - browserSwitchResult); - - SEPADirectDebitLifecycleObserver sut = - new SEPADirectDebitLifecycleObserver(sepaDirectDebitClient); - - sut.onStateChanged(activity, Lifecycle.Event.ON_RESUME); - - verify(sepaDirectDebitClient, never()).onBrowserSwitchResult(any(FragmentActivity.class)); - } -}