chromium/ui/views/controls/menu/menu_controller_unittest.cc

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/menu/menu_controller.h"

#include <functional>
#include <type_traits>
#include <utility>
#include <vector>

#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/i18n/rtl.h"
#include "base/memory/raw_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/strings/stringprintf.h"
#include "base/strings/to_string.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/current_thread.h"
#include "base/task/single_thread_task_runner.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_mode.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/platform/ax_platform_for_test.h"
#include "ui/accessibility/platform/ax_platform_node.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/owned_window_anchor.h"
#include "ui/base/ozone_buildflags.h"
#include "ui/base/ui_base_types.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_handler.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
#include "ui/events/types/event_type.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/menu/menu_controller_delegate.h"
#include "ui/views/controls/menu/menu_delegate.h"
#include "ui/views/controls/menu/menu_host.h"
#include "ui/views/controls/menu/menu_host_root_view.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_scroll_view_container.h"
#include "ui/views/controls/menu/menu_types.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/test/ax_event_counter.h"
#include "ui/views/test/menu_test_utils.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget_utils.h"

#if defined(USE_AURA)
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/drag_drop_client.h"
#include "ui/aura/client/drag_drop_client_observer.h"
#include "ui/aura/null_window_targeter.h"
#include "ui/aura/scoped_window_targeter.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/views/controls/menu/menu_pre_target_handler.h"
#endif

#if BUILDFLAG(IS_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#endif

#if BUILDFLAG(IS_OZONE_X11)
#include "ui/events/test/events_test_utils_x11.h"
#endif

namespace views {
namespace {

DragOperation;

constexpr MenuAnchorPosition kBubblePositions[] =;

bool ShouldIgnoreScreenBoundsForMenus() {}

gfx::Size GetPreferredSizeForSubmenu(SubmenuView& submenu) {}

// Unfortunately a macro rather than a function, to work around ASSERT_ not
// working in non-void functions.
#define GET_CHILD_BUTTON(name, parent, index)

// Test implementation of MenuControllerDelegate that only reports the values
// called of OnMenuClosed.
class TestMenuControllerDelegate : public internal::MenuControllerDelegate {};

void TestMenuControllerDelegate::OnMenuClosed(NotifyType type,
                                              MenuItemView* menu,
                                              int mouse_event_flags) {}

void TestMenuControllerDelegate::SiblingMenuCreated(MenuItemView* menu) {}

class TestEventHandler : public ui::EventHandler {};

// A test widget that counts gesture events.
class GestureTestWidget : public Widget {};

void GestureTestWidget::OnGestureEvent(ui::GestureEvent* event) {}

#if defined(USE_AURA)
// A DragDropClient which does not trigger a nested run loop. Instead a
// callback is triggered during StartDragAndDrop in order to allow testing.
class TestDragDropClient : public aura::client::DragDropClient {};

DragOperation TestDragDropClient::StartDragAndDrop(
    std::unique_ptr<ui::OSExchangeData> data,
    aura::Window* root_window,
    aura::Window* source_window,
    const gfx::Point& screen_location,
    int allowed_operations,
    ui::mojom::DragEventSource source) {}

void TestDragDropClient::DragCancel() {}
bool TestDragDropClient::IsDragDropInProgress() {}

#endif  // defined(USE_AURA)

// View which cancels the menu it belongs to on mouse press.
class CancelMenuOnMousePressView : public View {};

bool CancelMenuOnMousePressView::OnMousePressed(const ui::MouseEvent& event) {}

gfx::Size CancelMenuOnMousePressView::CalculatePreferredSize(
    const SizeBounds& /*available_size*/) const {}

BEGIN_METADATA()

}  // namespace

struct MenuBoundsOptions {};

class MenuControllerTest : public ViewsTestBase,
                           public testing::WithParamInterface<bool> {};

void MenuControllerTest::SetUp() {}

void MenuControllerTest::TearDown() {}

void MenuControllerTest::ReleaseTouchId(int id) {}

void MenuControllerTest::PressKey(ui::KeyboardCode key_code) {}

void MenuControllerTest::DispatchKey(ui::KeyboardCode key_code) {}

gfx::Rect MenuControllerTest::CalculateMenuBounds(
    const MenuBoundsOptions& options) {}

gfx::Rect MenuControllerTest::CalculateBubbleMenuBoundsWithoutInsets(
    const MenuBoundsOptions& options,
    MenuItemView* menu_item) {}

gfx::Rect MenuControllerTest::CalculateExpectedMenuAnchorRect(
    MenuItemView* menu_item) {}

MenuController::MenuOpenDirection
MenuControllerTest::GetChildMenuOpenDirectionAtDepth(size_t depth) const {}

void MenuControllerTest::SetChildMenuOpenDirectionAtDepth(
    size_t depth,
    MenuController::MenuOpenDirection direction) {}

void MenuControllerTest::MenuChildrenChanged(MenuItemView* item) {}

// static
MenuAnchorPosition MenuControllerTest::AdjustAnchorPositionForRtl(
    MenuAnchorPosition position) {}

#if defined(USE_AURA)
void MenuControllerTest::TestAsyncEscapeKey() {}

void MenuControllerTest::TestCancelEvent() {}
#endif  // defined(USE_AURA)

void MenuControllerTest::VerifyDragCompleteThenDestroy() {}

void MenuControllerTest::TestDragCompleteThenDestroyOnMenuClosed() {}

void MenuControllerTest::TestMenuControllerReplacementDuringDrag() {}

void MenuControllerTest::TestCancelAllDuringDrag() {}

void MenuControllerTest::TestDestroyedDuringViewsRelease() {}

void MenuControllerTest::TestMenuFitsOnScreen(
    MenuAnchorPosition menu_anchor_position,
    const gfx::Rect& monitor_bounds) {}

void MenuControllerTest::TestMenuFitsOnScreenSmallAnchor(
    MenuAnchorPosition menu_anchor_position,
    const gfx::Rect& monitor_bounds) {}

void MenuControllerTest::TestMenuFitsOnSmallScreen(
    MenuAnchorPosition menu_anchor_position,
    const gfx::Rect& monitor_bounds) {}

void MenuControllerTest::TestSubmenuFitsOnScreen(
    MenuItemView* item,
    const gfx::Rect& monitor_bounds,
    const gfx::Rect& parent_bounds,
    MenuAnchorPosition menu_anchor) {}

void MenuControllerTest::SetPendingStateItem(MenuItemView* item) {}

void MenuControllerTest::SetState(MenuItemView* item) {}

void MenuControllerTest::IncrementSelection() {}

void MenuControllerTest::DecrementSelection() {}

void MenuControllerTest::DestroyMenuControllerOnMenuClosed(
    TestMenuControllerDelegate* delegate) {}

void MenuControllerTest::DestroyMenuControllerDelegate() {}

MenuItemView* MenuControllerTest::FindInitialSelectableMenuItemDown(
    MenuItemView* parent) {}

MenuItemView* MenuControllerTest::FindInitialSelectableMenuItemUp(
    MenuItemView* parent) {}

MenuHostRootView* MenuControllerTest::CreateMenuHostRootView(MenuHost* host) {}

void MenuControllerTest::MenuHostOnDragWillStart(MenuHost* host) {}

void MenuControllerTest::MenuHostOnDragComplete(MenuHost* host) {}

void MenuControllerTest::SelectByChar(char16_t character) {}

void MenuControllerTest::SetDropMenuItem(MenuItemView* target,
                                         MenuDelegate::DropPosition position) {}

void MenuControllerTest::SetComboboxType(
    MenuController::ComboboxType combobox_type) {}

void MenuControllerTest::SetSelectionOnPointerDown(
    SubmenuView* source,
    const ui::MouseEvent& event) {}

bool MenuControllerTest::ProcessMousePressed(SubmenuView* source,
                                             const ui::MouseEvent& event) {}

bool MenuControllerTest::ProcessMouseDragged(SubmenuView* source,
                                             const ui::MouseEvent& event) {}

void MenuControllerTest::ProcessMouseReleased(SubmenuView* source,
                                              const ui::MouseEvent& event) {}

void MenuControllerTest::ProcessMouseMoved(SubmenuView* source,
                                           const ui::MouseEvent& event) {}

void MenuControllerTest::ProcessGestureEvent(SubmenuView* source,
                                             const ui::GestureEvent& event) {}

void MenuControllerTest::ProcessTouchEvent(SubmenuView* source,
                                           const ui::TouchEvent& event) {}

void MenuControllerTest::Accept(MenuItemView* item, int event_flags) {}

void MenuControllerTest::StartDrag() {}

void MenuControllerTest::SetUpMenuControllerForCalculateBounds(
    const MenuBoundsOptions& options,
    MenuItemView* menu_item) {}

MenuItemView* MenuControllerTest::AddButtonMenuItems(bool single_child) {}

void MenuControllerTest::DestroyMenuItem() {}

void MenuControllerTest::SetHotTrackedButton(Button* hot_button) {}

void MenuControllerTest::ExitMenuRun() {}

void MenuControllerTest::DestroyMenuController() {}

// static
bool MenuControllerTest::SelectionWraps() {}

void MenuControllerTest::OpenMenu(MenuItemView* parent,
                                  const MenuBoundsOptions& options) {}

gfx::Insets MenuControllerTest::GetBorderAndShadowInsets(bool is_submenu) {}

INSTANTIATE_TEST_SUITE_P();

#if defined(USE_AURA)
// Tests that an event targeter which blocks events will be honored by the menu
// event dispatcher.
TEST_F(MenuControllerTest, EventTargeter) {}
#endif  // defined(USE_AURA)

#if BUILDFLAG(IS_OZONE_X11)
// Tests that touch event ids are released correctly. See crbug.com/439051 for
// details. When the ids aren't managed correctly, we get stuck down touches.
TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) {}
#endif  // BUILDFLAG(IS_OZONE_X11)

// Tests that initial selected menu items are correct when items are enabled or
// disabled.
TEST_F(MenuControllerTest, InitialSelectedItem) {}

// Verifies that the context menu bubble should prioritize its cached menu
// position (above or below the anchor) after its size updates
// (https://crbug.com/1126244).
TEST_F(MenuControllerTest, VerifyMenuBubblePositionAfterSizeChanges) {}

// Verifies that the context menu bubble position,
// MenuAnchorPosition::kBubbleBottomRight, does not shift as items are removed.
// The menu position will shift if items are added and the menu no longer fits
// in its previous position.
TEST_F(MenuControllerTest, VerifyContextMenuBubblePositionAfterSizeChanges) {}

// Tests that opening the menu and pressing 'Home' selects the first menu item.
TEST_F(MenuControllerTest, FirstSelectedItem) {}

// Tests that opening the menu and pressing 'End' selects the last enabled menu
// item.
TEST_F(MenuControllerTest, LastSelectedItem) {}

// MenuController tests which set expectations about how menu item selection
// behaves should verify test cases work as intended for all supported selection
// mechanisms.
class MenuControllerSelectionTest : public MenuControllerTest {};

// Tests that opening menu and exercising various mechanisms to update
// selection iterates over enabled items.
TEST_F(MenuControllerSelectionTest, NextSelectedItem) {}

// Tests that opening menu and exercising various mechanisms to decrement
// selection selects the last enabled menu item.
TEST_F(MenuControllerSelectionTest, PreviousSelectedItem) {}

// Tests that the APIs related to the current selected item work correctly.
TEST_F(MenuControllerTest, CurrentSelectedItem) {}

// Tests that opening menu and calling SelectByChar works correctly.
TEST_F(MenuControllerTest, SelectByChar) {}

TEST_F(MenuControllerTest, SelectChildButtonView) {}

TEST_F(MenuControllerTest, DeleteChildButtonView) {}

// Verifies that the child button is hot tracked after the host menu item is
// selected by `MenuController::SelectItemAndOpenSubmenu()`.
TEST_F(MenuControllerTest, ChildButtonHotTrackedAfterMenuItemSelection) {}

// Verifies that the child button of the menu item which is under mouse
// hovering is hot tracked (https://crbug.com/1135000).
TEST_F(MenuControllerTest, ChildButtonHotTrackedAfterMouseMove) {}

// Creates a menu with Button child views, simulates running a nested
// menu and tests that existing the nested run restores hot-tracked child
// view.
TEST_F(MenuControllerTest, ChildButtonHotTrackedWhenNested) {}

// Tests that a menu opened asynchronously, will notify its
// MenuControllerDelegate when Accept is called.
TEST_F(MenuControllerTest, AsynchronousAccept) {}

// Tests that a menu opened asynchronously, will notify its
// MenuControllerDelegate when CancelAll is called.
TEST_F(MenuControllerTest, AsynchronousCancelAll) {}

// Tests that canceling a nested menu restores the previous
// MenuControllerDelegate, and notifies each delegate.
TEST_F(MenuControllerTest, AsynchronousNestedDelegate) {}

// Tests that dropping within an asynchronous menu stops the menu from showing
// and does not notify the controller.
TEST_F(MenuControllerTest, AsynchronousPerformDrop) {}

// Tests that dragging within an asynchronous menu notifies the
// MenuControllerDelegate for shutdown.
TEST_F(MenuControllerTest, AsynchronousDragComplete) {}

// Tests that if Cancel is called during a drag, that OnMenuClosed is still
// notified when the drag completes.
TEST_F(MenuControllerTest, AsynchronousCancelDuringDrag) {}

// Tests that if a menu is destroyed while drag operations are occurring, that
// the MenuHost does not crash as the drag completes.
TEST_F(MenuControllerTest, AsynchronousDragHostDeleted) {}

// Tests that getting the drop callback stops the menu from showing and
// does not notify the controller.
TEST_F(MenuControllerTest, AsyncDropCallback) {}

// Widget destruction and cleanup occurs on the MessageLoop after the
// MenuController has been destroyed. A MenuHostRootView should not attempt to
// access a destroyed MenuController. This test should not cause a crash.
TEST_F(MenuControllerTest, HostReceivesInputBeforeDestruction) {}

// Tests that an asynchronous menu nested within an asynchronous menu closes
// both menus, and notifies both delegates.
TEST_F(MenuControllerTest, DoubleAsynchronousNested) {}

// Tests that setting send_gesture_events_to_owner flag forwards gesture
// events to owner and the forwarding stops when the current gesture sequence
// ends.
TEST_F(MenuControllerTest, PreserveGestureForOwner) {}

#if defined(USE_AURA)
// Tests that setting `send_gesture_events_to_owner` flag forwards gesture
// events to the NativeView specified for gestures and not the owner's
// NativeView.
TEST_F(MenuControllerTest, ForwardsEventsToNativeViewForGestures) {}
#endif

// Tests that touch outside menu does not closes the menu when forwarding
// gesture events to owner.
TEST_F(MenuControllerTest, NoTouchCloseWhenSendingGesturesToOwner) {}

// Tests that a nested menu does not crash when trying to repost events that
// occur outside of the bounds of the menu. Instead a proper shutdown should
// occur.
TEST_F(MenuControllerTest, AsynchronousRepostEvent) {}

// Tests that an asynchronous menu reposts touch events that occur outside of
// the bounds of the menu, and that the menu closes.
TEST_F(MenuControllerTest, AsynchronousTouchEventRepostEvent) {}

// Tests that having the MenuController deleted during RepostEvent does not
// cause a crash. ASAN bots should not detect use-after-free in
// MenuController.
TEST_F(MenuControllerTest, AsynchronousRepostEventDeletesController) {}

// Tests that having the MenuController deleted during OnGestureEvent does not
// cause a crash. ASAN bots should not detect use-after-free in
// MenuController.
TEST_F(MenuControllerTest, AsynchronousGestureDeletesController) {}

// Test that the menu is properly placed where it best fits.
TEST_F(MenuControllerTest, CalculateMenuBoundsBestFitTest) {}

// Tests that the menu is properly placed according to its anchor.
TEST_F(MenuControllerTest, CalculateMenuBoundsAnchorTest) {}

// Regression test for https://crbug.com/1217711
TEST_F(MenuControllerTest, MenuAnchorPositionFlippedInRtl) {}

TEST_F(MenuControllerTest, CalculateMenuBoundsMonitorFitTest) {}

// Test that menus show up on screen with non-zero sized anchors.
TEST_P(MenuControllerTest, TestMenuFitsOnScreen) {}

// Test that menus show up on screen with zero sized anchors.
TEST_P(MenuControllerTest, TestMenuFitsOnScreenSmallAnchor) {}

// Test that menus fit a small screen.
TEST_P(MenuControllerTest, TestMenuFitsOnSmallScreen) {}

// Test that submenus are displayed within the screen bounds on smaller
// screens.
TEST_P(MenuControllerTest, TestSubmenuFitsOnScreen) {}

// Test that a menu that was originally drawn below the anchor does not get
// squished or move above the anchor when it grows vertically and horizontally
// beyond the monitor bounds.
TEST_F(MenuControllerTest, GrowingMenuMovesLaterallyNotVertically) {}

#if defined(USE_AURA)
// This tests that mouse moved events from the initial position of the mouse
// when the menu was shown don't select the menu item at the mouse position.
TEST_F(MenuControllerTest, MouseAtMenuItemOnShow) {}

// Tests that when an asynchronous menu receives a cancel event, that it
// closes.
TEST_F(MenuControllerTest, AsynchronousCancelEvent) {}

TEST_F(MenuControllerTest, WidgetStateChangeCancelsMenu) {}

// TODO(pkasting): The test below fails most of the time on Wayland; not clear
// it's important to support this case.
#if BUILDFLAG(ENABLE_DESKTOP_AURA) && !BUILDFLAG(IS_OZONE_WAYLAND)
class DesktopMenuControllerTest : public MenuControllerTest {
 public:
  // MenuControllerTest:
  void SetUp() override {
    set_native_widget_type(NativeWidgetType::kDesktop);
    MenuControllerTest::SetUp();
  }
};

// Tests that menus without parent widgets do not crash in
// MenuPreTargetHandler. Having neither parent nor context pointers when
// creating a Widget is only valid in desktop Aura.
TEST_F(DesktopMenuControllerTest, RunWithoutWidgetDoesntCrash) {
  ExitMenuRun();
  menu_controller()->Run(nullptr, nullptr, menu_item(), gfx::Rect(),
                         MenuAnchorPosition::kTopLeft, false, false);
}
#endif  // BUILDFLAG(ENABLE_DESKTOP_AURA) && !BUILDFLAG(IS_OZONE_WAYLAND)

// Tests that if a MenuController is destroying during drag/drop, and another
// MenuController becomes active, that the exiting of drag does not cause a
// crash.
TEST_F(MenuControllerTest, MenuControllerReplacedDuringDrag) {}

// Tests that if a CancelAll is called during drag-and-drop that it does not
// destroy the MenuController. On Windows and Linux this destruction also
// destroys the Widget used for drag-and-drop, thereby ending the drag.
TEST_F(MenuControllerTest, CancelAllDuringDrag) {}

// Tests that when releasing the ref on ViewsDelegate and MenuController is
// deleted, that shutdown occurs without crashing.
TEST_F(MenuControllerTest, DestroyedDuringViewsRelease) {}

// Tests that when a context menu is opened above an empty menu item, and a
// right-click occurs over the empty item, that the bottom menu is not hidden,
// that a request to relaunch the context menu is received, and that
// subsequently pressing ESC does not crash the browser.
TEST_F(MenuControllerTest, RepostEventToEmptyMenuItem) {}

// Drag the mouse from an external view into a menu
// When the mouse leaves the menu while still in the process of dragging
// the menu item view highlight should turn off
TEST_F(MenuControllerTest, DragFromViewIntoMenuAndExit) {}

// Tests that |MenuHost::InitParams| are correctly forwarded to the created
// |aura::Window|.
TEST_F(MenuControllerTest, AuraWindowIsInitializedWithMenuHostInitParams) {}

// Tests that |aura::Window| has the correct properties when a context menu is
// shown.
TEST_F(MenuControllerTest, ContextMenuInitializesAuraWindowWhenShown) {}

// Tests that |aura::Window| has the correct properties when a root or a child
// menu is shown.
TEST_F(MenuControllerTest, RootAndChildMenusInitializeAuraWindowWhenShown) {}

// Test that if `SetTriggerActionWithNonIconChildViews` true that even with
// child views the click will be registered. Detect that the click was
// registered by checking if the TestMenuControllerDelegate received the signal
// that the menu should be closed.
TEST_F(MenuControllerTest, RegisterClickWithChildViews) {}

#endif  // defined(USE_AURA)

// Tests that having the MenuController deleted during OnMousePressed does not
// cause a crash. ASAN bots should not detect use-after-free in
// MenuController.
TEST_F(MenuControllerTest, NoUseAfterFreeWhenMenuCanceledOnMousePress) {}

TEST_F(MenuControllerTest, SetSelectionIndices_MenuItemsOnly) {}

TEST_F(MenuControllerTest,
       SetSelectionIndices_MenuItemsOnly_SkipHiddenAndDisabled) {}

TEST_F(MenuControllerTest, SetSelectionIndices_Buttons) {}

TEST_F(MenuControllerTest, SetSelectionIndices_Buttons_SkipHiddenAndDisabled) {}

TEST_F(MenuControllerTest, SetSelectionIndices_NestedButtons) {}

TEST_F(MenuControllerTest, SetSelectionIndices_ChildrenChanged) {}

// Tests that a menu opened asynchronously, will notify its
// MenuControllerDelegate when accessibility performs a do default action.
TEST_F(MenuControllerTest, AccessibilityDoDefaultCallsAccept) {}

// Test that the kSelectedChildrenChanged event is emitted on
// the root menu item when the selected menu item changes.
TEST_F(MenuControllerTest, AccessibilityEmitsSelectChildrenChanged) {}

// Test that in accessibility mode disabled menu items are taken into account
// during items indices assignment.
TEST_F(MenuControllerTest, AccessibilityDisabledItemsIndices) {}

#if BUILDFLAG(IS_MAC)
// This test exercises a Mac-specific behavior, by which hotkeys using
// modifiers cause menus to close and the hotkeys to be handled by the browser
// window. This specific test case tries using cmd-ctrl-f, which normally
// means "Fullscreen".
TEST_F(MenuControllerTest, BrowserHotkeysCancelMenusAndAreRedispatched) {
  menu_controller()->Run(owner(), nullptr, menu_item(), gfx::Rect(),
                         MenuAnchorPosition::kTopLeft, false, false);

  int options = ui::EF_COMMAND_DOWN;
  ui::KeyEvent press_cmd(ui::EventType::kKeyPressed, ui::VKEY_COMMAND, options);
  menu_controller()->OnWillDispatchKeyEvent(&press_cmd);
  EXPECT_TRUE(showing());  // ensure the command press itself doesn't cancel

  options |= ui::EF_CONTROL_DOWN;
  ui::KeyEvent press_ctrl(ui::EventType::kKeyPressed, ui::VKEY_CONTROL,
                          options);
  menu_controller()->OnWillDispatchKeyEvent(&press_ctrl);
  EXPECT_TRUE(showing());

  ui::KeyEvent press_f(ui::EventType::kKeyPressed, ui::VKEY_F, options);
  menu_controller()->OnWillDispatchKeyEvent(&press_f);
  EXPECT_FALSE(showing());
  EXPECT_FALSE(press_f.handled());
  EXPECT_FALSE(press_f.stopped_propagation());
}
#endif

TEST_F(MenuControllerTest, SubmenuOpenByKey) {}

class ExecuteCommandWithoutClosingMenuTest : public MenuControllerTest {};

TEST_F(ExecuteCommandWithoutClosingMenuTest, OnClick) {}

TEST_F(ExecuteCommandWithoutClosingMenuTest, OnTap) {}

TEST_F(ExecuteCommandWithoutClosingMenuTest, OnReturnKey) {}

// Simple test to ensure child menu open direction is correctly set and
// retrieved.
TEST_F(MenuControllerTest, ChildMenuOpenDirectionStateUpdatesCorrectly) {}

TEST_F(MenuControllerTest, MenuHostHasCorrectZOrderLevel) {}

}  // namespace views