apps_search_results_controller_unittest.mm revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import "ui/app_list/cocoa/apps_search_results_controller.h"
6
7#include "base/memory/scoped_nsobject.h"
8#include "base/stringprintf.h"
9#include "base/strings/sys_string_conversions.h"
10#include "base/strings/utf_string_conversions.h"
11#import "testing/gtest_mac.h"
12#include "ui/app_list/search_result.h"
13#include "ui/app_list/test/app_list_test_model.h"
14#import "ui/base/test/ui_cocoa_test_helper.h"
15#include "ui/gfx/image/image_skia_util_mac.h"
16
17@interface TestAppsSearchResultsDelegate : NSObject<AppsSearchResultsDelegate> {
18 @private
19  app_list::test::AppListTestModel appListModel_;
20  app_list::SearchResult* lastOpenedResult_;
21}
22
23@property(readonly, nonatomic) app_list::SearchResult* lastOpenedResult;
24
25@end
26
27@implementation TestAppsSearchResultsDelegate
28
29@synthesize lastOpenedResult = lastOpenedResult_;
30
31- (app_list::AppListModel*)appListModel {
32  return &appListModel_;
33}
34
35- (void)openResult:(app_list::SearchResult*)result {
36  lastOpenedResult_ = result;
37}
38
39@end
40
41namespace {
42
43const int kDefaultResultsCount = 3;
44
45}  // namespace
46
47namespace app_list {
48namespace test {
49
50class AppsSearchResultsControllerTest : public ui::CocoaTest {
51 public:
52  AppsSearchResultsControllerTest() {}
53
54  void AddTestResultAtIndex(size_t index,
55                            const std::string& title,
56                            const std::string& details) {
57    scoped_ptr<SearchResult> result(new SearchResult());
58    result->set_title(ASCIIToUTF16(title));
59    result->set_details(ASCIIToUTF16(details));
60    AppListModel::SearchResults* results = [delegate_ appListModel]->results();
61    results->AddAt(index, result.release());
62  }
63
64  SearchResult* ModelResultAt(size_t index) {
65    return [delegate_ appListModel]->results()->GetItemAt(index);
66  }
67
68  NSCell* ViewResultAt(NSInteger index) {
69    NSTableView* table_view = [apps_search_results_controller_ tableView];
70    return [table_view preparedCellAtColumn:0
71                                        row:index];
72  }
73
74  BOOL SimulateKeyAction(SEL c) {
75    return [apps_search_results_controller_ handleCommandBySelector:c];
76  }
77
78  void ExpectConsistent();
79
80  // ui::CocoaTest overrides:
81  virtual void SetUp() OVERRIDE;
82  virtual void TearDown() OVERRIDE;
83
84 protected:
85  scoped_nsobject<TestAppsSearchResultsDelegate> delegate_;
86  scoped_nsobject<AppsSearchResultsController> apps_search_results_controller_;
87
88 private:
89  DISALLOW_COPY_AND_ASSIGN(AppsSearchResultsControllerTest);
90};
91
92void AppsSearchResultsControllerTest::ExpectConsistent() {
93  NSInteger item_count = [delegate_ appListModel]->results()->item_count();
94  ASSERT_EQ(item_count,
95            [[apps_search_results_controller_ tableView] numberOfRows]);
96
97  // Compare content strings to ensure the order of items is consistent, and any
98  // model data that should have been reloaded has been reloaded in the view.
99  for (NSInteger i = 0; i < item_count; ++i) {
100    SearchResult* result = ModelResultAt(i);
101    base::string16 string_in_model = result->title();
102    if (!result->details().empty())
103      string_in_model += ASCIIToUTF16("\n") + result->details();
104    EXPECT_NSEQ(base::SysUTF16ToNSString(string_in_model),
105                [[ViewResultAt(i) attributedStringValue] string]);
106  }
107}
108
109void AppsSearchResultsControllerTest::SetUp() {
110  apps_search_results_controller_.reset(
111      [[AppsSearchResultsController alloc] initWithAppsSearchResultsFrameSize:
112          NSMakeSize(400, 400)]);
113  // The view is initially hidden. Give it a non-zero height so it draws.
114  [[apps_search_results_controller_ view] setFrameSize:NSMakeSize(400, 400)];
115
116  delegate_.reset([[TestAppsSearchResultsDelegate alloc] init]);
117
118  // Populate with some results so that TEST_VIEW does something non-trivial.
119  for (int i = 0; i < kDefaultResultsCount; ++i)
120    AddTestResultAtIndex(i, base::StringPrintf("Result %d", i), "ItemDetail");
121
122  SearchResult::Tags test_tags;
123  // Apply markup to the substring "Result" in the first item.
124  test_tags.push_back(SearchResult::Tag(SearchResult::Tag::NONE, 0, 1));
125  test_tags.push_back(SearchResult::Tag(SearchResult::Tag::URL, 1, 2));
126  test_tags.push_back(SearchResult::Tag(SearchResult::Tag::MATCH, 2, 3));
127  test_tags.push_back(SearchResult::Tag(SearchResult::Tag::DIM, 3, 4));
128  test_tags.push_back(SearchResult::Tag(SearchResult::Tag::MATCH |
129                                        SearchResult::Tag::URL, 4, 5));
130  test_tags.push_back(SearchResult::Tag(SearchResult::Tag::MATCH |
131                                        SearchResult::Tag::DIM, 5, 6));
132
133  SearchResult* result = ModelResultAt(0);
134  result->SetIcon(gfx::ImageSkiaFromNSImage(
135      [NSImage imageNamed:NSImageNameStatusAvailable]));
136  result->set_title_tags(test_tags);
137
138  [apps_search_results_controller_ setDelegate:delegate_];
139
140  ui::CocoaTest::SetUp();
141  [[test_window() contentView] addSubview:
142      [apps_search_results_controller_ view]];
143}
144
145void AppsSearchResultsControllerTest::TearDown() {
146  [apps_search_results_controller_ setDelegate:nil];
147  ui::CocoaTest::TearDown();
148}
149
150TEST_VIEW(AppsSearchResultsControllerTest,
151          [apps_search_results_controller_ view]);
152
153TEST_F(AppsSearchResultsControllerTest, ModelObservers) {
154  NSTableView* table_view = [apps_search_results_controller_ tableView];
155  ExpectConsistent();
156
157  EXPECT_EQ(1, [table_view numberOfColumns]);
158  EXPECT_EQ(kDefaultResultsCount, [table_view numberOfRows]);
159
160  // Insert at start.
161  AddTestResultAtIndex(0, "One", std::string());
162  EXPECT_EQ(kDefaultResultsCount + 1, [table_view numberOfRows]);
163  ExpectConsistent();
164
165  // Remove from end.
166  [delegate_ appListModel]->results()->DeleteAt(kDefaultResultsCount);
167  EXPECT_EQ(kDefaultResultsCount, [table_view numberOfRows]);
168  ExpectConsistent();
169
170  // Insert at end.
171  AddTestResultAtIndex(kDefaultResultsCount, "Four", std::string());
172  EXPECT_EQ(kDefaultResultsCount + 1, [table_view numberOfRows]);
173  ExpectConsistent();
174
175  // Delete from start.
176  [delegate_ appListModel]->results()->DeleteAt(0);
177  EXPECT_EQ(kDefaultResultsCount, [table_view numberOfRows]);
178  ExpectConsistent();
179
180  // Test clearing results.
181  [delegate_ appListModel]->results()->DeleteAll();
182  EXPECT_EQ(0, [table_view numberOfRows]);
183  ExpectConsistent();
184}
185
186TEST_F(AppsSearchResultsControllerTest, KeyboardSelectAndActivate) {
187  NSTableView* table_view = [apps_search_results_controller_ tableView];
188  EXPECT_EQ(-1, [table_view selectedRow]);
189
190  // Pressing up when nothing is selected should select the last item.
191  EXPECT_TRUE(SimulateKeyAction(@selector(moveUp:)));
192  EXPECT_EQ(kDefaultResultsCount - 1, [table_view selectedRow]);
193  [table_view deselectAll:nil];
194  EXPECT_EQ(-1, [table_view selectedRow]);
195
196  // Pressing down when nothing is selected should select the first item.
197  EXPECT_TRUE(SimulateKeyAction(@selector(moveDown:)));
198  EXPECT_EQ(0, [table_view selectedRow]);
199
200  // Pressing up should wrap around.
201  EXPECT_TRUE(SimulateKeyAction(@selector(moveUp:)));
202  EXPECT_EQ(kDefaultResultsCount - 1, [table_view selectedRow]);
203
204  // Down should now also wrap, since the selection is at the end.
205  EXPECT_TRUE(SimulateKeyAction(@selector(moveDown:)));
206  EXPECT_EQ(0, [table_view selectedRow]);
207
208  // Regular down and up movement, ensuring the cells have correct backgrounds.
209  EXPECT_TRUE(SimulateKeyAction(@selector(moveDown:)));
210  EXPECT_EQ(1, [table_view selectedRow]);
211  EXPECT_EQ(NSBackgroundStyleDark, [ViewResultAt(1) backgroundStyle]);
212  EXPECT_EQ(NSBackgroundStyleLight, [ViewResultAt(0) backgroundStyle]);
213
214  EXPECT_TRUE(SimulateKeyAction(@selector(moveUp:)));
215  EXPECT_EQ(0, [table_view selectedRow]);
216  EXPECT_EQ(NSBackgroundStyleDark, [ViewResultAt(0) backgroundStyle]);
217  EXPECT_EQ(NSBackgroundStyleLight, [ViewResultAt(1) backgroundStyle]);
218
219  // Test activating items.
220  EXPECT_TRUE(SimulateKeyAction(@selector(insertNewline:)));
221  EXPECT_EQ(ModelResultAt(0), [delegate_ lastOpenedResult]);
222  EXPECT_TRUE(SimulateKeyAction(@selector(moveDown:)));
223  EXPECT_TRUE(SimulateKeyAction(@selector(insertNewline:)));
224  EXPECT_EQ(ModelResultAt(1), [delegate_ lastOpenedResult]);
225}
226
227}  // namespace test
228}  // namespace app_list
229