blob: 8d6746dd2d31dbcb7c08cc004cc897cc86ac6055 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.support.design.widget;
import static android.support.design.testutils.DrawerLayoutActions.closeDrawer;
import static android.support.design.testutils.DrawerLayoutActions.openDrawer;
import static android.support.design.testutils.NavigationViewActions.addHeaderView;
import static android.support.design.testutils.NavigationViewActions.inflateHeaderView;
import static android.support.design.testutils.NavigationViewActions.removeHeaderView;
import static android.support.design.testutils.NavigationViewActions.removeMenuItem;
import static android.support.design.testutils.NavigationViewActions.setCheckedItem;
import static android.support.design.testutils.NavigationViewActions.setIconForMenuItem;
import static android.support.design.testutils.NavigationViewActions.setItemBackground;
import static android.support.design.testutils.NavigationViewActions.setItemBackgroundResource;
import static android.support.design.testutils.NavigationViewActions.setItemIconTintList;
import static android.support.design.testutils.NavigationViewActions.setItemTextAppearance;
import static android.support.design.testutils.NavigationViewActions.setItemTextColor;
import static android.support.design.testutils.TestUtilsActions.reinflateMenu;
import static android.support.design.testutils.TestUtilsActions.restoreHierarchyState;
import static android.support.design.testutils.TestUtilsMatchers.isActionViewOf;
import static android.support.design.testutils.TestUtilsMatchers.isChildOfA;
import static android.support.design.testutils.TestUtilsMatchers.withBackgroundFill;
import static android.support.design.testutils.TestUtilsMatchers.withStartDrawableFilledWith;
import static android.support.design.testutils.TestUtilsMatchers.withTextColor;
import static android.support.design.testutils.TestUtilsMatchers.withTextSize;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.Visibility;
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static android.support.test.espresso.matcher.ViewMatchers.isChecked;
import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.isNotChecked;
import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.core.AllOf.allOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.res.Resources;
import android.os.Build;
import android.os.Parcelable;
import android.support.annotation.ColorInt;
import android.support.annotation.IdRes;
import android.support.design.test.R;
import android.support.design.testutils.TestDrawable;
import android.support.test.filters.MediumTest;
import android.support.test.filters.SdkSuppress;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SwitchCompat;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
@MediumTest
public class NavigationViewTest
extends BaseInstrumentationTestCase<NavigationViewActivity> {
private static final int[] MENU_CONTENT_ITEM_IDS = { R.id.destination_home,
R.id.destination_profile, R.id.destination_people, R.id.destination_settings };
private Map<Integer, String> mMenuStringContent;
private DrawerLayout mDrawerLayout;
private NavigationTestView mNavigationView;
public NavigationViewTest() {
super(NavigationViewActivity.class);
}
@Before
public void setUp() throws Exception {
final NavigationViewActivity activity = mActivityTestRule.getActivity();
mDrawerLayout = (DrawerLayout) activity.findViewById(R.id.drawer_layout);
mNavigationView = (NavigationTestView) mDrawerLayout.findViewById(R.id.start_drawer);
// Close the drawer to reset the state for the next test
onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START));
final Resources res = activity.getResources();
mMenuStringContent = new HashMap<>(MENU_CONTENT_ITEM_IDS.length);
mMenuStringContent.put(R.id.destination_home, res.getString(R.string.navigate_home));
mMenuStringContent.put(R.id.destination_profile, res.getString(R.string.navigate_profile));
mMenuStringContent.put(R.id.destination_people, res.getString(R.string.navigate_people));
mMenuStringContent.put(R.id.destination_settings,
res.getString(R.string.navigate_settings));
}
@Test
public void testBasics() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
// Check the contents of the Menu object
final Menu menu = mNavigationView.getMenu();
assertNotNull("Menu should not be null", menu);
assertEquals("Should have matching number of items", MENU_CONTENT_ITEM_IDS.length + 1,
menu.size());
for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
final MenuItem currItem = menu.getItem(i);
assertEquals("ID for Item #" + i, MENU_CONTENT_ITEM_IDS[i], currItem.getItemId());
}
// Check that we have the expected menu items in our NavigationView
for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
onView(allOf(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
isDescendantOfA(withId(R.id.start_drawer)))).check(matches(isDisplayed()));
}
}
@Test
public void testWillNotDraw() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
if (Build.VERSION.SDK_INT >= 21) {
if (mNavigationView.hasSystemWindowInsets()) {
assertFalse(mNavigationView.willNotDraw());
} else {
assertTrue(mNavigationView.willNotDraw());
}
} else {
assertTrue(mNavigationView.willNotDraw());
}
}
@Test
public void testTextAppearance() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
final Resources res = mActivityTestRule.getActivity().getResources();
final int defaultTextSize = res.getDimensionPixelSize(R.dimen.text_medium_size);
// Check the default style of the menu items in our NavigationView
for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
onView(allOf(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
isDescendantOfA(withId(R.id.start_drawer)))).check(
matches(withTextSize(defaultTextSize)));
}
// Set a new text appearance on our NavigationView
onView(withId(R.id.start_drawer)).perform(setItemTextAppearance(R.style.TextSmallStyle));
// And check that all the menu items have the new style
final int newTextSize = res.getDimensionPixelSize(R.dimen.text_small_size);
for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
onView(allOf(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
isDescendantOfA(withId(R.id.start_drawer)))).check(
matches(withTextSize(newTextSize)));
}
}
@Test
public void testTextColor() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
final Resources res = mActivityTestRule.getActivity().getResources();
final @ColorInt int defaultTextColor = ResourcesCompat.getColor(res,
R.color.emerald_text, null);
// Check the default text color of the menu items in our NavigationView
for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
onView(allOf(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
isDescendantOfA(withId(R.id.start_drawer)))).check(
matches(withTextColor(defaultTextColor)));
}
// Set a new text color on our NavigationView
onView(withId(R.id.start_drawer)).perform(setItemTextColor(
ResourcesCompat.getColorStateList(res, R.color.color_state_list_lilac, null)));
// And check that all the menu items have the new color
final @ColorInt int newTextColor = ResourcesCompat.getColor(res,
R.color.lilac_default, null);
for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
onView(allOf(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
isDescendantOfA(withId(R.id.start_drawer)))).check(
matches(withTextColor(newTextColor)));
}
}
@Test
public void testBackground() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
final Resources res = mActivityTestRule.getActivity().getResources();
final @ColorInt int defaultFillColor = ResourcesCompat.getColor(res,
R.color.sand_default, null);
// Check the default fill color of the menu items in our NavigationView
for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
// Note that here we're tying ourselves to the implementation details of the
// internal structure of the NavigationView. Specifically, we're looking at the
// direct child of RecyclerView which is expected to have the background set
// on it. If the internal implementation of NavigationView changes, the second
// Matcher below will need to be tweaked.
Matcher menuItemMatcher = allOf(
hasDescendant(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i]))),
isChildOfA(isAssignableFrom(RecyclerView.class)),
isDescendantOfA(withId(R.id.start_drawer)));
onView(menuItemMatcher).check(matches(withBackgroundFill(defaultFillColor)));
}
// Set a new background (flat fill color) on our NavigationView
onView(withId(R.id.start_drawer)).perform(setItemBackgroundResource(
R.drawable.test_background_blue));
// And check that all the menu items have the new fill
final @ColorInt int newFillColorBlue = ResourcesCompat.getColor(res,
R.color.test_blue, null);
for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
Matcher menuItemMatcher = allOf(
hasDescendant(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i]))),
isChildOfA(isAssignableFrom(RecyclerView.class)),
isDescendantOfA(withId(R.id.start_drawer)));
onView(menuItemMatcher).check(matches(withBackgroundFill(newFillColorBlue)));
}
// Set another new background on our NavigationView
onView(withId(R.id.start_drawer)).perform(setItemBackground(
ResourcesCompat.getDrawable(res, R.drawable.test_background_green, null)));
// And check that all the menu items have the new fill
final @ColorInt int newFillColorGreen = ResourcesCompat.getColor(res,
R.color.test_green, null);
for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
Matcher menuItemMatcher = allOf(
hasDescendant(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i]))),
isChildOfA(isAssignableFrom(RecyclerView.class)),
isDescendantOfA(withId(R.id.start_drawer)));
onView(menuItemMatcher).check(matches(withBackgroundFill(newFillColorGreen)));
}
}
@Test
public void testIconTinting() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
final Resources res = mActivityTestRule.getActivity().getResources();
final @ColorInt int redFill = ResourcesCompat.getColor(res, R.color.test_red, null);
final @ColorInt int greenFill = ResourcesCompat.getColor(res, R.color.test_green, null);
final @ColorInt int blueFill = ResourcesCompat.getColor(res, R.color.test_blue, null);
final int iconSize = res.getDimensionPixelSize(R.dimen.drawable_small_size);
onView(withId(R.id.start_drawer)).perform(setIconForMenuItem(R.id.destination_home,
new TestDrawable(redFill, iconSize, iconSize)));
onView(withId(R.id.start_drawer)).perform(setIconForMenuItem(R.id.destination_profile,
new TestDrawable(greenFill, iconSize, iconSize)));
onView(withId(R.id.start_drawer)).perform(setIconForMenuItem(R.id.destination_people,
new TestDrawable(blueFill, iconSize, iconSize)));
final @ColorInt int defaultTintColor = ResourcesCompat.getColor(res,
R.color.emerald_translucent, null);
// We're allowing a margin of error in checking the color of the items' icons.
// This is due to the translucent color being used in the icon tinting
// and off-by-one discrepancies of SRC_IN when it's compositing
// translucent color. Note that all the checks below are written for the current
// logic on NavigationView that uses the default SRC_IN tint mode - effectively
// replacing all non-transparent pixels in the destination (original icon) with
// our translucent tint color.
final int allowedComponentVariance = 1;
// Note that here we're tying ourselves to the implementation details of the
// internal structure of the NavigationView. Specifically, we're checking the
// start drawable of the text view with the specific text. If the internal
// implementation of NavigationView changes, the second Matcher in the lookups
// below will need to be tweaked.
onView(allOf(withText(mMenuStringContent.get(R.id.destination_home)),
isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
withStartDrawableFilledWith(defaultTintColor, allowedComponentVariance)));
onView(allOf(withText(mMenuStringContent.get(R.id.destination_profile)),
isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
withStartDrawableFilledWith(defaultTintColor, allowedComponentVariance)));
onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
withStartDrawableFilledWith(defaultTintColor, allowedComponentVariance)));
final @ColorInt int newTintColor = ResourcesCompat.getColor(res,
R.color.red_translucent, null);
onView(withId(R.id.start_drawer)).perform(setItemIconTintList(
ResourcesCompat.getColorStateList(res, R.color.color_state_list_red_translucent,
null)));
// Check that all menu items with icons now have icons tinted with the newly set color
onView(allOf(withText(mMenuStringContent.get(R.id.destination_home)),
isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
withStartDrawableFilledWith(newTintColor, allowedComponentVariance)));
onView(allOf(withText(mMenuStringContent.get(R.id.destination_profile)),
isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
withStartDrawableFilledWith(newTintColor, allowedComponentVariance)));
onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
withStartDrawableFilledWith(newTintColor, allowedComponentVariance)));
// And now remove all icon tinting
onView(withId(R.id.start_drawer)).perform(setItemIconTintList(null));
// And verify that all menu items with icons now have the original colors for their icons.
// Note that since there is no tinting at this point, we don't allow any color variance
// in these checks.
onView(allOf(withText(mMenuStringContent.get(R.id.destination_home)),
isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
withStartDrawableFilledWith(redFill, 0)));
onView(allOf(withText(mMenuStringContent.get(R.id.destination_profile)),
isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
withStartDrawableFilledWith(greenFill, 0)));
onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
withStartDrawableFilledWith(blueFill, 0)));
}
/**
* Gets the list of header IDs (which can be empty) and verifies that the actual header content
* of our navigation view matches the expected header content.
*/
private void verifyHeaders(@IdRes int ... expectedHeaderIds) {
final int expectedHeaderCount = (expectedHeaderIds != null) ? expectedHeaderIds.length : 0;
final int actualHeaderCount = mNavigationView.getHeaderCount();
assertEquals("Header count", expectedHeaderCount, actualHeaderCount);
if (expectedHeaderCount > 0) {
for (int i = 0; i < expectedHeaderCount; i++) {
final View currentHeader = mNavigationView.getHeaderView(i);
assertEquals("Header at #" + i, expectedHeaderIds[i], currentHeader.getId());
}
}
}
@Test
public void testHeaders() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
// We should have no headers at the start
verifyHeaders();
// Inflate one header and check that it's there in the navigation view
onView(withId(R.id.start_drawer)).perform(
inflateHeaderView(R.layout.design_navigation_view_header1));
verifyHeaders(R.id.header1);
final LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
// Add one more header and check that it's there in the navigation view
onView(withId(R.id.start_drawer)).perform(
addHeaderView(inflater, R.layout.design_navigation_view_header2));
verifyHeaders(R.id.header1, R.id.header2);
final View header1 = mNavigationView.findViewById(R.id.header1);
// Remove the first header and check that we still have the second header
onView(withId(R.id.start_drawer)).perform(removeHeaderView(header1));
verifyHeaders(R.id.header2);
// Add one more header and check that we now have two headers
onView(withId(R.id.start_drawer)).perform(
inflateHeaderView(R.layout.design_navigation_view_header3));
verifyHeaders(R.id.header2, R.id.header3);
// Add another "copy" of the header from the just-added layout and check that we now
// have three headers
onView(withId(R.id.start_drawer)).perform(
addHeaderView(inflater, R.layout.design_navigation_view_header3));
verifyHeaders(R.id.header2, R.id.header3, R.id.header3);
}
@SdkSuppress(minSdkVersion = 11)
@Test
public void testHeaderState() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
// Inflate a header with a toggle switch and check that it's there in the navigation view
onView(withId(R.id.start_drawer)).perform(
inflateHeaderView(R.layout.design_navigation_view_header_switch));
verifyHeaders(R.id.header_frame);
onView(withId(R.id.header_toggle))
.check(matches(isNotChecked()))
.perform(click())
.check(matches(isChecked()));
// Save the current state
SparseArray<Parcelable> container = new SparseArray<>();
mNavigationView.saveHierarchyState(container);
// Remove the header
final View header = mNavigationView.findViewById(R.id.header_frame);
onView(withId(R.id.start_drawer)).perform(removeHeaderView(header));
verifyHeaders();
// Inflate the header again
onView(withId(R.id.start_drawer)).perform(
inflateHeaderView(R.layout.design_navigation_view_header_switch));
verifyHeaders(R.id.header_frame);
// Restore the saved state
onView(withId(R.id.start_drawer)).perform(
restoreHierarchyState(container));
// Confirm that the state was restored
onView(withId(R.id.header_toggle))
.check(matches(isChecked()));
}
@SdkSuppress(minSdkVersion = 11)
@Test
public void testActionViewState() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
final Menu menu = mNavigationView.getMenu();
onView(isActionViewOf(menu, R.id.destination_people))
.check(matches(isNotChecked())) // Not checked by default
.perform(click()) // Check it
.check(matches(isChecked()));
// Remove the other action view to simulate the case where it is not yet inflated
onView(isActionViewOf(menu, R.id.destination_custom))
.check(matches(isDisplayed()));
onView(withId(R.id.start_drawer))
.perform(removeMenuItem(R.id.destination_custom));
// Save the current state
SparseArray<Parcelable> container = new SparseArray<>();
mNavigationView.saveHierarchyState(container);
// Restore the saved state
onView(withId(R.id.start_drawer))
.perform(reinflateMenu(R.menu.navigation_view_content))
.perform(restoreHierarchyState(container));
// Checked state should be restored
onView(isActionViewOf(menu, R.id.destination_people))
.check(matches(isChecked()));
}
@Test
public void testNavigationSelectionListener() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
// Click one of our items
onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
isDescendantOfA(withId(R.id.start_drawer)))).perform(click());
// Check that the drawer is still open
assertTrue("Drawer is still open after click",
mDrawerLayout.isDrawerOpen(GravityCompat.START));
// Register a listener
NavigationView.OnNavigationItemSelectedListener mockedListener =
mock(NavigationView.OnNavigationItemSelectedListener.class);
mNavigationView.setNavigationItemSelectedListener(mockedListener);
// Click one of our items
onView(allOf(withText(mMenuStringContent.get(R.id.destination_profile)),
isDescendantOfA(withId(R.id.start_drawer)))).perform(click());
// Check that the drawer is still open
assertTrue("Drawer is still open after click",
mDrawerLayout.isDrawerOpen(GravityCompat.START));
// And that our listener has been notified of the click
verify(mockedListener, times(1)).onNavigationItemSelected(
mNavigationView.getMenu().findItem(R.id.destination_profile));
// Set null listener to test that the next click is not going to notify the
// previously set listener
mNavigationView.setNavigationItemSelectedListener(null);
// Click one of our items
onView(allOf(withText(mMenuStringContent.get(R.id.destination_settings)),
isDescendantOfA(withId(R.id.start_drawer)))).perform(click());
// Check that the drawer is still open
assertTrue("Drawer is still open after click",
mDrawerLayout.isDrawerOpen(GravityCompat.START));
// And that our previous listener has not been notified of the click
verifyNoMoreInteractions(mockedListener);
}
private void verifyCheckedAppearance(@IdRes int checkedItemId,
@ColorInt int uncheckedItemForeground, @ColorInt int checkedItemForeground,
@ColorInt int uncheckedItemBackground, @ColorInt int checkedItemBackground) {
for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
final boolean expectedToBeChecked = (MENU_CONTENT_ITEM_IDS[i] == checkedItemId);
final @ColorInt int expectedItemForeground =
expectedToBeChecked ? checkedItemForeground : uncheckedItemForeground;
final @ColorInt int expectedItemBackground =
expectedToBeChecked ? checkedItemBackground : uncheckedItemBackground;
// For the background fill check we need to select a view that has its background
// set by the current implementation (see disclaimer in testBackground)
Matcher menuItemMatcher = allOf(
hasDescendant(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i]))),
isChildOfA(isAssignableFrom(RecyclerView.class)),
isDescendantOfA(withId(R.id.start_drawer)));
onView(menuItemMatcher).check(matches(withBackgroundFill(expectedItemBackground)));
// And for the foreground color check we need to select a view with the text content
Matcher menuItemTextMatcher = allOf(
withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
isDescendantOfA(withId(R.id.start_drawer)));
onView(menuItemTextMatcher).check(matches(withTextColor(expectedItemForeground)));
}
}
@Test
public void testCheckedAppearance() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
// Reconfigure our navigation view to use foreground (text) and background visuals
// with explicitly different colors for the checked state
final Resources res = mActivityTestRule.getActivity().getResources();
onView(withId(R.id.start_drawer)).perform(setItemTextColor(
ResourcesCompat.getColorStateList(res, R.color.color_state_list_sand, null)));
onView(withId(R.id.start_drawer)).perform(setItemBackgroundResource(
R.drawable.test_drawable_state_list));
final @ColorInt int uncheckedItemForeground = ResourcesCompat.getColor(res,
R.color.sand_default, null);
final @ColorInt int checkedItemForeground = ResourcesCompat.getColor(res,
R.color.sand_checked, null);
final @ColorInt int uncheckedItemBackground = ResourcesCompat.getColor(res,
R.color.test_green, null);
final @ColorInt int checkedItemBackground = ResourcesCompat.getColor(res,
R.color.test_blue, null);
// Verify that all items are rendered with unchecked visuals
verifyCheckedAppearance(0, uncheckedItemForeground, checkedItemForeground,
uncheckedItemBackground, checkedItemBackground);
// Mark one of the items as checked
onView(withId(R.id.start_drawer)).perform(setCheckedItem(R.id.destination_profile));
// And verify that it's now rendered with checked visuals
verifyCheckedAppearance(R.id.destination_profile,
uncheckedItemForeground, checkedItemForeground,
uncheckedItemBackground, checkedItemBackground);
// Register a navigation listener that "marks" the selected item
mNavigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem item) {
return true;
}
});
// Click one of our items
onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
isDescendantOfA(withId(R.id.start_drawer)))).perform(click());
// and verify that it's now checked
verifyCheckedAppearance(R.id.destination_people,
uncheckedItemForeground, checkedItemForeground,
uncheckedItemBackground, checkedItemBackground);
// Register a navigation listener that doesn't "mark" the selected item
mNavigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem item) {
return false;
}
});
// Click another items
onView(allOf(withText(mMenuStringContent.get(R.id.destination_settings)),
isDescendantOfA(withId(R.id.start_drawer)))).perform(click());
// and verify that the checked state remains on the previously clicked item
// since the current navigation listener returns false from its callback
// implementation
verifyCheckedAppearance(R.id.destination_people,
uncheckedItemForeground, checkedItemForeground,
uncheckedItemBackground, checkedItemBackground);
}
@Test
public void testActionLayout() {
// Open our drawer
onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
// There are four conditions to "find" the menu item with action layout (switch):
// 1. Is in the NavigationView
// 2. Is direct child of a class that extends RecyclerView
// 3. Has a child with "people" text
// 4. Has fully displayed child that extends SwitchCompat
// Note that condition 2 makes a certain assumption about the internal implementation
// details of the NavigationMenu, while conditions 3 and 4 aim to be as generic as
// possible and to not rely on the internal details of the current layout implementation
// of an individual menu item in NavigationMenu.
Matcher menuItemMatcher = allOf(
isDescendantOfA(withId(R.id.start_drawer)),
isChildOfA(isAssignableFrom(RecyclerView.class)),
hasDescendant(withText(mMenuStringContent.get(R.id.destination_people))),
hasDescendant(allOf(
isAssignableFrom(SwitchCompat.class),
isCompletelyDisplayed())));
// While we don't need to perform any action on our row, the invocation of perform()
// makes our matcher actually run. If for some reason NavigationView fails to inflate and
// display our SwitchCompat action layout, the next line will fail in the matcher pass.
onView(menuItemMatcher).perform(click());
// Check that the full custom view is displayed without title and icon.
final Resources res = mActivityTestRule.getActivity().getResources();
Matcher customItemMatcher = allOf(
isDescendantOfA(withId(R.id.start_drawer)),
isChildOfA(isAssignableFrom(RecyclerView.class)),
hasDescendant(withText(res.getString(R.string.navigate_custom))),
hasDescendant(allOf(
isAssignableFrom(TextView.class),
withEffectiveVisibility(Visibility.GONE))));
onView(customItemMatcher).perform(click());
}
}