blob: b44c4e7b8f54ff8f538018ea3668b139ddd53dcc [file] [log] [blame]
// Copyright 2017 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 "base/command_line.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/installed_payment_apps_finder.h"
#include "content/public/browser/payment_app_provider.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/payments/payment_app.mojom.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
namespace content {
namespace {
using ::payments::mojom::CanMakePaymentEventData;
using ::payments::mojom::CanMakePaymentEventDataPtr;
using ::payments::mojom::CanMakePaymentResponsePtr;
using ::payments::mojom::PaymentCurrencyAmount;
using ::payments::mojom::PaymentDetailsModifier;
using ::payments::mojom::PaymentDetailsModifierPtr;
using ::payments::mojom::PaymentHandlerResponsePtr;
using ::payments::mojom::PaymentItem;
using ::payments::mojom::PaymentMethodData;
using ::payments::mojom::PaymentRequestEventData;
using ::payments::mojom::PaymentRequestEventDataPtr;
void OnPaymentAppInstall(base::OnceClosure done_callback,
bool* out_success,
bool success) {
*out_success = success;
std::move(done_callback).Run();
}
void GetAllPaymentAppsCallback(
base::OnceClosure done_callback,
InstalledPaymentAppsFinder::PaymentApps* out_apps,
InstalledPaymentAppsFinder::PaymentApps apps) {
*out_apps = std::move(apps);
std::move(done_callback).Run();
}
void CaptureCanMakePaymentResult(base::OnceClosure done_callback,
bool* out_payment_event_result,
CanMakePaymentResponsePtr response) {
*out_payment_event_result = response->can_make_payment;
std::move(done_callback).Run();
}
void CaptureAbortResult(base::OnceClosure done_callback,
bool* out_payment_event_result,
bool payment_event_result) {
*out_payment_event_result = payment_event_result;
std::move(done_callback).Run();
}
void InvokePaymentAppCallback(base::OnceClosure done_callback,
PaymentHandlerResponsePtr* out_response,
PaymentHandlerResponsePtr response) {
*out_response = std::move(response);
std::move(done_callback).Run();
}
} // namespace
class PaymentAppBrowserTest : public ContentBrowserTest {
public:
PaymentAppBrowserTest() {}
PaymentAppBrowserTest(const PaymentAppBrowserTest&) = delete;
PaymentAppBrowserTest& operator=(const PaymentAppBrowserTest&) = delete;
~PaymentAppBrowserTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
}
void SetUpOnMainThread() override {
https_server_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTPS);
https_server_->ServeFilesFromSourceDirectory(GetTestDataFilePath());
ASSERT_TRUE(https_server_->Start());
ASSERT_TRUE(NavigateToURL(
shell(),
https_server_->GetURL("/payments/payment_app_invocation.html")));
ContentBrowserTest::SetUpOnMainThread();
}
std::string RunScript(const std::string& script) {
return EvalJs(shell()->web_contents(), script).ExtractString();
}
std::string PopConsoleString() {
return RunScript("resultQueue.pop().then(result => String(result))");
}
void RegisterPaymentApp() {
SkBitmap app_icon;
constexpr int kBitmapDimension = 16;
app_icon.allocN32Pixels(kBitmapDimension, kBitmapDimension);
app_icon.eraseColor(SK_ColorRED);
GURL service_worker_javascript_file_url =
https_server_->GetURL("/payments/payment_app.js");
base::RunLoop run_loop;
bool success = false;
PaymentAppProvider::GetOrCreateForWebContents(shell()->web_contents())
->InstallPaymentAppForTesting(
app_icon, service_worker_javascript_file_url,
/*service_worker_scope=*/
service_worker_javascript_file_url.GetWithoutFilename(),
/*payment_method_identifier=*/"https://bobpay.com",
base::BindOnce(&OnPaymentAppInstall, run_loop.QuitClosure(),
&success));
run_loop.Run();
ASSERT_TRUE(success);
}
url::Origin GetTestServerOrigin() {
return url::Origin::Create(https_server_->GetURL("/"));
}
std::vector<int64_t> GetAllPaymentAppRegistrationIDs() {
base::RunLoop run_loop;
InstalledPaymentAppsFinder::PaymentApps apps;
InstalledPaymentAppsFinder::GetInstance(
shell()->web_contents()->GetBrowserContext())
->GetAllPaymentApps(base::BindOnce(&GetAllPaymentAppsCallback,
run_loop.QuitClosure(), &apps));
run_loop.Run();
std::vector<int64_t> registrationIds;
for (const auto& app_info : apps) {
registrationIds.push_back(app_info.second->registration_id);
}
return registrationIds;
}
bool AbortPayment(int64_t registration_id,
const url::Origin& sw_origin,
const std::string& payment_request_id) {
base::RunLoop run_loop;
bool payment_aborted = false;
PaymentAppProvider::GetOrCreateForWebContents(shell()->web_contents())
->AbortPayment(
registration_id, sw_origin, payment_request_id,
base::BindOnce(&CaptureAbortResult, run_loop.QuitClosure(),
&payment_aborted));
run_loop.Run();
return payment_aborted;
}
bool CanMakePaymentWithTestData(int64_t registration_id,
const url::Origin& sw_origin,
const std::string& payment_request_id,
const std::string& supported_method) {
CanMakePaymentEventDataPtr event_data =
CreateCanMakePaymentEventData(supported_method);
base::RunLoop run_loop;
bool can_make_payment = false;
PaymentAppProvider::GetOrCreateForWebContents(shell()->web_contents())
->CanMakePayment(
registration_id, sw_origin, payment_request_id,
std::move(event_data),
base::BindOnce(&CaptureCanMakePaymentResult, run_loop.QuitClosure(),
&can_make_payment));
run_loop.Run();
return can_make_payment;
}
PaymentHandlerResponsePtr InvokePaymentAppWithTestData(
int64_t registration_id,
const url::Origin& sw_origin,
const std::string& supported_method,
const std::string& instrument_key) {
base::RunLoop run_loop;
PaymentHandlerResponsePtr response;
PaymentAppProvider::GetOrCreateForWebContents(shell()->web_contents())
->InvokePaymentApp(
registration_id, sw_origin,
CreatePaymentRequestEventData(supported_method, instrument_key),
base::BindOnce(&InvokePaymentAppCallback, run_loop.QuitClosure(),
&response));
run_loop.Run();
return response;
}
void ClearStoragePartitionData() {
// Clear data from the storage partition. Parameters are set to clear data
// for service workers, for all origins, for an unbounded time range.
base::RunLoop run_loop;
static_cast<StoragePartitionImpl*>(shell()
->web_contents()
->GetBrowserContext()
->GetDefaultStoragePartition())
->ClearData(StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS,
StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
blink::StorageKey(), base::Time(), base::Time::Max(),
run_loop.QuitClosure());
run_loop.Run();
}
private:
CanMakePaymentEventDataPtr CreateCanMakePaymentEventData(
const std::string& supported_method) {
CanMakePaymentEventDataPtr event_data = CanMakePaymentEventData::New();
event_data->top_origin = GURL("https://example.test");
event_data->payment_request_origin = GURL("https://example.test");
event_data->method_data.push_back(PaymentMethodData::New());
event_data->method_data[0]->supported_method = supported_method;
PaymentDetailsModifierPtr modifier = PaymentDetailsModifier::New();
modifier->total = PaymentItem::New();
modifier->total->amount = PaymentCurrencyAmount::New();
modifier->total->amount->currency = "USD";
modifier->total->amount->value = "55";
modifier->method_data = PaymentMethodData::New();
modifier->method_data->supported_method = supported_method;
event_data->modifiers.push_back(std::move(modifier));
return event_data;
}
PaymentRequestEventDataPtr CreatePaymentRequestEventData(
const std::string& supported_method,
const std::string& instrument_key) {
PaymentRequestEventDataPtr event_data = PaymentRequestEventData::New();
event_data->top_origin = GURL("https://example.test");
event_data->payment_request_origin = GURL("https://example.test");
event_data->payment_request_id = "payment-request-id";
event_data->method_data.push_back(PaymentMethodData::New());
event_data->method_data[0]->supported_method = supported_method;
event_data->total = PaymentCurrencyAmount::New();
event_data->total->currency = "USD";
event_data->total->value = "55";
PaymentDetailsModifierPtr modifier = PaymentDetailsModifier::New();
modifier->total = PaymentItem::New();
modifier->total->amount = PaymentCurrencyAmount::New();
modifier->total->amount->currency = "USD";
modifier->total->amount->value = "55";
modifier->method_data = PaymentMethodData::New();
modifier->method_data->supported_method = supported_method;
event_data->modifiers.push_back(std::move(modifier));
event_data->instrument_key = instrument_key;
return event_data;
}
std::unique_ptr<net::EmbeddedTestServer> https_server_;
};
IN_PROC_BROWSER_TEST_F(PaymentAppBrowserTest,
AbortPaymentWithInvalidRegistrationId) {
RegisterPaymentApp();
std::vector<int64_t> registrationIds = GetAllPaymentAppRegistrationIDs();
ASSERT_EQ(1U, registrationIds.size());
bool payment_aborted =
AbortPayment(blink::mojom::kInvalidServiceWorkerRegistrationId,
GetTestServerOrigin(), "id");
ASSERT_FALSE(payment_aborted);
ClearStoragePartitionData();
}
IN_PROC_BROWSER_TEST_F(PaymentAppBrowserTest, AbortPayment) {
RegisterPaymentApp();
std::vector<int64_t> registrationIds = GetAllPaymentAppRegistrationIDs();
ASSERT_EQ(1U, registrationIds.size());
bool payment_aborted =
AbortPayment(registrationIds[0], GetTestServerOrigin(), "id");
ASSERT_TRUE(payment_aborted);
ClearStoragePartitionData();
}
IN_PROC_BROWSER_TEST_F(PaymentAppBrowserTest, CanMakePayment) {
RegisterPaymentApp();
std::vector<int64_t> registrationIds = GetAllPaymentAppRegistrationIDs();
ASSERT_EQ(1U, registrationIds.size());
bool can_make_payment = CanMakePaymentWithTestData(
registrationIds[0], GetTestServerOrigin(), "id", "basic-card");
ASSERT_TRUE(can_make_payment);
EXPECT_EQ("undefined", PopConsoleString() /* topOrigin */);
EXPECT_EQ("undefined", PopConsoleString() /* paymentRequestOrigin */);
EXPECT_EQ("undefined", PopConsoleString() /* methodData */);
EXPECT_EQ("undefined", PopConsoleString() /* modifiers */);
ClearStoragePartitionData();
}
IN_PROC_BROWSER_TEST_F(PaymentAppBrowserTest, PaymentAppInvocationAndFailed) {
RegisterPaymentApp();
std::vector<int64_t> registrationIds = GetAllPaymentAppRegistrationIDs();
ASSERT_EQ(1U, registrationIds.size());
// Remove all payment apps and service workers to cause error.
ClearStoragePartitionData();
PaymentHandlerResponsePtr response(
InvokePaymentAppWithTestData(registrationIds[0], GetTestServerOrigin(),
"basic-card", "basic-card-payment-app-id"));
ASSERT_EQ("", response->method_name);
ClearStoragePartitionData();
}
IN_PROC_BROWSER_TEST_F(PaymentAppBrowserTest, PaymentAppInvocation) {
RegisterPaymentApp();
std::vector<int64_t> registrationIds = GetAllPaymentAppRegistrationIDs();
ASSERT_EQ(1U, registrationIds.size());
PaymentHandlerResponsePtr response(
InvokePaymentAppWithTestData(registrationIds[0], GetTestServerOrigin(),
"basic-card", "basic-card-payment-app-id"));
ASSERT_EQ("test", response->method_name);
EXPECT_EQ("https://example.test/", PopConsoleString() /* topOrigin */);
EXPECT_EQ("https://example.test/",
PopConsoleString() /* paymentRequestOrigin */);
EXPECT_EQ("payment-request-id", PopConsoleString() /* paymentRequestId */);
EXPECT_EQ("[{\"supportedMethods\":\"basic-card\"}]",
PopConsoleString() /* methodData */);
EXPECT_EQ(
"{\"currency\":\"USD\","
"\"value\":\"55\"}",
PopConsoleString() /* total */);
EXPECT_EQ(
"[{\"additionalDisplayItems\":[],\"supportedMethods\":\"basic-card\","
"\"total\":{\"amount\":{\"currency\":\"USD\","
"\"value\":\"55\"},\"label\":\"\",\"pending\":false}}"
"]",
PopConsoleString() /* modifiers */);
EXPECT_EQ("basic-card-payment-app-id",
PopConsoleString() /* instrumentKey */);
ClearStoragePartitionData();
registrationIds = GetAllPaymentAppRegistrationIDs();
ASSERT_EQ(0U, registrationIds.size());
}
IN_PROC_BROWSER_TEST_F(PaymentAppBrowserTest, PaymentAppOpenWindowFailed) {
RegisterPaymentApp();
std::vector<int64_t> registrationIds = GetAllPaymentAppRegistrationIDs();
ASSERT_EQ(1U, registrationIds.size());
PaymentHandlerResponsePtr response(InvokePaymentAppWithTestData(
registrationIds[0], GetTestServerOrigin(), "https://bobpay.test",
"bobpay-payment-app-id"));
// InvokePaymentAppCallback returns empty method_name in case of failure, like
// in PaymentRequestRespondWithObserver::OnResponseRejected.
ASSERT_EQ("", response->method_name);
EXPECT_EQ("https://example.test/", PopConsoleString() /* topOrigin */);
EXPECT_EQ("https://example.test/",
PopConsoleString() /* paymentRequestOrigin */);
EXPECT_EQ("payment-request-id", PopConsoleString() /* paymentRequestId */);
EXPECT_EQ("[{\"supportedMethods\":\"https://bobpay.test\"}]",
PopConsoleString() /* methodData */);
EXPECT_EQ("{\"currency\":\"USD\",\"value\":\"55\"}",
PopConsoleString() /* total */);
EXPECT_EQ(
"[{\"additionalDisplayItems\":[],\"supportedMethods\":\"https://"
"bobpay.test\","
"\"total\":{\"amount\":{\"currency\":\"USD\","
"\"value\":\"55\"},\"label\":\"\",\"pending\":false}}"
"]",
PopConsoleString() /* modifiers */);
EXPECT_EQ("bobpay-payment-app-id", PopConsoleString() /* instrumentKey */);
ClearStoragePartitionData();
registrationIds = GetAllPaymentAppRegistrationIDs();
ASSERT_EQ(0U, registrationIds.size());
}
} // namespace content