appium-webdriveragent 3.9.2 → 3.12.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 (65) hide show
  1. package/PrivateHeaders/.DS_Store +0 -0
  2. package/PrivateHeaders/XCTest/XCTestManager_ManagerInterface-Protocol.h +3 -0
  3. package/README.md +0 -1
  4. package/WebDriverAgent.xcodeproj/project.pbxproj +56 -56
  5. package/WebDriverAgent.xcodeproj/project.xcworkspace/xcuserdata/elf.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  6. package/WebDriverAgent.xcodeproj/xcuserdata/elf.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +51 -0
  7. package/WebDriverAgent.xcodeproj/xcuserdata/{kazuaki.xcuserdatad → elf.xcuserdatad}/xcschemes/xcschememanagement.plist +23 -8
  8. package/WebDriverAgentLib/.DS_Store +0 -0
  9. package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +0 -17
  10. package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +0 -16
  11. package/WebDriverAgentLib/Categories/XCUIElement+FBIsVisible.m +28 -14
  12. package/WebDriverAgentLib/Categories/XCUIElement+FBResolve.h +37 -0
  13. package/WebDriverAgentLib/Categories/XCUIElement+FBResolve.m +51 -0
  14. package/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.m +8 -2
  15. package/WebDriverAgentLib/Commands/FBElementCommands.m +3 -1
  16. package/WebDriverAgentLib/Commands/FBOrientationCommands.m +20 -9
  17. package/WebDriverAgentLib/Commands/FBSessionCommands.m +119 -99
  18. package/WebDriverAgentLib/FBApplication.m +1 -1
  19. package/WebDriverAgentLib/Routing/FBElementCache.m +42 -11
  20. package/WebDriverAgentLib/Routing/FBResponsePayload.m +9 -3
  21. package/WebDriverAgentLib/Routing/FBSession.h +6 -0
  22. package/WebDriverAgentLib/Routing/FBSession.m +45 -55
  23. package/WebDriverAgentLib/Utilities/FBAlertsMonitor.h +1 -3
  24. package/WebDriverAgentLib/Utilities/FBAlertsMonitor.m +6 -13
  25. package/WebDriverAgentLib/Utilities/FBCapabilities.h +23 -0
  26. package/WebDriverAgentLib/Utilities/FBCapabilities.m +23 -0
  27. package/WebDriverAgentLib/Utilities/FBConfiguration.m +6 -1
  28. package/WebDriverAgentLib/Utilities/FBScreenshot.m +125 -14
  29. package/WebDriverAgentLib/Utilities/FBSettings.h +23 -23
  30. package/WebDriverAgentLib/Utilities/FBSettings.m +23 -23
  31. package/WebDriverAgentLib/Utilities/FBXCodeCompatibility.h +16 -0
  32. package/WebDriverAgentLib/Utilities/FBXCodeCompatibility.m +16 -0
  33. package/WebDriverAgentLib/Utilities/FBXPath.m +15 -44
  34. package/WebDriverAgentLib/Utilities/LRUCache/.DS_Store +0 -0
  35. package/WebDriverAgentLib/Utilities/LRUCache/LRUCache.h +59 -0
  36. package/WebDriverAgentLib/Utilities/LRUCache/LRUCache.m +104 -0
  37. package/WebDriverAgentLib/Utilities/LRUCache/LRUCacheNode.h +43 -0
  38. package/WebDriverAgentLib/Utilities/LRUCache/LRUCacheNode.m +59 -0
  39. package/WebDriverAgentLib/Vendor/.DS_Store +0 -0
  40. package/WebDriverAgentRunner/.DS_Store +0 -0
  41. package/WebDriverAgentRunner-Runner.app.zip +0 -0
  42. package/WebDriverAgentTests/IntegrationApp/Resources/.DS_Store +0 -0
  43. package/WebDriverAgentTests/IntegrationTests/FBSessionIntegrationTests.m +20 -16
  44. package/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m +2 -1
  45. package/WebDriverAgentTests/IntegrationTests/XCUIElementFBFindTests.m +11 -0
  46. package/WebDriverAgentTests/UnitTests/Doubles/FBApplicationDouble.h +3 -0
  47. package/WebDriverAgentTests/UnitTests/Doubles/FBApplicationDouble.m +15 -0
  48. package/WebDriverAgentTests/UnitTests/Doubles/XCUIElementDouble.h +1 -0
  49. package/WebDriverAgentTests/UnitTests/Doubles/XCUIElementDouble.m +1 -0
  50. package/WebDriverAgentTests/UnitTests/FBSessionTests.m +11 -1
  51. package/WebDriverAgentTests/UnitTests/FBXPathTests.m +3 -1
  52. package/WebDriverAgentTests/UnitTests_tvOS/Doubles/XCUIElementDouble.h +1 -0
  53. package/WebDriverAgentTests/UnitTests_tvOS/Doubles/XCUIElementDouble.m +1 -0
  54. package/package.json +3 -3
  55. package/WebDriverAgent.xcodeproj/project.xcworkspace/xcuserdata/kazuaki.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  56. package/WebDriverAgent.xcodeproj/project.xcworkspace/xcuserdata/kazuaki.xcuserdatad/WorkspaceSettings.xcsettings +0 -20
  57. package/WebDriverAgent.xcodeproj/xcuserdata/kazuaki.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -906
  58. package/WebDriverAgentLib/Vendor/YYCache/YYCache.h +0 -206
  59. package/WebDriverAgentLib/Vendor/YYCache/YYCache.m +0 -140
  60. package/WebDriverAgentLib/Vendor/YYCache/YYDiskCache.h +0 -412
  61. package/WebDriverAgentLib/Vendor/YYCache/YYDiskCache.m +0 -462
  62. package/WebDriverAgentLib/Vendor/YYCache/YYKVStorage.h +0 -325
  63. package/WebDriverAgentLib/Vendor/YYCache/YYKVStorage.m +0 -1068
  64. package/WebDriverAgentLib/Vendor/YYCache/YYMemoryCache.h +0 -213
  65. package/WebDriverAgentLib/Vendor/YYCache/YYMemoryCache.m +0 -508
@@ -9,7 +9,7 @@
9
9
 
10
10
  #import "FBElementCache.h"
11
11
 
12
- #import "YYCache.h"
12
+ #import "LRUCache.h"
13
13
  #import "FBAlert.h"
14
14
  #import "FBExceptions.h"
15
15
  #import "FBXCodeCompatibility.h"
@@ -19,11 +19,14 @@
19
19
  #import "XCUIElement+FBUtilities.h"
20
20
  #import "XCUIElement+FBWebDriverAttributes.h"
21
21
  #import "XCUIElement+FBUID.h"
22
+ #import "XCUIElement+FBResolve.h"
23
+ #import "XCUIElementQuery.h"
22
24
 
23
25
  const int ELEMENT_CACHE_SIZE = 1024;
24
26
 
25
27
  @interface FBElementCache ()
26
- @property (atomic, strong) YYMemoryCache *elementCache;
28
+ @property (nonatomic, strong) LRUCache *elementCache;
29
+ @property (nonatomic) BOOL elementsNeedReset;
27
30
  @end
28
31
 
29
32
  @implementation FBElementCache
@@ -34,8 +37,8 @@ const int ELEMENT_CACHE_SIZE = 1024;
34
37
  if (!self) {
35
38
  return nil;
36
39
  }
37
- _elementCache = [[YYMemoryCache alloc] init];
38
- _elementCache.countLimit = ELEMENT_CACHE_SIZE;
40
+ _elementCache = [[LRUCache alloc] initWithCapacity:ELEMENT_CACHE_SIZE];
41
+ _elementsNeedReset = YES;
39
42
  return self;
40
43
  }
41
44
 
@@ -45,7 +48,10 @@ const int ELEMENT_CACHE_SIZE = 1024;
45
48
  if (nil == uuid) {
46
49
  return nil;
47
50
  }
48
- [self.elementCache setObject:element forKey:uuid];
51
+ @synchronized (self.elementCache) {
52
+ [self.elementCache setObject:element forKey:uuid];
53
+ }
54
+ self.elementsNeedReset = YES;
49
55
  return uuid;
50
56
  }
51
57
 
@@ -63,7 +69,15 @@ const int ELEMENT_CACHE_SIZE = 1024;
63
69
  @throw [NSException exceptionWithName:FBInvalidArgumentException reason:reason userInfo:@{}];
64
70
  }
65
71
 
66
- XCUIElement *element = [self.elementCache objectForKey:uuid];
72
+ XCUIElement *element;
73
+ @synchronized (self.elementCache) {
74
+ [self resetElements];
75
+ element = [self.elementCache objectForKey:uuid];
76
+ }
77
+ if (nil == element) {
78
+ NSString *reason = [NSString stringWithFormat:@"The element identified by \"%@\" is either not present or it has expired from the internal cache. Try to find it again", uuid];
79
+ @throw [NSException exceptionWithName:FBStaleElementException reason:reason userInfo:@{}];
80
+ }
67
81
  // This will throw FBStaleElementException exception if the element is stale
68
82
  // or resolve the element and set lastSnapshot property
69
83
  if (nil == additionalAttributes) {
@@ -73,17 +87,34 @@ const int ELEMENT_CACHE_SIZE = 1024;
73
87
  [attributes addObjectsFromArray:additionalAttributes];
74
88
  [element fb_snapshotWithAttributes:attributes.copy maxDepth:maxDepth];
75
89
  }
76
- if (nil == element) {
77
- NSString *reason = [NSString stringWithFormat:@"The element identified by \"%@\" is either not present or it has expired from the internal cache. Try to find it again", uuid];
78
- @throw [NSException exceptionWithName:FBStaleElementException reason:reason userInfo:@{}];
79
- }
80
90
  element.fb_isResolvedFromCache = @(YES);
81
91
  return element;
82
92
  }
83
93
 
84
94
  - (BOOL)hasElementWithUUID:(NSString *)uuid
85
95
  {
86
- return nil == uuid ? NO : [self.elementCache containsObjectForKey:(NSString *)uuid];
96
+ if (nil == uuid) {
97
+ return NO;
98
+ }
99
+ @synchronized (self.elementCache) {
100
+ return nil != [self.elementCache objectForKey:(NSString *)uuid];
101
+ }
102
+ }
103
+
104
+ - (void)resetElements
105
+ {
106
+ if (!self.elementsNeedReset) {
107
+ return;
108
+ }
109
+
110
+ for (XCUIElement *element in self.elementCache.allObjects) {
111
+ element.lastSnapshot = nil;
112
+ if (nil != element.query) {
113
+ element.query.rootElementSnapshot = nil;
114
+ }
115
+ element.fb_isResolvedFromCache = @(NO);
116
+ }
117
+ self.elementsNeedReset = NO;
87
118
  }
88
119
 
89
120
  @end
@@ -16,8 +16,8 @@
16
16
  #import "FBConfiguration.h"
17
17
  #import "FBMacros.h"
18
18
  #import "FBProtocolHelpers.h"
19
-
20
19
  #import "XCUIElementQuery.h"
20
+ #import "XCUIElement+FBResolve.h"
21
21
  #import "XCUIElement+FBUtilities.h"
22
22
  #import "XCUIElement+FBWebDriverAttributes.h"
23
23
 
@@ -35,15 +35,21 @@ id<FBResponsePayload> FBResponseWithObject(id object)
35
35
 
36
36
  id<FBResponsePayload> FBResponseWithCachedElement(XCUIElement *element, FBElementCache *elementCache, BOOL compact)
37
37
  {
38
- [elementCache storeElement:element];
38
+ BOOL useNativeCachingStrategy = nil == FBSession.activeSession
39
+ ? YES
40
+ : FBSession.activeSession.useNativeCachingStrategy;
41
+ [elementCache storeElement:(useNativeCachingStrategy ? element : element.fb_stableInstance)];
39
42
  return FBResponseWithStatus([FBCommandStatus okWithValue: FBDictionaryResponseWithElement(element, compact)]);
40
43
  }
41
44
 
42
45
  id<FBResponsePayload> FBResponseWithCachedElements(NSArray<XCUIElement *> *elements, FBElementCache *elementCache, BOOL compact)
43
46
  {
44
47
  NSMutableArray *elementsResponse = [NSMutableArray array];
48
+ BOOL useNativeCachingStrategy = nil == FBSession.activeSession
49
+ ? YES
50
+ : FBSession.activeSession.useNativeCachingStrategy;
45
51
  for (XCUIElement *element in elements) {
46
- [elementCache storeElement:element];
52
+ [elementCache storeElement:(useNativeCachingStrategy ? element : element.fb_stableInstance)];
47
53
  [elementsResponse addObject:FBDictionaryResponseWithElement(element, compact)];
48
54
  }
49
55
  return FBResponseWithStatus([FBCommandStatus okWithValue:elementsResponse]);
@@ -34,6 +34,12 @@ NS_ASSUME_NONNULL_BEGIN
34
34
  /*! The action to apply to unexpected alerts. Either "accept"/"dismiss" or nil/empty string (by default) to do nothing */
35
35
  @property (nonatomic, nullable) NSString *defaultAlertAction;
36
36
 
37
+ /*! Whether to use the native caching strategy for elements or the custom one: https://discuss.appium.io/t/elements-state-coming-from-xpath-vs-ios-predicate-string/34016 */
38
+ @property (nonatomic) BOOL useNativeCachingStrategy;
39
+
40
+ /*! Keeps cached visibility values for the current snapshots tree */
41
+ @property (nonatomic, readonly) NSMutableDictionary<NSNumber *, NSMutableDictionary<NSString *, NSNumber *> *> *elementsVisibilityCache;
42
+
37
43
  + (nullable instancetype)activeSession;
38
44
 
39
45
  /**
@@ -32,9 +32,10 @@ NSString *const FBDefaultApplicationAuto = @"auto";
32
32
 
33
33
  @interface FBSession ()
34
34
  @property (nonatomic) NSString *testedApplicationBundleId;
35
- @property (nonatomic) NSDictionary<NSString *, FBApplication *> *applications;
36
- @property (nonatomic, strong, readwrite) FBApplication *testedApplication;
35
+ @property (nonatomic) BOOL isTestedApplicationExpectedToRun;
36
+ @property (nonatomic) BOOL shouldAppsWaitForQuiescence;
37
37
  @property (nonatomic, nullable) FBAlertsMonitor *alertsMonitor;
38
+ @property (nonatomic, readwrite) NSMutableDictionary<NSNumber *, NSMutableDictionary<NSString *, NSNumber *> *> *elementsVisibilityCache;
38
39
  @end
39
40
 
40
41
  @interface FBSession (FBAlertsMonitorDelegate)
@@ -70,6 +71,7 @@ NSString *const FBDefaultApplicationAuto = @"auto";
70
71
  @implementation FBSession
71
72
 
72
73
  static FBSession *_activeSession = nil;
74
+
73
75
  + (instancetype)activeSession
74
76
  {
75
77
  return _activeSession;
@@ -77,7 +79,7 @@ static FBSession *_activeSession = nil;
77
79
 
78
80
  + (void)markSessionActive:(FBSession *)session
79
81
  {
80
- if (_activeSession && _activeSession.testedApplication.bundleID != session.testedApplication.bundleID) {
82
+ if (_activeSession) {
81
83
  [_activeSession kill];
82
84
  }
83
85
  _activeSession = session;
@@ -97,28 +99,29 @@ static FBSession *_activeSession = nil;
97
99
  + (instancetype)initWithApplication:(FBApplication *)application
98
100
  {
99
101
  FBSession *session = [FBSession new];
102
+ session.useNativeCachingStrategy = YES;
100
103
  session.alertsMonitor = nil;
101
104
  session.defaultAlertAction = nil;
105
+ session.elementsVisibilityCache = [NSMutableDictionary dictionary];
102
106
  session.identifier = [[NSUUID UUID] UUIDString];
103
107
  session.defaultActiveApplication = FBDefaultApplicationAuto;
104
108
  session.testedApplicationBundleId = nil;
105
- NSMutableDictionary *apps = [NSMutableDictionary dictionary];
109
+ session.isTestedApplicationExpectedToRun = nil != application && application.running;
106
110
  if (application) {
107
- [apps setObject:application forKey:application.bundleID];
108
111
  session.testedApplicationBundleId = application.bundleID;
112
+ session.shouldAppsWaitForQuiescence = application.fb_shouldWaitForQuiescence;
109
113
  }
110
- session.applications = apps.copy;
111
114
  session.elementCache = [FBElementCache new];
112
115
  [FBSession markSessionActive:session];
113
116
  return session;
114
117
  }
115
118
 
116
- + (instancetype)initWithApplication:(nullable FBApplication *)application defaultAlertAction:(NSString *)defaultAlertAction
119
+ + (instancetype)initWithApplication:(nullable FBApplication *)application
120
+ defaultAlertAction:(NSString *)defaultAlertAction
117
121
  {
118
122
  FBSession *session = [self.class initWithApplication:application];
119
123
  session.alertsMonitor = [[FBAlertsMonitor alloc] init];
120
124
  session.alertsMonitor.delegate = (id<FBAlertsMonitorDelegate>)session;
121
- session.alertsMonitor.application = application;
122
125
  session.defaultAlertAction = [defaultAlertAction lowercaseString];
123
126
  [session.alertsMonitor enable];
124
127
  return session;
@@ -126,14 +129,27 @@ static FBSession *_activeSession = nil;
126
129
 
127
130
  - (void)kill
128
131
  {
132
+ if (nil == _activeSession) {
133
+ return;
134
+ }
135
+
129
136
  if (nil != self.alertsMonitor) {
130
137
  [self.alertsMonitor disable];
131
138
  self.alertsMonitor = nil;
132
139
  }
133
140
 
134
- if (self.testedApplicationBundleId && [FBConfiguration shouldTerminateApp]) {
135
- [[self.applications objectForKey:self.testedApplicationBundleId] terminate];
141
+ if (self.testedApplicationBundleId && [FBConfiguration shouldTerminateApp]
142
+ && ![self.testedApplicationBundleId isEqualToString:FBApplication.fb_systemApplication.bundleID]) {
143
+ FBApplication *app = [[FBApplication alloc] initWithBundleIdentifier:self.testedApplicationBundleId];
144
+ if ([app running]) {
145
+ @try {
146
+ [app terminate];
147
+ } @catch (NSException *e) {
148
+ [FBLogger logFmt:@"%@", e.description];
149
+ }
150
+ }
136
151
  }
152
+
137
153
  _activeSession = nil;
138
154
  }
139
155
 
@@ -144,56 +160,28 @@ static FBSession *_activeSession = nil;
144
160
  : self.defaultActiveApplication;
145
161
  FBApplication *application = [FBApplication fb_activeApplicationWithDefaultBundleId:defaultBundleId];
146
162
  FBApplication *testedApplication = nil;
147
- if (self.testedApplicationBundleId) {
148
- testedApplication = [self.applications objectForKey:self.testedApplicationBundleId];
163
+ if (self.testedApplicationBundleId && self.isTestedApplicationExpectedToRun) {
164
+ testedApplication = nil != application.bundleID && [application.bundleID isEqualToString:self.testedApplicationBundleId]
165
+ ? application
166
+ : [[FBApplication alloc] initWithBundleIdentifier:self.testedApplicationBundleId];
149
167
  }
150
168
  if (testedApplication && !testedApplication.running) {
151
169
  NSString *description = [NSString stringWithFormat:@"The application under test with bundle id '%@' is not running, possibly crashed", self.testedApplicationBundleId];
152
170
  [[NSException exceptionWithName:FBApplicationCrashedException reason:description userInfo:nil] raise];
153
171
  }
154
- if (nil != self.alertsMonitor) {
155
- self.alertsMonitor.application = application;
156
- }
157
172
  return application;
158
173
  }
159
174
 
160
- - (FBApplication *)registerApplicationWithBundleId:(NSString *)bundleIdentifier
161
- {
162
- FBApplication *app = [self.applications objectForKey:bundleIdentifier];
163
- if (!app) {
164
- app = [[FBApplication alloc] initPrivateWithPath:nil bundleID:bundleIdentifier];
165
- NSMutableDictionary *apps = self.applications.mutableCopy;
166
- [apps setObject:app forKey:bundleIdentifier];
167
- self.applications = apps.copy;
168
- }
169
- return app;
170
- }
171
-
172
- - (BOOL)unregisterApplicationWithBundleId:(NSString *)bundleIdentifier
173
- {
174
- FBApplication *app = [self.applications objectForKey:bundleIdentifier];
175
- if (app) {
176
- NSMutableDictionary *apps = self.applications.mutableCopy;
177
- [apps removeObjectForKey:bundleIdentifier];
178
- self.applications = apps.copy;
179
- return YES;
180
- }
181
- return NO;
182
- }
183
-
184
175
  - (FBApplication *)launchApplicationWithBundleId:(NSString *)bundleIdentifier
185
176
  shouldWaitForQuiescence:(nullable NSNumber *)shouldWaitForQuiescence
186
177
  arguments:(nullable NSArray<NSString *> *)arguments
187
178
  environment:(nullable NSDictionary <NSString *, NSString *> *)environment
188
179
  {
189
- FBApplication *app = [self registerApplicationWithBundleId:bundleIdentifier];
180
+ FBApplication *app = [[FBApplication alloc] initWithBundleIdentifier:bundleIdentifier];
190
181
  if (app.fb_state < 2) {
191
182
  if (nil == shouldWaitForQuiescence) {
192
183
  // Iherit the quiescence check setting from the main app under test by default
193
- FBApplication *testedApplication = nil == self.testedApplicationBundleId
194
- ? nil
195
- : [self.applications objectForKey:self.testedApplicationBundleId];
196
- app.fb_shouldWaitForQuiescence = nil == testedApplication || testedApplication.fb_shouldWaitForQuiescence;
184
+ app.fb_shouldWaitForQuiescence = nil != self.testedApplicationBundleId && self.shouldAppsWaitForQuiescence;
197
185
  } else {
198
186
  app.fb_shouldWaitForQuiescence = [shouldWaitForQuiescence boolValue];
199
187
  }
@@ -203,35 +191,37 @@ static FBSession *_activeSession = nil;
203
191
  } else {
204
192
  [app fb_activate];
205
193
  }
194
+ if (nil != self.testedApplicationBundleId
195
+ && [bundleIdentifier isEqualToString:(NSString *)self.testedApplicationBundleId]) {
196
+ self.isTestedApplicationExpectedToRun = YES;
197
+ }
206
198
  return app;
207
199
  }
208
200
 
209
201
  - (FBApplication *)activateApplicationWithBundleId:(NSString *)bundleIdentifier
210
202
  {
211
- FBApplication *app = [self registerApplicationWithBundleId:bundleIdentifier];
203
+ FBApplication *app = [[FBApplication alloc] initWithBundleIdentifier:bundleIdentifier];
212
204
  [app fb_activate];
213
205
  return app;
214
206
  }
215
207
 
216
208
  - (BOOL)terminateApplicationWithBundleId:(NSString *)bundleIdentifier
217
209
  {
218
- FBApplication *app = [self registerApplicationWithBundleId:bundleIdentifier];
219
- BOOL result = NO;
210
+ FBApplication *app = [[FBApplication alloc] initWithBundleIdentifier:bundleIdentifier];
211
+ if (nil != self.testedApplicationBundleId
212
+ && [bundleIdentifier isEqualToString:(NSString *)self.testedApplicationBundleId]) {
213
+ self.isTestedApplicationExpectedToRun = NO;
214
+ }
220
215
  if (app.fb_state >= 2) {
221
216
  [app terminate];
222
- result = YES;
217
+ return YES;
223
218
  }
224
- [self unregisterApplicationWithBundleId:bundleIdentifier];
225
- return result;
219
+ return NO;
226
220
  }
227
221
 
228
222
  - (NSUInteger)applicationStateWithBundleId:(NSString *)bundleIdentifier
229
223
  {
230
- FBApplication *app = [self.applications objectForKey:bundleIdentifier];
231
- if (!app) {
232
- app = [[FBApplication alloc] initPrivateWithPath:nil bundleID:bundleIdentifier];
233
- }
234
- return app.fb_state;
224
+ return [[FBApplication alloc] initWithBundleIdentifier:bundleIdentifier].fb_state;
235
225
  }
236
226
 
237
227
  @end
@@ -26,10 +26,8 @@ NS_ASSUME_NONNULL_BEGIN
26
26
 
27
27
  @interface FBAlertsMonitor : NSObject
28
28
 
29
- /*! The delegate which decides on what to do whwn an alert is detected */
29
+ /*! The delegate which decides on what to do when an alert is detected */
30
30
  @property (nonatomic, nullable, weak) id<FBAlertsMonitorDelegate> delegate;
31
- /*! The active appication instance. It is updated by the session instance when necessary */
32
- @property (nonatomic, nullable) XCUIApplication *application;
33
31
 
34
32
  /**
35
33
  Creates an instance of alerts monitor.
@@ -10,7 +10,7 @@
10
10
  #import "FBAlertsMonitor.h"
11
11
 
12
12
  #import "FBAlert.h"
13
- #import "XCUIApplication.h"
13
+ #import "FBApplication.h"
14
14
  #import "XCUIApplication+FBAlert.h"
15
15
 
16
16
  static const NSTimeInterval FB_MONTORING_INTERVAL = 2.0;
@@ -26,7 +26,6 @@ static const NSTimeInterval FB_MONTORING_INTERVAL = 2.0;
26
26
  - (instancetype)init
27
27
  {
28
28
  if ((self = [super init])) {
29
- _application = nil;
30
29
  _isMonitoring = NO;
31
30
  _delegate = nil;
32
31
  }
@@ -49,18 +48,12 @@ static const NSTimeInterval FB_MONTORING_INTERVAL = 2.0;
49
48
  }
50
49
 
51
50
  dispatch_async(dispatch_get_main_queue(), ^{
52
- if (nil == self.application || !self.application.running) {
53
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delta), dispatch_get_main_queue(), ^{
54
- [self scheduleNextTick];
55
- });
56
- return;
57
- }
58
-
59
- id<FBAlertsMonitorDelegate> handler = self.delegate;
60
- if (nil != handler) {
61
- XCUIElement *alertElement = [self.application fb_alertElement];
51
+ NSArray<FBApplication *> *activeApps = FBApplication.fb_activeApplications;
52
+ for (FBApplication *activeApp in activeApps) {
53
+ XCUIElement *alertElement = activeApp.fb_alertElement;
62
54
  if (nil != alertElement) {
63
- [handler didDetectAlert:[FBAlert alertWithElement:alertElement]];
55
+ [self.delegate didDetectAlert:[FBAlert alertWithElement:alertElement]];
56
+ break;
64
57
  }
65
58
  }
66
59
 
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ */
9
+
10
+ #import <Foundation/Foundation.h>
11
+
12
+ extern NSString* const FB_CAP_USE_TEST_MANAGER_FOR_VISIBLITY_DETECTION;
13
+ extern NSString* const FB_CAP_MAX_TYPING_FREQUNCY;
14
+ extern NSString* const FB_CAP_USE_SINGLETON_TEST_MANAGER;
15
+ extern NSString* const FB_CAP_DISABLE_AUTOMATIC_SCREENSHOTS;
16
+ extern NSString* const FB_CAP_SHOULD_TERMINATE_APP;
17
+ extern NSString* const FB_CAP_EVENT_LOOP_IDLE_DELAY_SEC;
18
+ extern NSString* const FB_CAP_BUNDLE_ID;
19
+ extern NSString* const FB_CAP_FORCE_APP_LAUNCH;
20
+ extern NSString* const FB_CAP_SHOULD_WAIT_FOR_QUIESCENCE;
21
+ extern NSString* const FB_CAP_ARGUMENTS;
22
+ extern NSString* const FB_CAP_ENVIRNOMENT;
23
+ extern NSString* const FB_CAP_USE_NATIVE_CACHING_STRATEGY;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ */
9
+
10
+ #import "FBCapabilities.h"
11
+
12
+ NSString* const FB_CAP_USE_TEST_MANAGER_FOR_VISIBLITY_DETECTION = @"shouldUseTestManagerForVisibilityDetection";
13
+ NSString* const FB_CAP_MAX_TYPING_FREQUNCY = @"maxTypingFrequency";
14
+ NSString* const FB_CAP_USE_SINGLETON_TEST_MANAGER = @"shouldUseSingletonTestManager";
15
+ NSString* const FB_CAP_DISABLE_AUTOMATIC_SCREENSHOTS = @"disableAutomaticScreenshots";
16
+ NSString* const FB_CAP_SHOULD_TERMINATE_APP = @"shouldTerminateApp";
17
+ NSString* const FB_CAP_EVENT_LOOP_IDLE_DELAY_SEC = @"eventloopIdleDelaySec";
18
+ NSString* const FB_CAP_BUNDLE_ID = @"bundleId";
19
+ NSString* const FB_CAP_FORCE_APP_LAUNCH = @"forceAppLaunch";
20
+ NSString* const FB_CAP_SHOULD_WAIT_FOR_QUIESCENCE = @"shouldWaitForQuiescence";
21
+ NSString* const FB_CAP_ARGUMENTS = @"arguments";
22
+ NSString* const FB_CAP_ENVIRNOMENT = @"environment";
23
+ NSString* const FB_CAP_USE_NATIVE_CACHING_STRATEGY = @"useNativeCachingStrategy";
@@ -257,7 +257,12 @@ static UIInterfaceOrientation FBScreenshotOrientation = UIInterfaceOrientationUn
257
257
  // This can avoid 'Keyboard is not present' error which can happen
258
258
  // when send_keys are called by client
259
259
  [[UIKeyboardImpl sharedInstance] setAutomaticMinimizationEnabled:NO];
260
- [[UIKeyboardImpl sharedInstance] setSoftwareKeyboardShownByTouch:YES];
260
+
261
+ if ([(NSObject *)[UIKeyboardImpl sharedInstance]
262
+ respondsToSelector:@selector(setSoftwareKeyboardShownByTouch:)]) {
263
+ // Xcode 13 no longer has this method
264
+ [[UIKeyboardImpl sharedInstance] setSoftwareKeyboardShownByTouch:YES];
265
+ }
261
266
  #endif
262
267
 
263
268
  void *handle = dlopen(controllerPrefBundlePath, RTLD_LAZY);
@@ -15,6 +15,7 @@
15
15
  #import "FBErrorBuilder.h"
16
16
  #import "FBImageIOScaler.h"
17
17
  #import "FBLogger.h"
18
+ #import "FBMacros.h"
18
19
  #import "FBXCodeCompatibility.h"
19
20
  #import "FBXCTestDaemonsProxy.h"
20
21
  #import "XCTestManager_ManagerInterface-Protocol.h"
@@ -155,33 +156,143 @@ NSString *formatTimeInterval(NSTimeInterval interval) {
155
156
  __block NSData *screenshotData = nil;
156
157
  __block NSError *innerError = nil;
157
158
  dispatch_semaphore_t sem = dispatch_semaphore_create(0);
158
- [proxy _XCT_requestScreenshotOfScreenWithID:screenID
159
- withRect:CGRectNull
160
- uti:uti
161
- compressionQuality:compressionQuality
162
- withReply:^(NSData *data, NSError *err) {
163
- if (nil != err) {
164
- innerError = err;
165
- } else {
166
- screenshotData = data;
159
+ if ([self.class shouldUseScreenshotRequestApiForProxy:(NSObject *)proxy]) {
160
+ id screnshotRequest = [self.class screenshotRequestWithScreenID:screenID
161
+ rect:CGRectNull
162
+ uti:uti
163
+ compressionQuality:compressionQuality
164
+ error:error];
165
+ if (nil == screnshotRequest) {
166
+ return nil;
167
167
  }
168
- dispatch_semaphore_signal(sem);
169
- }];
170
- if (nil != error && innerError) {
171
- *error = innerError;
168
+ [proxy _XCT_requestScreenshot:screnshotRequest
169
+ withReply:^(id image, NSError *err) {
170
+ if (nil != err) {
171
+ innerError = err;
172
+ } else {
173
+ screenshotData = [image data];
174
+ }
175
+ dispatch_semaphore_signal(sem);
176
+ }];
177
+ } else {
178
+ [proxy _XCT_requestScreenshotOfScreenWithID:screenID
179
+ withRect:CGRectNull
180
+ uti:uti
181
+ compressionQuality:compressionQuality
182
+ withReply:^(NSData *data, NSError *err) {
183
+ if (nil != err) {
184
+ innerError = err;
185
+ } else {
186
+ screenshotData = data;
187
+ }
188
+ dispatch_semaphore_signal(sem);
189
+ }];
172
190
  }
173
191
  int64_t timeoutNs = (int64_t)(timeout * NSEC_PER_SEC);
174
192
  if (0 != dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, timeoutNs))) {
175
193
  NSString *timeoutMsg = [NSString stringWithFormat:@"Cannot take a screenshot within %@ timeout", formatTimeInterval(SCREENSHOT_TIMEOUT)];
176
194
  if (nil == error) {
177
195
  [FBLogger log:timeoutMsg];
178
- } else {
196
+ } else if (nil == innerError) {
179
197
  [[[FBErrorBuilder builder]
180
198
  withDescription:timeoutMsg]
181
199
  buildError:error];
182
200
  }
183
201
  };
202
+ if (nil != error && nil != innerError) {
203
+ *error = innerError;
204
+ }
184
205
  return screenshotData;
185
206
  }
186
207
 
208
+ + (BOOL)shouldUseScreenshotRequestApiForProxy:(NSObject *)proxy
209
+ {
210
+ static dispatch_once_t shouldUseSRApi;
211
+ static BOOL result;
212
+ dispatch_once(&shouldUseSRApi, ^{
213
+ if ([proxy respondsToSelector:@selector(_XCT_requestScreenshot:withReply:)]) {
214
+ #if TARGET_OS_SIMULATOR
215
+ result = YES;
216
+ #else
217
+ result = SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"15.0");
218
+ #endif
219
+ } else {
220
+ result = NO;
221
+ }
222
+ });
223
+ return result;
224
+ }
225
+
226
+ + (nullable id)imageEncodingWithUniformTypeIdentifier:(NSString *)uti
227
+ compressionQuality:(CGFloat)compressionQuality
228
+ error:(NSError **)error
229
+ {
230
+ // TODO: Use native accessors after we drop the support of Xcode 12.4 and below
231
+ Class imageEncodingClass = NSClassFromString(@"XCTImageEncoding");
232
+ if (nil == imageEncodingClass) {
233
+ [[[FBErrorBuilder builder]
234
+ withDescription:@"Cannot find XCTImageEncoding class"]
235
+ buildError:error];
236
+ return nil;
237
+ }
238
+ id imageEncodingAllocated = [imageEncodingClass alloc];
239
+ SEL imageEncodingConstructorSelector = NSSelectorFromString(@"initWithUniformTypeIdentifier:compressionQuality:");
240
+ if (![imageEncodingAllocated respondsToSelector:imageEncodingConstructorSelector]) {
241
+ [[[FBErrorBuilder builder]
242
+ withDescription:@"'initWithUniformTypeIdentifier:compressionQuality:' contructor is not found on XCTImageEncoding class"]
243
+ buildError:error];
244
+ return nil;
245
+ }
246
+ NSMethodSignature *imageEncodingContructorSignature = [imageEncodingAllocated methodSignatureForSelector:imageEncodingConstructorSelector];
247
+ NSInvocation *imageEncodingInitInvocation = [NSInvocation invocationWithMethodSignature:imageEncodingContructorSignature];
248
+ [imageEncodingInitInvocation setSelector:imageEncodingConstructorSelector];
249
+ [imageEncodingInitInvocation setArgument:&uti atIndex:2];
250
+ [imageEncodingInitInvocation setArgument:&compressionQuality atIndex:3];
251
+ [imageEncodingInitInvocation invokeWithTarget:imageEncodingAllocated];
252
+ id __unsafe_unretained imageEncoding;
253
+ [imageEncodingInitInvocation getReturnValue:&imageEncoding];
254
+ return imageEncoding;
255
+ }
256
+
257
+ + (nullable id)screenshotRequestWithScreenID:(unsigned int)screenID
258
+ rect:(struct CGRect)rect
259
+ uti:(NSString *)uti
260
+ compressionQuality:(CGFloat)compressionQuality
261
+ error:(NSError **)error
262
+ {
263
+ // TODO: Use native accessors after we drop the support of Xcode 12.4 and below
264
+ id imageEncoding = [self.class imageEncodingWithUniformTypeIdentifier:uti
265
+ compressionQuality:compressionQuality
266
+ error:error];
267
+ if (nil == imageEncoding) {
268
+ return nil;
269
+ }
270
+
271
+ Class screenshotRequestClass = NSClassFromString(@"XCTScreenshotRequest");
272
+ if (nil == screenshotRequestClass) {
273
+ [[[FBErrorBuilder builder]
274
+ withDescription:@"Cannot find XCTScreenshotRequest class"]
275
+ buildError:error];
276
+ return nil;
277
+ }
278
+ id screenshotRequestAllocated = [screenshotRequestClass alloc];
279
+ SEL screenshotRequestConstructorSelector = NSSelectorFromString(@"initWithScreenID:rect:encoding:");
280
+ if (![screenshotRequestAllocated respondsToSelector:screenshotRequestConstructorSelector]) {
281
+ [[[FBErrorBuilder builder]
282
+ withDescription:@"'initWithScreenID:rect:encoding:' contructor is not found on XCTScreenshotRequest class"]
283
+ buildError:error];
284
+ return nil;
285
+ }
286
+ NSMethodSignature *screenshotRequestContructorSignature = [screenshotRequestAllocated methodSignatureForSelector:screenshotRequestConstructorSelector];
287
+ NSInvocation *screenshotRequestInitInvocation = [NSInvocation invocationWithMethodSignature:screenshotRequestContructorSignature];
288
+ [screenshotRequestInitInvocation setSelector:screenshotRequestConstructorSelector];
289
+ [screenshotRequestInitInvocation setArgument:&screenID atIndex:2];
290
+ [screenshotRequestInitInvocation setArgument:&rect atIndex:3];
291
+ [screenshotRequestInitInvocation setArgument:&imageEncoding atIndex:4];
292
+ [screenshotRequestInitInvocation invokeWithTarget:screenshotRequestAllocated];
293
+ id __unsafe_unretained screenshotRequest;
294
+ [screenshotRequestInitInvocation getReturnValue:&screenshotRequest];
295
+ return screenshotRequest;
296
+ }
297
+
187
298
  @end