OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import <XCTest/XCTest.h> |
| 6 |
| 7 #include "base/ios/ios_util.h" |
| 8 #include "components/strings/grit/components_strings.h" |
| 9 #include "ios/chrome/test/app/web_view_interaction_test_util.h" |
| 10 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h" |
| 11 #import "ios/chrome/test/earl_grey/chrome_matchers.h" |
| 12 #import "ios/chrome/test/earl_grey/chrome_test_case.h" |
| 13 #import "ios/testing/earl_grey/disabled_test_macros.h" |
| 14 #import "ios/web/public/test/http_server.h" |
| 15 #include "ios/web/public/test/http_server_util.h" |
| 16 #include "ios/web/public/test/response_providers/data_response_provider.h" |
| 17 #include "ui/base/l10n/l10n_util.h" |
| 18 |
| 19 using chrome_test_util::backButton; |
| 20 using chrome_test_util::forwardButton; |
| 21 using chrome_test_util::TapWebViewElementWithId; |
| 22 using chrome_test_util::webViewContainingText; |
| 23 |
| 24 namespace { |
| 25 |
| 26 // URL for the test window.history.go() test file. The page at this URL |
| 27 // contains several buttons that trigger window.history commands. Additionally |
| 28 // the page contains several divs used to display the state of the page: |
| 29 // - A div that is populated with |kOnLoadText| when the onload event fires. |
| 30 // - A div that is populated with |kNoOpText| 1s after a button is tapped. |
| 31 // - A div that is populated with |kPopStateReceivedText| when a popstate event |
| 32 // is received by the page. |
| 33 // - A div that is populated with the state object (if it's a string) upon the |
| 34 // receipt of a popstate event. |
| 35 // - A div that is populated with |kHashChangeReceivedText| when a hashchange |
| 36 // event is received. |
| 37 // When a button on the page is tapped, all pre-existing div text is cleared, |
| 38 // so matching against this webview text after a button is tapped ensures that |
| 39 // the state is set in response to the most recently executed script. |
| 40 const char kWindowHistoryGoTestURL[] = |
| 41 "http://ios/testing/data/http_server_files/history_go.html"; |
| 42 |
| 43 // URL of a sample file-based page. |
| 44 const char kSampleFileBasedURL[] = |
| 45 "http://ios/testing/data/http_server_files/chromium_logo_page.html"; |
| 46 |
| 47 // Strings used by history_go.html. |
| 48 const char kOnLoadText[] = "OnLoadText"; |
| 49 const char kNoOpText[] = "NoOpText"; |
| 50 |
| 51 // Button ids for history_go.html. |
| 52 NSString* const kGoNoParameterID = @"go-no-parameter"; |
| 53 NSString* const kGoZeroID = @"go-zero"; |
| 54 NSString* const kGoTwoID = @"go-2"; |
| 55 NSString* const kGoBackTwoID = @"go-back-2"; |
| 56 |
| 57 // URLs and labels for tests that navigate back and forward. |
| 58 const char kBackHTMLButtonLabel[] = "BackHTMLButton"; |
| 59 const char kForwardHTMLButtonLabel[] = "ForwardHTMLButton"; |
| 60 const char kForwardHTMLSentinel[] = "Forward page loaded"; |
| 61 const char kBackURL[] = "http://back"; |
| 62 const char kForwardURL[] = "http://forward"; |
| 63 const char kTestURL[] = "http://test"; |
| 64 |
| 65 // URLs and labels for scenarioWindowLocation* tests. |
| 66 const char kHashChangeWithHistoryLabel[] = "hashChangedWithHistory"; |
| 67 const char kHashChangeWithoutHistoryLabel[] = "hashChangedWithoutHistory"; |
| 68 const char kPage1URL[] = "http://page1"; |
| 69 const char kHashChangedWithHistoryURL[] = |
| 70 "http://page1/#hashChangedWithHistory"; |
| 71 const char kHashChangedWithoutHistoryURL[] = |
| 72 "http://page1/#hashChangedWithoutHistory"; |
| 73 const char kNoHashChangeText[] = "No hash change"; |
| 74 // An HTML page with two links that run JavaScript when they're clicked. The |
| 75 // first link updates |window.location.hash|, the second link changes |
| 76 // |window.location|. |
| 77 const char kHashChangedHTML[] = |
| 78 "<html><body>" |
| 79 "<a href='javascript:window.location.hash=\"#hashChangedWithHistory\"' " |
| 80 " id=\"hashChangedWithHistory\"'>hashChangedWithHistory</a><br />" |
| 81 "<a href='javascript:" |
| 82 " window.location.replace(\"#hashChangedWithoutHistory\")' " |
| 83 " id=\"hashChangedWithoutHistory\">hashChangedWithoutHistory</a>" |
| 84 "</body></html>"; |
| 85 |
| 86 void SetupBackAndForwardResponseProvider() { |
| 87 std::map<GURL, std::string> responses; |
| 88 GURL testURL = web::test::HttpServer::MakeUrl(kTestURL); |
| 89 GURL backURL = web::test::HttpServer::MakeUrl(kBackURL); |
| 90 GURL forwardURL = web::test::HttpServer::MakeUrl(kForwardURL); |
| 91 responses[testURL] = "<html>Test Page</html>"; |
| 92 responses[backURL] = |
| 93 "<html>" |
| 94 "<input type=\"button\" value=\"BackHTMLButton\" id=\"BackHTMLButton\"" |
| 95 "onclick=\"window.history.back()\" />" |
| 96 "</html>"; |
| 97 responses[forwardURL] = |
| 98 "<html>" |
| 99 "<input type=\"button\" value=\"ForwardHTMLButton\"" |
| 100 "id=\"ForwardHTMLButton\" onclick=\"window.history.forward()\" /></br>" |
| 101 "Forward page loaded</html>"; |
| 102 web::test::SetUpSimpleHttpServer(responses); |
| 103 } |
| 104 |
| 105 // Matcher for the error page. |
| 106 // TODO(crbug.com/638674): Evaluate if this can move to shared code. See |
| 107 // ios/chrome/browser/ui/error_page_egtest.mm. |
| 108 id<GREYMatcher> errorPage() { |
| 109 NSString* const kDNSError = |
| 110 l10n_util::GetNSString(IDS_ERRORPAGES_HEADING_NOT_AVAILABLE); |
| 111 NSString* const kInternetDisconnectedError = |
| 112 l10n_util::GetNSString(IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED); |
| 113 return grey_anyOf(chrome_test_util::staticHtmlViewContainingText(kDNSError), |
| 114 chrome_test_util::staticHtmlViewContainingText( |
| 115 kInternetDisconnectedError), |
| 116 nil); |
| 117 } |
| 118 |
| 119 // URLs for server redirect tests. |
| 120 const char kRedirectIndexURL[] = "http://redirect"; |
| 121 const char kRedirect301URL[] = "http://redirect/redirect?code=301"; |
| 122 const char kRedirectWindowURL[] = "http://redirect/redirectWindow.html"; |
| 123 const char kRedirectRefreshURL[] = "http://redirect/redirectRefresh.html"; |
| 124 const char kDestinationURL[] = "http://redirect/destination.html"; |
| 125 const char kLastURL[] = "http://redirect/last.html"; |
| 126 |
| 127 class RedirectResponseProvider : public web::DataResponseProvider { |
| 128 public: |
| 129 RedirectResponseProvider() |
| 130 : index_url_(web::test::HttpServer::MakeUrl(kRedirectIndexURL)), |
| 131 redirect_301_url_(web::test::HttpServer::MakeUrl(kRedirect301URL)), |
| 132 redirect_refresh_url_( |
| 133 web::test::HttpServer::MakeUrl(kRedirectRefreshURL)), |
| 134 redirect_window_url_( |
| 135 web::test::HttpServer::MakeUrl(kRedirectWindowURL)), |
| 136 destination_url_(web::test::HttpServer::MakeUrl(kDestinationURL)) {} |
| 137 |
| 138 private: |
| 139 bool CanHandleRequest(const Request& request) override { |
| 140 return request.url == index_url_ || request.url == redirect_window_url_ || |
| 141 request.url == redirect_refresh_url_ || |
| 142 request.url == redirect_301_url_ || request.url == destination_url_; |
| 143 } |
| 144 void GetResponseHeadersAndBody( |
| 145 const Request& request, |
| 146 scoped_refptr<net::HttpResponseHeaders>* headers, |
| 147 std::string* response_body) override { |
| 148 *headers = GetDefaultResponseHeaders(); |
| 149 if (request.url == index_url_) { |
| 150 *response_body = |
| 151 "<p><a href=\"redirect?code=301\"" |
| 152 " id=\"redirect301\">redirect301</a></p>" |
| 153 "<p><a href=\"redirectRefresh.html\"" |
| 154 " id=\"redirectRefresh\">redirectRefresh</a></p>" |
| 155 "<p><a href=\"redirectWindow.html\"" |
| 156 " id=\"redirectWindow\">redirectWindow</a></p>"; |
| 157 } else if (request.url == redirect_301_url_) { |
| 158 *headers = GetRedirectResponseHeaders(destination_url_.spec(), |
| 159 net::HTTP_MOVED_PERMANENTLY); |
| 160 } else if (request.url == redirect_refresh_url_) { |
| 161 *response_body = |
| 162 "<head>" |
| 163 " <meta HTTP-EQUIV=\"REFRESH\" content=\"0; url=destination.html\">" |
| 164 "</head>" |
| 165 "<body><p>Redirecting</p></body>"; |
| 166 } else if (request.url == redirect_window_url_) { |
| 167 *response_body = |
| 168 "<head>" |
| 169 " <meta HTTP-EQUIV=\"REFRESH\" content=\"0; url=destination.html\">" |
| 170 "</head>" |
| 171 "<body>Redirecting" |
| 172 " <script>window.open(\"destination.html\", \"_self\");</script>" |
| 173 "</body>"; |
| 174 } else if (request.url == destination_url_) { |
| 175 *response_body = "<html><body><p>You've arrived</p></body></html>"; |
| 176 } else if (request.url == last_url_) { |
| 177 *response_body = "<html><body><p>Go back from here</p></body></html>"; |
| 178 } else { |
| 179 NOTREACHED(); |
| 180 } |
| 181 } |
| 182 |
| 183 // Member variables for test URLs. |
| 184 const GURL index_url_; |
| 185 const GURL redirect_301_url_; |
| 186 const GURL redirect_refresh_url_; |
| 187 const GURL redirect_window_url_; |
| 188 const GURL destination_url_; |
| 189 const GURL last_url_; |
| 190 }; |
| 191 |
| 192 } // namespace |
| 193 |
| 194 // Integration tests for navigating history via JavaScript and the forward and |
| 195 // back buttons. |
| 196 @interface NavigationTestCase : ChromeTestCase |
| 197 |
| 198 // Adds hashchange listener to the page that changes the inner html of the page |
| 199 // to |content| when a hashchange is detected. |
| 200 - (void)addHashChangeListenerWithContent:(std::string)content; |
| 201 |
| 202 // Loads index page for redirect operations, taps the link with |redirectLabel| |
| 203 // and then perform series of back-forward navigations asserting the proper |
| 204 // behavior. |
| 205 - (void)verifyBackAndForwardAfterRedirect:(std::string)redirectLabel; |
| 206 |
| 207 @end |
| 208 |
| 209 @implementation NavigationTestCase |
| 210 |
| 211 #pragma mark window.history.go operations |
| 212 |
| 213 // Tests reloading the current page via window.history.go() with no parameters. |
| 214 - (void)testHistoryGoNoParameter { |
| 215 web::test::SetUpFileBasedHttpServer(); |
| 216 |
| 217 // Load the history test page and ensure that its onload text is visible. |
| 218 const GURL windowHistoryURL = |
| 219 web::test::HttpServer::MakeUrl(kWindowHistoryGoTestURL); |
| 220 [ChromeEarlGrey loadURL:windowHistoryURL]; |
| 221 [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
| 222 assertWithMatcher:grey_notNil()]; |
| 223 |
| 224 // Tap on the window.history.go() button. This will clear |kOnLoadText|, so |
| 225 // the subsequent check for |kOnLoadText| will only pass if a reload has |
| 226 // occurred. |
| 227 [ChromeEarlGrey tapWebViewElementWithID:kGoNoParameterID]; |
| 228 |
| 229 // Verify that the onload text is reset. |
| 230 [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
| 231 assertWithMatcher:grey_notNil()]; |
| 232 } |
| 233 |
| 234 // Tests reloading the current page via history.go(0). |
| 235 - (void)testHistoryGoDeltaZero { |
| 236 web::test::SetUpFileBasedHttpServer(); |
| 237 |
| 238 // Load the history test page and ensure that its onload text is visible. |
| 239 const GURL windowHistoryURL = |
| 240 web::test::HttpServer::MakeUrl(kWindowHistoryGoTestURL); |
| 241 [ChromeEarlGrey loadURL:windowHistoryURL]; |
| 242 [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
| 243 assertWithMatcher:grey_notNil()]; |
| 244 |
| 245 // Tap on the window.history.go() button. This will clear |kOnLoadText|, so |
| 246 // the subsequent check for |kOnLoadText| will only pass if a reload has |
| 247 // occurred. |
| 248 [ChromeEarlGrey tapWebViewElementWithID:kGoZeroID]; |
| 249 |
| 250 // Verify that the onload text is reset. |
| 251 [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
| 252 assertWithMatcher:grey_notNil()]; |
| 253 } |
| 254 |
| 255 // Tests that calling window.history.go() with an offset that is out of bounds |
| 256 // is a no-op. |
| 257 - (void)testHistoryGoOutOfBounds { |
| 258 web::test::SetUpFileBasedHttpServer(); |
| 259 |
| 260 // Load the history test page and ensure that its onload text is visible. |
| 261 const GURL windowHistoryURL = |
| 262 web::test::HttpServer::MakeUrl(kWindowHistoryGoTestURL); |
| 263 [ChromeEarlGrey loadURL:windowHistoryURL]; |
| 264 [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
| 265 assertWithMatcher:grey_notNil()]; |
| 266 |
| 267 // Tap on the window.history.go(2) button. This will clear all div text, so |
| 268 // the subsequent check for |kNoOpText| will only pass if no navigations have |
| 269 // occurred. |
| 270 [ChromeEarlGrey tapWebViewElementWithID:kGoTwoID]; |
| 271 [[EarlGrey selectElementWithMatcher:webViewContainingText(kNoOpText)] |
| 272 assertWithMatcher:grey_notNil()]; |
| 273 |
| 274 // Tap on the window.history.go(-2) button. This will clear all div text, so |
| 275 // the subsequent check for |kNoOpText| will only pass if no navigations have |
| 276 // occurred. |
| 277 [ChromeEarlGrey tapWebViewElementWithID:kGoBackTwoID]; |
| 278 [[EarlGrey selectElementWithMatcher:webViewContainingText(kNoOpText)] |
| 279 assertWithMatcher:grey_notNil()]; |
| 280 } |
| 281 |
| 282 // Tests going back and forward via history.go(). |
| 283 - (void)testHistoryGoDelta { |
| 284 std::map<GURL, std::string> responses; |
| 285 const GURL firstURL = web::test::HttpServer::MakeUrl("http://page1"); |
| 286 const GURL secondURL = web::test::HttpServer::MakeUrl("http://page2"); |
| 287 const GURL thirdURL = web::test::HttpServer::MakeUrl("http://page3"); |
| 288 const GURL fourthURL = web::test::HttpServer::MakeUrl("http://page4"); |
| 289 responses[firstURL] = |
| 290 "page1 <input type='button' value='goForward' id='goForward' " |
| 291 "onclick='window.history.go(2)' />"; |
| 292 responses[secondURL] = "page2"; |
| 293 responses[thirdURL] = "page3"; |
| 294 responses[fourthURL] = |
| 295 "page4 <input type='button' value='goBack' id='goBack' " |
| 296 "onclick='window.history.go(-3)' />"; |
| 297 web::test::SetUpSimpleHttpServer(responses); |
| 298 |
| 299 // Load 4 pages. |
| 300 [ChromeEarlGrey loadURL:firstURL]; |
| 301 [ChromeEarlGrey loadURL:secondURL]; |
| 302 [ChromeEarlGrey loadURL:thirdURL]; |
| 303 [ChromeEarlGrey loadURL:fourthURL]; |
| 304 [[EarlGrey selectElementWithMatcher:webViewContainingText("page4")] |
| 305 assertWithMatcher:grey_notNil()]; |
| 306 |
| 307 // Tap button to go back 3 pages. |
| 308 TapWebViewElementWithId("goBack"); |
| 309 [[EarlGrey selectElementWithMatcher:webViewContainingText("page1")] |
| 310 assertWithMatcher:grey_notNil()]; |
| 311 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 312 firstURL.GetContent())] |
| 313 assertWithMatcher:grey_notNil()]; |
| 314 |
| 315 // Tap button to go forward 2 pages. |
| 316 TapWebViewElementWithId("goForward"); |
| 317 [[EarlGrey selectElementWithMatcher:webViewContainingText("page3")] |
| 318 assertWithMatcher:grey_notNil()]; |
| 319 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 320 thirdURL.GetContent())] |
| 321 assertWithMatcher:grey_notNil()]; |
| 322 } |
| 323 |
| 324 // Tests that calls to window.history.go() that span multiple documents causes |
| 325 // a load to occur. |
| 326 - (void)testHistoryCrossDocumentLoad { |
| 327 web::test::SetUpFileBasedHttpServer(); |
| 328 |
| 329 // Load the history test page and ensure that its onload text is visible. |
| 330 const GURL windowHistoryURL = |
| 331 web::test::HttpServer::MakeUrl(kWindowHistoryGoTestURL); |
| 332 [ChromeEarlGrey loadURL:windowHistoryURL]; |
| 333 [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
| 334 assertWithMatcher:grey_notNil()]; |
| 335 |
| 336 const GURL sampleURL = web::test::HttpServer::MakeUrl(kSampleFileBasedURL); |
| 337 [ChromeEarlGrey loadURL:sampleURL]; |
| 338 |
| 339 [ChromeEarlGrey loadURL:windowHistoryURL]; |
| 340 [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
| 341 assertWithMatcher:grey_notNil()]; |
| 342 |
| 343 // Tap the window.history.go(-2) button. This will clear the current page's |
| 344 // |kOnLoadText|, so the subsequent check will only pass if another load |
| 345 // occurs. |
| 346 [ChromeEarlGrey tapWebViewElementWithID:kGoBackTwoID]; |
| 347 [[EarlGrey selectElementWithMatcher:webViewContainingText(kOnLoadText)] |
| 348 assertWithMatcher:grey_notNil()]; |
| 349 } |
| 350 |
| 351 #pragma mark window.history.[back/forward] operations |
| 352 |
| 353 // Tests going back via history.back() then forward via forward button. |
| 354 - (void)testHistoryBackNavigation { |
| 355 SetupBackAndForwardResponseProvider(); |
| 356 |
| 357 // Navigate to a URL. |
| 358 const GURL firstURL = web::test::HttpServer::MakeUrl(kTestURL); |
| 359 [ChromeEarlGrey loadURL:firstURL]; |
| 360 |
| 361 // Navigate to an HTML page with a back button. |
| 362 const GURL secondURL = web::test::HttpServer::MakeUrl(kBackURL); |
| 363 [ChromeEarlGrey loadURL:secondURL]; |
| 364 |
| 365 // Tap the back button in the HTML and verify the first URL is loaded. |
| 366 TapWebViewElementWithId(kBackHTMLButtonLabel); |
| 367 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 368 firstURL.GetContent())] |
| 369 assertWithMatcher:grey_notNil()]; |
| 370 |
| 371 // Tap the forward button in the toolbar and verify the second URL is loaded. |
| 372 [[EarlGrey selectElementWithMatcher:forwardButton()] |
| 373 performAction:grey_tap()]; |
| 374 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 375 secondURL.GetContent())] |
| 376 assertWithMatcher:grey_notNil()]; |
| 377 } |
| 378 |
| 379 // Tests going back via back button then forward via history.forward(). |
| 380 - (void)testHistoryForwardNavigation { |
| 381 SetupBackAndForwardResponseProvider(); |
| 382 |
| 383 // Navigate to an HTML page with a forward button. |
| 384 const GURL firstURL = web::test::HttpServer::MakeUrl(kForwardURL); |
| 385 [ChromeEarlGrey loadURL:firstURL]; |
| 386 |
| 387 // Navigate to some other page. |
| 388 const GURL secondURL = web::test::HttpServer::MakeUrl(kTestURL); |
| 389 [ChromeEarlGrey loadURL:secondURL]; |
| 390 |
| 391 // Tap the back button in the toolbar and verify the page with forward button |
| 392 // is loaded. |
| 393 [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
| 394 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 395 firstURL.GetContent())] |
| 396 assertWithMatcher:grey_notNil()]; |
| 397 [[EarlGrey |
| 398 selectElementWithMatcher:webViewContainingText(kForwardHTMLSentinel)] |
| 399 assertWithMatcher:grey_notNil()]; |
| 400 |
| 401 // Tap the forward button in the HTML and verify the second URL is loaded. |
| 402 TapWebViewElementWithId(kForwardHTMLButtonLabel); |
| 403 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 404 secondURL.GetContent())] |
| 405 assertWithMatcher:grey_notNil()]; |
| 406 |
| 407 // Verify that the forward button is not enabled. |
| 408 // TODO(crbug.com/638674): Evaluate if size class determination can move to |
| 409 // shared code. |
| 410 if (UIApplication.sharedApplication.keyWindow.traitCollection |
| 411 .horizontalSizeClass == UIUserInterfaceSizeClassCompact) { |
| 412 // In horizontally compact environments, the forward button is not visible. |
| 413 [[EarlGrey selectElementWithMatcher:forwardButton()] |
| 414 assertWithMatcher:grey_nil()]; |
| 415 } else { |
| 416 // In horizontally regular environments, the forward button is visible and |
| 417 // disabled. |
| 418 id<GREYMatcher> disabledForwardButton = grey_allOf( |
| 419 forwardButton(), |
| 420 grey_accessibilityTrait(UIAccessibilityTraitNotEnabled), nil); |
| 421 [[EarlGrey selectElementWithMatcher:disabledForwardButton] |
| 422 assertWithMatcher:grey_notNil()]; |
| 423 } |
| 424 } |
| 425 |
| 426 // Tests navigating forward via window.history.forward() to an error page. |
| 427 - (void)testHistoryForwardToErrorPage { |
| 428 SetupBackAndForwardResponseProvider(); |
| 429 |
| 430 // Go to page 1 with a button which calls window.history.forward(). |
| 431 const GURL forwardURL = web::test::HttpServer::MakeUrl(kForwardURL); |
| 432 [ChromeEarlGrey loadURL:forwardURL]; |
| 433 |
| 434 // Go to page 2: 'www.badurljkljkljklfloofy.com'. This page should display a |
| 435 // page not available error. |
| 436 const GURL badURL("http://www.badurljkljkljklfloofy.com"); |
| 437 [ChromeEarlGrey loadURL:badURL]; |
| 438 [[EarlGrey selectElementWithMatcher:errorPage()] |
| 439 assertWithMatcher:grey_notNil()]; |
| 440 |
| 441 // Go back to page 1 by clicking back button. |
| 442 [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
| 443 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 444 forwardURL.GetContent())] |
| 445 assertWithMatcher:grey_notNil()]; |
| 446 |
| 447 // Go forward to page 2 by calling window.history.forward() and assert that |
| 448 // the error page is shown. |
| 449 TapWebViewElementWithId(kForwardHTMLButtonLabel); |
| 450 [[EarlGrey selectElementWithMatcher:errorPage()] |
| 451 assertWithMatcher:grey_notNil()]; |
| 452 } |
| 453 |
| 454 #pragma mark window.location.hash operations |
| 455 |
| 456 // Loads a URL and modifies window.location.hash, then goes back and forward |
| 457 // and verifies the URLs and that hashchange event is fired. |
| 458 - (void)testWindowLocationChangeHash { |
| 459 std::map<GURL, std::string> responses; |
| 460 const GURL page1URL = web::test::HttpServer::MakeUrl(kPage1URL); |
| 461 const GURL hashChangedWithHistoryURL = |
| 462 web::test::HttpServer::MakeUrl(kHashChangedWithHistoryURL); |
| 463 responses[page1URL] = kHashChangedHTML; |
| 464 responses[hashChangedWithHistoryURL] = kHashChangedHTML; |
| 465 web::test::SetUpSimpleHttpServer(responses); |
| 466 [ChromeEarlGrey loadURL:page1URL]; |
| 467 |
| 468 // Click link to update location.hash and go to new URL (same page). |
| 469 chrome_test_util::TapWebViewElementWithId(kHashChangeWithHistoryLabel); |
| 470 |
| 471 // Navigate back to original URL. This should fire a hashchange event. |
| 472 std::string backHashChangeContent = "backHashChange"; |
| 473 [self addHashChangeListenerWithContent:backHashChangeContent]; |
| 474 [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
| 475 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 476 page1URL.GetContent())] |
| 477 assertWithMatcher:grey_notNil()]; |
| 478 [[EarlGrey |
| 479 selectElementWithMatcher:webViewContainingText(backHashChangeContent)] |
| 480 assertWithMatcher:grey_notNil()]; |
| 481 |
| 482 // Navigate forward to the new URL. This should fire a hashchange event. |
| 483 std::string forwardHashChangeContent = "forwardHashChange"; |
| 484 [self addHashChangeListenerWithContent:forwardHashChangeContent]; |
| 485 [[EarlGrey selectElementWithMatcher:forwardButton()] |
| 486 performAction:grey_tap()]; |
| 487 [[EarlGrey |
| 488 selectElementWithMatcher:chrome_test_util::omniboxText( |
| 489 hashChangedWithHistoryURL.GetContent())] |
| 490 assertWithMatcher:grey_notNil()]; |
| 491 [[EarlGrey |
| 492 selectElementWithMatcher:webViewContainingText(forwardHashChangeContent)] |
| 493 assertWithMatcher:grey_notNil()]; |
| 494 |
| 495 // Load a hash URL directly. This shouldn't fire a hashchange event. |
| 496 std::string hashChangeContent = "FAIL_loadUrlHashChange"; |
| 497 [self addHashChangeListenerWithContent:hashChangeContent]; |
| 498 [ChromeEarlGrey loadURL:hashChangedWithHistoryURL]; |
| 499 [[EarlGrey selectElementWithMatcher:webViewContainingText(hashChangeContent)] |
| 500 assertWithMatcher:grey_nil()]; |
| 501 } |
| 502 |
| 503 // Loads a URL and replaces its location, then updates its location.hash |
| 504 // and verifies that going back returns to the replaced entry. |
| 505 - (void)testWindowLocationReplaceAndChangeHash { |
| 506 std::map<GURL, std::string> responses; |
| 507 const GURL page1URL = web::test::HttpServer::MakeUrl(kPage1URL); |
| 508 const GURL hashChangedWithoutHistoryURL = |
| 509 web::test::HttpServer::MakeUrl(kHashChangedWithoutHistoryURL); |
| 510 const GURL hashChangedWithHistoryURL = |
| 511 web::test::HttpServer::MakeUrl(kHashChangedWithHistoryURL); |
| 512 responses[page1URL] = kHashChangedHTML; |
| 513 web::test::SetUpSimpleHttpServer(responses); |
| 514 [ChromeEarlGrey loadURL:page1URL]; |
| 515 |
| 516 // Tap link to replace the location value. |
| 517 TapWebViewElementWithId(kHashChangeWithoutHistoryLabel); |
| 518 [[EarlGrey |
| 519 selectElementWithMatcher:chrome_test_util::omniboxText( |
| 520 hashChangedWithoutHistoryURL.GetContent())] |
| 521 assertWithMatcher:grey_notNil()]; |
| 522 |
| 523 // Tap link to update the location.hash with a new value. |
| 524 TapWebViewElementWithId(kHashChangeWithHistoryLabel); |
| 525 [[EarlGrey |
| 526 selectElementWithMatcher:chrome_test_util::omniboxText( |
| 527 hashChangedWithHistoryURL.GetContent())] |
| 528 assertWithMatcher:grey_notNil()]; |
| 529 |
| 530 // Navigate back and verify that the URL that replaced window.location |
| 531 // has been reached. |
| 532 [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
| 533 [[EarlGrey |
| 534 selectElementWithMatcher:chrome_test_util::omniboxText( |
| 535 hashChangedWithoutHistoryURL.GetContent())] |
| 536 assertWithMatcher:grey_notNil()]; |
| 537 } |
| 538 |
| 539 // Loads a URL and modifies window.location.hash twice, verifying that there is |
| 540 // only one entry in the history by navigating back. |
| 541 - (void)testWindowLocationChangeToSameHash { |
| 542 std::map<GURL, std::string> responses; |
| 543 const GURL page1URL = web::test::HttpServer::MakeUrl(kPage1URL); |
| 544 const GURL hashChangedWithHistoryURL = |
| 545 web::test::HttpServer::MakeUrl(kHashChangedWithHistoryURL); |
| 546 responses[page1URL] = kHashChangedHTML; |
| 547 web::test::SetUpSimpleHttpServer(responses); |
| 548 [ChromeEarlGrey loadURL:page1URL]; |
| 549 |
| 550 // Tap link to update location.hash with a new value. |
| 551 TapWebViewElementWithId(kHashChangeWithHistoryLabel); |
| 552 [[EarlGrey |
| 553 selectElementWithMatcher:chrome_test_util::omniboxText( |
| 554 hashChangedWithHistoryURL.GetContent())] |
| 555 assertWithMatcher:grey_notNil()]; |
| 556 |
| 557 // Tap link to update location.hash with the same value. |
| 558 TapWebViewElementWithId(kHashChangeWithHistoryLabel); |
| 559 |
| 560 // Tap back once to return to original URL. |
| 561 [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
| 562 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 563 page1URL.GetContent())] |
| 564 assertWithMatcher:grey_notNil()]; |
| 565 |
| 566 // Navigate forward and verify the URL. |
| 567 [[EarlGrey selectElementWithMatcher:forwardButton()] |
| 568 performAction:grey_tap()]; |
| 569 [[EarlGrey |
| 570 selectElementWithMatcher:chrome_test_util::omniboxText( |
| 571 hashChangedWithHistoryURL.GetContent())] |
| 572 assertWithMatcher:grey_notNil()]; |
| 573 } |
| 574 |
| 575 #pragma mark Redirect operations |
| 576 |
| 577 // Navigates to a page that immediately redirects to another page via JavaScript |
| 578 // then verifies the browsing history. |
| 579 - (void)testJavaScriptRedirect { |
| 580 std::map<GURL, std::string> responses; |
| 581 // A starting page. |
| 582 const GURL initialURL = web::test::HttpServer::MakeUrl("http://initialURL"); |
| 583 // A page that redirects immediately via the window.open JavaScript method. |
| 584 const GURL originURL = web::test::HttpServer::MakeUrl( |
| 585 "http://scenarioJavaScriptRedirect_origin"); |
| 586 const GURL destinationURL = |
| 587 web::test::HttpServer::MakeUrl("http://destination"); |
| 588 responses[initialURL] = "<html><body>Initial page</body></html>"; |
| 589 responses[originURL] = |
| 590 "<script>window.open('" + destinationURL.spec() + "', '_self');</script>"; |
| 591 responses[destinationURL] = "scenarioJavaScriptRedirect destination"; |
| 592 |
| 593 web::test::SetUpSimpleHttpServer(responses); |
| 594 [ChromeEarlGrey loadURL:initialURL]; |
| 595 [ChromeEarlGrey loadURL:originURL]; |
| 596 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 597 destinationURL.GetContent())] |
| 598 assertWithMatcher:grey_notNil()]; |
| 599 |
| 600 // Navigating back takes the user to the new tab page. |
| 601 [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
| 602 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 603 initialURL.GetContent())] |
| 604 assertWithMatcher:grey_notNil()]; |
| 605 |
| 606 // Navigating forward take the user to destination page. |
| 607 [[EarlGrey selectElementWithMatcher:forwardButton()] |
| 608 performAction:grey_tap()]; |
| 609 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 610 destinationURL.GetContent())] |
| 611 assertWithMatcher:grey_notNil()]; |
| 612 } |
| 613 |
| 614 // Test to load a page that contains a redirect window, then does multiple back |
| 615 // and forth navigations. |
| 616 - (void)testRedirectWindow { |
| 617 std::unique_ptr<web::DataResponseProvider> provider( |
| 618 new RedirectResponseProvider()); |
| 619 web::test::SetUpHttpServer(std::move(provider)); |
| 620 [self verifyBackAndForwardAfterRedirect:"redirectWindow"]; |
| 621 } |
| 622 |
| 623 // Test to load a page that contains a redirect refresh, then does multiple back |
| 624 // and forth navigations. |
| 625 - (void)testRedirectRefresh { |
| 626 std::unique_ptr<web::DataResponseProvider> provider( |
| 627 new RedirectResponseProvider()); |
| 628 web::test::SetUpHttpServer(std::move(provider)); |
| 629 [self verifyBackAndForwardAfterRedirect:"redirectRefresh"]; |
| 630 } |
| 631 |
| 632 // Test to load a page that performs a 301 redirect, then does multiple back and |
| 633 // forth navigations. |
| 634 - (void)test301Redirect { |
| 635 std::unique_ptr<web::DataResponseProvider> provider( |
| 636 new RedirectResponseProvider()); |
| 637 web::test::SetUpHttpServer(std::move(provider)); |
| 638 [self verifyBackAndForwardAfterRedirect:"redirect301"]; |
| 639 } |
| 640 |
| 641 #pragma mark Utility methods |
| 642 |
| 643 - (void)addHashChangeListenerWithContent:(std::string)content { |
| 644 NSString* const script = |
| 645 [NSString stringWithFormat: |
| 646 @"document.body.innerHTML = '%s';" |
| 647 "window.addEventListener('hashchange', function(event) {" |
| 648 " document.body.innerHTML = '%s';" |
| 649 "});", |
| 650 kNoHashChangeText, content.c_str()]; |
| 651 |
| 652 NSError* error = nil; |
| 653 chrome_test_util::ExecuteJavaScript(script, &error); |
| 654 } |
| 655 |
| 656 - (void)verifyBackAndForwardAfterRedirect:(std::string)redirectLabel { |
| 657 const GURL indexURL(web::test::HttpServer::MakeUrl(kRedirectIndexURL)); |
| 658 const GURL destinationURL(web::test::HttpServer::MakeUrl(kDestinationURL)); |
| 659 const GURL lastURL(web::test::HttpServer::MakeUrl(kLastURL)); |
| 660 |
| 661 // Load index, tap on redirect link, and assert that the page is redirected |
| 662 // to the proper destination. |
| 663 [ChromeEarlGrey loadURL:indexURL]; |
| 664 TapWebViewElementWithId(redirectLabel); |
| 665 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 666 destinationURL.GetContent())] |
| 667 assertWithMatcher:grey_notNil()]; |
| 668 |
| 669 // Navigate to a new URL, navigate back and assert that the resulting page is |
| 670 // the proper destination. |
| 671 [ChromeEarlGrey loadURL:lastURL]; |
| 672 [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
| 673 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 674 destinationURL.GetContent())] |
| 675 assertWithMatcher:grey_notNil()]; |
| 676 |
| 677 // Navigate back and assert that the resulting page is the initial index. |
| 678 [[EarlGrey selectElementWithMatcher:backButton()] performAction:grey_tap()]; |
| 679 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 680 indexURL.GetContent())] |
| 681 assertWithMatcher:grey_notNil()]; |
| 682 |
| 683 // Navigate forward and assert the the resulting page is the proper |
| 684 // destination. |
| 685 [[EarlGrey selectElementWithMatcher:forwardButton()] |
| 686 performAction:grey_tap()]; |
| 687 [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
| 688 destinationURL.GetContent())] |
| 689 assertWithMatcher:grey_notNil()]; |
| 690 } |
| 691 |
| 692 @end |
OLD | NEW |