chromium/ios/chrome/browser/ui/settings/downloads/save_to_photos/save_to_photos_settings_account_selection_view_controller_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/ui/settings/downloads/save_to_photos/save_to_photos_settings_account_selection_view_controller.h"

#import "base/apple/foundation_util.h"
#import "ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/account_picker_selection_screen_identity_item_configurator.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_image_item.h"
#import "ios/chrome/browser/shared/ui/table_view/legacy_chrome_table_view_controller_test.h"
#import "ios/chrome/browser/ui/authentication/cells/table_view_identity_item.h"
#import "ios/chrome/browser/ui/settings/downloads/save_to_photos/fake_save_to_photos_settings_mutator.h"
#import "ios/chrome/browser/ui/settings/downloads/save_to_photos/save_to_photos_settings_account_selection_view_controller_action_delegate.h"
#import "ios/chrome/browser/ui/settings/downloads/save_to_photos/save_to_photos_settings_account_selection_view_controller_presentation_delegate.h"
#import "ios/chrome/browser/ui/settings/downloads/save_to_photos/save_to_photos_settings_mutator.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/grit/ios_strings.h"
#import "testing/gtest_mac.h"
#import "ui/base/l10n/l10n_util_mac.h"

namespace {

// Helper to create AccountPickerSelectionScreenIdentityItemConfigurator.
AccountPickerSelectionScreenIdentityItemConfigurator* CreateConfigurator(
    NSString* gaiaID,
    NSString* name,
    NSString* email,
    UIImage* avatar,
    BOOL selected) {
  AccountPickerSelectionScreenIdentityItemConfigurator* configurator =
      [[AccountPickerSelectionScreenIdentityItemConfigurator alloc] init];
  configurator.gaiaID = gaiaID;
  configurator.name = name;
  configurator.email = email;
  configurator.avatar = avatar;
  configurator.selected = selected;
  return configurator;
}

}  // namespace

// Fake presentation delegate.
@interface FakeSaveToPhotosSettingsAccountSelectionViewControllerPresentationDelegate
    : NSObject <
          SaveToPhotosSettingsAccountSelectionViewControllerPresentationDelegate>

@property(nonatomic, assign) BOOL wasRemovedCalled;

@end

@implementation
    FakeSaveToPhotosSettingsAccountSelectionViewControllerPresentationDelegate

- (void)saveToPhotosSettingsAccountSelectionViewControllerWasRemoved {
  self.wasRemovedCalled = YES;
}

@end

// Fake action delegate.
@interface FakeSaveToPhotosSettingsAccountSelectionViewControllerActionDelegate
    : NSObject <
          SaveToPhotosSettingsAccountSelectionViewControllerActionDelegate>

@property(nonatomic, assign) BOOL addAccountCalled;

@end

@implementation
    FakeSaveToPhotosSettingsAccountSelectionViewControllerActionDelegate

- (void)saveToPhotosSettingsAccountSelectionViewControllerAddAccount {
  self.addAccountCalled = YES;
}

@end

class SaveToPhotosSettingsAccountSelectionViewControllerTest
    : public LegacyChromeTableViewControllerTest {
 protected:
  void SetUp() final {
    LegacyChromeTableViewControllerTest::SetUp();

    mutator_ = [[FakeSaveToPhotosSettingsMutator alloc] init];
    presentation_delegate_ =
        [[FakeSaveToPhotosSettingsAccountSelectionViewControllerPresentationDelegate
            alloc] init];
    action_delegate_ =
        [[FakeSaveToPhotosSettingsAccountSelectionViewControllerActionDelegate
            alloc] init];

    CreateController();
  }

  void TearDown() final { LegacyChromeTableViewControllerTest::TearDown(); }

  LegacyChromeTableViewController* InstantiateController() final {
    SaveToPhotosSettingsAccountSelectionViewController* controller =
        [[SaveToPhotosSettingsAccountSelectionViewController alloc] init];
    controller.mutator = mutator_;
    controller.presentationDelegate = presentation_delegate_;
    controller.actionDelegate = action_delegate_;
    return controller;
  }

  FakeSaveToPhotosSettingsMutator* mutator_;
  FakeSaveToPhotosSettingsAccountSelectionViewControllerPresentationDelegate*
      presentation_delegate_;
  FakeSaveToPhotosSettingsAccountSelectionViewControllerActionDelegate*
      action_delegate_;
};

// Tests that the table loads the expected model after content is pushed through
// the consumer interface. Also tests that the mutator is called when an
// identity is selected, the action delegate is called when the user selects
// "Add account" and the presentation delegate is called when the view
// controller is removed.
TEST_F(SaveToPhotosSettingsAccountSelectionViewControllerTest,
       CanSelectAndAddAccount) {
  // Populate the table with three accounts.
  NSArray<AccountPickerSelectionScreenIdentityItemConfigurator*>*
      configurators = @[
        CreateConfigurator(@"gaiaID1", @"Person One", @"[email protected]", nil,
                           YES),
        CreateConfigurator(@"gaiaID2", @"Person Two", @"[email protected]", nil,
                           NO),
        CreateConfigurator(@"gaiaID3", @"Person Three", @"[email protected]",
                           nil, NO),
      ];
  SaveToPhotosSettingsAccountSelectionViewController*
      accountSelectionController = base::apple::ObjCCast<
          SaveToPhotosSettingsAccountSelectionViewController>(controller());
  ASSERT_TRUE(accountSelectionController);
  [accountSelectionController populateAccountsOnDevice:configurators];

  // Test that there are two section and the expected title.
  EXPECT_EQ(2, NumberOfSections());
  CheckTitleWithId(IDS_IOS_SETTINGS_DOWNLOADS_SAVE_TO_PHOTOS_HEADER);

  // Test account selection section has expected content.
  CheckSectionHeaderWithId(IDS_IOS_SETTINGS_DOWNLOADS_ACCOUNT_SELECTION_HEADER,
                           0);
  for (size_t i = 0; i < configurators.count; i++) {
    TableViewIdentityItem* item =
        base::apple::ObjCCast<TableViewIdentityItem>(GetTableViewItem(0, i));
    EXPECT_TRUE(item);
    EXPECT_NSEQ(configurators[i].gaiaID, item.gaiaID);
    EXPECT_NSEQ(configurators[i].name, item.name);
    EXPECT_NSEQ(configurators[i].email, item.email);
    EXPECT_NSEQ(configurators[i].avatar, item.avatar);
    EXPECT_EQ(configurators[i].selected, item.selected);
    EXPECT_EQ(IdentityViewStyleConsistency, item.identityViewStyle);
  }

  // Test that selecting an identity in the table calls the mutator.
  EXPECT_FALSE(mutator_.selectedIdentityGaiaID);
  [accountSelectionController tableView:controller().tableView
                didSelectRowAtIndexPath:[NSIndexPath indexPathForItem:1
                                                            inSection:0]];
  EXPECT_NSEQ(@"gaiaID2", mutator_.selectedIdentityGaiaID);
  [accountSelectionController tableView:controller().tableView
                didSelectRowAtIndexPath:[NSIndexPath indexPathForItem:0
                                                            inSection:0]];
  EXPECT_NSEQ(@"gaiaID1", mutator_.selectedIdentityGaiaID);

  // Test Add account section has expected content.
  TableViewImageItem* addAccountItem =
      base::apple::ObjCCast<TableViewImageItem>(GetTableViewItem(1, 0));
  EXPECT_TRUE(addAccountItem);
  EXPECT_NSEQ(l10n_util::GetNSString(IDS_IOS_CONSISTENCY_PROMO_ADD_ACCOUNT),
              addAccountItem.title);
  EXPECT_NSEQ([UIColor colorNamed:kBlueColor], addAccountItem.textColor);

  // Test that tapping the Add account button calls the action delegate.
  EXPECT_FALSE(action_delegate_.addAccountCalled);
  [accountSelectionController tableView:controller().tableView
                didSelectRowAtIndexPath:[NSIndexPath indexPathForItem:0
                                                            inSection:1]];
  EXPECT_TRUE(action_delegate_.addAccountCalled);

  // Test that removing the controller calls the presentation delegate.
  EXPECT_FALSE(presentation_delegate_.wasRemovedCalled);
  [controller() didMoveToParentViewController:nil];
  EXPECT_TRUE(presentation_delegate_.wasRemovedCalled);
}