File: interaction_test_util.h

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (305 lines) | stat: -rw-r--r-- 12,851 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_BASE_INTERACTION_INTERACTION_TEST_UTIL_H_
#define UI_BASE_INTERACTION_INTERACTION_TEST_UTIL_H_

#include <memory>
#include <vector>

#include "build/build_config.h"
#include "ui/base/interaction/element_tracker.h"

#if !BUILDFLAG(IS_IOS)
#include "ui/base/accelerators/accelerator.h"
#include "ui/events/keycodes/keyboard_codes.h"
#endif

namespace ui::test {

// Describes the result of a trying to perform a specific action as part of a
// test. Returned by individual functions and action simulators (see
// `InteractionTestUtil::Simulator` below).
enum class [[nodiscard]] ActionResult {
  // Indicates that the code did not know how to perform the action on the
  // requested target. In the case of an action simulator, other simulators
  // should be tried instead. Otherwise, treat as failure.
  kNotAttempted,

  // Indicates that the action succeeded.
  kSucceeded,

  // Indicates that the code *does* know how to perform the action on the
  // requested target, attempted to do so, and failed. No further attempts at
  // performing the action should be made.
  kFailed,

  // Indicates that the code *does* know how to perform the action, but
  // recognized that it would not succeed DUE TO A KNOWN ISSUE OR
  // INCOMPATIBILITY in the current platform, build, or job environment.
  //
  // An action that fails unexpectedly should always return kFailed instead.
  //
  // Code that returns this value should log or document the exact circumstances
  // that lead to the known incompatibility.
  //
  // No further attempts at performing the action should be made. Should be
  // treated as failure by default.
  kKnownIncompatible
};

// Platform- and framework-independent utility for delegating specific common
// actions to framework-specific handlers. Use so you can write your
// interaction tests without having to worry about framework specifics.
//
// Simulators are checked in the order they are added, so if more than one
// simulator can handle a particular action, add the one that has the more
// specific/desired behavior first.
//
// Example usage:
//
// class MyTest {
//   void SetUp() override {
//     test_util_.AddSimulator(
//         std::make_unique<InteractionTestUtilSimulatorViews>());
// #if BUILDFLAG(IS_MAC)
//     test_util_.AddSimulator(
//         std::make_unique<InteractionTestUtilSimulatorMac>());
// #endif
//     ...
//   }
//   InteractionTestUtil test_util_;
// };
//
// TEST_F(MyTest, TestClickButton) {
//   ...
//   step.SetStartCallback(base::BindLambdaForTesting([&] (
//       InteractionSequence* seq, TrackedElement* element) {
//     ActionResult result = test_util_.PressButton(element);
//     if (result == ActionResult::kError)
//       seq->FailForTesting();
//     else if (result = ActionResult::kNotSupportedOnThisPlatform)
//       seq->SkipForTesting();
//   }))
//   ...
// }
//
class InteractionTestUtil {
 public:
  // Indicates the type of input we want to apply to an element. Default in most
  // cases is `kDontCare` which will use the most reliable form of input (or may
  // even call code that directly simulates e.g. a button press).
  //
  // Only use values other than `kDontCare` if you REALLY want to test a
  // specific mode of input, as not all inputs will be supported for all
  // frameworks or platforms.
  enum class InputType {
    // Simulate the input in the most reliable way, which could be through
    // sending an input event or calling code that directly simulates the
    // interaction.
    kDontCare,
    // Simulate the input explicitly via mouse events.
    kMouse,
    // Simulate the input explicitly via kayboard events.
    kKeyboard,
    // Simulate the input explicitly via touch events.
    kTouch,
    // If values are added to the enumeration, update this value.
    kMaxValue = kTouch
  };

  // How should text be sent to a text input?
  enum class TextEntryMode {
    // Replaces all of the existing text with the new text.
    kReplaceAll,
    // Inserts the new text at the current cursor position, replacing any
    // existing selection.
    kInsertOrReplace,
    // Appends the new text to the end of the existing text.
    kAppend
  };

  // Provides framework-agnostic ways to send common input to the UI, such as
  // clicking buttons, typing text, etc.
  //
  // Framework-specific implementations will need to be provided to each
  // InteractionTestUtil instance you are using for testing.
  class Simulator {
   public:
    Simulator() = default;
    virtual ~Simulator() = default;
    Simulator(const Simulator&) = delete;
    void operator=(const Simulator&) = delete;

    using InputType = InteractionTestUtil::InputType;
    using TextEntryMode = InteractionTestUtil::TextEntryMode;

    // Tries to press `element` as if it is a button. Returns false if `element`
    // is an unsupported type or if `input_type` is not supported.
    virtual ActionResult PressButton(TrackedElement* element,
                                     InputType input_type);

    // Tries to select `element` as if it is a menu item. Returns false if
    // `element` is an unsupported type or if `input_type` is not supported.
    virtual ActionResult SelectMenuItem(TrackedElement* element,
                                        InputType input_type);

    // Triggers the default action of the target element, which is typically
    // whatever happens when the user clicks/taps it. If `element` is a button
    // or menu item, prefer PressButton() or SelectMenuItem() instead.
    virtual ActionResult DoDefaultAction(TrackedElement* element,
                                         InputType input_type);

    // Tries to select tab `index` in `tab_collection`. The collection could be
    // a tabbed pane, browser/tabstrip, or similar. Note that `index` is
    // zero-indexed. The index after the selection is verified; if for whatever
    // reason it should not be `index`, specify
    // `expected_index_after_selection`.
    virtual ActionResult SelectTab(
        TrackedElement* tab_collection,
        size_t index,
        InputType input_type,
        std::optional<size_t> expected_index_after_selection);

    // Tries to select item `index` in `dropdown`. The collection could be
    // a listbox, combobox, or similar. Note that `index` is zero-indexed.
    virtual ActionResult SelectDropdownItem(TrackedElement* dropdown,
                                            size_t index,
                                            InputType input_type);

    // Sets or modifies the text of a text box, editable combobox, etc.
    virtual ActionResult EnterText(TrackedElement* element,
                                   std::u16string text,
                                   TextEntryMode mode);

    // Activates the surface containing `element`.
    virtual ActionResult ActivateSurface(TrackedElement* element);

    // Focuses `element` within its surface. Does not necessarily activate the
    // surface. Note that on some platforms, `element` may not actually report
    // as focused until its surface is subsequently activated.
    virtual ActionResult FocusElement(TrackedElement* element);

#if !BUILDFLAG(IS_IOS)

    // Sends the given accelerator to the surface containing the element.
    virtual ActionResult SendAccelerator(TrackedElement* element,
                                         Accelerator accelerator);

    // Sends keypress with `key` and `flags` to `element` or its surface.
    virtual ActionResult SendKeyPress(TrackedElement* element,
                                      KeyboardCode key,
                                      int flags);

#endif  // !BUILDFLAG(IS_IOS)

    // Sends a "confirm" input to `element`, e.g. a RETURN keypress.
    virtual ActionResult Confirm(TrackedElement* element);
  };

  InteractionTestUtil();
  virtual ~InteractionTestUtil();
  InteractionTestUtil(const InteractionTestUtil&) = delete;
  void operator=(const InteractionTestUtil&) = delete;

  // Adds an input simulator for a specific framework.
  template <class T>
  T* AddSimulator(std::unique_ptr<T> simulator) {
    T* const result = simulator.get();
    simulators_.emplace_back(std::move(simulator));
    return result;
  }

  // Simulate a button press on `element`. Will fail if `element` is not a
  // button or if `input_type` is not supported.
  ActionResult PressButton(TrackedElement* element,
                           InputType input_type = InputType::kDontCare);

  // Simulate the menu item `element` being selected by the user. Will fail if
  // `element` is not a menu item or if `input_type` is not supported.
  ActionResult SelectMenuItem(TrackedElement* element,
                              InputType input_type = InputType::kDontCare);

  // Simulate selecting the `index`-th tab (zero-indexed) of `tab_collection`.
  // Will fail if the target object is not a supported type, if `index` is out
  // of bounds, or if `input_type` is not supported. The index after the
  // selection will be verified; if for whatever reason it should not be
  // `index`, specify `expected_index_after_selection`.
  ActionResult SelectTab(
      TrackedElement* tab_collection,
      size_t index,
      InputType input_type = InputType::kDontCare,
      std::optional<size_t> expected_index_after_selection = std::nullopt);

  // Simulate selecting item `index` in `dropdown`. The collection could be
  // a listbox, combobox, or similar. Will fail if the target object is not a
  // supported type, if `index` is out of bounds, or if `input_type` is not
  // supported.
  //
  // Note that if `input_type` is kDontCare, the approach with the broadest
  // possible compatibility will be used, possibly bypassing the dropdown menu
  // associated with the element. This is because dropdown menus vary in
  // implementation across platforms and can be a source of flakiness. Options
  // other than kDontCare may not be supported on all platforms for this reason;
  // if they are not, an error message will be printed and the test will fail.
  ActionResult SelectDropdownItem(TrackedElement* dropdown,
                                  size_t index,
                                  InputType input_type = InputType::kDontCare);

  // Simulate the default action for `element` - typically whatever happens when
  // the user clicks or taps on it. Will fail if `input_type` is not supported.
  // Prefer PressButton() for buttons and SelectMenuItem() for menu items.
  ActionResult DoDefaultAction(TrackedElement* element,
                               InputType input_type = InputType::kDontCare);

  // Sets or modifies the text of a text box, editable combobox, etc. `text` is
  // the text to enter, and `mode` specifies how it should be entered. Default
  // is replace existing text.
  ActionResult EnterText(TrackedElement* element,
                         std::u16string text,
                         TextEntryMode mode = TextEntryMode::kReplaceAll);

  // Activates the surface containing `element`. Prefer to use only in
  // single-process test fixtures like interactive_ui_tests, especially for
  // browser windows (as bringing a browser window to the front may require some
  // very aggressive system calls in certain cases and on certain platforms).
  ActionResult ActivateSurface(TrackedElement* element);

  // Focuses `element` within its surface. Does not necessarily activate the
  // surface. Note that on some platforms, `element` may not actually report as
  // focused until its surface is subsequently activated.
  virtual ActionResult FocusElement(TrackedElement* element);

#if !BUILDFLAG(IS_IOS)

  // Sends `accelerator` to the surface containing `element`. May not work if
  // the surface is not active. Prefer to use only in single-process test
  // fixtures like interactive_ui_tests, especially for app/browser
  // accelerators.
  ActionResult SendAccelerator(TrackedElement* element,
                               Accelerator accelerator);

  // Sends key press `key` with `flags` to `element` or its surface.
  ActionResult SendKeyPress(TrackedElement* element,
                            KeyboardCode key,
                            int flags);

#endif  // !BUILDFLAG(IS_IOS)

  // Sends a "confirm" input to `element`, e.g. a RETURN keypress.
  ActionResult Confirm(TrackedElement* element);

 private:
  // The list of known simulators.
  std::vector<std::unique_ptr<Simulator>> simulators_;
};

void PrintTo(InteractionTestUtil::InputType input_type, std::ostream* os);

std::ostream& operator<<(std::ostream& os,
                         InteractionTestUtil::InputType input_type);

}  // namespace ui::test

#endif  // UI_BASE_INTERACTION_INTERACTION_TEST_UTIL_H_