blob: feb43e4b05b03bf089c2acbe71e17c2f5e6e9562 [file] [log] [blame]
// 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/strings/sys_string_conversions.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/history/history_entry_item.h"
#import "ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h"
#import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
#import "ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h"
#import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h"
#import "ios/chrome/browser/ui/util/transparent_link_button.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_assertions.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/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/testing/wait_util.h"
#import "ios/web/public/test/http_server.h"
#import "ios/web/public/test/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;
namespace {
char kURL1[] = "http://firstURL";
char kURL2[] = "http://secondURL";
char kURL3[] = "http://thirdURL";
char kResponse1[] = "Test Page 1";
char kResponse2[] = "Test Page 2";
char kResponse3[] = "Test Page 3";
// Matcher for entry in history for URL.
id<GREYMatcher> HistoryEntryWithUrl(const GURL& url) {
NSString* url_spec = base::SysUTF8ToNSString(url.spec());
return grey_allOf(grey_text(url_spec), grey_sufficientlyVisible(), nil);
}
// Matcher for the history button in the tools menu.
id<GREYMatcher> HistoryButton() {
return ButtonWithAccessibilityLabelId(IDS_HISTORY_SHOW_HISTORY);
}
// Matcher for the done button in the navigation bar.
id<GREYMatcher> NavigationDoneButton() {
// Include sufficientlyVisible condition for the case of the clear browsing
// dialog, which also has a "Done" button and is displayed over the history
// panel.
return grey_allOf(
ButtonWithAccessibilityLabelId(IDS_IOS_NAVIGATION_BAR_DONE_BUTTON),
grey_sufficientlyVisible(), nil);
}
// Matcher for the edit button in the navigation bar.
id<GREYMatcher> NavigationEditButton() {
return ButtonWithAccessibilityLabelId(IDS_HISTORY_START_EDITING_BUTTON);
}
// Matcher for the delete button.
id<GREYMatcher> DeleteHistoryEntriesButton() {
// Include class restriction to exclude MDCCollectionViewInfoBar, which is
// hidden.
return grey_allOf(ButtonWithAccessibilityLabelId(
IDS_HISTORY_DELETE_SELECTED_ENTRIES_BUTTON),
grey_kindOfClass([UIButton class]), nil);
}
// Matcher for the search button.
id<GREYMatcher> SearchIconButton() {
return ButtonWithAccessibilityLabelId(IDS_IOS_ICON_SEARCH);
}
// Matcher for the cancel button.
id<GREYMatcher> CancelButton() {
return ButtonWithAccessibilityLabelId(IDS_HISTORY_CANCEL_EDITING_BUTTON);
}
// Matcher for the button to open the clear browsing data panel.
id<GREYMatcher> OpenClearBrowsingDataButton() {
return ButtonWithAccessibilityLabelId(
IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG);
}
// Matcher for the Open in New Tab option in the context menu.
id<GREYMatcher> OpenInNewTabButton() {
return ButtonWithAccessibilityLabelId(IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB);
}
// Matcher for the Open in New Incognito Tab option in the context menu.
id<GREYMatcher> OpenInNewIncognitoTabButton() {
return ButtonWithAccessibilityLabelId(
IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWINCOGNITOTAB);
}
// Matcher for the Copy URL option in the context menu.
id<GREYMatcher> CopyUrlButton() {
return ButtonWithAccessibilityLabelId(IDS_IOS_CONTENT_CONTEXT_COPY);
}
// Matcher for the clear cookies cell on the clear browsing data panel.
id<GREYMatcher> ClearCookiesButton() {
return grey_accessibilityID(kClearCookiesCellId);
}
// Matcher for the clear cache cell on the clear browsing data panel.
id<GREYMatcher> ClearCacheButton() {
return grey_allOf(grey_accessibilityID(kClearCacheCellId),
grey_sufficientlyVisible(), nil);
}
// Matcher for the clear browsing data button on the clear browsing data panel.
id<GREYMatcher> ClearBrowsingDataButton() {
return ButtonWithAccessibilityLabelId(IDS_IOS_CLEAR_BUTTON);
}
// Sign in with a mock identity.
void MockSignIn() {
// Set up a mock identity.
ChromeIdentity* identity =
[FakeChromeIdentity identityWithEmail:@"foo@gmail.com"
gaiaID:@"fooID"
name:@"Fake Foo"];
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
identity);
[ChromeEarlGreyUI openToolsMenu];
[[[EarlGrey
selectElementWithMatcher:grey_accessibilityID(kToolsMenuSettingsId)]
usingSearchAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)
onElementWithMatcher:grey_accessibilityID(kToolsMenuTableViewId)]
performAction:grey_tap()];
[[EarlGrey
selectElementWithMatcher:grey_accessibilityID(kSettingsSignInCellId)]
performAction:grey_tap()];
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabel(
identity.userEmail)]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:
chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_SIGNIN_BUTTON)]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:
chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_OK_BUTTON)]
performAction:grey_tap()];
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_NAVIGATION_BAR_DONE_BUTTON)]
performAction:grey_tap()];
}
} // 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;
// Asserts that the history UI displays no history entries.
- (void)assertNoHistoryShown;
// 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;
responses[web::test::HttpServer::MakeUrl(kURL1)] = kResponse1;
responses[web::test::HttpServer::MakeUrl(kURL2)] = kResponse2;
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:NavigationDoneButton()]
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];
[self assertNoHistoryShown];
}
// Tests that the history panel displays navigation history.
- (void)testDisplayHistory {
[self loadTestURLs];
[self openHistoryPanel];
// Assert that history displays three entries.
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL1)]
assertWithMatcher:grey_notNil()];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL2)]
assertWithMatcher:grey_notNil()];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL3)]
assertWithMatcher:grey_notNil()];
// Tap a history entry and assert that navigation to that entry's URL occurs.
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL1)]
performAction:grey_tap()];
id<GREYMatcher> webViewMatcher =
chrome_test_util::WebViewContainingText(kResponse1);
[[EarlGrey selectElementWithMatcher:webViewMatcher]
assertWithMatcher:grey_notNil()];
}
// Test that history displays a message about entries only if the user is logged
// in, and that tapping on the link in the message opens a new tab with the sync
// help page.
- (void)testHistoryEntriesStatusCell {
[self loadTestURLs];
[self openHistoryPanel];
// Assert that no message is shown when the user is not signed in.
NSRange range;
NSString* entriesMessage = ParseStringWithLink(
l10n_util::GetNSString(IDS_IOS_HISTORY_NO_SYNCED_RESULTS), &range);
[[EarlGrey selectElementWithMatcher:grey_text(entriesMessage)]
assertWithMatcher:grey_nil()];
[[EarlGrey selectElementWithMatcher:NavigationDoneButton()]
performAction:grey_tap()];
// Sign in and assert that the page indicates what type of history entries
// are shown.
MockSignIn();
[self openHistoryPanel];
// Assert that message about entries is shown. The "history is showing local
// entries" message will be shown because sync is not set up for this test.
[[EarlGrey selectElementWithMatcher:grey_text(entriesMessage)]
assertWithMatcher:grey_notNil()];
// Tap on "Learn more" link and assert that new tab with the link is opened.
[[EarlGrey
selectElementWithMatcher:grey_kindOfClass([TransparentLinkButton class])]
performAction:grey_tap()];
chrome_test_util::AssertMainTabCount(2);
id<GREYMatcher> webViewMatcher = chrome_test_util::WebViewContainingText(
"Sync and view tabs and history across devices");
[[EarlGrey selectElementWithMatcher:webViewMatcher]
assertWithMatcher:grey_notNil()];
}
// Tests that searching history displays only entries matching the search term.
- (void)testSearchHistory {
[self loadTestURLs];
[self openHistoryPanel];
[[EarlGrey selectElementWithMatcher:SearchIconButton()]
performAction:grey_tap()];
NSString* searchString =
[NSString stringWithFormat:@"%s", _URL1.path().c_str()];
[[EarlGrey selectElementWithMatcher:grey_keyWindow()]
performAction:grey_typeText(searchString)];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL1)]
assertWithMatcher:grey_notNil()];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL2)]
assertWithMatcher:grey_nil()];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL3)]
assertWithMatcher:grey_nil()];
}
// Tests deletion of history entries.
- (void)testDeleteHistory {
[self loadTestURLs];
[self openHistoryPanel];
// Assert that three history elements are present.
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL1)]
assertWithMatcher:grey_notNil()];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL2)]
assertWithMatcher:grey_notNil()];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL3)]
assertWithMatcher:grey_notNil()];
// Enter edit mode, select a history element, and press delete.
[[EarlGrey selectElementWithMatcher:NavigationEditButton()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL1)]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:DeleteHistoryEntriesButton()]
performAction:grey_tap()];
// Assert that the deleted entry is gone and the other two remain.
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL1)]
assertWithMatcher:grey_nil()];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL2)]
assertWithMatcher:grey_notNil()];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL3)]
assertWithMatcher:grey_notNil()];
// Enter edit mode, select both remaining entries, and press delete.
[[EarlGrey selectElementWithMatcher:NavigationEditButton()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL2)]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:HistoryEntryWithUrl(_URL3)]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:DeleteHistoryEntriesButton()]
performAction:grey_tap()];
[self assertNoHistoryShown];
}
// Tests clear browsing history.
- (void)testClearBrowsingHistory {
[self loadTestURLs];
[self openHistoryPanel];
// Open the Clear Browsing Data dialog.
[[EarlGrey selectElementWithMatcher:OpenClearBrowsingDataButton()]
performAction:grey_tap()];
// Uncheck "Cookies, Site Data" and "Cached Images and Files," which are
// checked by default, and press "Clear Browsing Data"
[[EarlGrey selectElementWithMatcher:ClearCookiesButton()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:ClearCacheButton()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:ClearBrowsingDataButton()]
performAction:grey_tap()];
// There is not currently a matcher for acessibilityElementIsFocused or
// userInteractionEnabled which could be used here instead of checking that
// the button is not a MDCCollectionViewTextCell. Use when available.
// TODO(crbug.com/638674): Evaluate if this can move to shared code.
id<GREYMatcher> confirmClear = grey_allOf(
ClearBrowsingDataButton(),
grey_not(grey_kindOfClass([MDCCollectionViewTextCell class])), nil);
[[EarlGrey selectElementWithMatcher:confirmClear] performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:NavigationDoneButton()]
performAction:grey_tap()];
[self assertNoHistoryShown];
}
// 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:HistoryEntryWithUrl(_URL1)]
performAction:grey_longPress()];
// Select "Open in New Tab" and confirm that new tab is opened with selected
// URL.
[[EarlGrey selectElementWithMatcher:OpenInNewTabButton()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
_URL1.GetContent())]
assertWithMatcher:grey_notNil()];
chrome_test_util::AssertMainTabCount(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:HistoryEntryWithUrl(_URL1)]
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_test_util::AssertMainTabCount(1);
chrome_test_util::AssertIncognitoTabCount(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:HistoryEntryWithUrl(_URL1)]
performAction:grey_longPress()];
// Tap "Copy URL" and wait for the URL to be copied to the pasteboard.
[[EarlGrey selectElementWithMatcher:CopyUrlButton()]
performAction:grey_tap()];
bool success =
testing::WaitUntilConditionOrTimeout(testing::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 openHistoryPanel];
chrome_test_util::VerifyAccessibilityForCurrentScreen();
// Close history.
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_NAVIGATION_BAR_DONE_BUTTON)]
performAction:grey_tap()];
}
#pragma mark Helper Methods
- (void)loadTestURLs {
[ChromeEarlGrey loadURL:_URL1];
id<GREYMatcher> response1Matcher =
chrome_test_util::WebViewContainingText(kResponse1);
[[EarlGrey selectElementWithMatcher:response1Matcher]
assertWithMatcher:grey_notNil()];
[ChromeEarlGrey loadURL:_URL2];
id<GREYMatcher> response2Matcher =
chrome_test_util::WebViewContainingText(kResponse2);
[[EarlGrey selectElementWithMatcher:response2Matcher]
assertWithMatcher:grey_notNil()];
[ChromeEarlGrey loadURL:_URL3];
id<GREYMatcher> response3Matcher =
chrome_test_util::WebViewContainingText(kResponse3);
[[EarlGrey selectElementWithMatcher:response3Matcher]
assertWithMatcher:grey_notNil()];
}
- (void)openHistoryPanel {
[ChromeEarlGreyUI openToolsMenu];
[[EarlGrey selectElementWithMatcher:HistoryButton()]
performAction:grey_tap()];
}
- (void)assertNoHistoryShown {
id<GREYMatcher> noHistoryMessageMatcher =
grey_allOf(grey_text(l10n_util::GetNSString(IDS_HISTORY_NO_RESULTS)),
grey_sufficientlyVisible(), nil);
[[EarlGrey selectElementWithMatcher:noHistoryMessageMatcher]
assertWithMatcher:grey_notNil()];
id<GREYMatcher> historyEntryMatcher =
grey_allOf(grey_kindOfClass([HistoryEntryCell class]),
grey_sufficientlyVisible(), nil);
[[EarlGrey selectElementWithMatcher:historyEntryMatcher]
assertWithMatcher:grey_nil()];
}
- (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