appium-webdriveragent 16.2.2 → 16.4.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/XCSynthesizedEventRecord.h +3 -3
- package/PrivateHeaders/XCTest/XCUIApplication.h +1 -1
- package/PrivateHeaders/XCTest/XCUIDevice.h +0 -1
- package/PrivateHeaders/XCTest/XCUIElement.h +1 -1
- package/Scripts/build.sh +6 -0
- package/WebDriverAgent.xcodeproj/project.pbxproj +1482 -2
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationApp_watchOS.xcscheme +76 -0
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationTests_watchOS.xcscheme +86 -0
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentLib_watchOS.xcscheme +85 -0
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner.xcscheme +1 -1
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner_watchOS.xcscheme +128 -0
- package/WebDriverAgentLib/Categories/XCUIApplication+FBAlert.m +5 -0
- package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +2 -0
- package/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.m +1 -1
- package/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.h +34 -0
- package/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +76 -4
- package/WebDriverAgentLib/Categories/XCUIDevice+FBRotation.h +1 -1
- package/WebDriverAgentLib/Categories/XCUIDevice+FBRotation.m +1 -1
- package/WebDriverAgentLib/Categories/XCUIElement+FBPickerWheel.m +1 -1
- package/WebDriverAgentLib/Categories/XCUIElement+FBTyping.m +11 -0
- package/WebDriverAgentLib/Commands/FBCustomCommands.m +55 -4
- package/WebDriverAgentLib/Commands/FBElementCommands.m +6 -5
- package/WebDriverAgentLib/Commands/FBOrientationCommands.m +1 -1
- package/WebDriverAgentLib/Commands/FBSessionCommands.m +29 -5
- package/WebDriverAgentLib/Info.plist +2 -2
- package/WebDriverAgentLib/Routing/FBTCPSocket.h +70 -0
- package/WebDriverAgentLib/Routing/FBTCPSocket.m +199 -0
- package/WebDriverAgentLib/Routing/FBWebServer.m +33 -3
- package/WebDriverAgentLib/Routing/WatchOS/FBWatchHTTPServer.h +68 -0
- package/WebDriverAgentLib/Routing/WatchOS/FBWatchHTTPServer.m +376 -0
- package/WebDriverAgentLib/Routing/WatchOS/RouteRequest.h +30 -0
- package/WebDriverAgentLib/Routing/WatchOS/RouteRequest.m +25 -0
- package/WebDriverAgentLib/Routing/WatchOS/RouteResponse.h +30 -0
- package/WebDriverAgentLib/Routing/WatchOS/RouteResponse.m +54 -0
- package/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.m +8 -0
- package/WebDriverAgentLib/Utilities/FBBaseActionsSynthesizer.h +1 -1
- package/WebDriverAgentLib/Utilities/FBBaseActionsSynthesizer.m +1 -1
- package/WebDriverAgentLib/Utilities/FBConfiguration.h +1 -1
- package/WebDriverAgentLib/Utilities/FBConfiguration.m +3 -3
- package/WebDriverAgentLib/Utilities/FBImageProcessor.m +7 -2
- package/WebDriverAgentLib/Utilities/FBMacros.h +14 -5
- package/WebDriverAgentLib/Utilities/FBMathUtils.h +1 -1
- package/WebDriverAgentLib/Utilities/FBMathUtils.m +1 -1
- package/WebDriverAgentLib/Utilities/FBPasteboard.h +1 -1
- package/WebDriverAgentLib/Utilities/FBPasteboard.m +1 -1
- package/WebDriverAgentLib/Utilities/FBSettingsHandler.m +2 -2
- package/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.h +1 -1
- package/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.m +1 -1
- package/WebDriverAgentTests/IntegrationApp_watchOS/ContentView.swift +31 -0
- package/WebDriverAgentTests/IntegrationApp_watchOS/WatchSpikeApp.swift +18 -0
- package/WebDriverAgentTests/IntegrationTests/FBForceTouchTests.m +3 -0
- package/WebDriverAgentTests/IntegrationTests/FBPasteboardTests.m +3 -2
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAAlertIntegrationTests.swift +23 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAAppLifecycleIntegrationTests.swift +57 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAClickIntegrationTests.swift +31 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDADeviceIntegrationTests.swift +113 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAElementAttributeIntegrationTests.swift +57 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAFindIntegrationTests.swift +126 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAScreenshotAndSourceIntegrationTests.swift +52 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDASessionIntegrationTests.swift +72 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDATypingIntegrationTests.swift +57 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAUnknownCommandIntegrationTests.swift +24 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAWatchHTTPClient.swift +95 -0
- package/WebDriverAgentTests/IntegrationTests_watchOS/WDAWatchIntegrationTestCase.swift +61 -0
- package/package.json +1 -1
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
#import "RouteResponse.h"
|
|
10
|
+
|
|
11
|
+
@interface RouteResponse ()
|
|
12
|
+
@property (nonatomic, copy) NSMutableDictionary<NSString *, NSString *> *mutableHeaders;
|
|
13
|
+
@end
|
|
14
|
+
|
|
15
|
+
@implementation RouteResponse
|
|
16
|
+
|
|
17
|
+
- (instancetype)init
|
|
18
|
+
{
|
|
19
|
+
if ((self = [super init])) {
|
|
20
|
+
_statusCode = kHTTPStatusCodeOK;
|
|
21
|
+
_mutableHeaders = [NSMutableDictionary dictionary];
|
|
22
|
+
}
|
|
23
|
+
return self;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
- (NSDictionary<NSString *, NSString *> *)headers
|
|
27
|
+
{
|
|
28
|
+
return self.mutableHeaders.copy;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
- (void)setHeader:(NSString *)field value:(NSString *)value
|
|
32
|
+
{
|
|
33
|
+
self.mutableHeaders[field] = value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
- (void)respondWithData:(NSData *)data
|
|
37
|
+
{
|
|
38
|
+
_responseData = data.copy;
|
|
39
|
+
if (nil == self.mutableHeaders[@"Content-Length"]) {
|
|
40
|
+
self.mutableHeaders[@"Content-Length"] = [NSString stringWithFormat:@"%lu", (unsigned long)data.length];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
- (void)respondWithString:(NSString *)string
|
|
45
|
+
{
|
|
46
|
+
[self respondWithString:string encoding:NSUTF8StringEncoding];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
- (void)respondWithString:(NSString *)string encoding:(NSStringEncoding)encoding
|
|
50
|
+
{
|
|
51
|
+
[self respondWithData:[string dataUsingEncoding:encoding] ?: [NSData data]];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@end
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
#import "FBActiveAppDetectionPoint.h"
|
|
10
10
|
|
|
11
|
+
#if TARGET_OS_WATCH
|
|
12
|
+
@import WatchKit;
|
|
13
|
+
#endif
|
|
14
|
+
|
|
11
15
|
#import "FBErrorBuilder.h"
|
|
12
16
|
#import "FBLogger.h"
|
|
13
17
|
#import "FBXCTestDaemonsProxy.h"
|
|
@@ -17,7 +21,11 @@
|
|
|
17
21
|
|
|
18
22
|
- (instancetype)init {
|
|
19
23
|
if ((self = [super init])) {
|
|
24
|
+
#if TARGET_OS_WATCH
|
|
25
|
+
CGSize screenSize = WKInterfaceDevice.currentDevice.screenBounds.size;
|
|
26
|
+
#else
|
|
20
27
|
CGSize screenSize = [UIScreen mainScreen].bounds.size;
|
|
28
|
+
#endif
|
|
21
29
|
// Consider the element, which is located close to the top left corner of the screen the on-screen one.
|
|
22
30
|
CGFloat pointDistance = MIN(screenSize.width, screenSize.height) * (CGFloat) 0.2;
|
|
23
31
|
_coordinates = CGPointMake(pointDistance, pointDistance);
|
|
@@ -37,7 +37,7 @@ static NSString *const axSettingsClassName = @"AXSettings";
|
|
|
37
37
|
@interface FBConfiguration ()
|
|
38
38
|
|
|
39
39
|
@property (atomic, strong) NSNumber *maxTypingFrequencyOverride;
|
|
40
|
-
#if !TARGET_OS_TV
|
|
40
|
+
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
41
41
|
@property (atomic, assign) UIInterfaceOrientation screenshotOrientationStorage;
|
|
42
42
|
#endif
|
|
43
43
|
|
|
@@ -292,7 +292,7 @@ static NSString *const axSettingsClassName = @"AXSettings";
|
|
|
292
292
|
return [FBGetCustomParameterForElementSnapshot(FBSnapshotMaxChildrenKey) intValue];
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
#if !TARGET_OS_TV
|
|
295
|
+
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
296
296
|
- (BOOL)setScreenshotOrientation:(NSString *)orientation error:(NSError **)error
|
|
297
297
|
{
|
|
298
298
|
// Only UIInterfaceOrientationUnknown is over iOS 8. Others are over iOS 2.
|
|
@@ -359,7 +359,7 @@ static NSString *const axSettingsClassName = @"AXSettings";
|
|
|
359
359
|
FBSetCustomParameterForElementSnapshot(FBSnapshotMaxChildrenKey, @INT_MAX);
|
|
360
360
|
self.useClearTextShortcut = YES;
|
|
361
361
|
self.limitXpathContextScope = YES;
|
|
362
|
-
#if !TARGET_OS_TV
|
|
362
|
+
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
363
363
|
self.screenshotOrientationStorage = UIInterfaceOrientationUnknown;
|
|
364
364
|
#endif
|
|
365
365
|
}
|
|
@@ -109,8 +109,12 @@ const CGFloat FBMaxCompressionQuality = 1.0f;
|
|
|
109
109
|
desiredOrientation:(nullable NSNumber *)orientation
|
|
110
110
|
{
|
|
111
111
|
scalingFactor = MAX(FBMinScalingFactor, MIN(FBMaxScalingFactor, scalingFactor));
|
|
112
|
-
BOOL usesScaling = scalingFactor > 0.0 && scalingFactor < FBMaxScalingFactor;
|
|
113
112
|
@autoreleasepool {
|
|
113
|
+
#if TARGET_OS_WATCH
|
|
114
|
+
// UIGraphicsImageRenderer is unavailable and the watch screen doesn't rotate, so skip both.
|
|
115
|
+
return [uti conformsToType:UTTypePNG] ? FBToPngData(imageData) : FBToJpegData(imageData, compressionQuality);
|
|
116
|
+
#else
|
|
117
|
+
BOOL usesScaling = scalingFactor > 0.0 && scalingFactor < FBMaxScalingFactor;
|
|
114
118
|
if (!usesScaling && !fixOrientation) {
|
|
115
119
|
return [uti conformsToType:UTTypePNG] ? FBToPngData(imageData) : FBToJpegData(imageData, compressionQuality);
|
|
116
120
|
}
|
|
@@ -157,6 +161,7 @@ const CGFloat FBMaxCompressionQuality = 1.0f;
|
|
|
157
161
|
actions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
|
|
158
162
|
[uiImage drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)];
|
|
159
163
|
}];
|
|
164
|
+
#endif
|
|
160
165
|
}
|
|
161
166
|
}
|
|
162
167
|
|
|
@@ -167,7 +172,7 @@ const CGFloat FBMaxCompressionQuality = 1.0f;
|
|
|
167
172
|
error:(NSError **)error
|
|
168
173
|
{
|
|
169
174
|
NSNumber *orientation = nil;
|
|
170
|
-
#if !TARGET_OS_TV
|
|
175
|
+
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
171
176
|
if (FBConfiguration.sharedInstance.screenshotOrientation == UIInterfaceOrientationPortrait) {
|
|
172
177
|
orientation = @(UIImageOrientationUp);
|
|
173
178
|
} else if (FBConfiguration.sharedInstance.screenshotOrientation == UIInterfaceOrientationPortraitUpsideDown) {
|
|
@@ -42,12 +42,21 @@
|
|
|
42
42
|
typeof(arg) arg = wda_weak_##arg \
|
|
43
43
|
_Pragma("clang diagnostic pop")
|
|
44
44
|
|
|
45
|
+
/*! Current OS version as a dotted string, e.g. "17.4". UIDevice.systemVersion is unavailable on
|
|
46
|
+
watchOS, so this is sourced from NSProcessInfo, which works identically on every platform. */
|
|
47
|
+
NS_INLINE NSString *FBSystemVersion(void) {
|
|
48
|
+
NSOperatingSystemVersion v = NSProcessInfo.processInfo.operatingSystemVersion;
|
|
49
|
+
return 0 == v.patchVersion
|
|
50
|
+
? [NSString stringWithFormat:@"%ld.%ld", (long)v.majorVersion, (long)v.minorVersion]
|
|
51
|
+
: [NSString stringWithFormat:@"%ld.%ld.%ld", (long)v.majorVersion, (long)v.minorVersion, (long)v.patchVersion];
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
/*! Returns YES if current system version satisfies the given codition */
|
|
46
|
-
#define SYSTEM_VERSION_EQUAL_TO(v) ([
|
|
47
|
-
#define SYSTEM_VERSION_GREATER_THAN(v) ([
|
|
48
|
-
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([
|
|
49
|
-
#define SYSTEM_VERSION_LESS_THAN(v) ([
|
|
50
|
-
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([
|
|
55
|
+
#define SYSTEM_VERSION_EQUAL_TO(v) ([FBSystemVersion() compare:v options:NSNumericSearch] == NSOrderedSame)
|
|
56
|
+
#define SYSTEM_VERSION_GREATER_THAN(v) ([FBSystemVersion() compare:v options:NSNumericSearch] == NSOrderedDescending)
|
|
57
|
+
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([FBSystemVersion() compare:v options:NSNumericSearch] != NSOrderedAscending)
|
|
58
|
+
#define SYSTEM_VERSION_LESS_THAN(v) ([FBSystemVersion() compare:v options:NSNumericSearch] == NSOrderedAscending)
|
|
59
|
+
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([FBSystemVersion() compare:v options:NSNumericSearch] != NSOrderedDescending)
|
|
51
60
|
|
|
52
61
|
/*! Converts the given number of milliseconds into seconds */
|
|
53
62
|
#define FBMillisToSeconds(ms) ((ms) / 1000.0)
|
|
@@ -30,7 +30,7 @@ BOOL FBSizeFuzzyEqualToSize(CGSize size1, CGSize size2, CGFloat threshold);
|
|
|
30
30
|
/*! Returns whether rect are equal within given threshold */
|
|
31
31
|
BOOL FBRectFuzzyEqualToRect(CGRect rect1, CGRect rect2, CGFloat threshold);
|
|
32
32
|
|
|
33
|
-
#if !TARGET_OS_TV
|
|
33
|
+
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
34
34
|
/*! Inverts size if necessary to match current screen orientation */
|
|
35
35
|
CGSize FBAdjustDimensionsForApplication(CGSize actualSize, UIInterfaceOrientation orientation);
|
|
36
36
|
#endif
|
|
@@ -44,7 +44,7 @@ BOOL FBRectFuzzyEqualToRect(CGRect rect1, CGRect rect2, CGFloat threshold)
|
|
|
44
44
|
FBSizeFuzzyEqualToSize(rect1.size, rect2.size, threshold);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
#if !TARGET_OS_TV
|
|
47
|
+
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
48
48
|
|
|
49
49
|
CGSize FBAdjustDimensionsForApplication(CGSize actualSize, UIInterfaceOrientation orientation)
|
|
50
50
|
{
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// Must not be less than FB_MONTORING_INTERVAL in FBAlertsMonitor
|
|
21
21
|
#define ALERT_CHECK_INTERVAL_SEC 2
|
|
22
22
|
|
|
23
|
-
#if !TARGET_OS_TV
|
|
23
|
+
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
24
24
|
@implementation FBPasteboard
|
|
25
25
|
|
|
26
26
|
+ (BOOL)setData:(NSData *)data forType:(NSString *)type error:(NSError **)error
|
|
@@ -186,7 +186,7 @@ static NSSet<NSString *> *FBNilClearableSettingKeys(void)
|
|
|
186
186
|
FBConfiguration.sharedInstance.limitXpathContextScope = [value boolValue];
|
|
187
187
|
return nil;
|
|
188
188
|
};
|
|
189
|
-
#if !TARGET_OS_TV
|
|
189
|
+
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
190
190
|
map[FB_SETTING_SCREENSHOT_ORIENTATION] = ^FBCommandStatus *(FBSession *session, id value) {
|
|
191
191
|
NSError *error;
|
|
192
192
|
if (![FBConfiguration.sharedInstance setScreenshotOrientation:(NSString *)value error:&error]) {
|
|
@@ -303,7 +303,7 @@ static NSSet<NSString *> *FBNilClearableSettingKeys(void)
|
|
|
303
303
|
map[FB_SETTING_LIMIT_XPATH_CONTEXT_SCOPE] = ^id(FBSession *session) {
|
|
304
304
|
return @(FBConfiguration.sharedInstance.limitXpathContextScope);
|
|
305
305
|
};
|
|
306
|
-
#if !TARGET_OS_TV
|
|
306
|
+
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
307
307
|
map[FB_SETTING_SCREENSHOT_ORIENTATION] = ^id(FBSession *session) {
|
|
308
308
|
return FBConfiguration.sharedInstance.humanReadableScreenshotOrientation;
|
|
309
309
|
};
|
|
@@ -63,7 +63,7 @@ static NSString *const FB_KEY_PARAMETERS = @"parameters";
|
|
|
63
63
|
static NSString *const FB_KEY_ACTIONS = @"actions";
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
#if !TARGET_OS_TV
|
|
66
|
+
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
67
67
|
@interface FBW3CGestureItem : FBBaseGestureItem
|
|
68
68
|
|
|
69
69
|
@property (nullable, readonly, nonatomic) FBBaseGestureItem *previousItem;
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
import SwiftUI
|
|
10
|
+
|
|
11
|
+
struct ContentView: View {
|
|
12
|
+
@State private var resultText = "Idle"
|
|
13
|
+
@State private var typedText = ""
|
|
14
|
+
|
|
15
|
+
var body: some View {
|
|
16
|
+
// ScrollView keeps content reachable - WDA's test-launch path shifts the scene down
|
|
17
|
+
// vs. a plain launch, which can push a plain VStack off-screen.
|
|
18
|
+
ScrollView {
|
|
19
|
+
VStack(spacing: 8) {
|
|
20
|
+
Text(resultText)
|
|
21
|
+
.accessibilityIdentifier("resultLabel")
|
|
22
|
+
Button("Tap Me") {
|
|
23
|
+
resultText = "Tapped"
|
|
24
|
+
}
|
|
25
|
+
.accessibilityIdentifier("tapMeButton")
|
|
26
|
+
TextField("Type here", text: $typedText)
|
|
27
|
+
.accessibilityIdentifier("typingField")
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
import SwiftUI
|
|
10
|
+
|
|
11
|
+
@main
|
|
12
|
+
struct WatchSpikeApp: App {
|
|
13
|
+
var body: some Scene {
|
|
14
|
+
WindowGroup {
|
|
15
|
+
ContentView()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -51,6 +51,9 @@
|
|
|
51
51
|
|
|
52
52
|
- (void)testGetPasteboard
|
|
53
53
|
{
|
|
54
|
+
XCTSkipIf(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"27.0"),
|
|
55
|
+
@"FIXME: Pasteboard retrieval has opposite button behavior on platform version 27.");
|
|
56
|
+
|
|
54
57
|
NSString *text = @"Happy copying";
|
|
55
58
|
XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
|
|
56
59
|
NSError *error;
|
|
@@ -87,5 +90,3 @@
|
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
@end
|
|
90
|
-
|
|
91
|
-
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the BSD-style license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import XCTest
|
|
10
|
+
|
|
11
|
+
/// Covers FBAlertViewCommands' error path - IntegrationApp_watchOS never presents a real alert,
|
|
12
|
+
/// so this only exercises the "no alert open" error, not the success path.
|
|
13
|
+
final class WDAAlertIntegrationTests: WDAWatchIntegrationTestCase {
|
|
14
|
+
func testAlertTextWithNoAlertPresentReturnsAnError() throws {
|
|
15
|
+
let response = try client.get("/session/\(sessionId!)/alert/text")
|
|
16
|
+
XCTAssertNotEqual(response.statusCode, 200)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
func testAlertButtonsWithNoAlertPresentReturnsAnError() throws {
|
|
20
|
+
let response = try client.get("/session/\(sessionId!)/wda/alert/buttons")
|
|
21
|
+
XCTAssertNotEqual(response.statusCode, 200)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
import XCTest
|
|
10
|
+
|
|
11
|
+
/// Covers the app lifecycle routes (launch/activate/terminate/state/list). Unlike
|
|
12
|
+
/// /wda/homescreen or /wda/lock, these only ever target IntegrationApp_watchOS, so they're
|
|
13
|
+
/// safe to run repeatedly without disrupting shared simulator state.
|
|
14
|
+
final class WDAAppLifecycleIntegrationTests: WDAWatchIntegrationTestCase {
|
|
15
|
+
// XCUIApplicationState raw values: NotRunning = 1, RunningForeground = 4.
|
|
16
|
+
private static let notRunningState = 1
|
|
17
|
+
private static let runningForegroundState = 4
|
|
18
|
+
|
|
19
|
+
func testAppsListIncludesTheAppUnderTest() throws {
|
|
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 })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func testTerminateThenRelaunchLifecycle() throws {
|
|
29
|
+
let bundleId = Self.integrationAppBundleId
|
|
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)
|
|
37
|
+
|
|
38
|
+
let launchResponse = try client.post("/session/\(sessionId!)/wda/apps/launch", body: ["bundleId": bundleId])
|
|
39
|
+
XCTAssertEqual(launchResponse.statusCode, 200)
|
|
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)
|
|
44
|
+
|
|
45
|
+
// The app relaunched fresh, so its accessibility tree should be back too.
|
|
46
|
+
XCTAssertNoThrow(try findElement(byAccessibilityId: "tapMeButton"))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func testActivateBringsTheAppToTheForeground() throws {
|
|
50
|
+
let bundleId = Self.integrationAppBundleId
|
|
51
|
+
let activateResponse = try client.post("/session/\(sessionId!)/wda/apps/activate", body: ["bundleId": bundleId])
|
|
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)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
import XCTest
|
|
10
|
+
|
|
11
|
+
/// Covers /element/:uuid/click - the only tap-like interaction registered on watchOS
|
|
12
|
+
/// (gesture synthesis routes aren't).
|
|
13
|
+
final class WDAClickIntegrationTests: WDAWatchIntegrationTestCase {
|
|
14
|
+
func testClickingButtonUpdatesTheResultLabel() throws {
|
|
15
|
+
let labelId = try findElement(byAccessibilityId: "resultLabel")
|
|
16
|
+
XCTAssertEqual(try attributeValue(labelId, "label"), "Idle")
|
|
17
|
+
|
|
18
|
+
let buttonId = try findElement(byAccessibilityId: "tapMeButton")
|
|
19
|
+
let clickResponse = try client.post("/session/\(sessionId!)/element/\(buttonId)/click")
|
|
20
|
+
XCTAssertEqual(clickResponse.statusCode, 200)
|
|
21
|
+
|
|
22
|
+
// Re-find: element UUIDs aren't stable across snapshots, and the label just changed.
|
|
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)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
import XCTest
|
|
10
|
+
|
|
11
|
+
/// Covers device-info and location routes. These only read device state or set a simulated
|
|
12
|
+
/// GPS fix, so unlike /wda/lock or /wda/voiceOver/enable, they won't disrupt later tests.
|
|
13
|
+
final class WDADeviceIntegrationTests: WDAWatchIntegrationTestCase {
|
|
14
|
+
func testDeviceInfo() throws {
|
|
15
|
+
let response = try client.get("/session/\(sessionId!)/wda/device/info")
|
|
16
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
17
|
+
XCTAssertFalse((response.valueDict?["name"] as? String ?? "").isEmpty)
|
|
18
|
+
XCTAssertFalse((response.valueDict?["model"] as? String ?? "").isEmpty)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func testActiveAppInfo() throws {
|
|
22
|
+
let response = try client.get("/session/\(sessionId!)/wda/activeAppInfo")
|
|
23
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
24
|
+
XCTAssertEqual(response.valueDict?["bundleId"] as? String, Self.integrationAppBundleId)
|
|
25
|
+
XCTAssertGreaterThan(response.valueDict?["pid"] as? Int ?? 0, 0)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func testDeviceLocation() throws {
|
|
29
|
+
let response = try client.get("/session/\(sessionId!)/wda/device/location")
|
|
30
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
31
|
+
XCTAssertNotNil(response.valueDict?["latitude"])
|
|
32
|
+
XCTAssertNotNil(response.valueDict?["authorizationStatus"])
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
func testSimulatedLocationRoundTrip() throws {
|
|
36
|
+
let setResponse = try client.post("/session/\(sessionId!)/wda/simulatedLocation", body: [
|
|
37
|
+
"latitude": 37.3230,
|
|
38
|
+
"longitude": -122.0322,
|
|
39
|
+
])
|
|
40
|
+
XCTAssertEqual(setResponse.statusCode, 200)
|
|
41
|
+
|
|
42
|
+
let getResponse = try client.get("/session/\(sessionId!)/wda/simulatedLocation")
|
|
43
|
+
XCTAssertEqual(getResponse.statusCode, 200)
|
|
44
|
+
XCTAssertEqual(getResponse.valueDict?["latitude"] as? Double ?? 0, 37.3230, accuracy: 0.001)
|
|
45
|
+
XCTAssertEqual(getResponse.valueDict?["longitude"] as? Double ?? 0, -122.0322, accuracy: 0.001)
|
|
46
|
+
|
|
47
|
+
let clearResponse = try client.delete("/session/\(sessionId!)/wda/simulatedLocation")
|
|
48
|
+
XCTAssertEqual(clearResponse.statusCode, 200)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// rotateDigitalCrownByDelta:/performHandGesture: were only added to the SDK in Xcode 16.3 - WDA calls
|
|
52
|
+
/// them dynamically (NSInvocation) so it keeps building on older Xcode too, but they only actually work
|
|
53
|
+
/// when the toolchain that built this very test bundle (same build as the runner) is new enough. Checked
|
|
54
|
+
/// the same way WDA itself does, via responds(to:), rather than hardcoding an Xcode/OS version number here.
|
|
55
|
+
private var supportsDigitalCrownAndHandGesture: Bool {
|
|
56
|
+
XCUIDevice.shared.responds(to: Selector(("rotateDigitalCrownByDelta:")))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func testRotateDigitalCrown() throws {
|
|
60
|
+
let response = try client.post("/session/\(sessionId!)/wda/rotateDigitalCrown", body: [
|
|
61
|
+
"delta": 0.2,
|
|
62
|
+
"velocity": 1.0,
|
|
63
|
+
])
|
|
64
|
+
XCTAssertEqual(response.statusCode, supportsDigitalCrownAndHandGesture ? 200 : 500)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func testRotateDigitalCrownDefaultVelocity() throws {
|
|
68
|
+
let response = try client.post("/session/\(sessionId!)/wda/rotateDigitalCrown", body: [
|
|
69
|
+
"delta": -0.2,
|
|
70
|
+
])
|
|
71
|
+
XCTAssertEqual(response.statusCode, supportsDigitalCrownAndHandGesture ? 200 : 500)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
func testRotateDigitalCrownMissingDelta() throws {
|
|
75
|
+
// Argument validation happens before the dynamic dispatch check, so this is always a 400.
|
|
76
|
+
let response = try client.post("/session/\(sessionId!)/wda/rotateDigitalCrown")
|
|
77
|
+
XCTAssertEqual(response.statusCode, 400)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
func testPerformHandGestureDoubleTap() throws {
|
|
81
|
+
let response = try client.post("/session/\(sessionId!)/wda/performHandGesture", body: [
|
|
82
|
+
"name": "doubleTap",
|
|
83
|
+
])
|
|
84
|
+
XCTAssertEqual(response.statusCode, supportsDigitalCrownAndHandGesture ? 200 : 500)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
func testPerformHandGestureFlick() throws {
|
|
88
|
+
let response = try client.post("/session/\(sessionId!)/wda/performHandGesture", body: [
|
|
89
|
+
"name": "flick",
|
|
90
|
+
])
|
|
91
|
+
// flick additionally needs watchOS 26+ (still internally versioned 12.0 pre-rename) on top of the
|
|
92
|
+
// Xcode 16.3+ toolchain floor - below that OS version it isn't even advertised as a supported name,
|
|
93
|
+
// so the server rejects it the same way it would reject any other unknown gesture name.
|
|
94
|
+
if #available(watchOS 12.0, *), supportsDigitalCrownAndHandGesture {
|
|
95
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
96
|
+
} else {
|
|
97
|
+
XCTAssertEqual(response.statusCode, 500)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
func testPerformHandGestureUnsupportedName() throws {
|
|
102
|
+
// Not a real gesture name in any environment, so always rejected regardless of toolchain/OS version.
|
|
103
|
+
let response = try client.post("/session/\(sessionId!)/wda/performHandGesture", body: [
|
|
104
|
+
"name": "clench",
|
|
105
|
+
])
|
|
106
|
+
XCTAssertEqual(response.statusCode, 500)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
func testPerformHandGestureMissingName() throws {
|
|
110
|
+
let response = try client.post("/session/\(sessionId!)/wda/performHandGesture")
|
|
111
|
+
XCTAssertEqual(response.statusCode, 400)
|
|
112
|
+
}
|
|
113
|
+
}
|
package/WebDriverAgentTests/IntegrationTests_watchOS/WDAElementAttributeIntegrationTests.swift
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
import XCTest
|
|
10
|
+
|
|
11
|
+
/// Covers FBElementCommands' read-only element/window attribute routes.
|
|
12
|
+
final class WDAElementAttributeIntegrationTests: WDAWatchIntegrationTestCase {
|
|
13
|
+
func testWindowSizeAndRect() throws {
|
|
14
|
+
let sizeResponse = try client.get("/session/\(sessionId!)/window/size")
|
|
15
|
+
XCTAssertEqual(sizeResponse.statusCode, 200)
|
|
16
|
+
XCTAssertGreaterThan(sizeResponse.valueDict?["width"] as? Double ?? 0, 0)
|
|
17
|
+
XCTAssertGreaterThan(sizeResponse.valueDict?["height"] as? Double ?? 0, 0)
|
|
18
|
+
|
|
19
|
+
let rectResponse = try client.get("/session/\(sessionId!)/window/rect")
|
|
20
|
+
XCTAssertEqual(rectResponse.statusCode, 200)
|
|
21
|
+
XCTAssertGreaterThan(rectResponse.valueDict?["width"] as? Double ?? 0, 0)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func testButtonAttributes() throws {
|
|
25
|
+
let buttonId = try findElement(byAccessibilityId: "tapMeButton")
|
|
26
|
+
|
|
27
|
+
let enabledResponse = try client.get("/session/\(sessionId!)/element/\(buttonId)/enabled")
|
|
28
|
+
XCTAssertEqual(enabledResponse.value as? Bool, true)
|
|
29
|
+
|
|
30
|
+
let displayedResponse = try client.get("/session/\(sessionId!)/element/\(buttonId)/displayed")
|
|
31
|
+
XCTAssertEqual(displayedResponse.value as? Bool, true)
|
|
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)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func testResultLabelTextAndSelectedState() throws {
|
|
48
|
+
let labelId = try findElement(byAccessibilityId: "resultLabel")
|
|
49
|
+
|
|
50
|
+
let textResponse = try client.get("/session/\(sessionId!)/element/\(labelId)/text")
|
|
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)
|
|
56
|
+
}
|
|
57
|
+
}
|