[go: nahoru, domu]

blob: 368a3e3df162f38526810047b508c64dae298a92 [file] [log] [blame]
Nektarios Paisiosbe87bc42018-10-12 23:47:031// 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
Nektarios Paisios31e51882020-05-05 22:17:127#include <set>
Lei Zhang998100f2021-06-25 17:58:198#include <string>
9#include <unordered_set>
10#include <utility>
Nektarios Paisios31e51882020-05-05 22:17:1211
Jan Wilken Dörrie721926d2020-12-09 23:57:4712#include "base/containers/contains.h"
Nektarios Paisiosbe87bc42018-10-12 23:47:0313#include "testing/gtest/include/gtest/gtest.h"
Ian Preste1c32992019-12-04 19:53:1314#include "ui/accessibility/ax_enum_util.h"
Nektarios Paisiosbe87bc42018-10-12 23:47:0315#include "ui/accessibility/ax_enums.mojom.h"
Ian Preste1c32992019-12-04 19:53:1316#include "ui/accessibility/ax_role_properties.h"
Nektarios Paisiosbe87bc42018-10-12 23:47:0317
18namespace ui {
19
20TEST(AXNodeDataTest, GetAndSetCheckedState) {
21 AXNodeData root;
22 EXPECT_EQ(ax::mojom::CheckedState::kNone, root.GetCheckedState());
23 EXPECT_FALSE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
24
25 root.SetCheckedState(ax::mojom::CheckedState::kMixed);
26 EXPECT_EQ(ax::mojom::CheckedState::kMixed, root.GetCheckedState());
27 EXPECT_TRUE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
28
29 root.SetCheckedState(ax::mojom::CheckedState::kFalse);
30 EXPECT_EQ(ax::mojom::CheckedState::kFalse, root.GetCheckedState());
31 EXPECT_TRUE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
32
33 root.SetCheckedState(ax::mojom::CheckedState::kNone);
34 EXPECT_EQ(ax::mojom::CheckedState::kNone, root.GetCheckedState());
35 EXPECT_FALSE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
36}
37
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5638TEST(AXNodeDataTest, TextAttributes) {
39 AXNodeData node_1;
40 node_1.AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 1.5);
41
42 AXNodeData node_2;
43 node_2.AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 1.5);
44 EXPECT_TRUE(node_1.GetTextStyles() == node_2.GetTextStyles());
45
46 node_2.AddIntAttribute(ax::mojom::IntAttribute::kColor, 100);
47 EXPECT_TRUE(node_1.GetTextStyles() != node_2.GetTextStyles());
48
49 node_1.AddIntAttribute(ax::mojom::IntAttribute::kColor, 100);
50 EXPECT_TRUE(node_1.GetTextStyles() == node_2.GetTextStyles());
51
52 node_2.RemoveIntAttribute(ax::mojom::IntAttribute::kColor);
53 EXPECT_TRUE(node_1.GetTextStyles() != node_2.GetTextStyles());
54
55 node_2.AddIntAttribute(ax::mojom::IntAttribute::kColor, 100);
56 EXPECT_TRUE(node_1.GetTextStyles() == node_2.GetTextStyles());
57
58 node_1.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
59 "test font");
60 EXPECT_TRUE(node_1.GetTextStyles() != node_2.GetTextStyles());
61
62 node_2.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
63 "test font");
64 EXPECT_TRUE(node_1.GetTextStyles() == node_2.GetTextStyles());
65
66 node_2.RemoveStringAttribute(ax::mojom::StringAttribute::kFontFamily);
67 EXPECT_TRUE(node_1.GetTextStyles() != node_2.GetTextStyles());
68
69 node_2.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
70 "test font");
71 EXPECT_TRUE(node_1.GetTextStyles() == node_2.GetTextStyles());
72
73 node_2.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
74 "different font");
75 EXPECT_TRUE(node_1.GetTextStyles() != node_2.GetTextStyles());
76
Sara Katoe38f905a2019-10-17 01:25:0077 std::string tooltip;
78 node_2.AddStringAttribute(ax::mojom::StringAttribute::kTooltip,
79 "test tooltip");
80 EXPECT_TRUE(node_2.GetStringAttribute(ax::mojom::StringAttribute::kTooltip,
81 &tooltip));
82 EXPECT_EQ(tooltip, "test tooltip");
83
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5684 AXNodeTextStyles node1_styles = node_1.GetTextStyles();
85 AXNodeTextStyles moved_styles = std::move(node1_styles);
86 EXPECT_TRUE(node1_styles != moved_styles);
87 EXPECT_TRUE(moved_styles == node_1.GetTextStyles());
88}
89
Victor Fei4f5aa7d2020-04-09 03:15:5090TEST(AXNodeDataTest, IsButtonPressed) {
91 // A non-button element with CheckedState::kTrue should not return true for
92 // IsButtonPressed.
93 AXNodeData non_button_pressed;
94 non_button_pressed.role = ax::mojom::Role::kGenericContainer;
95 non_button_pressed.SetCheckedState(ax::mojom::CheckedState::kTrue);
96 EXPECT_FALSE(IsButton(non_button_pressed.role));
97 EXPECT_FALSE(non_button_pressed.IsButtonPressed());
98
99 // A button element with CheckedState::kTrue should return true for
100 // IsButtonPressed.
101 AXNodeData button_pressed;
102 button_pressed.role = ax::mojom::Role::kButton;
103 button_pressed.SetCheckedState(ax::mojom::CheckedState::kTrue);
104 EXPECT_TRUE(IsButton(button_pressed.role));
105 EXPECT_TRUE(button_pressed.IsButtonPressed());
106
107 button_pressed.role = ax::mojom::Role::kPopUpButton;
108 EXPECT_TRUE(IsButton(button_pressed.role));
109 EXPECT_TRUE(button_pressed.IsButtonPressed());
110
111 button_pressed.role = ax::mojom::Role::kToggleButton;
112 EXPECT_TRUE(IsButton(button_pressed.role));
113 EXPECT_TRUE(button_pressed.IsButtonPressed());
114
115 // A button element does not have CheckedState::kTrue should return false for
116 // IsButtonPressed.
117 AXNodeData button_not_pressed;
118 button_not_pressed.role = ax::mojom::Role::kButton;
119 button_not_pressed.SetCheckedState(ax::mojom::CheckedState::kNone);
120 EXPECT_TRUE(IsButton(button_not_pressed.role));
121 EXPECT_FALSE(button_not_pressed.IsButtonPressed());
122
123 button_not_pressed.role = ax::mojom::Role::kPopUpButton;
124 button_not_pressed.SetCheckedState(ax::mojom::CheckedState::kFalse);
125 EXPECT_TRUE(IsButton(button_not_pressed.role));
126 EXPECT_FALSE(button_not_pressed.IsButtonPressed());
127
128 button_not_pressed.role = ax::mojom::Role::kToggleButton;
129 button_not_pressed.SetCheckedState(ax::mojom::CheckedState::kMixed);
130 EXPECT_TRUE(IsButton(button_not_pressed.role));
131 EXPECT_FALSE(button_not_pressed.IsButtonPressed());
132}
133
134TEST(AXNodeDataTest, IsClickable) {
Ian Preste1c32992019-12-04 19:53:13135 // Test for ax node data attribute with a custom default action verb.
136 AXNodeData data_default_action_verb;
137
138 for (int action_verb_idx =
139 static_cast<int>(ax::mojom::DefaultActionVerb::kMinValue);
140 action_verb_idx <=
141 static_cast<int>(ax::mojom::DefaultActionVerb::kMaxValue);
142 action_verb_idx++) {
143 data_default_action_verb.SetDefaultActionVerb(
144 static_cast<ax::mojom::DefaultActionVerb>(action_verb_idx));
145 bool is_clickable = data_default_action_verb.IsClickable();
146
147 SCOPED_TRACE(testing::Message()
148 << "ax::mojom::DefaultActionVerb="
149 << ToString(data_default_action_verb.GetDefaultActionVerb())
150 << ", Actual: isClickable=" << is_clickable
151 << ", Expected: isClickable=" << !is_clickable);
152
153 if (data_default_action_verb.GetDefaultActionVerb() ==
154 ax::mojom::DefaultActionVerb::kClickAncestor ||
155 data_default_action_verb.GetDefaultActionVerb() ==
156 ax::mojom::DefaultActionVerb::kNone)
157 EXPECT_FALSE(is_clickable);
158 else
159 EXPECT_TRUE(is_clickable);
160 }
161
162 // Test for iterating through all roles and validate if a role is clickable.
Nektarios Paisios31e51882020-05-05 22:17:12163 std::set<ax::mojom::Role> roles_expected_is_clickable = {
Ian Preste1c32992019-12-04 19:53:13164 ax::mojom::Role::kButton,
165 ax::mojom::Role::kCheckBox,
166 ax::mojom::Role::kColorWell,
Nektarios Paisios31e51882020-05-05 22:17:12167 ax::mojom::Role::kComboBoxMenuButton,
168 ax::mojom::Role::kDate,
169 ax::mojom::Role::kDateTime,
Ian Preste1c32992019-12-04 19:53:13170 ax::mojom::Role::kDisclosureTriangle,
171 ax::mojom::Role::kDocBackLink,
172 ax::mojom::Role::kDocBiblioRef,
173 ax::mojom::Role::kDocGlossRef,
174 ax::mojom::Role::kDocNoteRef,
Nektarios Paisios31e51882020-05-05 22:17:12175 ax::mojom::Role::kImeCandidate,
176 ax::mojom::Role::kInputTime,
Ian Preste1c32992019-12-04 19:53:13177 ax::mojom::Role::kLink,
Nektarios Paisios31e51882020-05-05 22:17:12178 ax::mojom::Role::kListBox,
Ian Preste1c32992019-12-04 19:53:13179 ax::mojom::Role::kListBoxOption,
Ian Preste1c32992019-12-04 19:53:13180 ax::mojom::Role::kMenuItem,
181 ax::mojom::Role::kMenuItemCheckBox,
182 ax::mojom::Role::kMenuItemRadio,
183 ax::mojom::Role::kMenuListOption,
Ankit Kumar 🌪️4d5bf202020-01-28 05:30:47184 ax::mojom::Role::kPdfActionableHighlight,
Ian Preste1c32992019-12-04 19:53:13185 ax::mojom::Role::kPopUpButton,
Nektarios Paisios31e51882020-05-05 22:17:12186 ax::mojom::Role::kPortal,
Ian Preste1c32992019-12-04 19:53:13187 ax::mojom::Role::kRadioButton,
Nektarios Paisios31e51882020-05-05 22:17:12188 ax::mojom::Role::kSearchBox,
189 ax::mojom::Role::kSpinButton,
Ian Preste1c32992019-12-04 19:53:13190 ax::mojom::Role::kSwitch,
191 ax::mojom::Role::kTab,
Nektarios Paisios31e51882020-05-05 22:17:12192 ax::mojom::Role::kTextField,
193 ax::mojom::Role::kTextFieldWithComboBox,
Ian Preste1c32992019-12-04 19:53:13194 ax::mojom::Role::kToggleButton};
195
196 AXNodeData data;
197
198 for (int role_idx = static_cast<int>(ax::mojom::Role::kMinValue);
199 role_idx <= static_cast<int>(ax::mojom::Role::kMaxValue); role_idx++) {
200 data.role = static_cast<ax::mojom::Role>(role_idx);
201 bool is_clickable = data.IsClickable();
202
203 SCOPED_TRACE(testing::Message()
204 << "ax::mojom::Role=" << ToString(data.role)
205 << ", Actual: isClickable=" << is_clickable
206 << ", Expected: isClickable=" << !is_clickable);
207
Nektarios Paisios31e51882020-05-05 22:17:12208 EXPECT_EQ(base::Contains(roles_expected_is_clickable, data.role),
209 is_clickable);
Ian Preste1c32992019-12-04 19:53:13210 }
211}
212
Victor Fei4f5aa7d2020-04-09 03:15:50213TEST(AXNodeDataTest, IsInvocable) {
Ian Preste1c32992019-12-04 19:53:13214 // Test for iterating through all roles and validate if a role is invocable.
215 // A role is invocable if it is clickable and supports neither expand collpase
216 // nor toggle.
217 AXNodeData data;
218 for (int role_idx = static_cast<int>(ax::mojom::Role::kMinValue);
219 role_idx <= static_cast<int>(ax::mojom::Role::kMaxValue); role_idx++) {
220 data.role = static_cast<ax::mojom::Role>(role_idx);
Nektarios Paisios31e51882020-05-05 22:17:12221 bool is_activatable = data.IsActivatable();
222 const bool supports_expand_collapse = data.SupportsExpandCollapse();
223 const bool supports_toggle = ui::SupportsToggle(data.role);
224 const bool is_clickable = data.IsClickable();
225 const bool is_invocable = data.IsInvocable();
Ian Preste1c32992019-12-04 19:53:13226
227 SCOPED_TRACE(testing::Message()
228 << "ax::mojom::Role=" << ToString(data.role)
Nektarios Paisios31e51882020-05-05 22:17:12229 << ", isClickable=" << is_clickable << ", isActivatable="
230 << is_activatable << ", supportsToggle=" << supports_toggle
Ian Preste1c32992019-12-04 19:53:13231 << ", supportsExpandCollapse=" << supports_expand_collapse
232 << ", Actual: isInvocable=" << is_invocable
233 << ", Expected: isInvocable=" << !is_invocable);
234
Nektarios Paisios31e51882020-05-05 22:17:12235 if (is_clickable && !is_activatable && !supports_toggle &&
236 !supports_expand_collapse)
Ian Preste1c32992019-12-04 19:53:13237 EXPECT_TRUE(is_invocable);
238 else
239 EXPECT_FALSE(is_invocable);
240 }
241}
242
Victor Fei4f5aa7d2020-04-09 03:15:50243TEST(AXNodeDataTest, IsMenuButton) {
244 // A non button element should return false for IsMenuButton.
245 AXNodeData non_button;
246 non_button.role = ax::mojom::Role::kGenericContainer;
247 EXPECT_FALSE(IsButton(non_button.role));
248 EXPECT_FALSE(non_button.IsMenuButton());
249
250 // Only button element with HasPopup::kMenu should return true for
251 // IsMenuButton. All other ax::mojom::HasPopup types should return false.
252 AXNodeData button_with_popup;
253
254 button_with_popup.role = ax::mojom::Role::kButton;
255
256 for (int has_popup_idx = static_cast<int>(ax::mojom::HasPopup::kMinValue);
257 has_popup_idx <= static_cast<int>(ax::mojom::HasPopup::kMaxValue);
258 has_popup_idx++) {
259 button_with_popup.SetHasPopup(
260 static_cast<ax::mojom::HasPopup>(has_popup_idx));
261 bool is_menu_button = button_with_popup.IsMenuButton();
262
263 SCOPED_TRACE(testing::Message()
264 << "ax::mojom::Role=" << ToString(button_with_popup.role)
265 << ", hasPopup=" << button_with_popup.GetHasPopup()
266 << ", Actual: isMenuButton=" << is_menu_button
267 << ", Expected: isMenuButton=" << !is_menu_button);
268
269 if (IsButton(button_with_popup.role) &&
270 button_with_popup.GetHasPopup() == ax::mojom::HasPopup::kMenu)
271 EXPECT_TRUE(is_menu_button);
272 else
273 EXPECT_FALSE(is_menu_button);
274 }
275}
276
277TEST(AXNodeDataTest, SupportsExpandCollapse) {
Ian Preste1c32992019-12-04 19:53:13278 // Test for iterating through all hasPopup attributes and validate if a
279 // hasPopup attribute supports expand collapse.
280 AXNodeData data_has_popup;
281
282 for (int has_popup_idx = static_cast<int>(ax::mojom::HasPopup::kMinValue);
283 has_popup_idx <= static_cast<int>(ax::mojom::HasPopup::kMaxValue);
284 has_popup_idx++) {
285 data_has_popup.SetHasPopup(static_cast<ax::mojom::HasPopup>(has_popup_idx));
286 bool supports_expand_collapse = data_has_popup.SupportsExpandCollapse();
287
288 SCOPED_TRACE(testing::Message() << "ax::mojom::HasPopup="
289 << ToString(data_has_popup.GetHasPopup())
290 << ", Actual: supportsExpandCollapse="
291 << supports_expand_collapse
292 << ", Expected: supportsExpandCollapse="
293 << !supports_expand_collapse);
294
295 if (data_has_popup.GetHasPopup() == ax::mojom::HasPopup::kFalse)
296 EXPECT_FALSE(supports_expand_collapse);
297 else
298 EXPECT_TRUE(supports_expand_collapse);
299 }
300
301 // Test for iterating through all states and validate if a state supports
302 // expand collapse.
303 AXNodeData data_state;
304
305 for (int state_idx = static_cast<int>(ax::mojom::State::kMinValue);
306 state_idx <= static_cast<int>(ax::mojom::State::kMaxValue);
307 state_idx++) {
308 ax::mojom::State state = static_cast<ax::mojom::State>(state_idx);
309
310 // skipping kNone here because AXNodeData::AddState, RemoveState forbids
311 // kNone to be added/removed and would fail DCHECK.
312 if (state == ax::mojom::State::kNone)
313 continue;
314
315 data_state.AddState(state);
316
317 bool supports_expand_collapse = data_state.SupportsExpandCollapse();
318
319 SCOPED_TRACE(testing::Message() << "ax::mojom::State=" << ToString(state)
320 << ", Actual: supportsExpandCollapse="
321 << supports_expand_collapse
322 << ", Expected: supportsExpandCollapse="
323 << !supports_expand_collapse);
324
325 if (data_state.HasState(ax::mojom::State::kExpanded) ||
326 data_state.HasState(ax::mojom::State::kCollapsed))
327 EXPECT_TRUE(supports_expand_collapse);
328 else
329 EXPECT_FALSE(supports_expand_collapse);
330
331 data_state.RemoveState(state);
332 }
333
334 // Test for iterating through all roles and validate if a role supports expand
335 // collapse.
336 AXNodeData data;
337
338 std::unordered_set<ax::mojom::Role> roles_expected_supports_expand_collapse =
339 {ax::mojom::Role::kComboBoxGrouping, ax::mojom::Role::kComboBoxMenuButton,
340 ax::mojom::Role::kDisclosureTriangle,
341 ax::mojom::Role::kTextFieldWithComboBox, ax::mojom::Role::kTreeItem};
342
343 for (int role_idx = static_cast<int>(ax::mojom::Role::kMinValue);
344 role_idx <= static_cast<int>(ax::mojom::Role::kMaxValue); role_idx++) {
345 data.role = static_cast<ax::mojom::Role>(role_idx);
346 bool supports_expand_collapse = data.SupportsExpandCollapse();
347
348 SCOPED_TRACE(testing::Message() << "ax::mojom::Role=" << ToString(data.role)
349 << ", Actual: supportsExpandCollapse="
350 << supports_expand_collapse
351 << ", Expected: supportsExpandCollapse="
352 << !supports_expand_collapse);
353
354 if (roles_expected_supports_expand_collapse.find(data.role) !=
355 roles_expected_supports_expand_collapse.end())
356 EXPECT_TRUE(supports_expand_collapse);
357 else
358 EXPECT_FALSE(supports_expand_collapse);
359 }
360}
361
Hiroki Satofdb71f412020-02-27 01:18:37362TEST(AXNodeDataTest, BitFieldsSanityCheck) {
363 EXPECT_LT(static_cast<size_t>(ax::mojom::State::kMaxValue),
364 sizeof(AXNodeData::state) * 8);
365 EXPECT_LT(static_cast<size_t>(ax::mojom::Action::kMaxValue),
366 sizeof(AXNodeData::actions) * 8);
367}
368
Nektarios Paisiosbe87bc42018-10-12 23:47:03369} // namespace ui