appium-webdriveragent 5.15.7 → 5.15.9

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 (53) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/PrivateHeaders/XCTest/XCUIApplication.h +1 -0
  3. package/WebDriverAgent.xcodeproj/project.pbxproj +18 -6
  4. package/WebDriverAgent.xcodeproj/project.xcworkspace/xcuserdata/kazu.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  5. package/WebDriverAgent.xcodeproj/xcuserdata/kazu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +1342 -0
  6. package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +0 -38
  7. package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +5 -148
  8. package/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +1 -1
  9. package/WebDriverAgentLib/Categories/XCUIDevice+FBRotation.h +1 -0
  10. package/WebDriverAgentLib/Categories/XCUIDevice+FBRotation.m +3 -5
  11. package/WebDriverAgentLib/Categories/XCUIElement+FBTVFocuse.m +2 -2
  12. package/WebDriverAgentLib/Commands/FBAlertViewCommands.m +4 -4
  13. package/WebDriverAgentLib/Commands/FBCustomCommands.m +2 -2
  14. package/WebDriverAgentLib/Commands/FBDebugCommands.m +3 -2
  15. package/WebDriverAgentLib/Commands/FBElementCommands.m +4 -2
  16. package/WebDriverAgentLib/Commands/FBFindElementCommands.m +1 -0
  17. package/WebDriverAgentLib/Commands/FBOrientationCommands.m +8 -9
  18. package/WebDriverAgentLib/Commands/FBSessionCommands.m +9 -7
  19. package/WebDriverAgentLib/Commands/FBTouchActionCommands.m +1 -0
  20. package/WebDriverAgentLib/FBAlert.m +3 -3
  21. package/WebDriverAgentLib/FBApplication.h +49 -0
  22. package/WebDriverAgentLib/FBApplication.m +185 -0
  23. package/WebDriverAgentLib/Routing/FBSession.h +14 -13
  24. package/WebDriverAgentLib/Routing/FBSession.m +46 -49
  25. package/WebDriverAgentLib/Utilities/FBAlertsMonitor.m +3 -3
  26. package/WebDriverAgentLib/Utilities/FBKeyboard.m +5 -6
  27. package/WebDriverAgentLib/Utilities/FBMjpegServer.m +1 -0
  28. package/WebDriverAgentLib/Utilities/FBPasteboard.m +2 -2
  29. package/WebDriverAgentLib/Utilities/FBScreen.m +2 -1
  30. package/WebDriverAgentLib/Utilities/FBTVNavigationTracker.m +2 -1
  31. package/WebDriverAgentLib/Utilities/FBXCodeCompatibility.h +33 -0
  32. package/WebDriverAgentLib/Utilities/FBXCodeCompatibility.m +40 -2
  33. package/WebDriverAgentLib/WebDriverAgentLib.h +1 -0
  34. package/WebDriverAgentTests/IntegrationTests/FBAutoAlertsHandlerTests.m +3 -2
  35. package/WebDriverAgentTests/IntegrationTests/FBElementVisibilityTests.m +1 -0
  36. package/WebDriverAgentTests/IntegrationTests/FBIntegrationTestCase.h +3 -1
  37. package/WebDriverAgentTests/IntegrationTests/FBIntegrationTestCase.m +5 -5
  38. package/WebDriverAgentTests/IntegrationTests/FBSafariAlertTests.m +2 -1
  39. package/WebDriverAgentTests/IntegrationTests/FBSessionIntegrationTests.m +10 -11
  40. package/WebDriverAgentTests/IntegrationTests/FBTapTest.m +1 -0
  41. package/WebDriverAgentTests/IntegrationTests/FBW3CMultiTouchActionsIntegrationTests.m +1 -0
  42. package/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m +8 -8
  43. package/WebDriverAgentTests/IntegrationTests/XCUIDeviceHelperTests.m +4 -5
  44. package/WebDriverAgentTests/IntegrationTests/XCUIElementHelperIntegrationTests.m +1 -0
  45. package/WebDriverAgentTests/UnitTests/Doubles/{XCUIApplicationDouble.h → FBApplicationDouble.h} +1 -1
  46. package/WebDriverAgentTests/UnitTests/Doubles/{XCUIApplicationDouble.m → FBApplicationDouble.m} +3 -3
  47. package/WebDriverAgentTests/UnitTests/FBSessionTests.m +3 -3
  48. package/build/lib/utils.d.ts +2 -2
  49. package/build/lib/utils.d.ts.map +1 -1
  50. package/build/lib/utils.js +7 -5
  51. package/build/lib/utils.js.map +1 -1
  52. package/lib/utils.js +7 -5
  53. package/package.json +1 -1
@@ -0,0 +1,185 @@
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 "FBApplication.h"
11
+
12
+ #import "FBXCAccessibilityElement.h"
13
+ #import "FBLogger.h"
14
+ #import "FBExceptions.h"
15
+ #import "FBRunLoopSpinner.h"
16
+ #import "FBMacros.h"
17
+ #import "FBActiveAppDetectionPoint.h"
18
+ #import "FBXCodeCompatibility.h"
19
+ #import "FBXCTestDaemonsProxy.h"
20
+ #import "XCUIApplication.h"
21
+ #import "XCUIApplication+FBHelpers.h"
22
+ #import "XCUIApplicationImpl.h"
23
+ #import "XCUIApplicationProcess.h"
24
+ #import "XCUIElement.h"
25
+ #import "XCUIElementQuery.h"
26
+ #import "FBXCAXClientProxy.h"
27
+
28
+
29
+ static const NSTimeInterval APP_STATE_CHANGE_TIMEOUT = 5.0;
30
+
31
+ @interface FBApplication ()
32
+ @end
33
+
34
+ @implementation FBApplication
35
+
36
+ + (instancetype)fb_activeApplication
37
+ {
38
+ return [self fb_activeApplicationWithDefaultBundleId:nil];
39
+ }
40
+
41
+ + (NSArray<FBApplication *> *)fb_activeApplications
42
+ {
43
+ NSArray<id<FBXCAccessibilityElement>> *activeApplicationElements = [FBXCAXClientProxy.sharedClient activeApplications];
44
+ NSMutableArray<FBApplication *> *result = [NSMutableArray array];
45
+ if (activeApplicationElements.count > 0) {
46
+ for (id<FBXCAccessibilityElement> applicationElement in activeApplicationElements) {
47
+ FBApplication *app = [FBApplication fb_applicationWithPID:applicationElement.processIdentifier];
48
+ if (nil != app) {
49
+ [result addObject:app];
50
+ }
51
+ }
52
+ }
53
+ return result.count > 0 ? result.copy : @[self.class.fb_systemApplication];
54
+ }
55
+
56
+ + (instancetype)fb_activeApplicationWithDefaultBundleId:(nullable NSString *)bundleId
57
+ {
58
+ NSArray<id<FBXCAccessibilityElement>> *activeApplicationElements = [FBXCAXClientProxy.sharedClient activeApplications];
59
+ id<FBXCAccessibilityElement> activeApplicationElement = nil;
60
+ id<FBXCAccessibilityElement> currentElement = nil;
61
+ if (nil != bundleId) {
62
+ currentElement = FBActiveAppDetectionPoint.sharedInstance.axElement;
63
+ if (nil != currentElement) {
64
+ NSArray<NSDictionary *> *appInfos = [self fb_appsInfoWithAxElements:@[currentElement]];
65
+ [FBLogger logFmt:@"Detected on-screen application: %@", appInfos.firstObject[@"bundleId"]];
66
+ if ([[appInfos.firstObject objectForKey:@"bundleId"] isEqualToString:(id)bundleId]) {
67
+ activeApplicationElement = currentElement;
68
+ }
69
+ }
70
+ }
71
+ if (nil == activeApplicationElement && activeApplicationElements.count > 1) {
72
+ if (nil != bundleId) {
73
+ NSArray<NSDictionary *> *appInfos = [self fb_appsInfoWithAxElements:activeApplicationElements];
74
+ NSMutableArray<NSString *> *bundleIds = [NSMutableArray array];
75
+ for (NSDictionary *appInfo in appInfos) {
76
+ [bundleIds addObject:(NSString *)appInfo[@"bundleId"]];
77
+ }
78
+ [FBLogger logFmt:@"Detected system active application(s): %@", bundleIds];
79
+ // Try to select the desired application first
80
+ for (NSUInteger appIdx = 0; appIdx < appInfos.count; appIdx++) {
81
+ if ([[[appInfos objectAtIndex:appIdx] objectForKey:@"bundleId"] isEqualToString:(id)bundleId]) {
82
+ activeApplicationElement = [activeApplicationElements objectAtIndex:appIdx];
83
+ break;
84
+ }
85
+ }
86
+ }
87
+ // Fall back to the "normal" algorithm if the desired application is either
88
+ // not set or is not active
89
+ if (nil == activeApplicationElement) {
90
+ if (nil == currentElement) {
91
+ currentElement = FBActiveAppDetectionPoint.sharedInstance.axElement;
92
+ }
93
+ if (nil == currentElement) {
94
+ [FBLogger log:@"Cannot precisely detect the current application. Will use the system's recently active one"];
95
+ if (nil == bundleId) {
96
+ [FBLogger log:@"Consider changing the 'defaultActiveApplication' setting to the bundle identifier of the desired application under test"];
97
+ }
98
+ } else {
99
+ for (id<FBXCAccessibilityElement> appElement in activeApplicationElements) {
100
+ if (appElement.processIdentifier == currentElement.processIdentifier) {
101
+ activeApplicationElement = appElement;
102
+ break;
103
+ }
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ if (nil != activeApplicationElement) {
110
+ FBApplication *application = [FBApplication fb_applicationWithPID:activeApplicationElement.processIdentifier];
111
+ if (nil != application) {
112
+ return application;
113
+ }
114
+ [FBLogger log:@"Cannot translate the active process identifier into an application object"];
115
+ }
116
+
117
+ if (activeApplicationElements.count > 0) {
118
+ [FBLogger logFmt:@"Getting the most recent active application (out of %@ total items)", @(activeApplicationElements.count)];
119
+ for (id<FBXCAccessibilityElement> appElement in activeApplicationElements) {
120
+ FBApplication *application = [FBApplication fb_applicationWithPID:appElement.processIdentifier];
121
+ if (nil != application) {
122
+ return application;
123
+ }
124
+ }
125
+ }
126
+
127
+ [FBLogger log:@"Cannot retrieve any active applications. Assuming the system application is the active one"];
128
+ return [self fb_systemApplication];
129
+ }
130
+
131
+ + (instancetype)fb_systemApplication
132
+ {
133
+ return [self fb_applicationWithPID:
134
+ [[FBXCAXClientProxy.sharedClient systemApplication] processIdentifier]];
135
+ }
136
+
137
+ + (instancetype)applicationWithPID:(pid_t)processID
138
+ {
139
+ if ([NSProcessInfo processInfo].processIdentifier == processID) {
140
+ return nil;
141
+ }
142
+ return (FBApplication *)[FBXCAXClientProxy.sharedClient monitoredApplicationWithProcessIdentifier:processID];
143
+ }
144
+
145
+ - (void)launch
146
+ {
147
+ [super launch];
148
+ if (![self fb_waitForAppElement:APP_STATE_CHANGE_TIMEOUT]) {
149
+ [FBLogger logFmt:@"The application '%@' is not running in foreground after %.2f seconds", self.bundleID, APP_STATE_CHANGE_TIMEOUT];
150
+ }
151
+ }
152
+
153
+ - (void)terminate
154
+ {
155
+ [super terminate];
156
+ if (![self waitForState:XCUIApplicationStateNotRunning timeout:APP_STATE_CHANGE_TIMEOUT]) {
157
+ [FBLogger logFmt:@"The active application is still '%@' after %.2f seconds timeout", self.bundleID, APP_STATE_CHANGE_TIMEOUT];
158
+ }
159
+ }
160
+
161
+ + (BOOL)fb_switchToSystemApplicationWithError:(NSError **)error
162
+ {
163
+ FBApplication *systemApp = self.fb_systemApplication;
164
+ @try {
165
+ if ([systemApp fb_state] < 2) {
166
+ [systemApp launch];
167
+ } else {
168
+ [systemApp fb_activate];
169
+ }
170
+ } @catch (NSException *e) {
171
+ return [[[FBErrorBuilder alloc]
172
+ withDescription:nil == e ? @"Cannot open the home screen" : e.reason]
173
+ buildError:error];
174
+ }
175
+ return [[[[FBRunLoopSpinner new]
176
+ timeout:5]
177
+ timeoutErrorMessage:@"Timeout waiting until the home screen is visible"]
178
+ spinUntilTrue:^BOOL{
179
+ FBApplication *activeApp = self.fb_activeApplication;
180
+ return nil != activeApp && [activeApp.bundleID isEqualToString:systemApp.bundleID];
181
+ }
182
+ error:error];
183
+ }
184
+
185
+ @end
@@ -9,8 +9,8 @@
9
9
 
10
10
  #import <Foundation/Foundation.h>
11
11
 
12
+ @class FBApplication;
12
13
  @class FBElementCache;
13
- @class XCUIApplication;
14
14
 
15
15
  NS_ASSUME_NONNULL_BEGIN
16
16
 
@@ -20,16 +20,16 @@ NS_ASSUME_NONNULL_BEGIN
20
20
  @interface FBSession : NSObject
21
21
 
22
22
  /*! Application tested during that session */
23
- @property (nonatomic, readonly) XCUIApplication *activeApplication;
23
+ @property (nonatomic, strong, readonly) FBApplication *activeApplication;
24
24
 
25
25
  /*! Session's identifier */
26
- @property (nonatomic, readonly) NSString *identifier;
26
+ @property (nonatomic, copy, readonly) NSString *identifier;
27
27
 
28
28
  /*! Element cache related to that session */
29
- @property (nonatomic, readonly) FBElementCache *elementCache;
29
+ @property (nonatomic, strong, readonly) FBElementCache *elementCache;
30
30
 
31
31
  /*! The identifier of the active application */
32
- @property (nonatomic) NSString *defaultActiveApplication;
32
+ @property (nonatomic, copy) NSString *defaultActiveApplication;
33
33
 
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;
@@ -57,7 +57,7 @@ NS_ASSUME_NONNULL_BEGIN
57
57
  @param application The application that we want to create session for
58
58
  @return new session
59
59
  */
60
- + (instancetype)initWithApplication:(nullable XCUIApplication *)application;
60
+ + (instancetype)initWithApplication:(nullable FBApplication *)application;
61
61
 
62
62
  /**
63
63
  Creates and saves new session for application with default alert handling behaviour
@@ -66,8 +66,7 @@ NS_ASSUME_NONNULL_BEGIN
66
66
  @param defaultAlertAction The default reaction to on-screen alert. Either 'accept' or 'dismiss'
67
67
  @return new session
68
68
  */
69
- + (instancetype)initWithApplication:(nullable XCUIApplication *)application
70
- defaultAlertAction:(NSString *)defaultAlertAction;
69
+ + (instancetype)initWithApplication:(nullable FBApplication *)application defaultAlertAction:(NSString *)defaultAlertAction;
71
70
 
72
71
  /**
73
72
  Kills application associated with that session and removes session
@@ -83,11 +82,12 @@ NS_ASSUME_NONNULL_BEGIN
83
82
  @param arguments The optional array of application command line arguments. The arguments are going to be applied if the application was not running before.
84
83
  @param environment The optional dictionary of environment variables for the application, which is going to be executed. The environment variables are going to be applied if the application was not running before.
85
84
  @return The application instance
85
+ @throws FBApplicationMethodNotSupportedException if the method is not supported with the current XCTest SDK
86
86
  */
87
- - (XCUIApplication *)launchApplicationWithBundleId:(NSString *)bundleIdentifier
88
- shouldWaitForQuiescence:(nullable NSNumber *)shouldWaitForQuiescence
89
- arguments:(nullable NSArray<NSString *> *)arguments
90
- environment:(nullable NSDictionary <NSString *, NSString *> *)environment;
87
+ - (FBApplication *)launchApplicationWithBundleId:(NSString *)bundleIdentifier
88
+ shouldWaitForQuiescence:(nullable NSNumber *)shouldWaitForQuiescence
89
+ arguments:(nullable NSArray<NSString *> *)arguments
90
+ environment:(nullable NSDictionary <NSString *, NSString *> *)environment;
91
91
 
92
92
  /**
93
93
  Activate an application with given bundle identifier in scope of current session.
@@ -95,8 +95,9 @@ NS_ASSUME_NONNULL_BEGIN
95
95
 
96
96
  @param bundleIdentifier Valid bundle identifier of the application to be activated
97
97
  @return The application instance
98
+ @throws FBApplicationMethodNotSupportedException if the method is not supported with the current XCTest SDK
98
99
  */
99
- - (XCUIApplication *)activateApplicationWithBundleId:(NSString *)bundleIdentifier;
100
+ - (FBApplication *)activateApplicationWithBundleId:(NSString *)bundleIdentifier;
100
101
 
101
102
  /**
102
103
  Terminate an application with the given bundle id. The application should be previously
@@ -14,6 +14,7 @@
14
14
 
15
15
  #import "FBXCAccessibilityElement.h"
16
16
  #import "FBAlertsMonitor.h"
17
+ #import "FBApplication.h"
17
18
  #import "FBConfiguration.h"
18
19
  #import "FBElementCache.h"
19
20
  #import "FBExceptions.h"
@@ -30,7 +31,7 @@
30
31
  NSString *const FBDefaultApplicationAuto = @"auto";
31
32
 
32
33
  @interface FBSession ()
33
- @property (nullable, nonatomic) XCUIApplication *testedApplication;
34
+ @property (nonatomic) NSString *testedApplicationBundleId;
34
35
  @property (nonatomic) BOOL isTestedApplicationExpectedToRun;
35
36
  @property (nonatomic) BOOL shouldAppsWaitForQuiescence;
36
37
  @property (nonatomic, nullable) FBAlertsMonitor *alertsMonitor;
@@ -95,7 +96,7 @@ static FBSession *_activeSession = nil;
95
96
  return _activeSession;
96
97
  }
97
98
 
98
- + (instancetype)initWithApplication:(XCUIApplication *)application
99
+ + (instancetype)initWithApplication:(FBApplication *)application
99
100
  {
100
101
  FBSession *session = [FBSession new];
101
102
  session.useNativeCachingStrategy = YES;
@@ -104,10 +105,10 @@ static FBSession *_activeSession = nil;
104
105
  session.elementsVisibilityCache = [NSMutableDictionary dictionary];
105
106
  session.identifier = [[NSUUID UUID] UUIDString];
106
107
  session.defaultActiveApplication = FBDefaultApplicationAuto;
107
- session.testedApplication = nil;
108
+ session.testedApplicationBundleId = nil;
108
109
  session.isTestedApplicationExpectedToRun = nil != application && application.running;
109
110
  if (application) {
110
- session.testedApplication = application;
111
+ session.testedApplicationBundleId = application.bundleID;
111
112
  session.shouldAppsWaitForQuiescence = application.fb_shouldWaitForQuiescence;
112
113
  }
113
114
  session.elementCache = [FBElementCache new];
@@ -115,7 +116,7 @@ static FBSession *_activeSession = nil;
115
116
  return session;
116
117
  }
117
118
 
118
- + (instancetype)initWithApplication:(nullable XCUIApplication *)application
119
+ + (instancetype)initWithApplication:(nullable FBApplication *)application
119
120
  defaultAlertAction:(NSString *)defaultAlertAction
120
121
  {
121
122
  FBSession *session = [self.class initWithApplication:application];
@@ -137,78 +138,81 @@ static FBSession *_activeSession = nil;
137
138
  self.alertsMonitor = nil;
138
139
  }
139
140
 
140
- if (nil != self.testedApplication
141
- && FBConfiguration.shouldTerminateApp
142
- && self.testedApplication.running
143
- && ![self.testedApplication fb_isSameAppAs:XCUIApplication.fb_systemApplication]) {
144
- @try {
145
- [self.testedApplication terminate];
146
- } @catch (NSException *e) {
147
- [FBLogger logFmt:@"%@", e.description];
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
+ }
148
150
  }
149
151
  }
150
152
 
151
153
  _activeSession = nil;
152
154
  }
153
155
 
154
- - (XCUIApplication *)activeApplication
156
+ - (FBApplication *)activeApplication
155
157
  {
156
- if (nil != self.testedApplication) {
157
- XCUIApplicationState testedAppState = self.testedApplication.state;
158
- if (testedAppState >= XCUIApplicationStateRunningForeground) {
159
- return (XCUIApplication *)self.testedApplication;
160
- }
161
- if (self.isTestedApplicationExpectedToRun && testedAppState <= XCUIApplicationStateNotRunning) {
162
- NSString *description = [NSString stringWithFormat:@"The application under test with bundle id '%@' is not running, possibly crashed", self.testedApplication.bundleID];
163
- @throw [NSException exceptionWithName:FBApplicationCrashedException reason:description userInfo:nil];
164
- }
165
- }
166
-
167
158
  NSString *defaultBundleId = [self.defaultActiveApplication isEqualToString:FBDefaultApplicationAuto]
168
159
  ? nil
169
160
  : self.defaultActiveApplication;
170
- return [XCUIApplication fb_activeApplicationWithDefaultBundleId:defaultBundleId];
161
+ FBApplication *application = [FBApplication fb_activeApplicationWithDefaultBundleId:defaultBundleId];
162
+ FBApplication *testedApplication = nil;
163
+ if (self.testedApplicationBundleId && self.isTestedApplicationExpectedToRun) {
164
+ testedApplication = nil != application.bundleID && [application.bundleID isEqualToString:self.testedApplicationBundleId]
165
+ ? application
166
+ : [[FBApplication alloc] initWithBundleIdentifier:self.testedApplicationBundleId];
167
+ }
168
+ if (testedApplication && !testedApplication.running) {
169
+ NSString *description = [NSString stringWithFormat:@"The application under test with bundle id '%@' is not running, possibly crashed", self.testedApplicationBundleId];
170
+ [[NSException exceptionWithName:FBApplicationCrashedException reason:description userInfo:nil] raise];
171
+ }
172
+ return application;
171
173
  }
172
174
 
173
- - (XCUIApplication *)launchApplicationWithBundleId:(NSString *)bundleIdentifier
174
- shouldWaitForQuiescence:(nullable NSNumber *)shouldWaitForQuiescence
175
- arguments:(nullable NSArray<NSString *> *)arguments
176
- environment:(nullable NSDictionary <NSString *, NSString *> *)environment
175
+ - (FBApplication *)launchApplicationWithBundleId:(NSString *)bundleIdentifier
176
+ shouldWaitForQuiescence:(nullable NSNumber *)shouldWaitForQuiescence
177
+ arguments:(nullable NSArray<NSString *> *)arguments
178
+ environment:(nullable NSDictionary <NSString *, NSString *> *)environment
177
179
  {
178
- XCUIApplication *app = [self makeApplicationWithBundleId:bundleIdentifier];
180
+ FBApplication *app = [[FBApplication alloc] initWithBundleIdentifier:bundleIdentifier];
179
181
  if (nil == shouldWaitForQuiescence) {
180
182
  // Iherit the quiescence check setting from the main app under test by default
181
- app.fb_shouldWaitForQuiescence = nil != self.testedApplication && self.shouldAppsWaitForQuiescence;
183
+ app.fb_shouldWaitForQuiescence = nil != self.testedApplicationBundleId && self.shouldAppsWaitForQuiescence;
182
184
  } else {
183
185
  app.fb_shouldWaitForQuiescence = [shouldWaitForQuiescence boolValue];
184
186
  }
185
- if (!app.running) {
187
+ if (app.fb_state < 2) {
186
188
  app.launchArguments = arguments ?: @[];
187
189
  app.launchEnvironment = environment ?: @{};
188
190
  [app launch];
189
191
  } else {
190
- [app activate];
192
+ [app fb_activate];
191
193
  }
192
- if ([app fb_isSameAppAs:self.testedApplication]) {
194
+ if (nil != self.testedApplicationBundleId
195
+ && [bundleIdentifier isEqualToString:(NSString *)self.testedApplicationBundleId]) {
193
196
  self.isTestedApplicationExpectedToRun = YES;
194
197
  }
195
198
  return app;
196
199
  }
197
200
 
198
- - (XCUIApplication *)activateApplicationWithBundleId:(NSString *)bundleIdentifier
201
+ - (FBApplication *)activateApplicationWithBundleId:(NSString *)bundleIdentifier
199
202
  {
200
- XCUIApplication *app = [self makeApplicationWithBundleId:bundleIdentifier];
201
- [app activate];
203
+ FBApplication *app = [[FBApplication alloc] initWithBundleIdentifier:bundleIdentifier];
204
+ [app fb_activate];
202
205
  return app;
203
206
  }
204
207
 
205
208
  - (BOOL)terminateApplicationWithBundleId:(NSString *)bundleIdentifier
206
209
  {
207
- XCUIApplication *app = [self makeApplicationWithBundleId:bundleIdentifier];
208
- if ([app fb_isSameAppAs:self.testedApplication]) {
210
+ FBApplication *app = [[FBApplication alloc] initWithBundleIdentifier:bundleIdentifier];
211
+ if (nil != self.testedApplicationBundleId
212
+ && [bundleIdentifier isEqualToString:(NSString *)self.testedApplicationBundleId]) {
209
213
  self.isTestedApplicationExpectedToRun = NO;
210
214
  }
211
- if (app.running) {
215
+ if (app.fb_state >= 2) {
212
216
  [app terminate];
213
217
  return YES;
214
218
  }
@@ -217,14 +221,7 @@ static FBSession *_activeSession = nil;
217
221
 
218
222
  - (NSUInteger)applicationStateWithBundleId:(NSString *)bundleIdentifier
219
223
  {
220
- return [self makeApplicationWithBundleId:bundleIdentifier].state;
221
- }
222
-
223
- - (XCUIApplication *)makeApplicationWithBundleId:(NSString *)bundleIdentifier
224
- {
225
- return nil != self.testedApplication && [bundleIdentifier isEqualToString:(NSString *)self.testedApplication.bundleID]
226
- ? self.testedApplication
227
- : [[XCUIApplication alloc] initWithBundleIdentifier:bundleIdentifier];
224
+ return [[FBApplication alloc] initWithBundleIdentifier:bundleIdentifier].fb_state;
228
225
  }
229
226
 
230
227
  @end
@@ -10,9 +10,9 @@
10
10
  #import "FBAlertsMonitor.h"
11
11
 
12
12
  #import "FBAlert.h"
13
+ #import "FBApplication.h"
13
14
  #import "FBLogger.h"
14
15
  #import "XCUIApplication+FBAlert.h"
15
- #import "XCUIApplication+FBHelpers.h"
16
16
 
17
17
  static const NSTimeInterval FB_MONTORING_INTERVAL = 2.0;
18
18
 
@@ -49,8 +49,8 @@ static const NSTimeInterval FB_MONTORING_INTERVAL = 2.0;
49
49
  }
50
50
 
51
51
  dispatch_async(dispatch_get_main_queue(), ^{
52
- NSArray<XCUIApplication *> *activeApps = XCUIApplication.fb_activeApplications;
53
- for (XCUIApplication *activeApp in activeApps) {
52
+ NSArray<FBApplication *> *activeApps = FBApplication.fb_activeApplications;
53
+ for (FBApplication *activeApp in activeApps) {
54
54
  XCUIElement *alertElement = nil;
55
55
  @try {
56
56
  alertElement = activeApp.fb_alertElement;
@@ -9,6 +9,8 @@
9
9
 
10
10
  #import "FBKeyboard.h"
11
11
 
12
+
13
+ #import "FBApplication.h"
12
14
  #import "FBConfiguration.h"
13
15
  #import "FBXCTestDaemonsProxy.h"
14
16
  #import "FBErrorBuilder.h"
@@ -23,13 +25,10 @@
23
25
 
24
26
  @implementation FBKeyboard
25
27
 
26
- + (BOOL)waitUntilVisibleForApplication:(XCUIApplication *)app
27
- timeout:(NSTimeInterval)timeout
28
- error:(NSError **)error
28
+ + (BOOL)waitUntilVisibleForApplication:(XCUIApplication *)app timeout:(NSTimeInterval)timeout error:(NSError **)error
29
29
  {
30
30
  BOOL (^isKeyboardVisible)(void) = ^BOOL(void) {
31
- XCUIElement *keyboard = app.keyboards.fb_firstMatch;
32
- if (nil == keyboard) {
31
+ if (!app.keyboard.exists) {
33
32
  return NO;
34
33
  }
35
34
 
@@ -37,7 +36,7 @@
37
36
  NSDictionary *bindings) {
38
37
  return snapshot.label.length > 0;
39
38
  }];
40
- XCUIElement *firstKey = [[keyboard descendantsMatchingType:XCUIElementTypeKey]
39
+ XCUIElement *firstKey = [[app.keyboard descendantsMatchingType:XCUIElementTypeKey]
41
40
  matchingPredicate:keySearchPredicate].allElementsBoundByIndex.firstObject;
42
41
  return firstKey.exists && firstKey.hittable;
43
42
  };
@@ -13,6 +13,7 @@
13
13
  @import UniformTypeIdentifiers;
14
14
 
15
15
  #import "GCDAsyncSocket.h"
16
+ #import "FBApplication.h"
16
17
  #import "FBConfiguration.h"
17
18
  #import "FBLogger.h"
18
19
  #import "FBScreenshot.h"
@@ -11,9 +11,9 @@
11
11
 
12
12
  #import <mach/mach_time.h>
13
13
  #import "FBAlert.h"
14
+ #import "FBApplication.h"
14
15
  #import "FBErrorBuilder.h"
15
16
  #import "FBMacros.h"
16
- #import "XCUIApplication+FBHelpers.h"
17
17
  #import "XCUIApplication+FBAlert.h"
18
18
 
19
19
  #define ALERT_TIMEOUT_SEC 30
@@ -101,7 +101,7 @@
101
101
  break;
102
102
  }
103
103
 
104
- XCUIElement *alertElement = XCUIApplication.fb_systemApplication.fb_alertElement;
104
+ XCUIElement *alertElement = FBApplication.fb_systemApplication.fb_alertElement;
105
105
  if (nil != alertElement) {
106
106
  FBAlert *alert = [FBAlert alertWithElement:alertElement];
107
107
  [alert acceptWithError:nil];
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  #import "FBScreen.h"
11
+ #import "FBApplication.h"
11
12
  #import "XCUIElement+FBIsVisible.h"
12
13
  #import "FBXCodeCompatibility.h"
13
14
  #import "XCUIScreen.h"
@@ -21,7 +22,7 @@
21
22
 
22
23
  + (CGSize)statusBarSizeForApplication:(XCUIApplication *)application
23
24
  {
24
- XCUIApplication *app = XCUIApplication.fb_systemApplication;
25
+ XCUIApplication *app = FBApplication.fb_systemApplication;
25
26
  // Since iOS 13 the status bar is no longer part of the application, it’s part of the SpringBoard
26
27
  XCUIElement *mainStatusBar = app.statusBars.allElementsBoundByIndex.firstObject;
27
28
  if (nil == mainStatusBar) {
@@ -10,6 +10,7 @@
10
10
  #import "FBTVNavigationTracker.h"
11
11
  #import "FBTVNavigationTracker-Private.h"
12
12
 
13
+ #import "FBApplication.h"
13
14
  #import "FBMathUtils.h"
14
15
  #import "XCUIElement+FBCaching.h"
15
16
  #import "XCUIElement+FBUtilities.h"
@@ -68,7 +69,7 @@
68
69
 
69
70
  - (FBTVDirection)directionToFocusedElement
70
71
  {
71
- XCUIElement *focused = XCUIApplication.fb_activeApplication.fb_focusedElement;
72
+ XCUIElement *focused = FBApplication.fb_activeApplication.fb_focusedElement;
72
73
 
73
74
  CGPoint focusedCenter = FBRectGetCenter(focused.wdFrame);
74
75
  FBTVNavigationItem *item = [self navigationItemWithElement:focused];
@@ -28,6 +28,39 @@ NSInteger FBTestmanagerdVersion(void);
28
28
 
29
29
  NS_ASSUME_NONNULL_BEGIN
30
30
 
31
+ /**
32
+ The exception happends if one tries to call application method,
33
+ which is not supported in the current iOS version
34
+ */
35
+ extern NSString *const FBApplicationMethodNotSupportedException;
36
+
37
+ @interface XCUIApplication (FBCompatibility)
38
+
39
+ + (nullable instancetype)fb_applicationWithPID:(pid_t)processID;
40
+
41
+ /**
42
+ Get the state of the application. This method only returns reliable results on Xcode SDK 9+
43
+
44
+ @return State value as enum item. See https://developer.apple.com/documentation/xctest/xcuiapplicationstate?language=objc for more details.
45
+ */
46
+ - (NSUInteger)fb_state;
47
+
48
+ /**
49
+ Activate the application by restoring it from the background.
50
+ Nothing will happen if the application is already in foreground.
51
+ This method is only supported since Xcode9.
52
+
53
+ @throws FBTimeoutException if the app is still not active after the timeout
54
+ */
55
+ - (void)fb_activate;
56
+
57
+ /**
58
+ Terminate the application and wait until it disappears from the list of active apps
59
+ */
60
+ - (void)fb_terminate;
61
+
62
+ @end
63
+
31
64
  @interface XCUIElementQuery (FBCompatibility)
32
65
 
33
66
  /* Performs short-circuit UI tree traversion in iOS 11+ to get the first element matched by the query. Equals to nil if no matching elements are found */
@@ -9,7 +9,6 @@
9
9
 
10
10
  #import "FBXCodeCompatibility.h"
11
11
 
12
- #import "FBXCAXClientProxy.h"
13
12
  #import "FBConfiguration.h"
14
13
  #import "FBErrorBuilder.h"
15
14
  #import "FBLogger.h"
@@ -18,6 +17,45 @@
18
17
  #import "FBXCTestDaemonsProxy.h"
19
18
  #import "XCTestManager_ManagerInterface-Protocol.h"
20
19
 
20
+ static const NSTimeInterval APP_STATE_CHANGE_TIMEOUT = 5.0;
21
+
22
+ NSString *const FBApplicationMethodNotSupportedException = @"FBApplicationMethodNotSupportedException";
23
+
24
+ @implementation XCUIApplication (FBCompatibility)
25
+
26
+ + (instancetype)fb_applicationWithPID:(pid_t)processID
27
+ {
28
+ if (0 == processID) {
29
+ return nil;
30
+ }
31
+
32
+ return [self applicationWithPID:processID];
33
+ }
34
+
35
+ - (void)fb_activate
36
+ {
37
+ [self activate];
38
+ if (![self waitForState:XCUIApplicationStateRunningForeground timeout:APP_STATE_CHANGE_TIMEOUT / 2] || ![self fb_waitForAppElement:APP_STATE_CHANGE_TIMEOUT / 2]) {
39
+ [FBLogger logFmt:@"The application '%@' is not running in foreground after %.2f seconds", self.bundleID, APP_STATE_CHANGE_TIMEOUT];
40
+ }
41
+ }
42
+
43
+ - (void)fb_terminate
44
+ {
45
+ [self terminate];
46
+ if (![self waitForState:XCUIApplicationStateNotRunning timeout:APP_STATE_CHANGE_TIMEOUT]) {
47
+ [FBLogger logFmt:@"The active application is still '%@' after %.2f seconds timeout", self.bundleID, APP_STATE_CHANGE_TIMEOUT];
48
+ }
49
+ }
50
+
51
+ - (NSUInteger)fb_state
52
+ {
53
+ return [[self valueForKey:@"state"] intValue];
54
+ }
55
+
56
+ @end
57
+
58
+
21
59
  @implementation XCUIElementQuery (FBCompatibility)
22
60
 
23
61
  - (XCElementSnapshot *)fb_uniqueSnapshotWithError:(NSError **)error
@@ -51,7 +89,7 @@
51
89
  static dispatch_once_t hasIncludingNonModalElements;
52
90
  static BOOL result;
53
91
  dispatch_once(&hasIncludingNonModalElements, ^{
54
- result = [XCUIApplication.fb_systemApplication.query respondsToSelector:@selector(includingNonModalElements)];
92
+ result = [FBApplication.fb_systemApplication.query respondsToSelector:@selector(includingNonModalElements)];
55
93
  });
56
94
  return result;
57
95
  }
@@ -16,6 +16,7 @@ FOUNDATION_EXPORT double WebDriverAgentLib_VersionNumber;
16
16
  FOUNDATION_EXPORT const unsigned char WebDriverAgentLib_VersionString[];
17
17
 
18
18
  #import <WebDriverAgentLib/FBAlert.h>
19
+ #import <WebDriverAgentLib/FBApplication.h>
19
20
  #import <WebDriverAgentLib/FBCommandHandler.h>
20
21
  #import <WebDriverAgentLib/FBCommandStatus.h>
21
22
  #import <WebDriverAgentLib/FBConfiguration.h>