| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/views/toolbar/toolbar_controller.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include <memory> |
| #include <variant> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "chrome/browser/ui/toolbar/pinned_toolbar/pinned_toolbar_actions_model.h" |
| #include "chrome/browser/ui/views/chrome_layout_provider.h" |
| #include "chrome/browser/ui/views/toolbar/overflow_button.h" |
| #include "chrome/browser/ui/views/toolbar/pinned_toolbar_button_status_indicator.h" |
| #include "chrome/browser/ui/views/toolbar/toolbar_view.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chrome/test/views/chrome_views_test_base.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "ui/actions/actions.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/menus/simple_menu_model.h" |
| #include "ui/views/controls/menu/menu_item_view.h" |
| #include "ui/views/controls/menu/submenu_view.h" |
| #include "ui/views/interaction/element_tracker_views.h" |
| #include "ui/views/layout/fill_layout.h" |
| #include "ui/views/layout/flex_layout.h" |
| #include "ui/views/layout/flex_layout_types.h" |
| #include "ui/views/test/views_test_base.h" |
| #include "ui/views/test/views_test_utils.h" |
| #include "ui/views/view_class_properties.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_utils.h" |
| |
| namespace { |
| // Toolbar button size is ~34dp. |
| constexpr gfx::Size kButtonSize(34, 34); |
| |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kDummyButton1); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kDummyButton2); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kDummyButton3); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kDummyButton4); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kDummyObservedView); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kDummyActivateView); |
| |
| class TestDelegate : public ToolbarController::PinnedActionsDelegate { |
| public: |
| MOCK_METHOD(void, |
| DummyAction, |
| (actions::ActionItem*, actions::ActionInvocationContext)); |
| TestDelegate() { |
| for (const auto& id : action_ids_) { |
| action_items_.push_back( |
| actions::ActionItem::ActionItemBuilder( |
| base::BindRepeating(&TestDelegate::DummyAction, |
| base::Unretained(this))) |
| .SetActionId(id) |
| .SetImage( |
| ui::ImageModel::FromVectorIcon(vector_icons::kDogfoodIcon)) |
| .SetProperty(kActionItemUnderlineIndicatorKey, true) |
| .SetText( |
| base::StrCat({u"DummyAction", base::NumberToString16(id)})) |
| .Build()); |
| if (id == 0) { |
| kIdToOverflowedMap_[id] = false; |
| } else { |
| kIdToOverflowedMap_[id] = true; |
| ++overflowed_count_; |
| } |
| kIdToItemMap_[id] = action_items_[id].get(); |
| } |
| } |
| ~TestDelegate() override = default; |
| |
| actions::ActionItem* GetActionItemFor(actions::ActionId id) override { |
| return kIdToItemMap_.at(id); |
| } |
| bool IsOverflowed(actions::ActionId id) override { |
| return kIdToOverflowedMap_.at(id); |
| } |
| views::View* GetContainerView() override { return container_view_; } |
| void SetContainerView(views::View* view) { container_view_ = view; } |
| bool ShouldAnyButtonsOverflow(gfx::Size available_size) const override { |
| return true; |
| } |
| int get_overflowed_count() { return overflowed_count_; } |
| std::vector<actions::ActionId> get_action_ids() { return action_ids_; } |
| |
| const std::vector<actions::ActionId>& PinnedActionIds() const override { |
| return action_ids_; |
| } |
| |
| private: |
| int overflowed_count_ = 0; |
| std::vector<actions::ActionId> action_ids_ = {0, 1, 2}; |
| std::vector<std::unique_ptr<actions::ActionItem>> action_items_; |
| base::flat_map<actions::ActionId, |
| raw_ptr<actions::ActionItem, CtnExperimental>> |
| kIdToItemMap_; |
| base::flat_map<actions::ActionId, bool> kIdToOverflowedMap_; |
| raw_ptr<views::View> container_view_; |
| }; |
| |
| class TestDelegateFromModel : public ToolbarController::PinnedActionsDelegate { |
| public: |
| MOCK_METHOD(void, |
| DummyAction, |
| (actions::ActionItem*, actions::ActionInvocationContext)); |
| explicit TestDelegateFromModel(PinnedToolbarActionsModel* model) { |
| model_ = model; |
| } |
| ~TestDelegateFromModel() override = default; |
| |
| actions::ActionItem* GetActionItemFor(actions::ActionId id) override { |
| for (const auto& action_item : action_items_) { |
| if (action_item->GetActionId() == id) { |
| return action_item.get(); |
| } |
| } |
| action_items_.push_back( |
| actions::ActionItem::ActionItemBuilder( |
| base::BindRepeating(&TestDelegateFromModel::DummyAction, |
| base::Unretained(this))) |
| .SetActionId(id) |
| .SetImage( |
| ui::ImageModel::FromVectorIcon(vector_icons::kDogfoodIcon)) |
| .SetProperty(kActionItemUnderlineIndicatorKey, true) |
| .SetText(base::StrCat({u"DummyAction", base::NumberToString16(id)})) |
| .Build()); |
| return action_items_.back().get(); |
| } |
| bool IsOverflowed(actions::ActionId id) override { return false; } |
| views::View* GetContainerView() override { return &container_view_; } |
| bool ShouldAnyButtonsOverflow(gfx::Size available_size) const override { |
| return false; |
| } |
| const std::vector<actions::ActionId>& PinnedActionIds() const override { |
| return model_->PinnedActionIds(); |
| } |
| |
| private: |
| raw_ptr<PinnedToolbarActionsModel> model_; |
| views::View container_view_; |
| std::vector<std::unique_ptr<actions::ActionItem>> action_items_; |
| }; |
| |
| class MockToolbarController : public ToolbarController { |
| public: |
| MockToolbarController( |
| const std::vector<ToolbarController::ResponsiveElementInfo>& |
| responsive_elements, |
| const std::vector<ui::ElementIdentifier>& elements_in_overflow_order, |
| int element_flex_order_start, |
| views::View* toolbar_container_view, |
| OverflowButton* overflow_button, |
| TestDelegate* delegate, |
| TestingProfile* profile) |
| : ToolbarController(responsive_elements, |
| elements_in_overflow_order, |
| element_flex_order_start, |
| toolbar_container_view, |
| overflow_button, |
| delegate, |
| PinnedToolbarActionsModel::Get(profile)) {} |
| MOCK_METHOD(bool, PopOut, (ui::ElementIdentifier identifier), (override)); |
| MOCK_METHOD(bool, EndPopOut, (ui::ElementIdentifier identifier), (override)); |
| }; |
| |
| class PopOutHandlerTest : public ChromeViewsTestBase { |
| public: |
| PopOutHandlerTest() = default; |
| |
| PopOutHandlerTest(const PopOutHandlerTest&) = delete; |
| PopOutHandlerTest& operator=(const PopOutHandlerTest&) = delete; |
| |
| ~PopOutHandlerTest() override = default; |
| |
| protected: |
| views::Widget* widget() { return widget_.get(); } |
| views::View* container_view() { return container_view_; } |
| OverflowButton* overflow_button() { return overflow_button_; } |
| |
| private: |
| std::unique_ptr<views::Widget> widget_; |
| raw_ptr<views::View, DanglingUntriaged> container_view_; |
| raw_ptr<OverflowButton, DanglingUntriaged> overflow_button_; |
| |
| void SetUp() override { |
| ChromeViewsTestBase::SetUp(); |
| |
| widget_ = std::make_unique<views::Widget>(); |
| views::Widget::InitParams params = |
| CreateParams(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, |
| views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); |
| params.bounds = gfx::Rect(0, 0, 650, 650); |
| widget_->Init(std::move(params)); |
| widget_->Show(); |
| |
| std::unique_ptr<views::View> toolbar_container_view = |
| std::make_unique<views::View>(); |
| toolbar_container_view |
| ->SetLayoutManager(std::make_unique<views::FlexLayout>()) |
| ->SetOrientation(views::LayoutOrientation::kHorizontal) |
| .SetCrossAxisAlignment(views::LayoutAlignment::kCenter) |
| .SetDefault(views::kFlexBehaviorKey, |
| views::FlexSpecification( |
| views::LayoutOrientation::kHorizontal, |
| views::MinimumFlexSizeRule::kPreferredSnapToZero, |
| views::MaximumFlexSizeRule::kPreferred)); |
| container_view_ = |
| widget_->SetContentsView(std::move(toolbar_container_view)); |
| |
| auto overflow_button = std::make_unique<OverflowButton>(); |
| overflow_button_ = |
| container_view_->AddChildView(std::move(overflow_button)); |
| } |
| |
| void TearDown() override { |
| widget_.reset(); |
| ChromeViewsTestBase::TearDown(); |
| } |
| }; |
| |
| TEST_F(PopOutHandlerTest, PopOutAndEndPopOut) { |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kDummyButton); |
| auto test_delegate = std::make_unique<TestDelegate>(); |
| auto test_profile = std::make_unique<TestingProfile>(); |
| |
| MockToolbarController toolbar_controller( |
| std::vector<ToolbarController::ResponsiveElementInfo>{ |
| ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo( |
| kDummyButton, 0, &vector_icons::kErrorIcon, |
| kDummyActivateView, kDummyObservedView), |
| false)}, |
| std::vector<ui::ElementIdentifier>({kDummyButton}), 1, container_view(), |
| overflow_button(), test_delegate.get(), test_profile.get()); |
| |
| overflow_button()->set_toolbar_controller(&toolbar_controller); |
| |
| ui::ElementContext context = |
| views::ElementTrackerViews::GetContextForWidget(widget()); |
| ToolbarController::PopOutHandler pop_out_controller( |
| &toolbar_controller, context, kDummyButton, kDummyObservedView); |
| |
| EXPECT_CALL(toolbar_controller, PopOut(kDummyButton)); |
| auto observed_view = std::make_unique<views::View>(); |
| observed_view->SetProperty(views::kElementIdentifierKey, kDummyObservedView); |
| views::View* view = container_view()->AddChildView(std::move(observed_view)); |
| |
| EXPECT_CALL(toolbar_controller, EndPopOut(kDummyButton)); |
| container_view()->RemoveChildViewT<views::View>(view); |
| } |
| |
| constexpr int kElementFlexOrderStart = 1; |
| |
| } // namespace |
| |
| class TestToolbarController : public ToolbarController { |
| public: |
| TestToolbarController( |
| const std::vector<ToolbarController::ResponsiveElementInfo>& |
| responsive_elements, |
| const std::vector<ui::ElementIdentifier>& elements_in_overflow_order, |
| int element_flex_order_start, |
| views::View* toolbar_container_view, |
| OverflowButton* overflow_button, |
| TestDelegate* delegate, |
| PinnedToolbarActionsModel* model) |
| : ToolbarController(responsive_elements, |
| elements_in_overflow_order, |
| element_flex_order_start, |
| toolbar_container_view, |
| overflow_button, |
| delegate, |
| model) {} |
| |
| std::u16string GetMenuText(const ToolbarController::ResponsiveElementInfo& |
| element_info) const override { |
| static const base::flat_map<ui::ElementIdentifier, std::u16string> |
| kToolbarToMenuTextMap = {{kDummyButton1, u"DummyButton1"}, |
| {kDummyButton2, u"DummyButton2"}, |
| {kDummyButton3, u"DummyButton3"}, |
| {kDummyButton4, u"DummyButton4"}}; |
| |
| return kToolbarToMenuTextMap.at( |
| std::get<ToolbarController::ElementIdInfo>(element_info.overflow_id) |
| .overflow_identifier); |
| } |
| }; |
| |
| class ToolbarControllerUnitTest : public ChromeViewsTestBase { |
| public: |
| ToolbarControllerUnitTest() = default; |
| ToolbarControllerUnitTest(const ToolbarControllerUnitTest&) = delete; |
| ToolbarControllerUnitTest& operator=(const ToolbarControllerUnitTest&) = |
| delete; |
| ~ToolbarControllerUnitTest() override = default; |
| |
| void SetUp() override { |
| ChromeViewsTestBase::SetUp(); |
| widget_ = |
| CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET); |
| widget_->Show(); |
| |
| std::unique_ptr<views::View> toolbar_container_view = |
| std::make_unique<views::View>(); |
| toolbar_container_view |
| ->SetLayoutManager(std::make_unique<views::FlexLayout>()) |
| ->SetOrientation(views::LayoutOrientation::kHorizontal) |
| .SetCrossAxisAlignment(views::LayoutAlignment::kCenter) |
| .SetDefault(views::kFlexBehaviorKey, |
| views::FlexSpecification( |
| views::LayoutOrientation::kHorizontal, |
| views::MinimumFlexSizeRule::kPreferredSnapToZero, |
| views::MaximumFlexSizeRule::kPreferred)); |
| toolbar_container_view_ = |
| widget_->SetContentsView(std::move(toolbar_container_view)); |
| |
| std::vector<ui::ElementIdentifier> element_ids = { |
| kDummyButton1, kDummyButton2, kDummyButton3}; |
| InitToolbarContainerViewWithTestButtons(element_ids); |
| |
| auto overflow_button = std::make_unique<OverflowButton>(); |
| overflow_button_ = |
| toolbar_container_view_->AddChildView(std::move(overflow_button)); |
| overflow_button_->SetVisible(false); |
| test_delegate_ = std::make_unique<TestDelegate>(); |
| testing_profile_ = std::make_unique<TestingProfile>(); |
| toolbar_controller_ = std::make_unique<TestToolbarController>( |
| std::vector<ToolbarController::ResponsiveElementInfo>{ |
| {ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo( |
| kDummyButton1, 0, &vector_icons::kErrorIcon, |
| kDummyActivateView, kDummyObservedView), |
| false), |
| ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo( |
| kDummyButton2, 0, &vector_icons::kErrorIcon, |
| kDummyActivateView, kDummyObservedView), |
| true), |
| ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo( |
| kDummyButton3, 0, &vector_icons::kErrorIcon, |
| kDummyActivateView, kDummyObservedView), |
| true)}}, |
| std::vector<ui::ElementIdentifier>( |
| {kDummyButton3, kDummyButton2, kDummyButton1}), |
| kElementFlexOrderStart, toolbar_container_view_, overflow_button_, |
| test_delegate_.get(), |
| PinnedToolbarActionsModel::Get(testing_profile_.get())); |
| overflow_button_->set_toolbar_controller(toolbar_controller_.get()); |
| event_generator_ = std::make_unique<ui::test::EventGenerator>( |
| views::GetRootWindow(widget_.get())); |
| } |
| |
| // Add test buttons with `ids` to `toolbar_container_view_`. |
| void InitToolbarContainerViewWithTestButtons( |
| std::vector<ui::ElementIdentifier> ids) { |
| for (size_t i = 0; i < ids.size(); ++i) { |
| auto button = std::make_unique<views::View>(); |
| button->SetProperty(views::kElementIdentifierKey, ids[i]); |
| button->SetPreferredSize(kButtonSize); |
| button->GetViewAccessibility().SetRole(ax::mojom::Role::kButton); |
| button->GetViewAccessibility().SetName( |
| base::StrCat({u"DummyButton", base::NumberToString16(i)})); |
| button->SetVisible(true); |
| test_buttons_.emplace_back( |
| toolbar_container_view_->AddChildView(std::move(button))); |
| } |
| } |
| |
| void TearDown() override { |
| overflow_button_->set_toolbar_controller(nullptr); |
| overflow_button_ = nullptr; |
| toolbar_container_view_ = nullptr; |
| event_generator_.reset(); |
| toolbar_controller_.reset(); |
| widget_.reset(); |
| test_delegate_.reset(); |
| testing_profile_.reset(); |
| ChromeViewsTestBase::TearDown(); |
| } |
| |
| void UpdateOverflowButtonVisibility() { |
| toolbar_controller()->overflow_button()->SetVisible( |
| toolbar_controller()->ShouldShowOverflowButton(widget()->GetSize())); |
| } |
| |
| views::Widget* widget() { return widget_.get(); } |
| ToolbarController* toolbar_controller() { return toolbar_controller_.get(); } |
| ui::test::EventGenerator* event_generator() { return event_generator_.get(); } |
| views::View* toolbar_container_view() { return toolbar_container_view_; } |
| OverflowButton* overflow_button() { return overflow_button_; } |
| const std::vector<raw_ptr<views::View, VectorExperimental>>& test_buttons() { |
| return test_buttons_; |
| } |
| const ui::SimpleMenuModel* overflow_menu() { |
| return toolbar_controller_->menu_model_for_testing(); |
| } |
| std::vector<const ToolbarController::ResponsiveElementInfo*> |
| GetOverflowedElements() { |
| return toolbar_controller()->GetOverflowedElements(); |
| } |
| const std::vector<ToolbarController::ResponsiveElementInfo>& |
| GetResponsiveElements(const ToolbarController* toolbar_controller) { |
| return toolbar_controller->responsive_elements_; |
| } |
| bool IsOverflowed(const ToolbarController::ResponsiveElementInfo& element) { |
| return toolbar_controller_->IsOverflowed(element); |
| } |
| |
| std::vector<ToolbarController::ResponsiveElementInfo> |
| GetResponsiveElementsWithOrderedActions( |
| const ToolbarController* toolbar_controller) const { |
| return toolbar_controller->GetResponsiveElementsWithOrderedActions(); |
| } |
| PinnedToolbarActionsModel* GetPinnedToolbarActionsModel() { |
| return PinnedToolbarActionsModel::Get(testing_profile_.get()); |
| } |
| |
| private: |
| std::unique_ptr<views::Widget> widget_; |
| std::unique_ptr<ToolbarController> toolbar_controller_; |
| std::unique_ptr<ui::test::EventGenerator> event_generator_; |
| raw_ptr<views::View> toolbar_container_view_; |
| raw_ptr<OverflowButton> overflow_button_; |
| std::unique_ptr<TestDelegate> test_delegate_; |
| std::unique_ptr<TestingProfile> testing_profile_; |
| |
| // Buttons being tested. |
| std::vector<raw_ptr<views::View, VectorExperimental>> test_buttons_; |
| }; |
| |
| TEST_F(ToolbarControllerUnitTest, OverflowButtonVisibility) { |
| // Initialize widget width with total width of all test buttons. |
| // Should not see overflowed buttons. |
| widget()->SetSize(gfx::Size(kButtonSize.width() * test_buttons().size(), |
| kButtonSize.height())); |
| EXPECT_EQ(GetOverflowedElements().size(), size_t(0)); |
| UpdateOverflowButtonVisibility(); |
| EXPECT_FALSE(overflow_button()->GetVisible()); |
| |
| // Shrink widget width with one button width smaller. |
| widget()->SetSize(gfx::Size(kButtonSize.width() * (test_buttons().size() - 1), |
| kButtonSize.height())); |
| EXPECT_EQ(GetOverflowedElements().size(), size_t(1)); |
| UpdateOverflowButtonVisibility(); |
| EXPECT_TRUE(overflow_button()->GetVisible()); |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, OverflowedButtonsMatchMenu) { |
| widget()->SetSize(gfx::Size(kButtonSize.width() * (test_buttons().size() - 1), |
| kButtonSize.height())); |
| UpdateOverflowButtonVisibility(); |
| EXPECT_TRUE(overflow_button()->GetVisible()); |
| |
| widget()->LayoutRootViewIfNecessary(); |
| event_generator()->MoveMouseTo( |
| overflow_button()->GetBoundsInScreen().CenterPoint()); |
| event_generator()->PressLeftButton(); |
| |
| const ui::SimpleMenuModel* menu = overflow_menu(); |
| const auto overflowed_buttons = GetOverflowedElements(); |
| |
| // Overflowed buttons should match overflow menu. |
| EXPECT_TRUE(menu); |
| const auto& responsive_elements = GetResponsiveElements(toolbar_controller()); |
| for (size_t i = 0; i < responsive_elements.size(); ++i) { |
| if (IsOverflowed(responsive_elements[i])) { |
| EXPECT_EQ(toolbar_controller()->GetMenuText(responsive_elements[i]), |
| menu->GetLabelAt(menu->GetIndexOfCommandId(i).value())); |
| } |
| } |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, RunningMenuAddsStatusIndicator) { |
| widget()->SetSize(gfx::Size(kButtonSize.width() * (test_buttons().size() - 1), |
| kButtonSize.height())); |
| UpdateOverflowButtonVisibility(); |
| EXPECT_TRUE(overflow_button()->GetVisible()); |
| |
| widget()->LayoutRootViewIfNecessary(); |
| event_generator()->MoveMouseTo( |
| overflow_button()->GetBoundsInScreen().CenterPoint()); |
| event_generator()->PressLeftButton(); |
| |
| const ui::SimpleMenuModel* menu = overflow_menu(); |
| |
| // Overflowed buttons should match overflow menu. |
| EXPECT_TRUE(menu); |
| views::SubmenuView* sub_menu = |
| toolbar_controller()->root_menu_item()->GetSubmenu(); |
| |
| for (auto* menu_item : sub_menu->GetMenuItems()) { |
| PinnedToolbarButtonStatusIndicator* status_indicator = nullptr; |
| |
| for (auto& child : menu_item->icon_view()->children()) { |
| if (views::AsViewClass<PinnedToolbarButtonStatusIndicator>(child)) { |
| status_indicator = |
| views::AsViewClass<PinnedToolbarButtonStatusIndicator>(child); |
| } |
| |
| EXPECT_TRUE(status_indicator); |
| } |
| } |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, MenuSeparator) { |
| // Set widget to be small enough to ensure all the buttons overflow. |
| widget()->SetSize(gfx::Size(1, 1)); |
| UpdateOverflowButtonVisibility(); |
| |
| // All 3 buttons overflowed. |
| EXPECT_EQ(GetOverflowedElements().size(), static_cast<size_t>(3)); |
| const auto menu = toolbar_controller()->CreateOverflowMenuModel(); |
| EXPECT_TRUE(menu); |
| |
| // There is no separator between button1 and 2 because button1 is not a menu |
| // section end. |
| // There is a separator between button2 and button3 because |
| // 1) button2 is a section end; |
| // 2) the section button2 is in is valid; |
| // 3) the section button3 is in is valid. |
| // There is no separator after button3 because there is no valid next section. |
| EXPECT_EQ(menu->GetItemCount(), static_cast<size_t>(4)); |
| EXPECT_EQ(menu->GetTypeAt(0), ui::MenuModel::ItemType::TYPE_COMMAND); |
| EXPECT_EQ(menu->GetLabelAt(0), u"DummyButton1"); |
| EXPECT_EQ(menu->GetTypeAt(1), ui::MenuModel::ItemType::TYPE_COMMAND); |
| EXPECT_EQ(menu->GetLabelAt(1), u"DummyButton2"); |
| EXPECT_EQ(menu->GetTypeAt(2), ui::MenuModel::ItemType::TYPE_SEPARATOR); |
| EXPECT_EQ(menu->GetTypeAt(3), ui::MenuModel::ItemType::TYPE_COMMAND); |
| EXPECT_EQ(menu->GetLabelAt(3), u"DummyButton3"); |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, InValidFirstSectionAddsNoLeadingSeparator) { |
| auto test_delegate = std::make_unique<TestDelegate>(); |
| std::unique_ptr<ToolbarController> test_controller = |
| std::make_unique<TestToolbarController>( |
| std::vector<ToolbarController::ResponsiveElementInfo>( |
| {ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton1, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| true), |
| ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton2, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| true), |
| ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton3, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| true)}), |
| std::vector<ui::ElementIdentifier>( |
| {kDummyButton3, kDummyButton2, kDummyButton1}), |
| kElementFlexOrderStart, toolbar_container_view(), |
| const_cast<OverflowButton*>(overflow_button()), test_delegate.get(), |
| GetPinnedToolbarActionsModel()); |
| |
| widget()->SetSize(kButtonSize); |
| UpdateOverflowButtonVisibility(); |
| EXPECT_TRUE(overflow_button()->GetVisible()); |
| |
| views::View* button1 = test_buttons()[0]; |
| views::View* button2 = test_buttons()[1]; |
| views::View* button3 = test_buttons()[2]; |
| |
| // Button2 and 3 overflowed. |
| EXPECT_TRUE(button1->GetVisible()); |
| EXPECT_FALSE(button2->GetVisible()); |
| EXPECT_FALSE(button3->GetVisible()); |
| EXPECT_EQ(GetOverflowedElements().size(), static_cast<size_t>(2)); |
| const auto menu = test_controller->CreateOverflowMenuModel(); |
| EXPECT_TRUE(menu); |
| |
| // The first section (contains Button1) is invalid. It should not add a |
| // separator before Button2. |
| EXPECT_EQ(menu->GetItemCount(), static_cast<size_t>(3)); |
| EXPECT_EQ(menu->GetTypeAt(0), ui::MenuModel::ItemType::TYPE_COMMAND); |
| EXPECT_EQ(menu->GetLabelAt(0), u"DummyButton2"); |
| EXPECT_EQ(menu->GetTypeAt(1), ui::MenuModel::ItemType::TYPE_SEPARATOR); |
| EXPECT_EQ(menu->GetTypeAt(2), ui::MenuModel::ItemType::TYPE_COMMAND); |
| EXPECT_EQ(menu->GetLabelAt(2), u"DummyButton3"); |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, InValidSectionInMiddleAddsNoExtraSeparator) { |
| auto test_delegate = std::make_unique<TestDelegate>(); |
| std::unique_ptr<ToolbarController> test_controller = |
| std::make_unique<TestToolbarController>( |
| std::vector<ToolbarController::ResponsiveElementInfo>( |
| {ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton1, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| true), |
| ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton2, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| true), |
| ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton3, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| true)}), |
| std::vector<ui::ElementIdentifier>( |
| {kDummyButton1, kDummyButton3, kDummyButton2}), |
| kElementFlexOrderStart, toolbar_container_view(), |
| const_cast<OverflowButton*>(overflow_button()), test_delegate.get(), |
| GetPinnedToolbarActionsModel()); |
| |
| widget()->SetSize(kButtonSize); |
| UpdateOverflowButtonVisibility(); |
| EXPECT_TRUE(overflow_button()->GetVisible()); |
| |
| views::View* button1 = test_buttons()[0]; |
| views::View* button2 = test_buttons()[1]; |
| views::View* button3 = test_buttons()[2]; |
| |
| // Button1 and 3 overflowed. |
| EXPECT_FALSE(button1->GetVisible()); |
| EXPECT_TRUE(button2->GetVisible()); |
| EXPECT_FALSE(button3->GetVisible()); |
| EXPECT_EQ(GetOverflowedElements().size(), static_cast<size_t>(2)); |
| const auto menu = test_controller->CreateOverflowMenuModel(); |
| EXPECT_TRUE(menu); |
| |
| // The second section (contains Button2) is invalid. It should not add a |
| // redundant separator. |
| EXPECT_EQ(menu->GetItemCount(), static_cast<size_t>(3)); |
| EXPECT_EQ(menu->GetTypeAt(0), ui::MenuModel::ItemType::TYPE_COMMAND); |
| EXPECT_EQ(menu->GetLabelAt(0), u"DummyButton1"); |
| EXPECT_EQ(menu->GetTypeAt(1), ui::MenuModel::ItemType::TYPE_SEPARATOR); |
| EXPECT_EQ(menu->GetTypeAt(2), ui::MenuModel::ItemType::TYPE_COMMAND); |
| EXPECT_EQ(menu->GetLabelAt(2), u"DummyButton3"); |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, InValidLastSectionAddsNoTrailingSeparator) { |
| auto test_delegate = std::make_unique<TestDelegate>(); |
| std::unique_ptr<ToolbarController> test_controller = |
| std::make_unique<TestToolbarController>( |
| std::vector<ToolbarController::ResponsiveElementInfo>( |
| {ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton1, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| true), |
| ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton2, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| true), |
| ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton3, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| true)}), |
| std::vector<ui::ElementIdentifier>( |
| {kDummyButton1, kDummyButton2, kDummyButton3}), |
| kElementFlexOrderStart, toolbar_container_view(), |
| const_cast<OverflowButton*>(overflow_button()), test_delegate.get(), |
| GetPinnedToolbarActionsModel()); |
| |
| widget()->SetSize(kButtonSize); |
| UpdateOverflowButtonVisibility(); |
| EXPECT_TRUE(overflow_button()->GetVisible()); |
| |
| views::View* button1 = test_buttons()[0]; |
| views::View* button2 = test_buttons()[1]; |
| views::View* button3 = test_buttons()[2]; |
| |
| // Button1 and 2 overflowed. |
| EXPECT_FALSE(button1->GetVisible()); |
| EXPECT_FALSE(button2->GetVisible()); |
| EXPECT_TRUE(button3->GetVisible()); |
| EXPECT_EQ(GetOverflowedElements().size(), static_cast<size_t>(2)); |
| const auto menu = test_controller->CreateOverflowMenuModel(); |
| EXPECT_TRUE(menu); |
| |
| // The third section (contains Button3) is invalid. It should not add a |
| // redundant trailing separator. |
| EXPECT_EQ(menu->GetItemCount(), static_cast<size_t>(3)); |
| EXPECT_EQ(menu->GetTypeAt(0), ui::MenuModel::ItemType::TYPE_COMMAND); |
| EXPECT_EQ(menu->GetLabelAt(0), u"DummyButton1"); |
| EXPECT_EQ(menu->GetTypeAt(1), ui::MenuModel::ItemType::TYPE_SEPARATOR); |
| EXPECT_EQ(menu->GetTypeAt(2), ui::MenuModel::ItemType::TYPE_COMMAND); |
| EXPECT_EQ(menu->GetLabelAt(2), u"DummyButton2"); |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, PopOutState) { |
| auto& pop_out_state = toolbar_controller()->pop_out_state_for_testing(); |
| EXPECT_FALSE(pop_out_state.at(kDummyButton1)->original_spec.has_value()); |
| EXPECT_FALSE(pop_out_state.at(kDummyButton2)->original_spec.has_value()); |
| EXPECT_FALSE(pop_out_state.at(kDummyButton3)->original_spec.has_value()); |
| EXPECT_EQ(pop_out_state.at(kDummyButton1)->responsive_spec.order(), |
| kElementFlexOrderStart); |
| EXPECT_EQ(pop_out_state.at(kDummyButton2)->responsive_spec.order(), |
| kElementFlexOrderStart + 1); |
| EXPECT_EQ(pop_out_state.at(kDummyButton3)->responsive_spec.order(), |
| kElementFlexOrderStart + 2); |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, PopOutButton) { |
| views::View* button1 = test_buttons()[0]; |
| views::View* button2 = test_buttons()[1]; |
| views::View* button3 = test_buttons()[2]; |
| |
| // Enough space to accommodate 3 buttons. |
| widget()->SetSize(gfx::Size(kButtonSize.width() * test_buttons().size(), |
| kButtonSize.height())); |
| EXPECT_TRUE(button1->GetVisible()); |
| EXPECT_TRUE(button2->GetVisible()); |
| EXPECT_TRUE(button3->GetVisible()); |
| |
| // Not enough space. Button3 is hidden. |
| widget()->SetSize(gfx::Size(kButtonSize.width() * (test_buttons().size() - 1), |
| kButtonSize.height())); |
| EXPECT_TRUE(button1->GetVisible()); |
| EXPECT_TRUE(button2->GetVisible()); |
| EXPECT_FALSE(button3->GetVisible()); |
| |
| // Pop out button3. Button2 is hidden. |
| EXPECT_TRUE(toolbar_controller()->PopOut(kDummyButton3)); |
| views::test::RunScheduledLayout(toolbar_container_view()); |
| EXPECT_TRUE(button1->GetVisible()); |
| EXPECT_FALSE(button2->GetVisible()); |
| EXPECT_TRUE(button3->GetVisible()); |
| |
| // Button3 is already popped out. |
| EXPECT_FALSE(toolbar_controller()->PopOut(kDummyButton3)); |
| |
| // End button3 pop out. Button3 is hidden again. |
| EXPECT_TRUE(toolbar_controller()->EndPopOut(kDummyButton3)); |
| views::test::RunScheduledLayout(toolbar_container_view()); |
| EXPECT_TRUE(button1->GetVisible()); |
| EXPECT_TRUE(button2->GetVisible()); |
| EXPECT_FALSE(button3->GetVisible()); |
| |
| // Button3 already ended popped out. |
| EXPECT_FALSE(toolbar_controller()->EndPopOut(kDummyButton3)); |
| |
| // kDummyButton4 does not exist. |
| EXPECT_FALSE(toolbar_controller()->PopOut(kDummyButton4)); |
| } |
| |
| // Buttons overflow in order: 3, 2, 1. |
| TEST_F(ToolbarControllerUnitTest, ButtonsOverflowRightToLeftInContainer) { |
| views::View* button1 = test_buttons()[0]; |
| views::View* button2 = test_buttons()[1]; |
| views::View* button3 = test_buttons()[2]; |
| |
| // Enough space to accommodate 3 buttons. |
| widget()->SetSize(gfx::Size(kButtonSize.width() * test_buttons().size(), |
| kButtonSize.height())); |
| EXPECT_TRUE(button1->GetVisible()); |
| EXPECT_TRUE(button2->GetVisible()); |
| EXPECT_TRUE(button3->GetVisible()); |
| |
| // Not enough space. Button3 is hidden. |
| widget()->SetSize(gfx::Size(kButtonSize.width() * (test_buttons().size() - 1), |
| kButtonSize.height())); |
| EXPECT_TRUE(button1->GetVisible()); |
| EXPECT_TRUE(button2->GetVisible()); |
| EXPECT_FALSE(button3->GetVisible()); |
| |
| // Keep resizing smaller. Button2 is hidden. |
| widget()->SetSize(gfx::Size(kButtonSize.width() * (test_buttons().size() - 2), |
| kButtonSize.height())); |
| EXPECT_TRUE(button1->GetVisible()); |
| EXPECT_FALSE(button2->GetVisible()); |
| EXPECT_FALSE(button3->GetVisible()); |
| |
| // Keep resizing smaller. Button1 is hidden. |
| widget()->SetSize(gfx::Size(1, kButtonSize.height())); |
| EXPECT_FALSE(button1->GetVisible()); |
| EXPECT_FALSE(button2->GetVisible()); |
| EXPECT_FALSE(button3->GetVisible()); |
| } |
| |
| // Buttons overflow in order: 1, 2, 3. |
| TEST_F(ToolbarControllerUnitTest, ButtonsOverflowLeftToRightInContainer) { |
| auto test_delegate = std::make_unique<TestDelegate>(); |
| std::unique_ptr<ToolbarController> dummy_controller = |
| std::make_unique<TestToolbarController>( |
| std::vector<ToolbarController::ResponsiveElementInfo>( |
| {ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton1, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| false), |
| ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton2, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| false), |
| ToolbarController::ResponsiveElementInfo( |
| ToolbarController::ElementIdInfo{kDummyButton3, 0, |
| &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| false)}), |
| std::vector<ui::ElementIdentifier>( |
| {kDummyButton1, kDummyButton2, kDummyButton3}), |
| kElementFlexOrderStart, toolbar_container_view(), |
| const_cast<OverflowButton*>(overflow_button()), test_delegate.get(), |
| GetPinnedToolbarActionsModel()); |
| |
| views::View* button1 = test_buttons()[0]; |
| views::View* button2 = test_buttons()[1]; |
| views::View* button3 = test_buttons()[2]; |
| |
| // Enough space to accommodate 3 buttons. |
| widget()->SetSize(gfx::Size(kButtonSize.width() * test_buttons().size(), |
| kButtonSize.height())); |
| EXPECT_TRUE(button1->GetVisible()); |
| EXPECT_TRUE(button2->GetVisible()); |
| EXPECT_TRUE(button3->GetVisible()); |
| |
| // Not enough space. Button1 is hidden. |
| widget()->SetSize(gfx::Size(kButtonSize.width() * (test_buttons().size() - 1), |
| kButtonSize.height())); |
| EXPECT_FALSE(button1->GetVisible()); |
| EXPECT_TRUE(button2->GetVisible()); |
| EXPECT_TRUE(button3->GetVisible()); |
| |
| // Keep resizing smaller. Button2 is hidden. |
| widget()->SetSize(gfx::Size(kButtonSize.width() * (test_buttons().size() - 2), |
| kButtonSize.height())); |
| EXPECT_FALSE(button1->GetVisible()); |
| EXPECT_FALSE(button2->GetVisible()); |
| EXPECT_TRUE(button3->GetVisible()); |
| |
| // Keep resizing smaller. Button3 is hidden. |
| widget()->SetSize(gfx::Size(1, kButtonSize.height())); |
| EXPECT_FALSE(button1->GetVisible()); |
| EXPECT_FALSE(button2->GetVisible()); |
| EXPECT_FALSE(button3->GetVisible()); |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, MenuItemUsability) { |
| views::View* button3 = test_buttons()[2]; |
| button3->SetEnabled(false); |
| |
| // Not enough space. Button3 is hidden. |
| widget()->SetSize(gfx::Size(kButtonSize.width() * (test_buttons().size() - 1), |
| kButtonSize.height())); |
| UpdateOverflowButtonVisibility(); |
| EXPECT_TRUE(overflow_button()->GetVisible()); |
| EXPECT_FALSE(button3->GetVisible()); |
| |
| widget()->LayoutRootViewIfNecessary(); |
| event_generator()->MoveMouseTo( |
| overflow_button()->GetBoundsInScreen().CenterPoint()); |
| event_generator()->PressLeftButton(); |
| |
| const ui::SimpleMenuModel* menu = overflow_menu(); |
| const auto overflowed_buttons = GetOverflowedElements(); |
| |
| EXPECT_TRUE(menu); |
| const auto& responsive_elements = GetResponsiveElements(toolbar_controller()); |
| for (size_t i = 0; i < responsive_elements.size(); ++i) { |
| if (IsOverflowed(responsive_elements[i])) { |
| EXPECT_EQ(ToolbarController::FindToolbarElementWithId( |
| toolbar_container_view(), |
| std::get<ToolbarController::ElementIdInfo>( |
| responsive_elements[i].overflow_id) |
| .overflow_identifier) |
| ->GetEnabled(), |
| menu->IsEnabledAt(menu->GetIndexOfCommandId(i).value())); |
| } |
| } |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, ResponsiveActionsAreOrdered) { |
| auto test_delegate = std::make_unique<TestDelegate>(); |
| |
| using ElementIdInfo = ToolbarController::ElementIdInfo; |
| using ResponsiveElementInfo = ToolbarController::ResponsiveElementInfo; |
| using ActionId = actions::ActionId; |
| |
| ResponsiveElementInfo element0( |
| ElementIdInfo{kDummyButton1, 0, &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| false); |
| ResponsiveElementInfo action0(test_delegate->get_action_ids()[0]); |
| ResponsiveElementInfo action1(test_delegate->get_action_ids()[1]); |
| ResponsiveElementInfo action2(test_delegate->get_action_ids()[2]); |
| |
| auto test_controller = ToolbarController( |
| std::vector<ResponsiveElementInfo>( |
| {action2, action1, action0, element0, action2, action0}), |
| std::vector<ui::ElementIdentifier>({kDummyButton1}), |
| kElementFlexOrderStart, toolbar_container_view(), |
| const_cast<OverflowButton*>(overflow_button()), test_delegate.get(), |
| GetPinnedToolbarActionsModel()); |
| |
| std::vector<ToolbarController::ResponsiveElementInfo> elements = |
| GetResponsiveElementsWithOrderedActions(&test_controller); |
| EXPECT_EQ(int(elements.size()), 6); |
| |
| // Both sections of actions are reordered |
| EXPECT_EQ(std::get<ActionId>(elements[0].overflow_id), |
| std::get<ActionId>(action0.overflow_id)); |
| EXPECT_EQ(std::get<ActionId>(elements[1].overflow_id), |
| std::get<ActionId>(action1.overflow_id)); |
| EXPECT_EQ(std::get<ActionId>(elements[2].overflow_id), |
| std::get<ActionId>(action2.overflow_id)); |
| EXPECT_EQ( |
| std::get<ElementIdInfo>(elements[3].overflow_id).overflow_identifier, |
| std::get<ElementIdInfo>(element0.overflow_id).overflow_identifier); |
| EXPECT_EQ(std::get<ActionId>(elements[4].overflow_id), |
| std::get<ActionId>(action0.overflow_id)); |
| EXPECT_EQ(std::get<ActionId>(elements[5].overflow_id), |
| std::get<ActionId>(action2.overflow_id)); |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, ResponsiveActionsRemainOrdered) { |
| using ResponsiveElementInfo = ToolbarController::ResponsiveElementInfo; |
| using ActionId = actions::ActionId; |
| |
| ResponsiveElementInfo action0(0); |
| ResponsiveElementInfo action1(1); |
| PinnedToolbarActionsModel* model = GetPinnedToolbarActionsModel(); |
| model->UpdatePinnedState(std::get<ActionId>(action0.overflow_id), true); |
| model->UpdatePinnedState(std::get<ActionId>(action1.overflow_id), true); |
| auto delegate = std::make_unique<TestDelegateFromModel>(model); |
| |
| // Create the controller with the ActionIds in the reversed order |
| // (action1, action0) with respect to delegate.PinnedActionIds(). |
| // They should be sorted in responsive_elements right after the controller |
| // is instantiated. |
| auto controller = ToolbarController( |
| std::vector<ResponsiveElementInfo>({action1, action0}), |
| std::vector<ui::ElementIdentifier>({kDummyButton1}), |
| kElementFlexOrderStart, toolbar_container_view(), |
| const_cast<OverflowButton*>(overflow_button()), delegate.get(), model); |
| std::vector<ResponsiveElementInfo> elements = |
| GetResponsiveElements(&controller); |
| EXPECT_EQ(int(elements.size()), 2); |
| EXPECT_EQ(std::get<ActionId>(elements[0].overflow_id), |
| std::get<ActionId>(action0.overflow_id)); |
| EXPECT_EQ(std::get<ActionId>(elements[1].overflow_id), |
| std::get<ActionId>(action1.overflow_id)); |
| |
| // Move action1 to the first index. responsive_elements should be reordered. |
| model->MovePinnedAction(std::get<ActionId>(action1.overflow_id), 0); |
| elements = GetResponsiveElements(&controller); |
| EXPECT_EQ(int(elements.size()), 2); |
| EXPECT_EQ(std::get<ActionId>(elements[0].overflow_id), |
| std::get<ActionId>(action1.overflow_id)); |
| EXPECT_EQ(std::get<ActionId>(elements[1].overflow_id), |
| std::get<ActionId>(action0.overflow_id)); |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, ResponsiveActionsAreNotOrdered) { |
| auto test_delegate = std::make_unique<TestDelegate>(); |
| |
| using ElementIdInfo = ToolbarController::ElementIdInfo; |
| using ResponsiveElementInfo = ToolbarController::ResponsiveElementInfo; |
| using ActionId = actions::ActionId; |
| |
| ResponsiveElementInfo element0( |
| ElementIdInfo{kDummyButton1, 0, &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| false); |
| ResponsiveElementInfo element1( |
| ElementIdInfo{kDummyButton2, 0, &vector_icons::kErrorIcon, |
| kDummyActivateView}, |
| false); |
| ResponsiveElementInfo action0(test_delegate->get_action_ids()[0]); |
| ResponsiveElementInfo action1(test_delegate->get_action_ids()[1]); |
| ResponsiveElementInfo action2(test_delegate->get_action_ids()[2]); |
| |
| auto test_controller = ToolbarController( |
| std::vector<ResponsiveElementInfo>( |
| {element1, element0, action2, element0, action0, element0, action1}), |
| std::vector<ui::ElementIdentifier>({kDummyButton1, kDummyButton2}), |
| kElementFlexOrderStart, toolbar_container_view(), |
| const_cast<OverflowButton*>(overflow_button()), test_delegate.get(), |
| GetPinnedToolbarActionsModel()); |
| |
| std::vector<ToolbarController::ResponsiveElementInfo> elements = |
| GetResponsiveElementsWithOrderedActions(&test_controller); |
| EXPECT_EQ(int(elements.size()), 7); |
| |
| // Only sections of actions are reordered, so we |
| // expect the order not to change |
| EXPECT_EQ( |
| std::get<ElementIdInfo>(elements[0].overflow_id).overflow_identifier, |
| std::get<ElementIdInfo>(element1.overflow_id).overflow_identifier); |
| EXPECT_EQ( |
| std::get<ElementIdInfo>(elements[1].overflow_id).overflow_identifier, |
| std::get<ElementIdInfo>(element0.overflow_id).overflow_identifier); |
| EXPECT_EQ(std::get<ActionId>(elements[2].overflow_id), |
| std::get<ActionId>(action2.overflow_id)); |
| EXPECT_EQ( |
| std::get<ElementIdInfo>(elements[3].overflow_id).overflow_identifier, |
| std::get<ElementIdInfo>(element0.overflow_id).overflow_identifier); |
| EXPECT_EQ(std::get<ActionId>(elements[4].overflow_id), |
| std::get<ActionId>(action0.overflow_id)); |
| EXPECT_EQ( |
| std::get<ElementIdInfo>(elements[5].overflow_id).overflow_identifier, |
| std::get<ElementIdInfo>(element0.overflow_id).overflow_identifier); |
| EXPECT_EQ(std::get<ActionId>(elements[6].overflow_id), |
| std::get<ActionId>(action1.overflow_id)); |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, SupportActionIds) { |
| auto test_delegate = std::make_unique<TestDelegate>(); |
| auto test_controller = std::make_unique<ToolbarController>( |
| std::vector<ToolbarController::ResponsiveElementInfo>( |
| {ToolbarController::ResponsiveElementInfo( |
| test_delegate->get_action_ids()[0]), |
| ToolbarController::ResponsiveElementInfo( |
| test_delegate->get_action_ids()[1]), |
| ToolbarController::ResponsiveElementInfo( |
| test_delegate->get_action_ids()[2])}), |
| std::vector<ui::ElementIdentifier>(), kElementFlexOrderStart, |
| toolbar_container_view(), const_cast<OverflowButton*>(overflow_button()), |
| test_delegate.get(), GetPinnedToolbarActionsModel()); |
| test_delegate->SetContainerView( |
| toolbar_container_view()->AddChildView(std::make_unique<views::View>())); |
| |
| toolbar_controller()->overflow_button()->SetVisible( |
| test_controller->ShouldShowOverflowButton(widget()->GetSize())); |
| EXPECT_TRUE(overflow_button()->GetVisible()); |
| |
| const auto menu = test_controller->CreateOverflowMenuModel(); |
| EXPECT_TRUE(menu); |
| EXPECT_EQ(menu->GetItemCount(), |
| static_cast<size_t>(test_delegate->get_overflowed_count())); |
| |
| // Overflowed actions should match overflow menu. |
| const auto& responsive_elements = |
| GetResponsiveElements(test_controller.get()); |
| for (size_t i = 0; i < responsive_elements.size(); ++i) { |
| if (IsOverflowed(responsive_elements[i])) { |
| size_t index = menu->GetIndexOfCommandId(i).value(); |
| EXPECT_EQ(test_controller->GetMenuText(responsive_elements[i]), |
| menu->GetLabelAt(index)); |
| EXPECT_CALL(*test_delegate, DummyAction); |
| menu->ActivatedAt(index); |
| } |
| } |
| } |
| |
| TEST_F(ToolbarControllerUnitTest, StatusIndicatorVisibilityUpdates) { |
| auto test_delegate = std::make_unique<TestDelegate>(); |
| auto test_controller = std::make_unique<ToolbarController>( |
| std::vector<ToolbarController::ResponsiveElementInfo>( |
| {ToolbarController::ResponsiveElementInfo( |
| test_delegate->get_action_ids()[0]), |
| ToolbarController::ResponsiveElementInfo( |
| test_delegate->get_action_ids()[1]), |
| ToolbarController::ResponsiveElementInfo( |
| test_delegate->get_action_ids()[2])}), |
| std::vector<ui::ElementIdentifier>(), kElementFlexOrderStart, |
| toolbar_container_view(), const_cast<OverflowButton*>(overflow_button()), |
| test_delegate.get(), GetPinnedToolbarActionsModel()); |
| test_delegate->SetContainerView( |
| toolbar_container_view()->AddChildView(std::make_unique<views::View>())); |
| |
| toolbar_controller()->overflow_button()->SetVisible( |
| test_controller->ShouldShowOverflowButton(widget()->GetSize())); |
| EXPECT_TRUE(overflow_button()->GetVisible()); |
| |
| overflow_button()->set_toolbar_controller(test_controller.get()); |
| |
| widget()->LayoutRootViewIfNecessary(); |
| overflow_button()->RunMenu(); |
| |
| const ui::SimpleMenuModel* menu = test_controller->menu_model_for_testing(); |
| |
| // Overflowed buttons should match overflow menu. |
| EXPECT_TRUE(menu); |
| views::SubmenuView* sub_menu = |
| test_controller->root_menu_item()->GetSubmenu(); |
| |
| for (auto* menu_item : sub_menu->GetMenuItems()) { |
| PinnedToolbarButtonStatusIndicator* status_indicator = |
| PinnedToolbarButtonStatusIndicator::GetStatusIndicator( |
| menu_item->icon_view()); |
| EXPECT_TRUE(status_indicator); |
| EXPECT_EQ(status_indicator->GetVisible(), true); |
| } |
| |
| const auto& responsive_elements = |
| GetResponsiveElements(test_controller.get()); |
| for (size_t i = 0; i < responsive_elements.size(); ++i) { |
| if (IsOverflowed(responsive_elements[i])) { |
| actions::ActionId element_action_id = |
| std::get<actions::ActionId>(responsive_elements[i].overflow_id); |
| test_delegate->GetActionItemFor(element_action_id) |
| ->SetProperty(kActionItemUnderlineIndicatorKey, false); |
| |
| size_t index = menu->GetIndexOfCommandId(i).value(); |
| |
| views::MenuItemView* menu_item = sub_menu->GetMenuItemAt(index); |
| PinnedToolbarButtonStatusIndicator* status_indicator = |
| PinnedToolbarButtonStatusIndicator::GetStatusIndicator( |
| menu_item->icon_view()); |
| EXPECT_EQ(status_indicator->GetVisible(), false); |
| } |
| } |
| |
| for (size_t i = 0; i < responsive_elements.size(); ++i) { |
| if (IsOverflowed(responsive_elements[i])) { |
| actions::ActionId element_action_id = |
| std::get<actions::ActionId>(responsive_elements[i].overflow_id); |
| test_delegate->GetActionItemFor(element_action_id) |
| ->SetProperty(kActionItemUnderlineIndicatorKey, true); |
| |
| size_t index = menu->GetIndexOfCommandId(i).value(); |
| |
| views::MenuItemView* menu_item = sub_menu->GetMenuItemAt(index); |
| PinnedToolbarButtonStatusIndicator* status_indicator = |
| PinnedToolbarButtonStatusIndicator::GetStatusIndicator( |
| menu_item->icon_view()); |
| EXPECT_EQ(status_indicator->GetVisible(), true); |
| } |
| } |
| |
| overflow_button()->set_toolbar_controller(nullptr); |
| } |