chromium/ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_mediator_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/ui_bundled/authentication/otp_input_dialog_mediator.h"

#import "base/memory/weak_ptr.h"
#import "base/strings/sys_string_conversions.h"
#import "components/autofill/core/browser/payments/otp_unmask_delegate.h"
#import "components/autofill/core/browser/ui/payments/card_unmask_otp_input_dialog_controller_impl.h"
#import "ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_consumer.h"
#import "ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_content.h"
#import "ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_mediator_delegate.h"
#import "ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_mutator.h"
#import "testing/gmock/include/gmock/gmock.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/gtest_support.h"

using autofill::CardUnmaskChallengeOption;

// Mock version of CardUnmaskOtpInputDialogController.
class MockOtpUnmaskDelegate : public autofill::OtpUnmaskDelegate {
 public:
  MockOtpUnmaskDelegate() = default;
  ~MockOtpUnmaskDelegate() = default;

  MOCK_METHOD(void,
              OnUnmaskPromptAccepted,
              (const std::u16string& otp),
              (override));
  MOCK_METHOD(void,
              OnUnmaskPromptClosed,
              (bool user_closed_dialog),
              (override));
  MOCK_METHOD(void, OnNewOtpRequested, (), (override));

  base::WeakPtr<MockOtpUnmaskDelegate> GetWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }

 private:
  base::WeakPtrFactory<MockOtpUnmaskDelegate> weak_ptr_factory_{this};
};

class OtpInputDialogMediatorTest : public PlatformTest {
 protected:
  OtpInputDialogMediatorTest() {
    consumer_ = OCMProtocolMock(@protocol(OtpInputDialogConsumer));
    delegate_ = OCMProtocolMock(@protocol(OtpInputDialogMediatorDelegate));
    CardUnmaskChallengeOption option = CardUnmaskChallengeOption(
        CardUnmaskChallengeOption::ChallengeOptionId("123"),
        autofill::CardUnmaskChallengeOptionType::kSmsOtp,
        /*challenge_info=*/u"xxx-xxx-3547",
        /*challenge_input_length=*/6U);
    model_controller_ =
        std::make_unique<autofill::CardUnmaskOtpInputDialogControllerImpl>(
            option, unmask_delegate_.GetWeakPtr());
    mediator_ = std::make_unique<OtpInputDialogMediator>(
        model_controller_->GetImplWeakPtr(), delegate_);
  }

  id<OtpInputDialogConsumer> consumer_;
  id<OtpInputDialogMediatorDelegate> delegate_;
  testing::NiceMock<MockOtpUnmaskDelegate> unmask_delegate_;
  std::unique_ptr<autofill::CardUnmaskOtpInputDialogControllerImpl>
      model_controller_;
  std::unique_ptr<OtpInputDialogMediator> mediator_;
};

// Tests consumer receives the correct contents.
TEST_F(OtpInputDialogMediatorTest, SetConsumer) {
  OCMExpect([consumer_
      setContent:[OCMArg checkWithBlock:^BOOL(OtpInputDialogContent* content) {
        EXPECT_TRUE([content.windowTitle
            isEqualToString:base::SysUTF16ToNSString(
                                model_controller_->GetWindowTitle())]);
        EXPECT_TRUE([content.textFieldPlaceholder
            isEqualToString:base::SysUTF16ToNSString(
                                model_controller_
                                    ->GetTextfieldPlaceholderText())]);
        EXPECT_TRUE([content.confirmButtonLabel
            isEqualToString:base::SysUTF16ToNSString(
                                model_controller_->GetOkButtonLabel())]);
        return YES;
      }]]);

  mediator_->SetConsumer(consumer_);

  EXPECT_OCMOCK_VERIFY((id)consumer_);
}

TEST_F(OtpInputDialogMediatorTest, DidTapConfirmButton) {
  NSString* otp = @"123456";
  OCMExpect([consumer_ showPendingState]);
  EXPECT_CALL(unmask_delegate_,
              OnUnmaskPromptAccepted(base::SysNSStringToUTF16(otp)));

  [mediator_->AsMutator() didTapConfirmButton:otp];
}

TEST_F(OtpInputDialogMediatorTest, DidTapCancelButton) {
  OCMExpect([delegate_ dismissDialog]);
  EXPECT_CALL(unmask_delegate_,
              OnUnmaskPromptClosed(/*user_closed_dialog=*/true));

  [mediator_->AsMutator() didTapCancelButton];
}

TEST_F(OtpInputDialogMediatorTest, OnOtpInputChanges) {
  OCMExpect([consumer_ setConfirmButtonEnabled:NO]);

  [mediator_->AsMutator() onOtpInputChanges:@"12345"];

  OCMExpect([consumer_ setConfirmButtonEnabled:YES]);

  [mediator_->AsMutator() onOtpInputChanges:@"123456"];
}

TEST_F(OtpInputDialogMediatorTest, Dismiss) {
  OCMExpect([delegate_ dismissDialog]);
  EXPECT_CALL(unmask_delegate_,
              OnUnmaskPromptClosed(/*user_closed_dialog=*/true));

  mediator_->Dismiss(/*show_confirmation_before_closing=*/false,
                     /*user_closed_dialog=*/true);
}

TEST_F(OtpInputDialogMediatorTest, NewCodeRequested) {
  EXPECT_CALL(unmask_delegate_, OnNewOtpRequested());

  [mediator_->AsMutator() didTapNewCodeLink];
}