apps_grid_controller_unittest.mm revision f2477e01787aa58f445919b809d89e252beef54f
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
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 AppListItemModel {
135 public:
136  explicit AppListItemWithMenu(const std::string& title)
137      : AppListItemModel(title),
138        menu_model_(NULL),
139        menu_ready_(true) {
140    SetTitleAndFullName(title, title);
141    menu_model_.AddItem(0, UTF8ToUTF16("Menu For: " + title));
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()->title());
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()->title());
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()->title());
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::AppListItemModel* item_model = model()->item_list()->item_at(2);
452  item_model->SetTitleAndFullName("UpdatedItem", "UpdatedItem");
453  EXPECT_NSEQ(@"UpdatedItem", [button title]);
454  EXPECT_EQ(std::string("|Item 0,Item 1,UpdatedItem|"), GetViewContent());
455
456  // Test icon updates through the model observer by ensuring the icon changes.
457  NSSize icon_size = [[button image] size];
458  EXPECT_EQ(0, icon_size.width);
459  EXPECT_EQ(0, icon_size.height);
460
461  SkBitmap bitmap;
462  const int kTestImageSize = 10;
463  const int kTargetImageSize = 48;
464  bitmap.setConfig(SkBitmap::kARGB_8888_Config, kTestImageSize, kTestImageSize);
465  item_model->SetIcon(gfx::ImageSkia::CreateFrom1xBitmap(bitmap), false);
466  icon_size = [[button image] size];
467  // Icon should always be resized to 48x48.
468  EXPECT_EQ(kTargetImageSize, icon_size.width);
469  EXPECT_EQ(kTargetImageSize, icon_size.height);
470}
471
472TEST_F(AppsGridControllerTest, ModelAdd) {
473  model()->PopulateApps(2);
474  EXPECT_EQ(2u, [[GetPageAt(0) content] count]);
475  EXPECT_EQ(std::string("|Item 0,Item 1|"), GetViewContent());
476
477  app_list::AppListItemList* item_list = model()->item_list();
478
479  model()->CreateAndAddItem("Item 2");
480  ASSERT_EQ(3u, item_list->item_count());
481  EXPECT_EQ(3u, [apps_grid_controller_ itemCount]);
482  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
483
484  // Test adding an item whose position is in the middle.
485  app_list::AppListItemModel* item0 = item_list->item_at(0);
486  app_list::AppListItemModel* item1 = item_list->item_at(1);
487  app_list::AppListItemModel* item3 =
488      model()->CreateItem("Item Three", "Item Three");
489  item3->set_position(item0->position().CreateBetween(item1->position()));
490  item_list->AddItem(item3);
491  EXPECT_EQ(4u, [apps_grid_controller_ itemCount]);
492  EXPECT_EQ(std::string("|Item 0,Item Three,Item 1,Item 2|"), GetViewContent());
493}
494
495TEST_F(AppsGridControllerTest, ModelMove) {
496  model()->PopulateApps(3);
497  EXPECT_EQ(3u, [[GetPageAt(0) content] count]);
498  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
499
500  // Test swapping items (e.g. rearranging via sync).
501  model()->item_list()->MoveItem(1, 2);
502  EXPECT_EQ(std::string("|Item 0,Item 2,Item 1|"), GetViewContent());
503}
504
505TEST_F(AppsGridControllerTest, ModelRemove) {
506  model()->PopulateApps(3);
507  EXPECT_EQ(3u, [[GetPageAt(0) content] count]);
508  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
509
510  // Test removing an item at the end.
511  model()->item_list()->DeleteItem("Item 2");
512  EXPECT_EQ(2u, [apps_grid_controller_ itemCount]);
513  EXPECT_EQ(std::string("|Item 0,Item 1|"), GetViewContent());
514
515  // Test removing in the middle.
516  model()->CreateAndAddItem("Item 2");
517  EXPECT_EQ(3u, [apps_grid_controller_ itemCount]);
518  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
519  model()->item_list()->DeleteItem("Item 1");
520  EXPECT_EQ(2u, [apps_grid_controller_ itemCount]);
521  EXPECT_EQ(std::string("|Item 0,Item 2|"), GetViewContent());
522}
523
524TEST_F(AppsGridControllerTest, ModelRemoveAlll) {
525  model()->PopulateApps(3);
526  EXPECT_EQ(3u, [[GetPageAt(0) content] count]);
527  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
528
529  // Test removing multiple items via the model.
530  model()->item_list()->DeleteItemsByType(NULL /* all items */);
531  EXPECT_EQ(0u, [apps_grid_controller_ itemCount]);
532  EXPECT_EQ(std::string("||"), GetViewContent());
533}
534
535TEST_F(AppsGridControllerTest, ModelRemovePage) {
536  app_list::AppListItemList* item_list = model()->item_list();
537
538  model()->PopulateApps(kItemsPerPage + 1);
539  ASSERT_EQ(kItemsPerPage + 1, item_list->item_count());
540  EXPECT_EQ(kItemsPerPage + 1, [apps_grid_controller_ itemCount]);
541  EXPECT_EQ(2u, [apps_grid_controller_ pageCount]);
542
543  // Test removing the last item when there is one item on the second page.
544  app_list::AppListItemModel* last_item = item_list->item_at(kItemsPerPage);
545  item_list->DeleteItem(last_item->id());
546  EXPECT_EQ(kItemsPerPage, item_list->item_count());
547  EXPECT_EQ(kItemsPerPage, [apps_grid_controller_ itemCount]);
548  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
549}
550
551// Test install progress bars, and install flow with item highlighting.
552TEST_F(AppsGridControllerTest, ItemInstallProgress) {
553  ReplaceTestModel(kItemsPerPage + 1);
554  EXPECT_EQ(2u, [apps_grid_controller_ pageCount]);
555  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
556  app_list::AppListItemModel* item_model =
557      model()->item_list()->item_at(kItemsPerPage);
558
559  // Highlighting an item should activate the page it is on.
560  item_model->SetHighlighted(true);
561  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
562
563  // Clearing a highlight stays on the current page.
564  [apps_grid_controller_ scrollToPage:0];
565  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
566  item_model->SetHighlighted(false);
567  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
568
569  // Starting install should add a progress bar, and temporarily clear the
570  // button title.
571  NSButton* button = GetItemViewAt(kItemsPerPage);
572  NSView* containerView = [button superview];
573  EXPECT_EQ(1u, [[containerView subviews] count]);
574  EXPECT_NSEQ(@"Item 16", [button title]);
575  item_model->SetHighlighted(true);
576  item_model->SetIsInstalling(true);
577  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
578
579  EXPECT_EQ(2u, [[containerView subviews] count]);
580  EXPECT_NSEQ(@"", [button title]);
581  NSProgressIndicator* progressIndicator =
582      [[containerView subviews] objectAtIndex:1];
583  EXPECT_FALSE([progressIndicator isIndeterminate]);
584  EXPECT_EQ(0.0, [progressIndicator doubleValue]);
585
586  // Updating the progress in the model should update the progress bar.
587  item_model->SetPercentDownloaded(50);
588  EXPECT_EQ(50.0, [progressIndicator doubleValue]);
589
590  // Two things can be installing simultaneously. When one starts or completes
591  // the model builder will ask for the item to be highlighted.
592  app_list::AppListItemModel* alternate_item_model =
593      model()->item_list()->item_at(0);
594  item_model->SetHighlighted(false);
595  alternate_item_model->SetHighlighted(true);
596  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
597
598  // Update the first item (page doesn't change on updates).
599  item_model->SetPercentDownloaded(100);
600  EXPECT_EQ(100.0, [progressIndicator doubleValue]);
601  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
602
603  // A percent of -1 indicates the download is complete and the unpack/install
604  // process has started.
605  item_model->SetPercentDownloaded(-1);
606  EXPECT_TRUE([progressIndicator isIndeterminate]);
607
608  // Completing install removes the progress bar, and restores the title.
609  // ExtensionAppModelBuilder will reload the ExtensionAppItem, which also
610  // highlights. Do the same here.
611  alternate_item_model->SetHighlighted(false);
612  item_model->SetHighlighted(true);
613  item_model->SetIsInstalling(false);
614  EXPECT_EQ(1u, [[containerView subviews] count]);
615  EXPECT_NSEQ(@"Item 16", [button title]);
616  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
617
618  // Things should cleanup OK with |alternate_item_model| left installing.
619}
620
621// Test mouseover selection.
622TEST_F(AppsGridControllerTest, MouseoverSelects) {
623  model()->PopulateApps(2);
624  EXPECT_EQ(nil, GetSelectedView());
625
626  // Test entering and exiting the first item.
627  SimulateMouseEnterItemAt(0);
628  EXPECT_EQ(GetItemViewAt(0), GetSelectedView());
629  SimulateMouseExitItemAt(0);
630  EXPECT_EQ(nil, GetSelectedView());
631
632  // AppKit doesn't guarantee the order, so test moving between items.
633  SimulateMouseEnterItemAt(0);
634  EXPECT_EQ(GetItemViewAt(0), GetSelectedView());
635  SimulateMouseEnterItemAt(1);
636  EXPECT_EQ(GetItemViewAt(1), GetSelectedView());
637  SimulateMouseExitItemAt(0);
638  EXPECT_EQ(GetItemViewAt(1), GetSelectedView());
639  SimulateMouseExitItemAt(1);
640  EXPECT_EQ(nil, GetSelectedView());
641}
642
643// Test AppsGridPaginationObserver totalPagesChanged().
644TEST_F(AppsGridControllerTest, PaginationObserverPagesChanged) {
645  base::scoped_nsobject<TestPaginationObserver> observer(
646      [[TestPaginationObserver alloc] init]);
647  [apps_grid_controller_ setPaginationObserver:observer];
648
649  // Test totalPagesChanged.
650  model()->PopulateApps(kItemsPerPage);
651  EXPECT_EQ(0, [observer totalPagesChangedCount]);
652  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
653  model()->PopulateApps(1);
654  EXPECT_EQ(1, [observer totalPagesChangedCount]);
655  EXPECT_EQ(2u, [apps_grid_controller_ pageCount]);
656  ReplaceTestModel(0);
657  EXPECT_EQ(2, [observer totalPagesChangedCount]);
658  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
659  ReplaceTestModel(kItemsPerPage * 3 + 1);
660  EXPECT_EQ(3, [observer totalPagesChangedCount]);
661  EXPECT_EQ(4u, [apps_grid_controller_ pageCount]);
662
663  EXPECT_FALSE([observer readVisibilityDidChange]);
664  EXPECT_EQ(0, [observer selectedPageChangedCount]);
665
666  [apps_grid_controller_ setPaginationObserver:nil];
667}
668
669// Test AppsGridPaginationObserver selectedPageChanged().
670TEST_F(AppsGridControllerTest, PaginationObserverSelectedPageChanged) {
671  base::scoped_nsobject<TestPaginationObserver> observer(
672      [[TestPaginationObserver alloc] init]);
673  [apps_grid_controller_ setPaginationObserver:observer];
674  EXPECT_EQ(0, [[NSAnimationContext currentContext] duration]);
675
676  ReplaceTestModel(kItemsPerPage * 3 + 1);
677  EXPECT_EQ(1, [observer totalPagesChangedCount]);
678  EXPECT_EQ(4u, [apps_grid_controller_ pageCount]);
679
680  EXPECT_FALSE([observer readVisibilityDidChange]);
681  EXPECT_EQ(0, [observer selectedPageChangedCount]);
682
683  [apps_grid_controller_ scrollToPage:1];
684  EXPECT_EQ(1, [observer selectedPageChangedCount]);
685  EXPECT_EQ(1, [observer lastNewSelectedPage]);
686  EXPECT_TRUE([observer readVisibilityDidChange]);
687  EXPECT_FALSE([observer readVisibilityDidChange]);  // Testing test behaviour.
688  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:0]);
689  EXPECT_EQ(1.0, [apps_grid_controller_ visiblePortionOfPage:1]);
690  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:2]);
691  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:3]);
692
693  [apps_grid_controller_ scrollToPage:0];
694  EXPECT_EQ(2, [observer selectedPageChangedCount]);
695  EXPECT_EQ(0, [observer lastNewSelectedPage]);
696  EXPECT_TRUE([observer readVisibilityDidChange]);
697  EXPECT_EQ(1.0, [apps_grid_controller_ visiblePortionOfPage:0]);
698  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:1]);
699
700  [apps_grid_controller_ scrollToPage:3];
701  // Note: with no animations, there is only a single page change. However, with
702  // animations we expect multiple updates depending on the rate that the scroll
703  // view updates and sends out NSViewBoundsDidChangeNotification.
704  EXPECT_EQ(3, [observer selectedPageChangedCount]);
705  EXPECT_EQ(3, [observer lastNewSelectedPage]);
706  EXPECT_TRUE([observer readVisibilityDidChange]);
707  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:0]);
708  EXPECT_EQ(1.0, [apps_grid_controller_ visiblePortionOfPage:3]);
709
710  [apps_grid_controller_ setPaginationObserver:nil];
711}
712
713// Test basic item moves with two items; swapping them around, dragging outside
714// of the view bounds, and dragging on the background.
715TEST_F(AppsGridControllerTest, DragAndDropSimple) {
716  model()->PopulateApps(2);
717  NSCollectionView* page = [apps_grid_controller_ collectionViewAtPageIndex:0];
718  NSEvent* mouse_at_cell_0 = MouseEventInCell(page, 0);
719  NSEvent* mouse_at_cell_1 = MouseEventInCell(page, 1);
720  NSEvent* mouse_at_page_centre = MouseEventInCell(page, 6);
721  NSEvent* mouse_off_page = MouseEventInCell(page, kItemsPerPage * 2);
722
723  const std::string kOrdered = "Item 0,Item 1";
724  const std::string kSwapped = "Item 1,Item 0";
725  const std::string kOrderedView = "|Item 0,Item 1|";
726  const std::string kSwappedView = "|Item 1,Item 0|";
727
728  EXPECT_EQ(kOrdered, model()->GetModelContent());
729  EXPECT_EQ(kOrderedView, GetViewContent());
730  AppsCollectionViewDragManager* drag_manager =
731      [apps_grid_controller_ dragManager];
732
733  // Drag first item over the second item and release.
734  [drag_manager onMouseDownInPage:page
735                        withEvent:mouse_at_cell_0];
736  [drag_manager onMouseDragged:mouse_at_cell_1];
737  EXPECT_EQ(kOrdered, model()->GetModelContent());
738  EXPECT_EQ(kSwappedView, GetViewContent());  // View swaps first.
739  [drag_manager onMouseUp:mouse_at_cell_1];
740  EXPECT_EQ(kSwapped, model()->GetModelContent());
741  EXPECT_EQ(kSwappedView, GetViewContent());
742
743  // Drag item back.
744  [drag_manager onMouseDownInPage:page
745                        withEvent:mouse_at_cell_1];
746  [drag_manager onMouseDragged:mouse_at_cell_0];
747  EXPECT_EQ(kSwapped, model()->GetModelContent());
748  EXPECT_EQ(kOrderedView, GetViewContent());
749  [drag_manager onMouseUp:mouse_at_cell_0];
750  EXPECT_EQ(kOrdered, model()->GetModelContent());
751  EXPECT_EQ(kOrderedView, GetViewContent());
752
753  // Drag first item to centre of view (should put in last place).
754  [drag_manager onMouseDownInPage:page
755                        withEvent:mouse_at_cell_0];
756  [drag_manager onMouseDragged:mouse_at_page_centre];
757  EXPECT_EQ(kOrdered, model()->GetModelContent());
758  EXPECT_EQ(kSwappedView, GetViewContent());
759  [drag_manager onMouseUp:mouse_at_page_centre];
760  EXPECT_EQ(kSwapped, model()->GetModelContent());
761  EXPECT_EQ(kSwappedView, GetViewContent());
762
763  // Drag item to centre again (should leave it in the last place).
764  [drag_manager onMouseDownInPage:page
765                        withEvent:mouse_at_cell_1];
766  [drag_manager onMouseDragged:mouse_at_page_centre];
767  EXPECT_EQ(kSwapped, model()->GetModelContent());
768  EXPECT_EQ(kSwappedView, GetViewContent());
769  [drag_manager onMouseUp:mouse_at_page_centre];
770  EXPECT_EQ(kSwapped, model()->GetModelContent());
771  EXPECT_EQ(kSwappedView, GetViewContent());
772
773  // Drag starting in the centre of the view, should do nothing.
774  [drag_manager onMouseDownInPage:page
775                        withEvent:mouse_at_page_centre];
776  [drag_manager onMouseDragged:mouse_at_cell_0];
777  EXPECT_EQ(kSwapped, model()->GetModelContent());
778  EXPECT_EQ(kSwappedView, GetViewContent());
779  [drag_manager onMouseUp:mouse_at_cell_0];
780  EXPECT_EQ(kSwapped, model()->GetModelContent());
781  EXPECT_EQ(kSwappedView, GetViewContent());
782
783  // Click off page.
784  [drag_manager onMouseDownInPage:page
785                        withEvent:mouse_off_page];
786  [drag_manager onMouseDragged:mouse_at_cell_0];
787  EXPECT_EQ(kSwapped, model()->GetModelContent());
788  EXPECT_EQ(kSwappedView, GetViewContent());
789  [drag_manager onMouseUp:mouse_at_cell_0];
790  EXPECT_EQ(kSwapped, model()->GetModelContent());
791  EXPECT_EQ(kSwappedView, GetViewContent());
792
793  // Drag to first over second item, then off page.
794  [drag_manager onMouseDownInPage:page
795                        withEvent:mouse_at_cell_0];
796  [drag_manager onMouseDragged:mouse_at_cell_1];
797  EXPECT_EQ(kSwapped, model()->GetModelContent());
798  EXPECT_EQ(kOrderedView, GetViewContent());
799  [drag_manager onMouseDragged:mouse_off_page];
800  EXPECT_EQ(kSwapped, model()->GetModelContent());
801  EXPECT_EQ(kOrderedView, GetViewContent());
802  [drag_manager onMouseUp:mouse_off_page];
803  EXPECT_EQ(kOrdered, model()->GetModelContent());
804  EXPECT_EQ(kOrderedView, GetViewContent());
805
806  // Replace with an empty model, and ensure we do not break.
807  ReplaceTestModel(0);
808  EXPECT_EQ(std::string(), model()->GetModelContent());
809  EXPECT_EQ(std::string("||"), GetViewContent());
810  [drag_manager onMouseDownInPage:page
811                        withEvent:mouse_at_cell_0];
812  [drag_manager onMouseDragged:mouse_at_cell_1];
813  [drag_manager onMouseUp:mouse_at_cell_1];
814  EXPECT_EQ(std::string(), model()->GetModelContent());
815  EXPECT_EQ(std::string("||"), GetViewContent());
816}
817
818// Test item moves between pages.
819TEST_F(AppsGridControllerTest, DragAndDropMultiPage) {
820  const size_t kPagesToTest = 3;
821  // Put one item on the last page to hit more edge cases.
822  ReplaceTestModel(kItemsPerPage * (kPagesToTest - 1) + 1);
823  NSCollectionView* page[kPagesToTest];
824  for (size_t i = 0; i < kPagesToTest; ++i)
825    page[i] = [apps_grid_controller_ collectionViewAtPageIndex:i];
826
827  const std::string kSecondItemMovedToSecondPage =
828      "|Item 0,Item 2,Item 3,Item 4,Item 5,Item 6,Item 7,Item 8,"
829      "Item 9,Item 10,Item 11,Item 12,Item 13,Item 14,Item 15,Item 16|"
830      "|Item 17,Item 1,Item 18,Item 19,Item 20,Item 21,Item 22,Item 23,"
831      "Item 24,Item 25,Item 26,Item 27,Item 28,Item 29,Item 30,Item 31|"
832      "|Item 32|";
833
834  NSEvent* mouse_at_cell_0 = MouseEventInCell(page[0], 0);
835  NSEvent* mouse_at_cell_1 = MouseEventInCell(page[0], 1);
836  AppsCollectionViewDragManager* drag_manager =
837      [apps_grid_controller_ dragManager];
838  [drag_manager onMouseDownInPage:page[0]
839                        withEvent:mouse_at_cell_1];
840
841  // Initiate dragging before changing pages.
842  [drag_manager onMouseDragged:mouse_at_cell_0];
843
844  // Scroll to the second page.
845  [apps_grid_controller_ scrollToPage:1];
846  [drag_manager onMouseDragged:mouse_at_cell_1];
847
848  // Do one exhaustive check, and then spot-check corner cases.
849  EXPECT_EQ(kSecondItemMovedToSecondPage, GetViewContent());
850  EXPECT_EQ(0u, GetPageIndexForItem(0));
851  EXPECT_EQ(1u, GetPageIndexForItem(1));
852  EXPECT_EQ(0u, GetPageIndexForItem(2));
853  EXPECT_EQ(0u, GetPageIndexForItem(16));
854  EXPECT_EQ(1u, GetPageIndexForItem(17));
855  EXPECT_EQ(1u, GetPageIndexForItem(31));
856  EXPECT_EQ(2u, GetPageIndexForItem(32));
857
858  // Scroll to the third page and drag some more.
859  [apps_grid_controller_ scrollToPage:2];
860  [drag_manager onMouseDragged:mouse_at_cell_1];
861  EXPECT_EQ(2u, GetPageIndexForItem(1));
862  EXPECT_EQ(1u, GetPageIndexForItem(31));
863  EXPECT_EQ(1u, GetPageIndexForItem(32));
864
865  // Scroll backwards.
866  [apps_grid_controller_ scrollToPage:1];
867  [drag_manager onMouseDragged:mouse_at_cell_1];
868  EXPECT_EQ(kSecondItemMovedToSecondPage, GetViewContent());
869  EXPECT_EQ(1u, GetPageIndexForItem(1));
870  EXPECT_EQ(1u, GetPageIndexForItem(31));
871  EXPECT_EQ(2u, GetPageIndexForItem(32));
872
873  // Simulate installing an item while dragging (or have it appear during sync).
874  model()->PopulateAppWithId(33);
875  // Item should go back to its position before the drag.
876  EXPECT_EQ(0u, GetPageIndexForItem(1));
877  EXPECT_EQ(1u, GetPageIndexForItem(31));
878  EXPECT_EQ(2u, GetPageIndexForItem(32));
879  // New item should appear at end.
880  EXPECT_EQ(2u, GetPageIndexForItem(33));
881
882  // Scroll to end again, and keep dragging (should be ignored).
883  [apps_grid_controller_ scrollToPage:2];
884  [drag_manager onMouseDragged:mouse_at_cell_0];
885  EXPECT_EQ(0u, GetPageIndexForItem(1));
886  [drag_manager onMouseUp:mouse_at_cell_0];
887  EXPECT_EQ(0u, GetPageIndexForItem(1));
888}
889
890// Test scrolling when dragging past edge or over the pager.
891TEST_F(AppsGridControllerTest, ScrollingWhileDragging) {
892  base::scoped_nsobject<TestPaginationObserver> observer(
893      [[TestPaginationObserver alloc] init]);
894  [apps_grid_controller_ setPaginationObserver:observer];
895
896  ReplaceTestModel(kItemsPerPage * 3);
897  // Start on the middle page.
898  [apps_grid_controller_ scrollToPage:1];
899  NSCollectionView* page = [apps_grid_controller_ collectionViewAtPageIndex:1];
900  NSEvent* mouse_at_cell_0 = MouseEventInCell(page, 0);
901
902  NSEvent* at_center = MouseEventForScroll([apps_grid_controller_ view], 0.0);
903  NSEvent* at_left = MouseEventForScroll([apps_grid_controller_ view], -1.1);
904  NSEvent* at_right = MouseEventForScroll([apps_grid_controller_ view], 1.1);
905
906  AppsCollectionViewDragManager* drag_manager =
907      [apps_grid_controller_ dragManager];
908  [drag_manager onMouseDownInPage:page
909                        withEvent:mouse_at_cell_0];
910  [drag_manager onMouseDragged:at_center];
911
912  // Nothing should be scheduled: target page is visible page.
913  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
914  EXPECT_EQ(1u, [apps_grid_controller_ scheduledScrollPage]);
915
916  // Drag to the left, should go to first page and no further.
917  [drag_manager onMouseDragged:at_left];
918  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
919  EXPECT_EQ(0u, [apps_grid_controller_ scheduledScrollPage]);
920  [apps_grid_controller_ scrollToPage:0];  // Commit without timer for testing.
921  [drag_manager onMouseDragged:at_left];
922  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
923  EXPECT_EQ(0u, [apps_grid_controller_ scheduledScrollPage]);
924
925  // Drag to the right, should go to last page and no futher.
926  [drag_manager onMouseDragged:at_right];
927  EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]);
928  EXPECT_EQ(1u, [apps_grid_controller_ scheduledScrollPage]);
929  [apps_grid_controller_ scrollToPage:1];
930  [drag_manager onMouseDragged:at_right];
931  EXPECT_EQ(1u, [apps_grid_controller_ visiblePage]);
932  EXPECT_EQ(2u, [apps_grid_controller_ scheduledScrollPage]);
933  [apps_grid_controller_ scrollToPage:2];
934  [drag_manager onMouseDragged:at_right];
935  EXPECT_EQ(2u, [apps_grid_controller_ visiblePage]);
936  EXPECT_EQ(2u, [apps_grid_controller_ scheduledScrollPage]);
937
938  // Simulate a hover over the first pager segment.
939  [observer setHoveredSegmentForTest:0];
940  [drag_manager onMouseDragged:at_center];
941  EXPECT_EQ(2u, [apps_grid_controller_ visiblePage]);
942  EXPECT_EQ(0u, [apps_grid_controller_ scheduledScrollPage]);
943
944  // Drag it back, should cancel schedule.
945  [observer setHoveredSegmentForTest:-1];
946  [drag_manager onMouseDragged:at_center];
947  EXPECT_EQ(2u, [apps_grid_controller_ visiblePage]);
948  EXPECT_EQ(2u, [apps_grid_controller_ scheduledScrollPage]);
949
950  // Hover again, now over middle segment, and ensure a release also cancels.
951  [observer setHoveredSegmentForTest:1];
952  [drag_manager onMouseDragged:at_center];
953  EXPECT_EQ(2u, [apps_grid_controller_ visiblePage]);
954  EXPECT_EQ(1u, [apps_grid_controller_ scheduledScrollPage]);
955  [drag_manager onMouseUp:at_center];
956  EXPECT_EQ(2u, [apps_grid_controller_ visiblePage]);
957  EXPECT_EQ(2u, [apps_grid_controller_ scheduledScrollPage]);
958
959  [apps_grid_controller_ setPaginationObserver:nil];
960}
961
962TEST_F(AppsGridControllerTest, ContextMenus) {
963  AppListItemWithMenu* item_two_model = new AppListItemWithMenu("Item Two");
964  model()->item_list()->AddItem(new AppListItemWithMenu("Item One"));
965  model()->item_list()->AddItem(item_two_model);
966  EXPECT_EQ(2u, [apps_grid_controller_ itemCount]);
967
968  NSCollectionView* page = [apps_grid_controller_ collectionViewAtPageIndex:0];
969  NSEvent* mouse_at_cell_0 = MouseEventInCell(page, 0);
970  NSEvent* mouse_at_cell_1 = MouseEventInCell(page, 1);
971
972  NSMenu* menu = [page menuForEvent:mouse_at_cell_0];
973  EXPECT_EQ(1, [menu numberOfItems]);
974  EXPECT_NSEQ(@"Menu For: Item One", [[menu itemAtIndex:0] title]);
975
976  // Test a context menu request while the item is still installing.
977  item_two_model->SetMenuReadyForTesting(false);
978  menu = [page menuForEvent:mouse_at_cell_1];
979  EXPECT_EQ(nil, menu);
980
981  item_two_model->SetMenuReadyForTesting(true);
982  menu = [page menuForEvent:mouse_at_cell_1];
983  EXPECT_EQ(1, [menu numberOfItems]);
984  EXPECT_NSEQ(@"Menu For: Item Two", [[menu itemAtIndex:0] title]);
985}
986
987}  // namespace test
988}  // namespace app_list
989