Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions e2e/workspaces/demo_app/swipe_from_to.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
appId: com.example.example
tags:
- passing
---
- launchApp:
clearState: true
- tapOn:
text: Swipe Test (from_to)
- assertVisible:
text: "Selected: Item 1"
- swipe:
from:
id: "scrollWheel"
to: 50%, 47% # x, y
- assertVisible:
text: "Selected: Item 2"
- swipe:
from:
id: "scrollWheel"
to: 50%, 47% # x, y
- assertVisible:
text: "Selected: Item 3"
- swipe:
from:
id: "scrollWheel"
to: 50%, 47% # x, y
- assertVisible:
text: "Selected: Item 4"
- swipe:
from:
id: "scrollWheel"
to: 50%, 47% # x, y
- assertVisible:
text: "Selected: Item 5"
- swipe:
from:
id: "scrollWheel"
to: 50%, 58% # x, y
- assertVisible:
text: "Selected: Item 4"
- swipe:
from:
id: "scrollWheel"
to: 50%, 58% # x, y
- assertVisible:
text: "Selected: Item 3"
- swipe:
from:
id: "scrollWheel"
to: 50%, 58% # x, y
- assertVisible:
text: "Selected: Item 2"
- swipe:
from:
id: "scrollWheel"
to: 50%, 58% # x, y
- assertVisible:
text: "Selected: Item 1"
24 changes: 24 additions & 0 deletions maestro-client/src/main/java/maestro/Maestro.kt
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,30 @@ class Maestro(
waitForAppToSettle(waitToSettleTimeoutMs = waitToSettleTimeoutMs)
}

fun swipeFromElementToPoint(
uiElement: UiElement,
endPoint: Point? = null,
endRelative: String? = null,
duration: Long = 300L
) {
val deviceInfo = deviceInfo()

when {
endPoint != null -> driver.swipe(uiElement.bounds.center(), endPoint, duration)
endRelative != null -> {
val endPoints = endRelative.replace("%", "")
.split(",").map { it.trim().toInt() }
val endX = deviceInfo.widthGrid * endPoints[0] / 100
val endY = deviceInfo.heightGrid * endPoints[1] / 100
val end = Point(endX, endY)

driver.swipe(uiElement.bounds.center(), end, duration)
}
}

waitForAppToSettle ()
}

fun scrollVertical() {
LOGGER.info("Scrolling vertically")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ data class SwipeCommand(
startRelative != null && endRelative != null -> {
"Swipe from ($startRelative) to ($endRelative) in $duration ms"
}
elementSelector != null && (endPoint != null || endRelative != null) -> {
"Swiping from ${elementSelector.description()} to ${endPoint ?: endRelative} coordinates"
}
else -> "Invalid input to swipe command"
}

Expand Down
16 changes: 15 additions & 1 deletion maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1374,7 +1374,21 @@ class Orchestra(
duration = command.duration,
waitToSettleTimeoutMs = command.waitToSettleTimeoutMs
)

elementSelector != null && (end != null || endRelative != null) -> {
val uiElement = findElement(elementSelector, optional = command.optional)
if (end != null)
maestro.swipeFromElementToPoint(
uiElement = uiElement.element,
endPoint = end,
duration = command.duration
)
else
maestro.swipeFromElementToPoint(
uiElement = uiElement.element,
endRelative = endRelative,
duration = command.duration
)
}
else -> error("Illegal arguments for swiping")
}
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,7 @@ data class YamlFluentCommand(
}

is YamlSwipeElement -> return swipeElementCommand(swipe)
is YamlRelativeCoordinateSwipeElement -> return swipeRelativeCoordinatesSwipeElementCommand(swipe)
else -> {
throw IllegalStateException(
"Provide swipe direction UP, DOWN, RIGHT OR LEFT or by giving explicit " +
Expand All @@ -861,6 +862,18 @@ data class YamlFluentCommand(
)
}

private fun swipeRelativeCoordinatesSwipeElementCommand(swipeElement: YamlRelativeCoordinateSwipeElement): MaestroCommand {
return MaestroCommand(
swipeCommand = SwipeCommand(
elementSelector = toElementSelector(swipeElement.from),
endRelative = swipeElement.to,
duration = swipeElement.duration,
label = swipeElement.label,
optional = swipeElement.optional,
)
)
}

private fun toElementSelector(selectorUnion: YamlElementSelectorUnion): ElementSelector {
return if (selectorUnion is StringElementSelector) {
ElementSelector(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ data class YamlSwipeElement(
override val waitToSettleTimeoutMs: Int? = null,
) : YamlSwipe

data class YamlRelativeCoordinateSwipeElement(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add this case in YamlCommandReaderTest?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what that means. Can you clarify?

val from: YamlElementSelectorUnion,
val to: String,
override val duration: Long = DEFAULT_DURATION_IN_MILLIS,
override val label: String? = null,
override val optional: Boolean,
override val waitToSettleTimeoutMs: Int? = null,
) : YamlSwipe

private const val DEFAULT_DURATION_IN_MILLIS = 400L

class YamlSwipeDeserializer : JsonDeserializer<YamlSwipe>() {
Expand All @@ -68,7 +77,7 @@ class YamlSwipeDeserializer : JsonDeserializer<YamlSwipe>() {
val optional = getOptional(root)
val waitToSettleTimeoutMs = getWaitToSettleTimeoutMs(root)
when {
input.contains("start") || input.contains("end") -> {
input.contains("start") && input.contains("end") -> {
check(root.get("direction") == null) { "You cannot provide direction with start/end swipe." }
check(root.get("start") != null && root.get("end") != null) {
"You need to provide both start and end coordinates, to swipe with coordinates"
Expand All @@ -94,18 +103,53 @@ class YamlSwipeDeserializer : JsonDeserializer<YamlSwipe>() {
mapper.convertValue(root, YamlSwipeElement::class.java)
}
}
input.contains("from") && input.contains("to") -> {
return resolveRelativeCoordinateSwipeElement(root, duration, label, optional, mapper)
}
else -> {
throw IllegalArgumentException(
"Swipe command takes either: \n" +
"\t1. direction: Direction based swipe with: \"RIGHT\", \"LEFT\", \"UP\", or \"DOWN\" or \n" +
"\t2. start and end: Coordinates based swipe with: \"start\" and \"end\" coordinates \n" +
"\t3. direction and element to swipe directionally on element\n" +
"It seems you provided invalid input with: $input"
"\t1. direction: Direction based swipe with: \"RIGHT\", \"LEFT\", \"UP\", or \"DOWN\" or \n" +
"\t2. start and end: Coordinates based swipe with: \"start\" and \"end\" coordinates \n" +
"\t3. direction and element to swipe directionally on element\n" +
"\t4. from and direction/to: more precise swipe from one point to another\n" +

"It seems you provided invalid input with: $input"
)
}
}
}

private fun resolveRelativeCoordinateSwipeElement(
root: TreeNode,
duration: Long,
label: String?,
optional: Boolean,
mapper: ObjectMapper
): YamlRelativeCoordinateSwipeElement {
val from = mapper.convertValue(root.path("from"), YamlElementSelectorUnion::class.java)
val to = root.path("to").toString().replace("\"", "")

val isRelative = to.contains("%")

if (isRelative) {
val endPoints = to
.replace("%", "")
.split(",")
.map { it.trim().toInt() }
check(endPoints[0] in 0..100 && endPoints[1] in 0..100) {
"Invalid end point: $to should be between 0 to 100 when using relative coordinates."
}
} else {
val endPoints = to
.split(",")
.map { it.trim().toInt() }
check(endPoints.size == 2) { "Invalid format for absolute coordinates: $to" }
}

return YamlRelativeCoordinateSwipeElement(from, to, duration, label, optional)
}

private fun resolveCoordinateSwipe(
root: TreeNode,
duration: Long,
Expand Down
Loading