appium-webdriveragent 4.6.0 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/PrivateHeaders/XCTest/XCAXClient_iOS.h +2 -4
  2. package/PrivateHeaders/XCTest/XCActivityRecord.h +3 -3
  3. package/PrivateHeaders/XCTest/XCApplicationQuery.h +3 -3
  4. package/PrivateHeaders/XCTest/XCTElementSetTransformer-Protocol.h +2 -2
  5. package/PrivateHeaders/XCTest/XCTRunnerDaemonSession.h +1 -1
  6. package/PrivateHeaders/XCTest/XCTestManager_IDEInterface-Protocol.h +5 -5
  7. package/PrivateHeaders/XCTest/XCTestManager_ManagerInterface-Protocol.h +8 -8
  8. package/PrivateHeaders/XCTest/XCUIApplication.h +2 -2
  9. package/PrivateHeaders/XCTest/XCUIApplicationImpl.h +2 -2
  10. package/PrivateHeaders/XCTest/XCUIApplicationProcess.h +3 -5
  11. package/PrivateHeaders/XCTest/XCUIElement.h +4 -4
  12. package/PrivateHeaders/XCTest/XCUIElementQuery.h +4 -4
  13. package/PrivateHeaders/XCTest/XCUIRecorderNodeFinder.h +3 -3
  14. package/PrivateHeaders/XCTest/XCUIRecorderUtilities.h +3 -3
  15. package/Scripts/build.sh +3 -6
  16. package/WebDriverAgent.xcodeproj/project.pbxproj +66 -70
  17. package/WebDriverAgent.xcodeproj/project.xcworkspace/xcuserdata/elf.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  18. package/WebDriverAgentLib/Categories/{XCElementSnapshot+FBHelpers.h → FBXCElementSnapshotWrapper+Helpers.h} +18 -11
  19. package/WebDriverAgentLib/Categories/{XCElementSnapshot+FBHelpers.m → FBXCElementSnapshotWrapper+Helpers.m} +57 -39
  20. package/WebDriverAgentLib/Categories/NSExpression+FBFormat.m +8 -3
  21. package/WebDriverAgentLib/Categories/XCUIApplication+FBAlert.m +8 -7
  22. package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +2 -2
  23. package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +29 -28
  24. package/WebDriverAgentLib/Categories/XCUIApplicationProcess+FBQuiescence.m +5 -0
  25. package/WebDriverAgentLib/Categories/XCUICoordinate+FBFix.m +0 -1
  26. package/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +8 -5
  27. package/WebDriverAgentLib/Categories/XCUIElement+FBAccessibility.h +2 -2
  28. package/WebDriverAgentLib/Categories/XCUIElement+FBAccessibility.m +5 -4
  29. package/WebDriverAgentLib/Categories/XCUIElement+FBCaching.m +3 -2
  30. package/WebDriverAgentLib/Categories/XCUIElement+FBFind.m +22 -18
  31. package/WebDriverAgentLib/Categories/XCUIElement+FBIsVisible.h +2 -3
  32. package/WebDriverAgentLib/Categories/XCUIElement+FBIsVisible.m +28 -26
  33. package/WebDriverAgentLib/Categories/XCUIElement+FBPickerWheel.m +2 -1
  34. package/WebDriverAgentLib/Categories/XCUIElement+FBResolve.m +8 -4
  35. package/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.h +10 -0
  36. package/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.m +81 -50
  37. package/WebDriverAgentLib/Categories/XCUIElement+FBTyping.m +11 -10
  38. package/WebDriverAgentLib/Categories/XCUIElement+FBUID.h +10 -5
  39. package/WebDriverAgentLib/Categories/XCUIElement+FBUID.m +15 -3
  40. package/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.h +7 -7
  41. package/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.m +29 -20
  42. package/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.h +2 -2
  43. package/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m +16 -13
  44. package/WebDriverAgentLib/Categories/XCUIElementQuery+FBHelpers.h +2 -3
  45. package/WebDriverAgentLib/Categories/XCUIElementQuery+FBHelpers.m +3 -3
  46. package/WebDriverAgentLib/Commands/FBElementCommands.m +29 -15
  47. package/WebDriverAgentLib/Commands/FBFindElementCommands.m +5 -6
  48. package/WebDriverAgentLib/FBAlert.m +16 -13
  49. package/WebDriverAgentLib/FBApplication.m +8 -8
  50. package/WebDriverAgentLib/Routing/FBElementUtils.h +11 -2
  51. package/WebDriverAgentLib/Routing/FBElementUtils.m +15 -12
  52. package/WebDriverAgentLib/Routing/FBResponsePayload.m +11 -10
  53. package/WebDriverAgentLib/Routing/FBSession.m +1 -1
  54. package/{PrivateHeaders/XCTest/XCAccessibilityElement.h → WebDriverAgentLib/Routing/FBXCAccessibilityElement.h} +19 -18
  55. package/WebDriverAgentLib/{Categories/XCAccessibilityElement+FBComparison.m → Routing/FBXCAccessibilityElement.m} +5 -11
  56. package/WebDriverAgentLib/Routing/FBXCDeviceEvent.h +35 -0
  57. package/WebDriverAgentLib/Routing/FBXCDeviceEvent.m +43 -0
  58. package/{PrivateHeaders/XCTest/XCElementSnapshot.h → WebDriverAgentLib/Routing/FBXCElementSnapshot.h} +25 -44
  59. package/WebDriverAgentLib/Routing/FBXCElementSnapshot.m +10 -0
  60. package/WebDriverAgentLib/Routing/FBXCElementSnapshotWrapper.h +30 -0
  61. package/WebDriverAgentLib/Routing/FBXCElementSnapshotWrapper.m +45 -0
  62. package/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.h +3 -4
  63. package/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.m +4 -4
  64. package/WebDriverAgentLib/Utilities/FBBaseActionsSynthesizer.h +2 -2
  65. package/WebDriverAgentLib/Utilities/FBBaseActionsSynthesizer.m +7 -8
  66. package/WebDriverAgentLib/Utilities/FBClassChainQueryParser.m +1 -2
  67. package/WebDriverAgentLib/Utilities/FBConfiguration.m +0 -1
  68. package/WebDriverAgentLib/Utilities/FBImageIOScaler.m +3 -0
  69. package/WebDriverAgentLib/Utilities/FBKeyboard.m +1 -2
  70. package/WebDriverAgentLib/Utilities/FBTVNavigationTracker.m +1 -1
  71. package/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.m +3 -3
  72. package/WebDriverAgentLib/Utilities/FBXCAXClientProxy.h +10 -9
  73. package/WebDriverAgentLib/Utilities/FBXCAXClientProxy.m +14 -8
  74. package/WebDriverAgentLib/Utilities/FBXCTestDaemonsProxy.h +0 -1
  75. package/WebDriverAgentLib/Utilities/FBXCTestDaemonsProxy.m +6 -0
  76. package/WebDriverAgentLib/Utilities/FBXCodeCompatibility.h +0 -10
  77. package/WebDriverAgentLib/Utilities/FBXCodeCompatibility.m +0 -24
  78. package/WebDriverAgentLib/Utilities/FBXPath-Private.h +1 -1
  79. package/WebDriverAgentLib/Utilities/FBXPath.h +3 -3
  80. package/WebDriverAgentLib/Utilities/FBXPath.m +15 -13
  81. package/WebDriverAgentLib/Utilities/NSPredicate+FBFormat.h +14 -1
  82. package/WebDriverAgentLib/Utilities/NSPredicate+FBFormat.m +18 -9
  83. package/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.m +9 -3
  84. package/WebDriverAgentLib/Utilities/XCUIApplicationProcessDelay.m +7 -1
  85. package/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncSocket.m +7 -1
  86. package/WebDriverAgentLib/WebDriverAgentLib.h +0 -1
  87. package/WebDriverAgentRunner-Runner.app.zip +0 -0
  88. package/WebDriverAgentTests/IntegrationTests/FBScrollingTests.m +10 -0
  89. package/WebDriverAgentTests/IntegrationTests/FBXPathIntegrationTests.m +26 -19
  90. package/WebDriverAgentTests/IntegrationTests/XCElementSnapshotHelperTests.m +24 -29
  91. package/WebDriverAgentTests/IntegrationTests/XCElementSnapshotHitPointTests.m +2 -2
  92. package/WebDriverAgentTests/IntegrationTests/XCUIElementFBFindTests.m +5 -3
  93. package/WebDriverAgentTests/IntegrationTests/XCUIElementHelperIntegrationTests.m +2 -1
  94. package/{WebDriverAgentLib/Categories/XCElementSnapshot+FBHitPoint.h → WebDriverAgentTests/UnitTests/Doubles/XCElementSnapshotDouble.h} +3 -14
  95. package/WebDriverAgentTests/UnitTests/Doubles/XCElementSnapshotDouble.m +99 -0
  96. package/WebDriverAgentTests/UnitTests/FBXPathTests.m +21 -13
  97. package/WebDriverAgentTests/UnitTests/NSPredicateFBFormatTests.m +4 -5
  98. package/package.json +1 -2
  99. package/WebDriverAgentLib/Categories/XCAccessibilityElement+FBComparison.h +0 -27
  100. package/WebDriverAgentLib/Categories/XCElementSnapshot+FBHitPoint.m +0 -29
  101. package/WebDriverAgentLib/Utilities/FBPredicate.h +0 -28
  102. package/WebDriverAgentLib/Utilities/FBPredicate.m +0 -29
@@ -11,6 +11,7 @@
11
11
 
12
12
  #import <objc/runtime.h>
13
13
 
14
+ #import "FBXCElementSnapshotWrapper+Helpers.h"
14
15
  #import "XCUIElement+FBWebDriverAttributes.h"
15
16
  #import "XCUIElement+FBUtilities.h"
16
17
  #import "XCUIElement+FBUID.h"
@@ -46,8 +47,8 @@ static char XCUIELEMENT_CACHE_ID_KEY;
46
47
  if ([self isKindOfClass:XCUIApplication.class]) {
47
48
  uid = self.fb_uid;
48
49
  } else {
49
- XCElementSnapshot *snapshot = self.fb_cachedSnapshot ?: self.fb_takeSnapshot;
50
- uid = snapshot.wdUID;
50
+ id<FBXCElementSnapshot> snapshot = self.fb_cachedSnapshot ?: self.fb_takeSnapshot;
51
+ uid = [FBXCElementSnapshotWrapper ensureWrapped:snapshot].wdUID;
51
52
  }
52
53
 
53
54
  objc_setAssociatedObject(self, &XCUIELEMENT_CACHE_ID_KEY, uid, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
@@ -12,10 +12,8 @@
12
12
 
13
13
  #import "FBMacros.h"
14
14
  #import "FBElementTypeTransformer.h"
15
- #import "FBPredicate.h"
16
15
  #import "NSPredicate+FBFormat.h"
17
- #import "XCElementSnapshot.h"
18
- #import "XCElementSnapshot+FBHelpers.h"
16
+ #import "FBXCElementSnapshotWrapper+Helpers.h"
19
17
  #import "FBXCodeCompatibility.h"
20
18
  #import "XCUIElement+FBCaching.h"
21
19
  #import "XCUIElement+FBUtilities.h"
@@ -37,7 +35,7 @@
37
35
  return matchedElement ? @[matchedElement] : @[];
38
36
  }
39
37
 
40
- - (XCElementSnapshot *)fb_cachedSnapshotWithQuery:(XCUIElementQuery *)query
38
+ - (id<FBXCElementSnapshot>)fb_cachedSnapshotWithQuery:(XCUIElementQuery *)query
41
39
  {
42
40
  return [self isKindOfClass:XCUIApplication.class] ? query.rootElementSnapshot : self.fb_cachedSnapshot;
43
41
  }
@@ -51,7 +49,7 @@
51
49
  XCUIElementQuery *query = [self.fb_query descendantsMatchingType:type];
52
50
  NSMutableArray *result = [NSMutableArray array];
53
51
  [result addObjectsFromArray:[self.class fb_extractMatchingElementsFromQuery:query shouldReturnAfterFirstMatch:shouldReturnAfterFirstMatch]];
54
- XCElementSnapshot *cachedSnapshot = [self fb_cachedSnapshotWithQuery:query];
52
+ id<FBXCElementSnapshot> cachedSnapshot = [self fb_cachedSnapshotWithQuery:query];
55
53
  if (type == XCUIElementTypeAny || cachedSnapshot.elementType == type) {
56
54
  if (shouldReturnAfterFirstMatch || result.count == 0) {
57
55
  return @[self];
@@ -73,7 +71,9 @@
73
71
  return elements;
74
72
  }
75
73
 
76
- - (void)descendantsWithProperty:(NSString *)property value:(NSString *)value partial:(BOOL)partialSearch results:(NSMutableArray<XCUIElement *> *)results
74
+ - (void)descendantsWithProperty:(NSString *)property value:(NSString *)value
75
+ partial:(BOOL)partialSearch
76
+ results:(NSMutableArray<XCUIElement *> *)results
77
77
  {
78
78
  if (partialSearch) {
79
79
  NSString *text = [self fb_valueForWDAttributeName:property];
@@ -88,12 +88,12 @@
88
88
  }
89
89
 
90
90
  property = [FBElementUtils wdAttributeNameForAttributeName:property];
91
- value = [value stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];
92
- NSString *operation = partialSearch ?
93
- [NSString stringWithFormat:@"%@ like '*%@*'", property, value] :
94
- [NSString stringWithFormat:@"%@ == '%@'", property, value];
95
-
96
- NSPredicate *predicate = [FBPredicate predicateWithFormat:operation];
91
+ NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id<FBXCElementSnapshot> snapshot,
92
+ NSDictionary<NSString *,id> * _Nullable bindings) {
93
+ FBXCElementSnapshotWrapper *wrappedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:snapshot];
94
+ NSString *propertyValue = [NSString stringWithFormat:@"%@", [wrappedSnapshot fb_valueForWDAttributeName:property]];
95
+ return partialSearch ? [value containsString:propertyValue] : [value isEqualToString:propertyValue];
96
+ }];
97
97
  XCUIElementQuery *query = [[self.fb_query descendantsMatchingType:XCUIElementTypeAny] matchingPredicate:predicate];
98
98
  NSArray *childElements = query.fb_allMatches;
99
99
  [results addObjectsFromArray:childElements];
@@ -105,12 +105,12 @@
105
105
  - (NSArray<XCUIElement *> *)fb_descendantsMatchingPredicate:(NSPredicate *)predicate
106
106
  shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch
107
107
  {
108
- NSPredicate *formattedPredicate = [NSPredicate fb_formatSearchPredicate:predicate];
108
+ NSPredicate *formattedPredicate = [NSPredicate fb_snapshotBlockPredicateWithPredicate:predicate];
109
109
  XCUIElementQuery *query = [[self.fb_query descendantsMatchingType:XCUIElementTypeAny] matchingPredicate:formattedPredicate];
110
110
  NSMutableArray<XCUIElement *> *result = [NSMutableArray array];
111
111
  [result addObjectsFromArray:[self.class fb_extractMatchingElementsFromQuery:query
112
112
  shouldReturnAfterFirstMatch:shouldReturnAfterFirstMatch]];
113
- XCElementSnapshot *cachedSnapshot = [self fb_cachedSnapshotWithQuery:query];
113
+ id<FBXCElementSnapshot> cachedSnapshot = [self fb_cachedSnapshotWithQuery:query];
114
114
  // Include self element into predicate search
115
115
  if ([formattedPredicate evaluateWithObject:cachedSnapshot]) {
116
116
  if (shouldReturnAfterFirstMatch || result.count == 0) {
@@ -129,16 +129,16 @@
129
129
  {
130
130
  // XPath will try to match elements only class name, so requesting elements by XCUIElementTypeAny will not work. We should use '*' instead.
131
131
  xpathQuery = [xpathQuery stringByReplacingOccurrencesOfString:@"XCUIElementTypeAny" withString:@"*"];
132
- NSArray<XCElementSnapshot *> *matchingSnapshots = [FBXPath matchesWithRootElement:self forQuery:xpathQuery];
132
+ NSArray<id<FBXCElementSnapshot>> *matchingSnapshots = [FBXPath matchesWithRootElement:self forQuery:xpathQuery];
133
133
  if (0 == [matchingSnapshots count]) {
134
134
  return @[];
135
135
  }
136
136
  if (shouldReturnAfterFirstMatch) {
137
- XCElementSnapshot *snapshot = matchingSnapshots.firstObject;
137
+ id<FBXCElementSnapshot> snapshot = matchingSnapshots.firstObject;
138
138
  matchingSnapshots = @[snapshot];
139
139
  }
140
140
  return [self fb_filterDescendantsWithSnapshots:matchingSnapshots
141
- selfUID:self.lastSnapshot.wdUID
141
+ selfUID:[FBXCElementSnapshotWrapper ensureWrapped:self.lastSnapshot].wdUID
142
142
  onlyChildren:NO];
143
143
  }
144
144
 
@@ -148,7 +148,11 @@
148
148
  - (NSArray<XCUIElement *> *)fb_descendantsMatchingIdentifier:(NSString *)accessibilityId
149
149
  shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch
150
150
  {
151
- NSPredicate *predicate = [FBPredicate predicateWithFormat:@"name == %@", accessibilityId];
151
+ NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id<FBXCElementSnapshot> snapshot,
152
+ NSDictionary<NSString *,id> * _Nullable bindings) {
153
+ FBXCElementSnapshotWrapper *wrappedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:snapshot];
154
+ return [wrappedSnapshot.wdName isEqualToString:accessibilityId];
155
+ }];
152
156
  return [self fb_descendantsMatchingPredicate:predicate
153
157
  shouldReturnAfterFirstMatch:shouldReturnAfterFirstMatch];
154
158
  }
@@ -7,8 +7,7 @@
7
7
  * of patent rights can be found in the PATENTS file in the same directory.
8
8
  */
9
9
 
10
- #import <WebDriverAgentLib/XCElementSnapshot.h>
11
- #import <WebDriverAgentLib/XCUIElement.h>
10
+ #import "FBXCElementSnapshotWrapper.h"
12
11
 
13
12
  NS_ASSUME_NONNULL_BEGIN
14
13
 
@@ -20,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
20
19
  @end
21
20
 
22
21
 
23
- @interface XCElementSnapshot (FBIsVisible)
22
+ @interface FBXCElementSnapshotWrapper (FBIsVisible)
24
23
 
25
24
  /*! Whether or not the element is visible */
26
25
  @property (atomic, readonly) BOOL fb_isVisible;
@@ -14,10 +14,9 @@
14
14
  #import "FBMathUtils.h"
15
15
  #import "FBActiveAppDetectionPoint.h"
16
16
  #import "FBSession.h"
17
+ #import "FBXCAccessibilityElement.h"
17
18
  #import "FBXCodeCompatibility.h"
18
- #import "XCAccessibilityElement+FBComparison.h"
19
- #import "XCElementSnapshot+FBHelpers.h"
20
- #import "XCElementSnapshot+FBHitPoint.h"
19
+ #import "FBXCElementSnapshotWrapper+Helpers.h"
21
20
  #import "XCUIElement+FBUtilities.h"
22
21
  #import "XCUIElement+FBUID.h"
23
22
  #import "XCTestPrivateSymbols.h"
@@ -26,13 +25,14 @@
26
25
 
27
26
  - (BOOL)fb_isVisible
28
27
  {
29
- return [self fb_snapshotWithAttributes:@[FB_XCAXAIsVisibleAttributeName]
30
- maxDepth:@1].fb_isVisible;
28
+ id<FBXCElementSnapshot> snapshot = [self fb_snapshotWithAttributes:@[FB_XCAXAIsVisibleAttributeName]
29
+ maxDepth:@1];
30
+ return [FBXCElementSnapshotWrapper ensureWrapped:snapshot].fb_isVisible;
31
31
  }
32
32
 
33
33
  @end
34
34
 
35
- @implementation XCElementSnapshot (FBIsVisible)
35
+ @implementation FBXCElementSnapshotWrapper (FBIsVisible)
36
36
 
37
37
  - (NSString *)fb_uniqId
38
38
  {
@@ -57,7 +57,7 @@
57
57
  }
58
58
 
59
59
  - (BOOL)fb_cacheVisibilityWithValue:(BOOL)isVisible
60
- forAncestors:(nullable NSArray<XCElementSnapshot *> *)ancestors
60
+ forAncestors:(nullable NSArray<id<FBXCElementSnapshot>> *)ancestors
61
61
  {
62
62
  NSMutableDictionary *cache = FBSession.activeSession.elementsVisibilityCache;
63
63
  if (nil == cache) {
@@ -72,8 +72,8 @@
72
72
  [destination setObject:visibleObj forKey:self.fb_uniqId];
73
73
  if (isVisible && nil != ancestors) {
74
74
  // if an element is visible then all its ancestors must be visible as well
75
- for (XCElementSnapshot *ancestor in ancestors) {
76
- NSString *ancestorId = ancestor.fb_uniqId;
75
+ for (id<FBXCElementSnapshot> ancestor in ancestors) {
76
+ NSString *ancestorId = [FBXCElementSnapshotWrapper ensureWrapped:ancestor].fb_uniqId;
77
77
  if (nil == [destination objectForKey:ancestorId]) {
78
78
  [destination setObject:visibleObj forKey:ancestorId];
79
79
  }
@@ -82,17 +82,17 @@
82
82
  return isVisible;
83
83
  }
84
84
 
85
- - (CGRect)fb_frameInContainer:(XCElementSnapshot *)container
85
+ - (CGRect)fb_frameInContainer:(id<FBXCElementSnapshot>)container
86
86
  hierarchyIntersection:(nullable NSValue *)intersectionRectange
87
87
  {
88
88
  CGRect currentRectangle = nil == intersectionRectange ? self.frame : [intersectionRectange CGRectValue];
89
- XCElementSnapshot *parent = self.parent;
89
+ id<FBXCElementSnapshot> parent = self.parent;
90
90
  CGRect parentFrame = parent.frame;
91
91
  CGRect containerFrame = container.frame;
92
92
  if (CGSizeEqualToSize(parentFrame.size, CGSizeZero) &&
93
93
  CGPointEqualToPoint(parentFrame.origin, CGPointZero)) {
94
94
  // Special case (or XCTest bug). Shift the origin and return immediately after shift
95
- XCElementSnapshot *nextParent = parent.parent;
95
+ id<FBXCElementSnapshot> nextParent = parent.parent;
96
96
  BOOL isGrandparent = YES;
97
97
  while (nextParent && nextParent != container) {
98
98
  CGRect nextParentFrame = nextParent.frame;
@@ -133,18 +133,19 @@
133
133
  if (CGRectIsEmpty(intersectionWithParent) || parent == container) {
134
134
  return intersectionWithParent;
135
135
  }
136
- return [parent fb_frameInContainer:container hierarchyIntersection:[NSValue valueWithCGRect:intersectionWithParent]];
136
+ return [[FBXCElementSnapshotWrapper ensureWrapped:parent] fb_frameInContainer:container
137
+ hierarchyIntersection:[NSValue valueWithCGRect:intersectionWithParent]];
137
138
  }
138
139
 
139
140
  - (BOOL)fb_hasAnyVisibleLeafs
140
141
  {
141
- NSArray<XCElementSnapshot *> *children = self.children;
142
+ NSArray<id<FBXCElementSnapshot>> *children = self.children;
142
143
  if (0 == children.count) {
143
144
  return self.fb_isVisible;
144
145
  }
145
146
 
146
- for (XCElementSnapshot *child in children) {
147
- if (child.fb_hasAnyVisibleLeafs) {
147
+ for (id<FBXCElementSnapshot> child in children) {
148
+ if ([FBXCElementSnapshotWrapper ensureWrapped:child].fb_hasAnyVisibleLeafs) {
148
149
  return YES;
149
150
  }
150
151
  }
@@ -169,13 +170,13 @@
169
170
  return [self fb_cacheVisibilityWithValue:NO forAncestors:nil];
170
171
  }
171
172
 
172
- NSArray<XCElementSnapshot *> *ancestors = self.fb_ancestors;
173
+ NSArray<id<FBXCElementSnapshot>> *ancestors = self.fb_ancestors;
173
174
  if ([FBConfiguration shouldUseTestManagerForVisibilityDetection]) {
174
175
  BOOL visibleAttrValue = [(NSNumber *)[self fb_attributeValue:FB_XCAXAIsVisibleAttributeName] boolValue];
175
176
  return [self fb_cacheVisibilityWithValue:visibleAttrValue forAncestors:ancestors];
176
177
  }
177
178
 
178
- XCElementSnapshot *parentWindow = ancestors.count > 1 ? [ancestors objectAtIndex:ancestors.count - 2] : nil;
179
+ id<FBXCElementSnapshot> parentWindow = ancestors.count > 1 ? [ancestors objectAtIndex:ancestors.count - 2] : nil;
179
180
  CGRect visibleRect = selfFrame;
180
181
  if (nil != parentWindow) {
181
182
  visibleRect = [self fb_frameInContainer:parentWindow hierarchyIntersection:nil];
@@ -186,7 +187,7 @@
186
187
  CGPoint midPoint = CGPointMake(visibleRect.origin.x + visibleRect.size.width / 2,
187
188
  visibleRect.origin.y + visibleRect.size.height / 2);
188
189
  #if !TARGET_OS_TV // TV has no orientation, so it does not need to coordinate
189
- XCElementSnapshot *appElement = ancestors.count > 0 ? [ancestors lastObject] : self;
190
+ id<FBXCElementSnapshot> appElement = ancestors.count > 0 ? [ancestors lastObject] : self;
190
191
  CGRect appFrame = appElement.frame;
191
192
  CGRect windowFrame = nil == parentWindow ? selfFrame : parentWindow.frame;
192
193
  if ((appFrame.size.height > appFrame.size.width && windowFrame.size.height < windowFrame.size.width) ||
@@ -197,22 +198,23 @@
197
198
  midPoint = FBInvertPointForApplication(midPoint, appFrame.size, FBApplication.fb_activeApplication.interfaceOrientation);
198
199
  }
199
200
  #endif
200
- XCAccessibilityElement *hitElement = [FBActiveAppDetectionPoint axElementWithPoint:midPoint];
201
+ id<FBXCAccessibilityElement> hitElement = [FBActiveAppDetectionPoint axElementWithPoint:midPoint];
201
202
  if (nil != hitElement) {
202
- if ([self.accessibilityElement fb_isEqualToElement:hitElement]) {
203
+ if (FBIsAXElementEqualToOther(self.accessibilityElement, hitElement)) {
203
204
  return [self fb_cacheVisibilityWithValue:YES forAncestors:ancestors];
204
205
  }
205
- for (XCElementSnapshot *ancestor in ancestors) {
206
- if ([hitElement fb_isEqualToElement:ancestor.accessibilityElement]) {
206
+ for (id<FBXCElementSnapshot> ancestor in ancestors) {
207
+ if (FBIsAXElementEqualToOther(hitElement, ancestor.accessibilityElement)) {
207
208
  return [self fb_cacheVisibilityWithValue:YES forAncestors:ancestors];
208
209
  }
209
210
  }
210
211
  }
211
212
  if (self.children.count > 0) {
212
213
  if (nil != hitElement) {
213
- for (XCElementSnapshot *descendant in self._allDescendants) {
214
- if ([hitElement fb_isEqualToElement:descendant.accessibilityElement]) {
215
- return [self fb_cacheVisibilityWithValue:YES forAncestors:descendant.fb_ancestors];
214
+ for (id<FBXCElementSnapshot> descendant in self._allDescendants) {
215
+ if (FBIsAXElementEqualToOther(hitElement, descendant.accessibilityElement)) {
216
+ return [self fb_cacheVisibilityWithValue:YES
217
+ forAncestors:[FBXCElementSnapshotWrapper ensureWrapped:descendant].fb_ancestors];
216
218
  }
217
219
  }
218
220
  }
@@ -10,6 +10,7 @@
10
10
  #import "XCUIElement+FBPickerWheel.h"
11
11
 
12
12
  #import "FBRunLoopSpinner.h"
13
+ #import "FBXCElementSnapshot.h"
13
14
  #import "FBXCodeCompatibility.h"
14
15
  #import "XCUIApplication+FBTouchAction.h"
15
16
  #import "XCUICoordinate.h"
@@ -23,7 +24,7 @@ static const NSTimeInterval VALUE_CHANGE_TIMEOUT = 2;
23
24
 
24
25
  - (BOOL)fb_scrollWithOffset:(CGFloat)relativeHeightOffset error:(NSError **)error
25
26
  {
26
- XCElementSnapshot *snapshot = self.fb_isResolvedFromCache.boolValue
27
+ id<FBXCElementSnapshot> snapshot = self.fb_isResolvedFromCache.boolValue
27
28
  ? self.lastSnapshot
28
29
  : self.fb_takeSnapshot;
29
30
  NSString *previousValue = snapshot.value;
@@ -41,11 +41,15 @@ static char XCUIELEMENT_IS_RESOLVED_NATIVELY_KEY;
41
41
  XCUIElementQuery *query = [self isKindOfClass:XCUIApplication.class]
42
42
  ? self.application.fb_query
43
43
  : [self.application.fb_query descendantsMatchingType:XCUIElementTypeAny];
44
- XCElementSnapshot *cachedSnapshot = self.fb_cachedSnapshot;
44
+ FBXCElementSnapshotWrapper *cachedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:self.fb_cachedSnapshot];
45
45
  NSString *uid = nil == cachedSnapshot ? self.fb_uid : cachedSnapshot.fb_uid;
46
- return nil == uid
47
- ? self
48
- : [query matchingPredicate:[NSPredicate predicateWithFormat:@"%K = %@", FBStringify(XCUIElement, wdUID), uid]].fb_firstMatch;
46
+ if (nil == uid) {
47
+ return self;
48
+ }
49
+ NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id<FBXCElementSnapshot> snapshot, NSDictionary *bindings) {
50
+ return [[FBXCElementSnapshotWrapper ensureWrapped:snapshot].fb_uid isEqualToString:uid];
51
+ }];
52
+ return (XCUIElement *)[query matchingPredicate:predicate].fb_firstMatch;
49
53
  }
50
54
 
51
55
  @end
@@ -60,6 +60,16 @@ typedef NS_ENUM(NSUInteger, FBXCUIElementScrollDirection) {
60
60
  */
61
61
  - (BOOL)fb_scrollToVisibleWithError:(NSError **)error;
62
62
 
63
+ /**
64
+ Scrolls parent scroll view till the current element is visible.
65
+ This call is fast as it uses a native XCTest implementation.
66
+ The element must be hittable.
67
+
68
+ @param error If there is an error, upon return contains an NSError object that describes the problem.
69
+ @return YES if the operation succeeds, otherwise NO.
70
+ */
71
+ - (BOOL)fb_nativeScrollToVisibleWithError:(NSError **)error;
72
+
63
73
  /**
64
74
  Scrolls parent scroll view till receiver is visible. Whenever element is invisible it scrolls by normalizedScrollDistance
65
75
  in its direction. E.g. if normalizedScrollDistance is equal to 0.5, each step will scroll by half of scroll view's size.
@@ -13,12 +13,11 @@
13
13
  #import "FBLogger.h"
14
14
  #import "FBMacros.h"
15
15
  #import "FBMathUtils.h"
16
- #import "FBPredicate.h"
17
16
  #import "FBXCodeCompatibility.h"
17
+ #import "FBXCElementSnapshotWrapper.h"
18
+ #import "FBXCElementSnapshotWrapper+Helpers.h"
18
19
  #import "XCUIApplication+FBTouchAction.h"
19
20
  #import "XCUIElement+FBCaching.h"
20
- #import "XCElementSnapshot+FBHelpers.h"
21
- #import "XCElementSnapshot.h"
22
21
  #import "XCUIApplication.h"
23
22
  #import "XCUICoordinate.h"
24
23
  #import "XCUICoordinate+FBFix.h"
@@ -34,7 +33,7 @@ const CGFloat FBScrollTouchProportion = 0.75f;
34
33
 
35
34
  #if !TARGET_OS_TV
36
35
 
37
- @interface XCElementSnapshot (FBScrolling)
36
+ @interface FBXCElementSnapshotWrapper (FBScrolling)
38
37
 
39
38
  - (void)fb_scrollUpByNormalizedDistance:(CGFloat)distance inApplication:(XCUIApplication *)application;
40
39
  - (void)fb_scrollDownByNormalizedDistance:(CGFloat)distance inApplication:(XCUIApplication *)application;
@@ -47,36 +46,49 @@ const CGFloat FBScrollTouchProportion = 0.75f;
47
46
 
48
47
  @implementation XCUIElement (FBScrolling)
49
48
 
49
+ - (BOOL)fb_nativeScrollToVisibleWithError:(NSError **)error
50
+ {
51
+ id<FBXCElementSnapshot> snapshot = self.fb_isResolvedFromCache.boolValue
52
+ ? self.lastSnapshot
53
+ : self.fb_takeSnapshot;
54
+ return nil != [self _hitPointByAttemptingToScrollToVisibleSnapshot:snapshot
55
+ error:error];
56
+ }
57
+
50
58
  - (void)fb_scrollUpByNormalizedDistance:(CGFloat)distance
51
59
  {
52
- XCElementSnapshot *snapshot = self.fb_isResolvedFromCache.boolValue
60
+ id<FBXCElementSnapshot> snapshot = self.fb_isResolvedFromCache.boolValue
53
61
  ? self.lastSnapshot
54
62
  : self.fb_takeSnapshot;
55
- [snapshot fb_scrollUpByNormalizedDistance:distance inApplication:self.application];
63
+ [[FBXCElementSnapshotWrapper ensureWrapped:snapshot] fb_scrollUpByNormalizedDistance:distance
64
+ inApplication:self.application];
56
65
  }
57
66
 
58
67
  - (void)fb_scrollDownByNormalizedDistance:(CGFloat)distance
59
68
  {
60
- XCElementSnapshot *snapshot = self.fb_isResolvedFromCache.boolValue
69
+ id<FBXCElementSnapshot> snapshot = self.fb_isResolvedFromCache.boolValue
61
70
  ? self.lastSnapshot
62
71
  : self.fb_takeSnapshot;
63
- [snapshot fb_scrollDownByNormalizedDistance:distance inApplication:self.application];
72
+ [[FBXCElementSnapshotWrapper ensureWrapped:snapshot] fb_scrollDownByNormalizedDistance:distance
73
+ inApplication:self.application];
64
74
  }
65
75
 
66
76
  - (void)fb_scrollLeftByNormalizedDistance:(CGFloat)distance
67
77
  {
68
- XCElementSnapshot *snapshot = self.fb_isResolvedFromCache.boolValue
78
+ id<FBXCElementSnapshot> snapshot = self.fb_isResolvedFromCache.boolValue
69
79
  ? self.lastSnapshot
70
80
  : self.fb_takeSnapshot;
71
- [snapshot fb_scrollLeftByNormalizedDistance:distance inApplication:self.application];
81
+ [[FBXCElementSnapshotWrapper ensureWrapped:snapshot] fb_scrollLeftByNormalizedDistance:distance
82
+ inApplication:self.application];
72
83
  }
73
84
 
74
85
  - (void)fb_scrollRightByNormalizedDistance:(CGFloat)distance
75
86
  {
76
- XCElementSnapshot *snapshot = self.fb_isResolvedFromCache.boolValue
87
+ id<FBXCElementSnapshot> snapshot = self.fb_isResolvedFromCache.boolValue
77
88
  ? self.lastSnapshot
78
89
  : self.fb_takeSnapshot;
79
- [snapshot fb_scrollRightByNormalizedDistance:distance inApplication:self.application];
90
+ [[FBXCElementSnapshotWrapper ensureWrapped:snapshot] fb_scrollRightByNormalizedDistance:distance
91
+ inApplication:self.application];
80
92
  }
81
93
 
82
94
  - (BOOL)fb_scrollToVisibleWithError:(NSError **)error
@@ -91,9 +103,11 @@ const CGFloat FBScrollTouchProportion = 0.75f;
91
103
  error:error];
92
104
  }
93
105
 
94
- - (BOOL)fb_scrollToVisibleWithNormalizedScrollDistance:(CGFloat)normalizedScrollDistance scrollDirection:(FBXCUIElementScrollDirection)scrollDirection error:(NSError **)error
106
+ - (BOOL)fb_scrollToVisibleWithNormalizedScrollDistance:(CGFloat)normalizedScrollDistance
107
+ scrollDirection:(FBXCUIElementScrollDirection)scrollDirection
108
+ error:(NSError **)error
95
109
  {
96
- XCElementSnapshot *prescrollSnapshot = [self fb_takeSnapshot];
110
+ FBXCElementSnapshotWrapper *prescrollSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:[self fb_takeSnapshot]];
97
111
  if (prescrollSnapshot.isWDVisible) {
98
112
  return YES;
99
113
  }
@@ -109,23 +123,30 @@ const CGFloat FBScrollTouchProportion = 0.75f;
109
123
  ];
110
124
  });
111
125
 
112
- __block NSArray<XCElementSnapshot *> *cellSnapshots, *visibleCellSnapshots;
113
- XCElementSnapshot *scrollView = [prescrollSnapshot fb_parentMatchingOneOfTypes:acceptedParents
114
- filter:^(XCElementSnapshot *snapshot) {
115
-
116
- if (![snapshot isWDVisible]) {
117
- return NO;
118
- }
119
-
120
- cellSnapshots = [snapshot fb_descendantsCellSnapshots];
126
+ __block NSArray<id<FBXCElementSnapshot>> *cellSnapshots;
127
+ __block NSMutableArray<id<FBXCElementSnapshot>> *visibleCellSnapshots = [NSMutableArray new];
128
+ id<FBXCElementSnapshot> scrollView = [prescrollSnapshot fb_parentMatchingOneOfTypes:acceptedParents
129
+ filter:^(id<FBXCElementSnapshot> snapshot) {
130
+ FBXCElementSnapshotWrapper *wrappedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:snapshot];
131
+
132
+ if (![wrappedSnapshot isWDVisible]) {
133
+ return NO;
134
+ }
121
135
 
122
- visibleCellSnapshots = [cellSnapshots filteredArrayUsingPredicate:[FBPredicate predicateWithFormat:@"%K == YES", FBStringify(XCUIElement, fb_isVisible)]];
136
+ cellSnapshots = [wrappedSnapshot fb_descendantsCellSnapshots];
137
+
138
+ for (id<FBXCElementSnapshot> cellSnapshot in cellSnapshots) {
139
+ FBXCElementSnapshotWrapper *wrappedCellSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:cellSnapshot];
140
+ if (wrappedCellSnapshot.wdVisible) {
141
+ [visibleCellSnapshots addObject:cellSnapshot];
142
+ }
143
+ }
123
144
 
124
- if (visibleCellSnapshots.count > 1) {
125
- return YES;
126
- }
127
- return NO;
128
- }];
145
+ if (visibleCellSnapshots.count > 1) {
146
+ return YES;
147
+ }
148
+ return NO;
149
+ }];
129
150
 
130
151
  if (scrollView == nil) {
131
152
  return
@@ -134,20 +155,21 @@ const CGFloat FBScrollTouchProportion = 0.75f;
134
155
  buildError:error];
135
156
  }
136
157
 
137
- XCElementSnapshot *targetCellSnapshot = [prescrollSnapshot fb_parentCellSnapshot];
138
- XCElementSnapshot *lastSnapshot = visibleCellSnapshots.lastObject;
158
+ id<FBXCElementSnapshot> targetCellSnapshot = [prescrollSnapshot fb_parentCellSnapshot];
159
+ id<FBXCElementSnapshot> lastSnapshot = visibleCellSnapshots.lastObject;
139
160
  // Can't just do indexOfObject, because targetCellSnapshot may represent the same object represented by a member of cellSnapshots, yet be a different object
140
161
  // than that member. This reflects the fact that targetCellSnapshot came out of self.fb_parentCellSnapshot, not out of cellSnapshots directly.
141
162
  // If the result is NSNotFound, we'll just proceed by scrolling downward/rightward, since NSNotFound will always be larger than the current index.
142
- NSUInteger targetCellIndex = [cellSnapshots indexOfObjectPassingTest:^BOOL(XCElementSnapshot *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
163
+ NSUInteger targetCellIndex = [cellSnapshots indexOfObjectPassingTest:^BOOL(id<FBXCElementSnapshot> _Nonnull obj,
164
+ NSUInteger idx, BOOL *_Nonnull stop) {
143
165
  return [obj _matchesElement:targetCellSnapshot];
144
166
  }];
145
167
  NSUInteger visibleCellIndex = [cellSnapshots indexOfObject:lastSnapshot];
146
168
 
147
169
  if (scrollDirection == FBXCUIElementScrollDirectionUnknown) {
148
170
  // Try to determine the scroll direction by determining the vector between the first and last visible cells
149
- XCElementSnapshot *firstVisibleCell = visibleCellSnapshots.firstObject;
150
- XCElementSnapshot *lastVisibleCell = visibleCellSnapshots.lastObject;
171
+ id<FBXCElementSnapshot> firstVisibleCell = visibleCellSnapshots.firstObject;
172
+ id<FBXCElementSnapshot> lastVisibleCell = visibleCellSnapshots.lastObject;
151
173
  CGVector cellGrowthVector = CGVectorMake(firstVisibleCell.frame.origin.x - lastVisibleCell.frame.origin.x,
152
174
  firstVisibleCell.frame.origin.y - lastVisibleCell.frame.origin.y
153
175
  );
@@ -160,17 +182,22 @@ const CGFloat FBScrollTouchProportion = 0.75f;
160
182
 
161
183
  const NSUInteger maxScrollCount = 25;
162
184
  NSUInteger scrollCount = 0;
185
+ FBXCElementSnapshotWrapper *scrollViewWrapped = [FBXCElementSnapshotWrapper ensureWrapped:scrollView];
163
186
  // Scrolling till cell is visible and get current value of frames
164
187
  while (![self fb_isEquivalentElementSnapshotVisible:prescrollSnapshot] && scrollCount < maxScrollCount) {
165
188
  if (targetCellIndex < visibleCellIndex) {
166
189
  scrollDirection == FBXCUIElementScrollDirectionVertical ?
167
- [scrollView fb_scrollUpByNormalizedDistance:normalizedScrollDistance inApplication:self.application] :
168
- [scrollView fb_scrollLeftByNormalizedDistance:normalizedScrollDistance inApplication:self.application];
190
+ [scrollViewWrapped fb_scrollUpByNormalizedDistance:normalizedScrollDistance
191
+ inApplication:self.application] :
192
+ [scrollViewWrapped fb_scrollLeftByNormalizedDistance:normalizedScrollDistance
193
+ inApplication:self.application];
169
194
  }
170
195
  else {
171
196
  scrollDirection == FBXCUIElementScrollDirectionVertical ?
172
- [scrollView fb_scrollDownByNormalizedDistance:normalizedScrollDistance inApplication:self.application] :
173
- [scrollView fb_scrollRightByNormalizedDistance:normalizedScrollDistance inApplication:self.application];
197
+ [scrollViewWrapped fb_scrollDownByNormalizedDistance:normalizedScrollDistance
198
+ inApplication:self.application] :
199
+ [scrollViewWrapped fb_scrollRightByNormalizedDistance:normalizedScrollDistance
200
+ inApplication:self.application];
174
201
  }
175
202
  scrollCount++;
176
203
  // Wait for scroll animation
@@ -187,28 +214,32 @@ const CGFloat FBScrollTouchProportion = 0.75f;
187
214
  // Cell is now visible, but it might be only partialy visible, scrolling till whole frame is visible.
188
215
  // Sometimes, attempting to grab the parent snapshot of the target cell after scrolling is complete causes a stale element reference exception.
189
216
  // Trying fb_cachedSnapshot first
190
- targetCellSnapshot = [([self fb_cachedSnapshot] ?: [self fb_takeSnapshot]) fb_parentCellSnapshot];
191
- CGRect visibleFrame = targetCellSnapshot.fb_visibleFrameWithFallback;
217
+ FBXCElementSnapshotWrapper *targetCellSnapshotWrapped = [FBXCElementSnapshotWrapper ensureWrapped:([self fb_cachedSnapshot] ?: [self fb_takeSnapshot])];
218
+ targetCellSnapshot = [targetCellSnapshotWrapped fb_parentCellSnapshot];
219
+ CGRect visibleFrame = [FBXCElementSnapshotWrapper ensureWrapped:targetCellSnapshot].fb_visibleFrameWithFallback;
192
220
 
193
221
  CGVector scrollVector = CGVectorMake(visibleFrame.size.width - targetCellSnapshot.frame.size.width,
194
222
  visibleFrame.size.height - targetCellSnapshot.frame.size.height
195
223
  );
196
- if (![scrollView fb_scrollByVector:scrollVector inApplication:self.application error:error]) {
197
- return NO;
198
- }
199
- return YES;
224
+ return [scrollViewWrapped fb_scrollByVector:scrollVector
225
+ inApplication:self.application
226
+ error:error];
200
227
  }
201
228
 
202
- - (BOOL)fb_isEquivalentElementSnapshotVisible:(XCElementSnapshot *)snapshot
229
+ - (BOOL)fb_isEquivalentElementSnapshotVisible:(id<FBXCElementSnapshot>)snapshot
203
230
  {
204
- if (snapshot.isWDVisible) {
231
+ FBXCElementSnapshotWrapper *wrappedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:snapshot];
232
+
233
+ if (wrappedSnapshot.isWDVisible) {
205
234
  return YES;
206
235
  }
207
236
 
208
- XCElementSnapshot *appSnapshot = [self.application fb_takeSnapshot];
209
- for (XCElementSnapshot *elementSnapshot in appSnapshot._allDescendants.copy) {
237
+ id<FBXCElementSnapshot> appSnapshot = [self.application fb_takeSnapshot];
238
+ for (id<FBXCElementSnapshot> elementSnapshot in appSnapshot._allDescendants.copy) {
239
+ FBXCElementSnapshotWrapper *wrappedElementSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:elementSnapshot];
210
240
  // We are comparing pre-scroll snapshot so frames are irrelevant.
211
- if ([snapshot fb_framelessFuzzyMatchesElement:elementSnapshot] && elementSnapshot.isWDVisible) {
241
+ if ([wrappedSnapshot fb_framelessFuzzyMatchesElement:elementSnapshot]
242
+ && wrappedElementSnapshot.isWDVisible) {
212
243
  return YES;
213
244
  }
214
245
  }
@@ -218,7 +249,7 @@ const CGFloat FBScrollTouchProportion = 0.75f;
218
249
  @end
219
250
 
220
251
 
221
- @implementation XCElementSnapshot (FBScrolling)
252
+ @implementation FBXCElementSnapshotWrapper (FBScrolling)
222
253
 
223
254
  - (CGRect)scrollingFrame
224
255
  {