appium-webdriveragent 8.12.2 → 9.0.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 (56) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/PrivateHeaders/XCTest/XCTestManager_ManagerInterface-Protocol.h +1 -0
  3. package/WebDriverAgent.xcodeproj/project.pbxproj +12 -0
  4. package/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.h +4 -9
  5. package/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.m +13 -25
  6. package/WebDriverAgentLib/Categories/XCUIApplication+FBAlert.m +2 -2
  7. package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +4 -20
  8. package/WebDriverAgentLib/Categories/XCUIElement+FBAccessibility.m +15 -4
  9. package/WebDriverAgentLib/Categories/XCUIElement+FBCaching.h +0 -3
  10. package/WebDriverAgentLib/Categories/XCUIElement+FBCaching.m +1 -22
  11. package/WebDriverAgentLib/Categories/XCUIElement+FBFind.m +1 -1
  12. package/WebDriverAgentLib/Categories/XCUIElement+FBIsVisible.m +28 -173
  13. package/WebDriverAgentLib/Categories/XCUIElement+FBPickerWheel.m +1 -3
  14. package/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.m +33 -30
  15. package/WebDriverAgentLib/Categories/XCUIElement+FBTyping.m +4 -8
  16. package/WebDriverAgentLib/Categories/XCUIElement+FBUID.m +2 -2
  17. package/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.h +3 -36
  18. package/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.m +10 -68
  19. package/WebDriverAgentLib/Categories/XCUIElement+FBVisibleFrame.h +36 -0
  20. package/WebDriverAgentLib/Categories/XCUIElement+FBVisibleFrame.m +53 -0
  21. package/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m +7 -20
  22. package/WebDriverAgentLib/Commands/FBCustomCommands.m +2 -1
  23. package/WebDriverAgentLib/Commands/FBElementCommands.m +25 -47
  24. package/WebDriverAgentLib/Commands/FBFindElementCommands.m +10 -9
  25. package/WebDriverAgentLib/Commands/FBSessionCommands.m +0 -8
  26. package/WebDriverAgentLib/FBAlert.m +6 -5
  27. package/WebDriverAgentLib/Info.plist +2 -2
  28. package/WebDriverAgentLib/Routing/FBElementCache.h +4 -10
  29. package/WebDriverAgentLib/Routing/FBElementCache.m +4 -33
  30. package/WebDriverAgentLib/Routing/FBResponsePayload.m +1 -7
  31. package/WebDriverAgentLib/Utilities/FBConfiguration.h +0 -8
  32. package/WebDriverAgentLib/Utilities/FBConfiguration.m +0 -12
  33. package/WebDriverAgentLib/Utilities/FBSettings.h +0 -3
  34. package/WebDriverAgentLib/Utilities/FBSettings.m +0 -2
  35. package/WebDriverAgentLib/Utilities/FBTVNavigationTracker.m +1 -3
  36. package/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.m +1 -1
  37. package/WebDriverAgentLib/Utilities/FBXCAXClientProxy.h +4 -3
  38. package/WebDriverAgentLib/Utilities/FBXCAXClientProxy.m +7 -11
  39. package/WebDriverAgentLib/Utilities/FBXCodeCompatibility.h +3 -1
  40. package/WebDriverAgentLib/Utilities/FBXCodeCompatibility.m +2 -2
  41. package/WebDriverAgentLib/Utilities/FBXPath.m +1 -4
  42. package/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.h +3 -0
  43. package/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.m +1 -0
  44. package/WebDriverAgentTests/IntegrationTests/FBElementAttributeTests.m +2 -1
  45. package/WebDriverAgentTests/IntegrationTests/FBElementSwipingTests.m +1 -1
  46. package/WebDriverAgentTests/IntegrationTests/FBW3CTouchActionsIntegrationTests.m +1 -1
  47. package/WebDriverAgentTests/IntegrationTests/FBXPathIntegrationTests.m +2 -2
  48. package/WebDriverAgentTests/IntegrationTests/XCElementSnapshotHelperTests.m +21 -10
  49. package/WebDriverAgentTests/IntegrationTests/XCElementSnapshotHitPointTests.m +1 -1
  50. package/WebDriverAgentTests/IntegrationTests/XCUIElementHelperIntegrationTests.m +5 -3
  51. package/WebDriverAgentTests/UnitTests/Doubles/XCElementSnapshotDouble.m +10 -0
  52. package/WebDriverAgentTests/UnitTests/Doubles/XCUIElementDouble.h +1 -2
  53. package/WebDriverAgentTests/UnitTests/Doubles/XCUIElementDouble.m +1 -1
  54. package/WebDriverAgentTests/UnitTests/FBElementCacheTests.m +0 -2
  55. package/WebDriverAgentTests/UnitTests_tvOS/Doubles/XCUIElementDouble.h +0 -1
  56. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [9.0.0](https://github.com/appium/WebDriverAgent/compare/v8.12.2...v9.0.0) (2025-01-16)
2
+
3
+ ### ⚠ BREAKING CHANGES
4
+
5
+ * snapshotTimeout and customSnapshotTimeout settings have been removed as a result of the custom snapshotting logic removal
6
+
7
+ ### Features
8
+
9
+ * Refactor snapshotting mechanism ([#970](https://github.com/appium/WebDriverAgent/issues/970)) ([08f1306](https://github.com/appium/WebDriverAgent/commit/08f13060119c710f53b34a98c95683287c0365a0))
10
+
1
11
  ## [8.12.2](https://github.com/appium/WebDriverAgent/compare/v8.12.1...v8.12.2) (2025-01-13)
2
12
 
3
13
  ### Miscellaneous Chores
@@ -27,6 +27,7 @@
27
27
  - (void)_XCT_requestElementAtPoint:(CGPoint)arg1 reply:(void (^)(id/*XCAccessibilityElement*/, NSError *))arg2;
28
28
  - (void)_XCT_fetchParameterizedAttributeForElement:(id/*XCAccessibilityElement*/)arg1 attributes:(NSNumber *)arg2 parameter:(id)arg3 reply:(void (^)(id, NSError *))arg4;
29
29
  - (void)_XCT_setAttribute:(NSNumber *)arg1 value:(id)arg2 element:(id/*XCAccessibilityElement*/)arg3 reply:(void (^)(BOOL, NSError *))arg4;
30
+ - (void)_XCT_fetchAttributes:(id)attributes forElement:(id)element reply:(void (^)(NSDictionary *, NSError *))reply;
30
31
  - (void)_XCT_fetchAttributesForElement:(id/*XCAccessibilityElement*/)arg1 attributes:(NSArray *)arg2 reply:(void (^)(NSDictionary *, NSError *))arg3;
31
32
  - (void)_XCT_terminateApplicationWithBundleID:(NSString *)arg1 completion:(void (^)(NSError *))arg2;
32
33
  - (void)_XCT_performAccessibilityAction:(int)arg1 onElement:(id/*XCAccessibilityElement*/)arg2 withValue:(id)arg3 reply:(void (^)(NSError *))arg4;
@@ -451,6 +451,10 @@
451
451
  71A7EAFA1E224648001DA4F2 /* FBClassChainQueryParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 71A7EAF81E224648001DA4F2 /* FBClassChainQueryParser.m */; };
452
452
  71A7EAFC1E229302001DA4F2 /* FBClassChainTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 71A7EAFB1E229302001DA4F2 /* FBClassChainTests.m */; };
453
453
  71ACF5B8242F2FDC00F0AAD4 /* FBSafariAlertTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 71ACF5B7242F2FDC00F0AAD4 /* FBSafariAlertTests.m */; };
454
+ 71AE3CF72D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 71AE3CF52D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.h */; };
455
+ 71AE3CF82D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 71AE3CF62D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.m */; };
456
+ 71AE3CF92D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 71AE3CF52D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.h */; };
457
+ 71AE3CFA2D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 71AE3CF62D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.m */; };
454
458
  71B155DA23070ECF00646AFB /* FBHTTPStatusCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 71B155D923070ECF00646AFB /* FBHTTPStatusCodes.h */; settings = {ATTRIBUTES = (Public, ); }; };
455
459
  71B155DC230711E900646AFB /* FBCommandStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 71B155DB230711E900646AFB /* FBCommandStatus.m */; };
456
460
  71B155DF23080CA600646AFB /* FBProtocolHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 71B155DD23080CA600646AFB /* FBProtocolHelpers.h */; };
@@ -1050,6 +1054,8 @@
1050
1054
  71A7EAF81E224648001DA4F2 /* FBClassChainQueryParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBClassChainQueryParser.m; sourceTree = "<group>"; };
1051
1055
  71A7EAFB1E229302001DA4F2 /* FBClassChainTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBClassChainTests.m; sourceTree = "<group>"; };
1052
1056
  71ACF5B7242F2FDC00F0AAD4 /* FBSafariAlertTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSafariAlertTests.m; sourceTree = "<group>"; };
1057
+ 71AE3CF52D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCUIElement+FBVisibleFrame.h"; sourceTree = "<group>"; };
1058
+ 71AE3CF62D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "XCUIElement+FBVisibleFrame.m"; sourceTree = "<group>"; };
1053
1059
  71B155D923070ECF00646AFB /* FBHTTPStatusCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBHTTPStatusCodes.h; sourceTree = "<group>"; };
1054
1060
  71B155DB230711E900646AFB /* FBCommandStatus.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBCommandStatus.m; sourceTree = "<group>"; };
1055
1061
  71B155DD23080CA600646AFB /* FBProtocolHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBProtocolHelpers.h; sourceTree = "<group>"; };
@@ -1781,6 +1787,8 @@
1781
1787
  71B49EC61ED1A58100D51AD6 /* XCUIElement+FBUID.m */,
1782
1788
  EEE3763F1D59F81400ED88DD /* XCUIElement+FBUtilities.h */,
1783
1789
  EEE376401D59F81400ED88DD /* XCUIElement+FBUtilities.m */,
1790
+ 71AE3CF52D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.h */,
1791
+ 71AE3CF62D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.m */,
1784
1792
  EEE376471D59FAE900ED88DD /* XCUIElement+FBWebDriverAttributes.h */,
1785
1793
  EEE376481D59FAE900ED88DD /* XCUIElement+FBWebDriverAttributes.m */,
1786
1794
  641EE7042240CDCF00173FCB /* XCUIElement+FBTVFocuse.h */,
@@ -2399,6 +2407,7 @@
2399
2407
  641EE6A42240C5CA00173FCB /* FBCommandHandler.h in Headers */,
2400
2408
  641EE6A52240C5CA00173FCB /* FBSessionCommands.h in Headers */,
2401
2409
  641EE70C2240CE2D00173FCB /* FBTVNavigationTracker.h in Headers */,
2410
+ 71AE3CF72D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.h in Headers */,
2402
2411
  641EE6A62240C5CA00173FCB /* FBImageProcessor.h in Headers */,
2403
2412
  641EE6A72240C5CA00173FCB /* FBSession-Private.h in Headers */,
2404
2413
  641EE6A82240C5CA00173FCB /* NSString+FBXMLSafeString.h in Headers */,
@@ -2585,6 +2594,7 @@
2585
2594
  714EAA0D2673FDFE005C5B47 /* FBCapabilities.h in Headers */,
2586
2595
  EE35AD5C1E3B77D600A02D78 /* XCTNSPredicateExpectation.h in Headers */,
2587
2596
  EE35AD521E3B77D600A02D78 /* XCTestObservationCenter.h in Headers */,
2597
+ 71AE3CF92D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.h in Headers */,
2588
2598
  EE35AD5B1E3B77D600A02D78 /* XCTNSNotificationExpectation.h in Headers */,
2589
2599
  E444DC97249131D40060D7EB /* HTTPServer.h in Headers */,
2590
2600
  E444DCAE24913C220060D7EB /* HTTPResponseProxy.h in Headers */,
@@ -3160,6 +3170,7 @@
3160
3170
  641EE60E2240C5CA00173FCB /* XCUIElement+FBTyping.m in Sources */,
3161
3171
  641EE60F2240C5CA00173FCB /* XCUIElement+FBAccessibility.m in Sources */,
3162
3172
  641EE6102240C5CA00173FCB /* FBImageUtils.m in Sources */,
3173
+ 71AE3CF82D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.m in Sources */,
3163
3174
  641EE6112240C5CA00173FCB /* FBSession.m in Sources */,
3164
3175
  641EE6122240C5CA00173FCB /* FBFindElementCommands.m in Sources */,
3165
3176
  71A5C67629A4F39600421C37 /* XCTIssue+FBPatcher.m in Sources */,
@@ -3232,6 +3243,7 @@
3232
3243
  713AE576243A53BE0000D657 /* FBW3CActionsHelpers.m in Sources */,
3233
3244
  71B155E123080CA600646AFB /* FBProtocolHelpers.m in Sources */,
3234
3245
  EE158AB11CBD456F00A3E3F0 /* XCUIElement+FBIsVisible.m in Sources */,
3246
+ 71AE3CFA2D38EE8E0039FC36 /* XCUIElement+FBVisibleFrame.m in Sources */,
3235
3247
  EEBBD48C1D47746D00656A81 /* XCUIElement+FBFind.m in Sources */,
3236
3248
  EE158ADD1CBD456F00A3E3F0 /* FBResponsePayload.m in Sources */,
3237
3249
  E444DCB524913C220060D7EB /* RouteRequest.m in Sources */,
@@ -58,9 +58,11 @@ NS_ASSUME_NONNULL_BEGIN
58
58
 
59
59
  @param attribute attribute's accessibility identifier. Can be one of
60
60
  `XC_kAXXCAttribute`-prefixed attribute names.
61
- @return value for given accessibility property identifier
61
+ @param error Error instance in case of a failure
62
+ @return value for given accessibility property identifier or nil in case of failure
62
63
  */
63
- - (nullable id)fb_attributeValue:(NSString *)attribute;
64
+ - (nullable id)fb_attributeValue:(NSString *)attribute
65
+ error:(NSError **)error;
64
66
 
65
67
  /**
66
68
  Method used to determine whether given element matches receiver by comparing it's parameters except frame.
@@ -87,13 +89,6 @@ NS_ASSUME_NONNULL_BEGIN
87
89
  /**! Human-readable snapshot description */
88
90
  - (NSString *)fb_description;
89
91
 
90
- /**
91
- Returns the snapshot visibleFrame with a fallback to direct attribute retrieval from FBXCAXClient in case of a snapshot fault (nil visibleFrame)
92
-
93
- @return the snapshot visibleFrame
94
- */
95
- - (CGRect)fb_visibleFrameWithFallback;
96
-
97
92
  /**
98
93
  Wrapper for Apple's hitpoint, thats resolves few known issues
99
94
 
@@ -10,9 +10,11 @@
10
10
  #import "FBXCElementSnapshotWrapper+Helpers.h"
11
11
 
12
12
  #import "FBFindElementCommands.h"
13
+ #import "FBErrorBuilder.h"
13
14
  #import "FBRunLoopSpinner.h"
14
15
  #import "FBLogger.h"
15
16
  #import "FBXCElementSnapshot.h"
17
+ #import "FBXCTestDaemonsProxy.h"
16
18
  #import "FBXCAXClientProxy.h"
17
19
  #import "XCTestDriver.h"
18
20
  #import "XCTestPrivateSymbols.h"
@@ -20,6 +22,8 @@
20
22
  #import "XCUIElement+FBWebDriverAttributes.h"
21
23
  #import "XCUIHitPointResult.h"
22
24
 
25
+ #define ATTRIBUTE_FETCH_WARN_TIME_LIMIT 0.05
26
+
23
27
  inline static BOOL isSnapshotTypeAmongstGivenTypes(id<FBXCElementSnapshot> snapshot,
24
28
  NSArray<NSNumber *> *types);
25
29
 
@@ -65,10 +69,17 @@ inline static BOOL isSnapshotTypeAmongstGivenTypes(id<FBXCElementSnapshot> snaps
65
69
  }
66
70
 
67
71
  - (id)fb_attributeValue:(NSString *)attribute
72
+ error:(NSError **)error
68
73
  {
74
+ NSDate *start = [NSDate date];
69
75
  NSDictionary *result = [FBXCAXClientProxy.sharedClient attributesForElement:[self accessibilityElement]
70
- attributes:@[attribute]];
71
- return result[attribute];
76
+ attributes:@[attribute]
77
+ error:error];
78
+ NSTimeInterval elapsed = ABS([start timeIntervalSinceNow]);
79
+ if (elapsed > ATTRIBUTE_FETCH_WARN_TIME_LIMIT) {
80
+ NSLog(@"! Fetching of %@ value for %@ took %@s", attribute, self.fb_description, @(elapsed));
81
+ }
82
+ return [result objectForKey:attribute];
72
83
  }
73
84
 
74
85
  inline static BOOL areValuesEqual(id value1, id value2);
@@ -140,29 +151,6 @@ inline static BOOL isNilOrEmpty(id value);
140
151
  return targetCellSnapshot;
141
152
  }
142
153
 
143
- - (CGRect)fb_visibleFrameWithFallback
144
- {
145
- CGRect thisVisibleFrame = [self visibleFrame];
146
- if (!CGRectIsEmpty(thisVisibleFrame)) {
147
- return thisVisibleFrame;
148
- }
149
-
150
- NSDictionary *visibleFrameDict = (NSDictionary*)[self fb_attributeValue:@"XC_kAXXCAttributeVisibleFrame"];
151
- if (visibleFrameDict == nil) {
152
- return thisVisibleFrame;
153
- }
154
-
155
- id x = [visibleFrameDict objectForKey:@"X"];
156
- id y = [visibleFrameDict objectForKey:@"Y"];
157
- id height = [visibleFrameDict objectForKey:@"Height"];
158
- id width = [visibleFrameDict objectForKey:@"Width"];
159
- if (x != nil && y != nil && height != nil && width != nil) {
160
- return CGRectMake([x doubleValue], [y doubleValue], [width doubleValue], [height doubleValue]);
161
- }
162
-
163
- return thisVisibleFrame;
164
- }
165
-
166
154
  - (NSValue *)fb_hitPoint
167
155
  {
168
156
  NSError *error;
@@ -52,7 +52,7 @@ NSString *const FB_SAFARI_APP_NAME = @"Safari";
52
52
  // and conatins at least one text view
53
53
  __block NSUInteger buttonsCount = 0;
54
54
  __block NSUInteger textViewsCount = 0;
55
- id<FBXCElementSnapshot> snapshot = candidate.fb_cachedSnapshot ?: candidate.fb_takeSnapshot;
55
+ id<FBXCElementSnapshot> snapshot = candidate.fb_cachedSnapshot ?: [candidate fb_takeSnapshot:YES];
56
56
  [snapshot enumerateDescendantsUsingBlock:^(id<FBXCElementSnapshot> descendant) {
57
57
  XCUIElementType curType = descendant.elementType;
58
58
  if (curType == XCUIElementTypeButton) {
@@ -73,7 +73,7 @@ NSString *const FB_SAFARI_APP_NAME = @"Safari";
73
73
  if (nil == alert) {
74
74
  return nil;
75
75
  }
76
- id<FBXCElementSnapshot> alertSnapshot = alert.fb_cachedSnapshot ?: alert.fb_takeSnapshot;
76
+ id<FBXCElementSnapshot> alertSnapshot = alert.fb_cachedSnapshot ?: [alert fb_takeSnapshot:YES];
77
77
 
78
78
  if (alertSnapshot.elementType == XCUIElementTypeAlert) {
79
79
  return alert;
@@ -176,21 +176,7 @@ NSDictionary<NSString *, NSString *> *customExclusionAttributesMap(void) {
176
176
 
177
177
  - (NSDictionary *)fb_tree:(nullable NSSet<NSString *> *)excludedAttributes
178
178
  {
179
- // This set includes XCTest-specific internal attribute names,
180
- // while the `excludedAttributes` arg contains human-readable ones
181
- NSMutableSet* includedAttributeNames = [NSMutableSet setWithArray:FBCustomAttributeNames()];
182
- [includedAttributeNames addObjectsFromArray:FBStandardAttributeNames()];
183
- if (nil != excludedAttributes) {
184
- for (NSString *attr in excludedAttributes) {
185
- NSString *mappedName = [customExclusionAttributesMap() objectForKey:attr];
186
- if (nil != mappedName) {
187
- [includedAttributeNames removeObject:attr];
188
- }
189
- }
190
- }
191
- id<FBXCElementSnapshot> snapshot = nil == excludedAttributes
192
- ? [self fb_snapshotWithAllAttributesAndMaxDepth:nil]
193
- : [self fb_snapshotWithAttributes:[includedAttributeNames allObjects] maxDepth:nil];
179
+ id<FBXCElementSnapshot> snapshot = [self fb_takeSnapshot:YES];
194
180
  return [self.class dictionaryForElement:snapshot
195
181
  recursive:YES
196
182
  excludedAttributes:excludedAttributes];
@@ -198,9 +184,7 @@ NSDictionary<NSString *, NSString *> *customExclusionAttributesMap(void) {
198
184
 
199
185
  - (NSDictionary *)fb_accessibilityTree
200
186
  {
201
- id<FBXCElementSnapshot> snapshot = self.fb_isResolvedFromCache.boolValue
202
- ? self.lastSnapshot
203
- : [self fb_snapshotWithAllAttributesAndMaxDepth:nil];
187
+ id<FBXCElementSnapshot> snapshot = [self fb_takeSnapshot:YES];
204
188
  return [self.class accessibilityInfoForElement:snapshot];
205
189
  }
206
190
 
@@ -445,8 +429,8 @@ NSDictionary<NSString *, NSString *> *customExclusionAttributesMap(void) {
445
429
 
446
430
  id extractedElement = extractIssueProperty(issue, @"element");
447
431
 
448
- id<FBXCElementSnapshot> elementSnapshot = [extractedElement fb_cachedSnapshot] ?: [extractedElement fb_takeSnapshot];
449
- NSDictionary *elementAttributes = elementSnapshot
432
+ id<FBXCElementSnapshot> elementSnapshot = [extractedElement fb_cachedSnapshot] ?: [extractedElement fb_takeSnapshot:NO];
433
+ NSDictionary *elementAttributes = elementSnapshot
450
434
  ? [self.class dictionaryForElement:elementSnapshot
451
435
  recursive:NO
452
436
  excludedAttributes:customAttributesToExclude]
@@ -18,8 +18,7 @@
18
18
 
19
19
  - (BOOL)fb_isAccessibilityElement
20
20
  {
21
- id<FBXCElementSnapshot> snapshot = [self fb_snapshotWithAttributes:@[FB_XCAXAIsElementAttributeName]
22
- maxDepth:@1];
21
+ id<FBXCElementSnapshot> snapshot = [self fb_takeSnapshot:NO];
23
22
  return [FBXCElementSnapshotWrapper ensureWrapped:snapshot].fb_isAccessibilityElement;
24
23
  }
25
24
 
@@ -33,8 +32,20 @@
33
32
  if (nil != isAccessibilityElement) {
34
33
  return isAccessibilityElement.boolValue;
35
34
  }
36
-
37
- return [(NSNumber *)[self fb_attributeValue:FB_XCAXAIsElementAttributeName] boolValue];
35
+
36
+ NSError *error;
37
+ NSNumber *attributeValue = [self fb_attributeValue:FB_XCAXAIsElementAttributeName
38
+ error:&error];
39
+ if (nil != attributeValue) {
40
+ NSMutableDictionary *updatedValue = [NSMutableDictionary dictionaryWithDictionary:self.additionalAttributes ?: @{}];
41
+ [updatedValue setObject:attributeValue forKey:FB_XCAXAIsElementAttribute];
42
+ self.snapshot.additionalAttributes = updatedValue.copy;
43
+ return [attributeValue boolValue];
44
+ }
45
+
46
+ NSLog(@"Cannot determine accessibility of '%@' natively: %@. Defaulting to: %@",
47
+ self.fb_description, error.description, @(NO));
48
+ return NO;
38
49
  }
39
50
 
40
51
  @end
@@ -13,9 +13,6 @@ NS_ASSUME_NONNULL_BEGIN
13
13
 
14
14
  @interface XCUIElement (FBCaching)
15
15
 
16
- /*! This property is set to YES if the given element has been resolved from the cache, so it is safe to use the `lastSnapshot` property */
17
- @property (nullable, nonatomic) NSNumber *fb_isResolvedFromCache;
18
-
19
16
  @property (nonatomic, readonly) NSString *fb_cacheId;
20
17
 
21
18
  @end
@@ -18,20 +18,6 @@
18
18
 
19
19
  @implementation XCUIElement (FBCaching)
20
20
 
21
- static char XCUIELEMENT_IS_RESOLVED_FROM_CACHE_KEY;
22
-
23
- @dynamic fb_isResolvedFromCache;
24
-
25
- - (void)setFb_isResolvedFromCache:(NSNumber *)isResolvedFromCache
26
- {
27
- objc_setAssociatedObject(self, &XCUIELEMENT_IS_RESOLVED_FROM_CACHE_KEY, isResolvedFromCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
28
- }
29
-
30
- - (NSNumber *)fb_isResolvedFromCache
31
- {
32
- return (NSNumber *)objc_getAssociatedObject(self, &XCUIELEMENT_IS_RESOLVED_FROM_CACHE_KEY);
33
- }
34
-
35
21
  static char XCUIELEMENT_CACHE_ID_KEY;
36
22
 
37
23
  @dynamic fb_cacheId;
@@ -43,14 +29,7 @@ static char XCUIELEMENT_CACHE_ID_KEY;
43
29
  return (NSString *)result;
44
30
  }
45
31
 
46
- NSString *uid;
47
- if ([self isKindOfClass:XCUIApplication.class]) {
48
- uid = self.fb_uid;
49
- } else {
50
- id<FBXCElementSnapshot> snapshot = self.fb_cachedSnapshot ?: self.fb_takeSnapshot;
51
- uid = [FBXCElementSnapshotWrapper wdUIDWithSnapshot:snapshot];
52
- }
53
-
32
+ NSString *uid = self.fb_uid;
54
33
  objc_setAssociatedObject(self, &XCUIELEMENT_CACHE_ID_KEY, uid, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
55
34
  return uid;
56
35
  }
@@ -110,7 +110,7 @@
110
110
  matchingSnapshots = @[snapshot];
111
111
  }
112
112
  return [self fb_filterDescendantsWithSnapshots:matchingSnapshots
113
- selfUID:[FBXCElementSnapshotWrapper wdUIDWithSnapshot:self.lastSnapshot]
113
+ selfUID:self.fb_uid
114
114
  onlyChildren:NO];
115
115
  }
116
116
 
@@ -9,24 +9,23 @@
9
9
 
10
10
  #import "XCUIElement+FBIsVisible.h"
11
11
 
12
- #import "FBConfiguration.h"
13
12
  #import "FBElementUtils.h"
14
- #import "FBMathUtils.h"
15
- #import "FBActiveAppDetectionPoint.h"
16
- #import "FBSession.h"
17
- #import "FBXCAccessibilityElement.h"
18
13
  #import "FBXCodeCompatibility.h"
19
14
  #import "FBXCElementSnapshotWrapper+Helpers.h"
20
15
  #import "XCUIElement+FBUtilities.h"
21
- #import "XCUIElement+FBUID.h"
16
+ #import "XCUIElement+FBVisibleFrame.h"
22
17
  #import "XCTestPrivateSymbols.h"
23
18
 
19
+ NSNumber* _Nullable fetchSnapshotVisibility(id<FBXCElementSnapshot> snapshot)
20
+ {
21
+ return nil == snapshot.additionalAttributes ? nil : snapshot.additionalAttributes[FB_XCAXAIsVisibleAttribute];
22
+ }
23
+
24
24
  @implementation XCUIElement (FBIsVisible)
25
25
 
26
26
  - (BOOL)fb_isVisible
27
27
  {
28
- id<FBXCElementSnapshot> snapshot = [self fb_snapshotWithAttributes:@[FB_XCAXAIsVisibleAttributeName]
29
- maxDepth:@1];
28
+ id<FBXCElementSnapshot> snapshot = [self fb_takeSnapshot:NO];
30
29
  return [FBXCElementSnapshotWrapper ensureWrapped:snapshot].fb_isVisible;
31
30
  }
32
31
 
@@ -34,187 +33,43 @@
34
33
 
35
34
  @implementation FBXCElementSnapshotWrapper (FBIsVisible)
36
35
 
37
- + (NSString *)fb_uniqIdWithSnapshot:(id<FBXCElementSnapshot>)snapshot
38
- {
39
- return [FBXCElementSnapshotWrapper wdUIDWithSnapshot:snapshot] ?: [NSString stringWithFormat:@"%p", (void *)snapshot];
40
- }
41
-
42
- - (nullable NSNumber *)fb_cachedVisibilityValue
36
+ - (BOOL)fb_hasVisibleDescendants
43
37
  {
44
- NSMutableDictionary *cache = FBSession.activeSession.elementsVisibilityCache;
45
- if (nil == cache) {
46
- return nil;
47
- }
48
-
49
- NSDictionary<NSString *, NSNumber *> *result = cache[@(self.generation)];
50
- if (nil == result) {
51
- // There is no need to keep the cached data for the previous generations
52
- [cache removeAllObjects];
53
- cache[@(self.generation)] = [NSMutableDictionary dictionary];
54
- return nil;
55
- }
56
- return result[[self.class fb_uniqIdWithSnapshot:self.snapshot]];
57
- }
58
-
59
- - (BOOL)fb_cacheVisibilityWithValue:(BOOL)isVisible
60
- forAncestors:(nullable NSArray<id<FBXCElementSnapshot>> *)ancestors
61
- {
62
- NSMutableDictionary *cache = FBSession.activeSession.elementsVisibilityCache;
63
- if (nil == cache) {
64
- return isVisible;
65
- }
66
- NSMutableDictionary<NSString *, NSNumber *> *destination = cache[@(self.generation)];
67
- if (nil == destination) {
68
- return isVisible;
69
- }
70
-
71
- NSNumber *visibleObj = [NSNumber numberWithBool:isVisible];
72
- destination[[self.class fb_uniqIdWithSnapshot:self.snapshot]] = visibleObj;
73
- if (isVisible && nil != ancestors) {
74
- // if an element is visible then all its ancestors must be visible as well
75
- for (id<FBXCElementSnapshot> ancestor in ancestors) {
76
- NSString *ancestorId = [self.class fb_uniqIdWithSnapshot:ancestor];
77
- if (nil == destination[ancestorId]) {
78
- destination[ancestorId] = visibleObj;
79
- }
80
- }
81
- }
82
- return isVisible;
83
- }
84
-
85
- - (CGRect)fb_frameInContainer:(id<FBXCElementSnapshot>)container
86
- hierarchyIntersection:(nullable NSValue *)intersectionRectange
87
- {
88
- CGRect currentRectangle = nil == intersectionRectange ? self.frame : [intersectionRectange CGRectValue];
89
- id<FBXCElementSnapshot> parent = self.parent;
90
- CGRect parentFrame = parent.frame;
91
- CGRect containerFrame = container.frame;
92
- if (CGSizeEqualToSize(parentFrame.size, CGSizeZero) &&
93
- CGPointEqualToPoint(parentFrame.origin, CGPointZero)) {
94
- // Special case (or XCTest bug). Shift the origin and return immediately after shift
95
- id<FBXCElementSnapshot> nextParent = parent.parent;
96
- BOOL isGrandparent = YES;
97
- while (nextParent && nextParent != container) {
98
- CGRect nextParentFrame = nextParent.frame;
99
- if (isGrandparent &&
100
- CGSizeEqualToSize(nextParentFrame.size, CGSizeZero) &&
101
- CGPointEqualToPoint(nextParentFrame.origin, CGPointZero)) {
102
- // Double zero-size container inclusion means that element coordinates are absolute
103
- return CGRectIntersection(currentRectangle, containerFrame);
104
- }
105
- isGrandparent = NO;
106
- if (!CGPointEqualToPoint(nextParentFrame.origin, CGPointZero)) {
107
- currentRectangle.origin.x += nextParentFrame.origin.x;
108
- currentRectangle.origin.y += nextParentFrame.origin.y;
109
- return CGRectIntersection(currentRectangle, containerFrame);
110
- }
111
- nextParent = nextParent.parent;
112
- }
113
- return CGRectIntersection(currentRectangle, containerFrame);
114
- }
115
- // Skip parent containers if they are outside of the viewport
116
- CGRect intersectionWithParent = CGRectIntersectsRect(parentFrame, containerFrame) || parent.elementType != XCUIElementTypeOther
117
- ? CGRectIntersection(currentRectangle, parentFrame)
118
- : currentRectangle;
119
- if (CGRectIsEmpty(intersectionWithParent) &&
120
- parent != container &&
121
- self.elementType == XCUIElementTypeOther) {
122
- // Special case (or XCTest bug). Shift the origin
123
- if (CGSizeEqualToSize(parentFrame.size, containerFrame.size) ||
124
- // The size might be inverted in landscape
125
- CGSizeEqualToSize(parentFrame.size, CGSizeMake(containerFrame.size.height, containerFrame.size.width)) ||
126
- CGSizeEqualToSize(self.frame.size, CGSizeZero)) {
127
- // Covers ActivityListView and RemoteBridgeView cases
128
- currentRectangle.origin.x += parentFrame.origin.x;
129
- currentRectangle.origin.y += parentFrame.origin.y;
130
- return CGRectIntersection(currentRectangle, containerFrame);
131
- }
132
- }
133
- if (CGRectIsEmpty(intersectionWithParent) || parent == container) {
134
- return intersectionWithParent;
135
- }
136
- return [[FBXCElementSnapshotWrapper ensureWrapped:parent] fb_frameInContainer:container
137
- hierarchyIntersection:[NSValue valueWithCGRect:intersectionWithParent]];
138
- }
139
-
140
- - (BOOL)fb_hasAnyVisibleLeafs
141
- {
142
- NSArray<id<FBXCElementSnapshot>> *children = self.children;
143
- if (0 == children.count) {
144
- return self.fb_isVisible;
145
- }
146
-
147
- for (id<FBXCElementSnapshot> child in children) {
148
- if ([FBXCElementSnapshotWrapper ensureWrapped:child].fb_hasAnyVisibleLeafs) {
38
+ for (id<FBXCElementSnapshot> descendant in (self._allDescendants ?: @[])) {
39
+ if ([fetchSnapshotVisibility(descendant) boolValue]) {
149
40
  return YES;
150
41
  }
151
42
  }
152
-
153
43
  return NO;
154
44
  }
155
45
 
156
46
  - (BOOL)fb_isVisible
157
47
  {
158
- NSNumber *isVisible = self.additionalAttributes[FB_XCAXAIsVisibleAttribute];
159
- if (isVisible != nil) {
48
+ NSNumber *isVisible = fetchSnapshotVisibility(self);
49
+ if (nil != isVisible) {
160
50
  return isVisible.boolValue;
161
51
  }
162
-
163
- NSNumber *cachedValue = [self fb_cachedVisibilityValue];
164
- if (nil != cachedValue) {
165
- return [cachedValue boolValue];
166
- }
167
52
 
168
- CGRect selfFrame = self.frame;
169
- if (CGRectIsEmpty(selfFrame)) {
170
- return [self fb_cacheVisibilityWithValue:NO forAncestors:nil];
53
+ // Fetching the attribute value is expensive.
54
+ // Shortcircuit here to save time and assume if any of descendants
55
+ // is already determined as visible then the container should be visible as well
56
+ if ([self fb_hasVisibleDescendants]) {
57
+ return YES;
171
58
  }
172
59
 
173
- NSArray<id<FBXCElementSnapshot>> *ancestors = self.fb_ancestors;
174
- if ([FBConfiguration shouldUseTestManagerForVisibilityDetection]) {
175
- BOOL visibleAttrValue = [(NSNumber *)[self fb_attributeValue:FB_XCAXAIsVisibleAttributeName] boolValue];
176
- return [self fb_cacheVisibilityWithValue:visibleAttrValue forAncestors:ancestors];
60
+ NSError *error;
61
+ NSNumber *attributeValue = [self fb_attributeValue:FB_XCAXAIsVisibleAttributeName
62
+ error:&error];
63
+ if (nil != attributeValue) {
64
+ NSMutableDictionary *updatedValue = [NSMutableDictionary dictionaryWithDictionary:self.additionalAttributes ?: @{}];
65
+ [updatedValue setObject:attributeValue forKey:FB_XCAXAIsVisibleAttribute];
66
+ self.snapshot.additionalAttributes = updatedValue.copy;
67
+ return [attributeValue boolValue];
177
68
  }
178
69
 
179
- id<FBXCElementSnapshot> parentWindow = ancestors.count > 1 ? [ancestors objectAtIndex:ancestors.count - 2] : nil;
180
- CGRect visibleRect = selfFrame;
181
- if (nil != parentWindow) {
182
- visibleRect = [self fb_frameInContainer:parentWindow hierarchyIntersection:nil];
183
- }
184
- if (CGRectIsEmpty(visibleRect)) {
185
- return [self fb_cacheVisibilityWithValue:NO forAncestors:ancestors];
186
- }
187
- CGPoint midPoint = CGPointMake(visibleRect.origin.x + visibleRect.size.width / 2,
188
- visibleRect.origin.y + visibleRect.size.height / 2);
189
- id<FBXCAccessibilityElement> hitElement = [FBActiveAppDetectionPoint axElementWithPoint:midPoint];
190
- if (nil != hitElement) {
191
- if (FBIsAXElementEqualToOther(self.accessibilityElement, hitElement)) {
192
- return [self fb_cacheVisibilityWithValue:YES forAncestors:ancestors];
193
- }
194
- for (id<FBXCElementSnapshot> ancestor in ancestors) {
195
- if (FBIsAXElementEqualToOther(hitElement, ancestor.accessibilityElement)) {
196
- return [self fb_cacheVisibilityWithValue:YES forAncestors:ancestors];
197
- }
198
- }
199
- }
200
- if (self.children.count > 0) {
201
- if (nil != hitElement) {
202
- for (id<FBXCElementSnapshot> descendant in self._allDescendants) {
203
- if (FBIsAXElementEqualToOther(hitElement, descendant.accessibilityElement)) {
204
- return [self fb_cacheVisibilityWithValue:YES
205
- forAncestors:[FBXCElementSnapshotWrapper ensureWrapped:descendant].fb_ancestors];
206
- }
207
- }
208
- }
209
- if (self.fb_hasAnyVisibleLeafs) {
210
- return [self fb_cacheVisibilityWithValue:YES forAncestors:ancestors];
211
- }
212
- } else if (nil == hitElement) {
213
- // Sometimes XCTest returns nil for leaf elements hit test even if such elements are hittable
214
- // Assume such elements are visible if their rectInContainer is visible
215
- return [self fb_cacheVisibilityWithValue:YES forAncestors:ancestors];
216
- }
217
- return [self fb_cacheVisibilityWithValue:NO forAncestors:ancestors];
70
+ NSLog(@"Cannot determine visiblity of %@ natively: %@. Defaulting to: %@",
71
+ self.fb_description, error.description, @(NO));
72
+ return NO;
218
73
  }
219
74
 
220
75
  @end
@@ -23,9 +23,7 @@ static const NSTimeInterval VALUE_CHANGE_TIMEOUT = 2;
23
23
 
24
24
  - (BOOL)fb_scrollWithOffset:(CGFloat)relativeHeightOffset error:(NSError **)error
25
25
  {
26
- id<FBXCElementSnapshot> snapshot = self.fb_isResolvedFromCache.boolValue
27
- ? self.lastSnapshot
28
- : self.fb_takeSnapshot;
26
+ id<FBXCElementSnapshot> snapshot = [self fb_takeSnapshot:NO];
29
27
  NSString *previousValue = snapshot.value;
30
28
  XCUICoordinate *startCoord = [self coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5)];
31
29
  XCUICoordinate *endCoord = [startCoord coordinateWithOffset:CGVectorMake(0.0, relativeHeightOffset * snapshot.frame.size.height)];