// Copyright 2016 The Chromium Authors
// 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.compositor.overlays.strip;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import android.animation.Animator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.text.TextUtils;
import android.view.ContextThemeWrapper;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.graphics.ColorUtils;
import androidx.test.core.app.ApplicationProvider;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
import org.chromium.base.Callback;
import org.chromium.base.supplier.Supplier;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.base.test.util.HistogramWatcher;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.compositor.LayerTitleCache;
import org.chromium.chrome.browser.compositor.layouts.LayoutManagerHost;
import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton;
import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.CompositorOnClickHandler;
import org.chromium.chrome.browser.compositor.layouts.components.TintedCompositorButton;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.layouts.animation.CompositorAnimationHandler;
import org.chromium.chrome.browser.layouts.components.VirtualView;
import org.chromium.chrome.browser.multiwindow.MultiWindowTestUtils;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.MockTab;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabCreationState;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncIphController;
import org.chromium.chrome.browser.tabmodel.TabClosureParams;
import org.chromium.chrome.browser.tabmodel.TabCreator;
import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver;
import org.chromium.chrome.browser.tasks.tab_management.ActionConfirmationManager;
import org.chromium.chrome.browser.tasks.tab_management.ActionConfirmationManager.ConfirmationResult;
import org.chromium.chrome.browser.tasks.tab_management.TabGroupTitleEditor;
import org.chromium.chrome.browser.tasks.tab_management.TabUiThemeUtil;
import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel;
import org.chromium.components.browser_ui.styles.SemanticColorUtils;
import org.chromium.components.prefs.PrefService;
import org.chromium.ui.base.LocalizationUtils;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.shadows.ShadowAppCompatResources;
import org.chromium.ui.widget.RectProvider;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
/** Tests for {@link StripLayoutHelper}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(
manifest = Config.NONE,
qualifiers = "sw600dp",
shadows = {ShadowAppCompatResources.class})
@LooperMode(Mode.LEGACY)
public class StripLayoutHelperTest {
@Mock private View mInteractingTabView;
@Mock private LayoutManagerHost mManagerHost;
@Mock private LayoutUpdateHost mUpdateHost;
@Mock private LayoutRenderHost mRenderHost;
@Mock private CompositorButton mModelSelectorBtn;
@Mock private TabGroupModelFilter mTabGroupModelFilter;
@Mock private View mToolbarContainerView;
@Mock private StripTabHoverCardView mTabHoverCardView;
@Mock private Profile mProfile;
@Mock private CompositorOnClickHandler mClickHandler;
@Mock private TabDragSource mTabDragSource;
@Mock private WindowAndroid mWindowAndroid;
@Mock private LayerTitleCache mLayerTitleCache;
@Mock private ActionConfirmationManager mActionConfirmationManager;
@Mock private PrefService mPrefService;
@Mock private TabGroupContextMenuCoordinator mTabGroupContextMenuCoordinator;
@Captor private ArgumentCaptor<Callback<Integer>> mConfirmationResultCaptor;
private Activity mActivity;
private Context mContext;
private TestTabModel mModel = new TestTabModel();
private StripLayoutHelper mStripLayoutHelper;
private boolean mIncognito;
private static final String[] TEST_TAB_TITLES = {"Tab 1", "Tab 2", "Tab 3", "", null};
private static final String TEST_GROUP_TITLE = "Group";
private static final String EXPECTED_MARGIN = "The tab should have a trailing margin.";
private static final String EXPECTED_NO_MARGIN = "The tab should not have a trailing margin.";
private static final String EXPECTED_TAB = "The view should be a tab.";
private static final String EXPECTED_TITLE = "The view should be a title.";
private static final String EXPECTED_NON_TITLE = "The view should not be a title.";
private static final String CLOSE_TAB = "Close %1$s tab";
private static final String IDENTIFIER = "Tab";
private static final String IDENTIFIER_SELECTED = "Selected Tab";
private static final String INCOGNITO_IDENTIFIER = "Incognito Tab";
private static final String INCOGNITO_IDENTIFIER_SELECTED = "Selected Incognito Tab";
private static final float SCREEN_WIDTH = 800.f;
private static final float SCREEN_WIDTH_LANDSCAPE = 1200.f;
// TODO(wenyufu): This needs to be renamed to TAB_STRIP_HEIGHT.
private static final float SCREEN_HEIGHT = 40.f;
private static final float TAB_WIDTH_1 = 140.f;
private static final float TAB_WIDTH_SMALL = 108.f;
private static final float TAB_OVERLAP_WIDTH = 28.f;
private static final float TAB_WIDTH_MEDIUM = 156.f;
private static final long TIMESTAMP = 5000;
private static final float NEW_TAB_BTN_X_RTL = 100.f;
private static final float NEW_TAB_BTN_X = 700.f;
private static final float NEW_TAB_BTN_Y = 1400.f;
private static final float NEW_TAB_BTN_WIDTH = 100.f;
private static final float NEW_TAB_BTN_HEIGHT = 100.f;
private static final float PADDING_LEFT = 10.f;
private static final float PADDING_RIGHT = 20.f;
private static final float REORDER_OVERLAP_SWITCH_PERCENTAGE = 0.53f;
private static final PointF DRAG_START_POINT = new PointF(70f, 20f);
private static final float EPSILON = 0.001f;
private final Supplier<Rect> mWindowRectSupplier = () -> new Rect(1, 1, 1000, 100);
/** Reset the environment before each test. */
@Before
public void beforeTest() {
MockitoAnnotations.initMocks(this);
when(mTabGroupModelFilter.isTabInTabGroup(any())).thenReturn(false);
mContext =
new ContextThemeWrapper(
ApplicationProvider.getApplicationContext(),
R.style.Theme_BrowserUI_DayNight);
mActivity = Robolectric.setupActivity(Activity.class);
mActivity.setTheme(org.chromium.chrome.R.style.Theme_BrowserUI);
when(mWindowAndroid.getActivity()).thenReturn(new WeakReference<>(mActivity));
}
@After
public void tearDown() {
if (mStripLayoutHelper != null) {
mStripLayoutHelper.stopReorderModeForTesting();
mStripLayoutHelper.setTabAtPositionForTesting(null);
mStripLayoutHelper.setRunningAnimatorForTesting(null);
}
mTabDragSource = null;
}
/**
* Test method for {@link StripLayoutHelper#getVirtualViews(List<VirtualView>)}.
*
* Checks that it returns the correct order of tabs, including correct content.
*/
@Test
@Feature({"Accessibility"})
public void testSimpleTabOrder() {
initializeTest(false, false, 0);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
assertTabStripAndOrder(getExpectedAccessibilityDescriptions(0));
}
/**
* Test method for {@link StripLayoutHelper#getVirtualViews(List<VirtualView>)}.
*
* Checks that it returns the correct order of tabs, even when a tab except the first one is
* selected.
*/
@Test
@Feature({"Accessibility"})
public void testTabOrderWithIndex() {
initializeTest(false, false, 1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Tabs should be in left to right order regardless of index
assertTabStripAndOrder(getExpectedAccessibilityDescriptions(1));
}
/**
* Test method for {@link StripLayoutHelper#getVirtualViews(List<VirtualView>)}.
*
* Checks that it returns the correct order of tabs, even in RTL mode.
*/
@Test
@Feature({"Accessibility"})
public void testTabOrderRtl() {
initializeTest(true, false, 0);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Tabs should be in linear order even in RTL.
// Android will take care of reversing it.
assertTabStripAndOrder(getExpectedAccessibilityDescriptions(0));
}
/**
* Test method for {@link StripLayoutHelper#getVirtualViews(List<VirtualView>)}.
*
* Checks that it returns the correct order of tabs, even in incognito mode.
*/
@Test
@Feature({"Accessibility"})
public void testIncognitoAccessibilityDescriptions() {
initializeTest(false, true, 0);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
assertTabStripAndOrder(getExpectedAccessibilityDescriptions(0));
}
@Test
@Feature({"Accessibility"})
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testGroupIndicatorAccessibilityDescriptions_OneTab() {
// Setup and group first tab.
initializeTest(false, false, 0);
groupTabs(0, 1);
// Verify.
String expectedDescription = "1 tab - Tab 1";
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue("First should be a group title.", views[0] instanceof StripLayoutGroupTitle);
assertEquals(
"A11y description for group title was wrong.",
expectedDescription,
views[0].getAccessibilityDescription());
}
@Test
@Feature({"Accessibility"})
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testGroupIndicatorAccessibilityDescriptions_MultipleTabs() {
// Setup and group first three tabs.
initializeTest(false, false, 0);
groupTabs(0, 3);
// Verify.
String expectedDescription = "3 tabs - Tab 1 and 2 other tabs";
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue("First should be a group title.", views[0] instanceof StripLayoutGroupTitle);
assertEquals(
"A11y description for group title was wrong.",
expectedDescription,
views[0].getAccessibilityDescription());
}
@Test
@Feature({"Accessibility"})
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testGroupIndicatorAccessibilityDescriptions_MultipleTabs_NamedGroup() {
// Setup and group first three tabs. Name the group.
when(mTabGroupModelFilter.getTabGroupTitle(0)).thenReturn("Group name");
initializeTest(false, false, 0);
groupTabs(0, 3);
// Verify.
String expectedDescription = "Group name - Tab 1 and 2 other tabs";
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue("First should be a group title.", views[0] instanceof StripLayoutGroupTitle);
assertEquals(
"A11y description for group title was wrong.",
expectedDescription,
views[0].getAccessibilityDescription());
}
@Test
public void testResizeStripOnTabClose_DoNotAnimateIfNotMoving() {
final int numTabs = 10;
initializeTest(false, false, false, 0, numTabs);
// Trigger a size change so the strip layout tab heights and widths get set.
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Set the initial scroll offset to trigger an update to draw X positions.
mStripLayoutHelper.setScrollOffsetForTesting(0);
final StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Somewhat arbitrary, just pick a tab with an index in (0, numTabs).
final int closeTabIndex = 8;
final StripLayoutHelper stripLayoutHelperSpy = spy(mStripLayoutHelper);
stripLayoutHelperSpy.handleCloseButtonClick(tabs[closeTabIndex], TIMESTAMP);
final Animator runningAnimator = stripLayoutHelperSpy.getRunningAnimatorForTesting();
// Initial animation is the tab removal animation, and after that ends the
// resizeStripOnTabClose animations begin.
runningAnimator.end();
final ArgumentCaptor<List<Animator>> animationListCaptor =
ArgumentCaptor.forClass(List.class);
final InOrder stripLayoutOrder = inOrder(stripLayoutHelperSpy);
stripLayoutOrder.verify(stripLayoutHelperSpy).startAnimationList(any(), any());
stripLayoutOrder
.verify(stripLayoutHelperSpy)
.startAnimationList(animationListCaptor.capture(), any());
final List<Animator> animationList = animationListCaptor.getValue();
// Only the tabs that come after the closed tab should have to move and get animations
// created, plus the new tab button offset animation.
final int expectedAnimationCount = numTabs - closeTabIndex;
assertEquals(expectedAnimationCount, animationList.size());
}
@Test
public void
testResizeStripOnTabClose_DoNotAnimateIfNotVisible_OutsideVisibleBounds_ToTheRight() {
final int numTabs = 50;
initializeTest(false, false, false, 0, numTabs);
// Trigger a size change so the strip layout tab heights and widths get set.
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Simplify the visible bounds by removing the right fade.
mStripLayoutHelper.setRightFadeWidth(0);
// Set the initial scroll offset to trigger an update to draw X positions.
mStripLayoutHelper.setScrollOffsetForTesting(0);
final StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Pick a tab that will be well outside of the visible bounds to the right.
final int closeTabIndex = 40;
assertTrue(
"Tab getting closed should be outside of the visible bounds",
tabs[closeTabIndex].getDrawX() > mStripLayoutHelper.getVisibleRightBound());
final StripLayoutHelper stripLayoutHelperSpy = spy(mStripLayoutHelper);
stripLayoutHelperSpy.handleCloseButtonClick(tabs[closeTabIndex], TIMESTAMP);
final Animator runningAnimator = stripLayoutHelperSpy.getRunningAnimatorForTesting();
// Initial animation is the tab removal animation, and after that ends the
// resizeStripOnTabClose animations begin.
runningAnimator.end();
final ArgumentCaptor<List<Animator>> animationListCaptor =
ArgumentCaptor.forClass(List.class);
final InOrder stripLayoutOrder = inOrder(stripLayoutHelperSpy);
stripLayoutOrder.verify(stripLayoutHelperSpy).startAnimationList(any(), any());
stripLayoutOrder
.verify(stripLayoutHelperSpy)
.startAnimationList(animationListCaptor.capture(), any());
final List<Animator> animationList = animationListCaptor.getValue();
assertEquals(
"The only animation should be for the new tab button offset",
1,
animationList.size());
}
@Test
public void
testResizeStripOnTabClose_DoNotAnimateIfNotVisible_OutsideVisibleBounds_ToTheLeft() {
final int numTabs = 50;
initializeTest(false, false, false, 0, numTabs);
// Trigger a size change so the strip layout tab heights and widths get set.
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Simplify the visible bounds by removing the left fade.
mStripLayoutHelper.setLeftFadeWidth(0);
// Set the initial scroll offset to trigger an update to draw X positions.
mStripLayoutHelper.setScrollOffsetForTesting(-1000);
final StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Pick a tab that will be outside the visible bounds to the left.
final int closeTabIndex = 5;
assertTrue(
"Tab getting closed should be outside of the visible bounds",
tabs[closeTabIndex].getDrawX() + tabs[closeTabIndex].getWidth()
< mStripLayoutHelper.getVisibleLeftBound());
final StripLayoutHelper stripLayoutHelperSpy = spy(mStripLayoutHelper);
stripLayoutHelperSpy.handleCloseButtonClick(tabs[closeTabIndex], TIMESTAMP);
final Animator runningAnimator = stripLayoutHelperSpy.getRunningAnimatorForTesting();
// Initial animation is the tab removal animation, and after that ends the
// resizeStripOnTabClose animations begin.
runningAnimator.end();
final ArgumentCaptor<List<Animator>> animationListCaptor =
ArgumentCaptor.forClass(List.class);
final InOrder stripLayoutOrder = inOrder(stripLayoutHelperSpy);
stripLayoutOrder.verify(stripLayoutHelperSpy).startAnimationList(any(), any());
stripLayoutOrder
.verify(stripLayoutHelperSpy)
.startAnimationList(animationListCaptor.capture(), any());
final List<Animator> animationList = animationListCaptor.getValue();
assertEquals(
"There should be 11 animations for the visible tabs, "
+ "plus the new tab button offset animation",
12,
animationList.size());
}
@Test
public void testResizeStripOnTabClose_AnimateTab_MovingIntoVisibleBounds() {
final int numTabs = 50;
initializeTest(false, false, false, 0, numTabs);
// Trigger a size change so the strip layout tab heights and widths get set.
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Set the initial scroll offset to trigger an update to draw X positions.
mStripLayoutHelper.setScrollOffsetForTesting(0);
final StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Simplify the visible bounds by removing the right fade.
mStripLayoutHelper.setRightFadeWidth(0);
final int firstNotVisibleIndex =
IntStream.range(0, tabs.length)
.filter(i -> tabs[i].getDrawX() > mStripLayoutHelper.getVisibleRightBound())
.findFirst()
.getAsInt();
final int closeTabIndex = firstNotVisibleIndex - 1;
assertTrue(
"Tab getting closed should be inside of the visible bounds",
tabs[closeTabIndex].getDrawX() <= mStripLayoutHelper.getVisibleRightBound());
final StripLayoutHelper stripLayoutHelperSpy = spy(mStripLayoutHelper);
stripLayoutHelperSpy.handleCloseButtonClick(tabs[closeTabIndex], TIMESTAMP);
final Animator runningAnimator = stripLayoutHelperSpy.getRunningAnimatorForTesting();
// Initial animation is the tab removal animation, and after that ends the
// resizeStripOnTabClose animations begin.
runningAnimator.end();
final ArgumentCaptor<List<Animator>> animationListCaptor =
ArgumentCaptor.forClass(List.class);
final InOrder stripLayoutOrder = inOrder(stripLayoutHelperSpy);
stripLayoutOrder.verify(stripLayoutHelperSpy).startAnimationList(any(), any());
stripLayoutOrder
.verify(stripLayoutHelperSpy)
.startAnimationList(animationListCaptor.capture(), any());
final List<Animator> animationList = animationListCaptor.getValue();
assertEquals(
"There should be one animation for the tab moving into the visible bounds, "
+ "plus the new tab button offset animation",
2,
animationList.size());
}
@Test
public void testComputeAndUpdateTabWidth_DontAnimateIfSizeNotChanging() {
// Create a high number of tabs to ensure they're already at the minimum size
final int numTabs = 50;
initializeTest(false, false, false, 0, numTabs);
// Trigger a size change so the strip layout tab heights and widths get set.
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Set the initial scroll offset to trigger an update to draw X positions.
mStripLayoutHelper.setScrollOffsetForTesting(0);
assertEquals(
"Tabs should be at minimum width for this test to be valid",
mStripLayoutHelper.getMinTabWidthForTesting(),
mStripLayoutHelper.getCachedTabWidthForTesting(),
EPSILON);
final StripLayoutHelper stripLayoutHelperSpy = spy(mStripLayoutHelper);
mModel.addTab("New tab");
stripLayoutHelperSpy.tabCreated(
TIMESTAMP, mModel.getTabAt(mModel.getCount() - 1).getId(), 0, true, false, false);
final ArgumentCaptor<List<Animator>> animationListCaptor =
ArgumentCaptor.forClass(List.class);
verify(stripLayoutHelperSpy).startAnimationList(animationListCaptor.capture(), any());
final List<Animator> animationList = animationListCaptor.getValue();
assertEquals(
"There should be one animation for the newly created tab width, "
+ "plus one more animation for the new tab y offset",
2,
animationList.size());
}
@Test
public void testAllTabsClosed() {
initializeTest(false, false, 0);
assertTrue(
mStripLayoutHelper.getStripLayoutTabsForTesting().length == TEST_TAB_TITLES.length);
// Close all tabs
mModel.closeTabs(TabClosureParams.closeAllTabs().build());
// Notify strip of tab closure
mStripLayoutHelper.willCloseAllTabs();
// Verify strip has no tabs.
assertTrue(mStripLayoutHelper.getStripLayoutTabsForTesting().length == 0);
}
@Test
public void testTabSelected_SelectedNonLastTab_ShowCloseBtn() {
initializeTest(false, true, 3);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Non-last tab not overlapping strip fade:
// drawX(530) + tabWidth(140 - 28) < width(800) - offsetXRight(20) - longRightFadeWidth(136)
when(tabs[3].getDrawX()).thenReturn(530.f);
mStripLayoutHelper.tabSelected(TIMESTAMP, 3, Tab.INVALID_TAB_ID, false);
// Close btn is visible on the selected tab.
verify(tabs[3]).setCanShowCloseButton(true, false);
// Close btn is hidden for the rest of tabs.
verify(tabs[0]).setCanShowCloseButton(false, false);
verify(tabs[1]).setCanShowCloseButton(false, false);
verify(tabs[2]).setCanShowCloseButton(false, false);
verify(tabs[4]).setCanShowCloseButton(false, false);
}
@Test
public void testTabSelected_SelectedNonLastTab_HideCloseBtn() {
initializeTest(false, true, 3);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Non-last tab overlapping strip fade:
// drawX(600) + tabWidth(140 - 28) > width(800) - offsetXRight(20) - longRightFadeWidth(136)
when(tabs[3].getDrawX()).thenReturn(600.f);
mStripLayoutHelper.tabSelected(TIMESTAMP, 3, Tab.INVALID_TAB_ID, false);
// Close btn is hidden on the selected tab.
verify(tabs[3]).setCanShowCloseButton(false, false);
// Close btn is hidden for the rest of tabs as well.
verify(tabs[0]).setCanShowCloseButton(false, false);
verify(tabs[1]).setCanShowCloseButton(false, false);
verify(tabs[2]).setCanShowCloseButton(false, false);
verify(tabs[4]).setCanShowCloseButton(false, false);
}
@Test
public void testTabSelected_SelectedLastTab_ShowCloseBtn() {
initializeTest(false, true, 4);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.getNewTabButton().setDrawX(NEW_TAB_BTN_X);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Last tab not overlapping NTB:
// drawX(550) > NTB_X(700) + tabOverlapWidth(28) - tabWidth(140)
when(tabs[4].getDrawX()).thenReturn(550.f);
mStripLayoutHelper.tabSelected(TIMESTAMP, 4, Tab.INVALID_TAB_ID, false);
// Close btn is visible on the selected last tab.
verify(tabs[4]).setCanShowCloseButton(true, false);
// Close button is hidden for the rest of tabs.
verify(tabs[0]).setCanShowCloseButton(false, false);
verify(tabs[1]).setCanShowCloseButton(false, false);
verify(tabs[2]).setCanShowCloseButton(false, false);
verify(tabs[3]).setCanShowCloseButton(false, false);
}
@Test
public void testTabSelected_SelectedLastTab_HideCloseBtn() {
initializeTest(false, true, 4);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.getNewTabButton().setDrawX(NEW_TAB_BTN_X);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Last tab overlapping NTB:
// drawX(600) > NTB_X(700) + tabOverlapWidth(28) - tabWidth(140)
when(tabs[4].getDrawX()).thenReturn(600.f);
mStripLayoutHelper.tabSelected(TIMESTAMP, 4, Tab.INVALID_TAB_ID, false);
// Close btn is hidden on the selected last tab.
verify(tabs[4]).setCanShowCloseButton(false, false);
// Close button is hidden for the rest of tabs.
verify(tabs[0]).setCanShowCloseButton(false, false);
verify(tabs[1]).setCanShowCloseButton(false, false);
verify(tabs[2]).setCanShowCloseButton(false, false);
verify(tabs[3]).setCanShowCloseButton(false, false);
}
@Test
public void testTabSelected_SelectedNonLastTab_NoModelSelBtn_HideCloseBtn() {
initializeTest(false, false, 3);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Non-last tab overlapping strip fade:
// drawX(630) + tabWidth(140 - 28) > width(800) - offsetXRight(20) -
// mediumRightFadeWidth(72)
when(tabs[3].getDrawX()).thenReturn(630.f);
mStripLayoutHelper.tabSelected(TIMESTAMP, 3, Tab.INVALID_TAB_ID, false);
// Close button is hidden for selected tab.
verify(tabs[3]).setCanShowCloseButton(false, false);
// Close button is hidden for the rest of tabs as well.
verify(tabs[0]).setCanShowCloseButton(false, false);
verify(tabs[1]).setCanShowCloseButton(false, false);
verify(tabs[2]).setCanShowCloseButton(false, false);
verify(tabs[4]).setCanShowCloseButton(false, false);
}
@Test
public void testTabSelected_SelectedNonLastTab_NoModelSelBtn_ShowCloseBtn() {
initializeTest(false, false, 3);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Non-last tab not overlapping strip fade:
// drawX(580) + tabWidth(140 - 28) > width(800) - offsetXRight(20) -
// mediumRightFadeWidth(72)
when(tabs[3].getDrawX()).thenReturn(580.f);
mStripLayoutHelper.tabSelected(TIMESTAMP, 3, Tab.INVALID_TAB_ID, false);
// Close button is visible for selected tab
verify(tabs[3]).setCanShowCloseButton(true, false);
// Close button is hidden for the rest of tabs.
verify(tabs[0]).setCanShowCloseButton(false, false);
verify(tabs[1]).setCanShowCloseButton(false, false);
verify(tabs[2]).setCanShowCloseButton(false, false);
verify(tabs[4]).setCanShowCloseButton(false, false);
}
@Test
public void testTabSelected_SelectedLastTab_RTL_HideCloseBtn() {
initializeTest(true, false, 4);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.getNewTabButton().setDrawX(NEW_TAB_BTN_X_RTL);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Last tab overlapping NTB:
// drawX(100) + tabOverlapWidth(28) < NTB_X(100) + NTB_WIDTH(100)
when(tabs[4].getDrawX()).thenReturn(100.f);
mStripLayoutHelper.tabSelected(TIMESTAMP, 4, Tab.INVALID_TAB_ID, false);
// Close button is hidden for the selected last tab.
verify(tabs[4]).setCanShowCloseButton(false, false);
// Close button is hidden for the rest of tabs as well.
verify(tabs[0]).setCanShowCloseButton(false, false);
verify(tabs[1]).setCanShowCloseButton(false, false);
verify(tabs[2]).setCanShowCloseButton(false, false);
verify(tabs[3]).setCanShowCloseButton(false, false);
}
@Test
public void testTabSelected_SelectedLastTab_RTL_ShowCloseBtn() {
initializeTest(true, false, 4);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.getNewTabButton().setDrawX(NEW_TAB_BTN_X_RTL);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Last tab not overlapping NTB:
// drawX(200) + tabOverlapWidth(28) > NTB_X(100) + NTB_WIDTH(100)
when(tabs[4].getDrawX()).thenReturn(200.f);
mStripLayoutHelper.tabSelected(TIMESTAMP, 4, Tab.INVALID_TAB_ID, false);
// Close button is visible for selected last tab.
verify(tabs[4]).setCanShowCloseButton(true, false);
// Close button is hidden for the rest of tabs.
verify(tabs[0]).setCanShowCloseButton(false, false);
verify(tabs[1]).setCanShowCloseButton(false, false);
verify(tabs[2]).setCanShowCloseButton(false, false);
verify(tabs[3]).setCanShowCloseButton(false, false);
}
@Test
public void testTabSelected_SelectedNonLastTab_RTL_HideCloseBtn() {
initializeTest(true, false, 3);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Non-last tab overlapping strip fade:
// drawX(50) + tabOverlapWidth(28) < offsetXRight(20) + mediumRightFadeWidth(72)
when(tabs[3].getDrawX()).thenReturn(50.f);
mStripLayoutHelper.tabSelected(TIMESTAMP, 3, Tab.INVALID_TAB_ID, false);
// Close btn is hidden for selected tab.
verify(tabs[3]).setCanShowCloseButton(false, false);
// Close btn is hidden for all the rest of tabs as well.
verify(tabs[0]).setCanShowCloseButton(false, false);
verify(tabs[1]).setCanShowCloseButton(false, false);
verify(tabs[2]).setCanShowCloseButton(false, false);
verify(tabs[4]).setCanShowCloseButton(false, false);
}
@Test
public void testTabSelected_SelectedNonLastTab_RTL_ShowCloseBtn() {
initializeTest(true, false, 3);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.getNewTabButton().setDrawX(NEW_TAB_BTN_X);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Non-last tab not overlapping strip fade:
// drawX(70) + tabOverlapWidth(28) > offsetXRight(20) + mediumRightFadeWidth(72)
when(tabs[3].getDrawX()).thenReturn(70.f);
mStripLayoutHelper.tabSelected(TIMESTAMP, 3, Tab.INVALID_TAB_ID, false);
// Close button is visible for the selected tab.
verify(tabs[3]).setCanShowCloseButton(true, false);
// Close button is hidden for the rest of tabs.
verify(tabs[0]).setCanShowCloseButton(false, false);
verify(tabs[1]).setCanShowCloseButton(false, false);
verify(tabs[2]).setCanShowCloseButton(false, false);
verify(tabs[4]).setCanShowCloseButton(false, false);
}
@Test
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testUpdateDividers_WithTabSelected() {
// Setup with 5 tabs. Select tab 2.
initializeTest(false, false, 2);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// group 2nd and 3rd tab.
groupTabs(1, 3);
// Trigger update to set divider values.
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify tabs 2 and 3's start dividers are hidden due to selection.
assertFalse(
"First start divider should always be hidden.", tabs[0].isStartDividerVisible());
assertTrue("Start divider should be visible.", tabs[1].isStartDividerVisible());
assertFalse(
"Start divider is for selected tab and should be hidden.",
tabs[2].isStartDividerVisible());
assertFalse(
"Start divider is adjacent to selected tab and should be hidden.",
tabs[3].isStartDividerVisible());
assertTrue("Start divider should be visible.", tabs[4].isStartDividerVisible());
// Verify only last tab's end divider is visible.
assertFalse("End divider should be hidden.", tabs[0].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[1].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[2].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[3].isEndDividerVisible());
assertTrue("End divider should be visible.", tabs[4].isEndDividerVisible());
}
@Test
public void testUpdateDividers_InReorderMode() {
// Setup with 5 tabs. Select 2nd tab.
initializeTest(false, false, true, 1, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Start reorder mode at 2nd tab
mStripLayoutHelper.startReorderModeAtIndexForTesting(1);
// Trigger update to set divider values.
mStripLayoutHelper.updateLayout(TIMESTAMP);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Verify only 4th and 5th tab's start divider is visible.
assertFalse(
"First start divider should always be hidden.", tabs[0].isStartDividerVisible());
assertFalse("Start divider should be hidden.", tabs[1].isStartDividerVisible());
assertFalse("Start divider should be hidden.", tabs[2].isStartDividerVisible());
assertTrue("Start divider should be visible.", tabs[3].isStartDividerVisible());
assertTrue("Start divider should be visible.", tabs[4].isStartDividerVisible());
// Verify end divider visible only for 5th tab.
assertFalse("End divider should be hidden.", tabs[0].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[1].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[2].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[3].isEndDividerVisible());
assertTrue("End divider should be visible.", tabs[4].isEndDividerVisible());
}
@Test
public void testUpdateDividers_InReorderModeWithTabGroups() {
// Setup with 5 tabs. Select 2nd tab.
initializeTest(false, false, true, 1, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// group 2nd and 3rd tab.
groupTabs(1, 3);
// Start reorder mode at 2nd tab
mStripLayoutHelper.startReorderModeAtIndexForTesting(1);
// Trigger update to set divider values.
mStripLayoutHelper.updateLayout(TIMESTAMP);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Verify only 4th and 5th tab's start divider is visible.
assertFalse(
"First start divider should always be hidden.", tabs[0].isStartDividerVisible());
assertFalse("Start divider should be hidden.", tabs[1].isStartDividerVisible());
assertFalse("Start divider should be hidden.", tabs[2].isStartDividerVisible());
assertTrue("Start divider should be visible.", tabs[3].isStartDividerVisible());
assertTrue("Start divider should be visible.", tabs[4].isStartDividerVisible());
// Verify end divider visible for 1st and 5th tab.
assertTrue("End divider should be visible.", tabs[0].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[1].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[2].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[3].isEndDividerVisible());
assertTrue("End divider should be visible.", tabs[4].isEndDividerVisible());
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testUpdateDividers_InReorderModeWithTabGroups_TabGroupIndicators() {
// Setup with 5 tabs. Select 2nd tab.
initializeTest(false, false, true, 1, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// group 2nd and 3rd tab.
groupTabs(1, 3);
// Start reorder mode at 2nd tab
mStripLayoutHelper.startReorderModeAtIndexForTesting(1);
// Trigger update to set divider values.
mStripLayoutHelper.updateLayout(TIMESTAMP);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Verify only 4th and 5th tab's start divider is visible.
assertFalse(
"First start divider should always be hidden.", tabs[0].isStartDividerVisible());
assertFalse("Start divider should be hidden.", tabs[1].isStartDividerVisible());
assertFalse("Start divider should be hidden.", tabs[2].isStartDividerVisible());
assertTrue("Start divider should be visible.", tabs[3].isStartDividerVisible());
assertTrue("Start divider should be visible.", tabs[4].isStartDividerVisible());
// Verify end divider visible for 1st and 5th tab.
assertTrue("End divider should be visible.", tabs[0].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[1].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[2].isEndDividerVisible());
assertFalse("End divider should be hidden.", tabs[3].isEndDividerVisible());
assertTrue("End divider should be visible.", tabs[4].isEndDividerVisible());
}
@Test
public void testUpdateForegroundTabContainers() {
// Setup with 5 tabs. Select tab 2.
initializeTest(false, false, 2);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Trigger update to set foreground container visibility.
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify tabs 2 and 3's dividers are hidden due to selection.
float hiddenOpacity = StripLayoutHelper.TAB_OPACITY_HIDDEN;
float visibleOpacity = StripLayoutHelper.TAB_OPACITY_VISIBLE_FOREGROUND;
assertEquals(
"Tab is not selected and container should not be visible.",
hiddenOpacity,
tabs[0].getContainerOpacity(),
EPSILON);
assertEquals(
"Tab is not selected and container should not be visible.",
hiddenOpacity,
tabs[1].getContainerOpacity(),
EPSILON);
assertEquals(
"Tab is selected and container should be visible.",
visibleOpacity,
tabs[2].getContainerOpacity(),
EPSILON);
assertEquals(
"Tab is not selected and container should not be visible.",
hiddenOpacity,
tabs[3].getContainerOpacity(),
EPSILON);
assertEquals(
"Tab is not selected and container should not be visible.",
hiddenOpacity,
tabs[4].getContainerOpacity(),
EPSILON);
}
@Test
public void testNewTabButtonYPosition_Folio() {
int tabCount = 4;
initializeTest(false, false, false, 3, tabCount);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Set New tab button position.
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify new tab button y-position.
assertEquals(
"New tab button y-position is not as expected",
3.f,
mStripLayoutHelper.getNewTabButton().getDrawY(),
EPSILON);
}
@Test
public void testNewTabButtonXPosition() {
// Setup
int tabCount = 1;
initializeTest(false, false, false, 0, tabCount);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify new tab button position.
// tabWidth(237) + tabOverLapWidth(28) - ntbOffsetTowardsTabs(4) + offsetXLeft(10) = 271
assertEquals(
"New tab button x-position is not as expected",
271.f,
mStripLayoutHelper.getNewTabButton().getDrawX(),
EPSILON);
// rightBound(311) = expectedNtbDrawX(271) + ntbWidth(32) + touchSlop(8)
assertEquals(
"TouchableRect does not match. Right size should match ntb.getDrawX() + width.",
new RectF(PADDING_LEFT, 0, 311.f, SCREEN_HEIGHT),
mStripLayoutHelper.getTouchableRect());
}
@Test
public void testNewTabButtonXPosition_TabStripFull() {
// Setup
int tabCount = 5;
initializeTest(false, false, false, 0, tabCount);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify new tab button position.
// stripWidth(800) - offsetXRight(20) - stripEndPadding(8) - NtbWidth(32) = 740
assertEquals(
"New tab button x-position is not as expected",
740.f,
mStripLayoutHelper.getNewTabButton().getDrawX(),
EPSILON);
assertEquals(
"TouchableRect does not match. Strip is full, touch size should match the strip.",
new RectF(PADDING_LEFT, 0, SCREEN_WIDTH - PADDING_RIGHT, SCREEN_HEIGHT),
mStripLayoutHelper.getTouchableRect());
}
@Test
public void testNewTabButtonXPosition_RTL() {
int tabCount = 1;
initializeTest(true, false, false, 0, tabCount);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify new tab button position.
// stripWidth(800) - offsetXRight(20) - tabWidth(237) - tabOverLapWidth(28) - NtbWidth(32) +
// ntbOffsetTowardsTabs(4) = 487
assertEquals(
"New tab button x-position is not as expected",
487,
mStripLayoutHelper.getNewTabButton().getDrawX(),
EPSILON);
// leftBound(479) = drawX(487) - touchSlop(8)
assertEquals(
"TouchableRect does not match. Left side should equal to ntb.getDrawX()",
new RectF(479.f, 0, SCREEN_WIDTH - PADDING_RIGHT, SCREEN_HEIGHT),
mStripLayoutHelper.getTouchableRect());
}
@Test
public void testNewTabButtonXPosition_TabStripFull_RTL() {
// Setup
int tabCount = 5;
initializeTest(true, false, false, 0, tabCount);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify new tab button position.
// offsetXLeft(10) + buttonEndPadding(8) = 28.
assertEquals(
"New tab button x-position is not as expected",
18.f,
mStripLayoutHelper.getNewTabButton().getDrawX(),
EPSILON);
assertEquals(
"TouchableRect does not match. Strip is full, touch size should match the strip.",
new RectF(PADDING_LEFT, 0, SCREEN_WIDTH - PADDING_RIGHT, SCREEN_HEIGHT),
mStripLayoutHelper.getTouchableRect());
}
@Test
public void testNewTabButtonStyle_ButtonStyleDisabled() {
int tabCount = 1;
initializeTest(false, false, false, 0, tabCount);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
assertEquals(
"Unexpected incognito button color.",
AppCompatResources.getColorStateList(mContext, R.color.default_icon_color_tint_list)
.getDefaultColor(),
((org.chromium.chrome.browser.compositor.layouts.components.TintedCompositorButton)
mStripLayoutHelper.getNewTabButton())
.getTint());
}
@Test
@Feature("Advanced Peripherals Support")
public void testNewTabButtonHoverHighlightProperties() {
// Setup
initializeTest(false, false, false, 0, 1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify new tab button hover highlight resource id.
assertEquals(
"New tab button hover highlight is not as expected",
R.drawable.bg_circle_tab_strip_button,
mStripLayoutHelper.getNewTabButton().getBackgroundResourceId());
// Verify new tab button hover highlight default tint.
TintedCompositorButton ntb = spy(mStripLayoutHelper.getNewTabButton());
when(ntb.isHovered()).thenReturn(true);
int defaultNTBHoverBackgroundTint =
ColorUtils.setAlphaComponent(
SemanticColorUtils.getDefaultTextColor(mContext), (int) (0.08 * 255));
assertEquals(
"New tab button hover highlight default tint is not as expected",
defaultNTBHoverBackgroundTint,
ntb.getBackgroundTint());
// Verify new tab button hover highlight pressed tint.
when(ntb.isHovered()).thenReturn(false);
when(ntb.isPressed()).thenReturn(true);
when(ntb.isPressedFromMouse()).thenReturn(true);
int pressedNTBHoverBackgroundTint =
ColorUtils.setAlphaComponent(
SemanticColorUtils.getDefaultTextColor(mContext), (int) (0.12 * 255));
assertEquals(
"New tab button hover highlight pressed tint is not as expected",
pressedNTBHoverBackgroundTint,
ntb.getBackgroundTint());
when(ntb.isPressedFromMouse()).thenReturn(false);
// Verify new tab button incognito hover highlight default tint.
when(ntb.isHovered()).thenReturn(true);
when(ntb.isIncognito()).thenReturn(true);
int defaultNTBHoverBackgroundIncognitoTint =
ColorUtils.setAlphaComponent(
mContext.getColor(R.color.tab_strip_button_hover_bg_color),
(int) (0.08 * 255));
assertEquals(
"New tab button hover highlight default tint is not as expected",
defaultNTBHoverBackgroundIncognitoTint,
ntb.getBackgroundTint());
// Verify new tab button incognito hover highlight pressed tint.
when(ntb.isHovered()).thenReturn(false);
when(ntb.isPressed()).thenReturn(true);
when(ntb.isPressedFromMouse()).thenReturn(true);
int hoverBackgroundPressedIncognitoColor =
ColorUtils.setAlphaComponent(
mContext.getColor(R.color.tab_strip_button_hover_bg_color),
(int) (0.12 * 255));
assertEquals(
"New tab button hover highlight pressed tint is not as expected",
hoverBackgroundPressedIncognitoColor,
ntb.getBackgroundTint());
}
@Test
@Feature("Advanced Peripherals Support")
public void testNewTabButtonHoverEnter() {
// Setup
initializeTest(false, false, true, 0, 1);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify new tab button is hovered.
int x = (int) mStripLayoutHelper.getNewTabButton().getDrawX();
mStripLayoutHelper.onHoverEnter(
x + 1, 0); // mouse position within NTB range(32dp width + 12dp click slop).
assertTrue(
"New tab button should be hovered",
mStripLayoutHelper.getNewTabButton().isHovered());
// Verify new tab button is NOT hovered
mStripLayoutHelper.onHoverEnter(
x + 45, 0); // mouse position out of NTB range(32dp width + 12dp click slop).
assertFalse(
"New tab button should NOT be hovered",
mStripLayoutHelper.getNewTabButton().isHovered());
}
@Test
@Feature("Advanced Peripherals Support")
public void testNewTabButtonHoverOnDown() {
// Setup
initializeTest(false, false, false, 0, 1);
// Verify new tab button is in pressed state, not hover state, when clicked from mouse.
mStripLayoutHelper.onDown(
1L, mStripLayoutHelper.getNewTabButton().getDrawX() + 1, 0, true, 1);
assertFalse(
"New tab button should not be hovered",
mStripLayoutHelper.getNewTabButton().isHovered());
assertTrue(
"New tab button should be pressed from mouse",
mStripLayoutHelper.getNewTabButton().isPressedFromMouse());
}
@Test
@Feature("Advanced Peripherals Support")
public void testCloseButtonHoverHighlightProperties() {
// Setup
initializeTest(false, false, 2);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
TintedCompositorButton closeButton = spy(tabs[0].getCloseButton());
// Verify close button hover highlight resource id.
assertEquals(
"Close button hover highlight is not as expected",
R.drawable.tab_close_button_bg,
tabs[0].getCloseButton().getBackgroundResourceId());
// Verify the non-hover background tint for the close button. It should always be
// transparent, as no background should be applied when it is not being hovered over.
assertEquals(
"Close button non-hover background tint is not as expected",
Color.TRANSPARENT,
closeButton.getBackgroundTint());
// Verify close button hover highlight default tint.
when(closeButton.isHovered()).thenReturn(true);
int defaultCloseButtonHoverBackgroundTint =
ColorUtils.setAlphaComponent(
SemanticColorUtils.getDefaultTextColor(mContext), (int) (0.08 * 255));
assertEquals(
"Close button hover highlight default tint is not as expected",
defaultCloseButtonHoverBackgroundTint,
closeButton.getBackgroundTint());
// Verify close button hover highlight pressed tint.
when(closeButton.isHovered()).thenReturn(false);
when(closeButton.isPressed()).thenReturn(true);
when(closeButton.isPressedFromMouse()).thenReturn(true);
int pressedCloseButtonHoverBackgroundTint =
ColorUtils.setAlphaComponent(
SemanticColorUtils.getDefaultTextColor(mContext), (int) (0.12 * 255));
assertEquals(
"Close button hover highlight pressed tint is not as expected",
pressedCloseButtonHoverBackgroundTint,
closeButton.getBackgroundTint());
when(closeButton.isPressed()).thenReturn(false);
when(closeButton.isPressedFromMouse()).thenReturn(false);
// Verify close button incognito hover highlight default tint.
when(closeButton.isIncognito()).thenReturn(true);
when(closeButton.isHovered()).thenReturn(true);
int defaultNTBHoverBackgroundIncognitoTint =
ColorUtils.setAlphaComponent(
mContext.getColor(R.color.tab_strip_button_hover_bg_color),
(int) (0.08 * 255));
assertEquals(
"Close button hover highlight default tint is not as expected",
defaultNTBHoverBackgroundIncognitoTint,
closeButton.getBackgroundTint());
// Verify close button incognito hover highlight pressed tint.
when(closeButton.isHovered()).thenReturn(false);
when(closeButton.isPressed()).thenReturn(true);
when(closeButton.isPressedFromMouse()).thenReturn(true);
int hoverBackgroundPressedIncognitoColor =
ColorUtils.setAlphaComponent(
mContext.getColor(R.color.tab_strip_button_hover_bg_color),
(int) (0.12 * 255));
assertEquals(
"Close button hover highlight pressed tint is not as expected",
hoverBackgroundPressedIncognitoColor,
closeButton.getBackgroundTint());
}
@Test
@Feature("Advanced Peripherals Support")
public void testCloseButtonHoverEnter() {
// Setup
initializeTest(false, false, 2);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
TintedCompositorButton closeButton =
new TintedCompositorButton(mContext, 24.f, 24.f, mClickHandler);
closeButton.setOpacity(1.f);
int x = (int) closeButton.getDrawX();
int y = (int) closeButton.getDrawY();
StripLayoutHelper stripLayoutHelper = spy(mStripLayoutHelper);
StripLayoutTab tab = spy(tabs[0]);
when(stripLayoutHelper.getTabAtPosition(x)).thenReturn(tab);
stripLayoutHelper.setTabAtPositionForTesting(tab);
tab.setCloseButtonForTesting(closeButton);
tab.setShowingCloseButtonForTesting(true);
// Verify close button is hovered on.
stripLayoutHelper.onHoverEnter(
x + 1,
y + 1); // mouse position within close button range(24dp width + 12dp click slop)
assertTrue("Close button should be hovered", tab.isCloseHovered());
// Verify close button is NOT hovered on.
stripLayoutHelper.onHoverEnter(
x + 37,
y); // mouse position out of close button range(24dp width + 12dp click slop).
assertFalse("Close button should NOT be hovered on", tab.isCloseHovered());
}
@Test
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_CONTEXT_MENU)
@Feature("Advanced Peripherals Support")
public void testCloseButtonHoverOnDown() {
// Setup
initializeTest(false, false, 2);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
TintedCompositorButton closeButton =
new TintedCompositorButton(mContext, 24.f, 24.f, mClickHandler);
closeButton.setOpacity(1.f);
int x = (int) closeButton.getDrawX();
int y = (int) closeButton.getDrawY();
tabs[0].setCloseButtonForTesting(closeButton);
// Verify close button is in pressed state, not hover state, when clicked from mouse.
mStripLayoutHelper.onDown(1L, x + 1, y + 1, true, 1);
assertFalse("Close button should not be hovered", closeButton.isHovered());
mStripLayoutHelper.onDown(1L, (int) x + 1, y + 1, true, 1);
assertFalse("Close should NOT be hovered", closeButton.isPressedFromMouse());
// Verify close button is not in hover state or press state when long-pressed.
mStripLayoutHelper.onLongPress(1L, x + 1, y + 1);
assertFalse("Close button should NOT be hovered", closeButton.isHovered());
assertFalse("Close button should NOT be pressed", closeButton.isPressed());
}
@Test
public void testScrollOffset_OnResume_StartOnLeft_SelectedRightmostTab() {
// Arrange: Initialize tabs with tenth tab selected and MSB visible (long fade).
initializeTest(false, true, true, 9, 12);
mStripLayoutHelper.setIsFirstLayoutPassForTesting(false);
// Set screen width to 800dp and scroll selected tab to view.
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
mStripLayoutHelper.scrollTabToView(TIMESTAMP, false);
// optimalEnd =
// stripWidth(800) - rightPadding(20) - rightFade(136) - (index(9) + 1) * tabWidth(108-28) -
// overlapWidth(28)
float expectedFinalX = -184.f;
assertEquals(expectedFinalX, mStripLayoutHelper.getScrollOffset(), EPSILON);
}
@Test
public void testScrollOffset_OnResume_StartOnLeft_NoModelSelBtn_SelectedRightmostTab() {
// Arrange: Initialize tabs with tenth tab selected and MSB not visible (medium fade).
initializeTest(false, false, true, 9, 12);
mStripLayoutHelper.setIsFirstLayoutPassForTesting(false);
// Set screen width to 800dp and scroll selected tab to view.
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
mStripLayoutHelper.scrollTabToView(TIMESTAMP, false);
// optimalEnd =
// stripWidth(800) - rightPadding(20) - rightFade(72) - (index(9) + 1) * tabWidth(108-28) -
// overlapWidth(28)
float expectedFinalX = -120.f;
assertEquals(expectedFinalX, mStripLayoutHelper.getScrollOffset(), EPSILON);
}
@Test
public void testScrollOffset_OnResume_StartOnRight_SelectedLeftmostTab() {
// Arrange: Initialize tabs with first tab selected.
initializeTest(false, true, false, 0, 10);
// Set screen width to 800dp and scroll selected tab to view.
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.scrollTabToView(TIMESTAMP, false);
// optimalStart = leftFade(60) - (index(0) * tabWidth(108-28)) - leftPadding(10)
int expectedFinalX = 50;
assertEquals(expectedFinalX, mStripLayoutHelper.getScrollerForTesting().getFinalX());
}
@Test
public void testScrollOffset_OnOrientationChange_SelectedTabVisible() {
// Arrange: Initialize tabs with last tab selected.
initializeTest(false, false, false, 9, 10);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_SMALL, 150.f, 10);
when(tabs[9].isVisible()).thenReturn(true);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Set screen width to 1200 to start.
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH_LANDSCAPE,
SCREEN_HEIGHT,
false,
TIMESTAMP,
PADDING_LEFT,
PADDING_RIGHT);
// Assert: finalX value before orientation change.
int initialFinalX = 0;
assertEquals(initialFinalX, mStripLayoutHelper.getScrollerForTesting().getFinalX());
// Act: change orientation.
// drawX: tabWidth(108-28) * 9
when(tabs[9].getDrawX()).thenReturn(720.f);
when(tabs[9].getIdealX()).thenReturn(720.f);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, true, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Assert: finalX value after orientation change.
// stripWidth(800) - rightFade(72) - rightPadding(20) - tabWidth(108-28) - idealX(720) -
// overlapWidth(28) + leftPadding(10)
int expectedFinalX = -110;
assertEquals(expectedFinalX, mStripLayoutHelper.getScrollerForTesting().getFinalX());
}
@Test
public void testScrollOffset_OnOrientationChange_SelectedTabNotVisible() {
// Arrange: Initialize tabs with last tab selected.
initializeTest(false, false, false, 9, 10);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10);
when(tabs[9].isVisible()).thenReturn(false);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Set screen width to 1200 to start
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH_LANDSCAPE,
SCREEN_HEIGHT,
false,
TIMESTAMP,
PADDING_LEFT,
PADDING_RIGHT);
// Assert: finalX value before orientation change.
int initialFinalX = 0;
assertEquals(initialFinalX, mStripLayoutHelper.getScrollerForTesting().getFinalX());
// Act: change orientation.
when(tabs[9].getDrawX()).thenReturn(-1.f);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, true, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Assert: finalX value remains the same on orientation change.
assertEquals(initialFinalX, mStripLayoutHelper.getScrollerForTesting().getFinalX());
}
@Test
public void testTabSelected_AfterTabClose_SkipsAutoScroll() {
initializeTest(false, true, 3);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Set initial scroller position to 1000.
mStripLayoutHelper.getScrollerForTesting().setFinalX(1000);
// Act: close a non selected tab.
mStripLayoutHelper.handleCloseButtonClick(tabs[1], TIMESTAMP);
// Assert: scroller position is not modified.
assertEquals(1000, mStripLayoutHelper.getScrollerForTesting().getFinalX());
}
@Test
public void testTabSelected_AfterSelectedTabClose_SkipsAutoScroll() {
initializeTest(false, true, 3);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Set initial scroller position to 1000.
mStripLayoutHelper.getScrollerForTesting().setFinalX(1000);
// Act: close the selected tab.
mStripLayoutHelper.handleCloseButtonClick(tabs[3], TIMESTAMP);
// Assert: scroller position is not modified.
assertEquals(1000, mStripLayoutHelper.getScrollerForTesting().getFinalX());
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testInReorderMode_StripStartMargin_TabGroupIndicators() {
// Initialize.
initializeTest(false, false, 5);
groupTabs(0, 2);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Update layout.
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Start reorder mode on the third tab.
mStripLayoutHelper.disableAnimationsForTesting();
mStripLayoutHelper.startReorderModeAtIndexForTesting(2);
// Verify that we enter reorder mode.
assertTrue("Should in reorder mode.", mStripLayoutHelper.getInReorderModeForTesting());
// Assert: StripStartMargin is about 1/4 tab width to create space for dragging first tab
// out of group on strip.
float expectedMargin =
(mStripLayoutHelper.getCachedTabWidthForTesting() - TAB_OVERLAP_WIDTH)
* REORDER_OVERLAP_SWITCH_PERCENTAGE
* REORDER_OVERLAP_SWITCH_PERCENTAGE;
assertEquals(
"StripStartMargin is incorrect",
expectedMargin,
mStripLayoutHelper.getStripStartMarginForReorderForTesting(),
0.1f);
// Assert: There should be a scroll offset equal to counter the stripStartMargin, so that
// the interacting tab would remain visually stationary.
assertEquals(
"scrollOffset is incorrect",
-expectedMargin,
mStripLayoutHelper.getScrollOffset(),
0.1f);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testInReorderMode_StripEndMargin_TabGroupIndicators() {
// Initialize.
initializeTest(false, false, 4);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
groupTabs(3, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Update layout.
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Start reorder mode on the fourth tab.
mStripLayoutHelper.startReorderModeAtIndexForTesting(3);
// Verify that we enter reorder mode.
assertTrue("Should in reorder mode.", mStripLayoutHelper.getInReorderModeForTesting());
// Assert: Last tab's trailingMargin should be about 1/4 tab width to create space for
// dragging last tab out of group on strip.
float expectedMargin =
(mStripLayoutHelper.getCachedTabWidthForTesting() - TAB_OVERLAP_WIDTH)
* REORDER_OVERLAP_SWITCH_PERCENTAGE
* REORDER_OVERLAP_SWITCH_PERCENTAGE;
assertEquals(
"Strip end margin is incorrect", expectedMargin, tabs[4].getTrailingMargin(), 0.1f);
}
@Test
public void testTabCreated_Animation() {
// Initialize with default amount of tabs. Clear any animations.
initializeTest(false, false, 3);
mStripLayoutHelper.finishAnimationsAndPushTabUpdates();
assertNull(
"Animation should not be running.",
mStripLayoutHelper.getRunningAnimatorForTesting());
// Act: Create new tab in model and trigger update in tab strip.
mModel.addTab("new tab");
mStripLayoutHelper.tabCreated(TIMESTAMP, 5, 3, true, false, false);
// Assert: Animation is running.
assertNotNull(
"Animation should running.", mStripLayoutHelper.getRunningAnimatorForTesting());
}
@Test
public void testTabCreated_RestoredTab_SkipsAutoscroll() {
initializeTest(false, true, 3);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Set initial scroller position to 1200.
mStripLayoutHelper.getScrollerForTesting().setFinalX((int) SCREEN_WIDTH_LANDSCAPE);
// Act: Tab was restored after undoing a tab closure.
boolean closureCancelled = true;
mModel.addTab("new tab");
mStripLayoutHelper.tabCreated(TIMESTAMP, 5, 3, false, closureCancelled, false);
// Assert: scroller position is not modified.
assertEquals(1200, mStripLayoutHelper.getScrollerForTesting().getFinalX());
}
@Test
public void testTabCreated_NonRestoredTab_Autoscrolls() {
initializeTest(false, true, 3);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Set initial scroller position to 1200.
mStripLayoutHelper.getScrollerForTesting().setFinalX((int) SCREEN_WIDTH_LANDSCAPE);
// Act: Tab was not restored after undoing a tab closure.
boolean closureCancelled = false;
mModel.addTab("new tab");
mStripLayoutHelper.tabCreated(TIMESTAMP, 5, 3, false, closureCancelled, false);
// Assert: scroller position is not modified.
assertNotEquals(1200, mStripLayoutHelper.getScrollerForTesting().getFinalX());
}
@Test
public void testTabCreated_BringSelectedTabToVisibleArea_StartupRestoredUnselectedTab() {
initializeTest(false, false, true, 1, 11);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Set initial scroller position to -500.
mStripLayoutHelper.setScrollOffsetForTesting(-500);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Act: Tab was restored during startup.
boolean selected = false;
boolean onStartup = true;
mModel.addTab("new tab");
mStripLayoutHelper.tabCreated(TIMESTAMP, 12, 12, selected, false, onStartup);
// Assert: We don't scroll to the created tab. The selected tab is not already visible, so
// we scroll to it. With right padding, the scroll offset needs to include the rightPadding
// so the last tab is made visible.
// Offset = -(1 tab width) + leftTabWidth - rightPadding(20)= -80 + 60 -20= -40.
float expectedOffset = -40f;
assertEquals(
"We should scroll to the selected tab",
expectedOffset,
mStripLayoutHelper.getScrollOffset(),
EPSILON);
}
@Test
public void testOnDown_OnNewTabButton() {
// Initialize.
initializeTest(false, false, true, 0, 5);
// Set new tab button location and dimensions.
mStripLayoutHelper.getNewTabButton().setDrawX(NEW_TAB_BTN_X);
mStripLayoutHelper.getNewTabButton().setDrawY(NEW_TAB_BTN_Y);
mStripLayoutHelper.getNewTabButton().setWidth(NEW_TAB_BTN_WIDTH);
mStripLayoutHelper.getNewTabButton().setHeight(NEW_TAB_BTN_HEIGHT);
// Press down on new tab button.
// CenterX = getX() + (getWidth() / 2) = 700 + (100 / 2) = 750
// CenterY = getY() + (getHeight() / 2) = 1400 + (100 / 2) = 1450
mStripLayoutHelper.onDown(TIMESTAMP, 750f, 1450f, false, 0);
// Verify.
assertTrue(
"New tab button should be pressed.",
mStripLayoutHelper.getNewTabButton().isPressed());
assertNull(
"Should not set an interacting tab when pressing down on new tab button.",
mStripLayoutHelper.getInteractingTabForTesting());
assertFalse(
"Should not start reorder mode when pressing down on new tab button.",
mStripLayoutHelper.getInReorderModeForTesting());
}
@Test
public void testOnDown_OnTab() {
// Initialize.
initializeTest(false, false, true, 0, 5);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(150f);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Press down on second tab.
when(tabs[1].checkCloseHitTest(anyFloat(), anyFloat())).thenReturn(false);
mStripLayoutHelper.setTabAtPositionForTesting(tabs[1]);
mStripLayoutHelper.onDown(TIMESTAMP, 150f, 0f, false, 0);
// Verify.
assertFalse(
"New tab button should not be pressed.",
mStripLayoutHelper.getNewTabButton().isPressed());
assertEquals(
"Second tab should be interacting tab.",
tabs[1],
mStripLayoutHelper.getInteractingTabForTesting());
assertFalse(
"Should not start reorder mode when pressing down on tab without mouse.",
mStripLayoutHelper.getInReorderModeForTesting());
verify(tabs[1], never()).setClosePressed(anyBoolean(), anyBoolean());
}
@Test
public void testOnDownAndDrag_OnTab_WithMouse() {
// Initialize.
initializeTest(false, false, true, 0, 5);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(150f);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Press down on second tab with mouse followed by drag.
when(tabs[1].checkCloseHitTest(anyFloat(), anyFloat())).thenReturn(false);
mStripLayoutHelper.setTabAtPositionForTesting(tabs[1]);
mStripLayoutHelper.onDown(TIMESTAMP, 150f, 0, true, MotionEvent.BUTTON_PRIMARY);
mStripLayoutHelper.drag(TIMESTAMP, DRAG_START_POINT.x, DRAG_START_POINT.y, 30f);
// Verify.
assertEquals(
"Second tab should be interacting tab.",
tabs[1],
mStripLayoutHelper.getInteractingTabForTesting());
assertTrue(
"Should start reorder mode when dragging on pressed on tab with mouse.",
mStripLayoutHelper.getInReorderModeForTesting());
verify(mTabDragSource)
.startTabDragAction(
mToolbarContainerView,
mModel.getTabAt(1),
DRAG_START_POINT,
tabs[1].getDrawX(),
tabs[1].getWidth());
}
@Test
public void testOnDown_OnTabCloseButton() {
// Initialize.
initializeTest(false, false, true, 0, 5);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(150f);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Press down on second tab's close button.
when(tabs[1].checkCloseHitTest(anyFloat(), anyFloat())).thenReturn(true);
mStripLayoutHelper.setTabAtPositionForTesting(tabs[1]);
mStripLayoutHelper.onDown(TIMESTAMP, 150f, 0f, false, 0);
// Verify.
assertFalse(
"New tab button should not be pressed.",
mStripLayoutHelper.getNewTabButton().isPressed());
assertEquals(
"Second tab should be interacting tab.",
tabs[1],
mStripLayoutHelper.getInteractingTabForTesting());
assertFalse(
"Should not start reorder mode from close button.",
mStripLayoutHelper.getInReorderModeForTesting());
verify(tabs[1]).setClosePressed(eq(true), eq(false));
}
@Test
public void testOnDown_OnTabCloseButton_WithMouse() {
// Initialize.
initializeTest(false, false, true, 0, 5);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(150f);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Press down on second tab's close button with mouse.
when(tabs[1].checkCloseHitTest(anyFloat(), anyFloat())).thenReturn(true);
mStripLayoutHelper.setTabAtPositionForTesting(tabs[1]);
mStripLayoutHelper.onDown(TIMESTAMP, 150f, 0f, true, 0);
// Verify.
assertFalse(
"New tab button should not be pressed.",
mStripLayoutHelper.getNewTabButton().isPressed());
assertEquals(
"Second tab should be interacting tab.",
tabs[1],
mStripLayoutHelper.getInteractingTabForTesting());
assertFalse(
"Should not start reorder mode from close button.",
mStripLayoutHelper.getInReorderModeForTesting());
verify(tabs[1]).setClosePressed(eq(true), eq(true));
}
@Test
public void testOnDown_WhileScrolling() {
// Initialize and assert scroller is finished.
initializeTest(false, false, true, 0, 5);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(150f);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
assertTrue(
"Scroller should be finished right after initializing.",
mStripLayoutHelper.getScrollerForTesting().isFinished());
// Start scroll and assert scroller is not finished.
mStripLayoutHelper.getScrollerForTesting().startScroll(0, 0, 0, 0, TIMESTAMP, 1000);
assertFalse(
"Scroller should not be finished after starting scroll.",
mStripLayoutHelper.getScrollerForTesting().isFinished());
// Press down on second tab and assert scroller is finished.
mStripLayoutHelper.setTabAtPositionForTesting(tabs[1]);
mStripLayoutHelper.onDown(TIMESTAMP, 150f, 0f, false, 0);
assertFalse(
"New tab button should not be pressed.",
mStripLayoutHelper.getNewTabButton().isPressed());
assertNull(
"Should not set an interacting tab when pressing down to stop scrolling.",
mStripLayoutHelper.getInteractingTabForTesting());
assertTrue(
"Scroller should be force finished after pressing down on strip.",
mStripLayoutHelper.getScrollerForTesting().isFinished());
}
@Test
@DisableFeatures({
ChromeFeatureList.TAB_DRAG_DROP_ANDROID,
ChromeFeatureList.TAB_STRIP_GROUP_CONTEXT_MENU
})
public void testOnLongPress_OnTab() {
onLongPress_OnTab();
// Verify we directly enter reorder mode.SS
assertTrue(
"Should be in reorder mode after long press on tab.",
mStripLayoutHelper.getInReorderModeForTesting());
}
@Test
@Feature("Tab Group Context Menu")
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_CONTEXT_MENU,
ChromeFeatureList.TAB_GROUP_PARITY_ANDROID,
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS
})
public void testOnLongPress_OnGroupTitle() {
// Initialize.
initializeTest(false, false, 0);
groupTabs(0, 1);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(150f);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Set up tabModel and menu coordinator.
MockTabModel tabModel = new MockTabModel(mProfile, null);
when(mProfile.isOffTheRecord()).thenReturn(true);
tabModel.setActive(true);
mStripLayoutHelper.setTabGroupContextMenuCoordinatorForTesting(
mTabGroupContextMenuCoordinator);
// Long press on group title
mStripLayoutHelper.onLongPress(TIMESTAMP, 10f, 0f);
ArgumentCaptor<RectProvider> rectProviderArgumentCaptor =
ArgumentCaptor.forClass(RectProvider.class);
// Verify tab group context menu is showing.
verify(mTabGroupContextMenuCoordinator)
.showMenu(rectProviderArgumentCaptor.capture(), anyInt());
// Verify anchorView coordinates.
StripLayoutView view = mStripLayoutHelper.getViewAtPositionX(10f, true);
assertTrue(view instanceof StripLayoutGroupTitle);
StripLayoutGroupTitle titleView = (StripLayoutGroupTitle) view;
Rect actualRect = rectProviderArgumentCaptor.getValue().getRect();
Rect expectedRect = new Rect();
titleView.getDrawBoundsOnScreen(expectedRect, mWindowRectSupplier);
assertEquals("Anchor view for menu is positioned incorrectly", expectedRect, actualRect);
}
@Test
@Config(sdk = Build.VERSION_CODES.R)
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_CONTEXT_MENU)
public void testOnLongPress_WithDragDrop_OnTab() {
// Extra setup for DragDrop
setTabDragSourceMock();
onLongPress_OnTab();
// Verify drag invoked
verify(mTabDragSource).startTabDragAction(any(), any(), any(), anyFloat(), anyFloat());
}
private void onLongPress_OnTab() {
// Initialize.
initializeTest(false, false, 0);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(150f);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Long press on second tab.
when(tabs[1].checkCloseHitTest(anyFloat(), anyFloat())).thenReturn(false);
mStripLayoutHelper.setTabAtPositionForTesting(tabs[1]);
mStripLayoutHelper.onLongPress(TIMESTAMP, 150f, 0f);
// Verify that we enter reorder mode.
assertFalse(
"Should not show tab menu after long press on tab.",
mStripLayoutHelper.isTabMenuShowingForTesting());
}
@Test
public void testOnLongPress_OnCloseButton() {
// Initialize.
initializeTest(false, false, 0);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(150f);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Mock tab's view.
View tabView = new View(mActivity);
tabView.setLayoutParams(new MarginLayoutParams(150, 50));
when(mModel.getTabAt(1).getView()).thenReturn(tabView);
// Long press on second tab's close button.
when(tabs[1].checkCloseHitTest(anyFloat(), anyFloat())).thenReturn(true);
mStripLayoutHelper.setTabAtPositionForTesting(tabs[1]);
mStripLayoutHelper.onLongPress(TIMESTAMP, 150f, 0f);
// Verify that we show the "Close all tabs" popup menu.
assertFalse(
"Should not be in reorder mode after long press on tab close button.",
mStripLayoutHelper.getInReorderModeForTesting());
assertTrue(
"Should show tab menu after long press on tab close button.",
mStripLayoutHelper.isTabMenuShowingForTesting());
}
@Test
@DisableFeatures({
ChromeFeatureList.TAB_DRAG_DROP_ANDROID,
ChromeFeatureList.TAB_STRIP_GROUP_CONTEXT_MENU
})
public void testOnLongPress_OffTab() {
onLongPress_OffTab();
}
@Test
@Config(sdk = Build.VERSION_CODES.R)
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_CONTEXT_MENU)
public void testOnLongPress_WithDragDrop_OffTab() {
// Extra setup for DragDrop
setTabDragSourceMock();
Activity activity = spy(mActivity);
when(mToolbarContainerView.getContext()).thenReturn(activity);
onLongPress_OffTab();
// verify tab drag not invoked.
verifyNoInteractions(mTabDragSource);
}
private void onLongPress_OffTab() {
// Initialize.
initializeTest(false, false, 0);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(150f);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
// Long press past the last tab.
mStripLayoutHelper.setTabAtPositionForTesting(null);
mStripLayoutHelper.onLongPress(TIMESTAMP, 150f, 0f);
// Verify that we show the "Close all tabs" popup menu.
assertFalse(
"Should not be in reorder mode after long press on empty space on tab strip.",
mStripLayoutHelper.getInReorderModeForTesting());
assertFalse(
"Should not show after long press on empty space on tab strip.",
mStripLayoutHelper.isTabMenuShowingForTesting());
}
@Test
@Feature("Tab Groups on Tab Strip")
public void testTabGroupMargins_BetweenTabs() {
// Initialize with 3 tabs.
initializeTest(false, false, true, 0, 3);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Start reorder.
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
// Verify no tabs have a trailing margin, since there are no tab groups.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[1].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[2].getTrailingMargin(), EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupMargins_TabToLeft() {
// Mock 1 tab to the left of a tab group with 3 tabs.
initializeTest(false, false, true, 0, 4);
groupTabs(1, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Start reorder.
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
// Verify the leftmost and final tabs have a trailing margin.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
float tabWidth = tabs[0].getWidth();
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[1].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[2].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[3].getTrailingMargin(), EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupMargins_TabToRight() {
// Mock 1 tab to the right of a tab group with 3 tabs.
initializeTest(false, false, true, 0, 4);
groupTabs(0, 3);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Start reorder.
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
// Verify the rightmost tab in the tab group has a trailing margin.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
float tabWidth = tabs[0].getWidth();
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[1].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[2].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[3].getTrailingMargin(), EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupMargins_BetweenGroups() {
// Mock a tab group with 2 tabs to the left of a tab group with 3 tabs.
initializeTest(false, false, true, 0, 5);
groupTabs(0, 2);
groupTabs(2, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Start reorder.
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
// Verify the rightmost tab in the first group has a trailing margin.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
float tabWidth = tabs[0].getWidth();
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[1].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[2].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[3].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[4].getTrailingMargin(), EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupMargins_BetweenGroups_RTL() {
// Mock a tab group with 2 tabs to the right of a tab group with 3 tabs.
initializeTest(true, false, true, 0, 5);
groupTabs(0, 2);
groupTabs(2, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Start reorder.
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
// Verify the leftmost tab in the first group has a trailing margin.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
float tabWidth = tabs[0].getWidth();
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[1].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[2].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[3].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[4].getTrailingMargin(), EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupMargins_StartReorder_Animated() {
// Mock 1 tab to the left of a tab group with 3 tabs.
initializeTest(false, false, false, 0, 4);
groupTabs(1, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Start reorder.
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
// Verify that only the last tab has a margin, since that one is not animated.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
float tabWidth = tabs[0].getWidth();
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[1].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[2].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[3].getTrailingMargin(), EPSILON);
// Complete the currently running animations.
assertNotNull(mStripLayoutHelper.getRunningAnimatorForTesting());
mStripLayoutHelper.getRunningAnimatorForTesting().end();
// Verify the leftmost and final tabs have a trailing margin.
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[1].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[2].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[3].getTrailingMargin(), EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupMargins_StopReorder_Animated() {
// Mock 1 tab to the left of a tab group with 3 tabs.
initializeTest(false, false, false, 0, 4);
groupTabs(1, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Finish starting reorder, then begin stopping reorder.
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
mStripLayoutHelper.getRunningAnimatorForTesting().end();
mStripLayoutHelper.stopReorderModeForTesting();
// Verify the leftmost and final tabs have a trailing margin.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
float tabWidth = tabs[0].getWidth();
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[1].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[2].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_MARGIN, tabWidth / 2, tabs[3].getTrailingMargin(), EPSILON);
// Complete the currently running animations.
assertNotNull(mStripLayoutHelper.getRunningAnimatorForTesting());
mStripLayoutHelper.getRunningAnimatorForTesting().end();
// Verify that there are no margins as we have stopped reordering.
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[1].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[2].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[3].getTrailingMargin(), EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
public void testTabGroupMargins_ResetMarginsOnStopReorder() {
// Mock 1 tab to the left of a tab group with 3 tabs.
initializeTest(false, false, true, 0, 4);
groupTabs(1, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Start then stop reorder.
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
mStripLayoutHelper.stopReorderModeForTesting();
// Verify no tabs have a trailing margin when reordering is stopped.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[1].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[2].getTrailingMargin(), EPSILON);
assertEquals(EXPECTED_NO_MARGIN, 0f, tabs[3].getTrailingMargin(), EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
public void testTabGroupMargins_NoScrollOnReorder() {
// Mock 1 tab to the right of 2 tab groups with 2 tabs each.
initializeTest(false, false, true, 0, 5);
groupTabs(2, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.setScrollOffsetForTesting(0);
// Start reorder on leftmost tab. No margins to left of tab, so shouldn't scroll.
// Verify the scroll offset is still 0.
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
assertEquals(
"There are no margins left of the selected tab, so we shouldn't scroll.",
0f,
mStripLayoutHelper.getScrollOffset(),
EPSILON);
// Stop reorder. Verify the scroll offset is still 0.
mStripLayoutHelper.stopReorderModeForTesting();
assertEquals(
"Scroll offset should return to 0 after stopping reorder mode.",
0f,
mStripLayoutHelper.getScrollOffset(),
EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupMargins_ScrollOnReorder() {
// Mock 6 tabs to the right of 2 tab groups with 2 tabs each.
initializeTest(false, false, true, 0, 10);
groupTabs(0, 2);
groupTabs(2, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.setScrollOffsetForTesting(0);
// Start reorder on tab to the right of groups. 2 margins to left of tab, so should scroll.
// Verify the scroll offset is 2 * (-marginWidth) + startMargin = 2 * -54 + -54 = -162
// marginWidth is half of 0.5 * minTabWidth = 108 / 2 = 54.
float expectedOffset = -162f;
mStripLayoutHelper.startReorderModeAtIndexForTesting(4);
assertEquals(
"There are margins left of the selected tab, so we should scroll.",
expectedOffset,
mStripLayoutHelper.getScrollOffset(),
EPSILON);
// Stop reorder. Verify the scroll offset is once again 0.
mStripLayoutHelper.stopReorderModeForTesting();
assertEquals(
"Scroll offset should return to 0 after stopping reorder mode.",
0,
mStripLayoutHelper.getScrollOffset(),
EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupMargins_ScrollOnReorder_Animated() {
// Mock 6 tabs to the right of 2 tab groups with 2 tabs each.
initializeTest(false, false, false, 0, 10);
groupTabs(0, 2);
groupTabs(2, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.setScrollOffsetForTesting(0);
// Start reorder on tab to the right of groups. 2 margins to left of tab, so should scroll.
// Verify the scroll offset has not yet changed.
mStripLayoutHelper.startReorderModeAtIndexForTesting(4);
assertEquals(
"The scroller has not finished yet, so the offset shouldn't change.",
0f,
mStripLayoutHelper.getScrollOffset(),
EPSILON);
// Finish animations.
// Verify the scroll offset is 2 * (-marginWidth) + startMargin = 2 * -54 + -54 = -162
// marginWidth is half of 0.5 * minTabWidth = 108 / 2 = 54.
float expectedOffset = -162f;
mStripLayoutHelper.getRunningAnimatorForTesting().end();
assertEquals(
"The scroller has finished, so the offset should change.",
expectedOffset,
mStripLayoutHelper.getScrollOffset(),
EPSILON);
// Stop reorder. Verify the scroll offset is still -285.
mStripLayoutHelper.stopReorderModeForTesting();
assertEquals(
"The scroller has not finished yet, so the offset shouldn't change.",
expectedOffset,
mStripLayoutHelper.getScrollOffset(),
EPSILON);
// Finish animations.
// Verify the scroll offset is once again 0.
mStripLayoutHelper.getRunningAnimatorForTesting().end();
assertEquals(
"The scroller has finished, so the offset should change.",
0,
mStripLayoutHelper.getScrollOffset(),
EPSILON);
}
@Test
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabOutline_SelectedTabInGroup_NotShow() {
// Initialize 5 tabs and make 2 tab groups each containing 2 tabs.
initializeTest(false, false, false, 0, 5);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
groupTabs(0, 2);
groupTabs(2, 4);
// Test tab outline should not show for selected tab in group.
assertFalse(
"Tab outline should show for selected tab in group",
mStripLayoutHelper.shouldShowTabOutline(tabs[0]));
// Test tab outline should not show for the rest of tabs.
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[1]));
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[2]));
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[3]));
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[4]));
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabOutline_SelectedTabInGroup_Show() {
// Initiailze 5 tabs and make 2 tab groups each containing 2 tabs.
initializeTest(false, false, false, 0, 5);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
groupTabs(0, 2);
groupTabs(2, 4);
// Test tab outline should show for selected tab in group.
assertTrue(
"Tab outline should show for selected tab in group",
mStripLayoutHelper.shouldShowTabOutline(tabs[0]));
// Test tab outline should not show for the rest of tabs.
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[1]));
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[2]));
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[3]));
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[4]));
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_DRAG_DROP_ANDROID
})
public void testTabOutline_ForegroundedTabInGroup_TabDroppedOntoDestinationStrip_Show() {
// Setup with 3 tabs and select the first tab.
initializeTest(false, false, true, 0, 3);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH_LANDSCAPE,
SCREEN_HEIGHT,
false,
TIMESTAMP,
PADDING_LEFT,
PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
groupTabs(0, 3);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Start reorder for tab drop between the 2nd and 3rd tab.
mStripLayoutHelper.updateStripForExternalTabDrop(300.f);
// Test tab outline should show for the foregrounded tab in destination window during tab
// drop.
assertTrue("Tab outline should show.", mStripLayoutHelper.shouldShowTabOutline(tabs[0]));
// Test tab outline should not show for the rest of tabs.
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[1]));
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[2]));
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabOutline_ReorderMode_NotShow() {
// Mock 5 tabs and make 2 tab groups each containing 2 tabs.
initializeTest(false, false, false, 0, 5);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1, 150f, 5);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
groupTabs(0, 2);
groupTabs(2, 4);
// Enter reorder mode.
mStripLayoutHelper.setInReorderModeForTesting(true);
// Test tab outline should not show for selected tab in group when enter reorder mode.
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[0]));
// Test tab outline should not show for the rest of tabs.
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[1]));
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[2]));
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[3]));
assertFalse(
"Tab outline should not show.", mStripLayoutHelper.shouldShowTabOutline(tabs[4]));
}
@Test
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testReorder_SetSelectedTabGroupContainersVisible() {
// Mock 5 tabs. Group the first two tabs.
initializeTest(false, false, true, 2, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(0, 2);
// Start reorder mode on third tab. Drag to hover over the tab group.
// -100 < -marginWidth = -95
mStripLayoutHelper.startReorderModeAtIndexForTesting(2);
float dragDistance = -100f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify hovered group tab containers are visible.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
float expectedHidden = StripLayoutHelper.TAB_OPACITY_HIDDEN;
float expectedVisibleBackground = StripLayoutHelper.TAB_OPACITY_VISIBLE_BACKGROUND;
float expectedVisibleForeground = StripLayoutHelper.TAB_OPACITY_VISIBLE_FOREGROUND;
assertEquals(
"Container in hovered group should be visible.",
expectedVisibleBackground,
tabs[0].getContainerOpacity(),
EPSILON);
assertEquals(
"Container in hovered group should be visible.",
expectedVisibleBackground,
tabs[1].getContainerOpacity(),
EPSILON);
assertEquals(
"Selected container should be visible.",
expectedVisibleForeground,
tabs[2].getContainerOpacity(),
EPSILON);
assertEquals(
"Background containers should not be visible.",
expectedHidden,
tabs[3].getContainerOpacity(),
EPSILON);
assertEquals(
"Background containers should not be visible.",
expectedHidden,
tabs[4].getContainerOpacity(),
EPSILON);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testReorder_SetSelectedTabGroupContainersVisible_TabGroupIndicators() {
// Mock 5 tabs. Group the first two tabs.
initializeTest(false, false, true, 2, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(0, 2);
// Start reorder mode on third tab. Drag to hover over the tab group.
// -100 < -marginWidth = -95
mStripLayoutHelper.startReorderModeAtIndexForTesting(2);
float dragDistance = -100f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify hovered group tab containers are not visible for Tab Group Indicator.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
float expectedHidden = StripLayoutHelper.TAB_OPACITY_HIDDEN;
float expectedVisibleForeground = StripLayoutHelper.TAB_OPACITY_VISIBLE_FOREGROUND;
assertEquals(
"Container in hovered group should not be visible.",
expectedHidden,
tabs[0].getContainerOpacity(),
EPSILON);
assertEquals(
"Container in hovered group should not be visible.",
expectedHidden,
tabs[1].getContainerOpacity(),
EPSILON);
assertEquals(
"Selected container should be visible.",
expectedVisibleForeground,
tabs[2].getContainerOpacity(),
EPSILON);
assertEquals(
"Background containers should not be visible.",
expectedHidden,
tabs[3].getContainerOpacity(),
EPSILON);
assertEquals(
"Background containers should not be visible.",
expectedHidden,
tabs[4].getContainerOpacity(),
EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
public void testReorder_HapticFeedback() {
// Mock 5 tabs.
initializeTest(false, false, 0);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Start reorder mode on first tab.
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
// Verify we performed haptic feedback for a long-press.
verify(mInteractingTabView).performHapticFeedback(eq(HapticFeedbackConstants.LONG_PRESS));
}
@Test
@Feature("Tab Groups on Tab Strip")
public void testReorder_NoGroups() {
// Mock 5 tabs.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
StripLayoutTab thirdTab = tabs[2];
StripLayoutTab fourthTab = tabs[3];
// Start reorder on third tab. Drag right to trigger swap with fourth tab.
// 100 > tabWidth * flipThreshold = (190-24) * 0.53 = 88
mStripLayoutHelper.startReorderModeAtIndexForTesting(2);
float dragDistance = 100f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Assert the tabs swapped.
assertEquals("Third and fourth tabs should have swapped.", thirdTab, tabs[3]);
assertEquals("Third and fourth tabs should have swapped.", fourthTab, tabs[2]);
}
@Test
@Feature("Tab Groups on Tab Strip")
public void testReorder_DragOutOfGroup() {
// Mock a tab group with 3 tabs with 1 tab to the left and 1 tab to the right.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
StripLayoutTab fourthTab = tabs[3];
groupTabs(1, 4);
// Start reorder on fourth tab. Drag right out of the tab group.
// 60 > marginWidth * flipThreshold = 95 * 0.53 = 51
mStripLayoutHelper.startReorderModeAtIndexForTesting(3);
float dragDistance = 60f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify fourth tab was dragged out of group, but not reordered.
assertEquals("Fourth tab should not have moved.", fourthTab, tabs[3]);
verify(mTabGroupModelFilter).moveTabOutOfGroupInDirection(fourthTab.getTabId(), true);
}
@Test
@Feature("Tab Groups on Tab Strip")
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testReorder_DragOutOfGroup_StartOfStrip() {
// Mock a tab group with 3 tabs with 2 tabs to the right.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
StripLayoutTab firstTab = tabs[0];
groupTabs(0, 3);
// Start reorder on first tab. Drag left out of the tab group.
// -60 < -(marginWidth * flipThreshold) = -(95 * 0.53) = -51
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
float dragDistance = -60f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify first tab was dragged out of group, but not reordered.
assertEquals("First tab should not have moved.", firstTab, tabs[0]);
verify(mTabGroupModelFilter).moveTabOutOfGroupInDirection(firstTab.getTabId(), false);
}
@Test
@Feature("Tab Groups on Tab Strip")
public void testReorder_DragOutOfGroup_EndOfStrip() {
// Mock a tab group with 3 tabs with 2 tabs to the left.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
StripLayoutTab fifthTab = tabs[4];
groupTabs(2, 5);
// Start reorder on fifth tab. Drag right out of the tab group.
// 60 > marginWidth * flipThreshold = 95 * 0.53 = 51
mStripLayoutHelper.startReorderModeAtIndexForTesting(4);
float dragDistance = 60f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify fifth tab was dragged out of group, but not reordered.
assertEquals("Fifth tab should not have moved.", fifthTab, tabs[4]);
verify(mTabGroupModelFilter).moveTabOutOfGroupInDirection(fifthTab.getTabId(), true);
}
@Test
@Feature("Tab Groups on Tab Strip")
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testReorder_DragPastGroup() {
// Mock a tab group with 3 tabs with 1 tab to the left and 1 tab to the right.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
StripLayoutTab firstTab = tabs[0];
groupTabs(1, 4);
// Start reorder on first tab. Drag right over the tab group.
// 650 > 3*tabWidth + margin + flipThreshold*margin = 3*(190-24) + 1.53*95 = 644 > 300
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
float dragDistance = 300f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify no reordering, since we have not hovered over the tab group long enough.
assertEquals("First tab should not have moved.", firstTab, tabs[0]);
// Drag past the tab group.
dragDistance = 650f;
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify reordering, since we have dragged past the tab group.
assertEquals(
"First tab should now be the fourth tab.", firstTab.getTabId(), tabs[3].getTabId());
}
@Test
@Feature("Tab Groups on Tab Strip")
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testReorder_MergeToGroup() {
// Mock 5 tabs. Group the first two tabs.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
StripLayoutTab thirdTab = tabs[2];
int oldSecondTabId = tabs[1].getTabId();
groupTabs(0, 2);
// Start reorder mode on third tab. Drag between tabs in group.
// -300 < -(tabWidth + marginWidth) = -(190 + 95) = -285
mStripLayoutHelper.startReorderModeAtIndexForTesting(2);
float dragDistance = -200f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify state has not yet changed.
tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertEquals("Third tab should not have moved.", thirdTab, tabs[2]);
verify(mTabGroupModelFilter, never()).mergeTabsToGroup(anyInt(), anyInt());
verify(mTabGroupModelFilter, never()).mergeTabsToGroup(anyInt(), anyInt(), anyBoolean());
// Wait minimum time to trigger merge.
// -10 > -(dropMaxDragOffset) = -36
dragDistance = -10;
startX = mStripLayoutHelper.getLastReorderXForTesting();
long timeDelta = StripLayoutHelper.DROP_INTO_GROUP_MS;
mStripLayoutHelper.drag(TIMESTAMP + timeDelta, startX + dragDistance, 0f, dragDistance);
// Verify interacting tab was merged into group at the second index.
tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertEquals("Third tab should now be second tab.", thirdTab, tabs[1]);
verify(mTabGroupModelFilter)
.mergeTabsToGroup(eq(thirdTab.getTabId()), eq(oldSecondTabId), eq(true));
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testReorder_MergeToGroup_TabGroupIndicators() {
// Mock 5 tabs. Group the first two tabs.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
StripLayoutTab thirdTab = tabs[2];
int oldSecondTabId = tabs[1].getTabId();
groupTabs(0, 2);
// Start reorder mode on third tab. Drag between tabs in group.
// -300 < -(tabWidth + marginWidth) = -(190 + 95) = -285
mStripLayoutHelper.startReorderModeAtIndexForTesting(2);
float dragDistance = -200f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify interacting tab was merged into group.
verify(mTabGroupModelFilter)
.mergeTabsToGroup(eq(thirdTab.getTabId()), eq(oldSecondTabId), eq(true));
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testReorder_MovePastCollapsedGroup_TabGroupIndicators() {
// Mock 5 tabs. Group the second and third tabs.
initializeTest(false, false, true, 3, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(1, 3);
// Collapse the group.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[1], true);
// Start reorder mode on fourth tab. Drag past the collapsed group.
// -50 < -groupTitleWidth(46)
mStripLayoutHelper.startReorderModeAtIndexForTesting(3);
StripLayoutView draggedTab = views[4];
assertEquals(
"Should be dragging the fourth tab.",
draggedTab,
mStripLayoutHelper.getInteractingTabForTesting());
float dragDistance = -50f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify interacting tab was moved past the collapsed group and is now the second tab.
assertEquals("Dragged tab should now be second tab.", draggedTab, views[1]);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testBottomIndicatorWidth_MergeToGroup_TabGroupIndicators() {
// Mock 5 tabs. Group the first two tabs.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
StripLayoutTab thirdTab = tabs[2];
groupTabs(0, 2);
int oldSecondTabId = tabs[1].getTabId();
// Assert: first view should be group title.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[0] instanceof StripLayoutGroupTitle);
StripLayoutGroupTitle groupTitle = ((StripLayoutGroupTitle) views[0]);
// Calculate tab and bottom indicator width.
float tabWidth = views[1].getWidth();
float expectedStartWidth = calculateExpectedBottomIndicatorWidth(tabWidth, 2, groupTitle);
float expectedEndWidth = calculateExpectedBottomIndicatorWidth(tabWidth, 3, groupTitle);
float expectedThreshold = mStripLayoutHelper.calculateTabGroupThreshold(2, false, false);
// Assert: bottom indicator start width.
assertEquals(
"Bottom indicator start width is incorrect",
expectedStartWidth,
groupTitle.getBottomIndicatorWidth(),
EPSILON);
// Start reorder mode on third tab. Drag between tabs in group.
// -300 < -(tabWidth + marginWidth) = -(190 + 95) = -285
mStripLayoutHelper.startReorderModeAtIndexForTesting(2);
float dragDistance = -200f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify interacting tab was merged into group.
verify(mTabGroupModelFilter)
.mergeTabsToGroup(eq(thirdTab.getTabId()), eq(oldSecondTabId), eq(true));
mStripLayoutHelper.maybeMergeToGroupForTabGroupIndicators(
-expectedThreshold - 1, 2, false, expectedThreshold, groupTitle);
assertEquals(
"Bottom indicator end width is incorrect",
expectedEndWidth,
(groupTitle).getBottomIndicatorWidth(),
EPSILON);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testBottomIndicatorWidth_DragOutOfGroup_TabGroupIndicators() {
// Mock 5 tabs. Group the first two tabs.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
StripLayoutTab thirdTab = tabs[2];
groupTabs(0, 3);
// Assert: first view should be group title.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[0] instanceof StripLayoutGroupTitle);
StripLayoutGroupTitle groupTitle = ((StripLayoutGroupTitle) views[0]);
// Calculate tab and bottom indicator width.
float tabWidth = views[1].getWidth();
float expectedStartWidth = calculateExpectedBottomIndicatorWidth(tabWidth, 3, groupTitle);
float expectedEndWidth = calculateExpectedBottomIndicatorWidth(tabWidth, 2, groupTitle);
// Assert: bottom indicator start width.
assertEquals(
"Bottom indicator start width is incorrect",
expectedStartWidth,
groupTitle.getBottomIndicatorWidth(),
EPSILON);
// Start reorder on fifth tab. Drag right out of the tab group.
// 60 > marginWidth * flipThreshold = 95 * 0.53 = 51
mStripLayoutHelper.startReorderModeAtIndexForTesting(2);
float dragDistance = 60f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify third tab was dragged out of group.
verify(mTabGroupModelFilter).moveTabOutOfGroupInDirection(thirdTab.getTabId(), true);
// Act: End the animations to apply final values.
Animator runningAnimator = mStripLayoutHelper.getRunningAnimatorForTesting();
runningAnimator.end();
assertEquals(
"Bottom indicator end width is incorrect",
expectedEndWidth,
(groupTitle).getBottomIndicatorWidth(),
EPSILON);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testBottomIndicatorWidthAfterTabResize_UngroupedTabClosed_TabGroupIndicators() {
// Arrange
int tabCount = 6;
initializeTest(false, false, false, 3, tabCount);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
groupTabs(0, 2);
// Assert: first view should be group title.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[0] instanceof StripLayoutGroupTitle);
StripLayoutGroupTitle groupTitle = ((StripLayoutGroupTitle) views[0]);
// Update layout and set up animation.
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
setupForAnimations();
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Check initial bottom indicator width.
float expectedStartWidth =
calculateExpectedBottomIndicatorWidth(
mStripLayoutHelper.getCachedTabWidthForTesting(), 2, groupTitle);
assertEquals(
"Unexpected bottom indicator width before resize.",
expectedStartWidth,
(groupTitle).getBottomIndicatorWidth(),
0.1f);
// Act: Call on close tab button handler.
mStripLayoutHelper.handleCloseButtonClick(tabs[2], TIMESTAMP);
// Assert: Animations started.
assertTrue(
"MultiStepAnimations should have started.",
mStripLayoutHelper.isMultiStepCloseAnimationsRunningForTesting());
// Assert: Animations are still running.
assertTrue(
"MultiStepAnimations should still be running.",
mStripLayoutHelper.isMultiStepCloseAnimationsRunningForTesting());
// Act: Set animation time forward by 250ms for next set of animations.
mStripLayoutHelper.getRunningAnimatorForTesting().end();
// Act: End the animations to apply final values.
Animator runningAnimator = mStripLayoutHelper.getRunningAnimatorForTesting();
runningAnimator.end();
// availableSize = width(800) - NTB(32) - endPadding(8) - offsetXLeft(10) - offsetXRight(20)
// - groupTitleWidth(46) - titleOverlapWidth(4) = 680.
// ExpectedWidth = (availableSize(680) + 4 * overlap(28)) / 5 = 160
float expectedWidthAfterResize = 160.f;
StripLayoutTab[] updatedTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
for (int i = 0; i < updatedTabs.length; i++) {
StripLayoutTab stripTab = updatedTabs[i];
assertEquals(
"Unexpected tab width after resize.",
expectedWidthAfterResize,
stripTab.getWidth(),
0.1f);
}
assertFalse(
"MultiStepAnimations should have ended.",
mStripLayoutHelper.isMultiStepCloseAnimationsRunningForTesting());
// Check bottom indicator end width.
float expectedEndWidth =
calculateExpectedBottomIndicatorWidth(expectedWidthAfterResize, 2, groupTitle);
assertEquals(
"Unexpected bottom indicator width after resize.",
expectedEndWidth,
groupTitle.getBottomIndicatorWidth(),
0.1f);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testBottomIndicatorWidthAfterTabResize_GroupedTabClosed_TabGroupIndicators() {
// Arrange
int tabCount = 6;
initializeTest(false, false, true, 0, tabCount);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
groupTabs(0, 2);
// Assert: first view should be a GroupTitle.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[0] instanceof StripLayoutGroupTitle);
StripLayoutGroupTitle groupTitle = ((StripLayoutGroupTitle) views[0]);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Check initial bottom indicator width.
float expectedStartWidth =
calculateExpectedBottomIndicatorWidth(
mStripLayoutHelper.getCachedTabWidthForTesting(), 2, groupTitle);
assertEquals(
"Unexpected bottom indicator width before resize.",
expectedStartWidth,
groupTitle.getBottomIndicatorWidth(),
0.1f);
setupForAnimations();
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Act: Close tab and remove from group.
mStripLayoutHelper.handleCloseButtonClick(tabs[0], TIMESTAMP);
when(mTabGroupModelFilter.getRelatedTabCountForRootId(eq(0))).thenReturn(1);
mStripLayoutHelper.finishAnimationsAndPushTabUpdates();
// availableSize = width(800) - NTB(32) - endPadding(8) - offsetXLeft(10) - offsetXRight(20)
// - groupTitleWidth(46) - titleOverlapWidth(4) = 680
// ExpectedWidth = (availableSize(680) + 4 * overlap(28)) / 5 = 160
float openTabWidth = 160.f;
StripLayoutTab[] updatedTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
for (int i = 0; i < updatedTabs.length; i++) {
StripLayoutTab stripTab = updatedTabs[i];
float expectedWidth = stripTab.isClosed() ? TAB_OVERLAP_WIDTH : openTabWidth;
assertEquals(
"Unexpected tab width after resize.", expectedWidth, stripTab.getWidth(), 0.1f);
}
// Check bottom indicator end width.
float expectedEndWidth = calculateExpectedBottomIndicatorWidth(openTabWidth, 1, groupTitle);
assertEquals(
"Unexpected bottom indicator width after resize.",
expectedEndWidth,
groupTitle.getBottomIndicatorWidth(),
0.1f);
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testBottomIndicatorWidth_CollapseAndExpand() {
// Mock 5 tabs, group first 3 tabs as group1 and group the rest as group2.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
groupTabs(0, 3);
groupTabs(3, 5);
// Assert: the first and fourth view should be group title.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[0] instanceof StripLayoutGroupTitle);
assertTrue(EXPECTED_TITLE, views[4] instanceof StripLayoutGroupTitle);
StripLayoutGroupTitle groupTitle1 = ((StripLayoutGroupTitle) views[0]);
StripLayoutGroupTitle groupTitle2 = ((StripLayoutGroupTitle) views[4]);
// Calculate tab and bottom indicator initial width.
float initialTabWidth = tabs[0].getWidth();
float expectedStartWidth1 =
calculateExpectedBottomIndicatorWidth(initialTabWidth, 3, groupTitle1);
float expectedStartWidth2 =
calculateExpectedBottomIndicatorWidth(initialTabWidth, 2, groupTitle1);
// Assert: bottom indicator start width as usual.
assertEquals(
"Group 1 bottom indicator start width is incorrect",
expectedStartWidth1,
groupTitle1.getBottomIndicatorWidth(),
EPSILON);
assertEquals(
"Group 2 bottom indicator start width is incorrect",
expectedStartWidth2,
groupTitle2.getBottomIndicatorWidth(),
EPSILON);
// Click to collapse the first tab group.
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[0], true);
// Assert: check bottom indicator end width for the 1st tab group should be 0.
assertEquals(
"Bottom indicator end width is incorrect",
0.f,
groupTitle1.getBottomIndicatorWidth(),
EPSILON);
// Assert: check bottom indicator end width for the 2nd tab group should been adjusted to
// match the new tab width after collapse, since there are only 2 active tabs on strip, tab
// width should become the max width.
assertEquals(
"Bottom indicator end width is incorrect",
calculateExpectedBottomIndicatorWidth(265.f, 2, groupTitle2),
groupTitle2.getBottomIndicatorWidth(),
EPSILON);
// Click to expand the first tab group.
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[0], false);
// Assert: check bottom indicator end width for the 1st tab group has been expanded to the
// initial length.
assertEquals(
"Bottom indicator end width is incorrect",
expectedStartWidth1,
groupTitle1.getBottomIndicatorWidth(),
EPSILON);
// Assert: check bottom indicator end width for the 2st tab group has been adjusted to the
// initial length.
assertEquals(
"Bottom indicator end width is incorrect",
expectedStartWidth2,
groupTitle2.getBottomIndicatorWidth(),
EPSILON);
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_DRAG_DROP_ANDROID
})
public void testBottomIndicatorWidth_TabHoveredOntoTabGroup_TabGroupIndicators() {
// Arrange
int tabCount = 6;
initializeTest(false, false, false, 0, tabCount);
groupTabs(0, 2);
mStripLayoutHelper.disableAnimationsForTesting();
// Assert: first view should be a GroupTitle.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[0] instanceof StripLayoutGroupTitle);
StripLayoutGroupTitle groupTitle = ((StripLayoutGroupTitle) views[0]);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Check initial bottom indicator width.
float expectedStartWidth =
calculateExpectedBottomIndicatorWidth(
mStripLayoutHelper.getCachedTabWidthForTesting(), 2, groupTitle);
assertEquals(
"Unexpected bottom indicator width before tab hover.",
expectedStartWidth,
groupTitle.getBottomIndicatorWidth(),
0.1f);
setupForAnimations();
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Start reorder for tab drop between the 1st and 2nd tab.
mStripLayoutHelper.updateStripForExternalTabDrop(150.f);
float expectedEndWidth =
expectedStartWidth + mStripLayoutHelper.getCachedTabWidthForTesting() / 2;
assertEquals(
"Unexpected bottom indicator width after tab hover.",
expectedEndWidth,
groupTitle.getBottomIndicatorWidth(),
0.5f);
}
@Test
public void testFolioAttached_ReattachAnimationSkipped() {
// Arrange
int tabCount = 6;
initializeTest(false, false, false, 0, tabCount);
groupTabs(0, 2);
StripLayoutHelper stripLayoutHelperSpy = spy(mStripLayoutHelper);
// Start and stop reorder mode for tab drop.
stripLayoutHelperSpy.updateStripForExternalTabDrop(10.f);
stripLayoutHelperSpy.stopReorderModeForTesting();
// Verify: folio reattachment animation does not run for tab drop.
verify(stripLayoutHelperSpy, never()).updateTabAttachState(any(), eq(true), notNull());
}
private float calculateExpectedBottomIndicatorWidth(
float tabWidth, float tabCount, StripLayoutGroupTitle groupTitle) {
// (tabWidth - tabOverlap(28.f)) * tabCount + groupTitleWidth -
// bottomIndicatorWidthOffset(27.f).
return (tabWidth - TAB_OVERLAP_WIDTH) * tabCount
+ groupTitle.getWidth()
- StripLayoutHelper.TAB_GROUP_BOTTOM_INDICATOR_WIDTH_OFFSET;
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testGroupTitleSlidingAnimation_MergeToGroup_TabGroupIndicators() {
// Mock 5 tabs. Group the first two tabs.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
groupTabs(1, 3);
int firstTabId = tabs[0].getTabId();
int secondTabId = tabs[1].getTabId();
// Assert: first view should be group title.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[1] instanceof StripLayoutGroupTitle);
StripLayoutGroupTitle groupTitle = ((StripLayoutGroupTitle) views[1]);
// Start reorder mode on first tab. Drag between tabs in group.
// 70 = (80(halfTabWidth) - 28(tabOverlapWidth)) * 0.53(ReorderOverlapSwitchPercentage).
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
float dragDistance = 70f;
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify interacting tab was merged into group.
verify(mTabGroupModelFilter).mergeTabsToGroup(eq(firstTabId), eq(secondTabId), eq(true));
// assert: verify group title sliding animation is running immediately when tab merge into
// group through group title.
assertTrue(mStripLayoutHelper.getGroupTitleSlidingForTesting());
// Assert: verify bottom indicator width correctly updated.
float expectedEndWidth =
calculateExpectedBottomIndicatorWidth(tabs[0].getWidth(), 2, groupTitle);
assertEquals(
"Bottom indicator end width is incorrect",
expectedEndWidth,
(groupTitle).getBottomIndicatorWidth(),
EPSILON);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testGroupTitleSlidingAnimation_dragOutOfGroup_TabGroupIndicators() {
// Mock 5 tabs. Group the first two tabs.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
groupTabs(1, 3);
int secondTabId = tabs[1].getTabId();
// Assert: first view should be group title.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[1] instanceof StripLayoutGroupTitle);
StripLayoutGroupTitle groupTitle = ((StripLayoutGroupTitle) views[1]);
// Start reorder mode on first tab. Drag between tabs in group.
// 38 = ((80(halfTabWidth) - 28(tabOverlapWidth)) * 0.53(ReorderOverlapSwitchPercentage)) *
// 0.53.
mStripLayoutHelper.startReorderModeAtIndexForTesting(1);
float dragDistance = -38f - groupTitle.getWidth();
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
// Verify interacting tab was moved out of group.
verify(mTabGroupModelFilter).moveTabOutOfGroupInDirection(secondTabId, false);
// assert: verify group title sliding animation is running immediately when tab move out of
// group through group title.
assertTrue(mStripLayoutHelper.getGroupTitleSlidingForTesting());
// Assert: verify bottom indicator width correctly updated.
float expectedEndWidth =
calculateExpectedBottomIndicatorWidth(tabs[0].getWidth(), 2, groupTitle);
assertEquals(
"Bottom indicator end width is incorrect",
expectedEndWidth,
(groupTitle).getBottomIndicatorWidth(),
EPSILON);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupDeleteDialog_ImmediateContinue() {
// Set up resources for testing tab group delete dialog.
setUpTabGroupAndDialog(0, 1, true);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Start dragging tab out of group.
startDraggingTab(tabs, false, 0);
// Verify action confirmation dialog triggers.
verify(mActionConfirmationManager)
.processRemoveTabAttempt(mConfirmationResultCaptor.capture());
mConfirmationResultCaptor.getValue().onResult(ConfirmationResult.IMMEDIATE_CONTINUE);
// Verify tab is moved out of group as user chooses delete tab group without showing the
// dialog.
verify(mTabGroupModelFilter).moveTabOutOfGroupInDirection(tabs[0].getTabId(), true);
// Verify group title is removed from the tab strip
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertFalse(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupDeleteDialog_ConfirmationPositive() {
// Set up resources for testing tab group delete dialog.
setUpTabGroupAndDialog(0, 1, false);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Start dragging tab out of group.
startDraggingTab(tabs, false, 0);
// Verify group title is temporarily disappeared from the tab strip
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertFalse(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
// Verify action confirmation dialog shows.
verify(mActionConfirmationManager)
.processRemoveTabAttempt(mConfirmationResultCaptor.capture());
mConfirmationResultCaptor.getValue().onResult(ConfirmationResult.CONFIRMATION_POSITIVE);
// Verify tab is moved out of group as user confirms tab group delete.
verify(mTabGroupModelFilter).moveTabOutOfGroupInDirection(tabs[0].getTabId(), true);
// Verify group title is removed from the tab strip
views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertFalse(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupDeleteDialog_ConfirmationNegative() {
// Set up resources for testing tab group delete dialog.
setUpTabGroupAndDialog(0, 1, false);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Start dragging tab out of group.
startDraggingTab(tabs, false, 0);
// Verify group title is temporarily disappeared from the tab strip
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertFalse(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
// Verify action confirmation dialog shows.
verify(mActionConfirmationManager)
.processRemoveTabAttempt(mConfirmationResultCaptor.capture());
mConfirmationResultCaptor.getValue().onResult(ConfirmationResult.CONFIRMATION_NEGATIVE);
// Verify tab is not moved out of group as user cancels tab group delete.
verify(mTabGroupModelFilter, never())
.moveTabOutOfGroupInDirection(tabs[0].getTabId(), true);
// Verify group title is restored back on the tab strip
views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[0] instanceof StripLayoutGroupTitle);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupDeleteDialog_DragTabOffStrip_ImmediateContinue() {
// Set up resources for testing tab group delete dialog.
setUpTabGroupAndDialog(0, 1, true);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Start dragging tab out of group.
startDraggingTab(tabs, true, 0);
// Verify action confirmation dialog triggers.
verify(mActionConfirmationManager)
.processRemoveTabAttempt(mConfirmationResultCaptor.capture());
mConfirmationResultCaptor.getValue().onResult(ConfirmationResult.IMMEDIATE_CONTINUE);
// Verify tab is not moved out of group for unhandled drops.
verify(mTabGroupModelFilter, never())
.moveTabOutOfGroupInDirection(tabs[0].getTabId(), false);
// Assume the drop is unsuccessful; the tab and the tab group will be restored to its
// original position.
mStripLayoutHelper.clearTabDragState();
// Verify group title is restored back on the tab strip.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupDeleteDialog_DragTabOffStrip_ConfirmationPositive() {
// Set up resources for testing tab group delete dialog.
setUpTabGroupAndDialog(0, 1, false);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Start dragging tab out of group.
startDraggingTab(tabs, true, 0);
// Verify group title is temporarily disappeared from the tab strip
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertFalse(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
// Verify action confirmation dialog shows.
verify(mActionConfirmationManager)
.processRemoveTabAttempt(mConfirmationResultCaptor.capture());
mConfirmationResultCaptor.getValue().onResult(ConfirmationResult.CONFIRMATION_POSITIVE);
// Verify tab is moved out of group as user confirms tab group delete.
verify(mTabGroupModelFilter).moveTabOutOfGroupInDirection(tabs[0].getTabId(), false);
// Verify group title is removed from the tab strip
views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertFalse(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupDeleteDialog_DragTabOffStrip_ConfirmationNegative() {
// Set up resources for testing tab group delete dialog.
setUpTabGroupAndDialog(0, 1, false);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Start dragging tab out of group.
startDraggingTab(tabs, true, 0);
// Verify group title is temporarily disappeared from the tab strip
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertFalse(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
// Verify action confirmation dialog shows.
verify(mActionConfirmationManager)
.processRemoveTabAttempt(mConfirmationResultCaptor.capture());
mConfirmationResultCaptor.getValue().onResult(ConfirmationResult.CONFIRMATION_NEGATIVE);
// Verify tab is not moved out of group as user cancels tab group delete.
verify(mTabGroupModelFilter, never())
.moveTabOutOfGroupInDirection(tabs[0].getTabId(), true);
// Verify group title is restored back on the tab strip
views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[0] instanceof StripLayoutGroupTitle);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupDeleteDialog_CloseTab_ImmediateContinue() {
// Set up resources for testing tab group delete dialog.
setUpTabGroupAndDialog(0, 1, true);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Close the first tab
mStripLayoutHelper.handleCloseButtonClick(tabs[0], TIMESTAMP);
// Verify action confirmation dialog shows.
verify(mActionConfirmationManager)
.processCloseTabAttempt(mConfirmationResultCaptor.capture());
mConfirmationResultCaptor.getValue().onResult(ConfirmationResult.IMMEDIATE_CONTINUE);
// Assert tab is being closed.
assertTrue("Tab should be closing", tabs[0].isDying());
// Verify group title is removed from the tab strip
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertFalse(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupDeleteDialog_CloseTab_ConfirmationPositive() {
// Set up resources for testing tab group delete dialog.
setUpTabGroupAndDialog(0, 1, false);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Close the first tab
mStripLayoutHelper.handleCloseButtonClick(tabs[0], TIMESTAMP);
// Verify group title is temporarily disappeared from the tab strip
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertFalse(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
// Verify action confirmation dialog triggered.
verify(mActionConfirmationManager)
.processCloseTabAttempt(mConfirmationResultCaptor.capture());
mConfirmationResultCaptor.getValue().onResult(ConfirmationResult.CONFIRMATION_POSITIVE);
// Assert tab is being closed.
assertTrue("Tab should be closing", tabs[0].isDying());
// Verify group title is removed from the tab strip
views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertFalse(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testTabGroupDeleteDialog_CloseTab_ConfirmationNegative() {
// Set up resources for testing tab group delete dialog.
setUpTabGroupAndDialog(0, 1, false);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Close the first tab
mStripLayoutHelper.handleCloseButtonClick(tabs[0], TIMESTAMP);
// Verify group title is temporarily disappeared from the tab strip
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertFalse(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
// Verify action confirmation dialog shows.
verify(mActionConfirmationManager)
.processCloseTabAttempt(mConfirmationResultCaptor.capture());
mConfirmationResultCaptor.getValue().onResult(ConfirmationResult.CONFIRMATION_NEGATIVE);
// Assert tab should not be closed.
assertFalse("Tab should not be closing", tabs[0].isDying());
// Verify group title is restored back to the tab strip
views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_NON_TITLE, views[0] instanceof StripLayoutGroupTitle);
}
private void setUpTabGroupAndDialog(
int groupStartIndex, int groupEndIndex, boolean skipDialog) {
// Mock 5 tabs. Group tab from start to endIndex.
initializeTest(false, false, true, 0, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(groupStartIndex, groupEndIndex);
// Assert: View should be group title.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[groupStartIndex] instanceof StripLayoutGroupTitle);
mStripLayoutHelper.setPrefServiceForTesting(mPrefService);
when(mPrefService.getBoolean(any())).thenReturn(skipDialog);
}
private void startDraggingTab(
StripLayoutTab[] tabs, boolean draggingTabOffStrip, int tabIndexToDrag) {
// Start drag tab out of group or drag off strip.
if (draggingTabOffStrip) {
mStripLayoutHelper.clearForTabDrop(TIMESTAMP, true, false);
} else {
float dragDistance =
(tabs[0].getWidth() - TAB_OVERLAP_WIDTH)
* REORDER_OVERLAP_SWITCH_PERCENTAGE
* REORDER_OVERLAP_SWITCH_PERCENTAGE;
startDragTabOutOfTabGroup(tabIndexToDrag, dragDistance + 1);
}
}
private void startDragTabOutOfTabGroup(int index, float dragDistance) {
// Start reorder and drag tab out of the tab group through group end.
mStripLayoutHelper.startReorderModeAtIndexForTesting(index);
float startX = mStripLayoutHelper.getLastReorderXForTesting();
mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance);
}
@Test
@Feature("Tab Groups on Tab Strip")
public void testReorder_NoExtraMinScroll() {
// Mock 2 tabs so each tab is at its maximum width.
initializeTest(false, false, true, 0, 2);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Start reorder mode on third tab.
mStripLayoutHelper.startReorderModeAtIndexForTesting(1);
// Verify extra scroll offset.
assertEquals(
"Extra min offset should not be set.",
0f,
mStripLayoutHelper.getReorderExtraMinScrollOffsetForTesting(),
EPSILON);
}
@Test
@Feature("Tab Groups on Tab Strip")
public void testReorder_ExtraMinScroll() {
// Mock 2 tabs so each tab is at its maximum width.
initializeTest(false, false, true, 0, 2);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(0, 1);
// Start reorder mode on third tab.
mStripLayoutHelper.startReorderModeAtIndexForTesting(1);
// Verify extra scroll offset.
assertNotEquals(
"Extra min offset should be set.",
0f,
mStripLayoutHelper.getReorderExtraMinScrollOffsetForTesting(),
EPSILON);
}
@Test
public void testTabClosed() {
// Initialize with 10 tabs.
int tabCount = 10;
initializeTest(false, false, false, 0, tabCount);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Remove tab from model and verify that the tab strip has not yet updated.
int closedTabId = 1;
int expectedNumTabs = tabCount;
mModel.closeTabs(TabClosureParams.closeTab(mModel.getTabAt(closedTabId)).build());
assertEquals(
"Tab strip should not yet have changed.",
expectedNumTabs,
mStripLayoutHelper.getStripLayoutTabsForTesting().length);
// Trigger update and verify the tab strip matches the tab model.
expectedNumTabs = 9;
mStripLayoutHelper.tabClosed(TIMESTAMP, closedTabId);
assertEquals(
"Tab strip should match tab model.",
expectedNumTabs,
mStripLayoutHelper.getStripLayoutTabsForTesting().length);
verify(mUpdateHost, times(7)).requestUpdate();
}
@Test
public void testTabClosing_NoTabResize() {
// Arrange
int tabCount = 15;
initializeTest(false, false, false, 14, tabCount);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
setupForAnimations();
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Act: Call on close tab button handler.
mStripLayoutHelper.handleCloseButtonClick(tabs[14], TIMESTAMP);
// Assert: Animations started.
assertTrue(
"MultiStepAnimations should have started.",
mStripLayoutHelper.isMultiStepCloseAnimationsRunningForTesting());
// Act: End the tab closing animations to apply final values.
Animator runningAnimator = mStripLayoutHelper.getRunningAnimatorForTesting();
runningAnimator.end();
// Assert: Tab is closed and animations are still running.
int expectedTabCount = 14;
assertEquals(
"Unexpected tabs count",
expectedTabCount,
mStripLayoutHelper.getStripLayoutTabsForTesting().length);
assertTrue(
"MultiStepAnimations should still be running.",
mStripLayoutHelper.isMultiStepCloseAnimationsRunningForTesting());
// Act: End next set of animations to apply final values.
mStripLayoutHelper.getRunningAnimatorForTesting().end();
// Assert: Animations completed. The tab width is not resized and drawX does not change.
// stripRightBound = width(800) - offsetXRight(20) = 780;
// visibleTabRightBound = rightBound(780)- NTBWidth(32) - endPadding(8) = 740
// lastTabDrawX = visibleTabRightBound(740) - tabWidth(108) = 632
float expectedDrawX = 632.f;
StripLayoutTab[] updatedTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
for (int i = updatedTabs.length - 1; i >= 0; i--) {
StripLayoutTab stripTab = updatedTabs[i];
assertEquals("Unexpected tab width after resize.", 108.f, stripTab.getWidth(), 0);
assertEquals("Unexpected tab position.", expectedDrawX, stripTab.getDrawX(), 0);
expectedDrawX -= TAB_WIDTH_SMALL - TAB_OVERLAP_WIDTH;
}
assertFalse(
"MultiStepAnimations should have stopped running.",
mStripLayoutHelper.isMultiStepCloseAnimationsRunningForTesting());
}
@Test
public void testTabClosing_NonLastTab_TabResize() {
// Arrange
int tabCount = 4;
initializeTest(false, false, false, 3, tabCount);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
setupForAnimations();
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Act: Call on close tab button handler.
mStripLayoutHelper.handleCloseButtonClick(tabs[2], TIMESTAMP);
// Assert: Animations started.
assertTrue(
"MultiStepAnimations should have started.",
mStripLayoutHelper.isMultiStepCloseAnimationsRunningForTesting());
// Act: End the animations to apply final values.
Animator runningAnimator = mStripLayoutHelper.getRunningAnimatorForTesting();
runningAnimator.end();
// Assert: Tab is closed and animations are still running.
int expectedTabCount = 3;
assertEquals(expectedTabCount, mStripLayoutHelper.getStripLayoutTabsForTesting().length);
assertTrue(
"MultiStepAnimations should still be running.",
mStripLayoutHelper.isMultiStepCloseAnimationsRunningForTesting());
// Act: Set animation time forward by 250ms for next set of animations.
mStripLayoutHelper.getRunningAnimatorForTesting().end();
// Assert: Animations completed. The tab width is resized, tab.drawX is changed and
// newTabButton.drawX is also changed.
float expectedDrawX = 10.f; // offsetXLeft(10)
// availableSize = width(800) - NTB(32) - endPadding(8) - offsetXLeft(10) - offsetXRight(20)
// = 730
// ExpectedWidth = (availableSize(730) + 2 * overlap(28)) / 3 = 262
float expectedWidthAfterResize = 262.f;
StripLayoutTab[] updatedTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
for (int i = 0; i < updatedTabs.length; i++) {
StripLayoutTab stripTab = updatedTabs[i];
assertEquals(
"Unexpected tab width after resize.",
expectedWidthAfterResize,
stripTab.getWidth(),
0.1f);
assertEquals("Unexpected tab position.", expectedDrawX, stripTab.getDrawX(), 0.1f);
expectedDrawX += (expectedWidthAfterResize - TAB_OVERLAP_WIDTH);
}
assertFalse(
"MultiStepAnimations should have ended.",
mStripLayoutHelper.isMultiStepCloseAnimationsRunningForTesting());
}
@Test
public void testTabClosingClearsTabHoverState() {
initializeTabHoverTest();
var tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Hover on tabs[2], and close it.
mStripLayoutHelper.updateLastHoveredTab(tabs[2]);
verify(mTabHoverCardView).show(any(), anyBoolean(), anyFloat(), anyFloat(), anyFloat());
mStripLayoutHelper.handleCloseButtonClick(tabs[2], TIMESTAMP);
// End the tab closure animation.
var runningAnimator = mStripLayoutHelper.getRunningAnimatorForTesting();
runningAnimator.end();
verify(mTabHoverCardView).hide();
}
@Test
public void testFlingLeft() {
// Arrange
initializeTest(false, false, false, 11, 12);
// Disable the padding as changing the visible width change the existing expected fling
// distance.
mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, 0, 0);
mStripLayoutHelper.updateLayout(TIMESTAMP);
mStripLayoutHelper.setScrollOffsetForTesting(-150);
// Act: Perform a fling and update layout.
float velocityX = -7000f;
// The velocityX value is used to calculate the scroller.finalX value.
mStripLayoutHelper.fling(TIMESTAMP, 0, 0, velocityX, 0);
// This will use the scroller.finalX value to update the scrollOffset. The timestamp
// value here will determine the fling duration and affects the final offset value.
mStripLayoutHelper.updateLayout(TIMESTAMP + 10);
// Assert: Final scrollOffset.
// The calculation of this value is done using the velocity. The velocity along a friction
// constant is used to calculate deceleration and distance. That together with the animation
// duration determines the final scroll offset position.
float expectedOffset = -220.f;
assertEquals(
"Unexpected scroll offset.",
expectedOffset,
mStripLayoutHelper.getScrollOffset(),
0.0);
}
@Test
public void testFlingRight() {
// Arrange
initializeTest(false, false, false, 10, 11);
// Disable the padding as changing the visible width change the existing expected fling
// distance.
mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, 0, 0);
// When updateLayout is called for the first time, bringSelectedTabToVisibleArea() method is
// invoked. That also affects the scrollOffset value. So we call updateLayout before
// performing a fling so that bringSelectedTabToVisible area isn't called after the fling.
mStripLayoutHelper.updateLayout(TIMESTAMP);
mStripLayoutHelper.setScrollOffsetForTesting(-150);
// Act: Perform a fling and update layout.
float velocity = 5000f;
// The velocityX value is used to calculate the scroller.finalX value.
mStripLayoutHelper.fling(TIMESTAMP, 0, 0, velocity, 0);
// This will use the scroller.finalX value to update the scrollOffset. The timestamp
// value here will determine the fling duration and affects the final offset value.
mStripLayoutHelper.updateLayout(TIMESTAMP + 20);
// Assert: Final scrollOffset.
// The calculation of this value is done using the velocity. The velocity along a friction
// constant is used to calculate deceleration and distance. That together with the animation
// duration determines the final scroll offset position.
float expectedOffset = -48.f;
assertEquals(
"Unexpected scroll offset.",
expectedOffset,
mStripLayoutHelper.getScrollOffset(),
0.0);
}
@Test
public void testDrag_UpdatesScrollOffset_ScrollingStrip() {
// Arrange
initializeTest(false, false, false, 13, 14);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// When updateLayout is called for the first time, bringSelectedTabToVisibleArea() method is
// invoked. That also affects the scrollOffset value. So we call updateLayout before
// performing a fling so that bringSelectedTabToVisible area isn't called after the fling.
mStripLayoutHelper.updateLayout(TIMESTAMP);
mStripLayoutHelper.setScrollOffsetForTesting(-150);
// Act: Drag and update layout.
float dragDeltaX = -200.f;
mStripLayoutHelper.drag(TIMESTAMP, 374.74f, 24.276f, dragDeltaX);
float expectedOffset = -350; // mScrollOffset + dragDeltaX = -200 - 150 = -350
// Assert scroll offset position.
assertEquals(
"Unexpected scroll offset.",
expectedOffset,
mStripLayoutHelper.getScrollOffset(),
0.0);
// Reorder mode is disabled for scrolling strip.
assertFalse(mStripLayoutHelper.getInReorderModeForTesting());
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_STARTUP_REFACTORING)
public void testPlaceholderStripLayout_NoTabModel() {
// Create StripLayoutHelper and mark that after tabs finish restoring, there will be five
// tabs, where the third tab will be the active tab.
mStripLayoutHelper = createStripLayoutHelper(false, false);
mStripLayoutHelper.setTabModelStartupInfo(5, 2, false);
// Verify there are 5 placeholders.
StripLayoutTab[] stripTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertTrue("Tab at position 0 should be a placeholder.", stripTabs[0].getIsPlaceholder());
assertTrue("Tab at position 1 should be a placeholder.", stripTabs[1].getIsPlaceholder());
assertTrue("Tab at position 2 should be a placeholder.", stripTabs[2].getIsPlaceholder());
assertTrue("Tab at position 3 should be a placeholder.", stripTabs[3].getIsPlaceholder());
assertTrue("Tab at position 4 should be a placeholder.", stripTabs[4].getIsPlaceholder());
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_STARTUP_REFACTORING)
public void testPlaceholderStripLayout_PrepareOnSetTabModel() {
// Create StripLayoutHelper and mark that after tabs finish restoring, there will be five
// tabs, where the third tab will be the active tab.
mStripLayoutHelper = createStripLayoutHelper(false, false);
mStripLayoutHelper.setTabModelStartupInfo(5, 2, false);
// Mock a tab model and set it in the StripLayoutHelper.
int expectedActiveTabId = 0;
MockTabModel tabModel = new MockTabModel(mProfile, null);
tabModel.addTab(expectedActiveTabId);
tabModel.setIndex(0, TabSelectionType.FROM_NEW);
tabModel.setActive(true);
mStripLayoutHelper.setTabModel(tabModel, null, false);
// Verify that the real and placeholder strip tabs were generated in the correct indices.
StripLayoutTab[] stripTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertTrue("Tab at position 0 should be a placeholder.", stripTabs[0].getIsPlaceholder());
assertTrue("Tab at position 1 should be a placeholder.", stripTabs[1].getIsPlaceholder());
assertFalse(
"Tab at position 2 should not be a placeholder.", stripTabs[2].getIsPlaceholder());
assertEquals(
"Tab at position 2 should be the same from the mock.",
expectedActiveTabId,
stripTabs[2].getTabId());
assertTrue("Tab at position 3 should be a placeholder.", stripTabs[3].getIsPlaceholder());
assertTrue("Tab at position 4 should be a placeholder.", stripTabs[4].getIsPlaceholder());
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_STARTUP_REFACTORING)
public void testPlaceholderStripLayout_PrepareOnSetTabModelInfo() {
// Create StripLayoutHelper and mock a tab model and set it in the StripLayoutHelper.
int expectedActiveTabId = 0;
MockTabModel tabModel = new MockTabModel(mProfile, null);
tabModel.addTab(expectedActiveTabId);
tabModel.setIndex(0, TabSelectionType.FROM_NEW);
tabModel.setActive(true);
mStripLayoutHelper = createStripLayoutHelper(false, false);
mStripLayoutHelper.setTabModel(tabModel, null, false);
// Verify that there are no placeholders yet.
StripLayoutTab[] stripTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertEquals("There should be no placeholders yet.", 0, stripTabs.length);
// Mark that after tabs finish restoring, there will be five tabs, where the third tab will
// be the active tab.
mStripLayoutHelper.setTabModelStartupInfo(5, 2, false);
// Verify that the real and placeholder strip tabs were generated in the correct indices.
stripTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertTrue("Tab at position 0 should be a placeholder.", stripTabs[0].getIsPlaceholder());
assertTrue("Tab at position 1 should be a placeholder.", stripTabs[1].getIsPlaceholder());
assertFalse(
"Tab at position 2 should not be a placeholder.", stripTabs[2].getIsPlaceholder());
assertEquals(
"Tab at position 2 should be the same from the mock.",
expectedActiveTabId,
stripTabs[2].getTabId());
assertTrue("Tab at position 3 should be a placeholder.", stripTabs[3].getIsPlaceholder());
assertTrue("Tab at position 4 should be a placeholder.", stripTabs[4].getIsPlaceholder());
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_STARTUP_REFACTORING)
public void testPlaceholderStripLayout_TabCreated() {
// Create StripLayoutHelper and mark that after tabs finish restoring, there will be five
// tabs, where the third tab will be the active tab.
mStripLayoutHelper = createStripLayoutHelper(false, false);
mStripLayoutHelper.setTabModelStartupInfo(5, 2, false);
// Mock a tab model and set it in the StripLayoutHelper.
int expectedActiveTabId = 0;
MockTabModel tabModel = new MockTabModel(mProfile, null);
tabModel.addTab(expectedActiveTabId);
tabModel.setIndex(0, TabSelectionType.FROM_NEW);
tabModel.setActive(true);
mStripLayoutHelper.setTabModel(tabModel, null, false);
// Mark that a tab was restored.
int expectedRestoredTabId = 1;
tabModel.addTab(
new MockTab(expectedRestoredTabId, mProfile),
0,
TabLaunchType.FROM_RESTORE,
TabCreationState.FROZEN_ON_RESTORE);
mStripLayoutHelper.tabCreated(
TIMESTAMP, expectedRestoredTabId, Tab.INVALID_TAB_ID, false, false, true);
// Verify that the third (active) and first tab are real.
StripLayoutTab[] stripTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertFalse(
"Tab at position 0 should not be a placeholder.", stripTabs[0].getIsPlaceholder());
assertEquals(
"Tab at position 0 should be the same from the mock.",
expectedRestoredTabId,
stripTabs[0].getTabId());
assertTrue("Tab at position 1 should be a placeholder.", stripTabs[1].getIsPlaceholder());
assertFalse(
"Tab at position 2 should not be a placeholder.", stripTabs[2].getIsPlaceholder());
assertEquals(
"Tab at position 2 should be the same from the mock.",
expectedActiveTabId,
stripTabs[2].getTabId());
assertTrue("Tab at position 3 should be a placeholder.", stripTabs[3].getIsPlaceholder());
assertTrue("Tab at position 4 should be a placeholder.", stripTabs[4].getIsPlaceholder());
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_STARTUP_REFACTORING)
public void testPlaceholderStripLayout_OnTabStateInitialized() {
// Create StripLayoutHelper and mark that after tabs finish restoring, there will be five
// tabs, where the third tab will be the active tab.
mStripLayoutHelper = createStripLayoutHelper(false, false);
mStripLayoutHelper.setTabModelStartupInfo(5, 2, false);
// Mock a tab model and set it in the StripLayoutHelper.
MockTabModel tabModel = new MockTabModel(mProfile, null);
tabModel.setActive(true);
mStripLayoutHelper.setTabModel(tabModel, null, false);
// Verify there are placeholders.
StripLayoutTab[] stripTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertTrue("Tab at position 0 should be a placeholder.", stripTabs[0].getIsPlaceholder());
assertTrue("Tab at position 1 should be a placeholder.", stripTabs[1].getIsPlaceholder());
assertTrue("Tab at position 2 should be a placeholder.", stripTabs[2].getIsPlaceholder());
assertTrue("Tab at position 3 should be a placeholder.", stripTabs[3].getIsPlaceholder());
assertTrue("Tab at position 4 should be a placeholder.", stripTabs[4].getIsPlaceholder());
// Add the remaining tabs and mark that the tab state is finished initializing.
tabModel.addTab(0);
tabModel.addTab(1);
tabModel.addTab(2);
tabModel.addTab(3);
tabModel.addTab(4);
tabModel.setIndex(2, TabSelectionType.FROM_NEW);
mStripLayoutHelper.onTabStateInitialized();
// Verify the placeholders have been replaced.
stripTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertFalse(
"Tab at position 0 should not be a placeholder.", stripTabs[0].getIsPlaceholder());
assertFalse(
"Tab at position 1 should not be a placeholder.", stripTabs[1].getIsPlaceholder());
assertFalse(
"Tab at position 2 should not be a placeholder.", stripTabs[2].getIsPlaceholder());
assertFalse(
"Tab at position 3 should not be a placeholder.", stripTabs[3].getIsPlaceholder());
assertFalse(
"Tab at position 4 should not be a placeholder.", stripTabs[4].getIsPlaceholder());
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_STARTUP_REFACTORING)
public void testPlaceholderStripLayout_ReorderBeforeTabStateInitialized() {
// Create StripLayoutHelper and mark that after tabs finish restoring, there will be five
// tabs, where the third tab will be the active tab.
mStripLayoutHelper = createStripLayoutHelper(false, false);
mStripLayoutHelper.setTabModelStartupInfo(5, 2, false);
// Attempt to start a reorder and verify that we don't start it.
mStripLayoutHelper.startReorderTab(TIMESTAMP, 0, 100);
assertFalse(
"Should not start reorder mode before tab restore finishes.",
mStripLayoutHelper.getInReorderModeForTesting());
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_STARTUP_REFACTORING)
public void testPlaceholderStripLayout_DragBeforeTabStateInitialized() {
// Create StripLayoutHelper and mark that after tabs finish restoring, there will be five
// tabs, where the third tab will be the active tab.
mStripLayoutHelper = createStripLayoutHelper(false, false);
mStripLayoutHelper.setTabModelStartupInfo(5, 2, false);
// Attempt to start a drag and drop and verify that we don't start it.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
mStripLayoutHelper.startDragAndDropTab(tabs[2], DRAG_START_POINT);
verify(mTabDragSource, never())
.startTabDragAction(any(), any(), any(), anyFloat(), anyFloat());
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_STARTUP_REFACTORING)
public void testPlaceholderStripLayout_ScrollOnStartup() {
// Create StripLayoutHelper and mark that after tabs finish restoring, there will be 20
// tabs, where the last tab will be the active tab.
mStripLayoutHelper = createStripLayoutHelper(false, false);
mStripLayoutHelper.setTabModelStartupInfo(20, 19, false);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Mock a tab model and set it in the StripLayoutHelper.
MockTabModel tabModel = new MockTabModel(mProfile, null);
tabModel.setActive(true);
mStripLayoutHelper.setTabModel(tabModel, null, false);
assertEquals("Offset should be 0.", 0, mStripLayoutHelper.getScrollOffset(), EPSILON);
// Set size.
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
assertNotEquals(
"Offset should have changed.", 0, mStripLayoutHelper.getScrollOffset(), EPSILON);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_STARTUP_REFACTORING)
public void testPlaceholderStripLayout_CreatedTabOnStartup() {
// Create StripLayoutHelper and mark that after tabs finish restoring, there will be five
// tabs, where the third tab will be the active tab.
mStripLayoutHelper = createStripLayoutHelper(false, false);
mStripLayoutHelper.setTabModelStartupInfo(5, 2, true);
// Mock a tab model and set it in the StripLayoutHelper.
int expectedCreatedTabId = 4;
MockTabModel tabModel = new MockTabModel(mProfile, null);
tabModel.addTab(expectedCreatedTabId);
tabModel.setIndex(0, TabSelectionType.FROM_NEW);
tabModel.setActive(true);
mStripLayoutHelper.setTabModel(tabModel, null, false);
// Verify that the fifth (tab created from "intent") is real.
StripLayoutTab[] stripTabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertTrue("Tab at position 0 should be a placeholder.", stripTabs[0].getIsPlaceholder());
assertTrue("Tab at position 1 should be a placeholder.", stripTabs[1].getIsPlaceholder());
assertTrue("Tab at position 2 should be a placeholder.", stripTabs[2].getIsPlaceholder());
assertTrue("Tab at position 3 should be a placeholder.", stripTabs[3].getIsPlaceholder());
assertFalse(
"Tab at position 4 should not be a placeholder.", stripTabs[4].getIsPlaceholder());
assertEquals(
"Tab at position 4 should be the same from the mock.",
expectedCreatedTabId,
stripTabs[4].getTabId());
}
private void setupForAnimations() {
CompositorAnimationHandler mHandler = new CompositorAnimationHandler(() -> {});
when(mUpdateHost.getAnimationHandler()).thenReturn(mHandler);
// Update layout when updateHost.requestUpdate is called.
doAnswer(
invocation -> {
mStripLayoutHelper.updateLayout(TIMESTAMP);
return null;
})
.when(mUpdateHost)
.requestUpdate();
}
private void initializeTest(
boolean rtl, boolean incognito, boolean disableAnimations, int tabIndex, int numTabs) {
mStripLayoutHelper = createStripLayoutHelper(rtl, incognito);
mIncognito = incognito;
if (disableAnimations) mStripLayoutHelper.disableAnimationsForTesting();
if (rtl) {
mStripLayoutHelper.setLeftFadeWidth(
incognito
? StripLayoutHelperManager.FADE_LONG_WIDTH_DP
: StripLayoutHelperManager.FADE_MEDIUM_WIDTH_DP);
mStripLayoutHelper.setRightFadeWidth(StripLayoutHelperManager.FADE_SHORT_WIDTH_DP);
} else {
mStripLayoutHelper.setLeftFadeWidth(StripLayoutHelperManager.FADE_SHORT_WIDTH_DP);
mStripLayoutHelper.setRightFadeWidth(
incognito
? StripLayoutHelperManager.FADE_LONG_WIDTH_DP
: StripLayoutHelperManager.FADE_MEDIUM_WIDTH_DP);
}
if (numTabs <= 5) {
for (int i = 0; i < numTabs; i++) {
mModel.addTab(TEST_TAB_TITLES[i]);
when(mModel.getTabAt(i).isHidden()).thenReturn(tabIndex != i);
when(mModel.getTabAt(i).getView()).thenReturn(mInteractingTabView);
when(mModel.getTabAt(i).getRootId()).thenReturn(i);
}
} else {
for (int i = 0; i < numTabs; i++) {
mModel.addTab("Tab " + i);
when(mModel.getTabAt(i).isHidden()).thenReturn(tabIndex != i);
when(mModel.getTabAt(i).getView()).thenReturn(mInteractingTabView);
when(mModel.getTabAt(i).getRootId()).thenReturn(i);
}
}
mModel.setIndex(tabIndex);
mStripLayoutHelper.setTabModel(mModel, null, true);
mStripLayoutHelper.setTabGroupModelFilter(mTabGroupModelFilter);
mStripLayoutHelper.setLayerTitleCache(mLayerTitleCache);
mStripLayoutHelper.tabSelected(0, tabIndex, 0, false);
// Flush UI updated
}
private void initializeTest(int tabIndex) {
initializeTest(false, false, tabIndex);
}
private void initializeTest(boolean rtl, boolean incognito, int tabIndex) {
initializeTest(rtl, incognito, false, tabIndex, 5);
}
private void assertTabStripAndOrder(String[] expectedAccessibilityDescriptions) {
// Each tab has a "close button", and there is one additional "new tab" button
final int expectedNumberOfViews = 2 * expectedAccessibilityDescriptions.length + 1;
final List<VirtualView> views = new ArrayList<>();
mStripLayoutHelper.getVirtualViews(views);
assertEquals(expectedNumberOfViews, views.size());
// Tab titles
for (int i = 0; i < expectedNumberOfViews - 1; i++) {
final String expectedDescription =
i % 2 == 0
? expectedAccessibilityDescriptions[i / 2]
: String.format(CLOSE_TAB, TEST_TAB_TITLES[i / 2]);
assertEquals(expectedDescription, views.get(i).getAccessibilityDescription());
}
assertEquals(
mActivity
.getResources()
.getString(
mIncognito
? R.string.accessibility_toolbar_btn_new_incognito_tab
: R.string.accessibility_toolbar_btn_new_tab),
views.get(views.size() - 1).getAccessibilityDescription());
}
private StripLayoutHelper createStripLayoutHelper(boolean rtl, boolean incognito) {
LocalizationUtils.setRtlForTesting(rtl);
final StripLayoutHelper stripLayoutHelper =
new StripLayoutHelper(
mActivity,
mManagerHost,
mUpdateHost,
mRenderHost,
mWindowRectSupplier,
incognito,
mModelSelectorBtn,
mTabDragSource,
mToolbarContainerView,
mWindowAndroid,
mActionConfirmationManager,
0,
() -> true);
// Initialize StackScroller
stripLayoutHelper.onContextChanged(mActivity);
return stripLayoutHelper;
}
private String[] getExpectedAccessibilityDescriptions(int tabIndex) {
final String[] expectedAccessibilityDescriptions = new String[TEST_TAB_TITLES.length];
for (int i = 0; i < TEST_TAB_TITLES.length; i++) {
final boolean isHidden = (i != tabIndex);
String suffix;
if (mIncognito) {
suffix = isHidden ? INCOGNITO_IDENTIFIER : INCOGNITO_IDENTIFIER_SELECTED;
} else {
suffix = isHidden ? IDENTIFIER : IDENTIFIER_SELECTED;
}
String expectedDescription = "";
if (!TextUtils.isEmpty(TEST_TAB_TITLES[i])) {
expectedDescription += TEST_TAB_TITLES[i] + ", ";
}
expectedAccessibilityDescriptions[i] = expectedDescription + suffix;
}
return expectedAccessibilityDescriptions;
}
private StripLayoutTab[] getMockedStripLayoutTabs(float tabWidth, float drawX, int numTabs) {
StripLayoutTab[] tabs = new StripLayoutTab[mModel.getCount()];
final float delta = tabWidth - mStripLayoutHelper.getTabOverlapWidthForTesting();
for (int i = 0; i < numTabs; i++) {
final StripLayoutTab tab = mockStripTab(i, tabWidth, drawX + i * delta);
tabs[i] = tab;
}
return tabs;
}
private StripLayoutTab[] getMockedStripLayoutTabs(float tabWidth) {
return getMockedStripLayoutTabs(tabWidth, 0.f, 5);
}
private StripLayoutTab mockStripTab(int id, float tabWidth, float drawX) {
StripLayoutTab tab = mock(StripLayoutTab.class);
when(tab.getWidth()).thenReturn(tabWidth);
when(tab.getTabId()).thenReturn(id);
when(tab.getDrawX()).thenReturn(drawX);
return tab;
}
/**
* Mock that the sequence of tabs from startIndex to endIndex are part of that same tab group.
*
* @param startIndex The index where we start including tabs in the group (inclusive).
* @param endIndex The index where we stop including tabs in the group (exclusive).
*/
private void groupTabs(int startIndex, int endIndex) {
int groupRootId = mModel.getTabAt(startIndex).getId();
int numTabs = endIndex - startIndex;
List<Tab> relatedTabs = new ArrayList<>();
for (int i = startIndex; i < endIndex; i++) {
Tab tab = mModel.getTabAt(i);
when(mTabGroupModelFilter.isTabInTabGroup(eq(tab))).thenReturn(true);
when(tab.getRootId()).thenReturn(groupRootId);
relatedTabs.add(tab);
}
when(mTabGroupModelFilter.getRelatedTabCountForRootId(eq(groupRootId))).thenReturn(numTabs);
when(mTabGroupModelFilter.getRelatedTabListForRootId(eq(groupRootId)))
.thenReturn(relatedTabs);
mStripLayoutHelper.updateGroupTitleText(groupRootId);
mStripLayoutHelper.rebuildStripViews();
}
private void setTabDragSourceMock() {
when(mTabDragSource.startTabDragAction(any(), any(), any(), anyFloat(), anyFloat()))
.thenReturn(true);
MultiWindowTestUtils.enableMultiInstance();
}
@Test
@Config(sdk = Build.VERSION_CODES.R)
public void testDrag_AllowMovingTabOutOfStripLayout_SetActiveTab() {
// Setup with 10 tabs and select tab 5.
setTabDragSourceMock();
initializeTest(false, false, false, 5, 10);
StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1, 150f, 10);
mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
mStripLayoutHelper.tabSelected(1, 5, 0, false);
// Trigger update to set foreground container visibility.
mStripLayoutHelper.updateLayout(TIMESTAMP);
StripLayoutTab theClickedTab = tabs[5];
// Clean active tab environment and ensure.
mStripLayoutHelper.clearTabDragState();
assertTrue(
"Dragged Tab should be empty before drag action.",
mStripLayoutHelper.getActiveClickedTabForTesting() == null);
// Act and verify.
mStripLayoutHelper.startDragAndDropTab(theClickedTab, DRAG_START_POINT);
verify(mTabDragSource, times(1))
.startTabDragAction(any(), any(), any(), anyFloat(), anyFloat());
assertTrue(
"Tab being dragged should exist during drag action.",
mStripLayoutHelper.getActiveClickedTabForTesting() != null);
assertTrue(
"Dragged Tab should match selected tab during drag action.",
mStripLayoutHelper.getActiveClickedTabForTesting() == theClickedTab);
mStripLayoutHelper.clearTabDragState();
assertTrue(
"Dragged Tab should be cleared at the end of drag action.",
mStripLayoutHelper.getActiveClickedTabForTesting() == null);
}
@Test
@Config(sdk = Build.VERSION_CODES.R)
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testDrag_clearState() {
// Initialize with 10 tabs.
int selectedIndex = 5;
initializeTest(false, false, false, selectedIndex, 10);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab draggedTab =
mStripLayoutHelper.getStripLayoutTabsForTesting()[selectedIndex];
draggedTab.setIsDraggedOffStrip(true);
// Clear any animators.
mStripLayoutHelper.finishAnimationsAndPushTabUpdates();
assertNull("Should not be animating.", mStripLayoutHelper.getRunningAnimatorForTesting());
// Act and verify.
mStripLayoutHelper.clearTabDragState();
assertNotNull("Should be animating.", mStripLayoutHelper.getRunningAnimatorForTesting());
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_CONTEXT_MENU)
@Config(sdk = Build.VERSION_CODES.R)
public void testDrag_sendMoveWindowBroadcast_success() {
// Setup with tabs and select first tab.
setTabDragSourceMock();
when(mToolbarContainerView.getContext()).thenReturn(mActivity);
initializeTest(false, false, false, 0, 5);
// Act and verify the broadcast is sent.
onLongPress_OffTab();
verify(mWindowAndroid, times(1)).sendBroadcast(any());
}
@Test
@Config(sdk = Build.VERSION_CODES.R)
public void testDrag_DragActiveClickedTabOntoStrip() {
// Setup and mark the active clicked tab.
initializeTest(false, false, false, 0, 5);
// Drag tab back onto strip.
float expectedOffsetX = 123.45f;
mStripLayoutHelper.setLastOffsetXForTesting(expectedOffsetX);
mStripLayoutHelper.prepareForTabDrop(TIMESTAMP, 0f, 0f, true, false);
// Verify we continue reorder mode with the correct x-offset.
assertFalse(
"Should mark the tab is not off the strip.",
mStripLayoutHelper.getInteractingTabForTesting().isDraggedOffStrip());
assertEquals(
"Should restore x-offset.",
expectedOffsetX,
mStripLayoutHelper.getInteractingTabForTesting().getOffsetX(),
EPSILON);
assertTrue(
"Should re-enter reorder mode.", mStripLayoutHelper.getInReorderModeForTesting());
}
@Test
@Config(sdk = Build.VERSION_CODES.R)
public void testDrag_DragActiveClickedTabOutOfStrip() {
// Setup and mark the active clicked tab.
initializeTest(false, false, false, 1, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Drag tab out of strip.
float expectedOffsetX = 123.45f;
mStripLayoutHelper.prepareForTabDrop(TIMESTAMP, 0f, 0f, true, false);
StripLayoutTab draggedTab = mStripLayoutHelper.getInteractingTabForTesting();
draggedTab.setOffsetX(expectedOffsetX);
mStripLayoutHelper.clearForTabDrop(TIMESTAMP, true, false);
// Finish animations.
assertNotNull(
"Animator should be running.", mStripLayoutHelper.getRunningAnimatorForTesting());
mStripLayoutHelper.finishAnimationsAndPushTabUpdates();
// Verify we stop reorder mode and animate the tab exiting.
assertEquals(
"Should have stored dragged tab's x-offset.",
expectedOffsetX,
mStripLayoutHelper.getLastOffsetXForTesting(),
EPSILON);
assertTrue("Should mark the tab is off the strip.", draggedTab.isDraggedOffStrip());
assertFalse(
"Should not be in reorder mode.", mStripLayoutHelper.getInReorderModeForTesting());
assertEquals(
"Tab should be translated off the strip.",
SCREEN_HEIGHT,
draggedTab.getOffsetY(),
EPSILON);
}
@Test
@Config(sdk = Build.VERSION_CODES.R)
public void testDrag2_DragActiveClickedTabOutOfStrip() {
// Setup and mark the active clicked tab.
initializeTest(false, false, false, 1, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Drag tab out of strip.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
mStripLayoutHelper.setTabAtPositionForTesting(tabs[1]);
mStripLayoutHelper.prepareForTabDrop(TIMESTAMP, 0f, 0f, true, false);
mStripLayoutHelper.clearForTabDrop(TIMESTAMP, true, false);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify 3rd, 4th and 5th tab's start divider is visible.
assertFalse("Start divider should be hidden.", tabs[0].isStartDividerVisible());
assertFalse("DraggedTab divider should be hidden.", tabs[1].isStartDividerVisible());
assertTrue(
"Tab after draggedTab start divider should be visible.",
tabs[2].isStartDividerVisible());
assertTrue("Start divider should be visible.", tabs[3].isStartDividerVisible());
assertTrue("Start divider should be visible.", tabs[4].isStartDividerVisible());
}
@Test
public void testGetTabIndexForTabDrop_FirstHalfOfTab() {
// Setup with 3 tabs.
initializeTest(false, false, true, 1, 3);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH_LANDSCAPE,
SCREEN_HEIGHT,
false,
TIMESTAMP,
PADDING_LEFT,
PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// First half of second tab:
// tabWidth(265) - overlapWidth(28) + inset(16) to +halfTabWidth(132.5) = 253 to 385.5
int expectedIndex = 1;
float dropX = 300.f;
assertEquals(
"Should prepare to drop at index 1.",
expectedIndex,
mStripLayoutHelper.getTabIndexForTabDrop(dropX));
}
@Test
public void testGetTabIndexForTabDrop_SecondHalfOfTab() {
// Setup with 3 tabs.
initializeTest(false, false, true, 1, 3);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH_LANDSCAPE,
SCREEN_HEIGHT,
false,
TIMESTAMP,
PADDING_LEFT,
PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// First half of second tab:
// tabWidth(265) - overlapWidth(28) + inset(16) to +halfTabWidth(132.5) = 253 to 385.5
int expectedIndex = 2;
float dropX = 400.f;
assertEquals(
"Should prepare to drop at index 2.",
expectedIndex,
mStripLayoutHelper.getTabIndexForTabDrop(dropX));
}
@Test
public void testGetTabIndexForTabDrop_OnStartGap() {
// Setup with 3 tabs.
initializeTest(false, false, true, 1, 3);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH_LANDSCAPE,
SCREEN_HEIGHT,
false,
TIMESTAMP,
PADDING_LEFT,
PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Prepare for tab drop.
mStripLayoutHelper.prepareForTabDrop(TIMESTAMP, 0.f, 0.f, false, false);
// Start gap will be tabWidth(265) / 2 = 132.5
mStripLayoutHelper.setScrollOffsetForTesting(-132);
// Last tab ends at:
// 3 * (tabWidth(265) - overlapWidth(28)) = 711
int expectedIndex = 0;
float dropX = 50;
assertEquals(
"Should prepare to drop at index 0.",
expectedIndex,
mStripLayoutHelper.getTabIndexForTabDrop(dropX));
}
@Test
public void testGetTabIndexForTabDrop_OnEndGap() {
// Setup with 3 tabs.
initializeTest(false, false, true, 1, 3);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH_LANDSCAPE,
SCREEN_HEIGHT,
false,
TIMESTAMP,
PADDING_LEFT,
PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Last tab ends at:
// 3 * (tabWidth(265) - overlapWidth(28)) = 711
int expectedIndex = 3;
float dropX = 750;
assertEquals(
"Should prepare to drop at index 3.",
expectedIndex,
mStripLayoutHelper.getTabIndexForTabDrop(dropX));
}
@Test
public void testPrepareForTabDrop() {
// Setup with 5 tabs.
initializeTest(false, false, true, 1, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH_LANDSCAPE,
SCREEN_HEIGHT,
false,
TIMESTAMP,
PADDING_LEFT,
PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Group 2nd and 3rd tab.
groupTabs(1, 3);
// Prepare for tab drop.
mStripLayoutHelper.prepareForTabDrop(TIMESTAMP, 0.f, 0.f, false, false);
// Verify.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertTrue("Should be in reorder mode.", mStripLayoutHelper.getInReorderModeForTesting());
assertNotEquals("Should be tab margin after tab 0.", 0, tabs[0].getTrailingMargin());
assertEquals(
"Should not be tab margin after tab 1.", 0, tabs[1].getTrailingMargin(), EPSILON);
assertNotEquals("Should be tab margin after tab 2.", 0, tabs[2].getTrailingMargin());
assertEquals(
"Should not be tab margin after tab 3.", 0, tabs[3].getTrailingMargin(), EPSILON);
assertNotEquals("Should be tab margin after tab 4.", 0, tabs[4].getTrailingMargin());
assertEquals(
"TouchableRect does not match. Touch size should match the strip during drag.",
new RectF(PADDING_LEFT, 0, SCREEN_WIDTH_LANDSCAPE - PADDING_RIGHT, SCREEN_HEIGHT),
mStripLayoutHelper.getTouchableRect());
}
@Test
public void testUpdateReorderPositionForTabDrop() {
// Setup with 4 tabs.
initializeTest(false, false, true, 1, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH_LANDSCAPE,
SCREEN_HEIGHT,
false,
TIMESTAMP,
PADDING_LEFT,
PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Prepare for tab drop.
mStripLayoutHelper.prepareForTabDrop(TIMESTAMP, 0.f, 0.f, false, false);
// Hover between 2nd and 3rd tab:
// 2 * (tabWidth(265) - overlapWidth(28)) = 474
mStripLayoutHelper.updateReorderPositionForTabDrop(474.f);
// Verify.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertEquals(
"Should not be tab margin after tab 0.", 0, tabs[0].getTrailingMargin(), EPSILON);
assertNotEquals("Should be tab margin after tab 1.", 0, tabs[1].getTrailingMargin());
assertEquals(
"Should not be tab margin after tab 2.", 0, tabs[2].getTrailingMargin(), EPSILON);
assertNotEquals("Should be tab margin after tab 3.", 0, tabs[3].getTrailingMargin());
// Now hover between 1st and 2nd tab:
// tabWidth(265) - overlapWidth(28) = 237
mStripLayoutHelper.updateReorderPositionForTabDrop(237.f);
// Verify.
assertNotEquals("Should be tab margin after tab 0.", 0, tabs[0].getTrailingMargin());
assertEquals(
"Should not be tab margin after tab 1.", 0, tabs[1].getTrailingMargin(), EPSILON);
assertEquals(
"Should not be tab margin after tab 2.", 0, tabs[2].getTrailingMargin(), EPSILON);
assertNotEquals("Should be tab margin after tab 3.", 0, tabs[3].getTrailingMargin());
}
@Test
public void testUpdateReorderPositionForTabDrop_StartAndEndGap() {
// Setup with 3 tabs.
initializeTest(false, false, true, 1, 3);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH_LANDSCAPE,
SCREEN_HEIGHT,
false,
TIMESTAMP,
PADDING_LEFT,
PADDING_RIGHT);
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Prepare for tab drop.
mStripLayoutHelper.prepareForTabDrop(TIMESTAMP, 0.f, 0.f, false, false);
// Start gap will be tabWidth(265) / 2 = 132.5
mStripLayoutHelper.setScrollOffsetForTesting(-132);
// Hover in start gap:
mStripLayoutHelper.updateReorderPositionForTabDrop(50);
// Verify.
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
assertEquals(
"Should not be tab margin after tab 0.", 0, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(
"Should not be tab margin after tab 1.", 0, tabs[1].getTrailingMargin(), EPSILON);
assertNotEquals("Should be tab margin after tab 2.", 0, tabs[2].getTrailingMargin());
// Hover in end gap:
mStripLayoutHelper.updateReorderPositionForTabDrop(1100);
// Verify.
assertEquals(
"Should not be tab margin after tab 0.", 0, tabs[0].getTrailingMargin(), EPSILON);
assertEquals(
"Should not be tab margin after tab 1.", 0, tabs[1].getTrailingMargin(), EPSILON);
assertNotEquals("Should be tab margin after tab 2.", 0, tabs[2].getTrailingMargin());
}
@Test
public void testDestinationStripForTabDrop_DifferentIncognitoState() {
// Setup with 3 tabs.
boolean isIncognito = false;
initializeTest(false, isIncognito, true, 1, 3);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Prepare and verify no interaction.
mStripLayoutHelper.prepareForTabDrop(TIMESTAMP, 0.f, 0.f, false, !isIncognito);
assertFalse(
"Shouldn't start reorder when dragged tab Incognito state is different.",
mStripLayoutHelper.getInReorderModeForTesting());
// Drag and verify no interaction.
float expectedOffset = mStripLayoutHelper.getScrollOffset();
mStripLayoutHelper.dragForTabDrop(TIMESTAMP, PADDING_LEFT, 0.f, 50.f, !isIncognito);
assertEquals(
"Shouldn't have scrolled when dragged tab Incognito is different.",
expectedOffset,
mStripLayoutHelper.getScrollOffset(),
EPSILON);
// Set reorder mode for testing, then clear for tab drop and verify no interaction.
mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
mStripLayoutHelper.clearForTabDrop(TIMESTAMP, false, !isIncognito);
assertTrue(
"Shouldn't stop reorder when dragged tab Incognito state is different.",
mStripLayoutHelper.getInReorderModeForTesting());
}
@Test
@DisableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testRebuildNonTabViews_IndicatorsDisabled() {
// Initialize with 10 tabs. Group tabs 2 through 3. Group tabs 5 through 8.
initializeTest(false, false, true, 0, 10);
groupTabs(1, 3);
groupTabs(4, 8);
// Verify.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertEquals("Should be 10 views (10 tabs).", 10, views.length);
for (StripLayoutView view : views) {
assertTrue(EXPECTED_TAB, view instanceof StripLayoutTab);
}
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testRebuildNonTabViews_IndicatorsEnabled() {
// Initialize with 10 tabs. Group tabs 2 through 3. Group tabs 5 through 8.
initializeTest(false, false, true, 0, 10);
groupTabs(1, 3);
groupTabs(4, 8);
// Verify.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertEquals("Should be 12 views (10 tabs and 2 titles).", 12, views.length);
assertTrue(EXPECTED_TAB, views[0] instanceof StripLayoutTab);
assertTrue(EXPECTED_TITLE, views[1] instanceof StripLayoutGroupTitle);
assertTrue(EXPECTED_TAB, views[2] instanceof StripLayoutTab);
assertTrue(EXPECTED_TAB, views[3] instanceof StripLayoutTab);
assertTrue(EXPECTED_TAB, views[4] instanceof StripLayoutTab);
assertTrue(EXPECTED_TITLE, views[5] instanceof StripLayoutGroupTitle);
assertTrue(EXPECTED_TAB, views[6] instanceof StripLayoutTab);
assertTrue(EXPECTED_TAB, views[7] instanceof StripLayoutTab);
assertTrue(EXPECTED_TAB, views[8] instanceof StripLayoutTab);
assertTrue(EXPECTED_TAB, views[9] instanceof StripLayoutTab);
assertTrue(EXPECTED_TAB, views[10] instanceof StripLayoutTab);
assertTrue(EXPECTED_TAB, views[11] instanceof StripLayoutTab);
// verify bottom indicator width.
float tabWidth = views[0].getWidth() - TAB_OVERLAP_WIDTH;
float expectedWidth1 =
views[1].getWidth()
+ tabWidth * 2
- StripLayoutHelper.TAB_GROUP_BOTTOM_INDICATOR_WIDTH_OFFSET;
float expectedWidth2 =
views[5].getWidth()
+ tabWidth * 4
- StripLayoutHelper.TAB_GROUP_BOTTOM_INDICATOR_WIDTH_OFFSET;
assertEquals(
expectedWidth1, ((StripLayoutGroupTitle) views[1]).getBottomIndicatorWidth(), 0.f);
assertEquals(
expectedWidth2, ((StripLayoutGroupTitle) views[5]).getBottomIndicatorWidth(), 0.f);
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testHandleGroupTitleClick_Collapse() {
// Initialize with 4 tabs. Group first three tabs.
HistogramWatcher histogramWatcher =
HistogramWatcher.newSingleRecordWatcher("Android.TabStrip.TabGroupCollapsed", true);
initializeTest(false, false, true, 3, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(0, 3);
// Fake a click on the group indicator.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
mStripLayoutHelper.handleGroupTitleClick((StripLayoutGroupTitle) views[0]);
// Verify the proper event was sent to the TabGroupModelFilter.
verify(mTabGroupModelFilter).setTabGroupCollapsed(0, true);
// Verify we record the correct metric.
histogramWatcher.assertExpected("Should record true, since we're collapsing.");
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testHandleGroupTitleClick_Expand() {
// Initialize with 4 tabs. Group first three tabs.
HistogramWatcher histogramWatcher =
HistogramWatcher.newSingleRecordWatcher(
"Android.TabStrip.TabGroupCollapsed", false);
initializeTest(false, false, true, 3, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(0, 3);
// Mark the group as collapsed. Fake a click on the group indicator.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[0], true);
when(mTabGroupModelFilter.getTabGroupCollapsed(0)).thenReturn(true);
mStripLayoutHelper.handleGroupTitleClick((StripLayoutGroupTitle) views[0]);
// Verify the proper event was sent to the TabGroupModelFilter.
verify(mTabGroupModelFilter).setTabGroupCollapsed(0, false);
// Verify we record the correct metric.
histogramWatcher.assertExpected("Should record false, since we're expanding.");
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testUpdateTabGroupCollapsed_Collapse() {
// Initialize with 4 tabs. Group first three tabs.
initializeTest(false, false, true, 3, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(0, 3);
// Verify initial dimensions.
// availableSize = width(800) - NTB(32) - endPadding(8) - offsetXLeft(10) - offsetXRight(20)
// - groupTitleWidth(46) - titleOverlapWidth(4) = 680.
// tabWidth = (availableSize(680) + 3 * overlap(28)) / 4 = 193.f
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
float initialTabWidth = 193.f;
assertEquals("Tab width is incorrect.", initialTabWidth, views[1].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", initialTabWidth, views[2].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", initialTabWidth, views[3].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", initialTabWidth, views[4].getWidth(), EPSILON);
// Collapse the group.
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[0], true);
// Verify final dimensions.
float collapsedWidth = TAB_OVERLAP_WIDTH;
float endTabWidth = TabUiThemeUtil.getMaxTabStripTabWidthDp();
assertEquals("Tab width is incorrect.", collapsedWidth, views[1].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", collapsedWidth, views[2].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", collapsedWidth, views[3].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", endTabWidth, views[4].getWidth(), EPSILON);
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testUpdateTabGroupCollapsed_Expand() {
// Initialize with 4 tabs. Group first three tabs.
initializeTest(false, false, true, 3, 4);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(0, 3);
// Collapse the group.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[0], true);
// Verify initial dimensions.
float collapsedWidth = TAB_OVERLAP_WIDTH;
float initialTabWidth = TabUiThemeUtil.getMaxTabStripTabWidthDp();
assertEquals("Tab width is incorrect.", collapsedWidth, views[1].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", collapsedWidth, views[2].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", collapsedWidth, views[3].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", initialTabWidth, views[4].getWidth(), EPSILON);
// Fake a click on the tab group to expand.
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[0], false);
// Verify final dimensions.
// availableSize = width(800) - NTB(32) - endPadding(8) - offsetXLeft(10) - offsetXRight(20)
// - groupTitleWidth(46) - titleOverlapWidth(4) = 680.
// tabWidth = (availableSize(680) + 3 * overlap(28)) / 4 = 193.f
float endTabWidth = 193.f;
assertEquals("Tab width is incorrect.", endTabWidth, views[1].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", endTabWidth, views[2].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", endTabWidth, views[3].getWidth(), EPSILON);
assertEquals("Tab width is incorrect.", endTabWidth, views[4].getWidth(), EPSILON);
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testSelectedTabCollapse_MiddleGroup_PrevTabSelected() {
// Initialize with 5 tabs. Group last two tabs.
initializeTest(false, false, true, 3, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(3, 4);
// Assert: the 4th tab is selected.
assertEquals(
"The tab selected is incorrect.", 3, mStripLayoutHelper.getSelectedStripTabIndex());
// Assert: the fourth view should be group title.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[3] instanceof StripLayoutGroupTitle);
// Click to collapse the first tab group.
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[3], true);
// Assert: the previous tab is selected as there is no expanded tab towards the end.
assertEquals(
"The tab selected is incorrect.", 2, mStripLayoutHelper.getSelectedStripTabIndex());
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testSelectedTabCollapse_StartGroup_NextTabSelected() {
// Initialize with 5 tabs. Group first three tabs.
initializeTest(false, false, true, 1, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(0, 3);
// Assert: the 2nd tab is selected.
assertEquals(
"The tab selected is incorrect.", 1, mStripLayoutHelper.getSelectedStripTabIndex());
// Assert: the first view should be group title.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[0] instanceof StripLayoutGroupTitle);
// Click to collapse the first tab group.
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[0], true);
// Assert: the fourth tab is selected.
assertEquals(
"The tab selected is incorrect.", 3, mStripLayoutHelper.getSelectedStripTabIndex());
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testCollapseSelectedTab_EndGroup_PrevTabSelected() {
// Initialize with 5 tabs. Group last two tabs.
initializeTest(false, false, true, 3, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(3, 5);
// Assert: the 4th tab is selected.
assertEquals(
"The tab selected is incorrect.", 3, mStripLayoutHelper.getSelectedStripTabIndex());
// Assert: the fourth view should be group title.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[3] instanceof StripLayoutGroupTitle);
// Click to collapse the first tab group.
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[3], true);
// Assert: the previous tab is selected as there is no expanded tab towards the end.
assertEquals(
"The tab selected is incorrect.", 2, mStripLayoutHelper.getSelectedStripTabIndex());
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testCollapseSelectedTab_OpenNtp() {
// Initialize with 5 tabs. Group all five tabs.
initializeTest(false, false, true, 3, 5);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
groupTabs(0, 5);
// Assert: the 4th tab is selected.
assertEquals(
"The tab selected is incorrect.", 3, mStripLayoutHelper.getSelectedStripTabIndex());
// Assert: the first view should be group title.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
assertTrue(EXPECTED_TITLE, views[0] instanceof StripLayoutGroupTitle);
// Click to collapse the first tab group.
TabCreator tabCreator = mock(TabCreator.class);
mStripLayoutHelper.setTabModel(spy(mModel), tabCreator, true);
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[0], true);
// Verify: Ntp opened since there is no expanded tab on strip.
verify(tabCreator).launchNtp();
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testTabSelected_ExpandsGroup() {
// Group first two tabs and collapse.
int startIndex = 3;
int groupId = 0;
initializeTest(startIndex);
groupTabs(groupId, 2);
when(mTabGroupModelFilter.getTabGroupCollapsed(groupId)).thenReturn(true);
// Select the first tab.
mStripLayoutHelper.tabSelected(TIMESTAMP, groupId, startIndex, /* skipAutoScroll= */ false);
// Verify we auto-expand.
verify(mTabGroupModelFilter).deleteTabGroupCollapsed(groupId);
}
private void testTabCreated_InCollapsedGroup(boolean selected) {
// Group first two tabs and collapse.
int groupId = 0;
initializeTest(/* tabIndex= */ 3);
groupTabs(groupId, 2);
when(mTabGroupModelFilter.getTabGroupCollapsed(groupId)).thenReturn(true);
// Create a tab in the collapsed group.
int tabId = 5;
mModel.addTab("new tab");
mModel.getTabById(tabId).setRootId(groupId);
mStripLayoutHelper.tabCreated(
TIMESTAMP,
tabId,
tabId,
selected,
/* closureCancelled */ false,
/* onStartup= */ false);
// Verify we only auto-expand if selected.
verify(mTabGroupModelFilter, times(selected ? 1 : 0)).deleteTabGroupCollapsed(groupId);
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testTabCreated_InCollapsedGroup_Selected() {
testTabCreated_InCollapsedGroup(/* selected= */ true);
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE
})
public void testTabCreated_InCollapsedGroup_NotSelected() {
testTabCreated_InCollapsedGroup(/* selected= */ false);
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE,
ChromeFeatureList.TAB_GROUP_SYNC_ANDROID
})
public void testTabGroupSyncIph_Show() {
// Setup tab group and Tab Group Sync iph.
TabGroupSyncIphController controller = setupTabGroupSyncIphOnTablet(4, 5);
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
StripLayoutGroupTitle groupTitle = ((StripLayoutGroupTitle) views[4]);
// Trigger show iph.
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify iph is displayed at the correct horizontal position.
verify(controller)
.maybeShowIphOnTabStrip(
any(),
eq(groupTitle.getDrawX()),
anyFloat(),
eq(SCREEN_WIDTH - groupTitle.getDrawX() - groupTitle.getWidth()),
anyFloat());
}
@Test
@EnableFeatures({
ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS,
ChromeFeatureList.TAB_STRIP_GROUP_COLLAPSE,
ChromeFeatureList.TAB_GROUP_SYNC_ANDROID
})
public void testTabGroupSyncIph_DismissOnOrientationChanged() {
// Setup tab group and Tab Group Sync iph.
TabGroupSyncIphController controller = setupTabGroupSyncIphOnTablet(4, 5);
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
StripLayoutGroupTitle groupTitle = ((StripLayoutGroupTitle) views[4]);
// Trigger show iph.
mStripLayoutHelper.updateLayout(TIMESTAMP);
// Verify iph is displayed at the correct horizontal position.
verify(controller)
.maybeShowIphOnTabStrip(
any(),
eq(groupTitle.getDrawX()),
anyFloat(),
eq(SCREEN_WIDTH - groupTitle.getDrawX() - groupTitle.getWidth()),
anyFloat());
// Change orientation.
mStripLayoutHelper.onSizeChanged(
SCREEN_HEIGHT, SCREEN_WIDTH, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
// Verify iph text bubble is dismissed on orientation change.
verify(controller).dismissTextBubble();
}
private TabGroupSyncIphController setupTabGroupSyncIphOnTablet(
int startTabIndex, int endTabIndex) {
initializeTest(false, false, true, 0, 5);
groupTabs(startTabIndex, endTabIndex);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
// Prepare iph and required objects.
TabGroupSyncIphController controller = mock(TabGroupSyncIphController.class);
mStripLayoutHelper.setTabGroupSyncIphControllerForTesting(controller);
TestTabModel tabModel = spy(mModel);
when(tabModel.getProfile()).thenReturn(mProfile);
mStripLayoutHelper.setTabModel(tabModel, null, false);
mStripLayoutHelper.setLastSyncedGroupIdForTesting(tabs[tabs.length - 1].getTabId());
mStripLayoutHelper.setTabGroupSyncIphControllerForTesting(controller);
return controller;
}
@Test
public void testUpdateLastHoveredTab() {
// Assume tab0 is selected, tab1 is hovered on.
initializeTabHoverTest();
var hoveredTab = mStripLayoutHelper.getStripLayoutTabsForTesting()[1];
mStripLayoutHelper.updateLastHoveredTab(hoveredTab);
assertEquals(
"Last hovered tab is not set.", hoveredTab, mStripLayoutHelper.getLastHoveredTab());
verify(mTabHoverCardView)
.show(
mModel.getTabAt(1),
false,
hoveredTab.getDrawX(),
hoveredTab.getWidth(),
SCREEN_HEIGHT);
assertEquals(
"Tab container opacity is incorrect.",
StripLayoutHelper.TAB_OPACITY_VISIBLE_FOREGROUND,
hoveredTab.getContainerOpacity(),
0.0);
}
@Test
public void testUpdateLastHoveredTab_animationRunning() {
initializeTabHoverTest();
var hoveredTab = mStripLayoutHelper.getStripLayoutTabsForTesting()[1];
// Assume that animations are running.
var animator = mock(Animator.class);
when(animator.isRunning()).thenReturn(true);
mStripLayoutHelper.setRunningAnimatorForTesting(animator);
mStripLayoutHelper.updateLastHoveredTab(hoveredTab);
verify(mTabHoverCardView, never())
.show(any(), anyBoolean(), anyFloat(), anyFloat(), anyFloat());
}
@Test
public void testIsViewCompletelyHidden() {
initializeTabHoverTest();
var hoveredTab = mStripLayoutHelper.getStripLayoutTabsForTesting()[1];
// Set simulated hovered StripLayoutTab drawX and width to assume a position beyond the left
// fade.
hoveredTab.setDrawX(-50f);
hoveredTab.setWidth(
StripLayoutHelperManager.FADE_SHORT_WIDTH_DP - 1 - hoveredTab.getDrawX());
assertTrue(
"Tab should be considered hidden for hover state.",
mStripLayoutHelper.isViewCompletelyHidden(hoveredTab));
// Set simulated hovered StripLayoutTab drawX to assume a position beyond the right fade.
hoveredTab.setDrawX(SCREEN_WIDTH - StripLayoutHelperManager.FADE_MEDIUM_WIDTH_DP + 1);
assertTrue(
"Tab should be considered hidden for hover state.",
mStripLayoutHelper.isViewCompletelyHidden(hoveredTab));
}
private void initializeTabHoverTest() {
initializeTest(false, false, true, 0, 3);
mStripLayoutHelper.onSizeChanged(
SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
mStripLayoutHelper.setTabHoverCardView(mTabHoverCardView);
// For ease of dp/px calculation.
mContext.getResources().getDisplayMetrics().density = 1f;
}
@Test
public void testSetTabModelFilter() {
// Setup and verify initial state.
initializeTest(false, false, 0);
TabGroupModelFilterObserver observer =
mStripLayoutHelper.getTabGroupModelFilterObserverForTesting();
verify(mTabGroupModelFilter).addTabGroupObserver(observer);
// Set a new TabGroupModelFilter.
TabGroupModelFilter newModelFilter = mock(TabGroupModelFilter.class);
mStripLayoutHelper.setTabGroupModelFilter(newModelFilter);
// Verify the observers have been updated as expected.
verify(mTabGroupModelFilter).removeTabGroupObserver(observer);
verify(newModelFilter).addTabGroupObserver(observer);
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testSetLayerTitleCache() {
// Setup. Group 2nd and 3rd tab.
String expectedTitle = TabGroupTitleEditor.getDefaultTitle(mContext, 2);
initializeTest(false, false, 0);
groupTabs(1, 3);
// Set a new LayerTitleCache.
LayerTitleCache newTitleCache = mock(LayerTitleCache.class);
mStripLayoutHelper.setLayerTitleCache(newTitleCache);
// Verify the observers have been updated as expected.
verify(newTitleCache).getGroupTitleWidth(eq(false), eq(expectedTitle));
}
@Test
@EnableFeatures(ChromeFeatureList.TAB_STRIP_GROUP_INDICATORS)
public void testDestroy() {
// Setup.
initializeTest(false, false, 0);
TabGroupModelFilterObserver observer =
mStripLayoutHelper.getTabGroupModelFilterObserverForTesting();
// Destroy the instance.
mStripLayoutHelper.destroy();
// Verify the observer has been removed as expected.
verify(mTabGroupModelFilter).removeTabGroupObserver(observer);
}
}