Merge "Expanding test coverage for Snackbar" into nyc-dev

GitOrigin-RevId=3af63c3c6f408da963a41a8a0a5179850461087c

PiperOrigin-RevId: 140559854
This commit is contained in:
Kirill Grouchnikov 2016-11-30 03:50:38 +00:00 committed by Travis Collins
parent 2a15110cc4
commit 01a0a7d756
4 changed files with 209 additions and 11 deletions

View File

@ -20,10 +20,13 @@
<uses-sdk
android:minSdkVersion="7"
android:targetSdkVersion="23"
tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
android.support.test.espresso, android.support.test.espresso.idling"/>
<application android:theme="@style/Theme.AppCompat">
<application
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat">
<uses-library android:name="android.test.runner"/>
<activity

View File

@ -20,4 +20,7 @@
<string name="navigate_profile">Profile</string>
<string name="navigate_people">People</string>
<string name="navigate_settings">Settings</string>
<string name="snackbar_text">This is a test message</string>
<string name="snackbar_action">Undo</string>
</resources>

View File

@ -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<View> 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();
}
};
}
}

View File

@ -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<SnackbarActivity> {
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<SnackbarActivity>
(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<SnackbarActivity>
// 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<SnackbarActivity>
@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<SnackbarActivity>
// 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));
}