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#include "base/mac/foundation_util.h"
6#include "base/mac/scoped_nsobject.h"
7#include "base/strings/utf_string_conversions.h"
8#include "skia/ext/skia_utils_mac.h"
9#import "testing/gtest_mac.h"
10#include "ui/app_list/app_list_constants.h"
11#include "ui/app_list/app_list_item.h"
12#import "ui/app_list/cocoa/apps_collection_view_drag_manager.h"
13#import "ui/app_list/cocoa/apps_grid_controller.h"
14#import "ui/app_list/cocoa/apps_grid_view_item.h"
15#import "ui/app_list/cocoa/apps_pagination_model_observer.h"
16#import "ui/app_list/cocoa/test/apps_grid_controller_test_helper.h"
17#include "ui/app_list/test/app_list_test_model.h"
18#include "ui/app_list/test/app_list_test_view_delegate.h"
19#include "ui/base/models/simple_menu_model.h"
20#import "ui/events/test/cocoa_test_event_utils.h"
21
22@interface TestPaginationObserver : NSObject<AppsPaginationModelObserver> {
23 @private
24  NSInteger hoveredSegmentForTest_;
25  int totalPagesChangedCount_;
26  int selectedPageChangedCount_;
27  int lastNewSelectedPage_;
28  bool visibilityDidChange_;
29}
30
31@property(assign, nonatomic) NSInteger hoveredSegmentForTest;
32@property(assign, nonatomic) int totalPagesChangedCount;
33@property(assign, nonatomic) int selectedPageChangedCount;
34@property(assign, nonatomic) int lastNewSelectedPage;
35
36- (bool)readVisibilityDidChange;
37
38@end
39
40@implementation TestPaginationObserver
41
42@synthesize hoveredSegmentForTest = hoveredSegmentForTest_;
43@synthesize totalPagesChangedCount = totalPagesChangedCount_;
44@synthesize selectedPageChangedCount = selectedPageChangedCount_;
45@synthesize lastNewSelectedPage = lastNewSelectedPage_;
46
47- (id)init {
48  if ((self = [super init]))
49    hoveredSegmentForTest_ = -1;
50
51  return self;
52}
53
54- (bool)readVisibilityDidChange {
55  bool truth = visibilityDidChange_;
56  visibilityDidChange_ = false;
57  return truth;
58}
59
60- (void)totalPagesChanged {
61  ++totalPagesChangedCount_;
62}
63
64- (void)selectedPageChanged:(int)newSelected {
65  ++selectedPageChangedCount_;
66  lastNewSelectedPage_ = newSelected;
67}
68
69- (void)pageVisibilityChanged {
70  visibilityDidChange_ = true;
71}
72
73- (NSInteger)pagerSegmentAtLocation:(NSPoint)locationInWindow {
74  return hoveredSegmentForTest_;
75}
76
77@end
78
79namespace app_list {
80namespace test {
81
82namespace {
83
84class AppsGridControllerTest : public AppsGridControllerTestHelper {
85 public:
86  AppsGridControllerTest() {}
87
88  AppListTestViewDelegate* delegate() {
89    return owned_delegate_.get();
90  }
91
92  NSColor* ButtonTitleColorAt(size_t index) {
93    NSDictionary* attributes =
94        [[[GetItemViewAt(index) cell] attributedTitle] attributesAtIndex:0
95                                                          effectiveRange:NULL];
96    return [attributes objectForKey:NSForegroundColorAttributeName];
97  }
98
99  virtual void SetUp() OVERRIDE {
100    owned_apps_grid_controller_.reset([[AppsGridController alloc] init]);
101    owned_delegate_.reset(new AppListTestViewDelegate);
102    [owned_apps_grid_controller_ setDelegate:owned_delegate_.get()];
103    AppsGridControllerTestHelper::SetUpWithGridController(
104        owned_apps_grid_controller_.get());
105
106    [[test_window() contentView] addSubview:[apps_grid_controller_ view]];
107    [test_window() makePretendKeyWindowAndSetFirstResponder:
108        [apps_grid_controller_ collectionViewAtPageIndex:0]];
109  }
110
111  virtual void TearDown() OVERRIDE {
112    [owned_apps_grid_controller_ setDelegate:NULL];
113    owned_apps_grid_controller_.reset();
114    AppsGridControllerTestHelper::TearDown();
115  }
116
117  void ReplaceTestModel(int item_count) {
118    // Clear the delegate before reseting and destroying the model.
119    [owned_apps_grid_controller_ setDelegate:NULL];
120
121    owned_delegate_->ReplaceTestModel(item_count);
122    [owned_apps_grid_controller_ setDelegate:owned_delegate_.get()];
123  }
124
125  AppListTestModel* model() { return owned_delegate_->GetTestModel(); }
126
127 private:
128  base::scoped_nsobject<AppsGridController> owned_apps_grid_controller_;
129  scoped_ptr<AppListTestViewDelegate> owned_delegate_;
130
131  DISALLOW_COPY_AND_ASSIGN(AppsGridControllerTest);
132};
133
134class AppListItemWithMenu : public AppListItem {
135 public:
136  explicit AppListItemWithMenu(const std::string& id)
137      : AppListItem(id),
138        menu_model_(NULL),
139        menu_ready_(true) {
140    SetName(id);
141    menu_model_.AddItem(0, base::UTF8ToUTF16("Menu For: " + id));
142  }
143
144  void SetMenuReadyForTesting(bool ready) {
145    menu_ready_ = ready;
146  }
147
148  virtual ui::MenuModel* GetContextMenuModel() OVERRIDE {
149    if (!menu_ready_)
150      return NULL;
151
152    return &menu_model_;
153  }
154
155 private:
156  ui::SimpleMenuModel menu_model_;
157  bool menu_ready_;
158
159  DISALLOW_COPY_AND_ASSIGN(AppListItemWithMenu);
160};
161
162// Generate a mouse event at the centre of the view in |page| with the given
163// |index_in_page| that can be used to initiate, update and complete drag
164// operations.
165NSEvent* MouseEventInCell(NSCollectionView* page, size_t index_in_page) {
166  NSRect cell_rect = [page frameForItemAtIndex:index_in_page];
167  NSPoint point_in_view = NSMakePoint(NSMidX(cell_rect), NSMidY(cell_rect));
168  NSPoint point_in_window = [page convertPoint:point_in_view
169                                        toView:nil];
170  return cocoa_test_event_utils::LeftMouseDownAtPoint(point_in_window);
171}
172
173NSEvent* MouseEventForScroll(NSView* view, CGFloat relative_x) {
174  NSRect view_rect = [view frame];
175  NSPoint point_in_view = NSMakePoint(NSMidX(view_rect), NSMidY(view_rect));
176  point_in_view.x += point_in_view.x * relative_x;
177  NSPoint point_in_window = [view convertPoint:point_in_view
178                                        toView:nil];
179  return cocoa_test_event_utils::LeftMouseDownAtPoint(point_in_window);
180}
181
182}  // namespace
183
184TEST_VIEW(AppsGridControllerTest, [apps_grid_controller_ view]);
185
186// Test showing with an empty model.
187TEST_F(AppsGridControllerTest, EmptyModelAndShow) {
188  EXPECT_TRUE([[apps_grid_controller_ view] superview]);
189
190  // First page should always exist, even if empty.
191  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
192  EXPECT_EQ(0u, [[GetPageAt(0) content] count]);
193  EXPECT_TRUE([GetPageAt(0) superview]);  // The pages container.
194  EXPECT_TRUE([[GetPageAt(0) superview] superview]);
195}
196
197// Test with a single item.
198// This test is disabled in builders until the delay to wait for the collection
199// view to load subviews can be removed, or some other solution is found.
200TEST_F(AppsGridControllerTest, DISABLED_SingleEntryModel) {
201  // We need to "wake up" the NSCollectionView, otherwise it does not
202  // immediately update its subviews later in this function.
203  // When this test is run by itself, it's enough just to send a keypress (and
204  // this delay is not needed).
205  DelayForCollectionView();
206  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
207  EXPECT_EQ(0u, [[GetPageAt(0) content] count]);
208
209  model()->PopulateApps(1);
210  SinkEvents();
211  EXPECT_FALSE([GetPageAt(0) animations]);
212
213  EXPECT_EQ(1u, [[GetPageAt(0) content] count]);
214  NSArray* subviews = [GetPageAt(0) subviews];
215  EXPECT_EQ(1u, [subviews count]);
216
217  // Note that using GetItemViewAt(0) here also works, and returns non-nil even
218  // without the delay, but a "click" on it does not register without the delay.
219  NSView* subview = [subviews objectAtIndex:0];
220
221  // Launch the item.
222  SimulateClick(subview);
223  SinkEvents();
224  EXPECT_EQ(1, model()->activate_count());
225  ASSERT_TRUE(model()->last_activated());
226  EXPECT_EQ(std::string("Item 0"), model()->last_activated()->name());
227}
228
229// Test activating an item on the second page (the 17th item).
230TEST_F(AppsGridControllerTest, DISABLED_TwoPageModel) {
231  DelayForCollectionView();
232  ReplaceTestModel(kItemsPerPage * 2);
233  [apps_grid_controller_ scrollToPage:1];
234
235  // The NSScrollView animator ignores the duration configured on the
236  // NSAnimationContext (set by CocoaTest::Init), so we need to delay here.
237  DelayForCollectionView();
238  NSArray* subviews = [GetPageAt(1) subviews];
239  NSView* subview = [subviews objectAtIndex:0];
240  // Launch the item.
241  SimulateClick(subview);
242  SinkEvents();
243  EXPECT_EQ(1, model()->activate_count());
244  ASSERT_TRUE(model()->last_activated());
245  EXPECT_EQ(std::string("Item 16"), model()->last_activated()->name());
246}
247
248// Test setModel.
249TEST_F(AppsGridControllerTest, ReplaceModel) {
250  const size_t kOrigItems = 1;
251  const size_t kNewItems = 2;
252
253  model()->PopulateApps(kOrigItems);
254  EXPECT_EQ(kOrigItems, [[GetPageAt(0) content] count]);
255
256  ReplaceTestModel(kNewItems);
257  EXPECT_EQ(kNewItems, [[GetPageAt(0) content] count]);
258}
259
260// Test pagination.
261TEST_F(AppsGridControllerTest, Pagination) {
262  model()->PopulateApps(1);
263  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
264  EXPECT_EQ(1u, [[GetPageAt(0) content] count]);
265
266  ReplaceTestModel(kItemsPerPage);
267  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
268  EXPECT_EQ(kItemsPerPage, [[GetPageAt(0) content] count]);
269
270  // Test adding an item onto the next page.
271  model()->PopulateApps(1);  // Now 17 items.
272  EXPECT_EQ(2u, [apps_grid_controller_ pageCount]);
273  EXPECT_EQ(kItemsPerPage, [[GetPageAt(0) content] count]);
274  EXPECT_EQ(1u, [[GetPageAt(1) content] count]);
275
276  // Test N pages with the last page having one empty spot.
277  const size_t kPagesToTest = 3;
278  ReplaceTestModel(kPagesToTest * kItemsPerPage - 1);
279  EXPECT_EQ(kPagesToTest, [apps_grid_controller_ pageCount]);
280  for (size_t page_index = 0; page_index < kPagesToTest - 1; ++page_index) {
281    EXPECT_EQ(kItemsPerPage, [[GetPageAt(page_index) content] count]);
282  }
283  EXPECT_EQ(kItemsPerPage - 1, [[GetPageAt(kPagesToTest - 1) content] count]);
284
285  // Test removing pages.
286  ReplaceTestModel(1);
287  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
288  EXPECT_EQ(1u, [[GetPageAt(0) content] count]);
289}
290
291// Tests that selecting an item changes the text color correctly.
292TEST_F(AppsGridControllerTest, SelectionChangesTextColor) {
293  model()->PopulateApps(2);
294  [apps_grid_controller_ selectItemAtIndex:0];
295  EXPECT_NSEQ(ButtonTitleColorAt(0),
296              gfx::SkColorToSRGBNSColor(app_list::kGridTitleHoverColor));
297  EXPECT_NSEQ(ButtonTitleColorAt(1),
298              gfx::SkColorToSRGBNSColor(app_list::kGridTitleColor));
299
300  [apps_grid_controller_ selectItemAtIndex:1];
301  EXPECT_NSEQ(ButtonTitleColorAt(0),
302              gfx::SkColorToSRGBNSColor(app_list::kGridTitleColor));
303  EXPECT_NSEQ(ButtonTitleColorAt(1),
304              gfx::SkColorToSRGBNSColor(app_list::kGridTitleHoverColor));
305}
306
307// Tests basic keyboard navigation on the first page.
308TEST_F(AppsGridControllerTest, FirstPageKeyboardNavigation) {
309  model()->PopulateApps(kItemsPerPage - 2);
310  EXPECT_EQ(kItemsPerPage - 2, [[GetPageAt(0) content] count]);
311
312  SimulateKeyAction(@selector(moveRight:));
313  EXPECT_EQ(0u, [apps_grid_controller_ selectedItemIndex]);
314
315  SimulateKeyAction(@selector(moveRight:));
316  EXPECT_EQ(1u, [apps_grid_controller_ selectedItemIndex]);
317
318  SimulateKeyAction(@selector(moveDown:));
319  EXPECT_EQ(5u, [apps_grid_controller_ selectedItemIndex]);
320
321  SimulateKeyAction(@selector(moveLeft:));
322  EXPECT_EQ(4u, [apps_grid_controller_ selectedItemIndex]);
323
324  SimulateKeyAction(@selector(moveUp:));
325  EXPECT_EQ(0u, [apps_grid_controller_ selectedItemIndex]);
326
327  // Go to the third item, and launch it.
328  SimulateKeyAction(@selector(moveRight:));
329  SimulateKeyAction(@selector(moveRight:));
330  EXPECT_EQ(2u, [apps_grid_controller_ selectedItemIndex]);
331  SimulateKeyAction(@selector(insertNewline:));
332  EXPECT_EQ(1, model()->activate_count());
333  ASSERT_TRUE(model()->last_activated());
334  EXPECT_EQ(std::string("Item 2"), model()->last_activated()->name());
335}
336
337// Tests keyboard navigation across pages.
338TEST_F(AppsGridControllerTest, CrossPageKeyboardNavigation) {
339  model()->PopulateApps(kItemsPerPage + 10);
340  EXPECT_EQ(kItemsPerPage, [[GetPageAt(0) content] count]);
341  EXPECT_EQ(10u, [[GetPageAt(1) content] count]);
342
343  // Moving Left, Up, or PageUp from the top-left corner of the first page does
344  // nothing.
345  [apps_grid_controller_ selectItemAtIndex:0];
346  SimulateKeyAction(@selector(moveLeft:));
347  EXPECT_EQ(0u, [apps_grid_controller_ selectedItemIndex]);
348  SimulateKeyAction(@selector(moveUp:));
349  EXPECT_EQ(0u, [apps_grid_controller_ selectedItemIndex]);
350  SimulateKeyAction(@selector(scrollPageUp:));
351  EXPECT_EQ(0u, [apps_grid_controller_ selectedItemIndex]);
352
353  // Moving Right from the right side goes to the next page. Moving Left goes
354  // back to the first page.
355  [apps_grid_controller_ selectItemAtIndex:3];
356  SimulateKeyAction(@selector(moveRight:));
357  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
358  EXPECT_EQ(kItemsPerPage, [apps_grid_controller_ selectedItemIndex]);
359  SimulateKeyAction(@selector(moveLeft:));
360  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
361  EXPECT_EQ(3u, [apps_grid_controller_ selectedItemIndex]);
362
363  // Moving Down from the bottom does nothing.
364  [apps_grid_controller_ selectItemAtIndex:13];
365  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
366  SimulateKeyAction(@selector(moveDown:));
367  EXPECT_EQ(13u, [apps_grid_controller_ selectedItemIndex]);
368
369  // Moving Right into a non-existent square on the next page will select the
370  // last item.
371  [apps_grid_controller_ selectItemAtIndex:15];
372  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
373  SimulateKeyAction(@selector(moveRight:));
374  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
375  EXPECT_EQ(kItemsPerPage + 9, [apps_grid_controller_ selectedItemIndex]);
376
377  // PageDown and PageUp switches pages while maintaining the same selection
378  // position.
379  [apps_grid_controller_ selectItemAtIndex:6];
380  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
381  SimulateKeyAction(@selector(scrollPageDown:));
382  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
383  EXPECT_EQ(kItemsPerPage + 6, [apps_grid_controller_ selectedItemIndex]);
384  SimulateKeyAction(@selector(scrollPageUp:));
385  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
386  EXPECT_EQ(6u, [apps_grid_controller_ selectedItemIndex]);
387
388  // PageDown into a non-existent square on the next page will select the last
389  // item.
390  [apps_grid_controller_ selectItemAtIndex:11];
391  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
392  SimulateKeyAction(@selector(scrollPageDown:));
393  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
394  EXPECT_EQ(kItemsPerPage + 9, [apps_grid_controller_ selectedItemIndex]);
395
396  // Moving Right, Down, or PageDown from the bottom-right corner of the last
397  // page (not the last item) does nothing.
398  [apps_grid_controller_ selectItemAtIndex:kItemsPerPage + 9];
399  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
400  SimulateKeyAction(@selector(moveRight:));
401  EXPECT_EQ(kItemsPerPage + 9, [apps_grid_controller_ selectedItemIndex]);
402  SimulateKeyAction(@selector(moveDown:));
403  EXPECT_EQ(kItemsPerPage + 9, [apps_grid_controller_ selectedItemIndex]);
404  SimulateKeyAction(@selector(scrollPageDown:));
405  EXPECT_EQ(kItemsPerPage + 9, [apps_grid_controller_ selectedItemIndex]);
406
407  // After page switch, arrow keys select first item on current page.
408  [apps_grid_controller_ scrollToPage:0];
409  [apps_grid_controller_ scrollToPage:1];
410  EXPECT_EQ(NSNotFound, [apps_grid_controller_ selectedItemIndex]);
411  SimulateKeyAction(@selector(moveUp:));
412  EXPECT_EQ(kItemsPerPage, [apps_grid_controller_ selectedItemIndex]);
413}
414
415// Highlighting an item should cause the page it's on to be visible.
416TEST_F(AppsGridControllerTest, EnsureHighlightedVisible) {
417  model()->PopulateApps(3 * kItemsPerPage);
418  EXPECT_EQ(kItemsPerPage, [[GetPageAt(2) content] count]);
419
420  // First and last items of first page.
421  [apps_grid_controller_ selectItemAtIndex:0];
422  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
423  [apps_grid_controller_ selectItemAtIndex:kItemsPerPage - 1];
424  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
425
426  // First item of second page.
427  [apps_grid_controller_ selectItemAtIndex:kItemsPerPage + 1];
428  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
429
430  // Last item in model.
431  [apps_grid_controller_ selectItemAtIndex:3 * kItemsPerPage - 1];
432  EXPECT_EQ(2u, [apps_grid_controller_ visiblePage]);
433}
434
435// Test runtime updates: adding items, removing items, and moving items (e.g. in
436// response to app install, uninstall, and chrome sync changes. Also test
437// changing titles and icons.
438TEST_F(AppsGridControllerTest, ModelUpdate) {
439  model()->PopulateApps(2);
440  EXPECT_EQ(2u, [[GetPageAt(0) content] count]);
441  EXPECT_EQ(std::string("|Item 0,Item 1|"), GetViewContent());
442
443  // Add an item (PopulateApps will create a new "Item 2").
444  model()->PopulateApps(1);
445  EXPECT_EQ(3u, [[GetPageAt(0) content] count]);
446  NSButton* button = GetItemViewAt(2);
447  EXPECT_NSEQ(@"Item 2", [button title]);
448  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
449
450  // Update the title via the ItemModelObserver.
451  app_list::AppListItem* item_model =
452      model()->top_level_item_list()->item_at(2);
453  model()->SetItemName(item_model, "UpdatedItem");
454  EXPECT_NSEQ(@"UpdatedItem", [button title]);
455
456  // Test icon updates through the model observer by ensuring the icon changes.
457  item_model->SetIcon(gfx::ImageSkia(), false);
458  NSSize icon_size = [[button image] size];
459  EXPECT_EQ(0, icon_size.width);
460  EXPECT_EQ(0, icon_size.height);
461
462  SkBitmap bitmap;
463  const int kTestImageSize = 10;
464  const int kTargetImageSize = 48;
465  bitmap.setInfo(SkImageInfo::MakeN32Premul(kTestImageSize, kTestImageSize));
466  item_model->SetIcon(gfx::ImageSkia::CreateFrom1xBitmap(bitmap), false);
467  icon_size = [[button image] size];
468  // Icon should always be resized to 48x48.
469  EXPECT_EQ(kTargetImageSize, icon_size.width);
470  EXPECT_EQ(kTargetImageSize, icon_size.height);
471}
472
473TEST_F(AppsGridControllerTest, ModelAdd) {
474  model()->PopulateApps(2);
475  EXPECT_EQ(2u, [[GetPageAt(0) content] count]);
476  EXPECT_EQ(std::string("|Item 0,Item 1|"), GetViewContent());
477
478  app_list::AppListItemList* item_list = model()->top_level_item_list();
479
480  model()->CreateAndAddItem("Item 2");
481  ASSERT_EQ(3u, item_list->item_count());
482  EXPECT_EQ(3u, [apps_grid_controller_ itemCount]);
483  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
484
485  // Test adding an item whose position is in the middle.
486  app_list::AppListItem* item0 = item_list->item_at(0);
487  app_list::AppListItem* item1 = item_list->item_at(1);
488  app_list::AppListItem* item3 = model()->CreateItem("Item Three");
489  model()->AddItem(item3);
490  item_list->SetItemPosition(
491      item3, item0->position().CreateBetween(item1->position()));
492  EXPECT_EQ(4u, [apps_grid_controller_ itemCount]);
493  EXPECT_EQ(std::string("|Item 0,Item Three,Item 1,Item 2|"), GetViewContent());
494}
495
496TEST_F(AppsGridControllerTest, ModelMove) {
497  model()->PopulateApps(3);
498  EXPECT_EQ(3u, [[GetPageAt(0) content] count]);
499  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
500
501  // Test swapping items (e.g. rearranging via sync).
502  model()->top_level_item_list()->MoveItem(1, 2);
503  EXPECT_EQ(std::string("|Item 0,Item 2,Item 1|"), GetViewContent());
504}
505
506TEST_F(AppsGridControllerTest, ModelRemove) {
507  model()->PopulateApps(3);
508  EXPECT_EQ(3u, [[GetPageAt(0) content] count]);
509  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
510
511  // Test removing an item at the end.
512  model()->DeleteItem("Item 2");
513  EXPECT_EQ(2u, [apps_grid_controller_ itemCount]);
514  EXPECT_EQ(std::string("|Item 0,Item 1|"), GetViewContent());
515
516  // Test removing in the middle.
517  model()->CreateAndAddItem("Item 2");
518  EXPECT_EQ(3u, [apps_grid_controller_ itemCount]);
519  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
520  model()->DeleteItem("Item 1");
521  EXPECT_EQ(2u, [apps_grid_controller_ itemCount]);
522  EXPECT_EQ(std::string("|Item 0,Item 2|"), GetViewContent());
523}
524
525TEST_F(AppsGridControllerTest, ModelRemoveSeveral) {
526  model()->PopulateApps(3);
527  EXPECT_EQ(3u, [[GetPageAt(0) content] count]);
528  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
529
530  // Test removing multiple items via the model.
531  model()->DeleteItem("Item 0");
532  model()->DeleteItem("Item 1");
533  model()->DeleteItem("Item 2");
534  EXPECT_EQ(0u, [apps_grid_controller_ itemCount]);
535  EXPECT_EQ(std::string("||"), GetViewContent());
536}
537
538TEST_F(AppsGridControllerTest, ModelRemovePage) {
539  app_list::AppListItemList* item_list = model()->top_level_item_list();
540
541  model()->PopulateApps(kItemsPerPage + 1);
542  ASSERT_EQ(kItemsPerPage + 1, item_list->item_count());
543  EXPECT_EQ(kItemsPerPage + 1, [apps_grid_controller_ itemCount]);
544  EXPECT_EQ(2u, [apps_grid_controller_ pageCount]);
545
546  // Test removing the last item when there is one item on the second page.
547  app_list::AppListItem* last_item = item_list->item_at(kItemsPerPage);
548  model()->DeleteItem(last_item->id());
549  EXPECT_EQ(kItemsPerPage, item_list->item_count());
550  EXPECT_EQ(kItemsPerPage, [apps_grid_controller_ itemCount]);
551  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
552}
553
554// Test install progress bars, and install flow with item highlighting.
555TEST_F(AppsGridControllerTest, ItemInstallProgress) {
556  ReplaceTestModel(kItemsPerPage + 1);
557  EXPECT_EQ(2u, [apps_grid_controller_ pageCount]);
558  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
559  app_list::AppListItem* item_model =
560      model()->top_level_item_list()->item_at(kItemsPerPage);
561
562  // Highlighting an item should activate the page it is on.
563  item_model->SetHighlighted(true);
564  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
565
566  // Clearing a highlight stays on the current page.
567  [apps_grid_controller_ scrollToPage:0];
568  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
569  item_model->SetHighlighted(false);
570  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
571
572  // Starting install should add a progress bar, and temporarily clear the
573  // button title.
574  NSButton* button = GetItemViewAt(kItemsPerPage);
575  NSView* containerView = [button superview];
576  EXPECT_EQ(1u, [[containerView subviews] count]);
577  EXPECT_NSEQ(@"Item 16", [button title]);
578  item_model->SetHighlighted(true);
579  item_model->SetIsInstalling(true);
580  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
581
582  EXPECT_EQ(2u, [[containerView subviews] count]);
583  EXPECT_NSEQ(@"", [button title]);
584  NSProgressIndicator* progressIndicator =
585      [[containerView subviews] objectAtIndex:1];
586  EXPECT_FALSE([progressIndicator isIndeterminate]);
587  EXPECT_EQ(0.0, [progressIndicator doubleValue]);
588
589  // Updating the progress in the model should update the progress bar.
590  item_model->SetPercentDownloaded(50);
591  EXPECT_EQ(50.0, [progressIndicator doubleValue]);
592
593  // Two things can be installing simultaneously. When one starts or completes
594  // the model builder will ask for the item to be highlighted.
595  app_list::AppListItem* alternate_item_model =
596      model()->top_level_item_list()->item_at(0);
597  item_model->SetHighlighted(false);
598  alternate_item_model->SetHighlighted(true);
599  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
600
601  // Update the first item (page doesn't change on updates).
602  item_model->SetPercentDownloaded(100);
603  EXPECT_EQ(100.0, [progressIndicator doubleValue]);
604  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
605
606  // A percent of -1 indicates the download is complete and the unpack/install
607  // process has started.
608  item_model->SetPercentDownloaded(-1);
609  EXPECT_TRUE([progressIndicator isIndeterminate]);
610
611  // Completing install removes the progress bar, and restores the title.
612  // ExtensionAppModelBuilder will reload the ExtensionAppItem, which also
613  // highlights. Do the same here.
614  alternate_item_model->SetHighlighted(false);
615  item_model->SetHighlighted(true);
616  item_model->SetIsInstalling(false);
617  EXPECT_EQ(1u, [[containerView subviews] count]);
618  EXPECT_NSEQ(@"Item 16", [button title]);
619  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
620
621  // Things should cleanup OK with |alternate_item_model| left installing.
622}
623
624// Test mouseover selection.
625TEST_F(AppsGridControllerTest, MouseoverSelects) {
626  model()->PopulateApps(2);
627  EXPECT_EQ(nil, GetSelectedView());
628
629  // Test entering and exiting the first item.
630  SimulateMouseEnterItemAt(0);
631  EXPECT_EQ(GetItemViewAt(0), GetSelectedView());
632  SimulateMouseExitItemAt(0);
633  EXPECT_EQ(nil, GetSelectedView());
634
635  // AppKit doesn't guarantee the order, so test moving between items.
636  SimulateMouseEnterItemAt(0);
637  EXPECT_EQ(GetItemViewAt(0), GetSelectedView());
638  SimulateMouseEnterItemAt(1);
639  EXPECT_EQ(GetItemViewAt(1), GetSelectedView());
640  SimulateMouseExitItemAt(0);
641  EXPECT_EQ(GetItemViewAt(1), GetSelectedView());
642  SimulateMouseExitItemAt(1);
643  EXPECT_EQ(nil, GetSelectedView());
644}
645
646// Test AppsGridPaginationObserver totalPagesChanged().
647TEST_F(AppsGridControllerTest, PaginationObserverPagesChanged) {
648  base::scoped_nsobject<TestPaginationObserver> observer(
649      [[TestPaginationObserver alloc] init]);
650  [apps_grid_controller_ setPaginationObserver:observer];
651
652  // Test totalPagesChanged.
653  model()->PopulateApps(kItemsPerPage);
654  EXPECT_EQ(0, [observer totalPagesChangedCount]);
655  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
656  model()->PopulateApps(1);
657  EXPECT_EQ(1, [observer totalPagesChangedCount]);
658  EXPECT_EQ(2u, [apps_grid_controller_ pageCount]);
659  ReplaceTestModel(0);
660  EXPECT_EQ(2, [observer totalPagesChangedCount]);
661  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
662  ReplaceTestModel(kItemsPerPage * 3 + 1);
663  EXPECT_EQ(3, [observer totalPagesChangedCount]);
664  EXPECT_EQ(4u, [apps_grid_controller_ pageCount]);
665
666  EXPECT_FALSE([observer readVisibilityDidChange]);
667  EXPECT_EQ(0, [observer selectedPageChangedCount]);
668
669  [apps_grid_controller_ setPaginationObserver:nil];
670}
671
672// Test AppsGridPaginationObserver selectedPageChanged().
673TEST_F(AppsGridControllerTest, PaginationObserverSelectedPageChanged) {
674  base::scoped_nsobject<TestPaginationObserver> observer(
675      [[TestPaginationObserver alloc] init]);
676  [apps_grid_controller_ setPaginationObserver:observer];
677  EXPECT_EQ(0, [[NSAnimationContext currentContext] duration]);
678
679  ReplaceTestModel(kItemsPerPage * 3 + 1);
680  EXPECT_EQ(1, [observer totalPagesChangedCount]);
681  EXPECT_EQ(4u, [apps_grid_controller_ pageCount]);
682
683  EXPECT_FALSE([observer readVisibilityDidChange]);
684  EXPECT_EQ(0, [observer selectedPageChangedCount]);
685
686  [apps_grid_controller_ scrollToPage:1];
687  EXPECT_EQ(1, [observer selectedPageChangedCount]);
688  EXPECT_EQ(1, [observer lastNewSelectedPage]);
689  EXPECT_TRUE([observer readVisibilityDidChange]);
690  EXPECT_FALSE([observer readVisibilityDidChange]);  // Testing test behaviour.
691  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:0]);
692  EXPECT_EQ(1.0, [apps_grid_controller_ visiblePortionOfPage:1]);
693  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:2]);
694  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:3]);
695
696  [apps_grid_controller_ scrollToPage:0];
697  EXPECT_EQ(2, [observer selectedPageChangedCount]);
698  EXPECT_EQ(0, [observer lastNewSelectedPage]);
699  EXPECT_TRUE([observer readVisibilityDidChange]);
700  EXPECT_EQ(1.0, [apps_grid_controller_ visiblePortionOfPage:0]);
701  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:1]);
702
703  [apps_grid_controller_ scrollToPage:3];
704  // Note: with no animations, there is only a single page change. However, with
705  // animations we expect multiple updates depending on the rate that the scroll
706  // view updates and sends out NSViewBoundsDidChangeNotification.
707  EXPECT_EQ(3, [observer selectedPageChangedCount]);
708  EXPECT_EQ(3, [observer lastNewSelectedPage]);
709  EXPECT_TRUE([observer readVisibilityDidChange]);
710  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:0]);
711  EXPECT_EQ(1.0, [apps_grid_controller_ visiblePortionOfPage:3]);
712
713  [apps_grid_controller_ setPaginationObserver:nil];
714}
715
716// Test basic item moves with two items; swapping them around, dragging outside
717// of the view bounds, and dragging on the background.
718TEST_F(AppsGridControllerTest, DragAndDropSimple) {
719  model()->PopulateApps(2);
720  NSCollectionView* page = [apps_grid_controller_ collectionViewAtPageIndex:0];
721  NSEvent* mouse_at_cell_0 = MouseEventInCell(page, 0);
722  NSEvent* mouse_at_cell_1 = MouseEventInCell(page, 1);
723  NSEvent* mouse_at_page_centre = MouseEventInCell(page, 6);
724  NSEvent* mouse_off_page = MouseEventInCell(page, kItemsPerPage * 2);
725
726  const std::string kOrdered = "Item 0,Item 1";
727  const std::string kSwapped = "Item 1,Item 0";
728  const std::string kOrderedView = "|Item 0,Item 1|";
729  const std::string kSwappedView = "|Item 1,Item 0|";
730
731  EXPECT_EQ(kOrdered, model()->GetModelContent());
732  EXPECT_EQ(kOrderedView, GetViewContent());
733  AppsCollectionViewDragManager* drag_manager =
734      [apps_grid_controller_ dragManager];
735
736  // Drag first item over the second item and release.
737  [drag_manager onMouseDownInPage:page
738                        withEvent:mouse_at_cell_0];
739  [drag_manager onMouseDragged:mouse_at_cell_1];
740  EXPECT_EQ(kOrdered, model()->GetModelContent());
741  EXPECT_EQ(kSwappedView, GetViewContent());  // View swaps first.
742  [drag_manager onMouseUp:mouse_at_cell_1];
743  EXPECT_EQ(kSwapped, model()->GetModelContent());
744  EXPECT_EQ(kSwappedView, GetViewContent());
745
746  // Drag item back.
747  [drag_manager onMouseDownInPage:page
748                        withEvent:mouse_at_cell_1];
749  [drag_manager onMouseDragged:mouse_at_cell_0];
750  EXPECT_EQ(kSwapped, model()->GetModelContent());
751  EXPECT_EQ(kOrderedView, GetViewContent());
752  [drag_manager onMouseUp:mouse_at_cell_0];
753  EXPECT_EQ(kOrdered, model()->GetModelContent());
754  EXPECT_EQ(kOrderedView, GetViewContent());
755
756  // Drag first item to centre of view (should put in last place).
757  [drag_manager onMouseDownInPage:page
758                        withEvent:mouse_at_cell_0];
759  [drag_manager onMouseDragged:mouse_at_page_centre];
760  EXPECT_EQ(kOrdered, model()->GetModelContent());
761  EXPECT_EQ(kSwappedView, GetViewContent());
762  [drag_manager onMouseUp:mouse_at_page_centre];
763  EXPECT_EQ(kSwapped, model()->GetModelContent());
764  EXPECT_EQ(kSwappedView, GetViewContent());
765
766  // Drag item to centre again (should leave it in the last place).
767  [drag_manager onMouseDownInPage:page
768                        withEvent:mouse_at_cell_1];
769  [drag_manager onMouseDragged:mouse_at_page_centre];
770  EXPECT_EQ(kSwapped, model()->GetModelContent());
771  EXPECT_EQ(kSwappedView, GetViewContent());
772  [drag_manager onMouseUp:mouse_at_page_centre];
773  EXPECT_EQ(kSwapped, model()->GetModelContent());
774  EXPECT_EQ(kSwappedView, GetViewContent());
775
776  // Drag starting in the centre of the view, should do nothing.
777  [drag_manager onMouseDownInPage:page
778                        withEvent:mouse_at_page_centre];
779  [drag_manager onMouseDragged:mouse_at_cell_0];
780  EXPECT_EQ(kSwapped, model()->GetModelContent());
781  EXPECT_EQ(kSwappedView, GetViewContent());
782  [drag_manager onMouseUp:mouse_at_cell_0];
783  EXPECT_EQ(kSwapped, model()->GetModelContent());
784  EXPECT_EQ(kSwappedView, GetViewContent());
785
786  // Click off page.
787  [drag_manager onMouseDownInPage:page
788                        withEvent:mouse_off_page];
789  [drag_manager onMouseDragged:mouse_at_cell_0];
790  EXPECT_EQ(kSwapped, model()->GetModelContent());
791  EXPECT_EQ(kSwappedView, GetViewContent());
792  [drag_manager onMouseUp:mouse_at_cell_0];
793  EXPECT_EQ(kSwapped, model()->GetModelContent());
794  EXPECT_EQ(kSwappedView, GetViewContent());
795
796  // Drag to first over second item, then off page.
797  [drag_manager onMouseDownInPage:page
798                        withEvent:mouse_at_cell_0];
799  [drag_manager onMouseDragged:mouse_at_cell_1];
800  EXPECT_EQ(kSwapped, model()->GetModelContent());
801  EXPECT_EQ(kOrderedView, GetViewContent());
802  [drag_manager onMouseDragged:mouse_off_page];
803  EXPECT_EQ(kSwapped, model()->GetModelContent());
804  EXPECT_EQ(kOrderedView, GetViewContent());
805  [drag_manager onMouseUp:mouse_off_page];
806  EXPECT_EQ(kOrdered, model()->GetModelContent());
807  EXPECT_EQ(kOrderedView, GetViewContent());
808
809  // Replace with an empty model, and ensure we do not break.
810  ReplaceTestModel(0);
811  EXPECT_EQ(std::string(), model()->GetModelContent());
812  EXPECT_EQ(std::string("||"), GetViewContent());
813  [drag_manager onMouseDownInPage:page
814                        withEvent:mouse_at_cell_0];
815  [drag_manager onMouseDragged:mouse_at_cell_1];
816  [drag_manager onMouseUp:mouse_at_cell_1];
817  EXPECT_EQ(std::string(), model()->GetModelContent());
818  EXPECT_EQ(std::string("||"), GetViewContent());
819}
820
821// Test item moves between pages.
822TEST_F(AppsGridControllerTest, DragAndDropMultiPage) {
823  const size_t kPagesToTest = 3;
824  // Put one item on the last page to hit more edge cases.
825  ReplaceTestModel(kItemsPerPage * (kPagesToTest - 1) + 1);
826  NSCollectionView* page[kPagesToTest];
827  for (size_t i = 0; i < kPagesToTest; ++i)
828    page[i] = [apps_grid_controller_ collectionViewAtPageIndex:i];
829
830  const std::string kSecondItemMovedToSecondPage =
831      "|Item 0,Item 2,Item 3,Item 4,Item 5,Item 6,Item 7,Item 8,"
832      "Item 9,Item 10,Item 11,Item 12,Item 13,Item 14,Item 15,Item 16|"
833      "|Item 17,Item 1,Item 18,Item 19,Item 20,Item 21,Item 22,Item 23,"
834      "Item 24,Item 25,Item 26,Item 27,Item 28,Item 29,Item 30,Item 31|"
835      "|Item 32|";
836
837  NSEvent* mouse_at_cell_0 = MouseEventInCell(page[0], 0);
838  NSEvent* mouse_at_cell_1 = MouseEventInCell(page[0], 1);
839  AppsCollectionViewDragManager* drag_manager =
840      [apps_grid_controller_ dragManager];
841  [drag_manager onMouseDownInPage:page[0]
842                        withEvent:mouse_at_cell_1];
843
844  // Initiate dragging before changing pages.
845  [drag_manager onMouseDragged:mouse_at_cell_0];
846
847  // Scroll to the second page.
848  [apps_grid_controller_ scrollToPage:1];
849  [drag_manager onMouseDragged:mouse_at_cell_1];
850
851  // Do one exhaustive check, and then spot-check corner cases.
852  EXPECT_EQ(kSecondItemMovedToSecondPage, GetViewContent());
853  EXPECT_EQ(0u, GetPageIndexForItem(0));
854  EXPECT_EQ(1u, GetPageIndexForItem(1));
855  EXPECT_EQ(0u, GetPageIndexForItem(2));
856  EXPECT_EQ(0u, GetPageIndexForItem(16));
857  EXPECT_EQ(1u, GetPageIndexForItem(17));
858  EXPECT_EQ(1u, GetPageIndexForItem(31));
859  EXPECT_EQ(2u, GetPageIndexForItem(32));
860
861  // Scroll to the third page and drag some more.
862  [apps_grid_controller_ scrollToPage:2];
863  [drag_manager onMouseDragged:mouse_at_cell_1];
864  EXPECT_EQ(2u, GetPageIndexForItem(1));
865  EXPECT_EQ(1u, GetPageIndexForItem(31));
866  EXPECT_EQ(1u, GetPageIndexForItem(32));
867
868  // Scroll backwards.
869  [apps_grid_controller_ scrollToPage:1];
870  [drag_manager onMouseDragged:mouse_at_cell_1];
871  EXPECT_EQ(kSecondItemMovedToSecondPage, GetViewContent());
872  EXPECT_EQ(1u, GetPageIndexForItem(1));
873  EXPECT_EQ(1u, GetPageIndexForItem(31));
874  EXPECT_EQ(2u, GetPageIndexForItem(32));
875
876  // Simulate installing an item while dragging (or have it appear during sync).
877  model()->PopulateAppWithId(33);
878  // Item should go back to its position before the drag.
879  EXPECT_EQ(0u, GetPageIndexForItem(1));
880  EXPECT_EQ(1u, GetPageIndexForItem(31));
881  EXPECT_EQ(2u, GetPageIndexForItem(32));
882  // New item should appear at end.
883  EXPECT_EQ(2u, GetPageIndexForItem(33));
884
885  // Scroll to end again, and keep dragging (should be ignored).
886  [apps_grid_controller_ scrollToPage:2];
887  [drag_manager onMouseDragged:mouse_at_cell_0];
888  EXPECT_EQ(0u, GetPageIndexForItem(1));
889  [drag_manager onMouseUp:mouse_at_cell_0];
890  EXPECT_EQ(0u, GetPageIndexForItem(1));
891}
892
893// Test scrolling when dragging past edge or over the pager.
894TEST_F(AppsGridControllerTest, ScrollingWhileDragging) {
895  base::scoped_nsobject<TestPaginationObserver> observer(
896      [[TestPaginationObserver alloc] init]);
897  [apps_grid_controller_ setPaginationObserver:observer];
898
899  ReplaceTestModel(kItemsPerPage * 3);
900  // Start on the middle page.
901  [apps_grid_controller_ scrollToPage:1];
902  NSCollectionView* page = [apps_grid_controller_ collectionViewAtPageIndex:1];
903  NSEvent* mouse_at_cell_0 = MouseEventInCell(page, 0);
904
905  NSEvent* at_center = MouseEventForScroll([apps_grid_controller_ view], 0.0);
906  NSEvent* at_left = MouseEventForScroll([apps_grid_controller_ view], -1.1);
907  NSEvent* at_right = MouseEventForScroll([apps_grid_controller_ view], 1.1);
908
909  AppsCollectionViewDragManager* drag_manager =
910      [apps_grid_controller_ dragManager];
911  [drag_manager onMouseDownInPage:page
912                        withEvent:mouse_at_cell_0];
913  [drag_manager onMouseDragged:at_center];
914
915  // Nothing should be scheduled: target page is visible page.
916  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
917  EXPECT_EQ(1u, [apps_grid_controller_ scheduledScrollPage]);
918
919  // Drag to the left, should go to first page and no further.
920  [drag_manager onMouseDragged:at_left];
921  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
922  EXPECT_EQ(0u, [apps_grid_controller_ scheduledScrollPage]);
923  [apps_grid_controller_ scrollToPage:0];  // Commit without timer for testing.
924  [drag_manager onMouseDragged:at_left];
925  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
926  EXPECT_EQ(0u, [apps_grid_controller_ scheduledScrollPage]);
927
928  // Drag to the right, should go to last page and no futher.
929  [drag_manager onMouseDragged:at_right];
930  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
931  EXPECT_EQ(1u, [apps_grid_controller_ scheduledScrollPage]);
932  [apps_grid_controller_ scrollToPage:1];
933  [drag_manager onMouseDragged:at_right];
934  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
935  EXPECT_EQ(2u, [apps_grid_controller_ scheduledScrollPage]);
936  [apps_grid_controller_ scrollToPage:2];
937  [drag_manager onMouseDragged:at_right];
938  EXPECT_EQ(2u, [apps_grid_controller_ visiblePage]);
939  EXPECT_EQ(2u, [apps_grid_controller_ scheduledScrollPage]);
940
941  // Simulate a hover over the first pager segment.
942  [observer setHoveredSegmentForTest:0];
943  [drag_manager onMouseDragged:at_center];
944  EXPECT_EQ(2u, [apps_grid_controller_ visiblePage]);
945  EXPECT_EQ(0u, [apps_grid_controller_ scheduledScrollPage]);
946
947  // Drag it back, should cancel schedule.
948  [observer setHoveredSegmentForTest:-1];
949  [drag_manager onMouseDragged:at_center];
950  EXPECT_EQ(2u, [apps_grid_controller_ visiblePage]);
951  EXPECT_EQ(2u, [apps_grid_controller_ scheduledScrollPage]);
952
953  // Hover again, now over middle segment, and ensure a release also cancels.
954  [observer setHoveredSegmentForTest:1];
955  [drag_manager onMouseDragged:at_center];
956  EXPECT_EQ(2u, [apps_grid_controller_ visiblePage]);
957  EXPECT_EQ(1u, [apps_grid_controller_ scheduledScrollPage]);
958  [drag_manager onMouseUp:at_center];
959  EXPECT_EQ(2u, [apps_grid_controller_ visiblePage]);
960  EXPECT_EQ(2u, [apps_grid_controller_ scheduledScrollPage]);
961
962  [apps_grid_controller_ setPaginationObserver:nil];
963}
964
965TEST_F(AppsGridControllerTest, ContextMenus) {
966  AppListItemWithMenu* item_two_model = new AppListItemWithMenu("Item Two");
967  model()->AddItem(new AppListItemWithMenu("Item One"));
968  model()->AddItem(item_two_model);
969  EXPECT_EQ(2u, [apps_grid_controller_ itemCount]);
970
971  NSCollectionView* page = [apps_grid_controller_ collectionViewAtPageIndex:0];
972  NSEvent* mouse_at_cell_0 = MouseEventInCell(page, 0);
973  NSEvent* mouse_at_cell_1 = MouseEventInCell(page, 1);
974
975  NSMenu* menu = [page menuForEvent:mouse_at_cell_0];
976  EXPECT_EQ(1, [menu numberOfItems]);
977  EXPECT_NSEQ(@"Menu For: Item One", [[menu itemAtIndex:0] title]);
978
979  // Test a context menu request while the item is still installing.
980  item_two_model->SetMenuReadyForTesting(false);
981  menu = [page menuForEvent:mouse_at_cell_1];
982  EXPECT_EQ(nil, menu);
983
984  item_two_model->SetMenuReadyForTesting(true);
985  menu = [page menuForEvent:mouse_at_cell_1];
986  EXPECT_EQ(1, [menu numberOfItems]);
987  EXPECT_NSEQ(@"Menu For: Item Two", [[menu itemAtIndex:0] title]);
988
989  // Test that a button being held down with the left button does not also show
990  // a context menu.
991  [GetItemViewAt(0) highlight:YES];
992  EXPECT_FALSE([page menuForEvent:mouse_at_cell_0]);
993  [GetItemViewAt(0) highlight:NO];
994  EXPECT_TRUE([page menuForEvent:mouse_at_cell_0]);
995}
996
997}  // namespace test
998}  // namespace app_list
999