appium-webdriveragent 4.10.24 → 4.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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [4.12.0](https://github.com/appium/WebDriverAgent/compare/v4.11.0...v4.12.0) (2023-02-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * Add support of the simulated geolocation setting ([#662](https://github.com/appium/WebDriverAgent/issues/662)) ([ebb9e60](https://github.com/appium/WebDriverAgent/commit/ebb9e60d56c0e0db9f509437ed639a3a39f6011b))
7
+
8
+ ## [4.11.0](https://github.com/appium/WebDriverAgent/compare/v4.10.24...v4.11.0) (2023-02-19)
9
+
10
+
11
+ ### Features
12
+
13
+ * Add openUrl handler available since Xcode 14.3 ([#661](https://github.com/appium/WebDriverAgent/issues/661)) ([bee564e](https://github.com/appium/WebDriverAgent/commit/bee564e8c6b975aff07fd1244583f0727a0f5470))
14
+
1
15
  ## [4.10.24](https://github.com/appium/WebDriverAgent/compare/v4.10.23...v4.10.24) (2023-02-17)
2
16
 
3
17
 
@@ -10,6 +10,9 @@
10
10
  #import <UIKit/UIKit.h>
11
11
 
12
12
  @class NSMutableDictionary, NSXPCConnection, XCSynthesizedEventRecord;
13
+ #if !TARGET_OS_TV // tvOS does not provide relevant APIs
14
+ @class CLLocation;
15
+ #endif
13
16
  @protocol XCTUIApplicationMonitor, XCTAXClient, XCTestManager_ManagerInterface;
14
17
 
15
18
  // iOS since 10.3
@@ -67,4 +70,14 @@
67
70
  - (void)_reportInvalidation;
68
71
  - (id)initWithConnection:(id)arg1;
69
72
 
73
+ // Since Xcode 14.3
74
+ - (void)openURL:(NSURL *)arg1 usingApplication:(NSString *)arg2 completion:(void (^)(_Bool, NSError *))arg3;
75
+ - (void)openDefaultApplicationForURL:(NSURL *)arg1 completion:(void (^)(_Bool, NSError *))arg2;
76
+ #if !TARGET_OS_TV // tvOS does not provide relevant APIs
77
+ - (void)setSimulatedLocation:(CLLocation *)arg1 completion:(void (^)(_Bool, NSError *))arg2;
78
+ - (void)getSimulatedLocationWithReply:(void (^)(CLLocation *, NSError *))arg1;
79
+ - (void)clearSimulatedLocationWithReply:(void (^)(_Bool, NSError *))arg1;
80
+ @property(readonly) _Bool supportsLocationSimulation;
81
+ #endif
82
+
70
83
  @end
@@ -3666,6 +3666,7 @@
3666
3666
  "-Wno-objc-messaging-id",
3667
3667
  "-Wno-direct-ivar-access",
3668
3668
  "-Wno-cast-qual",
3669
+ "-Wno-declaration-after-statement",
3669
3670
  );
3670
3671
  };
3671
3672
  name = Debug;
@@ -3728,6 +3729,7 @@
3728
3729
  "-Wno-objc-messaging-id",
3729
3730
  "-Wno-direct-ivar-access",
3730
3731
  "-Wno-cast-qual",
3732
+ "-Wno-declaration-after-statement",
3731
3733
  );
3732
3734
  };
3733
3735
  name = Release;
@@ -3974,6 +3976,7 @@
3974
3976
  "-Wno-objc-messaging-id",
3975
3977
  "-Wno-direct-ivar-access",
3976
3978
  "-Wno-cast-qual",
3979
+ "-Wno-declaration-after-statement",
3977
3980
  );
3978
3981
  };
3979
3982
  name = Debug;
@@ -4033,6 +4036,7 @@
4033
4036
  "-Wno-objc-messaging-id",
4034
4037
  "-Wno-direct-ivar-access",
4035
4038
  "-Wno-cast-qual",
4039
+ "-Wno-declaration-after-statement",
4036
4040
  );
4037
4041
  };
4038
4042
  name = Release;
@@ -9,6 +9,10 @@
9
9
 
10
10
  #import <XCTest/XCTest.h>
11
11
 
12
+ #if !TARGET_OS_TV
13
+ #import <CoreLocation/CoreLocation.h>
14
+ #endif
15
+
12
16
  NS_ASSUME_NONNULL_BEGIN
13
17
 
14
18
  typedef NS_ENUM(NSUInteger, FBUIInterfaceAppearance) {
@@ -71,8 +75,9 @@ typedef NS_ENUM(NSUInteger, FBUIInterfaceAppearance) {
71
75
  - (nullable NSString *)fb_wifiIPAddress;
72
76
 
73
77
  /**
74
- Opens the particular url scheme using Siri voice recognition helpers.
75
- This will only work since XCode 8.3/iOS 10.3
78
+ Opens the particular url scheme using the default application assigned to it.
79
+ This API only works since XCode 14.3/iOS 16.4
80
+ Older Xcode/iOS version try to use Siri fallback.
76
81
 
77
82
  @param url The url scheme represented as a string, for example https://apple.com
78
83
  @param error If there is an error, upon return contains an NSError object that describes the problem.
@@ -80,6 +85,17 @@ typedef NS_ENUM(NSUInteger, FBUIInterfaceAppearance) {
80
85
  */
81
86
  - (BOOL)fb_openUrl:(NSString *)url error:(NSError **)error;
82
87
 
88
+ /**
89
+ Opens the particular url scheme using the given application
90
+ This API only works since XCode 14.3/iOS 16.4
91
+
92
+ @param url The url scheme represented as a string, for example https://apple.com
93
+ @param bundleId The bundle identifier of an application to use in order to open the given URL
94
+ @param error If there is an error, upon return contains an NSError object that describes the problem.
95
+ @return YES if the operation was successful
96
+ */
97
+ - (BOOL)fb_openUrl:(NSString *)url withApplication:(NSString *)bundleId error:(NSError **)error;
98
+
83
99
  /**
84
100
  Presses the corresponding hardware button on the device with duration.
85
101
 
@@ -142,6 +158,36 @@ typedef NS_ENUM(NSUInteger, FBUIInterfaceAppearance) {
142
158
  */
143
159
  - (nullable NSNumber *)fb_getAppearance;
144
160
 
161
+ #if !TARGET_OS_TV
162
+ /**
163
+ Allows to set a simulated geolocation coordinates.
164
+ Only works since Xcode 14.3/iOS 16.4
165
+
166
+ @param location The simlated location coordinates to set
167
+ @param error If there is an error, upon return contains an NSError object that describes the problem.
168
+ @return YES if the simulated location has been successfully set
169
+ */
170
+ - (BOOL)fb_setSimulatedLocation:(CLLocation *)location error:(NSError **)error;
171
+
172
+ /**
173
+ Allows to get a simulated geolocation coordinates.
174
+ Only works since Xcode 14.3/iOS 16.4
175
+
176
+ @param error If there is an error, upon return contains an NSError object that describes the problem.
177
+ @return The current simulated location or nil in case of failure
178
+ */
179
+ - (nullable CLLocation *)fb_getSimulatedLocation:(NSError **)error;
180
+
181
+ /**
182
+ Allows to clear a previosuly set simulated geolocation coordinates.
183
+ Only works since Xcode 14.3/iOS 16.4
184
+
185
+ @param error If there is an error, upon return contains an NSError object that describes the problem.
186
+ @return YES if the simulated location has been successfully cleared
187
+ */
188
+ - (BOOL)fb_clearSimulatedLocation:(NSError **)error;
189
+ #endif
190
+
145
191
  @end
146
192
 
147
193
  NS_ASSUME_NONNULL_END
@@ -21,6 +21,7 @@
21
21
  #import "FBScreenshot.h"
22
22
  #import "FBXCDeviceEvent.h"
23
23
  #import "FBXCodeCompatibility.h"
24
+ #import "FBXCTestDaemonsProxy.h"
24
25
  #import "XCUIDevice.h"
25
26
 
26
27
  static const NSTimeInterval FBHomeButtonCoolOffTime = 1.;
@@ -157,20 +158,38 @@ static bool fb_isLocked;
157
158
  buildError:error];
158
159
  }
159
160
 
161
+ NSError *err;
162
+ if ([FBXCTestDaemonsProxy openDefaultApplicationForURL:parsedUrl error:&err]) {
163
+ return YES;
164
+ }
165
+ if (![err.description containsString:@"does not support"]) {
166
+ if (error) {
167
+ *error = err;
168
+ }
169
+ return NO;
170
+ }
171
+
160
172
  id siriService = [self valueForKey:@"siriService"];
161
173
  if (nil != siriService) {
162
174
  return [self fb_activateSiriVoiceRecognitionWithText:[NSString stringWithFormat:@"Open {%@}", url] error:error];
163
175
  }
164
- #pragma clang diagnostic push
165
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
166
- // The link never gets opened by this method: https://forums.developer.apple.com/thread/25355
167
- if (![[UIApplication sharedApplication] openURL:parsedUrl]) {
168
- #pragma clang diagnostic pop
176
+
177
+ NSString *description = [NSString stringWithFormat:@"Cannot open '%@' with the default application assigned for it. Consider upgrading to Xcode 14.3+/iOS 16.4+", url];
178
+ return [[[FBErrorBuilder builder]
179
+ withDescriptionFormat:@"%@", description]
180
+ buildError:error];;
181
+ }
182
+
183
+ - (BOOL)fb_openUrl:(NSString *)url withApplication:(NSString *)bundleId error:(NSError **)error
184
+ {
185
+ NSURL *parsedUrl = [NSURL URLWithString:url];
186
+ if (nil == parsedUrl) {
169
187
  return [[[FBErrorBuilder builder]
170
- withDescriptionFormat:@"The URL %@ cannot be opened", url]
188
+ withDescriptionFormat:@"'%@' is not a valid URL", url]
171
189
  buildError:error];
172
190
  }
173
- return YES;
191
+
192
+ return [FBXCTestDaemonsProxy openURL:parsedUrl usingApplication:bundleId error:error];
174
193
  }
175
194
 
176
195
  - (BOOL)fb_activateSiriVoiceRecognitionWithText:(NSString *)text error:(NSError **)error
@@ -336,4 +355,21 @@ static bool fb_isLocked;
336
355
  : nil;
337
356
  }
338
357
 
358
+ #if !TARGET_OS_TV
359
+ - (BOOL)fb_setSimulatedLocation:(CLLocation *)location error:(NSError **)error
360
+ {
361
+ return [FBXCTestDaemonsProxy setSimulatedLocation:location error:error];
362
+ }
363
+
364
+ - (nullable CLLocation *)fb_getSimulatedLocation:(NSError **)error
365
+ {
366
+ return [FBXCTestDaemonsProxy getSimulatedLocation:error];
367
+ }
368
+
369
+ - (BOOL)fb_clearSimulatedLocation:(NSError **)error
370
+ {
371
+ return [FBXCTestDaemonsProxy clearSimulatedLocation:error];
372
+ }
373
+ #endif
374
+
339
375
  @end
@@ -66,6 +66,14 @@
66
66
  [[FBRoute POST:@"/wda/device/appearance"].withoutSession respondWithTarget:self action:@selector(handleSetDeviceAppearance:)],
67
67
  [[FBRoute GET:@"/wda/device/location"] respondWithTarget:self action:@selector(handleGetLocation:)],
68
68
  [[FBRoute GET:@"/wda/device/location"].withoutSession respondWithTarget:self action:@selector(handleGetLocation:)],
69
+ #if !TARGET_OS_TV // tvOS does not provide relevant APIs
70
+ [[FBRoute GET:@"/wda/simulatedLocation"] respondWithTarget:self action:@selector(handleGetSimulatedLocation:)],
71
+ [[FBRoute GET:@"/wda/simulatedLocation"].withoutSession respondWithTarget:self action:@selector(handleGetSimulatedLocation:)],
72
+ [[FBRoute POST:@"/wda/simulatedLocation"] respondWithTarget:self action:@selector(handleSetSimulatedLocation:)],
73
+ [[FBRoute POST:@"/wda/simulatedLocation"].withoutSession respondWithTarget:self action:@selector(handleSetSimulatedLocation:)],
74
+ [[FBRoute DELETE:@"/wda/simulatedLocation"] respondWithTarget:self action:@selector(handleClearSimulatedLocation:)],
75
+ [[FBRoute DELETE:@"/wda/simulatedLocation"].withoutSession respondWithTarget:self action:@selector(handleClearSimulatedLocation:)],
76
+ #endif
69
77
  [[FBRoute OPTIONS:@"/*"].withoutSession respondWithTarget:self action:@selector(handlePingCommand:)],
70
78
  ];
71
79
  }
@@ -106,9 +114,9 @@
106
114
  BOOL isDismissed = [request.session.activeApplication fb_dismissKeyboardWithKeyNames:request.arguments[@"keyNames"]
107
115
  error:&error];
108
116
  return isDismissed
109
- ? FBResponseWithOK()
110
- : FBResponseWithStatus([FBCommandStatus invalidElementStateErrorWithMessage:error.description
111
- traceback:nil]);
117
+ ? FBResponseWithOK()
118
+ : FBResponseWithStatus([FBCommandStatus invalidElementStateErrorWithMessage:error.description
119
+ traceback:nil]);
112
120
  }
113
121
 
114
122
  + (id<FBResponsePayload>)handlePingCommand:(FBRouteRequest *)request
@@ -123,12 +131,12 @@
123
131
  FBSession *session = request.session;
124
132
  CGSize statusBarSize = [FBScreen statusBarSizeForApplication:session.activeApplication];
125
133
  return FBResponseWithObject(
126
- @{
134
+ @{
127
135
  @"statusBarSize": @{@"width": @(statusBarSize.width),
128
136
  @"height": @(statusBarSize.height),
129
- },
137
+ },
130
138
  @"scale": @([FBScreen scale]),
131
- });
139
+ });
132
140
  }
133
141
 
134
142
  + (id<FBResponsePayload>)handleLock:(FBRouteRequest *)request
@@ -325,7 +333,7 @@
325
333
  CLAuthorizationStatus authStatus;
326
334
  if ([locationManager respondsToSelector:@selector(authorizationStatus)]) {
327
335
  NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[locationManager class]
328
- instanceMethodSignatureForSelector:@selector(authorizationStatus)]];
336
+ instanceMethodSignatureForSelector:@selector(authorizationStatus)]];
329
337
  [invocation setSelector:@selector(authorizationStatus)];
330
338
  [invocation setTarget:locationManager];
331
339
  [invocation invoke];
@@ -379,8 +387,8 @@
379
387
  }
380
388
 
381
389
  FBUIInterfaceAppearance appearance = [name isEqualToString:@"light"]
382
- ? FBUIInterfaceAppearanceLight
383
- : FBUIInterfaceAppearanceDark;
390
+ ? FBUIInterfaceAppearanceLight
391
+ : FBUIInterfaceAppearanceDark;
384
392
  NSError *error;
385
393
  if (![XCUIDevice.sharedDevice fb_setAppearance:appearance error:&error]) {
386
394
  return FBResponseWithStatus([FBCommandStatus unknownErrorWithMessage:error.description
@@ -397,7 +405,7 @@
397
405
  NSString *currentLocale = [[NSLocale autoupdatingCurrentLocale] localeIdentifier];
398
406
 
399
407
  NSMutableDictionary *deviceInfo = [NSMutableDictionary dictionaryWithDictionary:
400
- @{
408
+ @{
401
409
  @"currentLocale": currentLocale,
402
410
  @"timeZone": self.timeZone,
403
411
  @"name": UIDevice.currentDevice.name,
@@ -490,4 +498,50 @@
490
498
  return [localTimeZone name];
491
499
  }
492
500
 
501
+ #if !TARGET_OS_TV // tvOS does not provide relevant APIs
502
+ + (id<FBResponsePayload>)handleGetSimulatedLocation:(FBRouteRequest *)request
503
+ {
504
+ NSError *error;
505
+ CLLocation *location = [XCUIDevice.sharedDevice fb_getSimulatedLocation:&error];
506
+ if (nil == location) {
507
+ return FBResponseWithStatus([FBCommandStatus unknownErrorWithMessage:error.description
508
+ traceback:nil]);
509
+ }
510
+ return FBResponseWithObject(@{
511
+ @"latitude": @(location.coordinate.latitude),
512
+ @"longiture": @(location.coordinate.longitude),
513
+ @"altitude": @(location.altitude),
514
+ });
515
+ }
516
+
517
+ + (id<FBResponsePayload>)handleSetSimulatedLocation:(FBRouteRequest *)request
518
+ {
519
+ NSNumber *longitude = request.arguments[@"longitude"];
520
+ NSNumber *latitude = request.arguments[@"latitude"];
521
+
522
+ if (nil == longitude || nil == latitude) {
523
+ return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:@"Both latitude and longitude must be provided"
524
+ traceback:nil]);
525
+ }
526
+ NSError *error;
527
+ CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude.doubleValue
528
+ longitude:longitude.doubleValue];
529
+ if (![XCUIDevice.sharedDevice fb_setSimulatedLocation:location error:&error]) {
530
+ return FBResponseWithStatus([FBCommandStatus unknownErrorWithMessage:error.description
531
+ traceback:nil]);
532
+ }
533
+ return FBResponseWithOK();
534
+ }
535
+
536
+ + (id<FBResponsePayload>)handleClearSimulatedLocation:(FBRouteRequest *)request
537
+ {
538
+ NSError *error;
539
+ if (![XCUIDevice.sharedDevice fb_clearSimulatedLocation:&error]) {
540
+ return FBResponseWithStatus([FBCommandStatus unknownErrorWithMessage:error.description
541
+ traceback:nil]);
542
+ }
543
+ return FBResponseWithOK();
544
+ }
545
+ #endif
546
+
493
547
  @end
@@ -66,9 +66,16 @@
66
66
  if (!urlString) {
67
67
  return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:@"URL is required" traceback:nil]);
68
68
  }
69
+ NSString* bundleId = request.arguments[@"bundleId"];
69
70
  NSError *error;
70
- if (![XCUIDevice.sharedDevice fb_openUrl:urlString error:&error]) {
71
- return FBResponseWithUnknownError(error);
71
+ if (nil == bundleId) {
72
+ if (![XCUIDevice.sharedDevice fb_openUrl:urlString error:&error]) {
73
+ return FBResponseWithUnknownError(error);
74
+ }
75
+ } else {
76
+ if (![XCUIDevice.sharedDevice fb_openUrl:urlString withApplication:bundleId error:&error]) {
77
+ return FBResponseWithUnknownError(error);
78
+ }
72
79
  }
73
80
  return FBResponseWithOK();
74
81
  }
@@ -24,7 +24,7 @@
24
24
 
25
25
  NSString *arbitraryAttrPrefix = @"attribute/";
26
26
 
27
- id<FBResponsePayload> FBResponseWithOK()
27
+ id<FBResponsePayload> FBResponseWithOK(void)
28
28
  {
29
29
  return FBResponseWithStatus(FBCommandStatus.ok);
30
30
  }
@@ -502,7 +502,9 @@ static NSNumberFormatter *numberFormatter = nil;
502
502
  return [[FBErrorBuilder.builder withDescription:fullDescription] build];
503
503
  }
504
504
 
505
- + (nullable FBClassChain*)compiledQueryWithTokenizedQuery:(NSArray<FBBaseClassChainToken *> *)tokenizedQuery originalQuery:(NSString *)originalQuery error:(NSError **)error
505
+ + (nullable FBClassChain*)compiledQueryWithTokenizedQuery:(NSArray<FBBaseClassChainToken *> *)tokenizedQuery
506
+ originalQuery:(NSString *)originalQuery
507
+ error:(NSError **)error
506
508
  {
507
509
  NSMutableArray *result = [NSMutableArray array];
508
510
  XCUIElementType chainElementType = XCUIElementTypeAny;
@@ -514,8 +516,10 @@ static NSNumberFormatter *numberFormatter = nil;
514
516
  for (FBBaseClassChainToken *token in tokenizedQuery) {
515
517
  if ([token isKindOfClass:FBClassNameToken.class]) {
516
518
  if (isTypeSet) {
517
- NSString *description = [NSString stringWithFormat:@"Unexpected token '%@'. The type name can be set only once.", token.asString];
518
- *error = [self.class compilationErrorWithQuery:originalQuery description:description];
519
+ if (error) {
520
+ NSString *description = [NSString stringWithFormat:@"Unexpected token '%@'. The type name can be set only once.", token.asString];
521
+ *error = [self.class compilationErrorWithQuery:originalQuery description:description];
522
+ }
519
523
  return nil;
520
524
  }
521
525
  @try {
@@ -523,16 +527,20 @@ static NSNumberFormatter *numberFormatter = nil;
523
527
  isTypeSet = YES;
524
528
  } @catch (NSException *e) {
525
529
  if ([e.name isEqualToString:FBInvalidArgumentException]) {
526
- NSString *description = [NSString stringWithFormat:@"'%@' class name is unknown to WDA", token.asString];
527
- *error = [self.class compilationErrorWithQuery:originalQuery description:description];
530
+ if (error) {
531
+ NSString *description = [NSString stringWithFormat:@"'%@' class name is unknown to WDA", token.asString];
532
+ *error = [self.class compilationErrorWithQuery:originalQuery description:description];
533
+ }
528
534
  return nil;
529
535
  }
530
536
  @throw e;
531
537
  }
532
538
  } else if ([token isKindOfClass:FBStarToken.class]) {
533
539
  if (isTypeSet) {
534
- NSString *description = [NSString stringWithFormat:@"Unexpected token '%@'. The type name can be set only once.", token.asString];
535
- *error = [self.class compilationErrorWithQuery:originalQuery description:description];
540
+ if (error) {
541
+ NSString *description = [NSString stringWithFormat:@"Unexpected token '%@'. The type name can be set only once.", token.asString];
542
+ *error = [self.class compilationErrorWithQuery:originalQuery description:description];
543
+ }
536
544
  return nil;
537
545
  }
538
546
  chainElementType = XCUIElementTypeAny;
@@ -540,7 +548,9 @@ static NSNumberFormatter *numberFormatter = nil;
540
548
  } else if ([token isKindOfClass:FBDescendantMarkerToken.class]) {
541
549
  if (isDescendantSet) {
542
550
  NSString *description = [NSString stringWithFormat:@"Unexpected token '%@'. Descendant markers cannot be duplicated.", token.asString];
543
- *error = [self.class compilationErrorWithQuery:originalQuery description:description];
551
+ if (error) {
552
+ *error = [self.class compilationErrorWithQuery:originalQuery description:description];
553
+ }
544
554
  return nil;
545
555
  }
546
556
  isTypeSet = NO;
@@ -550,12 +560,16 @@ static NSNumberFormatter *numberFormatter = nil;
550
560
  } else if ([token isKindOfClass:FBAbstractPredicateToken.class]) {
551
561
  if (isPositionSet) {
552
562
  NSString *description = [NSString stringWithFormat:@"Predicate value '%@' must be set before position value.", token.asString];
553
- *error = [self.class compilationErrorWithQuery:originalQuery description:description];
563
+ if (error) {
564
+ *error = [self.class compilationErrorWithQuery:originalQuery description:description];
565
+ }
554
566
  return nil;
555
567
  }
556
568
  if (!((FBAbstractPredicateToken *)token).isParsingCompleted) {
557
569
  NSString *description = [NSString stringWithFormat:@"Cannot find the end of '%@' predicate value.", token.asString];
558
- *error = [self.class compilationErrorWithQuery:originalQuery description:description];
570
+ if (error) {
571
+ *error = [self.class compilationErrorWithQuery:originalQuery description:description];
572
+ }
559
573
  return nil;
560
574
  }
561
575
  NSPredicate *value = [NSPredicate fb_snapshotBlockPredicateWithPredicate:[NSPredicate predicateWithFormat:token.asString]];
@@ -567,13 +581,17 @@ static NSNumberFormatter *numberFormatter = nil;
567
581
  } else if ([token isKindOfClass:FBNumberToken.class]) {
568
582
  if (isPositionSet) {
569
583
  NSString *description = [NSString stringWithFormat:@"Position value '%@' is expected to be set only once.", token.asString];
570
- *error = [self.class compilationErrorWithQuery:originalQuery description:description];
584
+ if (error) {
585
+ *error = [self.class compilationErrorWithQuery:originalQuery description:description];
586
+ }
571
587
  return nil;
572
588
  }
573
589
  NSNumber *position = [numberFormatter numberFromString:token.asString];
574
590
  if (nil == position || 0 == position.intValue) {
575
591
  NSString *description = [NSString stringWithFormat:@"Position value '%@' is expected to be a valid integer number not equal to zero.", token.asString];
576
- *error = [self.class compilationErrorWithQuery:originalQuery description:description];
592
+ if (error) {
593
+ *error = [self.class compilationErrorWithQuery:originalQuery description:description];
594
+ }
577
595
  return nil;
578
596
  }
579
597
  chainElementPosition = position;
@@ -602,8 +620,10 @@ static NSNumberFormatter *numberFormatter = nil;
602
620
  if (isTypeSet) {
603
621
  [result addObject:[[FBClassChainItem alloc] initWithType:chainElementType position:chainElementPosition predicates:predicates.copy isDescendant:YES]];
604
622
  } else {
605
- NSString *description = @"Descendants lookup modifier '**/' should be followed with the actual element type";
606
- *error = [self.class compilationErrorWithQuery:originalQuery description:description];
623
+ if (error) {
624
+ NSString *description = @"Descendants lookup modifier '**/' should be followed with the actual element type";
625
+ *error = [self.class compilationErrorWithQuery:originalQuery description:description];
626
+ }
607
627
  return nil;
608
628
  }
609
629
  } else {
@@ -47,7 +47,7 @@ void *FBRetrieveSymbolFromBinary(const char *binary, const char *name)
47
47
 
48
48
  static NSString *sdkVersion = nil;
49
49
  static dispatch_once_t onceSdkVersionToken;
50
- NSString * _Nullable FBSDKVersion()
50
+ NSString * _Nullable FBSDKVersion(void)
51
51
  {
52
52
  dispatch_once(&onceSdkVersionToken, ^{
53
53
  NSString *sdkName = [[NSBundle mainBundle] infoDictionary][@"DTSDKName"];
@@ -8,15 +8,17 @@
8
8
  */
9
9
 
10
10
  #import <XCTest/XCTest.h>
11
+
12
+ #if !TARGET_OS_TV
13
+ #import <CoreLocation/CoreLocation.h>
14
+ #endif
15
+
11
16
  #import "XCSynthesizedEventRecord.h"
12
17
 
13
18
  NS_ASSUME_NONNULL_BEGIN
14
19
 
15
20
  @protocol XCTestManager_ManagerInterface;
16
21
 
17
- /**
18
- Temporary class used to abstract interactions with TestManager daemon between Xcode 8.2.1 and Xcode 8.3-beta
19
- */
20
22
  @interface FBXCTestDaemonsProxy : NSObject
21
23
 
22
24
  + (id<XCTestManager_ManagerInterface>)testRunnerProxy;
@@ -28,6 +30,15 @@ NS_ASSUME_NONNULL_BEGIN
28
30
  + (BOOL)synthesizeEventWithRecord:(XCSynthesizedEventRecord *)record
29
31
  error:(NSError *__autoreleasing*)error;
30
32
 
33
+ + (BOOL)openURL:(NSURL *)url usingApplication:(NSString *)bundleId error:(NSError **)error;
34
+ + (BOOL)openDefaultApplicationForURL:(NSURL *)url error:(NSError **)error;
35
+
36
+ #if !TARGET_OS_TV
37
+ + (BOOL)setSimulatedLocation:(CLLocation *)location error:(NSError **)error;
38
+ + (nullable CLLocation *)getSimulatedLocation:(NSError **)error;
39
+ + (BOOL)clearSimulatedLocation:(NSError **)error;
40
+ #endif
41
+
31
42
  @end
32
43
 
33
44
  NS_ASSUME_NONNULL_END
@@ -12,6 +12,7 @@
12
12
  #import <objc/runtime.h>
13
13
 
14
14
  #import "FBConfiguration.h"
15
+ #import "FBErrorBuilder.h"
15
16
  #import "FBLogger.h"
16
17
  #import "FBRunLoopSpinner.h"
17
18
  #import "XCTestDriver.h"
@@ -96,4 +97,157 @@ static dispatch_once_t onceTestRunnerDaemonClass;
96
97
  return didSucceed;
97
98
  }
98
99
 
100
+ + (BOOL)openURL:(NSURL *)url usingApplication:(NSString *)bundleId error:(NSError *__autoreleasing*)error
101
+ {
102
+ XCTRunnerDaemonSession *session = [FBXCTRunnerDaemonSessionClass sharedSession];
103
+ if (![session respondsToSelector:@selector(openURL:usingApplication:completion:)]) {
104
+ if (error) {
105
+ [[[FBErrorBuilder builder]
106
+ withDescriptionFormat:@"The current Xcode SDK does not support opening of URLs with given application"]
107
+ buildError:error];
108
+ }
109
+ return NO;
110
+ }
111
+
112
+ __block BOOL didSucceed = NO;
113
+ [FBRunLoopSpinner spinUntilCompletion:^(void(^completion)(void)){
114
+ [session openURL:url usingApplication:bundleId completion:^(bool result, NSError *invokeError) {
115
+ if (error) {
116
+ *error = invokeError;
117
+ }
118
+ didSucceed = invokeError == nil && result;
119
+ completion();
120
+ }];
121
+ }];
122
+ return didSucceed;
123
+ }
124
+
125
+ + (BOOL)openDefaultApplicationForURL:(NSURL *)url error:(NSError *__autoreleasing*)error
126
+ {
127
+ XCTRunnerDaemonSession *session = [FBXCTRunnerDaemonSessionClass sharedSession];
128
+ if (![session respondsToSelector:@selector(openDefaultApplicationForURL:completion:)]) {
129
+ if (error) {
130
+ [[[FBErrorBuilder builder]
131
+ withDescriptionFormat:@"The current Xcode SDK does not support opening of URLs. Consider upgrading to Xcode 14.3+/iOS 16.4+"]
132
+ buildError:error];
133
+ }
134
+ return NO;
135
+ }
136
+
137
+ __block BOOL didSucceed = NO;
138
+ [FBRunLoopSpinner spinUntilCompletion:^(void(^completion)(void)){
139
+ [session openDefaultApplicationForURL:url completion:^(bool result, NSError *invokeError) {
140
+ if (error) {
141
+ *error = invokeError;
142
+ }
143
+ didSucceed = invokeError == nil && result;
144
+ completion();
145
+ }];
146
+ }];
147
+ return didSucceed;
148
+ }
149
+
150
+ #if !TARGET_OS_TV
151
+ + (BOOL)setSimulatedLocation:(CLLocation *)location error:(NSError *__autoreleasing*)error
152
+ {
153
+ XCTRunnerDaemonSession *session = [FBXCTRunnerDaemonSessionClass sharedSession];
154
+ if (![session respondsToSelector:@selector(setSimulatedLocation:completion:)]) {
155
+ if (error) {
156
+ [[[FBErrorBuilder builder]
157
+ withDescriptionFormat:@"The current Xcode SDK does not support location simulation. Consider upgrading to Xcode 14.3+/iOS 16.4+"]
158
+ buildError:error];
159
+ }
160
+ return NO;
161
+ }
162
+ if (![session supportsLocationSimulation]) {
163
+ if (error) {
164
+ [[[FBErrorBuilder builder]
165
+ withDescriptionFormat:@"Your device does not support location simulation"]
166
+ buildError:error];
167
+ }
168
+ return NO;
169
+ }
170
+
171
+ __block BOOL didSucceed = NO;
172
+ [FBRunLoopSpinner spinUntilCompletion:^(void(^completion)(void)){
173
+ [session setSimulatedLocation:location completion:^(bool result, NSError *invokeError) {
174
+ if (error) {
175
+ *error = invokeError;
176
+ }
177
+ didSucceed = invokeError == nil && result;
178
+ completion();
179
+ }];
180
+ }];
181
+ return didSucceed;
182
+ }
183
+
184
+ + (nullable CLLocation *)getSimulatedLocation:(NSError *__autoreleasing*)error;
185
+ {
186
+ XCTRunnerDaemonSession *session = [FBXCTRunnerDaemonSessionClass sharedSession];
187
+ if (![session respondsToSelector:@selector(getSimulatedLocationWithReply:)]) {
188
+ if (error) {
189
+ [[[FBErrorBuilder builder]
190
+ withDescriptionFormat:@"The current Xcode SDK does not support location simulation. Consider upgrading to Xcode 14.3+/iOS 16.4+"]
191
+ buildError:error];
192
+ }
193
+ return nil;
194
+ }
195
+ if (![session supportsLocationSimulation]) {
196
+ if (error) {
197
+ [[[FBErrorBuilder builder]
198
+ withDescriptionFormat:@"Your device does not support location simulation"]
199
+ buildError:error];
200
+ }
201
+ return nil;
202
+ }
203
+
204
+ __block CLLocation *location = nil;
205
+ [FBRunLoopSpinner spinUntilCompletion:^(void(^completion)(void)){
206
+ [session getSimulatedLocationWithReply:^(CLLocation *reply, NSError *invokeError) {
207
+ if (error) {
208
+ *error = invokeError;
209
+ }
210
+ if (nil == invokeError) {
211
+ location = reply;
212
+ }
213
+ completion();
214
+ }];
215
+ }];
216
+ return location;
217
+ }
218
+
219
+ + (BOOL)clearSimulatedLocation:(NSError *__autoreleasing*)error
220
+ {
221
+ XCTRunnerDaemonSession *session = [FBXCTRunnerDaemonSessionClass sharedSession];
222
+ if (![session respondsToSelector:@selector(clearSimulatedLocationWithReply:)]) {
223
+ if (error) {
224
+ [[[FBErrorBuilder builder]
225
+ withDescriptionFormat:@"The current Xcode SDK does not support location simulation. Consider upgrading to Xcode 14.3+/iOS 16.4+"]
226
+ buildError:error];
227
+ }
228
+ return NO;
229
+ }
230
+ if (![session supportsLocationSimulation]) {
231
+ if (error) {
232
+ [[[FBErrorBuilder builder]
233
+ withDescriptionFormat:@"Your device does not support location simulation"]
234
+ buildError:error];
235
+ }
236
+ return NO;
237
+ }
238
+
239
+ __block BOOL didSucceed = NO;
240
+ [FBRunLoopSpinner spinUntilCompletion:^(void(^completion)(void)){
241
+ [session clearSimulatedLocationWithReply:^(bool result, NSError *invokeError) {
242
+ if (error) {
243
+ *error = invokeError;
244
+ }
245
+ didSucceed = invokeError == nil && result;
246
+ completion();
247
+ }];
248
+ }];
249
+ return didSucceed;
250
+ }
251
+ #endif
252
+
99
253
  @end
@@ -118,15 +118,57 @@
118
118
  XCTAssertNil(error);
119
119
  }
120
120
 
121
- - (void)disabled_testUrlSchemeActivation
121
+ - (void)testUrlSchemeActivation
122
122
  {
123
- // This test is not stable on CI because of system slowness
123
+ if (SYSTEM_VERSION_LESS_THAN(@"16.4")) {
124
+ return;
125
+ }
126
+
124
127
  NSError *error;
125
128
  XCTAssertTrue([XCUIDevice.sharedDevice fb_openUrl:@"https://apple.com" error:&error]);
126
129
  FBAssertWaitTillBecomesTrue([FBApplication.fb_activeApplication.bundleID isEqualToString:@"com.apple.mobilesafari"]);
127
130
  XCTAssertNil(error);
128
131
  }
129
132
 
133
+ - (void)testUrlSchemeActivationWithApp
134
+ {
135
+ if (SYSTEM_VERSION_LESS_THAN(@"16.4")) {
136
+ return;
137
+ }
138
+
139
+ NSError *error;
140
+ XCTAssertTrue([XCUIDevice.sharedDevice fb_openUrl:@"https://apple.com"
141
+ withApplication:@"com.apple.mobilesafari"
142
+ error:&error]);
143
+ FBAssertWaitTillBecomesTrue([FBApplication.fb_activeApplication.bundleID isEqualToString:@"com.apple.mobilesafari"]);
144
+ XCTAssertNil(error);
145
+ }
146
+
147
+ #if !TARGET_OS_TV
148
+ - (void)testSimulatedLocationSetup
149
+ {
150
+ if (SYSTEM_VERSION_LESS_THAN(@"16.4")) {
151
+ return;
152
+ }
153
+
154
+ CLLocation *simulatedLocation = [[CLLocation alloc] initWithLatitude:50 longitude:50];
155
+ NSError *error;
156
+ XCTAssertTrue([XCUIDevice.sharedDevice fb_setSimulatedLocation:simulatedLocation error:&error]);
157
+ XCTAssertNil(error);
158
+ CLLocation *currentLocation = [XCUIDevice.sharedDevice fb_getSimulatedLocation:&error];
159
+ XCTAssertNil(error);
160
+ XCTAssertNotNil(currentLocation);
161
+ XCTAssertEqualWithAccuracy(simulatedLocation.coordinate.latitude, currentLocation.coordinate.latitude, 0.1);
162
+ XCTAssertEqualWithAccuracy(simulatedLocation.coordinate.longitude, currentLocation.coordinate.longitude, 0.1);
163
+ XCTAssertTrue([XCUIDevice.sharedDevice fb_clearSimulatedLocation:&error]);
164
+ XCTAssertNil(error);
165
+ currentLocation = [XCUIDevice.sharedDevice fb_getSimulatedLocation:&error];
166
+ XCTAssertNil(error);
167
+ XCTAssertNotEqualWithAccuracy(simulatedLocation.coordinate.latitude, currentLocation.coordinate.latitude, 0.1);
168
+ XCTAssertNotEqualWithAccuracy(simulatedLocation.coordinate.longitude, currentLocation.coordinate.longitude, 0.1);
169
+ }
170
+ #endif
171
+
130
172
  - (void)testPressingUnsupportedButton
131
173
  {
132
174
  NSError *error;
@@ -29,7 +29,7 @@
29
29
  xmlTextWriterPtr writer = xmlNewTextWriterDoc(&doc, 0);
30
30
  NSMutableDictionary *elementStore = [NSMutableDictionary dictionary];
31
31
  int buffersize;
32
- xmlChar *xmlbuff;
32
+ xmlChar *xmlbuff = NULL;
33
33
  int rc = xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL);
34
34
  if (rc >= 0) {
35
35
  rc = [FBXPath xmlRepresentationWithRootElement:element
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-webdriveragent",
3
- "version": "4.10.24",
3
+ "version": "4.12.0",
4
4
  "description": "Package bundling WebDriverAgent",
5
5
  "main": "./build/index.js",
6
6
  "scripts": {