chromium/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "base/containers/contains.h"
#import "base/format_macros.h"
#import "base/i18n/message_formatter.h"
#import "base/ios/ios_util.h"
#import "base/strings/string_util.h"
#import "base/strings/stringprintf.h"
#import "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "base/time/time.h"
#import "components/bookmarks/common/bookmark_pref_names.h"
#import "components/commerce/core/proto/price_tracking.pb.h"
#import "components/unified_consent/pref_names.h"
#import "ios/chrome/browser/bookmarks/model/bookmark_storage_type.h"
#import "ios/chrome/browser/bookmarks/ui_bundled/bookmark_earl_grey.h"
#import "ios/chrome/browser/history/ui_bundled/history_ui_constants.h"
#import "ios/chrome/browser/metrics/model/metrics_app_interface.h"
#import "ios/chrome/browser/optimization_guide/model/optimization_guide_test_app_interface.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/signin/model/fake_system_identity.h"
#import "ios/chrome/browser/start_surface/ui_bundled/start_surface_features.h"
#import "ios/chrome/browser/tabs/model/inactive_tabs/features.h"
#import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h"
#import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_app_interface.h"
#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_app_interface.h"
#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_constants.h"
#import "ios/chrome/browser/ui/settings/settings_app_interface.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_constants.h"
#import "ios/chrome/browser/ui/tab_switcher/test/tabs_egtest_util.h"
#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/earl_grey/chrome_actions.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_xcui_actions.h"
#import "ios/chrome/test/earl_grey/web_http_server_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/web/public/test/http_server/data_response_provider.h"
#import "ios/web/public/test/http_server/http_server.h"
#import "ios/web/public/test/http_server/http_server_util.h"
#import "net/base/apple/url_conversions.h"
#import "ui/base/device_form_factor.h"
#import "ui/base/l10n/l10n_util.h"

using base::test::ios::kWaitForUIElementTimeout;
using chrome_test_util::AddToBookmarksButton;
using chrome_test_util::AddToReadingListButton;
using chrome_test_util::CloseTabMenuButton;
using chrome_test_util::IncognitoTabGrid;
using chrome_test_util::LongPressCellAndDragToEdge;
using chrome_test_util::LongPressCellAndDragToOffsetOf;
using chrome_test_util::RegularTabGrid;
using chrome_test_util::TabGridCellAtIndex;
using chrome_test_util::TabGridEditMenuCloseAllButton;
using chrome_test_util::TabGridInactiveTabsButton;
using chrome_test_util::TabGridIncognitoTabsPanelButton;
using chrome_test_util::TabGridNormalModePageControl;
using chrome_test_util::TabGridOpenTabsPanelButton;
using chrome_test_util::TabGridOtherDevicesPanelButton;
using chrome_test_util::TabGridSearchBar;
using chrome_test_util::TabGridSearchCancelButton;
using chrome_test_util::TabGridSearchModeToolbar;
using chrome_test_util::TabGridSearchTabsButton;
using chrome_test_util::TabGridSelectTabsMenuButton;
using chrome_test_util::TabGridTabGroupsPanelButton;
using chrome_test_util::TabGridThirdPanelButton;
using chrome_test_util::TapAtOffsetOf;
using chrome_test_util::WindowWithNumber;

namespace {
const char kSearchEngineURL[] = "http://searchengine/?q={searchTerms}";
const char kSearchEngineHost[] = "searchengine";

char kURL1[] = "http://firstURL";
char kURL2[] = "http://secondURL";
char kURL3[] = "http://thirdURL";
char kURL4[] = "http://fourthURL";
NSString* const kTitle1 = @"Page one";
NSString* const kTitle2 = @"Page two";
NSString* const kTitle4 = @"Page four";
char kResponse1[] = "Test Page 1 content";
char kResponse2[] = "Test Page 2 content";
char kResponse3[] = "Test Page 3 content";
char kResponse4[] = "Test Page 4 content";
NSString* const kTypeURL =
    @"type.googleapis.com/optimization_guide.proto.PriceTrackingData";
const int64_t kCurrentPriceMicros = 5'000'000;
const int64_t kPreviousPriceMicros = 10'000'000;
const int64_t kOfferId = 50;
const char kCurrencyCode[] = "USD";
NSString* const kExpectedCurrentPrice = @"$5.00";
NSString* const kExpectedPreviousPrice = @"$10";

id<GREYMatcher> RecentlyClosedTabWithTitle(NSString* title) {
  return grey_allOf(grey_ancestor(grey_accessibilityID(
                        kRecentTabsTableViewControllerAccessibilityIdentifier)),
                    chrome_test_util::StaticTextWithAccessibilityLabel(title),
                    grey_sufficientlyVisible(), nil);
}

id<GREYMatcher> RecentlyClosedTabsSectionHeader() {
  return grey_allOf(chrome_test_util::HeaderWithAccessibilityLabelId(
                        IDS_IOS_RECENT_TABS_RECENTLY_CLOSED),
                    grey_sufficientlyVisible(), nil);
}

// Identifer for cell at given `index` in the tab grid.
NSString* IdentifierForCellAtIndex(unsigned int index) {
  return [NSString stringWithFormat:@"%@%u", kGridCellIdentifierPrefix, index];
}

id<GREYMatcher> DeselectAllButton() {
  return grey_allOf(chrome_test_util::ButtonWithAccessibilityLabelId(
                        IDS_IOS_TAB_GRID_DESELECT_ALL_BUTTON),
                    grey_userInteractionEnabled(), nullptr);
}

id<GREYMatcher> SelectAllButton() {
  return grey_allOf(chrome_test_util::ButtonWithAccessibilityLabelId(
                        IDS_IOS_TAB_GRID_SELECT_ALL_BUTTON),
                    grey_userInteractionEnabled(), nullptr);
}

id<GREYMatcher> VisibleTabGridEditButton() {
  return grey_allOf(chrome_test_util::TabGridEditButton(),
                    grey_sufficientlyVisible(), nil);
}

// Returns the matcher for the Recent Tabs table.
id<GREYMatcher> RecentTabsTable() {
  return grey_allOf(grey_accessibilityID(
                        kRecentTabsTableViewControllerAccessibilityIdentifier),
                    grey_sufficientlyVisible(), nil);
}

// Returns a matcher for the scrim view on the tab search.
id<GREYMatcher> VisibleSearchScrim() {
  return grey_allOf(grey_accessibilityID(kTabGridScrimIdentifier),
                    grey_minimumVisiblePercent(0.5), nil);
}

// Returns a matcher for the search bar text field containing `searchText`.
id<GREYMatcher> SearchBarWithSearchText(NSString* searchText) {
  return grey_accessibilityID([kTabGridSearchTextFieldIdentifierPrefix
      stringByAppendingString:searchText]);
}

// Returns a matcher for the search results header with title set with
// `title_id`.
id<GREYMatcher> SearchSectionHeaderWithTitleID(int title_id) {
  id<GREYMatcher> title_matcher =
      grey_allOf(grey_accessibilityLabel(l10n_util::GetNSString(title_id)),
                 grey_sufficientlyVisible(), nil);
  return grey_allOf(grey_accessibilityID(kGridSectionHeaderIdentifier),
                    grey_descendant(title_matcher), grey_sufficientlyVisible(),
                    nil);
}

// Returns a matcher for the search results open tabs section header.
id<GREYMatcher> SearchOpenTabsSectionHeader() {
  return SearchSectionHeaderWithTitleID(
      IDS_IOS_TABS_SEARCH_OPEN_TABS_SECTION_HEADER_TITLE);
}

// Returns a matcher for the search results suggested actions section header.
id<GREYMatcher> SearchSuggestedActionsSectionHeader() {
  return SearchSectionHeaderWithTitleID(IDS_IOS_TABS_SEARCH_SUGGESTED_ACTIONS);
}

// Returns a matcher for the search results open tabs section header with
// `count` set in the value label .
id<GREYMatcher> SearchOpenTabsHeaderWithValue(size_t count) {
  NSString* count_str = [NSString stringWithFormat:@"%" PRIuS, count];
  NSString* value = l10n_util::GetNSStringF(
      IDS_IOS_TABS_SEARCH_OPEN_TABS_COUNT, base::SysNSStringToUTF16(count_str));
  id<GREYMatcher> value_matcher = grey_allOf(grey_accessibilityLabel(value),
                                             grey_sufficientlyVisible(), nil);

  return grey_allOf(SearchOpenTabsSectionHeader(),
                    grey_descendant(value_matcher), grey_sufficientlyVisible(),
                    nil);
}

// Returns a matcher for the "Search on web" suggested action.
id<GREYMatcher> SearchOnWebSuggestedAction() {
  return grey_allOf(chrome_test_util::StaticTextWithAccessibilityLabelId(
                        IDS_IOS_TABS_SEARCH_SUGGESTED_ACTION_SEARCH_WEB),
                    grey_sufficientlyVisible(), nil);
}

// Returns a matcher for the "Search recent tabs" suggested action.
id<GREYMatcher> SearchRecentTabsSuggestedAction() {
  return grey_allOf(
      chrome_test_util::StaticTextWithAccessibilityLabelId(
          IDS_IOS_TABS_SEARCH_SUGGESTED_ACTION_SEARCH_RECENT_TABS),
      grey_sufficientlyVisible(), nil);
}

// Returns a matcher for the "Search open tabs" suggested action.
id<GREYMatcher> SearchOpenTabsSuggestedAction() {
  return grey_allOf(chrome_test_util::StaticTextWithAccessibilityLabelId(
                        IDS_IOS_TABS_SEARCH_SUGGESTED_ACTION_SEARCH_OPEN_TABS),
                    grey_sufficientlyVisible(), nil);
}

// Returns a matcher for the "Search history" suggested action.
id<GREYMatcher> SearchHistorySuggestedAction() {
  return grey_allOf(
      grey_accessibilityID(kTableViewTabsSearchSuggestedHistoryItemId),
      grey_sufficientlyVisible(), nil);
}

// Returns a matcher for the "Search history (`matches_count` Found)" suggested
// action on the regular tab grid.
id<GREYMatcher> SearchHistorySuggestedActionWithMatches(size_t matches_count) {
  NSString* count_str = [NSString stringWithFormat:@"%" PRIuS, matches_count];
  NSString* history_label = l10n_util::GetNSStringF(
      IDS_IOS_TABS_SEARCH_SUGGESTED_ACTION_SEARCH_HISTORY,
      base::SysNSStringToUTF16(count_str));
  return grey_allOf(grey_accessibilityLabel(history_label),
                    grey_sufficientlyVisible(), nil);
}

// Returns a matcher for the "Search history (`matches_count` Found)" suggested
// action on the recent tabs page.
id<GREYMatcher> RecentTabsSearchHistorySuggestedActionWithMatches(
    size_t matches_count) {
  return grey_allOf(
      RecentTabsTable(),
      grey_descendant(SearchHistorySuggestedActionWithMatches(matches_count)),
      nil);
}

// Returns a matcher for the search suggested actions section.
id<GREYMatcher> SearchSuggestedActionsSection() {
  return grey_allOf(grey_accessibilityID(kSuggestedActionsGridCellIdentifier),
                    grey_sufficientlyVisible(), nil);
}

// Returns a matcher for the search suggested actions section with the history
// item matches count set to `matches_count`.
id<GREYMatcher> SearchSuggestedActionsSectionWithHistoryMatchesCount(
    size_t matches_count) {
  return grey_allOf(
      SearchSuggestedActionsSection(),
      grey_descendant(SearchHistorySuggestedActionWithMatches(matches_count)),
      grey_sufficientlyVisible(), nil);
}
// Matcher for the select tabs button in the context menu.
id<GREYMatcher> SelectTabsContextMenuItem() {
  return chrome_test_util::ContextMenuItemWithAccessibilityLabelId(
      IDS_IOS_CONTENT_CONTEXT_SELECTTABS);
}

// Type `text` into the TabGridSearchBar and press enter.
void PerformTabGridSearch(NSString* text) {
  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(text)];
  // TODO(crbug.com/40916974): Use simulatePhysicalKeyboardEvent until
  // replaceText can properly handle \n.
  [ChromeEarlGrey simulatePhysicalKeyboardEvent:@"\n" flags:0];
}

#pragma mark - TestResponseProvider

// A ResponseProvider that provides html responses of the requested URL for
// requests to `kSearchEngineHost`.
class EchoURLDefaultSearchEngineResponseProvider
    : public web::DataResponseProvider {
 public:
  bool CanHandleRequest(const Request& request) override;
  void GetResponseHeadersAndBody(
      const Request& request,
      scoped_refptr<net::HttpResponseHeaders>* headers,
      std::string* response_body) override;
};

bool EchoURLDefaultSearchEngineResponseProvider::CanHandleRequest(
    const Request& request) {
  return base::Contains(request.url.spec(), kSearchEngineHost);
}

void EchoURLDefaultSearchEngineResponseProvider::GetResponseHeadersAndBody(
    const Request& request,
    scoped_refptr<net::HttpResponseHeaders>* headers,
    std::string* response_body) {
  const GURL& url = request.url;
  *headers = web::ResponseProvider::GetDefaultResponseHeaders();
  std::string url_string = base::ToLowerASCII(url.spec());
  *response_body =
      base::StringPrintf("<html><body>%s</body></html>", url_string.c_str());
}

}  // namespace

@interface TabGridTestCase : WebHttpServerChromeTestCase {
  GURL _URL1;
  GURL _URL2;
  GURL _URL3;
  GURL _URL4;
}
@end

@implementation TabGridTestCase

- (void)setUp {
  [super setUp];

  _URL1 = web::test::HttpServer::MakeUrl(kURL1);
  _URL2 = web::test::HttpServer::MakeUrl(kURL2);
  _URL3 = web::test::HttpServer::MakeUrl(kURL3);
  _URL4 = web::test::HttpServer::MakeUrl(kURL4);

  std::map<GURL, std::string> responses;
  const char kPageFormat[] = "<head><title>%s</title></head><body>%s</body>";
  responses[_URL1] = base::StringPrintf(
      kPageFormat, base::SysNSStringToUTF8(kTitle1).c_str(), kResponse1);
  responses[_URL2] = base::StringPrintf(
      kPageFormat, base::SysNSStringToUTF8(kTitle2).c_str(), kResponse2);
  // Page 3 does not have <title> tag, so URL will be its title.
  responses[_URL3] = kResponse3;
  responses[_URL4] = base::StringPrintf(
      kPageFormat, base::SysNSStringToUTF8(kTitle4).c_str(), kResponse4);
  web::test::SetUpSimpleHttpServer(responses);
}

- (void)tearDown {
  // Ensure that the default search engine is reset.
  if ([self isRunningTest:@selector
            (testSearchOnWebSuggestedActionInRegularTabsSearch)] ||
      [self isRunningTest:@selector
            (testSearchOnWebSuggestedActionInRecentTabsSearch)]) {
    [SettingsAppInterface resetSearchEngine];
  }

  // Ensure that pref set in testTabGridItemContextMenuAddToBookmarkGreyed is
  // reset even if the test failed.
  if ([self isRunningTest:@selector
            (testTabGridItemContextMenuAddToBookmarkGreyed)]) {
    [ChromeEarlGrey setBoolValue:YES
                     forUserPref:bookmarks::prefs::kEditBookmarksEnabled];
  }

  // Shutdown network process after tests run to avoid hanging from
  // clearing browsing data.
  // See https://crbug.com/1419875.
  [ChromeEarlGrey killWebKitNetworkProcess];

  // Wait for the end of sign-out before starting following tests.
  // See https://crbug.com/1448618.
  // Should be removed after TODO(crbug.com/40065405).
  if ([self isRunningTest:@selector
            (DISABLED_testSyncSpinnerDismissedInRecentlyClosedTabs)]) {
    [ChromeEarlGrey signOutAndClearIdentities];
  }

  [super tearDown];
}

// Tests entering and leaving the tab grid.
- (void)testEnteringAndLeavingTabGrid {
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];
}

// Tests that tapping on the first cell shows that tab.
- (void)testTappingOnFirstCell {
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that closing the cell shows no tabs, and displays the empty state.
- (void)testClosingFirstCell {
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridCloseButtonForCellAtIndex(0)]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridRegularTabsEmptyStateView()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that tapping Close All shows no tabs, shows Undo button, and displays
// the empty state, and ensures that it doesn't affect the other tab grid. Then
// tests tapping Undo shows Close All button again. Validates this case when Tab
// Grid Bulk Actions feature is enabled.
- (void)testCloseAllAndUndoCloseAll {
  // Also add a tab in incognito.
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey waitForIncognitoTabCount:1];

  // Open tab grid and go to regular tab page.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridNormalModePageControl()]
      performAction:grey_tap()];

  // Close all tabs.
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridEditMenuCloseAllButton()]
      performAction:grey_tap()];

  // Ensure normal tabs were closed.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      assertWithMatcher:grey_nil()];

  // Ensure the incognito tab isn't closed.
  GREYAssertEqual(1, [ChromeEarlGrey incognitoTabCount],
                  @"Expected that the \"Close All Tabs\" button should not "
                  @"close tabs in other pages.");

  // Ensure undo button is visible and edit button is not visible.
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridUndoCloseAllButton()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      assertWithMatcher:grey_nil()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridRegularTabsEmptyStateView()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Tap Undo button.
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridUndoCloseAllButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that tapping Close All also closes inactive tabs. Ensures it is
// correctly recovered when pressing undo and there is no selection mode when
// there are inactive tabs but no regular tabs.
- (void)testCloseAllAndUndoCloseAllWithInactiveTabs {
  if ([ChromeEarlGrey isIPadIdiom]) {
    EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
                           @"only supported on iPhone.");
  }
  [self loadTestURLsInNewTabs];
  [self relaunchAppWithInactiveTabsEnabled];

  [ChromeEarlGreyUI openTabGrid];
  GREYAssertEqual(1, [ChromeEarlGrey mainTabCount],
                  @"Expected only one tab (NTP), all other tabs should have "
                  @"been in inactive tab grid.");
  GREYAssertEqual(4, [ChromeEarlGrey inactiveTabCount],
                  @"Expected 4 inactive tabs.");

  // Verify that the Inactive Tabs button is showing.
  [[EarlGrey selectElementWithMatcher:TabGridInactiveTabsButton()]
      assertWithMatcher:grey_notNil()];

  // Ensure the edit button is visible.
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Close all tabs.
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridEditMenuCloseAllButton()]
      performAction:grey_tap()];

  // Ensure regular and inactive tabs were closed.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridRegularTabsEmptyStateView()]
      assertWithMatcher:grey_sufficientlyVisible()];
  GREYAssertEqual(0, [ChromeEarlGrey mainTabCount],
                  @"Expected all regular tab to be closed.");
  GREYAssertEqual(0, [ChromeEarlGrey inactiveTabCount],
                  @"Expected all inactive tab to be closed.");

  // Verify that the Inactive Tabs button is not showing.
  [[EarlGrey selectElementWithMatcher:TabGridInactiveTabsButton()]
      assertWithMatcher:grey_nil()];

  // Tap Undo button.
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridUndoCloseAllButton()]
      performAction:grey_tap()];
  GREYAssertEqual(1, [ChromeEarlGrey mainTabCount],
                  @"Expected only one tab (NTP), all other tabs should have "
                  @"been in inactive tab grid.");
  GREYAssertEqual(4, [ChromeEarlGrey inactiveTabCount],
                  @"Expected 4 inactive tabs.");
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Verify that the Inactive Tabs button is showing again.
  [[EarlGrey selectElementWithMatcher:TabGridInactiveTabsButton()]
      assertWithMatcher:grey_notNil()];

  // Closing the only tab in the regular tab grid and verify there is an empty
  // grid.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridCloseButtonForCellAtIndex(0)]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridRegularTabsEmptyStateView()]
      assertWithMatcher:grey_sufficientlyVisible()];
  GREYAssertEqual(0, [ChromeEarlGrey mainTabCount],
                  @"Expected no tab in regular tab grid.");
  GREYAssertEqual(4, [ChromeEarlGrey inactiveTabCount],
                  @"Expected 4 inactive tabs.");

  // Ensure there is no selection mode available when there is no tab in regular
  // tab grid.
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridSelectTabsMenuButton()]
      assertWithMatcher:grey_nil()];

  // Close all.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridEditMenuCloseAllButton()]
      performAction:grey_tap()];
  GREYAssertEqual(0, [ChromeEarlGrey mainTabCount],
                  @"Expected all regular tab to be closed.");
  GREYAssertEqual(0, [ChromeEarlGrey inactiveTabCount],
                  @"Expected all inactive tab to be closed.");

  // Tap on Undo button.
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridUndoCloseAllButton()]
      performAction:grey_tap()];
  GREYAssertEqual(0, [ChromeEarlGrey mainTabCount],
                  @"Expected no tab in regular tab grid.");
  GREYAssertEqual(4, [ChromeEarlGrey inactiveTabCount],
                  @"Expected 4 inactive tabs.");
}

- (void)testPriceDrops {
  commerce::PriceTrackingData price_tracking_data;
  price_tracking_data.mutable_product_update()->set_offer_id(kOfferId);
  price_tracking_data.mutable_product_update()
      ->mutable_old_price()
      ->set_currency_code(kCurrencyCode);
  price_tracking_data.mutable_product_update()
      ->mutable_new_price()
      ->set_currency_code(kCurrencyCode);
  price_tracking_data.mutable_product_update()
      ->mutable_new_price()
      ->set_amount_micros(kCurrentPriceMicros);
  price_tracking_data.mutable_product_update()
      ->mutable_old_price()
      ->set_amount_micros(kPreviousPriceMicros);

  std::string serialized_price_tracking_data;
  price_tracking_data.SerializeToString(&serialized_price_tracking_data);
  [OptimizationGuideTestAppInterface
      addHintForTesting:base::SysUTF8ToNSString(_URL1.spec())
                   type:optimization_guide::proto::OptimizationType::
                            PRICE_TRACKING
         serialized_any:[[NSData alloc]
                            initWithBytes:serialized_price_tracking_data.c_str()
                                   length:serialized_price_tracking_data.size()]
               type_url:kTypeURL];
  [ChromeEarlGrey setBoolValue:YES
                   forUserPref:unified_consent::prefs::
                                   kUrlKeyedAnonymizedDataCollectionEnabled];
  [ChromeEarlGrey setBoolValue:YES
                   forUserPref:prefs::kTrackPricesOnTabsEnabled];

  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey addFakeIdentity:fakeIdentity];
  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
  [ChromeEarlGreyUI openTabGrid];
  id<GREYMatcher> new_price = grey_text(kExpectedCurrentPrice);
  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:new_price];
  id<GREYMatcher> prev_price = grey_text(kExpectedPreviousPrice);
  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:prev_price];
}

// Tests that tapping Close All from the incognito grid shows no tabs and does
// not shows Undo button. Also ensure that it close the expected tabs to avoid
// crbug.com/1475005.
- (void)testCloseAllAndUndoCloseAllForIncognitoGrid {
  // Opens 3 incognito tabs and 1 regular.
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey openNewTab];

  [ChromeEarlGrey waitForMainTabCount:2];
  [ChromeEarlGrey waitForIncognitoTabCount:3];

  // Open the regular grid.
  [ChromeEarlGreyUI openTabGrid];

  // Scroll the tab grid to switch from regular grid to incognito grid.
  [[EarlGrey
      selectElementWithMatcher:grey_allOf(grey_accessibilityID(
                                              kTabGridScrollViewIdentifier),
                                          grey_sufficientlyVisible(), nil)]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeLeft)];

  // Close all incognito tabs
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridEditMenuCloseAllButton()]
      performAction:grey_tap()];

  // Ensure only incognito tabs were closed
  [ChromeEarlGrey waitForMainTabCount:2];
  [ChromeEarlGrey waitForIncognitoTabCount:0];

  // Ensure undo button is not visible and edit button is visible
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridUndoCloseAllButton()]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that the Undo button is no longer available after tapping Close All,
// then creating a new tab, then coming back to the tab grid.
// Validates this case when Tab Grid Bulk Actions feature is enabled.
- (void)testUndoCloseAllNotAvailableAfterNewTabCreation {
  [ChromeEarlGreyUI openTabGrid];

  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridEditMenuCloseAllButton()]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridUndoCloseAllButton()]
      assertWithMatcher:grey_sufficientlyVisible()];
  // Create a new tab then come back to tab grid.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridNewTabButton()]
      performAction:grey_tap()];

  [ChromeEarlGreyUI openTabGrid];
  // Undo is no longer available.
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridUndoCloseAllButton()]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests simulating a swipe with Voice Over from the third panel, making sure
// that the new tab button is working as expected.
- (void)testSwipeUsingVoiceOver {
  [ChromeEarlGreyUI openTabGrid];

  // Switch over to the third panel.
  [[EarlGrey selectElementWithMatcher:TabGridThirdPanelButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridNewTabButton()]
      assertWithMatcher:grey_nil()];

  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kTabGridScrollViewIdentifier)]
      performAction:chrome_test_util::AccessibilitySwipeRight()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridNewTabButton()]
      performAction:grey_tap()];
}

// Tests that Clear Browsing Data can be successfully done from tab grid.
- (void)FLAKY_testClearBrowsingData {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  // Load history.
  [self loadTestURLs];

  [ChromeEarlGreyUI openTabGrid];

  // Switch over to Recent Tabs.
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];

  // Tap on "Show History"
  // Undo is available after close all action.
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridSelectShowHistoryCell()]
      performAction:grey_tap()];
  [ChromeEarlGreyUI openAndClearBrowsingDataFromHistory];
  [ChromeEarlGreyUI assertHistoryHasNoEntries];
}

// Tests that the user interface style is respected after a drag and drop.
// TODO(crbug.com/332714545): Test is flaky.
- (void)FLAKY_testTraitCollection {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGreyUI openTabGrid];

  GREYAssert(chrome_test_util::LongPressCellAndDragToOffsetOf(
                 IdentifierForCellAtIndex(0), 0, IdentifierForCellAtIndex(1), 0,
                 CGVectorMake(0.5, 0.5)),
             @"Failed to DND cell on window");

  GREYMatchesBlock match = ^BOOL(UIView* element) {
    return element.traitCollection.userInterfaceStyle ==
           UIUserInterfaceStyleLight;
  };

  GREYDescribeToBlock describe = ^(id<GREYDescription> description) {
    [description appendText:@"Wrong style"];
  };

  id<GREYMatcher> matcher =
      [[GREYElementMatcherBlock alloc] initWithMatchesBlock:match
                                           descriptionBlock:describe];

  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          IdentifierForCellAtIndex(0))]
      assertWithMatcher:matcher];
}

#pragma mark - Recent Tabs

// Tests reopening a closed tab from an incognito tab.
- (void)testOpenCloseTabFromIncognito {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is only reachable via the tools menu in Regular mode. So the
  // test flow is not supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  // Close the tab, making it appear in Recent Tabs.
  [ChromeEarlGrey closeAllNormalTabs];

  [ChromeEarlGrey openNewIncognitoTab];

  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];

  [ChromeEarlGrey waitForMainTabCount:0];
  [ChromeEarlGrey waitForIncognitoTabCount:1];

  [[[EarlGrey
      selectElementWithMatcher:grey_allOf(grey_accessibilityLabel(kTitle1),
                                          grey_sufficientlyVisible(), nil)]
      atIndex:0] performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
      assertWithMatcher:chrome_test_util::OmniboxText(_URL1.GetContent())];

  [ChromeEarlGrey waitForMainTabCount:1];
  [ChromeEarlGrey waitForIncognitoTabCount:1];
}

// Tests that the Done button is disabled if there is no tab in the last active
// page. This also ensures that the last active page is the correct one
// (incognito if the last opened tab was incognito and regular if the last
// active page was regular), so the Done button opens a tab in the correct page.
// (Do not open a regular tab if the active page is an incognito one).
- (void)testRecentTabDoneButtonAndLastActivePage {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  // Load 1 regular tab.
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
  [ChromeEarlGrey waitForMainTabCount:1];
  [ChromeEarlGrey waitForIncognitoTabCount:0];

  // Open 1 incognito tab.
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];
  [ChromeEarlGrey waitForMainTabCount:1];
  [ChromeEarlGrey waitForIncognitoTabCount:1];

  // Go to regular and open the previously created tab.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridOpenTabsPanelButton()]
      performAction:grey_tap()];
  [self verifyVisibleTabsCount:1];
  [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle1, 0)]
      performAction:grey_tap()];

  // Go to the Recent Tabs panel and tap the Done button.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];

  // Ensure that we opened a regular tab as the last open tab was a regular one.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:RegularTabGrid()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Go to incognito and open the previously created tab.
  [[EarlGrey selectElementWithMatcher:TabGridIncognitoTabsPanelButton()]
      performAction:grey_tap()];
  [self verifyVisibleTabsCount:1];
  [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle2, 0)]
      performAction:grey_tap()];

  // Go to the Recent Tabs panel and tap the Done button.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];

  // Ensure that we opened an incognito tab as the last open tab was an
  // incognito one.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:IncognitoTabGrid()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Close the only incognito tab.
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:TabGridEditMenuCloseAllButton()]
      performAction:grey_tap()];
  [ChromeEarlGrey waitForMainTabCount:1];
  [ChromeEarlGrey waitForIncognitoTabCount:0];

  // Go to the Recent Tabs panel.
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];
  // Ensure the Done button is disabled.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      assertWithMatcher:grey_accessibilityTrait(
                            UIAccessibilityTraitNotEnabled)];
}

- (void)testTabGroupsDoneButtonAndRegularTabs {
  // When Tab Group Sync is disabled, Tab Groups is not present in Tab Grid.
  if (![ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Tab Groups panel is not available in Tab Grid when"
                           @"Tab Group Sync is disabled.");
  }

  // Load 1 regular tab.
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
  [ChromeEarlGrey waitForMainTabCount:1];
  [ChromeEarlGrey waitForIncognitoTabCount:0];

  // Go to regular and open the previously created tab.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridOpenTabsPanelButton()]
      performAction:grey_tap()];
  [self verifyVisibleTabsCount:1];
  [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle1, 0)]
      performAction:grey_tap()];

  // Go to the Tab Groups panel and tap the Done button.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridTabGroupsPanelButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];

  // Ensure that we opened a regular tab.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:RegularTabGrid()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Close the only regular tab.
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:TabGridEditMenuCloseAllButton()]
      performAction:grey_tap()];
  [ChromeEarlGrey waitForMainTabCount:0];
  [ChromeEarlGrey waitForIncognitoTabCount:0];

  // Go to the Tab Groups panel.
  [[EarlGrey selectElementWithMatcher:TabGridTabGroupsPanelButton()]
      performAction:grey_tap()];
  // Ensure the Done button is disabled.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      assertWithMatcher:grey_accessibilityTrait(
                            UIAccessibilityTraitNotEnabled)];
}

#pragma mark - Recent Tabs Context Menu

// Tests the Copy Link action on a Recent Tabs' context menu.
- (void)testRecentTabsContextMenuCopyLink {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  [self prepareRecentTabWithURL:_URL1 response:kResponse1];
  [self longPressTabWithTitle:kTitle1];

  [ChromeEarlGrey
      verifyCopyLinkActionWithText:[NSString
                                       stringWithUTF8String:_URL1.spec()
                                                                .c_str()]];
}

// Tests the Open in New Window action on a Recent Tabs' context menu.
- (void)testRecentTabsContextMenuOpenInNewWindow {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }
  if (![ChromeEarlGrey areMultipleWindowsSupported]) {
    EARL_GREY_TEST_DISABLED(@"Multiple windows can't be opened.");
  }

  [self prepareRecentTabWithURL:_URL1 response:kResponse1];
  [self longPressTabWithTitle:kTitle1];

  [ChromeEarlGrey verifyOpenInNewWindowActionWithContent:kResponse1];
}

// Tests the Share action on a Recent Tabs' context menu.
- (void)testRecentTabsContextMenuShare {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }
  [self prepareRecentTabWithURL:_URL1 response:kResponse1];
  [self longPressTabWithTitle:kTitle1];

  [ChromeEarlGrey verifyShareActionWithURL:_URL1 pageTitle:kTitle1];
}

// Tests the Serach action on a Recent Tabs.
- (void)testRecentTabsSearch {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }
  [self prepareRecentTabWithURL:_URL1 response:kResponse1];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Verify that search mode is active.
  [[EarlGrey selectElementWithMatcher:TabGridSearchModeToolbar()]
      assertWithMatcher:grey_notNil()];

  // Exit search mode.
  [[EarlGrey selectElementWithMatcher:VisibleSearchScrim()]
      performAction:grey_tap()];

  // Verify that normal mode is active.
  [[EarlGrey selectElementWithMatcher:TabGridNormalModePageControl()]
      assertWithMatcher:grey_notNil()];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Verify that search mode is active.
  [[EarlGrey selectElementWithMatcher:TabGridSearchModeToolbar()]
      assertWithMatcher:grey_notNil()];
}

#pragma mark - Tab Grid Item Context Menu

// Tests the Share action on a tab grid item's context menu.
- (void)testTabGridItemContextMenuShare {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGreyUI openTabGrid];

  [self longPressTabWithTitle:kTitle1];

  [ChromeEarlGrey verifyShareActionWithURL:_URL1 pageTitle:kTitle1];
}

// Tests the Add to Reading list action on a tab grid item's context menu.
- (void)testTabGridItemContextMenuAddToReadingList {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGreyUI openTabGrid];

  [self longPressTabWithTitle:kTitle1];

  [self waitForSnackBarMessage:IDS_IOS_READING_LIST_SNACKBAR_MESSAGE
      triggeredByTappingItemWithMatcher:AddToReadingListButton()];
}

// Tests the Add to Bookmarks action on a tab grid item's context menu.
- (void)testTabGridItemContextMenuAddToBookmarks {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [BookmarkEarlGrey waitForBookmarkModelLoaded];
  [ChromeEarlGreyUI openTabGrid];

  [self longPressTabWithTitle:kTitle1];

  NSString* snackbarMessage = base::SysUTF16ToNSString(
      l10n_util::GetPluralStringFUTF16(IDS_IOS_BOOKMARKS_BULK_SAVED, 1));
  [self waitForSnackBarMessageText:snackbarMessage
      triggeredByTappingItemWithMatcher:AddToBookmarksButton()];

  // The snackbar disappearance might have been faster than the context menu
  // disappearance. Wait for it to disappear.
  [self waitForContextMenuToDisappear];

  [self longPressTabWithTitle:kTitle1];

  [[EarlGrey selectElementWithMatcher:
                 chrome_test_util::ContextMenuItemWithAccessibilityLabelId(
                     IDS_IOS_TOOLS_MENU_EDIT_BOOKMARK)]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:
                 chrome_test_util::NavigationBarTitleWithAccessibilityLabelId(
                     IDS_IOS_BOOKMARK_EDIT_SCREEN_TITLE)]
      assertWithMatcher:grey_notNil()];

  [BookmarkEarlGrey
      verifyExistenceOfBookmarkWithURL:base::SysUTF8ToNSString(_URL1.spec())
                                  name:kTitle1
                             inStorage:BookmarkStorageType::kLocalOrSyncable];
}

// Tests that Add to Bookmarks action is greyed out when editBookmarksEnabled
// pref is set to false.
- (void)testTabGridItemContextMenuAddToBookmarkGreyed {
  [ChromeEarlGrey setBoolValue:NO
                   forUserPref:bookmarks::prefs::kEditBookmarksEnabled];

  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGreyUI openTabGrid];

  [self longPressTabWithTitle:kTitle1];
  [[EarlGrey selectElementWithMatcher:AddToBookmarksButton()]
      assertWithMatcher:grey_allOf(grey_notNil(),
                                   grey_accessibilityTrait(
                                       UIAccessibilityTraitNotEnabled),
                                   nil)];
  [ChromeEarlGrey setBoolValue:YES
                   forUserPref:bookmarks::prefs::kEditBookmarksEnabled];
}

// Tests the Share action on a tab grid item's context menu.
- (void)testTabGridItemContextCloseTab {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGreyUI openTabGrid];

  [self longPressTabWithTitle:kTitle1];

  // Close Tab.
  [[EarlGrey selectElementWithMatcher:CloseTabMenuButton()]
      performAction:grey_tap()];

  // Make sure that the tab is no longer present.
  [[EarlGrey selectElementWithMatcher:TabWithTitle(kTitle1)]
      assertWithMatcher:grey_nil()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridRegularTabsEmptyStateView()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testTabGridItemContextSelectTabs {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGreyUI openTabGrid];

  [self longPressTabWithTitle:kTitle1];

  [[EarlGrey selectElementWithMatcher:SelectTabsContextMenuItem()]
      performAction:grey_tap()];

  // Wait for the select all button to appear to confirm that edit mode was
  // entered.
  ConditionBlock condition = ^{
    NSError* error = nil;
    [[EarlGrey
        selectElementWithMatcher:chrome_test_util::TabGridEditSelectAllButton()]
        assertWithMatcher:grey_sufficientlyVisible()
                    error:&error];
    if (error == nil) {
      // Bypass egtest bug on iOS 15 in which the grid cell element briefly
      // unselectable.
      [[EarlGrey
          selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
          assertWithMatcher:grey_sufficientlyVisible()
                      error:&error];
    }
    return (error == nil);
  };
  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
                 kWaitForUIElementTimeout, condition),
             @"Wait for select all button to appear in tab grid mode.");

  // Confirm that the tab is selectable.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      performAction:grey_tap()];
  NSString* tabSelected = base::SysUTF16ToNSString(
      l10n_util::GetPluralStringFUTF16(IDS_IOS_TAB_GRID_SELECTED_TABS_TITLE,
                                       /*number=*/1));
  [[EarlGrey selectElementWithMatcher:grey_text(tabSelected)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

#pragma mark - Drag and drop in Multiwindow

// Tests that dragging a tab grid item to the edge opens a new window and that
// the tab is properly transferred, incuding navigation stack.
- (void)testDragAndDropAtEdgeToCreateNewWindow {
  if (![ChromeEarlGrey areMultipleWindowsSupported])
    EARL_GREY_TEST_SKIPPED(@"Multiple windows can't be opened.");

  // TODO(crbug.com/40752508): Test is failing on iPad devices and simulator.
  if ([ChromeEarlGrey isIPadIdiom]) {
    EARL_GREY_TEST_DISABLED(@"This test is failing.");
  }

  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
  [ChromeEarlGrey loadURL:_URL3];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey waitForMainTabCount:2 inWindowWithNumber:0];

  [ChromeEarlGreyUI openTabGrid];

  GREYWaitForAppToIdle(@"App failed to idle");

  GREYAssert(LongPressCellAndDragToEdge(IdentifierForCellAtIndex(0),
                                        kGREYContentEdgeRight, 0),
             @"Failed to DND cell");

  GREYWaitForAppToIdle(@"App failed to idle");

  // Assert two windows and the expected tabs in each.
  [ChromeEarlGrey waitForForegroundWindowCount:2];
  [ChromeEarlGrey waitForMainTabCount:1 inWindowWithNumber:0];
  [ChromeEarlGrey waitForMainTabCount:1 inWindowWithNumber:1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2
                             inWindowWithNumber:0];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3
                             inWindowWithNumber:1];

  // Navigate back on second window to check the navigation stack is intact.
  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(1)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
      performAction:grey_tap()];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1
                             inWindowWithNumber:1];

  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(0)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];
}

// Tests that dragging a tab grid incognito item to the edge opens a new window
// and that the tab is properly transferred, incuding navigation stack.
- (void)testIncognitoDragAndDropAtEdgeToCreateNewWindow {
  if (![ChromeEarlGrey areMultipleWindowsSupported])
    EARL_GREY_TEST_SKIPPED(@"Multiple windows can't be opened.");

  [ChromeEarlGrey closeAllNormalTabs];
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
  [ChromeEarlGrey loadURL:_URL3];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3];

  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey waitForIncognitoTabCount:2 inWindowWithNumber:0];

  [ChromeEarlGreyUI openTabGrid];

  GREYWaitForAppToIdle(@"App failed to idle");

  GREYAssert(LongPressCellAndDragToEdge(IdentifierForCellAtIndex(0),
                                        kGREYContentEdgeRight, 0),
             @"Failed to DND cell");

  GREYWaitForAppToIdle(@"App failed to idle");

  // Assert two windows and the expected tabs in each.
  [ChromeEarlGrey waitForForegroundWindowCount:2];
  [ChromeEarlGrey waitForIncognitoTabCount:1 inWindowWithNumber:0];
  [ChromeEarlGrey waitForIncognitoTabCount:1 inWindowWithNumber:1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2
                             inWindowWithNumber:0];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3
                             inWindowWithNumber:1];

  // Navigate back on second window to check the navigation stack is intact.
  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(1)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
      performAction:grey_tap()];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1
                             inWindowWithNumber:1];

  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(0)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];
}

// Tests dragging tab grid item between windows.
- (void)testDragAndDropBetweenWindows {
  if (![ChromeEarlGrey areMultipleWindowsSupported])
    EARL_GREY_TEST_SKIPPED(@"Multiple windows can't be opened.");

  // TODO(crbug.com/40868899): Test is failing on iPad devices and simulator.
  if ([ChromeEarlGrey isIPadIdiom]) {
    EARL_GREY_TEST_DISABLED(@"Test disabled on iPad.");
  }

  // Setup first window with tabs 1 and 2.
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey waitForMainTabCount:2 inWindowWithNumber:0];

  // Open second window.
  [ChromeEarlGrey openNewWindow];
  [ChromeEarlGrey waitUntilReadyWindowWithNumber:1];
  [ChromeEarlGrey waitForForegroundWindowCount:2];

  // Setup second window with tabs 3 and 4.
  [ChromeEarlGrey loadURL:_URL3 inWindowWithNumber:1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3
                             inWindowWithNumber:1];

  [ChromeEarlGrey openNewTabInWindowWithNumber:1];
  [ChromeEarlGrey loadURL:_URL4 inWindowWithNumber:1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse4
                             inWindowWithNumber:1];

  [ChromeEarlGrey waitForMainTabCount:2 inWindowWithNumber:1];

  // Open tab grid in both window.
  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(0)];
  [ChromeEarlGreyUI openTabGrid];

  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(1)];
  [ChromeEarlGreyUI openTabGrid];

  GREYWaitForAppToIdle(@"App failed to idle");

  // DnD first tab of left window to left edge of first tab in second window.
  // Note: move to left half of the destination tile, to avoid unwanted
  // scrolling that would happen closer to the left edge.
  GREYAssert(LongPressCellAndDragToOffsetOf(IdentifierForCellAtIndex(0), 0,
                                            IdentifierForCellAtIndex(0), 1,
                                            CGVectorMake(0.5, 0.5)),
             @"Failed to DND cell on cell");

  GREYWaitForAppToIdle(@"App failed to idle");

  [ChromeEarlGrey waitForMainTabCount:1 inWindowWithNumber:0];
  [ChromeEarlGrey waitForMainTabCount:3 inWindowWithNumber:1];

  // Move third cell of second window as second cell in first window.
  GREYAssert(LongPressCellAndDragToOffsetOf(IdentifierForCellAtIndex(2), 1,
                                            IdentifierForCellAtIndex(0), 0,
                                            CGVectorMake(1.0, 0.5)),
             @"Failed to DND cell on cell");

  GREYWaitForAppToIdle(@"App failed to idle");

  [ChromeEarlGrey waitForMainTabCount:2 inWindowWithNumber:0];
  [ChromeEarlGrey waitForMainTabCount:2 inWindowWithNumber:1];

  // Check content and order of tabs.
  [self fromGridCheckTabAtIndex:0 inWindowNumber:0 containsText:kResponse2];
  [self fromGridCheckTabAtIndex:1 inWindowNumber:0 containsText:kResponse4];
  [self fromGridCheckTabAtIndex:0 inWindowNumber:1 containsText:kResponse1];
  [self fromGridCheckTabAtIndex:1 inWindowNumber:1 containsText:kResponse3];

  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(0)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];

  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(1)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];
}

// Tests dragging incognito tab grid item between windows.
// TODO(crbug.com/40839724): Re-enable this test.
- (void)FLAKY_testDragAndDropIncognitoBetweenWindows {
  if (![ChromeEarlGrey areMultipleWindowsSupported])
    EARL_GREY_TEST_SKIPPED(@"Multiple windows can't be opened.");

  // Setup first window with one incognito tab.
  [ChromeEarlGrey closeAllNormalTabs];
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey waitForMainTabCount:0 inWindowWithNumber:0];
  [ChromeEarlGrey waitForIncognitoTabCount:1 inWindowWithNumber:0];

  // Open second window with main ntp.
  [ChromeEarlGrey openNewWindow];
  [ChromeEarlGrey waitUntilReadyWindowWithNumber:1];
  [ChromeEarlGrey waitForForegroundWindowCount:2];

  [ChromeEarlGrey waitForMainTabCount:1 inWindowWithNumber:1];

  // Open tab grid in both window.
  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(0)];
  [ChromeEarlGreyUI openTabGrid];

  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(1)];
  [ChromeEarlGreyUI openTabGrid];

  GREYWaitForAppToIdle(@"App failed to idle");

  // Try DnDing first incognito tab of left window to main tab panel on right
  // window.
  GREYAssert(LongPressCellAndDragToOffsetOf(IdentifierForCellAtIndex(0), 0,
                                            IdentifierForCellAtIndex(0), 1,
                                            CGVectorMake(1.0, 0.5)),
             @"Failed to DND cell on cell");

  GREYWaitForAppToIdle(@"App failed to idle");

  // It should fail and both windows should still have only one tab.
  [ChromeEarlGrey waitForIncognitoTabCount:1 inWindowWithNumber:0];
  [ChromeEarlGrey waitForMainTabCount:1 inWindowWithNumber:1];

  // Move second window to incognito tab panel.
  // Note: until reported bug is fixed in EarlGrey, grey_tap() doesn't always
  // work in second window, because it fails the visibility check.
  GREYAssert(TapAtOffsetOf(kTabGridIncognitoTabsPageButtonIdentifier, 1,
                           CGVectorMake(0.5, 0.5)),
             @"Failed to tap incognito panel button");

  // Try again to move tabs.
  GREYAssert(LongPressCellAndDragToOffsetOf(IdentifierForCellAtIndex(0), 0, nil,
                                            1, CGVectorMake(0.5, 0.5)),
             @"Failed to DND cell on window");

  GREYWaitForAppToIdle(@"App failed to idle");

  // Check that it worked and there are 2 incgnito tabs in second window.
  [ChromeEarlGrey waitForIncognitoTabCount:0 inWindowWithNumber:0];
  [ChromeEarlGrey waitForIncognitoTabCount:1 inWindowWithNumber:1];

  // Cleanup.
  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(0)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];

  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(1)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];
}

// Tests dragging tab grid item as URL between windows.
// TODO(crbug.com/40864920): Re-enable this test.
- (void)DISABLED_testDragAndDropURLBetweenWindows {
  if (![ChromeEarlGrey areMultipleWindowsSupported])
    EARL_GREY_TEST_SKIPPED(@"Multiple windows can't be opened.");

  // Setup first window with tabs 1 and 2.
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey waitForMainTabCount:2 inWindowWithNumber:0];

  // Open second window.
  [ChromeEarlGrey openNewWindow];
  [ChromeEarlGrey waitUntilReadyWindowWithNumber:1];
  [ChromeEarlGrey waitForForegroundWindowCount:2];

  // Setup second window with tab 3.
  [ChromeEarlGrey loadURL:_URL3 inWindowWithNumber:1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3
                             inWindowWithNumber:1];

  [ChromeEarlGrey waitForMainTabCount:1 inWindowWithNumber:1];

  // Open tab grid in first window.
  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(0)];
  [ChromeEarlGreyUI openTabGrid];

  GREYWaitForAppToIdle(@"App failed to idle");

  // DnD first tab of left window to second window.
  GREYAssert(LongPressCellAndDragToOffsetOf(IdentifierForCellAtIndex(0), 0, nil,
                                            1, CGVectorMake(0.5, 0.5)),
             @"Failed to DND cell on window");

  GREYWaitForAppToIdle(@"App failed to idle");

  // Tabs should not have changed.
  [ChromeEarlGrey waitForMainTabCount:2 inWindowWithNumber:0];
  [ChromeEarlGrey waitForMainTabCount:1 inWindowWithNumber:1];

  // Second window should show URL1
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1
                             inWindowWithNumber:1];

  // Navigate back to check the navigation stack is intact.
  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(1)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
      performAction:grey_tap()];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3
                             inWindowWithNumber:1];

  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(0)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];
}

// Tests dragging tab grid incognito item as URL to a main windows.
- (void)testDragAndDropIncognitoURLInMainWindow {
  if (![ChromeEarlGrey areMultipleWindowsSupported])
    EARL_GREY_TEST_SKIPPED(@"Multiple windows can't be opened.");

  // TODO(crbug.com/40868899): Test is failing on iPad devices and simulator.
  if ([ChromeEarlGrey isIPadIdiom]) {
    EARL_GREY_TEST_DISABLED(@"Test disabled on iPad.");
  }

  // Setup first window with one incognito tab 1.
  [ChromeEarlGrey closeAllNormalTabs];
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey waitForMainTabCount:0 inWindowWithNumber:0];
  [ChromeEarlGrey waitForIncognitoTabCount:1 inWindowWithNumber:0];

  // Open second window.
  [ChromeEarlGrey openNewWindow];
  [ChromeEarlGrey waitUntilReadyWindowWithNumber:1];
  [ChromeEarlGrey waitForForegroundWindowCount:2];

  // Setup second window with tab 3.
  [ChromeEarlGrey loadURL:_URL3 inWindowWithNumber:1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3
                             inWindowWithNumber:1];

  [ChromeEarlGrey waitForMainTabCount:1 inWindowWithNumber:1];

  // Open incognito tab grid in first window.
  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(0)];
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridIncognitoTabsPanelButton()]
      performAction:grey_tap()];

  GREYWaitForAppToIdle(@"App failed to idle");

  // DnD first tab of left window to second window.
  GREYAssert(LongPressCellAndDragToOffsetOf(IdentifierForCellAtIndex(0), 0, nil,
                                            1, CGVectorMake(0.5, 0.5)),
             @"Failed to DND cell on window");

  GREYWaitForAppToIdle(@"App failed to idle");

  // Tabs should not have changed.
  [ChromeEarlGrey waitForMainTabCount:0 inWindowWithNumber:0];
  [ChromeEarlGrey waitForIncognitoTabCount:1 inWindowWithNumber:0];
  [ChromeEarlGrey waitForMainTabCount:1 inWindowWithNumber:1];

  // Second window should show URL1
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1
                             inWindowWithNumber:1];

  // Navigate back to check the navigation stack is intact.
  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(1)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
      performAction:grey_tap()];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3
                             inWindowWithNumber:1];

  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(0)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];
}

// Tests dragging tab grid main item as URL to an incognito windows.
// TODO(crbug.com/40240640): Re-enable this test.
- (void)DISABLED_testDragAndDropMainURLInIncognitoWindow {
  if (![ChromeEarlGrey areMultipleWindowsSupported])
    EARL_GREY_TEST_SKIPPED(@"Multiple windows can't be opened.");

  // Setup first window with one incognito tab 1.
  [ChromeEarlGrey closeAllNormalTabs];
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey waitForMainTabCount:0 inWindowWithNumber:0];
  [ChromeEarlGrey waitForIncognitoTabCount:1 inWindowWithNumber:0];

  // Open second window.
  [ChromeEarlGrey openNewWindow];
  [ChromeEarlGrey waitUntilReadyWindowWithNumber:1];
  [ChromeEarlGrey waitForForegroundWindowCount:2];

  // Setup second window with tab 3.
  [ChromeEarlGrey loadURL:_URL3 inWindowWithNumber:1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3
                             inWindowWithNumber:1];

  [ChromeEarlGrey waitForMainTabCount:1 inWindowWithNumber:1];

  // Open incognito tab grid in first window.
  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(1)];
  [ChromeEarlGreyUI openTabGrid];

  GREYWaitForAppToIdle(@"App failed to idle");

  // DnD first tab of second window to first window.
  GREYAssert(LongPressCellAndDragToOffsetOf(IdentifierForCellAtIndex(0), 1, nil,
                                            0, CGVectorMake(0.5, 0.5)),
             @"Failed to DND cell on window");

  GREYWaitForAppToIdle(@"App failed to idle");

  // Tabs should not have changed.
  [ChromeEarlGrey waitForMainTabCount:0 inWindowWithNumber:0];
  [ChromeEarlGrey waitForIncognitoTabCount:1 inWindowWithNumber:0];
  [ChromeEarlGrey waitForMainTabCount:1 inWindowWithNumber:1];

  // First window should show URL3
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3
                             inWindowWithNumber:0];

  // Navigate back to check the navigation stack is intact.
  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(0)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
      performAction:grey_tap()];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1
                             inWindowWithNumber:0];

  [EarlGrey setRootMatcherForSubsequentInteractions:WindowWithNumber(1)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()]
      performAction:grey_tap()];
}

#pragma mark - Bulk Actions

// Tests closing a tab in the tab grid edit mode and that edit mode is exited
// after closing all tabs.
- (void)testTabGridBulkActionCloseTabs {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGreyUI openTabGrid];

  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridSelectTabsMenuButton()]
      performAction:grey_tap()];

  // Tap tab to select.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridEditCloseTabsButton()]
      performAction:grey_tap()];

  NSString* closeTabsButtonText =
      base::SysUTF16ToNSString(l10n_util::GetPluralStringFUTF16(
          IDS_IOS_TAB_GRID_CLOSE_ALL_TABS_CONFIRMATION,
          /*number=*/1));

  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabel(
                                   closeTabsButtonText)]
      performAction:grey_tap()];

  // Make sure that the tab is no longer present.
  [[EarlGrey selectElementWithMatcher:TabWithTitle(kTitle1)]
      assertWithMatcher:grey_nil()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridRegularTabsEmptyStateView()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Verify edit mode is exited.
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      assertWithMatcher:grey_notNil()];
}

// Tests selecting all items in the tab grid edit mode using the "Select all"
// button.
- (void)testTabGridBulkActionSelectAll {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL3];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3];

  [ChromeEarlGreyUI openTabGrid];

  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridSelectTabsMenuButton()]
      performAction:grey_tap()];

  // Tap "Select all" and close selected tabs.
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridEditSelectAllButton()]
      performAction:grey_tap()];
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridEditCloseTabsButton()]
      performAction:grey_tap()];
  NSString* closeTabsButtonText =
      base::SysUTF16ToNSString(l10n_util::GetPluralStringFUTF16(
          IDS_IOS_TAB_GRID_CLOSE_ALL_TABS_CONFIRMATION,
          /*number=*/3));
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabel(
                                   closeTabsButtonText)]
      performAction:grey_tap()];

  // Make sure that the tab grid is empty.
  [ChromeEarlGrey waitForMainTabCount:0 inWindowWithNumber:0];

  // Verify edit mode is exited.
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      assertWithMatcher:grey_notNil()];
}

// Tests deselecting all items in the tab grid edit mode using the "Deselect
// all" button.
- (void)testTabGridBulkActionDeselectAll {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL3];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3];

  [ChromeEarlGreyUI openTabGrid];

  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridSelectTabsMenuButton()]
      performAction:grey_tap()];

  // Ensure button label is "Select All" and select all items.
  [[EarlGrey selectElementWithMatcher:SelectAllButton()]
      performAction:grey_tap()];

  // Deselect all button should be visible when all items are selected.
  // Tapping deselect all button should deselect all items.
  [[EarlGrey selectElementWithMatcher:DeselectAllButton()]
      performAction:grey_tap()];

  // Verify deselection by manually tapping each item (to re-select) and closing
  // the selected items.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(1)]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(2)]
      performAction:grey_tap()];

  // All tabs should have been re-selected and closing selected tabs should
  // empty the tab grid.
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridEditCloseTabsButton()]
      performAction:grey_tap()];
  NSString* closeTabsButtonText =
      base::SysUTF16ToNSString(l10n_util::GetPluralStringFUTF16(
          IDS_IOS_TAB_GRID_CLOSE_ALL_TABS_CONFIRMATION,
          /*number=*/3));
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabel(
                                   closeTabsButtonText)]
      performAction:grey_tap()];

  // Make sure that the tab grid is empty.
  [ChromeEarlGrey waitForMainTabCount:0 inWindowWithNumber:0];
}

// Tests adding items to Bookmarks from the tab grid edit mode.
- (void)testTabGridBulkActionAddToBookmarks {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL4];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse4];

  [BookmarkEarlGrey waitForBookmarkModelLoaded];
  [ChromeEarlGreyUI openTabGrid];

  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridSelectTabsMenuButton()]
      performAction:grey_tap()];

  // Select the first and last items.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(2)]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridEditAddToButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:AddToBookmarksButton()]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityLabel(l10n_util::GetNSString(
                                   IDS_IOS_BOOKMARK_CHOOSE_GROUP_BUTTON))]
      assertWithMatcher:grey_notNil()];

  // Choose "Mobile Bookmarks" folder as the destination.
  // Duplicate matcher here instead of using +[BookmarkEarlGreyUI
  // openMobileBookmarks] in order to properly wait for the snackbar message.
  std::u16string pattern =
      l10n_util::GetStringUTF16(IDS_IOS_BOOKMARK_PAGE_SAVED_FOLDER);
  NSString* snackbarMessage = base::SysUTF16ToNSString(
      base::i18n::MessageFormatter::FormatWithNamedArgs(
          pattern, "count", 2, "title",
          base::SysNSStringToUTF16(@"Mobile Bookmarks")));
  [self waitForSnackBarMessageText:snackbarMessage
      triggeredByTappingItemWithMatcher:grey_allOf(grey_kindOfClassName(
                                                       @"UITableViewCell"),
                                                   grey_descendant(grey_text(
                                                       @"Mobile Bookmarks")),
                                                   nil)];

  [BookmarkEarlGrey
      verifyExistenceOfBookmarkWithURL:base::SysUTF8ToNSString(_URL1.spec())
                                  name:kTitle1
                             inStorage:BookmarkStorageType::kLocalOrSyncable];
  [BookmarkEarlGrey
      verifyExistenceOfBookmarkWithURL:base::SysUTF8ToNSString(_URL4.spec())
                                  name:kTitle4
                             inStorage:BookmarkStorageType::kLocalOrSyncable];
  [BookmarkEarlGrey
      verifyAbsenceOfBookmarkWithURL:base::SysUTF8ToNSString(_URL2.spec())
                           inStorage:BookmarkStorageType::kLocalOrSyncable];
}

// Tests adding items to the Reading List from the tab grid edit mode.
// TODO(crbug.com/40900596): Test flakes.
- (void)FLAKY_testTabGridBulkActionAddToReadingList {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL3];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3];

  [ChromeEarlGreyUI openTabGrid];

  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridSelectTabsMenuButton()]
      performAction:grey_tap()];

  // Select the first and last items.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(2)]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridEditAddToButton()]
      performAction:grey_tap()];

  [self waitForSnackBarMessage:IDS_IOS_READING_LIST_SNACKBAR_MESSAGE
      triggeredByTappingItemWithMatcher:AddToReadingListButton()];
}

// Tests sharing multiple tabs from the tab grid edit mode.
- (void)testTabGridBulkActionShare {
  // TODO(crbug.com/40193498): The pasteboard is "not available at this time"
  // when running on device.

#if !TARGET_OS_SIMULATOR
  EARL_GREY_TEST_SKIPPED(
      @"The pasteboard is inaccessible when running on device.");
#endif

  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL3];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3];

  [ChromeEarlGreyUI openTabGrid];

  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridSelectTabsMenuButton()]
      performAction:grey_tap()];

  // Select the first and last items.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(2)]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridEditShareButton()]
      performAction:grey_tap()];

  [ChromeEarlGrey tapButtonInActivitySheetWithID:@"Copy"];

  NSString* URL1String = base::SysUTF8ToNSString(_URL1.spec());
  NSString* URL3String = base::SysUTF8ToNSString(_URL3.spec());

  [ChromeEarlGrey verifyStringCopied:URL1String];
  [ChromeEarlGrey verifyStringCopied:URL3String];
}

#pragma mark - Tab Grid Search

// Tests entering and exit of the tab grid search mode.
- (void)testEnterExitSearch {
  [ChromeEarlGrey openNewTab];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Verify that search mode is active.
  [[EarlGrey selectElementWithMatcher:TabGridSearchModeToolbar()]
      assertWithMatcher:grey_notNil()];

  // Exit search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()]
      performAction:grey_tap()];

  // Verify that normal mode is active.
  [[EarlGrey selectElementWithMatcher:TabGridNormalModePageControl()]
      assertWithMatcher:grey_notNil()];
}

// Tests that exiting search mode reset the tabs count to the original number.
- (void)testTabGridResetAfterExitingSearch {
  [ChromeEarlGrey openNewTab];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode & search with a query that produce no results.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(@"hello")];

  // Verify that search reduced the number of visible tabs.
  [self verifyVisibleTabsCount:0];

  // Exit search mode & verify that tabs grid was reset.
  [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()]
      performAction:grey_tap()];
  [self verifyVisibleTabsCount:2];
}

// Tests that the scrim view is always shown when the search bar is empty in the
// search mode.
- (void)testScrimVisibleInSearchModeWhenSearchBarIsEmpty {
  [ChromeEarlGrey openNewTab];
  [ChromeEarlGreyUI openTabGrid];

  // Make sure the tab grid is on the regular tabs panel.
  [[EarlGrey selectElementWithMatcher:TabGridNormalModePageControl()]
      performAction:grey_tap()];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Upon entry, the search bar is empty. Verify that scrim is visible.
  [[EarlGrey selectElementWithMatcher:VisibleSearchScrim()]
      assertWithMatcher:grey_notNil()];

  // Searching with any query should render scrim invisible.
  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(@"text")];
  [[EarlGrey selectElementWithMatcher:VisibleSearchScrim()]
      assertWithMatcher:grey_nil()];

  // Clearing search bar text should render scrim visible again.
  // TODO(crbug.com/40916973): Revert to grey_clearText when fixed in EG.
  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(@"")];
  [[EarlGrey selectElementWithMatcher:VisibleSearchScrim()]
      assertWithMatcher:grey_notNil()];

  // Cancel search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()]
      performAction:grey_tap()];

  // Verify that scrim is not visible anymore.
  [[EarlGrey selectElementWithMatcher:VisibleSearchScrim()]
      assertWithMatcher:grey_nil()];
}

// Tests that tapping on the scrim view while in search mode dismisses the scrim
// and exits search mode.
- (void)testTapOnSearchScrimExitsSearchMode {
  [ChromeEarlGrey openNewTab];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Tap on scrim.
  [[EarlGrey selectElementWithMatcher:VisibleSearchScrim()]
      performAction:grey_tap()];

  // Verify that search mode is exit, scrim not visible, and transition to
  // normal mode was successful.
  [[EarlGrey selectElementWithMatcher:VisibleSearchScrim()]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:TabGridNormalModePageControl()]
      assertWithMatcher:grey_notNil()];
  [self verifyVisibleTabsCount:2];
}

// Tests that searching in open tabs in the regular mode will filter the tabs
// correctly.
// TODO(crbug.com/332714545): Test is flaky.
- (void)FLAKY_testSearchRegularOpenTabs {
  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];

  [self verifyVisibleTabsCount:4];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(@"Page")];

  // Verify that the header of the open tabs section has the correct results
  // count.
  [[EarlGrey selectElementWithMatcher:SearchOpenTabsHeaderWithValue(3)]
      assertWithMatcher:grey_notNil()];

  // Verify that there are 3 results for the query "Page" and they are in the
  // expected order.
  [self verifyVisibleTabsCount:3];
  [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle1, 0)]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle2, 1)]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle4, 2)]
      assertWithMatcher:grey_notNil()];

  // Update the search query with one that doesn't match any results.
  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(@"Foo")];

  // Verify that the header of the open tabs section has 0 as the results count.
  [[EarlGrey selectElementWithMatcher:SearchOpenTabsHeaderWithValue(0)]
      assertWithMatcher:grey_notNil()];

  // Verify that no tabs are visible and previously shown tabs disappeared.
  [self verifyVisibleTabsCount:0];

  [[EarlGrey selectElementWithMatcher:TabWithTitle(kTitle1)]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:TabWithTitle(kTitle2)]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:TabWithTitle(kTitle4)]
      assertWithMatcher:grey_nil()];
}

// Tests that open tabs search results header appear only when there is a query
// on the search bar.
- (void)testOpenTabsHeaderVisibleInSearchModeWhenSearchBarIsNotEmpty {
  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Verify that the header doesn't exist in normal mode.
  [[EarlGrey selectElementWithMatcher:SearchOpenTabsSectionHeader()]
      assertWithMatcher:grey_nil()];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Upon entry, the search bar is empty. Verify that the header doesn't exist.
  [[EarlGrey selectElementWithMatcher:SearchOpenTabsSectionHeader()]
      assertWithMatcher:grey_nil()];

  // Searching with any query should render the header visible.
  PerformTabGridSearch(@"text");

  [[EarlGrey selectElementWithMatcher:SearchOpenTabsSectionHeader()]
      assertWithMatcher:grey_notNil()];

  // Clearing search bar text should render the header invisible again.
  // TODO(crbug.com/40916973): Revert to grey_clearText when fixed in EG.
  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(@"")];
  [[EarlGrey selectElementWithMatcher:SearchOpenTabsSectionHeader()]
      assertWithMatcher:grey_nil()];

  // Searching a word then canceling the search mode should hide the section
  // header.
  PerformTabGridSearch(@"page");

  [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()]
      performAction:grey_tap()];
  [[self scrollUpViewMatcher:RegularTabGrid()
             toSelectMatcher:SearchOpenTabsSectionHeader()]
      assertWithMatcher:grey_nil()];
}

// Tests that suggested actions section is available whenever there is a query
// in the normal tabs search mode.
- (void)testSuggestedActionsVisibleInSearchModeWhenSearchBarIsNotEmpty {
  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Verify that the suggested actions section doesn't exist in normal mode.
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSectionHeader()]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSection()]
      assertWithMatcher:grey_nil()];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Upon entry, the search bar is empty. Verify that the suggested actions
  // section doesn't exist.
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSectionHeader()]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSection()]
      assertWithMatcher:grey_nil()];

  // Searching with a query with no results should show the suggested actions
  // section.
  PerformTabGridSearch(@"text");

  [[EarlGrey selectElementWithMatcher:SearchOpenTabsHeaderWithValue(0)]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSectionHeader()]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSection()]
      assertWithMatcher:grey_notNil()];

  // Clearing search bar text should hide the suggested actions section.
  // TODO(crbug.com/40916973): Revert to grey_clearText when fixed in EG.
  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(@"")];
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSectionHeader()]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSection()]
      assertWithMatcher:grey_nil()];

  // Searching with a query with results should show the suggested actions
  // section.
  // TODO(crbug.com/40916973): Revert to grey_clearText when fixed in EG.
  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(@"")];
  PerformTabGridSearch(kTitle2);

  // Check that the header is set correctly.
  [[EarlGrey selectElementWithMatcher:SearchOpenTabsHeaderWithValue(1)]
      assertWithMatcher:grey_notNil()];

  [[self scrollDownViewMatcher:RegularTabGrid()
               toSelectMatcher:SearchSuggestedActionsSectionHeader()]
      assertWithMatcher:grey_notNil()];
  [[self scrollDownViewMatcher:RegularTabGrid()
               toSelectMatcher:SearchSuggestedActionsSection()]
      assertWithMatcher:grey_notNil()];

  // Canceling search mode should hide the suggested actions section.
  [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSectionHeader()]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSection()]
      assertWithMatcher:grey_nil()];
}

// Tests that suggested actions section does not appear in search mode for
// incognito page.
- (void)testSuggestedActionsNotAvailableInIncognitoPageSearchMode {
  [self loadTestURLsInNewIncognitoTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Upon entry, the search bar is empty. Verify that the suggested actions
  // section doesn't exist.
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSectionHeader()]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSection()]
      assertWithMatcher:grey_nil()];

  // Searching with a query should not show suggested actions section.
  PerformTabGridSearch(kTitle2);

  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSectionHeader()]
      assertWithMatcher:grey_nil()];
  [[self scrollDownViewMatcher:chrome_test_util::IncognitoTabGrid()
               toSelectMatcher:SearchSuggestedActionsSection()]
      assertWithMatcher:grey_nil()];
}

// Tests that the search suggested actions section has the right rows in the
// regular grid.
- (void)testSearchSuggestedActionsSectionContentInRegularGrid {
  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode and enter a search query.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];
  // TODO(crbug.com/40827691): Scrolling doesn't work properly in very small
  // devices. Once that is fixed a more broad query can be used for searching
  // (eg. "page").
  PerformTabGridSearch(kTitle2);

  // Verify that the suggested actions section exist and has "Search on web",
  // "Search recent tabs", "Search history" rows.
  [[self scrollDownViewMatcher:RegularTabGrid()
               toSelectMatcher:SearchSuggestedActionsSectionHeader()]
      assertWithMatcher:grey_notNil()];

  id<GREYMatcher> recentTabsMatcher =
      grey_descendant(SearchRecentTabsSuggestedAction());
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    // When Tab Group Sync is enabled, Recent Tabs is not reachable from
    // Tab Grid.
    recentTabsMatcher = grey_not(recentTabsMatcher);
  }

  [[self
      scrollDownViewMatcher:RegularTabGrid()
            toSelectMatcher:grey_allOf(
                                SearchSuggestedActionsSection(),
                                grey_descendant(SearchOnWebSuggestedAction()),
                                recentTabsMatcher,
                                grey_descendant(SearchHistorySuggestedAction()),
                                grey_sufficientlyVisible(), nil)]
      assertWithMatcher:grey_notNil()];
}

// Tests that the search suggested actions section has the right rows in the
// Recent Tabs page.
- (void)testSearchSuggestedActionsSectionContentInRecentTabs {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];
  // Scroll all the way to the top of the Recent Tabs page because a prior
  // test may have left it partially scrolled down.
  [[EarlGrey selectElementWithMatcher:RecentTabsTable()]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];

  // Enter search mode and enter a search query.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];
  // TODO(crbug.com/40827691): Scrolling doesn't work properly in very small
  // devices. Once that is fixed a more broad query can be used for searching
  // (eg. "page").
  PerformTabGridSearch(kTitle2);

  // Verify that the suggested actions section exist and has "Search on web",
  // "Search open tabs", "Search history" rows.
  [[self scrollDownViewMatcher:RecentTabsTable()
               toSelectMatcher:chrome_test_util::HeaderWithAccessibilityLabelId(
                                   IDS_IOS_TABS_SEARCH_SUGGESTED_ACTIONS)]
      assertWithMatcher:grey_notNil()];
  [[self scrollDownViewMatcher:RecentTabsTable()
               toSelectMatcher:SearchOnWebSuggestedAction()]
      assertWithMatcher:grey_notNil()];
  [[self scrollDownViewMatcher:RecentTabsTable()
               toSelectMatcher:SearchOpenTabsSuggestedAction()]
      assertWithMatcher:grey_notNil()];
  [[self scrollDownViewMatcher:RecentTabsTable()
               toSelectMatcher:SearchHistorySuggestedAction()]
      assertWithMatcher:grey_notNil()];
}

// Tests that history row in the search suggested actions section displays the
// correct number of matches.
- (void)testSearchSuggestedActionsDisplaysCorrectHistoryMatchesCount {
  [ChromeEarlGrey clearBrowsingHistory];
  [self loadTestURLs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Verify that the suggested actions section is not visible.
  [[EarlGrey selectElementWithMatcher:SearchSuggestedActionsSection()]
      assertWithMatcher:grey_nil()];

  // Searching the word "page" matches 2 items from history.
  PerformTabGridSearch(@"page");

  [[self scrollDownViewMatcher:RegularTabGrid()
               toSelectMatcher:
                   SearchSuggestedActionsSectionWithHistoryMatchesCount(2)]
      assertWithMatcher:grey_notNil()];

  // Adding to the existing query " two" will search for "page two" and should
  // only match 1 item from the history.
  PerformTabGridSearch(@"page two");

  [[self scrollDownViewMatcher:RegularTabGrid()
               toSelectMatcher:
                   SearchSuggestedActionsSectionWithHistoryMatchesCount(1)]
      assertWithMatcher:grey_notNil()];

  // Cancel search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()]
      performAction:grey_tap()];
}

// Tests that history row in the search suggested actions section displays the
// correct number of matches in Recent Tabs.
- (void)testRecentTabsSearchSuggestedActionsDisplaysCorrectHistoryMatchesCount {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  [ChromeEarlGrey clearBrowsingHistory];
  [self loadTestURLs];
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];
  // Scroll all the way to the top of the recent tabs page because a prior
  // test may have left it partially scrolled down.
  [[EarlGrey selectElementWithMatcher:RecentTabsTable()]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Verify that the suggested actions section does not exist.
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::HeaderWithAccessibilityLabelId(
                                   IDS_IOS_TABS_SEARCH_SUGGESTED_ACTIONS)]
      assertWithMatcher:grey_notVisible()];

  // Searching the word "page" matches 2 items from history.
  PerformTabGridSearch(@"page");

  [[self
      scrollDownViewMatcher:RecentTabsTable()
            toSelectMatcher:RecentTabsSearchHistorySuggestedActionWithMatches(
                                2)]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Adding to the existing query " two" will search for "page two" and should
  // only match 1 item from the history.
  PerformTabGridSearch(@"page two");

  [[self
      scrollDownViewMatcher:RecentTabsTable()
            toSelectMatcher:RecentTabsSearchHistorySuggestedActionWithMatches(
                                1)]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Cancel search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()]
      performAction:grey_tap()];
}

// Tests that selecting an open tab search result in the regular mode will
// correctly open the expected tab.
- (void)testSearchRegularOpenTabsSelectResult {
  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(kTitle2)];

  [[EarlGrey selectElementWithMatcher:TabWithTitle(kTitle2)]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
      assertWithMatcher:grey_sufficientlyVisible()];

  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];
  const GURL currentURL = [ChromeEarlGrey webStateVisibleURL];
  GREYAssertEqual(_URL2, currentURL, @"Page navigated unexpectedly to %s",
                  currentURL.spec().c_str());
}

// Tests that selecting an open tab search result in incognito mode will
// correctly open the expected tab.
- (void)testSearchIncognitoOpenTabsSelectResult {
  [self loadTestURLsInNewIncognitoTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(kTitle2)];

  [[EarlGrey selectElementWithMatcher:TabWithTitle(kTitle2)]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
      assertWithMatcher:grey_sufficientlyVisible()];

  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];
  const GURL currentURL = [ChromeEarlGrey webStateVisibleURL];
  GREYAssertEqual(_URL2, currentURL, @"Page navigated unexpectedly to %s",
                  currentURL.spec().c_str());
}

// Tests that share action works successfully from the long press context menu
// on search results.
- (void)testSearchOpenTabsContextMenuShare {
  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(kTitle2)];

  [self longPressTabWithTitle:kTitle2];

  [ChromeEarlGrey verifyShareActionWithURL:_URL1 pageTitle:kTitle2];
}

// Tests that add to reading list action works successfully from the long press
// context menu on search results.
- (void)testSearchOpenTabsContextMenuAddToReadingList {
  // Clear the Reading List.
  GREYAssertNil([ReadingListAppInterface clearEntries],
                @"Unable to clear Reading List entries");
  GREYAssertEqual(0, [ReadingListAppInterface unreadEntriesCount],
                  @"Reading List should be empty");

  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(kTitle2)];

  [self longPressTabWithTitle:kTitle2];

  [[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");
}

// Tests that add to bookmarks action works successfully from the long press
// context menu on search results.
- (void)testSearchOpenTabsContextMenuAddToBookmarks {
  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(kTitle2)];

  [self longPressTabWithTitle:kTitle2];

  NSString* snackbarMessage = base::SysUTF16ToNSString(
      l10n_util::GetPluralStringFUTF16(IDS_IOS_BOOKMARKS_BULK_SAVED, 1));
  [self waitForSnackBarMessageText:snackbarMessage
      triggeredByTappingItemWithMatcher:AddToBookmarksButton()];

  [self longPressTabWithTitle:kTitle2];

  [[EarlGrey selectElementWithMatcher:
                 chrome_test_util::ContextMenuItemWithAccessibilityLabelId(
                     IDS_IOS_TOOLS_MENU_EDIT_BOOKMARK)]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:
                 chrome_test_util::NavigationBarTitleWithAccessibilityLabelId(
                     IDS_IOS_BOOKMARK_EDIT_SCREEN_TITLE)]
      assertWithMatcher:grey_notNil()];
}

// Tests that close tab action works successfully from the long press context
// menu on search results.
- (void)testSearchOpenTabsContextMenuCloseTab {
  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(kTitle2)];

  [self longPressTabWithTitle:kTitle2];

  // Close Tab.
  [[EarlGrey selectElementWithMatcher:CloseTabMenuButton()]
      performAction:grey_tap()];

  // Make sure that the tab is no longer present.
  [[EarlGrey selectElementWithMatcher:TabWithTitle(kTitle2)]
      assertWithMatcher:grey_nil()];
}

- (void)testSearchOpenTabsContextMenuSelectTabsUnavailable {
  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(kTitle2)];

  [self longPressTabWithTitle:kTitle2];

  [[EarlGrey selectElementWithMatcher:SelectTabsContextMenuItem()]
      assertWithMatcher:grey_nil()];

  // Dismiss the context menu.
  [[EarlGrey selectElementWithMatcher:RegularTabGrid()]
      performAction:grey_tapAtPoint(CGPointMake(0, 0))];
}

// Tests "search recent tabs" and "search open tabs" suggested actions switch
// the tab grid page correctly while staying in the search mode.
- (void)testSearchSuggestedActionsPageSwitch {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];
  // Enter search mode & perform a search.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  PerformTabGridSearch(kTitle2);

  // Verify that the RegularGridView is visible, use 50% for the visibility
  // percentage as in smaller devices the toolbars can occupy more space on the
  // screen.
  [[EarlGrey selectElementWithMatcher:RegularTabGrid()]
      assertWithMatcher:grey_minimumVisiblePercent(0.5)];

  // Tap on search recent tabs.
  [[self scrollDownViewMatcher:RegularTabGrid()
               toSelectMatcher:SearchRecentTabsSuggestedAction()]
      performAction:grey_tap()];

  // Verify successful transition to the recent tabs page with search mode on
  // and search text set to the original query.
  [[EarlGrey selectElementWithMatcher:TabGridSearchModeToolbar()]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:SearchBarWithSearchText(kTitle2)]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:RecentTabsTable()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:RegularTabGrid()]
      assertWithMatcher:grey_notVisible()];

  // Tap on search open tabs to go back to regular tabs page.
  [[self scrollDownViewMatcher:RecentTabsTable()
               toSelectMatcher:SearchOpenTabsSuggestedAction()]
      performAction:grey_tap()];

  // Verify successful transition to the regular tab grid page with search mode
  // on and search text set to the original query.
  [[EarlGrey selectElementWithMatcher:TabGridSearchModeToolbar()]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:SearchBarWithSearchText(kTitle2)]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:RegularTabGrid()]
      assertWithMatcher:grey_minimumVisiblePercent(0.5)];
  [[EarlGrey selectElementWithMatcher:RecentTabsTable()]
      assertWithMatcher:grey_notVisible()];
}

// Tests that tapping on search history action in the regular tabs search mode
// opens the history modal and dismissing it returns to the search mode.
- (void)testHistorySuggestedActionInRegularTabsSearch {
  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];
  // Enter search mode & perform a search.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  PerformTabGridSearch(kTitle2);

  // Tap on search history.
  [[self scrollDownViewMatcher:RegularTabGrid()
               toSelectMatcher:SearchHistorySuggestedAction()]
      performAction:grey_tap()];

  // Check that the history table is presented.
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kHistoryTableViewIdentifier)]
      assertWithMatcher:grey_sufficientlyVisible()];
  // Close History.
  [[EarlGrey selectElementWithMatcher:
                 grey_accessibilityID(
                     kHistoryNavigationControllerDoneButtonIdentifier)]
      performAction:grey_tap()];

  // Make sure that search mode is still active and searching the same query.
  [[EarlGrey selectElementWithMatcher:TabGridSearchModeToolbar()]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:SearchBarWithSearchText(kTitle2)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that tapping on search history action in the Recent Tabs search mode
// opens the history modal and dismissing it returns to the search mode.
- (void)testHistorySuggestedActionInRecentTabsSearch {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];
  // Scroll all the way to the top of the recent tabs page because a prior
  // test may have left it partially scrolled down.
  [[EarlGrey selectElementWithMatcher:RecentTabsTable()]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];

  // Enter search mode & perform a search.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];
  PerformTabGridSearch(kTitle2);

  // Tap on search history.
  [[self scrollDownViewMatcher:RecentTabsTable()
               toSelectMatcher:SearchHistorySuggestedAction()]
      performAction:grey_tap()];

  // Check that the history table is presented.
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kHistoryTableViewIdentifier)]
      assertWithMatcher:grey_sufficientlyVisible()];
  // Close History.
  [[EarlGrey selectElementWithMatcher:
                 grey_accessibilityID(
                     kHistoryNavigationControllerDoneButtonIdentifier)]
      performAction:grey_tap()];

  // Make sure that search mode is still active and searching the same query.
  [[EarlGrey selectElementWithMatcher:TabGridSearchModeToolbar()]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:SearchBarWithSearchText(kTitle2)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that tapping the search on web action in the regular tabs search mode
// opens a new tab on the default search engine with the search term from tab
// search. Additionally, checks that tab search mode is exited when the user
// returns to the tab grid.
- (void)testSearchOnWebSuggestedActionInRegularTabsSearch {
  // Configure a testing search engine to prevent real external url requests.
  web::test::AddResponseProvider(
      std::make_unique<EchoURLDefaultSearchEngineResponseProvider>());
  GURL searchEngineURL = web::test::HttpServer::MakeUrl(kSearchEngineURL);
  NSString* searchEngineURLString =
      base::SysUTF8ToNSString(searchEngineURL.spec());
  [SettingsAppInterface overrideSearchEngineWithURL:searchEngineURLString];

  // Enter tab grid search mode & perform a search.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  PerformTabGridSearch(@"queryfromtabsearch");

  // Scroll to search on web.
  [[self scrollDownViewMatcher:RegularTabGrid()
               toSelectMatcher:SearchOnWebSuggestedAction()]
      performAction:grey_tap()];

  // Ensure that the tab grid was exited.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Ensure the loaded page is a default search engine page for `searchQuery`.
  [ChromeEarlGrey waitForWebStateContainingText:kSearchEngineHost];
  [ChromeEarlGrey waitForWebStateContainingText:"queryfromtabsearch"];

  // Re-enter the tab grid and ensure search mode was exited.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that tapping the search on web action in the Recent Tabs search mode
// opens a new tab on the default search engine with the search term from tab
// search. Additionally, checks that tab search mode is exited when the user
// returns to the tab grid.
- (void)testSearchOnWebSuggestedActionInRecentTabsSearch {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  // Configure a testing search engine to prevent real external url requests.
  web::test::AddResponseProvider(
      std::make_unique<EchoURLDefaultSearchEngineResponseProvider>());
  GURL searchEngineURL = web::test::HttpServer::MakeUrl(kSearchEngineURL);
  NSString* searchEngineURLString =
      base::SysUTF8ToNSString(searchEngineURL.spec());
  [SettingsAppInterface overrideSearchEngineWithURL:searchEngineURLString];

  // Enter tab grid search mode & perform a search.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];
  // Scroll all the way to the top of the recent tabs page because a prior
  // test may have left it partially scrolled down.
  [[EarlGrey selectElementWithMatcher:RecentTabsTable()]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];

  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];
  PerformTabGridSearch(@"queryfromtabsearch");

  [[self scrollDownViewMatcher:RecentTabsTable()
               toSelectMatcher:SearchOnWebSuggestedAction()]
      performAction:grey_tap()];

  // Ensure that the tab grid was exited.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Ensure the loaded page is a default search engine page for `searchQuery`.
  [ChromeEarlGrey waitForWebStateContainingText:kSearchEngineHost];
  [ChromeEarlGrey waitForWebStateContainingText:"queryfromtabsearch"];

  // Re-enter the tab grid and ensure search mode was exited.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that a search with no results in incognito mode will show the empty
// state.
- (void)testEmptyStateAfterNoResultsSearchForIncognitoTabGrid {
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Search with a word that will produce no results and verify that the header
  // has 0 found results and that the empty state is visible.
  PerformTabGridSearch(@"text");

  [[EarlGrey selectElementWithMatcher:SearchOpenTabsHeaderWithValue(0)]
      assertWithMatcher:grey_notNil()];

  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(
                                   kTabGridIncognitoTabsEmptyStateIdentifier)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that closing a tab works successfully in search results.
- (void)testSearchResultCloseTab {
  [self loadTestURLsInNewTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  PerformTabGridSearch(kTitle2);

  [self verifyVisibleTabsCount:1];
  [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle2, 0)]
      assertWithMatcher:grey_notNil()];

  // Close Tab.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridCloseButtonForCellAtIndex(0)]
      performAction:grey_tap()];

  // Make sure that the tab is no longer present.
  [[EarlGrey selectElementWithMatcher:TabWithTitle(kTitle2)]
      assertWithMatcher:grey_nil()];
}

// Tests that closing a tab works successfully in incognito search results.
- (void)testSearchResultCloseTabInIncognito {
  [self loadTestURLsInNewIncognitoTabs];
  [ChromeEarlGreyUI openTabGrid];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  PerformTabGridSearch(kTitle2);

  [self verifyVisibleTabsCount:1];
  [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle2, 0)]
      assertWithMatcher:grey_notNil()];

  // Close Tab.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridCloseButtonForCellAtIndex(0)]
      performAction:grey_tap()];

  // Make sure that the tab is no longer present.
  [[EarlGrey selectElementWithMatcher:TabWithTitle(kTitle2)]
      assertWithMatcher:grey_nil()];
}

// Tests that searching in Recent Tabs will filter the items correctly.
- (void)testSearchRecentlyClosedTabs {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  [self clearAllRecentlyClosedItems];
  [self loadTestURLsAndCloseTabs];

  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];
  // Scroll all the way to the top of the recent tabs page because a prior
  // test may have left it partially scrolled down.
  [[EarlGrey selectElementWithMatcher:RecentTabsTable()]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];

  // Ensure all recently closed tab entries are present.
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle1)]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle2)]
      assertWithMatcher:grey_notNil()];

  // Enter search mode and search for `kTitle2`.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(kTitle2)];

  // The recently closed section header should be visible.
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabsSectionHeader()]
      assertWithMatcher:grey_notNil()];

  // The item for page 2 should be displayed.
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle2)]
      assertWithMatcher:grey_notNil()];

  // The item for page 1 should not be visible.
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle1)]
      assertWithMatcher:grey_nil()];

  // Exit search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()]
      performAction:grey_tap()];

  // Check that all items are visible again.
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle1)]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle2)]
      assertWithMatcher:grey_notNil()];
}

// Tests that searching in Recent Tabs with no matching results hides the
// unmatched items and the "Recently Closed" section header.
- (void)testSearchRecentlyClosedTabsNoResults {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  [self clearAllRecentlyClosedItems];
  [self loadTestURLsAndCloseTabs];

  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];
  // Scroll all the way to the top of the recent tabs page because a prior
  // test may have left it partially scrolled down.
  [[EarlGrey selectElementWithMatcher:RecentTabsTable()]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];

  // Ensure all recently closed tab entries are present.
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle1)]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle2)]
      assertWithMatcher:grey_notNil()];

  // Enter search mode and search for text which matches no items.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(@"foo")];

  // The recently closed section header should not be visible.
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabsSectionHeader()]
      assertWithMatcher:grey_nil()];

  // The items should not be visible.
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle1)]
      assertWithMatcher:grey_nil()];
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle2)]
      assertWithMatcher:grey_nil()];

  // Exit search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()]
      performAction:grey_tap()];

  // Check that all items are visible again.
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle1)]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle2)]
      assertWithMatcher:grey_notNil()];
}

// Tests that interacting with the Tab Grid search UI shows the correct header
// at each step.
- (void)testSearchHeaderWithInactiveTabs {
  if ([ChromeEarlGrey isIPadIdiom]) {
    EARL_GREY_TEST_SKIPPED(@"Skipped for iPad. The Inactive Tabs feature is "
                           @"only supported on iPhone.");
  }
  [self loadTestURLsInNewTabs];
  [self relaunchAppWithInactiveTabsEnabled];

  [ChromeEarlGreyUI openTabGrid];
  GREYAssertEqual(1, [ChromeEarlGrey mainTabCount],
                  @"Expected only one tab (NTP), all other tabs should have "
                  @"been in inactive tab grid.");
  GREYAssertEqual(4, [ChromeEarlGrey inactiveTabCount],
                  @"Expected 4 inactive tabs.");

  // Verify that the Inactive Tabs button is showing.
  [[EarlGrey selectElementWithMatcher:TabGridInactiveTabsButton()]
      assertWithMatcher:grey_notNil()];

  // Enter search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()]
      performAction:grey_tap()];

  // Verify that the Inactive Tabs button is not showing.
  [[EarlGrey selectElementWithMatcher:TabGridInactiveTabsButton()]
      assertWithMatcher:grey_nil()];

  // Verify that search mode is active.
  [[EarlGrey selectElementWithMatcher:TabGridSearchModeToolbar()]
      assertWithMatcher:grey_notNil()];

  // Verify that the Search results header is not showing, as the search field
  // is empty.
  [[EarlGrey selectElementWithMatcher:SearchOpenTabsSectionHeader()]
      assertWithMatcher:grey_nil()];

  // Enter some text.
  [[EarlGrey selectElementWithMatcher:TabGridSearchBar()]
      performAction:grey_replaceText(@"Page")];

  // Verify that the Search results header is showing now.
  [[EarlGrey selectElementWithMatcher:SearchOpenTabsSectionHeader()]
      assertWithMatcher:grey_notNil()];

  // Exit search mode.
  [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()]
      performAction:grey_tap()];

  // Verify that normal mode is active.
  [[EarlGrey selectElementWithMatcher:TabGridNormalModePageControl()]
      assertWithMatcher:grey_notNil()];

  // Verify that the Inactive Tabs button is showing.
  [[EarlGrey selectElementWithMatcher:TabGridInactiveTabsButton()]
      assertWithMatcher:grey_notNil()];

  // Verify that the Search results header is not showing, as the search field
  // is empty.
  [[EarlGrey selectElementWithMatcher:SearchOpenTabsSectionHeader()]
      assertWithMatcher:grey_nil()];
}

// Tests that once an account is signed in, the syncing spinner is eventually
// dismissed: https://crbug.com/1422634.
- (void)DISABLED_testSyncSpinnerDismissedInRecentlyClosedTabs {
  // Clear browsing history to reduce delay during sign-in and fix this test's
  // flakiness on iOS 16.
  [ChromeEarlGrey clearBrowsingHistory];

  // Sign-in with fake identity.
  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey signinWithFakeIdentity:fakeIdentity];

  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  // Open recently closed tabs.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];

  // Wait for the syncing view to disappear.
  [ChromeEarlGrey
      waitForUIElementToDisappearWithMatcher:
          grey_accessibilityID(kTableViewActivityIndicatorHeaderFooterViewId)];
}

// Regression test for crbug.com/1474793. Tests the sign-in promo in "tabs from
// other devices" reacts accordingly if the user signs in via a different
// surface. More specifically: on tap the promo shouldn't offer the sign-in
// sheet but only the history opt-in.
- (void)testPromoInTabsFromOtherDevicesListensToSignin {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Other Devices is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Other Devices is not available in Tab Grid when "
                           @"Tab Group Sync is enabled.");
  }

  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];

  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];
  [[EarlGrey
      selectElementWithMatcher:
          grey_allOf(grey_accessibilityID(
                         kRecentTabsTabSyncOffButtonAccessibilityIdentifier),
                     grey_accessibilityElement(), nil)]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityLabel(l10n_util::GetNSString(
                                   IDS_IOS_HISTORY_SYNC_TITLE))]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that closing a tab works successfully in incognito search results.
- (void)testLastIncognitoTabCloses {
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGreyUI openTabGrid];

  [self verifyVisibleTabsCount:1];
  [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle1, 0)]
      assertWithMatcher:grey_notNil()];

  // Close Tab.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridCloseButtonForCellAtIndex(0)]
      performAction:grey_tap()];

  // Make sure that the tab is no longer present.
  [[EarlGrey selectElementWithMatcher:TabWithTitle(kTitle1)]
      assertWithMatcher:grey_nil()];
}

// Tests that "undo" is still possible after navigating to the "recently
// closed tabs" panel.
- (void)testClosedTabsAddedToRecentlyClosedTabsAfterConfirmation {
  // When Tab Groups is the third panel (i.e. when Tab Group Sync is enabled),
  // Recent Tabs is not reachable from the Tab Grid. So the test flow is not
  // supported with Tab Group Sync enabled.
  if ([ChromeEarlGrey isTabGroupSyncEnabled]) {
    EARL_GREY_TEST_SKIPPED(@"Recent Tabs is not available in Tab Grid when Tab "
                           @"Group Sync is enabled.");
  }

  // Clear all recently closed tabs.
  [self clearAllRecentlyClosedItems];

  // Load some real URL (NTP is not added to recently closed tabs).
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  // Open the tab grid.
  [ChromeEarlGreyUI openTabGrid];

  // Close all tabs
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridEditMenuCloseAllButton()]
      performAction:grey_tap()];

  // Ensure tabs were closed
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      assertWithMatcher:grey_nil()];

  // Ensure undo button is visible and edit button is not visible
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridUndoCloseAllButton()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      assertWithMatcher:grey_nil()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridRegularTabsEmptyStateView()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Navigate to the "recently closed" panel.
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];

  // Check that there are no "recently closed" tabs visible.
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle1)]
      assertWithMatcher:grey_nil()];

  // Navigate back to the tab grid.
  [[EarlGrey selectElementWithMatcher:TabGridOpenTabsPanelButton()]
      performAction:grey_tap()];

  // Tap Undo button
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::TabGridUndoCloseAllButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Close all the tabs (again).
  [[EarlGrey selectElementWithMatcher:VisibleTabGridEditButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          TabGridEditMenuCloseAllButton()]
      performAction:grey_tap()];

  // Open a new tab. This should result in closing the tab grid, which will
  // confirm the close operation and add the tabs to recently closed.
  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  // Open the tab grid.
  [ChromeEarlGreyUI openTabGrid];

  // Navigate to the "recently closed" panel.
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];

  // Check that the tabs closed are now visible.
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle1)]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey selectElementWithMatcher:RecentlyClosedTabWithTitle(kTitle2)]
      assertWithMatcher:grey_nil()];

  // Navigate back to the tab grid.
  [[EarlGrey selectElementWithMatcher:TabGridOpenTabsPanelButton()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
      performAction:grey_tap()];
}

#pragma mark - Helper Methods

- (void)loadTestURLs {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey loadURL:_URL3];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3];
}

- (void)loadTestURLsInNewTabs {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL3];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL4];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse4];
}

// Open and close 2 unique tabs.
- (void)loadTestURLsAndCloseTabs {
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];
  [ChromeEarlGrey closeCurrentTab];
  [ChromeEarlGrey closeCurrentTab];
}

- (void)loadTestURLsInNewIncognitoTabs {
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL1];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];

  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL2];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse2];

  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL3];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse3];

  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey loadURL:_URL4];
  [ChromeEarlGrey waitForWebStateContainingText:kResponse4];
}

// Loads a URL in a new tab and deletes it to populate Recent Tabs. Then,
// navigates to the Recent tabs via tab grid.
// This should not be called when Tab Group Sync is enabled, as there is no
// Recent Tabs in Tab Grid.
- (void)prepareRecentTabWithURL:(const GURL&)URL
                       response:(const char*)response {
  GREYAssert(![ChromeEarlGrey isTabGroupSyncEnabled],
             @"Recent Tabs is not available in Tab Grid when Tab Group Sync is "
             @"enabled.");

  [ChromeEarlGrey loadURL:URL];
  [ChromeEarlGrey waitForWebStateContainingText:response];

  // Close the tab, making it appear in Recent Tabs.
  [ChromeEarlGrey closeCurrentTab];

  // Switch over to Recent Tabs.
  [[EarlGrey selectElementWithMatcher:TabGridOtherDevicesPanelButton()]
      performAction:grey_tap()];

  // Scroll all the way to the top of the recent tabs page because a prior
  // test may have left it partially scrolled down.
  [[EarlGrey selectElementWithMatcher:RecentTabsTable()]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];
}

// Long press on the recent tab entry or the tab item in the tab grid with
// `title`.
- (void)longPressTabWithTitle:(NSString*)title {
  // The test page may be there multiple times.
  // Don't use -waitForUIElementToAppearWithMatcher here as it doesn't support
  // the atIndex:0 check for multiple elements.
  ConditionBlock condition = ^{
    NSError* error = nil;
    [[[EarlGrey
        selectElementWithMatcher:grey_allOf(grey_accessibilityLabel(title),
                                            grey_sufficientlyVisible(), nil)]
        atIndex:0] assertWithMatcher:grey_notNil() error:&error];
    return error == nil;
  };
  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
                 kWaitForUIElementTimeout, condition),
             @"Tab did not appear.");

  [[[EarlGrey
      selectElementWithMatcher:grey_allOf(grey_accessibilityLabel(title),
                                          grey_sufficientlyVisible(), nil)]
      atIndex:0] performAction:grey_longPress()];
}

// Checks if the content of the given tab in the given window matches given
// text. This method exits the tab grid and re-enters it afterward.
- (void)fromGridCheckTabAtIndex:(int)tabIndex
                 inWindowNumber:(int)windowNumber
                   containsText:(const char*)text {
  [EarlGrey
      setRootMatcherForSubsequentInteractions:WindowWithNumber(windowNumber)];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(
                                          tabIndex)] performAction:grey_tap()];
  [ChromeEarlGrey waitForWebStateContainingText:text
                             inWindowWithNumber:windowNumber];
  [ChromeEarlGreyUI openTabGrid];
}

- (void)waitForSnackBarMessage:(int)messageIdentifier
    triggeredByTappingItemWithMatcher:(id<GREYMatcher>)matcher {
  NSString* snackBarLabel = l10n_util::GetNSStringWithFixup(messageIdentifier);
  WaitForSnackbarTriggeredByTappingItem(snackBarLabel, matcher);
}

- (void)waitForSnackBarMessageText:(NSString*)snackBarLabel
    triggeredByTappingItemWithMatcher:(id<GREYMatcher>)matcher {
  WaitForSnackbarTriggeredByTappingItem(snackBarLabel, matcher);
}

- (void)waitForContextMenuToDisappear {
  ConditionBlock wait_for_disappearance = ^{
    NSError* error = nil;
    [[EarlGrey
        selectElementWithMatcher:grey_allOf(grey_kindOfClassName(
                                                @"_UIContextMenuContainerView"),
                                            grey_sufficientlyVisible(), nil)]
        assertWithMatcher:grey_nil()
                    error:&error];
    return error == nil;
  };
  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
                 kWaitForUIElementTimeout, wait_for_disappearance),
             @"Context menu did not disappear.");
}

// Verifies that the tab grid has exactly `expectedCount` tabs.
- (void)verifyVisibleTabsCount:(NSUInteger)expectedCount {
  // Verify that the cell # `expectedCount` exist.
  if (expectedCount == 0) {
    [[EarlGrey selectElementWithMatcher:TabGridCell()]
        assertWithMatcher:grey_nil()];
  } else {
    [[[EarlGrey selectElementWithMatcher:TabGridCell()]
        atIndex:expectedCount - 1] assertWithMatcher:grey_notNil()];
  }
  // Then verify that there is no more cells after that.
  [[EarlGrey
      selectElementWithMatcher:grey_allOf(TabGridCell(),
                                          TabGridCellAtIndex(expectedCount),
                                          nil)] assertWithMatcher:grey_nil()];
}

// Returns an interaction that scrolls down on the view matched by `viewMatcher`
// to search for the given `matcher`.
- (id<GREYInteraction>)scrollDownViewMatcher:(id<GREYMatcher>)viewMatcher
                             toSelectMatcher:(id<GREYMatcher>)matcher {
  return [[EarlGrey selectElementWithMatcher:matcher]
         usingSearchAction:grey_scrollInDirectionWithStartPoint(
                               kGREYDirectionDown, /*amount=*/100,
                               /*xOriginStartPercentage=*/0.5,
                               /*yOriginStartPercentage=*/0.5)
      onElementWithMatcher:viewMatcher];
}

// Returns an interaction that scrolls up on the view matched by `viewMatcher`
// to search for the given `matcher`.
- (id<GREYInteraction>)scrollUpViewMatcher:(id<GREYMatcher>)viewMatcher
                           toSelectMatcher:(id<GREYMatcher>)matcher {
  return [[EarlGrey selectElementWithMatcher:matcher]
         usingSearchAction:grey_scrollInDirectionWithStartPoint(
                               kGREYDirectionUp, /*amount=*/100,
                               /*xOriginStartPercentage=*/0.5,
                               /*yOriginStartPercentage=*/0.5)
      onElementWithMatcher:viewMatcher];
}

// Ensures that all items are cleared out from the saved recently closed items.
- (void)clearAllRecentlyClosedItems {
  [ChromeEarlGrey clearBrowsingHistory];
  [RecentTabsAppInterface clearCollapsedListViewSectionStates];
}

// Relaunches the app with Inactive Tabs 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];
}

@end