blob: 44a9f7a376ad1980619b48546c61b3d15b7ac9be [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.tasks.tab_management;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
import static androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
import static androidx.test.espresso.matcher.ViewMatchers.isFocusable;
import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.chromium.chrome.browser.tasks.tab_management.RecyclerViewMatcherUtils.atPosition;
import static org.chromium.chrome.browser.tasks.tab_management.RecyclerViewMatcherUtils.atPositionWithViewHolder;
import static org.chromium.chrome.browser.tasks.tab_management.RecyclerViewMatcherUtils.withItemType;
import android.os.Build;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.espresso.NoMatchingRootException;
import androidx.test.espresso.NoMatchingViewException;
import androidx.test.espresso.Root;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.matcher.BoundedMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Assert;
/**
* This is the testing util class for TabSelectionEditor. It's used to perform action and verify
* result within the TabSelectionEditor.
*/
public class TabSelectionEditorTestingRobot {
/**
* @return A root matcher that matches the TabSelectionEditor popup decor view.
*/
public static Matcher<Root> isTabSelectionEditorPopup() {
return new TypeSafeMatcher<Root>() {
@Override
public void describeTo(Description description) {
description.appendText("is TabSelectionEditor Popup");
}
@Override
public boolean matchesSafely(Root root) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
return withDecorView(
withClassName(is(TabSelectionEditorLayout.class.getName())))
.matches(root);
} else {
return withDecorView(
withClassName(is("android.widget.PopupWindow$PopupDecorView")))
.matches(root);
}
}
};
}
/**
* @return A view matcher that matches the item is selected.
*/
public static Matcher<View> itemIsSelected() {
return new BoundedMatcher<View, SelectableTabGridView>(SelectableTabGridView.class) {
private SelectableTabGridView mSelectableTabGridView;
@Override
protected boolean matchesSafely(SelectableTabGridView selectableTabGridView) {
mSelectableTabGridView = selectableTabGridView;
return mSelectableTabGridView.isSelected() && actionButtonSelected()
&& highlightIndicatorIsVisible();
}
@Override
public void describeTo(Description description) {
description.appendText("Item is selected");
}
private boolean actionButtonSelected() {
return mSelectableTabGridView.getResources().getInteger(
org.chromium.chrome.tab_ui.R.integer.list_item_level_selected)
== mSelectableTabGridView
.findViewById(org.chromium.chrome.tab_ui.R.id.action_button)
.getBackground()
.getLevel();
}
private boolean highlightIndicatorIsVisible() {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
return mSelectableTabGridView
.findViewById(org.chromium.chrome.tab_ui.R.id
.selected_view_below_lollipop)
.getVisibility()
== View.VISIBLE;
} else {
return mSelectableTabGridView.getForeground() != null;
}
}
};
}
/**
* @return A view matcher that matches a divider view.
*/
public static Matcher<View> isDivider() {
return new TypeSafeMatcher<View>() {
@Override
protected boolean matchesSafely(View view) {
return view.getId() == org.chromium.chrome.tab_ui.R.id.divider_view;
}
@Override
public void describeTo(Description description) {
description.appendText("is divider");
}
};
}
public final TabSelectionEditorTestingRobot.Result resultRobot;
public final TabSelectionEditorTestingRobot.Action actionRobot;
public TabSelectionEditorTestingRobot() {
resultRobot = new Result();
actionRobot = new Action();
}
/**
* This Robot is used to perform action within the TabSelectionEditor.
*/
public static class Action {
public TabSelectionEditorTestingRobot.Action clickItemAtAdapterPosition(int position) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup())
.perform(actionOnItemAtPosition(position, click()));
return this;
}
public TabSelectionEditorTestingRobot.Action clickToolbarActionButton() {
onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
.inRoot(isTabSelectionEditorPopup())
.perform(click());
return this;
}
public TabSelectionEditorTestingRobot.Action clickToolbarNavigationButton() {
onView(allOf(withContentDescription(org.chromium.chrome.tab_ui.R.string.close),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
.inRoot(isTabSelectionEditorPopup())
.perform(click());
return this;
}
public TabSelectionEditorTestingRobot.Action clickEndButtonAtAdapterPosition(int position) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup())
.perform(new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public String getDescription() {
return "click on end button of item with index "
+ String.valueOf(position);
}
@Override
public void perform(UiController uiController, View view) {
RecyclerView recyclerView = (RecyclerView) view;
RecyclerView.ViewHolder viewHolder =
recyclerView.findViewHolderForAdapterPosition(position);
if (viewHolder.itemView == null) return;
viewHolder.itemView
.findViewById(org.chromium.chrome.tab_ui.R.id.end_button)
.performClick();
}
});
return this;
}
}
/**
* This Robot is used to verify result within the TabSelectionEditor.
*/
public static class Result {
public TabSelectionEditorTestingRobot.Result verifyTabSelectionEditorIsVisible() {
onView(withId(org.chromium.chrome.tab_ui.R.id.selectable_list))
.inRoot(isTabSelectionEditorPopup())
.check(matches(isDisplayed()));
return this;
}
public TabSelectionEditorTestingRobot.Result verifyTabSelectionEditorIsHidden() {
try {
onView(withId(org.chromium.chrome.tab_ui.R.id.selectable_list))
.inRoot(isTabSelectionEditorPopup())
.check(matches(isDisplayed()));
} catch (NoMatchingRootException | NoMatchingViewException e) {
return this;
}
assert false : "TabSelectionEditor should be hidden, but it's not.";
return this;
}
public TabSelectionEditorTestingRobot.Result verifyToolbarSelectionTextWithResourceId(
int resourceId) {
onView(withText(resourceId))
.inRoot(isTabSelectionEditorPopup())
.check(matches(isDisplayed()));
return this;
}
public TabSelectionEditorTestingRobot.Result verifyToolbarSelectionText(String text) {
onView(withText(text))
.inRoot(isTabSelectionEditorPopup())
.check(matches(isDisplayed()));
return this;
}
public TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonWithResourceId(
int resourceId) {
onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
.inRoot(isTabSelectionEditorPopup())
.check(matches(withText(resourceId)));
return this;
}
public TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonWithText(
String text) {
onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
.inRoot(isTabSelectionEditorPopup())
.check(matches(withText(text)));
return this;
}
public TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonDisabled() {
onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
.inRoot(isTabSelectionEditorPopup())
.check(matches(not(isEnabled())));
return this;
}
public TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonEnabled() {
onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
.inRoot(isTabSelectionEditorPopup())
.check(matches(isEnabled()));
return this;
}
public TabSelectionEditorTestingRobot.Result verifyHasAtLeastNItemVisible(int count) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup())
.check((v, noMatchException) -> {
if (noMatchException != null) throw noMatchException;
Assert.assertTrue(v instanceof RecyclerView);
Assert.assertTrue(((RecyclerView) v).getChildCount() >= count);
});
return this;
}
public TabSelectionEditorTestingRobot.Result verifyAdapterHasItemCount(int count) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup())
.check(matches(RecyclerViewMatcherUtils.adapterHasItemCount(count)));
return this;
}
public TabSelectionEditorTestingRobot.Result verifyItemNotSelectedAtAdapterPosition(
int position) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup())
.check(matches(
not(RecyclerViewMatcherUtils.atPosition(position, itemIsSelected()))));
return this;
}
public TabSelectionEditorTestingRobot.Result verifyItemSelectedAtAdapterPosition(
int position) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup())
.check(matches(
RecyclerViewMatcherUtils.atPosition(position, itemIsSelected())));
return this;
}
public TabSelectionEditorTestingRobot.Result verifyUndoSnackbarWithTextIsShown(
String text) {
onView(withText(text)).check(matches(isDisplayed()));
return this;
}
public Result verifyDividerAlwaysStartsAtTheEdgeOfScreen() {
onView(allOf(isDivider(),
withParent(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))))
.inRoot(isTabSelectionEditorPopup())
.check(matches(isDisplayed()))
.check((v, noMatchException) -> {
if (noMatchException != null) throw noMatchException;
View parentView = (View) v.getParent();
Assert.assertEquals(parentView.getPaddingStart(), (int) v.getX());
});
return this;
}
public Result verifyDividerAlwaysStartsAtTheEdgeOfScreenAtPosition(int position) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup())
.perform(scrollToPosition(position));
onView(atPosition(position, isDivider()))
.inRoot(isTabSelectionEditorPopup())
.check(matches(isDisplayed()))
.check((v, noMatchException) -> {
if (noMatchException != null) throw noMatchException;
View parentView = (View) v.getParent();
Assert.assertEquals(parentView.getPaddingStart(), (int) v.getX());
});
return this;
}
public Result verifyDividerNotClickableNotFocusable() {
onView(allOf(isDivider(),
withParent(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))))
.inRoot(isTabSelectionEditorPopup())
.check(matches(not(isClickable())))
.check(matches(not(isFocusable())));
return this;
}
/**
* Verifies the TabSelectionEditor has an ItemView at given position that matches the given
* targetItemViewType.
*
* First this method scrolls to the given adapter position to make sure ViewHolder for the
* given position is visible.
*
* @param position Adapter position.
* @param targetItemViewType The item view type to be matched.
* @return {@link Result} to do chain verification.
*/
public Result verifyHasItemViewTypeAtAdapterPosition(int position, int targetItemViewType) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup())
.perform(scrollToPosition(position));
onView(atPositionWithViewHolder(position, withItemType(targetItemViewType)))
.inRoot(isTabSelectionEditorPopup())
.check(matches(isDisplayed()));
return this;
}
}
}