Merge "TextView tests: open context menu on right click."
This commit is contained in:
committed by
Android (Google) Code Review
commit
fbca8264e0
@@ -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.");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.<br>
|
||||
* <br>
|
||||
* View constraints:
|
||||
* <ul>
|
||||
* <li>must be a TextView displayed on screen
|
||||
* <ul>
|
||||
*
|
||||
* @param index The index of the TextView's text to click on.
|
||||
* @param button the mouse button to use.
|
||||
*/
|
||||
public static ViewAction mouseClickOnTextAtIndex(int index,
|
||||
@MouseUiController.MouseButton int button) {
|
||||
return actionWithAssertions(
|
||||
new MouseClickAction(Tap.SINGLE, new TextCoordinates(index)));
|
||||
new MouseClickAction(Tap.SINGLE, new TextCoordinates(index), button));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user