OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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 "chrome/browser/automation/ui_controls.h" |
| 6 #include "chrome/test/interactive_ui/view_event_test_base.h" |
| 7 #include "views/controls/button/menu_button.h" |
| 8 #include "views/controls/menu/menu_controller.h" |
| 9 #include "views/controls/menu/menu_item_view.h" |
| 10 #include "views/controls/menu/submenu_view.h" |
| 11 #include "views/controls/menu/view_menu_delegate.h" |
| 12 #include "views/widget/root_view.h" |
| 13 #include "views/window/window.h" |
| 14 |
| 15 // This is a convenience base class for all tests to provide some |
| 16 // common functionality. It sets up a MenuButton and a MenuItemView |
| 17 // and clicks the MenuButton. |
| 18 // |
| 19 // Subclasses should implement: |
| 20 // BuildMenu() populate the menu |
| 21 // DoTestOnMessageLoop() initiate the test |
| 22 // |
| 23 // Subclasses can call: |
| 24 // Click() to post a mouse click on a View |
| 25 // |
| 26 // Although it should be possible to post a menu multiple times, |
| 27 // MenuItemView prevents repeated activation of a menu by clicks too |
| 28 // close in time. |
| 29 class MenuItemViewTestBase : public ViewEventTestBase, |
| 30 public views::ViewMenuDelegate, |
| 31 public views::MenuDelegate { |
| 32 public: |
| 33 MenuItemViewTestBase() : |
| 34 ViewEventTestBase(), |
| 35 button_(NULL), |
| 36 menu_(NULL) { |
| 37 } |
| 38 |
| 39 virtual ~MenuItemViewTestBase() { |
| 40 } |
| 41 |
| 42 // ViewEventTestBase implementation. |
| 43 |
| 44 virtual void SetUp() OVERRIDE { |
| 45 button_ = new views::MenuButton(NULL, L"Menu Test", this, true); |
| 46 menu_.reset(new views::MenuItemView(this)); |
| 47 BuildMenu(menu_.get()); |
| 48 |
| 49 ViewEventTestBase::SetUp(); |
| 50 } |
| 51 |
| 52 virtual void TearDown() OVERRIDE { |
| 53 menu_.reset(NULL); |
| 54 ViewEventTestBase::TearDown(); |
| 55 } |
| 56 |
| 57 virtual views::View* CreateContentsView() OVERRIDE { |
| 58 return button_; |
| 59 } |
| 60 |
| 61 virtual gfx::Size GetPreferredSize() OVERRIDE { |
| 62 return button_->GetPreferredSize(); |
| 63 } |
| 64 |
| 65 // views::ViewMenuDelegate implementation. |
| 66 virtual void RunMenu(views::View* source, const gfx::Point& pt) OVERRIDE { |
| 67 gfx::Point screen_location; |
| 68 views::View::ConvertPointToScreen(source, &screen_location); |
| 69 gfx::Rect bounds(screen_location, source->size()); |
| 70 menu_->RunMenuAt( |
| 71 source->GetWindow()->GetNativeWindow(), |
| 72 button_, |
| 73 bounds, |
| 74 views::MenuItemView::TOPLEFT, |
| 75 true); |
| 76 } |
| 77 |
| 78 protected: |
| 79 // Generate a mouse click on the specified view and post a new task. |
| 80 virtual void Click(views::View* view, Task* next) { |
| 81 ui_controls::MoveMouseToCenterAndPress( |
| 82 view, |
| 83 ui_controls::LEFT, |
| 84 ui_controls::DOWN | ui_controls::UP, |
| 85 next); |
| 86 } |
| 87 |
| 88 virtual void BuildMenu(views::MenuItemView* menu) { |
| 89 } |
| 90 |
| 91 #if defined(TOOLKIT_USES_GTK) |
| 92 // GTK processes widget size changes with an idle task so RootView |
| 93 // sizes are not updated reliably before an automation mouse click. |
| 94 // This can result in the event being marked outside the menu |
| 95 // bounds. To work around this issue for testing, this code short |
| 96 // circuits the RootView size update by setting it directly. |
| 97 void WorkaroundGTKRace() { |
| 98 views::Widget* widget = menu_->GetSubmenu()->GetWidget(); |
| 99 if (widget) { |
| 100 widget->GetRootView()->SetSize( |
| 101 widget->GetClientAreaScreenBounds().size()); |
| 102 } |
| 103 } |
| 104 #endif |
| 105 |
| 106 views::MenuButton* button_; |
| 107 scoped_ptr<views::MenuItemView> menu_; |
| 108 }; |
| 109 |
| 110 // Simple test for clicking a menu item. This template class clicks on an |
| 111 // item and checks that the returned id matches. The index of the item |
| 112 // is the template argument. |
| 113 template<int INDEX> |
| 114 class MenuItemViewTestBasic : public MenuItemViewTestBase { |
| 115 public: |
| 116 MenuItemViewTestBasic() : |
| 117 last_command_(0) { |
| 118 } |
| 119 |
| 120 virtual ~MenuItemViewTestBasic() { |
| 121 } |
| 122 |
| 123 // views::MenuDelegate implementation |
| 124 virtual void ExecuteCommand(int id) OVERRIDE { |
| 125 last_command_ = id; |
| 126 } |
| 127 |
| 128 // MenuItemViewTestBase implementation |
| 129 virtual void BuildMenu(views::MenuItemView* menu) OVERRIDE { |
| 130 menu->AppendMenuItemWithLabel(1, L"item 1"); |
| 131 menu->AppendMenuItemWithLabel(2, L"item 2"); |
| 132 menu->AppendSeparator(); |
| 133 menu->AppendMenuItemWithLabel(3, L"item 3"); |
| 134 } |
| 135 |
| 136 // ViewEventTestBase implementation |
| 137 virtual void DoTestOnMessageLoop() OVERRIDE { |
| 138 Click(button_, CreateEventTask(this, &MenuItemViewTestBasic::Step1)); |
| 139 } |
| 140 |
| 141 // Click on item INDEX. |
| 142 void Step1() { |
| 143 ASSERT_TRUE(menu_.get()); |
| 144 |
| 145 views::SubmenuView* submenu = menu_->GetSubmenu(); |
| 146 ASSERT_TRUE(submenu); |
| 147 ASSERT_TRUE(submenu->IsShowing()); |
| 148 ASSERT_EQ(3, submenu->GetMenuItemCount()); |
| 149 |
| 150 // click an item and pass control to the next step |
| 151 views::MenuItemView* item = submenu->GetMenuItemAt(INDEX); |
| 152 ASSERT_TRUE(item); |
| 153 Click(item, CreateEventTask(this, &MenuItemViewTestBasic::Step2)); |
| 154 } |
| 155 |
| 156 // Check the clicked item and complete the test. |
| 157 void Step2() { |
| 158 ASSERT_FALSE(menu_->GetSubmenu()->IsShowing()); |
| 159 ASSERT_EQ(INDEX + 1,last_command_); |
| 160 Done(); |
| 161 } |
| 162 |
| 163 private: |
| 164 int last_command_; |
| 165 }; |
| 166 |
| 167 // Click each item of a 3-item menu (with separator). |
| 168 typedef MenuItemViewTestBasic<0> MenuItemViewTestBasic0; |
| 169 typedef MenuItemViewTestBasic<1> MenuItemViewTestBasic1; |
| 170 typedef MenuItemViewTestBasic<2> MenuItemViewTestBasic2; |
| 171 VIEW_TEST(MenuItemViewTestBasic0, SelectItem0) |
| 172 VIEW_TEST(MenuItemViewTestBasic1, SelectItem1) |
| 173 VIEW_TEST(MenuItemViewTestBasic2, SelectItem2) |
| 174 |
| 175 // Test class for inserting a menu item while the menu is open. |
| 176 template<int INSERT_INDEX, int SELECT_INDEX> |
| 177 class MenuItemViewTestInsert : public MenuItemViewTestBase { |
| 178 public: |
| 179 MenuItemViewTestInsert() : |
| 180 last_command_(0), |
| 181 inserted_item_(NULL) { |
| 182 } |
| 183 |
| 184 virtual ~MenuItemViewTestInsert() { |
| 185 } |
| 186 |
| 187 // views::MenuDelegate implementation |
| 188 virtual void ExecuteCommand(int id) OVERRIDE { |
| 189 last_command_ = id; |
| 190 } |
| 191 |
| 192 // MenuItemViewTestBase implementation |
| 193 virtual void BuildMenu(views::MenuItemView* menu) OVERRIDE { |
| 194 menu->AppendMenuItemWithLabel(1, L"item 1"); |
| 195 menu->AppendMenuItemWithLabel(2, L"item 2"); |
| 196 } |
| 197 |
| 198 // ViewEventTestBase implementation |
| 199 virtual void DoTestOnMessageLoop() OVERRIDE { |
| 200 Click(button_, CreateEventTask(this, &MenuItemViewTestInsert::Step1)); |
| 201 } |
| 202 |
| 203 // Insert item at INSERT_INDEX and click item at SELECT_INDEX. |
| 204 void Step1() { |
| 205 ASSERT_TRUE(menu_.get()); |
| 206 |
| 207 views::SubmenuView* submenu = menu_->GetSubmenu(); |
| 208 ASSERT_TRUE(submenu); |
| 209 ASSERT_TRUE(submenu->IsShowing()); |
| 210 ASSERT_EQ(2, submenu->GetMenuItemCount()); |
| 211 |
| 212 inserted_item_ = menu_->AddMenuItemAt(INSERT_INDEX, 1000, |
| 213 L"inserted item", SkBitmap(), |
| 214 views::MenuItemView::NORMAL); |
| 215 ASSERT_TRUE(inserted_item_); |
| 216 menu_->ChildrenChanged(); |
| 217 #if defined(TOOLKIT_USES_GTK) |
| 218 WorkaroundGTKRace(); |
| 219 #endif |
| 220 |
| 221 // click an item and pass control to the next step |
| 222 views::MenuItemView* item = submenu->GetMenuItemAt(SELECT_INDEX); |
| 223 ASSERT_TRUE(item); |
| 224 Click(item, CreateEventTask(this, &MenuItemViewTestInsert::Step2)); |
| 225 } |
| 226 |
| 227 // Check clicked item and complete test. |
| 228 void Step2() { |
| 229 ASSERT_TRUE(menu_.get()); |
| 230 |
| 231 views::SubmenuView* submenu = menu_->GetSubmenu(); |
| 232 ASSERT_TRUE(submenu); |
| 233 ASSERT_FALSE(submenu->IsShowing()); |
| 234 ASSERT_EQ(3, submenu->GetMenuItemCount()); |
| 235 |
| 236 if (SELECT_INDEX == INSERT_INDEX) |
| 237 ASSERT_EQ(1000, last_command_); |
| 238 else if (SELECT_INDEX < INSERT_INDEX) |
| 239 ASSERT_EQ(SELECT_INDEX + 1, last_command_); |
| 240 else |
| 241 ASSERT_EQ(SELECT_INDEX, last_command_); |
| 242 |
| 243 Done(); |
| 244 } |
| 245 |
| 246 private: |
| 247 int last_command_; |
| 248 views::MenuItemView* inserted_item_; |
| 249 }; |
| 250 |
| 251 // MenuItemViewTestInsertXY inserts an item at index X and selects the |
| 252 // item at index Y (after the insertion). The tests here cover |
| 253 // inserting at the beginning, middle, and end, crossbarred with |
| 254 // selecting the first and last item. |
| 255 typedef MenuItemViewTestInsert<0,0> MenuItemViewTestInsert00; |
| 256 typedef MenuItemViewTestInsert<0,2> MenuItemViewTestInsert02; |
| 257 typedef MenuItemViewTestInsert<1,0> MenuItemViewTestInsert10; |
| 258 typedef MenuItemViewTestInsert<1,2> MenuItemViewTestInsert12; |
| 259 typedef MenuItemViewTestInsert<2,0> MenuItemViewTestInsert20; |
| 260 typedef MenuItemViewTestInsert<2,2> MenuItemViewTestInsert22; |
| 261 VIEW_TEST(MenuItemViewTestInsert00, InsertItem00) |
| 262 VIEW_TEST(MenuItemViewTestInsert02, InsertItem02) |
| 263 VIEW_TEST(MenuItemViewTestInsert10, InsertItem10) |
| 264 VIEW_TEST(MenuItemViewTestInsert12, InsertItem12) |
| 265 VIEW_TEST(MenuItemViewTestInsert20, InsertItem20) |
| 266 VIEW_TEST(MenuItemViewTestInsert22, InsertItem22) |
| 267 |
| 268 // Test class for inserting a menu item while a submenu is open. |
| 269 template<int INSERT_INDEX> |
| 270 class MenuItemViewTestInsertWithSubmenu : public MenuItemViewTestBase { |
| 271 public: |
| 272 MenuItemViewTestInsertWithSubmenu() : |
| 273 last_command_(0), |
| 274 submenu_(NULL), |
| 275 inserted_item_(NULL) { |
| 276 } |
| 277 |
| 278 virtual ~MenuItemViewTestInsertWithSubmenu() { |
| 279 } |
| 280 |
| 281 // views::MenuDelegate implementation |
| 282 virtual void ExecuteCommand(int id) OVERRIDE { |
| 283 last_command_ = id; |
| 284 } |
| 285 |
| 286 // MenuItemViewTestBase implementation |
| 287 virtual void BuildMenu(views::MenuItemView* menu) OVERRIDE { |
| 288 submenu_ = menu->AppendSubMenu(1, L"My Submenu"); |
| 289 submenu_->AppendMenuItemWithLabel(101, L"submenu item 1"); |
| 290 submenu_->AppendMenuItemWithLabel(101, L"submenu item 2"); |
| 291 menu->AppendMenuItemWithLabel(2, L"item 2"); |
| 292 } |
| 293 |
| 294 // ViewEventTestBase implementation |
| 295 virtual void DoTestOnMessageLoop() OVERRIDE { |
| 296 Click(button_, |
| 297 CreateEventTask(this, &MenuItemViewTestInsertWithSubmenu::Step1)); |
| 298 } |
| 299 |
| 300 // Post submenu. |
| 301 void Step1() { |
| 302 Click(submenu_, |
| 303 CreateEventTask(this, &MenuItemViewTestInsertWithSubmenu::Step2)); |
| 304 } |
| 305 |
| 306 // Insert item at INSERT_INDEX. |
| 307 void Step2() { |
| 308 inserted_item_ = menu_->AddMenuItemAt(INSERT_INDEX, 1000, |
| 309 L"inserted item", SkBitmap(), |
| 310 views::MenuItemView::NORMAL); |
| 311 ASSERT_TRUE(inserted_item_); |
| 312 menu_->ChildrenChanged(); |
| 313 #if defined(TOOLKIT_USES_GTK) |
| 314 WorkaroundGTKRace(); |
| 315 #endif |
| 316 |
| 317 Click(inserted_item_, |
| 318 CreateEventTask(this, &MenuItemViewTestInsertWithSubmenu::Step3)); |
| 319 } |
| 320 |
| 321 void Step3() { |
| 322 Done(); |
| 323 } |
| 324 |
| 325 private: |
| 326 int last_command_; |
| 327 views::MenuItemView* submenu_; |
| 328 views::MenuItemView* inserted_item_; |
| 329 }; |
| 330 |
| 331 // MenuItemViewTestInsertWithSubmenuX posts a menu and its submenu, |
| 332 // then inserts an item in the top-level menu at X. |
| 333 typedef MenuItemViewTestInsertWithSubmenu<0> MenuItemViewTestInsertWithSubmenu0; |
| 334 typedef MenuItemViewTestInsertWithSubmenu<1> MenuItemViewTestInsertWithSubmenu1; |
| 335 VIEW_TEST(MenuItemViewTestInsertWithSubmenu0, InsertItemWithSubmenu0) |
| 336 VIEW_TEST(MenuItemViewTestInsertWithSubmenu1, InsertItemWithSubmenu1) |
| 337 |
| 338 // Test class for removing a menu item while the menu is open. |
| 339 template<int REMOVE_INDEX, int SELECT_INDEX> |
| 340 class MenuItemViewTestRemove : public MenuItemViewTestBase { |
| 341 public: |
| 342 MenuItemViewTestRemove() : |
| 343 last_command_(0) { |
| 344 } |
| 345 |
| 346 virtual ~MenuItemViewTestRemove() { |
| 347 } |
| 348 |
| 349 // views::MenuDelegate implementation |
| 350 virtual void ExecuteCommand(int id) OVERRIDE { |
| 351 last_command_ = id; |
| 352 } |
| 353 |
| 354 // MenuItemViewTestBase implementation |
| 355 virtual void BuildMenu(views::MenuItemView* menu) OVERRIDE { |
| 356 menu->AppendMenuItemWithLabel(1, L"item 1"); |
| 357 menu->AppendMenuItemWithLabel(2, L"item 2"); |
| 358 menu->AppendMenuItemWithLabel(3, L"item 3"); |
| 359 } |
| 360 |
| 361 // ViewEventTestBase implementation |
| 362 virtual void DoTestOnMessageLoop() OVERRIDE { |
| 363 Click(button_, CreateEventTask(this, &MenuItemViewTestRemove::Step1)); |
| 364 } |
| 365 |
| 366 // Remove item at REMOVE_INDEX and click item at SELECT_INDEX. |
| 367 void Step1() { |
| 368 ASSERT_TRUE(menu_.get()); |
| 369 |
| 370 views::SubmenuView* submenu = menu_->GetSubmenu(); |
| 371 ASSERT_TRUE(submenu); |
| 372 ASSERT_TRUE(submenu->IsShowing()); |
| 373 ASSERT_EQ(3, submenu->GetMenuItemCount()); |
| 374 |
| 375 // remove |
| 376 menu_->RemoveMenuItemAt(REMOVE_INDEX); |
| 377 menu_->ChildrenChanged(); |
| 378 #if defined(TOOLKIT_USES_GTK) |
| 379 WorkaroundGTKRace(); |
| 380 #endif |
| 381 |
| 382 // click |
| 383 views::MenuItemView* item = submenu->GetMenuItemAt(SELECT_INDEX); |
| 384 ASSERT_TRUE(item); |
| 385 Click(item, CreateEventTask(this, &MenuItemViewTestRemove::Step2)); |
| 386 } |
| 387 |
| 388 // Check clicked item and complete test. |
| 389 void Step2() { |
| 390 ASSERT_TRUE(menu_.get()); |
| 391 |
| 392 views::SubmenuView* submenu = menu_->GetSubmenu(); |
| 393 ASSERT_TRUE(submenu); |
| 394 ASSERT_FALSE(submenu->IsShowing()); |
| 395 ASSERT_EQ(2, submenu->GetMenuItemCount()); |
| 396 |
| 397 if (SELECT_INDEX < REMOVE_INDEX) |
| 398 ASSERT_EQ(SELECT_INDEX + 1, last_command_); |
| 399 else |
| 400 ASSERT_EQ(SELECT_INDEX + 2, last_command_); |
| 401 |
| 402 Done(); |
| 403 } |
| 404 |
| 405 private: |
| 406 int last_command_; |
| 407 }; |
| 408 |
| 409 typedef MenuItemViewTestRemove<0,0> MenuItemViewTestRemove00; |
| 410 typedef MenuItemViewTestRemove<0,1> MenuItemViewTestRemove01; |
| 411 typedef MenuItemViewTestRemove<1,0> MenuItemViewTestRemove10; |
| 412 typedef MenuItemViewTestRemove<1,1> MenuItemViewTestRemove11; |
| 413 typedef MenuItemViewTestRemove<2,0> MenuItemViewTestRemove20; |
| 414 typedef MenuItemViewTestRemove<2,1> MenuItemViewTestRemove21; |
| 415 VIEW_TEST(MenuItemViewTestRemove00, RemoveItem00) |
| 416 VIEW_TEST(MenuItemViewTestRemove01, RemoveItem01) |
| 417 VIEW_TEST(MenuItemViewTestRemove10, RemoveItem10) |
| 418 VIEW_TEST(MenuItemViewTestRemove11, RemoveItem11) |
| 419 VIEW_TEST(MenuItemViewTestRemove20, RemoveItem20) |
| 420 VIEW_TEST(MenuItemViewTestRemove21, RemoveItem21) |
| 421 |
| 422 // Test class for removing a menu item while a submenu is open. |
| 423 template<int REMOVE_INDEX> |
| 424 class MenuItemViewTestRemoveWithSubmenu : public MenuItemViewTestBase { |
| 425 public: |
| 426 MenuItemViewTestRemoveWithSubmenu() : |
| 427 last_command_(0), |
| 428 submenu_(NULL) { |
| 429 } |
| 430 |
| 431 virtual ~MenuItemViewTestRemoveWithSubmenu() { |
| 432 } |
| 433 |
| 434 // views::MenuDelegate implementation |
| 435 virtual void ExecuteCommand(int id) OVERRIDE { |
| 436 last_command_ = id; |
| 437 } |
| 438 |
| 439 // MenuItemViewTestBase implementation |
| 440 virtual void BuildMenu(views::MenuItemView* menu) OVERRIDE { |
| 441 menu->AppendMenuItemWithLabel(1, L"item 1"); |
| 442 submenu_ = menu->AppendSubMenu(2, L"My Submenu"); |
| 443 submenu_->AppendMenuItemWithLabel(101, L"submenu item 1"); |
| 444 submenu_->AppendMenuItemWithLabel(102, L"submenu item 2"); |
| 445 } |
| 446 |
| 447 // ViewEventTestBase implementation |
| 448 virtual void DoTestOnMessageLoop() OVERRIDE { |
| 449 Click(button_, |
| 450 CreateEventTask(this, &MenuItemViewTestRemoveWithSubmenu::Step1)); |
| 451 } |
| 452 |
| 453 // Post submenu. |
| 454 void Step1() { |
| 455 ASSERT_TRUE(menu_.get()); |
| 456 |
| 457 views::SubmenuView* submenu = menu_->GetSubmenu(); |
| 458 ASSERT_TRUE(submenu); |
| 459 ASSERT_TRUE(submenu->IsShowing()); |
| 460 |
| 461 Click(submenu_, |
| 462 CreateEventTask(this, &MenuItemViewTestRemoveWithSubmenu::Step2)); |
| 463 } |
| 464 |
| 465 // Remove item at REMOVE_INDEX and select it to exit the menu loop. |
| 466 void Step2() { |
| 467 ASSERT_TRUE(menu_.get()); |
| 468 |
| 469 views::SubmenuView* submenu = menu_->GetSubmenu(); |
| 470 ASSERT_TRUE(submenu); |
| 471 ASSERT_TRUE(submenu->IsShowing()); |
| 472 ASSERT_EQ(2, submenu->GetMenuItemCount()); |
| 473 |
| 474 // remove |
| 475 menu_->RemoveMenuItemAt(REMOVE_INDEX); |
| 476 menu_->ChildrenChanged(); |
| 477 #if defined(TOOLKIT_USES_GTK) |
| 478 WorkaroundGTKRace(); |
| 479 #endif |
| 480 |
| 481 // click |
| 482 Click(button_, |
| 483 CreateEventTask(this, &MenuItemViewTestRemoveWithSubmenu::Step3)); |
| 484 } |
| 485 |
| 486 void Step3() { |
| 487 ASSERT_TRUE(menu_.get()); |
| 488 |
| 489 views::SubmenuView* submenu = menu_->GetSubmenu(); |
| 490 ASSERT_TRUE(submenu); |
| 491 ASSERT_FALSE(submenu->IsShowing()); |
| 492 ASSERT_EQ(1, submenu->GetMenuItemCount()); |
| 493 |
| 494 Done(); |
| 495 } |
| 496 |
| 497 private: |
| 498 int last_command_; |
| 499 views::MenuItemView* submenu_; |
| 500 }; |
| 501 |
| 502 typedef MenuItemViewTestRemoveWithSubmenu<0> MenuItemViewTestRemoveWithSubmenu0; |
| 503 typedef MenuItemViewTestRemoveWithSubmenu<1> MenuItemViewTestRemoveWithSubmenu1; |
| 504 VIEW_TEST(MenuItemViewTestRemoveWithSubmenu0, RemoveItemWithSubmenu0) |
| 505 VIEW_TEST(MenuItemViewTestRemoveWithSubmenu1, RemoveItemWithSubmenu1) |
OLD | NEW |