| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/payments/content/payment_app.h" |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "components/payments/content/service_worker_payment_app.h" |
| #include "content/public/browser/stored_payment_app.h" |
| #include "content/public/browser/supported_delegations.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/test_browser_context.h" |
| #include "content/public/test/test_web_contents_factory.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/mojom/payments/payment_request.mojom.h" |
| |
| namespace payments { |
| |
| namespace { |
| |
| enum class RequiredPaymentOptions { |
| // None of the shipping address or contact information is required. |
| kNone, |
| // Shipping Address is required. |
| kShippingAddress, |
| // Payer's contact information(phone, name, email) is required. |
| kContactInformation, |
| // Payer's email is required. |
| kPayerEmail, |
| // Both contact information and shipping address are required. |
| kContactInformationAndShippingAddress, |
| }; |
| |
| static const RequiredPaymentOptions kRequiredPaymentOptionsValues[]{ |
| RequiredPaymentOptions::kNone, RequiredPaymentOptions::kShippingAddress, |
| RequiredPaymentOptions::kContactInformation, |
| RequiredPaymentOptions::kPayerEmail, |
| RequiredPaymentOptions::kContactInformationAndShippingAddress}; |
| |
| } // namespace |
| |
| class PaymentAppTest : public testing::TestWithParam<RequiredPaymentOptions>, |
| public PaymentRequestSpec::Observer { |
| public: |
| PaymentAppTest(const PaymentAppTest&) = delete; |
| PaymentAppTest& operator=(const PaymentAppTest&) = delete; |
| |
| protected: |
| PaymentAppTest() : required_options_(GetParam()) { |
| CreateSpec(); |
| web_contents_ = |
| test_web_contents_factory_.CreateWebContents(&browser_context_); |
| } |
| |
| std::unique_ptr<ServiceWorkerPaymentApp> CreateServiceWorkerPaymentApp( |
| bool can_preselect, |
| bool handles_shipping, |
| bool handles_name, |
| bool handles_phone, |
| bool handles_email) { |
| std::unique_ptr<content::StoredPaymentApp> stored_app = |
| std::make_unique<content::StoredPaymentApp>(); |
| stored_app->registration_id = 123456; |
| stored_app->scope = GURL("https://bobpay.test"); |
| stored_app->name = "bobpay"; |
| stored_app->icon = std::make_unique<SkBitmap>(); |
| if (can_preselect) { |
| PopulateIcon(stored_app->icon.get()); |
| } |
| if (handles_shipping) { |
| stored_app->supported_delegations.shipping_address = true; |
| } |
| if (handles_name) { |
| stored_app->supported_delegations.payer_name = true; |
| } |
| if (handles_phone) { |
| stored_app->supported_delegations.payer_phone = true; |
| } |
| if (handles_email) { |
| stored_app->supported_delegations.payer_email = true; |
| } |
| |
| return std::make_unique<ServiceWorkerPaymentApp>( |
| web_contents_, GURL("https://testmerchant.com"), |
| GURL("https://testmerchant.com/bobpay"), spec_->AsWeakPtr(), |
| std::move(stored_app), /*is_incognito=*/false, |
| /*show_processing_spinner=*/base::DoNothing()); |
| } |
| |
| static void PopulateIcon(SkBitmap* icon) { |
| constexpr int kBitmapDimension = 16; |
| icon->allocN32Pixels(kBitmapDimension, kBitmapDimension); |
| icon->eraseColor(SK_ColorRED); |
| } |
| |
| RequiredPaymentOptions required_options() const { return required_options_; } |
| |
| private: |
| // PaymentRequestSpec::Observer |
| void OnSpecUpdated() override {} |
| |
| void CreateSpec() { |
| std::vector<mojom::PaymentMethodDataPtr> method_data; |
| mojom::PaymentOptionsPtr payment_options = mojom::PaymentOptions::New(); |
| switch (required_options_) { |
| case RequiredPaymentOptions::kNone: |
| break; |
| case RequiredPaymentOptions::kShippingAddress: |
| payment_options->request_shipping = true; |
| break; |
| case RequiredPaymentOptions::kContactInformation: |
| payment_options->request_payer_name = true; |
| payment_options->request_payer_email = true; |
| payment_options->request_payer_phone = true; |
| break; |
| case RequiredPaymentOptions::kPayerEmail: |
| payment_options->request_payer_email = true; |
| break; |
| case RequiredPaymentOptions::kContactInformationAndShippingAddress: |
| payment_options->request_shipping = true; |
| payment_options->request_payer_name = true; |
| payment_options->request_payer_email = true; |
| payment_options->request_payer_phone = true; |
| break; |
| } |
| spec_ = std::make_unique<PaymentRequestSpec>( |
| std::move(payment_options), mojom::PaymentDetails::New(), |
| std::move(method_data), weak_ptr_factory_.GetWeakPtr(), "en-US"); |
| } |
| |
| content::BrowserTaskEnvironment task_environment_; |
| content::TestBrowserContext browser_context_; |
| content::TestWebContentsFactory test_web_contents_factory_; |
| raw_ptr<content::WebContents> web_contents_; |
| RequiredPaymentOptions required_options_; |
| std::unique_ptr<PaymentRequestSpec> spec_; |
| base::WeakPtrFactory<PaymentAppTest> weak_ptr_factory_{this}; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| PaymentAppTest, |
| ::testing::ValuesIn(kRequiredPaymentOptionsValues)); |
| |
| TEST_P(PaymentAppTest, SortApps) { |
| std::vector<PaymentApp*> apps; |
| |
| // Add a non-preselectable sw based payment app. |
| std::unique_ptr<ServiceWorkerPaymentApp> non_preselectable_sw_app = |
| CreateServiceWorkerPaymentApp( |
| false /* = can_preselect */, false /* = handles_shipping */, |
| false /* = handles_name */, false /* = handles_phone */, |
| false /* = handles_email */); |
| apps.push_back(non_preselectable_sw_app.get()); |
| |
| // Add a preselectable sw based payment app. |
| std::unique_ptr<ServiceWorkerPaymentApp> preselectable_sw_app = |
| CreateServiceWorkerPaymentApp( |
| true /* = can_preselect */, false /* = handles_shipping */, |
| false /* = handles_name */, false /* = handles_phone */, |
| false /* = handles_email */); |
| apps.push_back(preselectable_sw_app.get()); |
| |
| // Sort the apps and validate the new order. |
| PaymentApp::SortApps(&apps); |
| size_t i = 0; |
| EXPECT_EQ(apps[i++], preselectable_sw_app.get()); |
| EXPECT_EQ(apps[i++], non_preselectable_sw_app.get()); |
| } |
| |
| TEST_P(PaymentAppTest, SortAppsBasedOnSupportedDelegations) { |
| std::vector<PaymentApp*> apps; |
| // Add a preselectable sw based payment app which does not support |
| // shipping or contact delegation. |
| std::unique_ptr<ServiceWorkerPaymentApp> does_not_support_delegations = |
| CreateServiceWorkerPaymentApp( |
| true /* = can_preselect */, false /* = handles_shipping */, |
| false /* = handles_name */, false /* = handles_phone */, |
| false /* = handles_email */); |
| apps.push_back(does_not_support_delegations.get()); |
| |
| // Add a preselectable sw based payment app which handles shipping. |
| std::unique_ptr<ServiceWorkerPaymentApp> handles_shipping_address = |
| CreateServiceWorkerPaymentApp( |
| true /* = can_preselect */, true /* = handles_shipping */, |
| false /* = handles_name */, false /* = handles_phone */, |
| false /* = handles_email */); |
| apps.push_back(handles_shipping_address.get()); |
| |
| // Add a preselectable sw based payment app which handles payer's |
| // email. |
| std::unique_ptr<ServiceWorkerPaymentApp> handles_payer_email = |
| CreateServiceWorkerPaymentApp( |
| true /* = can_preselect */, false /* = handles_shipping */, |
| false /* = handles_name */, false /* = handles_phone */, |
| true /* = handles_email */); |
| apps.push_back(handles_payer_email.get()); |
| |
| // Add a preselectable sw based payment app which handles contact |
| // information. |
| std::unique_ptr<ServiceWorkerPaymentApp> handles_contact_info = |
| CreateServiceWorkerPaymentApp( |
| true /* = can_preselect */, false /* = handles_shipping */, |
| true /* = handles_name */, true /* = handles_phone */, |
| true /* = handles_email */); |
| apps.push_back(handles_contact_info.get()); |
| |
| // Add a preselectable sw based payment app which handles both shipping |
| // address and contact information. |
| std::unique_ptr<ServiceWorkerPaymentApp> handles_shipping_and_contact = |
| CreateServiceWorkerPaymentApp( |
| true /* = can_preselect */, true /* = handles_shipping */, |
| true /* = handles_name */, true /* = handles_phone */, |
| true /* = handles_email */); |
| apps.push_back(handles_shipping_and_contact.get()); |
| |
| PaymentApp::SortApps(&apps); |
| size_t i = 0; |
| |
| switch (required_options()) { |
| case RequiredPaymentOptions::kNone: { |
| // When no payment option is required the order of the payment apps |
| // does not change. |
| EXPECT_EQ(apps[i++], does_not_support_delegations.get()); |
| EXPECT_EQ(apps[i++], handles_shipping_address.get()); |
| EXPECT_EQ(apps[i++], handles_payer_email.get()); |
| EXPECT_EQ(apps[i++], handles_contact_info.get()); |
| EXPECT_EQ(apps[i++], handles_shipping_and_contact.get()); |
| break; |
| } |
| case RequiredPaymentOptions::kShippingAddress: { |
| // apps that handle shipping address come first. |
| EXPECT_EQ(apps[i++], handles_shipping_address.get()); |
| EXPECT_EQ(apps[i++], handles_shipping_and_contact.get()); |
| // The order is unchanged for apps that do not handle shipping. |
| EXPECT_EQ(apps[i++], does_not_support_delegations.get()); |
| EXPECT_EQ(apps[i++], handles_payer_email.get()); |
| EXPECT_EQ(apps[i++], handles_contact_info.get()); |
| break; |
| } |
| case RequiredPaymentOptions::kContactInformation: { |
| // apps that handle all required contact information come first. |
| EXPECT_EQ(apps[i++], handles_contact_info.get()); |
| EXPECT_EQ(apps[i++], handles_shipping_and_contact.get()); |
| // The app that partially handles contact information comes next. |
| EXPECT_EQ(apps[i++], handles_payer_email.get()); |
| // The order for apps that do not handle contact information is not |
| // changed. |
| EXPECT_EQ(apps[i++], does_not_support_delegations.get()); |
| EXPECT_EQ(apps[i++], handles_shipping_address.get()); |
| break; |
| } |
| case RequiredPaymentOptions::kPayerEmail: { |
| EXPECT_EQ(apps[i++], handles_payer_email.get()); |
| EXPECT_EQ(apps[i++], handles_contact_info.get()); |
| EXPECT_EQ(apps[i++], handles_shipping_and_contact.get()); |
| EXPECT_EQ(apps[i++], does_not_support_delegations.get()); |
| EXPECT_EQ(apps[i++], handles_shipping_address.get()); |
| break; |
| } |
| case RequiredPaymentOptions::kContactInformationAndShippingAddress: { |
| EXPECT_EQ(apps[i++], handles_shipping_and_contact.get()); |
| EXPECT_EQ(apps[i++], handles_shipping_address.get()); |
| EXPECT_EQ(apps[i++], handles_contact_info.get()); |
| EXPECT_EQ(apps[i++], handles_payer_email.get()); |
| EXPECT_EQ(apps[i++], does_not_support_delegations.get()); |
| break; |
| } |
| } |
| } |
| |
| } // namespace payments |