Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / app_list / views / app_list_main_view_unittest.cc
blobd59e45f2097ed0b131da950f8c0417a908b33b99
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.
5 #include "ui/app_list/views/app_list_main_view.h"
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/test/app_list_test_model.h"
13 #include "ui/app_list/test/app_list_test_view_delegate.h"
14 #include "ui/app_list/views/app_list_folder_view.h"
15 #include "ui/app_list/views/app_list_item_view.h"
16 #include "ui/app_list/views/apps_container_view.h"
17 #include "ui/app_list/views/apps_grid_view.h"
18 #include "ui/app_list/views/contents_view.h"
19 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
20 #include "ui/views/test/views_test_base.h"
21 #include "ui/views/view_model.h"
22 #include "ui/views/widget/widget.h"
24 namespace app_list {
25 namespace test {
27 namespace {
29 const int kInitialItems = 2;
31 class GridViewVisibleWaiter {
32 public:
33 explicit GridViewVisibleWaiter(AppsGridView* grid_view)
34 : grid_view_(grid_view) {}
35 ~GridViewVisibleWaiter() {}
37 void Wait() {
38 if (grid_view_->visible())
39 return;
41 check_timer_.Start(FROM_HERE,
42 base::TimeDelta::FromMilliseconds(50),
43 base::Bind(&GridViewVisibleWaiter::OnTimerCheck,
44 base::Unretained(this)));
45 run_loop_.reset(new base::RunLoop);
46 run_loop_->Run();
47 check_timer_.Stop();
50 private:
51 void OnTimerCheck() {
52 if (grid_view_->visible())
53 run_loop_->Quit();
56 AppsGridView* grid_view_;
57 scoped_ptr<base::RunLoop> run_loop_;
58 base::RepeatingTimer<GridViewVisibleWaiter> check_timer_;
60 DISALLOW_COPY_AND_ASSIGN(GridViewVisibleWaiter);
63 class AppListMainViewTest : public views::ViewsTestBase {
64 public:
65 AppListMainViewTest()
66 : widget_(NULL),
67 main_view_(NULL) {}
69 virtual ~AppListMainViewTest() {}
71 // testing::Test overrides:
72 virtual void SetUp() OVERRIDE {
73 views::ViewsTestBase::SetUp();
74 delegate_.reset(new AppListTestViewDelegate);
76 // In Ash, the third argument is a container aura::Window, but it is always
77 // NULL on Windows, and not needed for tests. It is only used to determine
78 // the scale factor for preloading icons.
79 main_view_ = new AppListMainView(delegate_.get(), 0, NULL);
80 main_view_->SetPaintToLayer(true);
81 main_view_->model()->SetFoldersEnabled(true);
83 widget_ = new views::Widget;
84 views::Widget::InitParams params =
85 CreateParams(views::Widget::InitParams::TYPE_POPUP);
86 params.bounds.set_size(main_view_->GetPreferredSize());
87 widget_->Init(params);
89 widget_->SetContentsView(main_view_);
92 virtual void TearDown() OVERRIDE {
93 widget_->Close();
94 views::ViewsTestBase::TearDown();
95 delegate_.reset();
98 // |point| is in |grid_view|'s coordinates.
99 AppListItemView* GetItemViewAtPointInGrid(AppsGridView* grid_view,
100 const gfx::Point& point) {
101 const views::ViewModel* view_model = grid_view->view_model_for_test();
102 for (int i = 0; i < view_model->view_size(); ++i) {
103 views::View* view = view_model->view_at(i);
104 if (view->bounds().Contains(point)) {
105 return static_cast<AppListItemView*>(view);
109 return NULL;
112 void SimulateClick(views::View* view) {
113 gfx::Point center = view->GetLocalBounds().CenterPoint();
114 view->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED,
115 center,
116 center,
117 ui::EF_LEFT_MOUSE_BUTTON,
118 ui::EF_LEFT_MOUSE_BUTTON));
119 view->OnMouseReleased(ui::MouseEvent(ui::ET_MOUSE_RELEASED,
120 center,
121 center,
122 ui::EF_LEFT_MOUSE_BUTTON,
123 ui::EF_LEFT_MOUSE_BUTTON));
126 // |point| is in |grid_view|'s coordinates.
127 AppListItemView* SimulateInitiateDrag(AppsGridView* grid_view,
128 AppsGridView::Pointer pointer,
129 const gfx::Point& point) {
130 AppListItemView* view = GetItemViewAtPointInGrid(grid_view, point);
131 DCHECK(view);
133 gfx::Point translated =
134 gfx::PointAtOffsetFromOrigin(point - view->bounds().origin());
135 ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, translated, point, 0, 0);
136 grid_view->InitiateDrag(view, pointer, pressed_event);
137 return view;
140 // |point| is in |grid_view|'s coordinates.
141 void SimulateUpdateDrag(AppsGridView* grid_view,
142 AppsGridView::Pointer pointer,
143 AppListItemView* drag_view,
144 const gfx::Point& point) {
145 DCHECK(drag_view);
146 gfx::Point translated =
147 gfx::PointAtOffsetFromOrigin(point - drag_view->bounds().origin());
148 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, translated, point, 0, 0);
149 grid_view->UpdateDragFromItem(pointer, drag_event);
152 AppsGridView* RootGridView() {
153 return main_view_->contents_view()->apps_container_view()->apps_grid_view();
156 AppListFolderView* FolderView() {
157 return main_view_->contents_view()
158 ->apps_container_view()
159 ->app_list_folder_view();
162 AppsGridView* FolderGridView() { return FolderView()->items_grid_view(); }
164 const views::ViewModel* RootViewModel() {
165 return RootGridView()->view_model_for_test();
168 const views::ViewModel* FolderViewModel() {
169 return FolderGridView()->view_model_for_test();
172 AppListItemView* CreateAndOpenSingleItemFolder() {
173 // Prepare single folder with a single item in it.
174 AppListFolderItem* folder_item =
175 delegate_->GetTestModel()->CreateSingleItemFolder("single_item_folder",
176 "single");
177 EXPECT_EQ(folder_item,
178 delegate_->GetTestModel()->FindFolderItem("single_item_folder"));
179 EXPECT_EQ(AppListFolderItem::kItemType, folder_item->GetItemType());
181 EXPECT_EQ(1, RootViewModel()->view_size());
182 AppListItemView* folder_item_view =
183 static_cast<AppListItemView*>(RootViewModel()->view_at(0));
184 EXPECT_EQ(folder_item_view->item(), folder_item);
186 // Click on the folder to open it.
187 EXPECT_FALSE(FolderView()->visible());
188 SimulateClick(folder_item_view);
189 base::RunLoop().RunUntilIdle();
190 EXPECT_TRUE(FolderView()->visible());
192 #if defined(OS_WIN)
193 AppsGridViewTestApi folder_grid_view_test_api(FolderGridView());
194 folder_grid_view_test_api.DisableSynchronousDrag();
195 #endif
196 return folder_item_view;
199 AppListItemView* StartDragForReparent(int index_in_folder) {
200 // Start to drag the item in folder.
201 views::View* item_view = FolderViewModel()->view_at(index_in_folder);
202 gfx::Point point = item_view->bounds().CenterPoint();
203 AppListItemView* dragged =
204 SimulateInitiateDrag(FolderGridView(), AppsGridView::MOUSE, point);
205 EXPECT_EQ(item_view, dragged);
206 EXPECT_FALSE(RootGridView()->visible());
207 EXPECT_TRUE(FolderView()->visible());
209 // Drag it to top left corner.
210 point = gfx::Point(0, 0);
211 // Two update drags needed to actually drag the view. The first changes
212 // state and the 2nd one actually moves the view. The 2nd call can be
213 // removed when UpdateDrag is fixed.
214 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point);
215 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point);
216 base::RunLoop().RunUntilIdle();
218 // Wait until the folder view is invisible and root grid view shows up.
219 GridViewVisibleWaiter(RootGridView()).Wait();
220 EXPECT_TRUE(RootGridView()->visible());
221 EXPECT_EQ(0, FolderView()->layer()->opacity());
223 return dragged;
226 protected:
227 views::Widget* widget_; // Owned by native window.
228 AppListMainView* main_view_; // Owned by |widget_|.
229 scoped_ptr<AppListTestViewDelegate> delegate_;
231 private:
232 DISALLOW_COPY_AND_ASSIGN(AppListMainViewTest);
235 } // namespace
237 // Tests changing the AppListModel when switching profiles.
238 TEST_F(AppListMainViewTest, ModelChanged) {
239 delegate_->GetTestModel()->PopulateApps(kInitialItems);
240 EXPECT_EQ(kInitialItems, RootViewModel()->view_size());
242 // The model is owned by a profile keyed service, which is never destroyed
243 // until after profile switching.
244 scoped_ptr<AppListModel> old_model(delegate_->ReleaseTestModel());
246 const int kReplacementItems = 5;
247 delegate_->ReplaceTestModel(kReplacementItems);
248 main_view_->ModelChanged();
249 EXPECT_EQ(kReplacementItems, RootViewModel()->view_size());
252 // Tests dragging an item out of a single item folder and drop it at the last
253 // slot.
254 TEST_F(AppListMainViewTest, DragLastItemFromFolderAndDropAtLastSlot) {
255 AppListItemView* folder_item_view = CreateAndOpenSingleItemFolder();
256 const gfx::Rect first_slot_tile = folder_item_view->bounds();
258 EXPECT_EQ(1, FolderViewModel()->view_size());
260 AppListItemView* dragged = StartDragForReparent(0);
262 // Drop it to the slot on the right of first slot.
263 gfx::Rect drop_target_tile(first_slot_tile);
264 drop_target_tile.Offset(first_slot_tile.width(), 0);
265 gfx::Point point = drop_target_tile.CenterPoint();
266 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point);
267 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point);
268 base::RunLoop().RunUntilIdle();
270 // Drop it.
271 FolderGridView()->EndDrag(false);
272 base::RunLoop().RunUntilIdle();
274 // Folder icon view should be gone and there is only one item view.
275 EXPECT_EQ(1, RootViewModel()->view_size());
276 EXPECT_EQ(AppListItemView::kViewClassName,
277 RootViewModel()->view_at(0)->GetClassName());
279 // The item view should be in slot 1 instead of slot 2 where it is dropped.
280 AppsGridViewTestApi root_grid_view_test_api(RootGridView());
281 root_grid_view_test_api.LayoutToIdealBounds();
282 EXPECT_EQ(first_slot_tile, RootViewModel()->view_at(0)->bounds());
284 // Single item folder should be auto removed.
285 EXPECT_EQ(NULL,
286 delegate_->GetTestModel()->FindFolderItem("single_item_folder"));
289 // Test that an interrupted drag while reparenting an item from a folder, when
290 // canceled via the root grid, correctly forwards the cancelation to the drag
291 // ocurring from the folder.
292 TEST_F(AppListMainViewTest, MouseDragItemOutOfFolderWithCancel) {
293 CreateAndOpenSingleItemFolder();
294 AppListItemView* dragged = StartDragForReparent(0);
296 // Now add an item to the model, not in any folder, e.g., as if by Sync.
297 EXPECT_TRUE(RootGridView()->has_dragged_view());
298 EXPECT_TRUE(FolderGridView()->has_dragged_view());
299 delegate_->GetTestModel()->CreateAndAddItem("Extra");
301 // The drag operation should get canceled.
302 EXPECT_FALSE(RootGridView()->has_dragged_view());
303 EXPECT_FALSE(FolderGridView()->has_dragged_view());
305 // Additional mouse move operations should be ignored.
306 gfx::Point point(1, 1);
307 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point);
308 EXPECT_FALSE(RootGridView()->has_dragged_view());
309 EXPECT_FALSE(FolderGridView()->has_dragged_view());
312 } // namespace test
313 } // namespace app_list