Hi all,
Apologies if this is a known bug, I’ve been digging into Ruby and Appium in the past week in an effort to setup Android automation and I came across an issue with both the scroll_to and scroll_to_exact Ruby methods. Disclaimer, I learned Ruby just this week but this does indeed look like a valid bug from what I can tell.
Issue 1
The issue is that these methods attempt to scroll by 3 different scenarios. They first scroll by text, then description, then resourceId. If I was to enter a valid resource id of “android:id/text1” then it would first scroll by text, then description, then eventually resourceId and then throw the following error:
Selenium::WebDriver::Error::UnknownCommandError: The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource.
from /usr/local/lib/ruby/gems/2.4.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/remote/response.rb:69:in `assert_ok’
The Easy Fix
The reason why this happens is because there is a semicolon in the following code:
scroll_uiselector(resource_id(text, "new UiSelector().resourceId(#{text});"), scrollable_index)
When I reverse engineer both the scroll_to methods in arc, I find that if I remove the semicolon then I’m able to successfully scroll to an element by its resourceId.
The code referencing the semicolon locations:
and
Repro Steps:
- Connect to emulator via arc for easy debugging then paste the following commands:
- scrollable_index = 0
- resource_id = “android:id/text1” # Note, use a valid resource-id, this is just an example one
- resource_id = %(“#{resource_id}”)
- content = “new UiSelector().resourceId(#{resource_id});”
- find_element :uiautomator, “new UiScrollable(new UiSelector().scrollable(true).instance(#{scrollable_index})).scrollIntoView(#{content}.instance(0))”
- An error of “Selenium::WebDriver::Error::UnknownCommandError” will be thrown
- Hit the up arrow twice and remove the semicolon then hit Enter
- Hit the up arrow twice and hit Enter (it should be on the find_element statement)
- Emulator correctly scrolls to the element
Issue 2
When scrolling by text, I notice that the scrolling will first go to the element, but then scroll away from it (most likely because the scroll by text gets executed and then the scroll by description does). The issue is that when you attempt to scroll to an element and then click on it, you are left with an exception being thrown because the element is no longer in view since the second scrolling script (description) executed. When I break out the 3 separate args script statements into their own action, then the scrolling scrolls correctly to the element and stays there.
Thus I believe it would be more ideal to break out the 3 separate arg script statements into their own methods imo.
Here is some sample code of the methods I stubbed out in case anyone else in the community would like to benefit from this:
def scroll_to_element_by_text(text, scrollable_index = 0)
text = %("#{text}")
content = "new UiSelector().textContains(#{text})"
find_element :uiautomator, "new UiScrollable(new UiSelector().scrollable(true).instance(#{scrollable_index})).scrollIntoView(#{content}.instance(0));"
end
def scroll_to_element_by_exact_text(exact_text, scrollable_index = 0)
exact_text = %("#{exact_text}")
content = "new UiSelector().text(#{exact_text})"
find_element :uiautomator, "new UiScrollable(new UiSelector().scrollable(true).instance(#{scrollable_index})).scrollIntoView(#{content}.instance(0));"
end
def scroll_to_element_by_content_description(content_desc_text, scrollable_index = 0)
content_desc_text = %("#{content_desc_text}")
content = "new UiSelector().description(#{content_desc_text})"
find_element :uiautomator, "new UiScrollable(new UiSelector().scrollable(true).instance(#{scrollable_index})).scrollIntoView(#{content}.instance(0));"
end
def scroll_to_element_by_resource_id(resource_id, scrollable_index = 0)
# Pass the full resource-id into the param e.g.:
# "com.example.Test:id/enter"
resource_id = %("#{resource_id}")
content = "new UiSelector().resourceId(#{resource_id})"
find_element :uiautomator, "new UiScrollable(new UiSelector().scrollable(true).instance(#{scrollable_index})).scrollIntoView(#{content}.instance(0));"
end