diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java index 83a9e01761d98..afd0bc4e52956 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java @@ -26,8 +26,11 @@ import static android.widget.espresso.TextViewActions.mouseDragOnText; import static android.widget.espresso.TextViewActions.mouseLongClickAndDragOnText; import static android.widget.espresso.TextViewActions.mouseTripleClickAndDragOnText; import static android.widget.espresso.TextViewActions.mouseTripleClickOnTextAtIndex; +import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex; import static android.widget.espresso.TextViewAssertions.hasSelection; + import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.Espresso.pressBack; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.replaceText; import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView; @@ -37,8 +40,11 @@ import static android.support.test.espresso.matcher.ViewMatchers.withId; import com.android.frameworks.coretests.R; +import android.support.test.espresso.Espresso; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; +import android.view.MotionEvent; +import android.widget.espresso.ContextMenuUtils; /** * Tests mouse interaction of the TextView widget from an Activity @@ -49,10 +55,13 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< super(TextViewActivity.class); } + @Override + public void setUp() { + getActivity(); + } + @SmallTest public void testSelectTextByDrag() throws Exception { - getActivity(); - final String helloWorld = "Hello world!"; onView(withId(R.id.textview)).perform(click()); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); @@ -77,8 +86,6 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< @SmallTest public void testSelectTextByDrag_reverse() throws Exception { - getActivity(); - final String helloWorld = "Hello world!"; onView(withId(R.id.textview)).perform(click()); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); @@ -89,9 +96,56 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< } @SmallTest - public void testSelectTextByLongClick() throws Exception { - getActivity(); + public void testContextMenu() throws Exception { + final String text = "abc def ghi."; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text)); + ContextMenuUtils.assertContextMenuIsNotDisplayed(); + + onView(withId(R.id.textview)).perform( + mouseClickOnTextAtIndex(text.indexOf("d"), MotionEvent.BUTTON_SECONDARY)); + + ContextMenuUtils.assertContextMenuContainsItemDisabled( + getActivity().getString(com.android.internal.R.string.copy)); + ContextMenuUtils.assertContextMenuContainsItemEnabled( + getActivity().getString(com.android.internal.R.string.undo)); + + // Hide context menu. + pressBack(); + ContextMenuUtils.assertContextMenuIsNotDisplayed(); + + onView(withId(R.id.textview)).perform( + mouseDragOnText(text.indexOf("c"), text.indexOf("h"))); + onView(withId(R.id.textview)).perform( + mouseClickOnTextAtIndex(text.indexOf("d"), MotionEvent.BUTTON_SECONDARY)); + + ContextMenuUtils.assertContextMenuContainsItemEnabled( + getActivity().getString(com.android.internal.R.string.copy)); + ContextMenuUtils.assertContextMenuContainsItemEnabled( + getActivity().getString(com.android.internal.R.string.undo)); + + // Hide context menu. + pressBack(); + + onView(withId(R.id.textview)).check(hasSelection("c def g")); + + onView(withId(R.id.textview)).perform( + mouseClickOnTextAtIndex(text.indexOf("i"), MotionEvent.BUTTON_SECONDARY)); + ContextMenuUtils.assertContextMenuContainsItemDisabled( + getActivity().getString(com.android.internal.R.string.copy)); + ContextMenuUtils.assertContextMenuContainsItemEnabled( + getActivity().getString(com.android.internal.R.string.undo)); + + // Hide context menu. + pressBack(); + + onView(withId(R.id.textview)).check(hasSelection("")); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("i"))); + } + + @SmallTest + public void testSelectTextByLongClick() throws Exception { final String helloWorld = "Hello world!"; onView(withId(R.id.textview)).perform(click()); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); @@ -117,8 +171,6 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< @SmallTest public void testSelectTextByDoubleClick() throws Exception { - getActivity(); - final String helloWorld = "Hello world!"; onView(withId(R.id.textview)).perform(click()); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); @@ -144,8 +196,6 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< @SmallTest public void testSelectTextByDoubleClickAndDrag() throws Exception { - getActivity(); - final String text = "abcd efg hijk lmn"; onView(withId(R.id.textview)).perform(click()); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text)); @@ -157,8 +207,6 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< @SmallTest public void testSelectTextByDoubleClickAndDrag_reverse() throws Exception { - getActivity(); - final String text = "abcd efg hijk lmn"; onView(withId(R.id.textview)).perform(click()); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text)); @@ -170,8 +218,6 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< @SmallTest public void testSelectTextByLongPressAndDrag() throws Exception { - getActivity(); - final String text = "abcd efg hijk lmn"; onView(withId(R.id.textview)).perform(click()); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text)); @@ -183,8 +229,6 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< @SmallTest public void testSelectTextByLongPressAndDrag_reverse() throws Exception { - getActivity(); - final String text = "abcd efg hijk lmn"; onView(withId(R.id.textview)).perform(click()); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text)); @@ -196,8 +240,6 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< @SmallTest public void testSelectTextByTripleClick() throws Exception { - getActivity(); - final StringBuilder builder = new StringBuilder(); builder.append("First paragraph.\n"); builder.append("Second paragraph."); @@ -232,8 +274,6 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< @SmallTest public void testSelectTextByTripleClickAndDrag() throws Exception { - getActivity(); - final StringBuilder builder = new StringBuilder(); builder.append("First paragraph.\n"); builder.append("Second paragraph."); @@ -263,8 +303,6 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< @SmallTest public void testSelectTextByTripleClickAndDrag_reverse() throws Exception { - getActivity(); - final StringBuilder builder = new StringBuilder(); builder.append("First paragraph.\n"); builder.append("Second paragraph."); diff --git a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java new file mode 100644 index 0000000000000..c8218aa490f26 --- /dev/null +++ b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 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.widget.espresso; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.RootMatchers.withDecorView; +import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; +import static android.support.test.espresso.matcher.ViewMatchers.hasFocus; +import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.isEnabled; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.not; + +import com.android.internal.view.menu.ListMenuItemView; + +import android.support.test.espresso.NoMatchingRootException; +import android.support.test.espresso.NoMatchingViewException; +import android.support.test.espresso.ViewInteraction; +import android.support.test.espresso.matcher.ViewMatchers; +import android.widget.MenuPopupWindow.MenuDropDownListView; + +/** + * Espresso utility methods for the context menu. + */ +public final class ContextMenuUtils { + private ContextMenuUtils() {} + + private static ViewInteraction onContextMenu() { + // TODO: Have more reliable way to get context menu. + return onView(ViewMatchers.isAssignableFrom(MenuDropDownListView.class)) + .inRoot(withDecorView(hasFocus())); + } + + /** + * Asserts that the context menu is displayed + * + * @throws AssertionError if the assertion fails + */ + private static void assertContextMenuIsDisplayed() { + onContextMenu().check(matches(isDisplayed())); + } + + /** + * Asserts that the context menu is not displayed + * + * @throws AssertionError if the assertion fails + */ + public static void assertContextMenuIsNotDisplayed() { + try { + assertContextMenuIsDisplayed(); + } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) { + return; + } + throw new AssertionError("Context menu is displayed"); + } + + /** + * Asserts that the context menu contains the specified item and the item has specified enabled + * state. + * + * @param itemLabel label of the item. + * @param enabled enabled state of the item. + * @throws AssertionError if the assertion fails + */ + private static void asssertContextMenuContainsItemWithEnabledState(String itemLabel, + boolean enabled) { + onContextMenu().check(matches( + hasDescendant(allOf( + isAssignableFrom(ListMenuItemView.class), + enabled ? isEnabled() : not(isEnabled()), + hasDescendant(withText(itemLabel)))))); + } + + /** + * Asserts that the context menu contains the specified item and the item is enabled. + * + * @param itemLabel label of the item. + * @throws AssertionError if the assertion fails + */ + public static void assertContextMenuContainsItemEnabled(String itemLabel) { + asssertContextMenuContainsItemWithEnabledState(itemLabel, true); + } + + /** + * Asserts that the context menu contains the specified item and the item is disabled. + * + * @param itemLabel label of the item. + * @throws AssertionError if the assertion fails + */ + public static void assertContextMenuContainsItemDisabled(String itemLabel) { + asssertContextMenuContainsItemWithEnabledState(itemLabel, false); + } +} diff --git a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java index e51f27857a4b8..b8ea2de3abfd0 100644 --- a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java +++ b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java @@ -24,9 +24,9 @@ import android.support.test.espresso.action.CoordinatesProvider; import android.support.test.espresso.action.GeneralClickAction; import android.support.test.espresso.action.MotionEvents; import android.support.test.espresso.action.MotionEvents.DownResultHolder; -import android.support.test.espresso.action.PrecisionDescriber; import android.support.test.espresso.action.Press; import android.support.test.espresso.action.Tapper; +import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; @@ -35,6 +35,8 @@ import android.view.ViewConfiguration; */ public final class MouseClickAction implements ViewAction { private final GeneralClickAction mGeneralClickAction; + @MouseUiController.MouseButton + private final int mButton; public enum CLICK implements Tapper { TRIPLE { @@ -86,8 +88,20 @@ public final class MouseClickAction implements ViewAction { }; public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider) { - mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider, - Press.PINPOINT); + this(tapper, coordinatesProvider, MotionEvent.BUTTON_PRIMARY); + } + + /** + * Constructs MouseClickAction + * + * @param tapper the tapper + * @param coordinatesProvider the provider of the event coordinates + * @param button the mouse button used to send motion events + */ + public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider, + @MouseUiController.MouseButton int button) { + mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider, Press.PINPOINT); + mButton = button; } @Override @@ -102,7 +116,7 @@ public final class MouseClickAction implements ViewAction { @Override public void perform(UiController uiController, View view) { - mGeneralClickAction.perform(new MouseUiController(uiController), view); + mGeneralClickAction.perform(new MouseUiController(uiController, mButton), view); long doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout(); if (0 < doubleTapTimeout) { // Wait to avoid false gesture detection. Without this wait, consecutive clicks can be diff --git a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java index f1387f801b01e..022be7603dc13 100644 --- a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java +++ b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java @@ -16,6 +16,12 @@ package android.widget.espresso; +import static com.android.internal.util.Preconditions.checkNotNull; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import android.annotation.IntDef; import android.support.test.espresso.InjectEventSecurityException; import android.support.test.espresso.UiController; import android.view.InputDevice; @@ -26,11 +32,28 @@ import android.view.MotionEvent; * Class to wrap an UiController to overwrite source of motion events to SOURCE_MOUSE. * Note that this doesn't change the tool type. */ -public class MouseUiController implements UiController { +public final class MouseUiController implements UiController { + @Retention(RetentionPolicy.SOURCE) + @IntDef({MotionEvent.BUTTON_PRIMARY, MotionEvent.BUTTON_SECONDARY, MotionEvent.BUTTON_TERTIARY}) + public @interface MouseButton {} + private final UiController mUiController; + @MouseButton + private final int mButton; public MouseUiController(UiController uiController) { - mUiController = uiController; + this(uiController, MotionEvent.BUTTON_PRIMARY); + } + + /** + * Constructs MouseUiController. + * + * @param uiController the uiController to wrap + * @param button the button to be used for generating input events. + */ + public MouseUiController(UiController uiController, @MouseButton int button) { + mUiController = checkNotNull(uiController); + mButton = button; } @Override @@ -40,9 +63,11 @@ public class MouseUiController implements UiController { @Override public boolean injectMotionEvent(MotionEvent event) throws InjectEventSecurityException { - // Modify the event to mimic mouse primary button event. + // Modify the event to mimic mouse event. event.setSource(InputDevice.SOURCE_MOUSE); - event.setButtonState(MotionEvent.BUTTON_PRIMARY); + if (event.getActionMasked() != MotionEvent.ACTION_UP) { + event.setButtonState(mButton); + } return mUiController.injectMotionEvent(event); } diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java index 54d5823c7aca5..1dd6e170e7183 100644 --- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java +++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java @@ -26,6 +26,7 @@ import android.support.test.espresso.action.Press; import android.support.test.espresso.action.Tap; import android.support.test.espresso.util.HumanReadables; import android.text.Layout; +import android.view.MotionEvent; import android.view.View; import android.widget.Editor; import android.widget.TextView; @@ -63,8 +64,24 @@ public final class TextViewActions { * @param index The index of the TextView's text to click on. */ public static ViewAction mouseClickOnTextAtIndex(int index) { + return mouseClickOnTextAtIndex(index, MotionEvent.BUTTON_PRIMARY); + } + + /** + * Returns an action that clicks by mouse on text at an index on the TextView.
+ *
+ * View constraints: + *