blob: 2ee980185a96e7c10ca091cd83a35ba36002c380 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/test/gmock_expected_support.h"
#include "build/build_config.h"
#include "chrome/browser/controlled_frame/controlled_frame_test_base.h"
#include "chrome/browser/controlled_frame/scoped_test_driver_proxy.h"
#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
#include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/permissions_policy/policy_helper_public.h"
namespace controlled_frame {
namespace {
#define IS_LINUX_OR_CROS (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
// TODO(crbug.com/423697478): frame_event_handlers_part_1 and webrequest_auth
// times out on win-asan bots and linux / chromeos bots.
const auto kTestFiles = testing::Values("add_content_scripts.window.js",
"camera.window.js",
"client_hints_user_agent.window.js",
#if !(BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER)) && !IS_LINUX_OR_CROS
"frame_event_handlers_part_1.window.js",
#endif
"frame_event_handlers_part_2.window.js",
"geolocation.window.js",
"navigation.window.js",
"new_window.window.js",
"no_callback.window.js",
"scheme.window.js",
"user_agent_override.window.js",
#if !(BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER)) && !IS_LINUX_OR_CROS
"webrequest_auth.window.js",
#endif
"webrequest_core.window.js",
"webrequest_modify.window.js",
"webrequest_read.window.js");
constexpr char kTestDirectory[] = "chrome/test/data/controlled_frame";
constexpr char kTestHarnessPath[] =
"third_party/blink/web_tests/external/wpt/resources/testharness.js";
constexpr char kTestDriverPath[] =
"third_party/wpt_tools/wpt/resources/testdriver.js";
constexpr char kHtmlWrapperSrc[] = R"(
<!doctype html>
<meta charset=utf-8>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<body>
)";
constexpr char kWptReporterSrc[] = R"(
(function() {
const policy = window.trustedTypes.createPolicy('policy', {
createScriptURL: url => url,
});
function isFailure(test) {
return test.status !== 0;
}
function generateMessage(test) {
const status_descriptions = ['PASS', 'FAIL', 'TIMEOUT', 'NOTRUN',
'PRECONDITION_FAILED'];
let msg = `${status_descriptions[test.status]}: ${test.name}`;
if (test.message) {
msg += `\n ${test.message}`;
}
if (test.stack) {
msg += `\n${test.stack}`;
}
return msg;
}
window.results = new Promise(async (resolve, reject) => {
add_completion_callback((tests) => {
if (tests.length === 0) {
reject('No test results found');
return;
}
const failed_tests = tests.filter(isFailure);
if (failed_tests.length === 0) {
resolve(tests.map(generateMessage).join('\n'));
return;
}
reject(failed_tests.map(generateMessage).join('\n\n'));
});
const testSrc = new URLSearchParams(location.search).get('test');
if (!testSrc) {
reject('Missing test parameter in URL query');
return;
}
const testBody = await (await fetch(testSrc)).text();
const importRegex = /^\/\/ META script=(.*)/gm;
const imports = [...testBody.matchAll(importRegex)]
.map((match) => match[1]);
imports.push(testSrc);
for (const importSrc of imports) {
const script = document.createElement('script');
script.src = policy.createScriptURL(importSrc);
script.async = false;
document.head.appendChild(script);
}
});
})();
)";
} // namespace
// These tests wrap WPT infrastructure in a browser test to allow us to run
// WPTs within an IWA. When IWA support is officially added to WPTs, we can
// move all the Javascript tests referenced here into the main WPT test
// directory and remove this test.
class ControlledFrameWptBrowserTest
: public ControlledFrameTestBase,
public testing::WithParamInterface<std::string> {};
// This test includes the WPT test harness and an adapter script that will:
// * Register a completion callback with the test harness.
// * Inject a script tag with a src equal to the URL query. This is how the
// test configures which WPT *.window.js test file is run.
// * Put the results in a top-level window.results promise that will contain
// the list of passed tests if there were no failures, or reject the promise
// with error information for failing tests otherwise.
IN_PROC_BROWSER_TEST_P(ControlledFrameWptBrowserTest, Run) {
device::ScopedGeolocationOverrider overrider(/*latitude=*/1, /*longitude=*/2);
base::FilePath test_data_dir;
ASSERT_TRUE(
base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &test_data_dir));
// Enable all permissions policies by default in the IWA hosting the tests.
// There's currently no way within in an individual WPT to declare that a
// specific permissions policy is needed, so we have to enable them all. If a
// test needs one disabled, it can achieve that through an iframe that
// doesn't delegate the feature.
web_app::ManifestBuilder manifest;
for (auto const& [feature, _] :
blink::GetPermissionsPolicyFeatureToNameMap()) {
manifest.AddPermissionsPolicyWildcard(feature);
}
std::unique_ptr<web_app::ScopedBundledIsolatedWebApp> app =
web_app::IsolatedWebAppBuilder(manifest)
.AddFolderFromDisk("/", test_data_dir.AppendASCII(kTestDirectory))
.AddFileFromDisk("/resources/testharness.js",
test_data_dir.AppendASCII(kTestHarnessPath))
.AddFileFromDisk("/resources/testdriver.js",
test_data_dir.AppendASCII(kTestDriverPath))
.AddHtml("/", kHtmlWrapperSrc)
.AddJs("/resources/testharnessreport.js", kWptReporterSrc)
.AddJs("/resources/testdriver-vendor.js",
ScopedTestDriverProxy::testdriver_override_script_src())
.BuildBundle();
app->TrustSigningKey();
ASSERT_OK_AND_ASSIGN(web_app::IsolatedWebAppUrlInfo url_info,
app->Install(profile()));
std::string test_params = "?test=" + GetParam() + "&https_origin=" +
https_server()->base_url().spec();
content::RenderFrameHost* app_frame = OpenApp(url_info.app_id(), test_params);
ScopedTestDriverProxy scoped_test_driver_proxy(app_frame);
LOG(INFO) << "Results:\n"
<< content::EvalJs(app_frame, "window.results").ExtractString();
}
INSTANTIATE_TEST_SUITE_P(
/* no prefix */,
ControlledFrameWptBrowserTest,
kTestFiles,
[](const testing::TestParamInfo<std::string>& param_info) {
std::string test_name = param_info.param;
base::ReplaceSubstringsAfterOffset(&test_name, 0, ".window.js", "");
base::ReplaceChars(test_name, ".", "_", &test_name);
return test_name;
});
} // namespace controlled_frame