chromium/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_mediator_unittest.mm

// 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.

#import "ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_mediator.h"

#import "base/strings/sys_string_conversions.h"
#import "components/password_manager/core/common/password_manager_pref_names.h"
#import "components/prefs/pref_registry_simple.h"
#import "components/prefs/testing_pref_service.h"
#import "ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_constants.h"
#import "ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_consumer.h"
#import "ios/chrome/browser/promos_manager/model/mock_promos_manager.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/public/commands/credential_provider_promo_commands.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
#import "ios/public/provider/chrome/browser/branded_images/branded_images_api.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/gtest_support.h"
#import "ui/base/l10n/l10n_util.h"

namespace {
NSString* const kFirstStepAnimation = @"CPE_promo_animation_edu_autofill";
NSString* const kLearnMoreAnimation = @"CPE_promo_animation_edu_how_to_enable";
UIImage* kImage = ios::provider::GetBrandedImage(
    ios::provider::BrandedImage::kPasswordSuggestionKey);

NSString* LearnMoreSubtitleString() {
  if (@available(iOS 16, *)) {
    return l10n_util::GetNSStringF(
        IDS_IOS_CREDENTIAL_PROVIDER_PROMO_LEARN_MORE_SUBTITLE_WITH_PH,
        base::SysNSStringToUTF16(l10n_util::GetNSString(
            IDS_IOS_CREDENTIAL_PROVIDER_PROMO_OS_PASSWORDS_SETTINGS_TITLE_IOS16)));
  } else {
    return l10n_util::GetNSStringF(
        IDS_IOS_CREDENTIAL_PROVIDER_PROMO_LEARN_MORE_SUBTITLE_WITH_PH,
        base::SysNSStringToUTF16(l10n_util::GetNSString(
            IDS_IOS_CREDENTIAL_PROVIDER_PROMO_OS_PASSWORDS_SETTINGS_TITLE_BELOW_IOS16)));
  }
}

}  // namespace

// Test fixture for testing the CredentialProviderPromoMediator class.
class CredentialProviderPromoMediatorTest : public PlatformTest {
 protected:
  CredentialProviderPromoMediatorTest() {
    CreateCredentialProviderPromoMediator();
    DisableCredentialProviderExtension();
  }

  PrefService* local_state() {
    return GetApplicationContext()->GetLocalState();
  }

 protected:
  IOSChromeScopedTestingLocalState scoped_testing_local_state_;
  std::unique_ptr<MockPromosManager> promos_manager_;
  CredentialProviderPromoMediator* mediator_;
  std::unique_ptr<TestingPrefServiceSimple> pref_service_;
  id consumer_;
  NSString* firstStepTitleString =
      l10n_util::GetNSString(IDS_IOS_CREDENTIAL_PROVIDER_PROMO_INITIAL_TITLE);
  NSString* firstStepSubtitleString = l10n_util::GetNSString(
      IDS_IOS_CREDENTIAL_PROVIDER_PROMO_INITIAL_SUBTITLE);
  NSString* learnMoreTitleString = l10n_util::GetNSString(
      IDS_IOS_CREDENTIAL_PROVIDER_PROMO_LEARN_MORE_TITLE);
  NSString* goToSettingsString =
      l10n_util::GetNSString(IDS_IOS_CREDENTIAL_PROVIDER_PROMO_GO_TO_SETTINGS);
  NSString* learnHowString =
      l10n_util::GetNSString(IDS_IOS_CREDENTIAL_PROVIDER_PROMO_LEARN_HOW);
  NSString* noThanksString =
      l10n_util::GetNSString(IDS_IOS_CREDENTIAL_PROVIDER_PROMO_NO_THANKS);
  NSString* remindMeLaterString =
      l10n_util::GetNSString(IDS_IOS_CREDENTIAL_PROVIDER_PROMO_REMIND_ME_LATER);

  void CreateCredentialProviderPromoMediator() {
    promos_manager_ = std::make_unique<MockPromosManager>();
    pref_service_ = std::make_unique<TestingPrefServiceSimple>();
    consumer_ = OCMProtocolMock(@protocol(CredentialProviderPromoConsumer));
    mediator_ = [[CredentialProviderPromoMediator alloc]
        initWithPromosManager:promos_manager_.get()
                  prefService:pref_service_.get()];
    mediator_.consumer = consumer_;
  }

  void DisableCredentialProviderExtension() {
    pref_service_->registry()->RegisterBooleanPref(
        password_manager::prefs::kCredentialProviderEnabledOnStartup, false);
  }

  void ExpectConsumerSetFieldsForFirstStep() {
    OCMExpect([consumer_ setTitleString:firstStepTitleString
                         subtitleString:firstStepSubtitleString
                    primaryActionString:learnHowString
                  secondaryActionString:noThanksString
                   tertiaryActionString:remindMeLaterString
                                  image:nil]);
    OCMExpect([consumer_ setAnimation:kFirstStepAnimation]);
  }

  void ExpectConsumerSetFieldsForFirstStepNoAnimation() {
    OCMExpect([consumer_ setTitleString:firstStepTitleString
                         subtitleString:firstStepSubtitleString
                    primaryActionString:learnHowString
                  secondaryActionString:noThanksString
                   tertiaryActionString:remindMeLaterString
                                  image:kImage]);
  }

  void ExpectConsumerSetFieldsForLearnMore() {
    OCMExpect([consumer_ setTitleString:learnMoreTitleString
                         subtitleString:LearnMoreSubtitleString()
                    primaryActionString:goToSettingsString
                  secondaryActionString:noThanksString
                   tertiaryActionString:remindMeLaterString
                                  image:nil]);
    OCMExpect([consumer_ setAnimation:kLearnMoreAnimation]);
  }
};

#pragma mark - Tests

// Tests that promo is NOT displayed when the user has already enabled the
// Credential Provider Extension.
TEST_F(CredentialProviderPromoMediatorTest,
       CredentialProviderExtensionEnabled) {
  pref_service_->SetBoolean(
      password_manager::prefs::kCredentialProviderEnabledOnStartup, true);

  EXPECT_FALSE([mediator_
      canShowCredentialProviderPromoWithTrigger:
          CredentialProviderPromoTrigger::SuccessfulLoginUsingExistingPassword
                                      promoSeen:NO]);
}

// Tests that promo will NOT be displayed when the promo has already been
// displayed in the current app session.
TEST_F(CredentialProviderPromoMediatorTest,
       CredentialProviderPromoAlreadySeen) {
  EXPECT_FALSE([mediator_
      canShowCredentialProviderPromoWithTrigger:
          CredentialProviderPromoTrigger::SuccessfulLoginUsingExistingPassword
                                      promoSeen:YES]);
}

// Tests that promo will NOT be displayed when the user has previously seen the
// promo and selected "No Thanks".
TEST_F(CredentialProviderPromoMediatorTest,
       CredentialProviderPromoNoThanksSelected) {
  local_state()->SetBoolean(prefs::kIosCredentialProviderPromoStopPromo, true);

  EXPECT_FALSE([mediator_
      canShowCredentialProviderPromoWithTrigger:
          CredentialProviderPromoTrigger::SuccessfulLoginUsingExistingPassword
                                      promoSeen:NO]);
}

// Tests that the promo will be displayed when all the trigger requirements are
// met.
TEST_F(CredentialProviderPromoMediatorTest,
       CredentialProviderPromoRequirementsMet) {
  EXPECT_TRUE([mediator_
      canShowCredentialProviderPromoWithTrigger:
          CredentialProviderPromoTrigger::SuccessfulLoginUsingExistingPassword
                                      promoSeen:NO]);
}

// Tests that the promo will always be displayed when the trigger is SetUpList.
TEST_F(CredentialProviderPromoMediatorTest,
       CredentialProviderPromoSetUpListTrigger) {
  local_state()->SetBoolean(prefs::kIosCredentialProviderPromoStopPromo, true);
  EXPECT_TRUE([mediator_ canShowCredentialProviderPromoWithTrigger:
                             CredentialProviderPromoTrigger::SetUpList
                                                         promoSeen:YES]);
}

// Tests that the consumer content is correctly set when the promo:
// - Is a “First Step” promo
// - Was triggered by the user successfully logging in using an existing
// password
TEST_F(CredentialProviderPromoMediatorTest,
       ConsumerContent_FirstStep_SuccessfulLoginUsingExistingPassword) {
  ExpectConsumerSetFieldsForFirstStepNoAnimation();

  [mediator_
      configureConsumerWithTrigger:CredentialProviderPromoTrigger::
                                       SuccessfulLoginUsingExistingPassword
                           context:CredentialProviderPromoContext::kFirstStep];

  EXPECT_OCMOCK_VERIFY(consumer_);
}

// Tests that the consumer content is correctly set when the user selects
// “Remind Me Later” AFTER seeing a promo that:
//  - Was a “First Step” promo
//  - Was triggered by the user successfully logging in using an existing
//  password
TEST_F(
    CredentialProviderPromoMediatorTest,
    ConsumerContent_FirstStep_RemindMeLater_AfterFirstStepSuccessfulLoginUsingExistingPassword) {
  // Need to check for this method call twice: once for the promo
  // triggered by successful login and once for the promo triggered by selecting
  // "Remind Me Later".
  ExpectConsumerSetFieldsForFirstStepNoAnimation();
  ExpectConsumerSetFieldsForFirstStepNoAnimation();

  [mediator_
      configureConsumerWithTrigger:CredentialProviderPromoTrigger::
                                       SuccessfulLoginUsingExistingPassword
                           context:CredentialProviderPromoContext::kFirstStep];
  [mediator_
      configureConsumerWithTrigger:CredentialProviderPromoTrigger::RemindMeLater
                           context:CredentialProviderPromoContext::kFirstStep];

  EXPECT_OCMOCK_VERIFY(consumer_);
}

// Tests that the consumer content is correctly set when the promo:
//  - Is a “Learn More” promo
//  - Was triggered by the user successfully logging in using an existing
//  password
TEST_F(CredentialProviderPromoMediatorTest,
       ConsumerContent_LearnMore_SuccessfulLoginUsingExistingPassword) {
  ExpectConsumerSetFieldsForLearnMore();

  [mediator_
      configureConsumerWithTrigger:CredentialProviderPromoTrigger::
                                       SuccessfulLoginUsingExistingPassword
                           context:CredentialProviderPromoContext::kLearnMore];

  EXPECT_OCMOCK_VERIFY(consumer_);
}

// Tests that the consumer content is correctly set when the user selects
// “Remind Me Later” after seeing a promo that:
//  - Was a “Learn More” promo
//  - Was triggered by the user successfully logging in using an existing
//  password
TEST_F(
    CredentialProviderPromoMediatorTest,
    ConsumerContent_LearnMore_RemindMeLater_AfterSuccessfulLoginUsingExistingPassword) {
  // Need to check for each of these method calls twice: once for the promo
  // triggered by successful login and once for the promo triggered by selecting
  // "Remind Me Later".
  ExpectConsumerSetFieldsForLearnMore();
  ExpectConsumerSetFieldsForLearnMore();

  [mediator_
      configureConsumerWithTrigger:CredentialProviderPromoTrigger::
                                       SuccessfulLoginUsingExistingPassword
                           context:CredentialProviderPromoContext::kLearnMore];
  [mediator_
      configureConsumerWithTrigger:CredentialProviderPromoTrigger::RemindMeLater
                           context:CredentialProviderPromoContext::kLearnMore];

  EXPECT_OCMOCK_VERIFY(consumer_);
}