Chromium Code Reviews| Index: ui/views/widget/native_widget_mac_accessibility_unittest.mm |
| diff --git a/ui/views/widget/native_widget_mac_accessibility_unittest.mm b/ui/views/widget/native_widget_mac_accessibility_unittest.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..a73b68819418f80786de989db37c02703761258e |
| --- /dev/null |
| +++ b/ui/views/widget/native_widget_mac_accessibility_unittest.mm |
| @@ -0,0 +1,219 @@ |
| +// Copyright 2016 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 <memory> |
| + |
| +#import <Cocoa/Cocoa.h> |
| + |
| +#include "base/strings/sys_string_conversions.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#import "testing/gtest_mac.h" |
| +#include "ui/accessibility/ax_enums.h" |
| +#include "ui/accessibility/ax_view_state.h" |
| +#import "ui/accessibility/platform/ax_platform_node_mac.h" |
| +#include "ui/base/ime/text_input_type.h" |
| +#import "ui/gfx/mac/coordinate_conversion.h" |
| +#include "ui/views/controls/textfield/textfield.h" |
| +#include "ui/views/test/widget_test.h" |
| +#include "ui/views/widget/widget.h" |
| + |
| +// Expose some methods from AXPlatformNodeCocoa for testing purposes only. |
| +@interface AXPlatformNodeCocoa (Testing) |
| +- (NSString*)AXRole; |
| +@end |
| + |
| +namespace views { |
| + |
| +namespace { |
| + |
| +NSString* const kTestStringValue = @"Test string value"; |
| +NSString* const kTestTitle = @"Test textfield"; |
| + |
| +class FlexibleRoleTestView : public View { |
| + public: |
| + explicit FlexibleRoleTestView(ui::AXRole role) : role_(role) {} |
| + void set_role(ui::AXRole role) { role_ = role; } |
| + |
| + // View: |
| + void GetAccessibleState(ui::AXViewState* state) override { |
| + View::GetAccessibleState(state); |
| + state->role = role_; |
| + } |
| + |
| + // Add a child view and resize to fit the child. |
| + void FitBoundsToNewChild(View* view) { |
|
tapted
2016/06/17 03:32:49
nit: new methods before overrides (except SetUp/Te
Patti Lor
2016/06/17 05:57:36
Done.
|
| + View::AddChildView(view); |
| + // Fit the parent widget to the size of the child for accurate hit tests. |
| + SetBoundsRect(view->bounds()); |
| + } |
| + |
| + private: |
| + ui::AXRole role_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(FlexibleRoleTestView); |
| +}; |
| + |
| +class NativeWidgetMacAccessibilityTest : public test::WidgetTest { |
| + public: |
| + NativeWidgetMacAccessibilityTest() {} |
| + |
| + void SetUp() override { |
| + test::WidgetTest::SetUp(); |
| + widget_ = CreateTopLevelPlatformWidget(); |
| + widget_->SetBounds(gfx::Rect(50, 50, 100, 100)); |
| + widget()->Show(); |
| + } |
| + |
| + void TearDown() override { |
| + widget_->CloseNow(); |
| + test::WidgetTest::TearDown(); |
| + } |
| + |
| + id AttributeValueAtMidpoint(NSString* attribute) { |
| + // Accessibility hit tests come in Cocoa screen coordinates. |
| + NSPoint midpoint_in_screen_ = gfx::ScreenPointToNSPoint( |
| + widget_->GetWindowBoundsInScreen().CenterPoint()); |
| + id hit = |
| + [widget_->GetNativeWindow() accessibilityHitTest:midpoint_in_screen_]; |
| + id value = [hit accessibilityAttributeValue:attribute]; |
| + return value; |
| + } |
| + |
| + Textfield* AddChildTextfield(const gfx::Size& size) { |
| + Textfield* textfield = new Textfield; |
| + textfield->SetText(base::SysNSStringToUTF16(kTestStringValue)); |
| + textfield->SetAccessibleName(base::SysNSStringToUTF16(kTestTitle)); |
| + textfield->SetSize(size); |
| + widget()->GetContentsView()->AddChildView(textfield); |
| + return textfield; |
| + } |
| + |
| + Widget* widget() { return widget_; } |
| + gfx::Rect GetWidgetBounds() { return widget_->GetClientAreaBoundsInScreen(); } |
| + |
| + private: |
| + Widget* widget_ = nullptr; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacAccessibilityTest); |
| +}; |
| + |
| +} // namespace |
| + |
| +// Test for NSAccessibilityChildrenAttribute, and ensure it excludes ignored |
| +// children from the accessibility tree. |
| +TEST_F(NativeWidgetMacAccessibilityTest, ChildrenAttribute) { |
| + // Check childless views don't have accessibility children. |
| + EXPECT_EQ(0u, |
| + [AttributeValueAtMidpoint(NSAccessibilityChildrenAttribute) count]); |
| + |
| + const size_t kNumChildren = 3; |
| + for (size_t i = 0; i < kNumChildren; ++i) { |
| + // Make sure the labels won't interfere with the hit test. |
| + AddChildTextfield(gfx::Size()); |
| + } |
| + |
| + EXPECT_EQ(kNumChildren, |
| + [AttributeValueAtMidpoint(NSAccessibilityChildrenAttribute) count]); |
| + |
| + // Check ignored children don't show up in the accessibility tree. |
| + widget()->GetContentsView()->AddChildView( |
| + new FlexibleRoleTestView(ui::AX_ROLE_IGNORED)); |
| + EXPECT_EQ(kNumChildren, |
| + [AttributeValueAtMidpoint(NSAccessibilityChildrenAttribute) count]); |
| +} |
| + |
| +// Test for NSAccessibilityParentAttribute, including for a Widget with no |
| +// parent. |
| +TEST_F(NativeWidgetMacAccessibilityTest, ParentAttribute) { |
| + Textfield* child = AddChildTextfield(widget()->GetContentsView()->size()); |
| + |
| + // Views with Widget parents will have a NSWindow parent. |
| + EXPECT_NSEQ( |
| + NSAccessibilityWindowRole, |
| + [AttributeValueAtMidpoint(NSAccessibilityParentAttribute) AXRole]); |
| + |
| + // Views with non-Widget parents will have the role of the parent view. |
| + widget()->GetContentsView()->RemoveChildView(child); |
| + FlexibleRoleTestView* parent = new FlexibleRoleTestView(ui::AX_ROLE_GROUP); |
| + parent->FitBoundsToNewChild(child); |
| + widget()->GetContentsView()->AddChildView(parent); |
| + EXPECT_NSEQ( |
| + NSAccessibilityGroupRole, |
| + [AttributeValueAtMidpoint(NSAccessibilityParentAttribute) AXRole]); |
| + |
| + // Test an ignored role parent is skipped in favor of the grandparent. |
| + parent->set_role(ui::AX_ROLE_IGNORED); |
| + EXPECT_NSEQ( |
| + NSAccessibilityWindowRole, |
| + [AttributeValueAtMidpoint(NSAccessibilityParentAttribute) AXRole]); |
| +} |
| + |
| +// Test for NSAccessibilityPositionAttribute, including on Widget movement |
| +// updates. |
| +TEST_F(NativeWidgetMacAccessibilityTest, PositionAttribute) { |
| + NSValue* widget_origin = |
| + [NSValue valueWithPoint:gfx::ScreenPointToNSPoint( |
| + GetWidgetBounds().bottom_left())]; |
| + EXPECT_NSEQ(widget_origin, |
| + AttributeValueAtMidpoint(NSAccessibilityPositionAttribute)); |
| + |
| + // Check the attribute is updated when the Widget is moved. |
| + gfx::Rect new_bounds(60, 80, 100, 100); |
| + widget()->SetBounds(new_bounds); |
| + widget_origin = [NSValue |
| + valueWithPoint:gfx::ScreenPointToNSPoint(new_bounds.bottom_left())]; |
| + EXPECT_NSEQ(widget_origin, |
| + AttributeValueAtMidpoint(NSAccessibilityPositionAttribute)); |
| +} |
| + |
| +// Tests for accessibility attributes on a views::Textfield. |
| +// TODO(patricialor): Test against Cocoa-provided attributes as well to ensure |
| +// consistency between Cocoa and toolkit-views. |
| +TEST_F(NativeWidgetMacAccessibilityTest, TextfieldAccessibility) { |
| + Textfield* textfield = AddChildTextfield(GetWidgetBounds().size()); |
| + |
| + // NSAccessibilityTitleAttribute. |
| + EXPECT_NSEQ(kTestTitle, |
| + AttributeValueAtMidpoint(NSAccessibilityTitleAttribute)); |
| + |
| + // NSAccessibilityValueAttribute. |
| + EXPECT_NSEQ(kTestStringValue, |
| + AttributeValueAtMidpoint(NSAccessibilityValueAttribute)); |
| + |
| + // NSAccessibilityRoleAttribute. |
| + EXPECT_NSEQ(NSAccessibilityTextFieldRole, |
| + AttributeValueAtMidpoint(NSAccessibilityRoleAttribute)); |
| + |
| + // NSAccessibilitySubroleAttribute and |
| + // NSAccessibilityRoleDescriptionAttribute. |
| + EXPECT_NSEQ(nil, AttributeValueAtMidpoint(NSAccessibilitySubroleAttribute)); |
| + NSString* role_description = |
| + NSAccessibilityRoleDescription(NSAccessibilityTextFieldRole, nil); |
| + EXPECT_NSEQ(role_description, AttributeValueAtMidpoint( |
| + NSAccessibilityRoleDescriptionAttribute)); |
| + |
| + // Test accessibility clients can see subroles as well. |
| + textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); |
| + EXPECT_NSEQ(NSAccessibilitySecureTextFieldSubrole, |
| + AttributeValueAtMidpoint(NSAccessibilitySubroleAttribute)); |
| + role_description = NSAccessibilityRoleDescription( |
| + NSAccessibilityTextFieldRole, NSAccessibilitySecureTextFieldSubrole); |
| + EXPECT_NSEQ(role_description, AttributeValueAtMidpoint( |
| + NSAccessibilityRoleDescriptionAttribute)); |
| + |
| + // Prevent the textfield from interfering with hit tests on the widget itself. |
| + widget()->GetContentsView()->RemoveChildView(textfield); |
| + |
| + // NSAccessibilitySizeAttribute. |
| + EXPECT_EQ(GetWidgetBounds().size(), |
| + gfx::Size([AttributeValueAtMidpoint(NSAccessibilitySizeAttribute) |
| + sizeValue])); |
| + // Check the attribute is updated when the Widget is resized. |
| + gfx::Size new_size(200, 40); |
| + widget()->SetSize(new_size); |
| + EXPECT_EQ(new_size, gfx::Size([AttributeValueAtMidpoint( |
| + NSAccessibilitySizeAttribute) sizeValue])); |
| +} |
| + |
| +} // namespace views |