chromium/ios/chrome/browser/overlays/ui_bundled/web_content_area/alerts/alert_overlay_mediator_unittest.mm

// Copyright 2019 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/overlays/ui_bundled/web_content_area/alerts/alert_overlay_mediator.h"

#import "base/functional/bind.h"
#import "base/test/metrics/user_action_tester.h"
#import "ios/chrome/browser/alert_view/ui_bundled/alert_action.h"
#import "ios/chrome/browser/alert_view/ui_bundled/test/fake_alert_consumer.h"
#import "ios/chrome/browser/overlays/model/public/overlay_callback_manager.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request_config.h"
#import "ios/chrome/browser/overlays/model/public/overlay_response_info.h"
#import "ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.h"
#import "ios/chrome/browser/shared/ui/elements/text_field_configuration.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"

using alert_overlays::AlertRequest;
using alert_overlays::AlertResponse;
using alert_overlays::ButtonConfig;

namespace {
// Alert setup consts.
const size_t kButtonIndexOkRow = 1;
const size_t kButtonIndexOkCol = 0;
const size_t kTextFieldIndex = 0;

// Recorded when OK button is tapped.
const char kOKTappedUserActionName[] = "OKTappedUserActionName";

// Fake response for use in tests.
class FakeResponseInfo : public OverlayResponseInfo<FakeResponseInfo> {
 public:
  ~FakeResponseInfo() override {}

  // Whether the user tapped the OK button on the dialog.
  bool ok_button_tapped() const { return ok_button_tapped_; }
  // The text field input.
  NSString* input() const { return input_; }

 private:
  OVERLAY_USER_DATA_SETUP(FakeResponseInfo);
  FakeResponseInfo(bool ok_button_tapped, NSString* input)
      : ok_button_tapped_(ok_button_tapped), input_(input) {}

  const bool ok_button_tapped_ = false;
  NSString* const input_ = nil;
};
OVERLAY_USER_DATA_SETUP_IMPL(FakeResponseInfo);

// Creates an OverlayResponse with a FakeResponseInfo from one created with an
// AlertResponse.
std::unique_ptr<OverlayResponse> CreateFakeResponse(
    std::unique_ptr<OverlayResponse> alert_response) {
  AlertResponse* alert_info = alert_response->GetInfo<AlertResponse>();
  return OverlayResponse::CreateWithInfo<FakeResponseInfo>(
      alert_info->tapped_button_row_index() == kButtonIndexOkRow &&
          alert_info->tapped_button_column_index() == kButtonIndexOkCol,
      alert_info->text_field_values()[kTextFieldIndex]);
}

// Fake request for use in tests.
class FakeRequestConfig : public OverlayResponseInfo<FakeRequestConfig> {
 public:
  ~FakeRequestConfig() override {}

 private:
  OVERLAY_USER_DATA_SETUP(FakeRequestConfig);
  FakeRequestConfig() {}

  void CreateAuxiliaryData(base::SupportsUserData* user_data) override {
    // Creates an AlertRequest with an OK and Cancel button and a single
    // text field.
    NSArray<TextFieldConfiguration*>* text_field_configs = @[
      [[TextFieldConfiguration alloc]
                     initWithText:@""
                      placeholder:@""
          accessibilityIdentifier:@""
           autocapitalizationType:UITextAutocapitalizationTypeSentences
                  secureTextEntry:NO],
    ];
    const std::vector<std::vector<ButtonConfig>> button_configs{
        {ButtonConfig(@"First Row")},
        {ButtonConfig(@"OK", kOKTappedUserActionName),
         ButtonConfig(@"Cancel", UIAlertActionStyleCancel)}};
    AlertRequest::CreateForUserData(user_data, @"title", @"message",
                                    @"accessibility_identifier",
                                    text_field_configs, button_configs,
                                    base::BindRepeating(&CreateFakeResponse));
  }
};
OVERLAY_USER_DATA_SETUP_IMPL(FakeRequestConfig);
}  // namespace

#pragma mark - FakeAlertOverlayMediatorDataSource

// Fake AlertOverlayMediatorDataSource for use in tests.
@interface FakeAlertOverlayMediatorDataSource
    : NSObject <AlertOverlayMediatorDataSource>
// The text field values to return for the data source protocol.
@property(nonatomic, strong) NSArray<NSString*>* textFieldValues;
@end
@implementation FakeAlertOverlayMediatorDataSource
- (NSString*)textFieldInputForMediator:(AlertOverlayMediator*)mediator
                        textFieldIndex:(NSUInteger)index {
  return index < self.textFieldValues.count ? self.textFieldValues[index] : nil;
}
@end

#pragma mark - AlertOverlayMediatorTest

// Test fixture for AlertOverlayMediator.
class AlertOverlayMediatorTest : public PlatformTest {
 protected:
  AlertOverlayMediatorTest()
      : request_(OverlayRequest::CreateWithConfig<FakeRequestConfig>()),
        consumer_([[FakeAlertConsumer alloc] init]),
        mediator_(
            [[AlertOverlayMediator alloc] initWithRequest:request_.get()]) {
    mediator_.consumer = consumer_;
  }

  std::unique_ptr<OverlayRequest> request_;
  FakeAlertConsumer* consumer_ = nil;
  AlertOverlayMediator* mediator_ = nil;
};

// Tests that the AlertOverlayMediator's subclassing properties are correctly
// applied to the consumer.
TEST_F(AlertOverlayMediatorTest, SetUpConsumer) {
  AlertRequest* alert_request = request_->GetConfig<AlertRequest>();
  ASSERT_TRUE(alert_request);
  EXPECT_NSEQ(alert_request->title(), consumer_.title);
  EXPECT_NSEQ(alert_request->message(), consumer_.message);
  EXPECT_NSEQ(alert_request->accessibility_identifier(),
              consumer_.alertAccessibilityIdentifier);
  EXPECT_NSEQ(alert_request->text_field_configs(),
              consumer_.textFieldConfigurations);
  ASSERT_EQ(2U, alert_request->button_configs().size());
  ASSERT_EQ(1U, alert_request->button_configs()[0].size());
  ASSERT_EQ(2U, alert_request->button_configs()[1].size());
  size_t rows_count = alert_request->button_configs().size();
  for (size_t i = 0; i < rows_count; ++i) {
    NSArray<AlertAction*>* actions = consumer_.actions[i];
    for (size_t j = 0; j < [actions count]; ++j) {
      AlertAction* consumer_action = actions[j];
      const ButtonConfig& button_config = alert_request->button_configs()[i][j];
      EXPECT_NSEQ(button_config.title, consumer_action.title);
      EXPECT_EQ(button_config.style, consumer_action.style);
    }
  }
}

// Tests that AlertOverlayMediator successfully converts OverlayResponses
// created with AlertResponses into their feature-specific response.
TEST_F(AlertOverlayMediatorTest, ResponseConversion) {
  // Set up a fake datasource for the text field values.
  FakeAlertOverlayMediatorDataSource* data_source =
      [[FakeAlertOverlayMediatorDataSource alloc] init];
  data_source.textFieldValues = @[ @"TextFieldValue" ];
  mediator_.dataSource = data_source;

  // Simulate a tap on the OK button.
  AlertAction* ok_button_action =
      consumer_.actions[kButtonIndexOkRow][kButtonIndexOkCol];
  ASSERT_TRUE(ok_button_action.handler);
  ok_button_action.handler(ok_button_action);

  // Verify that the request's completion callback is a FakeResponseInfo with
  // the expected values.
  OverlayResponse* response =
      request_->GetCallbackManager()->GetCompletionResponse();
  ASSERT_TRUE(response);
  FakeResponseInfo* info = response->GetInfo<FakeResponseInfo>();
  ASSERT_TRUE(info);
  EXPECT_TRUE(info->ok_button_tapped());
  EXPECT_NSEQ(data_source.textFieldValues[0], info->input());
}

// Tests UMA user action recording.
TEST_F(AlertOverlayMediatorTest, UserActionRecording) {
  // Tapping OK button records User Action.
  AlertAction* ok_button_action =
      consumer_.actions[kButtonIndexOkRow][kButtonIndexOkCol];
  base::UserActionTester user_action_tester;
  EXPECT_EQ(0, user_action_tester.GetActionCount(kOKTappedUserActionName));
  ok_button_action.handler(ok_button_action);
  EXPECT_EQ(1, user_action_tester.GetActionCount(kOKTappedUserActionName));
}