1717package io .appium .java_client ;
1818
1919import com .google .common .base .Preconditions ;
20+ import com .google .gson .Gson ;
2021import lombok .EqualsAndHashCode ;
2122import lombok .Getter ;
2223import org .openqa .selenium .By ;
2526import org .openqa .selenium .WebElement ;
2627
2728import java .io .Serializable ;
29+ import java .util .HashMap ;
2830import java .util .List ;
31+ import java .util .Map ;
2932
3033import static com .google .common .base .Strings .isNullOrEmpty ;
3134
@@ -169,9 +172,9 @@ public static By custom(final String selector) {
169172 * as for OpenCV library.
170173 * @return an instance of {@link ByImage}
171174 * @see <a href="https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/image-comparison.md">
172- * The documentation on Image Comparison Features</a>
175+ * The documentation on Image Comparison Features</a>
173176 * @see <a href="https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js">
174- * The settings available for lookup fine-tuning</a>
177+ * The settings available for lookup fine-tuning</a>
175178 * @since Appium 1.8.2
176179 */
177180 public static By image (final String b64Template ) {
@@ -250,6 +253,53 @@ public static FlutterBy flutterSemanticsLabel(final String semanticsLabel) {
250253 return new ByFlutterSemanticsLabel (semanticsLabel );
251254 }
252255
256+ /**
257+ * This locator strategy is available in FlutterIntegration Driver mode.
258+ *
259+ * @param of represents the parent widget locator
260+ * @param matching represents the descendant widget locator to match
261+ * @param matchRoot determines whether to include the root widget in the search
262+ * @param skipOffstage determines whether to skip offstage widgets
263+ * @return an instance of {@link AppiumBy.ByFlutterDescendant}
264+ */
265+ public static FlutterBy flutterDescendant (final FlutterBy of , final FlutterBy matching , boolean matchRoot , boolean skipOffstage ) {
266+ return new ByFlutterDescendant (of , matching , matchRoot , skipOffstage );
267+ }
268+
269+ /**
270+ * This locator strategy is available in FlutterIntegration Driver mode.
271+ *
272+ * @param of represents the parent widget locator
273+ * @param matching represents the descendant widget locator to match
274+ * @return an instance of {@link AppiumBy.ByFlutterDescendant}
275+ */
276+ public static FlutterBy flutterDescendant (final FlutterBy of , final FlutterBy matching ) {
277+ return flutterDescendant (of , matching , false , true );
278+ }
279+
280+ /**
281+ * This locator strategy is available in FlutterIntegration Driver mode.
282+ *
283+ * @param of represents the child widget locator
284+ * @param matching represents the ancestor widget locator to match
285+ * @param matchRoot determines whether to include the root widget in the search
286+ * @return an instance of {@link AppiumBy.ByFlutterAncestor}
287+ */
288+ public static FlutterBy flutterAncestor (final FlutterBy of , final FlutterBy matching , boolean matchRoot ) {
289+ return new ByFlutterAncestor (of , matching , matchRoot );
290+ }
291+
292+ /**
293+ * This locator strategy is available in FlutterIntegration Driver mode.
294+ *
295+ * @param of represents the child widget locator
296+ * @param matching represents the ancestor widget locator to match
297+ * @return an instance of {@link AppiumBy.ByFlutterAncestor}
298+ */
299+ public static FlutterBy flutterAncestor (final FlutterBy of , final FlutterBy matching ) {
300+ return flutterAncestor (of , matching , false );
301+ }
302+
253303 public static class ByAccessibilityId extends AppiumBy implements Serializable {
254304 public ByAccessibilityId (String accessibilityId ) {
255305 super ("accessibility id" , accessibilityId , "accessibilityId" );
@@ -328,6 +378,27 @@ protected FlutterBy(String selector, String locatorString, String locatorName) {
328378 }
329379 }
330380
381+ public abstract static class FlutterByHierarchy extends FlutterBy {
382+ private static final Gson GSON = new Gson ();
383+
384+ protected FlutterByHierarchy (String selector , FlutterBy of , FlutterBy matching , Map <String , Object > properties , String locatorName ) {
385+ super (selector , formatLocator (of , matching , properties ), locatorName );
386+ }
387+
388+ static Map <String , Object > parseFlutterLocator (FlutterBy by ) {
389+ Parameters params = by .getRemoteParameters ();
390+ return Map .of ("using" , params .using (), "value" , params .value ());
391+ }
392+
393+ static String formatLocator (FlutterBy of , FlutterBy matching , Map <String , Object > properties ) {
394+ Map <String , Object > locator = new HashMap <>();
395+ locator .put ("of" , parseFlutterLocator (of ));
396+ locator .put ("matching" , parseFlutterLocator (matching ));
397+ locator .put ("parameters" , properties );
398+ return GSON .toJson (locator );
399+ }
400+ }
401+
331402 public static class ByFlutterType extends FlutterBy implements Serializable {
332403 protected ByFlutterType (String locatorString ) {
333404 super ("-flutter type" , locatorString , "flutterType" );
@@ -358,4 +429,15 @@ protected ByFlutterTextContaining(String locatorString) {
358429 }
359430 }
360431
361- }
432+ public static class ByFlutterDescendant extends FlutterByHierarchy implements Serializable {
433+ protected ByFlutterDescendant (FlutterBy of , FlutterBy matching , boolean matchRoot , boolean skipOffstage ) {
434+ super ("-flutter descendant" , of , matching , Map .of ("matchRoot" , matchRoot , "skipOffstage" , skipOffstage ), "flutterDescendant" );
435+ }
436+ }
437+
438+ public static class ByFlutterAncestor extends FlutterByHierarchy implements Serializable {
439+ protected ByFlutterAncestor (FlutterBy of , FlutterBy matching , boolean matchRoot ) {
440+ super ("-flutter ancestor" , of , matching , Map .of ("matchRoot" , matchRoot ), "flutterAncestor" );
441+ }
442+ }
443+ }
0 commit comments