1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/accessibility/ax_node_data.h"
6 
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "ui/accessibility/ax_enum_util.h"
9 #include "ui/accessibility/ax_enums.mojom.h"
10 #include "ui/accessibility/ax_role_properties.h"
11 
12 namespace ui {
13 
TEST(AXNodeDataTest,GetAndSetCheckedState)14 TEST(AXNodeDataTest, GetAndSetCheckedState) {
15   AXNodeData root;
16   EXPECT_EQ(ax::mojom::CheckedState::kNone, root.GetCheckedState());
17   EXPECT_FALSE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
18 
19   root.SetCheckedState(ax::mojom::CheckedState::kMixed);
20   EXPECT_EQ(ax::mojom::CheckedState::kMixed, root.GetCheckedState());
21   EXPECT_TRUE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
22 
23   root.SetCheckedState(ax::mojom::CheckedState::kFalse);
24   EXPECT_EQ(ax::mojom::CheckedState::kFalse, root.GetCheckedState());
25   EXPECT_TRUE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
26 
27   root.SetCheckedState(ax::mojom::CheckedState::kNone);
28   EXPECT_EQ(ax::mojom::CheckedState::kNone, root.GetCheckedState());
29   EXPECT_FALSE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
30 }
31 
TEST(AXNodeDataTest,TextAttributes)32 TEST(AXNodeDataTest, TextAttributes) {
33   AXNodeData node_1;
34   node_1.AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 1.5);
35 
36   AXNodeData node_2;
37   node_2.AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 1.5);
38   EXPECT_TRUE(node_1.GetTextStyles() == node_2.GetTextStyles());
39 
40   node_2.AddIntAttribute(ax::mojom::IntAttribute::kColor, 100);
41   EXPECT_TRUE(node_1.GetTextStyles() != node_2.GetTextStyles());
42 
43   node_1.AddIntAttribute(ax::mojom::IntAttribute::kColor, 100);
44   EXPECT_TRUE(node_1.GetTextStyles() == node_2.GetTextStyles());
45 
46   node_2.RemoveIntAttribute(ax::mojom::IntAttribute::kColor);
47   EXPECT_TRUE(node_1.GetTextStyles() != node_2.GetTextStyles());
48 
49   node_2.AddIntAttribute(ax::mojom::IntAttribute::kColor, 100);
50   EXPECT_TRUE(node_1.GetTextStyles() == node_2.GetTextStyles());
51 
52   node_1.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
53                             "test font");
54   EXPECT_TRUE(node_1.GetTextStyles() != node_2.GetTextStyles());
55 
56   node_2.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
57                             "test font");
58   EXPECT_TRUE(node_1.GetTextStyles() == node_2.GetTextStyles());
59 
60   node_2.RemoveStringAttribute(ax::mojom::StringAttribute::kFontFamily);
61   EXPECT_TRUE(node_1.GetTextStyles() != node_2.GetTextStyles());
62 
63   node_2.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
64                             "test font");
65   EXPECT_TRUE(node_1.GetTextStyles() == node_2.GetTextStyles());
66 
67   node_2.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
68                             "different font");
69   EXPECT_TRUE(node_1.GetTextStyles() != node_2.GetTextStyles());
70 
71   std::string tooltip;
72   node_2.AddStringAttribute(ax::mojom::StringAttribute::kTooltip,
73                             "test tooltip");
74   EXPECT_TRUE(node_2.GetStringAttribute(ax::mojom::StringAttribute::kTooltip,
75                                         &tooltip));
76   EXPECT_EQ(tooltip, "test tooltip");
77 
78   AXNodeTextStyles node1_styles = node_1.GetTextStyles();
79   AXNodeTextStyles moved_styles = std::move(node1_styles);
80   EXPECT_TRUE(node1_styles != moved_styles);
81   EXPECT_TRUE(moved_styles == node_1.GetTextStyles());
82 }
83 
TEST(AXNodeDataTest,TestIsClickable)84 TEST(AXNodeDataTest, TestIsClickable) {
85   // Test for ax node data attribute with a custom default action verb.
86   AXNodeData data_default_action_verb;
87 
88   for (int action_verb_idx =
89            static_cast<int>(ax::mojom::DefaultActionVerb::kMinValue);
90        action_verb_idx <=
91        static_cast<int>(ax::mojom::DefaultActionVerb::kMaxValue);
92        action_verb_idx++) {
93     data_default_action_verb.SetDefaultActionVerb(
94         static_cast<ax::mojom::DefaultActionVerb>(action_verb_idx));
95     bool is_clickable = data_default_action_verb.IsClickable();
96 
97     SCOPED_TRACE(testing::Message()
98                  << "ax::mojom::DefaultActionVerb="
99                  << ToString(data_default_action_verb.GetDefaultActionVerb())
100                  << ", Actual: isClickable=" << is_clickable
101                  << ", Expected: isClickable=" << !is_clickable);
102 
103     if (data_default_action_verb.GetDefaultActionVerb() ==
104             ax::mojom::DefaultActionVerb::kClickAncestor ||
105         data_default_action_verb.GetDefaultActionVerb() ==
106             ax::mojom::DefaultActionVerb::kNone)
107       EXPECT_FALSE(is_clickable);
108     else
109       EXPECT_TRUE(is_clickable);
110   }
111 
112   // Test for iterating through all roles and validate if a role is clickable.
113   std::unordered_set<ax::mojom::Role> roles_expected_is_clickable = {
114       ax::mojom::Role::kButton,
115       ax::mojom::Role::kCheckBox,
116       ax::mojom::Role::kColorWell,
117       ax::mojom::Role::kDisclosureTriangle,
118       ax::mojom::Role::kDocBackLink,
119       ax::mojom::Role::kDocBiblioRef,
120       ax::mojom::Role::kDocGlossRef,
121       ax::mojom::Role::kDocNoteRef,
122       ax::mojom::Role::kLink,
123       ax::mojom::Role::kListBoxOption,
124       ax::mojom::Role::kMenuButton,
125       ax::mojom::Role::kMenuItem,
126       ax::mojom::Role::kMenuItemCheckBox,
127       ax::mojom::Role::kMenuItemRadio,
128       ax::mojom::Role::kMenuListOption,
129       ax::mojom::Role::kMenuListPopup,
130       ax::mojom::Role::kPdfActionableHighlight,
131       ax::mojom::Role::kPopUpButton,
132       ax::mojom::Role::kRadioButton,
133       ax::mojom::Role::kSwitch,
134       ax::mojom::Role::kTab,
135       ax::mojom::Role::kToggleButton};
136 
137   AXNodeData data;
138 
139   for (int role_idx = static_cast<int>(ax::mojom::Role::kMinValue);
140        role_idx <= static_cast<int>(ax::mojom::Role::kMaxValue); role_idx++) {
141     data.role = static_cast<ax::mojom::Role>(role_idx);
142     bool is_clickable = data.IsClickable();
143 
144     SCOPED_TRACE(testing::Message()
145                  << "ax::mojom::Role=" << ToString(data.role)
146                  << ", Actual: isClickable=" << is_clickable
147                  << ", Expected: isClickable=" << !is_clickable);
148 
149     if (roles_expected_is_clickable.find(data.role) !=
150         roles_expected_is_clickable.end())
151       EXPECT_TRUE(is_clickable);
152     else
153       EXPECT_FALSE(is_clickable);
154   }
155 }
156 
TEST(AXNodeDataTest,TestIsInvocable)157 TEST(AXNodeDataTest, TestIsInvocable) {
158   // Test for iterating through all roles and validate if a role is invocable.
159   // A role is invocable if it is clickable and supports neither expand collpase
160   // nor toggle.
161   AXNodeData data;
162   for (int role_idx = static_cast<int>(ax::mojom::Role::kMinValue);
163        role_idx <= static_cast<int>(ax::mojom::Role::kMaxValue); role_idx++) {
164     data.role = static_cast<ax::mojom::Role>(role_idx);
165     bool supports_expand_collapse = data.SupportsExpandCollapse();
166     bool supports_toggle = ui::SupportsToggle(data.role);
167     bool is_clickable = data.IsClickable();
168     bool is_invocable = data.IsInvocable();
169 
170     SCOPED_TRACE(testing::Message()
171                  << "ax::mojom::Role=" << ToString(data.role)
172                  << ", isClickable=" << is_clickable
173                  << ", supportsToggle=" << supports_toggle
174                  << ", supportsExpandCollapse=" << supports_expand_collapse
175                  << ", Actual: isInvocable=" << is_invocable
176                  << ", Expected: isInvocable=" << !is_invocable);
177 
178     if (is_clickable && !supports_toggle && !supports_expand_collapse)
179       EXPECT_TRUE(is_invocable);
180     else
181       EXPECT_FALSE(is_invocable);
182   }
183 }
184 
TEST(AXNodeDataTest,TestSupportsExpandCollapse)185 TEST(AXNodeDataTest, TestSupportsExpandCollapse) {
186   // Test for iterating through all hasPopup attributes and validate if a
187   // hasPopup attribute supports expand collapse.
188   AXNodeData data_has_popup;
189 
190   for (int has_popup_idx = static_cast<int>(ax::mojom::HasPopup::kMinValue);
191        has_popup_idx <= static_cast<int>(ax::mojom::HasPopup::kMaxValue);
192        has_popup_idx++) {
193     data_has_popup.SetHasPopup(static_cast<ax::mojom::HasPopup>(has_popup_idx));
194     bool supports_expand_collapse = data_has_popup.SupportsExpandCollapse();
195 
196     SCOPED_TRACE(testing::Message() << "ax::mojom::HasPopup="
197                                     << ToString(data_has_popup.GetHasPopup())
198                                     << ", Actual: supportsExpandCollapse="
199                                     << supports_expand_collapse
200                                     << ", Expected: supportsExpandCollapse="
201                                     << !supports_expand_collapse);
202 
203     if (data_has_popup.GetHasPopup() == ax::mojom::HasPopup::kFalse)
204       EXPECT_FALSE(supports_expand_collapse);
205     else
206       EXPECT_TRUE(supports_expand_collapse);
207   }
208 
209   // Test for iterating through all states and validate if a state supports
210   // expand collapse.
211   AXNodeData data_state;
212 
213   for (int state_idx = static_cast<int>(ax::mojom::State::kMinValue);
214        state_idx <= static_cast<int>(ax::mojom::State::kMaxValue);
215        state_idx++) {
216     ax::mojom::State state = static_cast<ax::mojom::State>(state_idx);
217 
218     // skipping kNone here because AXNodeData::AddState, RemoveState forbids
219     // kNone to be added/removed and would fail DCHECK.
220     if (state == ax::mojom::State::kNone)
221       continue;
222 
223     data_state.AddState(state);
224 
225     bool supports_expand_collapse = data_state.SupportsExpandCollapse();
226 
227     SCOPED_TRACE(testing::Message() << "ax::mojom::State=" << ToString(state)
228                                     << ", Actual: supportsExpandCollapse="
229                                     << supports_expand_collapse
230                                     << ", Expected: supportsExpandCollapse="
231                                     << !supports_expand_collapse);
232 
233     if (data_state.HasState(ax::mojom::State::kExpanded) ||
234         data_state.HasState(ax::mojom::State::kCollapsed))
235       EXPECT_TRUE(supports_expand_collapse);
236     else
237       EXPECT_FALSE(supports_expand_collapse);
238 
239     data_state.RemoveState(state);
240   }
241 
242   // Test for iterating through all roles and validate if a role supports expand
243   // collapse.
244   AXNodeData data;
245 
246   std::unordered_set<ax::mojom::Role> roles_expected_supports_expand_collapse =
247       {ax::mojom::Role::kComboBoxGrouping, ax::mojom::Role::kComboBoxMenuButton,
248        ax::mojom::Role::kDisclosureTriangle,
249        ax::mojom::Role::kTextFieldWithComboBox, ax::mojom::Role::kTreeItem};
250 
251   for (int role_idx = static_cast<int>(ax::mojom::Role::kMinValue);
252        role_idx <= static_cast<int>(ax::mojom::Role::kMaxValue); role_idx++) {
253     data.role = static_cast<ax::mojom::Role>(role_idx);
254     bool supports_expand_collapse = data.SupportsExpandCollapse();
255 
256     SCOPED_TRACE(testing::Message() << "ax::mojom::Role=" << ToString(data.role)
257                                     << ", Actual: supportsExpandCollapse="
258                                     << supports_expand_collapse
259                                     << ", Expected: supportsExpandCollapse="
260                                     << !supports_expand_collapse);
261 
262     if (roles_expected_supports_expand_collapse.find(data.role) !=
263         roles_expected_supports_expand_collapse.end())
264       EXPECT_TRUE(supports_expand_collapse);
265     else
266       EXPECT_FALSE(supports_expand_collapse);
267   }
268 }
269 
TEST(AXNodeDataTest,BitFieldsSanityCheck)270 TEST(AXNodeDataTest, BitFieldsSanityCheck) {
271   EXPECT_LT(static_cast<size_t>(ax::mojom::State::kMaxValue),
272             sizeof(AXNodeData::state) * 8);
273   EXPECT_LT(static_cast<size_t>(ax::mojom::Action::kMaxValue),
274             sizeof(AXNodeData::actions) * 8);
275 }
276 
277 }  // namespace ui
278