appium-webdriveragent 16.3.0 → 16.5.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 +12 -0
- package/PrivateHeaders/XCTest/XCUIDevice.h +0 -1
- package/Scripts/build-webdriveragent.mjs +9 -9
- package/Scripts/build.sh +8 -1
- package/WebDriverAgent.xcodeproj/project.pbxproj +125 -107
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationTests_watchOS.xcscheme +4 -4
- package/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.h +34 -0
- package/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +72 -0
- package/WebDriverAgentLib/Commands/FBCustomCommands.m +36 -0
- package/WebDriverAgentLib/FBAlert.m +8 -6
- package/WebDriverAgentLib/Info.plist +2 -2
- package/WebDriverAgentTests/IntegrationTests_watchOS/IntegrationTests_watchOS-Bridging-Header.h +21 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAAlertIntegrationTests.swift +11 -9
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAAppLifecycleIntegrationTests.swift +18 -38
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAClickIntegrationTests.swift +11 -17
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDADeviceIntegrationTests.swift +74 -32
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAElementAttributeIntegrationTests.swift +23 -41
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAFindIntegrationTests.swift +70 -95
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAScreenshotAndSourceIntegrationTests.swift +24 -22
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDATypingIntegrationTests.swift +44 -28
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAWatchInProcessTestCase.swift +51 -0
- package/build/lib/check-dependencies.d.ts.map +1 -1
- package/build/lib/check-dependencies.js +21 -7
- package/build/lib/check-dependencies.js.map +1 -1
- package/build/lib/constants.d.ts +1 -2
- package/build/lib/constants.d.ts.map +1 -1
- package/build/lib/constants.js +1 -2
- package/build/lib/constants.js.map +1 -1
- package/build/lib/types.d.ts +2 -2
- package/build/lib/types.d.ts.map +1 -1
- package/build/lib/utils/index.d.ts +1 -1
- package/build/lib/utils/index.d.ts.map +1 -1
- package/build/lib/utils/index.js +1 -1
- package/build/lib/utils/index.js.map +1 -1
- package/build/lib/utils/platform.d.ts +12 -0
- package/build/lib/utils/platform.d.ts.map +1 -1
- package/build/lib/utils/platform.js +23 -1
- package/build/lib/utils/platform.js.map +1 -1
- package/build/lib/utils/xctestrun.js +27 -3
- package/build/lib/utils/xctestrun.js.map +1 -1
- package/build/lib/webdriveragent.d.ts.map +1 -1
- package/build/lib/webdriveragent.js +4 -1
- package/build/lib/webdriveragent.js.map +1 -1
- package/build/lib/xcodebuild.d.ts.map +1 -1
- package/build/lib/xcodebuild.js +19 -9
- package/build/lib/xcodebuild.js.map +1 -1
- package/lib/check-dependencies.ts +23 -7
- package/lib/constants.ts +1 -2
- package/lib/types.ts +4 -2
- package/lib/utils/index.ts +1 -1
- package/lib/utils/platform.ts +25 -1
- package/lib/utils/xctestrun.ts +29 -3
- package/lib/webdriveragent.ts +5 -1
- package/lib/xcodebuild.ts +27 -12
- package/package.json +3 -2
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDASessionIntegrationTests.swift +0 -72
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAUnknownCommandIntegrationTests.swift +0 -24
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAWatchHTTPClient.swift +0 -95
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAWatchIntegrationTestCase.swift +0 -61
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
buildForAnalyzing = "NO">
|
|
15
15
|
<BuildableReference
|
|
16
16
|
BuildableIdentifier = "primary"
|
|
17
|
-
BlueprintIdentifier = "
|
|
17
|
+
BlueprintIdentifier = "5D9539D7055B5E6EED56D100"
|
|
18
18
|
BuildableName = "IntegrationTests_watchOS.xctest"
|
|
19
19
|
BlueprintName = "IntegrationTests_watchOS"
|
|
20
20
|
ReferencedContainer = "container:WebDriverAgent.xcodeproj">
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
skipped = "NO">
|
|
33
33
|
<BuildableReference
|
|
34
34
|
BuildableIdentifier = "primary"
|
|
35
|
-
BlueprintIdentifier = "
|
|
35
|
+
BlueprintIdentifier = "5D9539D7055B5E6EED56D100"
|
|
36
36
|
BuildableName = "IntegrationTests_watchOS.xctest"
|
|
37
37
|
BlueprintName = "IntegrationTests_watchOS"
|
|
38
38
|
ReferencedContainer = "container:WebDriverAgent.xcodeproj">
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
<MacroExpansion>
|
|
54
54
|
<BuildableReference
|
|
55
55
|
BuildableIdentifier = "primary"
|
|
56
|
-
BlueprintIdentifier = "
|
|
56
|
+
BlueprintIdentifier = "5D9539D7055B5E6EED56D100"
|
|
57
57
|
BuildableName = "IntegrationTests_watchOS.xctest"
|
|
58
58
|
BlueprintName = "IntegrationTests_watchOS"
|
|
59
59
|
ReferencedContainer = "container:WebDriverAgent.xcodeproj">
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
<MacroExpansion>
|
|
70
70
|
<BuildableReference
|
|
71
71
|
BuildableIdentifier = "primary"
|
|
72
|
-
BlueprintIdentifier = "
|
|
72
|
+
BlueprintIdentifier = "5D9539D7055B5E6EED56D100"
|
|
73
73
|
BuildableName = "IntegrationTests_watchOS.xctest"
|
|
74
74
|
BlueprintName = "IntegrationTests_watchOS"
|
|
75
75
|
ReferencedContainer = "container:WebDriverAgent.xcodeproj">
|
|
@@ -166,6 +166,40 @@ typedef NS_ENUM(NSUInteger, FBUIInterfaceAppearance) {
|
|
|
166
166
|
*/
|
|
167
167
|
- (nullable NSNumber *)fb_getAppearance;
|
|
168
168
|
|
|
169
|
+
#if TARGET_OS_WATCH
|
|
170
|
+
/**
|
|
171
|
+
Rotates the Digital Crown on Apple Watch. See
|
|
172
|
+
https://developer.apple.com/documentation/xcuiautomation/xcuidevice/rotatedigitalcrown(delta:velocity:)
|
|
173
|
+
and https://developer.apple.com/documentation/xcuiautomation/xcuidevice/rotatedigitalcrown(delta:)
|
|
174
|
+
Invoked dynamically (this selector was only added to the SDK in Xcode 16.3), so this compiles against
|
|
175
|
+
older Xcode versions too - it just fails at runtime with an error if the OS/SDK combination in use
|
|
176
|
+
doesn't actually implement it.
|
|
177
|
+
|
|
178
|
+
@param delta The number of full crown rotations, e.g. 1.0 is one complete turn.
|
|
179
|
+
The sign gives the direction: positive rotates up/clockwise, negative rotates down/counterclockwise.
|
|
180
|
+
@param velocity The rotation speed, in rotations per second, e.g. 1.0 completes one full
|
|
181
|
+
rotation per second. This is the raw value behind the public XCUIGestureVelocity
|
|
182
|
+
API, which only exposes it via named presets (.slow/.default/.fast).
|
|
183
|
+
Pass nil to use XCTest's own default velocity instead of specifying one explicitly.
|
|
184
|
+
@param error If there is an error, upon return contains an NSError object that describes the problem.
|
|
185
|
+
@return YES if the operation succeeds, otherwise NO.
|
|
186
|
+
*/
|
|
187
|
+
- (BOOL)fb_rotateDigitalCrown:(double)delta velocity:(nullable NSNumber *)velocity error:(NSError **)error;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
Performs a hand gesture on Apple Watch (e.g. Double Tap, Wrist Flick). See
|
|
191
|
+
https://developer.apple.com/documentation/xcuiautomation/xcuidevice/perform(handgesture:)
|
|
192
|
+
Invoked dynamically (this selector was only added to the SDK in Xcode 16.3), so this compiles against
|
|
193
|
+
older Xcode versions too - it just fails at runtime with an error if the OS/SDK combination in use
|
|
194
|
+
doesn't actually implement it.
|
|
195
|
+
|
|
196
|
+
@param gestureName One of the supported gesture names: doubleTap (watchOS 10+), flick (watchOS 26+).
|
|
197
|
+
@param error If there is an error, upon return contains an NSError object that describes the problem.
|
|
198
|
+
@return YES if the operation succeeds, otherwise NO.
|
|
199
|
+
*/
|
|
200
|
+
- (BOOL)fb_performHandGesture:(NSString *)gestureName error:(NSError **)error;
|
|
201
|
+
#endif // TARGET_OS_WATCH
|
|
202
|
+
|
|
169
203
|
#if !TARGET_OS_TV
|
|
170
204
|
/**
|
|
171
205
|
Allows to set a simulated geolocation coordinates.
|
|
@@ -86,6 +86,27 @@ NSDictionary<NSString *, NSNumber *> *fb_availableButtonNames(void) {
|
|
|
86
86
|
}
|
|
87
87
|
#endif
|
|
88
88
|
|
|
89
|
+
#if TARGET_OS_WATCH
|
|
90
|
+
// Raw values from XCUIDeviceHandGesture (XCUIAutomation/XCUIDeviceHandGesture.h): doubleTap = 1, flick = 2.
|
|
91
|
+
// Referenced by raw integer rather than the enum constant, and invoked via NSInvocation in
|
|
92
|
+
// fb_performHandGesture:error: below, since that enum/selector was only added to the SDK in Xcode 16.3
|
|
93
|
+
// (flick specifically needs watchOS 12.0/26 - see the @available check below). This way the code compiles
|
|
94
|
+
// against any Xcode version; unsupported gestures/selectors are only rejected at runtime.
|
|
95
|
+
NSDictionary<NSString *, NSNumber *> *fb_availableHandGestureNames(void) {
|
|
96
|
+
static dispatch_once_t onceToken;
|
|
97
|
+
static NSDictionary *result;
|
|
98
|
+
dispatch_once(&onceToken, ^{
|
|
99
|
+
NSMutableDictionary *gestures = [NSMutableDictionary dictionary];
|
|
100
|
+
gestures[@"doubletap"] = @(1);
|
|
101
|
+
if (@available(watchOS 12.0, *)) {
|
|
102
|
+
gestures[@"flick"] = @(2);
|
|
103
|
+
}
|
|
104
|
+
result = [gestures copy];
|
|
105
|
+
});
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
#endif // TARGET_OS_WATCH
|
|
109
|
+
|
|
89
110
|
@implementation XCUIDevice (FBHelpers)
|
|
90
111
|
|
|
91
112
|
static bool fb_isLocked;
|
|
@@ -386,6 +407,57 @@ static bool fb_isLocked;
|
|
|
386
407
|
: nil;
|
|
387
408
|
}
|
|
388
409
|
|
|
410
|
+
#if TARGET_OS_WATCH
|
|
411
|
+
- (BOOL)fb_rotateDigitalCrown:(double)delta velocity:(nullable NSNumber *)velocity error:(NSError **)error
|
|
412
|
+
{
|
|
413
|
+
SEL selector = nil == velocity
|
|
414
|
+
? NSSelectorFromString(@"rotateDigitalCrownByDelta:")
|
|
415
|
+
: NSSelectorFromString(@"rotateDigitalCrownByDelta:withVelocity:");
|
|
416
|
+
if (nil == selector || ![self respondsToSelector:selector]) {
|
|
417
|
+
return [[[FBErrorBuilder builder]
|
|
418
|
+
withDescriptionFormat:@"Digital Crown rotation is not supported by the current Xcode SDK/OS combination"]
|
|
419
|
+
buildError:error];
|
|
420
|
+
}
|
|
421
|
+
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
|
|
422
|
+
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
|
423
|
+
[invocation setSelector:selector];
|
|
424
|
+
[invocation setTarget:self];
|
|
425
|
+
[invocation setArgument:&delta atIndex:2];
|
|
426
|
+
if (velocity) {
|
|
427
|
+
double velocityValue = velocity.doubleValue;
|
|
428
|
+
[invocation setArgument:&velocityValue atIndex:3];
|
|
429
|
+
}
|
|
430
|
+
[invocation invoke];
|
|
431
|
+
return YES;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
- (BOOL)fb_performHandGesture:(NSString *)gestureName error:(NSError **)error
|
|
435
|
+
{
|
|
436
|
+
NSDictionary<NSString *, NSNumber *> *availableGestures = fb_availableHandGestureNames();
|
|
437
|
+
NSNumber *gestureValue = availableGestures[gestureName.lowercaseString];
|
|
438
|
+
if (!gestureValue) {
|
|
439
|
+
NSArray *sortedKeys = [availableGestures.allKeys sortedArrayUsingSelector:@selector(compare:)];
|
|
440
|
+
return [[[FBErrorBuilder builder]
|
|
441
|
+
withDescriptionFormat:@"The hand gesture '%@' is not supported. The device under test only supports the following hand gestures: %@", gestureName, sortedKeys]
|
|
442
|
+
buildError:error];
|
|
443
|
+
}
|
|
444
|
+
SEL selector = NSSelectorFromString(@"performHandGesture:");
|
|
445
|
+
if (nil == selector || ![self respondsToSelector:selector]) {
|
|
446
|
+
return [[[FBErrorBuilder builder]
|
|
447
|
+
withDescriptionFormat:@"Hand gesture automation is not supported by the current Xcode SDK/OS combination"]
|
|
448
|
+
buildError:error];
|
|
449
|
+
}
|
|
450
|
+
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
|
|
451
|
+
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
|
452
|
+
[invocation setSelector:selector];
|
|
453
|
+
[invocation setTarget:self];
|
|
454
|
+
NSInteger gestureRawValue = gestureValue.integerValue;
|
|
455
|
+
[invocation setArgument:&gestureRawValue atIndex:2];
|
|
456
|
+
[invocation invoke];
|
|
457
|
+
return YES;
|
|
458
|
+
}
|
|
459
|
+
#endif // TARGET_OS_WATCH
|
|
460
|
+
|
|
389
461
|
#if !TARGET_OS_TV
|
|
390
462
|
- (BOOL)fb_setSimulatedLocation:(CLLocation *)location error:(NSError **)error
|
|
391
463
|
{
|
|
@@ -63,6 +63,10 @@
|
|
|
63
63
|
[[FBRoute GET:@"/wda/batteryInfo"] respondWithTarget:self action:@selector(handleGetBatteryInfo:)],
|
|
64
64
|
#endif
|
|
65
65
|
[[FBRoute POST:@"/wda/pressButton"] respondWithTarget:self action:@selector(handlePressButtonCommand:)],
|
|
66
|
+
#if TARGET_OS_WATCH
|
|
67
|
+
[[FBRoute POST:@"/wda/rotateDigitalCrown"] respondWithTarget:self action:@selector(handleRotateDigitalCrownCommand:)],
|
|
68
|
+
[[FBRoute POST:@"/wda/performHandGesture"] respondWithTarget:self action:@selector(handlePerformHandGestureCommand:)],
|
|
69
|
+
#endif
|
|
66
70
|
[[FBRoute POST:@"/wda/performAccessibilityAudit"] respondWithTarget:self action:@selector(handlePerformAccessibilityAudit:)],
|
|
67
71
|
[[FBRoute POST:@"/wda/performIoHidEvent"] respondWithTarget:self action:@selector(handlePeformIOHIDEvent:)],
|
|
68
72
|
[[FBRoute POST:@"/wda/expectNotification"] respondWithTarget:self action:@selector(handleExpectNotification:)],
|
|
@@ -295,6 +299,38 @@
|
|
|
295
299
|
return FBResponseWithOK();
|
|
296
300
|
}
|
|
297
301
|
|
|
302
|
+
#if TARGET_OS_WATCH
|
|
303
|
+
+ (id<FBResponsePayload>)handleRotateDigitalCrownCommand:(FBRouteRequest *)request
|
|
304
|
+
{
|
|
305
|
+
NSNumber *delta = request.arguments[@"delta"];
|
|
306
|
+
if (nil == delta) {
|
|
307
|
+
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:@"'delta' argument is mandatory"
|
|
308
|
+
traceback:nil]);
|
|
309
|
+
}
|
|
310
|
+
NSError *error;
|
|
311
|
+
if (![XCUIDevice.sharedDevice fb_rotateDigitalCrown:delta.doubleValue
|
|
312
|
+
velocity:request.arguments[@"velocity"]
|
|
313
|
+
error:&error]) {
|
|
314
|
+
return FBResponseWithUnknownError(error);
|
|
315
|
+
}
|
|
316
|
+
return FBResponseWithOK();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
+ (id<FBResponsePayload>)handlePerformHandGestureCommand:(FBRouteRequest *)request
|
|
320
|
+
{
|
|
321
|
+
NSString *gestureName = request.arguments[@"name"];
|
|
322
|
+
if (nil == gestureName) {
|
|
323
|
+
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:@"'name' argument is mandatory"
|
|
324
|
+
traceback:nil]);
|
|
325
|
+
}
|
|
326
|
+
NSError *error;
|
|
327
|
+
if (![XCUIDevice.sharedDevice fb_performHandGesture:gestureName error:&error]) {
|
|
328
|
+
return FBResponseWithUnknownError(error);
|
|
329
|
+
}
|
|
330
|
+
return FBResponseWithOK();
|
|
331
|
+
}
|
|
332
|
+
#endif
|
|
333
|
+
|
|
298
334
|
+ (id<FBResponsePayload>)handleActivateSiri:(FBRouteRequest *)request
|
|
299
335
|
{
|
|
300
336
|
NSError *error;
|
|
@@ -236,17 +236,18 @@
|
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
XCUIElement *acceptButton = nil;
|
|
239
|
-
|
|
239
|
+
NSString *acceptAlertButtonSelector = FBConfiguration.sharedInstance.acceptAlertButtonSelector ?: @"";
|
|
240
|
+
if (acceptAlertButtonSelector.length) {
|
|
240
241
|
NSString *errorReason = nil;
|
|
241
242
|
@try {
|
|
242
|
-
acceptButton = [[alertElement fb_descendantsMatchingClassChain:
|
|
243
|
+
acceptButton = [[alertElement fb_descendantsMatchingClassChain:acceptAlertButtonSelector
|
|
243
244
|
shouldReturnAfterFirstMatch:YES] firstObject];
|
|
244
245
|
} @catch (NSException *ex) {
|
|
245
246
|
errorReason = ex.reason;
|
|
246
247
|
}
|
|
247
248
|
if (nil == acceptButton) {
|
|
248
249
|
[FBLogger logFmt:@"Cannot find any match for Accept alert button using the class chain selector '%@'",
|
|
249
|
-
|
|
250
|
+
acceptAlertButtonSelector];
|
|
250
251
|
if (nil != errorReason) {
|
|
251
252
|
[FBLogger logFmt:@"Original error: %@", errorReason];
|
|
252
253
|
}
|
|
@@ -293,17 +294,18 @@
|
|
|
293
294
|
}
|
|
294
295
|
|
|
295
296
|
XCUIElement *dismissButton = nil;
|
|
296
|
-
|
|
297
|
+
NSString *dismissAlertButtonSelector = FBConfiguration.sharedInstance.dismissAlertButtonSelector ?: @"";
|
|
298
|
+
if (dismissAlertButtonSelector.length) {
|
|
297
299
|
NSString *errorReason = nil;
|
|
298
300
|
@try {
|
|
299
|
-
dismissButton = [[alertElement fb_descendantsMatchingClassChain:
|
|
301
|
+
dismissButton = [[alertElement fb_descendantsMatchingClassChain:dismissAlertButtonSelector
|
|
300
302
|
shouldReturnAfterFirstMatch:YES] firstObject];
|
|
301
303
|
} @catch (NSException *ex) {
|
|
302
304
|
errorReason = ex.reason;
|
|
303
305
|
}
|
|
304
306
|
if (nil == dismissButton) {
|
|
305
307
|
[FBLogger logFmt:@"Cannot find any match for Dismiss alert button using the class chain selector '%@'",
|
|
306
|
-
|
|
308
|
+
dismissAlertButtonSelector];
|
|
307
309
|
if (nil != errorReason) {
|
|
308
310
|
[FBLogger logFmt:@"Original error: %@", errorReason];
|
|
309
311
|
}
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
<key>CFBundlePackageType</key>
|
|
16
16
|
<string>FMWK</string>
|
|
17
17
|
<key>CFBundleShortVersionString</key>
|
|
18
|
-
<string>16.
|
|
18
|
+
<string>16.5.0</string>
|
|
19
19
|
<key>CFBundleSignature</key>
|
|
20
20
|
<string>????</string>
|
|
21
21
|
<key>CFBundleVersion</key>
|
|
22
|
-
<string>16.
|
|
22
|
+
<string>16.5.0</string>
|
|
23
23
|
<key>NSPrincipalClass</key>
|
|
24
24
|
<string/>
|
|
25
25
|
</dict>
|
package/WebDriverAgentTests/IntegrationTests_watchOS/IntegrationTests_watchOS-Bridging-Header.h
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
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.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Exposes the WebDriverAgentLib_watchOS categories these tests call directly, in-process,
|
|
10
|
+
// instead of going through the HTTP server.
|
|
11
|
+
|
|
12
|
+
#import "XCUIElement+FBFind.h"
|
|
13
|
+
#import "XCUIElement+FBClassChain.h"
|
|
14
|
+
#import "XCUIElement+FBIsVisible.h"
|
|
15
|
+
#import "XCUIElement+FBUtilities.h"
|
|
16
|
+
#import "XCUIElement+FBWebDriverAttributes.h"
|
|
17
|
+
#import "XCUIElement+FBTyping.h"
|
|
18
|
+
#import "XCUIApplication+FBHelpers.h"
|
|
19
|
+
#import "XCUIDevice+FBHelpers.h"
|
|
20
|
+
#import "FBAlert.h"
|
|
21
|
+
#import "FBElement.h"
|
|
@@ -8,16 +8,18 @@
|
|
|
8
8
|
|
|
9
9
|
import XCTest
|
|
10
10
|
|
|
11
|
-
/// Covers
|
|
12
|
-
/// so this only exercises the "no alert open"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
/// Covers FBAlert's no-alert-present behavior directly, in-process. IntegrationApp_watchOS never
|
|
12
|
+
/// presents a real alert, so this only exercises the "no alert open" path (`text`/`buttonLabels`
|
|
13
|
+
/// both just return nil - no HTTP-error-style throw at this layer), not the success path.
|
|
14
|
+
final class WDAAlertIntegrationTests: WDAWatchInProcessTestCase {
|
|
15
|
+
// All reads, no taps/typing - reuse the same running app across the whole class.
|
|
16
|
+
override class var relaunchForEachTest: Bool { false }
|
|
17
|
+
|
|
18
|
+
func testAlertTextWithNoAlertPresentReturnsNil() {
|
|
19
|
+
XCTAssertNil(FBAlert(application: app).text())
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
func
|
|
20
|
-
|
|
21
|
-
XCTAssertNotEqual(response.statusCode, 200)
|
|
22
|
+
func testAlertButtonsWithNoAlertPresentReturnsNil() {
|
|
23
|
+
XCTAssertNil(FBAlert(application: app).buttonLabels())
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -8,50 +8,30 @@
|
|
|
8
8
|
|
|
9
9
|
import XCTest
|
|
10
10
|
|
|
11
|
-
/// Covers the app lifecycle routes (launch/activate/terminate/state/list).
|
|
12
|
-
///
|
|
13
|
-
///
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
let response = try client.get("/session/\(sessionId!)/wda/apps/list")
|
|
21
|
-
XCTAssertEqual(response.statusCode, 200)
|
|
22
|
-
guard let apps = response.value as? [[String: Any]] else {
|
|
23
|
-
return XCTFail("Expected an array of active apps: \(String(describing: response.json))")
|
|
24
|
-
}
|
|
25
|
-
XCTAssertTrue(apps.contains { $0["bundleId"] as? String == Self.integrationAppBundleId })
|
|
11
|
+
/// Covers the app lifecycle routes (launch/activate/terminate/state/list). FBSession's
|
|
12
|
+
/// implementations of these are themselves thin wrappers over plain XCTest XCUIApplication
|
|
13
|
+
/// APIs (see FBSession.m), so this calls `.launch()`/`.activate()`/`.terminate()`/`.state`
|
|
14
|
+
/// directly instead of going through the HTTP server; only the apps-list route has a real WDA
|
|
15
|
+
/// category behind it (`+fb_activeAppsInfo`).
|
|
16
|
+
final class WDAAppLifecycleIntegrationTests: WDAWatchInProcessTestCase {
|
|
17
|
+
func testAppsListIncludesTheAppUnderTest() {
|
|
18
|
+
let apps = XCUIApplication.fb_activeAppsInfo()
|
|
19
|
+
XCTAssertTrue(apps.contains { $0["bundleId"] as? String == WDAWatchInProcessTestCase.integrationAppBundleId })
|
|
26
20
|
}
|
|
27
21
|
|
|
28
|
-
func testTerminateThenRelaunchLifecycle()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
let terminateResponse = try client.post("/session/\(sessionId!)/wda/apps/terminate", body: ["bundleId": bundleId])
|
|
32
|
-
XCTAssertEqual(terminateResponse.statusCode, 200)
|
|
33
|
-
XCTAssertEqual(terminateResponse.value as? Bool, true)
|
|
34
|
-
|
|
35
|
-
let stateAfterTerminate = try client.post("/session/\(sessionId!)/wda/apps/state", body: ["bundleId": bundleId])
|
|
36
|
-
XCTAssertEqual(stateAfterTerminate.value as? Int, Self.notRunningState)
|
|
22
|
+
func testTerminateThenRelaunchLifecycle() {
|
|
23
|
+
app.terminate()
|
|
24
|
+
XCTAssertEqual(app.state, .notRunning)
|
|
37
25
|
|
|
38
|
-
|
|
39
|
-
XCTAssertEqual(
|
|
40
|
-
Thread.sleep(forTimeInterval: 1)
|
|
41
|
-
|
|
42
|
-
let stateAfterLaunch = try client.post("/session/\(sessionId!)/wda/apps/state", body: ["bundleId": bundleId])
|
|
43
|
-
XCTAssertEqual(stateAfterLaunch.value as? Int, Self.runningForegroundState)
|
|
26
|
+
app.launch()
|
|
27
|
+
XCTAssertEqual(app.state, .runningForeground)
|
|
44
28
|
|
|
45
29
|
// The app relaunched fresh, so its accessibility tree should be back too.
|
|
46
|
-
|
|
30
|
+
XCTAssertTrue(app.buttons["tapMeButton"].waitForExistence(timeout: 15))
|
|
47
31
|
}
|
|
48
32
|
|
|
49
|
-
func testActivateBringsTheAppToTheForeground()
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
XCTAssertEqual(activateResponse.statusCode, 200)
|
|
53
|
-
|
|
54
|
-
let stateResponse = try client.post("/session/\(sessionId!)/wda/apps/state", body: ["bundleId": bundleId])
|
|
55
|
-
XCTAssertEqual(stateResponse.value as? Int, Self.runningForegroundState)
|
|
33
|
+
func testActivateBringsTheAppToTheForeground() {
|
|
34
|
+
app.activate()
|
|
35
|
+
XCTAssertEqual(app.state, .runningForeground)
|
|
56
36
|
}
|
|
57
37
|
}
|
|
@@ -8,24 +8,18 @@
|
|
|
8
8
|
|
|
9
9
|
import XCTest
|
|
10
10
|
|
|
11
|
-
/// Covers /element/:uuid/click
|
|
12
|
-
/// (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
/// Covers /element/:uuid/click, which on watchOS (like iOS) is just standard XCTest `-tap`
|
|
12
|
+
/// (see FBElementCommands.handleClick:) - so this just calls `.tap()` directly, the same way
|
|
13
|
+
/// the iOS/tvOS integration tests do. The "click a nonexistent element UUID" case isn't a `tap`
|
|
14
|
+
/// behavior at all (it's FBElementCache's UUID-lookup failure), so it's covered by the HTTP
|
|
15
|
+
/// end-to-end tests instead, where there's an actual UUID/route layer to miss against.
|
|
16
|
+
final class WDAClickIntegrationTests: WDAWatchInProcessTestCase {
|
|
17
|
+
func testClickingButtonUpdatesTheResultLabel() {
|
|
18
|
+
let label = app.staticTexts["resultLabel"]
|
|
19
|
+
XCTAssertEqual(label.wdValue ?? label.wdLabel, "Idle")
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
let clickResponse = try client.post("/session/\(sessionId!)/element/\(buttonId)/click")
|
|
20
|
-
XCTAssertEqual(clickResponse.statusCode, 200)
|
|
21
|
+
app.buttons["tapMeButton"].tap()
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
let updatedLabelId = try findElement(byAccessibilityId: "resultLabel")
|
|
24
|
-
XCTAssertEqual(try attributeValue(updatedLabelId, "label"), "Tapped")
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
func testClickingAnElementThatDoesNotExistReturnsAnError() throws {
|
|
28
|
-
let response = try client.post("/session/\(sessionId!)/element/00000000-0000-0000-0000-000000000000/click")
|
|
29
|
-
XCTAssertNotEqual(response.statusCode, 200)
|
|
23
|
+
XCTAssertEqual(label.wdValue ?? label.wdLabel, "Tapped")
|
|
30
24
|
}
|
|
31
25
|
}
|
|
@@ -6,45 +6,87 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import CoreLocation
|
|
10
|
+
import WatchKit
|
|
9
11
|
import XCTest
|
|
10
12
|
|
|
11
|
-
/// Covers device-info
|
|
12
|
-
///
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
XCTAssertFalse((
|
|
13
|
+
/// Covers device-info, active-app-info, simulated location, digital crown rotation, and hand
|
|
14
|
+
/// gesture, calling the same underlying APIs FBCustomCommands/XCUIDevice+FBHelpers use directly
|
|
15
|
+
/// in-process. The real (non-simulated) GPS location route and the two pure argument-validation
|
|
16
|
+
/// cases (missing delta/gesture name, rejected before any category method is even called) aren't
|
|
17
|
+
/// WDA category behavior, so they're not covered here.
|
|
18
|
+
final class WDADeviceIntegrationTests: WDAWatchInProcessTestCase {
|
|
19
|
+
func testDeviceInfo() {
|
|
20
|
+
XCTAssertFalse(WKInterfaceDevice.current().name.isEmpty)
|
|
21
|
+
XCTAssertFalse(WKInterfaceDevice.current().model.isEmpty)
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
func testActiveAppInfo()
|
|
22
|
-
let
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
XCTAssertGreaterThan(
|
|
24
|
+
func testActiveAppInfo() {
|
|
25
|
+
let apps = XCUIApplication.fb_activeAppsInfo()
|
|
26
|
+
let ourApp = apps.first { $0["bundleId"] as? String == WDAWatchInProcessTestCase.integrationAppBundleId }
|
|
27
|
+
XCTAssertNotNil(ourApp)
|
|
28
|
+
XCTAssertGreaterThan(ourApp?["pid"] as? Int ?? 0, 0)
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
func
|
|
29
|
-
let
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
func testSimulatedLocationRoundTrip() throws {
|
|
32
|
+
let location = CLLocation(latitude: 37.3230, longitude: -122.0322)
|
|
33
|
+
try XCUIDevice.shared.fb_setSimulatedLocation(location)
|
|
34
|
+
|
|
35
|
+
let fetched = try XCUIDevice.shared.fb_getSimulatedLocation()
|
|
36
|
+
XCTAssertEqual(fetched.coordinate.latitude, 37.3230, accuracy: 0.001)
|
|
37
|
+
XCTAssertEqual(fetched.coordinate.longitude, -122.0322, accuracy: 0.001)
|
|
38
|
+
|
|
39
|
+
try XCUIDevice.shared.fb_clearSimulatedLocation()
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
/// rotateDigitalCrownByDelta:/performHandGesture: were only added to the SDK in Xcode 16.3 - WDA calls
|
|
43
|
+
/// them dynamically (NSInvocation) so it keeps building on older Xcode too, but they only actually work
|
|
44
|
+
/// when the toolchain that built this very test bundle (same build as the runner) is new enough. Checked
|
|
45
|
+
/// the same way WDA itself does, via responds(to:), rather than hardcoding an Xcode/OS version number here.
|
|
46
|
+
private var supportsDigitalCrownAndHandGesture: Bool {
|
|
47
|
+
XCUIDevice.shared.responds(to: NSSelectorFromString("rotateDigitalCrownByDelta:"))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private func assertSucceeds(_ expectedToSucceed: Bool, file: StaticString = #filePath, line: UInt = #line, _ body: () throws -> Void) {
|
|
51
|
+
do {
|
|
52
|
+
try body()
|
|
53
|
+
XCTAssertTrue(expectedToSucceed, "expected this to throw, but it didn't", file: file, line: line)
|
|
54
|
+
} catch {
|
|
55
|
+
XCTAssertFalse(expectedToSucceed, "expected this to succeed, but it threw \(error)", file: file, line: line)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func testRotateDigitalCrown() {
|
|
60
|
+
assertSucceeds(supportsDigitalCrownAndHandGesture) {
|
|
61
|
+
try XCUIDevice.shared.fb_rotateDigitalCrown(0.2, velocity: 1.0)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func testPerformHandGestureDoubleTap() {
|
|
66
|
+
assertSucceeds(supportsDigitalCrownAndHandGesture) {
|
|
67
|
+
try XCUIDevice.shared.fb_performHandGesture("doubleTap")
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
func testPerformHandGestureFlick() {
|
|
72
|
+
// flick additionally needs watchOS 26+ (still internally versioned 12.0 pre-rename) on top of the
|
|
73
|
+
// Xcode 16.3+ toolchain floor - below that OS version it isn't even advertised as a supported name,
|
|
74
|
+
// so the server rejects it the same way it would reject any other unknown gesture name.
|
|
75
|
+
let shouldSucceed: Bool
|
|
76
|
+
if #available(watchOS 12.0, *) {
|
|
77
|
+
shouldSucceed = supportsDigitalCrownAndHandGesture
|
|
78
|
+
} else {
|
|
79
|
+
shouldSucceed = false
|
|
80
|
+
}
|
|
81
|
+
assertSucceeds(shouldSucceed) {
|
|
82
|
+
try XCUIDevice.shared.fb_performHandGesture("flick")
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
func testPerformHandGestureUnsupportedName() {
|
|
87
|
+
// Not a real gesture name in any environment, so always rejected regardless of toolchain/OS version.
|
|
88
|
+
assertSucceeds(false) {
|
|
89
|
+
try XCUIDevice.shared.fb_performHandGesture("clench")
|
|
90
|
+
}
|
|
49
91
|
}
|
|
50
92
|
}
|
package/WebDriverAgentTests/IntegrationTests_watchOS/WDAElementAttributeIntegrationTests.swift
CHANGED
|
@@ -8,50 +8,32 @@
|
|
|
8
8
|
|
|
9
9
|
import XCTest
|
|
10
10
|
|
|
11
|
-
/// Covers
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
XCTAssertGreaterThan(rectResponse.valueDict?["width"] as? Double ?? 0, 0)
|
|
11
|
+
/// Covers the FBElement attribute properties (wdEnabled/wdVisible/wdRect/wdType/wdLabel/
|
|
12
|
+
/// wdSelected/wdAccessible/fb_valueForWDAttributeName:) directly, in-process - the same
|
|
13
|
+
/// properties FBElementCommands' attribute routes read from.
|
|
14
|
+
final class WDAElementAttributeIntegrationTests: WDAWatchInProcessTestCase {
|
|
15
|
+
// All reads, no taps/typing - reuse the same running app across the whole class.
|
|
16
|
+
override class var relaunchForEachTest: Bool { false }
|
|
17
|
+
|
|
18
|
+
func testWindowSizeAndRect() {
|
|
19
|
+
XCTAssertGreaterThan(app.wdFrame.width, 0)
|
|
20
|
+
XCTAssertGreaterThan(app.wdFrame.height, 0)
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
func testButtonAttributes()
|
|
25
|
-
let
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
XCTAssertEqual(
|
|
32
|
-
|
|
33
|
-
let rectResponse = try client.get("/session/\(sessionId!)/element/\(buttonId)/rect")
|
|
34
|
-
XCTAssertEqual(rectResponse.statusCode, 200)
|
|
35
|
-
XCTAssertGreaterThan(rectResponse.valueDict?["width"] as? Double ?? 0, 0)
|
|
36
|
-
|
|
37
|
-
let nameResponse = try client.get("/session/\(sessionId!)/element/\(buttonId)/name")
|
|
38
|
-
XCTAssertEqual(nameResponse.valueString, "XCUIElementTypeButton")
|
|
39
|
-
|
|
40
|
-
let labelResponse = try client.get("/session/\(sessionId!)/element/\(buttonId)/attribute/label")
|
|
41
|
-
XCTAssertEqual(labelResponse.valueString, "Tap Me")
|
|
42
|
-
|
|
43
|
-
let accessibleResponse = try client.get("/session/\(sessionId!)/wda/element/\(buttonId)/accessible")
|
|
44
|
-
XCTAssertEqual(accessibleResponse.statusCode, 200)
|
|
23
|
+
func testButtonAttributes() {
|
|
24
|
+
let button = app.buttons["tapMeButton"]
|
|
25
|
+
XCTAssertTrue(button.isWDEnabled)
|
|
26
|
+
XCTAssertTrue(button.isWDVisible)
|
|
27
|
+
XCTAssertGreaterThan(button.wdRect["width"] as? Double ?? 0, 0)
|
|
28
|
+
XCTAssertEqual(button.wdType, "XCUIElementTypeButton")
|
|
29
|
+
XCTAssertEqual(button.wdLabel, "Tap Me")
|
|
30
|
+
XCTAssertEqual(button.fb_value(forWDAttributeName: "label") as? String, "Tap Me")
|
|
31
|
+
XCTAssertTrue(button.isWDAccessible)
|
|
45
32
|
}
|
|
46
33
|
|
|
47
|
-
func testResultLabelTextAndSelectedState()
|
|
48
|
-
let
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
XCTAssertEqual(textResponse.valueString, "Idle")
|
|
52
|
-
|
|
53
|
-
let selectedResponse = try client.get("/session/\(sessionId!)/element/\(labelId)/selected")
|
|
54
|
-
XCTAssertEqual(selectedResponse.statusCode, 200)
|
|
55
|
-
XCTAssertEqual(selectedResponse.value as? Bool, false)
|
|
34
|
+
func testResultLabelTextAndSelectedState() {
|
|
35
|
+
let label = app.staticTexts["resultLabel"]
|
|
36
|
+
XCTAssertEqual(label.wdValue ?? label.wdLabel, "Idle")
|
|
37
|
+
XCTAssertFalse(label.isWDSelected)
|
|
56
38
|
}
|
|
57
39
|
}
|