diff --git a/docs/gestures.md b/docs/gestures.md index e77939b8526..c63bc1c21ab 100644 --- a/docs/gestures.md +++ b/docs/gestures.md @@ -79,6 +79,7 @@ In the case of these new mobile methods, `script` must be one of: * `mobile: flick` * `mobile: swipe` * `mobile: scrollTo` + * `mobile: scroll` * `mobile: shake` (The `mobile:` prefix allows us to route these requests to the appropriate endpoint). @@ -193,6 +194,10 @@ In these examples, note that the element parameter is always optional. ### Swipe +*Note*: Swiping is unfortunately broken in iOS7, because of a bug in Apple's +frameworks. For iOS7, see `mobile: scroll` as a workaround that works for most +cases. + * **WD.js:** ```js @@ -222,6 +227,26 @@ In these examples, note that the element parameter is always optional. js.executeScript("mobile: swipe", swipeObject); ``` +### Scroll + +* **WD.js:** + + ```js + // scroll the view down + driver.execute("mobile: scroll", [{direction: 'down'}], function(err) { + // continue testing + }); + ``` + +* **Java:** + + ```java + JavascriptExecutor js = (JavascriptExecutor) driver; + HashMap scrollObject = new HashMap(); + scrollObject.put("direction", "down"); + js.executeScript("mobile: scroll", scrollObject); + ``` + ### Slider * **Java** diff --git a/lib/devices/android/android-controller.js b/lib/devices/android/android-controller.js index be706d49b9f..62a929effab 100644 --- a/lib/devices/android/android-controller.js +++ b/lib/devices/android/android-controller.js @@ -522,6 +522,10 @@ androidController.scrollTo = function (elementId, text, cb) { this.proxy(["element:scrollTo", opts], cb); }; +androidController.scroll = function (direction, cb) { + cb(new NotYetImplementedError(), null); +}; + androidController.shake = function (cb) { cb(new NotYetImplementedError(), null); }; diff --git a/lib/devices/ios/ios-controller.js b/lib/devices/ios/ios-controller.js index 2b8a6a14e99..3d5d18bb20a 100644 --- a/lib/devices/ios/ios-controller.js +++ b/lib/devices/ios/ios-controller.js @@ -1052,6 +1052,11 @@ iOSController.scrollTo = function (elementId, text, cb) { this.proxy(command, cb); }; +iOSController.scroll = function (direction, cb) { + var command = "au.scrollFirstView('" + direction + "')"; + this.proxy(command, cb); +}; + iOSController.shake = function (cb) { this.proxy("au.shake()", cb); }; diff --git a/lib/devices/ios/uiauto/appium/app.js b/lib/devices/ios/uiauto/appium/app.js index 7019bd67d11..ed18ff57778 100644 --- a/lib/devices/ios/uiauto/appium/app.js +++ b/lib/devices/ios/uiauto/appium/app.js @@ -640,6 +640,33 @@ $.extend(au, { }; } +, scrollFirstView: function (direction) { + var viewRes = this.getElementByType('scrollview'); + console.log(JSON.stringify(viewRes)); + var doScroll = function (elId) { + var el = this.getElement(elId); + var method = 'scroll' + direction[0].toUpperCase() + direction.slice(1); + el[method](); + return { + status: codes.Success.code, + value: true + }; + }.bind(this); + + if (viewRes.status === codes.Success.code) { + return doScroll(viewRes.value.ELEMENT); + } else { + viewRes = this.getElementByType('tableview'); + if (viewRes.status === codes.Success.code) { + return doScroll(viewRes.value.ELEMENT); + } + } + return { + status: codes.NoSuchElement.code, + value: null + }; + } + , flickApp: function (startX, startY, endX, endY) { var coords = this.getAbsCoords(startX, startY, endX, endY); diff --git a/lib/server/controller.js b/lib/server/controller.js index d54efb673b4..82aee4599fe 100644 --- a/lib/server/controller.js +++ b/lib/server/controller.js @@ -449,6 +449,20 @@ exports.mobileScrollTo = function (req, res) { req.device.scrollTo(element, text, getResponseHandler(req, res)); }; +exports.mobileScroll = function (req, res) { + req.body = _.defaults(req.body, { + direction: "down" + }); + var direction = req.body.direction.toString().toLowerCase(); + + if (!_.contains(['up', 'left', 'right', 'down'], direction)) { + return respondError(req, res, status.codes.UnknownCommand.code, + new Error("Direction " + direction + " is not valid for scroll")); + } + + req.device.scroll(direction, getResponseHandler(req, res)); +}; + exports.mobileShake = function (req, res) { req.device.shake(getResponseHandler(req, res)); }; @@ -947,6 +961,7 @@ var mobileCmdMap = { , 'flick': exports.mobileFlick , 'swipe': exports.mobileSwipe , 'scrollTo': exports.mobileScrollTo +, 'scroll': exports.mobileScroll , 'shake': exports.mobileShake , 'setLocation' : exports.mobileSetLocation , 'hideKeyboard': exports.hideKeyboard diff --git a/test/functional/ios/uicatalog/gestures-specs.js b/test/functional/ios/uicatalog/gestures-specs.js index d53c1c4115e..7cb014d3b93 100644 --- a/test/functional/ios/uicatalog/gestures-specs.js +++ b/test/functional/ios/uicatalog/gestures-specs.js @@ -423,6 +423,37 @@ describe('uicatalog - gestures -', function () { }); }); + describe('mobile: scroll', function () { + var driver; + setup(this, desired).then(function (d) { driver = d; }); + + it('should scroll down and up', function (done) { + var firstEl, location1, location2; + driver + .elementByTagName('tableCell') + .then(function (el) { firstEl = el; return el.getLocation(); }) + .then(function (loc) { location1 = loc; }) + .then(function () { + return driver.execute("mobile: scroll", [{direction: 'down'}]); + }) + .then(function () { return firstEl.getLocation(); }) + .then(function (loc2) { + location2 = loc2; + loc2.x.should.equal(location1.x); + loc2.y.should.not.equal(location1.y); + }) + .then(function () { + return driver.execute("mobile: scroll", [{direction: 'up'}]); + }) + .then(function () { return firstEl.getLocation(); }) + .then(function (loc3) { + loc3.x.should.equal(location2.x); + loc3.y.should.not.equal(location2.y); + }) + .nodeify(done); + }); + }); + describe('mobile shake', function () { var driver; setup(this, desired).then(function (d) { driver = d; });