chromium/ios/chrome/browser/ui/tab_switcher/tab_strip/test/tab_strip_drag_drop_egtest.mm

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

#import <XCTest/XCTest.h>

#import "base/test/ios/wait_util.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_strip/ui/swift_constants_for_objective_c.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
#import "ios/testing/earl_grey/earl_grey_test.h"

using base::test::ios::kWaitForUIElementTimeout;
using ::base::test::ios::WaitUntilConditionOrTimeout;

namespace {

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

// Matcher for the reguar cell at the given `index`.
id<GREYMatcher> RegularCellAtIndex(unsigned int index) {
  return grey_allOf(
      grey_accessibilityID(IdentifierForRegularCellAtIndex(index)),
      grey_kindOfClassName(@"TabStripTabCell"), grey_sufficientlyVisible(),
      nil);
}

// Finds the element with the given `identifier` of given `type`.
XCUIElement* GetElementMatchingIdentifier(XCUIApplication* app,
                                          NSString* identifier,
                                          XCUIElementType type) {
  XCUIElementQuery* query = [[app.windows.firstMatch
      descendantsMatchingType:type] matchingIdentifier:identifier];
  return [query elementBoundByIndex:0];
}

// Drags and drops the cell with the given `src_cell_identifier` to the
// `dst_cell_identifier` position.
void DragDropTabStripTabCellInTabStripView(NSString* src_cell_identifier,
                                           NSString* dst_cell_identifier) {
  XCUIApplication* app = [[XCUIApplication alloc] init];
  XCUIElement* src_element = GetElementMatchingIdentifier(
      app, src_cell_identifier, XCUIElementTypeCell);
  XCUICoordinate* start_point =
      [src_element coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5)];

  XCUIElement* dst_element = GetElementMatchingIdentifier(
      app, dst_cell_identifier, XCUIElementTypeCell);
  XCUICoordinate* end_point =
      [dst_element coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5)];

  [start_point pressForDuration:1.5
           thenDragToCoordinate:end_point
                   withVelocity:XCUIGestureVelocityDefault
            thenHoldForDuration:1.0];
}

// Checks that the regular cell matching `tab_title` moved to `tab_index`
void AssertRegularCellMovedToNewPosition(unsigned int tab_index,
                                         NSString* tab_title) {
  ConditionBlock condition = ^{
    NSError* error = nil;

    [[EarlGrey
        selectElementWithMatcher:grey_allOf(
                                     RegularCellAtIndex(tab_index),
                                     grey_descendant(grey_text(tab_title)),
                                     nil)] assertWithMatcher:grey_notNil()
                                                       error:&error];
    return !error;
  };
  GREYAssert(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, condition),
             @"The drag drop action has failed.");
}

}  // namespace

// Tests for the tab strip drag drop interactions on iPad.
@interface TabStripDragDropTestCase : ChromeTestCase
@end

@implementation TabStripDragDropTestCase

- (AppLaunchConfiguration)appConfigurationForTestCase {
  AppLaunchConfiguration config;
  config.features_enabled.push_back(kModernTabStrip);
  return config;
}

// Checks that dragging a regular cell to a new position correctly moves the
// cell.
// TODO(crbug.com/40285917): Test is flaky on simluator.
#if TARGET_OS_SIMULATOR
#define MAYBE_testDragTabStripTabCellInTabStripView \
  FLAKY_testDragTabStripTabCellInTabStripView
#else
#define MAYBE_testDragTabStripTabCellInTabStripView \
  testDragTabStripTabCellInTabStripView
#endif
- (void)MAYBE_testDragTabStripTabCellInTabStripView {
  if ([ChromeEarlGrey isCompactWidth]) {
    EARL_GREY_TEST_SKIPPED(@"No tab strip on this device.");
  }

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:GURL("chrome://about")];

  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:GURL("chrome://version")];

  // Tab0: New Tab
  // Tab1: Chrome URLs
  // Tab2: About Version

  // Move Tab0 to Tab2.
  DragDropTabStripTabCellInTabStripView(IdentifierForRegularCellAtIndex(0),
                                        IdentifierForRegularCellAtIndex(2));
  AssertRegularCellMovedToNewPosition(/*tab_index*/ 2,
                                      /*tab_title*/ @"New Tab");

  // Tab0: Chrome URLs
  // Tab1: About Version
  // Tab2: New Tab

  // Move Tab1 to Tab0.
  DragDropTabStripTabCellInTabStripView(IdentifierForRegularCellAtIndex(1),
                                        IdentifierForRegularCellAtIndex(0));
  AssertRegularCellMovedToNewPosition(/*tab_index*/ 0,
                                      /*tab_title*/ @"About Version");
}

@end