Merge pull request #849 from mutualmobile/android_scrollTo_support

Added scrollTo support for Android.
This commit is contained in:
bootstraponline
2013-07-03 08:20:41 -07:00
7 changed files with 139 additions and 5 deletions
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
};
+33
View File
@@ -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);
});
});
});
});
});