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