diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index dd1bbf03c..774a66d71 100755 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -20,10 +20,13 @@ - + Profile People Settings + + This is a test message + Undo \ No newline at end of file diff --git a/tests/src/android/support/design/testutils/TestUtilsActions.java b/tests/src/android/support/design/testutils/TestUtilsActions.java index 320f6e761..e0c6bd326 100644 --- a/tests/src/android/support/design/testutils/TestUtilsActions.java +++ b/tests/src/android/support/design/testutils/TestUtilsActions.java @@ -21,12 +21,14 @@ import android.support.annotation.Nullable; import android.support.design.widget.TabLayout; import android.support.test.espresso.UiController; import android.support.test.espresso.ViewAction; +import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import org.hamcrest.Matcher; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast; public class TestUtilsActions { @@ -73,4 +75,30 @@ public class TestUtilsActions { } }; } + + /** + * Sets layout direction on the view. + */ + public static ViewAction setLayoutDirection(final int layoutDirection) { + return new ViewAction() { + @Override + public Matcher getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return "set layout direction"; + } + + @Override + public void perform(UiController uiController, View view) { + uiController.loopMainThreadUntilIdle(); + + ViewCompat.setLayoutDirection(view, layoutDirection); + + uiController.loopMainThreadUntilIdle(); + } + }; + } } diff --git a/tests/src/android/support/design/widget/SnackbarTest.java b/tests/src/android/support/design/widget/SnackbarTest.java index 9f429a787..145261eba 100644 --- a/tests/src/android/support/design/widget/SnackbarTest.java +++ b/tests/src/android/support/design/widget/SnackbarTest.java @@ -16,28 +16,42 @@ package android.support.design.widget; +import android.content.res.Resources; import android.os.SystemClock; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; import android.support.design.test.R; +import android.support.test.InstrumentationRegistry; import android.support.test.espresso.ViewAction; import android.support.test.espresso.ViewInteraction; -import android.support.test.espresso.action.ViewActions; +import android.support.v4.view.ViewCompat; import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; +import android.text.TextUtils; import android.view.View; import org.junit.Before; import org.junit.Test; +import static android.support.design.testutils.TestUtilsActions.setLayoutDirection; import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.action.ViewActions.*; +import static android.support.test.espresso.matcher.ViewMatchers.*; +import static org.hamcrest.core.AllOf.allOf; import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; public class SnackbarTest extends BaseInstrumentationTestCase { private static final String MESSAGE_TEXT = "Test Message"; + private static final @StringRes int MESSAGE_ID = R.string.snackbar_text; private static final String ACTION_TEXT = "Action"; + private static final @StringRes int ACTION_ID = R.string.snackbar_action; private CoordinatorLayout mCoordinatorLayout; + private static interface DismissAction { + void dismiss(Snackbar snackbar); + } + public SnackbarTest() { super(SnackbarActivity.class); } @@ -48,7 +62,74 @@ public class SnackbarTest extends BaseInstrumentationTestCase (CoordinatorLayout) mActivityTestRule.getActivity().findViewById(R.id.col); } - private void verifyDismissCallback(final ViewInteraction interaction, final ViewAction action, + private void verifySnackbarContent(final Snackbar snackbar, final String expectedMessage, + final String expectedAction) { + // Now show the Snackbar + snackbar.show(); + // Sleep for the animation + SystemClock.sleep(Snackbar.ANIMATION_DURATION + 50); + + // Verify that we're showing the message + withText(expectedMessage).matches(allOf( + isDescendantOfA(isAssignableFrom(Snackbar.SnackbarLayout.class)), + isCompletelyDisplayed())); + + // If the action is not empty, verify that we're showing it + if (!TextUtils.isEmpty(expectedAction)) { + withText(expectedAction).matches(allOf( + isDescendantOfA(isAssignableFrom(Snackbar.SnackbarLayout.class)), + isCompletelyDisplayed())); + } + + // Dismiss the snackbar + InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + snackbar.dismiss(); + } + }); + } + + @Test + @SmallTest + public void testBasicContent() { + // Verify different combinations of snackbar content (message and action) and duration + + final Resources res = mActivityTestRule.getActivity().getResources(); + final String resolvedMessage = res.getString(MESSAGE_ID); + final String resolvedAction = res.getString(ACTION_ID); + + // String message and no action + verifySnackbarContent( + Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_SHORT), + MESSAGE_TEXT, null); + + // Resource message and no action + verifySnackbarContent( + Snackbar.make(mCoordinatorLayout, MESSAGE_ID, Snackbar.LENGTH_LONG), + resolvedMessage, null); + + // String message and string action + verifySnackbarContent( + Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE) + .setAction(ACTION_TEXT, mock(View.OnClickListener.class)), + MESSAGE_TEXT, ACTION_TEXT); + + // String message and resource action + verifySnackbarContent( + Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_SHORT) + .setAction(ACTION_ID, mock(View.OnClickListener.class)), + MESSAGE_TEXT, resolvedAction); + + // Resource message and resource action + verifySnackbarContent( + Snackbar.make(mCoordinatorLayout, MESSAGE_ID, Snackbar.LENGTH_LONG) + .setAction(ACTION_ID, mock(View.OnClickListener.class)), + resolvedMessage, resolvedAction); + } + + private void verifyDismissCallback(final ViewInteraction interaction, + final @Nullable ViewAction action, final @Nullable DismissAction dismissAction, final int length, @Snackbar.Callback.DismissEvent final int expectedEvent) { final Snackbar.Callback mockCallback = mock(Snackbar.Callback.class); final Snackbar snackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, length) @@ -60,7 +141,16 @@ public class SnackbarTest extends BaseInstrumentationTestCase // Sleep for the animation SystemClock.sleep(Snackbar.ANIMATION_DURATION + 50); // ...and perform the UI interaction - interaction.perform(action); + if (action != null) { + interaction.perform(action); + } else if (dismissAction != null) { + InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + dismissAction.dismiss(snackbar); + } + }); + } // Now wait until the Snackbar has been removed from the view hierarchy while (snackbar.isShownOrQueued()) { SystemClock.sleep(20); @@ -73,24 +163,98 @@ public class SnackbarTest extends BaseInstrumentationTestCase @Test @MediumTest - public void testActionClickDismiss() { + public void testDismissViaActionClick() { verifyDismissCallback( onView(withId(R.id.snackbar_action)), - ViewActions.click(), + click(), + null, Snackbar.LENGTH_LONG, Snackbar.Callback.DISMISS_EVENT_ACTION); } @Test @MediumTest - public void testSwipeDismissCallback() { + public void testDismissViaSwipe() { verifyDismissCallback( onView(isAssignableFrom(Snackbar.SnackbarLayout.class)), - ViewActions.swipeRight(), + swipeRight(), + null, Snackbar.LENGTH_LONG, Snackbar.Callback.DISMISS_EVENT_SWIPE); } + @Test + @MediumTest + public void testDismissViaSwipeRtl() { + onView(withId(R.id.col)).perform(setLayoutDirection(ViewCompat.LAYOUT_DIRECTION_RTL)); + if (ViewCompat.getLayoutDirection(mCoordinatorLayout) == ViewCompat.LAYOUT_DIRECTION_RTL) { + // On devices that support RTL layout, the start-to-end dismiss swipe is done + // with swipeLeft() action + verifyDismissCallback( + onView(isAssignableFrom(Snackbar.SnackbarLayout.class)), + swipeLeft(), + null, + Snackbar.LENGTH_LONG, + Snackbar.Callback.DISMISS_EVENT_SWIPE); + } + } + + @Test + @MediumTest + public void testDismissViaApi() { + verifyDismissCallback( + onView(isAssignableFrom(Snackbar.SnackbarLayout.class)), + null, + new DismissAction() { + @Override + public void dismiss(Snackbar snackbar) { + snackbar.dismiss(); + } + }, + Snackbar.LENGTH_LONG, + Snackbar.Callback.DISMISS_EVENT_MANUAL); + } + + @Test + @MediumTest + public void testDismissViaTimeout() { + verifyDismissCallback( + onView(isAssignableFrom(Snackbar.SnackbarLayout.class)), + null, + null, + Snackbar.LENGTH_LONG, + Snackbar.Callback.DISMISS_EVENT_TIMEOUT); + } + + @Test + @MediumTest + public void testDismissViaAnotherSnackbar() { + final Snackbar anotherSnackbar = + Snackbar.make(mCoordinatorLayout, "A different message", Snackbar.LENGTH_SHORT); + + // Our dismiss action is to show another snackbar (and verify that the original snackbar + // is now dismissed with CONSECUTIVE event) + verifyDismissCallback( + onView(isAssignableFrom(Snackbar.SnackbarLayout.class)), + null, + new DismissAction() { + @Override + public void dismiss(Snackbar snackbar) { + anotherSnackbar.show(); + } + }, + Snackbar.LENGTH_LONG, + Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE); + + // And dismiss the second snackbar to get back to clean state + InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + anotherSnackbar.dismiss(); + } + }); + } + @Test @MediumTest public void testActionClickListener() { @@ -103,7 +267,7 @@ public class SnackbarTest extends BaseInstrumentationTestCase // Sleep for the animation SystemClock.sleep(Snackbar.ANIMATION_DURATION + 50); // Perform the action click - onView(withId(R.id.snackbar_action)).perform(ViewActions.click()); + onView(withId(R.id.snackbar_action)).perform(click()); verify(mockClickListener, times(1)).onClick(any(View.class)); }