11import 'package:appium_flutter_server/src/internal/widget_predicates.dart' ;
2+ import 'package:appium_flutter_server/src/models/api/find_element.dart' ;
23import 'package:flutter/material.dart' ;
34import 'package:flutter_test/flutter_test.dart' ;
45
@@ -10,12 +11,12 @@ enum ElementLookupStrategy {
1011 BY_TEXT_CONTAINING ,
1112 BY_TYPE ,
1213 BY_ICON_POINT ,
13- BY_ICON_NAME
14- // BY_ICON,
15- // BY_ELEMENT_PREDICATE ,
16- // BY_SUBTYPE ,
17- // BY_WIDGET ,
18- //BY_WIDGET_PREDICATE ,
14+ BY_ICON_NAME ,
15+ // Complex strategies
16+ BY_ANCESTOR ,
17+ BY_DESCENDANT ,
18+ BY_PAGE_OBJECT ,
19+ BY_CUSTOM_PREDICATE ,
1920}
2021
2122var a = Icons .accessible;
@@ -25,37 +26,162 @@ extension ElementLookupStrategyExtension on ElementLookupStrategy {
2526 return ElementLookupStrategy .values.byName (strategyName);
2627 }
2728
28- Finder toFinder (dynamic selector) {
29+ /// Convert FindElementModel to Finder - supports both simple and complex finders
30+ Future <Finder > toFinder (FindElementModel model) async {
2931 switch (this ) {
32+ // Simple strategies
3033 case ElementLookupStrategy .BY_KEY :
31- return find.byKey (Key (selector ), skipOffstage: false );
34+ return find.byKey (Key (model.selectorString ! ), skipOffstage: false );
3235 case ElementLookupStrategy .BY_SEMANTICS_LABEL :
33- return find.bySemanticsLabel (selector, skipOffstage: false );
36+ return find.bySemanticsLabel (model.selectorString! ,
37+ skipOffstage: false );
3438 case ElementLookupStrategy .BY_TOOLTIP :
35- return find.byTooltip (selector , skipOffstage: false );
39+ return find.byTooltip (model.selectorString ! , skipOffstage: false );
3640 case ElementLookupStrategy .BY_TEXT :
37- return find.text (selector , skipOffstage: false );
41+ return find.text (model.selectorString ! , skipOffstage: false );
3842 case ElementLookupStrategy .BY_TEXT_CONTAINING :
39- return find.textContaining (selector , skipOffstage: false );
43+ return find.textContaining (model.selectorString ! , skipOffstage: false );
4044 case ElementLookupStrategy .BY_TYPE :
41- return find.byWidgetPredicate (filterByWidgetName (selector),
42- skipOffstage: false );
45+ return find.byWidgetPredicate (
46+ filterByWidgetName (model.selectorString! ),
47+ skipOffstage: false ,
48+ );
4349 case ElementLookupStrategy .BY_ICON_POINT :
44- return find.byWidgetPredicate (filterByIconCode (int .parse (selector)),
45- skipOffstage: false );
46- // return find.byIcon(const IconData(0xe0c4));
47- // case ElementLookupStrategy.BY_ELEMENT_PREDICATE:
48- // return find.byElementPredicate(Key(selector));
49- // case ElementLookupStrategy.BY_SUBTYPE:
50- // return find.bySubtype<SnackBar>();
51- // case ElementLookupStrategy.BY_TYPE:
52- // return find.byType(SingleChildRenderObjectElement);
53- // case ElementLookupStrategy.BY_WIDGET:
54- // return find.byWidget(SnackBar);
55- // case ElementLookupStrategy.BY_WIDGET_PREDICATE:
56- // return find.byWidget(SnackBar);
50+ return find.byWidgetPredicate (
51+ filterByIconCode (int .parse (model.selectorString! )),
52+ skipOffstage: false ,
53+ );
54+ case ElementLookupStrategy .BY_ICON_NAME :
55+ return find.byIcon (_getIconByName (model.selectorString! ));
56+
57+ // Complex strategies
58+ case ElementLookupStrategy .BY_ANCESTOR :
59+ return await _createAncestorFinder (model);
60+ case ElementLookupStrategy .BY_DESCENDANT :
61+ return await _createDescendantFinder (model);
62+ case ElementLookupStrategy .BY_PAGE_OBJECT :
63+ return await _createPageObjectFinder (model);
64+ case ElementLookupStrategy .BY_CUSTOM_PREDICATE :
65+ return _createCustomPredicateFinder (model);
66+ }
67+ }
68+
69+ /// Create ancestor finder from complex model
70+ Future <Finder > _createAncestorFinder (FindElementModel model) async {
71+ final selectorMap = model.selectorMap! ;
72+ final ofModel = FindElementModel .fromJson (selectorMap['of' ]);
73+ final matchingModel = FindElementModel .fromJson (selectorMap['matching' ]);
74+ final matchRoot = selectorMap['matchRoot' ] ?? false ;
75+
76+ final ofFinder = await _resolveFinder (ofModel);
77+ final matchingFinder = await _resolveFinder (matchingModel);
78+
79+ return find.ancestor (
80+ of: ofFinder,
81+ matching: matchingFinder,
82+ matchRoot: matchRoot,
83+ );
84+ }
85+
86+ /// Create descendant finder from complex model
87+ Future <Finder > _createDescendantFinder (FindElementModel model) async {
88+ final selectorMap = model.selectorMap! ;
89+ final ofModel = FindElementModel .fromJson (selectorMap['of' ]);
90+ final matchingModel = FindElementModel .fromJson (selectorMap['matching' ]);
91+ final matchRoot = selectorMap['matchRoot' ] ?? false ;
92+
93+ final ofFinder = await _resolveFinder (ofModel);
94+ final matchingFinder = await _resolveFinder (matchingModel);
95+
96+ return find.descendant (
97+ of: ofFinder,
98+ matching: matchingFinder,
99+ matchRoot: matchRoot,
100+ );
101+ }
102+
103+ /// Create page object finder (custom implementation)
104+ Future <Finder > _createPageObjectFinder (FindElementModel model) async {
105+ final selectorMap = model.selectorMap! ;
106+ final pageObjectName = selectorMap['pageObject' ] as String ;
107+ final elementModel = FindElementModel .fromJson (selectorMap['element' ]);
108+
109+ // This could be extended to load page objects from configuration
110+ // For now, just resolve the inner element
111+ print ('Loading page object: $pageObjectName ' );
112+ return await _resolveFinder (elementModel);
113+ }
114+
115+ /// Create custom predicate finder
116+ Finder _createCustomPredicateFinder (FindElementModel model) {
117+ final selectorMap = model.selectorMap! ;
118+ final predicateType = selectorMap['predicateType' ] as String ;
119+ final parameters = selectorMap['parameters' ] as Map <String , dynamic >;
120+
121+ switch (predicateType) {
122+ case 'widget_property' :
123+ return find.byWidgetPredicate (
124+ (widget) => _checkWidgetProperty (widget, parameters),
125+ skipOffstage: false ,
126+ );
127+ case 'element_property' :
128+ return find.byElementPredicate (
129+ (element) => _checkElementProperty (element, parameters),
130+ );
131+ default :
132+ throw ArgumentError ('Unknown predicate type: $predicateType ' );
133+ }
134+ }
135+
136+ /// Recursively resolve any FindElementModel to a Finder
137+ Future <Finder > _resolveFinder (FindElementModel model) async {
138+ final strategy = ElementLookupStrategy .values
139+ .firstWhere ((s) => s.name == model.strategy);
140+ return await strategy.toFinder (model);
141+ }
142+
143+ /// Helper methods for custom predicates
144+ bool _checkWidgetProperty (Widget widget, Map <String , dynamic > parameters) {
145+ final propertyName = parameters['property' ] as String ;
146+ final expectedValue = parameters['value' ];
147+
148+ // This could be extended with reflection or a property registry
149+ switch (propertyName) {
150+ case 'runtimeType' :
151+ return widget.runtimeType.toString () == expectedValue;
152+ case 'key' :
153+ return widget.key? .toString () == expectedValue;
154+ default :
155+ return false ;
156+ }
157+ }
158+
159+ bool _checkElementProperty (Element element, Map <String , dynamic > parameters) {
160+ final propertyName = parameters['property' ] as String ;
161+ final expectedValue = parameters['value' ];
162+
163+ switch (propertyName) {
164+ case 'semanticsLabel' :
165+ return element.renderObject? .debugSemantics? .label == expectedValue;
166+ default :
167+ return false ;
168+ }
169+ }
170+
171+ /// Get icon by name (helper method)
172+ IconData _getIconByName (String iconName) {
173+ // This could be extended with a comprehensive icon registry
174+ switch (iconName.toLowerCase ()) {
175+ case 'home' :
176+ return Icons .home;
177+ case 'search' :
178+ return Icons .search;
179+ case 'settings' :
180+ return Icons .settings;
181+ case 'menu' :
182+ return Icons .menu;
57183 default :
58- return find. text (selector);
184+ return Icons .help_outline; // Default fallback
59185 }
60186 }
61187
@@ -77,18 +203,14 @@ extension ElementLookupStrategyExtension on ElementLookupStrategy {
77203 return '-flutter icon point' ;
78204 case ElementLookupStrategy .BY_ICON_NAME :
79205 return '-flutter icon name' ;
80- // case ElementLookupStrategy.BY_ICON:
81- // return '-flutter icon';
82- // case ElementLookupStrategy.BY_ELEMENT_PREDICATE:
83- // return '-flutter element predicate';
84- // case ElementLookupStrategy.BY_SUBTYPE:
85- // return '-flutter subtype';
86- // case ElementLookupStrategy.BY_TYPE:
87- // return '-flutter type';
88- // case ElementLookupStrategy.BY_WIDGET:
89- // return '-flutter widget';
90- // case ElementLookupStrategy.BY_WIDGET_PREDICATE:
91- // return '-flutter widget predicate';
206+ case ElementLookupStrategy .BY_ANCESTOR :
207+ return '-flutter ancestor' ;
208+ case ElementLookupStrategy .BY_DESCENDANT :
209+ return '-flutter descendant' ;
210+ case ElementLookupStrategy .BY_PAGE_OBJECT :
211+ return '-flutter page_object' ;
212+ case ElementLookupStrategy .BY_CUSTOM_PREDICATE :
213+ return '-flutter custom_predicate' ;
92214 }
93215 }
94216}
0 commit comments