diff --git a/src/io/appium/java_client/AppiumDriver.java b/src/io/appium/java_client/AppiumDriver.java index aa37e209f..4196a0361 100644 --- a/src/io/appium/java_client/AppiumDriver.java +++ b/src/io/appium/java_client/AppiumDriver.java @@ -17,6 +17,7 @@ package io.appium.java_client; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.openqa.selenium.*; import org.openqa.selenium.remote.*; @@ -50,6 +51,8 @@ public AppiumDriver(URL remoteAddress, Capabilities desiredCapabilities){ .put(HIDE_KEYBOARD, postC("/session/:sessionId/appium/device/hide_keyboard")) .put(PUSH_FILE, postC("/session/:sessionId/appium/device/push_file")) .put(RUN_APP_IN_BACKGROUND, postC("/session/:sessionId/appium/app/background")) + .put(PERFORM_TOUCH_ACTION, postC("/session/:sessionId/touch/perform")) + .put(PERFORM_MULTI_TOUCH, postC("/session/:sessionId/touch/multi/perform")) ; ImmutableMap mobileCommands = builder.build(); @@ -176,6 +179,180 @@ public void runAppInBackground(int seconds) { execute(RUN_APP_IN_BACKGROUND, ImmutableMap.of("seconds", seconds)); } + /** + * Performs a chain of touch actions, which together can be considered an entire gesture. + * See the Webriver 3 spec https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html + * + * It's more convenient to call the perform() method of the TouchAction object itself. + * + * @param touchAction A TouchAction object, which contains a list of individual touch actions to perform + * @return the same touchaction object + */ + public TouchAction performTouchAction(TouchAction touchAction) { + ImmutableMap parameters = touchAction.getParameters(); + touchAction.clearParameters(); + execute(PERFORM_TOUCH_ACTION, parameters); + + return touchAction; + } + + /** + * Performs multiple TouchAction gestures at the same time, to simulate multiple fingers/touch inputs. + * See the Webriver 3 spec https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html + * + * It's more convenient to call the perform() method of the MultiTouchAction object. + * + * @param multiAction the MultiTouchAction object to perform. + */ + public void performMultiTouchAction(MultiTouchAction multiAction) { + ImmutableMap parameters = multiAction.getParameters(); + execute(PERFORM_MULTI_TOUCH, parameters); + } + + /** + * Convenience method for tapping the center of an element on the screen + * @param fingers number of fingers/appendages to tap with + * @param element element to tap + * @param duration how long between pressing down, and lifting fingers/appendages + */ + public void tap(int fingers, WebElement element, int duration) { + MultiTouchAction multiTouch = new MultiTouchAction(this); + + for (int i = 0; i < fingers; i++) { + multiTouch.add(createTap(element, duration)); + } + + multiTouch.perform(); + } + + /** + * Convenience method for tapping a position on the screen + * @param fingers number of fingers/appendages to tap with + * @param x x coordinate + * @param y y coordinate + * @param duration + */ + public void tap(int fingers, int x, int y, int duration) { + MultiTouchAction multiTouch = new MultiTouchAction(this); + + for (int i = 0; i < fingers; i++) { + multiTouch.add(createTap(x, y, duration)); + } + + multiTouch.perform(); + } + + /** + * Convenience method for swiping across the screen + * @param startx starting x coordinate + * @param starty starting y coordinate + * @param endx ending x coordinate + * @param endy ending y coordinate + * @param duration amount of time in milliseconds for the entire swipe action to take + */ + public void swipe(int startx, int starty, int endx, int endy, int duration) { + TouchAction touchAction = new TouchAction(this); + + //appium converts press-wait-moveto-release to a swipe action + touchAction.press(startx, starty).waitAction(duration).moveTo(endx, endy).release(); + + touchAction.perform(); + } + + /** + * Convenience method for pinching an element on the screen. + * "pinching" refers to the action of two appendages pressing the screen and sliding towards each other. + * NOTE: + * This convenience method places the initial touches around the element, if this would happen to place one of them + * off the screen, appium with return an outOfBounds error. In this case, revert to using the MultiTouchAction api + * instead of this method. + * + * @param el The element to pinch + */ + public void pinch(WebElement el) { + MultiTouchAction multiTouch = new MultiTouchAction(this); + + Dimension dimensions = el.getSize(); + Point upperLeft = el.getLocation(); + Point center = new Point(upperLeft.getX() + dimensions.getWidth() / 2, upperLeft.getY() + dimensions.getHeight() / 2); + + TouchAction action0 = new TouchAction(this).press(el, center.getX(), center.getY() - 100).moveTo(el).release(); + TouchAction action1 = new TouchAction(this).press(el, center.getX(), center.getY() + 100).moveTo(el).release(); + + multiTouch.add(action0).add(action1); + + multiTouch.perform(); + } + + /** + * Convenience method for pinching an element on the screen. + * "pinching" refers to the action of two appendages pressing the screen and sliding towards each other. + * NOTE: + * This convenience method places the initial touches around the element at a distance, if this would happen to place + * one of them off the screen, appium will return an outOfBounds error. In this case, revert to using the + * MultiTouchAction api instead of this method. + * + * @param x x coordinate to terminate the pinch on + * @param y y coordinate to terminate the pinch on + */ + public void pinch(int x, int y) { + MultiTouchAction multiTouch = new MultiTouchAction(this); + + TouchAction action0 = new TouchAction(this).press(x, y-100).moveTo(x, y).release(); + TouchAction action1 = new TouchAction(this).press(x, y+100).moveTo(x, y).release(); + + multiTouch.add(action0).add(action1); + + multiTouch.perform(); + } + + /** + * Convenience method for "zooming in" on an element on the screen. + * "zooming in" refers to the action of two appendages pressing the screen and sliding away from each other. + * NOTE: + * This convenience method slides touches away from the element, if this would happen to place one of them + * off the screen, appium will return an outOfBounds error. In this case, revert to using the MultiTouchAction api + * instead of this method. + * + * @param el The element to pinch + */ + public void zoom(WebElement el) { + MultiTouchAction multiTouch = new MultiTouchAction(this); + + Dimension dimensions = el.getSize(); + Point upperLeft = el.getLocation(); + Point center = new Point(upperLeft.getX() + dimensions.getWidth() / 2, upperLeft.getY() + dimensions.getHeight() / 2); + + TouchAction action0 = new TouchAction(this).press(el).moveTo(el, center.getX(), center.getY() - 100).release(); + TouchAction action1 = new TouchAction(this).press(el).moveTo(el, center.getX(), center.getY() + 100).release(); + + multiTouch.add(action0).add(action1); + + multiTouch.perform(); + } + + /** + * Convenience method for "zooming in" on an element on the screen. + * "zooming in" refers to the action of two appendages pressing the screen and sliding away from each other. + * NOTE: + * This convenience method slides touches away from the element, if this would happen to place one of them + * off the screen, appium will return an outOfBounds error. In this case, revert to using the MultiTouchAction api + * instead of this method. + * + * @param x x coordinate to start zoom on + * @param y y coordinate to start zoom on + */ + public void zoom(int x, int y) { + MultiTouchAction multiTouch = new MultiTouchAction(this); + + TouchAction action0 = new TouchAction(this).press(x, y).moveTo(x, y-100).release(); + TouchAction action1 = new TouchAction(this).press(x, y).moveTo(x, y+100).release(); + + multiTouch.add(action0).add(action1); + + multiTouch.perform(); + } + @Override public WebDriver context(String name) { @@ -183,7 +360,6 @@ public WebDriver context(String name) { throw new IllegalArgumentException("Must supply a context name"); } - execute(DriverCommand.SWITCH_TO_CONTEXT, ImmutableMap.of("name", name)); return AppiumDriver.this; } @@ -239,6 +415,16 @@ public List findElementsByAccessibilityId(String using) { return findElements("accessibility id", using); } + private TouchAction createTap(WebElement element, int duration) { + TouchAction tap = new TouchAction(this); + return tap.press(element).waitAction(duration).release(); + } + + private TouchAction createTap(int x, int y, int duration) { + TouchAction tap = new TouchAction(this); + return tap.press(x, y).waitAction(duration).release(); + } + private static CommandInfo getC(String url) { return new CommandInfo(url, HttpVerb.GET); } diff --git a/src/io/appium/java_client/MobileCommand.java b/src/io/appium/java_client/MobileCommand.java index 062186d22..8f999d906 100644 --- a/src/io/appium/java_client/MobileCommand.java +++ b/src/io/appium/java_client/MobileCommand.java @@ -33,6 +33,8 @@ public interface MobileCommand { String PUSH_FILE = "pushFile"; String HIDE_KEYBOARD = "hideKeyboard"; String RUN_APP_IN_BACKGROUND = "runAppInBackground"; + String PERFORM_TOUCH_ACTION = "performTouchAction"; + String PERFORM_MULTI_TOUCH = "performMultiTouch"; } diff --git a/src/io/appium/java_client/MobileDriver.java b/src/io/appium/java_client/MobileDriver.java index 80453b202..f876b59ac 100644 --- a/src/io/appium/java_client/MobileDriver.java +++ b/src/io/appium/java_client/MobileDriver.java @@ -29,4 +29,7 @@ public interface MobileDriver extends WebDriver, ContextAware { public Response execute(String driverCommand, Map parameters); + public TouchAction performTouchAction(TouchAction touchAction); + + public void performMultiTouchAction(MultiTouchAction multiAction); } diff --git a/src/io/appium/java_client/MultiTouchAction.java b/src/io/appium/java_client/MultiTouchAction.java new file mode 100644 index 000000000..8c1d061ed --- /dev/null +++ b/src/io/appium/java_client/MultiTouchAction.java @@ -0,0 +1,81 @@ +/* + +Copyright 2014 Appium contributors + +Copyright 2014 Software Freedom Conservancy + + + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + + + + http://www.apache.org/licenses/LICENSE-2.0 + + + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + + */ + +package io.appium.java_client; + + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +/** + * Used for Webdriver 3 multi-touch gestures + * See the Webriver 3 spec https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html + * + * The MultiTouchAction object is a collection of TouchAction objects + * (remember that TouchAction objects are in turn, a chain of individual actions) + * + * Add multiple TouchAction objects using the add() method. + * When perform() method is called, all actions are sent to the driver. + * + * The driver performs the first step of each TouchAction object simultaneously as a multi-touch "execution group". + * Conceptually, the number of TouchAction objects added to the MultiTouchAction is equal to the number of "fingers" or + * other appendages or tools touching the screen at the same time as part of this multi-gesture. + * Then the driver performs the second step of each TouchAction object and another "execution group", and the third, and so on. + * + * Using a waitAction() action within a TouchAction takes up one of the slots in an "execution group", so these can be used to + * sync up complex actions. + * + * Calling perform() sends the action command to the Mobile Driver. Otherwise, more and more actions can be chained. + */ +public class MultiTouchAction { + + private MobileDriver driver; + ImmutableList.Builder actions; + + public MultiTouchAction(MobileDriver driver) { + this.driver = driver; + actions = ImmutableList.builder(); + } + + /** + * Add a TouchAction to this multi-touch gesture + * @param action TouchAction to add to this gesture + * @return This MultiTouchAction, for chaining + */ + public MultiTouchAction add(TouchAction action) { + actions.add(action); + + return this; + } + + /** + * Perform the multi-touch action on the mobile driver. + */ + public void perform() { + driver.performMultiTouchAction(this); + } + + protected ImmutableMap getParameters() { + ImmutableList.Builder listOfActionChains = ImmutableList.builder(); + ImmutableList touchActions = actions.build(); + + for (TouchAction action : touchActions) { + listOfActionChains.add(action.getParameters().get("actions")); + } + return ImmutableMap.of("actions", listOfActionChains.build()); + } +} diff --git a/src/io/appium/java_client/TouchAction.java b/src/io/appium/java_client/TouchAction.java new file mode 100644 index 000000000..a1889abc2 --- /dev/null +++ b/src/io/appium/java_client/TouchAction.java @@ -0,0 +1,303 @@ +/* + +Copyright 2014 Appium contributors + +Copyright 2014 Software Freedom Conservancy + + + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + + + + http://www.apache.org/licenses/LICENSE-2.0 + + + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + + */ + +package io.appium.java_client; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebElement; + + +/** + * Used for Webdriver 3 touch actions + * See the Webriver 3 spec https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html + * + * The flow is to chain individual touch actions into an entire gesture. e.g. + * TouchAction action = new TouchAction(driver); + * action.press(element).waitAction(300).moveTo(element1).release().perform(); + * + * Calling perform() sends the action command to the Mobile Driver. Otherwise, more and more actions can be chained. + */ +public class TouchAction { + + private MobileDriver driver; + ImmutableList.Builder parameterBuilder; + + public TouchAction(MobileDriver driver) { + this.driver = driver; + parameterBuilder = ImmutableList.builder(); + } + + /** + * Press on the center of an element. + * @param el element to press on + * @return this TouchAction, for chaining + */ + public TouchAction press(WebElement el) { + ActionParameter action = new ActionParameter("press", (RemoteWebElement)el); + parameterBuilder.add(action); + return this; + } + + /** + * Press on an absolute position on the screen + * @param x x coordinate + * @param y y coordinate + * @return this TouchAction, for chaining + */ + public TouchAction press(int x, int y) { + ActionParameter action = new ActionParameter("press"); + action.addParameter("x", x); + action.addParameter("y", y); + parameterBuilder.add(action); + return this; + } + + /** + * Press on an element, offset from upper left corner by a number of pixels + * @param el element to press on + * @param x x offset + * @param y y offset + * @return this TouchAction, for chaining + */ + public TouchAction press(WebElement el, int x, int y) { + ActionParameter action = new ActionParameter("press", (RemoteWebElement)el); + action.addParameter("x", x); + action.addParameter("y", y); + parameterBuilder.add(action); + return this; + } + + /** + * Remove the current touching implement from the screen (withdraw your touch) + * @return this TouchAction, for chaining + */ + public TouchAction release() { + ActionParameter action = new ActionParameter("release"); + parameterBuilder.add(action); + return this; + } + + /** + * Move current touch to center of an element. + * @param el element to move to + * @return this TouchAction, for chaining + */ + public TouchAction moveTo(WebElement el) { + ActionParameter action = new ActionParameter("moveTo", (RemoteWebElement)el); + parameterBuilder.add(action); + return this; + } + + /** + * Move current touch to an absolute position on the screen + * @param x x coordinate + * @param y y coordinate + * @return this TouchAction, for chaining + */ + public TouchAction moveTo(int x, int y) { + ActionParameter action = new ActionParameter("moveTo"); + action.addParameter("x", x); + action.addParameter("y", y); + parameterBuilder.add(action); + return this; + } + + /** + * Move current touch to an element, offset from upper left corner + * @param el element to move current touch to + * @param x x offset + * @param y y offset + * @return this TouchAction, for chaining + */ + public TouchAction moveTo(WebElement el, int x, int y) { + ActionParameter action = new ActionParameter("moveTo", (RemoteWebElement)el); + action.addParameter("x", x); + action.addParameter("y", y); + parameterBuilder.add(action); + return this; + } + + /** + * Tap the center of an element. + * @param el element to tap + * @return this TouchAction, for chaining + */ + public TouchAction tap(WebElement el) { + ActionParameter action = new ActionParameter("tap", (RemoteWebElement)el); + parameterBuilder.add(action); + return this; + } + + /** + * Tap an absolute position on the screen + * @param x x coordinate + * @param y y coordinate + * @return this TouchAction, for chaining + */ + public TouchAction tap(int x, int y) { + ActionParameter action = new ActionParameter("tap"); + action.addParameter("x", x); + action.addParameter("y", y); + parameterBuilder.add(action); + return this; + } + + /** + * Tap an element, offset from upper left corner + * @param el element to tap + * @param x x offset + * @param y y offset + * @return this TouchAction, for chaining + */ + public TouchAction tap(WebElement el, int x, int y) { + ActionParameter action = new ActionParameter("tap", (RemoteWebElement)el); + action.addParameter("x", x); + action.addParameter("y", y); + parameterBuilder.add(action); + return this; + } + + /** + * A wait action, used as a NOP in multi-chaining + * + * @return this TouchAction, for chaining + */ + public TouchAction waitAction() { + ActionParameter action = new ActionParameter("wait"); + parameterBuilder.add(action); + return this; + } + + /** + * Waits for specified amount of time to pass before continue to next touch action + * @param ms time in milliseconds to wait + * @return this TouchAction, for chaining + */ + public TouchAction waitAction(int ms) { + ActionParameter action = new ActionParameter("wait"); + action.addParameter("ms", ms); + parameterBuilder.add(action); + return this; + } + + /** + * Press and hold the at the center of an element until the contextmenu event has fired. + * @param el element to long-press + * @return this TouchAction, for chaining + */ + public TouchAction longPress(WebElement el) { + ActionParameter action = new ActionParameter("longPress", (RemoteWebElement)el); + parameterBuilder.add(action); + return this; + } + + /** + * Press and hold the at an absolute position on the screen until the contextmenu event has fired. + * @param x x coordinate + * @param y y coordinate + * @return this TouchAction, for chaining + */ + public TouchAction longPress(int x, int y) { + ActionParameter action = new ActionParameter("longPress"); + action.addParameter("x", x); + action.addParameter("y", y); + parameterBuilder.add(action); + return this; + } + + /** + * Press and hold the at an elements upper-left corner, offset by the given amount, until the contextmenu event has fired. + * @param el element to long-press + * @param x x offset + * @param y y offset + * @return this TouchAction, for chaining + */ + public TouchAction longPress(WebElement el, int x, int y) { + ActionParameter action = new ActionParameter("longPress", (RemoteWebElement)el); + action.addParameter("x", x); + action.addParameter("y", y); + parameterBuilder.add(action); + return this; + } + + /** + * Cancel this action, if it was partially completed by the driver + */ + public void cancel() { + ActionParameter action = new ActionParameter("wait"); + parameterBuilder.add(action); + this.perform(); + } + + /** + * Perform this chain of actions on the driver. + * @return this TouchAction, for possible segmented-touches. + */ + public TouchAction perform() { + driver.performTouchAction(this); + return this; + } + + /** + * Get the mjsonwp parameters for this Action + * @return A map of parameters for this touch action to pass as part of mjsonwp + */ + protected ImmutableMap getParameters() { + + ImmutableList.Builder parameters = ImmutableList.builder(); + ImmutableList actionList = parameterBuilder.build(); + for (ActionParameter action : actionList){ + parameters.add(action.getParameterMap()); + } + return ImmutableMap.of("actions", parameters.build()); + } + + protected void clearParameters() { + parameterBuilder = ImmutableList.builder(); + } + + /** + * Just holds values to eventually return the parameters required for the mjsonwp + */ + private class ActionParameter { + private String actionName; + private ImmutableMap.Builder optionsBuilder; + + public ActionParameter(String actionName) { + this.actionName = actionName; + optionsBuilder = ImmutableMap.builder(); + } + + public ActionParameter(String actionName, RemoteWebElement el) { + this.actionName = actionName; + optionsBuilder = ImmutableMap.builder(); + addParameter("element", el.getId()); + } + + public ImmutableMap getParameterMap() { + ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.put("action", actionName).put("options", optionsBuilder.build()); + return builder.build(); + } + + public void addParameter(String name, Object value) { + optionsBuilder.put(name, value); + } + } +} diff --git a/test/io/appium/java_client/MobileDriverGestureTest.java b/test/io/appium/java_client/MobileDriverGestureTest.java new file mode 100644 index 000000000..ae8e407dd --- /dev/null +++ b/test/io/appium/java_client/MobileDriverGestureTest.java @@ -0,0 +1,122 @@ +/* + +Copyright 2014 Appium contributors + +Copyright 2014 Software Freedom Conservancy + + + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + + + + http://www.apache.org/licenses/LICENSE-2.0 + + + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + + */ + +package io.appium.java_client; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.Alert; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.CapabilityType; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.io.File; +import java.net.URL; + +/** + * Test Mobile Driver features + */ +public class MobileDriverGestureTest { + + private AppiumDriver driver; + + @Before + public void setup() throws Exception { + File appDir = new File("test/io/appium/java_client"); + File app = new File(appDir, "TestApp.app.zip"); + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(CapabilityType.BROWSER_NAME, ""); + capabilities.setCapability(CapabilityType.VERSION, "7.1"); + capabilities.setCapability(CapabilityType.PLATFORM, "Mac"); + capabilities.setCapability("device", "iPhone Simulator"); + capabilities.setCapability("app", app.getAbsolutePath()); + driver = new AppiumDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities); + } + + @After + public void tearDown() throws Exception { + driver.quit(); + } + + @Test + public void TouchActionTest() throws InterruptedException { + WebElement button = driver.findElementsByTagName("button").get(3); + TouchAction action = new TouchAction(driver); + action.press(button).perform(); + Thread.sleep(2000); + } + + @Test + public void TouchActionChainTest() throws InterruptedException { + WebDriverWait wait = new WebDriverWait(driver, 2); + + WebElement button = driver.findElementsByTagName("button").get(5); + TouchAction action = new TouchAction(driver); + action.press(button).perform(); + + wait.until(ExpectedConditions.alertIsPresent()); + Alert alert = driver.switchTo().alert(); + alert.accept(); + + WebElement mapview = driver.findElementByXPath("//UIAWindow[1]/UIAMapView[1]"); + action = new TouchAction(driver); + action.press(mapview).moveTo(mapview, 0, 100).release().perform(); + Thread.sleep(2000); + } + + @Test + public void MultiGestureTest() throws InterruptedException { + WebDriverWait wait = new WebDriverWait(driver, 2); + + WebElement button = driver.findElementsByTagName("button").get(5); + TouchAction action = new TouchAction(driver); + action.press(button).perform(); + + wait.until(ExpectedConditions.alertIsPresent()); + Alert alert = driver.switchTo().alert(); + alert.accept(); + + WebElement mapview = driver.findElementByXPath("//UIAWindow[1]/UIAMapView[1]"); + + MultiTouchAction multiTouch = new MultiTouchAction(driver); + TouchAction action0 = new TouchAction(driver).press(mapview, 100, 0).moveTo(mapview, 0,-80).release(); + TouchAction action1 = new TouchAction(driver).press(mapview, 100, 50).moveTo(mapview, 0,80).release(); + multiTouch.add(action0).add(action1).perform(); + Thread.sleep(2000); + } + + @Test + public void ZoomTest() throws InterruptedException { + WebDriverWait wait = new WebDriverWait(driver, 2); + + WebElement button = driver.findElementsByTagName("button").get(5); + TouchAction action = new TouchAction(driver); + action.press(button).perform(); + + wait.until(ExpectedConditions.alertIsPresent()); + Alert alert = driver.switchTo().alert(); + alert.accept(); + + WebElement mapview = driver.findElementByXPath("//UIAWindow[1]/UIAMapView[1]"); + + driver.zoom(mapview); + Thread.sleep(2000); + } +} diff --git a/test/io/appium/java_client/TestApp.app.zip b/test/io/appium/java_client/TestApp.app.zip new file mode 100755 index 000000000..4f330c638 Binary files /dev/null and b/test/io/appium/java_client/TestApp.app.zip differ