// 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/functional/bind.h"
#import "base/strings/sys_string_conversions.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/ui/toolbar/adaptive_toolbar_app_interface.h"
#import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
#import "ios/chrome/common/ui/elements/form_input_accessory_view.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_test_case.h"
#import "ios/chrome/test/scoped_eg_synchronization_disabler.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
#import "ios/testing/earl_grey/disabled_test_macros.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#import "ios/web/common/features.h"
#import "ios/web/public/test/element_selector.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"
#import "ui/base/l10n/l10n_util_mac.h"
namespace {
using chrome_test_util::BackButton;
using chrome_test_util::ForwardButton;
using chrome_test_util::TapWebElementWithId;
using chrome_test_util::WebStateScrollViewMatcher;
using chrome_test_util::WebViewMatcher;
const char kPageURL[] = "/test-page.html";
const char kPageURL2[] = "/test-page-2.html";
const char kPageURL3[] = "/test-page-3.html";
const char kLinkID[] = "linkID";
const char kPageLoadedString[] = "Page loaded!";
// The title of the test infobar.
NSString* kTestInfoBarTitle = @"TestInfoBar";
// Defines the visibility of an element, in relation to the toolbar.
typedef NS_ENUM(NSInteger, ButtonVisibility) {
ButtonVisibilityNone,
ButtonVisibilityPrimary,
ButtonVisibilitySecondary
};
// Provides responses for redirect and changed window location 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);
http_response->set_content(
"<html><body><p>" + std::string(kPageLoadedString) +
"</p><a style='font-size:50pt' href=\"" + kPageURL3 + "\" id=\"" +
kLinkID + "\">link!</a></body></html>");
return std::move(http_response);
}
// Returns a matcher for the visible share button.
id<GREYMatcher> ShareButton() {
return grey_allOf(grey_accessibilityID(kToolbarShareButtonIdentifier),
grey_sufficientlyVisible(), nil);
}
// Returns a matcher for the reload button.
id<GREYMatcher> ReloadButton() {
return chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_ACCNAME_RELOAD);
}
// Returns a matcher for the tools menu button.
id<GREYMatcher> ToolsMenuButton() {
return chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_TOOLBAR_SETTINGS);
}
// Returns a matcher for the cancel button.
id<GREYMatcher> CancelButton() {
return chrome_test_util::ButtonWithAccessibilityLabelId(IDS_CANCEL);
}
// Returns a matcher for the search button.
id<GREYMatcher> NewTabButton() {
return grey_accessibilityID(kToolbarNewTabButtonIdentifier);
}
// Returns a matcher for the tab grid button.
id<GREYMatcher> TabGridButton() {
return chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_TOOLBAR_SHOW_TABS);
}
// Returns a matcher for a UIResponder object being first responder.
id<GREYMatcher> firstResponder() {
GREYMatchesBlock matches = ^BOOL(UIResponder* responder) {
return [responder isFirstResponder];
};
GREYDescribeToBlock describe = ^void(id<GREYDescription> description) {
[description appendText:@"first responder"];
};
return grey_allOf(
grey_kindOfClass([UIResponder class]),
[[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches
descriptionBlock:describe],
nil);
}
// Returns a matcher for elements being subviews of the PrimaryToolbarView and
// sufficientlyVisible.
id<GREYMatcher> VisibleInPrimaryToolbar() {
return grey_allOf(grey_ancestor(grey_kindOfClassName(@"PrimaryToolbarView")),
grey_sufficientlyVisible(), nil);
}
// Returns a matcher for elements being subviews of the SecondaryToolbarView and
// sufficientlyVisible.
id<GREYMatcher> VisibleInSecondaryToolbar() {
return grey_allOf(
grey_ancestor(grey_kindOfClassName(@"SecondaryToolbarView")),
grey_sufficientlyVisible(), nil);
}
// Checks that the element designated by `matcher` is `visible` in the primary
// toolbar.
void CheckVisibleInPrimaryToolbar(id<GREYMatcher> matcher, BOOL visible) {
id<GREYMatcher> assertionMatcher = visible ? grey_notNil() : grey_nil();
[[EarlGrey
selectElementWithMatcher:grey_allOf(matcher, VisibleInPrimaryToolbar(),
nil)]
assertWithMatcher:assertionMatcher];
}
// Checks that the element designed by `matcher` is `visible` in the secondary
// toolbar.
void CheckVisibleInSecondaryToolbar(id<GREYMatcher> matcher, BOOL visible) {
id<GREYMatcher> assertionMatcher = visible ? grey_notNil() : grey_nil();
[[EarlGrey
selectElementWithMatcher:grey_allOf(matcher, VisibleInSecondaryToolbar(),
nil)]
assertWithMatcher:assertionMatcher];
}
// Rotate the device if it is an iPhone or change the trait collection to
// compact width if it is an iPad. Returns the new trait collection.
UITraitCollection* RotateOrChangeTraitCollection(
UITraitCollection* originalTraitCollection,
UIViewController* topViewController) {
// Change the orientation or the trait collection.
if ([ChromeEarlGrey isIPadIdiom]) {
// Simulate a multitasking by overriding the trait collections of the view
// controllers. The rotation doesn't work on iPad.
return [AdaptiveToolbarAppInterface
changeTraitCollection:originalTraitCollection
forViewController:topViewController];
} else {
// On iPhone rotate to test the the landscape orientation.
[EarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeLeft
error:nil];
return topViewController.traitCollection;
}
}
// Checks that the element associated with `matcher` is visible in the toolbar
// defined by `visibility`.
void CheckVisibilityInToolbar(id<GREYMatcher> matcher,
ButtonVisibility visibility) {
CheckVisibleInPrimaryToolbar(matcher, visibility == ButtonVisibilityPrimary);
CheckVisibleInSecondaryToolbar(matcher,
visibility == ButtonVisibilitySecondary);
}
// Checks the visibility of the different part of the omnibox, depending on it
// being focused or not.
void CheckOmniboxVisibility(BOOL omniboxFocused) {
// Check omnibox/steady view visibility.
if (omniboxFocused) {
// Check that the omnibox is visible.
CheckVisibleInPrimaryToolbar(chrome_test_util::Omnibox(), YES);
} else {
// Check that location view is visible.
BOOL isBottomOmnibox = [ChromeEarlGrey isUnfocusedOmniboxAtBottom];
ButtonVisibility locationBarVisibility =
isBottomOmnibox ? ButtonVisibilitySecondary : ButtonVisibilityPrimary;
CheckVisibilityInToolbar(chrome_test_util::DefocusedLocationView(),
locationBarVisibility);
}
}
// Check the visibility of the buttons if the device is an iPhone in portrait or
// an iPad in multitasking.
void CheckButtonsVisibilityIPhonePortrait(BOOL omniboxFocused) {
// Check that the cancel button visibility.
if (omniboxFocused) {
CheckVisibilityInToolbar(CancelButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(ShareButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(ReloadButton(), ButtonVisibilityNone);
// Those buttons are hidden by the keyboard.
CheckVisibilityInToolbar(BackButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(ForwardButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(NewTabButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(TabGridButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(ToolsMenuButton(), ButtonVisibilityNone);
} else {
CheckVisibilityInToolbar(CancelButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(ShareButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(ReloadButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(BackButton(), ButtonVisibilitySecondary);
CheckVisibilityInToolbar(ForwardButton(), ButtonVisibilitySecondary);
CheckVisibilityInToolbar(NewTabButton(), ButtonVisibilitySecondary);
CheckVisibilityInToolbar(TabGridButton(), ButtonVisibilitySecondary);
CheckVisibilityInToolbar(ToolsMenuButton(), ButtonVisibilitySecondary);
}
}
// Check the visibility of the buttons if the device is an iPhone in landscape.
void CheckButtonsVisibilityIPhoneLandscape(BOOL omniboxFocused) {
if (omniboxFocused) {
// Omnibox focused in iPhone landscape.
CheckVisibilityInToolbar(CancelButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(ShareButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(ReloadButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(BackButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(ForwardButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(NewTabButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(TabGridButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(ToolsMenuButton(), ButtonVisibilityNone);
} else {
CheckVisibilityInToolbar(CancelButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(ShareButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(ReloadButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(BackButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(ForwardButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(NewTabButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(TabGridButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(ToolsMenuButton(), ButtonVisibilityPrimary);
}
// The secondary toolbar is not visible.
[[EarlGrey
selectElementWithMatcher:grey_kindOfClassName(@"SecondaryToolbarView")]
assertWithMatcher:grey_not(grey_sufficientlyVisible())];
}
// Check the visibility of the buttons if the device is an iPad not in
// multitasking.
void CheckButtonsVisibilityIPad() {
CheckVisibilityInToolbar(CancelButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(ShareButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(ReloadButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(TabGridButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(BackButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(ForwardButton(), ButtonVisibilityPrimary);
CheckVisibilityInToolbar(NewTabButton(), ButtonVisibilityNone);
CheckVisibilityInToolbar(ToolsMenuButton(), ButtonVisibilityPrimary);
// The secondary toolbar is not visible.
[[EarlGrey
selectElementWithMatcher:grey_kindOfClassName(@"SecondaryToolbarView")]
assertWithMatcher:grey_not(grey_sufficientlyVisible())];
}
// Check that the button displayed are the ones which should be displayed in the
// environment described by `traitCollection` and with `omniboxFocused`.
void CheckToolbarButtonVisibility(UITraitCollection* traitCollection,
BOOL omniboxFocused) {
CheckOmniboxVisibility(omniboxFocused);
// Button checks.
if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
CheckButtonsVisibilityIPhonePortrait(omniboxFocused);
} else if (traitCollection.verticalSizeClass ==
UIUserInterfaceSizeClassCompact) {
CheckButtonsVisibilityIPhoneLandscape(omniboxFocused);
} else {
CheckButtonsVisibilityIPad();
}
}
// Check that current URL contains a given string by tapping on the location
// view to focus the omnibox where the full URL can be seen, then comparing
// the strings, and finally defocusing the omnibox.
void CheckCurrentURLContainsString(std::string string) {
[[EarlGrey
selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
assertWithMatcher:chrome_test_util::OmniboxContainingText(string)];
if ([ChromeEarlGrey isIPadIdiom]) {
// Defocus omnibox by tapping the typing shield.
[[EarlGrey
selectElementWithMatcher:grey_accessibilityID(@"Typing Shield")]
performAction:grey_tap()];
} else {
[[EarlGrey
selectElementWithMatcher:
grey_accessibilityID(kToolbarCancelOmniboxEditButtonIdentifier)]
performAction:grey_tap()];
}
}
void FocusOmnibox() {
[ChromeEarlGrey waitForUIElementToAppearWithMatcher:
grey_allOf(chrome_test_util::DefocusedLocationView(),
grey_interactable(), nil)];
[[EarlGrey selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
performAction:grey_tap()];
[ChromeEarlGrey
waitForUIElementToAppearWithMatcher:grey_allOf(
chrome_test_util::Omnibox(),
grey_interactable(), nil)];
[[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
assertWithMatcher:firstResponder()];
}
UIViewController* TopPresentedViewControllerFrom(
UIViewController* base_view_controller) {
UIViewController* topController = base_view_controller;
for (UIViewController* controller = [topController presentedViewController];
controller && ![controller isBeingDismissed];
controller = [controller presentedViewController]) {
topController = controller;
}
return topController;
}
UIViewController* TopPresentedViewController() {
UIViewController* rootViewController =
chrome_test_util::GetAnyKeyWindow().rootViewController;
return TopPresentedViewControllerFrom(rootViewController);
}
// Returns "Move Address Bar to Top" button from the location bar context menu.
id<GREYMatcher> MoveAddressBarToTopContextMenuButton() {
return grey_allOf(chrome_test_util::ContextMenuItemWithAccessibilityLabelId(
IDS_IOS_TOOLBAR_MENU_TOP_OMNIBOX),
grey_accessibilityTrait(UIAccessibilityTraitButton),
grey_hidden(NO), nil);
}
// Returns "Move Address Bar to Bottom" button from the location bar context
// menu.
id<GREYMatcher> MoveAddressBarToBottomContextMenuButton() {
return grey_allOf(chrome_test_util::ContextMenuItemWithAccessibilityLabelId(
IDS_IOS_TOOLBAR_MENU_BOTTOM_OMNIBOX),
grey_accessibilityTrait(UIAccessibilityTraitButton),
grey_hidden(NO), nil);
}
// Returns the matcher for the omnibox typing shield in the form input accessory
// view.
id<GREYMatcher> FormInputAccessoryOmniboxTypingShield() {
return grey_allOf(
grey_accessibilityID(
kFormInputAccessoryViewOmniboxTypingShieldAccessibilityID),
grey_interactable(), nil);
}
} // namespace
#pragma mark - TestCase
// Test case for the adaptive toolbar UI.
@interface AdaptiveToolbarTestCase : ChromeTestCase
@end
@implementation AdaptiveToolbarTestCase
- (void)setUp {
[super setUp];
[ChromeEarlGrey setBoolValue:NO forLocalStatePref:prefs::kBottomOmnibox];
}
// Tests that tapping a button cancels the focus on the omnibox.
- (void)testCancelOmniboxEdit {
if ([ChromeEarlGrey isCompactWidth]) {
EARL_GREY_TEST_SKIPPED(@"No button to tap in compact width.");
}
// Navigate to a page to enable the back button.
[ChromeEarlGrey loadURL:GURL("chrome://version")];
FocusOmnibox();
// Tap the back button and check the omnibox is unfocused.
[[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
assertWithMatcher:grey_not(firstResponder())];
}
// Check the button visibility of the toolbar when the omnibox is focused from a
// different orientation than the default one.
- (void)testFocusOmniboxFromOtherOrientation {
// Load a page to have the toolbar visible (hidden on NTP).
[ChromeEarlGrey loadURL:GURL("chrome://version")];
// Get the original trait collection.
UIViewController* topViewController = TopPresentedViewController();
UITraitCollection* originalTraitCollection =
topViewController.traitCollection;
// Change the orientation or the trait collection.
UITraitCollection* secondTraitCollection =
RotateOrChangeTraitCollection(originalTraitCollection, topViewController);
FocusOmnibox();
// Check the visiblity when focusing the omnibox.
CheckToolbarButtonVisibility(secondTraitCollection, /*omniboxFocused=*/YES);
// Revert the orientation/trait collection to the original.
if ([ChromeEarlGrey isIPadIdiom]) {
// Remove the override.
for (UIViewController* child in topViewController.childViewControllers) {
[topViewController setOverrideTraitCollection:originalTraitCollection
forChildViewController:child];
}
} else {
// Cancel the rotation.
[EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait error:nil];
}
// Check the visiblity after a rotation.
CheckToolbarButtonVisibility(originalTraitCollection, /*omniboxFocused=*/YES);
}
// Check the button visibility of the toolbar when the omnibox is focused from
// the default orientation.
- (void)testFocusOmniboxFromPortrait {
// Load a page to have the toolbar visible (hidden on NTP).
[ChromeEarlGrey loadURL:GURL("chrome://version")];
FocusOmnibox();
// Get the original trait collection.
UIViewController* topViewController = TopPresentedViewController();
UITraitCollection* originalTraitCollection =
topViewController.traitCollection;
// Check the button visibility.
CheckToolbarButtonVisibility(originalTraitCollection, /*omniboxFocused=*/YES);
// Change the orientation or the trait collection.
UITraitCollection* secondTraitCollection =
RotateOrChangeTraitCollection(originalTraitCollection, topViewController);
// Check the visiblity after a size class change.
CheckToolbarButtonVisibility(secondTraitCollection, /*omniboxFocused=*/YES);
if ([ChromeEarlGrey isIPadIdiom]) {
// Remove the override.
for (UIViewController* child in topViewController.childViewControllers) {
[topViewController setOverrideTraitCollection:originalTraitCollection
forChildViewController:child];
}
} else {
// Cancel the rotation.
[EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait error:nil];
}
// Check the visiblity after a size class change. This should let the trait
// collection change come into effect.
CheckToolbarButtonVisibility(originalTraitCollection, /*omniboxFocused=*/YES);
}
// Verifies that the back/forward buttons are working and are correctly enabled
// during navigations.
- (void)testNavigationButtons {
// Setup the server.
self.testServer->RegisterRequestHandler(
base::BindRepeating(&StandardResponse));
GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
// Loads two url and check the navigation buttons status.
[ChromeEarlGrey loadURL:self.testServer->GetURL(kPageURL)];
[ChromeEarlGrey loadURL:self.testServer->GetURL(kPageURL2)];
[[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
assertWithMatcher:grey_interactable()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::ForwardButton()]
assertWithMatcher:grey_not(grey_enabled())];
// Check the navigation to the second page occurred.
CheckCurrentURLContainsString(kPageURL2);
// Go back.
[[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
performAction:grey_tap()];
CheckCurrentURLContainsString(kPageURL);
// Check the buttons status.
[[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
assertWithMatcher:grey_interactable()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::ForwardButton()]
assertWithMatcher:grey_interactable()];
// Go forward.
[[EarlGrey selectElementWithMatcher:chrome_test_util::ForwardButton()]
performAction:grey_tap()];
CheckCurrentURLContainsString(kPageURL2);
// Check the buttons status.
[[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
assertWithMatcher:grey_interactable()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::ForwardButton()]
assertWithMatcher:grey_not(grey_enabled())];
// Open a page in a new incognito tab to have the focus.
[[EarlGrey selectElementWithMatcher:WebViewMatcher()]
performAction:chrome_test_util::LongPressElementForContextMenu(
[ElementSelector selectorWithElementID:kLinkID],
true /* menu should appear */)];
[[EarlGrey selectElementWithMatcher:
chrome_test_util::StaticTextWithAccessibilityLabelId(
IDS_IOS_OPEN_IN_INCOGNITO_ACTION_TITLE)]
performAction:grey_tap()];
// Check the buttons status.
[[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
assertWithMatcher:grey_not(grey_enabled())];
[[EarlGrey selectElementWithMatcher:chrome_test_util::ForwardButton()]
assertWithMatcher:grey_not(grey_enabled())];
// Close incognito tab.
[ChromeEarlGrey closeAllTabs];
}
// Tests that tapping the NewTab button opens a new tab.
- (void)testNewTabButton {
if (![ChromeEarlGrey isSplitToolbarMode]) {
EARL_GREY_TEST_SKIPPED(@"No button to tap.");
}
[ChromeEarlGrey waitForMainTabCount:1];
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(
kToolbarNewTabButtonIdentifier)]
performAction:grey_tap()];
[ChromeEarlGrey waitForMainTabCount:2];
}
// Tests share button is enabled only on pages that can be shared.
- (void)testShareButton {
if (![ChromeEarlGrey isIPadIdiom]) {
// If this test is run on an iPhone, rotate it to have the unsplit toolbar.
[EarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeLeft
error:nil];
}
// Setup the server.
self.testServer->RegisterRequestHandler(
base::BindRepeating(&StandardResponse));
GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
const GURL pageURL = self.testServer->GetURL(kPageURL);
// Navigate to another page and check that the share button is enabled.
[ChromeEarlGrey loadURL:pageURL];
[[EarlGrey selectElementWithMatcher:ShareButton()]
assertWithMatcher:grey_interactable()];
if (![ChromeEarlGrey isIPadIdiom]) {
// Cancel rotation.
[EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait error:nil];
}
}
// Test that the bottom toolbar is still visible after closing the last
// incognito tab using long press. See https://crbug.com/849937.
- (void)testBottomToolbarHeightAfterClosingTab {
if (![ChromeEarlGrey isSplitToolbarMode])
EARL_GREY_TEST_SKIPPED(@"This test needs a bottom toolbar.");
// Close all tabs.
[[EarlGrey selectElementWithMatcher:TabGridButton()]
performAction:grey_tap()];
[[self class] closeAllTabs];
// Open incognito tab.
[[EarlGrey selectElementWithMatcher:chrome_test_util::
TabGridIncognitoTabsPanelButton()]
performAction:grey_tap()];
[[EarlGrey
selectElementWithMatcher:chrome_test_util::TabGridNewIncognitoTabButton()]
performAction:grey_tap()];
GREYWaitForAppToIdleWithTimeout(5.0, @"App failed to idle");
[[self class] closeAllTabs];
[ChromeEarlGrey openNewTab];
// Check that the bottom toolbar is visible.
[[EarlGrey selectElementWithMatcher:NewTabButton()]
assertWithMatcher:grey_sufficientlyVisible()];
}
// Verifies the existence and state of toolbar UI elements.
- (void)testToolbarUI {
// Load a page to have the toolbar visible (hidden on NTP).
[ChromeEarlGrey loadURL:GURL("chrome://version")];
// Get the original trait collection.
UIViewController* topViewController = TopPresentedViewController();
UITraitCollection* originalTraitCollection =
topViewController.traitCollection;
// Check the button visibility.
CheckToolbarButtonVisibility(originalTraitCollection, /*omniboxFocused=*/NO);
// Change the orientation or the trait collection.
UITraitCollection* secondTraitCollection =
RotateOrChangeTraitCollection(originalTraitCollection, topViewController);
// Check the visiblity after a size class change.
CheckToolbarButtonVisibility(secondTraitCollection, /*omniboxFocused=*/NO);
if ([ChromeEarlGrey isIPadIdiom]) {
// Remove the override.
for (UIViewController* child in topViewController.childViewControllers) {
[topViewController setOverrideTraitCollection:originalTraitCollection
forChildViewController:child];
}
} else {
// Cancel the rotation.
[EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait error:nil];
}
}
// Verifies that the location bar is hidden on NTP.
- (void)testLocationBarHiddenOnNTP {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"The NTP is sometimes scrolled on iPad, making the "
@"location bar visible.");
}
// Location bar should be hidden when loading NTP.
CheckVisibilityInToolbar(chrome_test_util::DefocusedLocationView(),
ButtonVisibilityNone);
// Location bar should be hidden when returning to NTP with the back button.
[ChromeEarlGrey loadURL:GURL("chrome://version")];
[[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
performAction:grey_tap()];
[ChromeEarlGrey waitForPageToFinishLoading];
CheckVisibilityInToolbar(chrome_test_util::DefocusedLocationView(),
ButtonVisibilityNone);
// Change the orientation or the trait collection.
UIViewController* topViewController = TopPresentedViewController();
UITraitCollection* originalTraitCollection =
topViewController.traitCollection;
RotateOrChangeTraitCollection(originalTraitCollection, topViewController);
CheckVisibilityInToolbar(chrome_test_util::DefocusedLocationView(),
ButtonVisibilityNone);
// Revert the orientation/trait collection to the original.
if ([ChromeEarlGrey isIPadIdiom]) {
// Remove the override.
for (UIViewController* child in topViewController.childViewControllers) {
[topViewController setOverrideTraitCollection:originalTraitCollection
forChildViewController:child];
}
} else {
// Cancel the rotation.
[EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait error:nil];
}
// Check the visiblity after a rotation.
CheckVisibilityInToolbar(chrome_test_util::DefocusedLocationView(),
ButtonVisibilityNone);
}
@end
#pragma mark - Bottom omnibox tests
// AdaptiveToolbarTestCase with a bottom default omnibox position.
@interface AdaptiveToolbarBottomOmniboxTestCase : AdaptiveToolbarTestCase
@end
@implementation AdaptiveToolbarBottomOmniboxTestCase
- (void)setUp {
[super setUp];
[ChromeEarlGrey setBoolValue:YES forLocalStatePref:prefs::kBottomOmnibox];
}
// Verifies that the address bar can be moved from the location bar context
// menu.
- (void)testMoveAddressBarContextAction {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Bottom address bar is only available on iPhone.");
}
// Load a page to have the toolbar visible (hidden on NTP).
[ChromeEarlGrey loadURL:GURL("chrome://version")];
CheckVisibilityInToolbar(chrome_test_util::DefocusedLocationView(),
ButtonVisibilitySecondary);
// Check address bar can be moved to the top toolbar.
[[EarlGrey selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
performAction:grey_longPress()];
[[EarlGrey selectElementWithMatcher:MoveAddressBarToTopContextMenuButton()]
performAction:grey_tap()];
[ChromeEarlGrey waitForUIElementToAppearWithMatcher:
grey_allOf(chrome_test_util::DefocusedLocationView(),
VisibleInPrimaryToolbar(), nil)];
CheckVisibilityInToolbar(chrome_test_util::DefocusedLocationView(),
ButtonVisibilityPrimary);
// Check address bar can be moved to the bottom toolbar.
[[EarlGrey selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
performAction:grey_longPress()];
[[EarlGrey selectElementWithMatcher:MoveAddressBarToBottomContextMenuButton()]
performAction:grey_tap()];
[ChromeEarlGrey waitForUIElementToAppearWithMatcher:
grey_allOf(chrome_test_util::DefocusedLocationView(),
VisibleInSecondaryToolbar(), nil)];
CheckVisibilityInToolbar(chrome_test_util::DefocusedLocationView(),
ButtonVisibilitySecondary);
}
// Verifies that the location bar is above the keyboard when tapping a text
// field on web. Use the `matcherForOmniboxAboveKeyboard` to dismiss the
// keyboard. When voice over is off, tapping the collapsed bottom omnibox
// interacts with the `omniboxTypingShield` of `formInputAccessoryView`. When
// voice over is on, `DefocusedLocationView` is focused instead.
- (void)verifyTapLocationBarAboveTheKeyboardWithMatcher:
(id<GREYMatcher>)matcherForOmniboxAboveKeyboard {
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Bottom address bar is only available on iPhone.");
}
GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
GURL URL = self.testServer->GetURL("/multi_field_form.html");
[ChromeEarlGrey loadURL:URL];
[ChromeEarlGrey waitForWebStateContainingText:"hello!"];
// Opening the keyboard from a webview blocks EarlGrey's synchronization.
{
ScopedSynchronizationDisabler disabler;
// Brings up the keyboard by tapping on one of the form's field.
[[EarlGrey selectElementWithMatcher:WebViewMatcher()]
performAction:TapWebElementWithId("username")];
GREYWaitForAppToIdleWithTimeout(5.0, @"App failed to idle");
// Taping the location bar above the keyboard should dismiss the keyboard.
[[EarlGrey selectElementWithMatcher:matcherForOmniboxAboveKeyboard]
performAction:grey_tap()];
GREYWaitForAppToIdleWithTimeout(5.0, @"App failed to idle");
// Taping the location bar again should focus the omnibox.
FocusOmnibox();
}
}
// Verifies that the location bar is above the keyboard when tapping a text
// field on web. Tapping it should dismiss the keyboard.
- (void)testTapLocationBarAboveTheKeyboard {
[self verifyTapLocationBarAboveTheKeyboardWithMatcher:
FormInputAccessoryOmniboxTypingShield()];
}
// Verifies that the location bar is above the keyboard when tapping a text
// field on web. Simulate a tap with voice over and verify that it dismisses the
// keyboard.
- (void)testTapLocationBarAboveTheKeyboardForVoiceOver {
[self verifyTapLocationBarAboveTheKeyboardWithMatcher:
chrome_test_util::DefocusedLocationView()];
}
@end