Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / ui / app_list / views / app_list_main_view_unittest.cc
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/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"
23
24 namespace app_list {
25 namespace test {
26
27 namespace {
28
29 const int kInitialItems = 2;
30
31 class GridViewVisibleWaiter {
32  public:
33   explicit GridViewVisibleWaiter(AppsGridView* grid_view)
34       : grid_view_(grid_view) {}
35   ~GridViewVisibleWaiter() {}
36
37   void Wait() {
38     if (grid_view_->visible())
39       return;
40
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();
48   }
49
50  private:
51   void OnTimerCheck() {
52     if (grid_view_->visible())
53       run_loop_->Quit();
54   }
55
56   AppsGridView* grid_view_;
57   scoped_ptr<base::RunLoop> run_loop_;
58   base::RepeatingTimer<GridViewVisibleWaiter> check_timer_;
59
60   DISALLOW_COPY_AND_ASSIGN(GridViewVisibleWaiter);
61 };
62
63 class AppListMainViewTest : public views::ViewsTestBase {
64  public:
65   AppListMainViewTest()
66       : widget_(NULL),
67         main_view_(NULL) {}
68
69   virtual ~AppListMainViewTest() {}
70
71   // testing::Test overrides:
72   virtual void SetUp() OVERRIDE {
73     views::ViewsTestBase::SetUp();
74     delegate_.reset(new AppListTestViewDelegate);
75
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);
82
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);
88
89     widget_->SetContentsView(main_view_);
90   }
91
92   virtual void TearDown() OVERRIDE {
93     widget_->Close();
94     views::ViewsTestBase::TearDown();
95     delegate_.reset();
96   }
97
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);
106       }
107     }
108
109     return NULL;
110   }
111
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));
124   }
125
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);
132
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;
138   }
139
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);
150   }
151
152   AppsGridView* RootGridView() {
153     return main_view_->contents_view()->apps_container_view()->apps_grid_view();
154   }
155
156   AppListFolderView* FolderView() {
157     return main_view_->contents_view()
158         ->apps_container_view()
159         ->app_list_folder_view();
160   }
161
162   AppsGridView* FolderGridView() { return FolderView()->items_grid_view(); }
163
164   const views::ViewModel* RootViewModel() {
165     return RootGridView()->view_model_for_test();
166   }
167
168   const views::ViewModel* FolderViewModel() {
169     return FolderGridView()->view_model_for_test();
170   }
171
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());
180
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);
185
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());
191
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;
197   }
198
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());
208
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();
217
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());
222
223     return dragged;
224   }
225
226  protected:
227   views::Widget* widget_;  // Owned by native window.
228   AppListMainView* main_view_;  // Owned by |widget_|.
229   scoped_ptr<AppListTestViewDelegate> delegate_;
230
231  private:
232   DISALLOW_COPY_AND_ASSIGN(AppListMainViewTest);
233 };
234
235 }  // namespace
236
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());
241
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());
245
246   const int kReplacementItems = 5;
247   delegate_->ReplaceTestModel(kReplacementItems);
248   main_view_->ModelChanged();
249   EXPECT_EQ(kReplacementItems, RootViewModel()->view_size());
250 }
251
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();
257
258   EXPECT_EQ(1, FolderViewModel()->view_size());
259
260   AppListItemView* dragged = StartDragForReparent(0);
261
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() * 2, 0);
265   gfx::Point point = drop_target_tile.CenterPoint();
266   SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point);
267
268   // Drop it.
269   FolderGridView()->EndDrag(false);
270
271   // Folder icon view should be gone and there is only one item view.
272   EXPECT_EQ(1, RootViewModel()->view_size());
273   EXPECT_EQ(AppListItemView::kViewClassName,
274             RootViewModel()->view_at(0)->GetClassName());
275
276   // The item view should be in slot 1 instead of slot 2 where it is dropped.
277   AppsGridViewTestApi root_grid_view_test_api(RootGridView());
278   root_grid_view_test_api.LayoutToIdealBounds();
279   EXPECT_EQ(first_slot_tile, RootViewModel()->view_at(0)->bounds());
280
281   // Single item folder should be auto removed.
282   EXPECT_EQ(NULL,
283             delegate_->GetTestModel()->FindFolderItem("single_item_folder"));
284 }
285
286 // Tests dragging an item out of a single item folder and dropping it onto the
287 // page switcher. Regression test for http://crbug.com/415530/.
288 TEST_F(AppListMainViewTest, DragReparentItemOntoPageSwitcher) {
289   AppListItemView* folder_item_view = CreateAndOpenSingleItemFolder();
290   const gfx::Rect first_slot_tile = folder_item_view->bounds();
291
292   delegate_->GetTestModel()->PopulateApps(20);
293
294   EXPECT_EQ(1, FolderViewModel()->view_size());
295   EXPECT_EQ(21, RootViewModel()->view_size());
296
297   AppListItemView* dragged = StartDragForReparent(0);
298
299   gfx::Rect main_view_bounds = main_view_->bounds();
300   // Drag the reparent item to the page switcher.
301   gfx::Point point =
302       gfx::Point(main_view_bounds.width() / 2,
303                  main_view_bounds.bottom() - first_slot_tile.height());
304   SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point);
305
306   // Drop it.
307   FolderGridView()->EndDrag(false);
308
309   // The folder should be destroyed.
310   EXPECT_EQ(21, RootViewModel()->view_size());
311   EXPECT_EQ(NULL,
312             delegate_->GetTestModel()->FindFolderItem("single_item_folder"));
313 }
314
315 // Test that an interrupted drag while reparenting an item from a folder, when
316 // canceled via the root grid, correctly forwards the cancelation to the drag
317 // ocurring from the folder.
318 TEST_F(AppListMainViewTest, MouseDragItemOutOfFolderWithCancel) {
319   CreateAndOpenSingleItemFolder();
320   AppListItemView* dragged = StartDragForReparent(0);
321
322   // Now add an item to the model, not in any folder, e.g., as if by Sync.
323   EXPECT_TRUE(RootGridView()->has_dragged_view());
324   EXPECT_TRUE(FolderGridView()->has_dragged_view());
325   delegate_->GetTestModel()->CreateAndAddItem("Extra");
326
327   // The drag operation should get canceled.
328   EXPECT_FALSE(RootGridView()->has_dragged_view());
329   EXPECT_FALSE(FolderGridView()->has_dragged_view());
330
331   // Additional mouse move operations should be ignored.
332   gfx::Point point(1, 1);
333   SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point);
334   EXPECT_FALSE(RootGridView()->has_dragged_view());
335   EXPECT_FALSE(FolderGridView()->has_dragged_view());
336 }
337
338 }  // namespace test
339 }  // namespace app_list