1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package dev.aherscu.qa.jgiven.webdriver.steps;
17
18 import static dev.aherscu.qa.jgiven.commons.utils.WebDriverEx.*;
19 import static java.lang.Boolean.*;
20 import static java.util.Objects.*;
21
22 import java.net.*;
23 import java.time.*;
24 import java.util.*;
25 import java.util.function.*;
26
27 import javax.annotation.concurrent.*;
28
29 import org.openqa.selenium.*;
30 import org.openqa.selenium.NoSuchContextException;
31 import org.openqa.selenium.NoSuchElementException;
32
33 import com.tngtech.jgiven.annotation.*;
34
35 import dev.aherscu.qa.jgiven.commons.steps.*;
36 import dev.aherscu.qa.jgiven.commons.utils.*;
37 import dev.aherscu.qa.jgiven.webdriver.model.*;
38 import io.appium.java_client.*;
39 import io.appium.java_client.remote.*;
40 import io.appium.java_client.windows.*;
41 import lombok.*;
42 import lombok.extern.slf4j.*;
43 import net.jodah.failsafe.*;
44
45
46
47
48
49
50
51
52 @ThreadSafe
53 @Slf4j
54 @SuppressWarnings({ "boxing", "ClassWithTooManyMethods" })
55 public class WebDriverActions<SELF extends WebDriverActions<SELF>>
56 extends GenericActions<WebDriverScenarioType, SELF>
57 implements MayAttachScreenshots<SELF> {
58
59
60
61 @ExpectedScenarioState
62 protected ThreadLocal<WebDriverEx> webDriver;
63
64 @Override
65 @Hidden
66 public SELF attaching_screenshot() {
67 return attaching_screenshot(0);
68 }
69
70 @Override
71 @Hidden
72 public SELF attaching_screenshot(final int delayMs) {
73 attachScreenshot(thisWebDriver(), delayMs);
74 return self();
75 }
76
77
78
79
80
81
82
83
84
85 public SELF clicking(final By locator) {
86 return clicking(() -> element(locator));
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 public SELF clicking(final Supplier<WebElement> elementSupplier) {
103 try (val clickTimerContext = clickTimer.time()) {
104 return retrying(self -> clicking_once(elementSupplier));
105 }
106 }
107
108 public SELF clicking_once(final By locator) {
109 return clicking_once(() -> element(locator));
110 }
111
112 public SELF clicking_once(final Supplier<WebElement> elementSupplier) {
113 val element = elementSupplier.get();
114 log.debug("clicking {}", descriptionOf(element));
115 element.click();
116 return self();
117 }
118
119
120
121
122
123
124
125
126
127
128
129
130
131 @Hidden
132 public List<WebElement> elements(final By locator) {
133 log.debug("locating {}", locator);
134 return elements(locator, thisWebDriver().asGeneric());
135 }
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150 @As("clicking")
151 public SELF forcefullyClicking(final Supplier<WebElement> elementSupplier) {
152 try (val clickTimerContext = clickTimer.time()) {
153 return retrying(self -> {
154 val element = elementSupplier.get();
155 log.debug("forcefully clicking {}", descriptionOf(element));
156 thisWebDriver().forceClick(element);
157 return self;
158 });
159 }
160 }
161
162
163
164
165
166
167
168
169
170
171 @As("clicking")
172 public SELF forcefullyClicking(final By locator) {
173 return forcefullyClicking(() -> element(locator));
174 }
175
176
177
178
179
180
181
182
183
184 public SELF long_pressing(final Supplier<WebElement> elementSupplier) {
185 return retrying(self -> {
186 val element = elementSupplier.get();
187 log.debug("long pressing {}", descriptionOf(element));
188 thisWebDriver()
189 .dispatch(new WebDriverEx.PointerEvent(
190 PointerEvent.PointerEventType.pointerdown,
191 new HashMap<String, Object>() {
192 {
193 put("bubbles", TRUE);
194 }
195 }),
196 element);
197 return self;
198 });
199 }
200
201
202
203
204
205
206
207
208
209 public SELF long_pressing(final By locator) {
210 return long_pressing(() -> element(locator));
211 }
212
213
214
215
216
217
218
219
220 public SELF opening(final URI url) {
221 thisWebDriver().asGeneric().get(url.toString());
222
223 if (!thisWebDriver().is(JavascriptExecutor.class))
224 return self();
225
226 return retrying(
227 self -> {
228 if (thisWebDriver().asJavaScriptExecutor()
229 .executeScript("return document.readyState")
230 .equals("complete"))
231 return self;
232 throw new FailsafeException();
233 });
234 }
235
236 @SneakyThrows(URISyntaxException.class)
237 public SELF opening(final String url) {
238 return opening(new URI(url));
239 }
240
241
242
243
244
245
246
247
248
249 public SELF rotating_device_to(final ScreenOrientation orientation) {
250 ((SupportsRotation) thisWebDriver()).rotate(orientation);
251 return self();
252 }
253
254
255
256
257
258
259
260
261 public SELF sending_application_to_background_for(final Duration duration) {
262 log.debug("sending application to background for {}", duration);
263 ((InteractsWithApps) thisWebDriver()).runAppInBackground(duration);
264 log.debug("returned from background");
265 return self();
266 }
267
268
269
270
271
272
273
274
275
276 public SELF submitting_the_form_containing(final By locator) {
277 return retrying(self -> {
278 log.debug("submitting {}", locator);
279 element(locator).submit();
280 return self();
281 });
282 }
283
284
285
286
287
288
289
290
291 public SELF terminating_application(final String appId) {
292 log.debug("application {} was running and stopped successfully: {}",
293 appId,
294 ((InteractsWithApps) thisWebDriver()).terminateApp(appId));
295 return self();
296 }
297
298
299
300
301
302
303
304
305
306
307
308 public SELF typing_$_into(
309 @Quoted final String value,
310 final Supplier<WebElement> elementSupplier) {
311 try (val sendKeysTimerContext = sendKeysTimer.time()) {
312 return retrying(self -> {
313 val element = elementSupplier.get();
314 log.debug("typing {} into {}", value, descriptionOf(element));
315
316
317
318 element.clear();
319 element.sendKeys(value);
320 return self.hiding_keyboard();
321 });
322 }
323 }
324
325
326
327
328
329
330
331
332
333
334
335 public SELF typing_$_into(
336 @Quoted final String value,
337 final By locator) {
338 return typing_$_into(value, () -> element(locator));
339 }
340
341 public SELF typing_$_into_without_clearing(
342 @Quoted final String value,
343 final Supplier<WebElement> elementSupplier) {
344 try (val sendKeysTimerContext = sendKeysTimer.time()) {
345 return retrying(self -> {
346 val element = elementSupplier.get();
347 log.debug("typing {} into {}", value, descriptionOf(element));
348 element.sendKeys(value);
349 return self.hiding_keyboard();
350 });
351 }
352 }
353
354 public SELF typing_$_into_without_clearing(
355 @Quoted final String value,
356 final By locator) {
357 return typing_$_into_without_clearing(value, () -> element(locator));
358 }
359
360
361
362
363
364
365
366
367 @Hidden
368 protected SELF activating_application(final String appId) {
369 log.debug("activating application {}", appId);
370 ((InteractsWithApps) thisWebDriver()).activateApp(appId);
371 return self();
372 }
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389 @Hidden
390 protected WebElement element(final By locator) {
391 log.debug("locating {}", locator);
392 return element(locator, thisWebDriver().asGeneric());
393 }
394
395
396
397
398
399
400
401
402
403
404
405
406
407 @Hidden
408 protected List<WebElement> ensureElements(final By locator) {
409 log.debug("locating {}", locator);
410 return ensureElements(locator, thisWebDriver().asGeneric());
411 }
412
413
414
415
416
417
418
419
420
421
422
423
424
425 @Hidden
426 protected boolean hasElements(final By locator) {
427 return !ensureElements(locator).isEmpty();
428 }
429
430
431
432
433
434
435 @Hidden
436 protected SELF hiding_keyboard() {
437 log.debug("hiding the keyboard");
438 if (webDriver.get().asGeneric() instanceof WindowsDriver)
439
440 return self();
441
442
443
444 return forcefullyClicking(By.xpath("/html/body"));
445
446 }
447
448
449
450
451
452
453
454
455
456
457
458
459 @Hidden
460 @Override
461 protected WebElement scrollIntoView(final WebElement element) {
462 try (val scrollIntoViewTimerContext = scrollIntoViewTimer.time()) {
463 log.debug("scrolling to {}", descriptionOf(element));
464 thisWebDriver().scrollIntoView(element);
465 }
466 return element;
467 }
468
469
470
471
472
473
474
475
476
477
478 @Hidden
479 protected SELF switching_to_context(final Predicate<String> byRule) {
480 return context(byRule, (ContextAware) thisWebDriver());
481 }
482
483
484
485
486
487
488
489
490 @Hidden
491 protected SELF switching_to_window(@Hidden final String nameOrHandle) {
492 log.debug("switching to window {}", nameOrHandle);
493 thisWebDriver().asGeneric().switchTo().window(nameOrHandle);
494 return self();
495 }
496
497 protected final WebDriverEx thisWebDriver() {
498 return requireNonNull(
499 requireNonNull(webDriver, "web driver fixtures stage omitted")
500 .get(),
501 "web driver not initialized");
502 }
503 }