├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── pom.xml
├── scripts
├── capabilities.js
└── script.js
└── src
├── main
└── java
│ └── com
│ └── mobilebox
│ └── repl
│ ├── Appium.java
│ ├── app
│ ├── APKInspector.java
│ └── ApkData.java
│ ├── commands
│ ├── AndroidCommands.java
│ ├── AndroidDeviceCommands.java
│ ├── AppiumCommands.java
│ ├── CommandRef.java
│ ├── CommandsDoc.java
│ └── IOSCommands.java
│ ├── config
│ └── ConfigCapabilities.java
│ ├── devices
│ ├── AndroidDeviceKeyEvent.java
│ └── AndroidDeviceProperties.java
│ ├── exceptions
│ └── CommandsException.java
│ ├── misc
│ └── Utils.java
│ └── selectors
│ └── UISelector.java
└── test
└── java
└── com
└── mobilebox
└── repl
├── AppiumTest.java
├── commands
├── CommandsDocTest.java
└── DummyCommand.java
├── misc
└── UtilsTest.java
└── selectors
└── UISelectorTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | test-output
3 | lib
4 | bin
5 | .classpath
6 | .project
7 | .settings
8 | dependency-reduced-pom.xml
9 |
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | jdk:
4 | - oraclejdk8
5 |
6 | matrix:
7 | fast_finish: true
8 |
9 | script:
10 | - "mvn clean install"
11 |
12 | sudo: false
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2018 MobileBox. http://mobileboxlab.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 | ___
4 |
5 | [](https://travis-ci.org/mobileboxlab/appium-java-repl) [](https://www.codacy.com/app/dev-github/appium-java-repl?utm_source=github.com&utm_medium=referral&utm_content=mobileboxlab/appium-java-repl&utm_campaign=Badge_Grade)
6 |
7 | Simple Java REPL for controlling mobile apps through Appium.
8 |
9 | **Appium Java REPL** is a great place to start experimenting with Appium, there are advantages to using the REPL: it's simple, and should work without any complex installation or configuration.
10 |
11 | **Appium Java REPL** is built on top of [Java-REPL](https://github.com/albertlatacz/java-repl) (by [Albert Latacz](https://github.com/albertlatacz)) implementation.
12 |
13 |
14 | Check out the **Appium Java REPL** documentation [here](https://mobileboxlab.github.io/appium-java-repl/)
15 |
16 | ___
17 |
18 |
19 | 
20 |
21 |
22 |
23 |
24 | ## Contribution
25 |
26 | Any ideas are welcome. Feel free to submit any issues or pull requests.
27 |
28 | [](http://makeapullrequest.com)
29 |
30 | ---
31 | **Appium Java REPL** is developed and maintained by [Mobilebox](http://mobileboxlab.com) team.
32 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.mobilebox.appium.repl
5 | appium-repl
6 | 1.0.0
7 | Appium REPL
8 | Simple Java REPL (Read-eval-print Loop) for controlling mobile apps through Appium
9 |
10 |
11 | UTF-8
12 |
13 |
14 |
15 |
16 | jitpack.io
17 | https://jitpack.io
18 |
19 |
20 | oss.sonatype.org
21 | https://oss.sonatype.org/content/repositories/snapshots/
22 |
23 |
24 |
25 |
26 |
27 | io.appium
28 | java-client
29 | 6.0.0-BETA2
30 |
31 |
32 |
33 | com.javarepl
34 | javarepl
35 | 428
36 |
37 |
38 |
39 | dom4j
40 | dom4j
41 | 1.6.1
42 |
43 |
44 |
45 | com.github.vidstige
46 | jadb
47 | v1.0.1
48 |
49 |
50 |
51 | org.testng
52 | testng
53 | 6.8.21
54 | test
55 |
56 |
57 |
58 | org.assertj
59 | assertj-core
60 | 3.4.1
61 | test
62 |
63 |
64 |
65 | org.aeonbits.owner
66 | owner
67 | 1.0.9
68 |
69 |
70 |
71 | net.dongliu
72 | apk-parser
73 | 2.1.0
74 |
75 |
76 |
77 | com.jcabi
78 | jcabi-xml
79 | 1.0-SNAPSHOT
80 |
81 |
82 |
83 | org.zeroturnaround
84 | zt-zip
85 | 1.9
86 | jar
87 |
88 |
89 |
90 | com.cedarsoftware
91 | json-io
92 | 4.5.0
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | org.apache.maven.plugins
101 | maven-compiler-plugin
102 | 3.2
103 |
104 | 1.8
105 | 1.8
106 |
107 |
108 |
109 | org.apache.maven.plugins
110 | maven-shade-plugin
111 | 2.3
112 |
113 | true
114 |
115 |
116 | *:*
117 |
118 | META-INF/*.SF
119 | META-INF/*.DSA
120 | META-INF/*.RSA
121 |
122 |
123 |
124 |
125 |
126 |
127 | package
128 |
129 | shade
130 |
131 |
132 |
133 | jar-with-dependencies
134 |
135 |
136 |
138 | com.mobilebox.repl.Appium
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 | https://mobileboxlab.github.io/appium-java-rep
151 |
--------------------------------------------------------------------------------
/scripts/capabilities.js:
--------------------------------------------------------------------------------
1 |
2 | //The main method providing an entry point for the Appium JAVA REPL.
3 | function main() {
4 | var DesiredCapabilities = Java.type("org.openqa.selenium.remote.DesiredCapabilities")
5 | var caps = new DesiredCapabilities();
6 | caps.setCapability("appiumServer","http://127.0.0.1:4723/wd/hub");
7 | caps.setCapability("udid", "ID");
8 | caps.setCapability("deviceName", "NAME");
9 | caps.setCapability("newCommandTimeout", "900000");
10 | caps.setCapability("app", "ApiDemos-debug.apk");
11 | caps.setCapability("platformName","android");
12 | caps.setCapability("appActivity","io.appium.android.apis.ApiDemos");
13 | caps.setCapability("appPackage","io.appium.android.apis");
14 | return caps;
15 | }
16 |
--------------------------------------------------------------------------------
/scripts/script.js:
--------------------------------------------------------------------------------
1 |
2 | //The REPL object.
3 | var $;
4 |
5 | //The main method providing an entry point for the Appium JAVA REPL scripting capabilities.
6 | function main(repl){
7 | $ = repl;
8 | iteration();
9 | locators();
10 | appInfo();
11 | moreinfo();
12 | deviceActions();
13 | }
14 |
15 | //Access to REPL commands.
16 | function iteration(){
17 | var elements = $.ids("android:id/text1");
18 | for each (element in elements) {
19 | element.click();
20 | $.back();
21 | }
22 | }
23 |
24 | //Some device actions through JADB.
25 | function deviceActions(){
26 | var Device = $.device();
27 | Device.brightness(100);
28 | Device.call();
29 | Device.volumenUp();
30 | Device.volumenDown();
31 |
32 | print(Device.version());
33 | print(Device.vm());
34 | print(Device.dhcp());
35 | print(Device.gsm());
36 | print(Device.net());
37 | }
38 |
39 | function locators(){
40 | print($.className("android.widget.TextView").getText());
41 | print($.id("android:id/text1").getText());
42 | print($.xpath('//*[@text="NFC"]').getText());
43 |
44 | //List
45 | print($.ids("android:id/text1").size());
46 | print($.classNames("android.widget.TextView").size());
47 | print($.xpaths('//*[@text="NFC"]').size());
48 |
49 | //UISelector
50 | print($.text("App"));
51 | print($.texts("App").size());
52 | print($.textContain("Te"));
53 | print($.textContains("Te").size());
54 | }
55 |
56 | //Application under test info through APKInspector.
57 | function appInfo(){
58 | print($.appInfo());
59 | }
60 |
61 | //Some Appium commands.
62 | function moreinfo(){
63 | print($.activity());
64 | print($.orientation());
65 | print($.capabilities());
66 | print($.session());
67 | print($.source());
68 | print($.time());
69 | print($.context());
70 | print($.contextHandles());
71 | print($.sessionDetails());
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/Appium.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl;
2 |
3 | import static com.mobilebox.repl.commands.CommandsDoc.printCommands;
4 | import static com.mobilebox.repl.commands.CommandsDoc.SEPARATOR;
5 | import static com.mobilebox.repl.misc.Utils.console;
6 | import static java.lang.System.getProperty;
7 |
8 | import javarepl.Main;
9 |
10 | import com.mobilebox.repl.commands.AndroidCommands;
11 | import com.mobilebox.repl.commands.AndroidDeviceCommands;
12 | import com.mobilebox.repl.commands.AppiumCommands;
13 | import com.mobilebox.repl.commands.CommandRef;
14 | import com.mobilebox.repl.commands.IOSCommands;
15 |
16 | /**
17 | * This class acts as entry point to JavaREPL also provides commands that basically are wrappers
18 | * over some Appium methods such as find an element, get source, etc.
19 | *
20 | * @see JavaREPL
21 | *
22 | */
23 | public class Appium {
24 |
25 | public static AndroidCommands android;
26 | public static IOSCommands ios;
27 |
28 | static {
29 | android = new AndroidCommands();
30 | ios = new IOSCommands();
31 | }
32 |
33 | public static void main(String... args) throws Exception {
34 | welcome();
35 | Main.main(args);
36 | }
37 |
38 | private static void welcome() {
39 | console("----------------------------");
40 | console(" :::- Appium Java REPL -::: " + SEPARATOR);
41 | console("Type import static com.mobilebox.repl.Appium.*;" + SEPARATOR);
42 | console("Type help() for more options.");
43 | console("-----------------------------" + SEPARATOR);
44 | }
45 |
46 | @CommandRef(desc = "Prints this help.")
47 | public static void help() {
48 | printCommands(Appium.class);
49 | }
50 |
51 | @CommandRef(desc = "Quit Appium REPL")
52 | public static void exit() {
53 | System.exit(0);
54 | }
55 |
56 | @CommandRef(desc = "Prints all commands available for Android and iOS.")
57 | public static void appium() {
58 | printCommands(AppiumCommands.class);
59 | }
60 |
61 | @CommandRef(desc = "Prints all commands available especifc for Android.")
62 | public static void android() {
63 | printCommands(AndroidCommands.class);
64 | }
65 |
66 | @CommandRef(desc = "Prints all commands available for iOS.")
67 | public static void ios() {
68 | printCommands(IOSCommands.class);
69 | }
70 |
71 | @CommandRef(desc = "Prints all commands available for Android Device.")
72 | public static void android_device() {
73 | printCommands(AndroidDeviceCommands.class);
74 | }
75 |
76 | @CommandRef(desc = "Prints the user home directory.")
77 | public static void user_home() {
78 | System.out.println(getProperty("user.home"));
79 | }
80 |
81 | @CommandRef(desc = "Prints my name")
82 | public static void my_name() {
83 | System.out.println("My Name \n it's a joke, a bad one...");
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/app/APKInspector.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.app;
2 |
3 | import static com.google.common.base.Strings.nullToEmpty;
4 | import static java.util.UUID.randomUUID;
5 | import static org.zeroturnaround.zip.ZipUtil.containsEntry;
6 | import static org.zeroturnaround.zip.ZipUtil.unpackEntry;
7 |
8 | import java.awt.image.BufferedImage;
9 | import java.io.ByteArrayOutputStream;
10 | import java.io.File;
11 | import java.io.FileInputStream;
12 | import java.io.IOException;
13 | import java.nio.charset.StandardCharsets;
14 | import java.util.ArrayList;
15 | import java.util.Base64;
16 | import java.util.List;
17 | import java.util.Locale;
18 | import java.util.regex.Matcher;
19 | import java.util.regex.Pattern;
20 |
21 | import javax.imageio.ImageIO;
22 |
23 | import com.jcabi.xml.XML;
24 | import com.jcabi.xml.XMLDocument;
25 |
26 | import net.dongliu.apk.parser.ApkParser;
27 | import net.dongliu.apk.parser.bean.ApkMeta;
28 |
29 | /**
30 | * A Simple APK inspector.
31 | */
32 | @SuppressWarnings("resource")
33 | public class APKInspector {
34 |
35 | /**
36 | * Retrieve basic apk metas, such as title, icon, package name, version, etc.
37 | *
38 | * @param appPath The app path.
39 | * @return An {@link ApkData} instance.
40 | * @throws CommandsException
41 | */
42 | public ApkData inspect(final String appPath) {
43 | File apkFile = new File(appPath);
44 | ApkData app = new ApkData();
45 |
46 | if (apkFile.exists()) {
47 | try {
48 | ApkParser parser = new ApkParser(appPath);
49 | parser.setPreferredLocale(Locale.getDefault());
50 | ApkMeta data = parser.getApkMeta();
51 |
52 | app.setPackageName(nullToEmpty(data.getPackageName()))
53 | .setLabel(nullToEmpty(data.getLabel()))
54 | .setVersionName(nullToEmpty(data.getVersionName()))
55 | .setMaxSdkVersion(nullToEmpty(data.getMaxSdkVersion()))
56 | .setMinSdkVersion(nullToEmpty(data.getMinSdkVersion()))
57 | .setTargetSdkVersion(nullToEmpty(data.getTargetSdkVersion()))
58 | .setUsesFeature(data.getUsesFeatures()).setUserPermissions(data.getUsesPermissions())
59 | .setActivities(getActivities(parser.getManifestXml()))
60 | .setIcon(icon2Base64(appPath, data.getIcon()));
61 |
62 | } catch (Exception e) {
63 | throw new RuntimeException("Error: " + e.getMessage());
64 | }
65 | }
66 |
67 | return app;
68 | }
69 |
70 | /**
71 | * Return information about activities in the package in activities.
72 | *
73 | * @param manifest The app manifest.
74 | * @return List of activities
75 | */
76 | private List getActivities(final String manifest) {
77 | List activities = new ArrayList();
78 | Pattern p = Pattern.compile("android:name=\"(.*?)\"");
79 |
80 | XML xml = new XMLDocument(manifest);
81 | XML je = new XMLDocument(xml.toString());
82 | List name = je.nodes("//activity");
83 |
84 | for (XML node : name) {
85 | Matcher m = p.matcher(node.toString());
86 | if (m.find()) {
87 | activities.add(m.group(1));
88 | }
89 | }
90 | return activities;
91 | }
92 |
93 | /**
94 | * Retrieve the icon associated with an application. If it has not defined an icon, the default
95 | * app icon is returned.
96 | *
97 | * @param appPath The aplication under test path.
98 | * @iconPath The path to the icon on the APK file.
99 | * @return The image of the icon, or the default application icon if it could not be found in
100 | * base64.
101 | * @throws MoneyException
102 | */
103 | public String icon2Base64(final String appPath, final String iconPath) {
104 | String icon =
105 | "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAABRZJREFUeJztm89vVFUUxz/3vJm22FakZVoTRFaGUliI8QcsXICu5EcTE2ICf4BGggYTaBDoy0AFiyILjZq6xehKEH+wsZi40BohkShWlmpIoENLkZZOmTfnuGjHFKaFDn1vXgnzWU0mZ84595tz37333DdQoUKFChXuX9xUXzb62RZn8rbD1oLURxPaXsvsq3o/Gt8zp0iARj/bIuZ6QeZHG1rzZtJ2eX/ym2jj3J4iARZ23DjucG1mdtyTxMuX0tIfZsBUR84Kn1V1WDyezaSrfw0zRinIrV+Mlz1EMfjJGHwgInWY+7rhTV0UVZw7USRAYc5HOXiAyy6xHfQkuEVeIv9Vyre6KONNxxQClIm0C3Q0+ZLCb8BK9MZnbDKv3GnEJwAw8I5cU5dYr6oXcbK+sSU4Uu4cYhUA4Era/Y3nNig6Ko5tqY4b28oZP3YBAAbSVac9ky2gBnZk4d7cunLFnhMCAPTvTx7D3E4Qz0w/T/ljj5cj7pwRACCzP3kY5ZNyLo9zSgBwlulPbDXT78q1PJZdAEVHAJp2afOUBt0ul5OqTSh/UIblsewCCO4UgCWC7ulEuJp2Q2LBekUzUS+PRWeBwl49sy855UlxtjTsyS7zxP1U6mErqnzKXgGDnTV9ebXVmJ0oTIc4ScQRdLCzpg9om4nt5NNjFMyxVaD8VASIO4G4Cf0ZsMS3mmHLbRXcFkVbBakOw+9snwWKjonKORM7WuuSH/2VdlkIuQIW+Pbo9XxwRnDvAivDGnwYCFKN8ITDvTdiwekFu3UxhFgBj2zXeWP54CRCK9CnRnviunfq0mGJfakDSPlWpwRrxTjkYLnn8t8u8e2p0AQYqwu24lwr0BeMJVZf6XJXw/IdBpm0GwZOzPfthyoLep2wYkRzr4Y3BZzbDGCOnXNt8JO5mnZDzmgHcM5tDk0ARVsBvGHv+7B8RoVJogdAVZeHNgUKD7yp5nyzP7ZClS51rBFk3kTw6yKuJ6/aPrEzBKZ/2is6IuZ6VLR9IF3z593aw/h0SHXkEJGayPcBzf7YCjV+xMkLhcEDiMgD4DZ4uN6GPdlld/IjSC3ObRRzvY1+tiUs+8jPAqp04aTezL5IBIlXLh6UDECzr015C7qdSJuHdQEbJ//u1tPf//ZIm6h1cctZolT7ApFXgDrWAEwePIxfvEgu8TKATtxG3Y6b7C08+8gFKJT95MEX6D8olyZsamfiy6pcST2BmdjfM2eBlG8PY7luACeuJyz7svUDSt3LF9lbADhQHTKsfbb2Be6ZCgC9Ztgx8+yZgc6a82HZl60CpuvpTVcZpfYA77ZneA9VQDRUBIg7gbipCBB3AnET2s1QY8dYVpBqGfHq5koXaDoad2i9zMv/q+hoaBUgKucAgrr8c2H5jAqvJv88gCC/hyaAOfsUQIxD8317KCy/YfOgrw3m6AIws6OhCVAryQ8NzgFLqyzobdqba4vr1bepaNyh9Sk/92Iyn/8ZeEzh7GVJfhzq7fCC3brY8/InHSwPIefIUDhrgbdu8IBcCHUVuPKW/FPrEk8a9gZwRtHRMP3PholcfjGz1wdc4unBA3JhSsNUR86ivpEthajzue/3ARUB4k4gbooEKLy20uxrU/nTuZnCS1SqOhxVjCIBxMb7Z3kLuuMUoWmXNlsimHEP8G4p/svMnuxSwfUiMjd2c6pDiq2aWRusdIoqYKCz5rxiqwz7Ms63uFR12MyOz7wHWKFChQql8x+anVpT1E4upAAAAABJRU5ErkJggg==";
106 | File apkFile = new File(appPath);
107 | if (apkFile.exists()) {
108 | if (containsEntry(apkFile, iconPath)) {
109 | String tempIcon = randomUUID().toString() + ".png";
110 | File icono = new File(tempIcon);
111 | unpackEntry(apkFile, iconPath, icono);
112 | icon = imageToBase64(icono.getAbsolutePath());
113 | icono.delete();
114 | }
115 | }
116 | return icon;
117 | }
118 |
119 | /**
120 | * Image to Base64 converter
121 | *
122 | * @param imagePath - The image path.
123 | * @return The image as base64 data.
124 | * @throws MonkeyException
125 | */
126 | private String imageToBase64(final String imagePath) {
127 | final ByteArrayOutputStream os = new ByteArrayOutputStream();
128 | try {
129 | FileInputStream inputStream = new FileInputStream(imagePath);
130 | BufferedImage inputImage = ImageIO.read(inputStream);
131 | ImageIO.write(inputImage, "png", Base64.getEncoder().wrap(os));
132 | return os.toString(StandardCharsets.ISO_8859_1.name());
133 | } catch (final IOException e) {
134 | throw new RuntimeException(
135 | "We have an error with pseudo image to 64 route converter: " + e.getMessage());
136 | }
137 | }
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/app/ApkData.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.app;
2 |
3 | import static com.mobilebox.repl.misc.Utils.console;
4 |
5 | import java.util.List;
6 |
7 | import com.google.gson.Gson;
8 |
9 | import net.dongliu.apk.parser.bean.UseFeature;
10 |
11 | public class ApkData {
12 |
13 | private String packageName;
14 | private String label;
15 | private String icon;
16 | private String versionName;
17 | private String minSdkVersion;
18 | private String targetSdkVersion;
19 | private String maxSdkVersion;
20 | private List usesFeature;
21 | private List userPermissions;
22 | private List activities;
23 |
24 | public String getPackageName() {
25 | return packageName;
26 | }
27 |
28 | public ApkData setPackageName(String packageName) {
29 | this.packageName = packageName;
30 | return this;
31 | }
32 |
33 | public String getLabel() {
34 | return label;
35 | }
36 |
37 | public ApkData setLabel(String label) {
38 | this.label = label;
39 | return this;
40 | }
41 |
42 | public String getIcon() {
43 | return icon;
44 | }
45 |
46 | public ApkData setIcon(String icon) {
47 | this.icon = icon;
48 | return this;
49 | }
50 |
51 | public String getVersionName() {
52 | return versionName;
53 | }
54 |
55 | public ApkData setVersionName(String versionName) {
56 | this.versionName = versionName;
57 | return this;
58 | }
59 |
60 | public String getMinSdkVersion() {
61 | return minSdkVersion;
62 | }
63 |
64 | public ApkData setMinSdkVersion(String minSdkVersion) {
65 | this.minSdkVersion = minSdkVersion;
66 | return this;
67 | }
68 |
69 | public String getTargetSdkVersion() {
70 | return targetSdkVersion;
71 | }
72 |
73 | public ApkData setTargetSdkVersion(String targetSdkVersion) {
74 | this.targetSdkVersion = targetSdkVersion;
75 | return this;
76 | }
77 |
78 | public String getMaxSdkVersion() {
79 | return maxSdkVersion;
80 | }
81 |
82 | public ApkData setMaxSdkVersion(String maxSdkVersion) {
83 | this.maxSdkVersion = maxSdkVersion;
84 | return this;
85 | }
86 |
87 | public String toJson() {
88 | return new Gson().toJson(this);
89 | }
90 |
91 | public List getUsesFeature() {
92 | return usesFeature;
93 | }
94 |
95 | public ApkData setUsesFeature(List usesFeature) {
96 | this.usesFeature = usesFeature;
97 | return this;
98 | }
99 |
100 | public List getUserPermissions() {
101 | return userPermissions;
102 | }
103 |
104 | public ApkData setUserPermissions(List userPermissions) {
105 | this.userPermissions = userPermissions;
106 | return this;
107 | }
108 |
109 | public List getActivities() {
110 | return activities;
111 | }
112 |
113 | public ApkData setActivities(List activities) {
114 | this.activities = activities;
115 | return this;
116 | }
117 |
118 | public void toConsole() {
119 | console("---> Package name: " + packageName);
120 | console("---> Label: " + label);
121 | console("---> Version name: " + versionName);
122 | console("---> Min SDK version: " + minSdkVersion);
123 | console("---> Target SDK version: " + targetSdkVersion);
124 | console("---> Max SDK version: " + maxSdkVersion);
125 | console("---> Uses Feature: " + usesFeature);
126 | console("---> Uses Permissions: " + userPermissions);
127 | console("---> Activities: " + activities);
128 | }
129 |
130 | }
131 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/commands/AndroidCommands.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.commands;
2 |
3 | import static com.mobilebox.repl.misc.Utils.console;
4 | import static io.appium.java_client.remote.MobileCapabilityType.APP;
5 | import static io.appium.java_client.remote.MobileCapabilityType.DEVICE_NAME;
6 | import static io.appium.java_client.remote.MobileCapabilityType.NEW_COMMAND_TIMEOUT;
7 | import static io.appium.java_client.remote.MobileCapabilityType.PLATFORM_NAME;
8 | import static io.appium.java_client.remote.MobileCapabilityType.UDID;
9 | import static io.appium.java_client.remote.MobilePlatform.ANDROID;
10 |
11 | import java.net.MalformedURLException;
12 | import java.net.URL;
13 | import java.util.List;
14 |
15 | import org.openqa.selenium.remote.DesiredCapabilities;
16 |
17 | import com.mobilebox.repl.app.APKInspector;
18 | import com.mobilebox.repl.exceptions.CommandsException;
19 | import com.mobilebox.repl.selectors.UISelector;
20 |
21 | import io.appium.java_client.android.AndroidDriver;
22 | import io.appium.java_client.android.AndroidElement;
23 |
24 | @SuppressWarnings({"rawtypes"})
25 | public class AndroidCommands extends AppiumCommands {
26 |
27 | @Override
28 | @CommandRef(desc = "Start a new Appium session for Android.", params = {
29 | "deviceName - The kind of mobile device or emulator to use",
30 | "udid - Unique device identifier of the connected physical device",
31 | "app - The absolute local path or remote http URL to an .ipa or .apk",
32 | "server - The Appium server URL",
33 | "timeout - How long (in seconds) Appium will wait for a new command from the client before assuming the client quit and ending the session."})
34 | public void start(String deviceName, String udid, String app, String server, String timeout)
35 | throws CommandsException, MalformedURLException {
36 | DesiredCapabilities capabilities = new DesiredCapabilities();
37 | capabilities.setCapability(PLATFORM_NAME, ANDROID);
38 | capabilities.setCapability(DEVICE_NAME, deviceName);
39 | capabilities.setCapability(NEW_COMMAND_TIMEOUT, timeout);
40 | capabilities.setCapability(UDID, udid);
41 | capabilities.setCapability(APP, app);
42 | setDriver(new AndroidDriver(new URL(server), capabilities));
43 | setDeviceID(udid);
44 | setApp(app);
45 | }
46 |
47 | @CommandRef(desc = "Retrieves an AndroidDeviceCommands instance.")
48 | public AndroidDeviceCommands device() throws CommandsException {
49 | return new AndroidDeviceCommands(getDeviceID());
50 | }
51 |
52 | @CommandRef(desc = "Find element by text.",
53 | params = {
54 | "text - The visible text displayed in a widget (for example, the text label to launch an app)."},
55 | ret = "An AndroidElement.")
56 | public AndroidElement text(final String text) {
57 | return (AndroidElement) findElement(UISelector.text(text));
58 | }
59 |
60 | @CommandRef(desc = "Find elements by text.",
61 | params = {
62 | "text - The visible text displayed in a widget (for example, the text label to launch an app)."},
63 | ret = "A list of AndroidElement. This list is empty when no elements are found.")
64 | public List texts(final String text) {
65 | return (List) findElements(UISelector.text(text));
66 | }
67 |
68 | @CommandRef(desc = "Find element by text that contains a given text.",
69 | params = {
70 | "text - The visible text displayed in a widget (for example, the text label to launch an app). "
71 | + "The text for the element must match exactly with the string in your input argument. "
72 | + "Matching is case-sensitive."},
73 | ret = "An AndroidElement.")
74 | public AndroidElement textContain(final String text) {
75 | return (AndroidElement) findElement(UISelector.textContains(text));
76 | }
77 |
78 | @CommandRef(desc = "Find elements by text that contains a given text.",
79 | params = {
80 | "text - The visible text displayed in a widget (for example, the text label to launch an app). "
81 | + "The text for the element must match exactly with the string in your input argument. "
82 | + "Matching is case-sensitive."},
83 | ret = "A list of AndroidElement. This list is empty when no elements are found.")
84 | public List textContains(final String text) {
85 | return (List) findElements(UISelector.textContains(text));
86 | }
87 |
88 | @CommandRef(
89 | desc = "Find element by an UISelector expression. Chaining the search criteria on 'new UiSelector()'.",
90 | params = {
91 | "selector - The UISelector expression. E.g: className('android.widget.RelativeLayout').enabled(true).instance(0);"},
92 | ret = "An AndroidElement.")
93 | public AndroidElement uiSelector(final String selector) {
94 | return (AndroidElement) findElement(UISelector.selectorChaining(selector));
95 | }
96 |
97 | @CommandRef(
98 | desc = "Find elements by an UISelector expression. Chaining the search criteria on 'new UiSelector()'.",
99 | params = {
100 | "selector - The UISelector expression. E.g: className('android.widget.RelativeLayout').enabled(true).instance(0);"},
101 | ret = "A list of AndroidElement. This list is empty when no elements are found.")
102 | public List uiSelectors(final String selector) {
103 | return findElements(UISelector.selectorChaining(selector));
104 | }
105 |
106 | @CommandRef(desc = "Prints the current activity being run on the mobile device.")
107 | public void activity() {
108 | console(getDriver().currentActivity());
109 | }
110 |
111 | @CommandRef(desc = "Open the notification shade, on Android devices.")
112 | public void openNotifications() {
113 | getDriver().openNotifications();
114 | }
115 |
116 | @CommandRef(desc = "Close the app which was provided in the capabilities at session creation.")
117 | public void closeApp() {
118 | getDriver().closeApp();
119 | };
120 |
121 | @CommandRef(desc = "Launch the app which was provided in the capabilities at session creation.")
122 | public void launchApp() {
123 | getDriver().launchApp();
124 | };
125 |
126 | @CommandRef(desc = "Prints detailed information about the app.")
127 | public void appInfo() {
128 | new APKInspector().inspect(getApp()).toConsole();
129 | };
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/commands/AndroidDeviceCommands.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.commands;
2 |
3 | import static com.mobilebox.repl.misc.Utils.consoleTitle;
4 | import static com.mobilebox.repl.misc.Utils.console;
5 | import static com.mobilebox.repl.devices.AndroidDeviceProperties.*;
6 | import static com.google.common.base.Preconditions.*;
7 |
8 | import java.io.File;
9 | import java.io.FileOutputStream;
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.nio.charset.StandardCharsets;
13 | import java.util.List;
14 | import java.util.Map.Entry;
15 | import java.util.Scanner;
16 | import java.util.stream.Collectors;
17 |
18 | import com.mobilebox.repl.devices.AndroidDeviceKeyEvent;
19 | import com.mobilebox.repl.exceptions.CommandsException;
20 |
21 | import se.vidstige.jadb.JadbConnection;
22 | import se.vidstige.jadb.JadbDevice;
23 | import se.vidstige.jadb.JadbException;
24 | import se.vidstige.jadb.Stream;
25 |
26 | /**
27 | * Commands for control an Android device or emulator from outside of Appium code. With these
28 | * commands you can takes screenshots, set brightness, retrieves devices properties, etc. For
29 | * provide these functions we uses an ADB client implemented in pure Java: JADB
30 | *
31 | * @see JADB
32 | */
33 | public class AndroidDeviceCommands {
34 |
35 | private String serial;
36 | private JadbConnection jadb;
37 | private JadbDevice device;
38 |
39 | /**
40 | * Create a new instance of {@link AndroidDeviceCommands}.
41 | *
42 | * @param serial The Android Device serial.
43 | * @throws CommandsException
44 | */
45 | public AndroidDeviceCommands(final String serial) throws CommandsException {
46 | checkArgument(!serial.isEmpty(), "Cannot load the device the device serial is empty.");
47 |
48 | try {
49 | jadb = new JadbConnection();
50 | List devices = jadb.getDevices().stream()
51 | .filter(d -> d.getSerial().contains(serial)).collect(Collectors.toList());
52 | this.serial = serial;
53 | this.device = devices.get(0);
54 | } catch (IOException | JadbException e) {
55 | throw new CommandsException("Cannot load the device. Error:" + e.getMessage());
56 | }
57 | }
58 |
59 | @CommandRef(desc = "Prints the Android device serial.")
60 | public void serial() {
61 | console("Android Device ID: " + serial);
62 | }
63 |
64 | @CommandRef(desc = "Set brightness.", params = {"brightness - Value into the range [0,255]."})
65 | public void brightness(final int brightness) throws IOException, JadbException {
66 | device.executeShell("settings", "put", "system", "screen_brightness",
67 | Integer.toString(brightness));
68 | }
69 |
70 | @CommandRef(desc = "Sends the KEYCODE_CALL KeyEvent.")
71 | public void call() throws IOException, JadbException {
72 | keyEvent(AndroidDeviceKeyEvent.KEYCODE_CALL);
73 | }
74 |
75 | @CommandRef(desc = "Sends the KEYCODE_VOLUME_UP KeyEvent.")
76 | public void volumenUp() throws IOException, JadbException {
77 | keyEvent(AndroidDeviceKeyEvent.KEYCODE_VOLUME_UP);
78 | }
79 |
80 | @CommandRef(desc = "Sends the KEYCODE_VOLUME_DOWN KeyEvent.")
81 | public void volumenDown() throws IOException, JadbException {
82 | keyEvent(AndroidDeviceKeyEvent.KEYCODE_VOLUME_DOWN);
83 | }
84 |
85 | @CommandRef(desc = "Prints the device Android version.")
86 | public void version() throws IOException, JadbException {
87 | console("Android Version Release: " + prop("ro.build.version.release"));
88 | }
89 |
90 | @CommandRef(desc = "Prints device information related to Dalvik VM.")
91 | public void vm() throws IOException, JadbException {
92 | consoleTitle("Device Dalvik VM properties");
93 | for (Entry entry : DALVIK_VM.entrySet()) {
94 | console(entry.getKey() + " = " + prop(entry.getValue()));
95 | }
96 | }
97 |
98 | @CommandRef(desc = "Prints device information related to DHCP.")
99 | public void dhcp() throws IOException, JadbException {
100 | consoleTitle("Device DHCP properties");
101 | for (Entry entry : DHCP.entrySet()) {
102 | console(entry.getKey() + " = " + prop(entry.getValue()));
103 | }
104 | }
105 |
106 | @CommandRef(desc = "Prints device information related to GSM.")
107 | public void gsm() throws IOException, JadbException {
108 | consoleTitle("Device GSM properties");
109 | for (Entry entry : GSM.entrySet()) {
110 | console(entry.getKey() + " = " + prop(entry.getValue()));
111 | }
112 | }
113 |
114 | @CommandRef(desc = "Prints device information related to Net.")
115 | public void net() throws IOException, JadbException {
116 | consoleTitle("Device Net properties");
117 | for (Entry entry : NET.entrySet()) {
118 | console(entry.getKey() + " = " + prop(entry.getValue()));
119 | }
120 | }
121 |
122 | @CommandRef(desc = "Prints device information related to product.")
123 | public void productInfo() throws IOException, JadbException {
124 | consoleTitle("Device Product properties");
125 | for (Entry entry : PRODUCT.entrySet()) {
126 | console(entry.getKey() + " = " + prop(entry.getValue()));
127 | }
128 | }
129 |
130 | @CommandRef(desc = "Prints device information related to Wifi.")
131 | public void wifi() throws IOException, JadbException {
132 | consoleTitle("Device WIFI properties");
133 | for (Entry entry : WIFI.entrySet()) {
134 | console(entry.getKey() + " = " + prop(entry.getValue()));
135 | }
136 | }
137 |
138 | @CommandRef(desc = " Remove a file.", params = {"filePath - The file path to remove."})
139 | public void remove(final String filePath) throws IOException, JadbException {
140 | device.executeShell("rm", "-f", filePath);
141 | }
142 |
143 | @CommandRef(desc = "Prints all packages on the device.")
144 | public void packages() throws IOException, JadbException {
145 | console(getResponse(device.executeShell("pm", "list", "packages")));
146 | }
147 |
148 | @CommandRef(desc = "Prints battery status.")
149 | public void battery() throws IOException, JadbException {
150 | console(getResponse(device.executeShell("dumpsys", "battery")));
151 | }
152 |
153 | @CommandRef(desc = "Prints disk space usage.")
154 | public void disk() throws IOException, JadbException {
155 | console(getResponse(device.executeShell("df")));
156 | }
157 |
158 | @CommandRef(desc = "Prints a list of all the available shell programs.")
159 | public void shellPrograms() throws IOException, JadbException {
160 | console(getResponse(device.executeShell("ls", "/system/bin")));
161 | }
162 |
163 | @CommandRef(desc = "Take and save a device screenshot to file.",
164 | params = {"filePath - Full path to file"})
165 | public void screenshot(final String filePath) throws IOException, JadbException {
166 | FileOutputStream outputStream = null;
167 | try {
168 | outputStream = new FileOutputStream(new File(filePath));
169 | InputStream stdout = device.executeShell("screencap", "-p");
170 | Stream.copy(stdout, outputStream);
171 | } finally {
172 | if (outputStream != null)
173 | outputStream.close();
174 | }
175 | }
176 |
177 | @CommandRef(
178 | desc = "Execute a shell command in the emulator/device instance and then exits the remote shell.",
179 | params = {"command - A shell command.", "args - Shell commands arguments."})
180 | public void shell(final String command, final String... args) throws IOException, JadbException {
181 | console(getResponse(device.executeShell(command, args)));
182 | }
183 |
184 | @CommandRef(
185 | desc = "Given the name of a system environment variable,returns its value for this device.",
186 | params = {"propertye - The property name"}, ret = "The property value")
187 | public String prop(final String property) throws IOException, JadbException {
188 | return getResponse(device.executeShell("getprop", property));
189 | }
190 |
191 | private void keyEvent(final int event) throws IOException, JadbException {
192 | console("Sends key event to the device. KeyEvent: [" + event + "]");
193 | device.executeShell("input", "keyevent", Integer.toString(event));
194 | }
195 |
196 | private String getResponse(InputStream input) {
197 | try (Scanner scanner = new Scanner(input, StandardCharsets.UTF_8.name())) {
198 | return scanner.useDelimiter("\\A").next();
199 | }
200 | }
201 |
202 | }
203 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/commands/AppiumCommands.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.commands;
2 |
3 | import static com.mobilebox.repl.commands.CommandsDoc.SEPARATOR;
4 | import static com.mobilebox.repl.misc.Utils.console;
5 | import static com.mobilebox.repl.misc.Utils.prettyXML;
6 | import static io.appium.java_client.remote.MobileCapabilityType.APP;
7 | import static io.appium.java_client.remote.MobileCapabilityType.DEVICE_NAME;
8 | import static io.appium.java_client.remote.MobileCapabilityType.NEW_COMMAND_TIMEOUT;
9 | import static io.appium.java_client.remote.MobileCapabilityType.PLATFORM_NAME;
10 | import static io.appium.java_client.remote.MobileCapabilityType.UDID;
11 | import static java.lang.String.format;
12 | import static java.lang.System.getProperty;
13 |
14 | import java.io.FileReader;
15 | import java.io.IOException;
16 | import java.net.MalformedURLException;
17 | import java.net.URL;
18 | import java.util.List;
19 |
20 | import javax.script.Invocable;
21 | import javax.script.ScriptEngine;
22 | import javax.script.ScriptEngineManager;
23 |
24 | import org.aeonbits.owner.ConfigFactory;
25 | import org.dom4j.DocumentException;
26 | import org.openqa.selenium.By;
27 | import org.openqa.selenium.Dimension;
28 | import org.openqa.selenium.Point;
29 | import org.openqa.selenium.WebElement;
30 | import org.openqa.selenium.remote.DesiredCapabilities;
31 |
32 | import com.mobilebox.repl.config.ConfigCapabilities;
33 | import com.mobilebox.repl.exceptions.CommandsException;
34 |
35 | import io.appium.java_client.AppiumDriver;
36 | import io.appium.java_client.android.AndroidDriver;
37 | import io.appium.java_client.ios.IOSDriver;
38 |
39 | @SuppressWarnings({"unchecked", "rawtypes"})
40 | public abstract class AppiumCommands {
41 |
42 | private String udid;
43 | private String app;
44 | private AppiumDriver driver;
45 | private final String ENGINE_NAME = "nashorn";
46 | private boolean logElement = true;
47 |
48 | abstract void start(String deviceName, String udid, String app, String server, String timeout)
49 | throws CommandsException, MalformedURLException;
50 |
51 | @CommandRef(desc = "Start a new Appium session from ${user.home}/appium.txt file")
52 | public void start() throws MalformedURLException, CommandsException {
53 | try {
54 | ConfigCapabilities config = ConfigFactory.create(ConfigCapabilities.class);
55 | DesiredCapabilities caps = new DesiredCapabilities();
56 |
57 | caps.setCapability(DEVICE_NAME, config.deviceName());
58 | caps.setCapability(NEW_COMMAND_TIMEOUT, config.cmdTimeout());
59 | caps.setCapability(APP, config.app());
60 | setApp(config.app());
61 |
62 | if (!config.udid().isEmpty()) {
63 | caps.setCapability(UDID, config.udid());
64 | setDeviceID(config.udid());
65 | }
66 |
67 | URL urlServer = new URL(config.appiumServer());
68 | setPlatformName(config.platformName(), urlServer, caps);
69 |
70 | } catch (Exception e) {
71 | throw new RuntimeException(
72 | format("An error has occurred: [%s] Please check the appium.txt file on: [%s]",
73 | e.getMessage(), getProperty("user.home")));
74 | }
75 | }
76 |
77 | @CommandRef(
78 | desc = "Start a new Appium session given Nashorn (JS) script file with DesiredCapabilities.",
79 | params = {"path - The script full path."}, ret = "")
80 | public void start(final String path) {
81 | final ScriptEngineManager manager = new ScriptEngineManager();
82 | ScriptEngine engine = manager.getEngineByName(ENGINE_NAME);
83 |
84 | try {
85 | engine.eval(new FileReader(path));
86 | Invocable invocable = (Invocable) engine;
87 | DesiredCapabilities caps = (DesiredCapabilities) invocable.invokeFunction("main");
88 |
89 | String app = (String) caps.getCapability("app");
90 | if (app != null) {
91 | setApp(app);
92 | }
93 |
94 | String udid = (String) caps.getCapability("udid");
95 | if (udid != null) {
96 | setDeviceID(udid);
97 | }
98 |
99 | URL urlServer = new URL((String) caps.getCapability("appiumServer"));
100 | String platform = (String) caps.getCapability("platformName");
101 | setPlatformName(platform, urlServer, caps);
102 | } catch (Exception e) {
103 | throw new RuntimeException(
104 | format("An error has occurred: [%s] Please check the DesiredCapabilities on: [%s]",
105 | e.getMessage(), path));
106 | }
107 | }
108 |
109 | @CommandRef(desc = "Execute a Nashorn (JS) script.", params = {"path - The script full path."},
110 | ret = "")
111 | public void run(final String path) {
112 | final ScriptEngineManager manager = new ScriptEngineManager();
113 | ScriptEngine engine = manager.getEngineByName(ENGINE_NAME);
114 |
115 | try {
116 | engine.eval(new FileReader(path));
117 | Invocable invocable = (Invocable) engine;
118 | invocable.invokeFunction("main", this);
119 | } catch (Exception e) {
120 | throw new RuntimeException(format("An error has occurred: [%s] Please check the script: [%s]",
121 | e.getMessage(), path));
122 | }
123 | }
124 |
125 | protected T getDriver() {
126 | if (driver == null) {
127 | throw new RuntimeException("The driver is null. Please start a new session");
128 | }
129 | return (T) driver;
130 | };
131 |
132 | protected String getDeviceID() {
133 | return udid;
134 | };
135 |
136 | protected String getApp() {
137 | return app;
138 | };
139 |
140 | protected void setDriver(AppiumDriver driver) {
141 | this.driver = driver;
142 | }
143 |
144 | protected void setDeviceID(String id) {
145 | this.udid = id;
146 | };
147 |
148 | protected void setApp(String app) {
149 | this.app = app;
150 | };
151 |
152 | public void logElement(boolean log){
153 | logElement = log;
154 | }
155 |
156 | @CommandRef(desc = "Terminates the driver instance.")
157 | public void quit() {
158 | ((AppiumDriver) getDriver()).quit();
159 | };
160 |
161 | @CommandRef(desc = "Find elements by ID.", params = {"id - The element id"},
162 | ret = "An element (AndroidElement or IOSElement).")
163 | public E id(String id) {
164 | return findElement(By.id(id));
165 | }
166 |
167 | @CommandRef(desc = "Find elements by ID.", params = {"id - The element id"},
168 | ret = "A list of elements (AndroidElement or IOSElement). This list is empty when no elements are found.")
169 | public List ids(String id) {
170 | return findElements(By.id(id));
171 | }
172 |
173 | @CommandRef(desc = "Find element by class name.",
174 | params = {"className - The class property (for example, 'android.widget.Button')"},
175 | ret = "An element (AndroidElement or IOSElement).")
176 | public E className(String className) {
177 | return findElement(By.className(className));
178 | }
179 |
180 | @CommandRef(desc = "Find elements by class name.",
181 | params = {"className - The class property (for example, 'android.widget.Button')"},
182 | ret = "A list of elements (AndroidElement or IOSElement). This list is empty when no elements are found.")
183 | public List classNames(String className) {
184 | return findElements(By.className(className));
185 | }
186 |
187 | @CommandRef(desc = "Find element by Xpath.", params = {"xpath - A Xpath expression"},
188 | ret = "An element (AndroidElement or IOSElement).")
189 | public E xpath(String xpath) {
190 | return findElement(By.xpath(xpath));
191 | }
192 |
193 | @CommandRef(desc = "Find elements by Xpath.", params = {"xpath - A Xpath expression"},
194 | ret = "A list of elements. This list is empty when no elements are found.")
195 | public List xpaths(String xpath) {
196 | return findElements(By.xpath(xpath));
197 | }
198 |
199 | @CommandRef(desc = "Move back")
200 | public void back() {
201 | getDriver().navigate().back();
202 | };
203 |
204 | @CommandRef(desc = "Prints the current orientation of a mobile devices desktop.")
205 | public void orientation() {
206 | console(getDriver().getOrientation());
207 | }
208 |
209 | @CommandRef(desc = "Prints the capabilities of the current driver.")
210 | public void capabilities() {
211 | console(getDriver().getCapabilities().asMap());
212 | }
213 |
214 | @CommandRef(desc = "Retrieves a XML view of the current screen.",
215 | ret = "A XML view of the current screen.")
216 | public void source() throws IOException, DocumentException {
217 | console(prettyXML(getDriver().getPageSource()));
218 | }
219 |
220 | @CommandRef(desc = "Prints the ID of this session.")
221 | public void session() {
222 | console(getDriver().getSessionId());
223 | };
224 |
225 | @CommandRef(desc = "Prints all defined Strings from an app for the default language.")
226 | public void strings() {
227 | console(getDriver().getAppStringMap());
228 | };
229 |
230 | @CommandRef(desc = "Prints the device date and time for both iOS and Android devices.")
231 | public void time() {
232 | console(getDriver().getDeviceTime());
233 | };
234 |
235 | @CommandRef(desc = "Prints the current context.")
236 | public void context() {
237 | console(getDriver().getContext());
238 | };
239 |
240 | @CommandRef(desc = "Prints the available contexts.")
241 | public void contextHandles() {
242 | getDriver().getContextHandles().forEach(item -> console(item));
243 | };
244 |
245 | @CommandRef(desc = "Switch to a new context.", params = {"context - The context name."})
246 | public void context(String context) {
247 | getDriver().context(context);
248 | };
249 |
250 | @CommandRef(desc = "Prints the session details.")
251 | public void sessionDetails() {
252 | console(getDriver().getSessionDetails());
253 | };
254 |
255 | @CommandRef(desc = "Hides the keyboard if it is showing.")
256 | public void hideKeyboard() {
257 | getDriver().hideKeyboard();
258 | };
259 |
260 | protected E findElement(By locator) {
261 | E element;
262 | try {
263 | element = (E) getDriver().findElement(locator);
264 | printElement(element);
265 | } catch (Exception e) {
266 | throw new RuntimeException("Element not found: " + e.getMessage());
267 | }
268 | return element;
269 | }
270 |
271 | protected List findElements(By locator) {
272 | List elements = (List) ((T) getDriver()).findElements(locator);
273 | console("Found: " + elements.size() + " elements" + SEPARATOR);
274 | for (E element : elements) {
275 | printElement(element);
276 | }
277 | return elements;
278 | }
279 |
280 | protected void printElement(E element) {
281 | if (logElement) {
282 | Point location = element.getLocation();
283 | Dimension size = element.getSize();
284 | console("---> Text: " + element.getText());
285 | console("---> TagName: " + element.getTagName());
286 | console("---> Enabled: " + element.isEnabled());
287 | console("---> Selected: " + element.isSelected());
288 | console("---> Displayed: " + element.isDisplayed());
289 | console("---> Location: [X=" + location.getX() + " Y=" + location.getY() + "]");
290 | console(
291 | "---> Size: [Height=" + size.getHeight() + " Width=" + size.getWidth() + "]" + SEPARATOR);
292 | }
293 | }
294 |
295 | private void setPlatformName(final String platform, URL urlServer, DesiredCapabilities caps) {
296 | switch (platform.toLowerCase()) {
297 | case "android":
298 | caps.setCapability(PLATFORM_NAME, "Android");
299 | setDriver(new AndroidDriver(urlServer, caps));
300 | break;
301 | case "ios":
302 | caps.setCapability(PLATFORM_NAME, "iOS");
303 | setDriver(new IOSDriver(urlServer, caps));
304 | break;
305 | default:
306 | throw new RuntimeException(
307 | format("Failed to start session. Please check the capabilities.", caps.toString()));
308 | }
309 | }
310 | }
311 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/commands/CommandRef.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.commands;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.Target;
5 | import java.lang.annotation.*;
6 |
7 | @Retention(RetentionPolicy.RUNTIME)
8 | @Target(value = ElementType.METHOD)
9 | public @interface CommandRef {
10 |
11 | public String desc() default "";
12 |
13 | public String[] params() default "";
14 |
15 | public String ret() default "";
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/commands/CommandsDoc.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.commands;
2 |
3 | import static com.mobilebox.repl.misc.Utils.console;
4 | import static com.mobilebox.repl.misc.Utils.consoleTitle;
5 |
6 | import java.lang.reflect.Method;
7 | import java.lang.reflect.Modifier;
8 | import java.util.Arrays;
9 |
10 | public abstract class CommandsDoc {
11 |
12 | public static final String SEPARATOR = System.getProperty("line.separator");
13 |
14 | /**
15 | * Prints all commands in a given class. In order to define command in Java code, you need to
16 | * annotate the respective methods with @CommandRef
annotation.
17 | *
18 | * @param clazz A class that contains commands.
19 | */
20 | public static void printCommands(Class> clazz) {
21 | consoleTitle("Available Commands for " + clazz.getSimpleName().replaceAll("Commands", ""));
22 |
23 | StringBuffer ref = new StringBuffer();
24 | ref.append("---> Command: %s").append(SEPARATOR).append("---> Description: %s")
25 | .append(SEPARATOR).append("---> Parameters: %s").append(SEPARATOR).append("---> Return: %s")
26 | .append(SEPARATOR);
27 |
28 | for (Method method : clazz.getMethods()) {
29 | String descOfMethod = "";
30 | String[] params = {""};
31 | String returns = "";
32 | if (Modifier.isPublic(method.getModifiers())) {
33 | CommandRef def = method.getAnnotation(CommandRef.class);
34 | if (def != null) {
35 | params = def.params();
36 | descOfMethod = def.desc();
37 | returns = def.ret();
38 | console(String.format(ref.toString(), method.getName(), descOfMethod,
39 | Arrays.toString(params), returns));
40 | }
41 |
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/commands/IOSCommands.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.commands;
2 |
3 | import static io.appium.java_client.remote.MobileCapabilityType.APP;
4 | import static io.appium.java_client.remote.MobileCapabilityType.DEVICE_NAME;
5 | import static io.appium.java_client.remote.MobileCapabilityType.NEW_COMMAND_TIMEOUT;
6 | import static io.appium.java_client.remote.MobileCapabilityType.PLATFORM_NAME;
7 | import static io.appium.java_client.remote.MobileCapabilityType.UDID;
8 | import static io.appium.java_client.remote.MobilePlatform.IOS;
9 |
10 | import java.net.MalformedURLException;
11 | import java.net.URL;
12 |
13 | import org.openqa.selenium.remote.DesiredCapabilities;
14 |
15 | import com.mobilebox.repl.exceptions.CommandsException;
16 |
17 | import io.appium.java_client.ios.IOSDriver;
18 | import io.appium.java_client.ios.IOSElement;
19 |
20 | @SuppressWarnings({"rawtypes"})
21 | public class IOSCommands extends AppiumCommands {
22 |
23 | @Override
24 | @CommandRef(desc = "Start a new Appium session for iOS.", params = {
25 | "deviceName - The kind of mobile device or emulator to use",
26 | "udid - Unique device identifier of the connected physical device",
27 | "app - The absolute local path or remote http URL to an .ipa or .apk",
28 | "server - The Appium server URL",
29 | "timeout - How long (in seconds) Appium will wait for a new command from the client before assuming the client quit and ending the session."})
30 | public void start(String deviceName, String udid, String app, String server, String timeout)
31 | throws CommandsException, MalformedURLException {
32 | DesiredCapabilities capabilities = new DesiredCapabilities();
33 | capabilities.setCapability(PLATFORM_NAME, IOS);
34 | capabilities.setCapability(DEVICE_NAME, deviceName);
35 | capabilities.setCapability(NEW_COMMAND_TIMEOUT, timeout);
36 | capabilities.setCapability(UDID, udid);
37 | capabilities.setCapability(APP, app);
38 | setDriver(new IOSDriver(new URL(server), capabilities));
39 | setApp(app);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/config/ConfigCapabilities.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.config;
2 |
3 | import org.aeonbits.owner.Config;
4 |
5 | import org.aeonbits.owner.Config.Sources;
6 | import org.aeonbits.owner.Config.HotReload;
7 |
8 | /**
9 | * Specify the necessary configurations of AUT through capabilities by storing them in a file.
10 | */
11 | @HotReload
12 | @Sources("file:${user.home}/appium.txt")
13 | public interface ConfigCapabilities extends Config {
14 |
15 | @DefaultValue("n/a")
16 | @Key("platform.name")
17 | String platformName();
18 |
19 | @Key("device.name")
20 | String deviceName();
21 |
22 | @Key("command.timeout")
23 | String cmdTimeout();
24 |
25 | @DefaultValue("")
26 | @Key("udid")
27 | String udid();
28 |
29 | @Key("app")
30 | String app();
31 |
32 | @DefaultValue("http://127.0.0.1:4723/wd/hub")
33 | @Key("appium.server")
34 | String appiumServer();
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/devices/AndroidDeviceKeyEvent.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.devices;
2 |
3 | public class AndroidDeviceKeyEvent {
4 |
5 | /** Key code constant: Unknown key code. */
6 | public static final int KEYCODE_UNKNOWN = 0;
7 | /**
8 | * Key code constant: Soft Left key. Usually situated below the display on phones and used as a
9 | * multi-function feature key for selecting a software defined function shown on the bottom left
10 | * of the display.
11 | */
12 | public static final int KEYCODE_SOFT_LEFT = 1;
13 | /**
14 | * Key code constant: Soft Right key. Usually situated below the display on phones and used as a
15 | * multi-function feature key for selecting a software defined function shown on the bottom right
16 | * of the display.
17 | */
18 | public static final int KEYCODE_SOFT_RIGHT = 2;
19 | /**
20 | * Key code constant: Home key. This key is handled by the framework and is never delivered to
21 | * applications.
22 | */
23 | public static final int KEYCODE_HOME = 3;
24 | /** Key code constant: Back key. */
25 | public static final int KEYCODE_BACK = 4;
26 | /** Key code constant: Call key. */
27 | public static final int KEYCODE_CALL = 5;
28 | /** Key code constant: End Call key. */
29 | public static final int KEYCODE_ENDCALL = 6;
30 | /** Key code constant: '0' key. */
31 | public static final int KEYCODE_0 = 7;
32 | /** Key code constant: '1' key. */
33 | public static final int KEYCODE_1 = 8;
34 | /** Key code constant: '2' key. */
35 | public static final int KEYCODE_2 = 9;
36 | /** Key code constant: '3' key. */
37 | public static final int KEYCODE_3 = 10;
38 | /** Key code constant: '4' key. */
39 | public static final int KEYCODE_4 = 11;
40 | /** Key code constant: '5' key. */
41 | public static final int KEYCODE_5 = 12;
42 | /** Key code constant: '6' key. */
43 | public static final int KEYCODE_6 = 13;
44 | /** Key code constant: '7' key. */
45 | public static final int KEYCODE_7 = 14;
46 | /** Key code constant: '8' key. */
47 | public static final int KEYCODE_8 = 15;
48 | /** Key code constant: '9' key. */
49 | public static final int KEYCODE_9 = 16;
50 | /** Key code constant: '*' key. */
51 | public static final int KEYCODE_STAR = 17;
52 | /** Key code constant: '#' key. */
53 | public static final int KEYCODE_POUND = 18;
54 | /**
55 | * Key code constant: Directional Pad Up key. May also be synthesized from trackball motions.
56 | */
57 | public static final int KEYCODE_DPAD_UP = 19;
58 | /**
59 | * Key code constant: Directional Pad Down key. May also be synthesized from trackball motions.
60 | */
61 | public static final int KEYCODE_DPAD_DOWN = 20;
62 | /**
63 | * Key code constant: Directional Pad Left key. May also be synthesized from trackball motions.
64 | */
65 | public static final int KEYCODE_DPAD_LEFT = 21;
66 | /**
67 | * Key code constant: Directional Pad Right key. May also be synthesized from trackball motions.
68 | */
69 | public static final int KEYCODE_DPAD_RIGHT = 22;
70 | /**
71 | * Key code constant: Directional Pad Center key. May also be synthesized from trackball motions.
72 | */
73 | public static final int KEYCODE_DPAD_CENTER = 23;
74 | /**
75 | * Key code constant: Volume Up key. Adjusts the speaker volume up.
76 | */
77 | public static final int KEYCODE_VOLUME_UP = 24;
78 | /**
79 | * Key code constant: Volume Down key. Adjusts the speaker volume down.
80 | */
81 | public static final int KEYCODE_VOLUME_DOWN = 25;
82 | /** Key code constant: Power key. */
83 | public static final int KEYCODE_POWER = 26;
84 | /**
85 | * Key code constant: Camera key. Used to launch a camera application or take pictures.
86 | */
87 | public static final int KEYCODE_CAMERA = 27;
88 | /** Key code constant: Clear key. */
89 | public static final int KEYCODE_CLEAR = 28;
90 | /** Key code constant: 'A' key. */
91 | public static final int KEYCODE_A = 29;
92 | /** Key code constant: 'B' key. */
93 | public static final int KEYCODE_B = 30;
94 | /** Key code constant: 'C' key. */
95 | public static final int KEYCODE_C = 31;
96 | /** Key code constant: 'D' key. */
97 | public static final int KEYCODE_D = 32;
98 | /** Key code constant: 'E' key. */
99 | public static final int KEYCODE_E = 33;
100 | /** Key code constant: 'F' key. */
101 | public static final int KEYCODE_F = 34;
102 | /** Key code constant: 'G' key. */
103 | public static final int KEYCODE_G = 35;
104 | /** Key code constant: 'H' key. */
105 | public static final int KEYCODE_H = 36;
106 | /** Key code constant: 'I' key. */
107 | public static final int KEYCODE_I = 37;
108 | /** Key code constant: 'J' key. */
109 | public static final int KEYCODE_J = 38;
110 | /** Key code constant: 'K' key. */
111 | public static final int KEYCODE_K = 39;
112 | /** Key code constant: 'L' key. */
113 | public static final int KEYCODE_L = 40;
114 | /** Key code constant: 'M' key. */
115 | public static final int KEYCODE_M = 41;
116 | /** Key code constant: 'N' key. */
117 | public static final int KEYCODE_N = 42;
118 | /** Key code constant: 'O' key. */
119 | public static final int KEYCODE_O = 43;
120 | /** Key code constant: 'P' key. */
121 | public static final int KEYCODE_P = 44;
122 | /** Key code constant: 'Q' key. */
123 | public static final int KEYCODE_Q = 45;
124 | /** Key code constant: 'R' key. */
125 | public static final int KEYCODE_R = 46;
126 | /** Key code constant: 'S' key. */
127 | public static final int KEYCODE_S = 47;
128 | /** Key code constant: 'T' key. */
129 | public static final int KEYCODE_T = 48;
130 | /** Key code constant: 'U' key. */
131 | public static final int KEYCODE_U = 49;
132 | /** Key code constant: 'V' key. */
133 | public static final int KEYCODE_V = 50;
134 | /** Key code constant: 'W' key. */
135 | public static final int KEYCODE_W = 51;
136 | /** Key code constant: 'X' key. */
137 | public static final int KEYCODE_X = 52;
138 | /** Key code constant: 'Y' key. */
139 | public static final int KEYCODE_Y = 53;
140 | /** Key code constant: 'Z' key. */
141 | public static final int KEYCODE_Z = 54;
142 | /** Key code constant: ',' key. */
143 | public static final int KEYCODE_COMMA = 55;
144 | /** Key code constant: '.' key. */
145 | public static final int KEYCODE_PERIOD = 56;
146 | /** Key code constant: Left Alt modifier key. */
147 | public static final int KEYCODE_ALT_LEFT = 57;
148 | /** Key code constant: Right Alt modifier key. */
149 | public static final int KEYCODE_ALT_RIGHT = 58;
150 | /** Key code constant: Left Shift modifier key. */
151 | public static final int KEYCODE_SHIFT_LEFT = 59;
152 | /** Key code constant: Right Shift modifier key. */
153 | public static final int KEYCODE_SHIFT_RIGHT = 60;
154 | /** Key code constant: Tab key. */
155 | public static final int KEYCODE_TAB = 61;
156 | /** Key code constant: Space key. */
157 | public static final int KEYCODE_SPACE = 62;
158 | /**
159 | * Key code constant: Symbol modifier key. Used to enter alternate symbols.
160 | */
161 | public static final int KEYCODE_SYM = 63;
162 | /**
163 | * Key code constant: Explorer special function key. Used to launch a browser application.
164 | */
165 | public static final int KEYCODE_EXPLORER = 64;
166 | /**
167 | * Key code constant: Envelope special function key. Used to launch a mail application.
168 | */
169 | public static final int KEYCODE_ENVELOPE = 65;
170 | /** Key code constant: Enter key. */
171 | public static final int KEYCODE_ENTER = 66;
172 | /**
173 | * Key code constant: Backspace key. Deletes characters before the insertion point, unlike
174 | * {@link #KEYCODE_FORWARD_DEL}.
175 | */
176 | public static final int KEYCODE_DEL = 67;
177 | /** Key code constant: '`' (backtick) key. */
178 | public static final int KEYCODE_GRAVE = 68;
179 | /** Key code constant: '-'. */
180 | public static final int KEYCODE_MINUS = 69;
181 | /** Key code constant: '=' key. */
182 | public static final int KEYCODE_EQUALS = 70;
183 | /** Key code constant: '[' key. */
184 | public static final int KEYCODE_LEFT_BRACKET = 71;
185 | /** Key code constant: ']' key. */
186 | public static final int KEYCODE_RIGHT_BRACKET = 72;
187 | /** Key code constant: '\' key. */
188 | public static final int KEYCODE_BACKSLASH = 73;
189 | /** Key code constant: ';' key. */
190 | public static final int KEYCODE_SEMICOLON = 74;
191 | /** Key code constant: ''' (apostrophe) key. */
192 | public static final int KEYCODE_APOSTROPHE = 75;
193 | /** Key code constant: '/' key. */
194 | public static final int KEYCODE_SLASH = 76;
195 | /** Key code constant: '@' key. */
196 | public static final int KEYCODE_AT = 77;
197 | /**
198 | * Key code constant: Number modifier key. Used to enter numeric symbols. This key is not Num
199 | * Lock; it is more like {@link #KEYCODE_ALT_LEFT} and is interpreted as an ALT key by
200 | * {@link android.text.method.MetaKeyKeyListener}.
201 | */
202 | public static final int KEYCODE_NUM = 78;
203 | /**
204 | * Key code constant: Headset Hook key. Used to hang up calls and stop media.
205 | */
206 | public static final int KEYCODE_HEADSETHOOK = 79;
207 | /**
208 | * Key code constant: Camera Focus key. Used to focus the camera.
209 | */
210 | public static final int KEYCODE_FOCUS = 80; // *Camera* focus
211 | /** Key code constant: '+' key. */
212 | public static final int KEYCODE_PLUS = 81;
213 | /** Key code constant: Menu key. */
214 | public static final int KEYCODE_MENU = 82;
215 | /** Key code constant: Notification key. */
216 | public static final int KEYCODE_NOTIFICATION = 83;
217 | /** Key code constant: Search key. */
218 | public static final int KEYCODE_SEARCH = 84;
219 | /** Key code constant: Play/Pause media key. */
220 | public static final int KEYCODE_MEDIA_PLAY_PAUSE = 85;
221 | /** Key code constant: Stop media key. */
222 | public static final int KEYCODE_MEDIA_STOP = 86;
223 | /** Key code constant: Play Next media key. */
224 | public static final int KEYCODE_MEDIA_NEXT = 87;
225 | /** Key code constant: Play Previous media key. */
226 | public static final int KEYCODE_MEDIA_PREVIOUS = 88;
227 | /** Key code constant: Rewind media key. */
228 | public static final int KEYCODE_MEDIA_REWIND = 89;
229 | /** Key code constant: Fast Forward media key. */
230 | public static final int KEYCODE_MEDIA_FAST_FORWARD = 90;
231 | /**
232 | * Key code constant: Mute key. Mutes the microphone, unlike {@link #KEYCODE_VOLUME_MUTE}.
233 | */
234 | public static final int KEYCODE_MUTE = 91;
235 | /** Key code constant: Page Up key. */
236 | public static final int KEYCODE_PAGE_UP = 92;
237 | /** Key code constant: Page Down key. */
238 | public static final int KEYCODE_PAGE_DOWN = 93;
239 | /**
240 | * Key code constant: Picture Symbols modifier key. Used to switch symbol sets (Emoji, Kao-moji).
241 | */
242 | public static final int KEYCODE_PICTSYMBOLS = 94; // switch symbol-sets
243 | // (Emoji,Kao-moji)
244 | /**
245 | * Key code constant: Switch Charset modifier key. Used to switch character sets (Kanji,
246 | * Katakana).
247 | */
248 | public static final int KEYCODE_SWITCH_CHARSET = 95; // switch char-sets
249 | // (Kanji,Katakana)
250 | /**
251 | * Key code constant: A Button key. On a game controller, the A button should be either the button
252 | * labeled A or the first button on the upper row of controller buttons.
253 | */
254 | public static final int KEYCODE_BUTTON_A = 96;
255 | /**
256 | * Key code constant: B Button key. On a game controller, the B button should be either the button
257 | * labeled B or the second button on the upper row of controller buttons.
258 | */
259 | public static final int KEYCODE_BUTTON_B = 97;
260 | /**
261 | * Key code constant: C Button key. On a game controller, the C button should be either the button
262 | * labeled C or the third button on the upper row of controller buttons.
263 | */
264 | public static final int KEYCODE_BUTTON_C = 98;
265 | /**
266 | * Key code constant: X Button key. On a game controller, the X button should be either the button
267 | * labeled X or the first button on the lower row of controller buttons.
268 | */
269 | public static final int KEYCODE_BUTTON_X = 99;
270 | /**
271 | * Key code constant: Y Button key. On a game controller, the Y button should be either the button
272 | * labeled Y or the second button on the lower row of controller buttons.
273 | */
274 | public static final int KEYCODE_BUTTON_Y = 100;
275 | /**
276 | * Key code constant: Z Button key. On a game controller, the Z button should be either the button
277 | * labeled Z or the third button on the lower row of controller buttons.
278 | */
279 | public static final int KEYCODE_BUTTON_Z = 101;
280 | /**
281 | * Key code constant: L1 Button key. On a game controller, the L1 button should be either the
282 | * button labeled L1 (or L) or the top left trigger button.
283 | */
284 | public static final int KEYCODE_BUTTON_L1 = 102;
285 | /**
286 | * Key code constant: R1 Button key. On a game controller, the R1 button should be either the
287 | * button labeled R1 (or R) or the top right trigger button.
288 | */
289 | public static final int KEYCODE_BUTTON_R1 = 103;
290 | /**
291 | * Key code constant: L2 Button key. On a game controller, the L2 button should be either the
292 | * button labeled L2 or the bottom left trigger button.
293 | */
294 | public static final int KEYCODE_BUTTON_L2 = 104;
295 | /**
296 | * Key code constant: R2 Button key. On a game controller, the R2 button should be either the
297 | * button labeled R2 or the bottom right trigger button.
298 | */
299 | public static final int KEYCODE_BUTTON_R2 = 105;
300 | /**
301 | * Key code constant: Left Thumb Button key. On a game controller, the left thumb button indicates
302 | * that the left (or only) joystick is pressed.
303 | */
304 | public static final int KEYCODE_BUTTON_THUMBL = 106;
305 | /**
306 | * Key code constant: Right Thumb Button key. On a game controller, the right thumb button
307 | * indicates that the right joystick is pressed.
308 | */
309 | public static final int KEYCODE_BUTTON_THUMBR = 107;
310 | /**
311 | * Key code constant: Start Button key. On a game controller, the button labeled Start.
312 | */
313 | public static final int KEYCODE_BUTTON_START = 108;
314 | /**
315 | * Key code constant: Select Button key. On a game controller, the button labeled Select.
316 | */
317 | public static final int KEYCODE_BUTTON_SELECT = 109;
318 | /**
319 | * Key code constant: Mode Button key. On a game controller, the button labeled Mode.
320 | */
321 | public static final int KEYCODE_BUTTON_MODE = 110;
322 | /** Key code constant: Escape key. */
323 | public static final int KEYCODE_ESCAPE = 111;
324 | /**
325 | * Key code constant: Forward Delete key. Deletes characters ahead of the insertion point, unlike
326 | * {@link #KEYCODE_DEL}.
327 | */
328 | public static final int KEYCODE_FORWARD_DEL = 112;
329 | /** Key code constant: Left Control modifier key. */
330 | public static final int KEYCODE_CTRL_LEFT = 113;
331 | /** Key code constant: Right Control modifier key. */
332 | public static final int KEYCODE_CTRL_RIGHT = 114;
333 | /** Key code constant: Caps Lock key. */
334 | public static final int KEYCODE_CAPS_LOCK = 115;
335 | /** Key code constant: Scroll Lock key. */
336 | public static final int KEYCODE_SCROLL_LOCK = 116;
337 | /** Key code constant: Left Meta modifier key. */
338 | public static final int KEYCODE_META_LEFT = 117;
339 | /** Key code constant: Right Meta modifier key. */
340 | public static final int KEYCODE_META_RIGHT = 118;
341 | /** Key code constant: Function modifier key. */
342 | public static final int KEYCODE_FUNCTION = 119;
343 | /** Key code constant: System Request / Print Screen key. */
344 | public static final int KEYCODE_SYSRQ = 120;
345 | /** Key code constant: Break / Pause key. */
346 | public static final int KEYCODE_BREAK = 121;
347 | /**
348 | * Key code constant: Home Movement key. Used for scrolling or moving the cursor around to the
349 | * start of a line or to the top of a list.
350 | */
351 | public static final int KEYCODE_MOVE_HOME = 122;
352 | /**
353 | * Key code constant: End Movement key. Used for scrolling or moving the cursor around to the end
354 | * of a line or to the bottom of a list.
355 | */
356 | public static final int KEYCODE_MOVE_END = 123;
357 | /**
358 | * Key code constant: Insert key. Toggles insert / overwrite edit mode.
359 | */
360 | public static final int KEYCODE_INSERT = 124;
361 | /**
362 | * Key code constant: Forward key. Navigates forward in the history stack. Complement of
363 | * {@link #KEYCODE_BACK}.
364 | */
365 | public static final int KEYCODE_FORWARD = 125;
366 | /** Key code constant: Play media key. */
367 | public static final int KEYCODE_MEDIA_PLAY = 126;
368 | /** Key code constant: Pause media key. */
369 | public static final int KEYCODE_MEDIA_PAUSE = 127;
370 | /**
371 | * Key code constant: Close media key. May be used to close a CD tray, for example.
372 | */
373 | public static final int KEYCODE_MEDIA_CLOSE = 128;
374 | /**
375 | * Key code constant: Eject media key. May be used to eject a CD tray, for example.
376 | */
377 | public static final int KEYCODE_MEDIA_EJECT = 129;
378 | /** Key code constant: Record media key. */
379 | public static final int KEYCODE_MEDIA_RECORD = 130;
380 | /** Key code constant: F1 key. */
381 | public static final int KEYCODE_F1 = 131;
382 | /** Key code constant: F2 key. */
383 | public static final int KEYCODE_F2 = 132;
384 | /** Key code constant: F3 key. */
385 | public static final int KEYCODE_F3 = 133;
386 | /** Key code constant: F4 key. */
387 | public static final int KEYCODE_F4 = 134;
388 | /** Key code constant: F5 key. */
389 | public static final int KEYCODE_F5 = 135;
390 | /** Key code constant: F6 key. */
391 | public static final int KEYCODE_F6 = 136;
392 | /** Key code constant: F7 key. */
393 | public static final int KEYCODE_F7 = 137;
394 | /** Key code constant: F8 key. */
395 | public static final int KEYCODE_F8 = 138;
396 | /** Key code constant: F9 key. */
397 | public static final int KEYCODE_F9 = 139;
398 | /** Key code constant: F10 key. */
399 | public static final int KEYCODE_F10 = 140;
400 | /** Key code constant: F11 key. */
401 | public static final int KEYCODE_F11 = 141;
402 | /** Key code constant: F12 key. */
403 | public static final int KEYCODE_F12 = 142;
404 | /**
405 | * Key code constant: Num Lock key. This is the Num Lock key; it is different from
406 | * {@link #KEYCODE_NUM}. This key alters the behavior of other keys on the numeric keypad.
407 | */
408 | public static final int KEYCODE_NUM_LOCK = 143;
409 | /** Key code constant: Numeric keypad '0' key. */
410 | public static final int KEYCODE_NUMPAD_0 = 144;
411 | /** Key code constant: Numeric keypad '1' key. */
412 | public static final int KEYCODE_NUMPAD_1 = 145;
413 | /** Key code constant: Numeric keypad '2' key. */
414 | public static final int KEYCODE_NUMPAD_2 = 146;
415 | /** Key code constant: Numeric keypad '3' key. */
416 | public static final int KEYCODE_NUMPAD_3 = 147;
417 | /** Key code constant: Numeric keypad '4' key. */
418 | public static final int KEYCODE_NUMPAD_4 = 148;
419 | /** Key code constant: Numeric keypad '5' key. */
420 | public static final int KEYCODE_NUMPAD_5 = 149;
421 | /** Key code constant: Numeric keypad '6' key. */
422 | public static final int KEYCODE_NUMPAD_6 = 150;
423 | /** Key code constant: Numeric keypad '7' key. */
424 | public static final int KEYCODE_NUMPAD_7 = 151;
425 | /** Key code constant: Numeric keypad '8' key. */
426 | public static final int KEYCODE_NUMPAD_8 = 152;
427 | /** Key code constant: Numeric keypad '9' key. */
428 | public static final int KEYCODE_NUMPAD_9 = 153;
429 | /** Key code constant: Numeric keypad '/' key (for division). */
430 | public static final int KEYCODE_NUMPAD_DIVIDE = 154;
431 | /** Key code constant: Numeric keypad '*' key (for multiplication). */
432 | public static final int KEYCODE_NUMPAD_MULTIPLY = 155;
433 | /** Key code constant: Numeric keypad '-' key (for subtraction). */
434 | public static final int KEYCODE_NUMPAD_SUBTRACT = 156;
435 | /** Key code constant: Numeric keypad '+' key (for addition). */
436 | public static final int KEYCODE_NUMPAD_ADD = 157;
437 | /**
438 | * Key code constant: Numeric keypad '.' key (for decimals or digit grouping).
439 | */
440 | public static final int KEYCODE_NUMPAD_DOT = 158;
441 | /**
442 | * Key code constant: Numeric keypad ',' key (for decimals or digit grouping).
443 | */
444 | public static final int KEYCODE_NUMPAD_COMMA = 159;
445 | /** Key code constant: Numeric keypad Enter key. */
446 | public static final int KEYCODE_NUMPAD_ENTER = 160;
447 | /** Key code constant: Numeric keypad '=' key. */
448 | public static final int KEYCODE_NUMPAD_EQUALS = 161;
449 | /** Key code constant: Numeric keypad '(' key. */
450 | public static final int KEYCODE_NUMPAD_LEFT_PAREN = 162;
451 | /** Key code constant: Numeric keypad ')' key. */
452 | public static final int KEYCODE_NUMPAD_RIGHT_PAREN = 163;
453 | /**
454 | * Key code constant: Volume Mute key. Mutes the speaker, unlike {@link #KEYCODE_MUTE}. This key
455 | * should normally be implemented as a toggle such that the first press mutes the speaker and the
456 | * second press restores the original volume.
457 | */
458 | public static final int KEYCODE_VOLUME_MUTE = 164;
459 | /**
460 | * Key code constant: Info key. Common on TV remotes to show additional information related to
461 | * what is currently being viewed.
462 | */
463 | public static final int KEYCODE_INFO = 165;
464 | /**
465 | * Key code constant: Channel up key. On TV remotes, increments the television channel.
466 | */
467 | public static final int KEYCODE_CHANNEL_UP = 166;
468 | /**
469 | * Key code constant: Channel down key. On TV remotes, decrements the television channel.
470 | */
471 | public static final int KEYCODE_CHANNEL_DOWN = 167;
472 | /** Key code constant: Zoom in key. */
473 | public static final int KEYCODE_ZOOM_IN = 168;
474 | /** Key code constant: Zoom out key. */
475 | public static final int KEYCODE_ZOOM_OUT = 169;
476 | /**
477 | * Key code constant: TV key. On TV remotes, switches to viewing live TV.
478 | */
479 | public static final int KEYCODE_TV = 170;
480 | /**
481 | * Key code constant: Window key. On TV remotes, toggles picture-in-picture mode or other
482 | * windowing functions.
483 | */
484 | public static final int KEYCODE_WINDOW = 171;
485 | /**
486 | * Key code constant: Guide key. On TV remotes, shows a programming guide.
487 | */
488 | public static final int KEYCODE_GUIDE = 172;
489 | /**
490 | * Key code constant: DVR key. On some TV remotes, switches to a DVR mode for recorded shows.
491 | */
492 | public static final int KEYCODE_DVR = 173;
493 | /**
494 | * Key code constant: Bookmark key. On some TV remotes, bookmarks content or web pages.
495 | */
496 | public static final int KEYCODE_BOOKMARK = 174;
497 | /**
498 | * Key code constant: Toggle captions key. Switches the mode for closed-captioning text, for
499 | * example during television shows.
500 | */
501 | public static final int KEYCODE_CAPTIONS = 175;
502 | /**
503 | * Key code constant: Settings key. Starts the system settings activity.
504 | */
505 | public static final int KEYCODE_SETTINGS = 176;
506 | /**
507 | * Key code constant: TV power key. On TV remotes, toggles the power on a television screen.
508 | */
509 | public static final int KEYCODE_TV_POWER = 177;
510 | /**
511 | * Key code constant: TV input key. On TV remotes, switches the input on a television screen.
512 | */
513 | public static final int KEYCODE_TV_INPUT = 178;
514 | /**
515 | * Key code constant: Set-top-box power key. On TV remotes, toggles the power on an external
516 | * Set-top-box.
517 | */
518 | public static final int KEYCODE_STB_POWER = 179;
519 | /**
520 | * Key code constant: Set-top-box input key. On TV remotes, switches the input mode on an external
521 | * Set-top-box.
522 | */
523 | public static final int KEYCODE_STB_INPUT = 180;
524 | /**
525 | * Key code constant: A/V Receiver power key. On TV remotes, toggles the power on an external A/V
526 | * Receiver.
527 | */
528 | public static final int KEYCODE_AVR_POWER = 181;
529 | /**
530 | * Key code constant: A/V Receiver input key. On TV remotes, switches the input mode on an
531 | * external A/V Receiver.
532 | */
533 | public static final int KEYCODE_AVR_INPUT = 182;
534 | /**
535 | * Key code constant: Red "programmable" key. On TV remotes, acts as a contextual/programmable
536 | * key.
537 | */
538 | public static final int KEYCODE_PROG_RED = 183;
539 | /**
540 | * Key code constant: Green "programmable" key. On TV remotes, actsas a contextual/programmable
541 | * key.
542 | */
543 | public static final int KEYCODE_PROG_GREEN = 184;
544 | /**
545 | * Key code constant: Yellow "programmable" key. On TV remotes, acts as a contextual/programmable
546 | * key.
547 | */
548 | public static final int KEYCODE_PROG_YELLOW = 185;
549 | /**
550 | * Key code constant: Blue "programmable" key. On TV remotes, acts as a contextual/programmable
551 | * key.
552 | */
553 | public static final int KEYCODE_PROG_BLUE = 186;
554 | /**
555 | * Key code constant: App switch key. Should bring up the application switcher dialog.
556 | */
557 | public static final int KEYCODE_APP_SWITCH = 187;
558 | /** Key code constant: Generic Game Pad Button #1. */
559 | public static final int KEYCODE_BUTTON_1 = 188;
560 | /** Key code constant: Generic Game Pad Button #2. */
561 | public static final int KEYCODE_BUTTON_2 = 189;
562 | /** Key code constant: Generic Game Pad Button #3. */
563 | public static final int KEYCODE_BUTTON_3 = 190;
564 | /** Key code constant: Generic Game Pad Button #4. */
565 | public static final int KEYCODE_BUTTON_4 = 191;
566 | /** Key code constant: Generic Game Pad Button #5. */
567 | public static final int KEYCODE_BUTTON_5 = 192;
568 | /** Key code constant: Generic Game Pad Button #6. */
569 | public static final int KEYCODE_BUTTON_6 = 193;
570 | /** Key code constant: Generic Game Pad Button #7. */
571 | public static final int KEYCODE_BUTTON_7 = 194;
572 | /** Key code constant: Generic Game Pad Button #8. */
573 | public static final int KEYCODE_BUTTON_8 = 195;
574 | /** Key code constant: Generic Game Pad Button #9. */
575 | public static final int KEYCODE_BUTTON_9 = 196;
576 | /** Key code constant: Generic Game Pad Button #10. */
577 | public static final int KEYCODE_BUTTON_10 = 197;
578 | /** Key code constant: Generic Game Pad Button #11. */
579 | public static final int KEYCODE_BUTTON_11 = 198;
580 | /** Key code constant: Generic Game Pad Button #12. */
581 | public static final int KEYCODE_BUTTON_12 = 199;
582 | /** Key code constant: Generic Game Pad Button #13. */
583 | public static final int KEYCODE_BUTTON_13 = 200;
584 | /** Key code constant: Generic Game Pad Button #14. */
585 | public static final int KEYCODE_BUTTON_14 = 201;
586 | /** Key code constant: Generic Game Pad Button #15. */
587 | public static final int KEYCODE_BUTTON_15 = 202;
588 | /** Key code constant: Generic Game Pad Button #16. */
589 | public static final int KEYCODE_BUTTON_16 = 203;
590 | /**
591 | * Key code constant: Language Switch key. Toggles the current input language such as switching
592 | * between English and Japanese on a QWERTY keyboard. On some devices, the same function may be
593 | * performed by pressing Shift+Spacebar.
594 | */
595 | public static final int KEYCODE_LANGUAGE_SWITCH = 204;
596 | /**
597 | * Key code constant: Manner Mode key. Toggles silent or vibrate mode on and off to make the
598 | * device behave more politely in certain settings such as on a crowded train. On some devices,
599 | * the key may only operate when long-pressed.
600 | */
601 | public static final int KEYCODE_MANNER_MODE = 205;
602 | /**
603 | * Key code constant: 3D Mode key. Toggles the display between 2D and 3D mode.
604 | */
605 | public static final int KEYCODE_3D_MODE = 206;
606 | /**
607 | * Key code constant: Contacts special function key. Used to launch an address book application.
608 | */
609 | public static final int KEYCODE_CONTACTS = 207;
610 | /**
611 | * Key code constant: Calendar special function key. Used to launch a calendar application.
612 | */
613 | public static final int KEYCODE_CALENDAR = 208;
614 | /**
615 | * Key code constant: Music special function key. Used to launch a music player application.
616 | */
617 | public static final int KEYCODE_MUSIC = 209;
618 | /**
619 | * Key code constant: Calculator special function key. Used to launch a calculator application.
620 | */
621 | public static final int KEYCODE_CALCULATOR = 210;
622 | }
623 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/devices/AndroidDeviceProperties.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.devices;
2 |
3 | import java.util.Map;
4 |
5 | import com.google.common.collect.ImmutableMap;
6 |
7 | /**
8 | * Android has a set of properties about the device that can be read using the getprop command line
9 | * utility. This class is a place to collect theses properties.
10 | */
11 | public class AndroidDeviceProperties {
12 |
13 | public static final Map DALVIK_VM = ImmutableMap.builder()
14 | .put("Heap Growth Limit", "dalvik.vm.heapgrowthlimit")
15 | .put("Heap Max Free", "dalvik.vm.heapmaxfree").put("Heap Min Free", "dalvik.vm.heapminfree")
16 | .put("Heap Size", "dalvik.vm.heapsize").put("Heap Start Size", "dalvik.vm.heapstartsize")
17 | .put("Stack Trace File", "dalvik.vm.stack-trace-file").build();
18 |
19 | public static final Map DHCP =
20 | ImmutableMap.builder().put("DNS 1", "dhcp.wlan0.dns1")
21 | .put("DNS 2", "dhcp.wlan0.dns2").put("DNS 3", "dhcp.wlan0.dns3")
22 | .put("DNS 4", "dhcp.wlan0.dns4").put("Domain", "dhcp.wlan0.domain")
23 | .put("IP Address", "dhcp.wlan0.ipaddress").put("Mask", "dhcp.wlan0.mask")
24 | .put("Roaming", "dhcp.wlan0.roaming").put("Server", "dhcp.wlan0.server")
25 | .put("Gateway", "dhcp.wlan0.gateway").put("Vendor Info", "dhcp.wlan0.vendorInfo").build();
26 |
27 | public static final Map GSM = ImmutableMap.builder()
28 | .put("STK Setup Menu", "gsm.STK_SETUP_MENU").put("Phone Type", "[gsm.current.phone-type")
29 | .put("Default PDP Context", "gsm.defaultpdpcontext.active")
30 | .put("Network Type", "gsm.network.type").put("Operator Alpha", "gsm.operator.alpha")
31 | .put("ISO-Country", "gsm.operator.iso-country")
32 | .put("ISPS Roaming", "gsm.operator.ispsroaming").put("Is Roaming?", "gsm.operator.isroaming")
33 | .put("SIM State", "gsm.sim.state").put("Version Baseband", "gsm.version.baseband").build();
34 |
35 | public static final Map NET =
36 | ImmutableMap.builder().put("BT Name", "net.bt.name")
37 | .put("Change", "net.change").put("DNS 1", "net.dns1").put("DNS 2", "net.dns2").build();
38 |
39 | public static final Map PRODUCT = ImmutableMap.builder()
40 | .put("Board", "ro.product.board").put("Brand", "ro.product.brand")
41 | .put("CPU ABI2", "ro.product.cpu.abi2").put("CPU ABI", "ro.product.cpu.abi")
42 | .put("Device", "ro.product.device").put("Locale Language", "ro.product.locale.language")
43 | .put("Locale Region", "ro.product.locale.region")
44 | .put("Manufacturer", "ro.product.manufacturer").put("Model", "ro.product.model")
45 | .put("Name", "ro.product.name").put("Ship", "ro.product_ship").build();
46 |
47 | public static final Map WIFI =
48 | ImmutableMap.builder().put("Interface", "wifi.interface")
49 | .put("Driver Status", "wlan.driver.status").put("WFD Status", "wlan.wfd.status").build();
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/exceptions/CommandsException.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.exceptions;
2 |
3 | public class CommandsException extends Exception {
4 |
5 | private static final long serialVersionUID = -1822933755277157084L;
6 |
7 | public CommandsException(final String message) {
8 | super(message.replace("\n", ""));
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/misc/Utils.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.misc;
2 |
3 | import static com.mobilebox.repl.commands.CommandsDoc.SEPARATOR;
4 | import com.cedarsoftware.util.io.JsonWriter;
5 |
6 | import java.io.IOException;
7 | import java.io.StringWriter;
8 |
9 | import org.dom4j.Document;
10 | import org.dom4j.DocumentException;
11 | import org.dom4j.DocumentHelper;
12 | import org.dom4j.io.OutputFormat;
13 | import org.dom4j.io.XMLWriter;
14 |
15 |
16 | public class Utils {
17 |
18 | /**
19 | * Pretty-Print XML.
20 | *
21 | * @param xml The XML
22 | * @return A beautiful XML
23 | * @throws IOException
24 | * @throws DocumentException
25 | */
26 | public static String prettyXML(final String xml) throws IOException, DocumentException {
27 | Document doc = DocumentHelper.parseText(xml);
28 | StringWriter sw = new StringWriter();
29 | OutputFormat format = OutputFormat.createPrettyPrint();
30 | format.setIndent(true);
31 | format.setIndentSize(3);
32 | XMLWriter xw = new XMLWriter(sw, format);
33 | xw.write(doc);
34 | return sw.toString();
35 | }
36 |
37 | /**
38 | * Pretty-Print JSON.
39 | *
40 | * @param json The JSON
41 | * @return A beautiful JSON
42 | */
43 | public static String prettyJson(final Object json) {
44 | return JsonWriter.formatJson(json.toString());
45 | }
46 |
47 | /**
48 | * Prints a String on console and then terminate the line.
49 | *
50 | * @param msg The object to be printed.
51 | */
52 | public static void console(final Object msg) {
53 | System.out.println(msg.toString());
54 | }
55 |
56 | public static void consoleTitle(final String msg) {
57 | System.out.println("------------------------------------");
58 | System.out.println("::- " + msg + " -::");
59 | System.out.println("------------------------------------" + SEPARATOR);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/mobilebox/repl/selectors/UISelector.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.selectors;
2 |
3 | import static java.lang.String.format;
4 |
5 | import org.openqa.selenium.By;
6 |
7 | import io.appium.java_client.MobileBy;
8 |
9 | /**
10 | * A very simple Android UISelector Expression Builder. Builds some of the most common UISelector
11 | * expressions. See
12 | * UiSelector
13 | */
14 | public abstract class UISelector {
15 |
16 | /**
17 | * The UISelector String constructor.
18 | */
19 | private static final String UISELECTOR_CONSTRUCTOR = "new UiSelector().%s";
20 |
21 | /**
22 | * Set the search criteria to match the class property for a widget (for example,
23 | * "android.widget.Button").
24 | *
25 | * @param className The class name
26 | *
27 | * @return the UISelector expression. E.g: new UISelector.className("android.widget.Button").
28 | */
29 | public static By className(final String className) {
30 | return MobileBy
31 | .AndroidUIAutomator(format(UISELECTOR_CONSTRUCTOR, "className(\"" + className + "\")"));
32 | }
33 |
34 | /**
35 | * Set the search criteria to match the given resource ID.
36 | *
37 | * @param id Value to match
38 | *
39 | * @return the UISelector expression. E.g: new UISelector.resourceId("android:id/list")
40 | */
41 | public static By resourceId(final String id) {
42 | return MobileBy
43 | .AndroidUIAutomator(format(UISELECTOR_CONSTRUCTOR, "resourceId(\"" + id + "\")"));
44 | }
45 |
46 | /**
47 | * Set the search criteria to match the content-description property for a widget. The
48 | * content-description is typically used by the Android Accessibility framework to provide an
49 | * audio prompt for the widget when the widget is selected. The content-description for the widget
50 | * must match exactly with the string in your input argument. Matching is case-sensitive.
51 | *
52 | * @param desc Value to match
53 | *
54 | * @return the UISelector expression. E.g: new UISelector.description("my description")
55 | */
56 | public static By description(final String desc) {
57 | return MobileBy
58 | .AndroidUIAutomator(format(UISELECTOR_CONSTRUCTOR, "description(\"" + desc + "\")"));
59 | }
60 |
61 | /**
62 | * Set the search criteria to match the content-description property for a widget. The
63 | * content-description is typically used by the Android Accessibility framework to provide an
64 | * audio prompt for the widget when the widget is selected. The content-description for the widget
65 | * must contain the string in your input argument. Matching is case-insensitive.
66 | *
67 | * @param desc Value to match
68 | *
69 | * @return the UISelector expression. E.g: new UISelector.descriptionContains("my description")
70 | */
71 | public static By descriptionContains(final String desc) {
72 | return MobileBy.AndroidUIAutomator(
73 | format(UISELECTOR_CONSTRUCTOR, "descriptionContains(\"" + desc + "\")"));
74 | }
75 |
76 | /**
77 | * Set the search criteria to match the visible text displayed in a widget (for example, the text
78 | * label to launch an app). The text for the element must match exactly with the string in your
79 | * input argument. Matching is case-sensitive.
80 | *
81 | * @param text Value to match
82 | *
83 | * @return the UISelector expression. E.g: new UISelector.text("my text")
84 | */
85 | public static By text(final String text) {
86 | return MobileBy.AndroidUIAutomator(format(UISELECTOR_CONSTRUCTOR, "text(\"" + text + "\")"));
87 | }
88 |
89 | /**
90 | * Set the search criteria to match the visible text in a widget where the visible text must
91 | * contain the string in your input argument. The matching is case-sensitive.
92 | *
93 | * @param text Value to match
94 | *
95 | * @return the UISelector expression. E.g: new UISelector.textContains("my text")
96 | */
97 | public static By textContains(final String text) {
98 | return MobileBy
99 | .AndroidUIAutomator(format(UISELECTOR_CONSTRUCTOR, "textContains(\"" + text + "\")"));
100 | }
101 |
102 | /**
103 | * Chaining the search criteria on "new UiSelector()." Useful for to distinguish similar elements
104 | * based in the hierarchies they're in.
105 | *
106 | * @param locator Value to match
107 | * @return the UISelector expression. E.g: new
108 | * UiSelector().className("android.widget.RelativeLayout" ).enabled(true).instance(0);
109 | */
110 | public static By selectorChaining(final String locator) {
111 | return MobileBy.AndroidUIAutomator(format(UISELECTOR_CONSTRUCTOR, locator));
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/test/java/com/mobilebox/repl/AppiumTest.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.PrintStream;
5 |
6 | import org.testng.annotations.AfterClass;
7 | import org.testng.annotations.BeforeClass;
8 | import org.testng.annotations.Test;
9 |
10 | import com.mobilebox.repl.Appium;
11 |
12 | import static org.assertj.core.api.Assertions.*;
13 |
14 | public class AppiumTest {
15 |
16 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
17 |
18 | @BeforeClass
19 | public void setUpStreams() {
20 | System.setOut(new PrintStream(outContent));
21 | }
22 |
23 | @AfterClass
24 | public void cleanUpStreams() {
25 | System.setOut(null);
26 | }
27 |
28 | @Test
29 | public void commandsAppiumTest() {
30 | Appium.appium();
31 | assertThat(outContent.toString()).contains("Available Commands for Appium");
32 | }
33 |
34 | @Test
35 | public void commandsAndroidTest() {
36 | Appium.android();
37 | assertThat(outContent.toString()).contains("Available Commands for Android");
38 | }
39 |
40 | @Test
41 | public void commandsAndroidDeviceTest() {
42 | Appium.android_device();
43 | assertThat(outContent.toString()).contains("Available Commands for AndroidDevice");
44 | }
45 |
46 | @Test
47 | public void commandsIOSTest() {
48 | Appium.ios();
49 | assertThat(outContent.toString()).contains("Available Commands for IOS");
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/com/mobilebox/repl/commands/CommandsDocTest.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.commands;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import java.io.ByteArrayOutputStream;
6 | import java.io.PrintStream;
7 |
8 | import org.testng.annotations.AfterClass;
9 | import org.testng.annotations.BeforeClass;
10 | import org.testng.annotations.Test;
11 |
12 | import com.mobilebox.repl.commands.CommandsDoc;
13 |
14 | public class CommandsDocTest {
15 |
16 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
17 |
18 | @BeforeClass
19 | public void setUpStreams() {
20 | System.setOut(new PrintStream(outContent));
21 | }
22 |
23 | @AfterClass
24 | public void cleanUpStreams() {
25 | System.setOut(null);
26 | }
27 |
28 | @Test
29 | public void printCommands() {
30 | String expected =
31 | "------------------------------------\n::- Available Commands for DummyCommand -::\n------------------------------------\n---> Command: command\n---> Description: Command three\n---> Parameters: [param1 - description one}]\n---> Return: void\n";
32 | CommandsDoc.printCommands(DummyCommand.class);
33 | assertThat(outContent.toString()).isEqualToIgnoringWhitespace(expected);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/test/java/com/mobilebox/repl/commands/DummyCommand.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.commands;
2 |
3 | import com.mobilebox.repl.commands.CommandRef;
4 |
5 |
6 | public class DummyCommand {
7 |
8 | @CommandRef(desc = "Command three", params = {"param1 - description one}"}, ret = "void")
9 | public void command() {}
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/test/java/com/mobilebox/repl/misc/UtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.misc;
2 |
3 | import java.io.IOException;
4 |
5 | import org.dom4j.DocumentException;
6 | import org.testng.annotations.Test;
7 |
8 | import com.mobilebox.repl.misc.Utils;
9 |
10 | import static org.assertj.core.api.Assertions.*;
11 |
12 | public class UtilsTest {
13 |
14 | @Test
15 | public void prettyXMLTest() throws IOException, DocumentException {
16 | String xml =
17 | "IreneoFunes";
18 | String expected =
19 | "\n\n\n \n Ireneo\n Funes\n \n\n";
20 | assertThat(Utils.prettyXML(xml)).isEqualToIgnoringCase(expected);
21 | }
22 |
23 | @Test
24 | public void prettyXMLTestShouldBeTrowDocumentException() {
25 | String xml = "FrayBentosFunes";
26 | assertThatThrownBy(() -> {
27 | Utils.prettyXML(xml);
28 | }).isInstanceOf(DocumentException.class).hasMessageContaining("Error on line 1 of document");
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/java/com/mobilebox/repl/selectors/UISelectorTest.java:
--------------------------------------------------------------------------------
1 | package com.mobilebox.repl.selectors;
2 |
3 | import org.openqa.selenium.By;
4 | import org.testng.annotations.Test;
5 |
6 | import com.mobilebox.repl.selectors.UISelector;
7 |
8 | import static org.assertj.core.api.Assertions.*;
9 |
10 | public class UISelectorTest {
11 |
12 | @Test
13 | public void classNameTest() {
14 | String expected = "className(\"my.classname\")";
15 | By locator = UISelector.className("my.classname");
16 | assertThatByIsEqualTo(locator.toString(), expected);
17 | }
18 |
19 | @Test
20 | public void descriptionTest() {
21 | String expected = "description(\"desc\")";
22 | By locator = UISelector.description("desc");
23 | assertThatByIsEqualTo(locator.toString(), expected);
24 | }
25 |
26 | @Test
27 | public void descriptionContainsTest() {
28 | String expected = "descriptionContains(\"desc\")";
29 | By locator = UISelector.descriptionContains("desc");
30 | assertThatByIsEqualTo(locator.toString(), expected);
31 | }
32 |
33 | @Test
34 | public void resourceIdTest() {
35 | String expected = "resourceId(\"id\")";
36 | By locator = UISelector.resourceId("id");
37 | assertThatByIsEqualTo(locator.toString(), expected);
38 | }
39 |
40 | @Test
41 | public void selectorChainingTest() {
42 | String expected = "resourceId(\"id\").enabled(true).instance(0)";
43 | By locator = UISelector.selectorChaining(expected);
44 | assertThatByIsEqualTo(locator.toString(), expected);
45 | }
46 |
47 | @Test
48 | public void textTest() {
49 | String expected = "text(\"text\")";
50 | By locator = UISelector.text("text");
51 | assertThatByIsEqualTo(locator.toString(), expected);
52 | }
53 |
54 | @Test
55 | public void textContainsTest() {
56 | String expected = "textContains(\"text\")";
57 | By locator = UISelector.textContains("text");
58 | assertThatByIsEqualTo(locator.toString(), expected);
59 | }
60 |
61 | private void assertThatByIsEqualTo(final String actual, final String expected) {
62 | String msg = "By.AndroidUIAutomator: new UiSelector().";
63 | assertThat(actual).isEqualToIgnoringCase(msg + expected);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------