Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/views/controls/tabbed_pane/tabbed_pane.h" | 5 #include "ui/views/controls/tabbed_pane/tabbed_pane.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 | 8 |
| 9 #include "base/macros.h" | 9 #include "base/macros.h" |
| 10 #include "base/message_loop/message_loop.h" | 10 #include "base/message_loop/message_loop.h" |
| 11 #include "base/strings/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 #include "ui/accessibility/ax_action_data.h" | |
| 14 #include "ui/accessibility/ax_enums.h" | |
| 13 #include "ui/events/keycodes/keyboard_code_conversion.h" | 15 #include "ui/events/keycodes/keyboard_code_conversion.h" |
| 16 #include "ui/views/accessibility/native_view_accessibility.h" | |
| 14 #include "ui/views/test/views_test_base.h" | 17 #include "ui/views/test/views_test_base.h" |
| 18 #include "ui/views/widget/widget.h" | |
| 15 | 19 |
| 16 using base::ASCIIToUTF16; | 20 using base::ASCIIToUTF16; |
| 17 | 21 |
| 18 namespace views { | 22 namespace views { |
| 19 | 23 |
|
tapted
2017/01/10 16:22:19
nit: remove blank line
Patti Lor
2017/01/11 05:51:45
Done.
| |
| 24 namespace test { | |
| 25 | |
|
tapted
2017/01/10 16:22:19
nit: remove blank line
Patti Lor
2017/01/11 05:51:45
Done.
| |
| 26 namespace { | |
| 27 | |
| 20 // A view for testing that takes a fixed preferred size upon construction. | 28 // A view for testing that takes a fixed preferred size upon construction. |
| 21 class FixedSizeView : public View { | 29 class FixedSizeView : public View { |
| 22 public: | 30 public: |
| 23 explicit FixedSizeView(const gfx::Size& size) | 31 explicit FixedSizeView(const gfx::Size& size) |
| 24 : size_(size) {} | 32 : size_(size) {} |
| 25 | 33 |
| 26 // Overridden from View: | 34 // Overridden from View: |
| 27 gfx::Size GetPreferredSize() const override { return size_; } | 35 gfx::Size GetPreferredSize() const override { return size_; } |
| 28 | 36 |
| 29 private: | 37 private: |
| 30 const gfx::Size size_; | 38 const gfx::Size size_; |
| 31 | 39 |
| 32 DISALLOW_COPY_AND_ASSIGN(FixedSizeView); | 40 DISALLOW_COPY_AND_ASSIGN(FixedSizeView); |
| 33 }; | 41 }; |
| 34 | 42 |
| 35 typedef ViewsTestBase TabbedPaneTest; | 43 base::string16 DefaultTabTitle() { |
| 44 return ASCIIToUTF16("tab"); | |
| 45 } | |
| 46 | |
| 47 } // namespace | |
| 48 | |
| 49 class TabbedPaneTest : public ViewsTestBase { | |
| 50 public: | |
| 51 TabbedPaneTest() {} | |
| 52 | |
| 53 void SetUp() override { | |
| 54 tabbed_pane_ = new TabbedPane(); | |
|
tapted
2017/01/10 16:22:19
There's a lifetime problem here -- the existing te
Patti Lor
2017/01/11 05:51:44
Done, thanks!
| |
| 55 ViewsTestBase::SetUp(); | |
| 56 } | |
| 57 | |
| 58 void TearDown() override { | |
| 59 if (tabbed_pane_) | |
| 60 tabbed_pane_ = nullptr; | |
| 61 ViewsTestBase::TearDown(); | |
| 62 } | |
| 63 | |
| 64 protected: | |
| 65 Tab* GetTabAt(int index) { | |
| 66 return static_cast<Tab*>(tabbed_pane_->tab_strip_->child_at(index)); | |
| 67 } | |
| 68 | |
| 69 View* GetSelectedTabContentView() { | |
| 70 return tabbed_pane_->GetSelectedTabContentView(); | |
| 71 } | |
| 72 | |
| 73 void SendKeyPressToSelectedTab(ui::KeyboardCode keyboard_code) { | |
| 74 tabbed_pane_->GetSelectedTab()->OnKeyPressed( | |
| 75 ui::KeyEvent(ui::ET_KEY_PRESSED, keyboard_code, | |
| 76 ui::UsLayoutKeyboardCodeToDomCode(keyboard_code), 0)); | |
| 77 } | |
| 78 | |
| 79 TabbedPane* tabbed_pane_; | |
|
tapted
2017/01/10 16:22:19
this needs to be a unique_ptr
Patti Lor
2017/01/11 05:51:45
Done.
| |
| 80 | |
| 81 private: | |
| 82 DISALLOW_COPY_AND_ASSIGN(TabbedPaneTest); | |
| 83 }; | |
| 36 | 84 |
| 37 // Tests TabbedPane::GetPreferredSize() and TabbedPane::Layout(). | 85 // Tests TabbedPane::GetPreferredSize() and TabbedPane::Layout(). |
| 38 TEST_F(TabbedPaneTest, SizeAndLayout) { | 86 TEST_F(TabbedPaneTest, SizeAndLayout) { |
| 39 std::unique_ptr<TabbedPane> tabbed_pane(new TabbedPane()); | |
| 40 View* child1 = new FixedSizeView(gfx::Size(20, 10)); | 87 View* child1 = new FixedSizeView(gfx::Size(20, 10)); |
|
tapted
2017/01/10 16:22:19
Since we're refactoring.. I think instead of `Fixe
Patti Lor
2017/01/11 05:51:44
Done.
| |
| 41 tabbed_pane->AddTab(ASCIIToUTF16("tab1"), child1); | 88 tabbed_pane_->AddTab(ASCIIToUTF16("tab1"), child1); |
| 42 View* child2 = new FixedSizeView(gfx::Size(5, 5)); | 89 View* child2 = new FixedSizeView(gfx::Size(5, 5)); |
| 43 tabbed_pane->AddTab(ASCIIToUTF16("tab2"), child2); | 90 tabbed_pane_->AddTab(ASCIIToUTF16("tab2"), child2); |
| 44 tabbed_pane->SelectTabAt(0); | 91 tabbed_pane_->SelectTabAt(0); |
| 45 | 92 |
| 46 // The |tabbed_pane| implementation of Views has no border by default. | 93 // The |tabbed_pane_| implementation of Views has no border by default. |
| 47 // Therefore it should be as wide as the widest tab. The native Windows | 94 // Therefore it should be as wide as the widest tab. The native Windows |
| 48 // tabbed pane has a border that used up extra space. Therefore the preferred | 95 // tabbed pane has a border that used up extra space. Therefore the preferred |
| 49 // width is larger than the largest child. | 96 // width is larger than the largest child. |
| 50 gfx::Size pref(tabbed_pane->GetPreferredSize()); | 97 gfx::Size pref(tabbed_pane_->GetPreferredSize()); |
| 51 EXPECT_GE(pref.width(), 20); | 98 EXPECT_GE(pref.width(), 20); |
| 52 EXPECT_GT(pref.height(), 10); | 99 EXPECT_GT(pref.height(), 10); |
| 53 | 100 |
| 54 // The bounds of our children should be smaller than the tabbed pane's bounds. | 101 // The bounds of our children should be smaller than the tabbed pane's bounds. |
| 55 tabbed_pane->SetBounds(0, 0, 100, 200); | 102 tabbed_pane_->SetBounds(0, 0, 100, 200); |
| 56 RunPendingMessages(); | 103 RunPendingMessages(); |
| 57 gfx::Rect bounds(child1->bounds()); | 104 gfx::Rect bounds(child1->bounds()); |
| 58 EXPECT_GT(bounds.width(), 0); | 105 EXPECT_GT(bounds.width(), 0); |
| 59 // The |tabbed_pane| has no border. Therefore the children should be as wide | 106 // The |tabbed_pane_| has no border. Therefore the children should be as wide |
| 60 // as the |tabbed_pane|. | 107 // as the |tabbed_pane_|. |
| 61 EXPECT_LE(bounds.width(), 100); | 108 EXPECT_LE(bounds.width(), 100); |
| 62 EXPECT_GT(bounds.height(), 0); | 109 EXPECT_GT(bounds.height(), 0); |
| 63 EXPECT_LT(bounds.height(), 200); | 110 EXPECT_LT(bounds.height(), 200); |
| 64 | 111 |
| 65 // If we switch to the other tab, it should get assigned the same bounds. | 112 // If we switch to the other tab, it should get assigned the same bounds. |
| 66 tabbed_pane->SelectTabAt(1); | 113 tabbed_pane_->SelectTabAt(1); |
| 67 EXPECT_EQ(bounds, child2->bounds()); | 114 EXPECT_EQ(bounds, child2->bounds()); |
| 68 } | 115 } |
| 69 | 116 |
| 70 TEST_F(TabbedPaneTest, AddAndSelect) { | 117 TEST_F(TabbedPaneTest, AddAndSelect) { |
| 71 std::unique_ptr<TabbedPane> tabbed_pane(new TabbedPane()); | 118 // Add several tabs; only the first should be selected automatically. |
| 72 // Add several tabs; only the first should be a selected automatically. | |
| 73 for (int i = 0; i < 3; ++i) { | 119 for (int i = 0; i < 3; ++i) { |
| 74 View* tab = new View(); | 120 View* tab = new View(); |
| 75 tabbed_pane->AddTab(ASCIIToUTF16("tab"), tab); | 121 tabbed_pane_->AddTab(DefaultTabTitle(), tab); |
| 76 EXPECT_EQ(i + 1, tabbed_pane->GetTabCount()); | 122 EXPECT_EQ(i + 1, tabbed_pane_->GetTabCount()); |
| 77 EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex()); | 123 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); |
| 78 } | 124 } |
| 79 | 125 |
| 80 // Select each tab. | 126 // Select each tab. |
| 81 for (int i = 0; i < tabbed_pane->GetTabCount(); ++i) { | 127 for (int i = 0; i < tabbed_pane_->GetTabCount(); ++i) { |
| 82 tabbed_pane->SelectTabAt(i); | 128 tabbed_pane_->SelectTabAt(i); |
| 83 EXPECT_EQ(i, tabbed_pane->GetSelectedTabIndex()); | 129 EXPECT_EQ(i, tabbed_pane_->GetSelectedTabIndex()); |
| 84 } | 130 } |
| 85 | 131 |
| 86 // Add a tab at index 0, it should not be selected automatically. | 132 // Add a tab at index 0, it should not be selected automatically. |
| 87 View* tab0 = new View(); | 133 View* tab0 = new View(); |
| 88 tabbed_pane->AddTabAtIndex(0, ASCIIToUTF16("tab0"), tab0); | 134 tabbed_pane_->AddTabAtIndex(0, ASCIIToUTF16("tab0"), tab0); |
| 89 EXPECT_NE(tab0, tabbed_pane->GetSelectedTabContentView()); | 135 EXPECT_NE(tab0, GetSelectedTabContentView()); |
| 90 EXPECT_NE(0, tabbed_pane->GetSelectedTabIndex()); | 136 EXPECT_NE(0, tabbed_pane_->GetSelectedTabIndex()); |
| 91 } | |
| 92 | |
| 93 ui::KeyEvent MakeKeyPressedEvent(ui::KeyboardCode keyboard_code, int flags) { | |
| 94 return ui::KeyEvent(ui::ET_KEY_PRESSED, keyboard_code, | |
| 95 ui::UsLayoutKeyboardCodeToDomCode(keyboard_code), flags); | |
| 96 } | 137 } |
| 97 | 138 |
| 98 TEST_F(TabbedPaneTest, ArrowKeyBindings) { | 139 TEST_F(TabbedPaneTest, ArrowKeyBindings) { |
| 99 std::unique_ptr<TabbedPane> tabbed_pane(new TabbedPane()); | 140 // Add several tabs; only the first should be selected automatically. |
| 100 // Add several tabs; only the first should be a selected automatically. | |
| 101 for (int i = 0; i < 3; ++i) { | 141 for (int i = 0; i < 3; ++i) { |
| 102 View* tab = new View(); | 142 View* tab = new View(); |
| 103 tabbed_pane->AddTab(ASCIIToUTF16("tab"), tab); | 143 tabbed_pane_->AddTab(DefaultTabTitle(), tab); |
| 104 EXPECT_EQ(i + 1, tabbed_pane->GetTabCount()); | 144 EXPECT_EQ(i + 1, tabbed_pane_->GetTabCount()); |
| 105 } | 145 } |
| 106 | 146 |
| 107 EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex()); | 147 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); |
| 108 | 148 |
| 109 // Right arrow should select tab 1: | 149 // Right arrow should select tab 1: |
| 110 tabbed_pane->GetSelectedTab()->OnKeyPressed( | 150 SendKeyPressToSelectedTab(ui::VKEY_RIGHT); |
| 111 MakeKeyPressedEvent(ui::VKEY_RIGHT, 0)); | 151 EXPECT_EQ(1, tabbed_pane_->GetSelectedTabIndex()); |
| 112 EXPECT_EQ(1, tabbed_pane->GetSelectedTabIndex()); | |
| 113 | 152 |
| 114 // Left arrow should select tab 0: | 153 // Left arrow should select tab 0: |
| 115 tabbed_pane->GetSelectedTab()->OnKeyPressed( | 154 SendKeyPressToSelectedTab(ui::VKEY_LEFT); |
| 116 MakeKeyPressedEvent(ui::VKEY_LEFT, 0)); | 155 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); |
| 117 EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex()); | |
| 118 | 156 |
| 119 // Left arrow again should wrap to tab 2: | 157 // Left arrow again should wrap to tab 2: |
| 120 tabbed_pane->GetSelectedTab()->OnKeyPressed( | 158 SendKeyPressToSelectedTab(ui::VKEY_LEFT); |
| 121 MakeKeyPressedEvent(ui::VKEY_LEFT, 0)); | 159 EXPECT_EQ(2, tabbed_pane_->GetSelectedTabIndex()); |
| 122 EXPECT_EQ(2, tabbed_pane->GetSelectedTabIndex()); | |
| 123 | 160 |
| 124 // Right arrow again should wrap to tab 0: | 161 // Right arrow again should wrap to tab 0: |
| 125 tabbed_pane->GetSelectedTab()->OnKeyPressed( | 162 SendKeyPressToSelectedTab(ui::VKEY_RIGHT); |
| 126 MakeKeyPressedEvent(ui::VKEY_RIGHT, 0)); | 163 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); |
| 127 EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex()); | |
| 128 } | 164 } |
| 129 | 165 |
| 166 // Use TabbedPane::HandleAccessibleAction() to select tabs and make sure their | |
| 167 // a11y information is correct. | |
| 168 TEST_F(TabbedPaneTest, SelectTabWithAccessibleAction) { | |
| 169 // Testing accessibility information requires the View to have a Widget. | |
| 170 Widget* widget = new Widget; | |
| 171 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); | |
| 172 widget->Init(params); | |
| 173 widget->GetContentsView()->AddChildView(tabbed_pane_); | |
| 174 widget->Show(); | |
| 175 | |
| 176 constexpr int kNumTabs = 3; | |
| 177 for (int i = 0; i < kNumTabs; ++i) { | |
| 178 tabbed_pane_->AddTab(DefaultTabTitle(), new View()); | |
| 179 } | |
| 180 // Check the first tab is selected. | |
| 181 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); | |
| 182 | |
| 183 // Check the a11y information for each tab. | |
| 184 for (int i = 0; i < kNumTabs; ++i) { | |
| 185 ui::AXNodeData data = | |
| 186 NativeViewAccessibility::Create(GetTabAt(i))->GetData(); | |
|
tapted
2017/01/10 16:22:19
ooh - i think the result of `Create` here is leake
Patti Lor
2017/01/11 05:51:45
Oops, yeah you are right - thanks for picking them
| |
| 187 SCOPED_TRACE(testing::Message() << "Tab at index: " << i); | |
| 188 EXPECT_EQ(ui::AX_ROLE_TAB, data.role); | |
| 189 EXPECT_EQ(DefaultTabTitle(), data.GetString16Attribute(ui::AX_ATTR_NAME)); | |
| 190 EXPECT_TRUE(data.HasStateFlag(ui::AX_STATE_SELECTABLE)); | |
| 191 EXPECT_EQ(i == 0, data.HasStateFlag(ui::AX_STATE_SELECTED)); | |
| 192 } | |
| 193 | |
| 194 ui::AXActionData action; | |
| 195 action.action = ui::AX_ACTION_SET_SELECTION; | |
| 196 // Select the first tab. | |
| 197 NativeViewAccessibility* nva = NativeViewAccessibility::Create(GetTabAt(0)); | |
| 198 nva->AccessibilityPerformAction(action); | |
| 199 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); | |
| 200 nva->Destroy(); | |
| 201 | |
| 202 // Select the second tab. | |
| 203 nva = NativeViewAccessibility::Create(GetTabAt(1)); | |
| 204 nva->AccessibilityPerformAction(action); | |
| 205 EXPECT_EQ(1, tabbed_pane_->GetSelectedTabIndex()); | |
| 206 // Select the second tab again. | |
| 207 nva->AccessibilityPerformAction(action); | |
| 208 EXPECT_EQ(1, tabbed_pane_->GetSelectedTabIndex()); | |
| 209 nva->Destroy(); | |
| 210 | |
| 211 widget->CloseNow(); | |
| 212 } | |
| 213 | |
| 214 } // namespace test | |
| 215 | |
|
tapted
2017/01/10 16:22:19
nit: remove blank line
Patti Lor
2017/01/11 05:51:44
Done.
| |
| 130 } // namespace views | 216 } // namespace views |
| OLD | NEW |