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