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.
- package/PrivateHeaders/.DS_Store +0 -0
- package/PrivateHeaders/XCTest/XCTestManager_ManagerInterface-Protocol.h +3 -0
- package/README.md +0 -1
- package/WebDriverAgent.xcodeproj/project.pbxproj +56 -56
- package/WebDriverAgent.xcodeproj/project.xcworkspace/xcuserdata/elf.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/WebDriverAgent.xcodeproj/xcuserdata/elf.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +51 -0
- package/WebDriverAgent.xcodeproj/xcuserdata/{kazuaki.xcuserdatad → elf.xcuserdatad}/xcschemes/xcschememanagement.plist +23 -8
- package/WebDriverAgentLib/.DS_Store +0 -0
- package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +0 -17
- package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +0 -16
- package/WebDriverAgentLib/Categories/XCUIElement+FBIsVisible.m +28 -14
- package/WebDriverAgentLib/Categories/XCUIElement+FBResolve.h +37 -0
- package/WebDriverAgentLib/Categories/XCUIElement+FBResolve.m +51 -0
- package/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.m +8 -2
- package/WebDriverAgentLib/Commands/FBElementCommands.m +3 -1
- package/WebDriverAgentLib/Commands/FBOrientationCommands.m +20 -9
- package/WebDriverAgentLib/Commands/FBSessionCommands.m +119 -99
- package/WebDriverAgentLib/FBApplication.m +1 -1
- package/WebDriverAgentLib/Routing/FBElementCache.m +42 -11
- package/WebDriverAgentLib/Routing/FBResponsePayload.m +9 -3
- package/WebDriverAgentLib/Routing/FBSession.h +6 -0
- package/WebDriverAgentLib/Routing/FBSession.m +45 -55
- package/WebDriverAgentLib/Utilities/FBAlertsMonitor.h +1 -3
- package/WebDriverAgentLib/Utilities/FBAlertsMonitor.m +6 -13
- package/WebDriverAgentLib/Utilities/FBCapabilities.h +23 -0
- package/WebDriverAgentLib/Utilities/FBCapabilities.m +23 -0
- package/WebDriverAgentLib/Utilities/FBConfiguration.m +6 -1
- package/WebDriverAgentLib/Utilities/FBScreenshot.m +125 -14
- package/WebDriverAgentLib/Utilities/FBSettings.h +23 -23
- package/WebDriverAgentLib/Utilities/FBSettings.m +23 -23
- package/WebDriverAgentLib/Utilities/FBXCodeCompatibility.h +16 -0
- package/WebDriverAgentLib/Utilities/FBXCodeCompatibility.m +16 -0
- package/WebDriverAgentLib/Utilities/FBXPath.m +15 -44
- package/WebDriverAgentLib/Utilities/LRUCache/.DS_Store +0 -0
- package/WebDriverAgentLib/Utilities/LRUCache/LRUCache.h +59 -0
- package/WebDriverAgentLib/Utilities/LRUCache/LRUCache.m +104 -0
- package/WebDriverAgentLib/Utilities/LRUCache/LRUCacheNode.h +43 -0
- package/WebDriverAgentLib/Utilities/LRUCache/LRUCacheNode.m +59 -0
- package/WebDriverAgentLib/Vendor/.DS_Store +0 -0
- package/WebDriverAgentRunner/.DS_Store +0 -0
- package/WebDriverAgentRunner-Runner.app.zip +0 -0
- package/WebDriverAgentTests/IntegrationApp/Resources/.DS_Store +0 -0
- package/WebDriverAgentTests/IntegrationTests/FBSessionIntegrationTests.m +20 -16
- package/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m +2 -1
- package/WebDriverAgentTests/IntegrationTests/XCUIElementFBFindTests.m +11 -0
- package/WebDriverAgentTests/UnitTests/Doubles/FBApplicationDouble.h +3 -0
- package/WebDriverAgentTests/UnitTests/Doubles/FBApplicationDouble.m +15 -0
- package/WebDriverAgentTests/UnitTests/Doubles/XCUIElementDouble.h +1 -0
- package/WebDriverAgentTests/UnitTests/Doubles/XCUIElementDouble.m +1 -0
- package/WebDriverAgentTests/UnitTests/FBSessionTests.m +11 -1
- package/WebDriverAgentTests/UnitTests/FBXPathTests.m +3 -1
- package/WebDriverAgentTests/UnitTests_tvOS/Doubles/XCUIElementDouble.h +1 -0
- package/WebDriverAgentTests/UnitTests_tvOS/Doubles/XCUIElementDouble.m +1 -0
- package/package.json +3 -3
- package/WebDriverAgent.xcodeproj/project.xcworkspace/xcuserdata/kazuaki.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/WebDriverAgent.xcodeproj/project.xcworkspace/xcuserdata/kazuaki.xcuserdatad/WorkspaceSettings.xcsettings +0 -20
- package/WebDriverAgent.xcodeproj/xcuserdata/kazuaki.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -906
- package/WebDriverAgentLib/Vendor/YYCache/YYCache.h +0 -206
- package/WebDriverAgentLib/Vendor/YYCache/YYCache.m +0 -140
- package/WebDriverAgentLib/Vendor/YYCache/YYDiskCache.h +0 -412
- package/WebDriverAgentLib/Vendor/YYCache/YYDiskCache.m +0 -462
- package/WebDriverAgentLib/Vendor/YYCache/YYKVStorage.h +0 -325
- package/WebDriverAgentLib/Vendor/YYCache/YYKVStorage.m +0 -1068
- package/WebDriverAgentLib/Vendor/YYCache/YYMemoryCache.h +0 -213
- package/WebDriverAgentLib/Vendor/YYCache/YYMemoryCache.m +0 -508
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#import "FBElementCache.h"
|
|
11
11
|
|
|
12
|
-
#import "
|
|
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 (
|
|
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 = [[
|
|
38
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
36
|
-
@property (nonatomic
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 = [
|
|
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 = [
|
|
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
|
-
|
|
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 = [
|
|
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 = [
|
|
219
|
-
|
|
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
|
-
|
|
217
|
+
return YES;
|
|
223
218
|
}
|
|
224
|
-
|
|
225
|
-
return result;
|
|
219
|
+
return NO;
|
|
226
220
|
}
|
|
227
221
|
|
|
228
222
|
- (NSUInteger)applicationStateWithBundleId:(NSString *)bundleIdentifier
|
|
229
223
|
{
|
|
230
|
-
FBApplication
|
|
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
|
|
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 "
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
[
|
|
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
|
-
|
|
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
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|