| /* |
| * 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.matcher.ViewMatchers.assertThat; |
| import static com.android.internal.util.Preconditions.checkNotNull; |
| import static org.hamcrest.Matchers.is; |
| import static org.hamcrest.number.IsCloseTo.closeTo; |
| |
| import android.annotation.IntDef; |
| import android.graphics.Rect; |
| import android.graphics.drawable.Drawable; |
| import android.support.test.espresso.NoMatchingViewException; |
| import android.support.test.espresso.ViewAssertion; |
| import android.text.Spanned; |
| import android.text.TextUtils; |
| import android.view.View; |
| import android.widget.EditText; |
| import android.widget.TextView; |
| |
| import junit.framework.AssertionFailedError; |
| import org.hamcrest.Matcher; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| |
| /** |
| * A collection of assertions on a {@link android.widget.TextView}. |
| */ |
| public final class TextViewAssertions { |
| |
| private TextViewAssertions() {} |
| |
| /** |
| * Returns a {@link ViewAssertion} that asserts that the text view has a specified |
| * selection.<br> |
| * <br> |
| * View constraints: |
| * <ul> |
| * <li>must be a text view displayed on screen |
| * <ul> |
| * |
| * @param selection The expected selection. |
| */ |
| public static ViewAssertion hasSelection(String selection) { |
| return hasSelection(is(selection)); |
| } |
| |
| /** |
| * Returns a {@link ViewAssertion} that asserts that the text view has a specified |
| * selection.<br> |
| * <br> |
| * View constraints: |
| * <ul> |
| * <li>must be a text view displayed on screen |
| * <ul> |
| * |
| * @param selection A matcher representing the expected selection. |
| */ |
| public static ViewAssertion hasSelection(Matcher<String> selection) { |
| return new TextSelectionAssertion(selection); |
| } |
| |
| /** |
| * Returns a {@link ViewAssertion} that asserts that the text view insertion pointer is at |
| * a specified index.<br> |
| * <br> |
| * View constraints: |
| * <ul> |
| * <li>must be a text view displayed on screen |
| * <ul> |
| * |
| * @param index The expected index. |
| */ |
| public static ViewAssertion hasInsertionPointerAtIndex(int index) { |
| return hasInsertionPointerAtIndex(is(index)); |
| } |
| |
| /** |
| * Returns a {@link ViewAssertion} that asserts that the text view insertion pointer is at |
| * a specified index.<br> |
| * <br> |
| * View constraints: |
| * <ul> |
| * <li>must be a text view displayed on screen |
| * <ul> |
| * |
| * @param index A matcher representing the expected index. |
| */ |
| public static ViewAssertion hasInsertionPointerAtIndex(final Matcher<Integer> index) { |
| return (view, exception) -> { |
| if (view instanceof TextView) { |
| TextView textView = (TextView) view; |
| int selectionStart = textView.getSelectionStart(); |
| int selectionEnd = textView.getSelectionEnd(); |
| try { |
| assertThat(selectionStart, index); |
| assertThat(selectionEnd, index); |
| } catch (IndexOutOfBoundsException e) { |
| throw new AssertionFailedError(e.getMessage()); |
| } |
| } else { |
| throw new AssertionFailedError("TextView not found"); |
| } |
| }; |
| } |
| |
| /** |
| * Returns a {@link ViewAssertion} that asserts that the EditText insertion pointer is on |
| * the left edge. |
| */ |
| public static ViewAssertion hasInsertionPointerOnLeft() { |
| return new CursorPositionAssertion(CursorPositionAssertion.LEFT); |
| } |
| |
| /** |
| * Returns a {@link ViewAssertion} that asserts that the EditText insertion pointer is on |
| * the right edge. |
| */ |
| public static ViewAssertion hasInsertionPointerOnRight() { |
| return new CursorPositionAssertion(CursorPositionAssertion.RIGHT); |
| } |
| |
| /** |
| * Returns a {@link ViewAssertion} that asserts that the TextView does not contain styled text. |
| */ |
| public static ViewAssertion doesNotHaveStyledText() { |
| return (view, exception) -> { |
| final CharSequence text = ((TextView) view).getText(); |
| if (text instanceof Spanned && !TextUtils.hasStyleSpan((Spanned) text)) { |
| return; |
| } |
| throw new AssertionFailedError("TextView has styled text"); |
| }; |
| } |
| |
| /** |
| * A {@link ViewAssertion} to check the selected text in a {@link TextView}. |
| */ |
| private static final class TextSelectionAssertion implements ViewAssertion { |
| |
| private final Matcher<String> mSelection; |
| |
| public TextSelectionAssertion(Matcher<String> selection) { |
| mSelection = checkNotNull(selection); |
| } |
| |
| @Override |
| public void check(View view, NoMatchingViewException exception) { |
| if (view instanceof TextView) { |
| TextView textView = (TextView) view; |
| int selectionStart = textView.getSelectionStart(); |
| int selectionEnd = textView.getSelectionEnd(); |
| try { |
| String selectedText = textView.getText() |
| .subSequence(selectionStart, selectionEnd) |
| .toString(); |
| assertThat(selectedText, mSelection); |
| } catch (IndexOutOfBoundsException e) { |
| throw new AssertionFailedError(e.getMessage()); |
| } |
| } else { |
| throw new AssertionFailedError("TextView not found"); |
| } |
| } |
| } |
| |
| /** |
| * {@link ViewAssertion} to check that EditText cursor is on a given position. |
| */ |
| static class CursorPositionAssertion implements ViewAssertion { |
| |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef({LEFT, RIGHT}) |
| public @interface CursorEdgePositionType {} |
| public static final int LEFT = 0; |
| public static final int RIGHT = 1; |
| |
| private final int mPosition; |
| |
| private CursorPositionAssertion(@CursorEdgePositionType int position) { |
| this.mPosition = position; |
| } |
| |
| @Override |
| public void check(View view, NoMatchingViewException exception) { |
| if (!(view instanceof EditText)) { |
| throw new AssertionFailedError("View should be an instance of EditText"); |
| } |
| EditText editText = (EditText) view; |
| Drawable drawable = editText.getEditorForTesting().getCursorDrawable()[0]; |
| Rect drawableBounds = drawable.getBounds(); |
| Rect drawablePadding = new Rect(); |
| drawable.getPadding(drawablePadding); |
| |
| final int diff; |
| final String positionStr; |
| switch (mPosition) { |
| case LEFT: |
| positionStr = "left"; |
| diff = drawableBounds.left - editText.getScrollX() + drawablePadding.left; |
| break; |
| case RIGHT: |
| positionStr = "right"; |
| int maxRight = editText.getWidth() - editText.getCompoundPaddingRight() |
| - editText.getCompoundPaddingLeft() + editText.getScrollX(); |
| diff = drawableBounds.right - drawablePadding.right - maxRight; |
| break; |
| default: |
| throw new AssertionFailedError("Unknown position for cursor assertion"); |
| } |
| |
| assertThat("Cursor should be on the " + positionStr, Double.valueOf(diff), |
| closeTo(0f, 1f)); |
| } |
| } |
| } |