Index: ui/views/accessibility/native_view_accessibility_unittest.cc |
diff --git a/ui/views/accessibility/native_view_accessibility_unittest.cc b/ui/views/accessibility/native_view_accessibility_unittest.cc |
index eecbff06989d23b22813e8b76bfd5eff8c9dd24e..024afaa2ab251270a11b23c7b5e9e020277a351b 100644 |
--- a/ui/views/accessibility/native_view_accessibility_unittest.cc |
+++ b/ui/views/accessibility/native_view_accessibility_unittest.cc |
@@ -14,12 +14,10 @@ |
#include "ui/views/controls/button/button.h" |
#include "ui/views/controls/label.h" |
#include "ui/views/test/views_test_base.h" |
+#include "ui/views/widget/widget.h" |
namespace views { |
namespace test { |
- |
-class NativeViewAccessibilityTest; |
- |
namespace { |
class TestButton : public Button { |
@@ -27,6 +25,18 @@ class TestButton : public Button { |
TestButton() : Button(NULL) {} |
}; |
+// Adds a View with given bounds and focusability to a parent. |
+View* AddNewChildWithBoundsAndFocusability(View* parent, |
+ const gfx::Rect& bounds, |
+ bool focusable) { |
+ View* child = new View; |
+ child->SetBoundsRect(bounds); |
+ parent->AddChildView(child); |
+ child->SetFocusBehavior(focusable ? ClientView::FocusBehavior::ACCESSIBLE_ONLY |
+ : ClientView::FocusBehavior::NEVER); |
+ return child; |
+} |
+ |
} // namespace |
class NativeViewAccessibilityTest : public ViewsTestBase { |
@@ -37,44 +47,53 @@ class NativeViewAccessibilityTest : public ViewsTestBase { |
void SetUp() override { |
ViewsTestBase::SetUp(); |
- widget_ = new views::Widget; |
- views::Widget::InitParams params = |
- CreateParams(views::Widget::InitParams::TYPE_WINDOW); |
+ widget_ = new Widget; |
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); |
params.bounds = gfx::Rect(0, 0, 200, 200); |
widget_->Init(params); |
button_ = new TestButton(); |
button_->SetSize(gfx::Size(20, 20)); |
- button_accessibility_ = NativeViewAccessibility::Create(button_); |
+ button_accessibility_ = NativeViewAccessibility::GetForView(button_); |
label_ = new Label(); |
button_->AddChildView(label_); |
- label_accessibility_ = NativeViewAccessibility::Create(label_); |
+ label_accessibility_ = NativeViewAccessibility::GetForView(label_); |
widget_->GetContentsView()->AddChildView(button_); |
widget_->Show(); |
} |
void TearDown() override { |
+ // Closing the Widget will destroy all its children and their associated |
+ // NativeViewAccessibility instances. |
if (!widget_->IsClosed()) |
widget_->Close(); |
- button_accessibility_->Destroy(); |
- button_accessibility_ = NULL; |
- label_accessibility_->Destroy(); |
- label_accessibility_ = NULL; |
+ button_accessibility_ = nullptr; |
+ label_accessibility_ = nullptr; |
ViewsTestBase::TearDown(); |
} |
protected: |
- views::Widget* widget_; |
+ Widget* widget_; |
TestButton* button_; |
NativeViewAccessibility* button_accessibility_; |
tapted
2017/02/21 06:01:12
if we convert these to unique_ptr can we still use
Patti Lor
2017/02/27 05:31:04
I think this part of the tests it's possible, but
|
Label* label_; |
NativeViewAccessibility* label_accessibility_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityTest); |
}; |
TEST_F(NativeViewAccessibilityTest, RoleShouldMatch) { |
EXPECT_EQ(ui::AX_ROLE_BUTTON, button_accessibility_->GetData().role); |
+ // Since the label is a subview of |button_|, and the button is keyboard |
+ // focusable, the label is assumed to form part of the button and not have a |
+ // role of its own. |
+ EXPECT_EQ(ui::AX_ROLE_IGNORED, label_accessibility_->GetData().role); |
+ // This will happen for all potentially keyboard-focusable Views with |
+ // non-keyboard-focusable children, so if we make the button unfocusable, the |
+ // label will be allowed to have its own role again. |
+ button_->SetFocusBehavior(View::FocusBehavior::NEVER); |
EXPECT_EQ(ui::AX_ROLE_STATIC_TEXT, label_accessibility_->GetData().role); |
} |
@@ -86,11 +105,16 @@ TEST_F(NativeViewAccessibilityTest, BoundsShouldMatch) { |
} |
TEST_F(NativeViewAccessibilityTest, LabelIsChildOfButton) { |
+ EXPECT_EQ(0, button_accessibility_->GetChildCount()); |
+ EXPECT_EQ(nullptr, button_accessibility_->ChildAtIndex(0)); |
+ EXPECT_EQ(button_->GetNativeViewAccessible(), |
+ label_accessibility_->GetParent()); |
+ |
+ // If |button_| is no longer focusable, |label_| should show up again. |
+ button_->SetFocusBehavior(View::FocusBehavior::NEVER); |
EXPECT_EQ(1, button_accessibility_->GetChildCount()); |
EXPECT_EQ(label_->GetNativeViewAccessible(), |
button_accessibility_->ChildAtIndex(0)); |
- EXPECT_EQ(button_->GetNativeViewAccessible(), |
- label_accessibility_->GetParent()); |
} |
// Verify Views with invisible ancestors have AX_STATE_INVISIBLE. |
@@ -144,7 +168,7 @@ class TestNativeViewAccessibility : public NativeViewAccessibility { |
TEST_F(NativeViewAccessibilityTest, CrashOnWidgetDestroyed) { |
std::unique_ptr<Widget> parent_widget(new Widget); |
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); |
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
params.bounds = gfx::Rect(50, 50, 650, 650); |
parent_widget->Init(params); |
@@ -225,5 +249,210 @@ TEST_F(AXViewTest, LayoutCalledInvalidateRootView) { |
} |
#endif |
+// Separate test class for a complicated test case for ignored accessibility |
+// elements which are excluded from the accessibility tree. |
+class NativeViewAccessibilityIgnoredElementsTest : public ViewsTestBase { |
+ public: |
+ NativeViewAccessibilityIgnoredElementsTest() {} |
+ |
+ void SetUp() override { |
+ ViewsTestBase::SetUp(); |
+ // Set up an accessibility tree where X-nodes should be ignored. A Views |
+ // hierarchy as shown on the left should expose the accessibility tree in |
+ // the middle, and respond to hit tests as shown on the right. This test |
+ // is here for completeness (in practice, it's unlikely a keyboard-focusable |
+ // View will be needed as a descendant of another keyboard-focusable View). |
+ // |
+ // A A |
+ // / \ / | \ +++---+---+---++---+---+---+++ |
+ // B X1 B F E ||| C | D | B || F | A | E ||| |
+ // / | \ / | \ / \ +++---+---+---++---+---+---+++ |
+ // C D X2 X3 X4 E C D |
+ // | | |
+ // X5 F |
+ // |
+ // Note: For hit tests, the parent's bounds will be divided equally amongst |
+ // its children (including ignored elements). This means all Views belonging |
+ // on the same level of the Views hierarchy will be the same size. |
+ a_root_ = new View; |
+ a_root_->SetSize(gfx::Size(400, 20)); |
+ a_root_->SetFocusBehavior(View::FocusBehavior::ACCESSIBLE_ONLY); |
+ |
+ // Create Views in the left subtree from |a_root_|. |
+ gfx::Rect new_bounds(0, 0, a_root_->width() / 2, a_root_->height()); |
+ View* b = AddNewChildWithBoundsAndFocusability(a_root_, new_bounds, true); |
+ // |a_root_| has 6 leaves, so divvy up space equally between them for hit |
+ // tests. |
+ new_bounds = gfx::Rect(0, 0, a_root_->width() / 6, a_root_->height()); |
+ View* c = AddNewChildWithBoundsAndFocusability(b, new_bounds, true); |
+ View* x5 = AddNewChildWithBoundsAndFocusability(c, new_bounds, false); |
+ // Update the |new_bounds| origin to position leaves next to each other. |
+ new_bounds.set_x(new_bounds.x() + a_root_->width() / 6); |
+ View* d = AddNewChildWithBoundsAndFocusability(b, new_bounds, true); |
+ new_bounds.set_x(new_bounds.x() + a_root_->width() / 6); |
+ View* x2 = AddNewChildWithBoundsAndFocusability(b, new_bounds, false); |
+ new_bounds.set_x(new_bounds.x() + a_root_->width() / 6); |
+ |
+ // Create Views in the right subtree from root |a_root_|. |
+ new_bounds.set_size(b->size()); |
+ View* x1 = AddNewChildWithBoundsAndFocusability(a_root_, new_bounds, false); |
+ new_bounds.set_origin(gfx::Point()); |
+ View* x3 = AddNewChildWithBoundsAndFocusability(x1, new_bounds, false); |
+ View* f = AddNewChildWithBoundsAndFocusability(x3, new_bounds, true); |
+ new_bounds.set_x(new_bounds.x() + a_root_->width() / 6); |
+ View* x4 = AddNewChildWithBoundsAndFocusability(x1, new_bounds, false); |
+ new_bounds.set_x(new_bounds.x() + a_root_->width() / 6); |
+ View* e = AddNewChildWithBoundsAndFocusability(x1, new_bounds, true); |
+ |
+ // Populate NativeViewAccessibility instances for convenience. |
+ a_root_nva_ = NativeViewAccessibility::GetForView(a_root_); |
+ b_nva_ = NativeViewAccessibility::GetForView(b); |
+ c_b_nva_ = NativeViewAccessibility::GetForView(c); |
+ d_b_nva_ = NativeViewAccessibility::GetForView(d); |
+ e_x1_nva_ = NativeViewAccessibility::GetForView(e); |
+ f_x3_x1_nva_ = NativeViewAccessibility::GetForView(f); |
+ x1_nva_ = NativeViewAccessibility::GetForView(x1); |
+ x2_b_nva_ = NativeViewAccessibility::GetForView(x2); |
+ x3_x1_nva_ = NativeViewAccessibility::GetForView(x3); |
+ x4_x1_nva_ = NativeViewAccessibility::GetForView(x4); |
+ x5_c_b_nva_ = NativeViewAccessibility::GetForView(x5); |
+ |
+ // Add everything to a new widget, needed for hit testing and parent tests. |
+ widget_ = new Widget; |
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); |
+ widget_->Init(params); |
+ widget_->GetContentsView()->AddChildView(a_root_); |
+ widget_->Show(); |
+ } |
+ |
+ void TearDown() override { |
+ widget_->Close(); |
+ ViewsTestBase::TearDown(); |
+ } |
+ |
+ protected: |
+ // Owned by |widget_|. |
+ View* a_root_; |
+ |
+ // Owned by their respective View, which are owned by |widget_|. |
+ // The following NativeViewAccessibility objects are named after their |
+ // corresponding node as shown in the diagrams in SetUp(), then a list of |
+ // their ancestors (minus the root), separated by underscores. |
+ NativeViewAccessibility* a_root_nva_; |
+ NativeViewAccessibility* b_nva_; |
+ NativeViewAccessibility* c_b_nva_; |
+ NativeViewAccessibility* d_b_nva_; |
+ NativeViewAccessibility* e_x1_nva_; |
+ NativeViewAccessibility* f_x3_x1_nva_; |
+ NativeViewAccessibility* x1_nva_; |
+ NativeViewAccessibility* x2_b_nva_; |
+ NativeViewAccessibility* x3_x1_nva_; |
+ NativeViewAccessibility* x4_x1_nva_; |
+ NativeViewAccessibility* x5_c_b_nva_; |
+ |
+ Widget* widget_ = nullptr; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityIgnoredElementsTest); |
+}; |
+ |
+// Check the number of a11y children returned accounts for ignored elements. |
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, GetChildCount) { |
+ EXPECT_EQ(3, a_root_nva_->GetChildCount()); |
+ EXPECT_EQ(2, b_nva_->GetChildCount()); |
+ // Leaf elements will return 0. |
+ EXPECT_EQ(0, c_b_nva_->GetChildCount()); |
+ EXPECT_EQ(0, x5_c_b_nva_->GetChildCount()); |
+ EXPECT_EQ(0, d_b_nva_->GetChildCount()); |
+ EXPECT_EQ(0, x2_b_nva_->GetChildCount()); |
+ EXPECT_EQ(2, x1_nva_->GetChildCount()); |
+ EXPECT_EQ(1, x3_x1_nva_->GetChildCount()); |
+ EXPECT_EQ(0, f_x3_x1_nva_->GetChildCount()); |
+ EXPECT_EQ(0, x4_x1_nva_->GetChildCount()); |
+ EXPECT_EQ(0, e_x1_nva_->GetChildCount()); |
+} |
+ |
+// Make sure the correct a11y child element is returned at its corresponding |
+// index. Unignored children with ignored parents should move up in the tree. |
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, ChildAtIndex) { |
+ EXPECT_EQ(b_nva_->GetNativeObject(), a_root_nva_->ChildAtIndex(0)); |
+ EXPECT_EQ(f_x3_x1_nva_->GetNativeObject(), a_root_nva_->ChildAtIndex(1)); |
+ EXPECT_EQ(e_x1_nva_->GetNativeObject(), a_root_nva_->ChildAtIndex(2)); |
+ EXPECT_EQ(nullptr, a_root_nva_->ChildAtIndex(3)); |
+ |
+ EXPECT_EQ(c_b_nva_->GetNativeObject(), b_nva_->ChildAtIndex(0)); |
+ EXPECT_EQ(d_b_nva_->GetNativeObject(), b_nva_->ChildAtIndex(1)); |
+ EXPECT_EQ(nullptr, b_nva_->ChildAtIndex(2)); |
+ |
+ EXPECT_EQ(f_x3_x1_nva_->GetNativeObject(), x1_nva_->ChildAtIndex(0)); |
+ EXPECT_EQ(e_x1_nva_->GetNativeObject(), x1_nva_->ChildAtIndex(1)); |
+ EXPECT_EQ(nullptr, x1_nva_->ChildAtIndex(2)); |
+ |
+ EXPECT_EQ(f_x3_x1_nva_->GetNativeObject(), x3_x1_nva_->ChildAtIndex(0)); |
+ |
+ // Node with ignored children. |
+ EXPECT_EQ(nullptr, c_b_nva_->ChildAtIndex(0)); |
+ // Node with 0 children. |
+ EXPECT_EQ(nullptr, d_b_nva_->ChildAtIndex(0)); |
+ // Ignored node with 0 children. |
+ EXPECT_EQ(nullptr, x2_b_nva_->ChildAtIndex(0)); |
+} |
+ |
+// Check the a11y parents returned correspond to their unignored children. |
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, GetParent) { |
+ EXPECT_EQ(a_root_->parent()->GetNativeViewAccessible(), |
+ a_root_nva_->GetParent()); |
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), b_nva_->GetParent()); |
+ EXPECT_EQ(b_nva_->GetNativeObject(), c_b_nva_->GetParent()); |
+ EXPECT_EQ(c_b_nva_->GetNativeObject(), x5_c_b_nva_->GetParent()); |
+ EXPECT_EQ(b_nva_->GetNativeObject(), d_b_nva_->GetParent()); |
+ EXPECT_EQ(b_nva_->GetNativeObject(), x2_b_nva_->GetParent()); |
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), x1_nva_->GetParent()); |
+ |
+ // Skips X1. |
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), x3_x1_nva_->GetParent()); |
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), x4_x1_nva_->GetParent()); |
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), e_x1_nva_->GetParent()); |
+ |
+ // Skips X3 and X1. |
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), f_x3_x1_nva_->GetParent()); |
+} |
+ |
+// Test that a11y hit tests work correctly with ignored elements. A hit on a |
+// ignored element should return its first unignored ancestor. |
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, HitTestSync) { |
+ // Move the hit point horizontally, incrementing by the leaf View width. |
+ const int min_width = a_root_->GetBoundsInScreen().width() / 6; |
+ int curr_x = a_root_->GetBoundsInScreen().x() + min_width / 2; |
+ const int y = a_root_->GetBoundsInScreen().CenterPoint().y(); |
+ |
+ // HitTestSync isn't recursive, so manually do the recursion until arriving at |
+ // the correct answer. I.e., the last NativeViewAccessible being tested for |
+ // each |curr_x|, |y| pair below would be the correct return value if |
+ // HitTestSync were recursive. |
+ EXPECT_EQ(b_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(c_b_nva_->GetNativeObject(), b_nva_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(c_b_nva_->GetNativeObject(), c_b_nva_->HitTestSync(curr_x, y)); |
+ curr_x += min_width; |
+ EXPECT_EQ(b_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(d_b_nva_->GetNativeObject(), b_nva_->HitTestSync(curr_x, y)); |
+ curr_x += min_width; |
+ EXPECT_EQ(b_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(x2_b_nva_->GetNativeObject(), b_nva_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(nullptr, x2_b_nva_->HitTestSync(curr_x, y)); |
+ curr_x += min_width; |
+ EXPECT_EQ(x1_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(x3_x1_nva_->GetNativeObject(), x1_nva_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(f_x3_x1_nva_->GetNativeObject(), |
+ x3_x1_nva_->HitTestSync(curr_x, y)); |
+ curr_x += min_width; |
+ EXPECT_EQ(x1_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(x4_x1_nva_->GetNativeObject(), x1_nva_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(nullptr, x4_x1_nva_->HitTestSync(curr_x, y)); |
+ curr_x += min_width; |
+ EXPECT_EQ(x1_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(e_x1_nva_->GetNativeObject(), x1_nva_->HitTestSync(curr_x, y)); |
+} |
+ |
} // namespace test |
} // namespace views |