Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Touch methods #135

Merged
merged 7 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Docs/SupportedAPIs.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,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.precisionFlick()`|
tristanlabelle marked this conversation as resolved.
Show resolved Hide resolved
| 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(element: String, retryTimeout: TimeInterval? = nil) throws {
tristanlabelle marked this conversation as resolved.
Show resolved Hide resolved
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)
tristanlabelle marked this conversation as resolved.
Show resolved Hide resolved
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(element: String, xOffset: Double, yOffset: Double, speed: Double, retryTimeout: TimeInterval? = nil) throws {
tristanlabelle marked this conversation as resolved.
Show resolved Hide resolved
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
52 changes: 50 additions & 2 deletions Sources/WebDriver/Requests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -540,20 +540,68 @@ 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
tristanlabelle marked this conversation as resolved.
Show resolved Hide resolved
public var yOffset: Double
public var speed: Double
}
}

// 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
tristanlabelle marked this conversation as resolved.
Show resolved Hide resolved
public var ySpeed: Double
}
}

// 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 var method: HTTPMethod { .get }

public typealias Response = ResponseWithValue<ResponseValue>

public struct ResponseValue: Codable {
public var source: String
}

}

// https://www.selenium.dev/documentation/legacy/json_wire_protocol/#status
Expand Down
8 changes: 8 additions & 0 deletions Sources/WebDriver/Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,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 @@ -214,6 +214,30 @@ class APIToRequestMappingTests: XCTestCase {
XCTAssert(try session.size(window: "myWindow") == (width: 500, height: 500))
}


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(element: "myElement"))
}

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(element: "myElement", 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
Loading