blob: f2e3f28a39d131889d8873a4a1a77f99f3b20495 [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 "ui/views/controls/tabbed_pane/tabbed_pane.h"
#include <memory>
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
#include "ui/views/test/test_views.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget.h"
using base::ASCIIToUTF16;
namespace views {
namespace test {
namespace {
base::string16 DefaultTabTitle() {
return ASCIIToUTF16("tab");
}
} // namespace
class TabbedPaneTest : public ViewsTestBase {
public:
TabbedPaneTest() = default;
void SetUp() override {
ViewsTestBase::SetUp();
tabbed_pane_ = std::make_unique<TabbedPane>();
tabbed_pane_->set_owned_by_client();
}
protected:
void MakeTabbedPane(TabbedPane::Orientation orientation,
TabbedPane::TabStripStyle style) {
tabbed_pane_ = std::make_unique<TabbedPane>(orientation, style);
tabbed_pane_->set_owned_by_client();
}
Tab* GetTabAt(size_t index) {
return static_cast<Tab*>(tabbed_pane_->tab_strip_->children()[index]);
}
View* GetSelectedTabContentView() {
return tabbed_pane_->GetSelectedTabContentView();
}
void SendKeyPressToSelectedTab(ui::KeyboardCode keyboard_code) {
tabbed_pane_->GetSelectedTab()->OnKeyPressed(
ui::KeyEvent(ui::ET_KEY_PRESSED, keyboard_code,
ui::UsLayoutKeyboardCodeToDomCode(keyboard_code), 0));
}
std::unique_ptr<TabbedPane> tabbed_pane_;
private:
DISALLOW_COPY_AND_ASSIGN(TabbedPaneTest);
};
// Tests tab orientation.
TEST_F(TabbedPaneTest, HorizontalOrientation) {
EXPECT_EQ(tabbed_pane_->GetOrientation(),
TabbedPane::Orientation::kHorizontal);
}
// Tests tab orientation.
TEST_F(TabbedPaneTest, VerticalOrientation) {
MakeTabbedPane(TabbedPane::Orientation::kVertical,
TabbedPane::TabStripStyle::kBorder);
EXPECT_EQ(tabbed_pane_->GetOrientation(), TabbedPane::Orientation::kVertical);
}
// Tests tab strip style.
TEST_F(TabbedPaneTest, TabStripBorderStyle) {
EXPECT_EQ(tabbed_pane_->GetStyle(), TabbedPane::TabStripStyle::kBorder);
}
// Tests tab strip style.
TEST_F(TabbedPaneTest, TabStripHighlightStyle) {
MakeTabbedPane(TabbedPane::Orientation::kVertical,
TabbedPane::TabStripStyle::kHighlight);
EXPECT_EQ(tabbed_pane_->GetStyle(), TabbedPane::TabStripStyle::kHighlight);
}
// Tests the preferred size and layout when tabs are aligned horizontally.
// TabbedPane requests a size that fits its largest tab, and disregards the
// width of horizontal tab strips for preferred size calculations. Tab titles
// are elided to fit the actual width.
TEST_F(TabbedPaneTest, SizeAndLayout) {
View* child1 = new StaticSizedView(gfx::Size(20, 10));
tabbed_pane_->AddTab(ASCIIToUTF16("tab1 with very long text"), child1);
View* child2 = new StaticSizedView(gfx::Size(5, 5));
tabbed_pane_->AddTab(ASCIIToUTF16("tab2 with very long text"), child2);
tabbed_pane_->SelectTabAt(0);
// |tabbed_pane_| width should match the largest child in horizontal mode.
EXPECT_EQ(tabbed_pane_->GetPreferredSize().width(), 20);
// |tabbed_pane_| reserves extra height for the tab strip in horizontal mode.
EXPECT_GT(tabbed_pane_->GetPreferredSize().height(), 10);
// The child views should resize to fit in larger tabbed panes.
tabbed_pane_->SetBounds(0, 0, 100, 200);
RunPendingMessages();
// |tabbed_pane_| has no border. Therefore the children should be as wide as
// the |tabbed_pane_|.
EXPECT_EQ(child1->bounds().width(), 100);
EXPECT_GT(child1->bounds().height(), 0);
// |tabbed_pane_| reserves extra height for the tab strip. Therefore the
// children's height should be smaller than the |tabbed_pane_|'s height.
EXPECT_LT(child1->bounds().height(), 200);
// If we switch to the other tab, it should get assigned the same bounds.
tabbed_pane_->SelectTabAt(1);
EXPECT_EQ(child1->bounds(), child2->bounds());
}
// Tests the preferred size and layout when tabs are aligned vertically..
TEST_F(TabbedPaneTest, SizeAndLayoutInVerticalOrientation) {
MakeTabbedPane(TabbedPane::Orientation::kVertical,
TabbedPane::TabStripStyle::kBorder);
View* child1 = new StaticSizedView(gfx::Size(20, 10));
tabbed_pane_->AddTab(ASCIIToUTF16("tab1"), child1);
View* child2 = new StaticSizedView(gfx::Size(5, 5));
tabbed_pane_->AddTab(ASCIIToUTF16("tab2"), child2);
tabbed_pane_->SelectTabAt(0);
// |tabbed_pane_| reserves extra width for the tab strip in vertical mode.
EXPECT_GT(tabbed_pane_->GetPreferredSize().width(), 20);
// |tabbed_pane_| height should match the largest child in vertical mode.
EXPECT_EQ(tabbed_pane_->GetPreferredSize().height(), 10);
// The child views should resize to fit in larger tabbed panes.
tabbed_pane_->SetBounds(0, 0, 100, 200);
RunPendingMessages();
EXPECT_GT(child1->bounds().width(), 0);
// |tabbed_pane_| reserves extra width for the tab strip. Therefore the
// children's width should be smaller than the |tabbed_pane_|'s width.
EXPECT_LT(child1->bounds().width(), 100);
// |tabbed_pane_| has no border. Therefore the children should be as high as
// the |tabbed_pane_|.
EXPECT_EQ(child1->bounds().height(), 200);
// If we switch to the other tab, it should get assigned the same bounds.
tabbed_pane_->SelectTabAt(1);
EXPECT_EQ(child1->bounds(), child2->bounds());
}
TEST_F(TabbedPaneTest, AddAndSelect) {
// Add several tabs; only the first should be selected automatically.
for (size_t i = 0; i < 3; ++i) {
View* tab = new View();
tabbed_pane_->AddTab(DefaultTabTitle(), tab);
EXPECT_EQ(i + 1, tabbed_pane_->GetTabCount());
EXPECT_EQ(0u, tabbed_pane_->GetSelectedTabIndex());
}
// Select each tab.
for (size_t i = 0; i < tabbed_pane_->GetTabCount(); ++i) {
tabbed_pane_->SelectTabAt(i);
EXPECT_EQ(i, tabbed_pane_->GetSelectedTabIndex());
}
// Add a tab at index 0, it should not be selected automatically.
View* tab0 = new View();
tabbed_pane_->AddTabAtIndex(0, ASCIIToUTF16("tab0"), tab0);
EXPECT_NE(tab0, GetSelectedTabContentView());
EXPECT_NE(0u, tabbed_pane_->GetSelectedTabIndex());
}
TEST_F(TabbedPaneTest, ArrowKeyBindings) {
// Add several tabs; only the first should be selected automatically.
for (size_t i = 0; i < 3; ++i) {
View* tab = new View();
tabbed_pane_->AddTab(DefaultTabTitle(), tab);
EXPECT_EQ(i + 1, tabbed_pane_->GetTabCount());
}
EXPECT_EQ(0u, tabbed_pane_->GetSelectedTabIndex());
// Right arrow should select tab 1:
SendKeyPressToSelectedTab(ui::VKEY_RIGHT);
EXPECT_EQ(1u, tabbed_pane_->GetSelectedTabIndex());
// Left arrow should select tab 0:
SendKeyPressToSelectedTab(ui::VKEY_LEFT);
EXPECT_EQ(0u, tabbed_pane_->GetSelectedTabIndex());
// Left arrow again should wrap to tab 2:
SendKeyPressToSelectedTab(ui::VKEY_LEFT);
EXPECT_EQ(2u, tabbed_pane_->GetSelectedTabIndex());
// Right arrow again should wrap to tab 0:
SendKeyPressToSelectedTab(ui::VKEY_RIGHT);
EXPECT_EQ(0u, tabbed_pane_->GetSelectedTabIndex());
}
// Use TabbedPane::HandleAccessibleAction() to select tabs and make sure their
// a11y information is correct.
TEST_F(TabbedPaneTest, SelectTabWithAccessibleAction) {
// Testing accessibility information requires the View to have a Widget.
Widget* widget = new Widget;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
widget->Init(params);
widget->GetContentsView()->AddChildView(tabbed_pane_.get());
widget->Show();
constexpr size_t kNumTabs = 3;
for (size_t i = 0; i < kNumTabs; ++i) {
tabbed_pane_->AddTab(DefaultTabTitle(), new View());
}
// Check the first tab is selected.
EXPECT_EQ(0u, tabbed_pane_->GetSelectedTabIndex());
// Check the a11y information for each tab.
for (size_t i = 0; i < kNumTabs; ++i) {
ui::AXNodeData data;
GetTabAt(i)->GetAccessibleNodeData(&data);
SCOPED_TRACE(testing::Message() << "Tab at index: " << i);
EXPECT_EQ(ax::mojom::Role::kTab, data.role);
EXPECT_EQ(DefaultTabTitle(),
data.GetString16Attribute(ax::mojom::StringAttribute::kName));
EXPECT_EQ(i == 0,
data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
}
ui::AXActionData action;
action.action = ax::mojom::Action::kSetSelection;
// Select the first tab.
GetTabAt(0)->HandleAccessibleAction(action);
EXPECT_EQ(0u, tabbed_pane_->GetSelectedTabIndex());
// Select the second tab.
GetTabAt(1)->HandleAccessibleAction(action);
EXPECT_EQ(1u, tabbed_pane_->GetSelectedTabIndex());
// Select the second tab again.
GetTabAt(1)->HandleAccessibleAction(action);
EXPECT_EQ(1u, tabbed_pane_->GetSelectedTabIndex());
widget->CloseNow();
}
} // namespace test
} // namespace views