apps_grid_controller_unittest.mm revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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/memory/scoped_nsobject.h"
6#import "testing/gtest_mac.h"
7#include "ui/app_list/app_list_item_model.h"
8#import "ui/app_list/cocoa/apps_collection_view_drag_manager.h"
9#import "ui/app_list/cocoa/apps_grid_controller.h"
10#import "ui/app_list/cocoa/apps_grid_view_item.h"
11#import "ui/app_list/cocoa/apps_pagination_model_observer.h"
12#import "ui/app_list/cocoa/test/apps_grid_controller_test_helper.h"
13#include "ui/app_list/test/app_list_test_model.h"
14#include "ui/app_list/test/app_list_test_view_delegate.h"
15#import "ui/base/test/cocoa_test_event_utils.h"
16
17@interface TestPaginationObserver : NSObject<AppsPaginationModelObserver> {
18 @private
19  int totalPagesChangedCount_;
20  int selectedPageChangedCount_;
21  int lastNewSelectedPage_;
22  bool visibilityDidChange_;
23}
24
25@property(assign, nonatomic) int totalPagesChangedCount;
26@property(assign, nonatomic) int selectedPageChangedCount;
27@property(assign, nonatomic) int lastNewSelectedPage;
28
29- (bool)readVisibilityDidChange;
30
31@end
32
33@implementation TestPaginationObserver
34
35@synthesize totalPagesChangedCount = totalPagesChangedCount_;
36@synthesize selectedPageChangedCount = selectedPageChangedCount_;
37@synthesize lastNewSelectedPage = lastNewSelectedPage_;
38
39- (bool)readVisibilityDidChange {
40  bool truth = visibilityDidChange_;
41  visibilityDidChange_ = false;
42  return truth;
43}
44
45- (void)totalPagesChanged {
46  ++totalPagesChangedCount_;
47}
48
49- (void)selectedPageChanged:(int)newSelected {
50  ++selectedPageChangedCount_;
51  lastNewSelectedPage_ = newSelected;
52}
53
54- (void)pageVisibilityChanged {
55  visibilityDidChange_ = true;
56}
57
58@end
59
60namespace app_list {
61namespace test {
62
63namespace {
64
65class AppsGridControllerTest : public AppsGridControllerTestHelper {
66 public:
67  AppsGridControllerTest() {}
68
69  virtual void SetUp() OVERRIDE {
70    owned_apps_grid_controller_.reset([[AppsGridController alloc] init]);
71    [owned_apps_grid_controller_ setDelegate:delegate_.get()];
72    AppsGridControllerTestHelper::SetUpWithGridController(
73        owned_apps_grid_controller_.get());
74
75    [[test_window() contentView] addSubview:[apps_grid_controller_ view]];
76    [test_window() makePretendKeyWindowAndSetFirstResponder:
77        [apps_grid_controller_ collectionViewAtPageIndex:0]];
78  }
79
80  virtual void TearDown() OVERRIDE {
81    owned_apps_grid_controller_.reset();
82    AppsGridControllerTestHelper::TearDown();
83  }
84
85 private:
86  scoped_nsobject<AppsGridController> owned_apps_grid_controller_;
87
88  DISALLOW_COPY_AND_ASSIGN(AppsGridControllerTest);
89};
90
91}  // namespace
92
93TEST_VIEW(AppsGridControllerTest, [apps_grid_controller_ view]);
94
95// Test showing with an empty model.
96TEST_F(AppsGridControllerTest, EmptyModelAndShow) {
97  EXPECT_TRUE([[apps_grid_controller_ view] superview]);
98
99  // First page should always exist, even if empty.
100  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
101  EXPECT_EQ(0u, [[GetPageAt(0) content] count]);
102  EXPECT_TRUE([GetPageAt(0) superview]);  // The pages container.
103  EXPECT_TRUE([[GetPageAt(0) superview] superview]);
104}
105
106// Test with a single item.
107// This test is disabled in builders until the delay to wait for the collection
108// view to load subviews can be removed, or some other solution is found.
109TEST_F(AppsGridControllerTest, DISABLED_SingleEntryModel) {
110  // We need to "wake up" the NSCollectionView, otherwise it does not
111  // immediately update its subviews later in this function.
112  // When this test is run by itself, it's enough just to send a keypress (and
113  // this delay is not needed).
114  DelayForCollectionView();
115  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
116  EXPECT_EQ(0u, [[GetPageAt(0) content] count]);
117
118  model()->PopulateApps(1);
119  SinkEvents();
120  EXPECT_FALSE([GetPageAt(0) animations]);
121
122  EXPECT_EQ(1u, [[GetPageAt(0) content] count]);
123  NSArray* subviews = [GetPageAt(0) subviews];
124  EXPECT_EQ(1u, [subviews count]);
125
126  // Note that using GetItemViewAt(0) here also works, and returns non-nil even
127  // without the delay, but a "click" on it does not register without the delay.
128  NSView* subview = [subviews objectAtIndex:0];
129
130  // Launch the item.
131  SimulateClick(subview);
132  SinkEvents();
133  EXPECT_EQ(1, delegate()->activate_count());
134  EXPECT_EQ(std::string("Item 0"), delegate()->last_activated()->title());
135}
136
137// Test activating an item on the second page (the 17th item).
138TEST_F(AppsGridControllerTest, DISABLED_TwoPageModel) {
139  DelayForCollectionView();
140  ReplaceTestModel(kItemsPerPage * 2);
141  [apps_grid_controller_ scrollToPage:1];
142
143  // The NSScrollView animator ignores the duration configured on the
144  // NSAnimationContext (set by CocoaTest::Init), so we need to delay here.
145  DelayForCollectionView();
146  NSArray* subviews = [GetPageAt(1) subviews];
147  NSView* subview = [subviews objectAtIndex:0];
148  // Launch the item.
149  SimulateClick(subview);
150  SinkEvents();
151  EXPECT_EQ(1, delegate()->activate_count());
152  EXPECT_EQ(std::string("Item 16"), delegate()->last_activated()->title());
153}
154
155// Test setModel.
156TEST_F(AppsGridControllerTest, ReplaceModel) {
157  const size_t kOrigItems = 1;
158  const size_t kNewItems = 2;
159
160  model()->PopulateApps(kOrigItems);
161  EXPECT_EQ(kOrigItems, [[GetPageAt(0) content] count]);
162
163  ReplaceTestModel(kNewItems);
164  EXPECT_EQ(kNewItems, [[GetPageAt(0) content] count]);
165}
166
167// Test pagination.
168TEST_F(AppsGridControllerTest, Pagination) {
169  model()->PopulateApps(1);
170  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
171  EXPECT_EQ(1u, [[GetPageAt(0) content] count]);
172
173  ReplaceTestModel(kItemsPerPage);
174  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
175  EXPECT_EQ(kItemsPerPage, [[GetPageAt(0) content] count]);
176
177  // Test adding an item onto the next page.
178  model()->PopulateApps(1);  // Now 17 items.
179  EXPECT_EQ(2u, [apps_grid_controller_ pageCount]);
180  EXPECT_EQ(kItemsPerPage, [[GetPageAt(0) content] count]);
181  EXPECT_EQ(1u, [[GetPageAt(1) content] count]);
182
183  // Test N pages with the last page having one empty spot.
184  const size_t kPagesToTest = 3;
185  ReplaceTestModel(kPagesToTest * kItemsPerPage - 1);
186  EXPECT_EQ(kPagesToTest, [apps_grid_controller_ pageCount]);
187  for (size_t page_index = 0; page_index < kPagesToTest - 1; ++page_index) {
188    EXPECT_EQ(kItemsPerPage, [[GetPageAt(page_index) content] count]);
189  }
190  EXPECT_EQ(kItemsPerPage - 1, [[GetPageAt(kPagesToTest - 1) content] count]);
191
192  // Test removing pages.
193  ReplaceTestModel(1);
194  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
195  EXPECT_EQ(1u, [[GetPageAt(0) content] count]);
196}
197
198// Tests basic left-right keyboard navigation on the first page, later tests
199// will test keyboard navigation across pages and other corner cases.
200TEST_F(AppsGridControllerTest, DISABLED_FirstPageKeyboardNavigation) {
201  model()->PopulateApps(3);
202  SinkEvents();
203  EXPECT_EQ(3u, [[GetPageAt(0) content] count]);
204
205  SimulateKeyPress(NSRightArrowFunctionKey);
206  SinkEvents();
207  EXPECT_EQ(GetSelectedView(), GetItemViewAt(0));
208
209  SimulateKeyPress(NSRightArrowFunctionKey);
210  SinkEvents();
211  EXPECT_EQ(GetSelectedView(), GetItemViewAt(1));
212
213  SimulateKeyPress(NSLeftArrowFunctionKey);
214  SinkEvents();
215  EXPECT_EQ(GetSelectedView(), GetItemViewAt(0));
216
217  // Go to the last item, and launch it.
218  SimulateKeyPress(NSRightArrowFunctionKey);
219  SimulateKeyPress(NSRightArrowFunctionKey);
220  [apps_grid_controller_ activateSelection];
221  SinkEvents();
222  EXPECT_EQ(GetSelectedView(), GetItemViewAt(2));
223  EXPECT_EQ(1, delegate()->activate_count());
224  EXPECT_EQ(std::string("Item 2"), delegate()->last_activated()->title());
225}
226
227// Test runtime updates: adding items, removing items, and moving items (e.g. in
228// response to app install, uninstall, and chrome sync changes. Also test
229// changing titles and icons.
230TEST_F(AppsGridControllerTest, ModelUpdates) {
231  model()->PopulateApps(2);
232  EXPECT_EQ(2u, [[GetPageAt(0) content] count]);
233  EXPECT_EQ(std::string("|Item 0,Item 1|"), GetViewContent());
234
235  // Add an item (PopulateApps will create a duplicate "Item 0").
236  model()->PopulateApps(1);
237  EXPECT_EQ(3u, [[GetPageAt(0) content] count]);
238  NSButton* button = GetItemViewAt(2);
239  EXPECT_NSEQ(@"Item 0", [button title]);
240  EXPECT_EQ(std::string("|Item 0,Item 1,Item 0|"), GetViewContent());
241
242  // Update the title via the ItemModelObserver.
243  app_list::AppListItemModel* item_model = model()->apps()->GetItemAt(2);
244  item_model->SetTitle("UpdatedItem");
245  EXPECT_NSEQ(@"UpdatedItem", [button title]);
246  EXPECT_EQ(std::string("|Item 0,Item 1,UpdatedItem|"), GetViewContent());
247
248  // Update the icon, test by changing size.
249  NSSize icon_size = [[button image] size];
250  EXPECT_EQ(0, icon_size.width);
251  EXPECT_EQ(0, icon_size.height);
252
253  SkBitmap bitmap;
254  const int kTestImageSize = 10;
255  bitmap.setConfig(SkBitmap::kARGB_8888_Config, kTestImageSize, kTestImageSize);
256  item_model->SetIcon(gfx::ImageSkia::CreateFrom1xBitmap(bitmap), false);
257  icon_size = [[button image] size];
258  EXPECT_EQ(kTestImageSize, icon_size.width);
259  EXPECT_EQ(kTestImageSize, icon_size.height);
260
261  // Test removing an item at the end.
262  model()->apps()->DeleteAt(2);
263  EXPECT_EQ(2u, [apps_grid_controller_ itemCount]);
264  EXPECT_EQ(std::string("|Item 0,Item 1|"), GetViewContent());
265
266  // Test removing in the middle.
267  model()->AddItem("Item 2");
268  EXPECT_EQ(3u, [apps_grid_controller_ itemCount]);
269  EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent());
270  model()->apps()->DeleteAt(1);
271  EXPECT_EQ(2u, [apps_grid_controller_ itemCount]);
272  EXPECT_EQ(std::string("|Item 0,Item 2|"), GetViewContent());
273
274  // Test inserting in the middle.
275  model()->apps()->AddAt(1, model()->CreateItem("Item One"));
276  EXPECT_EQ(3u, [apps_grid_controller_ itemCount]);
277  EXPECT_EQ(std::string("|Item 0,Item One,Item 2|"), GetViewContent());
278
279  // Test swapping items (e.g. rearranging via sync).
280  model()->apps()->Move(1, 2);
281  EXPECT_EQ(std::string("|Item 0,Item 2,Item One|"), GetViewContent());
282
283  // Test removing multiple items via the model.
284  model()->apps()->DeleteAll();
285  EXPECT_EQ(0u, [apps_grid_controller_ itemCount]);
286  EXPECT_EQ(std::string("||"), GetViewContent());
287
288  // Test removing the last item when there is one item on the second page.
289  ReplaceTestModel(kItemsPerPage + 1);
290  EXPECT_EQ(kItemsPerPage + 1, [apps_grid_controller_ itemCount]);
291  EXPECT_EQ(2u, [apps_grid_controller_ pageCount]);
292  model()->apps()->DeleteAt(kItemsPerPage);
293  EXPECT_EQ(kItemsPerPage, [apps_grid_controller_ itemCount]);
294  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
295}
296
297// Test mouseover selection.
298TEST_F(AppsGridControllerTest, MouseoverSelects) {
299  model()->PopulateApps(2);
300  EXPECT_EQ(nil, GetSelectedView());
301
302  // Test entering and exiting the first item.
303  SimulateMouseEnterItemAt(0);
304  EXPECT_EQ(GetItemViewAt(0), GetSelectedView());
305  SimulateMouseExitItemAt(0);
306  EXPECT_EQ(nil, GetSelectedView());
307
308  // AppKit doesn't guarantee the order, so test moving between items.
309  SimulateMouseEnterItemAt(0);
310  EXPECT_EQ(GetItemViewAt(0), GetSelectedView());
311  SimulateMouseEnterItemAt(1);
312  EXPECT_EQ(GetItemViewAt(1), GetSelectedView());
313  SimulateMouseExitItemAt(0);
314  EXPECT_EQ(GetItemViewAt(1), GetSelectedView());
315  SimulateMouseExitItemAt(1);
316  EXPECT_EQ(nil, GetSelectedView());
317}
318
319// Test AppsGridPaginationObserver totalPagesChanged().
320TEST_F(AppsGridControllerTest, PaginationObserverPagesChanged) {
321  scoped_nsobject<TestPaginationObserver> observer(
322      [[TestPaginationObserver alloc] init]);
323  [apps_grid_controller_ setPaginationObserver:observer];
324
325  // Test totalPagesChanged.
326  model()->PopulateApps(kItemsPerPage);
327  EXPECT_EQ(0, [observer totalPagesChangedCount]);
328  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
329  model()->PopulateApps(1);
330  EXPECT_EQ(1, [observer totalPagesChangedCount]);
331  EXPECT_EQ(2u, [apps_grid_controller_ pageCount]);
332  ReplaceTestModel(0);
333  EXPECT_EQ(2, [observer totalPagesChangedCount]);
334  EXPECT_EQ(1u, [apps_grid_controller_ pageCount]);
335  ReplaceTestModel(kItemsPerPage * 3 + 1);
336  EXPECT_EQ(3, [observer totalPagesChangedCount]);
337  EXPECT_EQ(4u, [apps_grid_controller_ pageCount]);
338
339  EXPECT_FALSE([observer readVisibilityDidChange]);
340  EXPECT_EQ(0, [observer selectedPageChangedCount]);
341
342  [apps_grid_controller_ setPaginationObserver:nil];
343}
344
345// Test AppsGridPaginationObserver selectedPageChanged().
346TEST_F(AppsGridControllerTest, PaginationObserverSelectedPageChanged) {
347  [AppsGridController setScrollAnimationDuration:0.0];
348  scoped_nsobject<TestPaginationObserver> observer(
349      [[TestPaginationObserver alloc] init]);
350  [apps_grid_controller_ setPaginationObserver:observer];
351  EXPECT_EQ(0, [[NSAnimationContext currentContext] duration]);
352
353  ReplaceTestModel(kItemsPerPage * 3 + 1);
354  EXPECT_EQ(1, [observer totalPagesChangedCount]);
355  EXPECT_EQ(4u, [apps_grid_controller_ pageCount]);
356
357  EXPECT_FALSE([observer readVisibilityDidChange]);
358  EXPECT_EQ(0, [observer selectedPageChangedCount]);
359
360  [apps_grid_controller_ scrollToPage:1];
361  EXPECT_EQ(1, [observer selectedPageChangedCount]);
362  EXPECT_EQ(1, [observer lastNewSelectedPage]);
363  EXPECT_TRUE([observer readVisibilityDidChange]);
364  EXPECT_FALSE([observer readVisibilityDidChange]);  // Testing test behaviour.
365  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:0]);
366  EXPECT_EQ(1.0, [apps_grid_controller_ visiblePortionOfPage:1]);
367  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:2]);
368  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:3]);
369
370  [apps_grid_controller_ scrollToPage:0];
371  EXPECT_EQ(2, [observer selectedPageChangedCount]);
372  EXPECT_EQ(0, [observer lastNewSelectedPage]);
373  EXPECT_TRUE([observer readVisibilityDidChange]);
374  EXPECT_EQ(1.0, [apps_grid_controller_ visiblePortionOfPage:0]);
375  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:1]);
376
377  [apps_grid_controller_ scrollToPage:3];
378  // Note: with no animations, there is only a single page change. However, with
379  // animations we expect multiple updates depending on the rate that the scroll
380  // view updates and sends out NSViewBoundsDidChangeNotification.
381  EXPECT_EQ(3, [observer selectedPageChangedCount]);
382  EXPECT_EQ(3, [observer lastNewSelectedPage]);
383  EXPECT_TRUE([observer readVisibilityDidChange]);
384  EXPECT_EQ(0.0, [apps_grid_controller_ visiblePortionOfPage:0]);
385  EXPECT_EQ(1.0, [apps_grid_controller_ visiblePortionOfPage:3]);
386
387  [apps_grid_controller_ setPaginationObserver:nil];
388}
389
390namespace {
391
392// Generate a mouse event at the centre of the view in |page| with the given
393// |index_in_page| that can be used to initiate, update and complete drag
394// operations.
395NSEvent* MouseEventInCell(NSCollectionView* page, size_t index_in_page) {
396  NSRect cell_rect = [page frameForItemAtIndex:index_in_page];
397  NSPoint point_in_view = NSMakePoint(NSMidX(cell_rect), NSMidY(cell_rect));
398  NSPoint point_in_window = [page convertPoint:point_in_view
399                                        toView:nil];
400  return cocoa_test_event_utils::LeftMouseDownAtPoint(point_in_window);
401}
402
403}  // namespace
404
405// Test basic item moves with two items; swapping them around, dragging outside
406// of the view bounds, and dragging on the background.
407TEST_F(AppsGridControllerTest, DragAndDropSimple) {
408  model()->PopulateApps(2);
409  NSCollectionView* page = [apps_grid_controller_ collectionViewAtPageIndex:0];
410  NSEvent* mouse_at_cell_0 = MouseEventInCell(page, 0);
411  NSEvent* mouse_at_cell_1 = MouseEventInCell(page, 1);
412  NSEvent* mouse_at_page_centre = MouseEventInCell(page, 6);
413  NSEvent* mouse_off_page = MouseEventInCell(page, kItemsPerPage * 2);
414
415  const std::string kOrdered = "Item 0,Item 1";
416  const std::string kSwapped = "Item 1,Item 0";
417  const std::string kOrderedView = "|Item 0,Item 1|";
418  const std::string kSwappedView = "|Item 1,Item 0|";
419
420  EXPECT_EQ(kOrdered, model()->GetModelContent());
421  EXPECT_EQ(kOrderedView, GetViewContent());
422  AppsCollectionViewDragManager* drag_manager =
423      [apps_grid_controller_ dragManager];
424
425  // Drag first item over the second item and release.
426  [drag_manager onMouseDownInPage:page
427                        withEvent:mouse_at_cell_0];
428  [drag_manager onMouseDragged:mouse_at_cell_1];
429  EXPECT_EQ(kOrdered, model()->GetModelContent());
430  EXPECT_EQ(kSwappedView, GetViewContent());  // View swaps first.
431  [drag_manager onMouseUp:mouse_at_cell_1];
432  EXPECT_EQ(kSwapped, model()->GetModelContent());
433  EXPECT_EQ(kSwappedView, GetViewContent());
434
435  // Drag item back.
436  [drag_manager onMouseDownInPage:page
437                        withEvent:mouse_at_cell_1];
438  [drag_manager onMouseDragged:mouse_at_cell_0];
439  EXPECT_EQ(kSwapped, model()->GetModelContent());
440  EXPECT_EQ(kOrderedView, GetViewContent());
441  [drag_manager onMouseUp:mouse_at_cell_0];
442  EXPECT_EQ(kOrdered, model()->GetModelContent());
443  EXPECT_EQ(kOrderedView, GetViewContent());
444
445  // Drag first item to centre of view (should put in last place).
446  [drag_manager onMouseDownInPage:page
447                        withEvent:mouse_at_cell_0];
448  [drag_manager onMouseDragged:mouse_at_page_centre];
449  EXPECT_EQ(kOrdered, model()->GetModelContent());
450  EXPECT_EQ(kSwappedView, GetViewContent());
451  [drag_manager onMouseUp:mouse_at_page_centre];
452  EXPECT_EQ(kSwapped, model()->GetModelContent());
453  EXPECT_EQ(kSwappedView, GetViewContent());
454
455  // Drag item to centre again (should leave it in the last place).
456  [drag_manager onMouseDownInPage:page
457                        withEvent:mouse_at_cell_1];
458  [drag_manager onMouseDragged:mouse_at_page_centre];
459  EXPECT_EQ(kSwapped, model()->GetModelContent());
460  EXPECT_EQ(kSwappedView, GetViewContent());
461  [drag_manager onMouseUp:mouse_at_page_centre];
462  EXPECT_EQ(kSwapped, model()->GetModelContent());
463  EXPECT_EQ(kSwappedView, GetViewContent());
464
465  // Drag starting in the centre of the view, should do nothing.
466  [drag_manager onMouseDownInPage:page
467                        withEvent:mouse_at_page_centre];
468  [drag_manager onMouseDragged:mouse_at_cell_0];
469  EXPECT_EQ(kSwapped, model()->GetModelContent());
470  EXPECT_EQ(kSwappedView, GetViewContent());
471  [drag_manager onMouseUp:mouse_at_cell_0];
472  EXPECT_EQ(kSwapped, model()->GetModelContent());
473  EXPECT_EQ(kSwappedView, GetViewContent());
474
475  // Click off page.
476  [drag_manager onMouseDownInPage:page
477                        withEvent:mouse_off_page];
478  [drag_manager onMouseDragged:mouse_at_cell_0];
479  EXPECT_EQ(kSwapped, model()->GetModelContent());
480  EXPECT_EQ(kSwappedView, GetViewContent());
481  [drag_manager onMouseUp:mouse_at_cell_0];
482  EXPECT_EQ(kSwapped, model()->GetModelContent());
483  EXPECT_EQ(kSwappedView, GetViewContent());
484
485  // Drag to first over second item, then off page.
486  [drag_manager onMouseDownInPage:page
487                        withEvent:mouse_at_cell_0];
488  [drag_manager onMouseDragged:mouse_at_cell_1];
489  EXPECT_EQ(kSwapped, model()->GetModelContent());
490  EXPECT_EQ(kOrderedView, GetViewContent());
491  [drag_manager onMouseDragged:mouse_off_page];
492  EXPECT_EQ(kSwapped, model()->GetModelContent());
493  EXPECT_EQ(kOrderedView, GetViewContent());
494  [drag_manager onMouseUp:mouse_off_page];
495  EXPECT_EQ(kOrdered, model()->GetModelContent());
496  EXPECT_EQ(kOrderedView, GetViewContent());
497
498  // Replace with an empty model, and ensure we do not break.
499  ReplaceTestModel(0);
500  EXPECT_EQ(std::string(), model()->GetModelContent());
501  EXPECT_EQ(std::string("||"), GetViewContent());
502  [drag_manager onMouseDownInPage:page
503                        withEvent:mouse_at_cell_0];
504  [drag_manager onMouseDragged:mouse_at_cell_1];
505  [drag_manager onMouseUp:mouse_at_cell_1];
506  EXPECT_EQ(std::string(), model()->GetModelContent());
507  EXPECT_EQ(std::string("||"), GetViewContent());
508}
509
510// Test item moves between pages.
511TEST_F(AppsGridControllerTest, DragAndDropMultiPage) {
512  [AppsGridController setScrollAnimationDuration:0.0];
513  const size_t kPagesToTest = 3;
514  // Put one item on the last page to hit more edge cases.
515  ReplaceTestModel(kItemsPerPage * (kPagesToTest - 1) + 1);
516  NSCollectionView* page[kPagesToTest];
517  for (size_t i = 0; i < kPagesToTest; ++i)
518    page[i] = [apps_grid_controller_ collectionViewAtPageIndex:i];
519
520  const std::string kSecondItemMovedToSecondPage =
521      "|Item 0,Item 2,Item 3,Item 4,Item 5,Item 6,Item 7,Item 8,"
522      "Item 9,Item 10,Item 11,Item 12,Item 13,Item 14,Item 15,Item 16|"
523      "|Item 17,Item 1,Item 18,Item 19,Item 20,Item 21,Item 22,Item 23,"
524      "Item 24,Item 25,Item 26,Item 27,Item 28,Item 29,Item 30,Item 31|"
525      "|Item 32|";
526
527  NSEvent* mouse_at_cell_0 = MouseEventInCell(page[0], 0);
528  NSEvent* mouse_at_cell_1 = MouseEventInCell(page[0], 1);
529  AppsCollectionViewDragManager* drag_manager =
530      [apps_grid_controller_ dragManager];
531  [drag_manager onMouseDownInPage:page[0]
532                        withEvent:mouse_at_cell_1];
533
534  // Initiate dragging before changing pages.
535  [drag_manager onMouseDragged:mouse_at_cell_0];
536
537  // Scroll to the second page.
538  [apps_grid_controller_ scrollToPage:1];
539  [drag_manager onMouseDragged:mouse_at_cell_1];
540
541  // Do one exhaustive check, and then spot-check corner cases.
542  EXPECT_EQ(kSecondItemMovedToSecondPage, GetViewContent());
543  EXPECT_EQ(0u, GetPageIndexForItem(0));
544  EXPECT_EQ(1u, GetPageIndexForItem(1));
545  EXPECT_EQ(0u, GetPageIndexForItem(2));
546  EXPECT_EQ(0u, GetPageIndexForItem(16));
547  EXPECT_EQ(1u, GetPageIndexForItem(17));
548  EXPECT_EQ(1u, GetPageIndexForItem(31));
549  EXPECT_EQ(2u, GetPageIndexForItem(32));
550
551  // Scroll to the third page and drag some more.
552  [apps_grid_controller_ scrollToPage:2];
553  [drag_manager onMouseDragged:mouse_at_cell_1];
554  EXPECT_EQ(2u, GetPageIndexForItem(1));
555  EXPECT_EQ(1u, GetPageIndexForItem(31));
556  EXPECT_EQ(1u, GetPageIndexForItem(32));
557
558  // Scroll backwards.
559  [apps_grid_controller_ scrollToPage:1];
560  [drag_manager onMouseDragged:mouse_at_cell_1];
561  EXPECT_EQ(kSecondItemMovedToSecondPage, GetViewContent());
562  EXPECT_EQ(1u, GetPageIndexForItem(1));
563  EXPECT_EQ(1u, GetPageIndexForItem(31));
564  EXPECT_EQ(2u, GetPageIndexForItem(32));
565
566  // Simulate installing an item while dragging (or have it appear during sync).
567  model()->PopulateAppWithId(33);
568  // Item should go back to its position before the drag.
569  EXPECT_EQ(0u, GetPageIndexForItem(1));
570  EXPECT_EQ(1u, GetPageIndexForItem(31));
571  EXPECT_EQ(2u, GetPageIndexForItem(32));
572  // New item should appear at end.
573  EXPECT_EQ(2u, GetPageIndexForItem(33));
574
575  // Scroll to end again, and keep dragging (should be ignored).
576  [apps_grid_controller_ scrollToPage:2];
577  [drag_manager onMouseDragged:mouse_at_cell_0];
578  EXPECT_EQ(0u, GetPageIndexForItem(1));
579  [drag_manager onMouseUp:mouse_at_cell_0];
580  EXPECT_EQ(0u, GetPageIndexForItem(1));
581}
582
583}  // namespace test
584}  // namespace app_list
585