chromium/ios/chrome/browser/infobars/model/overlays/browser_agent/infobar_overlay_browser_agent_unittest.mm

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/infobars/model/overlays/browser_agent/infobar_overlay_browser_agent.h"

#import <map>

#import "base/memory/raw_ptr.h"
#import "ios/chrome/browser/infobars/model/overlays/browser_agent/interaction_handlers/test/mock_infobar_interaction_handler.h"
#import "ios/chrome/browser/infobars/model/overlays/infobar_overlay_type.h"
#import "ios/chrome/browser/infobars/model/test/fake_infobar_ios.h"
#import "ios/chrome/browser/overlays/model/public/common/infobars/infobar_overlay_request_config.h"
#import "ios/chrome/browser/overlays/model/public/overlay_callback_manager.h"
#import "ios/chrome/browser/overlays/model/public/overlay_presenter.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request_queue.h"
#import "ios/chrome/browser/overlays/model/public/overlay_response.h"
#import "ios/chrome/browser/overlays/model/test/fake_overlay_presentation_context.h"
#import "ios/chrome/browser/overlays/model/test/fake_overlay_request_callback_installer.h"
#import "ios/chrome/browser/overlays/model/test/overlay_test_macros.h"
#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_opener.h"
#import "ios/web/public/test/fakes/fake_web_state.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/gmock/include/gmock/gmock.h"
#import "testing/platform_test.h"

using testing::ByMove;
using testing::Return;

namespace {
// Fake dispatch response InfoType for use in tests.
DEFINE_TEST_OVERLAY_RESPONSE_INFO(DispatchInfo);

// Fake OverlayRequestSupport that supports requests configured with
// InfobarOverlayRequestConfigs with a specified InfobarOverlayType.
class FakeInfobarOverlayRequestSupport : public OverlayRequestSupport {
 public:
  FakeInfobarOverlayRequestSupport(InfobarOverlayType overlay_type)
      : overlay_type_(overlay_type) {}
  FakeInfobarOverlayRequestSupport(FakeInfobarOverlayRequestSupport&& other)
      : overlay_type_(other.overlay_type_) {}

  bool IsRequestSupported(OverlayRequest* request) const override {
    InfobarOverlayRequestConfig* config =
        request->GetConfig<InfobarOverlayRequestConfig>();
    return config && config->overlay_type() == overlay_type_;
  }

 private:
  InfobarOverlayType overlay_type_;
};
}  // namespace

// Test fixture for InfobarOverlayBrowserAgent.
class InfobarOverlayBrowserAgentTest
    : public testing::TestWithParam<InfobarOverlayType> {
 public:
  InfobarOverlayBrowserAgentTest()
      : browser_state_(TestChromeBrowserState::Builder().Build()),
        browser_(std::make_unique<TestBrowser>(browser_state_.get())),
        interaction_handler_builder_(InfobarType::kInfobarTypeConfirm) {
    // Add an activated WebState into whose queues infobar OverlayRequests will
    // be added.
    auto web_state = std::make_unique<web::FakeWebState>();
    web_state_ = web_state.get();
    browser_->GetWebStateList()->InsertWebState(
        std::move(web_state),
        WebStateList::InsertionParams::Automatic().Activate());
    // Set up the OverlayPresenter's presentation context so that presentation
    // can be faked.
    presenter()->SetPresentationContext(&presentation_context_);
    // Create the request supports for each InfobarOverlayType.
    request_supports_.emplace(
        InfobarOverlayType::kBanner,
        FakeInfobarOverlayRequestSupport(InfobarOverlayType::kBanner));
    request_supports_.emplace(
        InfobarOverlayType::kModal,
        FakeInfobarOverlayRequestSupport(InfobarOverlayType::kModal));
    // Create the interaction handler and set up the mock handlers to return
    // fake callback installers.
    std::unique_ptr<InfobarInteractionHandler> interaction_handler =
        std::move(interaction_handler_builder_).Build();
    EXPECT_CALL(*mock_handler(InfobarOverlayType::kBanner), CreateInstaller())
        .WillOnce(Return(ByMove(CreateInstaller(InfobarOverlayType::kBanner))));
    EXPECT_CALL(*mock_handler(InfobarOverlayType::kModal), CreateInstaller())
        .WillOnce(Return(ByMove(CreateInstaller(InfobarOverlayType::kModal))));
    // Set up the browser agent and mock interaction handler.
    InfobarOverlayBrowserAgent::CreateForBrowser(browser_.get());
    InfobarOverlayBrowserAgent::FromBrowser(browser_.get())
        ->AddInfobarInteractionHandler(std::move(interaction_handler));
  }
  ~InfobarOverlayBrowserAgentTest() override {
    presenter()->SetPresentationContext(nullptr);
  }

  // Creates the OverlayRequestCallbackInstaller to return from
  // CreateInstaller() for the mock interaction handler for `overlay_type`.
  // Returned installers forwards callbacks to the receivers in
  // `mock_callback_receivers_`.
  std::unique_ptr<FakeOverlayRequestCallbackInstaller> CreateInstaller(
      InfobarOverlayType overlay_type) {
    std::unique_ptr<FakeOverlayRequestCallbackInstaller> installer =
        std::make_unique<FakeOverlayRequestCallbackInstaller>(
            &mock_callback_receivers_[overlay_type],
            std::set<const OverlayResponseSupport*>(
                {DispatchInfo::ResponseSupport()}));
    installer->set_request_support(&request_supports_.at(overlay_type));
    return installer;
  }

  // Creates an OverlayRequest configured with an InfobarOverlayRequestConfig
  // that has the same InfobarOverlayType as the test fixture.
  std::unique_ptr<OverlayRequest> CreateRequest() {
    return OverlayRequest::CreateWithConfig<InfobarOverlayRequestConfig>(
        &infobar_, GetParam(), false);
  }

  OverlayModality modality() {
    return GetParam() == InfobarOverlayType::kBanner
               ? OverlayModality::kInfobarBanner
               : OverlayModality::kInfobarModal;
  }
  OverlayPresenter* presenter() {
    return OverlayPresenter::FromBrowser(browser_.get(), modality());
  }
  OverlayRequestQueue* queue() {
    return OverlayRequestQueue::FromWebState(web_state_, modality());
  }
  MockInfobarInteractionHandler::Handler* mock_handler(
      InfobarOverlayType overlay_type) {
    return interaction_handler_builder_.mock_handler(overlay_type);
  }
  MockInfobarInteractionHandler::Handler* mock_handler() {
    return mock_handler(GetParam());
  }
  MockOverlayRequestCallbackReceiver* mock_callback_receiver() {
    return &mock_callback_receivers_[GetParam()];
  }

 protected:
  web::WebTaskEnvironment task_environment_;
  std::unique_ptr<ChromeBrowserState> browser_state_;
  std::unique_ptr<TestBrowser> browser_;
  raw_ptr<web::WebState> web_state_ = nullptr;
  std::map<InfobarOverlayType, FakeInfobarOverlayRequestSupport>
      request_supports_;
  std::map<InfobarOverlayType, MockOverlayRequestCallbackReceiver>
      mock_callback_receivers_;
  MockInfobarInteractionHandler::Builder interaction_handler_builder_;
  FakeOverlayPresentationContext presentation_context_;
  FakeInfobarIOS infobar_;
};

// Tests the overlay presentation flow for a given InfobarOverlayType.
TEST_P(InfobarOverlayBrowserAgentTest, OverlayPresentation) {
  // Add a supported infobar request to the queue, expecting
  // MockInfobarBannerInteractionHandler::Handler::InfobarVisibilityChanged() to
  // be called.
  std::unique_ptr<OverlayRequest> added_request = CreateRequest();
  OverlayRequest* request = added_request.get();
  EXPECT_CALL(*mock_handler(),
              InfobarVisibilityChanged(&infobar_, /*visible=*/true));
  queue()->AddRequest(std::move(added_request));
  // Verify that dispatched responses sent through `request`'s callback manager
  // are received by the expected receiver.
  EXPECT_CALL(*mock_callback_receiver(),
              DispatchCallback(request, DispatchInfo::ResponseSupport()));
  request->GetCallbackManager()->DispatchResponse(
      OverlayResponse::CreateWithInfo<DispatchInfo>());
  // Simulate dismissal of the request's UI, expecting
  // MockInfobarBannerInteractionHandler::Handler::InfobarVisibilityChanged()
  // and MockOverlayRequestCallbackReceiver::CompletionCallback() to
  // be called.
  EXPECT_CALL(*mock_handler(),
              InfobarVisibilityChanged(&infobar_, /*visible=*/false));
  EXPECT_CALL(*mock_callback_receiver(), CompletionCallback(request));
  presentation_context_.SimulateDismissalForRequest(
      request, OverlayDismissalReason::kUserInteraction);
}

INSTANTIATE_TEST_SUITE_P(/* No InstantiationName */,
                         InfobarOverlayBrowserAgentTest,
                         testing::Values(InfobarOverlayType::kBanner,
                                         InfobarOverlayType::kModal));