mirror of
https://github.com/appium/appium.git
synced 2026-05-09 04:19:27 -05:00
Merge pull request #849 from mutualmobile/android_scrollTo_support
Added scrollTo support for Android.
This commit is contained in:
@@ -4,6 +4,7 @@ import io.appium.android.bootstrap.exceptions.AndroidCommandException;
|
||||
import io.appium.android.bootstrap.handler.Clear;
|
||||
import io.appium.android.bootstrap.handler.Click;
|
||||
import io.appium.android.bootstrap.handler.Find;
|
||||
import io.appium.android.bootstrap.handler.ScrollTo;
|
||||
import io.appium.android.bootstrap.handler.Flick;
|
||||
import io.appium.android.bootstrap.handler.GetAttribute;
|
||||
import io.appium.android.bootstrap.handler.GetDeviceSize;
|
||||
@@ -43,6 +44,7 @@ class AndroidCommandExecutor {
|
||||
map.put("getName", new GetName());
|
||||
map.put("getAttribute", new GetAttribute());
|
||||
map.put("getDeviceSize", new GetDeviceSize());
|
||||
map.put("scrollTo", new ScrollTo());
|
||||
map.put("find", new Find());
|
||||
map.put("getLocation", new GetLocation());
|
||||
map.put("wake", new Wake());
|
||||
|
||||
@@ -27,6 +27,10 @@ public class AndroidElement {
|
||||
el = uiObj;
|
||||
}
|
||||
|
||||
public UiObject getUiObject() {
|
||||
return this.el;
|
||||
}
|
||||
|
||||
public void clearText() throws UiObjectNotFoundException {
|
||||
el.clearTextField();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package io.appium.android.bootstrap.handler;
|
||||
|
||||
import com.android.uiautomator.core.UiObject;
|
||||
import com.android.uiautomator.core.UiObjectNotFoundException;
|
||||
import com.android.uiautomator.core.UiScrollable;
|
||||
import com.android.uiautomator.core.UiSelector;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import io.appium.android.bootstrap.AndroidCommand;
|
||||
import io.appium.android.bootstrap.AndroidCommandResult;
|
||||
import io.appium.android.bootstrap.AndroidElement;
|
||||
import io.appium.android.bootstrap.CommandHandler;
|
||||
import io.appium.android.bootstrap.WDStatus;
|
||||
import io.appium.android.bootstrap.exceptions.ElementNotInHashException;
|
||||
|
||||
/**
|
||||
* This handler is used to scroll to elements in the Android UI.
|
||||
*
|
||||
* Based on the element Id of the scrollable, scroll to the object with the text.
|
||||
*
|
||||
*/
|
||||
public class ScrollTo extends CommandHandler {
|
||||
|
||||
/*
|
||||
* @param command The {@link AndroidCommand}
|
||||
*
|
||||
* @return {@link AndroidCommandResult}
|
||||
*
|
||||
* @throws JSONException
|
||||
*
|
||||
* @see io.appium.android.bootstrap.CommandHandler#execute(io.appium.android.
|
||||
* bootstrap.AndroidCommand)
|
||||
*/
|
||||
@Override
|
||||
public AndroidCommandResult execute(final AndroidCommand command)
|
||||
throws JSONException {
|
||||
if (!command.isElementCommand()) {
|
||||
return getErrorResult("A scrollable view is required for this command.");
|
||||
}
|
||||
|
||||
try {
|
||||
Boolean result;
|
||||
final Hashtable<String, Object> params = command.params();
|
||||
String text = params.get("text").toString();
|
||||
|
||||
AndroidElement el = command.getElement();
|
||||
|
||||
final UiScrollable view = new UiScrollable(el.getUiObject().getSelector());
|
||||
|
||||
view.scrollToBeginning(100);
|
||||
view.setMaxSearchSwipes(100);
|
||||
result = view.scrollTextIntoView(text);
|
||||
view.waitForExists(5000);
|
||||
|
||||
// make sure we can get to the item
|
||||
UiObject listViewItem = view.getChildByText(new UiSelector()
|
||||
.className(android.widget.TextView.class.getName()), ""+text+"");
|
||||
|
||||
// We need to make sure that the item exists (visible)
|
||||
if(!(result && listViewItem.exists())) {
|
||||
return getErrorResult("Could not scroll element into view: "+text);
|
||||
}
|
||||
return getSuccessResult(result);
|
||||
} catch (final UiObjectNotFoundException e) {
|
||||
return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT,
|
||||
e.getMessage());
|
||||
} catch (final ElementNotInHashException e) {
|
||||
return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT,
|
||||
e.getMessage());
|
||||
} catch (final NullPointerException e) { // el is null
|
||||
return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT,
|
||||
e.getMessage());
|
||||
} catch (final Exception e) {
|
||||
return new AndroidCommandResult(WDStatus.UNKNOWN_ERROR,
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-2
@@ -812,8 +812,15 @@ Android.prototype.flick = function(startX, startY, endX, endY, touchCount, elId,
|
||||
}
|
||||
};
|
||||
|
||||
Android.prototype.scrollTo = function(elementId, cb) {
|
||||
cb(new NotYetImplementedError(), null);
|
||||
Android.prototype.scrollTo = function(elementId, text, cb) {
|
||||
// instead of the elementId as the element to be scrolled too,
|
||||
// it's the scrollable view to swipe until the uiobject that has the
|
||||
// text is found.
|
||||
var opts = {
|
||||
text: text
|
||||
, elementId: elementId
|
||||
};
|
||||
this.proxy(["element:scrollTo", opts], cb);
|
||||
};
|
||||
|
||||
Android.prototype.shake = function(cb) {
|
||||
|
||||
+8
-2
@@ -471,8 +471,14 @@ exports.mobileSwipe = function(req, res) {
|
||||
};
|
||||
|
||||
exports.mobileScrollTo = function(req, res) {
|
||||
var elementId = req.body.element;
|
||||
req.device.scrollTo(elementId, getResponseHandler(req, res));
|
||||
req.body = _.defaults(req.body, {
|
||||
element: null
|
||||
, text: null
|
||||
});
|
||||
var element = req.body.element
|
||||
, text = req.body.text;
|
||||
|
||||
req.device.scrollTo(element, text, getResponseHandler(req, res));
|
||||
};
|
||||
|
||||
exports.mobileShake = function(req, res) {
|
||||
|
||||
+2
-1
@@ -1509,7 +1509,8 @@ IOS.prototype.flick = function(startX, startY, endX, endY, touchCount, elId, cb)
|
||||
this.proxy(command, cb);
|
||||
};
|
||||
|
||||
IOS.prototype.scrollTo = function(elementId, cb) {
|
||||
IOS.prototype.scrollTo = function(elementId, text, cb) {
|
||||
// we ignore text for iOS, as the element is the one being scrolled too
|
||||
var command = ["au.getElement('", elementId, "').scrollToVisible()"].join('');
|
||||
this.proxy(command, cb);
|
||||
};
|
||||
|
||||
@@ -119,3 +119,36 @@ describeWd('gestures', function(h) {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describeWd('scroll to element', function(h) {
|
||||
it('should bring the element into view', function(done) {
|
||||
h.driver.elementsByTagName('textView', function(err, els) {
|
||||
should.not.exist(err);
|
||||
var el = els[els.length-1];
|
||||
el.click(function(err) {
|
||||
should.not.exist(err);
|
||||
h.driver.elementByTagName('listView', function(err, el) {
|
||||
should.not.exist(err);
|
||||
var scrollOpts = {
|
||||
element: el.value
|
||||
, text: 'Switches'
|
||||
};
|
||||
var next = function() {
|
||||
h.driver.execute("mobile: scrollTo", [scrollOpts], function(err) {
|
||||
should.not.exist(err);
|
||||
h.driver.elementByName('Switches', function(err, el) {
|
||||
should.not.exist(err);
|
||||
el.getAttribute('name', function(err, text) {
|
||||
should.not.exist(err);
|
||||
text.should.equal("Switches");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
setTimeout(next, 3000);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user