mirror of
https://github.com/appium/appium.git
synced 2026-02-09 11:18:51 -06:00
Merge pull request #2939 from bootstraponline/scroll_into_view
Add scrollIntoView
This commit is contained in:
37
docs/en/uiautomator_uiselector.md
Normal file
37
docs/en/uiautomator_uiselector.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# uiautomator UiSelector
|
||||
|
||||
Appium enables searching using [UiSelectors](http://developer.android.com/tools/help/uiautomator/UiSelector.html).
|
||||
[UiScrollable](http://developer.android.com/tools/help/uiautomator/UiScrollable.html)
|
||||
is also supported.
|
||||
|
||||
Note that the index selector is unreliable so prefer instance instead. The
|
||||
following examples are written against the api demos apk using Ruby.
|
||||
|
||||
|
||||
Find the first textview.
|
||||
|
||||
```ruby
|
||||
first_textview = find_element(:uiautomator, 'new UiSelector().className("android.widget.TextView").instance(0)');
|
||||
```
|
||||
|
||||
Find the first element by text.
|
||||
|
||||
```ruby
|
||||
first_text = find_element(:uiautomator, 'new UiSelector().text("Animation")')
|
||||
first_text.text # "Animation"
|
||||
```
|
||||
|
||||
Find the first scrollable element, then find a TextView with the text "Tabs".
|
||||
The "Tabs" element will be scrolled into view.
|
||||
|
||||
```ruby
|
||||
element = find_element(:uiautomator, 'new UiScrollable(new UiSelector().scrollable(true).instance(0)).getChildByText(new UiSelector().className("android.widget.TextView"), "Tabs")')
|
||||
```
|
||||
|
||||
As a special case, scrollIntoView returns the element that is scrolled into view.
|
||||
scrollIntoView allows scrolling to any UiSelector.
|
||||
|
||||
```ruby
|
||||
element = find_element(:uiautomator, 'new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("WebView").instance(0));')
|
||||
element.text # "WebView"
|
||||
```
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.appium.android.bootstrap.utils;
|
||||
|
||||
import com.android.uiautomator.core.UiSelector;
|
||||
import io.appium.android.bootstrap.Logger;
|
||||
import io.appium.android.bootstrap.exceptions.UiSelectorSyntaxException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -77,8 +78,10 @@ public class UiAutomatorParser {
|
||||
|
||||
statement = text.substring(0, index);
|
||||
if (UiScrollableParser.isUiScrollable(statement)) {
|
||||
Logger.debug("Parsing scrollable: " + statement);
|
||||
selectors.add(scrollableParser.parse(statement));
|
||||
} else {
|
||||
Logger.debug("Parsing selector: " + statement);
|
||||
selectors.add(selectorParser.parse(statement));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.appium.android.bootstrap.utils;
|
||||
import com.android.uiautomator.core.UiObject;
|
||||
import com.android.uiautomator.core.UiScrollable;
|
||||
import com.android.uiautomator.core.UiSelector;
|
||||
import io.appium.android.bootstrap.Logger;
|
||||
import io.appium.android.bootstrap.exceptions.UiSelectorSyntaxException;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -183,6 +184,12 @@ public class UiScrollableParser {
|
||||
}
|
||||
|
||||
private void applyArgsToMethod(Method method, ArrayList<String> arguments) throws UiSelectorSyntaxException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String arg : arguments) {
|
||||
sb.append(arg + ", ");
|
||||
}
|
||||
Logger.debug("UiScrollable invoking method: " + method + " args: " + sb.toString());
|
||||
|
||||
if (method.getGenericReturnType() == UiScrollable.class && returnedUiObject) {
|
||||
throw new UiSelectorSyntaxException("Cannot call UiScrollable method \"" + method.getName() + "\" on a UiObject instance");
|
||||
}
|
||||
@@ -208,17 +215,31 @@ public class UiScrollableParser {
|
||||
convertedArgs.add(coerceArgToType(parameterTypes[i], arguments.get(i)));
|
||||
}
|
||||
|
||||
if (method.getGenericReturnType() == UiScrollable.class) {
|
||||
String methodName = method.getName();
|
||||
Logger.debug("Method name: " + methodName);
|
||||
boolean scrollIntoView = methodName.contentEquals("scrollIntoView");
|
||||
|
||||
if (method.getGenericReturnType() == UiScrollable.class || scrollIntoView) {
|
||||
if (convertedArgs.size() > 1) {
|
||||
throw new UiSelectorSyntaxException("No UiScrollable method that returns type UiScrollable takes more than 1 argument");
|
||||
}
|
||||
try {
|
||||
scrollable = (UiScrollable)method.invoke(scrollable, convertedArgs.get(0));
|
||||
if (scrollIntoView) {
|
||||
Logger.debug("Setting uiObject for scrollIntoView");
|
||||
UiSelector arg = (UiSelector) convertedArgs.get(0);
|
||||
returnedUiObject = true;
|
||||
uiObject = new UiObject(arg);
|
||||
Logger.debug("Invoking method: " + method + " with: " + uiObject);
|
||||
method.invoke(scrollable, uiObject);
|
||||
Logger.debug("Invoke complete.");
|
||||
} else {
|
||||
scrollable = (UiScrollable)method.invoke(scrollable, convertedArgs.get(0));
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
throw new UiSelectorSyntaxException("problem using reflection to call this method");
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
Logger.error(e.getCause().toString()); // we're only interested in the cause. InvocationTarget wraps the underlying problem.
|
||||
throw new UiSelectorSyntaxException("problem using reflection to call this method");
|
||||
}
|
||||
}
|
||||
@@ -259,6 +280,7 @@ public class UiScrollableParser {
|
||||
}
|
||||
|
||||
private Object coerceArgToType(Type type, String argument) throws UiSelectorSyntaxException {
|
||||
Logger.debug("UiScrollable coerce type: " + type + " arg: " + argument);
|
||||
if (type == boolean.class) {
|
||||
if (argument.equals("true")) {
|
||||
return true;
|
||||
@@ -288,7 +310,7 @@ public class UiScrollableParser {
|
||||
}
|
||||
}
|
||||
|
||||
if (type == UiSelector.class) {
|
||||
if (type == UiSelector.class || type == UiObject.class) {
|
||||
UiSelectorParser parser = new UiSelectorParser();
|
||||
return parser.parse(argument);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.appium.android.bootstrap.utils;
|
||||
|
||||
import com.android.uiautomator.core.UiSelector;
|
||||
import io.appium.android.bootstrap.Logger;
|
||||
import io.appium.android.bootstrap.exceptions.UiSelectorSyntaxException;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -151,6 +152,7 @@ public class UiSelectorParser {
|
||||
}
|
||||
|
||||
private Object coerceArgToType(Type type, String argument) throws UiSelectorSyntaxException {
|
||||
Logger.debug("UiSelector coerce type: " + type + " arg: " + argument);
|
||||
if (type == boolean.class) {
|
||||
if (argument.equals("true")) {
|
||||
return true;
|
||||
|
||||
@@ -153,6 +153,13 @@ describe("apidemo - find elements - by uiautomator", function () {
|
||||
text.should.equal("Views");
|
||||
}).nodeify(done);
|
||||
});
|
||||
it('should allow UiScrollable scrollIntoView', function (done) {
|
||||
driver.elementByAndroidUIAutomator('new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("Views").instance(0));')
|
||||
.text()
|
||||
.then(function (text) {
|
||||
text.should.equal("Views");
|
||||
}).nodeify(done);
|
||||
});
|
||||
it('should error reasonably if a UiScrollable does not return a UiObject', function (done) {
|
||||
driver.elementByAndroidUIAutomator('new UiScrollable(new UiSelector().scrollable(true).instance(0)).setMaxSearchSwipes(10)')
|
||||
.should.be.rejectedWith(/status: 9/)
|
||||
|
||||
Reference in New Issue
Block a user