Commit 20372359 authored by Mei Liang's avatar Mei Liang Committed by Commit Bot

[TabSelectionEditor] Shows with preselected Tab

This CL adds an API to TabSelectionEditorController that shows the
TabSelectionEditor with the given tabs, and the first n tabs being
selected. There is also a divider shows between the first n selected
tabs and the rest of the tabs.

Change-Id: I32aa764dc62bbabc89fe54641963b9f64274134e
Bug: 1004570
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1896866Reviewed-by: default avatarWei-Yin Chen (陳威尹) <wychen@chromium.org>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Commit-Queue: Mei Liang <meiliang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#713897}
parent aafc1cb9
...@@ -129,6 +129,13 @@ public class TabListCoordinator implements Destroyable { ...@@ -129,6 +129,13 @@ public class TabListCoordinator implements Destroyable {
}, TabGridViewBinder::bindClosableTab); }, TabGridViewBinder::bindClosableTab);
recyclerListener = (holder) -> { recyclerListener = (holder) -> {
int holderItemViewType = holder.getItemViewType();
if (holderItemViewType != UiType.CLOSABLE
&& holderItemViewType != UiType.SELECTABLE) {
return;
}
ViewLookupCachingFrameLayout root = (ViewLookupCachingFrameLayout) holder.itemView; ViewLookupCachingFrameLayout root = (ViewLookupCachingFrameLayout) holder.itemView;
ImageView thumbnail = (ImageView) root.fastFindViewById(R.id.tab_thumbnail); ImageView thumbnail = (ImageView) root.fastFindViewById(R.id.tab_thumbnail);
if (thumbnail == null) return; if (thumbnail == null) return;
...@@ -305,4 +312,13 @@ public class TabListCoordinator implements Destroyable { ...@@ -305,4 +312,13 @@ public class TabListCoordinator implements Destroyable {
PropertyModelChangeProcessor.ViewBinder<PropertyModel, T, PropertyKey> binder) { PropertyModelChangeProcessor.ViewBinder<PropertyModel, T, PropertyKey> binder) {
mAdapter.registerType(typeId, builder, binder); mAdapter.registerType(typeId, builder, binder);
} }
/**
* Inserts a special {@link org.chromium.ui.modelutil.MVCListAdapter.ListItem} at given index of
* the model list.
* @see TabListMediator#addSpecialItemToModel(int, int, PropertyModel).
*/
void addSpecialListItem(int index, @UiType int uiType, PropertyModel model) {
mMediator.addSpecialItemToModel(index, uiType, model);
}
} }
...@@ -1155,4 +1155,17 @@ class TabListMediator { ...@@ -1155,4 +1155,17 @@ class TabListMediator {
void setTabRestoreCompletedForTesting(boolean isRestored) { void setTabRestoreCompletedForTesting(boolean isRestored) {
mTabRestoreCompleted = isRestored; mTabRestoreCompleted = isRestored;
} }
/**
* Inserts a special {@link org.chromium.ui.modelutil.MVCListAdapter.ListItem} at given index of
* the current {@link TabListModel}.
*
* @param index The index of the {@link org.chromium.ui.modelutil.MVCListAdapter.ListItem} to be
* inserted.
* @param uiType The view type the model will bind to.
* @param model The model that will be bound to a view.
*/
void addSpecialItemToModel(int index, @UiType int uiType, PropertyModel model) {
mModel.add(index, new SimpleRecyclerViewAdapter.ListItem(uiType, model));
}
} }
...@@ -23,13 +23,14 @@ import java.lang.annotation.RetentionPolicy; ...@@ -23,13 +23,14 @@ import java.lang.annotation.RetentionPolicy;
*/ */
public class TabProperties { public class TabProperties {
/** IDs for possible types of UI in the tab list. */ /** IDs for possible types of UI in the tab list. */
@IntDef({UiType.SELECTABLE, UiType.CLOSABLE, UiType.STRIP, UiType.MESSAGE}) @IntDef({UiType.SELECTABLE, UiType.CLOSABLE, UiType.STRIP, UiType.MESSAGE, UiType.DIVIDER})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface UiType { public @interface UiType {
int SELECTABLE = 0; int SELECTABLE = 0;
int CLOSABLE = 1; int CLOSABLE = 1;
int STRIP = 2; int STRIP = 2;
int MESSAGE = 3; int MESSAGE = 3;
int DIVIDER = 4;
} }
public static final PropertyModel.WritableIntPropertyKey TAB_ID = public static final PropertyModel.WritableIntPropertyKey TAB_ID =
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
package org.chromium.chrome.browser.tasks.tab_management; package org.chromium.chrome.browser.tasks.tab_management;
import android.content.Context; import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
...@@ -37,6 +39,14 @@ class TabSelectionEditorCoordinator { ...@@ -37,6 +39,14 @@ class TabSelectionEditorCoordinator {
*/ */
void show(List<Tab> tabs); void show(List<Tab> tabs);
/**
* Shows the TabSelectionEditor with the given {@Link Tab}s, and the first
* {@code preSelectedTabCount} tabs being selected.
* @param tabs List of {@link Tab}s to show.
* @param preSelectedTabCount Number of selected {@link Tab}s.
*/
void show(List<Tab> tabs, int preSelectedTabCount);
/** /**
* Hides the TabSelectionEditor. * Hides the TabSelectionEditor.
*/ */
...@@ -79,10 +89,32 @@ class TabSelectionEditorCoordinator { ...@@ -79,10 +89,32 @@ class TabSelectionEditorCoordinator {
mContext = context; mContext = context;
mParentView = parentView; mParentView = parentView;
mTabModelSelector = tabModelSelector; mTabModelSelector = tabModelSelector;
mTabListCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.GRID, context, mTabListCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.GRID, context,
mTabModelSelector, tabContentManager::getTabThumbnailWithCallback, null, false, mTabModelSelector, tabContentManager::getTabThumbnailWithCallback, null, false,
null, null, null, TabProperties.UiType.SELECTABLE, this::getSelectionDelegate, null, null, null, null, TabProperties.UiType.SELECTABLE, this::getSelectionDelegate, null,
null, false, COMPONENT_NAME); null, false, COMPONENT_NAME);
mTabListCoordinator.registerItemType(TabProperties.UiType.DIVIDER, () -> {
return LayoutInflater.from(context).inflate(R.layout.divider_preference, null, false);
}, (model, view, propertyKey) -> {});
RecyclerView.LayoutManager layoutManager =
mTabListCoordinator.getContainerView().getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
((GridLayoutManager) layoutManager)
.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int i) {
int itemType = mTabListCoordinator.getContainerView()
.getAdapter()
.getItemViewType(i);
if (itemType == TabProperties.UiType.DIVIDER) {
return ((GridLayoutManager) layoutManager).getSpanCount();
}
return 1;
}
});
}
mTabSelectionEditorLayout = LayoutInflater.from(context) mTabSelectionEditorLayout = LayoutInflater.from(context)
.inflate(R.layout.tab_selection_editor_layout, null) .inflate(R.layout.tab_selection_editor_layout, null)
...@@ -108,9 +140,16 @@ class TabSelectionEditorCoordinator { ...@@ -108,9 +140,16 @@ class TabSelectionEditorCoordinator {
/** /**
* Resets {@link TabListCoordinator} with the provided list. * Resets {@link TabListCoordinator} with the provided list.
* @param tabs List of {@link Tab}s to reset. * @param tabs List of {@link Tab}s to reset.
* @param preSelectedCount First {@code preSelectedCount} {@code tabs} are pre-selected.
*/ */
void resetWithListOfTabs(@Nullable List<Tab> tabs) { void resetWithListOfTabs(@Nullable List<Tab> tabs, int preSelectedCount) {
mTabListCoordinator.resetWithListOfTabs(tabs); mTabListCoordinator.resetWithListOfTabs(tabs);
if (tabs != null && preSelectedCount > 0) {
assert preSelectedCount < tabs.size();
mTabListCoordinator.addSpecialListItem(
preSelectedCount, TabProperties.UiType.DIVIDER, new PropertyModel());
}
} }
/** /**
......
...@@ -29,7 +29,9 @@ import org.chromium.chrome.tab_ui.R; ...@@ -29,7 +29,9 @@ import org.chromium.chrome.tab_ui.R;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* This class is the mediator that contains all business logic for TabSelectionEditor component. It * This class is the mediator that contains all business logic for TabSelectionEditor component. It
...@@ -45,8 +47,9 @@ class TabSelectionEditorMediator ...@@ -45,8 +47,9 @@ class TabSelectionEditorMediator
/** /**
* Handles the reset event. * Handles the reset event.
* @param tabs List of {@link Tab}s to reset. * @param tabs List of {@link Tab}s to reset.
* @param preSelectedCount First {@code preSelectedCount} {@code tabs} are pre-selected.
*/ */
void resetWithListOfTabs(@Nullable List<Tab> tabs); void resetWithListOfTabs(@Nullable List<Tab> tabs, int preSelectedCount);
} }
/** /**
...@@ -181,8 +184,27 @@ class TabSelectionEditorMediator ...@@ -181,8 +184,27 @@ class TabSelectionEditorMediator
*/ */
@Override @Override
public void show(List<Tab> tabs) { public void show(List<Tab> tabs) {
mResetHandler.resetWithListOfTabs(tabs); show(tabs, 0);
}
@Override
public void show(List<Tab> tabs, int preSelectedTabCount) {
mSelectionDelegate.setSelectionModeEnabledForZeroItems(true); mSelectionDelegate.setSelectionModeEnabledForZeroItems(true);
if (preSelectedTabCount > 0) {
assert preSelectedTabCount <= tabs.size();
Set<Integer> preSelectedTabIds = new HashSet<>();
for (int i = 0; i < preSelectedTabCount; i++) {
preSelectedTabIds.add(tabs.get(i).getId());
}
mSelectionDelegate.setSelectedItems(preSelectedTabIds);
}
mResetHandler.resetWithListOfTabs(tabs, preSelectedTabCount);
if (mPositionProvider != null) { if (mPositionProvider != null) {
mModel.set(TabSelectionEditorProperties.SELECTION_EDITOR_POSITION_RECT, mModel.set(TabSelectionEditorProperties.SELECTION_EDITOR_POSITION_RECT,
mPositionProvider.getSelectionEditorPositionRect()); mPositionProvider.getSelectionEditorPositionRect());
...@@ -221,7 +243,7 @@ class TabSelectionEditorMediator ...@@ -221,7 +243,7 @@ class TabSelectionEditorMediator
@Override @Override
public void hide() { public void hide() {
mResetHandler.resetWithListOfTabs(null); mResetHandler.resetWithListOfTabs(null, 0);
mModel.set(TabSelectionEditorProperties.IS_VISIBLE, false); mModel.set(TabSelectionEditorProperties.IS_VISIBLE, false);
} }
......
...@@ -10,6 +10,7 @@ import android.view.View; ...@@ -10,6 +10,7 @@ import android.view.View;
import org.hamcrest.Description; import org.hamcrest.Description;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
/** /**
* Contains useful RecyclerViewMatcher. * Contains useful RecyclerViewMatcher.
...@@ -66,4 +67,75 @@ public class RecyclerViewMatcherUtils { ...@@ -66,4 +67,75 @@ public class RecyclerViewMatcherUtils {
} }
}; };
} }
/**
* This matcher matches RecyclerView that has a ViewHolder that matches the given view holder
* matcher at the given adapter position.
*
* @param position The adapter position.
* @param viewHolderMatcher A view holder to match.
* @return A matcher that matches view at adapter position and matches the given viewHolder
* matcher.
*/
public static Matcher<View> atPositionWithViewHolder(
int position, Matcher<RecyclerView.ViewHolder> viewHolderMatcher) {
return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {
@Override
protected boolean matchesSafely(RecyclerView recyclerView) {
recyclerView.scrollToPosition(position);
RecyclerView.ViewHolder viewHolder =
recyclerView.findViewHolderForAdapterPosition(position);
if (viewHolder == null) return false;
return viewHolderMatcher.matches(viewHolder);
}
@Override
public void describeTo(Description description) {
description.appendText(viewHolderMatcher + " at position " + position);
}
};
}
/**
* This matcher matches the ViewHolder that has an adapter position equals to the given
* position.
*
* @param position The position to match.
* @return A matcher that matches the viewHolder at the given position.
*/
public static Matcher<RecyclerView.ViewHolder> withViewHolderAtPosition(int position) {
return new TypeSafeMatcher<RecyclerView.ViewHolder>() {
@Override
protected boolean matchesSafely(RecyclerView.ViewHolder viewHolder) {
return viewHolder.getAdapterPosition() == position;
}
@Override
public void describeTo(Description description) {
description.appendText("ViewHolder with adapter position: " + position);
}
};
}
/**
* This matcher matches a ViewHolder that has the given item type.
*
* @param itemType The item type to match.
* @return A matcher that matches a ViewHolder with the given item type.
*/
public static Matcher<RecyclerView.ViewHolder> withItemType(int itemType) {
return new TypeSafeMatcher<RecyclerView.ViewHolder>() {
@Override
protected boolean matchesSafely(RecyclerView.ViewHolder viewHolder) {
return viewHolder.getItemViewType() == itemType;
}
@Override
public void describeTo(Description description) {
description.appendText("ViewHolder with item type: " + itemType);
}
};
}
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.tasks.tab_management; package org.chromium.chrome.browser.tasks.tab_management;
import android.os.Build; import android.os.Build;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest; import android.support.test.filters.MediumTest;
import org.junit.Before; import org.junit.Before;
...@@ -24,6 +25,7 @@ import org.chromium.chrome.browser.tabmodel.TabModelSelector; ...@@ -24,6 +25,7 @@ import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.tab_ui.R; import org.chromium.chrome.tab_ui.R;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule; import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.util.ChromeTabUtils;
import org.chromium.chrome.test.util.browser.Features; import org.chromium.chrome.test.util.browser.Features;
import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.test.util.UiRestriction; import org.chromium.ui.test.util.UiRestriction;
...@@ -183,6 +185,58 @@ public class TabSelectionEditorTest { ...@@ -183,6 +185,58 @@ public class TabSelectionEditorTest {
mRobot.resultRobot.verifyToolbarActionButtonDisabled(); mRobot.resultRobot.verifyToolbarActionButtonDisabled();
} }
@Test
@MediumTest
public void testShowTabsWithPreSelectedTabs() {
List<Tab> tabs = getTabsInCurrentTabModel();
int preSelectedTabCount = 1;
TestThreadUtils.runOnUiThreadBlocking(
() -> { mTabSelectionEditorController.show(tabs, preSelectedTabCount); });
mRobot.resultRobot.verifyTabSelectionEditorIsVisible()
.verifyToolbarActionButtonDisabled()
.verifyToolbarActionButtonWithResourceId(R.string.tab_selection_editor_group)
.verifyToolbarSelectionText("1 selected")
.verifyHasAtLeastNItemVisible(tabs.size() + 1)
.verifyItemSelectedAtAdapterPosition(0)
.verifyHasItemViewTypeAtAdapterPosition(1, TabProperties.UiType.DIVIDER)
.verifyDividerAlwaysStartsAtTheEdgeOfScreen();
}
@Test
@MediumTest
public void testShowTabsWithPreSelectedTabs_10Tabs() {
int preSelectedTabCount = 10;
int additionalTabCount =
preSelectedTabCount + 1 - mTabModelSelector.getCurrentModel().getCount();
for (int i = 0; i < additionalTabCount; i++) {
ChromeTabUtils.newTabFromMenu(InstrumentationRegistry.getInstrumentation(),
mActivityTestRule.getActivity(), mTabModelSelector.isIncognitoSelected(), true);
}
List<Tab> tabs = getTabsInCurrentTabModel();
TestThreadUtils.runOnUiThreadBlocking(
() -> { mTabSelectionEditorController.show(tabs, preSelectedTabCount); });
mRobot.resultRobot.verifyToolbarSelectionText("10 selected")
.verifyHasItemViewTypeAtAdapterPosition(
preSelectedTabCount, TabProperties.UiType.DIVIDER)
.verifyDividerAlwaysStartsAtTheEdgeOfScreenAtPosition(preSelectedTabCount);
}
@Test
@MediumTest
public void testDividerIsNotClickable() {
List<Tab> tabs = getTabsInCurrentTabModel();
int preSelectedTabCount = 1;
TestThreadUtils.runOnUiThreadBlocking(
() -> { mTabSelectionEditorController.show(tabs, preSelectedTabCount); });
mRobot.resultRobot.verifyDividerNotClickableNotFocusable();
}
private List<Tab> getTabsInCurrentTabModel() { private List<Tab> getTabsInCurrentTabModel() {
List<Tab> tabs = new ArrayList<>(); List<Tab> tabs = new ArrayList<>();
......
...@@ -8,9 +8,12 @@ import static android.support.test.espresso.Espresso.onView; ...@@ -8,9 +8,12 @@ import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition; import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
import static android.support.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
import static android.support.test.espresso.matcher.RootMatchers.withDecorView; import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
import static android.support.test.espresso.matcher.ViewMatchers.isClickable;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; 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.isEnabled;
import static android.support.test.espresso.matcher.ViewMatchers.isFocusable;
import static android.support.test.espresso.matcher.ViewMatchers.withClassName; import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription; import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withId;
...@@ -21,6 +24,10 @@ import static org.hamcrest.Matchers.allOf; ...@@ -21,6 +24,10 @@ import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not; 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.os.Build;
import android.support.test.espresso.NoMatchingRootException; import android.support.test.espresso.NoMatchingRootException;
import android.support.test.espresso.Root; import android.support.test.espresso.Root;
...@@ -41,7 +48,7 @@ public class TabSelectionEditorTestingRobot { ...@@ -41,7 +48,7 @@ public class TabSelectionEditorTestingRobot {
/** /**
* @return A root matcher that matches the TabSelectionEditor popup decor view. * @return A root matcher that matches the TabSelectionEditor popup decor view.
*/ */
private static Matcher<Root> isTabSelectionEditorPopup() { public static Matcher<Root> isTabSelectionEditorPopup() {
return new TypeSafeMatcher<Root>() { return new TypeSafeMatcher<Root>() {
@Override @Override
public void describeTo(Description description) { public void describeTo(Description description) {
...@@ -66,7 +73,7 @@ public class TabSelectionEditorTestingRobot { ...@@ -66,7 +73,7 @@ public class TabSelectionEditorTestingRobot {
/** /**
* @return A view matcher that matches the item is selected. * @return A view matcher that matches the item is selected.
*/ */
private static Matcher<View> itemIsSelected() { public static Matcher<View> itemIsSelected() {
return new BoundedMatcher<View, SelectableTabGridView>(SelectableTabGridView.class) { return new BoundedMatcher<View, SelectableTabGridView>(SelectableTabGridView.class) {
private SelectableTabGridView mSelectableTabGridView; private SelectableTabGridView mSelectableTabGridView;
@Override @Override
...@@ -105,6 +112,23 @@ public class TabSelectionEditorTestingRobot { ...@@ -105,6 +112,23 @@ public class TabSelectionEditorTestingRobot {
}; };
} }
/**
* @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.Result resultRobot;
public final TabSelectionEditorTestingRobot.Action actionRobot; public final TabSelectionEditorTestingRobot.Action actionRobot;
...@@ -252,5 +276,67 @@ public class TabSelectionEditorTestingRobot { ...@@ -252,5 +276,67 @@ public class TabSelectionEditorTestingRobot {
onView(withText(text)).check(matches(isDisplayed())); onView(withText(text)).check(matches(isDisplayed()));
return this; return this;
} }
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;
}
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;
}
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.
*/
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;
}
} }
} }
...@@ -7,9 +7,12 @@ package org.chromium.chrome.browser.tasks.tab_management; ...@@ -7,9 +7,12 @@ package org.chromium.chrome.browser.tasks.tab_management;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
...@@ -1355,6 +1358,30 @@ public class TabListMediatorUnitTest { ...@@ -1355,6 +1358,30 @@ public class TabListMediatorUnitTest {
verify(mRemoveEditor).apply(); verify(mRemoveEditor).apply();
} }
@Test
public void addSpecialItem() {
mMediator.addSpecialItemToModel(0, TabProperties.UiType.DIVIDER, new PropertyModel());
assertTrue(mModel.size() > 0);
assertEquals(TabProperties.UiType.DIVIDER, mModel.get(0).type);
}
@Test
public void addSpecialItem_notPersistOnReset() {
mMediator.addSpecialItemToModel(0, TabProperties.UiType.DIVIDER, new PropertyModel());
assertEquals(TabProperties.UiType.DIVIDER, mModel.get(0).type);
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
mMediator.resetWithListOfTabs(tabs, false, false);
assertThat(mModel.size(), equalTo(2));
assertNotEquals(TabProperties.UiType.DIVIDER, mModel.get(0).type);
assertNotEquals(TabProperties.UiType.DIVIDER, mModel.get(1).type);
mMediator.addSpecialItemToModel(1, TabProperties.UiType.DIVIDER, new PropertyModel());
assertThat(mModel.size(), equalTo(3));
assertEquals(TabProperties.UiType.DIVIDER, mModel.get(1).type);
}
private void initAndAssertAllProperties() { private void initAndAssertAllProperties() {
List<Tab> tabs = new ArrayList<>(); List<Tab> tabs = new ArrayList<>();
for (int i = 0; i < mTabModel.getCount(); i++) { for (int i = 0; i < mTabModel.getCount(); i++) {
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. --> found in the LICENSE file. -->
<View <View
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/divider_view"
style="@style/HorizontalDivider" style="@style/HorizontalDivider"
android:importantForAccessibility="no" /> android:importantForAccessibility="no" />
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment