Skip to content

Commit

Permalink
Introduce Keys.sequence (#164)
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle authored Nov 13, 2024
1 parent eda2f0d commit 24f495b
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 57 deletions.
7 changes: 0 additions & 7 deletions Sources/WebDriver/Element.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
45 changes: 20 additions & 25 deletions Sources/WebDriver/Keys.swift
Original file line number Diff line number Diff line change
@@ -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")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -210,4 +205,4 @@ extension Keys {
default: return false
}
}
}
}
9 changes: 0 additions & 9 deletions Sources/WebDriver/Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions Tests/UnitTests/APIToRequestMappingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions Tests/WinAppDriverTests/MSInfo32App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
22 changes: 11 additions & 11 deletions Tests/WinAppDriverTests/RequestsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
}

Expand Down

0 comments on commit 24f495b

Please sign in to comment.