blob: 985424e98cb6766eeb96906b03099ffce71b7abf [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.tasks.tab_management;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstCardFromTabSwitcher;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstTabInDialog;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.closeFirstTabInDialog;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabs;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.enterTabSwitcher;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.isShowingPopupTabList;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.mergeAllNormalTabsToAGroup;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.prepareTabsWithThumbnail;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.rotateDeviceToOrientation;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyShowingPopupTabList;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyTabSwitcherCardCount;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.contrib.RecyclerViewActions;
import android.support.test.filters.MediumTest;
import android.view.View;
import android.widget.FrameLayout;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.compositor.layouts.Layout;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.fullscreen.FullscreenManagerTestUtils;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager.Variations;
import org.chromium.chrome.features.start_surface.StartSurfaceLayout;
import org.chromium.chrome.tab_ui.R;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.ui.UiSwitches;
import org.chromium.ui.test.util.UiRestriction;
import java.util.List;
/** End-to-end tests for TabGroupPopupUi component. */
@RunWith(ChromeJUnit4ClassRunner.class)
// clang-format off
// TODO(crbug.com/1058231): ENABLE_SCREENSHOT_UI_MODE is to disable IPHs for TabGroups.
// We should make this test more robust so that it's agnostic of IPHs.
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
UiSwitches.ENABLE_SCREENSHOT_UI_MODE})
@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID, ChromeFeatureList.CHROME_DUET,
ChromeFeatureList.DUET_TABSTRIP_INTEGRATION_ANDROID})
public class TabGroupPopupUiTest {
// clang-format on
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@Rule
public TestRule mProcessor = new Features.InstrumentationProcessor();
@After
public void tearDown() {
mActivityTestRule.getActivity().setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
@Test
@MediumTest
public void testOnAnchorViewChanged_HOME_SEARCH_TAB_SWITCHER() {
launchActivity(Variations.HOME_SEARCH_TAB_SWITCHER);
final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
// Tab strip should show automatically when entering tab group.
createTabGroupAndEnterTabPage(cta, 2, null);
CriteriaHelper.pollInstrumentationThread(() -> !isAnchoredOnTopToolbar(cta));
verifyShowingTabStrip(cta, 2);
// In portrait mode, tab strip should anchor on bottom toolbar; in landscape mode, bottom
// toolbar hides and tab strip should anchor on top toolbar.
rotateDeviceToOrientation(cta, Configuration.ORIENTATION_LANDSCAPE);
CriteriaHelper.pollInstrumentationThread(() -> isAnchoredOnTopToolbar(cta));
verifyShowingTabStrip(cta, 2);
rotateDeviceToOrientation(cta, Configuration.ORIENTATION_PORTRAIT);
CriteriaHelper.pollInstrumentationThread(() -> !isAnchoredOnTopToolbar(cta));
verifyShowingTabStrip(cta, 2);
}
@Test
@MediumTest
public void testOnAnchorViewChanged_HOME_SEARCH_SHARE() {
launchActivity(Variations.HOME_SEARCH_SHARE);
final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
// Tab strip should show automatically when entering tab group.
createTabGroupAndEnterTabPage(cta, 2, null);
CriteriaHelper.pollInstrumentationThread(() -> isAnchoredOnTopToolbar(cta));
verifyShowingTabStrip(cta, 2);
// In portrait mode, tab strip should anchor on bottom toolbar; in landscape mode, bottom
// toolbar hides and tab strip should anchor on top toolbar.
rotateDeviceToOrientation(cta, Configuration.ORIENTATION_LANDSCAPE);
CriteriaHelper.pollInstrumentationThread(() -> isAnchoredOnTopToolbar(cta));
verifyShowingTabStrip(cta, 2);
rotateDeviceToOrientation(cta, Configuration.ORIENTATION_PORTRAIT);
CriteriaHelper.pollInstrumentationThread(() -> isAnchoredOnTopToolbar(cta));
verifyShowingTabStrip(cta, 2);
}
@Test
@MediumTest
public void testOnAnchorViewChanged_NEW_TAB_SEARCH_SHARE() {
launchActivity(Variations.NEW_TAB_SEARCH_SHARE);
final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
// Tab strip should show automatically when entering tab group.
createTabGroupAndEnterTabPage(cta, 2, null);
CriteriaHelper.pollInstrumentationThread(() -> isAnchoredOnTopToolbar(cta));
verifyShowingTabStrip(cta, 2);
// In portrait mode, tab strip should anchor on bottom toolbar; in landscape mode, bottom
// toolbar hides and tab strip should anchor on top toolbar.
rotateDeviceToOrientation(cta, Configuration.ORIENTATION_LANDSCAPE);
CriteriaHelper.pollInstrumentationThread(() -> isAnchoredOnTopToolbar(cta));
verifyShowingTabStrip(cta, 2);
rotateDeviceToOrientation(cta, Configuration.ORIENTATION_PORTRAIT);
CriteriaHelper.pollInstrumentationThread(() -> isAnchoredOnTopToolbar(cta));
verifyShowingTabStrip(cta, 2);
}
@Test
@MediumTest
public void testTabStripShowHide() {
launchActivity();
final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
// Try to trigger tab strip in a single tab page.
triggerTabStripAndVerify(cta, 0);
// Tab strip should show automatically when entering tab group.
createTabGroupAndEnterTabPage(cta, 2, null);
verifyShowingTabStrip(cta, 2);
// Dismiss tab strip by clicking the left button.
onView(withId(R.id.toolbar_left_button))
.inRoot(withDecorView(not(cta.getWindow().getDecorView())))
.perform(click());
CriteriaHelper.pollInstrumentationThread(() -> !isTabStripShowing(cta));
// Re-show the tab strip.
triggerTabStripAndVerify(cta, 2);
// Tab strip should not show when overview mode is visible.
enterTabSwitcher(cta);
CriteriaHelper.pollInstrumentationThread(() -> !isTabStripShowing(cta));
// Re-verify that tab strip never shows in single tab.
clickFirstCardFromTabSwitcher(cta);
closeFirstTabInDialog(cta);
clickFirstTabInDialog(cta);
CriteriaHelper.pollInstrumentationThread(() -> !isTabStripShowing(cta));
triggerTabStripAndVerify(cta, 0);
}
@Test
@MediumTest
public void testTabStripUpdate() {
launchActivity();
final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
// Tab strip should show automatically when entering tab group.
createTabGroupAndEnterTabPage(cta, 2, null);
verifyShowingTabStrip(cta, 2);
// Adding tabs should trigger tab strip update.
onView(withId(R.id.toolbar_right_button))
.inRoot(withDecorView(not(cta.getWindow().getDecorView())))
.perform(click());
verifyShowingTabStrip(cta, 3);
onView(withId(R.id.toolbar_right_button))
.inRoot(withDecorView(not(cta.getWindow().getDecorView())))
.perform(click());
verifyShowingTabStrip(cta, 4);
// Click on the current tab in strip to close, and the closure should trigger tab strip
// update.
onView(withId(R.id.tab_list_view))
.inRoot(withDecorView(not(cta.getWindow().getDecorView())))
.perform(RecyclerViewActions.actionOnItemAtPosition(
getCurrentTabIndexInGroup(cta), click()));
verifyShowingTabStrip(cta, 3);
onView(withId(R.id.tab_list_view))
.inRoot(withDecorView(not(cta.getWindow().getDecorView())))
.perform(RecyclerViewActions.actionOnItemAtPosition(
getCurrentTabIndexInGroup(cta), click()));
verifyShowingTabStrip(cta, 2);
}
@Test
@MediumTest
@CommandLineFlags.Add({ChromeSwitches.DISABLE_MINIMUM_SHOW_DURATION})
public void testTabStripChangeWithScrolling() {
launchActivity();
final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
FullscreenManagerTestUtils.disableBrowserOverrides();
// Tab strip should show automatically when entering tab group.
EmbeddedTestServer testServer =
EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
String url = testServer.getURL("/chrome/test/data/android/navigate/three.html");
createTabGroupAndEnterTabPage(cta, 2, url);
CriteriaHelper.pollInstrumentationThread(() -> isTabStripShowing(cta));
FullscreenManagerTestUtils.scrollBrowserControls(mActivityTestRule, false);
onView(withId(R.id.main_content))
.inRoot(withDecorView(not(cta.getWindow().getDecorView())))
.check((v, e) -> {
View stripContainerView = (View) v.getParent().getParent();
assertTrue(stripContainerView instanceof FrameLayout);
assertEquals(0f, stripContainerView.getAlpha(), 0);
});
FullscreenManagerTestUtils.scrollBrowserControls(mActivityTestRule, true);
onView(withId(R.id.main_content))
.inRoot(withDecorView(not(cta.getWindow().getDecorView())))
.check((v, e) -> {
View stripContainerView = (View) v.getParent().getParent();
assertTrue(stripContainerView instanceof FrameLayout);
assertEquals(1f, stripContainerView.getAlpha(), 0);
});
}
private void createTabGroupAndEnterTabPage(ChromeTabbedActivity cta, int tabCount, String url) {
if (url == null) {
createTabs(cta, false, tabCount);
} else {
prepareTabsWithThumbnail(mActivityTestRule, tabCount, 0, url);
}
enterTabSwitcher(cta);
verifyTabSwitcherCardCount(cta, tabCount);
mergeAllNormalTabsToAGroup(cta);
verifyTabSwitcherCardCount(cta, 1);
clickFirstCardFromTabSwitcher(cta);
clickFirstTabInDialog(cta);
assertFalse(cta.getLayoutManager().overviewVisible());
}
/**
* Try to show tab strip through long press the tab switcher button and verify the tab count in
* tab strip. If {@code count} is 0, verify that the strip is not showing.
* @param cta The current running activity.
* @param count The count of tabs in tab strip. Could be 0.
*/
private void triggerTabStripAndVerify(ChromeTabbedActivity cta, int count) {
TestThreadUtils.runOnUiThreadBlocking(
() -> { cta.findViewById(R.id.tab_switcher_button).performLongClick(); });
if (count == 0) {
CriteriaHelper.pollInstrumentationThread(() -> !isTabStripShowing(cta));
return;
}
CriteriaHelper.pollInstrumentationThread(() -> isTabStripShowing(cta));
verifyShowingTabStrip(cta, count);
}
private boolean isAnchoredOnTopToolbar(ChromeTabbedActivity cta) {
boolean isTop = true;
try {
onView(withId(R.id.tab_list_view))
.inRoot(withDecorView(not(cta.getWindow().getDecorView())))
.check((v, e) -> {
int[] tabStripPosition = new int[2];
int[] topToolbarPosition = new int[2];
v.getLocationOnScreen(tabStripPosition);
View topToolbarView = cta.findViewById(R.id.toolbar);
topToolbarView.getLocationOnScreen(topToolbarPosition);
// If strip is anchored on top toolbar, it should locate close to the top
// toolbar.
int anchorDistance = topToolbarView.getHeight();
assertTrue(tabStripPosition[1] - topToolbarPosition[1] < anchorDistance);
});
} catch (AssertionError e) {
isTop = false;
} catch (Exception e) {
assert false : "error when inspecting pop up tab list.";
}
return isTop;
}
private boolean isTabStripShowing(ChromeTabbedActivity cta) {
return isShowingPopupTabList(cta);
}
private void verifyShowingTabStrip(ChromeTabbedActivity cta, int tabCount) {
verifyShowingPopupTabList(cta, tabCount);
}
private int getCurrentTabIndexInGroup(ChromeTabbedActivity cta) {
Tab currentTab = cta.getTabModelSelector().getCurrentTab();
List<Tab> tabGroup = cta.getTabModelSelector()
.getTabModelFilterProvider()
.getCurrentTabModelFilter()
.getRelatedTabList(currentTab.getId());
return tabGroup.indexOf(currentTab);
}
private void launchActivity() {
launchActivity(Variations.HOME_SEARCH_TAB_SWITCHER);
}
private void launchActivity(@Variations String variation) {
BottomToolbarVariationManager.setVariation(variation);
mActivityTestRule.startMainActivityFromLauncher();
Layout layout = mActivityTestRule.getActivity().getLayoutManager().getOverviewLayout();
assertTrue(layout instanceof StartSurfaceLayout);
CriteriaHelper.pollUiThread(mActivityTestRule.getActivity()
.getTabModelSelector()
.getTabModelFilterProvider()
.getCurrentTabModelFilter()::isTabModelRestored);
}
}