blob: 0ab03f37c389c632f603c44067245997777db259 [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 org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
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.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.chromium.chrome.browser.ChromeFeatureList.TAB_GROUPS_ANDROID;
import static org.chromium.chrome.browser.ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID;
import android.app.Activity;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.View;
import androidx.annotation.IntDef;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.Callback;
import org.chromium.base.ContextUtils;
import org.chromium.base.UserDataHost;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
import org.chromium.chrome.browser.favicon.FaviconHelper;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.flags.FeatureUtilities;
import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabImpl;
import org.chromium.chrome.browser.tab.TabObserver;
import org.chromium.chrome.browser.tabmodel.EmptyTabModelFilter;
import org.chromium.chrome.browser.tabmodel.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelFilter;
import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
import org.chromium.chrome.browser.tabmodel.TabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
import org.chromium.chrome.browser.util.UrlUtilities;
import org.chromium.chrome.browser.util.UrlUtilitiesJni;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.components.feature_engagement.EventConstants;
import org.chromium.components.feature_engagement.Tracker;
import org.chromium.testing.local.LocalRobolectricTestRunner;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Tests for {@link TabListMediator}.
*/
@RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@Features.EnableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
public class TabListMediatorUnitTest {
@Rule
public TestRule mProcessor = new Features.JUnitProcessor();
@Rule
public JniMocker mMocker = new JniMocker();
private static final String TAB1_TITLE = "Tab1";
private static final String TAB2_TITLE = "Tab2";
private static final String TAB3_TITLE = "Tab3";
private static final String NEW_TITLE = "New title";
private static final String CUSTOMIZED_DIALOG_TITLE1 = "Cool Tabs";
private static final String TAB_GROUP_TITLES_FILE_NAME = "tab_group_titles";
private static final String TAB1_DOMAIN = "tab1.com";
private static final String TAB2_DOMAIN = "tab2.com";
private static final String TAB3_DOMAIN = "tab3.com";
private static final String NEW_DOMAIN = "new.com";
private static final String TAB1_URL = "https://" + TAB1_DOMAIN;
private static final String TAB2_URL = "https://" + TAB2_DOMAIN;
private static final String TAB3_URL = "https://" + TAB3_DOMAIN;
private static final String NEW_URL = "https://" + NEW_DOMAIN;
private static final int TAB1_ID = 456;
private static final int TAB2_ID = 789;
private static final int TAB3_ID = 123;
private static final int POSITION1 = 0;
private static final int POSITION2 = 1;
@IntDef({TabListMediatorType.TAB_SWITCHER, TabListMediatorType.TAB_STRIP,
TabListMediatorType.TAB_GRID_DIALOG})
@Retention(RetentionPolicy.SOURCE)
public @interface TabListMediatorType {
int TAB_SWITCHER = 0;
int TAB_STRIP = 1;
int TAB_GRID_DIALOG = 2;
int NUM_ENTRIES = 3;
}
@Mock
TabContentManager mTabContentManager;
@Mock
TabModelSelectorImpl mTabModelSelector;
@Mock
TabModelFilterProvider mTabModelFilterProvider;
@Mock
TabModelFilter mTabModelFilter;
@Mock
TabModel mTabModel;
@Mock
TabListFaviconProvider mTabListFaviconProvider;
@Mock
RecyclerView mRecyclerView;
@Mock
RecyclerView.Adapter mAdapter;
@Mock
TabGroupModelFilter mTabGroupModelFilter;
@Mock
EmptyTabModelFilter mEmptyTabModelFilter;
@Mock
TabListMediator.TabGridDialogHandler mTabGridDialogHandler;
@Mock
TabListMediator.CreateGroupButtonProvider mCreateGroupButtonProvider;
@Mock
TabListMediator.GridCardOnClickListenerProvider mGridCardOnClickListenerProvider;
@Mock
Drawable mFaviconDrawable;
@Mock
Bitmap mFaviconBitmap;
@Mock
Activity mContext;
@Mock
TabListMediator.TabActionListener mOpenGroupActionListener;
@Mock
GridLayoutManager mGridLayoutManager;
@Mock
Profile mProfile;
@Mock
Tracker mTracker;
@Mock
TabListMediator.TitleProvider mTitleProvider;
@Mock
SharedPreferences mSharedPreferences;
@Mock
SharedPreferences.Editor mEditor;
@Mock
SharedPreferences.Editor mPutStringEditor;
@Mock
SharedPreferences.Editor mRemoveEditor;
@Mock
UrlUtilities.Natives mUrlUtilitiesJniMock;
@Captor
ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor;
@Captor
ArgumentCaptor<FaviconHelper.FaviconImageCallback> mFaviconCallbackCaptor;
@Captor
ArgumentCaptor<TabObserver> mTabObserverCaptor;
@Captor
ArgumentCaptor<Callback<Drawable>> mCallbackCaptor;
@Captor
ArgumentCaptor<TabGroupModelFilter.Observer> mTabGroupModelFilterObserverCaptor;
@Captor
ArgumentCaptor<ComponentCallbacks> mComponentCallbacksCaptor;
private TabImpl mTab1;
private TabImpl mTab2;
private TabListMediator mMediator;
private TabListModel mModel;
private SimpleRecyclerViewAdapter.ViewHolder mViewHolder1;
private SimpleRecyclerViewAdapter.ViewHolder mViewHolder2;
private RecyclerView.ViewHolder mDummyViewHolder1;
private RecyclerView.ViewHolder mDummyViewHolder2;
private View mItemView1 = mock(View.class);
private View mItemView2 = mock(View.class);
private TabGroupModelFilter.Observer mMediatorTabGroupModelFilterObserver;
@Before
public void setUp() {
RecordUserAction.setDisabledForTests(true);
RecordHistogram.setDisabledForTests(true);
MockitoAnnotations.initMocks(this);
mMocker.mock(UrlUtilitiesJni.TEST_HOOKS, mUrlUtilitiesJniMock);
FeatureUtilities.setTabGroupsAndroidEnabledForTesting(false);
FeatureUtilities.setStartSurfaceEnabledForTesting(false);
mTab1 = prepareTab(TAB1_ID, TAB1_TITLE, TAB1_URL);
mTab2 = prepareTab(TAB2_ID, TAB2_TITLE, TAB2_URL);
mViewHolder1 = prepareViewHolder(TAB1_ID, POSITION1);
mViewHolder2 = prepareViewHolder(TAB2_ID, POSITION2);
mDummyViewHolder1 = prepareDummyViewHolder(mItemView1, POSITION1);
mDummyViewHolder2 = prepareDummyViewHolder(mItemView2, POSITION2);
List<Tab> tabs1 = new ArrayList<>(Arrays.asList(mTab1));
List<Tab> tabs2 = new ArrayList<>(Arrays.asList(mTab2));
List<TabModel> tabModelList = new ArrayList<>();
tabModelList.add(mTabModel);
doNothing()
.when(mTabContentManager)
.getTabThumbnailWithCallback(any(), any(), anyBoolean(), anyBoolean());
doReturn(mTabModel).when(mTabModelSelector).getCurrentModel();
doReturn(tabModelList).when(mTabModelSelector).getModels();
doReturn(mTabModelFilterProvider).when(mTabModelSelector).getTabModelFilterProvider();
doReturn(mTabModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
doReturn(TAB1_ID).when(mTabModelSelector).getCurrentTabId();
doNothing()
.when(mTabModelFilterProvider)
.addTabModelFilterObserver(mTabModelObserverCaptor.capture());
doReturn(mTab1).when(mTabModel).getTabAt(POSITION1);
doReturn(mTab2).when(mTabModel).getTabAt(POSITION2);
doReturn(POSITION1).when(mTabModel).indexOf(mTab1);
doReturn(POSITION2).when(mTabModel).indexOf(mTab2);
doNothing().when(mTab1).addObserver(mTabObserverCaptor.capture());
doReturn(0).when(mTabModel).index();
doReturn(2).when(mTabModel).getCount();
doNothing()
.when(mTabListFaviconProvider)
.getFaviconForUrlAsync(anyString(), anyBoolean(), mCallbackCaptor.capture());
doReturn(mFaviconDrawable)
.when(mTabListFaviconProvider)
.getFaviconForUrlSync(anyString(), anyBoolean(), any(Bitmap.class));
doReturn(mTab1).when(mTabModelSelector).getTabById(TAB1_ID);
doReturn(mTab2).when(mTabModelSelector).getTabById(TAB2_ID);
doReturn(tabs1).when(mTabGroupModelFilter).getRelatedTabList(TAB1_ID);
doReturn(tabs2).when(mTabGroupModelFilter).getRelatedTabList(TAB2_ID);
doReturn(POSITION1).when(mTabGroupModelFilter).indexOf(mTab1);
doReturn(POSITION2).when(mTabGroupModelFilter).indexOf(mTab2);
doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION2);
doReturn(mOpenGroupActionListener)
.when(mGridCardOnClickListenerProvider)
.openTabGridDialog(any(Tab.class));
doNothing().when(mContext).registerComponentCallbacks(mComponentCallbacksCaptor.capture());
doReturn(mGridLayoutManager).when(mRecyclerView).getLayoutManager();
doReturn(mSharedPreferences)
.when(mContext)
.getSharedPreferences(TAB_GROUP_TITLES_FILE_NAME, Context.MODE_PRIVATE);
doReturn(mEditor).when(mSharedPreferences).edit();
doReturn(mRemoveEditor).when(mEditor).remove(any(String.class));
doReturn(mPutStringEditor).when(mEditor).putString(any(String.class), any(String.class));
doReturn(TAB1_DOMAIN)
.when(mUrlUtilitiesJniMock)
.getDomainAndRegistry(eq(TAB1_URL), anyBoolean());
doReturn(TAB2_DOMAIN)
.when(mUrlUtilitiesJniMock)
.getDomainAndRegistry(eq(TAB2_URL), anyBoolean());
doReturn(TAB3_DOMAIN)
.when(mUrlUtilitiesJniMock)
.getDomainAndRegistry(eq(TAB3_URL), anyBoolean());
mModel = new TabListModel();
mMediator = new TabListMediator(mContext, mModel, mTabModelSelector,
mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
mTabListFaviconProvider, false, null, null, mGridCardOnClickListenerProvider, null,
getClass().getSimpleName(), 0);
mMediator.registerOrientationListener(mGridLayoutManager);
TrackerFactory.setTrackerForTests(mTracker);
ContextUtils.initApplicationContextForTests(mContext);
}
@After
public void tearDown() {
RecordUserAction.setDisabledForTests(false);
RecordHistogram.setDisabledForTests(false);
FeatureUtilities.setTabGroupsAndroidEnabledForTesting(null);
FeatureUtilities.setStartSurfaceEnabledForTesting(null);
}
@Test
public void initializesWithCurrentTabs() {
initAndAssertAllProperties();
}
@Test
public void updatesTitle_WithoutStoredTitle() {
initAndAssertAllProperties();
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
doReturn(NEW_TITLE).when(mTitleProvider).getTitle(mTab1);
mTabObserverCaptor.getValue().onTitleUpdated(mTab1);
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(NEW_TITLE));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void updatesTitle_WithStoredTitle_TabGroup() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Mock that tab1 and new tab are in the same group with root ID as TAB1_ID.
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, newTab));
createTabGroup(tabs, TAB1_ID);
// Mock that we have a stored title stored with reference to root ID of tab1.
when(mSharedPreferences.getString(String.valueOf(mTab1.getRootId()), null))
.thenReturn(CUSTOMIZED_DIALOG_TITLE1);
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
mTabObserverCaptor.getValue().onTitleUpdated(mTab1);
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(CUSTOMIZED_DIALOG_TITLE1));
}
@Test
public void updatesFavicon_SingleTab_GTS() {
initAndAssertAllProperties();
mMediator.setActionOnAllRelatedTabsForTesting(true);
mModel.get(0).model.set(TabProperties.FAVICON, null);
assertNull(mModel.get(0).model.get(TabProperties.FAVICON));
mTabObserverCaptor.getValue().onFaviconUpdated(mTab1, mFaviconBitmap);
assertNotNull(mModel.get(0).model.get(TabProperties.FAVICON));
}
@Test
public void updatesFavicon_SingleTab_NonGTS() {
initAndAssertAllProperties();
mModel.get(0).model.set(TabProperties.FAVICON, null);
assertNull(mModel.get(0).model.get(TabProperties.FAVICON));
mTabObserverCaptor.getValue().onFaviconUpdated(mTab1, mFaviconBitmap);
assertNotNull(mModel.get(0).model.get(TabProperties.FAVICON));
}
@Test
public void updatesFavicon_TabGroup_GTS() {
initAndAssertAllProperties();
mMediator.setActionOnAllRelatedTabsForTesting(true);
assertNotNull(mModel.get(0).model.get(TabProperties.FAVICON));
// Assert that tab1 is in a group.
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
doReturn(Arrays.asList(mTab1, newTab)).when(mTabModelFilter).getRelatedTabList(eq(TAB1_ID));
mTabObserverCaptor.getValue().onFaviconUpdated(mTab1, mFaviconBitmap);
assertNull(mModel.get(0).model.get(TabProperties.FAVICON));
}
@Test
public void updateFavicon_StaleIndex() {
initAndAssertAllProperties();
mModel.get(0).model.set(TabProperties.FAVICON, null);
mModel.get(1).model.set(TabProperties.FAVICON, null);
mMediator.updateFaviconForTab(mTab2, null);
assertThat(mModel.indexFromId(TAB2_ID), equalTo(1));
// Before executing callback, there is a deletion in tab list model which makes the index
// stale.
mModel.removeAt(0);
assertThat(mModel.indexFromId(TAB2_ID), equalTo(0));
// Start to execute callback.
mCallbackCaptor.getValue().onResult(mFaviconDrawable);
assertThat(mModel.get(0).model.get(TabProperties.FAVICON), equalTo(mFaviconDrawable));
}
@Test
public void sendsSelectSignalCorrectly() {
initAndAssertAllProperties();
mModel.get(1)
.model.get(TabProperties.TAB_SELECTED_LISTENER)
.run(mModel.get(1).model.get(TabProperties.TAB_ID));
verify(mGridCardOnClickListenerProvider)
.onTabSelecting(mModel.get(1).model.get(TabProperties.TAB_ID));
}
@Test
public void sendsCloseSignalCorrectly() {
initAndAssertAllProperties();
mModel.get(1)
.model.get(TabProperties.TAB_CLOSED_LISTENER)
.run(mModel.get(1).model.get(TabProperties.TAB_ID));
verify(mTabModel).closeTab(eq(mTab2), eq(null), eq(false), eq(false), eq(true));
}
@Test
public void sendsMoveTabSignalCorrectlyWithoutGroup() {
initAndAssertAllProperties();
TabGridItemTouchHelperCallback itemTouchHelperCallback = getItemTouchHelperCallback();
doReturn(mEmptyTabModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
itemTouchHelperCallback.onMove(mRecyclerView, mViewHolder1, mViewHolder2);
verify(mTabModel).moveTab(eq(TAB1_ID), eq(2));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
public void sendsMoveTabSignalCorrectlyWithGroup() {
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
TabGridItemTouchHelperCallback itemTouchHelperCallback = getItemTouchHelperCallback();
itemTouchHelperCallback.setActionsOnAllRelatedTabsForTesting(true);
doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
itemTouchHelperCallback.onMove(mRecyclerView, mViewHolder1, mViewHolder2);
verify(mTabGroupModelFilter).moveRelatedTabs(eq(TAB1_ID), eq(2));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
public void sendsMoveTabSignalCorrectlyWithinGroup() {
setUpForTabGroupOperation(TabListMediatorType.TAB_GRID_DIALOG);
getItemTouchHelperCallback().onMove(mRecyclerView, mViewHolder1, mViewHolder2);
verify(mTabModel).moveTab(eq(TAB1_ID), eq(2));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
public void sendsMergeTabSignalCorrectly() {
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
TabGridItemTouchHelperCallback itemTouchHelperCallback = getItemTouchHelperCallback();
itemTouchHelperCallback.setActionsOnAllRelatedTabsForTesting(true);
itemTouchHelperCallback.setHoveredTabIndexForTesting(POSITION1);
itemTouchHelperCallback.setSelectedTabIndexForTesting(POSITION2);
itemTouchHelperCallback.getMovementFlags(mRecyclerView, mDummyViewHolder1);
doReturn(mAdapter).when(mRecyclerView).getAdapter();
// Simulate the drop action.
itemTouchHelperCallback.onSelectedChanged(
mDummyViewHolder2, ItemTouchHelper.ACTION_STATE_IDLE);
verify(mTabGroupModelFilter).mergeTabsToGroup(eq(TAB2_ID), eq(TAB1_ID));
verify(mGridLayoutManager).removeView(mItemView2);
verify(mTracker).notifyEvent(eq(EventConstants.TAB_DRAG_AND_DROP_TO_GROUP));
}
@Test
@Features.DisableFeatures({TAB_GROUPS_ANDROID, TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
public void neverSendsMergeTabSignal_Without_Group() {
initAndAssertAllProperties();
mMediator.setActionOnAllRelatedTabsForTesting(true);
TabGridItemTouchHelperCallback itemTouchHelperCallback = getItemTouchHelperCallback();
itemTouchHelperCallback.setActionsOnAllRelatedTabsForTesting(true);
itemTouchHelperCallback.setHoveredTabIndexForTesting(POSITION1);
itemTouchHelperCallback.setSelectedTabIndexForTesting(POSITION2);
itemTouchHelperCallback.getMovementFlags(mRecyclerView, mDummyViewHolder1);
doReturn(mAdapter).when(mRecyclerView).getAdapter();
// Simulate the drop action.
itemTouchHelperCallback.onSelectedChanged(
mDummyViewHolder1, ItemTouchHelper.ACTION_STATE_IDLE);
verify(mGridLayoutManager, never()).removeView(any(View.class));
}
@Test
@Features.EnableFeatures({TAB_GROUPS_ANDROID})
@Features.DisableFeatures({TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
public void neverSendsMergeTabSignal_With_Group_Without_Group_Improvement() {
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
TabGridItemTouchHelperCallback itemTouchHelperCallback = getItemTouchHelperCallback();
itemTouchHelperCallback.setActionsOnAllRelatedTabsForTesting(true);
itemTouchHelperCallback.setHoveredTabIndexForTesting(POSITION1);
itemTouchHelperCallback.setSelectedTabIndexForTesting(POSITION2);
itemTouchHelperCallback.getMovementFlags(mRecyclerView, mDummyViewHolder1);
doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
doReturn(mAdapter).when(mRecyclerView).getAdapter();
// Simulate the drop action.
itemTouchHelperCallback.onSelectedChanged(
mDummyViewHolder1, ItemTouchHelper.ACTION_STATE_IDLE);
verify(mTabGroupModelFilter, never()).mergeTabsToGroup(anyInt(), anyInt());
verify(mGridLayoutManager, never()).removeView(any(View.class));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void sendsUngroupSignalCorrectly() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_GRID_DIALOG);
TabGridItemTouchHelperCallback itemTouchHelperCallback = getItemTouchHelperCallback();
itemTouchHelperCallback.setActionsOnAllRelatedTabsForTesting(false);
itemTouchHelperCallback.setUnGroupTabIndexForTesting(POSITION1);
itemTouchHelperCallback.getMovementFlags(mRecyclerView, mDummyViewHolder1);
doReturn(mAdapter).when(mRecyclerView).getAdapter();
doReturn(1).when(mAdapter).getItemCount();
// Simulate the ungroup action.
itemTouchHelperCallback.onSelectedChanged(
mDummyViewHolder1, ItemTouchHelper.ACTION_STATE_IDLE);
verify(mTabGroupModelFilter).moveTabOutOfGroup(eq(TAB1_ID));
verify(mGridLayoutManager).removeView(mItemView1);
}
@Test
public void tabClosure() {
initAndAssertAllProperties();
assertThat(mModel.size(), equalTo(2));
mTabModelObserverCaptor.getValue().willCloseTab(mTab2, false);
assertThat(mModel.size(), equalTo(1));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID));
}
@Test
public void tabClosure_IgnoresUpdatesForTabsOutsideOfModel() {
initAndAssertAllProperties();
mTabModelObserverCaptor.getValue().willCloseTab(
prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL), false);
assertThat(mModel.size(), equalTo(2));
}
@Test
public void tabAddition_RestoreNotComplete() {
initAndAssertAllProperties();
mMediator.setActionOnAllRelatedTabsForTesting(true);
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
doReturn(mTab1).when(mTabModelFilter).getTabAt(0);
doReturn(mTab2).when(mTabModelFilter).getTabAt(1);
doReturn(newTab).when(mTabModelFilter).getTabAt(2);
doReturn(3).when(mTabModelFilter).getCount();
doReturn(Arrays.asList(newTab)).when(mTabModelFilter).getRelatedTabList(eq(TAB3_ID));
assertThat(mModel.size(), equalTo(2));
mTabModelObserverCaptor.getValue().didAddTab(newTab, TabLaunchType.FROM_RESTORE);
// When tab restoring stage is not yet finished, this tab info should not be added to
// property model.
assertThat(mModel.size(), equalTo(2));
}
@Test
public void tabAddition_Restore() {
initAndAssertAllProperties();
mMediator.setActionOnAllRelatedTabsForTesting(true);
// Mock that tab restoring stage is over.
mMediator.setTabRestoreCompletedForTesting(true);
TabListMediator.TabActionListener actionListenerBeforeUpdate =
mModel.get(1).model.get(TabProperties.TAB_SELECTED_LISTENER);
// Mock that newTab was in the same group with tab, and now it is restored.
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
List<Tab> tabs = Arrays.asList(mTab2, newTab);
doReturn(mTab1).when(mTabModelFilter).getTabAt(0);
doReturn(mTab2).when(mTabModelFilter).getTabAt(1);
doReturn(2).when(mTabModelFilter).getCount();
doReturn(1).when(mTabModelFilter).indexOf(newTab);
doReturn(tabs).when(mTabModelFilter).getRelatedTabList(eq(TAB3_ID));
doReturn(tabs).when(mTabModelFilter).getRelatedTabList(eq(TAB2_ID));
assertThat(mModel.size(), equalTo(2));
mTabModelObserverCaptor.getValue().didAddTab(newTab, TabLaunchType.FROM_RESTORE);
TabListMediator.TabActionListener actionListenerAfterUpdate =
mModel.get(1).model.get(TabProperties.TAB_SELECTED_LISTENER);
// The selection listener should be updated which indicates that corresponding property
// model is updated.
assertThat(actionListenerBeforeUpdate, not(actionListenerAfterUpdate));
assertThat(mModel.size(), equalTo(2));
}
@Test
public void tabAddition_GTS() {
initAndAssertAllProperties();
mMediator.setActionOnAllRelatedTabsForTesting(true);
mMediator.setTabRestoreCompletedForTesting(true);
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
doReturn(mTab1).when(mTabModelFilter).getTabAt(0);
doReturn(mTab2).when(mTabModelFilter).getTabAt(1);
doReturn(newTab).when(mTabModelFilter).getTabAt(2);
doReturn(3).when(mTabModelFilter).getCount();
doReturn(Arrays.asList(newTab))
.when(mTabModelFilter)
.getRelatedTabList(eq(TAB3_ID));
assertThat(mModel.size(), equalTo(2));
mTabModelObserverCaptor.getValue().didAddTab(newTab, TabLaunchType.FROM_CHROME_UI);
assertThat(mModel.size(), equalTo(3));
assertThat(mModel.get(2).model.get(TabProperties.TAB_ID), equalTo(TAB3_ID));
assertThat(mModel.get(2).model.get(TabProperties.TITLE), equalTo(TAB3_TITLE));
}
@Test
public void tabAddition_GTS_Skip() {
initAndAssertAllProperties();
mMediator.setActionOnAllRelatedTabsForTesting(true);
mMediator.setTabRestoreCompletedForTesting(true);
// Add a new tab to the group with mTab2.
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
doReturn(mTab1).when(mTabModelFilter).getTabAt(0);
doReturn(mTab2).when(mTabModelFilter).getTabAt(1);
doReturn(2).when(mTabModelFilter).getCount();
doReturn(Arrays.asList(mTab2, newTab))
.when(mTabModelFilter)
.getRelatedTabList(eq(TAB3_ID));
assertThat(mModel.size(), equalTo(2));
mTabModelObserverCaptor.getValue().didAddTab(newTab, TabLaunchType.FROM_CHROME_UI);
assertThat(mModel.size(), equalTo(2));
}
@Test
public void tabAddition_GTS_Middle() {
initAndAssertAllProperties();
mMediator.setActionOnAllRelatedTabsForTesting(true);
mMediator.setTabRestoreCompletedForTesting(true);
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
doReturn(mTab1).when(mTabModelFilter).getTabAt(0);
doReturn(newTab).when(mTabModelFilter).getTabAt(1);
doReturn(mTab2).when(mTabModelFilter).getTabAt(2);
doReturn(3).when(mTabModelFilter).getCount();
doReturn(Arrays.asList(newTab))
.when(mTabModelFilter)
.getRelatedTabList(eq(TAB3_ID));
assertThat(mModel.size(), equalTo(2));
mTabModelObserverCaptor.getValue().didAddTab(newTab, TabLaunchType.FROM_CHROME_UI);
assertThat(mModel.size(), equalTo(3));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB3_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB3_TITLE));
}
@Test
public void tabAddition_Dialog_End() {
initAndAssertAllProperties();
mMediator.setTabRestoreCompletedForTesting(true);
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
doReturn(3).when(mTabModel).getCount();
doReturn(Arrays.asList(mTab1, mTab2, newTab))
.when(mTabModelFilter)
.getRelatedTabList(eq(TAB1_ID));
assertThat(mModel.size(), equalTo(2));
mTabModelObserverCaptor.getValue().didAddTab(newTab, TabLaunchType.FROM_CHROME_UI);
assertThat(mModel.size(), equalTo(3));
assertThat(mModel.get(2).model.get(TabProperties.TAB_ID), equalTo(TAB3_ID));
assertThat(mModel.get(2).model.get(TabProperties.TITLE), equalTo(TAB3_TITLE));
}
@Test
public void tabAddition_Dialog_Middle() {
initAndAssertAllProperties();
mMediator.setTabRestoreCompletedForTesting(true);
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
doReturn(3).when(mTabModel).getCount();
doReturn(Arrays.asList(mTab1, newTab, mTab2))
.when(mTabModelFilter)
.getRelatedTabList(eq(TAB1_ID));
assertThat(mModel.size(), equalTo(2));
mTabModelObserverCaptor.getValue().didAddTab(newTab, TabLaunchType.FROM_CHROME_UI);
assertThat(mModel.size(), equalTo(3));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB3_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB3_TITLE));
}
@Test
public void tabAddition_Dialog_Skip() {
initAndAssertAllProperties();
mMediator.setTabRestoreCompletedForTesting(true);
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
// newTab is of another group.
doReturn(Arrays.asList(mTab1, mTab2)).when(mTabModelFilter).getRelatedTabList(eq(TAB1_ID));
assertThat(mModel.size(), equalTo(2));
mTabModelObserverCaptor.getValue().didAddTab(newTab, TabLaunchType.FROM_CHROME_UI);
assertThat(mModel.size(), equalTo(2));
}
@Test
public void tabSelection() {
initAndAssertAllProperties();
mTabModelObserverCaptor.getValue().didSelectTab(
mTab2, TabLaunchType.FROM_CHROME_UI, TAB1_ID);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.IS_SELECTED), equalTo(false));
assertThat(mModel.get(1).model.get(TabProperties.IS_SELECTED), equalTo(true));
}
@Test
public void tabClosureUndone() {
initAndAssertAllProperties();
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
doReturn(3).when(mTabModel).getCount();
doReturn(Arrays.asList(mTab1, mTab2, newTab))
.when(mTabModelFilter)
.getRelatedTabList(eq(TAB1_ID));
mTabModelObserverCaptor.getValue().tabClosureUndone(newTab);
assertThat(mModel.size(), equalTo(3));
assertThat(mModel.get(2).model.get(TabProperties.TAB_ID), equalTo(TAB3_ID));
assertThat(mModel.get(2).model.get(TabProperties.TITLE), equalTo(TAB3_TITLE));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabMergeIntoGroup() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Assume that moveTab in TabModel is finished. Selected tab in the group becomes mTab1.
doReturn(mTab1).when(mTabModel).getTabAt(POSITION2);
doReturn(mTab2).when(mTabModel).getTabAt(POSITION1);
doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
// Assume that reset in TabGroupModelFilter is finished.
doReturn(new ArrayList<>(Arrays.asList(mTab1, mTab2)))
.when(mTabGroupModelFilter)
.getRelatedTabList(TAB1_ID);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
assertThat(mModel.indexFromId(TAB1_ID), equalTo(POSITION1));
assertThat(mModel.indexFromId(TAB2_ID), equalTo(POSITION2));
assertNotNull(mModel.get(0).model.get(TabProperties.FAVICON));
assertNotNull(mModel.get(1).model.get(TabProperties.FAVICON));
mMediatorTabGroupModelFilterObserver.didMergeTabToGroup(mTab1, TAB2_ID);
assertThat(mModel.size(), equalTo(1));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
assertNull(mModel.get(0).model.get(TabProperties.FAVICON));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabMoveOutOfGroup_GTS_Moved_Tab_Selected() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Assume that two tabs are in the same group before ungroup.
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab2));
mMediator.resetWithListOfTabs(tabs, false, false);
assertThat(mModel.size(), equalTo(1));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
// Assume that TabGroupModelFilter is already updated.
doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION1);
doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION2);
doReturn(2).when(mTabGroupModelFilter).getCount();
mMediatorTabGroupModelFilterObserver.didMoveTabOutOfGroup(mTab1, POSITION1);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
assertThat(mModel.get(0).model.get(TabProperties.IS_SELECTED), equalTo(false));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
assertThat(mModel.get(1).model.get(TabProperties.IS_SELECTED), equalTo(true));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabMoveOutOfGroup_GTS_Origin_Tab_Selected() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Assume that two tabs are in the same group before ungroup.
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1));
mMediator.resetWithListOfTabs(tabs, false, false);
assertThat(mModel.size(), equalTo(1));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
// Assume that TabGroupModelFilter is already updated.
doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION2);
doReturn(2).when(mTabGroupModelFilter).getCount();
mMediatorTabGroupModelFilterObserver.didMoveTabOutOfGroup(mTab2, POSITION1);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
assertThat(mModel.get(0).model.get(TabProperties.IS_SELECTED), equalTo(true));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
assertThat(mModel.get(1).model.get(TabProperties.IS_SELECTED), equalTo(false));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabMoveOutOfGroup_GTS_LastTab() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Assume that tab1 is a single tab.
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1));
mMediator.resetWithListOfTabs(tabs, false, false);
doReturn(1).when(mTabGroupModelFilter).getCount();
doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
doReturn(tabs).when(mTabGroupModelFilter).getRelatedTabList(TAB1_ID);
// Ungroup the single tab.
mMediatorTabGroupModelFilterObserver.didMoveTabOutOfGroup(mTab1, POSITION1);
assertThat(mModel.size(), equalTo(1));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabMoveOutOfGroup_Dialog() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_GRID_DIALOG);
// Assume that filter is already updated.
doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION1);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
mMediatorTabGroupModelFilterObserver.didMoveTabOutOfGroup(mTab1, POSITION1);
assertThat(mModel.size(), equalTo(1));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
verify(mTabGridDialogHandler).updateDialogContent(TAB2_ID);
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabMoveOutOfGroup_Dialog_LastTab() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_GRID_DIALOG);
// Assume that tab1 is a single tab.
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1));
mMediator.resetWithListOfTabs(tabs, false, false);
doReturn(1).when(mTabGroupModelFilter).getCount();
doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
doReturn(tabs).when(mTabGroupModelFilter).getRelatedTabList(TAB1_ID);
// Ungroup the single tab.
mMediatorTabGroupModelFilterObserver.didMoveTabOutOfGroup(mTab1, POSITION1);
verify(mTabGridDialogHandler).updateDialogContent(Tab.INVALID_TAB_ID);
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabMoveOutOfGroup_Strip() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_STRIP);
// Assume that filter is already updated.
doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION1);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
mMediatorTabGroupModelFilterObserver.didMoveTabOutOfGroup(mTab1, POSITION1);
assertThat(mModel.size(), equalTo(1));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
verify(mTabGridDialogHandler, never()).updateDialogContent(anyInt());
}
@Test
public void tabMovementWithoutGroup_Forward() {
initAndAssertAllProperties();
doReturn(mEmptyTabModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
mTabModelObserverCaptor.getValue().didMoveTab(mTab2, POSITION1, POSITION2);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
}
@Test
public void tabMovementWithoutGroup_Backward() {
initAndAssertAllProperties();
doReturn(mEmptyTabModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
mTabModelObserverCaptor.getValue().didMoveTab(mTab1, POSITION2, POSITION1);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabMovementWithGroup_Forward() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Assume that moveTab in TabModel is finished.
doReturn(mTab1).when(mTabModel).getTabAt(POSITION2);
doReturn(mTab2).when(mTabModel).getTabAt(POSITION1);
doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
mMediatorTabGroupModelFilterObserver.didMoveTabGroup(mTab2, POSITION2, POSITION1);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabMovementWithGroup_Backward() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Assume that moveTab in TabModel is finished.
doReturn(mTab1).when(mTabModel).getTabAt(POSITION2);
doReturn(mTab2).when(mTabModel).getTabAt(POSITION1);
doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
mMediatorTabGroupModelFilterObserver.didMoveTabGroup(mTab1, POSITION1, POSITION2);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabMovementWithinGroup_Forward() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_GRID_DIALOG);
// Assume that moveTab in TabModel is finished.
doReturn(mTab1).when(mTabModel).getTabAt(POSITION2);
doReturn(mTab2).when(mTabModel).getTabAt(POSITION1);
doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
doReturn(TAB1_ID).when(mTab1).getRootId();
doReturn(TAB1_ID).when(mTab2).getRootId();
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
mMediatorTabGroupModelFilterObserver.didMoveWithinGroup(mTab2, POSITION2, POSITION1);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabMovementWithinGroup_Backward() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_GRID_DIALOG);
// Assume that moveTab in TabModel is finished.
doReturn(mTab1).when(mTabModel).getTabAt(POSITION2);
doReturn(mTab2).when(mTabModel).getTabAt(POSITION1);
doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
doReturn(TAB1_ID).when(mTab1).getRootId();
doReturn(TAB1_ID).when(mTab2).getRootId();
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
mMediatorTabGroupModelFilterObserver.didMoveWithinGroup(mTab1, POSITION1, POSITION2);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void undoGrouped_One_Adjacent_Tab() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Assume there are 3 tabs in TabModel, mTab2 just grouped with mTab1;
Tab tab3 = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, tab3));
mMediator.resetWithListOfTabs(tabs, false, false);
assertThat(mModel.size(), equalTo(2));
// Assume undo grouping mTab2 with mTab1.
doReturn(3).when(mTabGroupModelFilter).getCount();
doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION2);
doReturn(tab3).when(mTabGroupModelFilter).getTabAt(2);
mMediatorTabGroupModelFilterObserver.didMoveTabOutOfGroup(mTab2, POSITION1);
assertThat(mModel.size(), equalTo(3));
assertThat(mModel.indexFromId(TAB1_ID), equalTo(0));
assertThat(mModel.indexFromId(TAB2_ID), equalTo(1));
assertThat(mModel.indexFromId(TAB3_ID), equalTo(2));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void undoForwardGrouped_One_Tab() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Assume there are 3 tabs in TabModel, tab3 just grouped with mTab1;
Tab tab3 = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
mMediator.resetWithListOfTabs(tabs, false, false);
assertThat(mModel.size(), equalTo(2));
// Assume undo grouping tab3 with mTab1.
doReturn(3).when(mTabGroupModelFilter).getCount();
doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION2);
doReturn(tab3).when(mTabGroupModelFilter).getTabAt(2);
mMediatorTabGroupModelFilterObserver.didMoveTabOutOfGroup(tab3, POSITION1);
assertThat(mModel.size(), equalTo(3));
assertThat(mModel.indexFromId(TAB1_ID), equalTo(0));
assertThat(mModel.indexFromId(TAB2_ID), equalTo(1));
assertThat(mModel.indexFromId(TAB3_ID), equalTo(2));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void undoBackwardGrouped_One_Tab() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Assume there are 3 tabs in TabModel, mTab1 just grouped with mTab2;
Tab tab3 = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab2, tab3));
mMediator.resetWithListOfTabs(tabs, false, false);
assertThat(mModel.size(), equalTo(2));
// Assume undo grouping tab3 with mTab1.
doReturn(3).when(mTabGroupModelFilter).getCount();
doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION2);
doReturn(tab3).when(mTabGroupModelFilter).getTabAt(2);
mMediatorTabGroupModelFilterObserver.didMoveTabOutOfGroup(mTab1, POSITION2);
assertThat(mModel.size(), equalTo(3));
assertThat(mModel.indexFromId(TAB1_ID), equalTo(0));
assertThat(mModel.indexFromId(TAB2_ID), equalTo(1));
assertThat(mModel.indexFromId(TAB3_ID), equalTo(2));
}
@Test
public void updateSpanCount_Portrait_SingleWindow() {
initAndAssertAllProperties();
// Mock that we are switching to portrait mode.
Configuration configuration = new Configuration();
configuration.orientation = Configuration.ORIENTATION_PORTRAIT;
// Mock that we are in single window mode.
MultiWindowUtils.getInstance().setIsInMultiWindowModeForTesting(false);
mComponentCallbacksCaptor.getValue().onConfigurationChanged(configuration);
verify(mGridLayoutManager).setSpanCount(TabListCoordinator.GRID_LAYOUT_SPAN_COUNT_PORTRAIT);
}
@Test
public void updateSpanCount_Landscape_SingleWindow() {
initAndAssertAllProperties();
// Mock that we are switching to landscape mode.
Configuration configuration = new Configuration();
configuration.orientation = Configuration.ORIENTATION_LANDSCAPE;
// Mock that we are in single window mode.
MultiWindowUtils.getInstance().setIsInMultiWindowModeForTesting(false);
mComponentCallbacksCaptor.getValue().onConfigurationChanged(configuration);
verify(mGridLayoutManager)
.setSpanCount(TabListCoordinator.GRID_LAYOUT_SPAN_COUNT_LANDSCAPE);
}
@Test
public void updateSpanCount_MultiWindow() {
initAndAssertAllProperties();
Configuration portraitConfiguration = new Configuration();
portraitConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT;
Configuration landscapeConfiguration = new Configuration();
landscapeConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
// Mock that we are in multi window mode.
MultiWindowUtils.getInstance().setIsInMultiWindowModeForTesting(true);
mComponentCallbacksCaptor.getValue().onConfigurationChanged(landscapeConfiguration);
mComponentCallbacksCaptor.getValue().onConfigurationChanged(portraitConfiguration);
mComponentCallbacksCaptor.getValue().onConfigurationChanged(landscapeConfiguration);
// The span count is fixed to 2 for multi window mode regardless of the orientation change.
verify(mGridLayoutManager, times(3))
.setSpanCount(TabListCoordinator.GRID_LAYOUT_SPAN_COUNT_PORTRAIT);
}
@Test
public void resetWithListOfTabs_MruOrder() {
List<Tab> tabs = new ArrayList<>();
for (int i = 0; i < mTabModel.getCount(); i++) {
tabs.add(mTabModel.getTabAt(i));
}
assertThat(tabs.size(), equalTo(2));
long timestamp1 = 1;
long timestamp2 = 2;
doReturn(timestamp1).when(mTab1).getTimestampMillis();
doReturn(timestamp2).when(mTab2).getTimestampMillis();
mMediator.resetWithListOfTabs(tabs, /*quickMode =*/false, /*mruMode =*/true);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID));
assertThat(mMediator.indexOfTab(TAB1_ID), equalTo(1));
assertThat(mMediator.indexOfTab(TAB2_ID), equalTo(0));
doReturn(timestamp2).when(mTab1).getTimestampMillis();
doReturn(timestamp1).when(mTab2).getTimestampMillis();
mMediator.resetWithListOfTabs(tabs, /*quickMode =*/false, /*mruMode =*/true);
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mMediator.indexOfTab(TAB1_ID), equalTo(0));
assertThat(mMediator.indexOfTab(TAB2_ID), equalTo(1));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void getLatestTitle_NotGTS() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_GRID_DIALOG);
// Mock that we have a stored title stored with reference to root ID of tab1.
when(mSharedPreferences.getString(String.valueOf(mTab1.getRootId()), null))
.thenReturn(CUSTOMIZED_DIALOG_TITLE1);
assertThat(mMediator.getTabGroupTitleEditor().getTabGroupTitle(mTab1.getRootId()),
equalTo(CUSTOMIZED_DIALOG_TITLE1));
// Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID.
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabs, TAB1_ID);
// Even if we have a stored title, we only show it in tab switcher.
assertThat(mMediator.getLatestTitleForTab(mTab1), equalTo(TAB1_TITLE));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void getLatestTitle_SingleTab_GTS() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Mock that we have a stored title stored with reference to root ID of tab1.
when(mSharedPreferences.getString(String.valueOf(mTab1.getRootId()), null))
.thenReturn(CUSTOMIZED_DIALOG_TITLE1);
assertThat(mMediator.getTabGroupTitleEditor().getTabGroupTitle(mTab1.getRootId()),
equalTo(CUSTOMIZED_DIALOG_TITLE1));
// Mock that tab1 is a single tab.
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1));
createTabGroup(tabs, TAB1_ID);
// We never show stored title for single tab.
assertThat(mMediator.getLatestTitleForTab(mTab1), equalTo(TAB1_TITLE));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void getLatestTitle_Stored_GTS() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
// Mock that we have a stored title stored with reference to root ID of tab1.
when(mSharedPreferences.getString(String.valueOf(mTab1.getRootId()), null))
.thenReturn(CUSTOMIZED_DIALOG_TITLE1);
assertThat(mMediator.getTabGroupTitleEditor().getTabGroupTitle(mTab1.getRootId()),
equalTo(CUSTOMIZED_DIALOG_TITLE1));
// Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID.
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabs, TAB1_ID);
assertThat(mMediator.getLatestTitleForTab(mTab1), equalTo(CUSTOMIZED_DIALOG_TITLE1));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void updateTabGroupTitle_GTS() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
assertThat(mModel.get(POSITION1).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
// Mock that tab1 and newTab are in the same group and group root id is TAB1_ID.
TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, newTab));
createTabGroup(tabs, TAB1_ID);
doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
doReturn(POSITION1).when(mTabGroupModelFilter).indexOf(mTab1);
mMediator.getTabGroupTitleEditor().updateTabGroupTitle(mTab1, CUSTOMIZED_DIALOG_TITLE1);
assertThat(mModel.get(POSITION1).model.get(TabProperties.TITLE),
equalTo(CUSTOMIZED_DIALOG_TITLE1));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabGroupTitleEditor_storeTitle() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
TabGroupTitleEditor tabGroupTitleEditor = mMediator.getTabGroupTitleEditor();
tabGroupTitleEditor.storeTabGroupTitle(mTab1.getRootId(), CUSTOMIZED_DIALOG_TITLE1);
verify(mEditor).putString(
eq(String.valueOf(mTab1.getRootId())), eq(CUSTOMIZED_DIALOG_TITLE1));
verify(mPutStringEditor).apply();
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID})
// clang-format off
public void tabGroupTitleEditor_deleteTitle() {
// clang-format on
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
TabGroupTitleEditor tabGroupTitleEditor = mMediator.getTabGroupTitleEditor();
tabGroupTitleEditor.deleteTabGroupTitle(mTab1.getRootId());
verify(mEditor).remove(eq(String.valueOf(mTab1.getRootId())));
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);
}
@Test
@Features.DisableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID})
public void testUrlUpdated_forSingleTab_GTS_GroupNotEnabled() {
initAndAssertAllProperties();
assertNotEquals(NEW_URL, mModel.get(POSITION1).model.get(TabProperties.URL));
doReturn(NEW_URL).when(mTab1).getUrl();
mTabObserverCaptor.getValue().onUrlUpdated(mTab1);
assertEquals(NEW_URL, mModel.get(POSITION1).model.get(TabProperties.URL));
assertEquals(TAB2_URL, mModel.get(POSITION2).model.get(TabProperties.URL));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID,
ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID})
public void
testUrlUpdated_forSingleTab_GTS() {
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
assertNotEquals(NEW_URL, mModel.get(POSITION1).model.get(TabProperties.URL));
doReturn(NEW_URL).when(mTab1).getUrl();
mTabObserverCaptor.getValue().onUrlUpdated(mTab1);
assertEquals(NEW_URL, mModel.get(POSITION1).model.get(TabProperties.URL));
assertEquals(TAB2_URL, mModel.get(POSITION2).model.get(TabProperties.URL));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID,
ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID})
public void
testUrlUpdated_forGroup_GTS() {
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabs, TAB1_ID);
doReturn(POSITION1).when(mTabGroupModelFilter).indexOf(mTab1);
doReturn(POSITION1).when(mTabGroupModelFilter).indexOf(mTab2);
mMediatorTabGroupModelFilterObserver.didMergeTabToGroup(mTab2, TAB1_ID);
assertEquals(TAB1_DOMAIN + ", " + TAB2_DOMAIN,
mModel.get(POSITION1).model.get(TabProperties.URL));
doReturn(NEW_DOMAIN)
.when(mUrlUtilitiesJniMock)
.getDomainAndRegistry(eq(NEW_URL), anyBoolean());
// Update URL for mTab1.
doReturn(NEW_URL).when(mTab1).getUrl();
mTabObserverCaptor.getValue().onUrlUpdated(mTab1);
assertEquals(NEW_DOMAIN + ", " + TAB2_DOMAIN,
mModel.get(POSITION1).model.get(TabProperties.URL));
// Update URL for mTab2.
doReturn(NEW_URL).when(mTab2).getUrl();
mTabObserverCaptor.getValue().onUrlUpdated(mTab2);
assertEquals(
NEW_DOMAIN + ", " + NEW_DOMAIN, mModel.get(POSITION1).model.get(TabProperties.URL));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID,
ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID})
public void
testUrlUpdated_forGroup_Dialog() {
setUpForTabGroupOperation(TabListMediatorType.TAB_GRID_DIALOG);
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabs, TAB1_ID);
doReturn(POSITION1).when(mTabGroupModelFilter).indexOf(mTab1);
doReturn(POSITION1).when(mTabGroupModelFilter).indexOf(mTab2);
mMediatorTabGroupModelFilterObserver.didMergeTabToGroup(mTab2, TAB1_ID);
assertEquals(TAB1_URL, mModel.get(POSITION1).model.get(TabProperties.URL));
assertEquals(TAB2_URL, mModel.get(POSITION2).model.get(TabProperties.URL));
doReturn(NEW_DOMAIN)
.when(mUrlUtilitiesJniMock)
.getDomainAndRegistry(eq(NEW_URL), anyBoolean());
// Update URL for mTab1.
doReturn(NEW_URL).when(mTab1).getUrl();
mTabObserverCaptor.getValue().onUrlUpdated(mTab1);
assertEquals(NEW_URL, mModel.get(POSITION1).model.get(TabProperties.URL));
assertEquals(TAB2_URL, mModel.get(POSITION2).model.get(TabProperties.URL));
// Update URL for mTab2.
doReturn(NEW_URL).when(mTab2).getUrl();
mTabObserverCaptor.getValue().onUrlUpdated(mTab2);
assertEquals(NEW_URL, mModel.get(POSITION1).model.get(TabProperties.URL));
assertEquals(NEW_URL, mModel.get(POSITION2).model.get(TabProperties.URL));
}
@Test
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID,
ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID})
public void
testUrlUpdated_forUnGroup() {
setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabs, TAB1_ID);
mMediatorTabGroupModelFilterObserver.didMergeTabToGroup(mTab2, TAB1_ID);
assertEquals(TAB1_DOMAIN + ", " + TAB2_DOMAIN,
mModel.get(POSITION1).model.get(TabProperties.URL));
// Assume that TabGroupModelFilter is already updated.
when(mTabGroupModelFilter.getRelatedTabList(TAB1_ID)).thenReturn(Arrays.asList(mTab1));
when(mTabGroupModelFilter.getRelatedTabList(TAB2_ID)).thenReturn(Arrays.asList(mTab2));
doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION2);
doReturn(2).when(mTabGroupModelFilter).getCount();
mMediatorTabGroupModelFilterObserver.didMoveTabOutOfGroup(mTab2, POSITION1);
assertEquals(TAB1_URL, mModel.get(POSITION1).model.get(TabProperties.URL));
assertEquals(TAB2_URL, mModel.get(POSITION2).model.get(TabProperties.URL));
}
private void initAndAssertAllProperties() {
List<Tab> tabs = new ArrayList<>();
for (int i = 0; i < mTabModel.getCount(); i++) {
tabs.add(mTabModel.getTabAt(i));
}
mMediator.resetWithListOfTabs(tabs, false, false);
for (Callback<Drawable> callback : mCallbackCaptor.getAllValues()) {
callback.onResult(new ColorDrawable(Color.RED));
}
assertThat(mModel.size(), equalTo(2));
assertThat(mModel.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID));
assertThat(mModel.get(1).model.get(TabProperties.TAB_ID), equalTo(TAB2_ID));
assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
assertThat(mModel.get(1).model.get(TabProperties.TITLE), equalTo(TAB2_TITLE));
assertThat(mModel.get(0).model.get(TabProperties.FAVICON), instanceOf(Drawable.class));
assertThat(mModel.get(1).model.get(TabProperties.FAVICON), instanceOf(Drawable.class));
assertThat(mModel.get(0).model.get(TabProperties.URL), equalTo(TAB1_URL));
assertThat(mModel.get(1).model.get(TabProperties.URL), equalTo(TAB2_URL));
assertThat(mModel.get(0).model.get(TabProperties.IS_SELECTED), equalTo(true));
assertThat(mModel.get(1).model.get(TabProperties.IS_SELECTED), equalTo(false));
assertThat(mModel.get(0).model.get(TabProperties.THUMBNAIL_FETCHER),
instanceOf(TabListMediator.ThumbnailFetcher.class));
assertThat(mModel.get(1).model.get(TabProperties.THUMBNAIL_FETCHER),
instanceOf(TabListMediator.ThumbnailFetcher.class));
assertThat(mModel.get(0).model.get(TabProperties.TAB_SELECTED_LISTENER),
instanceOf(TabListMediator.TabActionListener.class));
assertThat(mModel.get(1).model.get(TabProperties.TAB_SELECTED_LISTENER),
instanceOf(TabListMediator.TabActionListener.class));
assertThat(mModel.get(0).model.get(TabProperties.TAB_CLOSED_LISTENER),
instanceOf(TabListMediator.TabActionListener.class));
assertThat(mModel.get(1).model.get(TabProperties.TAB_CLOSED_LISTENER),
instanceOf(TabListMediator.TabActionListener.class));
}
private TabImpl prepareTab(int id, String title, String url) {
TabImpl tab = mock(TabImpl.class);
when(tab.getView()).thenReturn(mock(View.class));
when(tab.getUserDataHost()).thenReturn(new UserDataHost());
doReturn(id).when(tab).getId();
doReturn(id).when(tab).getRootId();
doReturn(url).when(tab).getUrl();
doReturn(title).when(tab).getTitle();
doReturn(true).when(tab).isIncognito();
doReturn(title).when(mTitleProvider).getTitle(tab);
return tab;
}
private SimpleRecyclerViewAdapter.ViewHolder prepareViewHolder(int id, int position) {
SimpleRecyclerViewAdapter.ViewHolder viewHolder =
mock(SimpleRecyclerViewAdapter.ViewHolder.class);
PropertyModel model = new PropertyModel.Builder(TabProperties.ALL_KEYS_TAB_GRID)
.with(TabProperties.TAB_ID, id)
.build();
viewHolder.model = model;
doReturn(position).when(viewHolder).getAdapterPosition();
return viewHolder;
}
private RecyclerView.ViewHolder prepareDummyViewHolder(View itemView, int index) {
RecyclerView.ViewHolder viewHolder = new RecyclerView.ViewHolder(itemView) {};
when(mRecyclerView.findViewHolderForAdapterPosition(index)).thenReturn(viewHolder);
return viewHolder;
}
private TabGridItemTouchHelperCallback getItemTouchHelperCallback() {
return (TabGridItemTouchHelperCallback) mMediator.getItemTouchHelperCallback(
0f, 0f, 0f, mProfile);
}
private void setUpForTabGroupOperation(@TabListMediatorType int type) {
doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getTabModelFilter(true);
doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getTabModelFilter(false);
doNothing()
.when(mTabGroupModelFilter)
.addTabGroupObserver(mTabGroupModelFilterObserverCaptor.capture());
TabListMediator.TabGridDialogHandler handler =
type == TabListMediatorType.TAB_GRID_DIALOG ? mTabGridDialogHandler : null;
boolean actionOnRelatedTabs = type == TabListMediatorType.TAB_SWITCHER;
FeatureUtilities.setTabGroupsAndroidEnabledForTesting(true);
mMediator = new TabListMediator(mContext, mModel, mTabModelSelector,
mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
mTabListFaviconProvider, actionOnRelatedTabs, null, null, null, handler,
getClass().getSimpleName(), 0);
// There are two TabGroupModelFilter.Observer added when initializing TabListMediator, one
// from TabListMediator and the other from TabGroupTitleEditor. Here we only test the one
// from TabListMediator.
mMediatorTabGroupModelFilterObserver =
mTabGroupModelFilterObserverCaptor.getAllValues().get(0);
initAndAssertAllProperties();
}
private void createTabGroup(List<Tab> tabs, int rootId) {
for (Tab tab : tabs) {
when(mTabGroupModelFilter.getRelatedTabList(tab.getId())).thenReturn(tabs);
doReturn(rootId).when((TabImpl) tab).getRootId();
}
}
}