Commit c46964ab authored by rbpotter's avatar rbpotter Committed by Commit Bot

Web UI: Add chrome://test data source for testing JS modules

- Add a test data source that serves files from chrome://test
- Autogenerate HTML responses containing a single
<script type="module"> with src set to a requested JS file at runtime.
Request these responses by preloading
chrome://test?module=JS_TEST_FILE.js
- Remaining dependencies, other than mocha and mocha adapter, can
be imported via the JS test module, instead of using |extraLibraries|.
- Map requests for chrome://test URLs to the appropriate Web UI
controller using a new webuiHost parameter for tests.

Bug: 968804
Change-Id: I5b409bb54da5611be68fe590176edec3c7b668e2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1700294
Commit-Queue: Rebekah Potter <rbpotter@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#678925}
parent 2f79f009
......@@ -5638,6 +5638,8 @@ if (!is_android) {
"signin/token_revoker_test_utils.h",
"ui/webui/signin/login_ui_test_utils.cc",
"ui/webui/signin/login_ui_test_utils.h",
"ui/webui/test_data_source.cc",
"ui/webui/test_data_source.h",
"ui/webui/web_ui_test_handler.cc",
"ui/webui/web_ui_test_handler.h",
]
......
// Copyright 2019 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 "chrome/browser/ui/webui/test_data_source.h"
#include <memory>
#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/common/url_constants.h"
namespace {
const char kModuleQuery[] = "module=";
} // namespace
std::string TestDataSource::GetSource() {
return "test";
}
void TestDataSource::StartDataRequest(
const std::string& path,
const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
const content::URLDataSource::GotDataCallback& callback) {
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
base::BindOnce(&TestDataSource::ReadFile, base::Unretained(this), path,
callback));
}
std::string TestDataSource::GetMimeType(const std::string& path) {
if (base::EndsWith(path, ".html", base::CompareCase::INSENSITIVE_ASCII) ||
base::StartsWith(GetURLForPath(path).query(), kModuleQuery,
base::CompareCase::INSENSITIVE_ASCII)) {
// Direct request for HTML, or autogenerated HTML response for module query.
return "text/html";
}
// The test data source currently only serves HTML and JS.
CHECK(base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII));
return "application/javascript";
}
bool TestDataSource::ShouldServeMimeTypeAsContentTypeHeader() {
return true;
}
bool TestDataSource::AllowCaching() {
return false;
}
std::string TestDataSource::GetContentSecurityPolicyScriptSrc() {
return "script-src chrome://* 'self';";
}
GURL TestDataSource::GetURLForPath(const std::string& path) {
return GURL(std::string(content::kChromeUIScheme) + "://" + GetSource() +
"/" + path);
}
void TestDataSource::ReadFile(
const std::string& path,
const content::URLDataSource::GotDataCallback& callback) {
if (test_data_.empty()) {
CHECK(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_));
CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_));
}
base::FilePath root = test_data_.Append(FILE_PATH_LITERAL("webui"));
std::string content;
GURL url = GetURLForPath(path);
CHECK(url.is_valid());
if (base::StartsWith(url.query(), kModuleQuery,
base::CompareCase::INSENSITIVE_ASCII)) {
std::string js_path = url.query().substr(strlen(kModuleQuery));
base::FilePath file_path =
root.Append(base::FilePath::FromUTF8Unsafe(js_path));
// Do some basic validation of the JS file path provided in the query.
CHECK_EQ(file_path.Extension(), FILE_PATH_LITERAL(".js"));
CHECK(base::PathExists(file_path))
<< url.spec() << "=" << file_path.value();
content = "<script type=\"module\" src=\"" + js_path + "\"></script>";
} else {
base::FilePath file_path =
root.Append(base::FilePath::FromUTF8Unsafe(path));
CHECK(base::ReadFileToString(file_path, &content))
<< url.spec() << "=" << file_path.value();
}
scoped_refptr<base::RefCountedString> response =
base::RefCountedString::TakeString(&content);
callback.Run(response.get());
}
// Copyright 2019 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.
#ifndef CHROME_BROWSER_UI_WEBUI_TEST_DATA_SOURCE_H_
#define CHROME_BROWSER_UI_WEBUI_TEST_DATA_SOURCE_H_
#include <string>
#include "base/files/file_path.h"
#include "base/macros.h"
#include "content/public/browser/url_data_source.h"
#include "url/gurl.h"
// Serves files at chrome://test/ from //src/chrome/test/data/webui.
class TestDataSource : public content::URLDataSource {
public:
TestDataSource() = default;
~TestDataSource() override = default;
private:
void StartDataRequest(
const std::string& path,
const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
const content::URLDataSource::GotDataCallback& callback) override;
std::string GetMimeType(const std::string& path) override;
bool ShouldServeMimeTypeAsContentTypeHeader() override;
bool AllowCaching() override;
std::string GetSource() override;
std::string GetContentSecurityPolicyScriptSrc() override;
GURL GetURLForPath(const std::string& path);
void ReadFile(const std::string& path,
const content::URLDataSource::GotDataCallback& callback);
base::FilePath test_data_;
base::FilePath source_root_;
DISALLOW_COPY_AND_ASSIGN(TestDataSource);
};
#endif // CHROME_BROWSER_UI_WEBUI_TEST_DATA_SOURCE_H_
......@@ -386,6 +386,7 @@ function TEST_F(testFixture, testFunction, testBody, opt_preamble) {
var testShouldFail = this[testFixture].prototype.testShouldFail;
var testPredicate = testShouldFail ? 'ASSERT_FALSE' : 'ASSERT_TRUE';
var loaderFile = this[testFixture].prototype.loaderFile;
var webuiHost = this[testFixture].prototype.webuiHost;
var extraLibraries = genIncludes.concat(
this[testFixture].prototype.extraLibraries.map(includeFileToPath),
resolveClosureModuleDeps(this[testFixture].prototype.closureModuleDeps),
......@@ -529,6 +530,10 @@ for (var i = 0; i < extraLibraries.length; i++) {
output(`
set_loader_file("${loaderFile}");`);
}
if (webuiHost) {
output(`
set_webui_host("${webuiHost}");`);
}
if (testGenPreamble)
testGenPreamble(testFixture, testFunction);
if (browsePreload)
......
......@@ -5,6 +5,8 @@
#include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/test_data_source.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_controller.h"
......@@ -21,6 +23,11 @@ TestChromeWebUIControllerFactory::TestChromeWebUIControllerFactory() {
TestChromeWebUIControllerFactory::~TestChromeWebUIControllerFactory() {
}
void TestChromeWebUIControllerFactory::set_webui_host(
const std::string& webui_host) {
webui_host_ = webui_host;
}
void TestChromeWebUIControllerFactory::AddFactoryOverride(
const std::string& host, WebUIProvider* provider) {
DCHECK_EQ(0U, factory_overrides_.count(host));
......@@ -37,9 +44,11 @@ WebUI::TypeID TestChromeWebUIControllerFactory::GetWebUIType(
content::BrowserContext* browser_context,
const GURL& url) {
Profile* profile = Profile::FromBrowserContext(browser_context);
WebUIProvider* provider = GetWebUIProvider(profile, url);
return provider ? reinterpret_cast<WebUI::TypeID>(provider) :
ChromeWebUIControllerFactory::GetWebUIType(profile, url);
const GURL& webui_url = TestURLToWebUIURL(url);
WebUIProvider* provider = GetWebUIProvider(profile, webui_url);
return provider
? reinterpret_cast<WebUI::TypeID>(provider)
: ChromeWebUIControllerFactory::GetWebUIType(profile, webui_url);
}
std::unique_ptr<WebUIController>
......@@ -47,15 +56,31 @@ TestChromeWebUIControllerFactory::CreateWebUIControllerForURL(
content::WebUI* web_ui,
const GURL& url) {
Profile* profile = Profile::FromWebUI(web_ui);
WebUIProvider* provider = GetWebUIProvider(profile, url);
return provider ? provider->NewWebUI(web_ui, url)
: ChromeWebUIControllerFactory::CreateWebUIControllerForURL(
web_ui, url);
const GURL& webui_url = TestURLToWebUIURL(url);
WebUIProvider* provider = GetWebUIProvider(profile, webui_url);
auto controller =
provider ? provider->NewWebUI(web_ui, webui_url)
: ChromeWebUIControllerFactory::CreateWebUIControllerForURL(
web_ui, webui_url);
content::URLDataSource::Add(profile, std::make_unique<TestDataSource>());
return controller;
}
TestChromeWebUIControllerFactory::WebUIProvider*
TestChromeWebUIControllerFactory::GetWebUIProvider(
Profile* profile, const GURL& url) const {
auto found = factory_overrides_.find(url.host());
const GURL& webui_url = TestURLToWebUIURL(url);
auto found = factory_overrides_.find(webui_url.host());
return found != factory_overrides_.end() ? found->second : nullptr;
}
GURL TestChromeWebUIControllerFactory::TestURLToWebUIURL(
const GURL& url) const {
if (url.host() != "test" || webui_host_.empty())
return url;
GURL webui_url(url);
GURL::Replacements replacements;
replacements.SetHostStr(webui_host_);
return webui_url.ReplaceComponents(replacements);
}
......@@ -36,6 +36,9 @@ class TestChromeWebUIControllerFactory : public ChromeWebUIControllerFactory {
TestChromeWebUIControllerFactory();
~TestChromeWebUIControllerFactory() override;
// Sets the Web UI host.
void set_webui_host(const std::string& webui_host);
// Override the creation for urls having |host| with |provider|.
void AddFactoryOverride(const std::string& host, WebUIProvider* provider);
......@@ -53,9 +56,19 @@ class TestChromeWebUIControllerFactory : public ChromeWebUIControllerFactory {
// Return the WebUIProvider for the |url|'s host if it exists, otherwise NULL.
WebUIProvider* GetWebUIProvider(Profile* profile, const GURL& url) const;
// Replace |url|'s host with the Web UI host if |url| is a test URL served
// from the TestDataSource. This ensures the factory always creates the
// appropriate Web UI controller when these URLs are encountered instead of
// failing.
GURL TestURLToWebUIURL(const GURL& url) const;
// Stores the mapping of host to WebUIProvider.
FactoryOverridesMap factory_overrides_;
// Stores the Web UI host to create the correct Web UI controller for
// chrome://test URL requests.
std::string webui_host_;
DISALLOW_COPY_AND_ASSIGN(TestChromeWebUIControllerFactory);
};
......
......@@ -375,6 +375,10 @@ void BaseWebUIBrowserTest::set_loader_file(const std::string& loader_file) {
loader_file_ = loader_file;
}
void BaseWebUIBrowserTest::set_webui_host(const std::string& webui_host) {
test_factory_->set_webui_host(webui_host);
}
namespace {
// DataSource for the dummy URL. If no data source is provided then an error
......
......@@ -5,7 +5,9 @@
#ifndef CHROME_TEST_BASE_WEB_UI_BROWSER_TEST_H_
#define CHROME_TEST_BASE_WEB_UI_BROWSER_TEST_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/files/file_path.h"
......@@ -108,6 +110,7 @@ class BaseWebUIBrowserTest : public JavaScriptBrowserTest {
void set_preload_test_name(const std::string& preload_test_name);
void set_loader_file(const std::string& loader_file);
void set_webui_host(const std::string& webui_host);
// Enable command line flags for test.
void SetUpCommandLine(base::CommandLine* command_line) override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment