febc17cc1fa5eb59854760a1cd36b2274761e4ed
[platform/framework/web/crosswalk.git] / src / ui / app_list / views / apps_grid_view_unittest.cc
1 // Copyright (c) 2012 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 "ui/app_list/views/apps_grid_view.h"
6
7 #include <string>
8
9 #include "base/basictypes.h"
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "ui/app_list/app_list_constants.h"
18 #include "ui/app_list/app_list_folder_item.h"
19 #include "ui/app_list/app_list_item.h"
20 #include "ui/app_list/app_list_model.h"
21 #include "ui/app_list/app_list_switches.h"
22 #include "ui/app_list/pagination_model.h"
23 #include "ui/app_list/test/app_list_test_model.h"
24 #include "ui/app_list/views/app_list_item_view.h"
25 #include "ui/app_list/views/apps_grid_view_folder_delegate.h"
26 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
27 #include "ui/views/test/views_test_base.h"
28
29 namespace app_list {
30 namespace test {
31
32 namespace {
33
34 const int kCols = 2;
35 const int kRows = 2;
36 const int kTilesPerPage = kCols * kRows;
37
38 const int kWidth = 320;
39 const int kHeight = 240;
40
41 class PageFlipWaiter : public PaginationModelObserver {
42  public:
43   PageFlipWaiter(base::MessageLoopForUI* ui_loop, PaginationModel* model)
44       : ui_loop_(ui_loop), model_(model), wait_(false) {
45     model_->AddObserver(this);
46   }
47
48   virtual ~PageFlipWaiter() {
49     model_->RemoveObserver(this);
50   }
51
52   void Wait() {
53     DCHECK(!wait_);
54     wait_ = true;
55
56     ui_loop_->Run();
57     wait_ = false;
58   }
59
60   void Reset() { selected_pages_.clear(); }
61
62   const std::string& selected_pages() const { return selected_pages_; }
63
64  private:
65   // PaginationModelObserver overrides:
66   virtual void TotalPagesChanged() OVERRIDE {
67   }
68   virtual void SelectedPageChanged(int old_selected,
69                                    int new_selected) OVERRIDE {
70     if (!selected_pages_.empty())
71       selected_pages_ += ',';
72     selected_pages_ += base::IntToString(new_selected);
73
74     if (wait_)
75       ui_loop_->Quit();
76   }
77   virtual void TransitionStarted() OVERRIDE {
78   }
79   virtual void TransitionChanged() OVERRIDE {
80   }
81
82   base::MessageLoopForUI* ui_loop_;
83   PaginationModel* model_;
84   bool wait_;
85   std::string selected_pages_;
86
87   DISALLOW_COPY_AND_ASSIGN(PageFlipWaiter);
88 };
89
90 }  // namespace
91
92 class AppsGridViewTest : public views::ViewsTestBase {
93  public:
94   AppsGridViewTest() {}
95   virtual ~AppsGridViewTest() {}
96
97   // testing::Test overrides:
98   virtual void SetUp() OVERRIDE {
99     views::ViewsTestBase::SetUp();
100     model_.reset(new AppListTestModel);
101     model_->SetFoldersEnabled(true);
102
103     apps_grid_view_.reset(new AppsGridView(NULL));
104     apps_grid_view_->SetLayout(kCols, kRows);
105     apps_grid_view_->SetBoundsRect(gfx::Rect(gfx::Size(kWidth, kHeight)));
106     apps_grid_view_->SetModel(model_.get());
107     apps_grid_view_->SetItemList(model_->top_level_item_list());
108
109     test_api_.reset(new AppsGridViewTestApi(apps_grid_view_.get()));
110   }
111   virtual void TearDown() OVERRIDE {
112     apps_grid_view_.reset();  // Release apps grid view before models.
113     views::ViewsTestBase::TearDown();
114   }
115
116  protected:
117   void EnsureFoldersEnabled() {
118     // Folders require AppList sync to be enabled.
119     CommandLine::ForCurrentProcess()->AppendSwitch(
120         switches::kEnableSyncAppList);
121   }
122
123   AppListItemView* GetItemViewAt(int index) {
124     return static_cast<AppListItemView*>(
125         test_api_->GetViewAtModelIndex(index));
126   }
127
128   AppListItemView* GetItemViewForPoint(const gfx::Point& point) {
129     for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i) {
130       AppListItemView* view = GetItemViewAt(i);
131       if (view->bounds().Contains(point))
132         return view;
133     }
134     return NULL;
135   }
136
137   gfx::Rect GetItemTileRectAt(int row, int col) {
138     DCHECK_GT(model_->top_level_item_list()->item_count(), 0u);
139
140     gfx::Insets insets(apps_grid_view_->GetInsets());
141     gfx::Rect rect(gfx::Point(insets.left(), insets.top()),
142                    GetItemViewAt(0)->bounds().size());
143     rect.Offset(col * rect.width(), row * rect.height());
144     return rect;
145   }
146
147   PaginationModel* GetPaginationModel() {
148     return apps_grid_view_->pagination_model();
149   }
150
151   // Points are in |apps_grid_view_|'s coordinates.
152   void SimulateDrag(AppsGridView::Pointer pointer,
153                     const gfx::Point& from,
154                     const gfx::Point& to) {
155     AppListItemView* view = GetItemViewForPoint(from);
156     DCHECK(view);
157
158     gfx::Point translated_from = gfx::PointAtOffsetFromOrigin(
159         from - view->bounds().origin());
160     gfx::Point translated_to = gfx::PointAtOffsetFromOrigin(
161         to - view->bounds().origin());
162
163     ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED,
164                                  translated_from, from, 0, 0);
165     apps_grid_view_->InitiateDrag(view, pointer, pressed_event);
166
167     ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED,
168                               translated_to, to, 0, 0);
169     apps_grid_view_->UpdateDragFromItem(pointer, drag_event);
170   }
171
172   void SimulateKeyPress(ui::KeyboardCode key_code) {
173     ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, ui::EF_NONE);
174     apps_grid_view_->OnKeyPressed(key_event);
175   }
176
177   scoped_ptr<AppListTestModel> model_;
178   scoped_ptr<AppsGridView> apps_grid_view_;
179   scoped_ptr<AppsGridViewTestApi> test_api_;
180
181  private:
182   DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest);
183 };
184
185 class TestAppsGridViewFolderDelegate : public AppsGridViewFolderDelegate {
186  public:
187   TestAppsGridViewFolderDelegate() : show_bubble_(false) {}
188   virtual ~TestAppsGridViewFolderDelegate() {}
189
190   // Overridden from AppsGridViewFolderDelegate:
191   virtual void UpdateFolderViewBackground(bool show_bubble) OVERRIDE {
192     show_bubble_ = show_bubble;
193   }
194
195   virtual void ReparentItem(AppListItemView* original_drag_view,
196                             const gfx::Point& drag_point_in_folder_grid)
197       OVERRIDE {}
198
199   virtual void DispatchDragEventForReparent(
200       AppsGridView::Pointer pointer,
201       const gfx::Point& drag_point_in_folder_grid) OVERRIDE {}
202
203   virtual void DispatchEndDragEventForReparent(
204       bool events_forwarded_to_drag_drop_host,
205       bool cancel_drag) OVERRIDE {}
206
207   virtual bool IsPointOutsideOfFolderBoundary(const gfx::Point& point)
208       OVERRIDE {
209     return false;
210   }
211
212   virtual bool IsOEMFolder() const OVERRIDE { return false; }
213
214   virtual void SetRootLevelDragViewVisible(bool visible) OVERRIDE {}
215
216   bool show_bubble() { return show_bubble_; }
217
218  private:
219   bool show_bubble_;
220
221   DISALLOW_COPY_AND_ASSIGN(TestAppsGridViewFolderDelegate);
222 };
223
224 TEST_F(AppsGridViewTest, CreatePage) {
225   // Fully populates a page.
226   const int kPages = 1;
227   model_->PopulateApps(kPages * kTilesPerPage);
228   EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
229
230   // Adds one more and gets a new page created.
231   model_->CreateAndAddItem("Extra");
232   EXPECT_EQ(kPages + 1, GetPaginationModel()->total_pages());
233 }
234
235 TEST_F(AppsGridViewTest, EnsureHighlightedVisible) {
236   const int kPages = 3;
237   model_->PopulateApps(kPages * kTilesPerPage);
238   EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
239   EXPECT_EQ(0, GetPaginationModel()->selected_page());
240
241   // Highlight first one and last one one first page and first page should be
242   // selected.
243   model_->HighlightItemAt(0);
244   EXPECT_EQ(0, GetPaginationModel()->selected_page());
245   model_->HighlightItemAt(kTilesPerPage - 1);
246   EXPECT_EQ(0, GetPaginationModel()->selected_page());
247
248   // Highlight first one on 2nd page and 2nd page should be selected.
249   model_->HighlightItemAt(kTilesPerPage + 1);
250   EXPECT_EQ(1, GetPaginationModel()->selected_page());
251
252   // Highlight last one in the model and last page should be selected.
253   model_->HighlightItemAt(model_->top_level_item_list()->item_count() - 1);
254   EXPECT_EQ(kPages - 1, GetPaginationModel()->selected_page());
255 }
256
257 TEST_F(AppsGridViewTest, RemoveSelectedLastApp) {
258   const int kTotalItems = 2;
259   const int kLastItemIndex = kTotalItems - 1;
260
261   model_->PopulateApps(kTotalItems);
262
263   AppListItemView* last_view = GetItemViewAt(kLastItemIndex);
264   apps_grid_view_->SetSelectedView(last_view);
265   model_->DeleteItem(model_->GetItemName(kLastItemIndex));
266
267   EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view));
268
269   // No crash happens.
270   AppListItemView* view = GetItemViewAt(0);
271   apps_grid_view_->SetSelectedView(view);
272   EXPECT_TRUE(apps_grid_view_->IsSelectedView(view));
273 }
274
275 TEST_F(AppsGridViewTest, MouseDragWithFolderDisabled) {
276   model_->SetFoldersEnabled(false);
277   const int kTotalItems = 4;
278   model_->PopulateApps(kTotalItems);
279   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
280             model_->GetModelContent());
281
282   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
283   gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
284
285   // Dragging changes model order.
286   SimulateDrag(AppsGridView::MOUSE, from, to);
287   apps_grid_view_->EndDrag(false);
288   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
289             model_->GetModelContent());
290   test_api_->LayoutToIdealBounds();
291
292   // Canceling drag should keep existing order.
293   SimulateDrag(AppsGridView::MOUSE, from, to);
294   apps_grid_view_->EndDrag(true);
295   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
296             model_->GetModelContent());
297   test_api_->LayoutToIdealBounds();
298
299   // Deleting an item keeps remaining intact.
300   SimulateDrag(AppsGridView::MOUSE, from, to);
301   model_->DeleteItem(model_->GetItemName(0));
302   apps_grid_view_->EndDrag(false);
303   EXPECT_EQ(std::string("Item 1,Item 2,Item 3"),
304             model_->GetModelContent());
305   test_api_->LayoutToIdealBounds();
306
307   // Adding a launcher item cancels the drag and respects the order.
308   SimulateDrag(AppsGridView::MOUSE, from, to);
309   EXPECT_TRUE(apps_grid_view_->has_dragged_view());
310   model_->CreateAndAddItem("Extra");
311   // No need to EndDrag explicitly - adding an item should do this.
312   EXPECT_FALSE(apps_grid_view_->has_dragged_view());
313   // Even though cancelled, mouse move events can still arrive via the item
314   // view. Ensure that behaves sanely, and doesn't start a new drag.
315   ui::MouseEvent drag_event(
316       ui::ET_MOUSE_DRAGGED, gfx::Point(1, 1), gfx::Point(2, 2), 0, 0);
317   apps_grid_view_->UpdateDragFromItem(AppsGridView::MOUSE, drag_event);
318   EXPECT_FALSE(apps_grid_view_->has_dragged_view());
319
320   EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"),
321             model_->GetModelContent());
322   test_api_->LayoutToIdealBounds();
323 }
324
325 TEST_F(AppsGridViewTest, MouseDragItemIntoFolder) {
326   EnsureFoldersEnabled();
327
328   size_t kTotalItems = 3;
329   model_->PopulateApps(kTotalItems);
330   EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
331   EXPECT_EQ(std::string("Item 0,Item 1,Item 2"), model_->GetModelContent());
332
333   gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
334   gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint();
335
336   // Dragging item_1 over item_0 creates a folder.
337   SimulateDrag(AppsGridView::MOUSE, from, to);
338   apps_grid_view_->EndDrag(false);
339   EXPECT_EQ(kTotalItems - 1, model_->top_level_item_list()->item_count());
340   EXPECT_EQ(AppListFolderItem::kItemType,
341             model_->top_level_item_list()->item_at(0)->GetItemType());
342   AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
343       model_->top_level_item_list()->item_at(0));
344   EXPECT_EQ(2u, folder_item->ChildItemCount());
345   AppListItem* item_0 = model_->FindItem("Item 0");
346   EXPECT_TRUE(item_0->IsInFolder());
347   EXPECT_EQ(folder_item->id(), item_0->folder_id());
348   AppListItem* item_1 = model_->FindItem("Item 1");
349   EXPECT_TRUE(item_1->IsInFolder());
350   EXPECT_EQ(folder_item->id(), item_1->folder_id());
351   std::string expected_items = folder_item->id() + ",Item 2";
352   EXPECT_EQ(expected_items, model_->GetModelContent());
353   test_api_->LayoutToIdealBounds();
354
355   // Dragging item_2 to the folder adds item_2 to the folder.
356   SimulateDrag(AppsGridView::MOUSE, from, to);
357   apps_grid_view_->EndDrag(false);
358
359   EXPECT_EQ(kTotalItems - 2, model_->top_level_item_list()->item_count());
360   EXPECT_EQ(folder_item->id(), model_->GetModelContent());
361   EXPECT_EQ(3u, folder_item->ChildItemCount());
362   item_0 = model_->FindItem("Item 0");
363   EXPECT_TRUE(item_0->IsInFolder());
364   EXPECT_EQ(folder_item->id(), item_0->folder_id());
365   item_1 = model_->FindItem("Item 1");
366   EXPECT_TRUE(item_1->IsInFolder());
367   EXPECT_EQ(folder_item->id(), item_1->folder_id());
368   AppListItem* item_2 = model_->FindItem("Item 2");
369   EXPECT_TRUE(item_2->IsInFolder());
370   EXPECT_EQ(folder_item->id(), item_2->folder_id());
371   test_api_->LayoutToIdealBounds();
372 }
373
374 TEST_F(AppsGridViewTest, MouseDragMaxItemsInFolder) {
375   EnsureFoldersEnabled();
376
377   // Create and add a folder with 15 items in it.
378   size_t kTotalItems = kMaxFolderItems - 1;
379   model_->CreateAndPopulateFolderWithApps(kTotalItems);
380   EXPECT_EQ(1u, model_->top_level_item_list()->item_count());
381   EXPECT_EQ(AppListFolderItem::kItemType,
382             model_->top_level_item_list()->item_at(0)->GetItemType());
383   AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
384       model_->top_level_item_list()->item_at(0));
385   EXPECT_EQ(kTotalItems, folder_item->ChildItemCount());
386
387   // Create and add another 2 items.
388   model_->PopulateAppWithId(kTotalItems);
389   model_->PopulateAppWithId(kTotalItems + 1);
390   EXPECT_EQ(3u, model_->top_level_item_list()->item_count());
391   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
392   EXPECT_EQ(model_->GetItemName(kMaxFolderItems - 1),
393             model_->top_level_item_list()->item_at(1)->id());
394   EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
395             model_->top_level_item_list()->item_at(2)->id());
396
397   gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
398   gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint();
399
400   // Dragging one item into the folder, the folder should accept the item.
401   SimulateDrag(AppsGridView::MOUSE, from, to);
402   apps_grid_view_->EndDrag(false);
403   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
404   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
405   EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount());
406   EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
407             model_->top_level_item_list()->item_at(1)->id());
408   test_api_->LayoutToIdealBounds();
409
410   // Dragging the last item over the folder, the folder won't accept the new
411   // item.
412   SimulateDrag(AppsGridView::MOUSE, from, to);
413   apps_grid_view_->EndDrag(false);
414   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
415   EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount());
416   test_api_->LayoutToIdealBounds();
417 }
418
419 TEST_F(AppsGridViewTest, MouseDragItemReorder) {
420   // This test assumes Folders are enabled.
421   EnsureFoldersEnabled();
422
423   model_->PopulateApps(4);
424   EXPECT_EQ(4u, model_->top_level_item_list()->item_count());
425   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
426             model_->GetModelContent());
427
428   // Dragging an item towards its neighbours should not reorder until the drag
429   // is past the folder drop point.
430   gfx::Point top_right = GetItemTileRectAt(0, 1).CenterPoint();
431   gfx::Vector2d drag_vector;
432   int half_tile_width =
433       (GetItemTileRectAt(0, 1).x() - GetItemTileRectAt(0, 0).x()) / 2;
434   int tile_height = GetItemTileRectAt(1, 0).y() - GetItemTileRectAt(0, 0).y();
435
436   // Drag left but stop before the folder dropping circle.
437   drag_vector.set_x(-half_tile_width - 5);
438   SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
439   apps_grid_view_->EndDrag(false);
440   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
441             model_->GetModelContent());
442
443   // Drag left, past the folder dropping circle.
444   drag_vector.set_x(-3 * half_tile_width + 5);
445   SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
446   apps_grid_view_->EndDrag(false);
447   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
448             model_->GetModelContent());
449
450   // Drag down, between apps 2 and 3. The gap should open up, making space for
451   // app 0 in the bottom left.
452   drag_vector.set_x(-half_tile_width);
453   drag_vector.set_y(tile_height);
454   SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
455   apps_grid_view_->EndDrag(false);
456   EXPECT_EQ(std::string("Item 1,Item 2,Item 0,Item 3"),
457             model_->GetModelContent());
458
459   // Drag up, between apps 1 and 2. The gap should open up, making space for app
460   // 0 in the top right.
461   gfx::Point bottom_left = GetItemTileRectAt(1, 0).CenterPoint();
462   drag_vector.set_x(half_tile_width);
463   drag_vector.set_y(-tile_height);
464   SimulateDrag(AppsGridView::MOUSE, bottom_left, bottom_left + drag_vector);
465   apps_grid_view_->EndDrag(false);
466   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
467             model_->GetModelContent());
468
469   // Dragging down past the last app should reorder to the last position.
470   drag_vector.set_x(half_tile_width);
471   drag_vector.set_y(2 * tile_height);
472   SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
473   apps_grid_view_->EndDrag(false);
474   EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Item 0"),
475             model_->GetModelContent());
476 }
477
478 TEST_F(AppsGridViewTest, MouseDragFolderReorder) {
479   EnsureFoldersEnabled();
480
481   size_t kTotalItems = 2;
482   model_->CreateAndPopulateFolderWithApps(kTotalItems);
483   model_->PopulateAppWithId(kTotalItems);
484   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
485   EXPECT_EQ(AppListFolderItem::kItemType,
486             model_->top_level_item_list()->item_at(0)->GetItemType());
487   AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
488       model_->top_level_item_list()->item_at(0));
489   EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(1)->id());
490
491   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
492   gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
493
494   // Dragging folder over item_1 should leads to re-ordering these two
495   // items.
496   SimulateDrag(AppsGridView::MOUSE, from, to);
497   apps_grid_view_->EndDrag(false);
498   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
499   EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(0)->id());
500   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(1)->id());
501   test_api_->LayoutToIdealBounds();
502 }
503
504 TEST_F(AppsGridViewTest, MouseDragWithCancelDeleteAddItem) {
505   size_t kTotalItems = 4;
506   model_->PopulateApps(kTotalItems);
507   EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
508   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
509             model_->GetModelContent());
510
511   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
512   gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
513
514   // Canceling drag should keep existing order.
515   SimulateDrag(AppsGridView::MOUSE, from, to);
516   apps_grid_view_->EndDrag(true);
517   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
518             model_->GetModelContent());
519   test_api_->LayoutToIdealBounds();
520
521   // Deleting an item keeps remaining intact.
522   SimulateDrag(AppsGridView::MOUSE, from, to);
523   model_->DeleteItem(model_->GetItemName(2));
524   apps_grid_view_->EndDrag(false);
525   EXPECT_EQ(std::string("Item 0,Item 1,Item 3"), model_->GetModelContent());
526   test_api_->LayoutToIdealBounds();
527
528   // Adding a launcher item cancels the drag and respects the order.
529   SimulateDrag(AppsGridView::MOUSE, from, to);
530   model_->CreateAndAddItem("Extra");
531   apps_grid_view_->EndDrag(false);
532   EXPECT_EQ(std::string("Item 0,Item 1,Item 3,Extra"),
533             model_->GetModelContent());
534   test_api_->LayoutToIdealBounds();
535 }
536
537 TEST_F(AppsGridViewTest, MouseDragFlipPage) {
538   test_api_->SetPageFlipDelay(10);
539   GetPaginationModel()->SetTransitionDurations(10, 10);
540
541   PageFlipWaiter page_flip_waiter(message_loop(), GetPaginationModel());
542
543   const int kPages = 3;
544   model_->PopulateApps(kPages * kTilesPerPage);
545   EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
546   EXPECT_EQ(0, GetPaginationModel()->selected_page());
547
548   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
549   gfx::Point to = gfx::Point(apps_grid_view_->width(),
550                              apps_grid_view_->height() / 2);
551
552   // Drag to right edge.
553   page_flip_waiter.Reset();
554   SimulateDrag(AppsGridView::MOUSE, from, to);
555
556   // Page should be flipped after sometime to hit page 1 and 2 then stop.
557   while (test_api_->HasPendingPageFlip()) {
558     page_flip_waiter.Wait();
559   }
560   EXPECT_EQ("1,2", page_flip_waiter.selected_pages());
561   EXPECT_EQ(2, GetPaginationModel()->selected_page());
562
563   apps_grid_view_->EndDrag(true);
564
565   // Now drag to the left edge and test the other direction.
566   to.set_x(0);
567
568   page_flip_waiter.Reset();
569   SimulateDrag(AppsGridView::MOUSE, from, to);
570
571   while (test_api_->HasPendingPageFlip()) {
572     page_flip_waiter.Wait();
573   }
574   EXPECT_EQ("1,0", page_flip_waiter.selected_pages());
575   EXPECT_EQ(0, GetPaginationModel()->selected_page());
576
577   apps_grid_view_->EndDrag(true);
578 }
579
580 TEST_F(AppsGridViewTest, SimultaneousDragWithFolderDisabled) {
581   model_->SetFoldersEnabled(false);
582   const int kTotalItems = 4;
583   model_->PopulateApps(kTotalItems);
584   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
585             model_->GetModelContent());
586
587   gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
588   gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();
589
590   gfx::Point touch_from = GetItemTileRectAt(1, 0).CenterPoint();
591   gfx::Point touch_to = GetItemTileRectAt(1, 1).CenterPoint();
592
593   // Starts a mouse drag first then a touch drag.
594   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
595   SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
596   // Finishes the drag and mouse drag wins.
597   apps_grid_view_->EndDrag(false);
598   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
599             model_->GetModelContent());
600   test_api_->LayoutToIdealBounds();
601
602   // Starts a touch drag first then a mouse drag.
603   SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
604   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
605   // Finishes the drag and touch drag wins.
606   apps_grid_view_->EndDrag(false);
607   EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"),
608             model_->GetModelContent());
609   test_api_->LayoutToIdealBounds();
610 }
611
612 TEST_F(AppsGridViewTest, UpdateFolderBackgroundOnCancelDrag) {
613   EnsureFoldersEnabled();
614
615   const int kTotalItems = 4;
616   TestAppsGridViewFolderDelegate folder_delegate;
617   apps_grid_view_->set_folder_delegate(&folder_delegate);
618   model_->PopulateApps(kTotalItems);
619   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
620             model_->GetModelContent());
621
622   gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
623   gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();
624
625   // Starts a mouse drag and then cancels it.
626   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
627   EXPECT_TRUE(folder_delegate.show_bubble());
628   apps_grid_view_->EndDrag(true);
629   EXPECT_FALSE(folder_delegate.show_bubble());
630   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
631             model_->GetModelContent());
632 }
633
634 TEST_F(AppsGridViewTest, HighlightWithKeyboard) {
635   const int kPages = 3;
636   const int kItems = (kPages - 1) * kTilesPerPage + 1;
637   model_->PopulateApps(kItems);
638
639   const int first_index = 0;
640   const int last_index = kItems - 1;
641   const int last_index_on_page1_first_row = kRows - 1;
642   const int last_index_on_page1 = kTilesPerPage - 1;
643   const int first_index_on_page2 = kTilesPerPage;
644   const int first_index_on_page2_last_row = 2 * kTilesPerPage - kRows;
645   const int last_index_on_page2_last_row = 2 * kTilesPerPage - 1;
646
647   // Try moving off the item beyond the first one.
648   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
649   SimulateKeyPress(ui::VKEY_UP);
650   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
651   SimulateKeyPress(ui::VKEY_LEFT);
652   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
653
654   // Move to the last item and try to go past it.
655   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index));
656   SimulateKeyPress(ui::VKEY_DOWN);
657   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
658   SimulateKeyPress(ui::VKEY_RIGHT);
659   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
660
661   // Move right on last item on page 1 should get to first item on page 2's last
662   // row and vice versa.
663   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
664   SimulateKeyPress(ui::VKEY_RIGHT);
665   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
666       first_index_on_page2_last_row)));
667   SimulateKeyPress(ui::VKEY_LEFT);
668   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
669       last_index_on_page1)));
670
671   // Up/down on page boundary does nothing.
672   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
673   SimulateKeyPress(ui::VKEY_DOWN);
674   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
675       last_index_on_page1)));
676   apps_grid_view_->SetSelectedView(
677       GetItemViewAt(first_index_on_page2_last_row));
678   apps_grid_view_->
679       SetSelectedView(GetItemViewAt(last_index_on_page1_first_row));
680   SimulateKeyPress(ui::VKEY_UP);
681   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
682       last_index_on_page1_first_row)));
683
684   // Page up and down should go to the same item on the next and last page.
685   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index_on_page2));
686   SimulateKeyPress(ui::VKEY_PRIOR);
687   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
688       first_index)));
689   SimulateKeyPress(ui::VKEY_NEXT);
690   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
691       first_index_on_page2)));
692
693   // Moving onto a a page with too few apps to support the expected index snaps
694   // to the last available index.
695   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
696   SimulateKeyPress(ui::VKEY_RIGHT);
697   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
698       last_index)));
699   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
700   SimulateKeyPress(ui::VKEY_NEXT);
701   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
702       last_index)));
703
704
705
706   // After page switch, arrow keys select first item on current page.
707   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
708   GetPaginationModel()->SelectPage(1, false);
709   SimulateKeyPress(ui::VKEY_UP);
710   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
711       first_index_on_page2)));
712 }
713
714 TEST_F(AppsGridViewTest, ItemLabelShortNameOverride) {
715   // If the app's full name and short name differ, the title label's tooltip
716   // should always be the full name of the app.
717   std::string expected_text("xyz");
718   std::string expected_tooltip("tooltip");
719   AppListItem* item = model_->CreateAndAddItem("Item with short name");
720   model_->SetItemNameAndShortName(item, expected_tooltip, expected_text);
721
722   base::string16 actual_tooltip;
723   AppListItemView* item_view = GetItemViewAt(0);
724   ASSERT_TRUE(item_view);
725   const views::Label* title_label = item_view->title();
726   EXPECT_TRUE(title_label->GetTooltipText(
727       title_label->bounds().CenterPoint(), &actual_tooltip));
728   EXPECT_EQ(expected_tooltip, base::UTF16ToUTF8(actual_tooltip));
729   EXPECT_EQ(expected_text, base::UTF16ToUTF8(title_label->text()));
730 }
731
732 TEST_F(AppsGridViewTest, ItemLabelNoShortName) {
733   // If the app's full name and short name are the same, use the default tooltip
734   // behavior of the label (only show a tooltip if the title is truncated).
735   std::string title("a");
736   AppListItem* item = model_->CreateAndAddItem(title);
737   model_->SetItemNameAndShortName(item, title, "");
738
739   base::string16 actual_tooltip;
740   AppListItemView* item_view = GetItemViewAt(0);
741   ASSERT_TRUE(item_view);
742   const views::Label* title_label = item_view->title();
743   EXPECT_FALSE(title_label->GetTooltipText(
744       title_label->bounds().CenterPoint(), &actual_tooltip));
745   EXPECT_EQ(title, base::UTF16ToUTF8(title_label->text()));
746 }
747
748 }  // namespace test
749 }  // namespace app_list