Index: ios/chrome/browser/web/navigation_egtest.mm |
diff --git a/ios/chrome/browser/web/navigation_egtest.mm b/ios/chrome/browser/web/navigation_egtest.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..dc1eb710edd2c476c9ffa1965a6b20402b85de8b |
--- /dev/null |
+++ b/ios/chrome/browser/web/navigation_egtest.mm |
@@ -0,0 +1,692 @@ |
+// 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 <XCTest/XCTest.h> |
+ |
+#include "base/ios/ios_util.h" |
+#include "components/strings/grit/components_strings.h" |
+#include "ios/chrome/test/app/web_view_interaction_test_util.h" |
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.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/disabled_test_macros.h" |
+#import "ios/web/public/test/http_server.h" |
+#include "ios/web/public/test/http_server_util.h" |
+#include "ios/web/public/test/response_providers/data_response_provider.h" |
+#include "ui/base/l10n/l10n_util.h" |
+ |
+using chrome_test_util::backButton; |
+using chrome_test_util::forwardButton; |
+using chrome_test_util::TapWebViewElementWithId; |
+using chrome_test_util::webViewContainingText; |
+ |
+namespace { |
+ |
+// URL for the test window.history.go() test file. The page at this URL |
+// contains several buttons that trigger window.history commands. Additionally |
+// the page contains several divs used to display the state of the page: |
+// - A div that is populated with |kOnLoadText| when the onload event fires. |
+// - A div that is populated with |kNoOpText| 1s after a button is tapped. |
+// - A div that is populated with |kPopStateReceivedText| when a popstate event |
+// is received by the page. |
+// - A div that is populated with the state object (if it's a string) upon the |
+// receipt of a popstate event. |
+// - A div that is populated with |kHashChangeReceivedText| when a hashchange |
+// event is received. |
+// When a button on the page is tapped, all pre-existing div text is cleared, |
+// so matching against this webview text after a button is tapped ensures that |
+// the state is set in response to the most recently executed script. |
+const char kWindowHistoryGoTestURL[] = |
+ "http://ios/testing/data/http_server_files/history_go.html"; |
+ |
+// URL of a sample file-based page. |
+const char kSampleFileBasedURL[] = |
+ "http://ios/testing/data/http_server_files/chromium_logo_page.html"; |
+ |
+// Strings used by history_go.html. |
+const char kOnLoadText[] = "OnLoadText"; |
+const char kNoOpText[] = "NoOpText"; |
+ |
+// Button ids for history_go.html. |
+NSString* const kGoNoParameterID = @"go-no-parameter"; |
+NSString* const kGoZeroID = @"go-zero"; |
+NSString* const kGoTwoID = @"go-2"; |
+NSString* const kGoBackTwoID = @"go-back-2"; |
+ |
+// URLs and labels for tests that navigate back and forward. |
+const char kBackHTMLButtonLabel[] = "BackHTMLButton"; |
+const char kForwardHTMLButtonLabel[] = "ForwardHTMLButton"; |
+const char kForwardHTMLSentinel[] = "Forward page loaded"; |
+const char kBackURL[] = "http://back"; |
+const char kForwardURL[] = "http://forward"; |
+const char kTestURL[] = "http://test"; |
+ |
+// URLs and labels for scenarioWindowLocation* tests. |
+const char kHashChangeWithHistoryLabel[] = "hashChangedWithHistory"; |
+const char kHashChangeWithoutHistoryLabel[] = "hashChangedWithoutHistory"; |
+const char kPage1URL[] = "http://page1"; |
+const char kHashChangedWithHistoryURL[] = |
+ "http://page1/#hashChangedWithHistory"; |
+const char kHashChangedWithoutHistoryURL[] = |
+ "http://page1/#hashChangedWithoutHistory"; |
+const char kNoHashChangeText[] = "No hash change"; |
+// An HTML page with two links that run JavaScript when they're clicked. The |
+// first link updates |window.location.hash|, the second link changes |
+// |window.location|. |
+const char kHashChangedHTML[] = |
+ "<html><body>" |
+ "<a href='javascript:window.location.hash=\"#hashChangedWithHistory\"' " |
+ " id=\"hashChangedWithHistory\"'>hashChangedWithHistory</a><br />" |
+ "<a href='javascript:" |
+ " window.location.replace(\"#hashChangedWithoutHistory\")' " |
+ " id=\"hashChangedWithoutHistory\">hashChangedWithoutHistory</a>" |
+ "</body></html>"; |
+ |
+void SetupBackAndForwardResponseProvider() { |
+ std::map<GURL, std::string> responses; |
+ GURL testURL = web::test::HttpServer::MakeUrl(kTestURL); |
+ GURL backURL = web::test::HttpServer::MakeUrl(kBackURL); |
+ GURL forwardURL = web::test::HttpServer::MakeUrl(kForwardURL); |
+ responses[testURL] = "<html>Test Page</html>"; |
+ responses[backURL] = |
+ "<html>" |
+ "<input type=\"button\" value=\"BackHTMLButton\" id=\"BackHTMLButton\"" |
+ "onclick=\"window.history.back()\" />" |
+ "</html>"; |
+ responses[forwardURL] = |
+ "<html>" |
+ "<input type=\"button\" value=\"ForwardHTMLButton\"" |
+ "id=\"ForwardHTMLButton\" onclick=\"window.history.forward()\" /></br>" |
+ "Forward page loaded</html>"; |
+ web::test::SetUpSimpleHttpServer(responses); |
+} |
+ |
+// Matcher for the error page. |
+// TODO(crbug.com/638674): Evaluate if this can move to shared code. See |
+// ios/chrome/browser/ui/error_page_egtest.mm. |
+id<GREYMatcher> errorPage() { |
+ NSString* const kDNSError = |
+ l10n_util::GetNSString(IDS_ERRORPAGES_HEADING_NOT_AVAILABLE); |
+ NSString* const kInternetDisconnectedError = |
+ l10n_util::GetNSString(IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED); |
+ return grey_anyOf(chrome_test_util::staticHtmlViewContainingText(kDNSError), |
+ chrome_test_util::staticHtmlViewContainingText( |
+ kInternetDisconnectedError), |
+ nil); |
+} |
+ |
+// URLs for server redirect tests. |
+const char kRedirectIndexURL[] = "http://redirect"; |
+const char kRedirect301URL[] = "http://redirect/redirect?code=301"; |
+const char kRedirectWindowURL[] = "http://redirect/redirectWindow.html"; |
+const char kRedirectRefreshURL[] = "http://redirect/redirectRefresh.html"; |
+const char kDestinationURL[] = "http://redirect/destination.html"; |
+const char kLastURL[] = "http://redirect/last.html"; |
+ |
+class RedirectResponseProvider : public web::DataResponseProvider { |
+ public: |
+ RedirectResponseProvider() |
+ : index_url_(web::test::HttpServer::MakeUrl(kRedirectIndexURL)), |
+ redirect_301_url_(web::test::HttpServer::MakeUrl(kRedirect301URL)), |
+ redirect_refresh_url_( |
+ web::test::HttpServer::MakeUrl(kRedirectRefreshURL)), |
+ redirect_window_url_( |
+ web::test::HttpServer::MakeUrl(kRedirectWindowURL)), |
+ destination_url_(web::test::HttpServer::MakeUrl(kDestinationURL)) {} |
+ |
+ private: |
+ bool CanHandleRequest(const Request& request) override { |
+ return request.url == index_url_ || request.url == redirect_window_url_ || |
+ request.url == redirect_refresh_url_ || |
+ request.url == redirect_301_url_ || request.url == destination_url_; |
+ } |
+ void GetResponseHeadersAndBody( |
+ const Request& request, |
+ scoped_refptr<net::HttpResponseHeaders>* headers, |
+ std::string* response_body) override { |
+ *headers = GetDefaultResponseHeaders(); |
+ if (request.url == index_url_) { |
+ *response_body = |
+ "<p><a href=\"redirect?code=301\"" |
+ " id=\"redirect301\">redirect301</a></p>" |
+ "<p><a href=\"redirectRefresh.html\"" |
+ " id=\"redirectRefresh\">redirectRefresh</a></p>" |
+ "<p><a href=\"redirectWindow.html\"" |
+ " id=\"redirectWindow\">redirectWindow</a></p>"; |
+ } else if (request.url == redirect_301_url_) { |
+ *headers = GetRedirectResponseHeaders(destination_url_.spec(), |
+ net::HTTP_MOVED_PERMANENTLY); |
+ } else if (request.url == redirect_refresh_url_) { |
+ *response_body = |
+ "<head>" |
+ " <meta HTTP-EQUIV=\"REFRESH\" content=\"0; url=destination.html\">" |
+ "</head>" |
+ "<body><p>Redirecting</p></body>"; |
+ } else if (request.url == redirect_window_url_) { |
+ *response_body = |
+ "<head>" |
+ " <meta HTTP-EQUIV=\"REFRESH\" content=\"0; url=destination.html\">" |
+ "</head>" |
+ "<body>Redirecting" |
+ " <script>window.open(\"destination.html\", \"_self\");</script>" |
+ "</body>"; |
+ } else if (request.url == destination_url_) { |
+ *response_body = "<html><body><p>You've arrived</p></body></html>"; |
+ } else if (request.url == last_url_) { |
+ *response_body = "<html><body><p>Go back from here</p></body></html>"; |
+ } else { |
+ NOTREACHED(); |
+ } |
+ } |
+ |
+ // Member variables for test URLs. |
+ const GURL index_url_; |
+ const GURL redirect_301_url_; |
+ const GURL redirect_refresh_url_; |
+ const GURL redirect_window_url_; |
+ const GURL destination_url_; |
+ const GURL last_url_; |
+}; |
+ |
+} // namespace |
+ |
+// Integration tests for navigating history via JavaScript and the forward and |
+// back buttons. |
+@interface NavigationTestCase : ChromeTestCase |
+ |
+// Adds hashchange listener to the page that changes the inner html of the page |
+// to |content| when a hashchange is detected. |
+- (void)addHashChangeListenerWithContent:(std::string)content; |
+ |
+// Loads index page for redirect operations, taps the link with |redirectLabel| |
+// and then perform series of back-forward navigations asserting the proper |
+// behavior. |
+- (void)verifyBackAndForwardAfterRedirect:(std::string)redirectLabel; |
+ |
+@end |
+ |
+@implementation NavigationTestCase |
+ |
+#pragma mark window.history.go operations |
+ |
+// Tests reloading the current page via window.history.go() with no parameters. |
+- (void)testHistoryGoNoParameter { |
+ web::test::SetUpFileBasedHttpServer(); |
+ |
+ // Load the history test page and ensure that its onload text is visible. |
+ const GURL windowHistoryURL = |
+ web::test::HttpServer::MakeUrl(kWindowHistoryGoTestURL); |
+ [ChromeEarlGrey loadURL:windowHistoryURL]; |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Tap on the window.history.go() button. This will clear |kOnLoadText|, so |
+ // the subsequent check for |kOnLoadText| will only pass if a reload has |
+ // occurred. |
+ [ChromeEarlGrey tapWebViewElementWithID:kGoNoParameterID]; |
+ |
+ // Verify that the onload text is reset. |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
+ assertWithMatcher:grey_notNil()]; |
+} |
+ |
+// Tests reloading the current page via history.go(0). |
+- (void)testHistoryGoDeltaZero { |
+ web::test::SetUpFileBasedHttpServer(); |
+ |
+ // Load the history test page and ensure that its onload text is visible. |
+ const GURL windowHistoryURL = |
+ web::test::HttpServer::MakeUrl(kWindowHistoryGoTestURL); |
+ [ChromeEarlGrey loadURL:windowHistoryURL]; |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Tap on the window.history.go() button. This will clear |kOnLoadText|, so |
+ // the subsequent check for |kOnLoadText| will only pass if a reload has |
+ // occurred. |
+ [ChromeEarlGrey tapWebViewElementWithID:kGoZeroID]; |
+ |
+ // Verify that the onload text is reset. |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
+ assertWithMatcher:grey_notNil()]; |
+} |
+ |
+// Tests that calling window.history.go() with an offset that is out of bounds |
+// is a no-op. |
+- (void)testHistoryGoOutOfBounds { |
+ web::test::SetUpFileBasedHttpServer(); |
+ |
+ // Load the history test page and ensure that its onload text is visible. |
+ const GURL windowHistoryURL = |
+ web::test::HttpServer::MakeUrl(kWindowHistoryGoTestURL); |
+ [ChromeEarlGrey loadURL:windowHistoryURL]; |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Tap on the window.history.go(2) button. This will clear all div text, so |
+ // the subsequent check for |kNoOpText| will only pass if no navigations have |
+ // occurred. |
+ [ChromeEarlGrey tapWebViewElementWithID:kGoTwoID]; |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText(kNoOpText)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Tap on the window.history.go(-2) button. This will clear all div text, so |
+ // the subsequent check for |kNoOpText| will only pass if no navigations have |
+ // occurred. |
+ [ChromeEarlGrey tapWebViewElementWithID:kGoBackTwoID]; |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText(kNoOpText)] |
+ assertWithMatcher:grey_notNil()]; |
+} |
+ |
+// Tests going back and forward via history.go(). |
+- (void)testHistoryGoDelta { |
+ std::map<GURL, std::string> responses; |
+ const GURL firstURL = web::test::HttpServer::MakeUrl("http://page1"); |
+ const GURL secondURL = web::test::HttpServer::MakeUrl("http://page2"); |
+ const GURL thirdURL = web::test::HttpServer::MakeUrl("http://page3"); |
+ const GURL fourthURL = web::test::HttpServer::MakeUrl("http://page4"); |
+ responses[firstURL] = |
+ "page1 <input type='button' value='goForward' id='goForward' " |
+ "onclick='window.history.go(2)' />"; |
+ responses[secondURL] = "page2"; |
+ responses[thirdURL] = "page3"; |
+ responses[fourthURL] = |
+ "page4 <input type='button' value='goBack' id='goBack' " |
+ "onclick='window.history.go(-3)' />"; |
+ web::test::SetUpSimpleHttpServer(responses); |
+ |
+ // Load 4 pages. |
+ [ChromeEarlGrey loadURL:firstURL]; |
+ [ChromeEarlGrey loadURL:secondURL]; |
+ [ChromeEarlGrey loadURL:thirdURL]; |
+ [ChromeEarlGrey loadURL:fourthURL]; |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText("page4")] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Tap button to go back 3 pages. |
+ TapWebViewElementWithId("goBack"); |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText("page1")] |
+ assertWithMatcher:grey_notNil()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ firstURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Tap button to go forward 2 pages. |
+ TapWebViewElementWithId("goForward"); |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText("page3")] |
+ assertWithMatcher:grey_notNil()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ thirdURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+} |
+ |
+// Tests that calls to window.history.go() that span multiple documents causes |
+// a load to occur. |
+- (void)testHistoryCrossDocumentLoad { |
+ web::test::SetUpFileBasedHttpServer(); |
+ |
+ // Load the history test page and ensure that its onload text is visible. |
+ const GURL windowHistoryURL = |
+ web::test::HttpServer::MakeUrl(kWindowHistoryGoTestURL); |
+ [ChromeEarlGrey loadURL:windowHistoryURL]; |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ const GURL sampleURL = web::test::HttpServer::MakeUrl(kSampleFileBasedURL); |
+ [ChromeEarlGrey loadURL:sampleURL]; |
+ |
+ [ChromeEarlGrey loadURL:windowHistoryURL]; |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Tap the window.history.go(-2) button. This will clear the current page's |
+ // |kOnLoadText|, so the subsequent check will only pass if another load |
+ // occurs. |
+ [ChromeEarlGrey tapWebViewElementWithID:kGoBackTwoID]; |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
+ assertWithMatcher:grey_notNil()]; |
+} |
+ |
+#pragma mark window.history.[back/forward] operations |
+ |
+// Tests going back via history.back() then forward via forward button. |
+- (void)testHistoryBackNavigation { |
+ SetupBackAndForwardResponseProvider(); |
+ |
+ // Navigate to a URL. |
+ const GURL firstURL = web::test::HttpServer::MakeUrl(kTestURL); |
+ [ChromeEarlGrey loadURL:firstURL]; |
+ |
+ // Navigate to an HTML page with a back button. |
+ const GURL secondURL = web::test::HttpServer::MakeUrl(kBackURL); |
+ [ChromeEarlGrey loadURL:secondURL]; |
+ |
+ // Tap the back button in the HTML and verify the first URL is loaded. |
+ TapWebViewElementWithId(kBackHTMLButtonLabel); |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ firstURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Tap the forward button in the toolbar and verify the second URL is loaded. |
+ [[EarlGrey selectElementWithMatcher:forwardButton()] |
+ performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ secondURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+} |
+ |
+// Tests going back via back button then forward via history.forward(). |
+- (void)testHistoryForwardNavigation { |
+ SetupBackAndForwardResponseProvider(); |
+ |
+ // Navigate to an HTML page with a forward button. |
+ const GURL firstURL = web::test::HttpServer::MakeUrl(kForwardURL); |
+ [ChromeEarlGrey loadURL:firstURL]; |
+ |
+ // Navigate to some other page. |
+ const GURL secondURL = web::test::HttpServer::MakeUrl(kTestURL); |
+ [ChromeEarlGrey loadURL:secondURL]; |
+ |
+ // Tap the back button in the toolbar and verify the page with forward button |
+ // is loaded. |
+ [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ firstURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ [[EarlGrey |
+ selectElementWithMatcher:webViewContainingText(kForwardHTMLSentinel)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Tap the forward button in the HTML and verify the second URL is loaded. |
+ TapWebViewElementWithId(kForwardHTMLButtonLabel); |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ secondURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Verify that the forward button is not enabled. |
+ // TODO(crbug.com/638674): Evaluate if size class determination can move to |
+ // shared code. |
+ if (UIApplication.sharedApplication.keyWindow.traitCollection |
+ .horizontalSizeClass == UIUserInterfaceSizeClassCompact) { |
+ // In horizontally compact environments, the forward button is not visible. |
+ [[EarlGrey selectElementWithMatcher:forwardButton()] |
+ assertWithMatcher:grey_nil()]; |
+ } else { |
+ // In horizontally regular environments, the forward button is visible and |
+ // disabled. |
+ id<GREYMatcher> disabledForwardButton = grey_allOf( |
+ forwardButton(), |
+ grey_accessibilityTrait(UIAccessibilityTraitNotEnabled), nil); |
+ [[EarlGrey selectElementWithMatcher:disabledForwardButton] |
+ assertWithMatcher:grey_notNil()]; |
+ } |
+} |
+ |
+// Tests navigating forward via window.history.forward() to an error page. |
+- (void)testHistoryForwardToErrorPage { |
+ SetupBackAndForwardResponseProvider(); |
+ |
+ // Go to page 1 with a button which calls window.history.forward(). |
+ const GURL forwardURL = web::test::HttpServer::MakeUrl(kForwardURL); |
+ [ChromeEarlGrey loadURL:forwardURL]; |
+ |
+ // Go to page 2: 'www.badurljkljkljklfloofy.com'. This page should display a |
+ // page not available error. |
+ const GURL badURL("http://www.badurljkljkljklfloofy.com"); |
+ [ChromeEarlGrey loadURL:badURL]; |
+ [[EarlGrey selectElementWithMatcher:errorPage()] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Go back to page 1 by clicking back button. |
+ [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ forwardURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Go forward to page 2 by calling window.history.forward() and assert that |
+ // the error page is shown. |
+ TapWebViewElementWithId(kForwardHTMLButtonLabel); |
+ [[EarlGrey selectElementWithMatcher:errorPage()] |
+ assertWithMatcher:grey_notNil()]; |
+} |
+ |
+#pragma mark window.location.hash operations |
+ |
+// Loads a URL and modifies window.location.hash, then goes back and forward |
+// and verifies the URLs and that hashchange event is fired. |
+- (void)testWindowLocationChangeHash { |
+ std::map<GURL, std::string> responses; |
+ const GURL page1URL = web::test::HttpServer::MakeUrl(kPage1URL); |
+ const GURL hashChangedWithHistoryURL = |
+ web::test::HttpServer::MakeUrl(kHashChangedWithHistoryURL); |
+ responses[page1URL] = kHashChangedHTML; |
+ responses[hashChangedWithHistoryURL] = kHashChangedHTML; |
+ web::test::SetUpSimpleHttpServer(responses); |
+ [ChromeEarlGrey loadURL:page1URL]; |
+ |
+ // Click link to update location.hash and go to new URL (same page). |
+ chrome_test_util::TapWebViewElementWithId(kHashChangeWithHistoryLabel); |
+ |
+ // Navigate back to original URL. This should fire a hashchange event. |
+ std::string backHashChangeContent = "backHashChange"; |
+ [self addHashChangeListenerWithContent:backHashChangeContent]; |
+ [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ page1URL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ [[EarlGrey |
+ selectElementWithMatcher:webViewContainingText(backHashChangeContent)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Navigate forward to the new URL. This should fire a hashchange event. |
+ std::string forwardHashChangeContent = "forwardHashChange"; |
+ [self addHashChangeListenerWithContent:forwardHashChangeContent]; |
+ [[EarlGrey selectElementWithMatcher:forwardButton()] |
+ performAction:grey_tap()]; |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::omniboxText( |
+ hashChangedWithHistoryURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ [[EarlGrey |
+ selectElementWithMatcher:webViewContainingText(forwardHashChangeContent)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Load a hash URL directly. This shouldn't fire a hashchange event. |
+ std::string hashChangeContent = "FAIL_loadUrlHashChange"; |
+ [self addHashChangeListenerWithContent:hashChangeContent]; |
+ [ChromeEarlGrey loadURL:hashChangedWithHistoryURL]; |
+ [[EarlGrey selectElementWithMatcher:webViewContainingText(hashChangeContent)] |
+ assertWithMatcher:grey_nil()]; |
+} |
+ |
+// Loads a URL and replaces its location, then updates its location.hash |
+// and verifies that going back returns to the replaced entry. |
+- (void)testWindowLocationReplaceAndChangeHash { |
+ std::map<GURL, std::string> responses; |
+ const GURL page1URL = web::test::HttpServer::MakeUrl(kPage1URL); |
+ const GURL hashChangedWithoutHistoryURL = |
+ web::test::HttpServer::MakeUrl(kHashChangedWithoutHistoryURL); |
+ const GURL hashChangedWithHistoryURL = |
+ web::test::HttpServer::MakeUrl(kHashChangedWithHistoryURL); |
+ responses[page1URL] = kHashChangedHTML; |
+ web::test::SetUpSimpleHttpServer(responses); |
+ [ChromeEarlGrey loadURL:page1URL]; |
+ |
+ // Tap link to replace the location value. |
+ TapWebViewElementWithId(kHashChangeWithoutHistoryLabel); |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::omniboxText( |
+ hashChangedWithoutHistoryURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Tap link to update the location.hash with a new value. |
+ TapWebViewElementWithId(kHashChangeWithHistoryLabel); |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::omniboxText( |
+ hashChangedWithHistoryURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Navigate back and verify that the URL that replaced window.location |
+ // has been reached. |
+ [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::omniboxText( |
+ hashChangedWithoutHistoryURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+} |
+ |
+// Loads a URL and modifies window.location.hash twice, verifying that there is |
+// only one entry in the history by navigating back. |
+- (void)testWindowLocationChangeToSameHash { |
+ std::map<GURL, std::string> responses; |
+ const GURL page1URL = web::test::HttpServer::MakeUrl(kPage1URL); |
+ const GURL hashChangedWithHistoryURL = |
+ web::test::HttpServer::MakeUrl(kHashChangedWithHistoryURL); |
+ responses[page1URL] = kHashChangedHTML; |
+ web::test::SetUpSimpleHttpServer(responses); |
+ [ChromeEarlGrey loadURL:page1URL]; |
+ |
+ // Tap link to update location.hash with a new value. |
+ TapWebViewElementWithId(kHashChangeWithHistoryLabel); |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::omniboxText( |
+ hashChangedWithHistoryURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Tap link to update location.hash with the same value. |
+ TapWebViewElementWithId(kHashChangeWithHistoryLabel); |
+ |
+ // Tap back once to return to original URL. |
+ [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ page1URL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Navigate forward and verify the URL. |
+ [[EarlGrey selectElementWithMatcher:forwardButton()] |
+ performAction:grey_tap()]; |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::omniboxText( |
+ hashChangedWithHistoryURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+} |
+ |
+#pragma mark Redirect operations |
+ |
+// Navigates to a page that immediately redirects to another page via JavaScript |
+// then verifies the browsing history. |
+- (void)testJavaScriptRedirect { |
+ std::map<GURL, std::string> responses; |
+ // A starting page. |
+ const GURL initialURL = web::test::HttpServer::MakeUrl("http://initialURL"); |
+ // A page that redirects immediately via the window.open JavaScript method. |
+ const GURL originURL = web::test::HttpServer::MakeUrl( |
+ "http://scenarioJavaScriptRedirect_origin"); |
+ const GURL destinationURL = |
+ web::test::HttpServer::MakeUrl("http://destination"); |
+ responses[initialURL] = "<html><body>Initial page</body></html>"; |
+ responses[originURL] = |
+ "<script>window.open('" + destinationURL.spec() + "', '_self');</script>"; |
+ responses[destinationURL] = "scenarioJavaScriptRedirect destination"; |
+ |
+ web::test::SetUpSimpleHttpServer(responses); |
+ [ChromeEarlGrey loadURL:initialURL]; |
+ [ChromeEarlGrey loadURL:originURL]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ destinationURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Navigating back takes the user to the new tab page. |
+ [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ initialURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Navigating forward take the user to destination page. |
+ [[EarlGrey selectElementWithMatcher:forwardButton()] |
+ performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ destinationURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+} |
+ |
+// Test to load a page that contains a redirect window, then does multiple back |
+// and forth navigations. |
+- (void)testRedirectWindow { |
+ std::unique_ptr<web::DataResponseProvider> provider( |
+ new RedirectResponseProvider()); |
+ web::test::SetUpHttpServer(std::move(provider)); |
+ [self verifyBackAndForwardAfterRedirect:"redirectWindow"]; |
+} |
+ |
+// Test to load a page that contains a redirect refresh, then does multiple back |
+// and forth navigations. |
+- (void)testRedirectRefresh { |
+ std::unique_ptr<web::DataResponseProvider> provider( |
+ new RedirectResponseProvider()); |
+ web::test::SetUpHttpServer(std::move(provider)); |
+ [self verifyBackAndForwardAfterRedirect:"redirectRefresh"]; |
+} |
+ |
+// Test to load a page that performs a 301 redirect, then does multiple back and |
+// forth navigations. |
+- (void)test301Redirect { |
+ std::unique_ptr<web::DataResponseProvider> provider( |
+ new RedirectResponseProvider()); |
+ web::test::SetUpHttpServer(std::move(provider)); |
+ [self verifyBackAndForwardAfterRedirect:"redirect301"]; |
+} |
+ |
+#pragma mark Utility methods |
+ |
+- (void)addHashChangeListenerWithContent:(std::string)content { |
+ NSString* const script = |
+ [NSString stringWithFormat: |
+ @"document.body.innerHTML = '%s';" |
+ "window.addEventListener('hashchange', function(event) {" |
+ " document.body.innerHTML = '%s';" |
+ "});", |
+ kNoHashChangeText, content.c_str()]; |
+ |
+ NSError* error = nil; |
+ chrome_test_util::ExecuteJavaScript(script, &error); |
+} |
+ |
+- (void)verifyBackAndForwardAfterRedirect:(std::string)redirectLabel { |
+ const GURL indexURL(web::test::HttpServer::MakeUrl(kRedirectIndexURL)); |
+ const GURL destinationURL(web::test::HttpServer::MakeUrl(kDestinationURL)); |
+ const GURL lastURL(web::test::HttpServer::MakeUrl(kLastURL)); |
+ |
+ // Load index, tap on redirect link, and assert that the page is redirected |
+ // to the proper destination. |
+ [ChromeEarlGrey loadURL:indexURL]; |
+ TapWebViewElementWithId(redirectLabel); |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ destinationURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Navigate to a new URL, navigate back and assert that the resulting page is |
+ // the proper destination. |
+ [ChromeEarlGrey loadURL:lastURL]; |
+ [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ destinationURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Navigate back and assert that the resulting page is the initial index. |
+ [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ indexURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Navigate forward and assert the the resulting page is the proper |
+ // destination. |
+ [[EarlGrey selectElementWithMatcher:forwardButton()] |
+ performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ destinationURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+} |
+ |
+@end |