12da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov/* 22da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * Copyright (C) 2016 The Android Open Source Project 32da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * 42da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * Licensed under the Apache License, Version 2.0 (the "License"); 52da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * you may not use this file except in compliance with the License. 62da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * You may obtain a copy of the License at 72da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * 82da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * http://www.apache.org/licenses/LICENSE-2.0 92da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * 102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * Unless required by applicable law or agreed to in writing, software 112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * distributed under the License is distributed on an "AS IS" BASIS, 122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * See the License for the specific language governing permissions and 142da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * limitations under the License. 152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov */ 162da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovpackage android.support.v7.widget; 172da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 1870058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.Espresso.onData; 1970058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.Espresso.onView; 2070058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.action.ViewActions.click; 2170058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.assertion.ViewAssertions.doesNotExist; 2270058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.assertion.ViewAssertions.matches; 2370058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.RootMatchers.isPlatformPopup; 2470058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.RootMatchers.withDecorView; 2570058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; 2670058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.ViewMatchers.withClassName; 2770058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.ViewMatchers.withId; 2870058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.ViewMatchers.withText; 2970058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas 3070058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.hamcrest.Matchers.allOf; 3170058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.hamcrest.Matchers.anything; 3270058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.hamcrest.core.Is.is; 3370058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.hamcrest.core.IsNot.not; 3470058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.junit.Assert.assertEquals; 3570058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.junit.Assert.assertNotNull; 3670058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.mockito.Mockito.any; 3770058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.mockito.Mockito.mock; 3870058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.mockito.Mockito.never; 3970058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.mockito.Mockito.times; 4070058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.mockito.Mockito.verify; 410f3f71c172e87b05b0a6b766cf129f4be56179d5Chris Banes 422da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.app.Instrumentation; 432da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.content.res.Resources; 442da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.graphics.Rect; 452da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.graphics.drawable.Drawable; 462da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.os.SystemClock; 4770058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport android.support.test.InstrumentationRegistry; 482da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.support.test.espresso.Root; 492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.support.test.espresso.UiController; 502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.support.test.espresso.ViewAction; 5178f46d60d55f30fb384aa9da9eddb588f368e092Aurimas Liutikasimport android.support.test.filters.LargeTest; 524bb57b2c7c5e8fa607119bd5089535d5862cb979Aurimas Liutikasimport android.support.test.filters.MediumTest; 538d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikasimport android.support.test.rule.ActivityTestRule; 548d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikasimport android.support.test.runner.AndroidJUnit4; 552748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikasimport android.support.testutils.PollingCheck; 5644ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheevimport android.support.v4.view.MenuItemCompat; 572da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.support.v7.appcompat.test.R; 5844ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheevimport android.text.TextUtils; 592da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.view.MenuInflater; 602da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.view.MenuItem; 612da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.view.MotionEvent; 622da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.view.View; 632da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.view.ViewParent; 642da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.widget.Button; 652da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.widget.FrameLayout; 662da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 6770058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.hamcrest.Description; 6870058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.hamcrest.Matcher; 6970058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.hamcrest.Matchers; 7070058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.hamcrest.TypeSafeMatcher; 71c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikasimport org.junit.After; 7270058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.junit.Before; 738d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikasimport org.junit.Rule; 7470058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.junit.Test; 758d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikasimport org.junit.runner.RunWith; 768d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikas 778d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikas@RunWith(AndroidJUnit4.class) 788d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikaspublic class PopupMenuTest { 798d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikas @Rule 808d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikas public final ActivityTestRule<PopupTestActivity> mActivityTestRule = 818d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikas new ActivityTestRule<>(PopupTestActivity.class); 822da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 832da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Since PopupMenu doesn't expose any access to the underlying 842da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // implementation (like ListPopupWindow.getListView), we're relying on the 8594940fe6cff4af4f084b8cb5692e0501e3fe7912Kirill Grouchnikov // class name of the list view from MenuPopupWindow that is being used 862da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // in PopupMenu. This is not the cleanest, but it's not making any assumptions 872da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // on the platform-specific details of the popup windows. 882da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov private static final String DROP_DOWN_CLASS_NAME = 8994940fe6cff4af4f084b8cb5692e0501e3fe7912Kirill Grouchnikov "android.support.v7.widget.MenuPopupWindow$MenuDropDownListView"; 902da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov private FrameLayout mContainer; 912da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 922da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov private Button mButton; 932da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 942da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov private PopupMenu mPopupMenu; 952da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 96eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov private Resources mResources; 97eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov 98eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov private View mMainDecorView; 99eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov 1000f3f71c172e87b05b0a6b766cf129f4be56179d5Chris Banes @Before 1012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void setUp() throws Exception { 102eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov final PopupTestActivity activity = mActivityTestRule.getActivity(); 1038d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikas mContainer = activity.findViewById(R.id.container); 1048d3b808ab4720ef4e9d58f2bba4e31f741d3898cAurimas Liutikas mButton = mContainer.findViewById(R.id.test_button); 105eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov mResources = mActivityTestRule.getActivity().getResources(); 106eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov mMainDecorView = mActivityTestRule.getActivity().getWindow().getDecorView(); 1072da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 1082da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 109c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas @After 110c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas public void tearDown() { 111c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas if (mPopupMenu != null) { 112c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 113c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas @Override 114c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas public void run() { 115c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas mPopupMenu.dismiss(); 116c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas } 117c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas }); 118c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas } 119c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas } 120c60b5de9dc03985a6aa9028dbeb983e1013402a0Aurimas Liutikas 1212da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Test 1224bb57b2c7c5e8fa607119bd5089535d5862cb979Aurimas Liutikas @MediumTest 12344ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev public void testBasicContent() throws Throwable { 1242da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final Builder menuBuilder = new Builder(); 1252da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov menuBuilder.wireToActionButton(); 1262da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 1272da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withId(R.id.test_button)).perform(click()); 1282da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov assertNotNull("Popup menu created", mPopupMenu); 12944ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev 13044ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev final MenuItem hightlightItem = mPopupMenu.getMenu().findItem(R.id.action_highlight); 13144ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev assertEquals(mResources.getString(R.string.popup_menu_highlight_description), 13244ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev MenuItemCompat.getContentDescription(hightlightItem)); 13344ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev assertEquals(mResources.getString(R.string.popup_menu_highlight_tooltip), 13444ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev MenuItemCompat.getTooltipText(hightlightItem)); 13544ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev 13644ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev final MenuItem editItem = mPopupMenu.getMenu().findItem(R.id.action_edit); 13744ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev assertNotNull(MenuItemCompat.getContentDescription(hightlightItem)); 13844ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev assertNotNull(MenuItemCompat.getTooltipText(hightlightItem)); 13944ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev mActivityTestRule.runOnUiThread(new Runnable() { 14044ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev @Override 14144ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev public void run() { 14244ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev MenuItemCompat.setContentDescription(editItem, 14344ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev mResources.getString(R.string.popup_menu_edit_description)); 14444ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev MenuItemCompat.setTooltipText(editItem, 14544ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev mResources.getString(R.string.popup_menu_edit_tooltip)); 14644ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev } 14744ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev }); 14844ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev 1492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Unlike ListPopupWindow, PopupMenu doesn't have an API to check whether it is showing. 1502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Use a custom matcher to check the visibility of the drop down list view instead. 1512da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))) 1522da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .inRoot(isPlatformPopup()).check(matches(isDisplayed())); 1532da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 1542da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Note that MenuItem.isVisible() refers to the current "data" visibility state 1552da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // and not the "on screen" visibility state. This is why we're testing the display 1562da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // visibility of our main and sub menu items. 15770058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas // onData(anything()).atPosition(x) operates on the content of the popup. It is used to 15870058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas // scroll the popup if necessary to make sure the menu item views are visible. 15970058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas onData(anything()).atPosition(0).check(matches(isDisplayed())); 160eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_highlight))) 161eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(withDecorView(not(is(mMainDecorView)))) 16244ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev .check(matches(isDisplayed())) 16344ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev .check(matches(selfOrParentWithContentDescription( 16444ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev mResources.getString(R.string.popup_menu_highlight_description)))); 16570058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas onData(anything()).atPosition(1).check(matches(isDisplayed())); 166eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_edit))) 167eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(withDecorView(not(is(mMainDecorView)))) 16844ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev .check(matches(isDisplayed())) 16944ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev .check(matches(selfOrParentWithContentDescription( 17044ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev mResources.getString(R.string.popup_menu_edit_description)))); 17170058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas onData(anything()).atPosition(2).check(matches(isDisplayed())); 172eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_delete))) 173eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(withDecorView(not(is(mMainDecorView)))) 1742da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(matches(isDisplayed())); 17570058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas onData(anything()).atPosition(3).check(matches(isDisplayed())); 176eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_ignore))) 177eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(withDecorView(not(is(mMainDecorView)))) 1782da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(matches(isDisplayed())); 17970058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas onData(anything()).atPosition(4).check(matches(isDisplayed())); 180eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_share))) 181eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(withDecorView(not(is(mMainDecorView)))) 1822da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(matches(isDisplayed())); 18370058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas onData(anything()).atPosition(5).check(matches(isDisplayed())); 184eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_print))) 185eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(withDecorView(not(is(mMainDecorView)))) 1862da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(matches(isDisplayed())); 1872da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 1882da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Share submenu items should not be visible 189eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_share_email))) 190eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(withDecorView(not(is(mMainDecorView)))) 1912da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(doesNotExist()); 192eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_share_circles))) 193eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(withDecorView(not(is(mMainDecorView)))) 1942da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(doesNotExist()); 1952da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 1962da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 1972da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov /** 1982da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * Returns the location of our popup menu in its window. 1992da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov */ 2002da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov private int[] getPopupLocationInWindow() { 2012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final int[] location = new int[2]; 2022da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))) 2032da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .inRoot(isPlatformPopup()).perform(new ViewAction() { 2042da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 2052da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public Matcher<View> getConstraints() { 2062da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return isDisplayed(); 2072da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2082da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 2092da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 2102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public String getDescription() { 2112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return "Popup matcher"; 2122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2132da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 2142da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 2152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void perform(UiController uiController, View view) { 2162da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov view.getLocationInWindow(location); 2172da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2182da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov }); 2192da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return location; 2202da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2212da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 2222da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov /** 2232da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * Returns the location of our popup menu on the screen. 2242da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov */ 2252da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov private int[] getPopupLocationOnScreen() { 2262da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final int[] location = new int[2]; 2272da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))) 2282da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .inRoot(isPlatformPopup()).perform(new ViewAction() { 2292da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 2302da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public Matcher<View> getConstraints() { 2312da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return isDisplayed(); 2322da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2332da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 2342da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 2352da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public String getDescription() { 2362da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return "Popup matcher"; 2372da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2382da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 2392da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 2402da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void perform(UiController uiController, View view) { 2412da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov view.getLocationOnScreen(location); 2422da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2432da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov }); 2442da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return location; 2452da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2462da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 2472da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov /** 2482da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * Returns the combined padding around the content of our popup menu. 2492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov */ 2502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov private Rect getPopupPadding() { 2512da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final Rect result = new Rect(); 2522da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))) 2532da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .inRoot(isPlatformPopup()).perform(new ViewAction() { 2542da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 2552da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public Matcher<View> getConstraints() { 2562da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return isDisplayed(); 2572da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2582da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 2592da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 2602da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public String getDescription() { 2612da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return "Popup matcher"; 2622da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2632da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 2642da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 2652da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void perform(UiController uiController, View view) { 2662da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Traverse the parent hierarchy and combine all their paddings 2672da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov result.setEmpty(); 2682da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final Rect current = new Rect(); 2692da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov while (true) { 2702da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov ViewParent parent = view.getParent(); 2712da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov if (parent == null || !(parent instanceof View)) { 2722da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return; 2732da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2742da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 2752da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov view = (View) parent; 2762da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov Drawable currentBackground = view.getBackground(); 2772da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov if (currentBackground != null) { 2782da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov currentBackground.getPadding(current); 2792da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov result.left += current.left; 2802da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov result.right += current.right; 2812da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov result.top += current.top; 2822da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov result.bottom += current.bottom; 2832da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2842da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2852da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2862da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov }); 2872da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return result; 2882da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2892da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 2902da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov /** 2912da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * Returns a root matcher that matches roots that have window focus on their decor view. 2922da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov */ 2932da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov private static Matcher<Root> hasWindowFocus() { 2942da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return new TypeSafeMatcher<Root>() { 2952da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 2962da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void describeTo(Description description) { 2972da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov description.appendText("has window focus"); 2982da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 2992da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3002da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 3012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public boolean matchesSafely(Root root) { 3022da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov View rootView = root.getDecorView(); 3032da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return rootView.hasWindowFocus(); 3042da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 3052da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov }; 3062da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 3072da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3082da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Test 30978f46d60d55f30fb384aa9da9eddb588f368e092Aurimas Liutikas @LargeTest 3102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void testAnchoring() { 3112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov Builder menuBuilder = new Builder(); 3122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov menuBuilder.wireToActionButton(); 3132da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3142da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withId(R.id.test_button)).perform(click()); 3152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3162da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final int[] anchorOnScreenXY = new int[2]; 3172da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final int[] popupOnScreenXY = getPopupLocationOnScreen(); 3182da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final int[] popupInWindowXY = getPopupLocationInWindow(); 3192da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final Rect popupPadding = getPopupPadding(); 3202da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3212da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov mButton.getLocationOnScreen(anchorOnScreenXY); 3222da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3232da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Allow for off-by-one mismatch in anchoring 3242748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikas 3252748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikas // See if Anchoring Y gets animated to the expected value. 3262748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikas PollingCheck.waitFor(1000, new PollingCheck.PollingCheckCondition() { 3272748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikas @Override 3282748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikas public boolean canProceed() { 3292748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikas final int y = anchorOnScreenXY[1] + popupInWindowXY[1] + mButton.getHeight() 3302748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikas - popupPadding.top; 3312748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikas return Math.abs(y - popupOnScreenXY[1]) <= 1; 3322748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikas } 3332748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikas }); 3342748072a718079fc19991ccfe66149e784a4ddf1Aurimas Liutikas 3352da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov assertEquals("Anchoring X", anchorOnScreenXY[0] + popupInWindowXY[0], 3362da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov popupOnScreenXY[0], 1); 337d7d01383deb3aac00ea54aab340cb43ff1ba3984Kirill Grouchnikov assertEquals("Anchoring Y", 338d7d01383deb3aac00ea54aab340cb43ff1ba3984Kirill Grouchnikov anchorOnScreenXY[1] + popupInWindowXY[1] + mButton.getHeight() - popupPadding.top, 33994940fe6cff4af4f084b8cb5692e0501e3fe7912Kirill Grouchnikov popupOnScreenXY[1], 1); 3402da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 3412da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3422da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Test 3434bb57b2c7c5e8fa607119bd5089535d5862cb979Aurimas Liutikas @MediumTest 34442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas public void testDismissalViaAPI() throws Throwable { 3452da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov Builder menuBuilder = new Builder().withDismissListener(); 3462da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov menuBuilder.wireToActionButton(); 3472da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3482da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withId(R.id.test_button)).perform(click()); 3492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Since PopupMenu is not a View, we can't use Espresso's view actions to invoke 3512da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // the dismiss() API 35242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 3532da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 3542da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void run() { 3552da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov mPopupMenu.dismiss(); 3562da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 3572da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov }); 3582da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 35931c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu); 36031c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov 3612da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Unlike ListPopupWindow, PopupMenu doesn't have an API to check whether it is showing. 3622da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Use a custom matcher to check the visibility of the drop down list view instead. 3632da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist()); 3642da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 3652da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3662da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Test 3674bb57b2c7c5e8fa607119bd5089535d5862cb979Aurimas Liutikas @MediumTest 3682da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void testDismissalViaTouch() throws Throwable { 3692da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov Builder menuBuilder = new Builder().withDismissListener(); 3702da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov menuBuilder.wireToActionButton(); 3712da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3722da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withId(R.id.test_button)).perform(click()); 3732da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3742da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Determine the location of the popup on the screen so that we can emulate 3752da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // a tap outside of its bounds to dismiss it 3762da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final int[] popupOnScreenXY = getPopupLocationOnScreen(); 3772da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final Rect popupPadding = getPopupPadding(); 3782da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3792da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3802da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov int emulatedTapX = popupOnScreenXY[0] - popupPadding.left - 20; 3812da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov int emulatedTapY = popupOnScreenXY[1] + popupPadding.top + 20; 3822da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3832da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // The logic below uses Instrumentation to emulate a tap outside the bounds of the 3842da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // displayed popup menu. This tap is then treated by the framework to be "split" as 3852da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // the ACTION_OUTSIDE for the popup itself, as well as DOWN / MOVE / UP for the underlying 3862da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // view root if the popup is not modal. 3872da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // It is not correct to emulate these two sequences separately in the test, as it 3882da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // wouldn't emulate the user-facing interaction for this test. Note that usage 3892da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // of Instrumentation is necessary here since Espresso's actions operate at the level 3902da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // of view or data. Also, we don't want to use View.dispatchTouchEvent directly as 3912da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // that would require emulation of two separate sequences as well. 3922da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 393eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 3942da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 3952da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Inject DOWN event 3962da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov long downTime = SystemClock.uptimeMillis(); 3972da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov MotionEvent eventDown = MotionEvent.obtain( 3982da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov downTime, downTime, MotionEvent.ACTION_DOWN, emulatedTapX, emulatedTapY, 1); 3992da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov instrumentation.sendPointerSync(eventDown); 4002da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Inject MOVE event 4022da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov long moveTime = SystemClock.uptimeMillis(); 4032da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov MotionEvent eventMove = MotionEvent.obtain( 4042da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov moveTime, moveTime, MotionEvent.ACTION_MOVE, emulatedTapX, emulatedTapY, 1); 4052da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov instrumentation.sendPointerSync(eventMove); 4062da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4072da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Inject UP event 4082da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov long upTime = SystemClock.uptimeMillis(); 4092da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov MotionEvent eventUp = MotionEvent.obtain( 4102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov upTime, upTime, MotionEvent.ACTION_UP, emulatedTapX, emulatedTapY, 1); 4112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov instrumentation.sendPointerSync(eventUp); 4122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4132da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Wait for the system to process all events in the queue 4142da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov instrumentation.waitForIdleSync(); 4152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4162da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // At this point our popup should not be showing and should have notified its 4172da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // dismiss listener 41831c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu); 4192da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist()); 4202da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 4212da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4222da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Test 4234bb57b2c7c5e8fa607119bd5089535d5862cb979Aurimas Liutikas @MediumTest 4242da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void testSimpleMenuItemClickViaEvent() { 4252da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov Builder menuBuilder = new Builder().withMenuItemClickListener(); 4262da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov menuBuilder.wireToActionButton(); 4272da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4282da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withId(R.id.test_button)).perform(click()); 4292da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 43031c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Verify that our menu item click listener hasn't been called yet 43131c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class)); 4322da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 433eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_delete))) 434eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(withDecorView(not(is(mMainDecorView)))) 4352da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .perform(click()); 43631c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov 43731c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Verify that out menu item click listener has been called with the expected menu item 43831c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick( 43931c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov mPopupMenu.getMenu().findItem(R.id.action_delete)); 4402da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4412da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Popup menu should be automatically dismissed on selecting an item 4422da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist()); 4432da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 4442da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4452da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Test 4464bb57b2c7c5e8fa607119bd5089535d5862cb979Aurimas Liutikas @MediumTest 44742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas public void testSimpleMenuItemClickViaAPI() throws Throwable { 4482da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov Builder menuBuilder = new Builder().withMenuItemClickListener(); 4492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov menuBuilder.wireToActionButton(); 4502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4512da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withId(R.id.test_button)).perform(click()); 4522da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 45331c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Verify that our menu item click listener hasn't been called yet 45431c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class)); 45531c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov 45642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 4572da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 4582da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void run() { 4592da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov mPopupMenu.getMenu().performIdentifierAction(R.id.action_highlight, 0); 4602da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 4612da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov }); 4622da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 46331c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Verify that out menu item click listener has been called with the expected menu item 46431c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick( 46531c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov mPopupMenu.getMenu().findItem(R.id.action_highlight)); 4662da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4672da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Popup menu should be automatically dismissed on selecting an item 4682da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist()); 4692da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 4702da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4712da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Test 4724bb57b2c7c5e8fa607119bd5089535d5862cb979Aurimas Liutikas @MediumTest 4732da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void testSubMenuClicksViaEvent() throws Throwable { 4742da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov Builder menuBuilder = new Builder().withMenuItemClickListener(); 4752da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov menuBuilder.wireToActionButton(); 4762da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4772da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withId(R.id.test_button)).perform(click()); 4782da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 47931c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Verify that our menu item click listener hasn't been called yet 48031c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class)); 4812da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 482eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_share))) 483eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(withDecorView(not(is(mMainDecorView)))) 4842da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .perform(click()); 48531c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov 48631c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Verify that out menu item click listener has been called with the expected menu item 48731c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick( 48831c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov mPopupMenu.getMenu().findItem(R.id.action_share)); 4892da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4902da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Sleep for a bit to allow the menu -> submenu transition to complete 4912da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov Thread.sleep(1000); 4922da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 4932da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // At this point we should now have our sub-menu displayed. At this point on newer 4942da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // platform versions (L+) we have two view roots on the screen - one for the main popup 4952da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // menu and one for the submenu that has just been activated. If we only use the 4962da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // logic based on decor view, Espresso will time out on waiting for the first root 4972da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // to acquire window focus. This is why from this point on in this test we are using 4982da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // two root conditions to detect the submenu - one with decor view not being the same 4992da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // as the decor view of our main activity window, and the other that checks for window 5002da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // focus. 5012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5022da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Unlike ListPopupWindow, PopupMenu doesn't have an API to check whether it is showing. 5032da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Use a custom matcher to check the visibility of the drop down list view instead. 5042da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))) 505eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus())) 5062da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(matches(isDisplayed())); 5072da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5082da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Note that MenuItem.isVisible() refers to the current "data" visibility state 5092da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // and not the "on screen" visibility state. This is why we're testing the display 5102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // visibility of our main and sub menu items. 5112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Share submenu items should now be visible 513eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_share_email))) 514eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus())) 5152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(matches(isDisplayed())); 516eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_share_circles))) 517eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus())) 5182da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(matches(isDisplayed())); 5192da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5202da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Now click an item in the sub-menu 521eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_share_circles))) 522eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus())) 5232da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .perform(click()); 52431c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov 52531c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Verify that out menu item click listener has been called with the expected menu item 52631c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick( 52731c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov mPopupMenu.getMenu().findItem(R.id.action_share_circles)); 5282da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5292da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Popup menu should be automatically dismissed on selecting an item in the submenu 5302da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist()); 5312da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 5322da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5332da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Test 5344bb57b2c7c5e8fa607119bd5089535d5862cb979Aurimas Liutikas @MediumTest 5352da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void testSubMenuClicksViaAPI() throws Throwable { 5362da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov Builder menuBuilder = new Builder().withMenuItemClickListener(); 5372da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov menuBuilder.wireToActionButton(); 5382da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5392da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withId(R.id.test_button)).perform(click()); 5402da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 54131c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Verify that our menu item click listener hasn't been called yet 54231c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class)); 54331c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov 54442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 5452da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 5462da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void run() { 5472da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov mPopupMenu.getMenu().performIdentifierAction(R.id.action_share, 0); 5482da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 5492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov }); 5502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 55131c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Verify that out menu item click listener has been called with the expected menu item 55231c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick( 55331c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov mPopupMenu.getMenu().findItem(R.id.action_share)); 5542da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5552da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Sleep for a bit to allow the menu -> submenu transition to complete 5562da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov Thread.sleep(1000); 5572da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5582da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // At this point we should now have our sub-menu displayed. At this point on newer 5592da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // platform versions (L+) we have two view roots on the screen - one for the main popup 5602da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // menu and one for the submenu that has just been activated. If we only use the 5612da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // logic based on decor view, Espresso will time out on waiting for the first root 5622da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // to acquire window focus. This is why from this point on in this test we are using 5632da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // two root conditions to detect the submenu - one with decor view not being the same 5642da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // as the decor view of our main activity window, and the other that checks for window 5652da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // focus. 5662da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5672da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Unlike ListPopupWindow, PopupMenu doesn't have an API to check whether it is showing. 5682da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Use a custom matcher to check the visibility of the drop down list view instead. 5692da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))) 570eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus())) 5712da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(matches(isDisplayed())); 5722da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5732da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Note that MenuItem.isVisible() refers to the current "data" visibility state 5742da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // and not the "on screen" visibility state. This is why we're testing the display 5752da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // visibility of our main and sub menu items. 5762da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5772da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Share submenu items should now be visible 578eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_share_email))) 579eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus())) 5802da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(matches(isDisplayed())); 581eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov onView(withText(mResources.getString(R.string.popup_menu_share_circles))) 582eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus())) 5832da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov .check(matches(isDisplayed())); 5842da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5852da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Now ask the share submenu to perform an action on its specific menu item 58642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityTestRule.runOnUiThread(new Runnable() { 5872da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 5882da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void run() { 5892da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov mPopupMenu.getMenu().findItem(R.id.action_share).getSubMenu(). 5902da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov performIdentifierAction(R.id.action_share_email, 0); 5912da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 5922da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov }); 59331c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov 59431c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Verify that out menu item click listener has been called with the expected menu item 59531c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick( 59631c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov mPopupMenu.getMenu().findItem(R.id.action_share_email)); 5972da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 5982da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Popup menu should be automatically dismissed on selecting an item in the submenu 5992da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist()); 6002da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 6012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 6022da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov /** 6032da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * Inner helper class to configure an instance of <code>PopupMenu</code> for the 6042da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * specific test. The main reason for its existence is that once a popup menu is shown 6052da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * with the show() method, most of its configuration APIs are no-ops. This means that 6062da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * we can't add logic that is specific to a certain test once it's shown and we have a 6072da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * reference to a displayed PopupMenu. 6082da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov */ 60931c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov public class Builder { 6102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov private boolean mHasDismissListener; 6112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov private boolean mHasMenuItemClickListener; 6122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 61331c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov private PopupMenu.OnMenuItemClickListener mOnMenuItemClickListener; 61431c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov private PopupMenu.OnDismissListener mOnDismissListener; 6152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 6162da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public Builder withMenuItemClickListener() { 6172da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov mHasMenuItemClickListener = true; 6182da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return this; 6192da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 6202da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 6212da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public Builder withDismissListener() { 6222da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov mHasDismissListener = true; 6232da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov return this; 6242da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 6252da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 6262da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov private void show() { 6272da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov mPopupMenu = new PopupMenu(mContainer.getContext(), mButton); 6282da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov final MenuInflater menuInflater = mPopupMenu.getMenuInflater(); 6292da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov menuInflater.inflate(R.menu.popup_menu, mPopupMenu.getMenu()); 6302da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 6312da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov if (mHasMenuItemClickListener) { 63231c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Register a mock listener to be notified when a menu item in our popup menu has 6332da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // been clicked. 63431c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov mOnMenuItemClickListener = mock(PopupMenu.OnMenuItemClickListener.class); 63531c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov mPopupMenu.setOnMenuItemClickListener(mOnMenuItemClickListener); 6362da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 6372da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 6382da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov if (mHasDismissListener) { 63931c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov // Register a mock listener to be notified when our popup menu is dismissed. 64031c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov mOnDismissListener = mock(PopupMenu.OnDismissListener.class); 64131c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov mPopupMenu.setOnDismissListener(mOnDismissListener); 6422da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 6432da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 6442da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov // Show the popup menu 6452da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov mPopupMenu.show(); 6462da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 6472da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov 6482da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void wireToActionButton() { 6492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov mButton.setOnClickListener(new View.OnClickListener() { 6502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov @Override 6512da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov public void onClick(View v) { 6522da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov show(); 6532da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 6542da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov }); 6552da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 6562da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov } 65744ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev 65844ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev private static Matcher<View> selfOrParentWithContentDescription(final CharSequence expected) { 65944ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev return new TypeSafeMatcher<View>() { 66044ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev @Override 66144ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev public void describeTo(Description description) { 66244ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev description.appendText("self of parent has content description: " + expected); 66344ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev } 66444ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev 66544ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev @Override 66644ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev public boolean matchesSafely(View view) { 66744ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev return TextUtils.equals(expected, getSelfOrParentContentDescription(view)); 66844ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev } 66944ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev 67044ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev private CharSequence getSelfOrParentContentDescription(View view) { 67144ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev while (view != null) { 67244ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev final CharSequence contentDescription = view.getContentDescription(); 67344ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev if (contentDescription != null) { 67444ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev return contentDescription; 67544ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev } 67644ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev final ViewParent parent = view.getParent(); 67744ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev if (!(parent instanceof View)) { 67844ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev break; 67944ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev } 68044ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev view = (View) parent; 68144ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev } 68244ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev return null; 68344ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev } 68444ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev }; 68544ee68ea8e537af604d731f47cb7f0ac0ffb4903Vladislav Kaznacheev } 6862da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov} 687