chromium/ios/chrome/browser/passwords/model/password_tab_helper_unittest.mm

// Copyright 2022 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/passwords/model/password_tab_helper.h"

#import <Foundation/Foundation.h>

#import "base/memory/raw_ptr.h"
#import "base/strings/sys_string_conversions.h"
#import "base/test/bind.h"
#import "base/test/metrics/histogram_tester.h"
#import "components/password_manager/core/browser/manage_passwords_referrer.h"
#import "components/password_manager/core/browser/password_manager_constants.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/commands/settings_commands.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/test/fakes/fake_web_client.h"
#import "ios/web/public/test/fakes/fake_web_state_delegate.h"
#import "ios/web/public/test/scoped_testing_web_client.h"
#import "ios/web/public/test/task_observer_util.h"
#import "ios/web/public/test/web_task_environment.h"
#import "ios/web/public/test/web_view_content_test_util.h"
#import "net/test/embedded_test_server/http_request.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/gtest_support.h"

namespace {

NSString* const kWrongURL = @"https://example.com";

}  // namespace

class PasswordTabHelperTest : public PlatformTest {
 public:
  PasswordTabHelperTest()
      : web_client_(std::make_unique<web::FakeWebClient>()) {
    browser_state_ = TestChromeBrowserState::Builder().Build();

    web::WebState::CreateParams params(browser_state_.get());
    web_state_ = web::WebState::Create(params);

    PasswordTabHelper::CreateForWebState(web_state_.get());
  }

  void SetUp() override {
    PlatformTest::SetUp();

    id dispatcher = [[CommandDispatcher alloc] init];
    id mockSettingsCommandHandler =
        OCMProtocolMock(@protocol(SettingsCommands));
    dispatcher_ = mockSettingsCommandHandler;
    [dispatcher startDispatchingToTarget:mockSettingsCommandHandler
                             forProtocol:@protocol(SettingsCommands)];

    helper_ = PasswordTabHelper::FromWebState(web_state_.get());
    ASSERT_TRUE(helper_);

    helper_->SetDispatcher(dispatcher);
  }

 protected:
  web::ScopedTestingWebClient web_client_;
  web::WebTaskEnvironment task_environment_{
      web::WebTaskEnvironment::MainThreadType::IO};
  std::unique_ptr<TestChromeBrowserState> browser_state_;
  std::unique_ptr<web::WebState> web_state_;
  raw_ptr<PasswordTabHelper> helper_ = nullptr;
  id dispatcher_;
};

TEST_F(PasswordTabHelperTest, RedirectsToPasswordsAndCancelsRequest) {
  base::HistogramTester histogram_tester;
  NSURLRequest* request = [NSURLRequest
      requestWithURL:
          [NSURL URLWithString:base::SysUTF8ToNSString(
                                   password_manager::kManageMyPasswordsURL)]];
  const web::WebStatePolicyDecider::RequestInfo request_info(
      ui::PageTransition::PAGE_TRANSITION_LINK, /*target_frame_is_main=*/true,
      /*target_frame_is_cross_origin=*/false,
      /*target_window_is_cross_origin=*/false,
      /*is_user_initiated=*/false, /*user_tapped_recently=*/false);
  __block bool callback_called = false;
  __block web::WebStatePolicyDecider::PolicyDecision request_policy =
      web::WebStatePolicyDecider::PolicyDecision::Allow();
  auto callback =
      base::BindOnce(^(web::WebStatePolicyDecider::PolicyDecision decision) {
        request_policy = decision;
        callback_called = true;
      });

  OCMExpect([dispatcher_ showSavedPasswordsSettingsFromViewController:nil
                                                     showCancelButton:NO]);

  helper_->ShouldAllowRequest(request, request_info, std::move(callback));

  EXPECT_OCMOCK_VERIFY(dispatcher_);
  EXPECT_TRUE(callback_called);
  EXPECT_TRUE(request_policy.ShouldCancelNavigation());
  histogram_tester.ExpectBucketCount(
      "PasswordManager.ManagePasswordsReferrer",
      password_manager::ManagePasswordsReferrer::kPasswordsGoogleWebsite, 1);
}

TEST_F(PasswordTabHelperTest, NoRedirectWhenWrongLink) {
  base::HistogramTester histogram_tester;
  NSURLRequest* request =
      [NSURLRequest requestWithURL:[NSURL URLWithString:kWrongURL]];
  const web::WebStatePolicyDecider::RequestInfo request_info(
      ui::PageTransition::PAGE_TRANSITION_LINK, /*target_frame_is_main=*/true,
      /*target_frame_is_cross_origin=*/false,
      /*target_window_is_cross_origin=*/false,
      /*is_user_initiated=*/false, /*user_tapped_recently=*/false);
  __block bool callback_called = false;
  __block web::WebStatePolicyDecider::PolicyDecision request_policy =
      web::WebStatePolicyDecider::PolicyDecision::Allow();
  auto callback =
      base::BindOnce(^(web::WebStatePolicyDecider::PolicyDecision decision) {
        request_policy = decision;
        callback_called = true;
      });

  helper_->ShouldAllowRequest(request, request_info, std::move(callback));

  EXPECT_OCMOCK_VERIFY(dispatcher_);
  EXPECT_TRUE(callback_called);
  EXPECT_FALSE(request_policy.ShouldCancelNavigation());
  histogram_tester.ExpectTotalCount("PasswordManager.ManagePasswordsReferrer",
                                    0);
}

TEST_F(PasswordTabHelperTest, NoRedirectWhenWrongTransition) {
  base::HistogramTester histogram_tester;
  NSURLRequest* request = [NSURLRequest
      requestWithURL:
          [NSURL URLWithString:base::SysUTF8ToNSString(
                                   password_manager::kManageMyPasswordsURL)]];
  const web::WebStatePolicyDecider::RequestInfo request_info(
      ui::PageTransition::PAGE_TRANSITION_TYPED, /*target_frame_is_main=*/true,
      /*target_frame_is_cross_origin=*/false,
      /*target_window_is_cross_origin=*/false,
      /*is_user_initiated=*/false, /*user_tapped_recently=*/false);
  __block bool callback_called = false;
  __block web::WebStatePolicyDecider::PolicyDecision request_policy =
      web::WebStatePolicyDecider::PolicyDecision::Allow();
  auto callback =
      base::BindOnce(^(web::WebStatePolicyDecider::PolicyDecision decision) {
        request_policy = decision;
        callback_called = true;
      });

  helper_->ShouldAllowRequest(request, request_info, std::move(callback));

  EXPECT_OCMOCK_VERIFY(dispatcher_);
  EXPECT_TRUE(callback_called);
  EXPECT_FALSE(request_policy.ShouldCancelNavigation());
  histogram_tester.ExpectTotalCount("PasswordManager.ManagePasswordsReferrer",
                                    0);
}