[go: nahoru, domu]

blob: e5a7ca10220dda3ff5eb7b7052161e9d5f4ecf03 [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 Paisios70b6a5e2021-08-03 05:32:3617#include "ui/accessibility/ax_text_attributes.h"
Nektarios Paisiosbe87bc42018-10-12 23:47:0318
19namespace ui {
20
21TEST(AXNodeDataTest, GetAndSetCheckedState) {
22 AXNodeData root;
23 EXPECT_EQ(ax::mojom::CheckedState::kNone, root.GetCheckedState());
24 EXPECT_FALSE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
25
26 root.SetCheckedState(ax::mojom::CheckedState::kMixed);
27 EXPECT_EQ(ax::mojom::CheckedState::kMixed, root.GetCheckedState());
28 EXPECT_TRUE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
29
30 root.SetCheckedState(ax::mojom::CheckedState::kFalse);
31 EXPECT_EQ(ax::mojom::CheckedState::kFalse, root.GetCheckedState());
32 EXPECT_TRUE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
33
34 root.SetCheckedState(ax::mojom::CheckedState::kNone);
35 EXPECT_EQ(ax::mojom::CheckedState::kNone, root.GetCheckedState());
36 EXPECT_FALSE(root.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState));
37}
38
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5639TEST(AXNodeDataTest, TextAttributes) {
40 AXNodeData node_1;
41 node_1.AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 1.5);
42
43 AXNodeData node_2;
44 node_2.AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 1.5);
Nektarios Paisios70b6a5e2021-08-03 05:32:3645 EXPECT_TRUE(node_1.GetTextAttributes() == node_2.GetTextAttributes());
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5646
47 node_2.AddIntAttribute(ax::mojom::IntAttribute::kColor, 100);
Nektarios Paisios70b6a5e2021-08-03 05:32:3648 EXPECT_TRUE(node_1.GetTextAttributes() != node_2.GetTextAttributes());
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5649
50 node_1.AddIntAttribute(ax::mojom::IntAttribute::kColor, 100);
Nektarios Paisios70b6a5e2021-08-03 05:32:3651 EXPECT_TRUE(node_1.GetTextAttributes() == node_2.GetTextAttributes());
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5652
53 node_2.RemoveIntAttribute(ax::mojom::IntAttribute::kColor);
Nektarios Paisios70b6a5e2021-08-03 05:32:3654 EXPECT_TRUE(node_1.GetTextAttributes() != node_2.GetTextAttributes());
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5655
56 node_2.AddIntAttribute(ax::mojom::IntAttribute::kColor, 100);
Nektarios Paisios70b6a5e2021-08-03 05:32:3657 EXPECT_TRUE(node_1.GetTextAttributes() == node_2.GetTextAttributes());
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5658
59 node_1.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
60 "test font");
Nektarios Paisios70b6a5e2021-08-03 05:32:3661 EXPECT_TRUE(node_1.GetTextAttributes() != node_2.GetTextAttributes());
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5662
63 node_2.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
64 "test font");
Nektarios Paisios70b6a5e2021-08-03 05:32:3665 EXPECT_TRUE(node_1.GetTextAttributes() == node_2.GetTextAttributes());
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5666
67 node_2.RemoveStringAttribute(ax::mojom::StringAttribute::kFontFamily);
Nektarios Paisios70b6a5e2021-08-03 05:32:3668 EXPECT_TRUE(node_1.GetTextAttributes() != node_2.GetTextAttributes());
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5669
70 node_2.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
71 "test font");
Nektarios Paisios70b6a5e2021-08-03 05:32:3672 EXPECT_TRUE(node_1.GetTextAttributes() == node_2.GetTextAttributes());
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5673
74 node_2.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
75 "different font");
Nektarios Paisios70b6a5e2021-08-03 05:32:3676 EXPECT_TRUE(node_1.GetTextAttributes() != node_2.GetTextAttributes());
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5677
Sara Katoe38f905a2019-10-17 01:25:0078 std::string tooltip;
79 node_2.AddStringAttribute(ax::mojom::StringAttribute::kTooltip,
80 "test tooltip");
81 EXPECT_TRUE(node_2.GetStringAttribute(ax::mojom::StringAttribute::kTooltip,
82 &tooltip));
83 EXPECT_EQ(tooltip, "test tooltip");
84
Nektarios Paisios70b6a5e2021-08-03 05:32:3685 AXTextAttributes node1_attributes = node_1.GetTextAttributes();
86 AXTextAttributes moved_attributes = std::move(node1_attributes);
87 EXPECT_TRUE(node1_attributes != moved_attributes);
88 EXPECT_TRUE(moved_attributes == node_1.GetTextAttributes());
Kurt Catti-Schmidtf738bce2019-05-08 01:06:5689}
90
Victor Fei4f5aa7d2020-04-09 03:15:5091TEST(AXNodeDataTest, IsButtonPressed) {
92 // A non-button element with CheckedState::kTrue should not return true for
93 // IsButtonPressed.
94 AXNodeData non_button_pressed;
95 non_button_pressed.role = ax::mojom::Role::kGenericContainer;
96 non_button_pressed.SetCheckedState(ax::mojom::CheckedState::kTrue);
97 EXPECT_FALSE(IsButton(non_button_pressed.role));
98 EXPECT_FALSE(non_button_pressed.IsButtonPressed());
99
100 // A button element with CheckedState::kTrue should return true for
101 // IsButtonPressed.
102 AXNodeData button_pressed;
103 button_pressed.role = ax::mojom::Role::kButton;
104 button_pressed.SetCheckedState(ax::mojom::CheckedState::kTrue);
105 EXPECT_TRUE(IsButton(button_pressed.role));
106 EXPECT_TRUE(button_pressed.IsButtonPressed());
107
108 button_pressed.role = ax::mojom::Role::kPopUpButton;
109 EXPECT_TRUE(IsButton(button_pressed.role));
110 EXPECT_TRUE(button_pressed.IsButtonPressed());
111
112 button_pressed.role = ax::mojom::Role::kToggleButton;
113 EXPECT_TRUE(IsButton(button_pressed.role));
114 EXPECT_TRUE(button_pressed.IsButtonPressed());
115
116 // A button element does not have CheckedState::kTrue should return false for
117 // IsButtonPressed.
118 AXNodeData button_not_pressed;
119 button_not_pressed.role = ax::mojom::Role::kButton;
120 button_not_pressed.SetCheckedState(ax::mojom::CheckedState::kNone);
121 EXPECT_TRUE(IsButton(button_not_pressed.role));
122 EXPECT_FALSE(button_not_pressed.IsButtonPressed());
123
124 button_not_pressed.role = ax::mojom::Role::kPopUpButton;
125 button_not_pressed.SetCheckedState(ax::mojom::CheckedState::kFalse);
126 EXPECT_TRUE(IsButton(button_not_pressed.role));
127 EXPECT_FALSE(button_not_pressed.IsButtonPressed());
128
129 button_not_pressed.role = ax::mojom::Role::kToggleButton;
130 button_not_pressed.SetCheckedState(ax::mojom::CheckedState::kMixed);
131 EXPECT_TRUE(IsButton(button_not_pressed.role));
132 EXPECT_FALSE(button_not_pressed.IsButtonPressed());
133}
134
135TEST(AXNodeDataTest, IsClickable) {
Ian Preste1c32992019-12-04 19:53:13136 // Test for ax node data attribute with a custom default action verb.
137 AXNodeData data_default_action_verb;
138
139 for (int action_verb_idx =
140 static_cast<int>(ax::mojom::DefaultActionVerb::kMinValue);
141 action_verb_idx <=
142 static_cast<int>(ax::mojom::DefaultActionVerb::kMaxValue);
143 action_verb_idx++) {
144 data_default_action_verb.SetDefaultActionVerb(
145 static_cast<ax::mojom::DefaultActionVerb>(action_verb_idx));
146 bool is_clickable = data_default_action_verb.IsClickable();
147
148 SCOPED_TRACE(testing::Message()
149 << "ax::mojom::DefaultActionVerb="
150 << ToString(data_default_action_verb.GetDefaultActionVerb())
151 << ", Actual: isClickable=" << is_clickable
152 << ", Expected: isClickable=" << !is_clickable);
153
154 if (data_default_action_verb.GetDefaultActionVerb() ==
155 ax::mojom::DefaultActionVerb::kClickAncestor ||
156 data_default_action_verb.GetDefaultActionVerb() ==
157 ax::mojom::DefaultActionVerb::kNone)
158 EXPECT_FALSE(is_clickable);
159 else
160 EXPECT_TRUE(is_clickable);
161 }
162
163 // Test for iterating through all roles and validate if a role is clickable.
Nektarios Paisios31e51882020-05-05 22:17:12164 std::set<ax::mojom::Role> roles_expected_is_clickable = {
Ian Preste1c32992019-12-04 19:53:13165 ax::mojom::Role::kButton,
166 ax::mojom::Role::kCheckBox,
167 ax::mojom::Role::kColorWell,
Nektarios Paisios31e51882020-05-05 22:17:12168 ax::mojom::Role::kComboBoxMenuButton,
169 ax::mojom::Role::kDate,
170 ax::mojom::Role::kDateTime,
Ian Preste1c32992019-12-04 19:53:13171 ax::mojom::Role::kDisclosureTriangle,
172 ax::mojom::Role::kDocBackLink,
173 ax::mojom::Role::kDocBiblioRef,
174 ax::mojom::Role::kDocGlossRef,
175 ax::mojom::Role::kDocNoteRef,
Nektarios Paisios31e51882020-05-05 22:17:12176 ax::mojom::Role::kImeCandidate,
177 ax::mojom::Role::kInputTime,
Ian Preste1c32992019-12-04 19:53:13178 ax::mojom::Role::kLink,
Nektarios Paisios31e51882020-05-05 22:17:12179 ax::mojom::Role::kListBox,
Ian Preste1c32992019-12-04 19:53:13180 ax::mojom::Role::kListBoxOption,
Ian Preste1c32992019-12-04 19:53:13181 ax::mojom::Role::kMenuItem,
182 ax::mojom::Role::kMenuItemCheckBox,
183 ax::mojom::Role::kMenuItemRadio,
184 ax::mojom::Role::kMenuListOption,
Ankit Kumar 🌪️4d5bf202020-01-28 05:30:47185 ax::mojom::Role::kPdfActionableHighlight,
Ian Preste1c32992019-12-04 19:53:13186 ax::mojom::Role::kPopUpButton,
Nektarios Paisios31e51882020-05-05 22:17:12187 ax::mojom::Role::kPortal,
Ian Preste1c32992019-12-04 19:53:13188 ax::mojom::Role::kRadioButton,
Nektarios Paisios31e51882020-05-05 22:17:12189 ax::mojom::Role::kSearchBox,
190 ax::mojom::Role::kSpinButton,
Ian Preste1c32992019-12-04 19:53:13191 ax::mojom::Role::kSwitch,
192 ax::mojom::Role::kTab,
Nektarios Paisios31e51882020-05-05 22:17:12193 ax::mojom::Role::kTextField,
194 ax::mojom::Role::kTextFieldWithComboBox,
Ian Preste1c32992019-12-04 19:53:13195 ax::mojom::Role::kToggleButton};
196
197 AXNodeData data;
198
199 for (int role_idx = static_cast<int>(ax::mojom::Role::kMinValue);
200 role_idx <= static_cast<int>(ax::mojom::Role::kMaxValue); role_idx++) {
201 data.role = static_cast<ax::mojom::Role>(role_idx);
202 bool is_clickable = data.IsClickable();
203
204 SCOPED_TRACE(testing::Message()
205 << "ax::mojom::Role=" << ToString(data.role)
206 << ", Actual: isClickable=" << is_clickable
207 << ", Expected: isClickable=" << !is_clickable);
208
Nektarios Paisios31e51882020-05-05 22:17:12209 EXPECT_EQ(base::Contains(roles_expected_is_clickable, data.role),
210 is_clickable);
Ian Preste1c32992019-12-04 19:53:13211 }
212}
213
Victor Fei4f5aa7d2020-04-09 03:15:50214TEST(AXNodeDataTest, IsInvocable) {
Ian Preste1c32992019-12-04 19:53:13215 // Test for iterating through all roles and validate if a role is invocable.
216 // A role is invocable if it is clickable and supports neither expand collpase
217 // nor toggle.
218 AXNodeData data;
219 for (int role_idx = static_cast<int>(ax::mojom::Role::kMinValue);
220 role_idx <= static_cast<int>(ax::mojom::Role::kMaxValue); role_idx++) {
221 data.role = static_cast<ax::mojom::Role>(role_idx);
Nektarios Paisios31e51882020-05-05 22:17:12222 bool is_activatable = data.IsActivatable();
223 const bool supports_expand_collapse = data.SupportsExpandCollapse();
224 const bool supports_toggle = ui::SupportsToggle(data.role);
225 const bool is_clickable = data.IsClickable();
226 const bool is_invocable = data.IsInvocable();
Ian Preste1c32992019-12-04 19:53:13227
228 SCOPED_TRACE(testing::Message()
229 << "ax::mojom::Role=" << ToString(data.role)
Nektarios Paisios31e51882020-05-05 22:17:12230 << ", isClickable=" << is_clickable << ", isActivatable="
231 << is_activatable << ", supportsToggle=" << supports_toggle
Ian Preste1c32992019-12-04 19:53:13232 << ", supportsExpandCollapse=" << supports_expand_collapse
233 << ", Actual: isInvocable=" << is_invocable
234 << ", Expected: isInvocable=" << !is_invocable);
235
Nektarios Paisios31e51882020-05-05 22:17:12236 if (is_clickable && !is_activatable && !supports_toggle &&
237 !supports_expand_collapse)
Ian Preste1c32992019-12-04 19:53:13238 EXPECT_TRUE(is_invocable);
239 else
240 EXPECT_FALSE(is_invocable);
241 }
242}
243
Victor Fei4f5aa7d2020-04-09 03:15:50244TEST(AXNodeDataTest, IsMenuButton) {
245 // A non button element should return false for IsMenuButton.
246 AXNodeData non_button;
247 non_button.role = ax::mojom::Role::kGenericContainer;
248 EXPECT_FALSE(IsButton(non_button.role));
249 EXPECT_FALSE(non_button.IsMenuButton());
250
251 // Only button element with HasPopup::kMenu should return true for
252 // IsMenuButton. All other ax::mojom::HasPopup types should return false.
253 AXNodeData button_with_popup;
254
255 button_with_popup.role = ax::mojom::Role::kButton;
256
257 for (int has_popup_idx = static_cast<int>(ax::mojom::HasPopup::kMinValue);
258 has_popup_idx <= static_cast<int>(ax::mojom::HasPopup::kMaxValue);
259 has_popup_idx++) {
260 button_with_popup.SetHasPopup(
261 static_cast<ax::mojom::HasPopup>(has_popup_idx));
262 bool is_menu_button = button_with_popup.IsMenuButton();
263
264 SCOPED_TRACE(testing::Message()
265 << "ax::mojom::Role=" << ToString(button_with_popup.role)
266 << ", hasPopup=" << button_with_popup.GetHasPopup()
267 << ", Actual: isMenuButton=" << is_menu_button
268 << ", Expected: isMenuButton=" << !is_menu_button);
269
270 if (IsButton(button_with_popup.role) &&
271 button_with_popup.GetHasPopup() == ax::mojom::HasPopup::kMenu)
272 EXPECT_TRUE(is_menu_button);
273 else
274 EXPECT_FALSE(is_menu_button);
275 }
276}
277
278TEST(AXNodeDataTest, SupportsExpandCollapse) {
Ian Preste1c32992019-12-04 19:53:13279 // Test for iterating through all hasPopup attributes and validate if a
280 // hasPopup attribute supports expand collapse.
281 AXNodeData data_has_popup;
282
283 for (int has_popup_idx = static_cast<int>(ax::mojom::HasPopup::kMinValue);
284 has_popup_idx <= static_cast<int>(ax::mojom::HasPopup::kMaxValue);
285 has_popup_idx++) {
286 data_has_popup.SetHasPopup(static_cast<ax::mojom::HasPopup>(has_popup_idx));
287 bool supports_expand_collapse = data_has_popup.SupportsExpandCollapse();
288
289 SCOPED_TRACE(testing::Message() << "ax::mojom::HasPopup="
290 << ToString(data_has_popup.GetHasPopup())
291 << ", Actual: supportsExpandCollapse="
292 << supports_expand_collapse
293 << ", Expected: supportsExpandCollapse="
294 << !supports_expand_collapse);
295
296 if (data_has_popup.GetHasPopup() == ax::mojom::HasPopup::kFalse)
297 EXPECT_FALSE(supports_expand_collapse);
298 else
299 EXPECT_TRUE(supports_expand_collapse);
300 }
301
302 // Test for iterating through all states and validate if a state supports
303 // expand collapse.
304 AXNodeData data_state;
305
306 for (int state_idx = static_cast<int>(ax::mojom::State::kMinValue);
307 state_idx <= static_cast<int>(ax::mojom::State::kMaxValue);
308 state_idx++) {
309 ax::mojom::State state = static_cast<ax::mojom::State>(state_idx);
310
311 // skipping kNone here because AXNodeData::AddState, RemoveState forbids
312 // kNone to be added/removed and would fail DCHECK.
313 if (state == ax::mojom::State::kNone)
314 continue;
315
316 data_state.AddState(state);
317
318 bool supports_expand_collapse = data_state.SupportsExpandCollapse();
319
320 SCOPED_TRACE(testing::Message() << "ax::mojom::State=" << ToString(state)
321 << ", Actual: supportsExpandCollapse="
322 << supports_expand_collapse
323 << ", Expected: supportsExpandCollapse="
324 << !supports_expand_collapse);
325
326 if (data_state.HasState(ax::mojom::State::kExpanded) ||
327 data_state.HasState(ax::mojom::State::kCollapsed))
328 EXPECT_TRUE(supports_expand_collapse);
329 else
330 EXPECT_FALSE(supports_expand_collapse);
331
332 data_state.RemoveState(state);
333 }
334
335 // Test for iterating through all roles and validate if a role supports expand
336 // collapse.
337 AXNodeData data;
338
339 std::unordered_set<ax::mojom::Role> roles_expected_supports_expand_collapse =
340 {ax::mojom::Role::kComboBoxGrouping, ax::mojom::Role::kComboBoxMenuButton,
341 ax::mojom::Role::kDisclosureTriangle,
342 ax::mojom::Role::kTextFieldWithComboBox, ax::mojom::Role::kTreeItem};
343
344 for (int role_idx = static_cast<int>(ax::mojom::Role::kMinValue);
345 role_idx <= static_cast<int>(ax::mojom::Role::kMaxValue); role_idx++) {
346 data.role = static_cast<ax::mojom::Role>(role_idx);
347 bool supports_expand_collapse = data.SupportsExpandCollapse();
348
349 SCOPED_TRACE(testing::Message() << "ax::mojom::Role=" << ToString(data.role)
350 << ", Actual: supportsExpandCollapse="
351 << supports_expand_collapse
352 << ", Expected: supportsExpandCollapse="
353 << !supports_expand_collapse);
354
355 if (roles_expected_supports_expand_collapse.find(data.role) !=
356 roles_expected_supports_expand_collapse.end())
357 EXPECT_TRUE(supports_expand_collapse);
358 else
359 EXPECT_FALSE(supports_expand_collapse);
360 }
361}
362
Hiroki Satofdb71f412020-02-27 01:18:37363TEST(AXNodeDataTest, BitFieldsSanityCheck) {
364 EXPECT_LT(static_cast<size_t>(ax::mojom::State::kMaxValue),
365 sizeof(AXNodeData::state) * 8);
366 EXPECT_LT(static_cast<size_t>(ax::mojom::Action::kMaxValue),
367 sizeof(AXNodeData::actions) * 8);
368}
369
Nektarios Paisiosbe87bc42018-10-12 23:47:03370} // namespace ui