blob: 475c1b2539d3bc87fabb69afb344f7c51d4592f2 [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.
#include "base/mac/foundation_util.h"
#include "base/memory/ptr_util.h"
#import "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/test/web_view_interaction_test_util.h"
#import "ios/web/public/web_state.h"
#include "ios/web/public/web_state_observer.h"
#import "ios/web/test/web_int_test.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// URL for the test window.location test file. The page at this URL contains
// several buttons that trigger window.location commands. The page supports
// several JavaScript functions:
// - updateUrlToLoadText(), which takes a URL and updates a div on the page to
// contain that text. This URL is used as the parameter for window.location
// function calls triggered by button taps.
// - getUrl(), which returns the URL that was set via updateUrlToLoadText().
// - isOnLoadTextVisible(), which returns whether a placeholder string is
// present on the page. This string is added to the page in the onload event
// and is removed once a button is tapped. Verifying that the onload text is
// visible after tapping a button is equivalent to checking that a load has
// occurred as the result of the button tap.
const char kWindowLocationTestURL[] = "/window_location.html";
// Button IDs used in the window.location test page.
const char kWindowLocationAssignID[] = "location-assign";
const char kWindowLocationReplaceID[] = "location-replace";
const char kWindowLocationReloadID[] = "location-reload";
const char kWindowLocationSetToDOMStringID[] = "set-location-to-dom-string";
// JavaScript functions on the window.location test page.
NSString* const kUpdateURLScriptFormat = @"updateUrlToLoadText('%s')";
NSString* const kGetURLScript = @"getUrl()";
NSString* const kOnLoadCheckScript = @"isOnLoadTextVisible()";
NSString* const kNoOpCheckScript = @"isNoOpTextVisible()";
// URL of a sample file-based page.
const char kSampleFileBasedURL[] = "/chromium_logo_page.html";
} // namespace
// Test fixture for window.location integration tests.
class WindowLocationTest : public web::WebIntTest {
protected:
void SetUp() override {
web::WebIntTest::SetUp();
test_server_ = std::make_unique<net::EmbeddedTestServer>();
test_server_->ServeFilesFromSourceDirectory(
base::FilePath("ios/testing/data/http_server_files/"));
ASSERT_TRUE(test_server_->Start());
// Load the window.location test page.
window_location_url_ = test_server_->GetURL(kWindowLocationTestURL);
ASSERT_TRUE(LoadUrl(window_location_url()));
}
// The URL of the window.location test page.
const GURL& window_location_url() { return window_location_url_; }
// Executes JavaScript on the window.location test page to use |url| as the
// parameter for the window.location calls executed by tapping the buttons on
// the page.
void SetWindowLocationUrl(const GURL& url) {
ASSERT_EQ(window_location_url(), web_state()->GetLastCommittedURL());
std::string url_spec = url.possibly_invalid_spec();
NSString* set_url_script =
[NSString stringWithFormat:kUpdateURLScriptFormat, url_spec.c_str()];
ExecuteJavaScript(set_url_script);
std::unique_ptr<base::Value> injected_url =
ExecuteJavaScript(kGetURLScript);
ASSERT_TRUE(injected_url->is_string());
ASSERT_EQ(url_spec, injected_url->GetString());
}
// Executes JavaScript on the window.location test page and returns whether
// |kOnLoadText| is visible.
bool IsOnLoadTextVisible() {
std::unique_ptr<base::Value> text_visible =
ExecuteJavaScript(kOnLoadCheckScript);
return text_visible->GetBool();
}
// Executes JavaScript on the window.location test page and returns whether
// the no-op text is visible. It is displayed 0.5 seconds after a button is
// tapped, and can be used to verify that a navigation did not occur.
bool IsNoOpTextVisible() {
std::unique_ptr<base::Value> text_visible =
ExecuteJavaScript(kNoOpCheckScript);
return text_visible->GetBool();
}
std::unique_ptr<net::EmbeddedTestServer> test_server_;
private:
GURL window_location_url_;
};
// Tests that calling window.location.assign() creates a new NavigationItem.
TEST_F(WindowLocationTest, Assign) {
// Navigate to about:blank so there is a forward entry to prune.
GURL about_blank("about:blank");
ASSERT_TRUE(LoadUrl(about_blank));
web::NavigationItem* about_blank_item =
navigation_manager()->GetLastCommittedItem();
// Navigate back to the window.location test page.
ASSERT_TRUE(ExecuteBlockAndWaitForLoad(window_location_url(), ^{
navigation_manager()->GoBack();
}));
// Set the window.location test URL and tap the window.location.assign()
// button.
GURL sample_url = test_server_->GetURL(kSampleFileBasedURL);
SetWindowLocationUrl(sample_url);
ASSERT_TRUE(ExecuteBlockAndWaitForLoad(sample_url, ^{
ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(),
kWindowLocationAssignID));
}));
// Verify that |sample_url| was loaded and that |about_blank_item| was pruned.
EXPECT_EQ(sample_url, navigation_manager()->GetLastCommittedItem()->GetURL());
EXPECT_EQ(NSNotFound, GetIndexOfNavigationItem(about_blank_item));
}
// Tests that calling window.location.assign() with an unresolvable URL loads
// about:blank.
TEST_F(WindowLocationTest, WindowLocationAssignUnresolvable) {
// Attempt to call window.location.assign() using an unresolvable URL.
GURL unresolvable_url("http:https:not a url");
SetWindowLocationUrl(unresolvable_url);
ASSERT_TRUE(
web::test::TapWebViewElementWithId(web_state(), kWindowLocationAssignID));
// Wait for the no-op text to appear.
base::test::ios::WaitUntilCondition(^bool {
return IsNoOpTextVisible();
});
}
// Tests that calling window.location.replace() doesn't create a new
// NavigationItem.
TEST_F(WindowLocationTest, Replace) {
// Navigate to about:blank so there is a forward entry.
GURL about_blank("about:blank");
ASSERT_TRUE(LoadUrl(about_blank));
web::NavigationItem* about_blank_item =
navigation_manager()->GetLastCommittedItem();
// Navigate back to the window.location test page.
ASSERT_TRUE(ExecuteBlockAndWaitForLoad(window_location_url(), ^{
navigation_manager()->GoBack();
}));
// Set the window.location test URL and tap the window.location.replace()
// button.
GURL sample_url = test_server_->GetURL(kSampleFileBasedURL);
SetWindowLocationUrl(sample_url);
ASSERT_TRUE(ExecuteBlockAndWaitForLoad(sample_url, ^{
ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(),
kWindowLocationReplaceID));
}));
// Verify that |sample_url| was loaded and that |about_blank_item| was pruned.
web::NavigationItem* current_item =
navigation_manager()->GetLastCommittedItem();
EXPECT_EQ(sample_url, current_item->GetURL());
EXPECT_EQ(GetIndexOfNavigationItem(current_item) + 1,
GetIndexOfNavigationItem(about_blank_item));
}
// Tests that calling window.location.replace() with an unresolvable URL is a
// no-op.
TEST_F(WindowLocationTest, WindowLocationReplaceUnresolvable) {
if (@available(iOS 14, *)) {
// This is a syntax error in WebKit in iOS14.
return;
}
// Attempt to call window.location.assign() using an unresolvable URL.
GURL unresolvable_url("http:https:not a url");
SetWindowLocationUrl(unresolvable_url);
ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(),
kWindowLocationReplaceID));
// Wait for the no-op text to appear.
base::test::ios::WaitUntilCondition(^bool {
return IsNoOpTextVisible();
});
}
// Tests that calling window.location.reload() causes an onload event to occur.
TEST_F(WindowLocationTest, WindowLocationReload) {
// Tap the window.location.reload() button.
ASSERT_TRUE(ExecuteBlockAndWaitForLoad(window_location_url(), ^{
ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(),
kWindowLocationReloadID));
}));
// Verify that |kOnLoadText| is displayed and that no additional
// NavigationItems are added.
EXPECT_TRUE(IsOnLoadTextVisible());
EXPECT_EQ(1, navigation_manager()->GetItemCount());
}
// Tests that calling window.location.assign() creates a new NavigationItem.
TEST_F(WindowLocationTest, WindowLocationSetToDOMString) {
// Navigate to about:blank so there is a forward entry to prune.
GURL about_blank("about:blank");
ASSERT_TRUE(LoadUrl(about_blank));
web::NavigationItem* about_blank_item =
navigation_manager()->GetLastCommittedItem();
// Navigate back to the window.location test page.
ASSERT_TRUE(ExecuteBlockAndWaitForLoad(window_location_url(), ^{
navigation_manager()->GoBack();
}));
// Set the window.location test URL and tap the window.location.assign()
// button.
GURL sample_url = test_server_->GetURL(kSampleFileBasedURL);
SetWindowLocationUrl(sample_url);
ASSERT_TRUE(ExecuteBlockAndWaitForLoad(sample_url, ^{
ASSERT_TRUE(web::test::TapWebViewElementWithId(
web_state(), kWindowLocationSetToDOMStringID));
}));
// Verify that |sample_url| was loaded and that |about_blank_item| was pruned.
EXPECT_EQ(sample_url, navigation_manager()->GetLastCommittedItem()->GetURL());
EXPECT_EQ(NSNotFound, GetIndexOfNavigationItem(about_blank_item));
}