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.
Files changed (59) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/PrivateHeaders/XCTest/XCUIDevice.h +0 -1
  3. package/Scripts/build-webdriveragent.mjs +9 -9
  4. package/Scripts/build.sh +8 -1
  5. package/WebDriverAgent.xcodeproj/project.pbxproj +125 -107
  6. package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationTests_watchOS.xcscheme +4 -4
  7. package/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.h +34 -0
  8. package/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +72 -0
  9. package/WebDriverAgentLib/Commands/FBCustomCommands.m +36 -0
  10. package/WebDriverAgentLib/FBAlert.m +8 -6
  11. package/WebDriverAgentLib/Info.plist +2 -2
  12. package/WebDriverAgentTests/IntegrationTests_watchOS/IntegrationTests_watchOS-Bridging-Header.h +21 -0
  13. package/WebDriverAgentTests/IntegrationTests_watchOS/WDAAlertIntegrationTests.swift +11 -9
  14. package/WebDriverAgentTests/IntegrationTests_watchOS/WDAAppLifecycleIntegrationTests.swift +18 -38
  15. package/WebDriverAgentTests/IntegrationTests_watchOS/WDAClickIntegrationTests.swift +11 -17
  16. package/WebDriverAgentTests/IntegrationTests_watchOS/WDADeviceIntegrationTests.swift +74 -32
  17. package/WebDriverAgentTests/IntegrationTests_watchOS/WDAElementAttributeIntegrationTests.swift +23 -41
  18. package/WebDriverAgentTests/IntegrationTests_watchOS/WDAFindIntegrationTests.swift +70 -95
  19. package/WebDriverAgentTests/IntegrationTests_watchOS/WDAScreenshotAndSourceIntegrationTests.swift +24 -22
  20. package/WebDriverAgentTests/IntegrationTests_watchOS/WDATypingIntegrationTests.swift +44 -28
  21. package/WebDriverAgentTests/IntegrationTests_watchOS/WDAWatchInProcessTestCase.swift +51 -0
  22. package/build/lib/check-dependencies.d.ts.map +1 -1
  23. package/build/lib/check-dependencies.js +21 -7
  24. package/build/lib/check-dependencies.js.map +1 -1
  25. package/build/lib/constants.d.ts +1 -2
  26. package/build/lib/constants.d.ts.map +1 -1
  27. package/build/lib/constants.js +1 -2
  28. package/build/lib/constants.js.map +1 -1
  29. package/build/lib/types.d.ts +2 -2
  30. package/build/lib/types.d.ts.map +1 -1
  31. package/build/lib/utils/index.d.ts +1 -1
  32. package/build/lib/utils/index.d.ts.map +1 -1
  33. package/build/lib/utils/index.js +1 -1
  34. package/build/lib/utils/index.js.map +1 -1
  35. package/build/lib/utils/platform.d.ts +12 -0
  36. package/build/lib/utils/platform.d.ts.map +1 -1
  37. package/build/lib/utils/platform.js +23 -1
  38. package/build/lib/utils/platform.js.map +1 -1
  39. package/build/lib/utils/xctestrun.js +27 -3
  40. package/build/lib/utils/xctestrun.js.map +1 -1
  41. package/build/lib/webdriveragent.d.ts.map +1 -1
  42. package/build/lib/webdriveragent.js +4 -1
  43. package/build/lib/webdriveragent.js.map +1 -1
  44. package/build/lib/xcodebuild.d.ts.map +1 -1
  45. package/build/lib/xcodebuild.js +19 -9
  46. package/build/lib/xcodebuild.js.map +1 -1
  47. package/lib/check-dependencies.ts +23 -7
  48. package/lib/constants.ts +1 -2
  49. package/lib/types.ts +4 -2
  50. package/lib/utils/index.ts +1 -1
  51. package/lib/utils/platform.ts +25 -1
  52. package/lib/utils/xctestrun.ts +29 -3
  53. package/lib/webdriveragent.ts +5 -1
  54. package/lib/xcodebuild.ts +27 -12
  55. package/package.json +3 -2
  56. package/WebDriverAgentTests/IntegrationTests_watchOS/WDASessionIntegrationTests.swift +0 -72
  57. package/WebDriverAgentTests/IntegrationTests_watchOS/WDAUnknownCommandIntegrationTests.swift +0 -24
  58. package/WebDriverAgentTests/IntegrationTests_watchOS/WDAWatchHTTPClient.swift +0 -95
  59. package/WebDriverAgentTests/IntegrationTests_watchOS/WDAWatchIntegrationTestCase.swift +0 -61
package/lib/xcodebuild.ts CHANGED
@@ -15,12 +15,17 @@ import type {
15
15
  XcodeBuildSettings,
16
16
  XcodeShowBuildSettingsEntry,
17
17
  } from './types.js';
18
- import {getWDAUpgradeTimestamp, isTvOS, setRealDeviceSecurity, setXctestrunFile} from './utils/index.js';
18
+ import {
19
+ getPlatformSchemeSuffix,
20
+ getWDAUpgradeTimestamp,
21
+ isTvOS,
22
+ isWatchOS,
23
+ setRealDeviceSecurity,
24
+ setXctestrunFile,
25
+ } from './utils/index.js';
19
26
 
20
27
  const DEFAULT_SIGNING_ID = 'iPhone Developer';
21
28
  const PREBUILD_DELAY = 0;
22
- const RUNNER_SCHEME_IOS = 'WebDriverAgentRunner';
23
- const LIB_SCHEME_IOS = 'WebDriverAgentLib';
24
29
 
25
30
  const ERROR_WRITING_ATTACHMENT = 'Error writing attachment data to file';
26
31
  const ERROR_COPYING_ATTACHMENT = 'Error copying testing attachment';
@@ -29,9 +34,6 @@ const IGNORED_ERRORS_PATTERN = new RegExp(
29
34
  '(' + IGNORED_ERRORS.map((errStr) => util.escapeRegExp(errStr)).join('|') + ')',
30
35
  );
31
36
 
32
- const RUNNER_SCHEME_TV = 'WebDriverAgentRunner_tvOS';
33
- const LIB_SCHEME_TV = 'WebDriverAgentLib_tvOS';
34
-
35
37
  const REAL_DEVICES_CONFIG_DOCS_LINK =
36
38
  'https://appium.github.io/appium-xcuitest-driver/latest/preparation/real-device-config/';
37
39
 
@@ -215,8 +217,9 @@ export class XcodeBuild {
215
217
  * Cleans both the library and runner schemes for the appropriate platform.
216
218
  */
217
219
  async cleanProject(): Promise<void> {
218
- const libScheme = isTvOS(this.platformName || '') ? LIB_SCHEME_TV : LIB_SCHEME_IOS;
219
- const runnerScheme = isTvOS(this.platformName || '') ? RUNNER_SCHEME_TV : RUNNER_SCHEME_IOS;
220
+ const schemeSuffix = getPlatformSchemeSuffix(this.platformName || '');
221
+ const libScheme = `WebDriverAgentLib${schemeSuffix}`;
222
+ const runnerScheme = `WebDriverAgentRunner${schemeSuffix}`;
220
223
 
221
224
  for (const scheme of [libScheme, runnerScheme]) {
222
225
  this.log.debug(
@@ -387,7 +390,7 @@ export class XcodeBuild {
387
390
  if (this.useXctestrunFile && this.xctestrunFilePath) {
388
391
  args.push('-xctestrun', this.xctestrunFilePath);
389
392
  } else {
390
- const runnerScheme = isTvOS(this.platformName || '') ? RUNNER_SCHEME_TV : RUNNER_SCHEME_IOS;
393
+ const runnerScheme = `WebDriverAgentRunner${getPlatformSchemeSuffix(this.platformName || '')}`;
391
394
  args.push('-project', this.agentPath, '-scheme', runnerScheme);
392
395
  if (this.derivedDataPath) {
393
396
  args.push('-derivedDataPath', this.derivedDataPath);
@@ -397,9 +400,8 @@ export class XcodeBuild {
397
400
 
398
401
  const versionMatch = this.platformVersion ? new RegExp(/^(\d+)\.(\d+)/).exec(this.platformVersion) : null;
399
402
  if (versionMatch) {
400
- args.push(
401
- `${isTvOS(this.platformName || '') ? 'TV' : 'IPHONE'}OS_DEPLOYMENT_TARGET=${versionMatch[1]}.${versionMatch[2]}`,
402
- );
403
+ const deploymentTargetPrefix = getDeploymentTargetPrefix(this.platformName || '');
404
+ args.push(`${deploymentTargetPrefix}OS_DEPLOYMENT_TARGET=${versionMatch[1]}.${versionMatch[2]}`);
403
405
  } else {
404
406
  this.log.warn(
405
407
  `Cannot parse major and minor version numbers from platformVersion "${this.platformVersion}". ` +
@@ -549,6 +551,19 @@ export class XcodeBuild {
549
551
  }
550
552
  }
551
553
 
554
+ /**
555
+ * @returns The `xcodebuild` deployment target setting prefix for the given platform.
556
+ */
557
+ function getDeploymentTargetPrefix(platformName: string): string {
558
+ if (isTvOS(platformName)) {
559
+ return 'TV';
560
+ }
561
+ if (isWatchOS(platformName)) {
562
+ return 'WATCH';
563
+ }
564
+ return 'IPHONE';
565
+ }
566
+
552
567
  function buildSettingsArgsFromOptions(options?: RetrieveBuildSettingsOptions): string[] {
553
568
  const args: string[] = [];
554
569
  if (!options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-webdriveragent",
3
- "version": "16.3.0",
3
+ "version": "16.5.0",
4
4
  "description": "Package bundling WebDriverAgent",
5
5
  "keywords": [
6
6
  "Appium",
@@ -56,9 +56,10 @@
56
56
  "version": "npm run sync-wda-version",
57
57
  "test": "node --enable-source-maps --experimental-test-module-mocks --test --test-timeout=60000 \"./build/test/unit/**/*.spec.js\"",
58
58
  "e2e-test": "node --enable-source-maps --test --test-force-exit --test-concurrency=1 --test-timeout=600000 \"./build/test/functional/**/*.spec.js\"",
59
- "bundle": "npm run bundle:ios && npm run bundle:tv",
59
+ "bundle": "npm run bundle:ios && npm run bundle:tv && npm run bundle:watch",
60
60
  "bundle:ios": "TARGET=runner SDK=sim node ./Scripts/build-webdriveragent.mjs",
61
61
  "bundle:tv": "TARGET=tv_runner SDK=tv_sim node ./Scripts/build-webdriveragent.mjs",
62
+ "bundle:watch": "TARGET=watch_runner SDK=watch_sim node ./Scripts/build-webdriveragent.mjs",
62
63
  "fetch-prebuilt-wda": "node ./Scripts/fetch-prebuilt-wda.mjs",
63
64
  "sync-wda-version": "node ./Scripts/update-wda-version.mjs --package-version=${npm_package_version} && git add WebDriverAgentLib/Info.plist"
64
65
  },
@@ -1,72 +0,0 @@
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
- }
@@ -1,24 +0,0 @@
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 catch-all 404 routes and the OPTIONS ping route - neither needs a session.
12
- final class WDAUnknownCommandIntegrationTests: XCTestCase {
13
- let client = WDAWatchHTTPClient()
14
-
15
- func testUnknownRouteReturnsAnError() throws {
16
- let response = try client.get("/this/route/does/not/exist")
17
- XCTAssertNotEqual(response.statusCode, 200)
18
- }
19
-
20
- func testPing() throws {
21
- let response = try client.send("OPTIONS", "/anything")
22
- XCTAssertEqual(response.statusCode, 200)
23
- }
24
- }
@@ -1,95 +0,0 @@
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 Foundation
10
-
11
- /// Minimal synchronous JSON/HTTP client for driving the real WebDriverAgentRunner_watchOS
12
- /// server, the same way a real client (Appium, curl) would. Requires the server to already be
13
- /// running, e.g. via `xcodebuild test -scheme WebDriverAgentRunner_watchOS`.
14
- struct WDAWatchHTTPClient {
15
- struct Response {
16
- let statusCode: Int
17
- let json: [String: Any]?
18
-
19
- var value: Any? { json?["value"] }
20
- var valueDict: [String: Any]? { value as? [String: Any] }
21
- var valueString: String? { value as? String }
22
- }
23
-
24
- enum ClientError: Error, CustomStringConvertible {
25
- case requestFailed(String)
26
-
27
- var description: String {
28
- switch self {
29
- case .requestFailed(let message): return message
30
- }
31
- }
32
- }
33
-
34
- let baseURL: URL
35
-
36
- init() {
37
- let host = ProcessInfo.processInfo.environment["WDA_TEST_HOST"] ?? "127.0.0.1"
38
- let port = ProcessInfo.processInfo.environment["WDA_TEST_PORT"] ?? "8100"
39
- self.baseURL = URL(string: "http://\(host):\(port)")!
40
- }
41
-
42
- /// Splits path/query before resolving against baseURL - appendingPathComponent would
43
- /// percent-encode a literal "?" instead of treating it as a query delimiter.
44
- private func url(for path: String) -> URL {
45
- let parts = path.split(separator: "?", maxSplits: 1)
46
- var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)!
47
- components.path = components.path + parts[0]
48
- if parts.count > 1 {
49
- components.percentEncodedQuery = String(parts[1])
50
- }
51
- return components.url!
52
- }
53
-
54
- @discardableResult
55
- func send(_ method: String, _ path: String, body: [String: Any]? = nil, timeout: TimeInterval = 20) throws -> Response {
56
- var request = URLRequest(url: url(for: path))
57
- request.httpMethod = method
58
- request.timeoutInterval = timeout
59
- if let body = body {
60
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
61
- request.httpBody = try JSONSerialization.data(withJSONObject: body)
62
- }
63
-
64
- let semaphore = DispatchSemaphore(value: 0)
65
- var resultData: Data?
66
- var resultResponse: URLResponse?
67
- var resultError: Error?
68
-
69
- URLSession.shared.dataTask(with: request) { data, response, error in
70
- resultData = data
71
- resultResponse = response
72
- resultError = error
73
- semaphore.signal()
74
- }.resume()
75
-
76
- if semaphore.wait(timeout: .now() + timeout + 5) == .timedOut {
77
- throw ClientError.requestFailed("Request to \(method) \(path) timed out")
78
- }
79
- if let resultError = resultError {
80
- throw ClientError.requestFailed("Request to \(method) \(path) failed: \(resultError)")
81
- }
82
- guard let httpResponse = resultResponse as? HTTPURLResponse else {
83
- throw ClientError.requestFailed("Request to \(method) \(path) returned no HTTP response")
84
- }
85
- var json: [String: Any]?
86
- if let resultData = resultData, !resultData.isEmpty {
87
- json = try? JSONSerialization.jsonObject(with: resultData) as? [String: Any]
88
- }
89
- return Response(statusCode: httpResponse.statusCode, json: json)
90
- }
91
-
92
- func get(_ path: String) throws -> Response { try send("GET", path) }
93
- func post(_ path: String, body: [String: Any] = [:]) throws -> Response { try send("POST", path, body: body) }
94
- func delete(_ path: String) throws -> Response { try send("DELETE", path) }
95
- }
@@ -1,61 +0,0 @@
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
- /// Creates a fresh WDA session against IntegrationApp_watchOS per test, so each starts clean.
12
- class WDAWatchIntegrationTestCase: XCTestCase {
13
- static let integrationAppBundleId = ProcessInfo.processInfo.environment["WDA_TEST_BUNDLE_ID"]
14
- ?? "com.facebook.wda.IntegrationApp.watchOS"
15
-
16
- let client = WDAWatchHTTPClient()
17
- var sessionId: String!
18
-
19
- override func setUp() {
20
- super.setUp()
21
- continueAfterFailure = false
22
- do {
23
- let response = try client.post("/session", body: [
24
- "capabilities": ["alwaysMatch": ["bundleId": Self.integrationAppBundleId]]
25
- ])
26
- guard response.statusCode == 200, let sessionId = response.valueDict?["sessionId"] as? String else {
27
- XCTFail("Failed to create session: \(String(describing: response.json))")
28
- return
29
- }
30
- self.sessionId = sessionId
31
- } catch {
32
- XCTFail("Failed to create session: \(error)")
33
- }
34
- }
35
-
36
- override func tearDown() {
37
- if let sessionId = sessionId {
38
- _ = try? client.delete("/session/\(sessionId)")
39
- }
40
- super.tearDown()
41
- }
42
-
43
- /// Finds an element by accessibility id and returns its WDA element UUID.
44
- @discardableResult
45
- func findElement(byAccessibilityId accessibilityId: String, file: StaticString = #filePath, line: UInt = #line) throws -> String {
46
- let response = try client.post("/session/\(sessionId!)/element", body: [
47
- "using": "accessibility id",
48
- "value": accessibilityId,
49
- ])
50
- guard response.statusCode == 200, let elementId = response.valueDict?["ELEMENT"] as? String else {
51
- XCTFail("Could not find element '\(accessibilityId)': \(String(describing: response.json))", file: file, line: line)
52
- throw WDAWatchHTTPClient.ClientError.requestFailed("element not found")
53
- }
54
- return elementId
55
- }
56
-
57
- func attributeValue(_ elementId: String, _ name: String) throws -> String? {
58
- let response = try client.get("/session/\(sessionId!)/element/\(elementId)/attribute/\(name)")
59
- return response.valueString
60
- }
61
- }