Home | History | Annotate | Download | only in views
      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   AppListItemView* 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     return view;
    171   }
    172 
    173   void SimulateKeyPress(ui::KeyboardCode key_code) {
    174     ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, ui::EF_NONE);
    175     apps_grid_view_->OnKeyPressed(key_event);
    176   }
    177 
    178   scoped_ptr<AppListTestModel> model_;
    179   scoped_ptr<AppsGridView> apps_grid_view_;
    180   scoped_ptr<AppsGridViewTestApi> test_api_;
    181 
    182  private:
    183   DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest);
    184 };
    185 
    186 class TestAppsGridViewFolderDelegate : public AppsGridViewFolderDelegate {
    187  public:
    188   TestAppsGridViewFolderDelegate() : show_bubble_(false) {}
    189   virtual ~TestAppsGridViewFolderDelegate() {}
    190 
    191   // Overridden from AppsGridViewFolderDelegate:
    192   virtual void UpdateFolderViewBackground(bool show_bubble) OVERRIDE {
    193     show_bubble_ = show_bubble;
    194   }
    195 
    196   virtual void ReparentItem(AppListItemView* original_drag_view,
    197                             const gfx::Point& drag_point_in_folder_grid)
    198       OVERRIDE {}
    199 
    200   virtual void DispatchDragEventForReparent(
    201       AppsGridView::Pointer pointer,
    202       const gfx::Point& drag_point_in_folder_grid) OVERRIDE {}
    203 
    204   virtual void DispatchEndDragEventForReparent(
    205       bool events_forwarded_to_drag_drop_host,
    206       bool cancel_drag) OVERRIDE {}
    207 
    208   virtual bool IsPointOutsideOfFolderBoundary(const gfx::Point& point)
    209       OVERRIDE {
    210     return false;
    211   }
    212 
    213   virtual bool IsOEMFolder() const OVERRIDE { return false; }
    214 
    215   virtual void SetRootLevelDragViewVisible(bool visible) OVERRIDE {}
    216 
    217   bool show_bubble() { return show_bubble_; }
    218 
    219  private:
    220   bool show_bubble_;
    221 
    222   DISALLOW_COPY_AND_ASSIGN(TestAppsGridViewFolderDelegate);
    223 };
    224 
    225 TEST_F(AppsGridViewTest, CreatePage) {
    226   // Fully populates a page.
    227   const int kPages = 1;
    228   model_->PopulateApps(kPages * kTilesPerPage);
    229   EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
    230 
    231   // Adds one more and gets a new page created.
    232   model_->CreateAndAddItem("Extra");
    233   EXPECT_EQ(kPages + 1, GetPaginationModel()->total_pages());
    234 }
    235 
    236 TEST_F(AppsGridViewTest, EnsureHighlightedVisible) {
    237   const int kPages = 3;
    238   model_->PopulateApps(kPages * kTilesPerPage);
    239   EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
    240   EXPECT_EQ(0, GetPaginationModel()->selected_page());
    241 
    242   // Highlight first one and last one one first page and first page should be
    243   // selected.
    244   model_->HighlightItemAt(0);
    245   EXPECT_EQ(0, GetPaginationModel()->selected_page());
    246   model_->HighlightItemAt(kTilesPerPage - 1);
    247   EXPECT_EQ(0, GetPaginationModel()->selected_page());
    248 
    249   // Highlight first one on 2nd page and 2nd page should be selected.
    250   model_->HighlightItemAt(kTilesPerPage + 1);
    251   EXPECT_EQ(1, GetPaginationModel()->selected_page());
    252 
    253   // Highlight last one in the model and last page should be selected.
    254   model_->HighlightItemAt(model_->top_level_item_list()->item_count() - 1);
    255   EXPECT_EQ(kPages - 1, GetPaginationModel()->selected_page());
    256 }
    257 
    258 TEST_F(AppsGridViewTest, RemoveSelectedLastApp) {
    259   const int kTotalItems = 2;
    260   const int kLastItemIndex = kTotalItems - 1;
    261 
    262   model_->PopulateApps(kTotalItems);
    263 
    264   AppListItemView* last_view = GetItemViewAt(kLastItemIndex);
    265   apps_grid_view_->SetSelectedView(last_view);
    266   model_->DeleteItem(model_->GetItemName(kLastItemIndex));
    267 
    268   EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view));
    269 
    270   // No crash happens.
    271   AppListItemView* view = GetItemViewAt(0);
    272   apps_grid_view_->SetSelectedView(view);
    273   EXPECT_TRUE(apps_grid_view_->IsSelectedView(view));
    274 }
    275 
    276 TEST_F(AppsGridViewTest, MouseDragWithFolderDisabled) {
    277   model_->SetFoldersEnabled(false);
    278   const int kTotalItems = 4;
    279   model_->PopulateApps(kTotalItems);
    280   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    281             model_->GetModelContent());
    282 
    283   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
    284   gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
    285 
    286   // Dragging changes model order.
    287   SimulateDrag(AppsGridView::MOUSE, from, to);
    288   apps_grid_view_->EndDrag(false);
    289   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
    290             model_->GetModelContent());
    291   test_api_->LayoutToIdealBounds();
    292 
    293   // Canceling drag should keep existing order.
    294   SimulateDrag(AppsGridView::MOUSE, from, to);
    295   apps_grid_view_->EndDrag(true);
    296   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
    297             model_->GetModelContent());
    298   test_api_->LayoutToIdealBounds();
    299 
    300   // Deleting an item keeps remaining intact.
    301   SimulateDrag(AppsGridView::MOUSE, from, to);
    302   model_->DeleteItem(model_->GetItemName(0));
    303   apps_grid_view_->EndDrag(false);
    304   EXPECT_EQ(std::string("Item 1,Item 2,Item 3"),
    305             model_->GetModelContent());
    306   test_api_->LayoutToIdealBounds();
    307 
    308   // Adding a launcher item cancels the drag and respects the order.
    309   SimulateDrag(AppsGridView::MOUSE, from, to);
    310   EXPECT_TRUE(apps_grid_view_->has_dragged_view());
    311   model_->CreateAndAddItem("Extra");
    312   // No need to EndDrag explicitly - adding an item should do this.
    313   EXPECT_FALSE(apps_grid_view_->has_dragged_view());
    314   // Even though cancelled, mouse move events can still arrive via the item
    315   // view. Ensure that behaves sanely, and doesn't start a new drag.
    316   ui::MouseEvent drag_event(
    317       ui::ET_MOUSE_DRAGGED, gfx::Point(1, 1), gfx::Point(2, 2), 0, 0);
    318   apps_grid_view_->UpdateDragFromItem(AppsGridView::MOUSE, drag_event);
    319   EXPECT_FALSE(apps_grid_view_->has_dragged_view());
    320 
    321   EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"),
    322             model_->GetModelContent());
    323   test_api_->LayoutToIdealBounds();
    324 }
    325 
    326 TEST_F(AppsGridViewTest, MouseDragItemIntoFolder) {
    327   EnsureFoldersEnabled();
    328 
    329   size_t kTotalItems = 3;
    330   model_->PopulateApps(kTotalItems);
    331   EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
    332   EXPECT_EQ(std::string("Item 0,Item 1,Item 2"), model_->GetModelContent());
    333 
    334   gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
    335   gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint();
    336 
    337   // Dragging item_1 over item_0 creates a folder.
    338   SimulateDrag(AppsGridView::MOUSE, from, to);
    339   apps_grid_view_->EndDrag(false);
    340   EXPECT_EQ(kTotalItems - 1, model_->top_level_item_list()->item_count());
    341   EXPECT_EQ(AppListFolderItem::kItemType,
    342             model_->top_level_item_list()->item_at(0)->GetItemType());
    343   AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
    344       model_->top_level_item_list()->item_at(0));
    345   EXPECT_EQ(2u, folder_item->ChildItemCount());
    346   AppListItem* item_0 = model_->FindItem("Item 0");
    347   EXPECT_TRUE(item_0->IsInFolder());
    348   EXPECT_EQ(folder_item->id(), item_0->folder_id());
    349   AppListItem* item_1 = model_->FindItem("Item 1");
    350   EXPECT_TRUE(item_1->IsInFolder());
    351   EXPECT_EQ(folder_item->id(), item_1->folder_id());
    352   std::string expected_items = folder_item->id() + ",Item 2";
    353   EXPECT_EQ(expected_items, model_->GetModelContent());
    354   test_api_->LayoutToIdealBounds();
    355 
    356   // Dragging item_2 to the folder adds item_2 to the folder.
    357   SimulateDrag(AppsGridView::MOUSE, from, to);
    358   apps_grid_view_->EndDrag(false);
    359 
    360   EXPECT_EQ(kTotalItems - 2, model_->top_level_item_list()->item_count());
    361   EXPECT_EQ(folder_item->id(), model_->GetModelContent());
    362   EXPECT_EQ(3u, folder_item->ChildItemCount());
    363   item_0 = model_->FindItem("Item 0");
    364   EXPECT_TRUE(item_0->IsInFolder());
    365   EXPECT_EQ(folder_item->id(), item_0->folder_id());
    366   item_1 = model_->FindItem("Item 1");
    367   EXPECT_TRUE(item_1->IsInFolder());
    368   EXPECT_EQ(folder_item->id(), item_1->folder_id());
    369   AppListItem* item_2 = model_->FindItem("Item 2");
    370   EXPECT_TRUE(item_2->IsInFolder());
    371   EXPECT_EQ(folder_item->id(), item_2->folder_id());
    372   test_api_->LayoutToIdealBounds();
    373 }
    374 
    375 TEST_F(AppsGridViewTest, MouseDragMaxItemsInFolder) {
    376   EnsureFoldersEnabled();
    377 
    378   // Create and add a folder with 15 items in it.
    379   size_t kTotalItems = kMaxFolderItems - 1;
    380   model_->CreateAndPopulateFolderWithApps(kTotalItems);
    381   EXPECT_EQ(1u, model_->top_level_item_list()->item_count());
    382   EXPECT_EQ(AppListFolderItem::kItemType,
    383             model_->top_level_item_list()->item_at(0)->GetItemType());
    384   AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
    385       model_->top_level_item_list()->item_at(0));
    386   EXPECT_EQ(kTotalItems, folder_item->ChildItemCount());
    387 
    388   // Create and add another 2 items.
    389   model_->PopulateAppWithId(kTotalItems);
    390   model_->PopulateAppWithId(kTotalItems + 1);
    391   EXPECT_EQ(3u, model_->top_level_item_list()->item_count());
    392   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
    393   EXPECT_EQ(model_->GetItemName(kMaxFolderItems - 1),
    394             model_->top_level_item_list()->item_at(1)->id());
    395   EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
    396             model_->top_level_item_list()->item_at(2)->id());
    397 
    398   gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
    399   gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint();
    400 
    401   // Dragging one item into the folder, the folder should accept the item.
    402   SimulateDrag(AppsGridView::MOUSE, from, to);
    403   apps_grid_view_->EndDrag(false);
    404   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    405   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
    406   EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount());
    407   EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
    408             model_->top_level_item_list()->item_at(1)->id());
    409   test_api_->LayoutToIdealBounds();
    410 
    411   // Dragging the last item over the folder, the folder won't accept the new
    412   // item.
    413   SimulateDrag(AppsGridView::MOUSE, from, to);
    414   apps_grid_view_->EndDrag(false);
    415   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    416   EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount());
    417   test_api_->LayoutToIdealBounds();
    418 }
    419 
    420 // Check that moving items around doesn't allow a drop to happen into a full
    421 // folder.
    422 TEST_F(AppsGridViewTest, MouseDragMaxItemsInFolderWithMovement) {
    423   EnsureFoldersEnabled();
    424 
    425   // Create and add a folder with 16 items in it.
    426   size_t kTotalItems = kMaxFolderItems;
    427   model_->CreateAndPopulateFolderWithApps(kTotalItems);
    428   EXPECT_EQ(1u, model_->top_level_item_list()->item_count());
    429   EXPECT_EQ(AppListFolderItem::kItemType,
    430             model_->top_level_item_list()->item_at(0)->GetItemType());
    431   AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
    432       model_->top_level_item_list()->item_at(0));
    433   EXPECT_EQ(kTotalItems, folder_item->ChildItemCount());
    434 
    435   // Create and add another item.
    436   model_->PopulateAppWithId(kTotalItems);
    437   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    438   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
    439   EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
    440             model_->top_level_item_list()->item_at(1)->id());
    441 
    442   AppListItemView* folder_view =
    443       GetItemViewForPoint(GetItemTileRectAt(0, 0).CenterPoint());
    444 
    445   // Drag the new item to the left so that the grid reorders.
    446   gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
    447   gfx::Point to = GetItemTileRectAt(0, 0).bottom_left();
    448   AppListItemView* dragged_view = SimulateDrag(AppsGridView::MOUSE, from, to);
    449   test_api_->LayoutToIdealBounds();
    450 
    451   // The grid now looks like | blank | folder |.
    452   EXPECT_EQ(NULL, GetItemViewForPoint(GetItemTileRectAt(0, 0).CenterPoint()));
    453   EXPECT_EQ(folder_view,
    454             GetItemViewForPoint(GetItemTileRectAt(0, 1).CenterPoint()));
    455 
    456   // Move onto the folder and end the drag.
    457   to = GetItemTileRectAt(0, 1).CenterPoint();
    458   gfx::Point translated_to =
    459       gfx::PointAtOffsetFromOrigin(to - dragged_view->bounds().origin());
    460   ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, translated_to, to, 0, 0);
    461   apps_grid_view_->UpdateDragFromItem(AppsGridView::MOUSE, drag_event);
    462   apps_grid_view_->EndDrag(false);
    463 
    464   // The item should not have moved into the folder.
    465   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    466   EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount());
    467   test_api_->LayoutToIdealBounds();
    468 }
    469 
    470 TEST_F(AppsGridViewTest, MouseDragItemReorder) {
    471   // This test assumes Folders are enabled.
    472   EnsureFoldersEnabled();
    473 
    474   model_->PopulateApps(4);
    475   EXPECT_EQ(4u, model_->top_level_item_list()->item_count());
    476   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    477             model_->GetModelContent());
    478 
    479   // Dragging an item towards its neighbours should not reorder until the drag
    480   // is past the folder drop point.
    481   gfx::Point top_right = GetItemTileRectAt(0, 1).CenterPoint();
    482   gfx::Vector2d drag_vector;
    483   int half_tile_width =
    484       (GetItemTileRectAt(0, 1).x() - GetItemTileRectAt(0, 0).x()) / 2;
    485   int tile_height = GetItemTileRectAt(1, 0).y() - GetItemTileRectAt(0, 0).y();
    486 
    487   // Drag left but stop before the folder dropping circle.
    488   drag_vector.set_x(-half_tile_width - 5);
    489   SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
    490   apps_grid_view_->EndDrag(false);
    491   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    492             model_->GetModelContent());
    493 
    494   // Drag left, past the folder dropping circle.
    495   drag_vector.set_x(-3 * half_tile_width + 5);
    496   SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
    497   apps_grid_view_->EndDrag(false);
    498   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
    499             model_->GetModelContent());
    500 
    501   // Drag down, between apps 2 and 3. The gap should open up, making space for
    502   // app 0 in the bottom left.
    503   drag_vector.set_x(-half_tile_width);
    504   drag_vector.set_y(tile_height);
    505   SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
    506   apps_grid_view_->EndDrag(false);
    507   EXPECT_EQ(std::string("Item 1,Item 2,Item 0,Item 3"),
    508             model_->GetModelContent());
    509 
    510   // Drag up, between apps 1 and 2. The gap should open up, making space for app
    511   // 0 in the top right.
    512   gfx::Point bottom_left = GetItemTileRectAt(1, 0).CenterPoint();
    513   drag_vector.set_x(half_tile_width);
    514   drag_vector.set_y(-tile_height);
    515   SimulateDrag(AppsGridView::MOUSE, bottom_left, bottom_left + drag_vector);
    516   apps_grid_view_->EndDrag(false);
    517   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
    518             model_->GetModelContent());
    519 
    520   // Dragging down past the last app should reorder to the last position.
    521   drag_vector.set_x(half_tile_width);
    522   drag_vector.set_y(2 * tile_height);
    523   SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
    524   apps_grid_view_->EndDrag(false);
    525   EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Item 0"),
    526             model_->GetModelContent());
    527 }
    528 
    529 TEST_F(AppsGridViewTest, MouseDragFolderReorder) {
    530   EnsureFoldersEnabled();
    531 
    532   size_t kTotalItems = 2;
    533   model_->CreateAndPopulateFolderWithApps(kTotalItems);
    534   model_->PopulateAppWithId(kTotalItems);
    535   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    536   EXPECT_EQ(AppListFolderItem::kItemType,
    537             model_->top_level_item_list()->item_at(0)->GetItemType());
    538   AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
    539       model_->top_level_item_list()->item_at(0));
    540   EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(1)->id());
    541 
    542   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
    543   gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
    544 
    545   // Dragging folder over item_1 should leads to re-ordering these two
    546   // items.
    547   SimulateDrag(AppsGridView::MOUSE, from, to);
    548   apps_grid_view_->EndDrag(false);
    549   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    550   EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(0)->id());
    551   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(1)->id());
    552   test_api_->LayoutToIdealBounds();
    553 }
    554 
    555 TEST_F(AppsGridViewTest, MouseDragWithCancelDeleteAddItem) {
    556   size_t kTotalItems = 4;
    557   model_->PopulateApps(kTotalItems);
    558   EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
    559   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    560             model_->GetModelContent());
    561 
    562   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
    563   gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
    564 
    565   // Canceling drag should keep existing order.
    566   SimulateDrag(AppsGridView::MOUSE, from, to);
    567   apps_grid_view_->EndDrag(true);
    568   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    569             model_->GetModelContent());
    570   test_api_->LayoutToIdealBounds();
    571 
    572   // Deleting an item keeps remaining intact.
    573   SimulateDrag(AppsGridView::MOUSE, from, to);
    574   model_->DeleteItem(model_->GetItemName(2));
    575   apps_grid_view_->EndDrag(false);
    576   EXPECT_EQ(std::string("Item 0,Item 1,Item 3"), model_->GetModelContent());
    577   test_api_->LayoutToIdealBounds();
    578 
    579   // Adding a launcher item cancels the drag and respects the order.
    580   SimulateDrag(AppsGridView::MOUSE, from, to);
    581   model_->CreateAndAddItem("Extra");
    582   apps_grid_view_->EndDrag(false);
    583   EXPECT_EQ(std::string("Item 0,Item 1,Item 3,Extra"),
    584             model_->GetModelContent());
    585   test_api_->LayoutToIdealBounds();
    586 }
    587 
    588 TEST_F(AppsGridViewTest, MouseDragFlipPage) {
    589   test_api_->SetPageFlipDelay(10);
    590   GetPaginationModel()->SetTransitionDurations(10, 10);
    591 
    592   PageFlipWaiter page_flip_waiter(message_loop(), GetPaginationModel());
    593 
    594   const int kPages = 3;
    595   model_->PopulateApps(kPages * kTilesPerPage);
    596   EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
    597   EXPECT_EQ(0, GetPaginationModel()->selected_page());
    598 
    599   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
    600   gfx::Point to = gfx::Point(apps_grid_view_->width(),
    601                              apps_grid_view_->height() / 2);
    602 
    603   // Drag to right edge.
    604   page_flip_waiter.Reset();
    605   SimulateDrag(AppsGridView::MOUSE, from, to);
    606 
    607   // Page should be flipped after sometime to hit page 1 and 2 then stop.
    608   while (test_api_->HasPendingPageFlip()) {
    609     page_flip_waiter.Wait();
    610   }
    611   EXPECT_EQ("1,2", page_flip_waiter.selected_pages());
    612   EXPECT_EQ(2, GetPaginationModel()->selected_page());
    613 
    614   apps_grid_view_->EndDrag(true);
    615 
    616   // Now drag to the left edge and test the other direction.
    617   to.set_x(0);
    618 
    619   page_flip_waiter.Reset();
    620   SimulateDrag(AppsGridView::MOUSE, from, to);
    621 
    622   while (test_api_->HasPendingPageFlip()) {
    623     page_flip_waiter.Wait();
    624   }
    625   EXPECT_EQ("1,0", page_flip_waiter.selected_pages());
    626   EXPECT_EQ(0, GetPaginationModel()->selected_page());
    627 
    628   apps_grid_view_->EndDrag(true);
    629 }
    630 
    631 TEST_F(AppsGridViewTest, SimultaneousDragWithFolderDisabled) {
    632   model_->SetFoldersEnabled(false);
    633   const int kTotalItems = 4;
    634   model_->PopulateApps(kTotalItems);
    635   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    636             model_->GetModelContent());
    637 
    638   gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
    639   gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();
    640 
    641   gfx::Point touch_from = GetItemTileRectAt(1, 0).CenterPoint();
    642   gfx::Point touch_to = GetItemTileRectAt(1, 1).CenterPoint();
    643 
    644   // Starts a mouse drag first then a touch drag.
    645   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
    646   SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
    647   // Finishes the drag and mouse drag wins.
    648   apps_grid_view_->EndDrag(false);
    649   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
    650             model_->GetModelContent());
    651   test_api_->LayoutToIdealBounds();
    652 
    653   // Starts a touch drag first then a mouse drag.
    654   SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
    655   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
    656   // Finishes the drag and touch drag wins.
    657   apps_grid_view_->EndDrag(false);
    658   EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"),
    659             model_->GetModelContent());
    660   test_api_->LayoutToIdealBounds();
    661 }
    662 
    663 TEST_F(AppsGridViewTest, UpdateFolderBackgroundOnCancelDrag) {
    664   EnsureFoldersEnabled();
    665 
    666   const int kTotalItems = 4;
    667   TestAppsGridViewFolderDelegate folder_delegate;
    668   apps_grid_view_->set_folder_delegate(&folder_delegate);
    669   model_->PopulateApps(kTotalItems);
    670   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    671             model_->GetModelContent());
    672 
    673   gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
    674   gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();
    675 
    676   // Starts a mouse drag and then cancels it.
    677   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
    678   EXPECT_TRUE(folder_delegate.show_bubble());
    679   apps_grid_view_->EndDrag(true);
    680   EXPECT_FALSE(folder_delegate.show_bubble());
    681   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    682             model_->GetModelContent());
    683 }
    684 
    685 TEST_F(AppsGridViewTest, HighlightWithKeyboard) {
    686   const int kPages = 3;
    687   const int kItems = (kPages - 1) * kTilesPerPage + 1;
    688   model_->PopulateApps(kItems);
    689 
    690   const int first_index = 0;
    691   const int last_index = kItems - 1;
    692   const int last_index_on_page1_first_row = kRows - 1;
    693   const int last_index_on_page1 = kTilesPerPage - 1;
    694   const int first_index_on_page2 = kTilesPerPage;
    695   const int first_index_on_page2_last_row = 2 * kTilesPerPage - kRows;
    696   const int last_index_on_page2_last_row = 2 * kTilesPerPage - 1;
    697 
    698   // Try moving off the item beyond the first one.
    699   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
    700   SimulateKeyPress(ui::VKEY_UP);
    701   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
    702   SimulateKeyPress(ui::VKEY_LEFT);
    703   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
    704 
    705   // Move to the last item and try to go past it.
    706   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index));
    707   SimulateKeyPress(ui::VKEY_DOWN);
    708   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
    709   SimulateKeyPress(ui::VKEY_RIGHT);
    710   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
    711 
    712   // Move right on last item on page 1 should get to first item on page 2's last
    713   // row and vice versa.
    714   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
    715   SimulateKeyPress(ui::VKEY_RIGHT);
    716   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    717       first_index_on_page2_last_row)));
    718   SimulateKeyPress(ui::VKEY_LEFT);
    719   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    720       last_index_on_page1)));
    721 
    722   // Up/down on page boundary does nothing.
    723   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
    724   SimulateKeyPress(ui::VKEY_DOWN);
    725   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    726       last_index_on_page1)));
    727   apps_grid_view_->SetSelectedView(
    728       GetItemViewAt(first_index_on_page2_last_row));
    729   apps_grid_view_->
    730       SetSelectedView(GetItemViewAt(last_index_on_page1_first_row));
    731   SimulateKeyPress(ui::VKEY_UP);
    732   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    733       last_index_on_page1_first_row)));
    734 
    735   // Page up and down should go to the same item on the next and last page.
    736   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index_on_page2));
    737   SimulateKeyPress(ui::VKEY_PRIOR);
    738   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    739       first_index)));
    740   SimulateKeyPress(ui::VKEY_NEXT);
    741   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    742       first_index_on_page2)));
    743 
    744   // Moving onto a a page with too few apps to support the expected index snaps
    745   // to the last available index.
    746   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
    747   SimulateKeyPress(ui::VKEY_RIGHT);
    748   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    749       last_index)));
    750   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
    751   SimulateKeyPress(ui::VKEY_NEXT);
    752   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    753       last_index)));
    754 
    755 
    756 
    757   // After page switch, arrow keys select first item on current page.
    758   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
    759   GetPaginationModel()->SelectPage(1, false);
    760   SimulateKeyPress(ui::VKEY_UP);
    761   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    762       first_index_on_page2)));
    763 }
    764 
    765 TEST_F(AppsGridViewTest, ItemLabelShortNameOverride) {
    766   // If the app's full name and short name differ, the title label's tooltip
    767   // should always be the full name of the app.
    768   std::string expected_text("xyz");
    769   std::string expected_tooltip("tooltip");
    770   AppListItem* item = model_->CreateAndAddItem("Item with short name");
    771   model_->SetItemNameAndShortName(item, expected_tooltip, expected_text);
    772 
    773   base::string16 actual_tooltip;
    774   AppListItemView* item_view = GetItemViewAt(0);
    775   ASSERT_TRUE(item_view);
    776   const views::Label* title_label = item_view->title();
    777   EXPECT_TRUE(title_label->GetTooltipText(
    778       title_label->bounds().CenterPoint(), &actual_tooltip));
    779   EXPECT_EQ(expected_tooltip, base::UTF16ToUTF8(actual_tooltip));
    780   EXPECT_EQ(expected_text, base::UTF16ToUTF8(title_label->text()));
    781 }
    782 
    783 TEST_F(AppsGridViewTest, ItemLabelNoShortName) {
    784   // If the app's full name and short name are the same, use the default tooltip
    785   // behavior of the label (only show a tooltip if the title is truncated).
    786   std::string title("a");
    787   AppListItem* item = model_->CreateAndAddItem(title);
    788   model_->SetItemNameAndShortName(item, title, "");
    789 
    790   base::string16 actual_tooltip;
    791   AppListItemView* item_view = GetItemViewAt(0);
    792   ASSERT_TRUE(item_view);
    793   const views::Label* title_label = item_view->title();
    794   EXPECT_FALSE(title_label->GetTooltipText(
    795       title_label->bounds().CenterPoint(), &actual_tooltip));
    796   EXPECT_EQ(title, base::UTF16ToUTF8(title_label->text()));
    797 }
    798 
    799 }  // namespace test
    800 }  // namespace app_list
    801