[iOS] Add test support for ChromeTableViewController
This CL adds a class to ease testing of subclasses of
ChromeTableViewController, in the same way CollectionViewControllerTest
is easing the testing of subclasses of CollectionViewController.
Bug: 894791
Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;luci.chromium.try:ios-simulator-full-configs
Change-Id: I159849612b16d5475d581c79601f93de04e6b705
Reviewed-on: https://chromium-review.googlesource.com/c/1283025
Commit-Queue: Gauthier Ambard <gambard@chromium.org>
Reviewed-by: Sergio Collazos <sczs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#600706}
diff --git a/ios/chrome/browser/ui/table_view/BUILD.gn b/ios/chrome/browser/ui/table_view/BUILD.gn
index 7b48a48..7a23b74 100644
--- a/ios/chrome/browser/ui/table_view/BUILD.gn
+++ b/ios/chrome/browser/ui/table_view/BUILD.gn
@@ -77,6 +77,23 @@
]
}
+source_set("test_support") {
+ configs += [ "//build/config/compiler:enable_arc" ]
+ testonly = true
+ sources = [
+ "chrome_table_view_controller_test.h",
+ "chrome_table_view_controller_test.mm",
+ ]
+ deps = [
+ ":table_view",
+ "//base",
+ "//ios/chrome/browser/ui/table_view/cells",
+ "//ios/chrome/test:test_support",
+ "//testing/gtest",
+ "//ui/base",
+ ]
+}
+
source_set("unit_tests") {
configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
@@ -85,6 +102,7 @@
]
deps = [
":table_view",
+ ":test_support",
"//ios/chrome/browser/ui/table_view/cells",
"//testing/gtest",
]
diff --git a/ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.h b/ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.h
new file mode 100644
index 0000000..f74a1a8
--- /dev/null
+++ b/ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.h
@@ -0,0 +1,115 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_TABLE_VIEW_CHROME_TABLE_VIEW_CONTROLLER_TEST_H_
+#define IOS_CHROME_BROWSER_UI_TABLE_VIEW_CHROME_TABLE_VIEW_CONTROLLER_TEST_H_
+
+#import <UIKit/UIKit.h>
+
+#include "base/compiler_specific.h"
+#import "base/ios/block_types.h"
+#import "ios/chrome/test/block_cleanup_test.h"
+
+@class ChromeTableViewController;
+
+class ChromeTableViewControllerTest : public BlockCleanupTest {
+ public:
+ ChromeTableViewControllerTest();
+ ~ChromeTableViewControllerTest() override;
+
+ protected:
+ void TearDown() override;
+
+ // Derived classes allocate their controller here.
+ virtual ChromeTableViewController* InstantiateController() = 0;
+
+ // Tests should call this function to create their controller for testing.
+ void CreateController();
+
+ // Will call CreateController() if |controller_| is nil.
+ ChromeTableViewController* controller();
+
+ // Deletes the controller.
+ void ResetController();
+
+ // Tests that the controller's view, model, tableView, and delegate are
+ // valid. Also tests that the model is the tableView's data source.
+ void CheckController();
+
+ // Returns the number of sections in the tableView.
+ int NumberOfSections();
+
+ // Returns the number of items in |section|.
+ int NumberOfItemsInSection(int section);
+
+ // Returns the collection view item at |item| in |section|.
+ id GetTableViewItem(int section, int item);
+
+ // Verifies that the title matches |expected_title|.
+ void CheckTitle(NSString* expected_title);
+
+ // Verifies that the title matches the l10n string for |expected_title_id|.
+ void CheckTitleWithId(int expected_title_id);
+
+ // Verifies that the section footer at |section| matches the |expected_text|.
+ void CheckSectionFooter(NSString* expected_text, int section);
+
+ // Verifies that the section footer at |section| matches the l10n string for
+ // |expected_text_id|.
+ void CheckSectionFooterWithId(int expected_text_id, int section);
+
+ // Verifies that the text cell at |item| in |section| has a text property
+ // which matches |expected_text|.
+ void CheckTextCellText(NSString* expected_text, int section, int item);
+
+ // Verifies that the text cell at |item| in |section| has a text property
+ // which matches the l10n string for |expected_text_id|.
+ void CheckTextCellTextWithId(int expected_text_id, int section, int item);
+
+ // Verifies that the text cell at |item| in |section| has a text and
+ // detailText properties which match strings for |expected_text| and
+ // |expected_detail_text|, respectively.
+ void CheckTextCellTextAndDetailText(NSString* expected_text,
+ NSString* expected_detail_text,
+ int section,
+ int item);
+
+ // Verifies that the text cell at |item| in |section| has a text and
+ // detailText properties which match strings for |expected_text| and
+ // |expected_detail_text|, respectively.
+ void CheckDetailItemTextWithIds(int expected_text_id,
+ int expected_detail_text_id,
+ int section_id,
+ int item_id);
+
+ // Verifies that the switch cell at |item| in |section| has a title which
+ // matches |expected_title| and is currently in |state|.
+ void CheckSwitchCellStateAndTitle(BOOL expected_state,
+ NSString* expected_title,
+ int section,
+ int item);
+
+ // Verifies that the switch cell at |item| in |section| has a title which
+ // matches the l10n string for |expected_title_id| and is currently in
+ // |state|.
+ void CheckSwitchCellStateAndTitleWithId(BOOL expected_state,
+ int expected_title_id,
+ int section,
+ int item);
+
+ // Verifies that the cell at |item| in |section| has the given
+ // |accessory_type|.
+ void CheckAccessoryType(UITableViewCellAccessoryType accessory_type,
+ int section,
+ int item);
+
+ // For |section|, deletes the item at |item|. |completion_block| is called at
+ // the end of the call to -performBatchUpdates:completion:.
+ void DeleteItem(int section, int item, ProceduralBlock completion_block);
+
+ private:
+ ChromeTableViewController* controller_;
+};
+
+#endif // IOS_CHROME_BROWSER_UI_TABLE_VIEW_CHROME_TABLE_VIEW_CONTROLLER_TEST_H_
diff --git a/ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.mm b/ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.mm
new file mode 100644
index 0000000..9e1e395
--- /dev/null
+++ b/ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.mm
@@ -0,0 +1,225 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// 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/table_view/chrome_table_view_controller_test.h"
+
+#include "base/logging.h"
+#import "base/mac/foundation_util.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h"
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
+
+#include "testing/gtest_mac.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Add selectors to be tested in the helpers.
+@interface TableViewItem (DetailTextAddition)
+- (NSString*)text;
+- (NSString*)detailText;
+@end
+
+ChromeTableViewControllerTest::ChromeTableViewControllerTest() {}
+
+ChromeTableViewControllerTest::~ChromeTableViewControllerTest() {}
+
+void ChromeTableViewControllerTest::TearDown() {
+ // Delete the controller before deleting other test variables, such as a
+ // profile, to ensure things are cleaned up in the same order as in Chrome.
+ controller_ = nil;
+ BlockCleanupTest::TearDown();
+}
+
+void ChromeTableViewControllerTest::CreateController() {
+ DCHECK(!controller_);
+ controller_ = InstantiateController();
+ // Force the model to be loaded.
+ [controller_ loadModel];
+ // Force the tableView to be built.
+ EXPECT_TRUE([controller_ view]);
+}
+
+ChromeTableViewController* ChromeTableViewControllerTest::controller() {
+ if (!controller_)
+ CreateController();
+ return controller_;
+}
+
+void ChromeTableViewControllerTest::ResetController() {
+ controller_ = nil;
+}
+
+void ChromeTableViewControllerTest::CheckController() {
+ EXPECT_TRUE([controller_ view]);
+ EXPECT_TRUE([controller_ tableView]);
+ EXPECT_TRUE([controller_ tableViewModel]);
+ EXPECT_EQ(controller_, [controller_ tableView].dataSource);
+ EXPECT_EQ(controller_, [controller_ tableView].delegate);
+}
+
+int ChromeTableViewControllerTest::NumberOfSections() {
+ return [[controller_ tableViewModel] numberOfSections];
+}
+
+int ChromeTableViewControllerTest::NumberOfItemsInSection(int section) {
+ return [[controller_ tableViewModel] numberOfItemsInSection:section];
+}
+
+id ChromeTableViewControllerTest::GetTableViewItem(int section, int item) {
+ TableViewModel* model = [controller_ tableViewModel];
+ NSIndexPath* index_path =
+ [NSIndexPath indexPathForItem:item inSection:section];
+ TableViewItem* collection_view_item = [model hasItemAtIndexPath:index_path]
+ ? [model itemAtIndexPath:index_path]
+ : nil;
+ EXPECT_TRUE(collection_view_item);
+ return collection_view_item;
+}
+
+void ChromeTableViewControllerTest::CheckTitle(NSString* expected_title) {
+ EXPECT_NSEQ(expected_title, [controller_ title]);
+}
+
+void ChromeTableViewControllerTest::CheckTitleWithId(int expected_title_id) {
+ CheckTitle(l10n_util::GetNSString(expected_title_id));
+}
+
+void ChromeTableViewControllerTest::CheckSectionFooter(NSString* expected_text,
+ int section) {
+ // TODO(crbug.com/894791): Implement this.
+ NOTREACHED();
+}
+
+void ChromeTableViewControllerTest::CheckSectionFooterWithId(
+ int expected_text_id,
+ int section) {
+ return CheckSectionFooter(l10n_util::GetNSString(expected_text_id), section);
+}
+
+void ChromeTableViewControllerTest::CheckTextCellText(NSString* expected_text,
+ int section,
+ int item) {
+ id cell = GetTableViewItem(section, item);
+ ASSERT_TRUE([cell respondsToSelector:@selector(text)]);
+ ASSERT_TRUE([cell respondsToSelector:@selector(detailText)]);
+ EXPECT_NSEQ(expected_text, [cell text]);
+ EXPECT_FALSE([cell detailText]);
+}
+
+void ChromeTableViewControllerTest::CheckTextCellTextWithId(
+ int expected_text_id,
+ int section,
+ int item) {
+ CheckTextCellText(l10n_util::GetNSString(expected_text_id), section, item);
+}
+
+void ChromeTableViewControllerTest::CheckTextCellTextAndDetailText(
+ NSString* expected_text,
+ NSString* expected_detail_text,
+ int section,
+ int item) {
+ id cell = GetTableViewItem(section, item);
+ ASSERT_TRUE([cell respondsToSelector:@selector(text)]);
+ ASSERT_TRUE([cell respondsToSelector:@selector(detailText)]);
+ EXPECT_NSEQ(expected_text, [cell text]);
+ EXPECT_NSEQ(expected_detail_text, [cell detailText]);
+}
+
+void ChromeTableViewControllerTest::CheckDetailItemTextWithIds(
+ int expected_text_id,
+ int expected_detail_text_id,
+ int section_id,
+ int item_id) {
+ id item = GetTableViewItem(section_id, item_id);
+ ASSERT_TRUE([item respondsToSelector:@selector(text)]);
+ ASSERT_TRUE([item respondsToSelector:@selector(detailText)]);
+ EXPECT_NSEQ(l10n_util::GetNSString(expected_text_id), [item text]);
+ EXPECT_NSEQ(l10n_util::GetNSString(expected_detail_text_id),
+ [item detailText]);
+}
+
+void ChromeTableViewControllerTest::CheckSwitchCellStateAndTitle(
+ BOOL expected_state,
+ NSString* expected_title,
+ int section,
+ int item) {
+ // TODO(crbug.com/894791): Implement this.
+ NOTREACHED();
+}
+
+void ChromeTableViewControllerTest::CheckSwitchCellStateAndTitleWithId(
+ BOOL expected_state,
+ int expected_title_id,
+ int section,
+ int item) {
+ CheckSwitchCellStateAndTitle(
+ expected_state, l10n_util::GetNSString(expected_title_id), section, item);
+}
+
+void ChromeTableViewControllerTest::CheckAccessoryType(
+ UITableViewCellAccessoryType accessory_type,
+ int section,
+ int item) {
+ id text_item = GetTableViewItem(section, item);
+ EXPECT_TRUE([text_item respondsToSelector:@selector(accessoryType)]);
+ EXPECT_EQ(accessory_type, [text_item accessoryType]);
+}
+
+void ChromeTableViewControllerTest::DeleteItem(
+ int section,
+ int item,
+ ProceduralBlock completion_block) {
+ NSIndexPath* index_path =
+ [NSIndexPath indexPathForItem:item inSection:section];
+ __weak ChromeTableViewController* weak_controller = controller_;
+ void (^batch_updates)() = ^{
+ ChromeTableViewController* strong_controller = weak_controller;
+ if (!strong_controller)
+ return;
+ // Delete data in the model.
+ TableViewModel* model = strong_controller.tableViewModel;
+ NSInteger section_ID =
+ [model sectionIdentifierForSection:index_path.section];
+ NSInteger item_type = [model itemTypeForIndexPath:index_path];
+ NSUInteger index = [model indexInItemTypeForIndexPath:index_path];
+ [model removeItemWithType:item_type
+ fromSectionWithIdentifier:section_ID
+ atIndex:index];
+
+ // Delete in the table view.
+ [[strong_controller tableView]
+ deleteRowsAtIndexPaths:@[ index_path ]
+ withRowAnimation:UITableViewRowAnimationNone];
+ };
+
+ void (^completion)(BOOL finished) = ^(BOOL finished) {
+ if (completion_block) {
+ completion_block();
+ }
+ };
+ if (@available(iOS 11.0, *)) {
+ [[controller_ tableView] performBatchUpdates:batch_updates
+ completion:completion];
+ } else {
+ TableViewModel* model = controller_.tableViewModel;
+ NSInteger section_ID =
+ [model sectionIdentifierForSection:index_path.section];
+ NSInteger item_type = [model itemTypeForIndexPath:index_path];
+ NSUInteger index = [model indexInItemTypeForIndexPath:index_path];
+ [model removeItemWithType:item_type
+ fromSectionWithIdentifier:section_ID
+ atIndex:index];
+
+ // Delete in the table view.
+ [[controller_ tableView]
+ deleteRowsAtIndexPaths:@[ index_path ]
+ withRowAnimation:UITableViewRowAnimationNone];
+
+ if (completion_block) {
+ completion_block();
+ }
+ }
+}