blob: 31219ff2afd421aa9d520f98808de46aaf29faf6 [file] [log] [blame]
// 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/strings/utf_string_conversions.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/base/view_event_test_base.h"
#include "ui/base/test/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,
ui::MENU_SOURCE_NONE,
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<views::MenuRunner> 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<int INDEX>
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;
VIEW_TEST(MenuItemViewTestBasic0, SelectItem0)
VIEW_TEST(MenuItemViewTestBasic1, SelectItem1)
VIEW_TEST(MenuItemViewTestBasic2, SelectItem2)
// Test class for inserting a menu item while the menu is open.
template<int INSERT_INDEX, int SELECT_INDEX>
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"),
string16(),
string16(),
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;
VIEW_TEST(MenuItemViewTestInsert00, InsertItem00)
VIEW_TEST(MenuItemViewTestInsert02, InsertItem02)
VIEW_TEST(MenuItemViewTestInsert10, InsertItem10)
VIEW_TEST(MenuItemViewTestInsert12, InsertItem12)
VIEW_TEST(MenuItemViewTestInsert20, InsertItem20)
VIEW_TEST(MenuItemViewTestInsert22, InsertItem22)
// Test class for inserting a menu item while a submenu is open.
template<int INSERT_INDEX>
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"),
string16(),
string16(),
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;
VIEW_TEST(MenuItemViewTestInsertWithSubmenu0, InsertItemWithSubmenu0)
VIEW_TEST(MenuItemViewTestInsertWithSubmenu1, InsertItemWithSubmenu1)
// Test class for removing a menu item while the menu is open.
template<int REMOVE_INDEX, int SELECT_INDEX>
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;
VIEW_TEST(MenuItemViewTestRemove00, RemoveItem00)
VIEW_TEST(MenuItemViewTestRemove01, RemoveItem01)
VIEW_TEST(MenuItemViewTestRemove10, RemoveItem10)
VIEW_TEST(MenuItemViewTestRemove11, RemoveItem11)
VIEW_TEST(MenuItemViewTestRemove20, RemoveItem20)
VIEW_TEST(MenuItemViewTestRemove21, RemoveItem21)
// Test class for removing a menu item while a submenu is open.
template<int REMOVE_INDEX>
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;
VIEW_TEST(MenuItemViewTestRemoveWithSubmenu0, RemoveItemWithSubmenu0)
VIEW_TEST(MenuItemViewTestRemoveWithSubmenu1, RemoveItemWithSubmenu1)