diff --git a/archive/docs/How-to-propose-a-PR.md b/archive/docs/How-to-propose-a-PR.md new file mode 100644 index 000000000..98aea60d4 --- /dev/null +++ b/archive/docs/How-to-propose-a-PR.md @@ -0,0 +1,24 @@ +#A good pull-request should contain + +### Change list + +There should be provided briefly described change list which are you going to propose. + +### Types of changes + +What types of changes are proposed/introduced to Java client? +_Put an `x` in the boxes that apply_ + +- [ ] Bugfix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) + +### Details + +There should be provided more details about changes if it is necessary. If there are new features you +can provide code samples which show the way they work and possible use cases. Also you can create [gists](https://gist.github.com) +with pasted java code samples or put them at a PR description using markdown. About markdown please read [Mastering markdown](https://guides.github.com/features/mastering-markdown/) and [Writing on GitHub](https://help.github.com/categories/writing-on-github/) + +#Pull-request template + +There is [PULL_REQUEST_TEMPLATE.md)](https://github.com/appium/java-client/blob/master/PULL_REQUEST_TEMPLATE.md) which should help you to make a good pull request. diff --git a/archive/docs/How-to-report-an-issue.md b/archive/docs/How-to-report-an-issue.md new file mode 100644 index 000000000..f78f99cc2 --- /dev/null +++ b/archive/docs/How-to-report-an-issue.md @@ -0,0 +1,50 @@ +#Be sure that it is not a server-side problem if you are facing something that looks like a bug + +The Appium Java client is the thin client which just sends requests and receives responces generally. +Be sure that this bug is not reported [here](https://github.com/appium/appium/issues) and/or there is +no progress on this issue. + +#The good issue report should contain + +### Description + +The bug report should contain a brief description of a bug. +If it is the feature request then there should be the description of this feature and the way that it should work. + +### Environment (bug report) + +* java client build version or git revision if you use some shapshot: +* Appium server version or git revision if you use some shapshot: +* Desktop OS/version used to run Appium if necessary: +* Node.js version (unless using Appium.app|exe) or Appium CLI or Appium.app|exe: +* Mobile platform/version under test: +* Real device or emulator/simulator: + +### Details + +If it is necessary there should provided more details + + +### Code To Reproduce Issue (good to Have if you report a bug) + +It's easier to reproduce bug and much faster to fix it. +You can git clone https://github.com/appium/sample-code or https://github.com/appium/sample-code/tree/master/sample-code/apps and reproduce an issue using Java and sample apps. +Also you can create a [gist](https://gist.github.com) with pasted java code sample or paste it at ussue description using markdown. About markdown please read [Mastering markdown](https://guides.github.com/features/mastering-markdown/) and +[Writing on GitHub](https://help.github.com/categories/writing-on-github/) + +### Ecxeption stacktraces (bug report) + +There should be created a [gist](https://gist.github.com) with pasted stacktrace of exception thrown by java. + +### Link to Appium logs + +There should be created a [gist](https://gist.github.com) which is a paste of your _full_ Appium logs, and link them to a new issue. Do _not_ paste your full Appium logs at the issue description, as it will make this issue very long and hard to read! +If you are reporting a bug, _always_ include Appium logs as linked gists! It helps to define the problem correctly and clearly. + + +#Issue template +There is [ISSUE_TEMPLATE.md](https://github.com/appium/java-client/blob/master/ISSUE_TEMPLATE.md) which should help you to make a good issue report. + +#... And don't say that you weren't warned. + +If a report is not readable and/or there is no response from a reporter and some important details are needed then the issue will be closed after some time. diff --git a/archive/docs/Installing-the-project.md b/archive/docs/Installing-the-project.md new file mode 100644 index 000000000..a11590564 --- /dev/null +++ b/archive/docs/Installing-the-project.md @@ -0,0 +1,31 @@ +[Download the jar from Maven](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22io.appium%22%20AND%20a%3A%22java-client%22) or add the following to pom.xml: + +``` + + io.appium + java-client + 4.1.2 + +``` + +It currently depends on selenium-java 2.53.1. If it is necessary to use another version of Selenium then you can configure pom.xml as follows: + +``` + + io.appium + java-client + 4.1.1 + + + org.seleniumhq.selenium + selenium-java + + + + + + org.seleniumhq.selenium + selenium-java + ${selenium.version.you.require} + +``` diff --git a/archive/docs/Note-for-developers.md b/archive/docs/Note-for-developers.md new file mode 100644 index 000000000..97f9dbe2f --- /dev/null +++ b/archive/docs/Note-for-developers.md @@ -0,0 +1,63 @@ +# IDE + +The **Intellij Idea** is recommended. + +# Settings + +## Importing the project + +This is the Gradle project. + +![](https://cloud.githubusercontent.com/assets/4927589/18324097/6141ef7c-7543-11e6-8661-81d631615502.png) + +Be sure that: + +- The `JAVA_HOME` environmental contains a path to JDK > 7 + +- If non built-in gradle distribution is used then its version should be > 2.1 + +## Compiler + +This project is compiled in some not common way. We use `ecj` Eclipse Java Compiler. Below is the sample how to define this compiler by IDE. +![eclipse compiler](https://cloud.githubusercontent.com/assets/4927589/14228367/6fce184e-f91b-11e5-837c-2673446d24ea.png) + +## JDK + +Please check following settings: + +![](https://cloud.githubusercontent.com/assets/4927589/18324490/7ffd3ba4-7545-11e6-9f22-eb028737283c.png) + +![](https://cloud.githubusercontent.com/assets/4927589/18324593/f5254e3a-7545-11e6-85c5-e4c491ee268d.png) + +![](https://cloud.githubusercontent.com/assets/4927589/18324648/3f4635a6-7546-11e6-966c-2949059968ac.png) + +![](https://cloud.githubusercontent.com/assets/4927589/18324760/cbca4aee-7546-11e6-8cfb-e86d8018be6a.png) + +![](https://cloud.githubusercontent.com/assets/4927589/18324835/2e3bfc04-7547-11e6-8f5e-981aea8f1771.png) + +## Coding Standards + +Appium java-client strictly follows [Google Java Style](http://google-styleguide.googlecode.com/svn/trunk/javaguide.html) as a coding standards. Contributors are requested to follow this by configuring in their IDE's Editor Code style, + +* Clone [Google Style Guide](https://github.com/google/styleguide.git) from git + +**Intellij IDEA** users can configure this way, +`Files -> Other Settings -> Default Settings ->Editor -> Code Style -> Manage -> Manage -> Import -> eclipse-java-google-style.xml (Downloaded from Google Style Guide)-> Apply` + +Reformat your code before raising a Pull Request. + + +## Code Coverage + +`jacoco-maven-plugin` generates the coverage reports, once integration tests are successfully run by `maven-surefire-plugin` + +**Intellij IDEA** user's can configure and view this way, +`Analyse -> show coverage Data -> Add -> Select ${basedir}/target/coverage-reports/jacoco-unit.exec (jacoco-unit.exec generated after integration test run) -> Click Show Selected -> Coverage Results displayed in IDE` + +# Please do not forget to check the code before the pull-request proposal. + +It is needed to go to the directory where `java_client` is located. You can do it via command line. And then run the following command + +`gradle check`. If everything is ok then all checks should be passed. Otherwise you can open reports at `JAVA_CLIENT_DIRECTORY/build/reports` + +**The adding of new tests is required when new feature is added or some bug is fixed.** \ No newline at end of file diff --git a/archive/docs/Page-objects.md b/archive/docs/Page-objects.md new file mode 100644 index 000000000..9b8ae8aba --- /dev/null +++ b/archive/docs/Page-objects.md @@ -0,0 +1,586 @@ +Appium Java client has facilities which components to [Page Object](https://github.com/SeleniumHQ/selenium/wiki/PageObjects) design pattern and [Selenium PageFactory](https://github.com/SeleniumHQ/selenium/wiki/PageFactory). + + +# WebElement/list of WebElement field can be populated by default: +```java +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +... + +@FindBy(someStrategy) //for browser or web view html UI +//also for mobile native applications when other locator strategies are not defined +WebElement someElement; + +@FindBy(someStrategy) //for browser or web view html UI +//also for mobile native applications when other locator strategies are not defined +List someElements; +``` + +# If there is need to use convinient locators for mobile native applications then the following is available: + +```java +import io.appium.java_client.android.AndroidElement; +import org.openqa.selenium.remote.RemoteWebElement; +import io.appium.java_client.pagefactory.*; +import io.appium.java_client.ios.IOSElement; + +@AndroidFindBy(someStrategy) //for Android UI when Android UI automator is used +AndroidElement someElement; + +@AndroidFindBy(someStrategy) //for Android UI when Android UI automator is used +List someElements; + +@SelendroidFindBy(someStrategy) //for Android UI when Selendroid automation is used +RemoteWebElement someElement; + +@SelendroidFindBy(someStrategy) //for Android UI when Selendroid automation is used +List someElements; + +@iOSFindBy(someStrategy) //for iOS native UI +IOSElement someElement; + +@iOSFindBy(someStrategy) //for iOS native UI +List someElements; +``` + +# The example for the crossplatform mobile native testing + +```java +import io.appium.java_client.MobileElement; +import io.appium.java_client.pagefactory.*; + +@AndroidFindBy(someStrategy) +@iOSFindBy(someStrategy) +MobileElement someElement; + +@AndroidFindBy(someStrategy) //for the crossplatform mobile native +@iOSFindBy(someStrategy) //testing +List someElements; +``` + +# The fully cross platform examle + +```java +import org.openqa.selenium.remote.RemoteWebElement; +import io.appium.java_client.pagefactory.*; +import org.openqa.selenium.support.FindBy; + +//the fully cross platform examle +@FindBy(someStrategy) //for browser or web view html UI +@AndroidFindBy(someStrategy) //for Android native UI +@iOSFindBy(someStrategy) //for iOS native UI +RemoteWebElement someElement; + +//the fully cross platform examle +@FindBy(someStrategy) +@AndroidFindBy(someStrategy) //for Android native UI +@iOSFindBy(someStrategy) //for iOS native UI +List someElements; +``` + +# Also it is possible to define chained or any possible locators. + +## - Chained + +```java +import org.openqa.selenium.remote.RemoteWebElement; +import io.appium.java_client.pagefactory.*; +import org.openqa.selenium.support.FindBys; +import org.openqa.selenium.support.FindBy; + +@FindBys({@FindBy(someStrategy1), @FindBy(someStrategy2)}) +@AndroidFindBys({@AndroidFindBy(someStrategy1), @AndroidFindBy(someStrategy2)}) +@iOSFindBys({@iOSFindBy(someStrategy1), @iOSFindBy(someStrategy2)}) +RemoteWebElement someElement; + +@FindBys({@FindBy(someStrategy1), @FindBy(someStrategy2)}) +@AndroidFindBys({@AndroidFindBy(someStrategy1), @AndroidFindBy(someStrategy2)}) +@iOSFindBys({@iOSFindBy(someStrategy1), @iOSFindBy(someStrategy2)}) +List someElements; +``` + +## - Any possible + +```java +import org.openqa.selenium.remote.RemoteWebElement; +import io.appium.java_client.pagefactory.*; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.FindByAll; + +@FindAll({@FindBy(someStrategy1), @FindBy(someStrategy2)}) +@AndroidFindAll({@AndroidFindBy(someStrategy1), @AndroidFindBy(someStrategy2)}) +@iOSFindAll({@iOSFindBy(someStrategy1), @iOSFindBy(someStrategy2)}) +RemoteWebElement someElement; + +@FindAll({@FindBy(someStrategy1), @FindBy(someStrategy2)}) +@AndroidFindAll({@AndroidFindBy(someStrategy1), @AndroidFindBy(someStrategy2)}) +@iOSFindAll({@iOSFindBy(someStrategy1), @iOSFindBy(someStrategy2)}) +List someElements; +``` + +# Appium Java client is integrated with Selenium PageFactory by AppiumFieldDecorator. + +Object fields are populated as below: + +- +```java +import io.appium.java_client.pagefactory.*; +import org.openqa.selenium.support.PageFactory; + +PageFactory.initElements(new AppiumFieldDecorator(searchContext + /*searchContext is a WebDriver or WebElement + instance */), + pageObject //an instance of PageObject.class +); +``` + +- +```java +import io.appium.java_client.pagefactory.*; +import org.openqa.selenium.support.PageFactory; +import java.util.concurrent.TimeUnit; + +PageFactory.initElements(new AppiumFieldDecorator(searchContext, + /*searchContext is a WebDriver or WebElement + instance */ + 15, //default implicit waiting timeout for all strategies + TimeUnit.SECONDS), + pageObject //an instance of PageObject.class +); +``` + +- +```java +import io.appium.java_client.pagefactory.*; +import org.openqa.selenium.support.PageFactory; +import java.util.concurrent.TimeUnit; + +PageFactory.initElements(new AppiumFieldDecorator(searchContext, + /*searchContext is a WebDriver or WebElement + instance */ + new TimeOutDuration(15, //default implicit waiting timeout for all strategies + TimeUnit.SECONDS)), + pageObject //an instance of PageObject.class +); +``` + +If time of the waiting for elements differs from usual (longer, or shorter when element is needed only for quick checkings/assertions) then + +```java +import io.appium.java_client.pagefactory.*; + +@WithTimeout(timeOut = yourTime, timeUnit = yourTimeUnit) +RemoteWebElement someElement; + +@WithTimeout(timeOut = yourTime, timeUnit = yourTimeUnit) +List someElements; +``` + +# The additional feature. + +## The simple example +Let's imagine that the task is to check an Android client of the [http://www.rottentomatoes.com](http://www.rottentomatoes.com/). Let it be like a picture below + +![](https://cloud.githubusercontent.com/assets/4927589/11120641/51c1fda8-8962-11e5-8b17-323b5f236fce.png) Lets imagine that it is only a part of the screen. + +A typical page object could look like: + +```java +public class RottenTomatoesScreen { + //convinient locator + private List titles; + + //convinient locator + private List scores; + + //convinient locator + private List castings; + //element declaration goes on + + public String getMovieCount(){ + //....... + } + + public String getTitle(params){ + //....... + } + + public String getScore(params){ + //....... + } + + public String getCasting(params){ + //....... + } + + public void openMovieInfo(params){ + //....... + } + + //method declaration goes on +} +``` + +The description above can be decomposed. Let's work it out! + +Firstly a Movie-widget could be described this way: + +```java +import io.appium.java_client.pagefactory.Widget; +import org.openqa.selenium.WebElement; + +public class Movie extends Widget{ + protected Movie(WebElement element) { + super(element); + } + + //convinient locator + private AndroidElement title; + + //convinient locator + private AndroidElement score; + + //convinient locator + private AndroidElement casting; + + public String getTitle(params){ + //....... + } + + public String getScore(params){ + //....... + } + + public String getCasting(params){ + //....... + } + + public void openMovieInfo(params){ + ((AndroidElement) getWrappedElement()).tap(1, 1500); + } + +} +``` + +So, now page object looks + +```java +public class RottenTomatoesScreen { + + @AndroidFindBy(a locator which convinient to find a single movie-root - element) + private List movies; + + //element declaration goes on + + public String getMovieCount(){ + return movies.size(); + } + + public Movie getMovie(int index){ + //any interaction with sub-elements of a movie-element + //will be performed outside of the page-object instance + return movie.get(index); + } + //method declaration goes on +} +``` + +### Ok. What if Movie-class is reused and a wrapped root element is usually found by the same locator? + +Then +```java +//the class is annotated !!! +@AndroidFindBy(a locator which convinient to find a single movie-root - element) +public class Movie extends Widget{ +... +} + +``` +and + +```java +public class RottenTomatoesScreen { + //!!! locator is not necessary at this case + private List movies; +... +} +``` + +### Ok. What if movie list is not a whole screen? E.g. we want to describe it as a widget with nested movies. + +Then: + +```java +//with the usual locator or without it +public class Movies extends Widget{ + + //with a custom locator or without it + private List movies; +... +} +``` + +and + +```java +public class RottenTomatoesScreen { + + //with a custom locator or without it + Movies movies; +... +} +``` + +### Good! How to poputate all these fields? + +As usual: + +```java +RottenTomatoesScreen screen = new RottenTomatoesScreen(); +PageFactory.initElements(new AppiumFieldDecorator(searchContext /*WebDriver or WebElement + instance */), screen); +``` + + +## Specification + +A class which describes a widget or group of elements should extend + +```java +io.appium.java_client.pagefactory.Widget; +``` + +Any widget/group of elements can be described it terms of sub-elements or nested sub-widgets. +Appium-specific annotations are used for this purpose. + +### Any class which describes the real widget or group of elements can be annotated + +That means that when the same "widget" is used frequently and any root element of this can be found by the same locator then user can + +```java +@FindBy(relevant locator) //how to find a root element +public class UsersWidget extends Widget{ + + @FindBy(relevant locator) //this element will be found + //using the root element + WebElement subElement1; + + @FindBy(relevant locator) //this element will be found + //using the root element + WebElement subElement2; + + @FindBy(relevant locator) //a root element + //of this widget is the sub-element which + //will be found from top-element + UsersWidget subWidget; + + //and so on.. +} +``` + +and then it is enough + +```java + //above is the other field declaration + + UsersWidget widget; + + //below is the other field/method declaration +``` + +If the widget really should be found using an another locator then + +```java + //above is the other field declaration + @FindBy(another relevant locator) //this locator overrides + //the declared in the using class + UsersWidget widget; + + //below is the other field/method declaration +``` + +### Ok. What should users do if they want to implement a subclass which describes a similar group of elements for the same platform? + +There is nothing special. + +```java +@FindBy(relevant locator) //how to find a root element +public class UsersWidget extends Widget{ +... +} + +``` +```java +//at this case the root element will be found by the locator +//which is declared in superclass +public class UsersOverriddenWidget extends UsersWidget { +... +} +``` + +and + +```java +@FindBy(relevant locator2) //this locator overrides +//all locators declared in superclasses +public class UsersOverriddenWidget2 extends UsersWidget { +... +} +``` + +### Is it possible to reuse "widgets" in crossplatform testing? + +If there is no special details of interaction with an application browser version and/or versions for different mobile OS's then + + +```java +@FindBy(relevant locator for browser/webview html or by default) +@AndroidFindBy(relevant locator for Android UI automator) +@iOSFindBy(relevant locator for iOS UI automation) +public class UsersWidget extends Widget { + + @FindBy(relevant locator for browser/webview html or by default) + @AndroidFindBy(relevant locator for Android UI automator) + @iOSFindBy(relevant locator for iOS UI automation) + RemoteWebElement subElement1; + + @FindBy(relevant locator for browser/webview html or by default) + @AndroidFindBy(relevant locator for Android UI automator) + @iOSFindBy(relevant locator for iOS UI automation) + RemoteWebElement subElement2; + + //overrides a html/default + //locator declared in the used class + @FindBy(relevant locator for browser/webview html or by default) + //overrides an Android UI automator + //locator declared in the used class + @AndroidFindBy(relevant locator for Android UI automator) + //overrides an iOS UI automation + //locator declared in the using class + @iOSFindBy(relevant locator for iOS UI automation) + UsersWidget subWidget; + + //and so on.. +} +``` + +### What if interaction with a "widget" has special details for each used platform, but the same at high-level + +Then it is possible + +```java +public /*abstract*/ class DefaultAbstractUsersWidget extends Widget{ + +} +``` + +and + +```java +@FindBy(locator) +public class UsersWidgetForHtml extends DefaultAbstractUsersWidget { + +} +``` + +and + +```java +@AndroidFindBy(locator) +public class UsersWidgetForAndroid extends DefaultAbstractUsersWidget { + +} +``` + +and even + +```java +@iOSFindBy(locator) +public class UsersWidgetForIOS extends DefaultAbstractUsersWidget { + +} +``` + +and then + + +```java + import io.appium.java_client.pagefactory.OverrideWidget; + ... + + //above is the other field declaration + @OverrideWidget(html = UsersWidgetForHtml.class, + androidUIAutomator = UsersWidgetForAndroid.class, + iOSUIAutomation = UsersWidgetForIOS .class) + DefaultAbstractUsersWidget widget; + + //below is the other field/method declaration +``` + +This use case has some restrictions; + +- All classes which are declared by the OverrideWidget annotation should be subclasses of the class declared by field + +- All classes which are declared by the OverrideWidget should not be abstract. If a declared class is overriden partially like + +```java + //above is the other field declaration + + @OverrideWidget(iOSUIAutomation = UsersWidgetForIOS .class) + DefaultUsersWidget widget; //lets assume that there are differences of + //interaction with iOS and by default we use DefaultUsersWidget. + //Then DefaultUsersWidget should not be abstract too. + // + + //below is the other field/method declaration +``` + +- for now it is not possible to + +```java + import io.appium.java_client.pagefactory.OverrideWidget; + ... + + //above is the other field declaration + @OverrideWidget(html = UsersWidgetForHtml.class, + androidUIAutomator = UsersWidgetForAndroid.class, + iOSUIAutomation = UsersWidgetForIOS .class) + DefaultAbstractUsersWidget widget; + + //below is the other field/method declaration + + //user's code + ((UsersWidgetForAndroid) widget).doSpecialWorkForAndroing() +``` + +The workaround: + +```java + import io.appium.java_client.pagefactory.OverrideWidget; + ... + + //above is the other field declaration + @OverrideWidget(html = UsersWidgetForHtml.class, + androidUIAutomator = UsersWidgetForAndroid.class, + iOSUIAutomation = UsersWidgetForIOS .class) + DefaultAbstractUsersWidget widget; + + //below is the other field/method declaration + + //user's code + ((UsersWidgetForAndroid) widget.getSelfReference()).doSpecialWorkForAndroing() +``` + +### Good! What about widget lists? + +All that has been mentioned above is true for "widget" lists. + +### One more restriction + +It is strongly recommended to implement each subclass of __io.appium.java_client.pagefactory.Widget__ with this constructor + +```java + public /*or any other available modifier*/ WidgetSubclass(WebElement element) { + super(element); + } +``` \ No newline at end of file diff --git a/archive/docs/The-event_firing.md b/archive/docs/The-event_firing.md new file mode 100644 index 000000000..38071a9a7 --- /dev/null +++ b/archive/docs/The-event_firing.md @@ -0,0 +1,125 @@ +since 4.1.0 + +# The purpose + +This feature allows end user to organize the event logging on the client side. Also this feature may be useful in a binding with standard or custom reporting +frameworks. + + +# The API + +The API was designed the way which allows end user to select events (searching, navigation, exception throwing etc.) which should be listened to. It contains +the following list of interfaces (new items may be added further): + +- `io.appium.java_client.events.api.Listener` is the basic interface +- `io.appium.java_client.events.api.general.AlertEventListener` is for the listening to alerts +- `io.appium.java_client.events.api.general.ElementEventListener` is for the listening to actions related to elements +- `io.appium.java_client.events.api.general.JavaScriptEventListener` is for the listening to java script executing +- `io.appium.java_client.events.api.general.ListensToException` is for the listening to exceptions which are thrown +- `io.appium.java_client.events.api.general.NavigationEventListener` is for the listening to events related to navigation +- `io.appium.java_client.events.api.general.SearchingEventListener` is for the listening to events related to the searching. +- `io.appium.java_client.events.api.general.WindowEventListener` is for the listening to actions on a window +- `io.appium.java_client.events.api.mobile.ContextEventListener` is for the listening to the switching to mobile context +- `io.appium.java_client.events.api.mobile.RotationEventListener` is for the listening to screen rotation +- `io.appium.java_client.events.api.general.AppiumWebDriverEventListener` was added to provide the compatibility with +user's implementation of `org.openqa.selenium.support.events.WebDriverEventListener`. Also it extends some interfaces above. + +# Briefly about the engine. + +This is pretty similar solution as the `org.openqa.selenium.support.events.EventFiringWebDriver` of the Selenium project. You +can read about this thing there [The blog post](http://seleniumworks.blogspot.ru/2014/02/eventfiringwebdriver.html). + +Here we were trying to improve existing drawbacks and restrictions using: + +- API splitting, see above. + +- the binding of some [Spring framework engines](https://projects.spring.io/spring-framework/) with [AspectJ](https://en.wikipedia.org/wiki/AspectJ). + +# How to use + +It is easy. + +```java +import io.appium.java_client.events.api.general.AlertEventListener; + +public class AlertListener implements AlertEventListener { +... +} + +... +import io.appium.java_client.events.api.general.ElementEventListener; + +public class ElementListener implements ElementEventListener { +... +} + +//and so on +... +import io.appium.java_client.events.EventFiringWebDriverFactory; +import io.appium.java_client.events.api.Listener; +... + +AndroidDriver driver = new AndroidDriver(parameters); +driver = EventFiringWebDriverFactory.getEventFiringWebDriver(driver, new AlertListener(), + new ElementListener()); + +//or +AndroidDriver driver2 = new AndroidDriver(parameters); +List listeners = new ArrayList<>(); +listeners.add(new AlertListener()); +listeners.add(new ElementListener()); +driver = EventFiringWebDriverFactory.getEventFiringWebDriver(driver2, listeners); +``` + +## What if there are listeners which used everywhere by default. + +In order to avoid the repeating actions an end user is free to do these things: + +- create folders `/META-INF/services` and put the file `io.appium.java_client.events.api.Listener` there. Please read about +[SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html). + +![image](https://cloud.githubusercontent.com/assets/4927589/16731325/24eab680-4780-11e6-8551-a3c72d4b9c38.png) + +- define the list of default listeners at the `io.appium.java_client.events.api.Listener` + +![image](https://cloud.githubusercontent.com/assets/4927589/16731509/2734a4e0-4781-11e6-81cb-ab64a5924c35.png) + +And then it is enough + +```java + +//and so on +... +import io.appium.java_client.events.EventFiringWebDriverFactory; +... + +AndroidDriver driver = new AndroidDriver(parameters); +driver = EventFiringWebDriverFactory.getEventFiringWebDriver(driver); +``` + +If there are listeners defined externally when this collection is merged with default set of listeners. + +# How to reuse customized WebDriverEventListener + +If an end user has their own `org.openqa.selenium.support.events.WebDriverEventListener` implementation then in order to +make it compatible with this engine it is enough to do the following. + + +```java +import org.openqa.selenium.support.events.WebDriverEventListener; +import io.appium.java_client.events.api.general.AppiumWebDriverEventListener; + +public class UsersWebDriverEventListener implements WebDriverEventListener, AppiumWebDriverEventListener { +... +} +``` + +or just + +```java +import io.appium.java_client.events.api.general.AppiumWebDriverEventListener; + +public class UsersWebDriverEventListener implements AppiumWebDriverEventListener { +... +} +``` diff --git a/archive/docs/The-starting-of-an-Android-app.md b/archive/docs/The-starting-of-an-Android-app.md new file mode 100644 index 000000000..31ff4203e --- /dev/null +++ b/archive/docs/The-starting-of-an-Android-app.md @@ -0,0 +1,88 @@ +# Steps: + +- you have to prepare environment for Android. Details are provided here: http://appium.io/slate/en/master/?java#setup-(android) + +- you have to download the desktop app [for Windows or Mac OS X](https://bitbucket.org/appium/appium.app/downloads/) or install it using _npm_ +_$ npm install -g appium_ or _$ npm install appium@required_version_ + +- it needs to launch the appium server. If you use the server installed via npm then + + _$ node **the_path_to_js_file** --arg1 value1 --arg2 value2_ +where **the_path_to_js_file** is the full path to **appium.js** file (if the node server version version <= 1.4.16) or **main.js** (if the node server version version >= 1.5.0). It is not necessary to use arguments. The list of arguments: http://appium.io/slate/en/master/?java#appium-server-arguments + + +# The starting of an app + +It looks like creation of a common [RemoteWebDriver](https://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/remote/RemoteWebDriver.html) instance. + +[Common capabilities](http://appium.io/slate/en/master/?java#the---default-capabilities-flag) + +[Android-specific capabilities](http://appium.io/slate/en/master/?java#android-only) + +[Common capabilities provided by Java client](http://appium.github.io/java-client/io/appium/java_client/remote/MobileCapabilityType.html) + +[Android-specific capabilities provided by Java client](http://appium.github.io/java-client/io/appium/java_client/remote/AndroidMobileCapabilityType.html) + +```java +import java.io.File; +import org.openqa.selenium.remote.DesiredCapabilities; +import io.appium.java_client.AppiumDriver; +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.MobileElement; +import java.net.URL; + +... +File app = new File("The absolute or relative path to an *.apk file"); +DesiredCapabilities capabilities = new DesiredCapabilities(); +capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); +capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); +//you are free to set additional capabilities +AppiumDriver driver = new AndroidDriver<>( +new URL("http://target_ip:used_port/wd/hub"), //if it needs to use locally started server +//then the target_ip is 127.0.0.1 or 0.0.0.0 +//the default port is 4723 +capabilities); +``` + +If it needs to start browser then: + +```java +import org.openqa.selenium.remote.DesiredCapabilities; +import io.appium.java_client.remote.MobileBrowserType; +import io.appium.java_client.AppiumDriver; +import io.appium.java_client.android.AndroidDriver; +import org.openqa.selenium.remote.RemoteWebElement; +import java.net.URL; + + +... +DesiredCapabilities capabilities = new DesiredCapabilities(); +capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); +capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, MobileBrowserType.CHROME); +//if it is necessary to use the default Android browser then MobileBrowserType.BROWSER +//is your choise +... +//you are free to set additional capabilities +AppiumDriver driver = new AndroidDriver<>( +new URL("http://target_ip:used_port/wd/hub"), capabilities); +``` + +or + +```java +import org.openqa.selenium.remote.DesiredCapabilities; +import io.appium.java_client.remote.MobileBrowserType; +import io.appium.java_client.remote.MobilePlatform; +import org.openqa.selenium.remote.RemoteWebDriver; +import java.net.URL; + + +... +DesiredCapabilities capabilities = new DesiredCapabilities(); +capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, MobilePlatform.ANDROID); +capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); +capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, MobileBrowserType.CHROME); +//you are free to set additional capabilities +RemoteWebDriver driver = new RemoteWebDriver( +new URL("http://target_ip:used_port/wd/hub"), capabilities); +``` \ No newline at end of file diff --git a/archive/docs/The-starting-of-an-app-using-Appium-node-server-started-programmatically.md b/archive/docs/The-starting-of-an-app-using-Appium-node-server-started-programmatically.md new file mode 100644 index 000000000..6b897c61d --- /dev/null +++ b/archive/docs/The-starting-of-an-app-using-Appium-node-server-started-programmatically.md @@ -0,0 +1,289 @@ +# Requirements +- Installed Node.js 0.12 or greater. + +- At least an appium server instance installed via __npm__. + +# The basic principle. + +It works the similar way as common [ChromeDriver](https://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/chrome/ChromeDriver.html), [InternetExplorerDriver](https://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/ie/InternetExplorerDriver.html) of Selenium project or [PhantomJSDriver](http://cdn.ivandemarino.me/phantomjsdriver-javadoc/org/openqa/selenium/phantomjs/PhantomJSDriver.html). They use subclasses of the [DriverService](https://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/remote/service/DriverService.html). + +# Which capabilities this feature provides + +This feature providese abilities and options of the starting of a local Appium node server. End users still able to open apps as usual + +```java + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, ""); + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); + capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); + capabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 120); + driver = new AndroidDriver<>(new URL("remoteOrLocalAddress"), capabilities); +``` + +when the server is launched locally\remotely. Also user is free to launch a local Appium node server and open their app for the further testing the following way: + +```java + DesiredCapabilities capabilities = new DesiredCapabilities(); + capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, ""); + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); + capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); + capabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 120); + driver = new AndroidDriver<>(capabilities); +``` + +# How to prepare the local service before the starting + + +## If there is no specific parameters then + +```java + import io.appium.java_client.service.local.AppiumDriverLocalService; + ... + + AppiumDriverLocalService service = AppiumDriverLocalService.buildDefaultService(); + service.start(); + ... + service.stop(); +``` + +### FYI + +There are possible problems related to local environment which could break this: +```java +AppiumDriverLocalService service = AppiumDriverLocalService.buildDefaultService(); +``` + +It is more usual for UNIX/LINUX-like OS's. Also there are situations when should be used an another Node.JS instance, e.g. the instance which is installed in the directory that differs from one defined at the PATH environmental variable. The same may be true for Appium node server (it is related to _appium.js_ file (v <= 1.4.16) and _main.js_ (v >= 1.5.0)). + +At this case user is able to set up values of the **NODE_BINARY_PATH** (The environmental variable used to define the path to executable NodeJS file (node.exe for WIN and node for Linux/MacOS X)) and the **APPIUM_BINARY_PATH** (The environmental variable used to define the path to executable appium.js (1.4.x and lower) or main.js (1.5.x and higher)) environmental variables/system properties. Also it is possible to define these values programmatically: + +```java +//appium.node.js.exec.path +System.setProperty(AppiumServiceBuilder.NODE_PATH , +"the path to the desired node.js executable"); + +System.setProperty(AppiumServiceBuilder.APPIUM_PATH , +"the path to the desired appium.js or main.js"); + +AppiumDriverLocalService service = AppiumDriverLocalService.buildDefaultService(); +``` + +## If there should be non default parameters specified then + +```java +import io.appium.java_client.service.local.AppiumDriverLocalService; +import io.appium.java_client.service.local.AppiumServiceBuilder; +import io.appium.java_client.service.local.flags.GeneralServerFlag; +... + +AppiumDriverLocalService service = AppiumDriverLocalService. +buildService(new AppiumServiceBuilder(). +withArgument(GeneralServerFlag.TEMP_DIRECTORY, + "The_path_to_the_temporary_directory")); +``` + +or + +```java +import io.appium.java_client.service.local.AppiumDriverLocalService; +import io.appium.java_client.service.local.AppiumServiceBuilder; +import io.appium.java_client.service.local.flags.GeneralServerFlag; +... + +AppiumDriverLocalService service = new AppiumServiceBuilder(). +withArgument(GeneralServerFlag.TEMP_DIRECTORY, + "The_path_to_the_temporary_directory").build(); +``` + +Lists of available server side flags are here: + +- io.appium.java_client.service.local.flags.GeneralServerFlag; +- io.appium.java_client.service.local.flags.AndroidServerFlag; +- io.appium.java_client.service.local.flags.IOSServerFlag + + +## Which parameters also can be defined + +- If it is necessary to define some specific port or any free port + +```java +new AppiumServiceBuilder().usingPort(4000); +``` + +or + +```java +new AppiumServiceBuilder().usingAnyFreePort(); +``` + +- If it is necessary to use another IP address + +```java +new AppiumServiceBuilder().withIPAddress("127.0.0.1"); +``` + +- If it is necessary to define output log file + +```java +import java.io.File; + ... + +new AppiumServiceBuilder().withLogFile(logFile); +``` + +- If it is necessary to define another Node.js executable file + +```java +import java.io.File; + +... + +new AppiumServiceBuilder().usingDriverExecutable(nodeJSExecutable); +``` + +- If it is necessary to define another appium.js/main.js file + +```java +import java.io.File; + +... +//appiumJS is the full or relative path to +//the appium.js (v<=1.4.16) or maim.js (v>=1.5.0) +new AppiumServiceBuilder().withAppiumJS(new File(appiumJS)); +``` + +- It is possible to define server capabilities (node server v >= 1.5.0) + +```java +DesiredCapabilities serverCapabilities = new DesiredCapabilities(); +...//the capability filling + +AppiumServiceBuilder builder = new AppiumServiceBuilder(). +withCapabilities(serverCapabilities); +AppiumDriverLocalService service = builder.build(); +service.start(); +... +service.stop(); +``` + +Capabilities which are used by a builder can be completed/orerriden any similar way: + +```java +DesiredCapabilities serverCapabilities = new DesiredCapabilities(); +serverCapabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android"); +serverCapabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); +serverCapabilities.setCapability(MobileCapabilityType.FULL_RESET, true); +serverCapabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 60); +serverCapabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); +serverCapabilities.setCapability(AndroidMobileCapabilityType.CHROMEDRIVER_EXECUTABLE, +chrome.getAbsolutePath()); //this capability set can be used for all cases + +AppiumServiceBuilder builder = new AppiumServiceBuilder(). +withCapabilities(serverCapabilities); +AppiumDriverLocalService service = builder.build(); + +DesiredCapabilities clientCapabilities = new DesiredCapabilities(); +clientCapabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, +"io.appium.android.apis"); +clientCapabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, +".view.WebView1"); +``` + +then + +```java +AndroidDriver driver = +new AndroidDriver<>(service, clientCapabilities); +``` + +or + +```java +AndroidDriver driver = +new AndroidDriver<>(builder, clientCapabilities); +``` + +or + +```java +service.start(); +AndroidDriver driver = +new AndroidDriver<>(service.getUrl(), clientCapabilities); +``` + +# How to create an AppiumDriver instance + +Many constructors of [AndroidDriver](http://appium.github.io/java-client/io/appium/java_client/android/AndroidDriver.html)/[IOSDriver](http://appium.github.io/java-client/io/appium/java_client/ios/IOSDriver.html) use [AppiumDriverLocalService](http://appium.github.io/java-client/io/appium/java_client/service/local/AppiumDriverLocalService.html) or [AppiumServiceBuilder](http://appium.github.io/java-client/io/appium/java_client/service/local/AppiumServiceBuilder.html) as parameters. +The list of constructors is below. + +```java +public AndroidDriver(URL remoteAddress, + org.openqa.selenium.Capabilities desiredCapabilities) + +public AndroidDriver(URL remoteAddress, + org.openqa.selenium.remote.http.HttpClient.Factory httpClientFactory, + org.openqa.selenium.Capabilities desiredCapabilities) + +public AndroidDriver(AppiumDriverLocalService service, + org.openqa.selenium.Capabilities desiredCapabilities) + +public AndroidDriver(AppiumDriverLocalService service, + org.openqa.selenium.remote.http.HttpClient.Factory httpClientFactory, + org.openqa.selenium.Capabilities desiredCapabilities) + +public AndroidDriver(AppiumServiceBuilder builder, + org.openqa.selenium.Capabilities desiredCapabilities) + +public AndroidDriver(AppiumServiceBuilder builder, + org.openqa.selenium.remote.http.HttpClient.Factory httpClientFactory, + org.openqa.selenium.Capabilities desiredCapabilities) + +public AndroidDriver(org.openqa.selenium.remote.http.HttpClient.Factory httpClientFactory, + org.openqa.selenium.Capabilities desiredCapabilities) + +public AndroidDriver(org.openqa.selenium.Capabilities desiredCapabilities) +``` + +```java +public IOSDriver(URL remoteAddress, + org.openqa.selenium.Capabilities desiredCapabilities) + +public IOSDriver(URL remoteAddress, + org.openqa.selenium.remote.http.HttpClient.Factory httpClientFactory, + org.openqa.selenium.Capabilities desiredCapabilities) + +public IOSDriver(AppiumDriverLocalService service, + org.openqa.selenium.Capabilities desiredCapabilities) + +public IOSDriver(AppiumDriverLocalService service, + org.openqa.selenium.remote.http.HttpClient.Factory httpClientFactory, + org.openqa.selenium.Capabilities desiredCapabilities) + +public IOSDriver(AppiumServiceBuilder builder, + org.openqa.selenium.Capabilities desiredCapabilities) + +public IOSDriver(AppiumServiceBuilder builder, + org.openqa.selenium.remote.http.HttpClient.Factory httpClientFactory, + org.openqa.selenium.Capabilities desiredCapabilities) + +public IOSDriver(org.openqa.selenium.remote.http.HttpClient.Factory httpClientFactory, + org.openqa.selenium.Capabilities desiredCapabilities) + +public IOSDriver(org.openqa.selenium.Capabilities desiredCapabilities) +``` + +An instance of __AppiumDriverLocalService__ which has passed through constructors will be stopped when + +```java + driver.quit(); +``` + +If it is necessary to keep the service alive during a long time then something like that is available + +```java + service.start(); + + .... + + new IOSDriver(service.getUrl(), capabilities) +``` diff --git a/archive/docs/The-starting-of-an-iOS-app.md b/archive/docs/The-starting-of-an-iOS-app.md new file mode 100644 index 000000000..a470c4137 --- /dev/null +++ b/archive/docs/The-starting-of-an-iOS-app.md @@ -0,0 +1,91 @@ +# Steps: + +- you have to prepare environment for iOS. Details are provided here: http://appium.io/slate/en/master/?ruby#system-setup-(ios) + +- you have to download the desktop app [for Mac OS X](https://bitbucket.org/appium/appium.app/downloads/) or install it using _npm_ +_$ npm install -g appium_ or _$ npm install appium@required_version_ + +- it needs to launch the appium server. If you use the server installed via npm then + + _$ node **the_path_to_js_file** --arg1 value1 --arg2 value2_ +where **the_path_to_js_file** is the full path to **appium.js** file (if the node server version version <= 1.4.16) or **main.js** (if the node server version version >= 1.5.0). It is not necessary to use arguments. The list of arguments: http://appium.io/slate/en/master/?ruby#appium-server-arguments + +# The starting of an app + +It looks like creation of a common [RemoteWebDriver](https://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/remote/RemoteWebDriver.html) instance. + +[Common capabilities](http://appium.io/slate/en/master/?ruby#the---default-capabilities-flag) + +[iOS-specific capabilities](http://appium.io/slate/en/master/?ruby#ios-only) + +[Common capabilities provided by Java client](http://appium.github.io/java-client/io/appium/java_client/remote/MobileCapabilityType.html) + +[iOS-specific capabilities provided by Java client](http://appium.github.io/java-client/io/appium/java_client/remote/IOSMobileCapabilityType.html) + +```java +import java.io.File; +import org.openqa.selenium.remote.DesiredCapabilities; +import io.appium.java_client.AppiumDriver; +import io.appium.java_client.ios.IOSDriver; +import io.appium.java_client.MobileElement; +import java.net.URL; + +... +File app = new File("The absolute or relative path to an *.app, *.zip or ipa file"); +DesiredCapabilities capabilities = new DesiredCapabilities(); +capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone Simulator"); +capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "The_target_version"); +//The_target_version is the supported iOS version, e.g. 8.1, 8.2, 9.2 etc +capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); +//you are free to set additional capabilities +AppiumDriver driver = new IOSDriver<>( +new URL("http://target_ip:used_port/wd/hub"), //if it needs to use locally started server +//then the target_ip is 127.0.0.1 or 0.0.0.0 +//the default port is 4723 +capabilities); +``` + +If it needs to start browser then: + +```java +import org.openqa.selenium.remote.DesiredCapabilities; +import io.appium.java_client.remote.MobileBrowserType; +import io.appium.java_client.AppiumDriver; +import io.appium.java_client.ios.IOSDriver; +import org.openqa.selenium.remote.RemoteWebElement; +import java.net.URL; + + +... +DesiredCapabilities capabilities = new DesiredCapabilities(); +capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone Simulator"); +capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "The_target_version"); +//The_target_version is the supported iOS version, e.g. 8.1, 8.2, 9.2 etc +capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, MobileBrowserType.SAFARI); +... +//you are free to set additional capabilities +AppiumDriver driver = new IOSDriver<>( +new URL("http://target_ip:used_port/wd/hub"), capabilities); +``` + +or + +```java +import io.appium.java_client.remote.MobilePlatform; +import org.openqa.selenium.remote.DesiredCapabilities; +import io.appium.java_client.remote.MobileBrowserType; +import org.openqa.selenium.remote.RemoteWebDriver; +import java.net.URL; + + +... +DesiredCapabilities capabilities = new DesiredCapabilities(); +capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, MobilePlatform.IOS); +capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone Simulator"); +capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "The_target_version"); +//The_target_version is the supported iOS version, e.g. 8.1, 8.2, 9.2 etc +capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, MobileBrowserType.SAFARI); +//you are free to set additional capabilities +RemoteWebDriver driver = new RemoteWebDriver( +new URL("http://target_ip:used_port/wd/hub"), capabilities); +``` diff --git a/archive/docs/Touch-actions.md b/archive/docs/Touch-actions.md new file mode 100644 index 000000000..20467c458 --- /dev/null +++ b/archive/docs/Touch-actions.md @@ -0,0 +1 @@ +... under constraction \ No newline at end of file diff --git a/docs/Functions.md b/docs/Functions.md new file mode 100644 index 000000000..119629975 --- /dev/null +++ b/docs/Functions.md @@ -0,0 +1,147 @@ +Appium java client has some features based on [Java 8 Functional interfaces](https://www.oreilly.com/learning/java-8-functional-interfaces). + +#Conditions + +```java +io.appium.java_client.functions.AppiumFunction +``` +It extends +```java +java.util.function.Function +``` +and +```java +com.google.common.base.Function +``` +to make end user available to use _org.openqa.selenium.support.ui.Wait_. There is additional interface +```java +io.appium.java_client.functions.ExpectedCondition +``` +which extends +```java +io.appium.java_client.functions.AppiumFunction +``` + +and + +```java +org.openqa.selenium.support.ui.ExpectedCondition +``` + +This feature provides the ability to create complex condition of the waiting for something. + +```java +//waiting for elements + private final AppiumFunction> searchingFunction = input -> { + List result = input.findElements(By.tagName("a")); + + if (result.size() > 0) { + return result; + } + return null; +}; + +//waiting for some context using regular expression pattern +private final AppiumFunction contextFunction = input -> { + Set contexts = driver.getContextHandles(); + String current = driver.getContext(); + contexts.forEach(context -> { + Matcher m = input.matcher(context); + if (m.find()) { + driver.context(context); + } + }); + if (!current.equals(driver.getContext())) { + return driver; + } + return null; +}; +``` + +##using one function as pre-condition + +```java +@Test public void tezt() { + .... + Wait wait = new FluentWait<>(Pattern.compile("WEBVIEW")) + .withTimeout(30, TimeUnit.SECONDS); + List elements = wait.until(searchingFunction.compose(contextFunction)); + .... +} +``` + +##using one function as post-condition + +```java +import org.openqa.selenium.support.ui.FluentWait; +import org.openqa.selenium.support.ui.Wait; + +@Test public void tezt() { + .... + Wait wait = new FluentWait<>(Pattern.compile("WEBVIEW")) + .withTimeout(30, TimeUnit.SECONDS); + List elements = wait.until(contextFunction.andThen(searchingFunction)); + .... +} +``` + +#Touch action supplier + +[About touch actions](https://github.com/appium/java-client/blob/master/docs/Touch-actions.md) + +You can use suppliers to declare touch/multitouch actions for some screens/tests. Also it is possible to +create gesture libraries/utils using suppliers. Appium java client provides this interface + +```java +io.appium.java_client.functions.ActionSupplier +``` + +##Samples + +```java +private final ActionSupplier horizontalSwipe = () -> { + driver.findElementById("io.appium.android.apis:id/gallery"); + + AndroidElement gallery = driver.findElementById("io.appium.android.apis:id/gallery"); + List images = gallery + .findElementsByClassName("android.widget.ImageView"); + Point location = gallery.getLocation(); + Point center = gallery.getCenter(); + + return new TouchAction(driver).press(images.get(2), -10, center.y - location.y) + .waitAction(2000).moveTo(gallery, 10, center.y - location.y).release(); +}; + +private final ActionSupplier verticalSwiping = () -> + new TouchAction(driver).press(driver.findElementByAccessibilityId("Gallery")) + .waitAction(2000).moveTo(driver.findElementByAccessibilityId("Auto Complete")).release(); + +@Test public void tezt() { + ... + horizontalSwipe.get().perform(); + ... + verticalSwiping.get().perform(); + ... +} +``` + +```java +public class GestureUtils { + + public static ActionSupplier swipe(final AppiumDriver driver, final params) { + return () -> { + new TouchAction(driver).press(params) + .waitAction(params).moveTo(params).release(); + }; + } +} + +public class SomeTest { + @Test public void tezt() { + ... + GestureUtils.swipe(driver, params).get().perform(); + ... + } +} + +``` \ No newline at end of file diff --git a/docs/Installing-the-project.md b/docs/Installing-the-project.md index a11590564..9c7aad712 100644 --- a/docs/Installing-the-project.md +++ b/docs/Installing-the-project.md @@ -1,20 +1,30 @@ -[Download the jar from Maven](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22io.appium%22%20AND%20a%3A%22java-client%22) or add the following to pom.xml: +#Requirements + +Firstly you should install appium server. [Appium getting started](http://appium.io/getting-started.html). The version 1.6.3 or greater is recommended. + +Since version 5.x there many features based on Java 8. So we recommend to install JDK SE 8 and provide that source compatibility. + +#Maven + +Add the following to pom.xml: ``` io.appium java-client - 4.1.2 + ${version.you.require} + test ``` -It currently depends on selenium-java 2.53.1. If it is necessary to use another version of Selenium then you can configure pom.xml as follows: +If it is necessary to change the version of Selenium then you can configure pom.xml as follows: ``` io.appium java-client - 4.1.1 + ${version.you.require} + test org.seleniumhq.selenium @@ -29,3 +39,45 @@ It currently depends on selenium-java 2.53.1. If it is necessary to use another ${selenium.version.you.require} ``` + +#Gardle + +Add the following to build.gradle: + +``` +repositories { + jcenter() + maven { + url "http://repo.maven.apache.org/maven2" + } +} + +dependencies { + ... + testCompile group: 'io.appium', name: 'java-client', version: requiredVersion + ... +} +``` + +If it is necessary to change the version of Selenium then you can configure pom.xml as follows: + +``` +repositories { + jcenter() + maven { + url "http://repo.maven.apache.org/maven2" + } +} + +dependencies { + ... + testCompile group: 'io.appium', name: 'java-client', version: requiredVersion { + exclude module: 'selenium-java' + } + + testCompile group: 'org.seleniumhq.selenium', name: 'selenium-java', + version: requiredSeleniumVersion + ... +} +``` + diff --git a/docs/Page-objects.md b/docs/Page-objects.md index 7729bd14c..9a34063ca 100644 --- a/docs/Page-objects.md +++ b/docs/Page-objects.md @@ -82,27 +82,6 @@ List someElements; ## - Chained -### If you use build versions < 5.x.x - -```java -import org.openqa.selenium.remote.RemoteWebElement; -import io.appium.java_client.pagefactory.*; -import org.openqa.selenium.support.FindBys; -import org.openqa.selenium.support.FindBy; - -@FindBys({@FindBy(someStrategy1), @FindBy(someStrategy2)}) -@AndroidFindBys({@AndroidFindBy(someStrategy1), @AndroidFindBy(someStrategy2)}) -@iOSFindBys({@iOSFindBy(someStrategy1), @iOSFindBy(someStrategy2)}) -RemoteWebElement someElement; - -@FindBys({@FindBy(someStrategy1), @FindBy(someStrategy2)}) -@AndroidFindBys({@AndroidFindBy(someStrategy1), @AndroidFindBy(someStrategy2)}) -@iOSFindBys({@iOSFindBy(someStrategy1), @iOSFindBy(someStrategy2)}) -List someElements; -``` - -### If you use build versions >= 5.x.x - ```java import org.openqa.selenium.remote.RemoteWebElement; import io.appium.java_client.pagefactory.*; @@ -145,27 +124,6 @@ List someElements; ## - Any possible -### If you use build versions < 5.x.x - -```java -import org.openqa.selenium.remote.RemoteWebElement; -import io.appium.java_client.pagefactory.*; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.FindByAll; - -@FindAll({@FindBy(someStrategy1), @FindBy(someStrategy2)}) -@AndroidFindAll({@AndroidFindBy(someStrategy1), @AndroidFindBy(someStrategy2)}) -@iOSFindAll({@iOSFindBy(someStrategy1), @iOSFindBy(someStrategy2)}) -RemoteWebElement someElement; - -@FindAll({@FindBy(someStrategy1), @FindBy(someStrategy2)}) -@AndroidFindAll({@AndroidFindBy(someStrategy1), @AndroidFindBy(someStrategy2)}) -@iOSFindAll({@iOSFindBy(someStrategy1), @iOSFindBy(someStrategy2)}) -List someElements; -``` - -### If you use build versions >= 5.x.x - ```java import org.openqa.selenium.remote.RemoteWebElement; import io.appium.java_client.pagefactory.*; diff --git a/docs/Tech-stack.md b/docs/Tech-stack.md new file mode 100644 index 000000000..f684c3334 --- /dev/null +++ b/docs/Tech-stack.md @@ -0,0 +1,19 @@ +![](https://cloud.githubusercontent.com/assets/4927589/21467582/df8ab94e-ca03-11e6-969c-c6d30c6add67.png) +![](https://cloud.githubusercontent.com/assets/4927589/21467509/a97e084e-ca01-11e6-9d04-4f2b8e1c72df.png) +![](https://cloud.githubusercontent.com/assets/4927589/21467524/187a333a-ca02-11e6-8e3c-14c411448fdb.png) +![](https://cloud.githubusercontent.com/assets/4927589/21467531/6f576f1a-ca02-11e6-9f2b-2551ea0e0753.png) + **AspectJ** and **CGlib** + +This project is based on [Selenium java client](https://github.com/SeleniumHQ/selenium/tree/master/java/client). It already depends on it and extends it to mobile platforms. + +This project is built by [gradle](https://gradle.org/) + +Also tech stack includes [Spring framework](https://projects.spring.io/spring-framework/) in binding with AspectJ. This is used by [event firing feature](https://github.com/appium/java-client/blob/master/docs/The-event_firing.md). Also **CGlib** is used by [Page Object tools](https://github.com/appium/java-client/blob/master/docs/Page-objects.md). + +It is the client framework. It is the thin client which just sends requests to Appium server and receives responses. Also it has some +high-level features which were designed to simplify user's work. + +#It supports: + +![](https://cloud.githubusercontent.com/assets/4927589/21467612/4b6b3f70-ca05-11e6-9a31-d3820e98dac6.png) +![](https://cloud.githubusercontent.com/assets/4927589/21467614/73883828-ca05-11e6-846d-3ed8847a7e08.jpg) +![](https://cloud.githubusercontent.com/assets/4927589/21467621/aab3ff6c-ca05-11e6-9170-2e7a19d3307c.png) \ No newline at end of file diff --git a/docs/The-starting-of-an-Android-app.md b/docs/The-starting-of-an-Android-app.md index 31ff4203e..3edad343e 100644 --- a/docs/The-starting-of-an-Android-app.md +++ b/docs/The-starting-of-an-Android-app.md @@ -1,14 +1,11 @@ # Steps: -- you have to prepare environment for Android. Details are provided here: http://appium.io/slate/en/master/?java#setup-(android) +- you have to prepare environment for Android. [Details are provided here](http://appium.io/slate/en/master/?java#setup-(android)) -- you have to download the desktop app [for Windows or Mac OS X](https://bitbucket.org/appium/appium.app/downloads/) or install it using _npm_ -_$ npm install -g appium_ or _$ npm install appium@required_version_ +- it needs to launch the appium server. You can launch Appium desktop application. If you use the server installed via npm then -- it needs to launch the appium server. If you use the server installed via npm then - - _$ node **the_path_to_js_file** --arg1 value1 --arg2 value2_ -where **the_path_to_js_file** is the full path to **appium.js** file (if the node server version version <= 1.4.16) or **main.js** (if the node server version version >= 1.5.0). It is not necessary to use arguments. The list of arguments: http://appium.io/slate/en/master/?java#appium-server-arguments + _$ node **the_path_to_main.js_file** --arg1 value1 --arg2 value2_ +It is not necessary to use arguments. [The list of arguments](http://appium.io/slate/en/master/?java#appium-server-arguments) # The starting of an app @@ -36,53 +33,120 @@ File app = new File("The absolute or relative path to an *.apk file"); DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); +capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, MobilePlatform.ANDROID); //you are free to set additional capabilities -AppiumDriver driver = new AndroidDriver<>( +AppiumDriver driver = new AppiumDriver<>( new URL("http://target_ip:used_port/wd/hub"), //if it needs to use locally started server //then the target_ip is 127.0.0.1 or 0.0.0.0 //the default port is 4723 capabilities); ``` -If it needs to start browser then: +or ```java +import java.io.File; import org.openqa.selenium.remote.DesiredCapabilities; -import io.appium.java_client.remote.MobileBrowserType; import io.appium.java_client.AppiumDriver; -import io.appium.java_client.android.AndroidDriver; -import org.openqa.selenium.remote.RemoteWebElement; +import io.appium.java_client.MobileElement; import java.net.URL; - ... +File app = new File("The absolute or relative path to an *.apk file"); DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); -capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, MobileBrowserType.CHROME); -//if it is necessary to use the default Android browser then MobileBrowserType.BROWSER -//is your choise -... +capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); //you are free to set additional capabilities AppiumDriver driver = new AndroidDriver<>( -new URL("http://target_ip:used_port/wd/hub"), capabilities); +new URL("http://target_ip:used_port/wd/hub"), //if it needs to use locally started server +//then the target_ip is 127.0.0.1 or 0.0.0.0 +//the default port is 4723 +capabilities); ``` -or + +##If it needs to start browser then + +This capability should be used ```java -import org.openqa.selenium.remote.DesiredCapabilities; -import io.appium.java_client.remote.MobileBrowserType; -import io.appium.java_client.remote.MobilePlatform; -import org.openqa.selenium.remote.RemoteWebDriver; -import java.net.URL; +capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, MobileBrowserType.CHROME); +//if it is necessary to use the default Android browser then MobileBrowserType.BROWSER +//is your choice +``` +##There are three automation types + +```java +capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.SELENDROID); +``` + +This automation type is usually recommended for old versions (<4.2) of Android. + +Default Android UIAutomator does not require any specific capability. However you can +```java +capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.APPIUM); +``` + +You have to define this automation type to be able to use Android UIAutomator2 for new Android versions +```java +capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2); +``` + +# Possible cases + +You can use ```io.appium.java_client.AppiumDriver``` and ```io.appium.java_client.android.AndroidDriver``` as well. The main difference +is that ```AndroidDriver``` implements all API that describes interaction with Android native/hybrid app. ```AppiumDriver``` allows to +use Android-specific API eventually. + + _The sample of the activity starting by_ ```io.appium.java_client.AppiumDriver``` + + ```java + import io.appium.java_client.android.StartsActivity; ... -DesiredCapabilities capabilities = new DesiredCapabilities(); -capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, MobilePlatform.ANDROID); -capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); -capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, MobileBrowserType.CHROME); -//you are free to set additional capabilities -RemoteWebDriver driver = new RemoteWebDriver( -new URL("http://target_ip:used_port/wd/hub"), capabilities); -``` \ No newline at end of file + +StartsActivity startsActivity = new StartsActivity() { + @Override + public Response execute(String driverCommand, Map parameters) { + return driver.execute(driverCommand, parameters); + } + + @Override + public Response execute(String driverCommand) { + return driver.execute(driverCommand); + } +}; + +StartsActivity startsActivity.startActivity("your.package.name", ".ActivityName"); + ``` + +_Samples of the searching by AndroidUIAutomator using_ ```io.appium.java_client.AppiumDriver``` + +```java +import io.appium.java_client.FindsByAndroidUIAutomator; +import io.appium.java_client.android.AndroidElement; + +... + +FindsByAndroidUIAutomator findsByAndroidUIAutomator = + new FindsByAndroidUIAutomator() { + @Override + public AndroidElement findElement(String by, String using) { + return driver.findElement(by, using); + } + + @Override + public List findElements(String by, String using) { + return driver.findElements(by, using); + }; +}; + +findsByAndroidUIAutomator.findElementByAndroidUIAutomator("automatorString"); +``` + +```java +driver.findElement(MobileBy.AndroidUIAutomator("automatorString")); +``` + +All that ```AndroidDriver``` can do by design. \ No newline at end of file diff --git a/docs/The-starting-of-an-app-using-Appium-node-server-started-programmatically.md b/docs/The-starting-of-an-app-using-Appium-node-server-started-programmatically.md index 6b897c61d..6065d37e7 100644 --- a/docs/The-starting-of-an-app-using-Appium-node-server-started-programmatically.md +++ b/docs/The-starting-of-an-app-using-Appium-node-server-started-programmatically.md @@ -1,5 +1,5 @@ # Requirements -- Installed Node.js 0.12 or greater. +- Installed Node.js 4 or greater. - At least an appium server instance installed via __npm__. diff --git a/docs/The-starting-of-an-iOS-app.md b/docs/The-starting-of-an-iOS-app.md index a470c4137..a8a6a2c2c 100644 --- a/docs/The-starting-of-an-iOS-app.md +++ b/docs/The-starting-of-an-iOS-app.md @@ -1,14 +1,11 @@ # Steps: -- you have to prepare environment for iOS. Details are provided here: http://appium.io/slate/en/master/?ruby#system-setup-(ios) +- you have to prepare environment for iOS. [Details are provided here](http://appium.io/slate/en/master/?ruby#system-setup-(ios)) -- you have to download the desktop app [for Mac OS X](https://bitbucket.org/appium/appium.app/downloads/) or install it using _npm_ -_$ npm install -g appium_ or _$ npm install appium@required_version_ - -- it needs to launch the appium server. If you use the server installed via npm then +- it needs to launch the appium server. You can launch Appium desktop application. If you use the server installed via npm then _$ node **the_path_to_js_file** --arg1 value1 --arg2 value2_ -where **the_path_to_js_file** is the full path to **appium.js** file (if the node server version version <= 1.4.16) or **main.js** (if the node server version version >= 1.5.0). It is not necessary to use arguments. The list of arguments: http://appium.io/slate/en/master/?ruby#appium-server-arguments +It is not necessary to use arguments. [The list of arguments](http://appium.io/slate/en/master/?java#appium-server-arguments) # The starting of an app @@ -22,11 +19,11 @@ It looks like creation of a common [RemoteWebDriver](https://selenium.googlecode [iOS-specific capabilities provided by Java client](http://appium.github.io/java-client/io/appium/java_client/remote/IOSMobileCapabilityType.html) + ```java import java.io.File; import org.openqa.selenium.remote.DesiredCapabilities; import io.appium.java_client.AppiumDriver; -import io.appium.java_client.ios.IOSDriver; import io.appium.java_client.MobileElement; import java.net.URL; @@ -35,57 +32,91 @@ File app = new File("The absolute or relative path to an *.app, *.zip or ipa fi DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone Simulator"); capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "The_target_version"); +capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, MobilePlatform.IOS); //The_target_version is the supported iOS version, e.g. 8.1, 8.2, 9.2 etc capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); //you are free to set additional capabilities -AppiumDriver driver = new IOSDriver<>( +AppiumDriver driver = new AppiumDriver<>( new URL("http://target_ip:used_port/wd/hub"), //if it needs to use locally started server //then the target_ip is 127.0.0.1 or 0.0.0.0 //the default port is 4723 capabilities); ``` -If it needs to start browser then: +or ```java +import java.io.File; import org.openqa.selenium.remote.DesiredCapabilities; -import io.appium.java_client.remote.MobileBrowserType; import io.appium.java_client.AppiumDriver; import io.appium.java_client.ios.IOSDriver; -import org.openqa.selenium.remote.RemoteWebElement; +import io.appium.java_client.MobileElement; import java.net.URL; - ... +File app = new File("The absolute or relative path to an *.app, *.zip or ipa file"); DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone Simulator"); capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "The_target_version"); //The_target_version is the supported iOS version, e.g. 8.1, 8.2, 9.2 etc -capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, MobileBrowserType.SAFARI); -... +capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); //you are free to set additional capabilities AppiumDriver driver = new IOSDriver<>( -new URL("http://target_ip:used_port/wd/hub"), capabilities); +new URL("http://target_ip:used_port/wd/hub"), //if it needs to use locally started server +//then the target_ip is 127.0.0.1 or 0.0.0.0 +//the default port is 4723 +capabilities); ``` -or +##If it needs to start browser then ```java -import io.appium.java_client.remote.MobilePlatform; -import org.openqa.selenium.remote.DesiredCapabilities; -import io.appium.java_client.remote.MobileBrowserType; -import org.openqa.selenium.remote.RemoteWebDriver; -import java.net.URL; +capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, MobileBrowserType.SAFARI); +``` + +##There are two automation types +Default iOS Automation (v < iOS 10.x) does not require any specific capability. However you can +```java +capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.APPIUM); +``` + +You have to define this automation type to be able to use XCUIT mode for new iOS versions (v > 10.x) +```java +capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.IOS_XCUI_TEST); +``` + +# Possible cases + +You can use ```io.appium.java_client.AppiumDriver``` and ```io.appium.java_client.ios.IOSDriver``` as well. The main difference +is that ```IOSDriver``` implements all API that describes interaction with iOS native/hybrid app. ```AppiumDriver``` allows to +use iOS-specific API eventually. + +_Samples of the searching by iOSNsPredicateString using_ ```io.appium.java_client.AppiumDriver``` + +```java +import io.appium.java_client.FindsByIosNSPredicate; +import io.appium.java_client.ios.IOSElement; ... -DesiredCapabilities capabilities = new DesiredCapabilities(); -capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, MobilePlatform.IOS); -capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone Simulator"); -capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "The_target_version"); -//The_target_version is the supported iOS version, e.g. 8.1, 8.2, 9.2 etc -capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, MobileBrowserType.SAFARI); -//you are free to set additional capabilities -RemoteWebDriver driver = new RemoteWebDriver( -new URL("http://target_ip:used_port/wd/hub"), capabilities); + +FindsByIosNSPredicate findsByIosNSPredicate = new FindsByIosNSPredicate() { + @Override + public IOSElement findElement(String by, String using) { + return driver.findElement(by, using); + } + + @Override + public List findElements(String by, String using) { + return driver.findElements(by, using); + } +}; + +findsByIosNSPredicate.findElementByIosNsPredicate("some predicate"); ``` + +```java +driver.findElement(MobileBy.iOSNsPredicateString("some predicate")); +``` + +All that ```IOSDriver``` can do by design. diff --git a/docs/Touch-actions.md b/docs/Touch-actions.md index 20467c458..74328540c 100644 --- a/docs/Touch-actions.md +++ b/docs/Touch-actions.md @@ -1 +1,44 @@ -... under constraction \ No newline at end of file +Appium server side provides abilities to emulate touch actions. It is possible construct single, complex and multiple touch actions. + +#How to use a single touch action + +```java +import io.appium.java_client.TouchAction; + +... +//tap +new TouchAction(driver) + .tap(driver + .findElementById("io.appium.android.apis:id/start")).perform(); +``` + +#How to construct complex actions + +```java +import io.appium.java_client.TouchAction; + +... +//swipe +TouchAction swipe = new TouchAction(driver).press(images.get(2), -10, center.y - location.y) + .waitAction(2000).moveTo(gallery, 10, center.y - location.y).release(); +swipe.perform(); +``` + +#How to construct multiple touch action. + +```java +import io.appium.java_client.TouchAction; +import io.appium.java_client.MultiTouchAction; + +... +//tap by few fingers + MultiTouchAction multiTouch = new MultiTouchAction(driver); + +for (int i = 0; i < fingers; i++) { + TouchAction tap = new TouchAction(driver); + multiTouch.add(tap.press(element).waitAction(duration).release()); +} + +multiTouch.perform(); +``` +