Skip to content

Commit

Permalink
Fix: fallback to idb if XCTest driver can't find focused text field (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitry-zaitsev authored Jan 18, 2023
1 parent 54671a0 commit 0c82dab
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ class InputTextRouteHandler : RouteHandler {
let element = xcuiApplication
.descendants(matching: .any)
.element(matching: NSPredicate(format: "hasKeyboardFocus == true"))
if (element.exists) {
element.typeText(requestBody.text)

if (!element.exists) {
return HTTPResponse(statusCode: .notFound)
}

element.typeText(requestBody.text)

return HTTPResponse(statusCode: .ok)
}

Expand Down
7 changes: 7 additions & 0 deletions maestro-ios/src/main/java/ios/LocalIOSDevice.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ class LocalIOSDevice(

override fun input(text: String): Result<Unit, Throwable> {
return xcTestDevice.input(text)
.recoverIf(
{ it is XCTestIOSDevice.InputFieldNotFound },
{
idbIOSDevice.input(text)
.getOrThrow()
}
)
}

override fun install(stream: InputStream): Result<Unit, Throwable> {
Expand Down
12 changes: 11 additions & 1 deletion maestro-ios/src/main/java/ios/idb/IdbIOSDevice.kt
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,17 @@ class IdbIOSDevice(
override fun input(
text: String,
): Result<Unit, Throwable> {
error("Not supported")
return runCatching {
val responseObserver = BlockingStreamObserver<Idb.HIDResponse>()
val stream = asyncStub.hid(responseObserver)

TextInputUtil.textToListOfEvents(text)
.forEach {
stream.onNext(it)
Thread.sleep(75)
}
stream.onCompleted()
}
}

override fun install(stream: InputStream): Result<Unit, Throwable> {
Expand Down
146 changes: 146 additions & 0 deletions maestro-ios/src/main/java/ios/idb/TextInputUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ import idb.hIDEvent

internal object TextInputUtil {

/*
* Implementation is borrowed from Python idb client
*/

fun textToListOfEvents(text: String): List<HIDEvent> {
return text.toCharArray()
.toList()
.mapNotNull {
keyMap[it]
}
.flatten()
}

fun pressWithDuration(
action: HIDEvent.HIDPressAction,
): List<HIDEvent> {
Expand Down Expand Up @@ -55,10 +68,143 @@ internal object TextInputUtil {
)
}

private fun keyDownEvent(keycode: Long): HIDEvent {
return toEvent(
HIDEventKt.hIDPress {
this.action = HIDEventKt.hIDPressAction {
this.key = HIDEventKt.hIDKey {
this.keycode = keycode
}
}
this.direction = HIDEvent.HIDDirection.DOWN
}
)
}

private fun keyUpEvent(keycode: Long): HIDEvent {
return toEvent(
HIDEventKt.hIDPress {
this.action = HIDEventKt.hIDPressAction {
this.key = HIDEventKt.hIDKey {
this.keycode = keycode
}
}
this.direction = HIDEvent.HIDDirection.UP
}
)
}

private fun keyPressShiftedToEvents(keycode: Long): List<HIDEvent> {
return listOf(
keyDownEvent(225),
keyDownEvent(keycode),
keyUpEvent(keycode),
keyUpEvent(225),
)
}

private fun toEvent(press: HIDPress): HIDEvent {
return hIDEvent {
this.press = press
}
}

private val keyMap = mapOf(
'a' to keyPressToEvents(4),
'b' to keyPressToEvents(5),
'c' to keyPressToEvents(6),
'd' to keyPressToEvents(7),
'e' to keyPressToEvents(8),
'f' to keyPressToEvents(9),
'g' to keyPressToEvents(10),
'h' to keyPressToEvents(11),
'i' to keyPressToEvents(12),
'j' to keyPressToEvents(13),
'k' to keyPressToEvents(14),
'l' to keyPressToEvents(15),
'm' to keyPressToEvents(16),
'n' to keyPressToEvents(17),
'o' to keyPressToEvents(18),
'p' to keyPressToEvents(19),
'q' to keyPressToEvents(20),
'r' to keyPressToEvents(21),
's' to keyPressToEvents(22),
't' to keyPressToEvents(23),
'u' to keyPressToEvents(24),
'v' to keyPressToEvents(25),
'w' to keyPressToEvents(26),
'x' to keyPressToEvents(27),
'y' to keyPressToEvents(28),
'z' to keyPressToEvents(29),
'A' to keyPressShiftedToEvents(4),
'B' to keyPressShiftedToEvents(5),
'C' to keyPressShiftedToEvents(6),
'D' to keyPressShiftedToEvents(7),
'E' to keyPressShiftedToEvents(8),
'F' to keyPressShiftedToEvents(9),
'G' to keyPressShiftedToEvents(10),
'H' to keyPressShiftedToEvents(11),
'I' to keyPressShiftedToEvents(12),
'J' to keyPressShiftedToEvents(13),
'K' to keyPressShiftedToEvents(14),
'L' to keyPressShiftedToEvents(15),
'M' to keyPressShiftedToEvents(16),
'N' to keyPressShiftedToEvents(17),
'O' to keyPressShiftedToEvents(18),
'P' to keyPressShiftedToEvents(19),
'Q' to keyPressShiftedToEvents(20),
'R' to keyPressShiftedToEvents(21),
'S' to keyPressShiftedToEvents(22),
'T' to keyPressShiftedToEvents(23),
'U' to keyPressShiftedToEvents(24),
'V' to keyPressShiftedToEvents(25),
'W' to keyPressShiftedToEvents(26),
'X' to keyPressShiftedToEvents(27),
'Y' to keyPressShiftedToEvents(28),
'Z' to keyPressShiftedToEvents(29),
'1' to keyPressToEvents(30),
'2' to keyPressToEvents(31),
'3' to keyPressToEvents(32),
'4' to keyPressToEvents(33),
'5' to keyPressToEvents(34),
'6' to keyPressToEvents(35),
'7' to keyPressToEvents(36),
'8' to keyPressToEvents(37),
'9' to keyPressToEvents(38),
'0' to keyPressToEvents(39),
'\n' to keyPressToEvents(40),
';' to keyPressToEvents(51),
'=' to keyPressToEvents(46),
',' to keyPressToEvents(54),
'-' to keyPressToEvents(45),
'.' to keyPressToEvents(55),
'/' to keyPressToEvents(56),
'`' to keyPressToEvents(53),
'[' to keyPressToEvents(47),
'\\' to keyPressToEvents(49),
']' to keyPressToEvents(48),
'\'' to keyPressToEvents(52),
' ' to keyPressToEvents(44),
'!' to keyPressShiftedToEvents(30),
'@' to keyPressShiftedToEvents(31),
'#' to keyPressShiftedToEvents(32),
'$' to keyPressShiftedToEvents(33),
'%' to keyPressShiftedToEvents(34),
'^' to keyPressShiftedToEvents(35),
'&' to keyPressShiftedToEvents(36),
'*' to keyPressShiftedToEvents(37),
'(' to keyPressShiftedToEvents(38),
')' to keyPressShiftedToEvents(39),
'_' to keyPressShiftedToEvents(45),
'+' to keyPressShiftedToEvents(46),
'{' to keyPressShiftedToEvents(47),
'}' to keyPressShiftedToEvents(48),
':' to keyPressShiftedToEvents(51),
'"' to keyPressShiftedToEvents(52),
'|' to keyPressShiftedToEvents(49),
'<' to keyPressShiftedToEvents(54),
'>' to keyPressShiftedToEvents(55),
'?' to keyPressShiftedToEvents(56),
'~' to keyPressShiftedToEvents(53),
)
}
11 changes: 9 additions & 2 deletions maestro-ios/src/main/java/ios/xctest/XCTestIOSDevice.kt
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class XCTestIOSDevice(
endX = xEnd,
endY = yEnd,
velocity = velocity,
).use { }
).use {}
}
}

Expand All @@ -122,7 +122,13 @@ class XCTestIOSDevice(
client.inputText(
appId = appId,
text = text,
).use { }
).use {
if (!it.isSuccessful) {
if (it.code == 404) {
throw InputFieldNotFound()
}
}
}
}
}

Expand Down Expand Up @@ -212,6 +218,7 @@ class XCTestIOSDevice(
}

class IllegalArgumentSnapshotFailure : Throwable("Failed to capture view hierarchy due to kAXErrorIllegalArgument")
class InputFieldNotFound : Throwable("Unable to find focused input field")
class UnknownFailure(val errorResponse: String) : Throwable()

companion object {
Expand Down
Binary file not shown.
Binary file not shown.

0 comments on commit 0c82dab

Please sign in to comment.