diff --git a/Sources/SwiftWebDriver/API/Request/Elements/PostElementDoubleClickRequest.swift b/Sources/SwiftWebDriver/API/Request/Elements/PostElementDoubleClickRequest.swift new file mode 100644 index 0000000..aba8832 --- /dev/null +++ b/Sources/SwiftWebDriver/API/Request/Elements/PostElementDoubleClickRequest.swift @@ -0,0 +1,108 @@ +// PostElementDoubleClickRequest.swift +// Copyright (c) 2025 GetAutomaApp +// All source code and related assets are the property of GetAutomaApp. +// All rights reserved. + +import AnyCodable +import AsyncHTTPClient +import Foundation +import NIO +import NIOHTTP1 + +internal struct PostElementDoubleClickRequest: RequestType { + typealias Response = PostElementClickResponse + + var baseURL: URL + + var sessionId: String + + var elementId: String + + var path: String { + "session/\(sessionId)/actions" + } + + var method: HTTPMethod = .POST + + var headers: HTTPHeaders = [:] + + var body: HTTPClient.Body? { + let origin = WebDriverElementOrigin(element: elementId) + + let pointerActions = [ + PointerAction(type: "pointerMove", origin: origin, x: 0, y: 0), + PointerAction(type: "pointerDown", button: 0), + PointerAction(type: "pointerUp", button: 0), + PointerAction(type: "pause", duration: 50), + PointerAction(type: "pointerDown", button: 0), + PointerAction(type: "pointerUp", button: 0) + ] + + let pointerSource = PointerSource( + type: "pointer", + id: "mouse", + parameters: .init(pointerType: "mouse"), + actions: pointerActions + ) + + let payload = ActionsPayload(actions: [pointerSource]) + + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + let data = try? encoder.encode(payload) + + guard let data else { + return nil + } + + return .data(data) + } +} + +struct WebDriverElementOrigin: Encodable { + let element: String + + enum CodingKeys: String, CodingKey { + case element = "element-6066-11e4-a52e-4f735466cecf" + } +} + +struct PointerAction: Encodable { + let type: String + let origin: WebDriverElementOrigin? + let x: Int? + let y: Int? + let button: Int? + let duration: Int? + + init( + type: String, + origin: WebDriverElementOrigin? = nil, + x: Int? = nil, + y: Int? = nil, + button: Int? = nil, + duration: Int? = nil + ) { + self.type = type + self.origin = origin + self.x = x + self.y = y + self.button = button + self.duration = duration + } +} + +struct PointerSource: Encodable { + let type: String + let id: String + let parameters: Parameters + let actions: [PointerAction] + + struct Parameters: Encodable { + let pointerType: String + } +} + +struct ActionsPayload: Encodable { + let actions: [PointerSource] +} diff --git a/Sources/SwiftWebDriver/Element/Element.swift b/Sources/SwiftWebDriver/Element/Element.swift index 78a8435..1a5b25e 100644 --- a/Sources/SwiftWebDriver/Element/Element.swift +++ b/Sources/SwiftWebDriver/Element/Element.swift @@ -2,9 +2,6 @@ // Copyright (c) 2025 GetAutomaApp // All source code and related assets are the property of GetAutomaApp. // All rights reserved. -// -// This package is freely distributable under the MIT license. -// This Package is a modified fork of https://github.com/ashi-psn/SwiftWebDriver. import Foundation import NIO @@ -19,6 +16,7 @@ public protocol ElementCommandProtocol: FindElementProtocol { func text() async throws -> String func name() async throws -> String func click() async throws -> String? + func doubleClick() async throws -> String? func clear() async throws -> String? func attribute(name: String) async throws -> String func send(value: String) async throws -> String? @@ -79,6 +77,14 @@ public struct Element: ElementCommandProtocol, Sendable { return response.value } + @discardableResult + @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) + public func doubleClick() async throws -> String? { + let request = PostElementDoubleClickRequest(baseURL: baseURL, sessionId: sessionId, elementId: elementId) + let response = try await APIClient.shared.request(request) + return response.value + } + @discardableResult @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func clear() async throws -> String? { diff --git a/TestAssets/elementHandleTestPage.html b/TestAssets/elementHandleTestPage.html index d241b95..981d2b9 100644 --- a/TestAssets/elementHandleTestPage.html +++ b/TestAssets/elementHandleTestPage.html @@ -8,6 +8,11 @@ element.innerHTML = "clicked!" } + function doubleClick() { + let element = document.getElementById("doubleclick") + element.innerHTML += "i" + } + function startTimer() { window.setTimeout(function () { let element = document.getElementById("willAppendChild") @@ -27,6 +32,7 @@ +
diff --git a/Tests/SwiftWebDriverIntegrationTests/ChromeDriver/Element/ChromeDriverElementHandleIntegrationTests.swift b/Tests/SwiftWebDriverIntegrationTests/ChromeDriver/Element/ChromeDriverElementHandleIntegrationTests.swift index 86aa24c..4633017 100644 --- a/Tests/SwiftWebDriverIntegrationTests/ChromeDriver/Element/ChromeDriverElementHandleIntegrationTests.swift +++ b/Tests/SwiftWebDriverIntegrationTests/ChromeDriver/Element/ChromeDriverElementHandleIntegrationTests.swift @@ -2,9 +2,6 @@ // Copyright (c) 2025 GetAutomaApp // All source code and related assets are the property of GetAutomaApp. // All rights reserved. -// -// This package is freely distributable under the MIT license. -// This Package is a modified fork of https://github.com/ashi-psn/SwiftWebDriver. @testable import SwiftWebDriver import Testing @@ -12,7 +9,7 @@ import Testing @Suite("Chrome Driver Element Handles", .serialized) internal class ChromeDriverElementHandleIntegrationTests: ChromeDriverTest { @Test("Click Button") - public func clickButton() async throws { + func clickButton() async throws { page = "elementHandleTestPage.html" try await driver.navigateTo(urlString: testPageURL.absoluteString) @@ -22,8 +19,19 @@ internal class ChromeDriverElementHandleIntegrationTests: ChromeDriverTest { #expect(test == "clicked!") } + @Test("Double Click Button") + func doubleClickButton() async throws { + page = "elementHandleTestPage.html" + + try await driver.navigateTo(urlString: testPageURL.absoluteString) + let button = try await driver.findElement(.css(.id("doubleclick"))) + try await button.doubleClick() + let test = try await button.text() + #expect(test == "ii") + } + @Test("Get Element Attributes") - public func getAttribute() async throws { + func getAttribute() async throws { page = "elementHandleTestPage.html" try await driver.navigateTo(urlString: testPageURL.absoluteString) @@ -34,7 +42,7 @@ internal class ChromeDriverElementHandleIntegrationTests: ChromeDriverTest { } @Test("Clear Element") - public func clearElement() async throws { + func clearElement() async throws { page = "elementHandleTestPage.html" try await driver.navigateTo(urlString: testPageURL.absoluteString) @@ -48,7 +56,7 @@ internal class ChromeDriverElementHandleIntegrationTests: ChromeDriverTest { } @Test("Send Key") - public func sendKey() async throws { + func sendKey() async throws { page = "elementHandleTestPage.html" try await driver.navigateTo(urlString: testPageURL.absoluteString) let element = try await driver.findElement(.css(.id("sendValue"))) @@ -58,7 +66,7 @@ internal class ChromeDriverElementHandleIntegrationTests: ChromeDriverTest { } @Test("Get Screenshot") - public func getScreenshot() async throws { + func getScreenshot() async throws { page = "elementHandleTestPage.html" try await driver.navigateTo(urlString: testPageURL.absoluteString) let element = try await driver.findElement(.css(.id("sendValue"))) @@ -68,7 +76,7 @@ internal class ChromeDriverElementHandleIntegrationTests: ChromeDriverTest { } @Test("Fail any operation if element becomes stale") - public func throwStaleError() async throws { + func throwStaleError() async throws { let sleepTotal = 3 page = "elementHandleTestPage.html" try await driver.navigateTo(urlString: testPageURL.absoluteString)