Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/Installing-xcuitest-driver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Setting Up XCUITest
---

**Install Appium XCUITest Driver**

Clone Appium XCUITest Driver repo https://github.com/appium/appium-xcuitest-driver

```
git clone --recursive https://github.com/appium/appium-xcuitest-driver.git
cd appium-xcuitest-driver
npm install
```

Set your appium path to the appropriate location (Where you cloned appium-xcuitest-driver) and you can
start the driver by doing:

```
node .
```
13 changes: 12 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
~ limitations under the License.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>io.appium</groupId>
Expand Down Expand Up @@ -63,6 +64,16 @@
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.1</version>
</dependency>
</dependencies>
<packaging>jar</packaging>
<name>java-client</name>
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/io/appium/java_client/FindsByIosNsPredicate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.appium.java_client;

import org.openqa.selenium.WebElement;

import java.util.List;

public interface FindsByIosNsPredicate<T extends WebElement> {
T findElementByIosNsPredicate(String using);

List<T> findElementsByIosNsPredicate(String using);
}
37 changes: 37 additions & 0 deletions src/main/java/io/appium/java_client/MobileBy.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ public static By AccessibilityId(final String id) {
return new ByAccessibilityId(id);
}

/**
* This locator strategy is available in XCUITest Driver mode
* @param iOSNsPredicateString is an an iOS NsPredicate String
* @return an instance of {@link io.appium.java_client.MobileBy.ByIosNsPredicate}
*/
public static By IosNsPredicateString(final String iOSNsPredicateString) {
if (iOSNsPredicateString == null) {
throw new IllegalArgumentException("Must supply an iOS NsPredicate String");
}

return new ByIosNsPredicate(iOSNsPredicateString);
}

public static class ByIosUIAutomation extends By implements Serializable {

private final String automationText;
Expand All @@ -97,6 +110,30 @@ public List<WebElement> findElements(SearchContext context) {
}
}

public static class ByIosNsPredicate extends By implements Serializable {

private final String automationText;

public ByIosNsPredicate(String uiautomationText) {
automationText = uiautomationText;
}

@SuppressWarnings("unchecked")
@Override public List<WebElement> findElements(SearchContext context) {
return (List<WebElement>) ((FindsByIosNsPredicate<?>) context)
.findElementsByIosNsPredicate(automationText);
}

@Override public WebElement findElement(SearchContext context) {
return ((FindsByIosNsPredicate<?>) context)
.findElementByIosNsPredicate(automationText);
}

@Override public String toString() {
return "By.IosNsPredicate: " + automationText;
}
}


public static class ByAndroidUIAutomator extends By implements Serializable {

Expand Down
28 changes: 26 additions & 2 deletions src/main/java/io/appium/java_client/ios/IOSDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.FindsByAccessibilityId;
import io.appium.java_client.FindsByIosNsPredicate;
import io.appium.java_client.FindsByIosUIAutomation;
import io.appium.java_client.ScrollsTo;
import io.appium.java_client.ios.internal.JsonToIOSElementConverter;
Expand Down Expand Up @@ -53,9 +54,10 @@
public class IOSDriver<T extends WebElement>
extends AppiumDriver<T>
implements IOSDeviceActionShortcuts, GetsNamedTextField<T>,
FindsByIosUIAutomation<T> {
private static final String IOS_PLATFORM = MobilePlatform.IOS;
FindsByIosUIAutomation<T>, FindsByIosNsPredicate<T> {

private static final String IOS_PLATFORM = MobilePlatform.IOS;

/**
* @param remoteAddress is the address
* of remotely/locally started Appium server
Expand Down Expand Up @@ -247,6 +249,28 @@ public List<T> findElementsByIosUIAutomation(String using)
throws WebDriverException {
return (List<T>) findElements("-ios uiautomation", using);
}

/**
* @throws org.openqa.selenium.WebDriverException
* This method is not applicable with browser/webview UI.
*/
@SuppressWarnings("unchecked")
@Override
public T findElementByIosNsPredicate(String using)
throws WebDriverException {
return (T) findElement("-ios predicate string", using);
}

/**
* @throws org.openqa.selenium.WebDriverException
* This method is not applicable with browser/webview UI.
*/
@SuppressWarnings("unchecked")
@Override
public List<T> findElementsByIosNsPredicate(String using)
throws WebDriverException {
return (List<T>) findElements("-ios predicate string", using);
}

/**
* Lock the device (bring it to the lock screen) for a given number of
Expand Down
41 changes: 38 additions & 3 deletions src/main/java/io/appium/java_client/ios/IOSElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.common.collect.ImmutableMap;

import io.appium.java_client.FindsByIosNsPredicate;
import io.appium.java_client.FindsByIosUIAutomation;
import io.appium.java_client.MobileCommand;
import io.appium.java_client.MobileElement;
Expand All @@ -30,8 +31,8 @@
import java.util.List;

public class IOSElement extends MobileElement
implements FindsByIosUIAutomation<MobileElement>, ScrollsTo<MobileElement> {

implements FindsByIosUIAutomation<MobileElement>, ScrollsTo<MobileElement>,
FindsByIosNsPredicate<MobileElement> {
/**
* @throws WebDriverException
* This method is not applicable with browser/webview UI.
Expand All @@ -42,7 +43,8 @@ public class IOSElement extends MobileElement
}

/**
* @throws WebDriverException This method is not applicable with browser/webview UI.
* @throws WebDriverException
* This method is not applicable with browser/webview UI.
*/
@Override public List<MobileElement> findElementsByIosUIAutomation(String using)
throws WebDriverException {
Expand All @@ -53,6 +55,39 @@ public class IOSElement extends MobileElement
}
return result;
}

/**
* @throws org.openqa.selenium.WebDriverException
* This method is not applicable with browser/webview UI.
*/
@Override public MobileElement findElementByIosNsPredicate(String using)
throws WebDriverException {
return (IOSElement) findElement("-ios predicate string", using);
}

/**
* @throws WebDriverException This method is not applicable with browser/webview UI.
*/
@Override public List<MobileElement> findElementsByIosNsPredicate(String using)
throws WebDriverException {
List<MobileElement> result = new ArrayList<MobileElement>();
List<WebElement> found = findElements("-ios predicate string", using);
for (WebElement e : found) {
result.add((IOSElement) e);
}
return result;
}

/**
* Scroll to the element whose 'text' attribute contains the input text.
* Scrolling happens within this element
*
* @param text input text contained in text attribute
*/
@Override public MobileElement scrollTo(String text) {
return (IOSElement) findElementByIosUIAutomation(
".scrollToElementWithPredicate(\"name CONTAINS '" + text + "'\")");
}

/**
* Scroll to the element whose 'text' attribute contains the input text.
Expand Down
112 changes: 112 additions & 0 deletions src/test/java/io/appium/java_client/ios/XCUIDriverTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* 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.ios;

import static org.junit.Assert.assertTrue;

import io.appium.java_client.MobileBy;
import io.appium.java_client.MobileElement;
import io.appium.java_client.remote.IOSMobileCapabilityType;
import io.appium.java_client.remote.MobileCapabilityType;
import io.appium.java_client.service.local.AppiumDriverLocalService;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import org.apache.commons.io.FileUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.io.File;
import java.io.IOException;

public class XCUIDriverTest {

private static final String SOURCE = "src/test/java/io/appium/java_client/";
private static AppiumDriverLocalService service;
protected static IOSDriver<MobileElement> driver;

/**
* initialization.
*/
@BeforeClass
public static void beforeClass() throws Exception {
service = AppiumDriverLocalService.buildDefaultService();
service.start();

if (service == null || !service.isRunning()) {
throw new RuntimeException("An appium server node is not started!");
}

String source = SOURCE + "UICatalog.app.zip";

try {
ZipFile zipFile = new ZipFile(source);
zipFile.extractAll(SOURCE);
} catch (ZipException e) {
String msg = "Could not extract file";
throw new ZipException(msg, e);
}

File appDir = new File(SOURCE);
File app = new File(appDir, "UICatalog.app");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, "");
capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "9.3");
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone 6");
//sometimes environment has performance problems
capabilities.setCapability(IOSMobileCapabilityType.LAUNCH_TIMEOUT, 500000);
capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
driver = new IOSDriver<>(service.getUrl(), capabilities);
}

/**
* finishing.
*/
@AfterClass
public static void afterClass() throws IOException {
if (driver != null) {
driver.quit();
}
if (service != null) {
service.stop();
}
try {
FileUtils.deleteDirectory(new File(SOURCE + "/UICatalog.app"));
} catch (IOException e) {
throw e;
}

}

//TODO There is no ability to check this function usibg simulators.
// When CI will have been set up then this test will be returned
public void getDeviceTimeTest() {
String time = driver.getDeviceTime();
assertTrue(time.length() == 28);
}

/**
* Verifies UICatalog element is present in view.
*/
@Test
public void getiOSElementByPredicate() {
//Needs to run on the XCUITest ios Driver (https://github.com/appium/appium-xcuitest-driver.git).
driver.findElement(MobileBy.IosNsPredicateString("identifier == \"UICatalog\""));
}

}