From 24f495b4ed94da5769d7de58e114d05827f77e28 Mon Sep 17 00:00:00 2001 From: Tristan Labelle Date: Wed, 13 Nov 2024 05:59:00 -0500 Subject: [PATCH] Introduce Keys.sequence (#164) --- Sources/WebDriver/Element.swift | 7 --- Sources/WebDriver/Keys.swift | 45 +++++++++---------- Sources/WebDriver/Session.swift | 9 ---- .../UnitTests/APIToRequestMappingTests.swift | 6 +-- Tests/WinAppDriverTests/MSInfo32App.swift | 4 +- Tests/WinAppDriverTests/RequestsTests.swift | 22 ++++----- 6 files changed, 36 insertions(+), 57 deletions(-) diff --git a/Sources/WebDriver/Element.swift b/Sources/WebDriver/Element.swift index 0389138..198d599 100644 --- a/Sources/WebDriver/Element.swift +++ b/Sources/WebDriver/Element.swift @@ -107,13 +107,6 @@ public struct Element { session: session.id, element: id, attribute: name)).value } - /// Sends key presses to this element. - /// - Parameter keys: An array of key sequences according to the WebDriver spec. - public func sendKeys(_ keys: [Keys]) throws { - try webDriver.send(Requests.ElementValue( - session: session.id, element: id, value: keys.map { $0.rawValue })) - } - /// Sends key presses to this element. /// - Parameter keys: A key sequence according to the WebDriver spec. public func sendKeys(_ keys: Keys) throws { diff --git a/Sources/WebDriver/Keys.swift b/Sources/WebDriver/Keys.swift index f5513c7..b57e5d5 100644 --- a/Sources/WebDriver/Keys.swift +++ b/Sources/WebDriver/Keys.swift @@ -1,11 +1,23 @@ /// Represents a sequence of WebDriver key events and characters. public struct Keys: RawRepresentable { + /// A string encoding the key sequence as defined by the WebDriver spec. public var rawValue: String public init(rawValue: String) { self.rawValue = rawValue } - public static func +(lhs: Self, rhs: Self) -> Self { Self(rawValue: lhs.rawValue + rhs.rawValue) } + /// Concatenates multiple key sequences into a single one. + public static func sequence(_ keys: [Self]) -> Self { + Self(rawValue: keys.reduce("") { $0 + $1.rawValue }) + } + + /// Concatenates multiple key sequences into a single one. + public static func sequence(_ keys: Self...) -> Self { + sequence(keys) + } +} +// MARK: Key constants +extension Keys { public static let a = Self(rawValue: "a") public static let b = Self(rawValue: "b") public static let c = Self(rawValue: "c") @@ -109,47 +121,30 @@ public struct Keys: RawRepresentable { public static let releaseModifiers = Keys(rawValue: "\u{E000}") } +// MARK: Modifier sequences extension Keys { /// Wraps a keys sequence with holding and releasing the shift key. public static func shift(_ keys: Self) -> Self { - Self(rawValue: "\(shiftModifier.rawValue)\(keys.rawValue)\(shiftModifier.rawValue)") + sequence(shiftModifier, keys, shiftModifier) } /// Wraps a keys sequence with holding and releasing the control key. public static func control(_ keys: Self) -> Self { - Self(rawValue: "\(controlModifier.rawValue)\(keys.rawValue)\(controlModifier.rawValue)") + sequence(controlModifier, keys, controlModifier) } /// Wraps a keys sequence with holding and releasing the alt key. public static func alt(_ keys: Self) -> Self { - Self(rawValue: "\(altModifier.rawValue)\(keys.rawValue)\(altModifier.rawValue)") + sequence(altModifier, keys, altModifier) } /// Wraps a keys sequence with holding and releasing the meta key. public static func meta(_ keys: Self) -> Self { - Self(rawValue: "\(metaModifier.rawValue)\(keys.rawValue)\(metaModifier.rawValue)") - } - - /// Wraps a keys sequence with holding and releasing modifier keys. - public static func combo(_ keys: Self, shift: Bool = false, control: Bool = false, alt: Bool = false, meta: Bool = false) -> Self { - var rawValue = "" - - if shift { rawValue += shiftModifier.rawValue } - if control { rawValue += controlModifier.rawValue } - if alt { rawValue += altModifier.rawValue } - if meta { rawValue += metaModifier.rawValue } - - rawValue += keys.rawValue - - if meta { rawValue += metaModifier.rawValue } - if alt { rawValue += altModifier.rawValue } - if control { rawValue += controlModifier.rawValue } - if shift { rawValue += shiftModifier.rawValue } - - return Self(rawValue: rawValue) + sequence(metaModifier, keys, metaModifier) } } +// MARK: Text and typing extension Keys { public enum TypingStrategy { case assumeUSKeyboard @@ -210,4 +205,4 @@ extension Keys { default: return false } } -} \ No newline at end of file +} diff --git a/Sources/WebDriver/Session.swift b/Sources/WebDriver/Session.swift index c812b02..5e7b37f 100644 --- a/Sources/WebDriver/Session.swift +++ b/Sources/WebDriver/Session.swift @@ -282,15 +282,6 @@ public class Session { session: id, element: element?.id, xOffset: xOffset, yOffset: yOffset)) } - /// Sends key presses to this session. - /// - Parameter keys: An array of key sequences according to the WebDriver spec. - /// - Parameter releaseModifiers: A boolean indicating whether to release modifier keys at the end of the sequence. - public func sendKeys(_ keys: [Keys], releaseModifiers: Bool = true) throws { - var value = keys.map { $0.rawValue } - if releaseModifiers { value.append(Keys.releaseModifiers.rawValue) } - try webDriver.send(Requests.SessionKeys(session: id, value: value)) - } - /// Sends key presses to this session. /// - Parameter keys: A key sequence according to the WebDriver spec. /// - Parameter releaseModifiers: A boolean indicating whether to release modifier keys at the end of the sequence. diff --git a/Tests/UnitTests/APIToRequestMappingTests.swift b/Tests/UnitTests/APIToRequestMappingTests.swift index efc17cd..6a8089d 100644 --- a/Tests/UnitTests/APIToRequestMappingTests.swift +++ b/Tests/UnitTests/APIToRequestMappingTests.swift @@ -123,15 +123,15 @@ class APIToRequestMappingTests: XCTestCase { let session = Session(webDriver: mockWebDriver, existingId: "mySession") let element = Element(session: session, id: "myElement") - let keys = [ Keys.a, Keys.b, Keys.c ] + let keys = Keys.sequence(.a, .b, .c) mockWebDriver.expect(path: "session/mySession/keys", method: .post, type: Requests.SessionKeys.self) { - XCTAssertEqual($0.value, keys.map { $0.rawValue }) + XCTAssertEqual($0.value.first, keys.rawValue) return CodableNone() } try session.sendKeys(keys, releaseModifiers: false) mockWebDriver.expect(path: "session/mySession/element/myElement/value", method: .post, type: Requests.ElementValue.self) { - XCTAssertEqual($0.value, keys.map { $0.rawValue }) + XCTAssertEqual($0.value.first, keys.rawValue) return CodableNone() } try element.sendKeys(keys) diff --git a/Tests/WinAppDriverTests/MSInfo32App.swift b/Tests/WinAppDriverTests/MSInfo32App.swift index 8e0853b..c58b93a 100644 --- a/Tests/WinAppDriverTests/MSInfo32App.swift +++ b/Tests/WinAppDriverTests/MSInfo32App.swift @@ -2,8 +2,8 @@ import XCTest class MSInfo32App { - static let findWhatEditBoxAccelerator = Keys.alt(Keys.w) - static let searchSelectedCategoryOnlyCheckboxAccelerator = Keys.alt(Keys.s) + static let findWhatEditBoxAccelerator = Keys.alt(.w) + static let searchSelectedCategoryOnlyCheckboxAccelerator = Keys.alt(.s) let session: Session diff --git a/Tests/WinAppDriverTests/RequestsTests.swift b/Tests/WinAppDriverTests/RequestsTests.swift index 87ca2a9..d34003d 100644 --- a/Tests/WinAppDriverTests/RequestsTests.swift +++ b/Tests/WinAppDriverTests/RequestsTests.swift @@ -82,7 +82,7 @@ class RequestsTests: XCTestCase { // ł: Not typeable on a US Keyboard // ☃: Unicode BMP character let str = "kKł☃" - try app.findWhatEditBox.sendKeys(Keys.text(str, typingStrategy: .windowsKeyboardAgnostic)) + try app.findWhatEditBox.sendKeys(.text(str, typingStrategy: .windowsKeyboardAgnostic)) // Normally we should be able to read the text back immediately, // but the MSInfo32 "Find what" edit box seems to queue events @@ -97,40 +97,40 @@ class RequestsTests: XCTestCase { func testSendKeysWithAcceleratorsGivesFocus() throws { try app.session.sendKeys(MSInfo32App.findWhatEditBoxAccelerator) try XCTAssert(Self.hasKeyboardFocus(app.findWhatEditBox)) - try app.session.sendKeys(Keys.tab) + try app.session.sendKeys(.tab) try XCTAssert(!Self.hasKeyboardFocus(app.findWhatEditBox)) } func testSessionSendKeys_scopedModifiers() throws { try app.findWhatEditBox.click() - try app.session.sendKeys(Keys.shift(Keys.a) + Keys.a) + try app.session.sendKeys(.sequence(.shift(.a), .a)) XCTAssertEqual(try app.findWhatEditBox.text, "Aa") } func testSessionSendKeys_autoReleasedModifiers() throws { try app.findWhatEditBox.click() - try app.session.sendKeys(Keys.shiftModifier + Keys.a) - try app.session.sendKeys(Keys.a) + try app.session.sendKeys(.sequence(.shiftModifier, .a)) + try app.session.sendKeys(.a) XCTAssertEqual(try app.findWhatEditBox.text, "Aa") } func testSessionSendKeys_stickyModifiers() throws { try app.findWhatEditBox.click() - try app.session.sendKeys(Keys.shiftModifier + Keys.a, releaseModifiers: false) - try app.session.sendKeys(Keys.a) + try app.session.sendKeys(.sequence(.shiftModifier, .a), releaseModifiers: false) + try app.session.sendKeys(.a) try app.session.sendKeys(.releaseModifiers) - try app.session.sendKeys(Keys.a) + try app.session.sendKeys(.a) XCTAssertEqual(try app.findWhatEditBox.text, "AAa") } func testElementSendKeys_scopedModifiers() throws { - try app.findWhatEditBox.sendKeys(Keys.shift(Keys.a) + Keys.a) + try app.findWhatEditBox.sendKeys(.sequence(.shift(.a), .a)) XCTAssertEqual(try app.findWhatEditBox.text, "Aa") } func testElementSendKeys_autoReleasedModifiers() throws { - try app.findWhatEditBox.sendKeys(Keys.shiftModifier + Keys.a) - try app.findWhatEditBox.sendKeys(Keys.a) + try app.findWhatEditBox.sendKeys(.sequence(.shiftModifier, .a)) + try app.findWhatEditBox.sendKeys(.a) XCTAssertEqual(try app.findWhatEditBox.text, "Aa") }