Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(139)

Unified Diff: ui/views/accessibility/native_view_accessibility_unittest.cc

Issue 2119413004: a11y: Exclude children of nested keyboard accessible controls from a11y tree. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Before revert of cross-platform ignored a11y elements. Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 37435e205a1ebc3635ac2f8bc831bd657be62f4f..357377a253c888bf827988ac3255f9dc5c51abf4 100644
--- a/ui/views/accessibility/native_view_accessibility_unittest.cc
+++ b/ui/views/accessibility/native_view_accessibility_unittest.cc
@@ -9,6 +9,7 @@
#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 {
@@ -22,6 +23,20 @@ class TestButton : public Button {
TestButton() : Button(NULL) {}
};
+// Helper to add new View with specific bounds and focusability to a parent.
+View* AddNewChildWithBoundsAndFocusability(View& parent,
+ gfx::Rect bounds,
+ bool focusable) {
+ View* child = new View;
+ child->SetBounds(bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ parent.AddChildView(child);
+ if (focusable)
+ child->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY);
+ else
+ child->SetFocusBehavior(ClientView::FocusBehavior::NEVER);
+ return child;
+}
+
} // namespace
class NativeViewAccessibilityTest : public ViewsTestBase {
@@ -31,40 +46,157 @@ class NativeViewAccessibilityTest : public ViewsTestBase {
void SetUp() override {
ViewsTestBase::SetUp();
-
- widget_ = new views::Widget;
- views::Widget::InitParams params =
- CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
- params.bounds = gfx::Rect(0, 0, 200, 200);
+ widget_ = new Widget;
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
+ params.bounds = gfx::Rect(200, 50);
widget_->Init(params);
- button_ = new TestButton();
- button_->SetSize(gfx::Size(20, 20));
- button_accessibility_ = NativeViewAccessibility::Create(button_);
+ button_ = new TestButton;
+ button_accessibility_ = button_->GetNativeViewAccessibility();
- label_ = new Label();
+ label_ = new Label;
button_->AddChildView(label_);
- label_accessibility_ = NativeViewAccessibility::Create(label_);
+ label_accessibility_ = label_->GetNativeViewAccessibility();
- widget_->SetContentsView(button_);
+ // Make sure both views start off as non-leaf accessibility elements.
+ button_->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY);
+ label_->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY);
+ button_->SetSize(gfx::Size(200, 50));
+ label_->SetSize(gfx::Size(200, 50));
+
+ widget_->GetContentsView()->AddChildView(button_);
+ widget_->Show();
}
void TearDown() override {
- if (!widget_->IsClosed())
- widget_->Close();
- button_accessibility_->Destroy();
- button_accessibility_ = NULL;
- label_accessibility_->Destroy();
- label_accessibility_ = NULL;
+ // Closing the Widget deletes child Views and their NativeViewAccessibility.
+ widget_->Close();
+ button_accessibility_ = nullptr;
+ label_accessibility_ = nullptr;
ViewsTestBase::TearDown();
}
protected:
- views::Widget* widget_;
+ Widget* widget_ = nullptr;
TestButton* button_;
NativeViewAccessibility* button_accessibility_;
Label* label_;
NativeViewAccessibility* label_accessibility_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityTest);
+};
+
+// Separate test class for a complicated test case for ignored accessibility
+// elements.
+class NativeViewAccessibilityIgnoredElementsTest : public ViewsTestBase {
+ public:
+ NativeViewAccessibilityIgnoredElementsTest() {}
+ ~NativeViewAccessibilityIgnoredElementsTest() override {}
+
+ void SetUp() override {
+ ViewsTestBase::SetUp();
+ // Set up an accessibility tree like so (X should be ignored). So 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.
+ //
+ // A A
+ // / \ / | \ +++---+---+---++---+---+---+++
+ // B X1 B F E ||| C | F | 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_ = new View;
+ a_->SetSize(gfx::Size(400, 20));
+ a_->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY);
+
+ // Create Views in the left subtree from root |a_|.
+ gfx::Rect new_bounds(0, 0, a_->width() / 2, a_->height());
+ b_ = AddNewChildWithBoundsAndFocusability(*a_, new_bounds, true);
+ // |a_| has 6 leaves, so divvy up space equally between them for hit tests.
+ new_bounds = gfx::Rect(0, 0, a_->width() / 6, a_->height());
+ c_ = AddNewChildWithBoundsAndFocusability(*b_, new_bounds, true);
+ 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_->width() / 6);
+ d_ = AddNewChildWithBoundsAndFocusability(*b_, new_bounds, true);
+ new_bounds.set_x(new_bounds.x() + a_->width() / 6);
+ x2_ = AddNewChildWithBoundsAndFocusability(*b_, new_bounds, false);
+ new_bounds.set_x(new_bounds.x() + a_->width() / 6);
+
+ // Create Views in the right subtree from root |a_|.
+ new_bounds.set_size(b_->size());
+ x1_ = AddNewChildWithBoundsAndFocusability(*a_, new_bounds, false);
+ new_bounds.set_origin(gfx::Point());
+ x3_ = AddNewChildWithBoundsAndFocusability(*x1_, new_bounds, false);
+ f_ = AddNewChildWithBoundsAndFocusability(*x3_, new_bounds, true);
+ new_bounds.set_x(new_bounds.x() + a_->width() / 6);
+ x4_ = AddNewChildWithBoundsAndFocusability(*x1_, new_bounds, false);
+ new_bounds.set_x(new_bounds.x() + a_->width() / 6);
+ e_ = AddNewChildWithBoundsAndFocusability(*x1_, new_bounds, true);
+
+ // Populate NativeViewAccessibility instances for convenience.
+ a_ax_ = a_->GetNativeViewAccessibility();
+ b_ax_ = b_->GetNativeViewAccessibility();
+ c_ax_ = c_->GetNativeViewAccessibility();
+ d_ax_ = d_->GetNativeViewAccessibility();
+ e_ax_ = e_->GetNativeViewAccessibility();
+ f_ax_ = f_->GetNativeViewAccessibility();
+ x1_ax_ = x1_->GetNativeViewAccessibility();
+ x2_ax_ = x2_->GetNativeViewAccessibility();
+ x3_ax_ = x3_->GetNativeViewAccessibility();
+ x4_ax_ = x4_->GetNativeViewAccessibility();
+ x5_ax_ = x5_->GetNativeViewAccessibility();
+
+ // 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_);
+ widget_->Show();
+ }
+
+ void TearDown() override {
+ widget_->Close();
+ ViewsTestBase::TearDown();
+ }
+
+ protected:
+ // Don't use std::unique_ptr for the following Views - everything will be
+ // inside a widget, which will handle their deletion.
+ View* a_;
+ View* b_;
+ View* c_;
+ View* d_;
+ View* e_;
+ View* f_;
+ View* x1_;
+ View* x2_;
+ View* x3_;
+ View* x4_;
+ View* x5_;
+
+ // Destroyed when the View they are attached to is destroyed.
+ NativeViewAccessibility* a_ax_;
+ NativeViewAccessibility* b_ax_;
+ NativeViewAccessibility* c_ax_;
+ NativeViewAccessibility* d_ax_;
+ NativeViewAccessibility* e_ax_;
+ NativeViewAccessibility* f_ax_;
+ NativeViewAccessibility* x1_ax_;
+ NativeViewAccessibility* x2_ax_;
+ NativeViewAccessibility* x3_ax_;
+ NativeViewAccessibility* x4_ax_;
+ NativeViewAccessibility* x5_ax_;
+
+ Widget* widget_ = nullptr;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityIgnoredElementsTest);
};
TEST_F(NativeViewAccessibilityTest, RoleShouldMatch) {
@@ -85,6 +217,139 @@ TEST_F(NativeViewAccessibilityTest, LabelIsChildOfButton) {
button_accessibility_->ChildAtIndex(0));
EXPECT_EQ(button_->GetNativeViewAccessible(),
label_accessibility_->GetParent());
+
+ // Indicate the button should be a leaf node by ignoring the label.
+ label_->SetFocusBehavior(ClientView::FocusBehavior::NEVER);
+ EXPECT_EQ(button_, label_->parent());
+ EXPECT_EQ(0, button_accessibility_->GetChildCount());
+ EXPECT_EQ(nullptr, button_accessibility_->ChildAtIndex(0));
+
+ // Check this is still true even with a child inside the label.
+ View* label_child = AddNewChildWithBoundsAndFocusability(
+ *label_, label_->GetBoundsInScreen(), true);
+ EXPECT_EQ(1, button_accessibility_->GetChildCount());
+ EXPECT_EQ(label_child->GetNativeViewAccessible(),
+ button_accessibility_->ChildAtIndex(0));
+
+ label_child->SetFocusBehavior(ClientView::FocusBehavior::NEVER);
+ EXPECT_EQ(0, button_accessibility_->GetChildCount());
+ EXPECT_EQ(nullptr, button_accessibility_->ChildAtIndex(0));
+}
+
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, GetChildCount) {
+ EXPECT_EQ(3, a_ax_->GetChildCount());
+ EXPECT_EQ(2, b_ax_->GetChildCount());
+ EXPECT_EQ(0, c_ax_->GetChildCount());
+ EXPECT_EQ(0, x5_ax_->GetChildCount());
+ EXPECT_EQ(0, d_ax_->GetChildCount());
+ EXPECT_EQ(0, x2_ax_->GetChildCount());
+ EXPECT_EQ(2, x1_ax_->GetChildCount());
+ EXPECT_EQ(1, x3_ax_->GetChildCount());
+ EXPECT_EQ(0, f_ax_->GetChildCount());
+ EXPECT_EQ(0, x4_ax_->GetChildCount());
+ EXPECT_EQ(0, e_ax_->GetChildCount());
+}
+
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, ChildAtIndex) {
+ EXPECT_EQ(b_->GetNativeViewAccessible(), a_ax_->ChildAtIndex(0));
+ EXPECT_EQ(f_->GetNativeViewAccessible(), a_ax_->ChildAtIndex(1));
+ EXPECT_EQ(e_->GetNativeViewAccessible(), a_ax_->ChildAtIndex(2));
+ EXPECT_EQ(nullptr, a_ax_->ChildAtIndex(3));
+
+ EXPECT_EQ(c_->GetNativeViewAccessible(), b_ax_->ChildAtIndex(0));
+ EXPECT_EQ(d_->GetNativeViewAccessible(), b_ax_->ChildAtIndex(1));
+ EXPECT_EQ(nullptr, b_ax_->ChildAtIndex(2));
+
+ EXPECT_EQ(f_->GetNativeViewAccessible(), x1_ax_->ChildAtIndex(0));
+ EXPECT_EQ(e_->GetNativeViewAccessible(), x1_ax_->ChildAtIndex(1));
+ EXPECT_EQ(nullptr, x1_ax_->ChildAtIndex(2));
+
+ EXPECT_EQ(f_->GetNativeViewAccessible(), x3_ax_->ChildAtIndex(0));
+
+ // Node with ignored children.
+ EXPECT_EQ(nullptr, c_ax_->ChildAtIndex(0));
+ // Node with 0 children.
+ EXPECT_EQ(nullptr, d_ax_->ChildAtIndex(0));
+ // Ignored node with 0 children.
+ EXPECT_EQ(nullptr, x2_ax_->ChildAtIndex(0));
+}
+
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, GetParent) {
+#if defined(OS_MACOSX)
+ // Only Mac is set up to return the native view going up to the Widget-level.
+ EXPECT_EQ(widget_->GetNativeView(), a_ax_->GetParent());
+#else
+ EXPECT_EQ(nullptr, a_ax_->GetParent());
+#endif
+ EXPECT_EQ(a_->GetNativeViewAccessible(), b_ax_->GetParent());
+ EXPECT_EQ(b_->GetNativeViewAccessible(), c_ax_->GetParent());
+ EXPECT_EQ(c_->GetNativeViewAccessible(), x5_ax_->GetParent());
+ EXPECT_EQ(b_->GetNativeViewAccessible(), d_ax_->GetParent());
+ EXPECT_EQ(b_->GetNativeViewAccessible(), x2_ax_->GetParent());
+
+ EXPECT_EQ(a_->GetNativeViewAccessible(), x1_ax_->GetParent());
+ EXPECT_EQ(a_->GetNativeViewAccessible(), x3_ax_->GetParent());
+ EXPECT_EQ(a_->GetNativeViewAccessible(), x4_ax_->GetParent());
+ EXPECT_EQ(a_->GetNativeViewAccessible(), e_ax_->GetParent());
+ EXPECT_EQ(a_->GetNativeViewAccessible(), f_ax_->GetParent());
+}
+
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, IsLeafElement) {
+ EXPECT_FALSE(a_ax_->IsLeafElement());
+ EXPECT_FALSE(b_ax_->IsLeafElement());
+ EXPECT_TRUE(c_ax_->IsLeafElement());
+ EXPECT_TRUE(d_ax_->IsLeafElement());
+ EXPECT_TRUE(e_ax_->IsLeafElement());
+ EXPECT_TRUE(f_ax_->IsLeafElement());
+ EXPECT_FALSE(x1_ax_->IsLeafElement());
+ EXPECT_TRUE(x2_ax_->IsLeafElement());
+ EXPECT_FALSE(x3_ax_->IsLeafElement());
+ EXPECT_TRUE(x4_ax_->IsLeafElement());
+ EXPECT_TRUE(x5_ax_->IsLeafElement());
+}
+
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, HitTestSync) {
+ // Move the hit point horizontally, incrementing by the leaf View width.
+ int min_width = a_->GetBoundsInScreen().width() / 6;
+ int curr_x = a_->GetBoundsInScreen().x() + min_width / 2;
+ const int y = a_->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_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
+ EXPECT_EQ(c_->GetNativeViewAccessible(), b_ax_->HitTestSync(curr_x, y));
+ EXPECT_EQ(c_->GetNativeViewAccessible(), c_ax_->HitTestSync(curr_x, y));
+ curr_x += min_width;
+ EXPECT_EQ(b_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
+ EXPECT_EQ(d_->GetNativeViewAccessible(), b_ax_->HitTestSync(curr_x, y));
+ curr_x += min_width;
+ EXPECT_EQ(b_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
+ EXPECT_EQ(x2_->GetNativeViewAccessible(), b_ax_->HitTestSync(curr_x, y));
+ EXPECT_EQ(b_->GetNativeViewAccessible(), x2_ax_->HitTestSync(curr_x, y));
+ curr_x += min_width;
+ EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
+ EXPECT_EQ(x3_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y));
+ EXPECT_EQ(f_->GetNativeViewAccessible(), x3_ax_->HitTestSync(curr_x, y));
+ curr_x += min_width;
+ EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
+ EXPECT_EQ(x4_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y));
+ EXPECT_EQ(a_->GetNativeViewAccessible(), x4_ax_->HitTestSync(curr_x, y));
+ curr_x += min_width;
+ EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
+ EXPECT_EQ(e_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y));
+
+ // The Widget should be revealed if |e_| and |a_| are ignored.
+ a_->SetFocusBehavior(ClientView::FocusBehavior::NEVER);
+ e_->SetFocusBehavior(ClientView::FocusBehavior::NEVER);
+ EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
+ EXPECT_EQ(e_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y));
+#if defined(OS_MACOSX)
+ EXPECT_EQ(widget_->GetNativeView(), e_ax_->HitTestSync(curr_x, y));
+#else
+ EXPECT_EQ(nullptr, e_ax_->HitTestSync(curr_x, y));
+#endif
}
// Subclass of NativeViewAccessibility that destroys itself when its
« no previous file with comments | « ui/views/accessibility/native_view_accessibility_auralinux.cc ('k') | ui/views/cocoa/bridged_content_view.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698