Skip to content

Commit bb365f3

Browse files
author
Mykola Mokhnach
committed
# Conflicts: # src/main/java/io/appium/java_client/android/AndroidDriver.java
2 parents c978b1b + 8f824d6 commit bb365f3

36 files changed

+634
-100
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ This is the Java language binding for writing Appium Tests, conforms to [Mobile
2121

2222
*6.0.0 (under construction yet)*
2323

24+
- **[BUG FIX]** **[BREAKING CHANGE]** Refactor of the `io.appium.java_client.AppiumFluentWait`. It uses `java.time.Duration` for time settings instead of `org.openqa.selenium.support.ui.Duration` and `java.util.concurrent.TimeUnit` [#863](https://github.com/appium/java-client/pull/863)
25+
- **[BREAKING CHANGE]** `io.appium.java_client.pagefactory.TimeOutDuration` became deprecated. It is going to be removed. Use `java.time.Duration` instead. FIX [#742](https://github.com/appium/java-client/issues/742) [#863](https://github.com/appium/java-client/pull/863).
26+
- **[BREAKING CHANGE]** `io.appium.java_client.pagefactory.WithTimeOut#unit` became deprecated. It is going to be removed. Use `io.appium.java_client.pagefactory.WithTimeOut#chronoUnit` instead. FIX [#742](https://github.com/appium/java-client/issues/742) [#863](https://github.com/appium/java-client/pull/863).
27+
- **[BREAKING CHANGE]** constructors of `io.appium.java_client.pagefactory.AppiumElementLocatorFactory`, `io.appium.java_client.pagefactory.AppiumFieldDecorator` and `io.appium.java_client.pagefactory.AppiumElementLocator` which use `io.appium.java_client.pagefactory.TimeOutDuration` as a parameter became deprecated. Use new constructors which use `java.time.Duration`.
28+
- **[DEPENDENCY UPDATES]**
29+
- `org.seleniumhq.selenium:selenium-java` was updated to 3.11.0
30+
2431
*6.0.0-BETA4*
2532
- **[ENHANCEMENT]** Added handler for isDispalyed in W3C mode. [#833](https://github.com/appium/java-client/pull/833)
2633
- **[ENHANCEMENT]** Added handlers for sending SMS, making GSM Call, setting GSM signal, voice, power capacity and power AC. [#834](https://github.com/appium/java-client/pull/834)

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ compileJava {
5050
]
5151
}
5252

53-
ext.seleniumVersion = '3.9.1'
53+
ext.seleniumVersion = '3.11.0'
5454

5555
dependencies {
5656
compile ("org.seleniumhq.selenium:selenium-java:${seleniumVersion}") {

docs/environment.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Appium Environment Troubleshooting
2+
3+
Quite often there are questions about why Appium throws an error about missing environment variable, for example `ANDROID_HOME`, or about missing binaries, like `idevice_id`, `carthage` or `java`. This article explains what might be a cause of such problem and how to resolve it.
4+
5+
## Prerequisites
6+
7+
In order to understand this topic you should know concept of environment variables, how it works in your operating system and how you can change the system environment if needed. In particular, it is important to know the meaning of `PATH` environment variable. Read https://en.wikipedia.org/wiki/Environment_variable and https://en.wikipedia.org/wiki/PATH_(variable) for more details.
8+
9+
## How To Verify What Is Missing
10+
11+
Appium itself is a NodeJS application and uses the same environment as its host `node` process. If you experience an error related to local environment setup then verify the actual process environment first. In Mac OS, for example, it is possible to do this via `ps eww <PID>` command, where `PID` is the process identifier of the running Appium's host Node process. In Windows the [ProcessExplorer](technet.microsoft.com/en-us/sysinternals/bb896653.aspx) utility can be used for such purpose. Then make sure the corresponding variable is there and it is set to a proper value, or, in case there is an error finding some binary, make sure the parent folder of this binary is present in `PATH` list, the binary itself on the local file system and can be executed manually.
12+
13+
## How To Fix Missing Environment Variables
14+
15+
Usually, if one starts Appium from the command line, then it inherits all the environment variables from the parent process (`bash`/`cmd`, etc.). This means that if you start Appium manually or with a script then make sure its parent process has all the necessary environemnt variables set to proper values. Also, it is possible to set variables on [per-process](https://stackoverflow.com/questions/10856129/setting-an-environment-variable-before-a-command-in-bash-not-working-for-second) basis. This might be handy if Appium is set up to start automatically with the operating system, because on early stages of system initialization it might be that the "usual" environment is not present yet.
16+
17+
In case the Appium process is started programatically, for example with java client's `AppiumDriverLocalService` helper class, then it might be necessary to setup the environment [in the client code](https://github.com/appium/java-client/pull/753), because prior to version 6.0 the client does not inherit it from the the parent process by default.

src/main/java/io/appium/java_client/AppiumFluentWait.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,19 @@
1616

1717
package io.appium.java_client;
1818

19+
import static java.time.Duration.ofMillis;
20+
1921
import com.google.common.base.Throwables;
2022

2123
import org.openqa.selenium.TimeoutException;
2224
import org.openqa.selenium.WebDriverException;
2325
import org.openqa.selenium.support.ui.Clock;
24-
import org.openqa.selenium.support.ui.Duration;
2526
import org.openqa.selenium.support.ui.FluentWait;
2627
import org.openqa.selenium.support.ui.Sleeper;
2728

2829
import java.lang.reflect.Field;
30+
import java.time.Duration;
2931
import java.util.List;
30-
import java.util.concurrent.TimeUnit;
3132
import java.util.function.Function;
3233
import java.util.function.Supplier;
3334

@@ -167,7 +168,7 @@ protected T getInput() {
167168
/**
168169
* Sets the strategy for polling. The default strategy is null,
169170
* which means, that polling interval is always a constant value and is
170-
* set by {@link #pollingEvery(long, TimeUnit)} method. Otherwise the value set by that
171+
* set by {@link #pollingEvery(Duration)} method. Otherwise the value set by that
171172
* method might be just a helper to calculate the actual interval.
172173
* Although, by setting an alternative polling strategy you may flexibly control
173174
* the duration of this interval for each polling round.
@@ -228,7 +229,7 @@ public AppiumFluentWait<T> withPollingStrategy(Function<IterationInfo, Duration>
228229
@Override
229230
public <V> V until(Function<? super T, V> isTrue) {
230231
final long start = getClock().now();
231-
final long end = getClock().laterBy(getTimeout().in(TimeUnit.MILLISECONDS));
232+
final long end = getClock().laterBy(getTimeout().toMillis());
232233
long iterationNumber = 1;
233234
Throwable lastException;
234235
while (true) {
@@ -254,15 +255,15 @@ public <V> V until(Function<? super T, V> isTrue) {
254255
String timeoutMessage = String.format(
255256
"Expected condition failed: %s (tried for %d second(s) with %s interval)",
256257
message == null ? "waiting for " + isTrue : message,
257-
getTimeout().in(TimeUnit.SECONDS), getInterval());
258+
getTimeout().getSeconds(), getInterval());
258259
throw timeoutException(timeoutMessage, lastException);
259260
}
260261

261262
try {
262263
Duration interval = getInterval();
263264
if (pollingStrategy != null) {
264265
final IterationInfo info = new IterationInfo(iterationNumber,
265-
new Duration(getClock().now() - start, TimeUnit.MILLISECONDS), getTimeout(),
266+
ofMillis(getClock().now() - start), getTimeout(),
266267
interval);
267268
interval = pollingStrategy.apply(info);
268269
}

src/main/java/io/appium/java_client/MobileCommand.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ public class MobileCommand {
5858
protected static final String REMOVE_APP;
5959
//endregion
6060

61+
//region Clipboard
62+
public static final String GET_CLIPBOARD;
63+
public static final String SET_CLIPBOARD;
64+
//endregion
65+
6166
protected static final String GET_PERFORMANCE_DATA;
6267
protected static final String GET_SUPPORTED_PERFORMANCE_DATA_TYPES;
6368

@@ -127,6 +132,11 @@ public class MobileCommand {
127132
INSTALL_APP = "installApp";
128133
//endregion
129134

135+
//region Clipboard
136+
SET_CLIPBOARD = "setClipboard";
137+
GET_CLIPBOARD = "getClipboard";
138+
//endregion
139+
130140
GET_PERFORMANCE_DATA = "getPerformanceData";
131141
GET_SUPPORTED_PERFORMANCE_DATA_TYPES = "getSuppportedPerformanceDataTypes";
132142

@@ -205,6 +215,11 @@ public class MobileCommand {
205215
commandRepository.put(QUERY_APP_STATE, postC("/session/:sessionId/appium/device/app_state"));
206216
//endregion
207217

218+
//region Clipboard
219+
commandRepository.put(GET_CLIPBOARD, postC("/session/:sessionId/appium/device/get_clipboard"));
220+
commandRepository.put(SET_CLIPBOARD, postC("/session/:sessionId/appium/device/set_clipboard"));
221+
//endregion
222+
208223
//iOS
209224
commandRepository.put(SHAKE, postC("/session/:sessionId/appium/device/shake"));
210225
commandRepository.put(TOUCH_ID, postC("/session/:sessionId/appium/simulator/touch_id"));

src/main/java/io/appium/java_client/android/AndroidDriver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class AndroidDriver<T extends WebElement>
5353
FindsByAndroidUIAutomator<T>, LocksDevice, HasAndroidSettings, HasDeviceDetails,
5454
HasSupportedPerformanceDataType, AuthenticatesByFinger,
5555
CanRecordScreen, SupportsSpecialEmulatorCommands,
56-
SupportsNetworkStateManagement, ListensToLogcatMessages {
56+
SupportsNetworkStateManagement, ListensToLogcatMessages, HasAndroidClipboard {
5757

5858
private static final String ANDROID_PLATFORM = MobilePlatform.ANDROID;
5959

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* See the NOTICE file distributed with this work for additional
5+
* information regarding copyright ownership.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appium.java_client.android;
18+
19+
import static com.google.common.base.Preconditions.checkNotNull;
20+
import static io.appium.java_client.MobileCommand.SET_CLIPBOARD;
21+
import static io.appium.java_client.MobileCommand.prepareArguments;
22+
23+
import io.appium.java_client.CommandExecutionHelper;
24+
import io.appium.java_client.clipboard.ClipboardContentType;
25+
import io.appium.java_client.clipboard.HasClipboard;
26+
27+
import java.nio.charset.StandardCharsets;
28+
import java.util.AbstractMap;
29+
import java.util.Base64;
30+
31+
public interface HasAndroidClipboard extends HasClipboard {
32+
/**
33+
* Set the content of device's clipboard.
34+
*
35+
* @param label clipboard data label.
36+
* @param contentType one of supported content types.
37+
* @param base64Content base64-encoded content to be set.
38+
*/
39+
default void setClipboard(String label, ClipboardContentType contentType, byte[] base64Content) {
40+
String[] parameters = new String[]{"content", "contentType", "label"};
41+
Object[] values = new Object[]{new String(checkNotNull(base64Content), StandardCharsets.UTF_8),
42+
contentType.name().toLowerCase(), checkNotNull(label)};
43+
CommandExecutionHelper.execute(this, new AbstractMap.SimpleEntry<>(SET_CLIPBOARD,
44+
prepareArguments(parameters, values)));
45+
}
46+
47+
/**
48+
* Set the clipboard text.
49+
*
50+
* @param label clipboard data label.
51+
* @param text The actual text to be set.
52+
*/
53+
default void setClipboardText(String label, String text) {
54+
setClipboard(label, ClipboardContentType.PLAINTEXT, Base64
55+
.getEncoder()
56+
.encode(text.getBytes(StandardCharsets.UTF_8)));
57+
}
58+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* See the NOTICE file distributed with this work for additional
5+
* information regarding copyright ownership.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appium.java_client.clipboard;
18+
19+
public enum ClipboardContentType {
20+
PLAINTEXT, IMAGE, URL
21+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* See the NOTICE file distributed with this work for additional
5+
* information regarding copyright ownership.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appium.java_client.clipboard;
18+
19+
import static com.google.common.base.Preconditions.checkNotNull;
20+
import static io.appium.java_client.MobileCommand.GET_CLIPBOARD;
21+
import static io.appium.java_client.MobileCommand.SET_CLIPBOARD;
22+
import static io.appium.java_client.MobileCommand.prepareArguments;
23+
24+
import io.appium.java_client.CommandExecutionHelper;
25+
import io.appium.java_client.ExecutesMethod;
26+
27+
import java.nio.charset.StandardCharsets;
28+
import java.util.AbstractMap;
29+
import java.util.Base64;
30+
31+
public interface HasClipboard extends ExecutesMethod {
32+
/**
33+
* Set the content of device's clipboard.
34+
*
35+
* @param contentType one of supported content types.
36+
* @param base64Content base64-encoded content to be set.
37+
*/
38+
default void setClipboard(ClipboardContentType contentType, byte[] base64Content) {
39+
String[] parameters = new String[]{"content", "contentType"};
40+
Object[] values = new Object[]{new String(checkNotNull(base64Content), StandardCharsets.UTF_8),
41+
contentType.name().toLowerCase()};
42+
CommandExecutionHelper.execute(this, new AbstractMap.SimpleEntry<>(SET_CLIPBOARD,
43+
prepareArguments(parameters, values)));
44+
}
45+
46+
/**
47+
* Get the content of the clipboard.
48+
*
49+
* @param contentType one of supported content types.
50+
* @return the actual content of the clipboard as base64-encoded string or an empty string if the clipboard is empty
51+
*/
52+
default String getClipboard(ClipboardContentType contentType) {
53+
return CommandExecutionHelper.execute(this, new AbstractMap.SimpleEntry<>(GET_CLIPBOARD,
54+
prepareArguments("contentType", contentType.name().toLowerCase())));
55+
}
56+
57+
/**
58+
* Set the clipboard text.
59+
*
60+
* @param text The actual text to be set.
61+
*/
62+
default void setClipboardText(String text) {
63+
setClipboard(ClipboardContentType.PLAINTEXT, Base64
64+
.getEncoder()
65+
.encode(text.getBytes(StandardCharsets.UTF_8)));
66+
}
67+
68+
/**
69+
* Get the clipboard text.
70+
*
71+
* @return Either the text, which is stored in the clipboard or an empty string if the clipboard is empty
72+
*/
73+
default String getClipboardText() {
74+
byte[] base64decodedBytes = Base64
75+
.getDecoder()
76+
.decode(getClipboard(ClipboardContentType.PLAINTEXT));
77+
return new String(base64decodedBytes, StandardCharsets.UTF_8);
78+
}
79+
}

src/main/java/io/appium/java_client/events/DefaultAspect.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class DefaultAspect {
8383
+ ".rotate(..))";
8484
private static final String EXECUTION_CONTEXT = "execution(* org.openqa.selenium.ContextAware."
8585
+ "context(..))";
86+
private static final String EXECUTION_SWITCH_TO_WINDOW = "execution(* org.openqa.selenium.WebDriver.TargetLocator"
87+
+ ".window(..))";
8688
private static final String AROUND = "execution(* org.openqa.selenium.WebDriver.*(..)) || "
8789
+ "execution(* org.openqa.selenium.WebElement.*(..)) || "
8890
+ "execution(* org.openqa.selenium.WebDriver.Navigation.*(..)) || "
@@ -463,6 +465,24 @@ public void afterMaximization(JoinPoint joinPoint) throws Throwable {
463465
}
464466
}
465467

468+
@Before(EXECUTION_SWITCH_TO_WINDOW)
469+
public void beforeSwitchToWindow(JoinPoint joinPoint) throws Throwable {
470+
try {
471+
listener.beforeSwitchToWindow(castArgument(joinPoint, 0), driver);
472+
} catch (Throwable t) {
473+
throw getRootCause(t);
474+
}
475+
}
476+
477+
@After(EXECUTION_SWITCH_TO_WINDOW)
478+
public void afterSwitchToWindow(JoinPoint joinPoint) throws Throwable {
479+
try {
480+
listener.afterSwitchToWindow(castArgument(joinPoint, 0), driver);
481+
} catch (Throwable t) {
482+
throw getRootCause(t);
483+
}
484+
}
485+
466486
@Before(EXECUTION_ROTATE)
467487
public void beforeRotation(JoinPoint joinPoint) throws Throwable {
468488
try {

src/main/java/io/appium/java_client/events/DefaultListener.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,16 @@ public void afterWindowIsMoved(WebDriver driver, WebDriver.Window window, Point
209209
((WindowEventListener) dispatcher).afterWindowIsMaximized(driver, window);
210210
}
211211

212+
@Override
213+
public void beforeSwitchToWindow(String windowName, WebDriver driver) {
214+
((WebDriverEventListener) dispatcher).beforeSwitchToWindow(windowName, driver);
215+
}
216+
217+
@Override
218+
public void afterSwitchToWindow(String windowName, WebDriver driver) {
219+
((WebDriverEventListener) dispatcher).afterSwitchToWindow(windowName, driver);
220+
}
221+
212222
@Override public void beforeSwitchingToContext(WebDriver driver, String context) {
213223
((ContextEventListener) dispatcher).beforeSwitchingToContext(driver, context);
214224
}

src/main/java/io/appium/java_client/events/api/general/WindowEventListener.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,20 @@ void afterWindowIsMoved(WebDriver driver, WebDriver.Window window,
7878
* @param window is the window which has been maximized
7979
*/
8080
void afterWindowIsMaximized(WebDriver driver, WebDriver.Window window);
81+
82+
/**
83+
* This action will be performed each time before
84+
* {@link org.openqa.selenium.WebDriver.TargetLocator#window(java.lang.String)}.
85+
*
86+
* @param driver WebDriver
87+
*/
88+
void beforeSwitchToWindow(String windowName, WebDriver driver);
89+
90+
/**
91+
* This action will be performed each time after
92+
* {@link org.openqa.selenium.WebDriver.TargetLocator#window(java.lang.String)}.
93+
*
94+
* @param driver WebDriver
95+
*/
96+
void afterSwitchToWindow(String windowName, WebDriver driver);
8197
}

0 commit comments

Comments
 (0)