chromium/ios/chrome/browser/overlays/ui_bundled/infobar_modal/permissions/permissions_infobar_modal_overlay_mediator_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/overlays/ui_bundled/infobar_modal/permissions/permissions_infobar_modal_overlay_mediator.h"

#import "ios/chrome/browser/infobars/model/infobar_ios.h"
#import "ios/chrome/browser/infobars/model/infobar_type.h"
#import "ios/chrome/browser/infobars/model/overlays/default_infobar_overlay_request_factory.h"
#import "ios/chrome/browser/overlays/model/public/default/default_infobar_overlay_request_config.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request.h"
#import "ios/chrome/browser/permissions/model/permissions_infobar_delegate.h"
#import "ios/chrome/browser/permissions/ui_bundled/permission_info.h"
#import "ios/chrome/browser/permissions/ui_bundled/permissions_consumer.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/permissions/permissions.h"
#import "ios/web/public/test/fakes/fake_navigation_manager.h"
#import "ios/web/public/test/fakes/fake_web_state.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"
#import "ui/base/l10n/l10n_util.h"

// FakePermissionsConsumer used for testing purpose.
@interface FakePermissionsConsumer : NSObject <PermissionsConsumer>
@property(nonatomic, strong) NSString* permissionsDescription;
@property(nonatomic, strong) PermissionInfo* cameraInfo;
@property(nonatomic, strong) PermissionInfo* microphoneInfo;
@end

@implementation FakePermissionsConsumer

- (void)setPermissionsInfo:(NSArray<PermissionInfo*>*)permissionsInfo {
  for (PermissionInfo* info in permissionsInfo) {
    if (info.permission == web::PermissionCamera) {
      self.cameraInfo = info;
    } else {
      self.microphoneInfo = info;
    }
  }
}

- (void)permissionStateChanged:(PermissionInfo*)info {
  if (info.permission == web::PermissionCamera) {
    self.cameraInfo = info;
  } else {
    self.microphoneInfo = info;
  }
}

- (void)setPermissionsDescription:(NSString*)permissionsDescription {
  _permissionsDescription = permissionsDescription;
}

@end

// Test fixture for PermissionsInfobarModalOverlayMediator.
class PermissionsInfobarModalOverlayMediatorTest : public PlatformTest {
 public:
  PermissionsInfobarModalOverlayMediatorTest() {
    auto navigation_manager = std::make_unique<web::FakeNavigationManager>();
    item_ = web::NavigationItem::Create();
    GURL url("http://test.com/");
    item_->SetURL(url);
    navigation_manager->SetVisibleItem(item_.get());
    web_state_.SetNavigationManager(std::move(navigation_manager));
    // First parameter is used for banner; not needed for this test.
    std::unique_ptr<PermissionsInfobarDelegate> delegate =
        std::make_unique<PermissionsInfobarDelegate>([NSArray array],
                                                     &web_state_);
    infobar_ = std::make_unique<InfoBarIOS>(
        InfobarType::kInfobarTypePermissions, std::move(delegate));
    request_ =
        OverlayRequest::CreateWithConfig<DefaultInfobarOverlayRequestConfig>(
            infobar_.get(), InfobarOverlayType::kModal);
    mediator_ = [[PermissionsInfobarModalOverlayMediator alloc]
        initWithRequest:request_.get()];
  }

  ~PermissionsInfobarModalOverlayMediatorTest() override {
    [mediator_ disconnect];
  }

 protected:
  PermissionsInfobarModalOverlayMediator* mediator_;
  std::unique_ptr<OverlayRequest> request_;
  web::FakeWebState web_state_;
  std::unique_ptr<InfoBarIOS> infobar_;
  std::unique_ptr<web::NavigationItem> item_;
};

// Tests that a PermissionsInfobarModalOverlayMediator correctly sets up its
// consumer.
TEST_F(PermissionsInfobarModalOverlayMediatorTest, SetUpConsumer) {
  // Package the infobar into an OverlayRequest, then create a mediator that
  // uses this request in order to set up a fake consumer.
  FakePermissionsConsumer* consumer = [[FakePermissionsConsumer alloc] init];
  mediator_.consumer = consumer;
  EXPECT_EQ(web::PermissionStateNotAccessible, consumer.cameraInfo.state);
  EXPECT_EQ(web::PermissionStateNotAccessible, consumer.microphoneInfo.state);
  NSString* description = l10n_util::GetNSStringF(
      IDS_IOS_PERMISSIONS_INFOBAR_MODAL_DESCRIPTION, u"test.com");
  EXPECT_NSEQ(description, consumer.permissionsDescription);
}

// Tests that the mediator would update its consumer when web state permissions
// change.
TEST_F(PermissionsInfobarModalOverlayMediatorTest, PermissionStatesUpdate) {
  FakePermissionsConsumer* consumer = [[FakePermissionsConsumer alloc] init];
  mediator_.consumer = consumer;
  ASSERT_EQ(web::PermissionStateNotAccessible, consumer.cameraInfo.state);
  ASSERT_EQ(web::PermissionStateNotAccessible, consumer.microphoneInfo.state);
  // Update web state permission directly.
  web_state_.SetStateForPermission(web::PermissionStateAllowed,
                                   web::PermissionCamera);
  EXPECT_EQ(web::PermissionStateAllowed, consumer.cameraInfo.state);
  EXPECT_EQ(web::PermissionStateNotAccessible, consumer.microphoneInfo.state);
  web_state_.SetStateForPermission(web::PermissionStateBlocked,
                                   web::PermissionMicrophone);
  EXPECT_EQ(web::PermissionStateAllowed, consumer.cameraInfo.state);
  EXPECT_EQ(web::PermissionStateBlocked, consumer.microphoneInfo.state);
}

// Tests that calling `updateStateForPermission:` updates both the consumer and
// web state permissions.
TEST_F(PermissionsInfobarModalOverlayMediatorTest,
       UpdatePermissionStatesThroughInfobarModal) {
  FakePermissionsConsumer* consumer = [[FakePermissionsConsumer alloc] init];
  mediator_.consumer = consumer;
  ASSERT_EQ(web::PermissionStateNotAccessible, consumer.cameraInfo.state);
  ASSERT_EQ(web::PermissionStateNotAccessible, consumer.microphoneInfo.state);
  // Update web state permission directly.
  web_state_.SetStateForPermission(web::PermissionStateAllowed,
                                   web::PermissionCamera);
  ASSERT_EQ(web::PermissionStateAllowed, consumer.cameraInfo.state);

  // Update web state permissions through the infobar modal.
  PermissionInfo* permission_info_1 = [[PermissionInfo alloc] init];
  permission_info_1.permission = web::PermissionMicrophone;
  permission_info_1.state = web::PermissionStateAllowed;
  [mediator_ updateStateForPermission:permission_info_1];
  EXPECT_EQ(web::PermissionStateAllowed,
            web_state_.GetStateForPermission(web::PermissionCamera));
  EXPECT_EQ(web::PermissionStateAllowed, consumer.cameraInfo.state);
  EXPECT_EQ(web::PermissionStateAllowed,
            web_state_.GetStateForPermission(web::PermissionMicrophone));
  EXPECT_EQ(web::PermissionStateAllowed, consumer.microphoneInfo.state);

  PermissionInfo* permission_info_2 = [[PermissionInfo alloc] init];
  permission_info_2.permission = web::PermissionCamera;
  permission_info_2.state = web::PermissionStateBlocked;
  [mediator_ updateStateForPermission:permission_info_2];
  EXPECT_EQ(web::PermissionStateBlocked,
            web_state_.GetStateForPermission(web::PermissionCamera));
  EXPECT_EQ(web::PermissionStateBlocked, consumer.cameraInfo.state);
  EXPECT_EQ(web::PermissionStateAllowed,
            web_state_.GetStateForPermission(web::PermissionMicrophone));
  EXPECT_EQ(web::PermissionStateAllowed, consumer.microphoneInfo.state);
}

// Tests that a PermissionsInfobarModalOverlayMediator correctly removes itself
// as a WebStateObserver when the WebState is destroyed.
TEST_F(PermissionsInfobarModalOverlayMediatorTest, WebStateObserverRemoved) {
  std::unique_ptr<web::FakeNavigationManager> navigation_manager =
      std::make_unique<web::FakeNavigationManager>();
  navigation_manager->SetVisibleItem(item_.get());
  std::unique_ptr<web::FakeWebState> web_state =
      std::make_unique<web::FakeWebState>();
  web_state->SetNavigationManager(std::move(navigation_manager));
  std::unique_ptr<PermissionsInfobarDelegate> delegate =
      std::make_unique<PermissionsInfobarDelegate>([NSArray array],
                                                   web_state.get());
  std::unique_ptr<InfoBarIOS> infobar = std::make_unique<InfoBarIOS>(
      InfobarType::kInfobarTypePermissions, std::move(delegate));
  std::unique_ptr<OverlayRequest> request =
      OverlayRequest::CreateWithConfig<DefaultInfobarOverlayRequestConfig>(
          infobar.get(), InfobarOverlayType::kModal);
  PermissionsInfobarModalOverlayMediator* mediator =
      [[PermissionsInfobarModalOverlayMediator alloc]
          initWithRequest:request.get()];
  FakePermissionsConsumer* consumer = [[FakePermissionsConsumer alloc] init];
  mediator.consumer = consumer;

  // Destroy the WebState. If `mediator_` doesn't remove itself as an observer,
  // the WebState's observer list will hit a DCHECK on destruction.
  web_state.reset();
}