// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <XCTest/XCTest.h>
#import "base/strings/sys_string_conversions.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/tabs/model/inactive_tabs/features.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_app_interface.h"
#import "ios/chrome/browser/ui/settings/tabs/tabs_settings_constants.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_constants.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_constants.h"
#import "ios/chrome/browser/ui/tab_switcher/test/query_title_server_util.h"
#import "ios/chrome/browser/ui/tab_switcher/test/tabs_egtest_util.h"
#import "ios/chrome/common/ui/confirmation_alert/constants.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/testing/earl_grey/app_launch_configuration.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#import "ios/testing/earl_grey/matchers.h"
#import "net/test/embedded_test_server/embedded_test_server.h"
#import "ui/base/l10n/l10n_util.h"
using chrome_test_util::AddToBookmarksButton;
using chrome_test_util::AddToReadingListButton;
using chrome_test_util::BackButton;
using chrome_test_util::CancelButton;
using chrome_test_util::CloseTabMenuButton;
using chrome_test_util::CopyActivityButton;
using chrome_test_util::NavigationBarDoneButton;
using chrome_test_util::ShareButton;
using chrome_test_util::TabGridSearchBar;
using chrome_test_util::TabGridSearchTabsButton;
namespace {
// Matcher for the Inactive Tabs button.
id<GREYMatcher> GetMatcherForInactiveTabsButton() {
return grey_accessibilityID(kInactiveTabsButtonAccessibilityIdentifier);
}
// Matcher for the Inactive Tabs grid.
id<GREYMatcher> GetMatcherForInactiveTabsGrid() {
return grey_accessibilityID(kInactiveTabGridIdentifier);
}
// Matcher for the Close All Inactive button in the Inactive Tabs grid.
id<GREYMatcher> GetMatcherForCloseAllInactiveButton() {
return grey_accessibilityID(kInactiveTabGridCloseAllButtonIdentifier);
}
// Matcher for the Close All Inactive button in the Inactive Tabs grid.
id<GREYMatcher> GetMatcherForCloseAllAction() {
NSString* closeAllActionTitle = l10n_util::GetNSString(
IDS_IOS_INACTIVE_TABS_CLOSE_ALL_CONFIRMATION_OPTION);
return chrome_test_util::AlertAction(closeAllActionTitle);
}
// Matcher for Inactive Tabs Settings link in the preamble of the Inactive Tabs
// grid.
id<GREYMatcher> GetMatcherForInactiveTabsSettingsLink() {
return grey_allOf(
// The link is within a grid view header of class
// `InactiveTabsPreambleHeader`.
grey_ancestor(grey_kindOfClassName(@"InactiveTabsPreambleHeader")),
// UIKit instantiates a `UIAccessibilityLinkSubelement` for the link
// element in the label with attributed string.
grey_kindOfClassName(@"UIAccessibilityLinkSubelement"),
grey_accessibilityTrait(UIAccessibilityTraitLink), nil);
}
// Matcher for Inactive Tabs Settings screen.
id<GREYMatcher> GetMatcherForInactiveTabsSettings() {
return chrome_test_util::SettingsInactiveTabsTableView();
}
// Matcher for Inactive Tabs Setting' Disabled option.
id<GREYMatcher> GetMatcherForSettingsDisabledOption() {
return grey_allOf(grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_OPTIONS_INACTIVE_TABS_DISABLED)),
grey_userInteractionEnabled(), nil);
}
// Matcher for Inactive Tabs User Education screen.
id<GREYMatcher> GetMatcherForInactiveTabsUserEducation() {
return grey_accessibilityID(
kInactiveTabsUserEducationAccessibilityIdentifier);
}
// Matcher for Inactive Tabs User Education screen's Done button.
id<GREYMatcher> GetMatcherForUserEducationDoneButton() {
return grey_accessibilityID(
kConfirmationAlertPrimaryActionAccessibilityIdentifier);
}
// Matcher for Inactive Tabs User Education screen's Settings button.
id<GREYMatcher> GetMatcherForUserEducationSettingsButton() {
return grey_accessibilityID(
kConfirmationAlertSecondaryActionAccessibilityIdentifier);
}
} // namespace
// Tests related to the Inactive Tabs feature.
@interface InactiveTabsTestCase : ChromeTestCase
@end
@implementation InactiveTabsTestCase
- (void)setUp {
[super setUp];
[self setUpTestServer];
// Just have an NTP, no other previous tabs.
[[self class] closeAllTabs];
[ChromeEarlGrey openNewTab];
// Ensure that inactive tabs preference settings is set to its default state.
[ChromeEarlGrey setIntegerValue:0
forLocalStatePref:prefs::kInactiveTabsTimeThreshold];
GREYAssertEqual(
0,
[ChromeEarlGrey localStateIntegerPref:prefs::kInactiveTabsTimeThreshold],
@"Inactive tabs preference is not set to default value.");
// Mark the User Education screen as already-seen by default.
[ChromeEarlGrey setUserDefaultsObject:@YES
forKey:kInactiveTabsUserEducationShownOnceKey];
}
// Sets up the EmbeddedTestServer as needed for tests.
- (void)setUpTestServer {
RegisterQueryTitleHandler(self.testServer);
GREYAssertTrue(self.testServer->Start(), @"Test server failed to start");
}
// Relaunches the app with Inactive Tabs still enabled.
- (void)relaunchAppWithInactiveTabsEnabled {
AppLaunchConfiguration config;
config.relaunch_policy = ForceRelaunchByCleanShutdown;
config.additional_args.push_back(
"--enable-features=" + std::string(kTabInactivityThreshold.name) + ":" +
kTabInactivityThresholdParameterName + "/" +
kTabInactivityThresholdImmediateDemoParam);
[[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
}
// Relaunches the app with Inactive Tabs disabled.
- (void)relaunchAppWithInactiveTabsDisabled {
AppLaunchConfiguration config;
config.relaunch_policy = ForceRelaunchByCleanShutdown;
[[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
}
#pragma mark - Tests
// Checks that when Inactive Tabs is not enabled, tabs are not moved to Inactive
// Tabs.
- (void)testInactiveTabDisabled {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
// Create tabs.
CreateRegularTabs(1, self.testServer);
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 2,
@"Main tab count should be 2");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 0,
@"Inactive tab count should be 0");
// Relaunch the app with Inactive Tabs disabled.
[self relaunchAppWithInactiveTabsDisabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// The Inactive Tabs button should not be visible.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
assertWithMatcher:grey_notVisible()];
// There should be no inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 2,
@"Main tab count should be 2");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 0,
@"Inactive tab count should be 0");
}
// Checks that when Inactive Tabs is enabled and old tabs are found, the
// Inactive Tabs button appears in the Tab Grid.
- (void)testActiveTabsMoveToInactive {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
// Create tabs.
CreateRegularTabs(1, self.testServer);
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 2,
@"Main tab count should be 2");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 0,
@"Inactive tab count should be 0");
// Relaunch the app.
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// The Inactive Tabs button should be visible.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
assertWithMatcher:grey_sufficientlyVisible()];
// There should be one inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
}
// Checks that when there are inactive tabs and the feature is disabled, the
// Inactive Tabs button no longer appears in the Tab Grid.
- (void)testInactiveTabsMoveToActiveWhenDisabling {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
// Create tabs.
CreateRegularTabs(1, self.testServer);
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 2,
@"Main tab count should be 2");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 0,
@"Inactive tab count should be 0");
// Relaunch the app.
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// The Inactive Tabs button should be visible.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
assertWithMatcher:grey_sufficientlyVisible()];
// There should be one inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
// Relaunch the app with Inactive Tabs disabled.
[self relaunchAppWithInactiveTabsDisabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// The Inactive Tabs button should not be visible.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
assertWithMatcher:grey_notVisible()];
// There should be no inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 2,
@"Main tab count should be 2");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 0,
@"Inactive tab count should be 0");
}
// Checks that NTPs are not moved.
- (void)testActiveTabsDontMoveNTP {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
[ChromeEarlGrey openNewTab];
[ChromeEarlGrey openNewTab];
[ChromeEarlGrey openNewTab];
[ChromeEarlGrey openNewTab];
// Relaunch the app.
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// The Inactive Tabs button should not be visible.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
assertWithMatcher:grey_notVisible()];
// There should be no inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 0,
@"Inactive tab count should be 0");
}
// Checks that inactive tabs can be found with tab search.
- (void)testInactiveTabInTabSearch {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
// Create tabs with titles.
CreateRegularTab(self.testServer, @"Tab1");
CreateRegularTab(self.testServer, @"Tab2");
CreateRegularTab(self.testServer, @"Tab3");
// Relaunch the app.
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// Enter search mode.
[[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
performAction:grey_tap()];
// Search for the title of the 3rd inactive tab.
[[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
performAction:grey_replaceText(@"Tab3")];
// Check that the tab is here.
[[EarlGrey selectElementWithMatcher:TabWithTitle(@"Tab3")]
assertWithMatcher:grey_sufficientlyVisible()];
}
// Checks that tapping on an inactive tab opens it.
- (void)testReactivateInactiveTab {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
CreateRegularTab(self.testServer, @"Tab1");
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// There should be one inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
// Enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
// Tap on the inactive tab.
[[EarlGrey selectElementWithMatcher:TabWithTitle(@"Tab1")]
performAction:grey_tap()];
// There should be no inactive tab anymore, it should have moved alongside the
// initial NTP.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 2,
@"Main tab count should be 2");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 0,
@"Inactive tab count should be 0");
}
// Checks that long-pressing on an inactive tab and closing it works as
// expected.
- (void)testCloseInactiveTabByLongPressing {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
CreateRegularTab(self.testServer, @"Tab1");
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// There should be one inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
// Enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
// Long press the tab.
[[EarlGrey selectElementWithMatcher:TabWithTitle(@"Tab1")]
performAction:grey_longPress()];
// Close the tab.
[[EarlGrey selectElementWithMatcher:CloseTabMenuButton()]
performAction:grey_tap()];
// There should be no inactive tab anymore, just the initial NTP.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 0,
@"Inactive tab count should be 0");
}
// Checks tap on X symbols closes the inactive tab.
- (void)testCloseInactiveTabByCellCloseSymbol {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
CreateRegularTab(self.testServer, @"Tab1");
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// There should be one inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
// Enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::
TabGridCloseButtonForCellAtIndex(0)]
performAction:grey_tap()];
// There should be no inactive tab anymore, just the initial NTP.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
[ChromeEarlGrey waitForInactiveTabCount:0];
}
// Checks that long-pressing on an inactive tab and sharing it opens the share
// sheet.
- (void)testShareInactiveTab {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
CreateRegularTab(self.testServer, @"Tab1");
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// There should be one inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
// Enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
// Long press the tab.
[[EarlGrey selectElementWithMatcher:TabWithTitle(@"Tab1")]
performAction:grey_longPress()];
// Share the tab.
[[EarlGrey selectElementWithMatcher:ShareButton()] performAction:grey_tap()];
// Check the presence of the Share sheet, for example by looking for the Copy
// button.
[ChromeEarlGrey tapButtonInActivitySheetWithID:@"Copy"];
// There should be still be an inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
}
// Checks that long-pressing on an inactive tab and bookmarking it opens the
// "added bookmark" snackbar.
- (void)testBookmarkInactiveTab {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
CreateRegularTab(self.testServer, @"Tab1");
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// There should be one inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
// Enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
// Long press the tab.
[[EarlGrey selectElementWithMatcher:TabWithTitle(@"Tab1")]
performAction:grey_longPress()];
NSString* snackbarMessage = base::SysUTF16ToNSString(
l10n_util::GetPluralStringFUTF16(IDS_IOS_BOOKMARKS_BULK_SAVED, 1));
WaitForSnackbarTriggeredByTappingItem(snackbarMessage,
AddToBookmarksButton());
// There should be still be an inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
}
// Checks that long-pressing on an inactive tab and adding it to the Reading
// List opens the "added to Reading List" snackbar.
- (void)testAddToReadingListInactiveTab {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
CreateRegularTab(self.testServer, @"Tab1");
[self relaunchAppWithInactiveTabsEnabled];
// Clear the Reading List.
GREYAssertNil([ReadingListAppInterface clearEntries],
@"Unable to clear Reading List entries");
GREYAssertEqual(0, [ReadingListAppInterface unreadEntriesCount],
@"Reading List should be empty");
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// There should be one inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
// Enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
// Long press the tab.
[[EarlGrey selectElementWithMatcher:TabWithTitle(@"Tab1")]
performAction:grey_longPress()];
// Tap Add to Reading List.
[[EarlGrey selectElementWithMatcher:AddToReadingListButton()]
performAction:grey_tap()];
// Check that the tab was added to Reading List.
GREYAssertEqual(1, [ReadingListAppInterface unreadEntriesCount],
@"Reading List should have one element");
// There should be still be an inactive tab.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
}
// Checks that the Close All Inactive button and confirmation dialog work as
// expected.
- (void)testCloseAllInactiveTabs {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
// Create tabs.
CreateRegularTabs(3, self.testServer);
// Relaunch the app.
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// There should be three inactive tabs.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 3,
@"Inactive tab count should be 3");
// Enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
// Tab the Close All Inactive button.
[[EarlGrey selectElementWithMatcher:GetMatcherForCloseAllInactiveButton()]
performAction:grey_tap()];
// Tap Cancel.
[[EarlGrey selectElementWithMatcher:CancelButton()] performAction:grey_tap()];
// There should still be three inactive tabs.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 3,
@"Inactive tab count should be 3");
// Tab the Close All Inactive button again.
[[EarlGrey selectElementWithMatcher:GetMatcherForCloseAllInactiveButton()]
performAction:grey_tap()];
// Tap Close All.
[[EarlGrey selectElementWithMatcher:GetMatcherForCloseAllAction()]
performAction:grey_tap()];
// There should be no inactive tab anymore, just the initial NTP.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 0,
@"Inactive tab count should be 0");
// The Inactive Tabs grid should no longer be visible.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsGrid()]
assertWithMatcher:grey_notVisible()];
// The Inactive Tabs button should not be visible.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
assertWithMatcher:grey_notVisible()];
}
// Checks that tapping the Settings link from the Inactive Tabs grid preamble
// opens Inactive Tabs Settings.
- (void)testSettingsFromPreamble {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
CreateRegularTabs(1, self.testServer);
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// There should be one inactive tab, and the active NTP.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
// Enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
// Tap on the settings link from the preamble.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsSettingsLink()]
performAction:grey_tap()];
// Check that Inactive Tabs Settings are open.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsSettings()]
assertWithMatcher:grey_sufficientlyVisible()];
// Dismiss the settings screen.
[[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
performAction:grey_tap()];
// The Inactive Tabs grid should be visible again.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsGrid()]
assertWithMatcher:grey_sufficientlyVisible()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::RegularTabGrid()]
assertWithMatcher:grey_notVisible()];
}
// Checks that changing settings when presented from the Inactive Tabs grid
// updates the grid, and pops it when there are no inactive tabs anymore.
- (void)testSettingsChangesPopsInactiveTabs {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
CreateRegularTabs(1, self.testServer);
[self relaunchAppWithInactiveTabsEnabled];
[ChromeEarlGreyUI openTabGrid];
// Enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
// There should be one inactive tab, and the active NTP.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 1,
@"Main tab count should be 1");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 1,
@"Inactive tab count should be 1");
// Tap on the settings link from the preamble.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsSettingsLink()]
performAction:grey_tap()];
// Disable Inactive Tabs and dismiss the settings screen.
[[EarlGrey selectElementWithMatcher:GetMatcherForSettingsDisabledOption()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
performAction:grey_tap()];
// The Inactive Tabs grid should no longer be visible.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsGrid()]
assertWithMatcher:grey_notVisible()];
// There should be no inactive tab, just 2 active tabs.
GREYAssertTrue([ChromeEarlGrey mainTabCount] == 2,
@"Main tab count should be 2");
GREYAssertTrue([ChromeEarlGrey incognitoTabCount] == 0,
@"Incognito tab count should be 0");
GREYAssertTrue([ChromeEarlGrey inactiveTabCount] == 0,
@"Inactive tab count should be 0");
}
// Checks that the count of inactive tabs appears.
- (void)testShowCount {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
CreateRegularTabs(3, self.testServer);
[self relaunchAppWithInactiveTabsEnabled];
// Open the Tab Grid.
[ChromeEarlGreyUI openTabGrid];
// The Inactive Tabs count should be appended at the end of the button's
// label.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
assertWithMatcher:grey_accessibilityLabel(
@"Inactive Tabs, Tabs not used for 0 days, 3")];
}
// Checks that the User Education panel only appears the first time Inactive
// Tabs are opened.
- (void)testUserEducationAppearsOnce {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
// Reset the User-Education marker.
[ChromeEarlGrey
removeUserDefaultsObjectForKey:kInactiveTabsUserEducationShownOnceKey];
// Set up one inactive tab.
CreateRegularTabs(1, self.testServer);
[self relaunchAppWithInactiveTabsEnabled];
[ChromeEarlGreyUI openTabGrid];
// Enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
// The user education screen is shown.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsUserEducation()]
assertWithMatcher:grey_sufficientlyVisible()];
// Dismiss it, go back and re-enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForUserEducationDoneButton()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:testing::NavigationBarBackButton()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
// The user education screen is not shown.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsUserEducation()]
assertWithMatcher:grey_nil()];
}
// Checks that Settings can be opened from the User Education panel.
- (void)testUserEducationOpenSettings {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
@"only supported on iPhone.");
}
// Reset the User-Education marker.
[ChromeEarlGrey
removeUserDefaultsObjectForKey:kInactiveTabsUserEducationShownOnceKey];
// Set up one inactive tab.
CreateRegularTabs(1, self.testServer);
[self relaunchAppWithInactiveTabsEnabled];
[ChromeEarlGreyUI openTabGrid];
// Enter the Inactive Tabs grid.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsButton()]
performAction:grey_tap()];
// The user education screen is shown.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsUserEducation()]
assertWithMatcher:grey_sufficientlyVisible()];
// Go to settings from that screen.
[[EarlGrey
selectElementWithMatcher:GetMatcherForUserEducationSettingsButton()]
performAction:grey_tap()];
// Check that Inactive Tabs Settings are open.
[[EarlGrey selectElementWithMatcher:GetMatcherForInactiveTabsSettings()]
assertWithMatcher:grey_sufficientlyVisible()];
}
@end