@@ -120,6 +120,23 @@ public static By iOSNsPredicateString(final String iOSNsPredicateString) {
120
120
public static By windowsAutomation (final String windowsAutomation ) {
121
121
return new ByWindowsAutomation (windowsAutomation );
122
122
}
123
+
124
+ /**
125
+ * This locator strategy is available only if OpenCV libraries and
126
+ * NodeJS bindings are installed on the server machine.
127
+ *
128
+ * @see <a href="https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/image-comparison.md">
129
+ * The documentation on Image Comparison Features</a>
130
+ * @see <a href="https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js">
131
+ * The settings available for lookup fine-tuning</a>
132
+ * @since Appium 1.8.2
133
+ * @param b64Template base64-encoded template image string. Supported image formats are the same
134
+ * as for OpenCV library.
135
+ * @return an instance of {@link ByImage}
136
+ */
137
+ public static By image (final String b64Template ) {
138
+ return new ByImage (b64Template );
139
+ }
123
140
124
141
public static class ByIosUIAutomation extends MobileBy implements Serializable {
125
142
@@ -146,7 +163,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
146
163
.findElementsByIosUIAutomation (getLocatorString ());
147
164
}
148
165
149
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
166
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
150
167
return super .findElements (context );
151
168
}
152
169
@@ -171,7 +188,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
171
188
.findElementByIosUIAutomation (getLocatorString ());
172
189
}
173
190
174
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
191
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
175
192
return super .findElement (context );
176
193
}
177
194
@@ -211,7 +228,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
211
228
.findElementsByAndroidUIAutomator (getLocatorString ());
212
229
}
213
230
214
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
231
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
215
232
return super .findElements (context );
216
233
}
217
234
@@ -236,7 +253,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
236
253
.findElementByAndroidUIAutomator (getLocatorString ());
237
254
}
238
255
239
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
256
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
240
257
return super .findElement (context );
241
258
}
242
259
@@ -275,7 +292,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
275
292
.findElementsByAccessibilityId (getLocatorString ());
276
293
}
277
294
278
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
295
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
279
296
return super .findElements (context );
280
297
}
281
298
@@ -300,7 +317,7 @@ public List<WebElement> findElements(SearchContext context) throws WebDriverExce
300
317
.findElementByAccessibilityId (getLocatorString ());
301
318
}
302
319
303
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
320
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
304
321
return super .findElement (context );
305
322
}
306
323
@@ -336,7 +353,7 @@ protected ByIosClassChain(String locatorString) {
336
353
.findElementsByIosClassChain (getLocatorString ());
337
354
}
338
355
339
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
356
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
340
357
return super .findElements (context );
341
358
}
342
359
@@ -360,7 +377,7 @@ protected ByIosClassChain(String locatorString) {
360
377
.findElementByIosClassChain (getLocatorString ());
361
378
}
362
379
363
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
380
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
364
381
return super .findElement (context );
365
382
}
366
383
@@ -396,7 +413,7 @@ protected ByIosNsPredicate(String locatorString) {
396
413
.findElementsByIosNsPredicate (getLocatorString ());
397
414
}
398
415
399
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
416
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
400
417
return super .findElements (context );
401
418
}
402
419
@@ -420,7 +437,7 @@ protected ByIosNsPredicate(String locatorString) {
420
437
.findElementByIosNsPredicate (getLocatorString ());
421
438
}
422
439
423
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
440
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
424
441
return super .findElement (context );
425
442
}
426
443
@@ -456,7 +473,7 @@ protected ByWindowsAutomation(String locatorString) {
456
473
.findElementsByWindowsUIAutomation (getLocatorString ());
457
474
}
458
475
459
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
476
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
460
477
return super .findElements (context );
461
478
}
462
479
@@ -480,14 +497,70 @@ protected ByWindowsAutomation(String locatorString) {
480
497
.findElementByWindowsUIAutomation (getLocatorString ());
481
498
}
482
499
483
- if (FindsByFluentSelector .class .isAssignableFrom (context . getClass () )) {
500
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
484
501
return super .findElement (context );
485
502
}
486
503
487
504
throw formIllegalArgumentException (contextClass , FindsByIosNSPredicate .class ,
488
505
FindsByWindowsAutomation .class );
489
506
}
490
507
}
508
+
509
+ public static class ByImage extends MobileBy implements Serializable {
510
+
511
+ protected ByImage (String b64Template ) {
512
+ super (MobileSelector .IMAGE , b64Template );
513
+ }
514
+
515
+ /**
516
+ * {@inheritDoc}
517
+ *
518
+ * @throws WebDriverException when current session doesn't support the given selector or when
519
+ * value of the selector is not consistent.
520
+ * @throws IllegalArgumentException when it is impossible to find something on the given
521
+ * {@link SearchContext} instance
522
+ */
523
+ @ SuppressWarnings ("unchecked" )
524
+ @ Override public List <WebElement > findElements (SearchContext context ) {
525
+ Class <?> contextClass = context .getClass ();
526
+
527
+ if (FindsByImage .class .isAssignableFrom (contextClass )) {
528
+ return FindsByImage .class .cast (context ).findElementsByImage (getLocatorString ());
529
+ }
530
+
531
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
532
+ return super .findElements (context );
533
+ }
534
+
535
+ throw formIllegalArgumentException (contextClass , FindsByImage .class , FindsByFluentSelector .class );
536
+ }
537
+
538
+ /**
539
+ * {@inheritDoc}
540
+ *
541
+ * @throws WebDriverException when current session doesn't support the given selector or when
542
+ * value of the selector is not consistent.
543
+ * @throws IllegalArgumentException when it is impossible to find something on the given
544
+ * {@link SearchContext} instance
545
+ */
546
+ @ Override public WebElement findElement (SearchContext context ) {
547
+ Class <?> contextClass = context .getClass ();
548
+
549
+ if (FindsByImage .class .isAssignableFrom (contextClass )) {
550
+ return FindsByImage .class .cast (context ).findElementByImage (getLocatorString ());
551
+ }
552
+
553
+ if (FindsByFluentSelector .class .isAssignableFrom (contextClass )) {
554
+ return super .findElement (context );
555
+ }
556
+
557
+ throw formIllegalArgumentException (contextClass , FindsByImage .class , FindsByFluentSelector .class );
558
+ }
559
+
560
+ @ Override public String toString () {
561
+ return "By.Image: " + getLocatorString ();
562
+ }
563
+ }
491
564
}
492
565
493
566
0 commit comments