| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import <XCTest/XCTest.h> |
| |
| #import "ios/chrome/browser/shared/public/features/features.h" |
| #import "ios/chrome/browser/tabs/ui_bundled/tab_strip_constants.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/earl_grey_test.h" |
| #import "net/test/embedded_test_server/embedded_test_server.h" |
| #import "net/test/embedded_test_server/http_request.h" |
| #import "net/test/embedded_test_server/http_response.h" |
| |
| namespace { |
| |
| // URL to a destination page with a static message. |
| const char kDestinationPageUrl[] = "/destination"; |
| // HTML content of the destination page. |
| const char kDestinationHtml[] = |
| "<html><head><meta name='viewport' content='width=device-width, " |
| "initial-scale=1.0, maximum-scale=1.0, user-scalable=no' " |
| "/></head><body><script>document.title='new doc'</script>" |
| "<center><span id=\"message\">You made it!</span></center>" |
| "</body></html>"; |
| // Visible content of the destination page. |
| const char kDestinationContent[] = "You made it!"; |
| |
| // URL to an initial page with a link to the destination page. |
| const char kInitialPageUrl[] = "/scenarioDragURLToTabStrip"; |
| // HTML content of an initial page with a link to the destination page. |
| // Note that this string contains substrings that must exactly match the next |
| // two constants (`kInitialPageLinkId` and `kInitialPageDestinationLinkText`). |
| // If these were more complex or more liable to change, a sprintf template and |
| // runtime composition should be used instead. |
| const char kInitialPageHtml[] = |
| "<html><head><meta name='viewport' content='width=device-width, " |
| "initial-scale=1.0, maximum-scale=1.0, user-scalable=no' /></head><body><a " |
| "style='margin-left:150px' href='/destination' id='link' aria-label='link'>" |
| "link</a></body></html>"; |
| |
| // Accessibility ID of the link on the initial page. The `aria-label` attribute |
| // makes this visible to UIKit as an accessibility ID. |
| NSString* const kInitialPageLinkId = @"link"; |
| |
| // The text of the link to the destination page. |
| const char kInitialPageDestinationLinkText[] = "link"; |
| |
| // Provides responses for initial page and destination URLs. |
| std::unique_ptr<net::test_server::HttpResponse> StandardResponse( |
| const net::test_server::HttpRequest& request) { |
| std::unique_ptr<net::test_server::BasicHttpResponse> http_response = |
| std::make_unique<net::test_server::BasicHttpResponse>(); |
| http_response->set_code(net::HTTP_OK); |
| |
| if (request.relative_url == kInitialPageUrl) { |
| http_response->set_content(kInitialPageHtml); |
| } else if (request.relative_url == kDestinationPageUrl) { |
| http_response->set_content(kDestinationHtml); |
| } else { |
| return nullptr; |
| } |
| |
| return std::move(http_response); |
| } |
| |
| // 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 element with the given `src_identifier` identifier in the |
| // tab strip with the given `tab_strip_identifier` identifier. |
| void DragDrop(NSString* src_identifier, NSString* tab_strip_identifier) { |
| XCUIApplication* app = [[XCUIApplication alloc] init]; |
| XCUIElement* src_element = |
| GetElementMatchingIdentifier(app, src_identifier, XCUIElementTypeAny); |
| XCUICoordinate* start_point = |
| [src_element coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5)]; |
| |
| XCUIElement* dst_element = GetElementMatchingIdentifier( |
| app, tab_strip_identifier, XCUIElementTypeAny); |
| XCUICoordinate* end_point = |
| [dst_element coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.75)]; |
| |
| [start_point pressForDuration:1.5 |
| thenDragToCoordinate:end_point |
| withVelocity:XCUIGestureVelocityDefault |
| thenHoldForDuration:1.0]; |
| } |
| |
| } // namespace |
| |
| // Tests for the tab strip shown on iPad. |
| @interface LegacyTabStripTestCase : ChromeTestCase |
| @end |
| |
| @implementation LegacyTabStripTestCase |
| |
| - (AppLaunchConfiguration)appConfigurationForTestCase { |
| AppLaunchConfiguration config; |
| config.features_disabled.push_back(kModernTabStrip); |
| return config; |
| } |
| |
| - (void)setUp { |
| [super setUp]; |
| self.testServer->RegisterRequestHandler( |
| base::BindRepeating(&StandardResponse)); |
| GREYAssertTrue(self.testServer->Start(), @"Server did not start."); |
| } |
| |
| // Test switching tabs using the tab strip. |
| - (void)testTabStripSwitchTabs { |
| if ([ChromeEarlGrey isCompactWidth]) { |
| EARL_GREY_TEST_SKIPPED(@"No tab strip on this device."); |
| } |
| |
| // TODO(crbug.com/41010830): Make this test also handle the 'collapsed' tab |
| // case. |
| const int kNumberOfTabs = 3; |
| [ChromeEarlGreyUI openNewTab]; |
| [ChromeEarlGrey loadURL:GURL("chrome://about")]; |
| [ChromeEarlGreyUI openNewTab]; |
| [ChromeEarlGrey loadURL:GURL("chrome://version")]; |
| |
| // Note that the tab ordering wraps. E.g. if A, B, and C are open, |
| // and C is the current tab, the 'next' tab is 'A'. |
| for (int i = 0; i < kNumberOfTabs + 1; i++) { |
| GREYAssertTrue([ChromeEarlGrey mainTabCount] > 1, |
| [ChromeEarlGrey mainTabCount] ? @"Only one tab open." |
| : @"No more tabs."); |
| NSString* nextTabTitle = [ChromeEarlGrey nextTabTitle]; |
| |
| [[EarlGrey |
| selectElementWithMatcher:grey_allOf(grey_text(nextTabTitle), |
| grey_sufficientlyVisible(), nil)] |
| performAction:grey_tap()]; |
| |
| GREYAssertEqualObjects([ChromeEarlGrey currentTabTitle], nextTabTitle, |
| @"The selected tab did not change to the next tab."); |
| } |
| } |
| |
| // Tests dragging URL into regular tab strip. |
| // TODO(crbug.com/330842850): Test is flaky. |
| #if TARGET_IPHONE_SIMULATOR |
| #define MAYBE_testDragAndDropURLIntoRegularTabStrip \ |
| FLAKY_testDragAndDropURLIntoRegularTabStrip |
| #else |
| #define MAYBE_testDragAndDropURLIntoRegularTabStrip \ |
| testDragAndDropURLIntoRegularTabStrip |
| #endif |
| - (void)MAYBE_testDragAndDropURLIntoRegularTabStrip { |
| if ([ChromeEarlGrey isCompactWidth]) { |
| EARL_GREY_TEST_SKIPPED(@"No tab strip on this device."); |
| } |
| |
| const GURL initialURL = self.testServer->GetURL(kInitialPageUrl); |
| [ChromeEarlGrey loadURL:initialURL]; |
| [ChromeEarlGrey |
| waitForWebStateContainingText:kInitialPageDestinationLinkText]; |
| [ChromeEarlGrey waitForMainTabCount:1]; |
| |
| // Drag and drop the link on the page to the tab strip. This should open |
| // a new regular tab. |
| DragDrop(kInitialPageLinkId, kRegularTabStripId); |
| GREYWaitForAppToIdle(@"App failed to idle"); |
| |
| [ChromeEarlGrey waitForWebStateContainingText:kDestinationContent]; |
| [ChromeEarlGrey waitForMainTabCount:2]; |
| } |
| |
| // Tests dragging URL into incognito tab strip. |
| - (void)testDragAndDropURLIntoIncognitoTabStrip { |
| if ([ChromeEarlGrey isCompactWidth]) { |
| EARL_GREY_TEST_SKIPPED(@"No tab strip on this device."); |
| } |
| |
| [ChromeEarlGrey closeAllNormalTabs]; |
| [ChromeEarlGrey openNewIncognitoTab]; |
| |
| const GURL initialURL = self.testServer->GetURL(kInitialPageUrl); |
| [ChromeEarlGrey loadURL:initialURL]; |
| [ChromeEarlGrey |
| waitForWebStateContainingText:kInitialPageDestinationLinkText]; |
| [ChromeEarlGrey waitForIncognitoTabCount:1]; |
| |
| // Drag and drop the link on the page to the tab strip. This should open |
| // a new incognito tab. |
| DragDrop(kInitialPageLinkId, kIncognitoTabStripId); |
| GREYWaitForAppToIdle(@"App failed to idle"); |
| |
| [ChromeEarlGrey waitForWebStateContainingText:kDestinationContent]; |
| [ChromeEarlGrey waitForIncognitoTabCount:2]; |
| } |
| |
| @end |