appium-webdriveragent 16.2.2 → 16.3.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 +6 -0
- package/PrivateHeaders/XCTest/XCSynthesizedEventRecord.h +3 -3
- package/PrivateHeaders/XCTest/XCUIApplication.h +1 -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.m +4 -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 +19 -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 +50 -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
|
@@ -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,50 @@
|
|
|
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
|
+
}
|
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
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
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 FBFindElementCommands: find/sub-find/active-element, not-found errors, and every
|
|
12
|
+
/// locator strategy (accessibility id/name/id, class name, class chain, xpath, predicate
|
|
13
|
+
/// string, link text). getVisibleCells is skipped - no table/collection view to test it against.
|
|
14
|
+
final class WDAFindIntegrationTests: WDAWatchIntegrationTestCase {
|
|
15
|
+
func testFindElementByAccessibilityId() throws {
|
|
16
|
+
let elementId = try findElement(byAccessibilityId: "tapMeButton")
|
|
17
|
+
XCTAssertFalse(elementId.isEmpty)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
func testFindElementByNameAndIdAliases() throws {
|
|
21
|
+
// "name" and "id" are aliases for "accessibility id".
|
|
22
|
+
for using in ["name", "id"] {
|
|
23
|
+
let response = try client.post("/session/\(sessionId!)/element", body: [
|
|
24
|
+
"using": using,
|
|
25
|
+
"value": "tapMeButton",
|
|
26
|
+
])
|
|
27
|
+
XCTAssertEqual(response.statusCode, 200, "using: \(using)")
|
|
28
|
+
XCTAssertNotNil(response.valueDict?["ELEMENT"], "using: \(using)")
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func testFindElementByPredicateString() throws {
|
|
33
|
+
let response = try client.post("/session/\(sessionId!)/element", body: [
|
|
34
|
+
"using": "predicate string",
|
|
35
|
+
"value": "label == \"Tap Me\"",
|
|
36
|
+
])
|
|
37
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
38
|
+
XCTAssertNotNil(response.valueDict?["ELEMENT"])
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func testFindElementsByClassChain() throws {
|
|
42
|
+
let response = try client.post("/session/\(sessionId!)/elements", body: [
|
|
43
|
+
"using": "class chain",
|
|
44
|
+
"value": "**/XCUIElementTypeButton",
|
|
45
|
+
])
|
|
46
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
47
|
+
XCTAssertGreaterThanOrEqual((response.value as? [[String: Any]])?.count ?? 0, 1)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func testFindElementByXPath() throws {
|
|
51
|
+
let response = try client.post("/session/\(sessionId!)/element", body: [
|
|
52
|
+
"using": "xpath",
|
|
53
|
+
"value": "//XCUIElementTypeButton[@name=\"tapMeButton\"]",
|
|
54
|
+
])
|
|
55
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
56
|
+
XCTAssertNotNil(response.valueDict?["ELEMENT"])
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func testFindElementByLinkTextAndPartialLinkText() throws {
|
|
60
|
+
// WDA matches (partial) link text against `name`, not an actual hyperlink search.
|
|
61
|
+
for using in ["link text", "partial link text"] {
|
|
62
|
+
let value = using == "link text" ? "tapMeButton" : "tapMe"
|
|
63
|
+
let response = try client.post("/session/\(sessionId!)/element", body: [
|
|
64
|
+
"using": using,
|
|
65
|
+
"value": value,
|
|
66
|
+
])
|
|
67
|
+
XCTAssertEqual(response.statusCode, 200, "using: \(using)")
|
|
68
|
+
XCTAssertNotNil(response.valueDict?["ELEMENT"], "using: \(using)")
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
func testFindElementThatDoesNotExistReturnsAnError() throws {
|
|
73
|
+
let response = try client.post("/session/\(sessionId!)/element", body: [
|
|
74
|
+
"using": "accessibility id",
|
|
75
|
+
"value": "thisElementDoesNotExist",
|
|
76
|
+
])
|
|
77
|
+
XCTAssertNotEqual(response.statusCode, 200)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
func testFindElements() throws {
|
|
81
|
+
let response = try client.post("/session/\(sessionId!)/elements", body: [
|
|
82
|
+
"using": "class name",
|
|
83
|
+
"value": "XCUIElementTypeButton",
|
|
84
|
+
])
|
|
85
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
86
|
+
guard let elements = response.value as? [[String: Any]] else {
|
|
87
|
+
return XCTFail("Expected an array of elements: \(String(describing: response.json))")
|
|
88
|
+
}
|
|
89
|
+
XCTAssertGreaterThanOrEqual(elements.count, 1)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
func testFindSubElementAndSubElements() throws {
|
|
93
|
+
let windowResponse = try client.post("/session/\(sessionId!)/element", body: [
|
|
94
|
+
"using": "class name",
|
|
95
|
+
"value": "XCUIElementTypeWindow",
|
|
96
|
+
])
|
|
97
|
+
guard let windowId = windowResponse.valueDict?["ELEMENT"] as? String else {
|
|
98
|
+
return XCTFail("Could not find the root window element")
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let subElementResponse = try client.post("/session/\(sessionId!)/element/\(windowId)/element", body: [
|
|
102
|
+
"using": "accessibility id",
|
|
103
|
+
"value": "tapMeButton",
|
|
104
|
+
])
|
|
105
|
+
XCTAssertEqual(subElementResponse.statusCode, 200)
|
|
106
|
+
XCTAssertNotNil(subElementResponse.valueDict?["ELEMENT"])
|
|
107
|
+
|
|
108
|
+
let subElementsResponse = try client.post("/session/\(sessionId!)/element/\(windowId)/elements", body: [
|
|
109
|
+
"using": "class name",
|
|
110
|
+
"value": "XCUIElementTypeButton",
|
|
111
|
+
])
|
|
112
|
+
XCTAssertEqual(subElementsResponse.statusCode, 200)
|
|
113
|
+
XCTAssertGreaterThanOrEqual((subElementsResponse.value as? [[String: Any]])?.count ?? 0, 1)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func testActiveElementResolvesToTheFocusedTextField() throws {
|
|
117
|
+
let fieldId = try findElement(byAccessibilityId: "typingField")
|
|
118
|
+
try client.post("/session/\(sessionId!)/element/\(fieldId)/click")
|
|
119
|
+
// Give the keyboard-sheet transition a moment to finish before checking focus.
|
|
120
|
+
Thread.sleep(forTimeInterval: 1)
|
|
121
|
+
|
|
122
|
+
let response = try client.get("/session/\(sessionId!)/element/active")
|
|
123
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
124
|
+
XCTAssertNotNil(response.valueDict?["ELEMENT"])
|
|
125
|
+
}
|
|
126
|
+
}
|
package/WebDriverAgentTests/IntegrationTests_watchOS/WDAScreenshotAndSourceIntegrationTests.swift
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
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/element screenshots and /source, /wda/accessibleSource.
|
|
12
|
+
final class WDAScreenshotAndSourceIntegrationTests: WDAWatchIntegrationTestCase {
|
|
13
|
+
private static let pngMagicBytes: [UInt8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
|
|
14
|
+
|
|
15
|
+
private func assertLooksLikeAPNG(_ base64String: String?, file: StaticString = #filePath, line: UInt = #line) {
|
|
16
|
+
guard let base64String = base64String, let data = Data(base64Encoded: base64String) else {
|
|
17
|
+
return XCTFail("Response value was not base64 data", file: file, line: line)
|
|
18
|
+
}
|
|
19
|
+
XCTAssertGreaterThan(data.count, Self.pngMagicBytes.count, file: file, line: line)
|
|
20
|
+
XCTAssertEqual(Array(data.prefix(Self.pngMagicBytes.count)), Self.pngMagicBytes, "response was not a PNG", file: file, line: line)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func testDeviceScreenshot() throws {
|
|
24
|
+
let response = try client.get("/session/\(sessionId!)/screenshot")
|
|
25
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
26
|
+
assertLooksLikeAPNG(response.valueString)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func testElementScreenshot() throws {
|
|
30
|
+
let buttonId = try findElement(byAccessibilityId: "tapMeButton")
|
|
31
|
+
let response = try client.get("/session/\(sessionId!)/element/\(buttonId)/screenshot")
|
|
32
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
33
|
+
assertLooksLikeAPNG(response.valueString)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func testPageSourceContainsTheKnownElements() throws {
|
|
37
|
+
let response = try client.get("/session/\(sessionId!)/source?format=xml")
|
|
38
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
39
|
+
guard let xml = response.valueString else {
|
|
40
|
+
return XCTFail("No XML source returned")
|
|
41
|
+
}
|
|
42
|
+
XCTAssertTrue(xml.contains("tapMeButton"))
|
|
43
|
+
XCTAssertTrue(xml.contains("resultLabel"))
|
|
44
|
+
XCTAssertTrue(xml.contains("typingField"))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func testAccessibleSource() throws {
|
|
48
|
+
let response = try client.get("/session/\(sessionId!)/wda/accessibleSource")
|
|
49
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
50
|
+
XCTAssertNotNil(response.value)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
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 FBSessionCommands. Manages its own session lifecycle per test, since that's what's
|
|
12
|
+
/// under test here, rather than using WDAWatchIntegrationTestCase.
|
|
13
|
+
final class WDASessionIntegrationTests: XCTestCase {
|
|
14
|
+
let client = WDAWatchHTTPClient()
|
|
15
|
+
|
|
16
|
+
override func setUp() {
|
|
17
|
+
super.setUp()
|
|
18
|
+
continueAfterFailure = false
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func testStatusIsReadyWithoutASession() throws {
|
|
22
|
+
let response = try client.get("/status")
|
|
23
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
24
|
+
XCTAssertEqual(response.valueDict?["ready"] as? Bool, true)
|
|
25
|
+
XCTAssertEqual(response.valueDict?["state"] as? String, "success")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func testHealthCheck() throws {
|
|
29
|
+
let response = try client.get("/wda/healthcheck")
|
|
30
|
+
XCTAssertEqual(response.statusCode, 200)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func testCreateGetActiveAndDeleteSessionLifecycle() throws {
|
|
34
|
+
let createResponse = try client.post("/session", body: [
|
|
35
|
+
"capabilities": ["alwaysMatch": ["bundleId": WDAWatchIntegrationTestCase.integrationAppBundleId]]
|
|
36
|
+
])
|
|
37
|
+
XCTAssertEqual(createResponse.statusCode, 200)
|
|
38
|
+
guard let sessionId = createResponse.valueDict?["sessionId"] as? String else {
|
|
39
|
+
return XCTFail("No sessionId in create-session response: \(String(describing: createResponse.json))")
|
|
40
|
+
}
|
|
41
|
+
XCTAssertFalse(sessionId.isEmpty)
|
|
42
|
+
|
|
43
|
+
let activeResponse = try client.get("/session/\(sessionId)")
|
|
44
|
+
XCTAssertEqual(activeResponse.statusCode, 200)
|
|
45
|
+
XCTAssertEqual(activeResponse.valueDict?["sessionId"] as? String, sessionId)
|
|
46
|
+
|
|
47
|
+
let deleteResponse = try client.delete("/session/\(sessionId)")
|
|
48
|
+
XCTAssertEqual(deleteResponse.statusCode, 200)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func testTimeoutsAndAppiumSettingsRoundTrip() throws {
|
|
52
|
+
let createResponse = try client.post("/session", body: [
|
|
53
|
+
"capabilities": ["alwaysMatch": ["bundleId": WDAWatchIntegrationTestCase.integrationAppBundleId]]
|
|
54
|
+
])
|
|
55
|
+
guard let sessionId = createResponse.valueDict?["sessionId"] as? String else {
|
|
56
|
+
return XCTFail("No sessionId in create-session response")
|
|
57
|
+
}
|
|
58
|
+
defer { _ = try? client.delete("/session/\(sessionId)") }
|
|
59
|
+
|
|
60
|
+
let timeoutsResponse = try client.post("/session/\(sessionId)/timeouts", body: ["implicit": 500])
|
|
61
|
+
XCTAssertEqual(timeoutsResponse.statusCode, 200)
|
|
62
|
+
|
|
63
|
+
let getSettingsResponse = try client.get("/session/\(sessionId)/appium/settings")
|
|
64
|
+
XCTAssertEqual(getSettingsResponse.statusCode, 200)
|
|
65
|
+
XCTAssertNotNil(getSettingsResponse.valueDict)
|
|
66
|
+
|
|
67
|
+
let setSettingsResponse = try client.post("/session/\(sessionId)/appium/settings", body: [
|
|
68
|
+
"settings": ["shouldUseCompactResponses": false]
|
|
69
|
+
])
|
|
70
|
+
XCTAssertEqual(setSettingsResponse.statusCode, 200)
|
|
71
|
+
}
|
|
72
|
+
}
|