// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/callback.h" #include "base/utf_string_conversions.h" #include "chrome/test/base/ui_test_utils.h" #include "chrome/test/base/view_event_test_base.h" #include "ui/ui_controls/ui_controls.h" #include "ui/views/controls/button/menu_button.h" #include "ui/views/controls/button/menu_button_listener.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/widget.h" // This is a convenience base class for all tests to provide some // common functionality. It sets up a MenuButton and a MenuItemView // and clicks the MenuButton. // // Subclasses should implement: // BuildMenu() populate the menu // DoTestOnMessageLoop() initiate the test // // Subclasses can call: // Click() to post a mouse click on a View // // Although it should be possible to post a menu multiple times, // MenuItemView prevents repeated activation of a menu by clicks too // close in time. class MenuItemViewTestBase : public ViewEventTestBase, public views::MenuButtonListener, public views::MenuDelegate { public: MenuItemViewTestBase() : ViewEventTestBase(), button_(NULL), menu_(NULL) { } virtual ~MenuItemViewTestBase() { } // ViewEventTestBase implementation. virtual void SetUp() OVERRIDE { button_ = new views::MenuButton( NULL, ASCIIToUTF16("Menu Test"), this, true); menu_ = new views::MenuItemView(this); BuildMenu(menu_); menu_runner_.reset(new views::MenuRunner(menu_)); ViewEventTestBase::SetUp(); } virtual void TearDown() OVERRIDE { menu_runner_.reset(NULL); menu_ = NULL; ViewEventTestBase::TearDown(); } virtual views::View* CreateContentsView() OVERRIDE { return button_; } virtual gfx::Size GetPreferredSize() OVERRIDE { return button_->GetPreferredSize(); } // views::MenuButtonListener implementation. virtual void OnMenuButtonClicked(views::View* source, const gfx::Point& point) OVERRIDE { gfx::Point screen_location; views::View::ConvertPointToScreen(source, &screen_location); gfx::Rect bounds(screen_location, source->size()); ignore_result(menu_runner_->RunMenuAt( source->GetWidget(), button_, bounds, views::MenuItemView::TOPLEFT, views::MenuRunner::HAS_MNEMONICS)); } protected: // Generate a mouse click on the specified view and post a new task. virtual void Click(views::View* view, const base::Closure& next) { ui_test_utils::MoveMouseToCenterAndPress( view, ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP, next); } virtual void BuildMenu(views::MenuItemView* menu) { } views::MenuButton* button_; views::MenuItemView* menu_; scoped_ptr menu_runner_; }; // Simple test for clicking a menu item. This template class clicks on an // item and checks that the returned id matches. The index of the item // is the template argument. template class MenuItemViewTestBasic : public MenuItemViewTestBase { public: MenuItemViewTestBasic() : last_command_(0) { } virtual ~MenuItemViewTestBasic() { } // views::MenuDelegate implementation virtual void ExecuteCommand(int id) OVERRIDE { last_command_ = id; } // MenuItemViewTestBase implementation virtual void BuildMenu(views::MenuItemView* menu) OVERRIDE { menu->AppendMenuItemWithLabel(1, ASCIIToUTF16("item 1")); menu->AppendMenuItemWithLabel(2, ASCIIToUTF16("item 2")); menu->AppendSeparator(); menu->AppendMenuItemWithLabel(3, ASCIIToUTF16("item 3")); } // ViewEventTestBase implementation virtual void DoTestOnMessageLoop() OVERRIDE { Click(button_, CreateEventTask(this, &MenuItemViewTestBasic::Step1)); } // Click on item INDEX. void Step1() { ASSERT_TRUE(menu_); views::SubmenuView* submenu = menu_->GetSubmenu(); ASSERT_TRUE(submenu); ASSERT_TRUE(submenu->IsShowing()); ASSERT_EQ(3, submenu->GetMenuItemCount()); // click an item and pass control to the next step views::MenuItemView* item = submenu->GetMenuItemAt(INDEX); ASSERT_TRUE(item); Click(item, CreateEventTask(this, &MenuItemViewTestBasic::Step2)); } // Check the clicked item and complete the test. void Step2() { ASSERT_FALSE(menu_->GetSubmenu()->IsShowing()); ASSERT_EQ(INDEX + 1,last_command_); Done(); } private: int last_command_; }; // Click each item of a 3-item menu (with separator). typedef MenuItemViewTestBasic<0> MenuItemViewTestBasic0; typedef MenuItemViewTestBasic<1> MenuItemViewTestBasic1; typedef MenuItemViewTestBasic<2> MenuItemViewTestBasic2; #if defined(OS_WIN) #define MAYBE_SelectItem0 DISABLED_SelectItem0 #define MAYBE_SelectItem1 DISABLED_SelectItem1 #define MAYBE_SelectItem2 DISABLED_SelectItem2 #else #define MAYBE_SelectItem0 SelectItem0 #define MAYBE_SelectItem1 SelectItem1 #define MAYBE_SelectItem2 SelectItem2 #endif VIEW_TEST(MenuItemViewTestBasic0, MAYBE_SelectItem0) VIEW_TEST(MenuItemViewTestBasic1, MAYBE_SelectItem1) VIEW_TEST(MenuItemViewTestBasic2, MAYBE_SelectItem2) // Test class for inserting a menu item while the menu is open. template class MenuItemViewTestInsert : public MenuItemViewTestBase { public: MenuItemViewTestInsert() : last_command_(0), inserted_item_(NULL) { } virtual ~MenuItemViewTestInsert() { } // views::MenuDelegate implementation virtual void ExecuteCommand(int id) OVERRIDE { last_command_ = id; } // MenuItemViewTestBase implementation virtual void BuildMenu(views::MenuItemView* menu) OVERRIDE { menu->AppendMenuItemWithLabel(1, ASCIIToUTF16("item 1")); menu->AppendMenuItemWithLabel(2, ASCIIToUTF16("item 2")); } // ViewEventTestBase implementation virtual void DoTestOnMessageLoop() OVERRIDE { Click(button_, CreateEventTask(this, &MenuItemViewTestInsert::Step1)); } // Insert item at INSERT_INDEX and click item at SELECT_INDEX. void Step1() { ASSERT_TRUE(menu_); views::SubmenuView* submenu = menu_->GetSubmenu(); ASSERT_TRUE(submenu); ASSERT_TRUE(submenu->IsShowing()); ASSERT_EQ(2, submenu->GetMenuItemCount()); inserted_item_ = menu_->AddMenuItemAt(INSERT_INDEX, 1000, ASCIIToUTF16("inserted item"), gfx::ImageSkia(), views::MenuItemView::NORMAL, ui::NORMAL_SEPARATOR); ASSERT_TRUE(inserted_item_); menu_->ChildrenChanged(); // click an item and pass control to the next step views::MenuItemView* item = submenu->GetMenuItemAt(SELECT_INDEX); ASSERT_TRUE(item); Click(item, CreateEventTask(this, &MenuItemViewTestInsert::Step2)); } // Check clicked item and complete test. void Step2() { ASSERT_TRUE(menu_); views::SubmenuView* submenu = menu_->GetSubmenu(); ASSERT_TRUE(submenu); ASSERT_FALSE(submenu->IsShowing()); ASSERT_EQ(3, submenu->GetMenuItemCount()); if (SELECT_INDEX == INSERT_INDEX) ASSERT_EQ(1000, last_command_); else if (SELECT_INDEX < INSERT_INDEX) ASSERT_EQ(SELECT_INDEX + 1, last_command_); else ASSERT_EQ(SELECT_INDEX, last_command_); Done(); } private: int last_command_; views::MenuItemView* inserted_item_; }; // MenuItemViewTestInsertXY inserts an item at index X and selects the // item at index Y (after the insertion). The tests here cover // inserting at the beginning, middle, and end, crossbarred with // selecting the first and last item. typedef MenuItemViewTestInsert<0,0> MenuItemViewTestInsert00; typedef MenuItemViewTestInsert<0,2> MenuItemViewTestInsert02; typedef MenuItemViewTestInsert<1,0> MenuItemViewTestInsert10; typedef MenuItemViewTestInsert<1,2> MenuItemViewTestInsert12; typedef MenuItemViewTestInsert<2,0> MenuItemViewTestInsert20; typedef MenuItemViewTestInsert<2,2> MenuItemViewTestInsert22; #if defined(OS_WIN) #define MAYBE_InsertItem00 DISABLED_InsertItem00 #define MAYBE_InsertItem02 DISABLED_InsertItem02 #define MAYBE_InsertItem10 DISABLED_InsertItem10 #define MAYBE_InsertItem12 DISABLED_InsertItem12 #define MAYBE_InsertItem20 DISABLED_InsertItem20 #define MAYBE_InsertItem22 DISABLED_InsertItem22 #else #define MAYBE_InsertItem00 InsertItem00 #define MAYBE_InsertItem02 InsertItem02 #define MAYBE_InsertItem10 InsertItem10 #define MAYBE_InsertItem12 InsertItem12 #define MAYBE_InsertItem20 InsertItem20 #define MAYBE_InsertItem22 InsertItem22 #endif VIEW_TEST(MenuItemViewTestInsert00, MAYBE_InsertItem00) VIEW_TEST(MenuItemViewTestInsert02, MAYBE_InsertItem02) VIEW_TEST(MenuItemViewTestInsert10, MAYBE_InsertItem10) VIEW_TEST(MenuItemViewTestInsert12, MAYBE_InsertItem12) VIEW_TEST(MenuItemViewTestInsert20, MAYBE_InsertItem20) VIEW_TEST(MenuItemViewTestInsert22, MAYBE_InsertItem22) // Test class for inserting a menu item while a submenu is open. template class MenuItemViewTestInsertWithSubmenu : public MenuItemViewTestBase { public: MenuItemViewTestInsertWithSubmenu() : last_command_(0), submenu_(NULL), inserted_item_(NULL) { } virtual ~MenuItemViewTestInsertWithSubmenu() { } // views::MenuDelegate implementation virtual void ExecuteCommand(int id) OVERRIDE { last_command_ = id; } // MenuItemViewTestBase implementation virtual void BuildMenu(views::MenuItemView* menu) OVERRIDE { submenu_ = menu->AppendSubMenu(1, ASCIIToUTF16("My Submenu")); submenu_->AppendMenuItemWithLabel(101, ASCIIToUTF16("submenu item 1")); submenu_->AppendMenuItemWithLabel(101, ASCIIToUTF16("submenu item 2")); menu->AppendMenuItemWithLabel(2, ASCIIToUTF16("item 2")); } // ViewEventTestBase implementation virtual void DoTestOnMessageLoop() OVERRIDE { Click(button_, CreateEventTask(this, &MenuItemViewTestInsertWithSubmenu::Step1)); } // Post submenu. void Step1() { Click(submenu_, CreateEventTask(this, &MenuItemViewTestInsertWithSubmenu::Step2)); } // Insert item at INSERT_INDEX. void Step2() { inserted_item_ = menu_->AddMenuItemAt(INSERT_INDEX, 1000, ASCIIToUTF16("inserted item"), gfx::ImageSkia(), views::MenuItemView::NORMAL, ui::NORMAL_SEPARATOR); ASSERT_TRUE(inserted_item_); menu_->ChildrenChanged(); Click(inserted_item_, CreateEventTask(this, &MenuItemViewTestInsertWithSubmenu::Step3)); } void Step3() { Done(); } private: int last_command_; views::MenuItemView* submenu_; views::MenuItemView* inserted_item_; }; // MenuItemViewTestInsertWithSubmenuX posts a menu and its submenu, // then inserts an item in the top-level menu at X. typedef MenuItemViewTestInsertWithSubmenu<0> MenuItemViewTestInsertWithSubmenu0; typedef MenuItemViewTestInsertWithSubmenu<1> MenuItemViewTestInsertWithSubmenu1; #if defined(OS_WIN) #define MAYBE_InsertItemWithSubmenu0 DISABLED_InsertItemWithSubmenu0 #define MAYBE_InsertItemWithSubmenu1 DISABLED_InsertItemWithSubmenu1 #else #define MAYBE_InsertItemWithSubmenu0 InsertItemWithSubmenu0 #define MAYBE_InsertItemWithSubmenu1 InsertItemWithSubmenu1 #endif VIEW_TEST(MenuItemViewTestInsertWithSubmenu0, MAYBE_InsertItemWithSubmenu0) VIEW_TEST(MenuItemViewTestInsertWithSubmenu1, MAYBE_InsertItemWithSubmenu1) // Test class for removing a menu item while the menu is open. template class MenuItemViewTestRemove : public MenuItemViewTestBase { public: MenuItemViewTestRemove() : last_command_(0) { } virtual ~MenuItemViewTestRemove() { } // views::MenuDelegate implementation virtual void ExecuteCommand(int id) OVERRIDE { last_command_ = id; } // MenuItemViewTestBase implementation virtual void BuildMenu(views::MenuItemView* menu) OVERRIDE { menu->AppendMenuItemWithLabel(1, ASCIIToUTF16("item 1")); menu->AppendMenuItemWithLabel(2, ASCIIToUTF16("item 2")); menu->AppendMenuItemWithLabel(3, ASCIIToUTF16("item 3")); } // ViewEventTestBase implementation virtual void DoTestOnMessageLoop() OVERRIDE { Click(button_, CreateEventTask(this, &MenuItemViewTestRemove::Step1)); } // Remove item at REMOVE_INDEX and click item at SELECT_INDEX. void Step1() { ASSERT_TRUE(menu_); views::SubmenuView* submenu = menu_->GetSubmenu(); ASSERT_TRUE(submenu); ASSERT_TRUE(submenu->IsShowing()); ASSERT_EQ(3, submenu->GetMenuItemCount()); // remove menu_->RemoveMenuItemAt(REMOVE_INDEX); menu_->ChildrenChanged(); // click views::MenuItemView* item = submenu->GetMenuItemAt(SELECT_INDEX); ASSERT_TRUE(item); Click(item, CreateEventTask(this, &MenuItemViewTestRemove::Step2)); } // Check clicked item and complete test. void Step2() { ASSERT_TRUE(menu_); views::SubmenuView* submenu = menu_->GetSubmenu(); ASSERT_TRUE(submenu); ASSERT_FALSE(submenu->IsShowing()); ASSERT_EQ(2, submenu->GetMenuItemCount()); if (SELECT_INDEX < REMOVE_INDEX) ASSERT_EQ(SELECT_INDEX + 1, last_command_); else ASSERT_EQ(SELECT_INDEX + 2, last_command_); Done(); } private: int last_command_; }; typedef MenuItemViewTestRemove<0,0> MenuItemViewTestRemove00; typedef MenuItemViewTestRemove<0,1> MenuItemViewTestRemove01; typedef MenuItemViewTestRemove<1,0> MenuItemViewTestRemove10; typedef MenuItemViewTestRemove<1,1> MenuItemViewTestRemove11; typedef MenuItemViewTestRemove<2,0> MenuItemViewTestRemove20; typedef MenuItemViewTestRemove<2,1> MenuItemViewTestRemove21; #if defined(OS_WIN) #define MAYBE_RemoveItem00 DISABLED_RemoveItem00 #define MAYBE_RemoveItem01 DISABLED_RemoveItem01 #define MAYBE_RemoveItem10 DISABLED_RemoveItem10 #define MAYBE_RemoveItem11 DISABLED_RemoveItem11 #define MAYBE_RemoveItem20 DISABLED_RemoveItem20 #define MAYBE_RemoveItem21 DISABLED_RemoveItem21 #else #define MAYBE_RemoveItem00 RemoveItem00 #define MAYBE_RemoveItem01 RemoveItem01 #define MAYBE_RemoveItem10 RemoveItem10 #define MAYBE_RemoveItem11 RemoveItem11 #define MAYBE_RemoveItem20 RemoveItem20 #define MAYBE_RemoveItem21 RemoveItem21 #endif VIEW_TEST(MenuItemViewTestRemove00, MAYBE_RemoveItem00) VIEW_TEST(MenuItemViewTestRemove01, MAYBE_RemoveItem01) VIEW_TEST(MenuItemViewTestRemove10, MAYBE_RemoveItem10) VIEW_TEST(MenuItemViewTestRemove11, MAYBE_RemoveItem11) VIEW_TEST(MenuItemViewTestRemove20, MAYBE_RemoveItem20) VIEW_TEST(MenuItemViewTestRemove21, MAYBE_RemoveItem21) // Test class for removing a menu item while a submenu is open. template class MenuItemViewTestRemoveWithSubmenu : public MenuItemViewTestBase { public: MenuItemViewTestRemoveWithSubmenu() : last_command_(0), submenu_(NULL) { } virtual ~MenuItemViewTestRemoveWithSubmenu() { } // views::MenuDelegate implementation virtual void ExecuteCommand(int id) OVERRIDE { last_command_ = id; } // MenuItemViewTestBase implementation virtual void BuildMenu(views::MenuItemView* menu) OVERRIDE { menu->AppendMenuItemWithLabel(1, ASCIIToUTF16("item 1")); submenu_ = menu->AppendSubMenu(2, ASCIIToUTF16("My Submenu")); submenu_->AppendMenuItemWithLabel(101, ASCIIToUTF16("submenu item 1")); submenu_->AppendMenuItemWithLabel(102, ASCIIToUTF16("submenu item 2")); } // ViewEventTestBase implementation virtual void DoTestOnMessageLoop() OVERRIDE { Click(button_, CreateEventTask(this, &MenuItemViewTestRemoveWithSubmenu::Step1)); } // Post submenu. void Step1() { ASSERT_TRUE(menu_); views::SubmenuView* submenu = menu_->GetSubmenu(); ASSERT_TRUE(submenu); ASSERT_TRUE(submenu->IsShowing()); Click(submenu_, CreateEventTask(this, &MenuItemViewTestRemoveWithSubmenu::Step2)); } // Remove item at REMOVE_INDEX and select it to exit the menu loop. void Step2() { ASSERT_TRUE(menu_); views::SubmenuView* submenu = menu_->GetSubmenu(); ASSERT_TRUE(submenu); ASSERT_TRUE(submenu->IsShowing()); ASSERT_EQ(2, submenu->GetMenuItemCount()); // remove menu_->RemoveMenuItemAt(REMOVE_INDEX); menu_->ChildrenChanged(); // click Click(button_, CreateEventTask(this, &MenuItemViewTestRemoveWithSubmenu::Step3)); } void Step3() { ASSERT_TRUE(menu_); views::SubmenuView* submenu = menu_->GetSubmenu(); ASSERT_TRUE(submenu); ASSERT_FALSE(submenu->IsShowing()); ASSERT_EQ(1, submenu->GetMenuItemCount()); Done(); } private: int last_command_; views::MenuItemView* submenu_; }; typedef MenuItemViewTestRemoveWithSubmenu<0> MenuItemViewTestRemoveWithSubmenu0; typedef MenuItemViewTestRemoveWithSubmenu<1> MenuItemViewTestRemoveWithSubmenu1; #if defined(OS_WIN) #define MAYBE_RemoveItemWithSubmenu0 DISABLED_RemoveItemWithSubmenu0 #define MAYBE_RemoveItemWithSubmenu1 DISABLED_RemoveItemWithSubmenu1 #else #define MAYBE_RemoveItemWithSubmenu0 RemoveItemWithSubmenu0 #define MAYBE_RemoveItemWithSubmenu1 RemoveItemWithSubmenu1 #endif VIEW_TEST(MenuItemViewTestRemoveWithSubmenu0, MAYBE_RemoveItemWithSubmenu0) VIEW_TEST(MenuItemViewTestRemoveWithSubmenu1, MAYBE_RemoveItemWithSubmenu1)