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