Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
51d7c1e
feat: support directConnect
KazuCocoa Aug 25, 2022
65e494f
add an argument for directConnect
KazuCocoa Aug 25, 2022
05d0976
add flag in android drivers
KazuCocoa Aug 25, 2022
5c3f950
add config
KazuCocoa Aug 25, 2022
a5c3baa
add for ios
KazuCocoa Aug 25, 2022
48b42e8
add config to other drivers
KazuCocoa Aug 25, 2022
55ad185
update imports, license
KazuCocoa Aug 25, 2022
d0ff28a
use a tag
KazuCocoa Aug 25, 2022
16dd824
fix lint
KazuCocoa Aug 25, 2022
0125a97
update part of
KazuCocoa Aug 25, 2022
138fbd4
fix lint
KazuCocoa Aug 25, 2022
03833d7
create a new client with given config and factory
KazuCocoa Aug 27, 2022
2a89299
add constructor
KazuCocoa Aug 27, 2022
97e8b62
refer to this
KazuCocoa Aug 27, 2022
821c89d
update ios
KazuCocoa Aug 27, 2022
08a4d01
upadte others as well
KazuCocoa Aug 27, 2022
0afaf50
refer to super
KazuCocoa Aug 27, 2022
e6ea969
raise an exception
KazuCocoa Aug 27, 2022
9ee52cb
keep clientconfig as part of appiumclientconfig
KazuCocoa Aug 28, 2022
11c54b5
tweak
KazuCocoa Aug 28, 2022
e7a751a
tweak
KazuCocoa Aug 28, 2022
790857d
modify address
KazuCocoa Aug 28, 2022
fc96519
fix typo
KazuCocoa Aug 28, 2022
1751304
tweak base url
KazuCocoa Aug 28, 2022
160cf8e
fix the default local service
KazuCocoa Aug 28, 2022
8862d75
do not use orElse
KazuCocoa Aug 28, 2022
d923503
fixlint
KazuCocoa Aug 28, 2022
139161b
update null case
KazuCocoa Aug 28, 2022
3b576aa
tweak a bit
KazuCocoa Aug 29, 2022
5358125
Merge branch 'master' into direct-connect
KazuCocoa Sep 29, 2022
ca93f6e
update to extend client config
KazuCocoa Sep 29, 2022
bd1f78c
follows client config
KazuCocoa Sep 30, 2022
bab6830
tweak docstring
KazuCocoa Sep 30, 2022
8d9b785
rever some unnecessary changes
KazuCocoa Sep 30, 2022
839df21
run tests in internal as unitTests, add user agent definition
KazuCocoa Sep 30, 2022
ec63fb1
add tests, tweak some lines
KazuCocoa Sep 30, 2022
0460169
add a key
KazuCocoa Sep 30, 2022
6db723c
add docstring
KazuCocoa Sep 30, 2022
c2676ee
remove unnecessary argument
KazuCocoa Sep 30, 2022
7326811
chore: add comments
KazuCocoa Sep 30, 2022
397f596
chore: tweak
KazuCocoa Sep 30, 2022
9cc5e4a
chore: add UA in a new session request
KazuCocoa Sep 30, 2022
f2dc84a
fix: lint
KazuCocoa Sep 30, 2022
c00697d
chore: remove done todo
KazuCocoa Sep 30, 2022
b1a9413
Merge branch 'master' into direct-connect
KazuCocoa Oct 7, 2022
9ab8547
remove user agent stuff
KazuCocoa Oct 20, 2022
e455f52
remove ua stuff more
KazuCocoa Oct 20, 2022
f6570d2
remove UA stuff
KazuCocoa Oct 20, 2022
69ff238
collect similar process into one method
KazuCocoa Oct 20, 2022
7b2ff2a
tweak
KazuCocoa Oct 20, 2022
bd0395b
add a class object
KazuCocoa Oct 20, 2022
0a11b8a
Merge branch 'direct-connect' of github.com:KazuCocoa/java-client int…
KazuCocoa Oct 20, 2022
bfdd997
tweak
KazuCocoa Oct 20, 2022
6d3d3ec
remove a few lines
KazuCocoa Oct 20, 2022
a834d3b
Revert "remove user agent stuff"
KazuCocoa Oct 20, 2022
3a6aa98
Revert "remove ua stuff more"
KazuCocoa Oct 20, 2022
a3a0367
Revert "remove UA stuff"
KazuCocoa Oct 20, 2022
b26ab69
Merge branch 'master' into useragent
KazuCocoa Oct 23, 2022
70889d0
rename
KazuCocoa Oct 23, 2022
88258d6
fix conflict
KazuCocoa Oct 23, 2022
778d5b6
refer to com.google.common.net.HttpHeaders
KazuCocoa Oct 24, 2022
73c2a85
tweak the logic to include UA
KazuCocoa Oct 24, 2022
1d38839
use parameterized test
KazuCocoa Oct 26, 2022
1b49768
use parameterized more, defined const
KazuCocoa Oct 26, 2022
f0809f5
fix lint
KazuCocoa Oct 26, 2022
f457154
Merge branch 'master' into useragent
KazuCocoa Oct 26, 2022
8b548ca
modify the logic to customize the UA, add tests
KazuCocoa Oct 28, 2022
71c15ed
Merge branch 'useragent' of github.com:KazuCocoa/java-client into use…
KazuCocoa Oct 28, 2022
5ad80f8
match as case insensitive
KazuCocoa Oct 28, 2022
128e583
tweak, fixed addHeader not to add headers twice
KazuCocoa Oct 28, 2022
405a5ec
add annotation
KazuCocoa Oct 29, 2022
407bc18
define a method to build UA
KazuCocoa Oct 29, 2022
e95af13
Merge branch 'master' into useragent
KazuCocoa Oct 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ java {

ext {
seleniumVersion = project.property('selenium.version')
appiumClientVersion = project.property('appiumClient.version')
}

dependencies {
Expand Down Expand Up @@ -110,7 +111,7 @@ publishing {
mavenJava(MavenPublication) {
groupId = 'io.appium'
artifactId = 'java-client'
version = '8.2.0'
version = appiumClientVersion
from components.java
pom {
name = 'java-client'
Expand Down Expand Up @@ -186,7 +187,8 @@ wrapper {

processResources {
filter ReplaceTokens, tokens: [
'selenium.version': seleniumVersion
'selenium.version': seleniumVersion,
'appiumClient.version': appiumClientVersion
]
}

Expand Down
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ ossrhUsername=your-jira-id
ossrhPassword=your-jira-password

selenium.version=4.5.0
# Please increment the value in a release
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SrinivasanTarget Do you usually bump this version manually upon releasing the package or there is some script for that? Shall we sync more java client release scripts/tutorials about this change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mykola-mokhnach I do this manually. maybe its good to come up with a script or leaving it to dependabot can also be a good thing to do when there is a release.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would you have some time to document all steps of the release procedure?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I will do it in the following days.

appiumClient.version=8.2.0
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import org.openqa.selenium.Credentials;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.remote.http.AddSeleniumUserAgent;
import org.openqa.selenium.remote.http.ClientConfig;
import org.openqa.selenium.remote.http.Filter;

Expand All @@ -34,7 +33,7 @@
public class AppiumClientConfig extends ClientConfig {
private final boolean directConnect;

private static final Filter DEFAULT_FILTER = new AddSeleniumUserAgent();
private static final Filter DEFAULT_FILTER = new AppiumUserAgentFilter();

private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofMinutes(10);

Expand Down
91 changes: 91 additions & 0 deletions src/main/java/io/appium/java_client/AppiumUserAgentFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.appium.java_client;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.net.HttpHeaders;
import io.appium.java_client.internal.Config;
import org.openqa.selenium.remote.http.AddSeleniumUserAgent;
import org.openqa.selenium.remote.http.Filter;
import org.openqa.selenium.remote.http.HttpHandler;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
* Manage Appium Client configurations.
*/

public class AppiumUserAgentFilter implements Filter {

public static final String VERSION_KEY = "appiumClient.version";

private static final String USER_AGENT_PREFIX = "appium/";

/**
* A default User Agent name for Appium Java client.
* e.g. appium/8.2.0 (selenium/4.5.0 (java mac))
*/
public static final String USER_AGENT = buildUserAgentHeaderValue(AddSeleniumUserAgent.USER_AGENT);

private static String buildUserAgentHeaderValue(@Nonnull String previousUA) {
return String.format("%s%s (%s)",
USER_AGENT_PREFIX, Config.main().getValue(VERSION_KEY, String.class), previousUA);
}

/**
* Returns true if the given User Agent includes "appium/", which
* implies the User Agent already has the Appium UA by this method.
* The matching is case-insensitive.
* @param userAgent the User Agent in the request headers.
* @return whether the given User Agent includes Appium UA
* like by this filter.
*/
@VisibleForTesting
public static boolean containsAppiumName(@Nullable String userAgent) {
return userAgent != null && userAgent.toLowerCase().contains(USER_AGENT_PREFIX.toLowerCase());
}

/**
* Returns the User Agent. If the given UA already has
* {@link USER_AGENT_PREFIX}, it returns the UA.
* IF the given UA does not have {@link USER_AGENT_PREFIX},
* it returns UA with the Appium prefix.
* @param userAgent the User Agent in the request headers.
* @return the User Agent for the request
*/
public static String buildUserAgent(@Nullable String userAgent) {
if (userAgent == null) {
return USER_AGENT;
}

if (containsAppiumName(userAgent)) {
return userAgent;
}

return buildUserAgentHeaderValue(userAgent);
}

@Override
public HttpHandler apply(HttpHandler next) {

return req -> {
req.setHeader(HttpHeaders.USER_AGENT, buildUserAgent(req.getHeader(HttpHeaders.USER_AGENT)));
return next.execute(req);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;

import com.google.common.net.HttpHeaders;
import io.appium.java_client.AppiumUserAgentFilter;
import io.appium.java_client.AppiumClientConfig;
import org.openqa.selenium.SessionNotCreatedException;
import org.openqa.selenium.WebDriverException;
Expand Down Expand Up @@ -192,6 +194,8 @@ private Response createSession(Command command) throws IOException {

ProtocolHandshake.Result result = new AppiumProtocolHandshake().createSession(
getClient().with((httpHandler) -> (req) -> {
req.setHeader(HttpHeaders.USER_AGENT,
AppiumUserAgentFilter.buildUserAgent(req.getHeader(HttpHeaders.USER_AGENT)));
req.setHeader(IDEMPOTENCY_KEY_HEADER, UUID.randomUUID().toString().toLowerCase());
return httpHandler.execute(req);
}), command
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/main.properties
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
selenium.version=@selenium.version@
appiumClient.version=@appiumClient.version@
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package io.appium.java_client.internal;

import io.appium.java_client.AppiumUserAgentFilter;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;

public class AppiumUserAgentFilterTest {
@Test
void validateUserAgent() {
assertTrue(AppiumUserAgentFilter.USER_AGENT.startsWith("appium/"));
}

public static Stream<Arguments> userAgentParams() {
return Stream.of(
Arguments.of("selenium/4.5.0 (java mac)", false),
Arguments.of("appium/8.2.0 (selenium/4.5.0 (java mac))", true),
Arguments.of("APPIUM/8.2.0 (selenium/4.5.0 (java mac))", true),
Arguments.of("something (Appium/8.2.0 (selenium/4.5.0 (java mac)))", true),
Arguments.of("something (appium/8.2.0 (selenium/4.5.0 (java mac)))", true)
);
}

@ParameterizedTest
@MethodSource("userAgentParams")
void validUserAgentIfContainsAppiumName(String userAgent, boolean expected) {
assertEquals(AppiumUserAgentFilter.containsAppiumName(userAgent), expected);
}

@Test
void validBuildUserAgentNoUA() {
assertEquals(AppiumUserAgentFilter.buildUserAgent(null), AppiumUserAgentFilter.USER_AGENT);
}

@Test
void validBuildUserAgentNoAppium1() {
String ua = AppiumUserAgentFilter.buildUserAgent("selenium/4.5.0 (java mac)");
assertTrue(ua.startsWith("appium/"));
assertTrue(ua.endsWith("selenium/4.5.0 (java mac))"));
}

@Test
void validBuildUserAgentNoAppium2() {
String ua = AppiumUserAgentFilter.buildUserAgent("customSelenium/4.5.0 (java mac)");
assertTrue(ua.startsWith("appium/"));
assertTrue(ua.endsWith("customSelenium/4.5.0 (java mac))"));
}

@Test
void validBuildUserAgentAlreadyHasAppium1() {
// Won't modify since the UA already has appium prefix
String ua = AppiumUserAgentFilter.buildUserAgent("appium/8.1.0 (selenium/4.5.0 (java mac))");
assertEquals("appium/8.1.0 (selenium/4.5.0 (java mac))", ua);
}

@Test
void validBuildUserAgentAlreadyHasAppium2() {
// Won't modify since the UA already has appium prefix
String ua = AppiumUserAgentFilter.buildUserAgent("something (appium/8.1.0 (selenium/4.5.0 (java mac)))");
assertEquals("something (appium/8.1.0 (selenium/4.5.0 (java mac)))", ua);
}
}
23 changes: 16 additions & 7 deletions src/test/java/io/appium/java_client/internal/ConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,36 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import io.appium.java_client.AppiumUserAgentFilter;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.stream.Stream;

class ConfigTest {
private static final String SELENIUM_EXISTING_KEY = "selenium.version";

private static final String MISSING_KEY = "bla";

@Test
void verifyGettingExistingValue() {
assertThat(Config.main().getValue(SELENIUM_EXISTING_KEY, String.class).length(), greaterThan(0));
assertTrue(Config.main().getOptionalValue(SELENIUM_EXISTING_KEY, String.class).isPresent());
@ParameterizedTest
@ValueSource(strings = {SELENIUM_EXISTING_KEY, AppiumUserAgentFilter.VERSION_KEY})
void verifyGettingExistingValue(String key) {
assertThat(Config.main().getValue(key, String.class).length(), greaterThan(0));
assertTrue(Config.main().getOptionalValue(key, String.class).isPresent());
}

@Test
void verifyGettingNonExistingValue() {
assertThrows(IllegalArgumentException.class, () -> Config.main().getValue(MISSING_KEY, String.class));
}

@Test
void verifyGettingExistingValueWithWrongClass() {
assertThrows(ClassCastException.class, () -> Config.main().getValue(SELENIUM_EXISTING_KEY, Integer.class));
@ParameterizedTest
@ValueSource(strings = {SELENIUM_EXISTING_KEY, AppiumUserAgentFilter.VERSION_KEY})
void verifyGettingExistingValueWithWrongClass(String key) {
assertThrows(ClassCastException.class, () -> Config.main().getValue(key, Integer.class));
}

@Test
Expand Down