From 45c514c1210a864b0d6ef6ee80c9da62b7421cfd Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 21 Dec 2023 17:13:53 -0800 Subject: [PATCH 1/4] Fix missing import. (#137) The WebDriver / WinAppDriver split put a WebDriver class extension in WinAppDriver. Which I guess is fine if you build with SPM, but it is not fine if you build with CMake. This is needed to build Arc test targets with CMake. --- Sources/WinAppDriver/ErrorResponse+WinAppDriver.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/WinAppDriver/ErrorResponse+WinAppDriver.swift b/Sources/WinAppDriver/ErrorResponse+WinAppDriver.swift index 80f288e..7f49d8f 100644 --- a/Sources/WinAppDriver/ErrorResponse+WinAppDriver.swift +++ b/Sources/WinAppDriver/ErrorResponse+WinAppDriver.swift @@ -1,3 +1,5 @@ +import WebDriver + extension ErrorResponse.Status { // WinAppDriver returns when passing an incorrect window handle to attach to. static let winAppDriver_invalidArgument = Self(rawValue: 100) From e6b4a6cead9af319187d24e4102f0094d3884ace Mon Sep 17 00:00:00 2001 From: Brian Harvey Date: Tue, 2 Jan 2024 19:09:05 -0500 Subject: [PATCH 2/4] feat: Add execute script WebDriver functions (#123) This pr adds two legacy json wire protocol endpoints for executing scripts. Implements the `/session/:sessionId/execute` and `/session/:sessionId/execute_async` endpoints --------- Co-authored-by: Saleem Abdulrasool Co-authored-by: Jeff Co-authored-by: Tristan Labelle --- Docs/SupportedAPIs.md | 4 +++- Sources/WebDriver/Requests.swift | 17 +++++++++++++++++ Sources/WebDriver/Session.swift | 4 ++++ Tests/UnitTests/APIToRequestMappingTests.swift | 14 ++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Docs/SupportedAPIs.md b/Docs/SupportedAPIs.md index 1e6c987..918928b 100644 --- a/Docs/SupportedAPIs.md +++ b/Docs/SupportedAPIs.md @@ -37,6 +37,8 @@ Contributions to expand support to unimplemented functionality are always welcom | GET | `/session/:sessionId/element/:id/size` | Supported | `Element.size` | | GET | `/session/:sessionId/element/:id/text` | Supported | `Element.text` | | POST | `/session/:sessionId/element/:id/value` | Supported | `Element.sendKeys()`| +| POST | `/session/:sessionId/execute` | Not Supported| `Session.execute()` | +| POST | `/session/:sessionId/execute_async` | Not Supported| `Session.execute()` | | POST | `/session/:sessionId/forward` | Supported | `Session.forward()` | | POST | `/session/:sessionId/keys` | Supported | `Session.sendKeys()`| | GET | `/session/:sessionId/location` | Supported | Not implemented | @@ -68,4 +70,4 @@ Contributions to expand support to unimplemented functionality are always welcom | GET | `/session/:sessionId/window/:windowHandle/position` | Supported | Not implemented | | POST | `/session/:sessionId/window/:windowHandle/maximize` | Supported | Not implemented | | GET | `/session/:sessionId/window_handle` | Supported | Not implemented | -| GET | `/session/:sessionId/window_handles` | Supported | Not implemented | \ No newline at end of file +| GET | `/session/:sessionId/window_handles` | Supported | Not implemented | diff --git a/Sources/WebDriver/Requests.swift b/Sources/WebDriver/Requests.swift index 0351bc0..ebf226e 100644 --- a/Sources/WebDriver/Requests.swift +++ b/Sources/WebDriver/Requests.swift @@ -462,6 +462,23 @@ public enum Requests { } } + // https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidexecute + // https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidexecute_async + public struct SessionScript: Request { + public var session: String + public var script: String + public var args: [String] + public var async: Bool + + public var pathComponents: [String] { ["session", session, async ? "execute_async" : "execute"] } + public var method: HTTPMethod { .post } + public var body: Body { .init(script: script, args: args) } + public struct Body: Codable { + public var script: String + public var args: [String] + } + } + // https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidwindow public enum SessionWindow { public struct Post: Request { diff --git a/Sources/WebDriver/Session.swift b/Sources/WebDriver/Session.swift index 22bd368..e78b6c7 100644 --- a/Sources/WebDriver/Session.swift +++ b/Sources/WebDriver/Session.swift @@ -73,6 +73,10 @@ public class Session { Requests.SessionTimeouts(session: id, type: type, ms: duration * 1000)) } + public func execute(script: String, args: [String] = [], async: Bool = false) throws { + try webDriver.send(Requests.SessionScript(session: id, script: script, args: args, async: async)) + } + public func back() throws { try webDriver.send(Requests.SessionBack(session: id)) } diff --git a/Tests/UnitTests/APIToRequestMappingTests.swift b/Tests/UnitTests/APIToRequestMappingTests.swift index a674dd4..79f4769 100644 --- a/Tests/UnitTests/APIToRequestMappingTests.swift +++ b/Tests/UnitTests/APIToRequestMappingTests.swift @@ -170,6 +170,20 @@ class APIToRequestMappingTests: XCTestCase { XCTAssert(try element.enabled == true) } + func testSessionScript() throws { + let mockWebDriver = MockWebDriver() + let session = Session(webDriver: mockWebDriver, existingId: "mySession") + mockWebDriver.expect(path: "session/mySession/execute", method: .post) + XCTAssertNotNil(try session.execute(script: "return document.body", args: ["script"], async: false)) + } + + func testSessionScriptAsync() throws { + let mockWebDriver = MockWebDriver() + let session = Session(webDriver: mockWebDriver, existingId: "mySession") + mockWebDriver.expect(path: "session/mySession/execute_async", method: .post) + XCTAssertNotNil(try session.execute(script: "return document.body", args: ["script"], async: true)) + } + func testSessionTouchScroll() throws { let mockWebDriver: MockWebDriver = MockWebDriver() let session = Session(webDriver: mockWebDriver, existingId: "mySession") From b84c7a244d0447b16699c450fd393d30423dbdcc Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 6 Feb 2024 13:40:44 -0800 Subject: [PATCH 3/4] Refactor for xplat building. (#140) Compare to https://github.com/thebrowsercompany/swift-webdriver/pull/139. I propose this is more correct for how we want to make this xplat. Verified this builds and runs on Mac and Windows, and Testing passes on both. --- Package.swift | 33 ++++++++++++------- Sources/WebDriver/HTTPWebDriver.swift | 2 ++ Sources/WebDriver/URLRequestExtensions.swift | 2 ++ .../CommandLineTests.swift | 0 4 files changed, 25 insertions(+), 12 deletions(-) rename Tests/{UnitTests => WinAppDriverTests}/CommandLineTests.swift (100%) diff --git a/Package.swift b/Package.swift index c03fdfa..a32ec4d 100644 --- a/Package.swift +++ b/Package.swift @@ -5,28 +5,37 @@ import PackageDescription let package = Package( name: "swift-webdriver", products: [ - .library(name: "WebDriver", targets: ["WebDriver", "WinAppDriver"]), + .library(name: "WebDriver", targets: ["WebDriver"]), ], targets: [ .target( name: "WebDriver", path: "Sources/WebDriver"), - .target( - name: "WinAppDriver", - dependencies: ["WebDriver"], - path: "Sources/WinAppDriver"), .target( name: "TestsCommon", path: "Tests/Common"), - .testTarget( - name: "WinAppDriverTests", - dependencies: ["TestsCommon", "WebDriver", "WinAppDriver"], - // Ignore "LNK4217: locally defined symbol imported" spew due to SPM library support limitations - linkerSettings: [ .unsafeFlags(["-Xlinker", "-ignore:4217"]) ]), .testTarget( name: "UnitTests", - dependencies: ["TestsCommon", "WebDriver", "WinAppDriver"], + dependencies: ["TestsCommon", "WebDriver"], // Ignore "LNK4217: locally defined symbol imported" spew due to SPM library support limitations - linkerSettings: [ .unsafeFlags(["-Xlinker", "-ignore:4217"]) ]), + linkerSettings: [ .unsafeFlags(["-Xlinker", "-ignore:4217"], .when(platforms: [.windows])) ]), ] ) + +#if os(Windows) +package.products += [ + .library(name: "WinAppDriver", targets: ["WinAppDriver"]) +] +package.targets += [ + .target( + name: "WinAppDriver", + dependencies: ["WebDriver"], + path: "Sources/WinAppDriver"), + .testTarget( + name: "WinAppDriverTests", + dependencies: ["TestsCommon", "WebDriver", "WinAppDriver"], + // Ignore "LNK4217: locally defined symbol imported" spew due to SPM library support limitations + linkerSettings: [ .unsafeFlags(["-Xlinker", "-ignore:4217"]) ]), +] +#endif + diff --git a/Sources/WebDriver/HTTPWebDriver.swift b/Sources/WebDriver/HTTPWebDriver.swift index 064266e..acd6a81 100644 --- a/Sources/WebDriver/HTTPWebDriver.swift +++ b/Sources/WebDriver/HTTPWebDriver.swift @@ -1,5 +1,7 @@ import Foundation +#if canImport(FoundationNetworking) import FoundationNetworking +#endif public struct HTTPWebDriver: WebDriver { let rootURL: URL diff --git a/Sources/WebDriver/URLRequestExtensions.swift b/Sources/WebDriver/URLRequestExtensions.swift index 1739de5..e9edf88 100644 --- a/Sources/WebDriver/URLRequestExtensions.swift +++ b/Sources/WebDriver/URLRequestExtensions.swift @@ -1,5 +1,7 @@ import Foundation +#if canImport(FoundationNetworking) import FoundationNetworking +#endif extension URLSession { func dataTask( diff --git a/Tests/UnitTests/CommandLineTests.swift b/Tests/WinAppDriverTests/CommandLineTests.swift similarity index 100% rename from Tests/UnitTests/CommandLineTests.swift rename to Tests/WinAppDriverTests/CommandLineTests.swift From 0e6231013925b43feaf04340424399102f6e2357 Mon Sep 17 00:00:00 2001 From: Brian Harvey Date: Wed, 7 Feb 2024 09:23:16 -0500 Subject: [PATCH 4/4] feat: Source method (#134) Implements method: `/session/:sessionId/source` --- Docs/SupportedAPIs.md | 2 +- Sources/WebDriver/Requests.swift | 15 +++++++++++++++ Sources/WebDriver/Session.swift | 6 ++++++ Tests/UnitTests/APIToRequestMappingTests.swift | 10 ++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Docs/SupportedAPIs.md b/Docs/SupportedAPIs.md index 918928b..5e35d71 100644 --- a/Docs/SupportedAPIs.md +++ b/Docs/SupportedAPIs.md @@ -46,7 +46,7 @@ Contributions to expand support to unimplemented functionality are always welcom | GET | `/session/:sessionId/orientation` | Supported | Not implemented | | POST | `/session/:sessionId/refresh` | Not supported| `Session.refresh()` | | GET | `/session/:sessionId/screenshot` | Supported | `Session.screenshot()`| -| GET | `/session/:sessionId/source` | Supported | Not implemented | +| GET | `/session/:sessionId/source` | Supported | `Session.source` | | POST | `/session/:sessionId/timeouts` | Supported | `Session.setTimeout()`| | GET | `/session/:sessionId/title` | Supported | `Session.title` | | POST | `/session/:sessionId/touch/click` | Supported | `Element.touchClick()`| diff --git a/Sources/WebDriver/Requests.swift b/Sources/WebDriver/Requests.swift index ebf226e..29d2948 100644 --- a/Sources/WebDriver/Requests.swift +++ b/Sources/WebDriver/Requests.swift @@ -541,6 +541,21 @@ public enum Requests { } } + // https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidsource + public struct SessionSource: Request { + public var session: String + + public var pathComponents: [String] { ["session", session, "source"] } + public var method: HTTPMethod {.get} + + public typealias Response = ResponseWithValue + + public struct ResponseValue: Codable { + public var source: String + } + + } + // https://www.selenium.dev/documentation/legacy/json_wire_protocol/#status public struct Status: Request { public var pathComponents: [String] { ["status"] } diff --git a/Sources/WebDriver/Session.swift b/Sources/WebDriver/Session.swift index e78b6c7..b59f4ab 100644 --- a/Sources/WebDriver/Session.swift +++ b/Sources/WebDriver/Session.swift @@ -321,6 +321,12 @@ public class Session { try webDriver.send(Requests.SessionWindowSize.Post(session: id, windowHandle: handle, width: width, height: height)) } + /// - Returns: The current page source. + public func source() throws -> String { + let response = try webDriver.send(Requests.SessionSource(session: id)) + return response.value.source + } + /// Deletes the current session. public func delete() throws { guard shouldDelete else { return } diff --git a/Tests/UnitTests/APIToRequestMappingTests.swift b/Tests/UnitTests/APIToRequestMappingTests.swift index 79f4769..a054ee1 100644 --- a/Tests/UnitTests/APIToRequestMappingTests.swift +++ b/Tests/UnitTests/APIToRequestMappingTests.swift @@ -213,4 +213,14 @@ class APIToRequestMappingTests: XCTestCase { } XCTAssert(try session.size(window: "myWindow") == (width: 500, height: 500)) } + + func testSessionSource() throws { + let mockWebDriver: MockWebDriver = MockWebDriver() + let session = Session(webDriver: mockWebDriver, existingId: "mySession") + + mockWebDriver.expect(path: "session/mySession/source", method: .get, type: Requests.SessionSource.self) { + ResponseWithValue(.init(source: "currentSource")) + } + XCTAssert(try session.source() == "currentSource") + } }