chromium/chrome/browser/payments/webapps/payment_request_lacros_browsertest.cc

// Copyright 2023 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 <utility>

#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/apps/app_service/app_registry_cache_waiter.h"
#include "chrome/browser/payments/chrome_payment_request_delegate.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/user_display_mode.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chromeos/crosapi/mojom/web_app_service.mojom.h"
#include "chromeos/lacros/lacros_service.h"
#include "components/payments/core/features.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_mock_cert_verifier.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace {

using base::test::RunOnceCallback;
using testing::_;

constexpr char kTestAppHost[] = "payments.example.com";
constexpr char kTestAppPackageName[] = "com.example.app";
const std::u16string kTestAppTitle = u"Test App";

class MockWebAppService : public crosapi::mojom::WebAppService {
 public:
  MockWebAppService() = default;
  MockWebAppService(const MockWebAppService&) = delete;
  MockWebAppService& operator=(const MockWebAppService&) = delete;
  ~MockWebAppService() override = default;

  // crosapi::mojom::WebAppService:
  MOCK_METHOD(void,
              RegisterWebAppProviderBridge,
              (mojo::PendingRemote<crosapi::mojom::WebAppProviderBridge>
                   web_app_provider_bridge),
              (override));
  MOCK_METHOD(void,
              GetAssociatedAndroidPackage,
              (const std::string& web_app_id,
               GetAssociatedAndroidPackageCallback callback),
              (override));
  MOCK_METHOD(void,
              MigrateLauncherState,
              (const std::string& from_app_id,
               const std::string& to_app_id,
               MigrateLauncherStateCallback callback),
              (override));
};

crosapi::mojom::WebAppAndroidPackagePtr CreateWebAppAndroidPackage() {
  auto result = crosapi::mojom::WebAppAndroidPackage::New();
  result->package_name = kTestAppPackageName;
  return result;
}

}  // namespace

class PaymentRequestLacrosBrowserTest
    : public web_app::WebAppBrowserTestBase,
      public testing::WithParamInterface<bool> {
 public:
  PaymentRequestLacrosBrowserTest() : has_associated_package_(GetParam()) {}
  ~PaymentRequestLacrosBrowserTest() override = default;

  // web_app::WebAppBrowserTestBase:
  void SetUpOnMainThread() override {
    web_app::WebAppBrowserTestBase::SetUpOnMainThread();

    chromeos::LacrosService* service = chromeos::LacrosService::Get();
    ASSERT_TRUE(service);
    ASSERT_TRUE(service->IsAvailable<crosapi::mojom::WebAppService>());

    // Replace the production web app service with a mock for testing.
    mojo::Remote<crosapi::mojom::WebAppService>& remote =
        service->GetRemote<crosapi::mojom::WebAppService>();
    remote.reset();
    receiver_.Bind(remote.BindNewPipeAndPassReceiver());

    if (has_associated_package_) {
      EXPECT_CALL(service_, GetAssociatedAndroidPackage(_, _))
          .WillRepeatedly([](const std::string& web_app_id,
                             crosapi::mojom::WebAppService::
                                 GetAssociatedAndroidPackageCallback callback) {
            std::move(callback).Run(CreateWebAppAndroidPackage());
          });
    } else {
      EXPECT_CALL(service_, GetAssociatedAndroidPackage(_, _))
          .WillRepeatedly(base::test::RunOnceCallbackRepeatedly<1>(nullptr));
    }

    InstallTestApp();
  }

  void TearDownOnMainThread() override {
    UninstallTestApp();
    web_app::WebAppBrowserTestBase::TearDownOnMainThread();
  }

  GURL GetAppURL() {
    return https_server()->GetURL(kTestAppHost, "/simple.html");
  }

  const webapps::AppId& app_id() const { return app_id_; }

 protected:
  const bool has_associated_package_;

 private:
  void InstallTestApp() {
    auto app_info =
        web_app::WebAppInstallInfo::CreateWithStartUrlForTesting(GetAppURL());
    app_info->scope = app_info->start_url().GetWithoutFilename();
    app_info->title = kTestAppTitle;
    app_info->user_display_mode = web_app::mojom::UserDisplayMode::kStandalone;

    app_id_ =
        web_app::test::InstallWebApp(browser()->profile(), std::move(app_info));
    apps::AppReadinessWaiter(browser()->profile(), app_id_).Await();
  }

  void UninstallTestApp() {
    web_app::test::UninstallWebApp(browser()->profile(), app_id_);
    apps::AppReadinessWaiter(browser()->profile(), app_id_,
                             apps::Readiness::kUninstalledByUser)
        .Await();
  }

  base::test::ScopedFeatureList scoped_feature_list_{
      payments::features::kAppStoreBilling};

  testing::NiceMock<MockWebAppService> service_;
  mojo::Receiver<crosapi::mojom::WebAppService> receiver_{&service_};

  webapps::AppId app_id_;
};

IN_PROC_BROWSER_TEST_P(PaymentRequestLacrosBrowserTest, BrowserTab) {
  web_app::NavigateViaLinkClickToURLAndWait(browser(), GetAppURL());
  content::WebContents* contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  content::RenderFrameHost* render_frame_host = contents->GetPrimaryMainFrame();
  payments::ChromePaymentRequestDelegate delegate(render_frame_host);

  base::RunLoop run_loop;
  delegate.GetTwaPackageName(base::BindLambdaForTesting(
      [&run_loop](const std::string& twa_package_name) {
        EXPECT_TRUE(twa_package_name.empty());
        run_loop.Quit();
      }));
  run_loop.Run();
}

IN_PROC_BROWSER_TEST_P(PaymentRequestLacrosBrowserTest, AppWindow) {
  Browser* app_browser =
      web_app::LaunchWebAppBrowserAndWait(browser()->profile(), app_id());
  content::WebContents* contents =
      app_browser->tab_strip_model()->GetActiveWebContents();
  content::RenderFrameHost* render_frame_host = contents->GetPrimaryMainFrame();
  payments::ChromePaymentRequestDelegate delegate(render_frame_host);

  base::RunLoop run_loop;
  delegate.GetTwaPackageName(base::BindLambdaForTesting(
      [this, &run_loop](const std::string& twa_package_name) {
        if (has_associated_package_) {
          EXPECT_EQ(twa_package_name, kTestAppPackageName);
        } else {
          EXPECT_TRUE(twa_package_name.empty());
        }

        run_loop.Quit();
      }));
  run_loop.Run();
  web_app::CloseAndWait(app_browser);
}

IN_PROC_BROWSER_TEST_P(PaymentRequestLacrosBrowserTest, OutOfScope) {
  Browser* app_browser =
      web_app::LaunchWebAppBrowserAndWait(browser()->profile(), app_id());
  web_app::NavigateViaLinkClickToURLAndWait(
      app_browser, https_server()->GetURL("another.test.site", "/"));
  content::WebContents* contents =
      app_browser->tab_strip_model()->GetActiveWebContents();
  content::RenderFrameHost* render_frame_host = contents->GetPrimaryMainFrame();
  payments::ChromePaymentRequestDelegate delegate(render_frame_host);

  base::RunLoop run_loop;
  delegate.GetTwaPackageName(base::BindLambdaForTesting(
      [&run_loop](const std::string& twa_package_name) {
        EXPECT_TRUE(twa_package_name.empty());
        run_loop.Quit();
      }));
  run_loop.Run();
  web_app::CloseAndWait(app_browser);
}

INSTANTIATE_TEST_SUITE_P(All, PaymentRequestLacrosBrowserTest, testing::Bool());