chromium/ios/chrome/browser/autofill/ui_bundled/manual_fill/fallback_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/autofill/ui_bundled/manual_fill/fallback_view_controller.h"

#import "base/apple/foundation_util.h"
#import "base/test/with_feature_override.h"
#import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_text_cell.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_header_footer_item.h"
#import "ios/chrome/browser/shared/ui/table_view/legacy_chrome_table_view_controller_test.h"

namespace {

typedef NS_ENUM(NSInteger, ItemType) {
  ItemTypeSampleOne = kItemTypeEnumZero,
  ItemTypeSampleTwo,
  ItemTypeSampleThree,
  ItemTypeSampleFour
};

}  // namespace

class FallbackViewControllerTest : public LegacyChromeTableViewControllerTest,
                                   public base::test::WithFeatureOverride {
 public:
  FallbackViewControllerTest()
      : base::test::WithFeatureOverride(kIOSKeyboardAccessoryUpgrade) {}

 protected:
  void SetUp() override {
    LegacyChromeTableViewControllerTest::SetUp();
    CreateController();
    CheckController();
  }

  LegacyChromeTableViewController* InstantiateController() override {
    FallbackViewController* view_controller =
        [[FallbackViewController alloc] init];
    [view_controller loadModel];
    return view_controller;
  }

  FallbackViewController* GetFallbackViewController() {
    return base::apple::ObjCCastStrict<FallbackViewController>(controller());
  }

  // Returns the header item at `section`.
  id GetHeaderItem(int section) {
    return [controller().tableViewModel headerForSectionIndex:section];
  }

  // Returns the type of the header item at `section`.
  NSInteger GetHeaderItemType(int section) {
    return base::apple::ObjCCastStrict<TableViewHeaderFooterItem>(
               GetHeaderItem(section))
        .type;
  }

  // Returns the type of the table view item at `item` in `section`.
  NSInteger GetTableViewItemType(int section, int item) {
    return base::apple::ObjCCastStrict<TableViewItem>(
               GetTableViewItem(section, item))
        .type;
  }
};

// Tests the order of the elements in the view when all of data, action and
// header items are initialized.
TEST_P(FallbackViewControllerTest, CheckItems) {
  TableViewItem* item_one =
      [[TableViewItem alloc] initWithType:ItemTypeSampleOne];
  TableViewItem* item_two =
      [[TableViewItem alloc] initWithType:ItemTypeSampleTwo];
  NSArray<TableViewItem*>* data_items = @[ item_one, item_two ];

  TableViewItem* item_three =
      [[TableViewItem alloc] initWithType:ItemTypeSampleThree];
  NSArray<TableViewItem*>* action_items = @[ item_three ];

  TableViewHeaderFooterItem* item_four =
      [[TableViewHeaderFooterItem alloc] initWithType:ItemTypeSampleFour];

  FallbackViewController* fallbackViewController = GetFallbackViewController();

  [fallbackViewController presentDataItems:data_items];
  [fallbackViewController presentActionItems:action_items];
  [fallbackViewController presentHeaderItem:item_four];

  // When the kIOSKeyboardAccessoryUpgrade feature is enabled, data items each
  // have their own section. When disabled, data items are grouped in the same
  // section.
  if (IsKeyboardAccessoryUpgradeEnabled()) {
    EXPECT_EQ(NumberOfSections(), 4);

    // Header section stays at the top and has no items other than a header.
    EXPECT_EQ(NumberOfItemsInSection(0), 0);
    EXPECT_EQ(NumberOfItemsInSection(1), 1);
    EXPECT_EQ(NumberOfItemsInSection(2), 1);
    EXPECT_EQ(NumberOfItemsInSection(3), 1);

    EXPECT_EQ(GetHeaderItemType(/*section=*/0), ItemTypeSampleFour);
    EXPECT_EQ(GetTableViewItemType(/*section=*/1, /*item=*/0),
              ItemTypeSampleOne);
    EXPECT_EQ(GetTableViewItemType(/*section=*/2, /*item=*/0),
              ItemTypeSampleTwo);
    EXPECT_EQ(GetTableViewItemType(/*section=*/3, /*item=*/0),
              ItemTypeSampleThree);
  } else {
    EXPECT_EQ(NumberOfSections(), 3);

    // Header section stays at the top and has no items other than a header.
    EXPECT_EQ(NumberOfItemsInSection(0), 0);
    EXPECT_EQ(NumberOfItemsInSection(1), 2);
    EXPECT_EQ(NumberOfItemsInSection(2), 1);

    EXPECT_EQ(GetHeaderItemType(/*section=*/0), ItemTypeSampleFour);
    EXPECT_EQ(GetTableViewItemType(/*section=*/1, /*item=*/0),
              ItemTypeSampleOne);
    EXPECT_EQ(GetTableViewItemType(/*section=*/1, /*item=*/1),
              ItemTypeSampleTwo);
    EXPECT_EQ(GetTableViewItemType(/*section=*/2, /*item=*/0),
              ItemTypeSampleThree);
  }
}

// Tests that unused data item sections are deleted as expected.
TEST_P(FallbackViewControllerTest, RemoveUnusedDataItemSections) {
  TableViewItem* item_one =
      [[TableViewItem alloc] initWithType:ItemTypeSampleOne];
  TableViewItem* item_two =
      [[TableViewItem alloc] initWithType:ItemTypeSampleTwo];
  TableViewItem* item_three =
      [[TableViewItem alloc] initWithType:ItemTypeSampleThree];

  FallbackViewController* fallback_view_controller =
      GetFallbackViewController();

  // Present three data items.
  [fallback_view_controller
      presentDataItems:@[ item_one, item_two, item_three ]];
  // When the kIOSKeyboardAccessoryUpgrade feature is enabled, data items each
  // have their own section. When disabled, data items are grouped in the same
  // section.
  EXPECT_EQ(NumberOfSections(), IsKeyboardAccessoryUpgradeEnabled() ? 3 : 1);

  // Present 2 data items.
  [fallback_view_controller presentDataItems:@[ item_one, item_two ]];
  EXPECT_EQ(NumberOfSections(), IsKeyboardAccessoryUpgradeEnabled() ? 2 : 1);

  // Present no data items.
  [fallback_view_controller presentDataItems:@[]];
  EXPECT_EQ(NumberOfSections(), 0);
}

// Tests that the "no data items to show" message is displayed in the right
// place in the table view.
TEST_P(FallbackViewControllerTest, CheckNoDataItemsMessage) {
  FallbackViewController* fallback_view_controller =
      GetFallbackViewController();

  NSArray<TableViewItem*>* data_items;
  if (IsKeyboardAccessoryUpgradeEnabled()) {
    // Set `noDataItemsToShowHeaderItem`.
    TableViewTextHeaderFooterItem* header_item =
        [[TableViewTextHeaderFooterItem alloc] initWithType:ItemTypeSampleOne];
    fallback_view_controller.noDataItemsToShowHeaderItem = header_item;
    data_items = @[];
  } else {
    ManualFillTextItem* empty_credential_item =
        [[ManualFillTextItem alloc] initWithType:ItemTypeSampleTwo];
    data_items = @[ empty_credential_item ];
  }

  TableViewItem* action_item =
      [[TableViewItem alloc] initWithType:ItemTypeSampleThree];
  NSArray<TableViewItem*>* action_items = @[ action_item ];

  [fallback_view_controller presentDataItems:data_items];
  [fallback_view_controller presentActionItems:action_items];

  // When the kIOSKeyboardAccessoryUpgrade feature is enabled, the "no data
  // items to show" message is displayed as a header for the actions
  // section. When disabled, it is displayed as a regular table view item in the
  // data items section.
  if (IsKeyboardAccessoryUpgradeEnabled()) {
    // Only the actions section and no data item section should be present in
    // the table view.
    EXPECT_EQ(NumberOfSections(), 2);

    // The actions section should have a header and an item.
    EXPECT_TRUE(GetHeaderItem(/*section=*/0));
    EXPECT_EQ(NumberOfItemsInSection(0), 0);
    EXPECT_EQ(NumberOfItemsInSection(1), 1);

    EXPECT_EQ(GetHeaderItemType(/*section=*/0), ItemTypeSampleOne);
    EXPECT_EQ(GetTableViewItemType(/*section=*/1, /*item=*/0),
              ItemTypeSampleThree);
  } else {
    // Both the data items and the actions sections should be present in the
    // table view.
    EXPECT_EQ(NumberOfSections(), 2);

    // Both sections should have one item each and no header.
    EXPECT_FALSE(GetHeaderItem(/*section=*/0));
    EXPECT_EQ(NumberOfItemsInSection(0), 1);
    EXPECT_FALSE(GetHeaderItem(/*section=*/1));
    EXPECT_EQ(NumberOfItemsInSection(1), 1);

    EXPECT_EQ(GetTableViewItemType(/*section=*/0, /*item=*/0),
              ItemTypeSampleTwo);
    EXPECT_EQ(GetTableViewItemType(/*section=*/1, /*item=*/0),
              ItemTypeSampleThree);
  }
}

// Tests that the "no data items to show" message is displayed in the right
// place in the table view even if there are no action items to show.
TEST_P(FallbackViewControllerTest, CheckNoDataItemsMessageWhenNoActions) {
  // This test is only relevant when the Keyboard Accessory Upgrade feature is
  // enabled.
  if (!IsKeyboardAccessoryUpgradeEnabled()) {
    return;
  }

  FallbackViewController* fallback_view_controller =
      GetFallbackViewController();

  // Set `noDataItemsToShowHeaderItem`.
  TableViewTextHeaderFooterItem* header_item =
      [[TableViewTextHeaderFooterItem alloc] initWithType:ItemTypeSampleOne];
  fallback_view_controller.noDataItemsToShowHeaderItem = header_item;

  [fallback_view_controller presentDataItems:@[]];
  [fallback_view_controller presentActionItems:@[]];

  // Only the actions section should be present in the table view.
  EXPECT_EQ(NumberOfSections(), 1);

  // The actions section should only have a header.
  EXPECT_TRUE(GetHeaderItem(/*section=*/0));
  EXPECT_EQ(NumberOfItemsInSection(0), 0);

  EXPECT_EQ(GetHeaderItemType(/*section=*/0), ItemTypeSampleOne);
}

// Tests that the "no data items to show" message is removed once there are data
// items to show.
TEST_P(FallbackViewControllerTest, CheckNoDataItemsMessageRemoved) {
  // This test is only relevant when the Keyboard Accessory Upgrade feature is
  // enabled.
  if (!IsKeyboardAccessoryUpgradeEnabled()) {
    return;
  }

  FallbackViewController* fallback_view_controller =
      GetFallbackViewController();

  // Set `noDataItemsToShowHeaderItem`.
  TableViewTextHeaderFooterItem* header_item =
      [[TableViewTextHeaderFooterItem alloc] initWithType:ItemTypeSampleOne];
  fallback_view_controller.noDataItemsToShowHeaderItem = header_item;

  TableViewItem* action_item =
      [[TableViewItem alloc] initWithType:ItemTypeSampleTwo];

  // First, send no data items so that the "no data items to show" message is
  // displayed.
  [fallback_view_controller presentDataItems:@[]];
  [fallback_view_controller presentActionItems:@[ action_item ]];

  // Make sure that the table view content is as expected.
  EXPECT_EQ(NumberOfSections(), IsKeyboardAccessoryUpgradeEnabled() ? 2 : 1);
  EXPECT_TRUE(GetHeaderItem(/*section=*/0));
  EXPECT_EQ(NumberOfItemsInSection(0), !IsKeyboardAccessoryUpgradeEnabled());
  if (IsKeyboardAccessoryUpgradeEnabled()) {
    EXPECT_EQ(NumberOfItemsInSection(1), 1);
  }
  EXPECT_EQ(GetHeaderItemType(/*section=*/0), ItemTypeSampleOne);
  EXPECT_EQ(GetTableViewItemType(
                /*section=*/IsKeyboardAccessoryUpgradeEnabled(), /*item=*/0),
            ItemTypeSampleTwo);

  // Second, add a data item. This should have the effect of removing the "no
  // data items to show" message.
  TableViewItem* data_item =
      [[TableViewItem alloc] initWithType:ItemTypeSampleThree];
  [fallback_view_controller presentDataItems:@[ data_item ]];

  // There should now be two sections with one item each.
  EXPECT_EQ(NumberOfSections(), 2);
  EXPECT_EQ(NumberOfItemsInSection(0), 1);
  EXPECT_EQ(NumberOfItemsInSection(1), 1);
  EXPECT_EQ(GetTableViewItemType(/*section=*/0, /*item=*/0),
            ItemTypeSampleThree);
  EXPECT_EQ(GetTableViewItemType(/*section=*/1, /*item=*/0), ItemTypeSampleTwo);

  // The "no data items to show" message shouldn't be present anymore.
  EXPECT_FALSE(GetHeaderItem(/*section=*/1));
}

// Tests that the actions are separated by sections if they belong to different
// types.
TEST_P(FallbackViewControllerTest,
       CheckDifferentSectionsForActionsOfDifferentTypes) {
  TableViewItem* item_one =
      [[TableViewItem alloc] initWithType:ItemTypeSampleOne];
  TableViewItem* item_two =
      [[TableViewItem alloc] initWithType:ItemTypeSampleTwo];

  FallbackViewController* fallbackViewController = GetFallbackViewController();

  [fallbackViewController presentActionItems:@[ item_one ]];
  [fallbackViewController presentPlusAddressActionItems:@[ item_two ]];

  EXPECT_EQ(NumberOfSections(), 2);

  EXPECT_EQ(NumberOfItemsInSection(0), 1);
  EXPECT_EQ(NumberOfItemsInSection(1), 1);

  EXPECT_EQ(GetTableViewItemType(/*section=*/0, /*item=*/0), ItemTypeSampleOne);
  EXPECT_EQ(GetTableViewItemType(/*section=*/1, /*item=*/0), ItemTypeSampleTwo);
}

INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(FallbackViewControllerTest);