| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import <EarlGrey/EarlGrey.h> |
| #import <UIKit/UIKit.h> |
| #import <XCTest/XCTest.h> |
| |
| #include "base/ios/ios_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/sys_string_conversions.h" |
| #import "base/test/ios/wait_util.h" |
| #include "components/browsing_data/core/pref_names.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
| #include "ios/chrome/browser/chrome_url_constants.h" |
| #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view.h" |
| #import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h" |
| #import "ios/chrome/browser/ui/history/history_ui_constants.h" |
| #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h" |
| #import "ios/chrome/browser/ui/settings/settings_table_view_controller.h" |
| #import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h" |
| #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller_constants.h" |
| #import "ios/chrome/browser/ui/util/transparent_link_button.h" |
| #include "ios/chrome/browser/ui/util/ui_util.h" |
| #include "ios/chrome/common/string_util.h" |
| #include "ios/chrome/grit/ios_strings.h" |
| #import "ios/chrome/test/app/chrome_test_util.h" |
| #import "ios/chrome/test/earl_grey/accessibility_util.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_error_util.h" |
| #import "ios/chrome/test/earl_grey/chrome_matchers.h" |
| #import "ios/chrome/test/earl_grey/chrome_test_case.h" |
| #import "ios/public/provider/chrome/browser/signin/chrome_identity.h" |
| #import "ios/public/provider/chrome/browser/signin/fake_chrome_identity.h" |
| #import "ios/public/provider/chrome/browser/signin/fake_chrome_identity_service.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/mac/url_conversions.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| using chrome_test_util::ButtonWithAccessibilityLabelId; |
| using chrome_test_util::ContextMenuCopyButton; |
| using chrome_test_util::NavigationBarDoneButton; |
| using chrome_test_util::OpenLinkInNewTabButton; |
| |
| namespace { |
| char kURL1[] = "http://firstURL"; |
| char kURL2[] = "http://secondURL"; |
| char kURL3[] = "http://thirdURL"; |
| char kTitle1[] = "Page 1"; |
| char kTitle2[] = "Page 2"; |
| char kResponse1[] = "Test Page 1 content"; |
| char kResponse2[] = "Test Page 2 content"; |
| char kResponse3[] = "Test Page 3 content"; |
| |
| // Matcher for entry in history for URL and title. |
| id<GREYMatcher> HistoryEntry(const GURL& url, const std::string& title) { |
| NSString* url_spec_text = nil; |
| NSString* title_text = base::SysUTF8ToNSString(title); |
| |
| url_spec_text = base::SysUTF8ToNSString(url.GetOrigin().spec()); |
| |
| MatchesBlock matches = ^BOOL(TableViewURLCell* cell) { |
| return [cell.titleLabel.text isEqual:title_text] && |
| [cell.URLLabel.text isEqual:url_spec_text]; |
| }; |
| |
| DescribeToBlock describe = ^(id<GREYDescription> description) { |
| [description appendText:@"view containing URL text: "]; |
| [description appendText:url_spec_text]; |
| [description appendText:@" title text: "]; |
| [description appendText:title_text]; |
| }; |
| return grey_allOf( |
| grey_kindOfClass([TableViewURLCell class]), |
| [[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches |
| descriptionBlock:describe], |
| grey_sufficientlyVisible(), nil); |
| } |
| // Matcher for the history button in the tools menu. |
| id<GREYMatcher> HistoryButton() { |
| return grey_accessibilityID(kToolsMenuHistoryId); |
| } |
| // Matcher for the edit button in the navigation bar. |
| id<GREYMatcher> NavigationEditButton() { |
| return grey_accessibilityID(kHistoryToolbarEditButtonIdentifier); |
| } |
| // Matcher for the delete button. |
| id<GREYMatcher> DeleteHistoryEntriesButton() { |
| return grey_accessibilityID(kHistoryToolbarDeleteButtonIdentifier); |
| } |
| // Matcher for the search button. |
| id<GREYMatcher> SearchIconButton() { |
| return grey_accessibilityID(kHistorySearchControllerSearchBarIdentifier); |
| } |
| // Matcher for the cancel button. |
| id<GREYMatcher> CancelButton() { |
| return grey_accessibilityID(kHistoryToolbarCancelButtonIdentifier); |
| } |
| // Matcher for the Open in New Incognito Tab option in the context menu. |
| id<GREYMatcher> OpenInNewIncognitoTabButton() { |
| return ButtonWithAccessibilityLabelId( |
| IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWINCOGNITOTAB); |
| } |
| |
| } // namespace |
| |
| // History UI tests. |
| @interface HistoryUITestCase : ChromeTestCase { |
| GURL _URL1; |
| GURL _URL2; |
| GURL _URL3; |
| } |
| |
| // Loads three test URLs. |
| - (void)loadTestURLs; |
| // Displays the history UI. |
| - (void)openHistoryPanel; |
| // Resets which data is selected in the Clear Browsing Data UI. |
| - (void)resetBrowsingDataPrefs; |
| |
| @end |
| |
| @implementation HistoryUITestCase |
| |
| // Set up called once for the class. |
| + (void)setUp { |
| [super setUp]; |
| std::map<GURL, std::string> responses; |
| const char kPageFormat[] = "<head><title>%s</title></head><body>%s</body>"; |
| responses[web::test::HttpServer::MakeUrl(kURL1)] = |
| base::StringPrintf(kPageFormat, kTitle1, kResponse1); |
| responses[web::test::HttpServer::MakeUrl(kURL2)] = |
| base::StringPrintf(kPageFormat, kTitle2, kResponse2); |
| // Page 3 does not have <title> tag, so URL will be its title. |
| responses[web::test::HttpServer::MakeUrl(kURL3)] = kResponse3; |
| web::test::SetUpSimpleHttpServer(responses); |
| } |
| |
| - (void)setUp { |
| [super setUp]; |
| _URL1 = web::test::HttpServer::MakeUrl(kURL1); |
| _URL2 = web::test::HttpServer::MakeUrl(kURL2); |
| _URL3 = web::test::HttpServer::MakeUrl(kURL3); |
| [ChromeEarlGrey clearBrowsingHistory]; |
| // Some tests rely on a clean state for the "Clear Browsing Data" settings |
| // screen. |
| [self resetBrowsingDataPrefs]; |
| } |
| |
| - (void)tearDown { |
| NSError* error = nil; |
| // Dismiss search bar by pressing cancel, if present. Passing error prevents |
| // failure if the element is not found. |
| [[EarlGrey selectElementWithMatcher:CancelButton()] performAction:grey_tap() |
| error:&error]; |
| // Dismiss history panel by pressing done, if present. Passing error prevents |
| // failure if the element is not found. |
| [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()] |
| performAction:grey_tap() |
| error:&error]; |
| |
| // Some tests change the default values for the "Clear Browsing Data" settings |
| // screen. |
| [self resetBrowsingDataPrefs]; |
| [super tearDown]; |
| } |
| |
| #pragma mark Tests |
| |
| // Tests that no history is shown if there has been no navigation. |
| - (void)testDisplayNoHistory { |
| [self openHistoryPanel]; |
| [ChromeEarlGreyUI assertHistoryHasNoEntries]; |
| } |
| |
| // Tests that the history panel displays navigation history. |
| - (void)testDisplayHistory { |
| [self loadTestURLs]; |
| [self openHistoryPanel]; |
| |
| // Assert that history displays three entries. |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| assertWithMatcher:grey_notNil()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL2, kTitle2)] |
| assertWithMatcher:grey_notNil()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL3, _URL3.GetContent())] |
| assertWithMatcher:grey_notNil()]; |
| |
| // Tap a history entry and assert that navigation to that entry's URL occurs. |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| performAction:grey_tap()]; |
| CHROME_EG_ASSERT_NO_ERROR( |
| [ChromeEarlGrey waitForWebStateContainingText:kResponse1]); |
| } |
| |
| // Tests that history is not changed after performing back navigation. |
| - (void)testHistoryUpdateAfterBackNavigation { |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:_URL1]); |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:_URL2]); |
| |
| [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()] |
| performAction:grey_tap()]; |
| CHROME_EG_ASSERT_NO_ERROR( |
| [ChromeEarlGrey waitForWebStateContainingText:kResponse1]); |
| |
| [self openHistoryPanel]; |
| |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| assertWithMatcher:grey_notNil()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL2, kTitle2)] |
| assertWithMatcher:grey_notNil()]; |
| } |
| |
| // Tests that searching history displays only entries matching the search term. |
| - (void)testSearchHistory { |
| // TODO(crbug.com/753098): Re-enable this test on iPad once grey_typeText |
| // works on iOS 11. |
| if (IsIPadIdiom()) { |
| EARL_GREY_TEST_DISABLED(@"Test disabled on iPad."); |
| } |
| |
| [self loadTestURLs]; |
| [self openHistoryPanel]; |
| [[EarlGrey selectElementWithMatcher:SearchIconButton()] |
| performAction:grey_tap()]; |
| |
| // Verify that scrim is visible. |
| [[EarlGrey selectElementWithMatcher:grey_accessibilityID( |
| kHistorySearchScrimIdentifier)] |
| assertWithMatcher:grey_notNil()]; |
| |
| NSString* searchString = |
| [NSString stringWithFormat:@"%s", _URL1.path().c_str()]; |
| |
| [[EarlGrey selectElementWithMatcher:SearchIconButton()] |
| performAction:grey_typeText(searchString)]; |
| |
| // Verify that scrim is not visible. |
| [[EarlGrey selectElementWithMatcher:grey_accessibilityID( |
| kHistorySearchScrimIdentifier)] |
| assertWithMatcher:grey_nil()]; |
| |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| assertWithMatcher:grey_notNil()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL2, kTitle2)] |
| assertWithMatcher:grey_nil()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL3, _URL3.GetContent())] |
| assertWithMatcher:grey_nil()]; |
| } |
| |
| // Tests that long press on scrim while search box is enabled dismisses the |
| // search controller. |
| - (void)testSearchLongPressOnScrimCancelsSearchController { |
| [self loadTestURLs]; |
| [self openHistoryPanel]; |
| [[EarlGrey selectElementWithMatcher:SearchIconButton()] |
| performAction:grey_tap()]; |
| |
| // Try long press. |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| performAction:grey_longPress()]; |
| |
| // Verify context menu is not visible. |
| [[EarlGrey |
| selectElementWithMatcher:ButtonWithAccessibilityLabelId( |
| IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB)] |
| assertWithMatcher:grey_nil()]; |
| |
| // Verify that scrim is not visible. |
| [[EarlGrey selectElementWithMatcher:grey_accessibilityID( |
| kHistorySearchScrimIdentifier)] |
| assertWithMatcher:grey_nil()]; |
| |
| // Verifiy we went back to original folder content. |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| assertWithMatcher:grey_notNil()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL2, kTitle2)] |
| assertWithMatcher:grey_notNil()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL3, _URL3.GetContent())] |
| assertWithMatcher:grey_notNil()]; |
| } |
| |
| // Tests deletion of history entries. |
| - (void)testDeleteHistory { |
| [self loadTestURLs]; |
| [self openHistoryPanel]; |
| |
| // Assert that three history elements are present. |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| assertWithMatcher:grey_notNil()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL2, kTitle2)] |
| assertWithMatcher:grey_notNil()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL3, _URL3.GetContent())] |
| assertWithMatcher:grey_notNil()]; |
| |
| // Enter edit mode, select a history element, and press delete. |
| [[EarlGrey selectElementWithMatcher:NavigationEditButton()] |
| performAction:grey_tap()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| performAction:grey_tap()]; |
| [[EarlGrey selectElementWithMatcher:DeleteHistoryEntriesButton()] |
| performAction:grey_tap()]; |
| |
| // Assert that the deleted entry is gone and the other two remain. |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| assertWithMatcher:grey_nil()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL2, kTitle2)] |
| assertWithMatcher:grey_notNil()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL3, _URL3.GetContent())] |
| assertWithMatcher:grey_notNil()]; |
| |
| // Enter edit mode, select both remaining entries, and press delete. |
| [[EarlGrey selectElementWithMatcher:NavigationEditButton()] |
| performAction:grey_tap()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL2, kTitle2)] |
| performAction:grey_tap()]; |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL3, _URL3.GetContent())] |
| performAction:grey_tap()]; |
| [[EarlGrey selectElementWithMatcher:DeleteHistoryEntriesButton()] |
| performAction:grey_tap()]; |
| |
| [ChromeEarlGreyUI assertHistoryHasNoEntries]; |
| } |
| |
| // Tests clear browsing history. |
| - (void)testClearBrowsingHistory { |
| [self loadTestURLs]; |
| [self openHistoryPanel]; |
| |
| [ChromeEarlGreyUI openAndClearBrowsingDataFromHistory]; |
| [ChromeEarlGreyUI assertHistoryHasNoEntries]; |
| } |
| |
| // Tests display and selection of 'Open in New Tab' in a context menu on a |
| // history entry. |
| - (void)testContextMenuOpenInNewTab { |
| [self loadTestURLs]; |
| [self openHistoryPanel]; |
| |
| // Long press on the history element. |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| performAction:grey_longPress()]; |
| |
| // Select "Open in New Tab" and confirm that new tab is opened with selected |
| // URL. |
| [[EarlGrey selectElementWithMatcher:OpenLinkInNewTabButton()] |
| performAction:grey_tap()]; |
| [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText( |
| _URL1.GetContent())] |
| assertWithMatcher:grey_notNil()]; |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForMainTabCount:2]); |
| } |
| |
| // Tests display and selection of 'Open in New Incognito Tab' in a context menu |
| // on a history entry. |
| - (void)testContextMenuOpenInNewIncognitoTab { |
| [self loadTestURLs]; |
| [self openHistoryPanel]; |
| |
| // Long press on the history element. |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| performAction:grey_longPress()]; |
| |
| // Select "Open in New Incognito Tab" and confirm that new tab is opened in |
| // incognito with the selected URL. |
| [[EarlGrey selectElementWithMatcher:OpenInNewIncognitoTabButton()] |
| performAction:grey_tap()]; |
| [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText( |
| _URL1.GetContent())] |
| assertWithMatcher:grey_notNil()]; |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForMainTabCount:1]); |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForIncognitoTabCount:1]); |
| } |
| |
| // Tests display and selection of 'Copy URL' in a context menu on a history |
| // entry. |
| - (void)testContextMenuCopy { |
| ProceduralBlock clearPasteboard = ^{ |
| [[UIPasteboard generalPasteboard] setURLs:nil]; |
| }; |
| [self setTearDownHandler:clearPasteboard]; |
| clearPasteboard(); |
| |
| [self loadTestURLs]; |
| [self openHistoryPanel]; |
| |
| // Long press on the history element. |
| [[EarlGrey selectElementWithMatcher:HistoryEntry(_URL1, kTitle1)] |
| performAction:grey_longPress()]; |
| |
| // Tap "Copy URL" and wait for the URL to be copied to the pasteboard. |
| [[EarlGrey selectElementWithMatcher:ContextMenuCopyButton()] |
| performAction:grey_tap()]; |
| bool success = base::test::ios::WaitUntilConditionOrTimeout( |
| base::test::ios::kWaitForUIElementTimeout, ^{ |
| return _URL1 == |
| net::GURLWithNSURL([UIPasteboard generalPasteboard].URL); |
| }); |
| GREYAssertTrue(success, @"Pasteboard URL was not set to %s", |
| _URL1.spec().c_str()); |
| } |
| |
| // Navigates to history and checks elements for accessibility. |
| - (void)testAccessibilityOnHistory { |
| [self loadTestURLs]; |
| [self openHistoryPanel]; |
| chrome_test_util::VerifyAccessibilityForCurrentScreen(); |
| // Close history. |
| id<GREYMatcher> exitMatcher = |
| grey_accessibilityID(kHistoryNavigationControllerDoneButtonIdentifier); |
| [[EarlGrey selectElementWithMatcher:exitMatcher] performAction:grey_tap()]; |
| } |
| |
| #pragma mark Helper Methods |
| |
| - (void)loadTestURLs { |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:_URL1]); |
| CHROME_EG_ASSERT_NO_ERROR( |
| [ChromeEarlGrey waitForWebStateContainingText:kResponse1]); |
| |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:_URL2]); |
| CHROME_EG_ASSERT_NO_ERROR( |
| [ChromeEarlGrey waitForWebStateContainingText:kResponse2]); |
| |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:_URL3]); |
| CHROME_EG_ASSERT_NO_ERROR( |
| [ChromeEarlGrey waitForWebStateContainingText:kResponse3]); |
| } |
| |
| - (void)openHistoryPanel { |
| [ChromeEarlGreyUI openToolsMenu]; |
| [ChromeEarlGreyUI tapToolsMenuButton:HistoryButton()]; |
| } |
| |
| - (void)resetBrowsingDataPrefs { |
| PrefService* prefs = chrome_test_util::GetOriginalBrowserState()->GetPrefs(); |
| prefs->ClearPref(browsing_data::prefs::kDeleteBrowsingHistory); |
| prefs->ClearPref(browsing_data::prefs::kDeleteCookies); |
| prefs->ClearPref(browsing_data::prefs::kDeleteCache); |
| prefs->ClearPref(browsing_data::prefs::kDeletePasswords); |
| prefs->ClearPref(browsing_data::prefs::kDeleteFormData); |
| } |
| |
| @end |