Views Omnibox: tolerate minor click-to-select-all dragging.
[chromium-blink-merge.git] / ui / app_list / views / apps_grid_view_unittest.cc
blobfc7c47010f1157ffa416e12c7d01d6da3a7f1607
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.
5 #include "ui/app_list/views/apps_grid_view.h"
7 #include <string>
9 #include "base/basictypes.h"
10 #include "base/compiler_specific.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/timer/timer.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "ui/app_list/app_list_item.h"
17 #include "ui/app_list/app_list_model.h"
18 #include "ui/app_list/pagination_model.h"
19 #include "ui/app_list/test/app_list_test_model.h"
20 #include "ui/app_list/views/app_list_item_view.h"
21 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
22 #include "ui/views/test/views_test_base.h"
24 namespace app_list {
25 namespace test {
27 namespace {
29 const int kIconDimension = 48;
30 const int kCols = 2;
31 const int kRows = 2;
32 const int kTilesPerPage = kCols * kRows;
34 const int kWidth = 320;
35 const int kHeight = 240;
37 class PageFlipWaiter : public PaginationModelObserver {
38 public:
39 PageFlipWaiter(base::MessageLoopForUI* ui_loop, PaginationModel* model)
40 : ui_loop_(ui_loop), model_(model), wait_(false), page_changed_(false) {
41 model_->AddObserver(this);
44 virtual ~PageFlipWaiter() {
45 model_->RemoveObserver(this);
48 bool Wait(int time_out_ms) {
49 DCHECK(!wait_);
50 wait_ = true;
51 page_changed_ = false;
53 if (time_out_ms) {
54 wait_timer_.Stop();
55 wait_timer_.Start(FROM_HERE,
56 base::TimeDelta::FromMilliseconds(time_out_ms),
57 this, &PageFlipWaiter::OnWaitTimeOut);
60 ui_loop_->Run();
61 wait_ = false;
62 return page_changed_;
65 private:
66 void OnWaitTimeOut() {
67 ui_loop_->Quit();
70 // PaginationModelObserver overrides:
71 virtual void TotalPagesChanged() OVERRIDE {
73 virtual void SelectedPageChanged(int old_selected,
74 int new_selected) OVERRIDE {
75 page_changed_ = true;
76 if (wait_)
77 ui_loop_->Quit();
79 virtual void TransitionStarted() OVERRIDE {
81 virtual void TransitionChanged() OVERRIDE {
84 base::MessageLoopForUI* ui_loop_;
85 PaginationModel* model_;
86 bool wait_;
87 bool page_changed_;
88 base::OneShotTimer<PageFlipWaiter> wait_timer_;
90 DISALLOW_COPY_AND_ASSIGN(PageFlipWaiter);
93 } // namespace
95 class AppsGridViewTest : public views::ViewsTestBase {
96 public:
97 AppsGridViewTest() {}
98 virtual ~AppsGridViewTest() {}
100 // testing::Test overrides:
101 virtual void SetUp() OVERRIDE {
102 views::ViewsTestBase::SetUp();
103 model_.reset(new AppListTestModel);
104 pagination_model_.reset(new PaginationModel);
106 apps_grid_view_.reset(
107 new AppsGridView(NULL, pagination_model_.get(), NULL));
108 apps_grid_view_->SetLayout(kIconDimension, kCols, kRows);
109 apps_grid_view_->SetBoundsRect(gfx::Rect(gfx::Size(kWidth, kHeight)));
110 apps_grid_view_->SetModel(model_.get());
111 apps_grid_view_->SetItemList(model_->item_list());
113 test_api_.reset(new AppsGridViewTestApi(apps_grid_view_.get()));
115 virtual void TearDown() OVERRIDE {
116 apps_grid_view_.reset(); // Release apps grid view before models.
117 views::ViewsTestBase::TearDown();
120 protected:
121 AppListItemView* GetItemViewAt(int index) {
122 return static_cast<AppListItemView*>(
123 test_api_->GetViewAtModelIndex(index));
126 AppListItemView* GetItemViewForPoint(const gfx::Point& point) {
127 for (size_t i = 0; i < model_->item_list()->item_count(); ++i) {
128 AppListItemView* view = GetItemViewAt(i);
129 if (view->bounds().Contains(point))
130 return view;
132 return NULL;
135 gfx::Rect GetItemTileRectAt(int row, int col) {
136 DCHECK_GT(model_->item_list()->item_count(), 0u);
138 gfx::Insets insets(apps_grid_view_->GetInsets());
139 gfx::Rect rect(gfx::Point(insets.left(), insets.top()),
140 GetItemViewAt(0)->bounds().size());
141 rect.Offset(col * rect.width(), row * rect.height());
142 return rect;
145 // Points are in |apps_grid_view_|'s coordinates.
146 void SimulateDrag(AppsGridView::Pointer pointer,
147 const gfx::Point& from,
148 const gfx::Point& to) {
149 AppListItemView* view = GetItemViewForPoint(from);
150 DCHECK(view);
152 gfx::Point translated_from = gfx::PointAtOffsetFromOrigin(
153 from - view->bounds().origin());
154 gfx::Point translated_to = gfx::PointAtOffsetFromOrigin(
155 to - view->bounds().origin());
157 ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED,
158 translated_from, from, 0, 0);
159 apps_grid_view_->InitiateDrag(view, pointer, pressed_event);
161 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED,
162 translated_to, to, 0, 0);
163 apps_grid_view_->UpdateDragFromItem(pointer, drag_event);
166 void SimulateKeyPress(ui::KeyboardCode key_code) {
167 ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, 0, false);
168 apps_grid_view_->OnKeyPressed(key_event);
171 scoped_ptr<AppListTestModel> model_;
172 scoped_ptr<PaginationModel> pagination_model_;
173 scoped_ptr<AppsGridView> apps_grid_view_;
174 scoped_ptr<AppsGridViewTestApi> test_api_;
176 private:
177 DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest);
180 TEST_F(AppsGridViewTest, CreatePage) {
181 // Fully populates a page.
182 const int kPages = 1;
183 model_->PopulateApps(kPages * kTilesPerPage);
184 EXPECT_EQ(kPages, pagination_model_->total_pages());
186 // Adds one more and gets a new page created.
187 model_->CreateAndAddItem("Extra");
188 EXPECT_EQ(kPages + 1, pagination_model_->total_pages());
191 TEST_F(AppsGridViewTest, EnsureHighlightedVisible) {
192 const int kPages = 3;
193 model_->PopulateApps(kPages * kTilesPerPage);
194 EXPECT_EQ(kPages, pagination_model_->total_pages());
195 EXPECT_EQ(0, pagination_model_->selected_page());
197 // Highlight first one and last one one first page and first page should be
198 // selected.
199 model_->HighlightItemAt(0);
200 EXPECT_EQ(0, pagination_model_->selected_page());
201 model_->HighlightItemAt(kTilesPerPage - 1);
202 EXPECT_EQ(0, pagination_model_->selected_page());
204 // Highlight first one on 2nd page and 2nd page should be selected.
205 model_->HighlightItemAt(kTilesPerPage + 1);
206 EXPECT_EQ(1, pagination_model_->selected_page());
208 // Highlight last one in the model and last page should be selected.
209 model_->HighlightItemAt(model_->item_list()->item_count() - 1);
210 EXPECT_EQ(kPages - 1, pagination_model_->selected_page());
213 TEST_F(AppsGridViewTest, RemoveSelectedLastApp) {
214 const int kTotalItems = 2;
215 const int kLastItemIndex = kTotalItems - 1;
217 model_->PopulateApps(kTotalItems);
219 AppListItemView* last_view = GetItemViewAt(kLastItemIndex);
220 apps_grid_view_->SetSelectedView(last_view);
221 model_->DeleteItem(model_->GetItemName(kLastItemIndex));
223 EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view));
225 // No crash happens.
226 AppListItemView* view = GetItemViewAt(0);
227 apps_grid_view_->SetSelectedView(view);
228 EXPECT_TRUE(apps_grid_view_->IsSelectedView(view));
231 TEST_F(AppsGridViewTest, MouseDrag) {
232 const int kTotalItems = 4;
233 model_->PopulateApps(kTotalItems);
234 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
235 model_->GetModelContent());
237 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
238 gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
240 // Dragging changes model order.
241 SimulateDrag(AppsGridView::MOUSE, from, to);
242 apps_grid_view_->EndDrag(false);
243 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
244 model_->GetModelContent());
245 test_api_->LayoutToIdealBounds();
247 // Canceling drag should keep existing order.
248 SimulateDrag(AppsGridView::MOUSE, from, to);
249 apps_grid_view_->EndDrag(true);
250 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
251 model_->GetModelContent());
252 test_api_->LayoutToIdealBounds();
254 // Deleting an item keeps remaining intact.
255 SimulateDrag(AppsGridView::MOUSE, from, to);
256 model_->DeleteItem(model_->GetItemName(0));
257 apps_grid_view_->EndDrag(false);
258 EXPECT_EQ(std::string("Item 1,Item 2,Item 3"),
259 model_->GetModelContent());
260 test_api_->LayoutToIdealBounds();
262 // Adding a launcher item cancels the drag and respects the order.
263 SimulateDrag(AppsGridView::MOUSE, from, to);
264 model_->CreateAndAddItem("Extra");
265 apps_grid_view_->EndDrag(false);
266 EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"),
267 model_->GetModelContent());
268 test_api_->LayoutToIdealBounds();
271 TEST_F(AppsGridViewTest, MouseDragFlipPage) {
272 test_api_->SetPageFlipDelay(10);
273 pagination_model_->SetTransitionDurations(10, 10);
275 PageFlipWaiter page_flip_waiter(message_loop(),
276 pagination_model_.get());
278 const int kPages = 3;
279 model_->PopulateApps(kPages * kTilesPerPage);
280 EXPECT_EQ(kPages, pagination_model_->total_pages());
281 EXPECT_EQ(0, pagination_model_->selected_page());
283 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
284 gfx::Point to = gfx::Point(apps_grid_view_->width(),
285 apps_grid_view_->height() / 2);
287 // Drag to right edge.
288 SimulateDrag(AppsGridView::MOUSE, from, to);
290 // Page should be flipped after sometime.
291 EXPECT_TRUE(page_flip_waiter.Wait(0));
292 EXPECT_EQ(1, pagination_model_->selected_page());
294 // Stay there and page should be flipped again.
295 EXPECT_TRUE(page_flip_waiter.Wait(0));
296 EXPECT_EQ(2, pagination_model_->selected_page());
298 // Stay there longer and no page flip happen since we are at the last page.
299 EXPECT_FALSE(page_flip_waiter.Wait(100));
300 EXPECT_EQ(2, pagination_model_->selected_page());
302 apps_grid_view_->EndDrag(true);
304 // Now drag to the left edge and test the other direction.
305 to.set_x(0);
307 SimulateDrag(AppsGridView::MOUSE, from, to);
309 EXPECT_TRUE(page_flip_waiter.Wait(0));
310 EXPECT_EQ(1, pagination_model_->selected_page());
312 EXPECT_TRUE(page_flip_waiter.Wait(0));
313 EXPECT_EQ(0, pagination_model_->selected_page());
315 EXPECT_FALSE(page_flip_waiter.Wait(100));
316 EXPECT_EQ(0, pagination_model_->selected_page());
317 apps_grid_view_->EndDrag(true);
320 TEST_F(AppsGridViewTest, SimultaneousDrag) {
321 const int kTotalItems = 4;
322 model_->PopulateApps(kTotalItems);
323 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
324 model_->GetModelContent());
326 gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
327 gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();
329 gfx::Point touch_from = GetItemTileRectAt(1, 0).CenterPoint();
330 gfx::Point touch_to = GetItemTileRectAt(1, 1).CenterPoint();
332 // Starts a mouse drag first then a touch drag.
333 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
334 SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
335 // Finishes the drag and mouse drag wins.
336 apps_grid_view_->EndDrag(false);
337 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
338 model_->GetModelContent());
339 test_api_->LayoutToIdealBounds();
341 // Starts a touch drag first then a mouse drag.
342 SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
343 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
344 // Finishes the drag and touch drag wins.
345 apps_grid_view_->EndDrag(false);
346 EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"),
347 model_->GetModelContent());
348 test_api_->LayoutToIdealBounds();
351 TEST_F(AppsGridViewTest, HighlightWithKeyboard) {
352 const int kPages = 3;
353 const int kItems = (kPages - 1) * kTilesPerPage + 1;
354 model_->PopulateApps(kItems);
356 const int first_index = 0;
357 const int last_index = kItems - 1;
358 const int last_index_on_page1_first_row = kRows - 1;
359 const int last_index_on_page1 = kTilesPerPage - 1;
360 const int first_index_on_page2 = kTilesPerPage;
361 const int first_index_on_page2_last_row = 2 * kTilesPerPage - kRows;
362 const int last_index_on_page2_last_row = 2 * kTilesPerPage - 1;
364 // Try moving off the item beyond the first one.
365 apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
366 SimulateKeyPress(ui::VKEY_UP);
367 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
368 SimulateKeyPress(ui::VKEY_LEFT);
369 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
371 // Move to the last item and try to go past it.
372 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index));
373 SimulateKeyPress(ui::VKEY_DOWN);
374 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
375 SimulateKeyPress(ui::VKEY_RIGHT);
376 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
378 // Move right on last item on page 1 should get to first item on page 2's last
379 // row and vice versa.
380 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
381 SimulateKeyPress(ui::VKEY_RIGHT);
382 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
383 first_index_on_page2_last_row)));
384 SimulateKeyPress(ui::VKEY_LEFT);
385 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
386 last_index_on_page1)));
388 // Up/down on page boundary does nothing.
389 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
390 SimulateKeyPress(ui::VKEY_DOWN);
391 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
392 last_index_on_page1)));
393 apps_grid_view_->SetSelectedView(
394 GetItemViewAt(first_index_on_page2_last_row));
395 apps_grid_view_->
396 SetSelectedView(GetItemViewAt(last_index_on_page1_first_row));
397 SimulateKeyPress(ui::VKEY_UP);
398 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
399 last_index_on_page1_first_row)));
401 // Page up and down should go to the same item on the next and last page.
402 apps_grid_view_->SetSelectedView(GetItemViewAt(first_index_on_page2));
403 SimulateKeyPress(ui::VKEY_PRIOR);
404 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
405 first_index)));
406 SimulateKeyPress(ui::VKEY_NEXT);
407 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
408 first_index_on_page2)));
410 // Moving onto a a page with too few apps to support the expected index snaps
411 // to the last available index.
412 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
413 SimulateKeyPress(ui::VKEY_RIGHT);
414 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
415 last_index)));
416 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
417 SimulateKeyPress(ui::VKEY_NEXT);
418 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
419 last_index)));
423 // After page switch, arrow keys select first item on current page.
424 apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
425 pagination_model_->SelectPage(1, false);
426 SimulateKeyPress(ui::VKEY_UP);
427 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
428 first_index_on_page2)));
431 TEST_F(AppsGridViewTest, ItemLabelShortNameOverride) {
432 // If the app's full name and short name differ, the title label's tooltip
433 // should always be the full name of the app.
434 std::string expected_text("xyz");
435 std::string expected_tooltip("tooltip");
436 model_->CreateAndAddItem(expected_text, expected_tooltip);
438 base::string16 actual_tooltip;
439 AppListItemView* item_view = GetItemViewAt(0);
440 ASSERT_TRUE(item_view);
441 const views::Label* title_label = item_view->title();
442 EXPECT_TRUE(title_label->GetTooltipText(
443 title_label->bounds().CenterPoint(), &actual_tooltip));
444 EXPECT_EQ(expected_tooltip, base::UTF16ToUTF8(actual_tooltip));
445 EXPECT_EQ(expected_text, base::UTF16ToUTF8(title_label->text()));
448 TEST_F(AppsGridViewTest, ItemLabelNoShortName) {
449 // If the app's full name and short name are the same, use the default tooltip
450 // behavior of the label (only show a tooltip if the title is truncated).
451 std::string title("a");
452 model_->CreateAndAddItem(title, title);
454 base::string16 actual_tooltip;
455 AppListItemView* item_view = GetItemViewAt(0);
456 ASSERT_TRUE(item_view);
457 const views::Label* title_label = item_view->title();
458 EXPECT_FALSE(title_label->GetTooltipText(
459 title_label->bounds().CenterPoint(), &actual_tooltip));
460 EXPECT_EQ(title, base::UTF16ToUTF8(title_label->text()));
463 } // namespace test
464 } // namespace app_list