Home | History | Annotate | Download | only in espresso
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package android.widget.espresso;
     18 
     19 import static android.support.test.espresso.Espresso.onView;
     20 import static android.support.test.espresso.action.ViewActions.click;
     21 import static android.support.test.espresso.assertion.ViewAssertions.matches;
     22 import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
     23 import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
     24 import static android.support.test.espresso.matcher.ViewMatchers.hasFocus;
     25 import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
     26 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
     27 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
     28 import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
     29 import static android.support.test.espresso.matcher.ViewMatchers.withText;
     30 
     31 import static org.hamcrest.Matchers.allOf;
     32 import static org.hamcrest.Matchers.not;
     33 
     34 import android.support.test.espresso.NoMatchingRootException;
     35 import android.support.test.espresso.NoMatchingViewException;
     36 import android.support.test.espresso.ViewInteraction;
     37 import android.support.test.espresso.matcher.ViewMatchers;
     38 import android.view.View;
     39 import android.widget.MenuPopupWindow.MenuDropDownListView;
     40 
     41 import com.android.internal.view.menu.ListMenuItemView;
     42 
     43 import org.hamcrest.Description;
     44 import org.hamcrest.Matcher;
     45 import org.hamcrest.TypeSafeMatcher;
     46 
     47 /**
     48  * Espresso utility methods for the context menu.
     49  */
     50 public final class ContextMenuUtils {
     51     private ContextMenuUtils() {}
     52 
     53     private static ViewInteraction onContextMenu() {
     54         // TODO: Have more reliable way to get context menu.
     55         return onView(ViewMatchers.isAssignableFrom(MenuDropDownListView.class))
     56                 .inRoot(withDecorView(hasFocus()));
     57     }
     58 
     59     /**
     60      * Asserts that the context menu is displayed
     61      *
     62      * @throws AssertionError if the assertion fails
     63      */
     64     private static void assertContextMenuIsDisplayed() {
     65         onContextMenu().check(matches(isDisplayed()));
     66     }
     67 
     68     /**
     69      * Asserts that the context menu is not displayed
     70      *
     71      * @throws AssertionError if the assertion fails
     72      */
     73     public static void assertContextMenuIsNotDisplayed() {
     74         try {
     75             assertContextMenuIsDisplayed();
     76         } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
     77             return;
     78         }
     79         throw new AssertionError("Context menu is displayed");
     80     }
     81 
     82     /**
     83      * Asserts that the context menu contains the specified item and the item has specified enabled
     84      *  state.
     85      *
     86      * @param itemLabel label of the item.
     87      * @param enabled enabled state of the item.
     88      * @throws AssertionError if the assertion fails
     89      */
     90     private static void asssertContextMenuContainsItemWithEnabledState(String itemLabel,
     91             boolean enabled) {
     92         onContextMenu().check(matches(
     93                 hasDescendant(getVisibleMenuItemMatcher(itemLabel, enabled))));
     94     }
     95 
     96     private static Matcher<View> getVisibleMenuItemMatcher(String itemLabel, boolean enabled) {
     97         return allOf(
     98                 isAssignableFrom(ListMenuItemView.class),
     99                 hasDescendant(withText(itemLabel)),
    100                 enabled ? isEnabled() : not(isEnabled()),
    101                 isDisplayingAtLeast(90));
    102     }
    103 
    104     /**
    105      * Asserts that the context menu contains the specified item and the item is enabled.
    106      *
    107      * @param itemLabel label of the item.
    108      * @throws AssertionError if the assertion fails
    109      */
    110     public static void assertContextMenuContainsItemEnabled(String itemLabel) {
    111         asssertContextMenuContainsItemWithEnabledState(itemLabel, true);
    112     }
    113 
    114     /**
    115      * Asserts that the context menu contains the specified item and the item is disabled.
    116      *
    117      * @param itemLabel label of the item.
    118      * @throws AssertionError if the assertion fails
    119      */
    120     public static void assertContextMenuContainsItemDisabled(String itemLabel) {
    121         asssertContextMenuContainsItemWithEnabledState(itemLabel, false);
    122     }
    123 
    124     /**
    125      * Asserts that the context menu window is aligned to a given view with a given offset.
    126      *
    127      * @param anchor Anchor view.
    128      * @param offsetX x offset
    129      * @param offsetY y offset.
    130      * @throws AssertionError if the assertion fails
    131      */
    132     public static void assertContextMenuAlignment(View anchor, int offsetX, int offsetY) {
    133         int [] expectedLocation = new int[2];
    134         anchor.getLocationOnScreen(expectedLocation);
    135         expectedLocation[0] += offsetX;
    136         expectedLocation[1] += offsetY;
    137 
    138         final boolean rtl = anchor.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
    139 
    140         onContextMenu().check(matches(new TypeSafeMatcher<View>() {
    141             @Override
    142             public void describeTo(Description description) {
    143                 description.appendText("root view ");
    144                 description.appendText(rtl ? "right" : "left");
    145                 description.appendText("=");
    146                 description.appendText(Integer.toString(offsetX));
    147                 description.appendText(", top=");
    148                 description.appendText(Integer.toString(offsetY));
    149             }
    150 
    151             @Override
    152             public boolean matchesSafely(View view) {
    153                 View rootView = view.getRootView();
    154                 int [] actualLocation = new int[2];
    155                 rootView.getLocationOnScreen(actualLocation);
    156                 if (rtl) {
    157                     actualLocation[0] += rootView.getWidth();
    158                 }
    159                 return expectedLocation[0] == actualLocation[0]
    160                     && expectedLocation[1] == actualLocation[1];
    161             }
    162         }));
    163     }
    164 
    165     /**
    166      * Check is the menu item is clickable (i.e. visible and enabled).
    167      *
    168      * @param itemLabel Label of the item.
    169      * @return True if the menu item is clickable.
    170      */
    171     public static boolean isMenuItemClickable(String itemLabel) {
    172         try {
    173             onContextMenu().check(matches(
    174                     hasDescendant(getVisibleMenuItemMatcher(itemLabel, true))));
    175             return true;
    176         } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
    177             return false;
    178         }
    179     }
    180 
    181     /**
    182      * Click on a menu item with the specified label
    183      * @param itemLabel Label of the item.
    184      */
    185     public static void clickMenuItem(String itemLabel) {
    186         onView(getVisibleMenuItemMatcher(itemLabel, true))
    187                 .inRoot(withDecorView(hasFocus())).perform(click());
    188     }
    189 }
    190