chromium/chrome/browser/touch_to_fill/password_manager/no_passkeys/android/no_passkeys_bottom_sheet_bridge_unittest.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 "chrome/browser/touch_to_fill/password_manager/no_passkeys/android/no_passkeys_bottom_sheet_bridge.h"

#include <jni.h>

#include "base/functional/callback_helpers.h"
#include "base/test/mock_callback.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/android/window_android.h"

namespace {

using testing::_;
using testing::InvokeWithoutArgs;
using testing::Sequence;
using JniDelegate = NoPasskeysBottomSheetBridge::JniDelegate;

class MockJniDelegate : public JniDelegate {
 public:
  MockJniDelegate() = default;
  ~MockJniDelegate() override = default;

  MOCK_METHOD((void), Create, (ui::WindowAndroid*), (override));
  MOCK_METHOD((void), Show, (const std::string&), (override));
  MOCK_METHOD((void), Dismiss, (), (override));
};

}  // namespace

class NoPasskeysBottomSheetBridgeTest : public testing::Test {
 public:
  void SetUp() override {
    auto jni_delegate = std::make_unique<MockJniDelegate>();
    mock_jni_delegate_ = jni_delegate.get();
    no_passkeys_bridge_ = std::make_unique<NoPasskeysBottomSheetBridge>(
        base::PassKey<class NoPasskeysBottomSheetBridgeTest>(),
        std::move(jni_delegate));
  }

  MockJniDelegate& mock_jni_delegate() { return *mock_jni_delegate_; }

  NoPasskeysBottomSheetBridge& no_passkeys_bridge() {
    return *no_passkeys_bridge_;
  }

  void destroyNoPassleysBridge() { no_passkeys_bridge_.reset(); }

 private:
  raw_ptr<MockJniDelegate> mock_jni_delegate_;
  std::unique_ptr<NoPasskeysBottomSheetBridge> no_passkeys_bridge_;
};

TEST_F(NoPasskeysBottomSheetBridgeTest, CallDismissalDelegateOnHide) {
  auto scoped_window = ui::WindowAndroid::CreateForTesting();
  base::MockCallback<base::OnceClosure> on_dismissed_callback;
  const std::string kTestOrigin("origin.com");

  {  // Ensure the Show call creates a new bridge before showing.
    Sequence s;
    EXPECT_CALL(mock_jni_delegate(), Create);
    EXPECT_CALL(mock_jni_delegate(), Show(kTestOrigin));
  }
  no_passkeys_bridge().Show(scoped_window->get(), kTestOrigin,
                            on_dismissed_callback.Get(), base::DoNothing());

  EXPECT_CALL(on_dismissed_callback, Run);
  no_passkeys_bridge().OnDismissed(/*env=*/nullptr);
}

TEST_F(NoPasskeysBottomSheetBridgeTest, IgnoreRedundantDismissCalls) {
  auto scoped_window = ui::WindowAndroid::CreateForTesting();
  const std::string kTestOrigin("origin.com");

  {  // Ensure the Show call creates a new bridge before showing.
    Sequence s;
    EXPECT_CALL(mock_jni_delegate(), Create);
    EXPECT_CALL(mock_jni_delegate(), Show(kTestOrigin));
  }
  no_passkeys_bridge().Show(
      scoped_window->get(), kTestOrigin,
      /*on_dismissed_callback=*/base::DoNothing(),
      /*on_click_use_another_device_callback=*/base::DoNothing());

  EXPECT_CALL(mock_jni_delegate(), Dismiss)
      .WillOnce(InvokeWithoutArgs(
          [this]() { no_passkeys_bridge().OnDismissed(/*env=*/nullptr); }));
  no_passkeys_bridge().Dismiss();
  no_passkeys_bridge().Dismiss();  // This should not trigger a second call!
  destroyNoPassleysBridge();  // This also should not trigger a second call!
}

TEST_F(NoPasskeysBottomSheetBridgeTest, RunCallbackForOnClickUseAnotherDevice) {
  auto scoped_window = ui::WindowAndroid::CreateForTesting();
  base::MockCallback<base::OnceClosure> on_click_use_another_device_callback;
  const std::string kTestOrigin("origin.com");

  no_passkeys_bridge().Show(scoped_window->get(), kTestOrigin,
                            /*on_dismissed_callback=*/base::DoNothing(),
                            on_click_use_another_device_callback.Get());

  EXPECT_CALL(on_click_use_another_device_callback, Run);
  no_passkeys_bridge().OnClickUseAnotherDevice(/*env=*/nullptr);
}

TEST_F(NoPasskeysBottomSheetBridgeTest, DismissesOnDestruction) {
  auto scoped_window = ui::WindowAndroid::CreateForTesting();
  const std::string kTestOrigin("origin.com");

  no_passkeys_bridge().Show(
      scoped_window->get(), kTestOrigin,
      /*on_dismissed_callback=*/base::DoNothing(),
      /*on_click_use_another_device_callback=*/base::DoNothing());

  EXPECT_CALL(mock_jni_delegate(), Dismiss);
  destroyNoPassleysBridge();
}