Merge "TextView tests: open context menu on right click."

This commit is contained in:
Keisuke Kuroyanagi
2015-12-17 08:51:35 +00:00
committed by Android (Google) Code Review
5 changed files with 235 additions and 31 deletions

View File

@@ -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.");

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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));
}
/**