| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import <TargetConditionals.h> |
| |
| #import <functional> |
| #import <string> |
| |
| #import "base/functional/bind.h" |
| #import "base/test/ios/wait_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/earl_grey_test.h" |
| #import "ios/testing/embedded_test_server_handlers.h" |
| #import "ios/web/common/features.h" |
| #import "net/test/embedded_test_server/default_handlers.h" |
| #import "net/test/embedded_test_server/http_request.h" |
| #import "net/test/embedded_test_server/http_response.h" |
| #import "net/test/embedded_test_server/request_handler_util.h" |
| |
| namespace { |
| // Returns ERR_CONNECTION_CLOSED error message. |
| std::string GetErrorMessage() { |
| return net::ErrorToShortString(net::ERR_CONNECTION_CLOSED); |
| } |
| |
| const std::string kRedirectPage = "/redirect-page.html"; |
| |
| // Provides responses for the different pages. |
| std::unique_ptr<net::test_server::HttpResponse> StandardResponse( |
| const net::test_server::HttpRequest& request) { |
| if (request.GetURL().GetPath() == kRedirectPage) { |
| auto result = std::make_unique<net::test_server::BasicHttpResponse>(); |
| result->set_code(net::HTTP_MOVED_PERMANENTLY); |
| result->AddCustomHeader("Location", "data:text/plain,Hello World"); |
| return std::move(result); |
| } |
| |
| return nullptr; |
| } |
| |
| } // namespace |
| |
| // Tests critical user journeys reloated to page load errors. |
| @interface ErrorPageTestCase : ChromeTestCase |
| // YES if test server is replying with valid HTML content (URL query). NO if |
| // test server closes the socket. |
| @property(atomic) bool serverRespondsWithContent; |
| @end |
| |
| @implementation ErrorPageTestCase |
| @synthesize serverRespondsWithContent = _serverRespondsWithContent; |
| |
| - (void)setUp { |
| [super setUp]; |
| |
| RegisterDefaultHandlers(self.testServer); |
| self.testServer->RegisterRequestHandler(base::BindRepeating( |
| &net::test_server::HandlePrefixedRequest, "/echo-query", |
| base::BindRepeating(&testing::HandleEchoQueryOrCloseSocket, |
| std::cref(_serverRespondsWithContent)))); |
| self.testServer->RegisterDefaultHandler( |
| base::BindRepeating(&StandardResponse)); |
| |
| GREYAssertTrue(self.testServer->Start(), @"Test server failed to start."); |
| } |
| |
| // Tests that the error page is correctly displayed after navigating back to it |
| // multiple times. See http://crbug.com/944037 . |
| // TODO:(crbug.com/443199230): Fails on simulator and device. |
| - (void)DISABLED_testBackForwardErrorPage { |
| // TODO(crbug.com/40159013): Going back/forward on the same host is failing. |
| // Use chrome:// to have a different hosts. |
| std::string errorText = net::ErrorToShortString(net::ERR_INVALID_URL); |
| self.serverRespondsWithContent = YES; |
| |
| [ChromeEarlGrey loadURL:GURL("chrome://invalid")]; |
| [ChromeEarlGrey waitForWebStateContainingText:errorText]; |
| // Add some delay otherwise the back/forward navigations are occurring too |
| // fast. |
| base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.2)); |
| |
| // Navigate to a page which responds. |
| [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo-query?bar")]; |
| [ChromeEarlGrey waitForWebStateContainingText:"bar"]; |
| base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.2)); |
| |
| [ChromeEarlGrey goBack]; |
| [ChromeEarlGrey waitForWebStateContainingText:errorText]; |
| base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.2)); |
| |
| [ChromeEarlGrey goForward]; |
| [ChromeEarlGrey waitForWebStateContainingText:"bar"]; |
| base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.2)); |
| |
| [ChromeEarlGrey goBack]; |
| [ChromeEarlGrey waitForWebStateContainingText:errorText]; |
| base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.2)); |
| |
| [ChromeEarlGrey goForward]; |
| [ChromeEarlGrey waitForWebStateContainingText:"bar"]; |
| } |
| |
| // Loads the URL which fails to load, then sucessfully navigates back/forward to |
| // the page. |
| // TODO:(crbug.com/1185639): Re-enable this test on simulator. |
| #if TARGET_OS_SIMULATOR |
| #define MAYBE_testNavigateForwardToErrorPage \ |
| FLAKY_testNavigateForwardToErrorPage |
| #else |
| #define MAYBE_testNavigateForwardToErrorPage testNavigateForwardToErrorPage |
| #endif |
| - (void)MAYBE_testNavigateForwardToErrorPage { |
| self.serverRespondsWithContent = YES; |
| [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo-query?bar")]; |
| [ChromeEarlGrey waitForWebStateContainingText:"bar"]; |
| |
| // No response leads to ERR_CONNECTION_CLOSED error. |
| self.serverRespondsWithContent = NO; |
| [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo-query?foo")]; |
| [ChromeEarlGrey waitForWebStateContainingText:GetErrorMessage()]; |
| |
| self.serverRespondsWithContent = YES; |
| [ChromeEarlGrey goBack]; |
| [ChromeEarlGrey waitForWebStateContainingText:"bar"]; |
| |
| // Navigate forward to the error page, which should load without errors. |
| [ChromeEarlGrey goForward]; |
| [ChromeEarlGrey waitForWebStateContainingText:"foo"]; |
| } |
| |
| // Loads the URL which fails to load, then sucessfully reloads the page. |
| - (void)testReloadErrorPage { |
| // No response leads to ERR_CONNECTION_CLOSED error. |
| self.serverRespondsWithContent = NO; |
| [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo-query?foo")]; |
| [ChromeEarlGrey waitForWebStateContainingText:GetErrorMessage()]; |
| |
| // Reload the page, which should load without errors. |
| self.serverRespondsWithContent = YES; |
| [ChromeEarlGrey reload]; |
| [ChromeEarlGrey waitForWebStateContainingText:"foo"]; |
| } |
| |
| // Sucessfully loads the page, stops the server and reloads the page. |
| - (void)testReloadPageAfterServerIsDown { |
| // Sucessfully load the page. |
| self.serverRespondsWithContent = YES; |
| [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo-query?foo")]; |
| [ChromeEarlGrey waitForWebStateContainingText:"foo"]; |
| |
| // Reload the page, no response leads to ERR_CONNECTION_CLOSED error. |
| self.serverRespondsWithContent = NO; |
| [ChromeEarlGrey reload]; |
| [ChromeEarlGrey waitForWebStateContainingText:GetErrorMessage()]; |
| } |
| |
| // Loads a URL then restore the session and fail during the reload |
| - (void)testRestoreErrorPage { |
| // Load the page. |
| self.serverRespondsWithContent = YES; |
| [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo-query?foo")]; |
| [ChromeEarlGrey waitForWebStateContainingText:"foo"]; |
| GREYAssertEqual(1, [ChromeEarlGrey navigationBackListItemsCount], |
| @"The navigation back list should have only 1 entries before " |
| @"the restoration."); |
| |
| // Restore the session but with the page no longer loading. |
| self.serverRespondsWithContent = NO; |
| [self triggerRestoreByRestartingApplication]; |
| [ChromeEarlGrey waitForWebStateContainingText:GetErrorMessage()]; |
| |
| GREYAssertEqual(1, [ChromeEarlGrey navigationBackListItemsCount], |
| @"The navigation back list should still have only 1 entries " |
| @"after the restoration."); |
| } |
| |
| // Loads a URL which redirect to a data URL and check that the navigation is |
| // blocked on the first URL. |
| - (void)testRedirectToData { |
| // Disable the test on iOS 16.4 as WKWebView handles this internally now as of |
| // https://bugs.webkit.org/show_bug.cgi?id=230158 |
| // TODO(crbug.com/40267045): Remove redirect logic completely when dropping |
| // iOS 16. |
| if (@available(iOS 16.4, *)) { |
| EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 16.4."); |
| } |
| self.serverRespondsWithContent = YES; |
| [ChromeEarlGrey loadURL:self.testServer->GetURL(kRedirectPage)]; |
| [ChromeEarlGrey waitForWebStateContainingText:net::ErrorToShortString( |
| net::ERR_UNSAFE_REDIRECT)]; |
| [ChromeEarlGrey waitForWebStateContainingText:kRedirectPage]; |
| [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()] |
| assertWithMatcher:chrome_test_util::OmniboxContainingText(kRedirectPage)]; |
| } |
| |
| @end |