chromium/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper_unittest.mm

// Copyright 2024 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/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.h"

#import "base/test/scoped_feature_list.h"
#import "components/autofill/ios/browser/autofill_agent.h"
#import "components/autofill/ios/browser/autofill_driver_ios_factory.h"
#import "components/infobars/core/infobar.h"
#import "components/infobars/core/infobar_manager.h"
#import "components/password_manager/core/browser/features/password_features.h"
#import "ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_java_script_feature.h"
#import "ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h"
#import "ios/chrome/browser/infobars/model/infobar_manager_impl.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/public/commands/autofill_commands.h"
#import "ios/chrome/browser/web/model/chrome_web_client.h"
#import "ios/web/public/js_messaging/script_message.h"
#import "ios/web/public/test/scoped_testing_web_client.h"
#import "ios/web/public/test/web_state_test_util.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/OCMock/OCMockMacros.h"
#import "third_party/ocmock/gtest_support.h"

namespace {

// Test fixture to test AutofillBottomSheetTabHelper class.
class AutofillBottomSheetTabHelperTest : public PlatformTest {
 public:
  // Returns a valid form message body to trigger the proactive password
  // generation bottom sheet.
  std::unique_ptr<base::Value> ValidFormMessageBody(std::string frame_id) {
    return std::make_unique<base::Value>(
        base::Value::Dict()
            .Set("formName", "test_form")
            .Set("formRendererID", "1234")
            .Set("fieldIdentifier", "new_password")
            .Set("fieldRendererID", "0")
            .Set("fieldType", "new_password")
            .Set("type", "new_password")
            .Set("value", "new_password")
            .Set("hasUserGesture", "YES")
            .Set("frameID", frame_id));
  }

  // Returns a script message that represents a form.
  web::ScriptMessage ScriptMessageForForm(std::unique_ptr<base::Value> body) {
    return web::ScriptMessage(std::move(body),
                              /*is_user_interacting=*/true,
                              /*is_main_frame=*/true,
                              /*request_url=*/std::nullopt);
  }

 protected:
  class TestAutofillClient : public autofill::ChromeAutofillClientIOS {
   public:
    using ChromeAutofillClientIOS::ChromeAutofillClientIOS;
    autofill::AutofillCrowdsourcingManager* GetCrowdsourcingManager() override {
      return nullptr;
    }
  };

  AutofillBottomSheetTabHelperTest()
      : web_client_(std::make_unique<ChromeWebClient>()) {
    chrome_browser_state_ = TestChromeBrowserState::Builder().Build();

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

    AutofillBottomSheetTabHelper::CreateForWebState(web_state_.get());
    helper_ = AutofillBottomSheetTabHelper::FromWebState(web_state_.get());

    autofill_agent_ = [[AutofillAgent alloc]
        initWithPrefService:chrome_browser_state_->GetPrefs()
                   webState:web_state_.get()];

    InfoBarManagerImpl::CreateForWebState(web_state_.get());
    infobars::InfoBarManager* infobar_manager =
        InfoBarManagerImpl::FromWebState(web_state_.get());

    autofill_client_ = std::make_unique<TestAutofillClient>(
        chrome_browser_state_.get(), web_state_.get(), infobar_manager,
        autofill_agent_);

    autofill::AutofillDriverIOSFactory::CreateForWebState(
        web_state_.get(), autofill_client_.get(), autofill_agent_,
        /*app_locale=*/"en");
  }

  web::WebTaskEnvironment task_environment_;
  web::ScopedTestingWebClient web_client_;
  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
  std::unique_ptr<web::WebState> web_state_;
  AutofillBottomSheetTabHelper* helper_;
  std::unique_ptr<autofill::AutofillClient> autofill_client_;
  AutofillAgent* autofill_agent_;
  base::test::ScopedFeatureList scoped_feature_list_;
};

// Tests that receiving a valid form for password generation triggers the
// proactive password generation bottom sheet.
TEST_F(AutofillBottomSheetTabHelperTest,
       ShowProactivePasswordGenerationBottomSheet) {
  scoped_feature_list_.InitAndEnableFeature(
      password_manager::features::kIOSProactivePasswordGenerationBottomSheet);

  // Using LoadHtml to set up the JavaScript features needed by the
  // AttachListeners function.
  web::test::LoadHtml(@"<html><body></body></html>", web_state_.get());

  autofill::FieldRendererId new_password_rendererID(0);
  const std::vector<autofill::FieldRendererId> renderer_ids = {
      new_password_rendererID};
  web::WebFrame* frame = AutofillBottomSheetJavaScriptFeature::GetInstance()
                             ->GetWebFramesManager(web_state_.get())
                             ->GetMainWebFrame();
  helper_->AttachPasswordGenerationListeners(renderer_ids, frame->GetFrameId());

  id<PasswordGenerationProvider> generation_provider_mock =
      OCMProtocolMock(@protocol(PasswordGenerationProvider));
  autofill::FormRendererId form_renderer_ID(1234);
  OCMExpect([generation_provider_mock
      triggerPasswordGenerationForFormId:form_renderer_ID
                         fieldIdentifier:new_password_rendererID
                                 inFrame:frame
                               proactive:YES]);
  helper_->SetPasswordGenerationProvider(generation_provider_mock);

  id<AutofillCommands> commands_handler =
      OCMProtocolMock(@protocol(AutofillCommands));
  helper_->SetAutofillBottomSheetHandler(commands_handler);

  web::ScriptMessage form_message =
      ScriptMessageForForm(ValidFormMessageBody(frame->GetFrameId()));
  // Using OnFormMessageReceived to emulate receiving a signal from the
  // proactive password generation listeners. This emulates the trigger (the
  // focus on the listened field), so the bottom sheet should show.
  helper_->OnFormMessageReceived(form_message);

  // Verify that the bottom sheet is triggered upon receiving the signal from
  // the proactive password generation listeners.
  EXPECT_OCMOCK_VERIFY(generation_provider_mock);
}

}  // namespace