Skip to content

fix: defer coordinate parsing for swipe commands with script variables#3026

Open
vazidmansuri005 wants to merge 8 commits intomobile-dev-inc:mainfrom
vazidmansuri005:fix/swipe-evaluate-scripts
Open

fix: defer coordinate parsing for swipe commands with script variables#3026
vazidmansuri005 wants to merge 8 commits intomobile-dev-inc:mainfrom
vazidmansuri005:fix/swipe-evaluate-scripts

Conversation

@vazidmansuri005
Copy link
Copy Markdown

@vazidmansuri005 vazidmansuri005 commented Mar 4, 2026

Summary

Fixes #2149 — The output of relativePoint() JavaScript function can be used with tapOn (as the point parameter) but cannot be used with swipe (as start/end parameters). This is because swipe coordinate strings are parsed to Point objects during YAML deserialization, before evaluateScripts() resolves ${...} variables, causing a NumberFormatException.

Root cause: YamlFluentCommand.toCommand() calls start.split(",").map { it.trim().toInt() } at parse time, which throws on ${output.startPoint}.

Fix: Defer coordinate parsing to execution time when script variables are detected:

  1. YamlSwipeDeserializer — Detects ${ in start/end values and skips format validation, returning a raw YamlCoordinateSwipe
  2. YamlFluentCommand — When ${ is detected, stores raw strings in startPointStr/endPointStr instead of parsing to Point
  3. SwipeCommand — New startPointStr/endPointStr fields that pass through evaluateScripts()
  4. Orchestra — Parses the evaluated strings at execution time, supporting both absolute (x,y) and relative (x%,y%) coordinates

Before (broken)

- evalScript: ${output.startPoint = relativePoint(0.5, 0.8)}
- swipe:
    start: ${output.startPoint}  # NumberFormatException at parse time
    end: 50%,12%

After (works)

- evalScript: ${output.startPoint = relativePoint(0.5, 0.8)}
- swipe:
    start: ${output.startPoint}  # Deferred until runtime
    end: 50%,12%

Test plan

  • Existing swipe tests pass (./gradlew :maestro-orchestra:test)
  • Builds cleanly (compileKotlin for both maestro-orchestra and maestro-orchestra-models)
  • Unit test: swipe with script variables defers coordinate parsing — verifies YAML with ${...} variables produces SwipeCommand with startPointStr/endPointStr
  • Integration test: Case 139 - Swipe with script variables — verifies full execution path through Orchestra with FakeDriver
  • Non-variable swipe commands (direction, absolute coords, relative coords) unaffected
  • Manual E2E: iPhone 16 simulator (iOS 18.5) — relativePoint() and absolute coordinate variables both execute successfully via installLocally.sh

When using relativePoint output from other commands as swipe coordinates
(e.g., start: "${output.startPoint}"), the values were being parsed to
Point objects during YAML deserialization, before evaluateScripts() had
a chance to resolve the variables. This caused NumberFormatException.

The fix defers coordinate parsing to execution time when script variables
(${...}) are detected:
- YamlSwipeDeserializer skips validation for variable-containing values
- YamlFluentCommand stores raw strings instead of parsing to Point
- SwipeCommand gets new startPointStr/endPointStr fields that go through
  evaluateScripts()
- Orchestra parses the evaluated strings at execution time

Fixes mobile-dev-inc#2149
@Fishbowler
Copy link
Copy Markdown
Contributor

Should this be draft until your testing is complete?

This also feels like this would be worth both an automated test.

I think your examples are incorrect (or possibly my memory) - I don't think assertVisible sets those values.

Verifies that YAML swipe commands containing ${...} variables are parsed
with deferred coordinate resolution (startPointStr/endPointStr) instead
of being immediately parsed to Point objects.
@vazidmansuri005
Copy link
Copy Markdown
Author

Thanks for the review @Fishbowler — all three points addressed:

  1. Examples fixed — You were right, assertVisible does not set output.startPoint/output.endPoint. The actual use case from issue Output of relativePoint cannot be used with swipe command start and end parameters #2149 is relativePoint() via evalScript. Updated the PR description with the correct examples.

  2. Automated test added — New test swipe with script variables defers coordinate parsing in YamlCommandReaderTest verifies that YAML swipe commands containing ${...} variables produce SwipeCommand with deferred startPointStr/endPointStr instead of being immediately parsed to Point objects.

  3. Not marking as draft — The automated test is passing and manual testing is the only remaining item. I do not have a Maestro dev environment set up to run E2E flows, but the unit test covers the YAML parsing → command generation path which is where the bug lives.

@Fishbowler
Copy link
Copy Markdown
Contributor

A Maestro dev environment is incredibly similar to a normal Maestro environment. Just run ./installLocally.sh and use Maestro as you typically would.

You've added a YAML parsing test that proves you can read the string from the YAML, but nothing to say that the point is then honoured - maybe a test elsewhere could go a little further?

End-to-end test that verifies the full execution path:
evalScript sets output variables → swipe command defers parsing →
evaluateScripts() resolves variables → Orchestra parses evaluated
strings and executes the swipe with correct coordinates.
@vazidmansuri005
Copy link
Copy Markdown
Author

Good point — added an integration test (Case 139 - Swipe with script variables) in IntegrationTest.kt that verifies the full execution path:

  1. evalScript sets output.startPoint and output.endPoint as string variables
  2. Swipe command defers parsing (stores startPointStr/endPointStr)
  3. evaluateScripts() resolves ${output.startPoint}"100,500"
  4. Orchestra parses the evaluated string and executes the swipe
  5. FakeDriver asserts it received Event.Swipe(start = Point(100, 500), End = Point(100, 200), durationMs = 3000)

Thanks for the suggestion on installLocally.sh — will look into that for manual testing as well.

@vazidmansuri005
Copy link
Copy Markdown
Author

Ran full E2E on iPhone 16 simulator (iOS 18.5) using locally built Maestro via installLocally.sh:

Test 1 — relativePoint() variables (exact issue #2149 scenario):

Launch app "com.apple.Preferences"... COMPLETED
Run ${output.startPoint = relativePoint(0.5, 0.8)}... COMPLETED
Run ${output.endPoint = relativePoint(0.5, 0.3)}... COMPLETED
Swipe from (${output.startPoint}) to (${output.endPoint}) in 500 ms... COMPLETED

Test 2 — absolute coordinate variables:

Launch app "com.apple.Preferences"... COMPLETED
Run ${output.startPoint = '200,600'}... COMPLETED
Run ${output.endPoint = '200,200'}... COMPLETED
Swipe from (${output.startPoint}) to (${output.endPoint}) in 500 ms... COMPLETED

Both pass. Marking manual testing as complete in the PR description.

@vazidmansuri005
Copy link
Copy Markdown
Author

Hey @Fishbowler @amanjeetsingh150, could you please review this PR when you get a chance? Thanks!

@Fishbowler Fishbowler self-assigned this Mar 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Output of relativePoint cannot be used with swipe command start and end parameters

2 participants