Skip to content

Commit 60cf633

Browse files
Element.dragAndDrop(), Driver.dragAndDrop(), Element.rect() Implemented (#59)
* feat(PostElementDragToAnotherRequest): implemented drag method and request to webdriver api Code is untested. Code can compile. Refactored action types from double click request source file to its own source file * feat(Element,ChromeDriver): implemented rect, drag and drop on both element and driver methods Integration test for `Element.dragAndDrop()` implemented and passed as expected. `dragAndDrop()` webdriver method is a dynamic method that can either use javascript when element is draggable to drag element and fire off javascript event listeners, or use `Element.dragAndDrop()` when element is not draggable to use webdriver actions API instead. * chore: update submodules * test(ChromeDriverElementHandleIntegrationTests): implemented rect integation test, passed as expected * test(ChromeDriverDragAndDropIntegrationTests): implemented working integration test for drag and drop element to another element method
1 parent 80173a2 commit 60cf633

17 files changed

+499
-49
lines changed

.dotfiles

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// ActionsPayload.swift
2+
// Copyright (c) 2025 GetAutomaApp
3+
// All source code and related assets are the property of GetAutomaApp.
4+
// All rights reserved.
5+
6+
struct WebDriverElementOrigin: Encodable {
7+
let element: String
8+
9+
enum CodingKeys: String, CodingKey {
10+
case element = "element-6066-11e4-a52e-4f735466cecf"
11+
}
12+
}
13+
14+
struct PointerAction: Encodable {
15+
let type: String
16+
let origin: WebDriverElementOrigin?
17+
let x: Int?
18+
let y: Int?
19+
let button: Int?
20+
let duration: Int?
21+
22+
init(
23+
type: String,
24+
origin: WebDriverElementOrigin? = nil,
25+
x: Int? = nil,
26+
y: Int? = nil,
27+
button: Int? = nil,
28+
duration: Int? = nil
29+
) {
30+
self.type = type
31+
self.origin = origin
32+
self.x = x
33+
self.y = y
34+
self.button = button
35+
self.duration = duration
36+
}
37+
}
38+
39+
struct PointerSource: Encodable {
40+
let type: String
41+
let id: String
42+
let parameters: Parameters
43+
let actions: [PointerAction]
44+
45+
struct Parameters: Encodable {
46+
let pointerType: String
47+
}
48+
}
49+
50+
struct ActionsPayload: Encodable {
51+
let actions: [PointerSource]
52+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// GetElementRectRequest.swift
2+
// Copyright (c) 2025 GetAutomaApp
3+
// All source code and related assets are the property of GetAutomaApp.
4+
// All rights reserved.
5+
6+
import AsyncHTTPClient
7+
import Foundation
8+
import NIO
9+
import NIOHTTP1
10+
11+
internal struct GetElementRectRequest: RequestType {
12+
typealias Response = GetElementRectResponse
13+
14+
var baseURL: URL
15+
var sessionId: String
16+
var elementId: String
17+
18+
var path: String { "session/\(sessionId)/element/\(elementId)/rect" }
19+
var method: HTTPMethod = .GET
20+
var headers: HTTPHeaders = [:]
21+
var body: HTTPClient.Body? { nil }
22+
}

Sources/SwiftWebDriver/API/Request/Elements/PostElementDoubleClickRequest.swift

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -58,51 +58,3 @@ internal struct PostElementDoubleClickRequest: RequestType {
5858
return .data(data)
5959
}
6060
}
61-
62-
struct WebDriverElementOrigin: Encodable {
63-
let element: String
64-
65-
enum CodingKeys: String, CodingKey {
66-
case element = "element-6066-11e4-a52e-4f735466cecf"
67-
}
68-
}
69-
70-
struct PointerAction: Encodable {
71-
let type: String
72-
let origin: WebDriverElementOrigin?
73-
let x: Int?
74-
let y: Int?
75-
let button: Int?
76-
let duration: Int?
77-
78-
init(
79-
type: String,
80-
origin: WebDriverElementOrigin? = nil,
81-
x: Int? = nil,
82-
y: Int? = nil,
83-
button: Int? = nil,
84-
duration: Int? = nil
85-
) {
86-
self.type = type
87-
self.origin = origin
88-
self.x = x
89-
self.y = y
90-
self.button = button
91-
self.duration = duration
92-
}
93-
}
94-
95-
struct PointerSource: Encodable {
96-
let type: String
97-
let id: String
98-
let parameters: Parameters
99-
let actions: [PointerAction]
100-
101-
struct Parameters: Encodable {
102-
let pointerType: String
103-
}
104-
}
105-
106-
struct ActionsPayload: Encodable {
107-
let actions: [PointerSource]
108-
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// PostElementDragAndDropRequest.swift
2+
// Copyright (c) 2025 GetAutomaApp
3+
// All source code and related assets are the property of GetAutomaApp.
4+
// All rights reserved.
5+
6+
import AnyCodable
7+
import AsyncHTTPClient
8+
import Foundation
9+
import NIO
10+
import NIOHTTP1
11+
12+
internal struct PostElementDragAndDropRequest: RequestType {
13+
typealias Response = PostElementClickResponse
14+
15+
var baseURL: URL
16+
17+
var sessionId: String
18+
19+
var elementId: String
20+
21+
var toElementId: String
22+
23+
var elementRect: ElementRect
24+
25+
var targetElementRect: ElementRect
26+
27+
var path: String {
28+
"session/\(sessionId)/actions"
29+
}
30+
31+
var method: HTTPMethod = .POST
32+
33+
var headers: HTTPHeaders = [:]
34+
35+
var body: HTTPClient.Body? {
36+
let origin = WebDriverElementOrigin(element: elementId)
37+
let dragToOrigin = WebDriverElementOrigin(element: toElementId)
38+
39+
// let sourceCenterX = Int(elementRect.width / 2)
40+
// let sourceCenterY = Int(elementRect.height / 2)
41+
//
42+
let targetCenterX = Int(targetElementRect.width / 2)
43+
let targetCenterY = Int(targetElementRect.height / 2)
44+
45+
let pointerActions = [
46+
// PointerAction(type: "pointerMove", origin: origin, x: sourceCenterX, y: sourceCenterY),
47+
PointerAction(type: "pointerMove", origin: origin, x: 0, y: 0),
48+
PointerAction(type: "pointerDown", button: 0),
49+
PointerAction(type: "pause", duration: 100),
50+
PointerAction(type: "pointerMove", origin: dragToOrigin, x: targetCenterX, y: targetCenterY),
51+
PointerAction(type: "pointerUp", button: 0)
52+
]
53+
54+
let pointerSource = PointerSource(
55+
type: "pointer",
56+
id: "mouse",
57+
parameters: .init(pointerType: "mouse"),
58+
actions: pointerActions
59+
)
60+
61+
let payload = ActionsPayload(actions: [pointerSource])
62+
63+
let encoder = JSONEncoder()
64+
encoder.outputFormatting = .prettyPrinted
65+
let data = try? encoder.encode(payload)
66+
67+
guard let data else {
68+
return nil
69+
}
70+
71+
return .data(data)
72+
}
73+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// GetElementRectResponse.swift
2+
// Copyright (c) 2025 GetAutomaApp
3+
// All source code and related assets are the property of GetAutomaApp.
4+
// All rights reserved.
5+
6+
import Foundation
7+
8+
public struct GetElementRectResponse: ResponseType {
9+
public let value: ElementRect
10+
}

Sources/SwiftWebDriver/Element/Element.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ public protocol ElementCommandProtocol: FindElementProtocol {
1717
func name() async throws -> String
1818
func click() async throws -> String?
1919
func doubleClick() async throws -> String?
20+
func dragAndDrop(to: Element) async throws -> String?
2021
func clear() async throws -> String?
2122
func attribute(name: String) async throws -> String
2223
func send(value: String) async throws -> String?
2324
func screenshot() async throws -> String
25+
func rect() async throws -> ElementRect
2426
}
2527

2628
public struct Element: ElementCommandProtocol, Sendable {
@@ -85,6 +87,32 @@ public struct Element: ElementCommandProtocol, Sendable {
8587
return response.value
8688
}
8789

90+
@discardableResult
91+
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
92+
public func dragAndDrop(to: Element) async throws -> String? {
93+
let request = try await PostElementDragAndDropRequest(
94+
baseURL: baseURL,
95+
sessionId: sessionId,
96+
elementId: elementId,
97+
toElementId: to.elementId,
98+
elementRect: rect(),
99+
targetElementRect: to.rect()
100+
)
101+
let response = try await APIClient.shared.request(request)
102+
return response.value
103+
}
104+
105+
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
106+
public func rect() async throws -> ElementRect {
107+
let request = GetElementRectRequest(
108+
baseURL: baseURL,
109+
sessionId: sessionId,
110+
elementId: elementId
111+
)
112+
let response = try await APIClient.shared.request(request)
113+
return response.value
114+
}
115+
88116
@discardableResult
89117
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
90118
public func clear() async throws -> String? {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// ElementRect.swift
2+
// Copyright (c) 2025 GetAutomaApp
3+
// All source code and related assets are the property of GetAutomaApp.
4+
// All rights reserved.
5+
6+
public struct ElementRect: Codable, Sendable {
7+
public let x: Double
8+
public let y: Double
9+
public let width: Double
10+
public let height: Double
11+
}

Sources/SwiftWebDriver/WebDriver.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,9 @@ public class WebDriver<T: Driver> {
143143
public func setProperty(element: Element, propertyName: String, newValue: String) async throws {
144144
try await driver.setProperty(element: element, propertyName: propertyName, newValue: newValue)
145145
}
146+
147+
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
148+
public func dragAndDrop(from source: Element, to target: Element) async throws {
149+
try await driver.dragAndDrop(from: source, to: target)
150+
}
146151
}

Sources/SwiftWebDriver/WebDrivers/Chrome/ChromeDriver.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,10 @@ public class ChromeDriver: Driver {
332332
try await execute(script, args: args, type: .sync)
333333
}
334334

335+
public func dragAndDrop(from source: Element, to target: Element) async throws {
336+
try await ChromeDriverElementDragAndDropper(driver: self, from: source, to: target).dragAndDrop()
337+
}
338+
335339
deinit {
336340
let url = url
337341
let sessionId = sessionId

0 commit comments

Comments
 (0)