app_list_main_view_unittest.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 "ui/app_list/views/app_list_main_view.h" 6 7#include "base/memory/scoped_ptr.h" 8#include "base/run_loop.h" 9#include "base/time/time.h" 10#include "base/timer/timer.h" 11#include "testing/gtest/include/gtest/gtest.h" 12#include "ui/app_list/pagination_model.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#include "ui/app_list/views/app_list_folder_view.h" 16#include "ui/app_list/views/app_list_item_view.h" 17#include "ui/app_list/views/apps_container_view.h" 18#include "ui/app_list/views/apps_grid_view.h" 19#include "ui/app_list/views/contents_view.h" 20#include "ui/app_list/views/test/apps_grid_view_test_api.h" 21#include "ui/views/test/views_test_base.h" 22#include "ui/views/view_model.h" 23#include "ui/views/widget/widget.h" 24 25namespace app_list { 26namespace test { 27 28namespace { 29 30const int kInitialItems = 2; 31 32class GridViewVisibleWaiter { 33 public: 34 explicit GridViewVisibleWaiter(AppsGridView* grid_view) 35 : grid_view_(grid_view) {} 36 ~GridViewVisibleWaiter() {} 37 38 void Wait() { 39 if (grid_view_->visible()) 40 return; 41 42 check_timer_.Start(FROM_HERE, 43 base::TimeDelta::FromMilliseconds(50), 44 base::Bind(&GridViewVisibleWaiter::OnTimerCheck, 45 base::Unretained(this))); 46 run_loop_.reset(new base::RunLoop); 47 run_loop_->Run(); 48 check_timer_.Stop(); 49 } 50 51 private: 52 void OnTimerCheck() { 53 if (grid_view_->visible()) 54 run_loop_->Quit(); 55 } 56 57 AppsGridView* grid_view_; 58 scoped_ptr<base::RunLoop> run_loop_; 59 base::RepeatingTimer<GridViewVisibleWaiter> check_timer_; 60 61 DISALLOW_COPY_AND_ASSIGN(GridViewVisibleWaiter); 62}; 63 64class AppListMainViewTest : public views::ViewsTestBase { 65 public: 66 AppListMainViewTest() 67 : widget_(NULL), 68 main_view_(NULL) {} 69 70 virtual ~AppListMainViewTest() {} 71 72 // testing::Test overrides: 73 virtual void SetUp() OVERRIDE { 74 views::ViewsTestBase::SetUp(); 75 delegate_.reset(new AppListTestViewDelegate); 76 77 main_view_ = 78 new AppListMainView(delegate_.get(), &pagination_model_, GetContext()); 79 main_view_->SetPaintToLayer(true); 80 81 widget_ = new views::Widget; 82 views::Widget::InitParams params = 83 CreateParams(views::Widget::InitParams::TYPE_POPUP); 84 params.bounds.set_size(main_view_->GetPreferredSize()); 85 widget_->Init(params); 86 87 widget_->SetContentsView(main_view_); 88 } 89 90 virtual void TearDown() OVERRIDE { 91 widget_->Close(); 92 views::ViewsTestBase::TearDown(); 93 delegate_.reset(); 94 } 95 96 // |point| is in |grid_view|'s coordinates. 97 AppListItemView* GetItemViewAtPointInGrid(AppsGridView* grid_view, 98 const gfx::Point& point) { 99 const views::ViewModel* view_model = grid_view->view_model_for_test(); 100 for (int i = 0; i < view_model->view_size(); ++i) { 101 views::View* view = view_model->view_at(i); 102 if (view->bounds().Contains(point)) { 103 return static_cast<AppListItemView*>(view); 104 } 105 } 106 107 return NULL; 108 } 109 110 void SimulateClick(views::View* view) { 111 gfx::Point center = view->GetLocalBounds().CenterPoint(); 112 view->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED, 113 center, 114 center, 115 ui::EF_LEFT_MOUSE_BUTTON, 116 ui::EF_LEFT_MOUSE_BUTTON)); 117 view->OnMouseReleased(ui::MouseEvent(ui::ET_MOUSE_RELEASED, 118 center, 119 center, 120 ui::EF_LEFT_MOUSE_BUTTON, 121 ui::EF_LEFT_MOUSE_BUTTON)); 122 } 123 124 // |point| is in |grid_view|'s coordinates. 125 AppListItemView* SimulateInitiateDrag(AppsGridView* grid_view, 126 AppsGridView::Pointer pointer, 127 const gfx::Point& point) { 128 AppListItemView* view = GetItemViewAtPointInGrid(grid_view, point); 129 DCHECK(view); 130 131 gfx::Point translated = 132 gfx::PointAtOffsetFromOrigin(point - view->bounds().origin()); 133 ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, translated, point, 0, 0); 134 grid_view->InitiateDrag(view, pointer, pressed_event); 135 return view; 136 } 137 138 // |point| is in |grid_view|'s coordinates. 139 void SimulateUpdateDrag(AppsGridView* grid_view, 140 AppsGridView::Pointer pointer, 141 AppListItemView* drag_view, 142 const gfx::Point& point) { 143 DCHECK(drag_view); 144 gfx::Point translated = 145 gfx::PointAtOffsetFromOrigin(point - drag_view->bounds().origin()); 146 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, translated, point, 0, 0); 147 grid_view->UpdateDragFromItem(pointer, drag_event); 148 } 149 150 AppsGridView* RootGridView() { 151 return main_view_->contents_view()->apps_container_view()->apps_grid_view(); 152 } 153 154 AppListFolderView* FolderView() { 155 return main_view_->contents_view() 156 ->apps_container_view() 157 ->app_list_folder_view(); 158 } 159 160 AppsGridView* FolderGridView() { return FolderView()->items_grid_view(); } 161 162 const views::ViewModel* RootViewModel() { 163 return RootGridView()->view_model_for_test(); 164 } 165 166 const views::ViewModel* FolderViewModel() { 167 return FolderGridView()->view_model_for_test(); 168 } 169 170 protected: 171 views::Widget* widget_; // Owned by native window. 172 AppListMainView* main_view_; // Owned by |widget_|. 173 PaginationModel pagination_model_; 174 scoped_ptr<AppListTestViewDelegate> delegate_; 175 176 private: 177 DISALLOW_COPY_AND_ASSIGN(AppListMainViewTest); 178}; 179 180} // namespace 181 182// Tests changing the AppListModel when switching profiles. 183TEST_F(AppListMainViewTest, ModelChanged) { 184 delegate_->GetTestModel()->PopulateApps(kInitialItems); 185 EXPECT_EQ(kInitialItems, RootViewModel()->view_size()); 186 187 // The model is owned by a profile keyed service, which is never destroyed 188 // until after profile switching. 189 scoped_ptr<AppListModel> old_model(delegate_->ReleaseTestModel()); 190 191 const int kReplacementItems = 5; 192 delegate_->ReplaceTestModel(kReplacementItems); 193 main_view_->ModelChanged(); 194 EXPECT_EQ(kReplacementItems, RootViewModel()->view_size()); 195} 196 197// Tests dragging an item out of a single item folder and drop it at the last 198// slot. 199TEST_F(AppListMainViewTest, DragLastItemFromFolderAndDropAtLastSlot) { 200 // Prepare single folder with a single item in it. 201 AppListFolderItem* folder_item = 202 delegate_->GetTestModel()->CreateSingleItemFolder("single_item_folder", 203 "single"); 204 EXPECT_EQ(folder_item, 205 delegate_->GetTestModel()->FindFolderItem("single_item_folder")); 206 EXPECT_EQ(AppListFolderItem::kItemType, folder_item->GetItemType()); 207 208 EXPECT_EQ(1, RootViewModel()->view_size()); 209 AppListItemView* folder_item_view = 210 static_cast<AppListItemView*>(RootViewModel()->view_at(0)); 211 EXPECT_EQ(folder_item_view->item(), folder_item); 212 const gfx::Rect first_slot_tile = folder_item_view->bounds(); 213 214 // Click on the folder to open it. 215 EXPECT_FALSE(FolderView()->visible()); 216 SimulateClick(folder_item_view); 217 base::RunLoop().RunUntilIdle(); 218 EXPECT_TRUE(FolderView()->visible()); 219 220#if defined(OS_WIN) 221 AppsGridViewTestApi folder_grid_view_test_api(FolderGridView()); 222 folder_grid_view_test_api.DisableSynchronousDrag(); 223#endif 224 225 // Start to drag the item in folder. 226 EXPECT_EQ(1, FolderViewModel()->view_size()); 227 views::View* item_view = FolderViewModel()->view_at(0); 228 gfx::Point point = item_view->bounds().CenterPoint(); 229 AppListItemView* dragged = 230 SimulateInitiateDrag(FolderGridView(), AppsGridView::MOUSE, point); 231 EXPECT_EQ(item_view, dragged); 232 EXPECT_FALSE(RootGridView()->visible()); 233 EXPECT_TRUE(FolderView()->visible()); 234 235 // Drag it to top left corner. 236 point = gfx::Point(0, 0); 237 // Two update drags needed to actually drag the view. The first changes state 238 // and the 2nd one actually moves the view. The 2nd call can be removed when 239 // UpdateDrag is fixed. 240 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point); 241 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point); 242 base::RunLoop().RunUntilIdle(); 243 244 // Wait until the folder view is invisible and root grid view shows up. 245 GridViewVisibleWaiter(RootGridView()).Wait(); 246 EXPECT_TRUE(RootGridView()->visible()); 247 EXPECT_EQ(0, FolderView()->layer()->opacity()); 248 249 // Drop it to the slot on the right of first slot. 250 gfx::Rect drop_target_tile(first_slot_tile); 251 drop_target_tile.Offset(first_slot_tile.width(), 0); 252 point = drop_target_tile.CenterPoint(); 253 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point); 254 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point); 255 base::RunLoop().RunUntilIdle(); 256 257 // Drop it. 258 FolderGridView()->EndDrag(false); 259 base::RunLoop().RunUntilIdle(); 260 261 // Folder icon view should be gone and there is only one item view. 262 EXPECT_EQ(1, RootViewModel()->view_size()); 263 EXPECT_EQ(AppListItemView::kViewClassName, 264 RootViewModel()->view_at(0)->GetClassName()); 265 266 // The item view should be in slot 1 instead of slot 2 where it is dropped. 267 AppsGridViewTestApi root_grid_view_test_api(RootGridView()); 268 root_grid_view_test_api.LayoutToIdealBounds(); 269 EXPECT_EQ(first_slot_tile, RootViewModel()->view_at(0)->bounds()); 270 271 // Single item folder should be auto removed. 272 EXPECT_EQ(NULL, 273 delegate_->GetTestModel()->FindFolderItem("single_item_folder")); 274} 275 276} // namespace test 277} // namespace app_list 278