From 1d833b6c954b219b3364b00149fc54e68a8494ae Mon Sep 17 00:00:00 2001 From: Shyang Koong Date: Fri, 7 Jun 2019 14:00:09 -0700 Subject: [PATCH 1/2] Moving the receipts automation to receipts module --- receipt/build.gradle | 6 + .../android/receipt}/ListReceiptsTest.java | 95 +++++---- .../HyperwalletExternalResourceManager.java | 3 +- .../rule/HyperwalletMockWebServer.java | 115 +++++++++++ .../android/util/EspressoUtils.java | 187 ++++++++++++++++++ .../android/util/NestedScrollToAction.java | 41 ++++ .../util/RecyclerViewCountAssertion.java | 30 +++ .../util/TestAuthenticationProvider.java | 51 +++++ receipt/src/main/AndroidManifest.xml | 4 +- .../main/res/xml/network_security_config.xml | 6 + .../repository/ReceiptDataSourceTest.java | 2 +- .../HyperwalletExternalResourceManager.java | 72 +++++++ .../authentication_token_response.json | 3 + .../resources/receipt_credit_response.json | 0 .../resources/receipt_debit_response.json | 0 .../test/resources/receipt_list_response.json | 0 ui/src/androidTest/AndroidManifest.xml | 9 - 17 files changed, 572 insertions(+), 52 deletions(-) rename {ui/src/androidTest/java/com/hyperwallet/android/transfermethod/ui => receipt/src/androidTest/java/com/hyperwallet/android/receipt}/ListReceiptsTest.java (55%) rename receipt/src/{test/java/com/hyperwallet/android/receipt => androidTest/java/com/hyperwallet/android}/rule/HyperwalletExternalResourceManager.java (98%) create mode 100644 receipt/src/androidTest/java/com/hyperwallet/android/rule/HyperwalletMockWebServer.java create mode 100644 receipt/src/androidTest/java/com/hyperwallet/android/util/EspressoUtils.java create mode 100644 receipt/src/androidTest/java/com/hyperwallet/android/util/NestedScrollToAction.java create mode 100644 receipt/src/androidTest/java/com/hyperwallet/android/util/RecyclerViewCountAssertion.java create mode 100644 receipt/src/androidTest/java/com/hyperwallet/android/util/TestAuthenticationProvider.java create mode 100644 receipt/src/main/res/xml/network_security_config.xml create mode 100644 receipt/src/test/java/com/hyperwallet/android/rule/HyperwalletExternalResourceManager.java create mode 100644 receipt/src/test/resources/authentication_token_response.json rename {ui => receipt}/src/test/resources/receipt_credit_response.json (100%) rename {ui => receipt}/src/test/resources/receipt_debit_response.json (100%) rename {ui => receipt}/src/test/resources/receipt_list_response.json (100%) delete mode 100644 ui/src/androidTest/AndroidManifest.xml diff --git a/receipt/build.gradle b/receipt/build.gradle index dac1fd822..6aa8d9d42 100644 --- a/receipt/build.gradle +++ b/receipt/build.gradle @@ -11,7 +11,13 @@ dependencies { implementation "androidx.paging:paging-runtime:$pagingRuntimeVersion" testImplementation "org.robolectric:robolectric:$robolectricVersion" + androidTestImplementation "androidx.test:rules:$testRulesVersion" + androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion" + androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion" + androidTestImplementation "com.squareup.okhttp3:mockwebserver:$mockServerVersion" + androidTestImplementation "com.squareup.leakcanary:leakcanary-android-instrumentation:$leakcanaryVersion" + androidTestImplementation "com.squareup.leakcanary:leakcanary-support-fragment:$leakcanaryVersion" } def aarFile = file("$buildDir/outputs/aar/receipt-$version" + ".aar") diff --git a/ui/src/androidTest/java/com/hyperwallet/android/transfermethod/ui/ListReceiptsTest.java b/receipt/src/androidTest/java/com/hyperwallet/android/receipt/ListReceiptsTest.java similarity index 55% rename from ui/src/androidTest/java/com/hyperwallet/android/transfermethod/ui/ListReceiptsTest.java rename to receipt/src/androidTest/java/com/hyperwallet/android/receipt/ListReceiptsTest.java index 2bb3e86b2..46bbd331e 100644 --- a/ui/src/androidTest/java/com/hyperwallet/android/transfermethod/ui/ListReceiptsTest.java +++ b/receipt/src/androidTest/java/com/hyperwallet/android/receipt/ListReceiptsTest.java @@ -1,4 +1,4 @@ -package com.hyperwallet.android.transfermethod.ui; +package com.hyperwallet.android.receipt; import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.assertion.ViewAssertions.matches; @@ -14,19 +14,17 @@ import static java.net.HttpURLConnection.HTTP_NO_CONTENT; import static java.net.HttpURLConnection.HTTP_OK; -import static com.hyperwallet.android.util.EspressoUtils.atPosition; - import android.widget.TextView; 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.receipt.repository.ReceiptRepositoryFactory; import com.hyperwallet.android.receipt.view.ListReceiptActivity; import com.hyperwallet.android.rule.HyperwalletExternalResourceManager; import com.hyperwallet.android.rule.HyperwalletMockWebServer; -import com.hyperwallet.android.ui.repository.RepositoryFactory; +import com.hyperwallet.android.util.EspressoUtils; import com.hyperwallet.android.util.RecyclerViewCountAssertion; import com.hyperwallet.android.util.TestAuthenticationProvider; @@ -58,7 +56,7 @@ public void setup() { @After public void cleanup() { - RepositoryFactory.clearInstance(); + ReceiptRepositoryFactory.clearInstance(); } @Test @@ -75,37 +73,48 @@ public void testListReceipts_userHasMultipleTransactions() { onView(withId(R.id.list_receipts)).check(matches(isDisplayed())); onView(withId(R.id.list_receipts)) - .check(matches(atPosition(0, hasDescendant(withText("June 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, + .check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("June 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.credit))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("Payment"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("+ 20.00"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("June 07, 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("USD"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(0, hasDescendant(withText("Payment"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(0, hasDescendant(withText("+ 20.00"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(0, hasDescendant(withText("June 07, 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("USD"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(1, + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(1, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.credit))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(1, hasDescendant(withText("Payment"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(1, hasDescendant(withText("+ 25.00"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(1, hasDescendant(withText("June 02, 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(1, hasDescendant(withText("CAD"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(1, hasDescendant(withText("Payment"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(1, hasDescendant(withText("+ 25.00"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(1, hasDescendant(withText("June 02, 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(1, hasDescendant(withText("CAD"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(2, + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(2, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.debit))))); onView(withId(R.id.list_receipts)).check( - matches(atPosition(2, hasDescendant(withText("Card Activation Fee"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(2, hasDescendant(withText("- 1.95"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(2, hasDescendant(withText("June 01, 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(2, hasDescendant(withText("USD"))))); + matches(EspressoUtils.atPosition(2, hasDescendant(withText("Card Activation Fee"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(2, hasDescendant(withText("- 1.95"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(2, hasDescendant(withText("June 01, 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(2, hasDescendant(withText("USD"))))); onView(withId(R.id.list_receipts)) - .check(matches(atPosition(3, hasDescendant(withText("December 2018"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(3, + .check(matches(EspressoUtils.atPosition(3, hasDescendant(withText("December 2018"))))); + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(3, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.debit))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(3, hasDescendant(withText("Card Load"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(3, hasDescendant(withText("- 18.05"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(3, hasDescendant(withText("December 01, 2018"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(3, hasDescendant(withText("USD"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(3, hasDescendant(withText("Card Load"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(3, hasDescendant(withText("- 18.05"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(3, hasDescendant(withText("December 01, 2018"))))); + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(3, hasDescendant(withText("USD"))))); onView(withId(R.id.list_receipts)).check(new RecyclerViewCountAssertion(4)); } @@ -124,13 +133,16 @@ public void testListReceipts_displayCreditTransaction() { onView(withId(R.id.list_receipts)).check(matches(isDisplayed())); onView(withId(R.id.list_receipts)) - .check(matches(atPosition(0, hasDescendant(withText("June 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, + .check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("June 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.credit))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("Payment"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("+ 25.00"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("June 02, 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("CAD"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(0, hasDescendant(withText("Payment"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(0, hasDescendant(withText("+ 25.00"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(0, hasDescendant(withText("June 02, 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("CAD"))))); onView(withId(R.id.list_receipts)).check(new RecyclerViewCountAssertion(1)); } @@ -149,13 +161,16 @@ public void testListReceipts_displayDebitTransaction() { onView(withId(R.id.list_receipts)).check(matches(isDisplayed())); onView(withId(R.id.list_receipts)) - .check(matches(atPosition(0, hasDescendant(withText("May 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, + .check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("May 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.debit))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("Card Load"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("- 18.05"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("May 02, 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("USD"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(0, hasDescendant(withText("Card Load"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(0, hasDescendant(withText("- 18.05"))))); + onView(withId(R.id.list_receipts)).check( + matches(EspressoUtils.atPosition(0, hasDescendant(withText("May 02, 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("USD"))))); onView(withId(R.id.list_receipts)).check(new RecyclerViewCountAssertion(1)); } diff --git a/receipt/src/test/java/com/hyperwallet/android/receipt/rule/HyperwalletExternalResourceManager.java b/receipt/src/androidTest/java/com/hyperwallet/android/rule/HyperwalletExternalResourceManager.java similarity index 98% rename from receipt/src/test/java/com/hyperwallet/android/receipt/rule/HyperwalletExternalResourceManager.java rename to receipt/src/androidTest/java/com/hyperwallet/android/rule/HyperwalletExternalResourceManager.java index 0c5f6a743..e7e546063 100644 --- a/receipt/src/test/java/com/hyperwallet/android/receipt/rule/HyperwalletExternalResourceManager.java +++ b/receipt/src/androidTest/java/com/hyperwallet/android/rule/HyperwalletExternalResourceManager.java @@ -1,4 +1,4 @@ -package com.hyperwallet.android.receipt.rule; +package com.hyperwallet.android.rule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; @@ -35,6 +35,7 @@ public String getResourceContent(final String resourceName) { } private String getContent(final String resourceName) { + URL resource = classLoader.getResource(resourceName); InputStream inputStream = null; Writer writer = new StringWriter(); diff --git a/receipt/src/androidTest/java/com/hyperwallet/android/rule/HyperwalletMockWebServer.java b/receipt/src/androidTest/java/com/hyperwallet/android/rule/HyperwalletMockWebServer.java new file mode 100644 index 000000000..9f42f6128 --- /dev/null +++ b/receipt/src/androidTest/java/com/hyperwallet/android/rule/HyperwalletMockWebServer.java @@ -0,0 +1,115 @@ +package com.hyperwallet.android.rule; + +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; + +public final class HyperwalletMockWebServer extends TestWatcher { + + private MockWebServer mServer; + private int port; + + public HyperwalletMockWebServer(int port) { + this.port = port; + } + + @Override + protected void starting(Description description) { + super.starting(description); + mServer = new MockWebServer(); + try { + mServer.start(port); + } catch (IOException e) { + throw new IllegalStateException("Unable to start mock server", e); + } + } + + @Override + protected void finished(Description description) { + super.finished(description); + try { + mServer.shutdown(); + mServer.close(); + } catch (IOException e) { + throw new IllegalStateException("Un error occurred when shutting down mock server", e); + } + } + + public HyperwalletMockResponse mockResponse() { + return new Builder(mServer).build(); + } + + public MockWebServer getServer() { + return mServer; + } + + public static class HyperwalletMockResponse { + + private String path; + private String body; + private int httpResponseCode; + private Builder builder; + + HyperwalletMockResponse(Builder builder) { + this.path = builder.path; + this.httpResponseCode = builder.responseCode; + this.body = builder.body; + this.builder = builder; + } + + public HyperwalletMockResponse withHttpResponseCode(final int code) { + builder.responseCode(code); + return builder.build(); + } + + public HyperwalletMockResponse withBody(final String body) { + builder.body(body); + return builder.build(); + } + + public void mock() { + mockRequest(); + } + + private String mockRequest() { + builder.server.enqueue(new MockResponse().setResponseCode(httpResponseCode).setBody(body)); + return builder.server.url(path).toString(); + } + + } + + private static class Builder { + + private String path; + private String body; + private int responseCode; + private MockWebServer server; + + + Builder(final MockWebServer server) { + this.path = ""; + this.responseCode = HttpURLConnection.HTTP_OK; + this.body = ""; + this.server = server; + } + + Builder responseCode(final int code) { + this.responseCode = code; + return this; + } + + Builder body(final String body) { + this.body = body; + return this; + } + + HyperwalletMockResponse build() { + return new HyperwalletMockResponse(this); + } + } +} diff --git a/receipt/src/androidTest/java/com/hyperwallet/android/util/EspressoUtils.java b/receipt/src/androidTest/java/com/hyperwallet/android/util/EspressoUtils.java new file mode 100644 index 000000000..92a711f96 --- /dev/null +++ b/receipt/src/androidTest/java/com/hyperwallet/android/util/EspressoUtils.java @@ -0,0 +1,187 @@ +package com.hyperwallet.android.util; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.EditText; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.action.ViewActions; +import androidx.test.espresso.matcher.BoundedMatcher; + +import com.google.android.material.textfield.TextInputLayout; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import java.util.Objects; + +public class EspressoUtils { + + public static Matcher withHint(final String expectedHint) { + return new TypeSafeMatcher() { + + @Override + public boolean matchesSafely(View view) { + if (!(view instanceof TextInputLayout)) { + return false; + } + + String hint = Objects.toString(((TextInputLayout) view).getHint()); + return expectedHint.equals(hint); + } + + @Override + public void describeTo(Description description) { + description.appendText(expectedHint); + } + }; + } + + public static Matcher hasErrorText(final String expectedErrorMessage) { + return new TypeSafeMatcher() { + + @Override + public boolean matchesSafely(View view) { + if (!(view instanceof TextInputLayout)) { + return false; + } + + String errorMessage = Objects.toString(((TextInputLayout) view).getError()); + return expectedErrorMessage.equals(errorMessage); + } + + @Override + public void describeTo(Description description) { + description.appendText(expectedErrorMessage); + } + }; + } + + public static Matcher hasErrorText(final int resourceId) { + return new TypeSafeMatcher() { + + @Override + public boolean matchesSafely(View view) { + if (!(view instanceof TextInputLayout)) { + return false; + } + String expectedErrorMessage = view.getResources().getString(resourceId); + String errorMessage = Objects.toString(((TextInputLayout) view).getError()); + + return expectedErrorMessage.equals(errorMessage); + } + + @Override + public void describeTo(Description description) { + } + }; + } + + public static Matcher withDrawable(final int resourceId) { + return new TypeSafeMatcher() { + + @Override + public boolean matchesSafely(View view) { + if (!(view instanceof ImageView)) { + return false; + } + + Drawable drawable = ((ImageView) view).getDrawable(); + if (drawable == null) { + return false; + } + Drawable expectedDrawable = view.getContext().getResources().getDrawable(resourceId); + + Bitmap bitmap = getBitmap(drawable); + Bitmap expectedBitmap = getBitmap(expectedDrawable); + + return bitmap.sameAs(expectedBitmap); + } + + @Override + public void describeTo(Description description) { + } + }; + } + + public static Matcher atPosition(final int position, @NonNull final Matcher matcher) { + return new BoundedMatcher(RecyclerView.class) { + + @Override + protected boolean matchesSafely(final RecyclerView view) { + RecyclerView.ViewHolder viewHolder = view.findViewHolderForAdapterPosition(position); + + if (viewHolder == null) { + return false; + } + + return matcher.matches(viewHolder.itemView); + } + + @Override + public void describeTo(Description description) { + description.appendText("has item at position " + position + ": "); + matcher.describeTo(description); + } + }; + } + + public static ViewAction nestedScrollTo() { + return ViewActions.actionWithAssertions(new NestedScrollToAction()); + } + + private static Bitmap getBitmap(Drawable drawable) { + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + + return bitmap; + } + + public static Matcher hasNoErrorText() { + return new TypeSafeMatcher() { + + @Override + public boolean matchesSafely(View view) { + if (!(view instanceof TextInputLayout)) { + return false; + } + return ((TextInputLayout) view).getError() == null; + } + + @Override + public void describeTo(Description description) { + description.appendText("has no error text: "); + } + }; + } + + public static Matcher hasEmptyText() { + return new TypeSafeMatcher() { + + @Override + public boolean matchesSafely(View view) { + if (!(view instanceof EditText)) { + return false; + } + String text = ((EditText) view).getText().toString(); + + return text.isEmpty(); + } + + @Override + public void describeTo(Description description) { + } + }; + } +} + diff --git a/receipt/src/androidTest/java/com/hyperwallet/android/util/NestedScrollToAction.java b/receipt/src/androidTest/java/com/hyperwallet/android/util/NestedScrollToAction.java new file mode 100644 index 000000000..392867387 --- /dev/null +++ b/receipt/src/androidTest/java/com/hyperwallet/android/util/NestedScrollToAction.java @@ -0,0 +1,41 @@ +package com.hyperwallet.android.util; + +import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; +import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.anyOf; + +import android.view.View; + +import androidx.core.widget.NestedScrollView; +import androidx.test.espresso.UiController; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.action.ScrollToAction; +import androidx.test.espresso.matcher.ViewMatchers; + +import org.hamcrest.Matcher; + +public class NestedScrollToAction implements ViewAction { + private static final String TAG = ScrollToAction.class.getSimpleName(); + + @SuppressWarnings("unchecked") + @Override + public Matcher getConstraints() { + return allOf( + withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE), + isDescendantOfA( + anyOf(isAssignableFrom(NestedScrollView.class)))); + } + + @Override + public void perform(UiController uiController, View view) { + new ScrollToAction().perform(uiController, view); + } + + @Override + public String getDescription() { + return "scroll to"; + } +} diff --git a/receipt/src/androidTest/java/com/hyperwallet/android/util/RecyclerViewCountAssertion.java b/receipt/src/androidTest/java/com/hyperwallet/android/util/RecyclerViewCountAssertion.java new file mode 100644 index 000000000..bb3aecaff --- /dev/null +++ b/receipt/src/androidTest/java/com/hyperwallet/android/util/RecyclerViewCountAssertion.java @@ -0,0 +1,30 @@ +package com.hyperwallet.android.util; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +import android.view.View; + +import androidx.recyclerview.widget.RecyclerView; +import androidx.test.espresso.NoMatchingViewException; +import androidx.test.espresso.ViewAssertion; + +public class RecyclerViewCountAssertion implements ViewAssertion { + private final int mCount; + + public RecyclerViewCountAssertion(int count) { + this.mCount = count; + } + + @Override + public void check(View view, NoMatchingViewException noViewFoundException) { + if (noViewFoundException != null) { + throw noViewFoundException; + } + + RecyclerView recyclerView = (RecyclerView) view; + RecyclerView.Adapter adapter = recyclerView.getAdapter(); + + assertThat(adapter.getItemCount(), is(mCount)); + } +} diff --git a/receipt/src/androidTest/java/com/hyperwallet/android/util/TestAuthenticationProvider.java b/receipt/src/androidTest/java/com/hyperwallet/android/util/TestAuthenticationProvider.java new file mode 100644 index 000000000..686ccbf30 --- /dev/null +++ b/receipt/src/androidTest/java/com/hyperwallet/android/util/TestAuthenticationProvider.java @@ -0,0 +1,51 @@ +package com.hyperwallet.android.util; + +import com.hyperwallet.android.HyperwalletAuthenticationTokenListener; +import com.hyperwallet.android.HyperwalletAuthenticationTokenProvider; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.UUID; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +public class TestAuthenticationProvider implements HyperwalletAuthenticationTokenProvider { + + public static final MediaType JSON + = MediaType.get("application/json; charset=utf-8"); + private static final String mBaseUrl = "http://localhost:8080/rest/v3/users/{0}/authentication-token"; + private static final String mUserToken = "user_token"; + + @Override + public void retrieveAuthenticationToken(final HyperwalletAuthenticationTokenListener authenticationTokenListener) { + + OkHttpClient client = new OkHttpClient(); + + String payload = "{}"; + String baseUrl = MessageFormat.format(mBaseUrl, mUserToken); + + RequestBody body = RequestBody.create(JSON, payload); + Request request = new Request.Builder() + .url(baseUrl) + .post(body) + .build(); + + client.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + authenticationTokenListener.onFailure(UUID.randomUUID(), e.getMessage()); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + authenticationTokenListener.onSuccess(response.body().string()); + } + }); + } +} diff --git a/receipt/src/main/AndroidManifest.xml b/receipt/src/main/AndroidManifest.xml index 251bb4f88..3039f6d18 100644 --- a/receipt/src/main/AndroidManifest.xml +++ b/receipt/src/main/AndroidManifest.xml @@ -2,7 +2,9 @@ - + + + diff --git a/receipt/src/main/res/xml/network_security_config.xml b/receipt/src/main/res/xml/network_security_config.xml new file mode 100644 index 000000000..5e4ba9c97 --- /dev/null +++ b/receipt/src/main/res/xml/network_security_config.xml @@ -0,0 +1,6 @@ + + + + localhost + + \ No newline at end of file diff --git a/receipt/src/test/java/com/hyperwallet/android/receipt/repository/ReceiptDataSourceTest.java b/receipt/src/test/java/com/hyperwallet/android/receipt/repository/ReceiptDataSourceTest.java index c4f4894e5..0c71e1b61 100644 --- a/receipt/src/test/java/com/hyperwallet/android/receipt/repository/ReceiptDataSourceTest.java +++ b/receipt/src/test/java/com/hyperwallet/android/receipt/repository/ReceiptDataSourceTest.java @@ -27,7 +27,7 @@ import com.hyperwallet.android.model.paging.HyperwalletPageList; import com.hyperwallet.android.model.receipt.Receipt; import com.hyperwallet.android.model.receipt.ReceiptQueryParam; -import com.hyperwallet.android.receipt.rule.HyperwalletExternalResourceManager; +import com.hyperwallet.android.rule.HyperwalletExternalResourceManager; import org.hamcrest.Matchers; import org.json.JSONObject; diff --git a/receipt/src/test/java/com/hyperwallet/android/rule/HyperwalletExternalResourceManager.java b/receipt/src/test/java/com/hyperwallet/android/rule/HyperwalletExternalResourceManager.java new file mode 100644 index 000000000..9061af9b8 --- /dev/null +++ b/receipt/src/test/java/com/hyperwallet/android/rule/HyperwalletExternalResourceManager.java @@ -0,0 +1,72 @@ +package com.hyperwallet.android.rule; + +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class HyperwalletExternalResourceManager extends TestWatcher { + + private static final String EMPTY = ""; + private ClassLoader classLoader; + private Logger logger; + + @Override + protected void starting(Description description) { + super.starting(description); + classLoader = description.getTestClass().getClassLoader(); + logger = Logger.getLogger(description.getTestClass().getName()); + } + + public String getResourceContent(final String resourceName) { + if (resourceName == null) { + throw new IllegalArgumentException("Parameter resourceName cannot be null"); + } + + return getContent(resourceName); + } + + private String getContent(final String resourceName) { + URL resource = classLoader.getResource(resourceName); + InputStream inputStream = null; + Writer writer = new StringWriter(); + String resourceContent = EMPTY; + if (resource != null) { + try { + inputStream = resource.openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + String line = reader.readLine(); + while (line != null) { + writer.write(line); + line = reader.readLine(); + } + resourceContent = writer.toString(); + + } catch (Exception e) { + logger.log(Level.WARNING, "There was an error loading an external resource", e); + } finally { + try { + if (inputStream != null) { + inputStream.close(); + } + } catch (Exception e) { + logger.log(Level.SEVERE, "There was an error closing input stream", e); + } + try { + writer.close(); + } catch (IOException e) { + logger.log(Level.SEVERE, "There was an error closing writer", e); + } + } + } + return resourceContent; + } +} diff --git a/receipt/src/test/resources/authentication_token_response.json b/receipt/src/test/resources/authentication_token_response.json new file mode 100644 index 000000000..e8e41bf37 --- /dev/null +++ b/receipt/src/test/resources/authentication_token_response.json @@ -0,0 +1,3 @@ +{ + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c3ItZjZlNmZjY2EtNTBmNy00ZWY1LWExYzUtNWZmMDJlMDU2MzgzIiwiaWF0IjoxNTQ5NTgwMzk0LCJleHAiOjI1NDk1ODA5OTQsImF1ZCI6InBndS03YTEyMzJlOC0xNDc5LTQzNzAtOWY1NC03ODc1ZjdiMTg2NmMiLCJpc3MiOiJwcmctY2NhODAyNWUtODVhMy0xMWU2LTg2MGEtNThhZDVlY2NlNjFkIiwicmVzdC11cmkiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvcmVzdC92My8iLCJncmFwaHFsLXVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9ncmFwaHFsIn0.kILSynYHbepbl4sVqENnNog09iGByfTrckHhSCjVgnuRnuspI72cx3rt0SB2V_neHwzYkD_VfhNKk9gJDOwXeQ" +} \ No newline at end of file diff --git a/ui/src/test/resources/receipt_credit_response.json b/receipt/src/test/resources/receipt_credit_response.json similarity index 100% rename from ui/src/test/resources/receipt_credit_response.json rename to receipt/src/test/resources/receipt_credit_response.json diff --git a/ui/src/test/resources/receipt_debit_response.json b/receipt/src/test/resources/receipt_debit_response.json similarity index 100% rename from ui/src/test/resources/receipt_debit_response.json rename to receipt/src/test/resources/receipt_debit_response.json diff --git a/ui/src/test/resources/receipt_list_response.json b/receipt/src/test/resources/receipt_list_response.json similarity index 100% rename from ui/src/test/resources/receipt_list_response.json rename to receipt/src/test/resources/receipt_list_response.json diff --git a/ui/src/androidTest/AndroidManifest.xml b/ui/src/androidTest/AndroidManifest.xml deleted file mode 100644 index 9b2c52c35..000000000 --- a/ui/src/androidTest/AndroidManifest.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - \ No newline at end of file From 72a0bf1d32fec478e62871d18c6ca22c6dc1a8c4 Mon Sep 17 00:00:00 2001 From: Shyang Koong Date: Fri, 7 Jun 2019 15:01:41 -0700 Subject: [PATCH 2/2] Adding back locale test --- .../android/receipt/ListReceiptsTest.java | 111 ++++++++++++------ 1 file changed, 76 insertions(+), 35 deletions(-) diff --git a/receipt/src/androidTest/java/com/hyperwallet/android/receipt/ListReceiptsTest.java b/receipt/src/androidTest/java/com/hyperwallet/android/receipt/ListReceiptsTest.java index 46bbd331e..d0a99c292 100644 --- a/receipt/src/androidTest/java/com/hyperwallet/android/receipt/ListReceiptsTest.java +++ b/receipt/src/androidTest/java/com/hyperwallet/android/receipt/ListReceiptsTest.java @@ -14,8 +14,14 @@ import static java.net.HttpURLConnection.HTTP_NO_CONTENT; import static java.net.HttpURLConnection.HTTP_OK; +import static com.hyperwallet.android.util.EspressoUtils.atPosition; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; import android.widget.TextView; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.rule.ActivityTestRule; @@ -24,7 +30,6 @@ import com.hyperwallet.android.receipt.view.ListReceiptActivity; import com.hyperwallet.android.rule.HyperwalletExternalResourceManager; import com.hyperwallet.android.rule.HyperwalletMockWebServer; -import com.hyperwallet.android.util.EspressoUtils; import com.hyperwallet.android.util.RecyclerViewCountAssertion; import com.hyperwallet.android.util.TestAuthenticationProvider; @@ -35,6 +40,8 @@ import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Locale; + @RunWith(AndroidJUnit4.class) public class ListReceiptsTest { @@ -52,6 +59,8 @@ public void setup() { mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager .getResourceContent("authentication_token_response.json")).mock(); + + setLocale(Locale.US); } @After @@ -73,48 +82,48 @@ public void testListReceipts_userHasMultipleTransactions() { onView(withId(R.id.list_receipts)).check(matches(isDisplayed())); onView(withId(R.id.list_receipts)) - .check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("June 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, + .check(matches(atPosition(0, hasDescendant(withText("June 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.credit))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(0, hasDescendant(withText("Payment"))))); + matches(atPosition(0, hasDescendant(withText("Payment"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(0, hasDescendant(withText("+ 20.00"))))); + matches(atPosition(0, hasDescendant(withText("+ 20.00"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(0, hasDescendant(withText("June 07, 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("USD"))))); + matches(atPosition(0, hasDescendant(withText("June 07, 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("USD"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(1, + onView(withId(R.id.list_receipts)).check(matches(atPosition(1, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.credit))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(1, hasDescendant(withText("Payment"))))); + matches(atPosition(1, hasDescendant(withText("Payment"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(1, hasDescendant(withText("+ 25.00"))))); + matches(atPosition(1, hasDescendant(withText("+ 25.00"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(1, hasDescendant(withText("June 02, 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(1, hasDescendant(withText("CAD"))))); + matches(atPosition(1, hasDescendant(withText("June 02, 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(1, hasDescendant(withText("CAD"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(2, + onView(withId(R.id.list_receipts)).check(matches(atPosition(2, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.debit))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(2, hasDescendant(withText("Card Activation Fee"))))); + matches(atPosition(2, hasDescendant(withText("Card Activation Fee"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(2, hasDescendant(withText("- 1.95"))))); + matches(atPosition(2, hasDescendant(withText("- 1.95"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(2, hasDescendant(withText("June 01, 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(2, hasDescendant(withText("USD"))))); + matches(atPosition(2, hasDescendant(withText("June 01, 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(2, hasDescendant(withText("USD"))))); onView(withId(R.id.list_receipts)) - .check(matches(EspressoUtils.atPosition(3, hasDescendant(withText("December 2018"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(3, + .check(matches(atPosition(3, hasDescendant(withText("December 2018"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(3, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.debit))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(3, hasDescendant(withText("Card Load"))))); + matches(atPosition(3, hasDescendant(withText("Card Load"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(3, hasDescendant(withText("- 18.05"))))); + matches(atPosition(3, hasDescendant(withText("- 18.05"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(3, hasDescendant(withText("December 01, 2018"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(3, hasDescendant(withText("USD"))))); + matches(atPosition(3, hasDescendant(withText("December 01, 2018"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(3, hasDescendant(withText("USD"))))); onView(withId(R.id.list_receipts)).check(new RecyclerViewCountAssertion(4)); } @@ -133,16 +142,16 @@ public void testListReceipts_displayCreditTransaction() { onView(withId(R.id.list_receipts)).check(matches(isDisplayed())); onView(withId(R.id.list_receipts)) - .check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("June 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, + .check(matches(atPosition(0, hasDescendant(withText("June 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.credit))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(0, hasDescendant(withText("Payment"))))); + matches(atPosition(0, hasDescendant(withText("Payment"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(0, hasDescendant(withText("+ 25.00"))))); + matches(atPosition(0, hasDescendant(withText("+ 25.00"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(0, hasDescendant(withText("June 02, 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("CAD"))))); + matches(atPosition(0, hasDescendant(withText("June 02, 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("CAD"))))); onView(withId(R.id.list_receipts)).check(new RecyclerViewCountAssertion(1)); } @@ -161,16 +170,16 @@ public void testListReceipts_displayDebitTransaction() { onView(withId(R.id.list_receipts)).check(matches(isDisplayed())); onView(withId(R.id.list_receipts)) - .check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("May 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, + .check(matches(atPosition(0, hasDescendant(withText("May 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText(com.hyperwallet.android.receipt.R.string.debit))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(0, hasDescendant(withText("Card Load"))))); + matches(atPosition(0, hasDescendant(withText("Card Load"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(0, hasDescendant(withText("- 18.05"))))); + matches(atPosition(0, hasDescendant(withText("- 18.05"))))); onView(withId(R.id.list_receipts)).check( - matches(EspressoUtils.atPosition(0, hasDescendant(withText("May 02, 2019"))))); - onView(withId(R.id.list_receipts)).check(matches(EspressoUtils.atPosition(0, hasDescendant(withText("USD"))))); + matches(atPosition(0, hasDescendant(withText("May 02, 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("USD"))))); onView(withId(R.id.list_receipts)).check(new RecyclerViewCountAssertion(1)); } @@ -187,4 +196,36 @@ public void testListReceipt_userHasNoTransactions() { .check(matches(withText(R.string.title_activity_receipt_list))); //todo: check empty view when it will be ready } + + @Test + public void testListReceipts_checkDateTextOnLocaleChange() { + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("receipt_debit_response.json")).mock(); + setLocale(Locale.ITALY); + // run test + mActivityTestRule.launchActivity(null); + // assert + onView(withId(R.id.list_receipts)) + .check(matches(atPosition(0, hasDescendant(withText("maggio 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("maggio 02, 2019"))))); + mActivityTestRule.finishActivity(); + setLocale(Locale.US); + mActivityTestRule.launchActivity(null); + onView(withId(R.id.list_receipts)) + .check(matches(atPosition(0, hasDescendant(withText("May 2019"))))); + onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText("May 02, 2019"))))); + } + + private void setLocale(Locale locale) { + Context context = ApplicationProvider.getApplicationContext(); + Locale.setDefault(locale); + Resources resources = context.getResources(); + Configuration configuration = resources.getConfiguration(); + if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.JELLY_BEAN) { + configuration.setLocale(locale); + } else { + configuration.locale = locale; + } + resources.updateConfiguration(configuration, resources.getDisplayMetrics()); + } }