Skip to content

Commit

Permalink
feat: Touch methods (#135)
Browse files Browse the repository at this point in the history
  • Loading branch information
Squidonomics authored Mar 27, 2024
1 parent ab25b9a commit c07d968
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 4 deletions.
4 changes: 2 additions & 2 deletions Docs/SupportedAPIs.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ Contributions to expand support to unimplemented functionality are always welcom
| POST | `/session/:sessionId/timeouts` | Supported | `Session.setTimeout()`|
| GET | `/session/:sessionId/title` | Supported | `Session.title` |
| POST | `/session/:sessionId/touch/click` | Supported | `Element.touchClick()`|
| POST | `/session/:sessionId/touch/doubleclick` | Supported | Not implemented |
| POST | `/session/:sessionId/touch/doubleclick` | Supported | `Element.doubleClick()`|
| POST | `/session/:sessionId/touch/down` | Supported | `Session.touchDown()`|
| POST | `/session/:sessionId/touch/flick` | Supported | Not implemented |
| POST | `/session/:sessionId/touch/flick` | Supported | `Session.flick()`, `Element.flick()`|
| POST | `/session/:sessionId/touch/longclick` | Supported | Not implemented |
| POST | `/session/:sessionId/touch/move` | Supported | `Session.touchMove()`|
| POST | `/session/:sessionId/touch/scroll` | Supported | `Session.touchScroll()`|
Expand Down
37 changes: 37 additions & 0 deletions Sources/WebDriver/Element.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,43 @@ public struct Element {
if let notInteractableError = result.value { throw notInteractableError }
}

/// Double clicks an element by id.
public func doubleClick(retryTimeout: TimeInterval? = nil) throws {
let request = Requests.SessionTouchDoubleClick(session: session.id, element: id)
let result = try poll(timeout: retryTimeout ?? session.defaultRetryTimeout) {
do {
// Immediately bubble most failures, only retry on element not interactable.
try webDriver.send(request)
return PollResult.success(nil as ErrorResponse?)
} catch let error as ErrorResponse where error.status == .winAppDriver_elementNotInteractable {
return PollResult.failure(error)
}
}

if let notInteractableError = result.value { throw notInteractableError }
}

/// - Parameters:
/// - retryTimeout: Optional value to override defaultRetryTimeout.
/// - element: Element id to click
/// - xOffset: The x offset in pixels to flick by.
/// - yOffset: The y offset in pixels to flick by.
/// - speed: The speed in pixels per seconds.
public func flick(xOffset: Double, yOffset: Double, speed: Double, retryTimeout: TimeInterval? = nil) throws {
let request = Requests.SessionTouchFlickElement(session: session.id, element: id, xOffset: xOffset, yOffset: yOffset, speed: speed)
let result = try poll(timeout: retryTimeout ?? session.defaultRetryTimeout) {
do {
// Immediately bubble most failures, only retry on element not interactable.
try webDriver.send(request)
return PollResult.success(nil as ErrorResponse?)
} catch let error as ErrorResponse where error.status == .winAppDriver_elementNotInteractable {
return PollResult.failure(error)
}
}

if let notInteractableError = result.value { throw notInteractableError }
}

/// Finds an element by id, starting from this element.
/// - Parameter byId: id of the element to search for.
/// - Parameter retryTimeout: Optional value to override defaultRetryTimeout.
Expand Down
63 changes: 61 additions & 2 deletions Sources/WebDriver/Requests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,66 @@ public enum Requests {
}
}
}

// https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidtouchdoubleclick
public struct SessionTouchDoubleClick: Request {
public var session: String
public var element: String

public var pathComponents: [String] { ["session", session, "touch", "doubleclick"] }
public var method: HTTPMethod { .post }
public var body: Body { .init(element: element) }

public struct Body: Codable {
public var element: String
}
}

// https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidtouchflick
public struct SessionTouchFlickElement: Request {
public var session: String
public var element: String
public var xOffset: Double
public var yOffset: Double
public var speed: Double

public var pathComponents: [String] { ["session", session, "touch", "flick"] }
public var method: HTTPMethod { .post }
public var body: Body { .init(xOffset: xOffset, yOffset: yOffset, speed: speed) }

public struct Body: Codable {
public var xOffset: Double
public var yOffset: Double
public var speed: Double

private enum CodingKeys: String, CodingKey {
case xOffset = "xoffset"
case yOffset = "yoffset"
case speed = "speed"
}
}
}

// https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidtouchflick-1
public struct SessionTouchFlick: Request {
public var session: String
public var xSpeed: Double
public var ySpeed: Double

public var pathComponents: [String] { ["session", session, "touch", "flick"] }
public var method: HTTPMethod { .post }
public var body: Body { .init(xSpeed: xSpeed, ySpeed: ySpeed) }

public struct Body: Codable {
public var xSpeed: Double
public var ySpeed: Double

private enum CodingKeys: String, CodingKey {
case xSpeed = "xspeed"
case ySpeed = "yspeed"
}
}
}

// https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidlocation
public enum SessionLocation {
Expand Down Expand Up @@ -567,8 +627,7 @@ public enum Requests {
public var session: String

public var pathComponents: [String] { ["session", session, "source"] }
public var method: HTTPMethod {.get}

public var method: HTTPMethod { .get }

public typealias Response = ResponseWithValue<String>
}
Expand Down
8 changes: 8 additions & 0 deletions Sources/WebDriver/Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,14 @@ public class Session {
}.value
}

/// - Parameters:
/// - retryTimeout: Optional value to override defaultRetryTimeout.
/// - xSpeed: The x speed in pixels per second.
/// - ySpeed: The y speed in pixels per second.
public func flick(xSpeed: Double, ySpeed: Double) throws {
try webDriver.send(Requests.SessionTouchFlick(session: id, xSpeed: xSpeed, ySpeed: ySpeed))
}

/// Moves the pointer to a location relative to the current pointer position or an element.
/// - Parameter element: if not nil the top left of the element provides the origin.
/// - Parameter xOffset: x offset from the left of the element.
Expand Down
24 changes: 24 additions & 0 deletions Tests/UnitTests/APIToRequestMappingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,30 @@ class APIToRequestMappingTests: XCTestCase {
XCTAssert(try session.windowHandles == ["myWindow", "myWindow"])
}


func testElementDoubleClick() throws {
let mockWebDriver: MockWebDriver = MockWebDriver()
let session = Session(webDriver: mockWebDriver, existingId: "mySession")
let element = Element(session: session, id: "myElement")
mockWebDriver.expect(path: "session/mySession/touch/doubleclick", method: .post)
XCTAssertNotNil(try element.doubleClick())
}

func testElementFlick() throws {
let mockWebDriver: MockWebDriver = MockWebDriver()
let session = Session(webDriver: mockWebDriver, existingId: "mySession")
let element = Element(session: session, id: "myElement")
mockWebDriver.expect(path: "session/mySession/touch/flick", method: .post)
XCTAssertNotNil(try element.flick(xOffset: 5, yOffset: 20, speed: 2003))
}

func testSessionFlick() throws {
let mockWebDriver: MockWebDriver = MockWebDriver()
let session = Session(webDriver: mockWebDriver, existingId: "mySession")
mockWebDriver.expect(path: "session/mySession/touch/flick", method: .post)
XCTAssertNotNil(try session.flick(xSpeed: 5, ySpeed: 20))
}

func testSessionSource() throws {
let mockWebDriver: MockWebDriver = MockWebDriver()
let session = Session(webDriver: mockWebDriver, existingId: "mySession")
Expand Down

0 comments on commit c07d968

Please sign in to comment.