Skip to content

Commit a054753

Browse files
feat: Implement HasBiDi interface support in AppiumDriver
1 parent 42480fe commit a054753

File tree

2 files changed

+122
-84
lines changed

2 files changed

+122
-84
lines changed

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

Lines changed: 119 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import org.openqa.selenium.SessionNotCreatedException;
3131
import org.openqa.selenium.UnsupportedCommandException;
3232
import org.openqa.selenium.WebDriverException;
33+
import org.openqa.selenium.bidi.BiDi;
34+
import org.openqa.selenium.bidi.HasBiDi;
3335
import org.openqa.selenium.remote.CapabilityType;
3436
import org.openqa.selenium.remote.DriverCommand;
3537
import org.openqa.selenium.remote.ErrorHandler;
@@ -42,6 +44,8 @@
4244
import org.openqa.selenium.remote.http.HttpClient;
4345
import org.openqa.selenium.remote.http.HttpMethod;
4446

47+
import java.net.URI;
48+
import java.net.URISyntaxException;
4549
import java.net.URL;
4650
import java.util.Arrays;
4751
import java.util.Collections;
@@ -66,7 +70,8 @@ public class AppiumDriver extends RemoteWebDriver implements
6670
LogsEvents,
6771
HasBrowserCheck,
6872
CanRememberExtensionPresence,
69-
HasSettings {
73+
HasSettings,
74+
HasBiDi {
7075

7176
private static final ErrorHandler ERROR_HANDLER = new ErrorHandler(new ErrorCodesMobile(), true);
7277
// frequently used command parameters
@@ -76,6 +81,8 @@ public class AppiumDriver extends RemoteWebDriver implements
7681
protected final RemoteLocationContext locationContext;
7782
private final ExecuteMethod executeMethod;
7883
private final Set<String> absentExtensionNames = new HashSet<>();
84+
protected URI biDiUri;
85+
protected BiDi biDi;
7986

8087
/**
8188
* Creates a new instance based on command {@code executor} and {@code capabilities}.
@@ -171,54 +178,6 @@ public AppiumDriver(URL remoteSessionAddress, String platformName, String automa
171178
setSessionId(sessionAddress.getId());
172179
}
173180

174-
/**
175-
* Changes platform name if it is not set and returns merged capabilities.
176-
*
177-
* @param originalCapabilities the given {@link Capabilities}.
178-
* @param defaultName a platformName value which has to be set up
179-
* @return {@link Capabilities} with changed platform name value or the original capabilities
180-
*/
181-
protected static Capabilities ensurePlatformName(
182-
Capabilities originalCapabilities, String defaultName) {
183-
return originalCapabilities.getPlatformName() == null
184-
? originalCapabilities.merge(new ImmutableCapabilities(PLATFORM_NAME, defaultName))
185-
: originalCapabilities;
186-
}
187-
188-
/**
189-
* Changes automation name if it is not set and returns merged capabilities.
190-
*
191-
* @param originalCapabilities the given {@link Capabilities}.
192-
* @param defaultName a platformName value which has to be set up
193-
* @return {@link Capabilities} with changed mobile automation name value or the original capabilities
194-
*/
195-
protected static Capabilities ensureAutomationName(
196-
Capabilities originalCapabilities, String defaultName) {
197-
String currentAutomationName = CapabilityHelpers.getCapability(
198-
originalCapabilities, AUTOMATION_NAME_OPTION, String.class);
199-
if (isNullOrEmpty(currentAutomationName)) {
200-
String capabilityName = originalCapabilities.getCapabilityNames()
201-
.contains(AUTOMATION_NAME_OPTION) ? AUTOMATION_NAME_OPTION : APPIUM_PREFIX + AUTOMATION_NAME_OPTION;
202-
return originalCapabilities.merge(new ImmutableCapabilities(capabilityName, defaultName));
203-
}
204-
return originalCapabilities;
205-
}
206-
207-
/**
208-
* Changes platform and automation names if they are not set
209-
* and returns merged capabilities.
210-
*
211-
* @param originalCapabilities the given {@link Capabilities}.
212-
* @param defaultPlatformName a platformName value which has to be set up
213-
* @param defaultAutomationName The default automation name to set up for this class
214-
* @return {@link Capabilities} with changed platform/automation name value or the original capabilities
215-
*/
216-
protected static Capabilities ensurePlatformAndAutomationNames(
217-
Capabilities originalCapabilities, String defaultPlatformName, String defaultAutomationName) {
218-
Capabilities capsWithPlatformFixed = ensurePlatformName(originalCapabilities, defaultPlatformName);
219-
return ensureAutomationName(capsWithPlatformFixed, defaultAutomationName);
220-
}
221-
222181
@Override
223182
public ExecuteMethod getExecuteMethod() {
224183
return executeMethod;
@@ -260,39 +219,6 @@ public void addCommand(HttpMethod httpMethod, String url, String methodName) {
260219
((AppiumCommandExecutor) getCommandExecutor()).refreshAdditionalCommands();
261220
}
262221

263-
@Override
264-
protected void startSession(Capabilities capabilities) {
265-
var response = Optional.ofNullable(
266-
execute(DriverCommand.NEW_SESSION(singleton(capabilities)))
267-
).orElseThrow(() -> new SessionNotCreatedException(
268-
"The underlying command executor returned a null response."
269-
));
270-
271-
var rawCapabilities = Optional.ofNullable(response.getValue())
272-
.map(value -> {
273-
if (!(value instanceof Map)) {
274-
throw new SessionNotCreatedException(String.format(
275-
"The underlying command executor returned a response "
276-
+ "with a non well formed payload: %s", response)
277-
);
278-
}
279-
//noinspection unchecked
280-
return (Map<String, Object>) value;
281-
})
282-
.orElseThrow(() -> new SessionNotCreatedException(
283-
"The underlying command executor returned a response without payload: " + response)
284-
);
285-
286-
// TODO: remove this workaround for Selenium API enforcing some legacy capability values in major version
287-
rawCapabilities.remove("platform");
288-
if (rawCapabilities.containsKey(CapabilityType.BROWSER_NAME)
289-
&& isNullOrEmpty((String) rawCapabilities.get(CapabilityType.BROWSER_NAME))) {
290-
rawCapabilities.remove(CapabilityType.BROWSER_NAME);
291-
}
292-
this.capabilities = new BaseOptions<>(rawCapabilities);
293-
setSessionId(response.getSessionId());
294-
}
295-
296222
@Override
297223
public Response execute(String driverCommand, Map<String, ?> parameters) {
298224
return super.execute(driverCommand, parameters);
@@ -337,7 +263,118 @@ public AppiumDriver markExtensionAbsence(String extName) {
337263
return this;
338264
}
339265

266+
@Override
267+
public Optional<BiDi> maybeGetBiDi() {
268+
return Optional.ofNullable(this.biDi);
269+
}
270+
340271
protected HttpClient getHttpClient() {
341272
return ((HttpCommandExecutor) getCommandExecutor()).client;
342273
}
274+
275+
@Override
276+
protected void startSession(Capabilities capabilities) {
277+
var response = Optional.ofNullable(
278+
execute(DriverCommand.NEW_SESSION(singleton(capabilities)))
279+
).orElseThrow(() -> new SessionNotCreatedException(
280+
"The underlying command executor returned a null response."
281+
));
282+
283+
var rawCapabilities = Optional.ofNullable(response.getValue())
284+
.map(value -> {
285+
if (!(value instanceof Map)) {
286+
throw new SessionNotCreatedException(String.format(
287+
"The underlying command executor returned a response "
288+
+ "with a non well formed payload: %s", response)
289+
);
290+
}
291+
//noinspection unchecked
292+
return (Map<String, Object>) value;
293+
})
294+
.orElseThrow(() -> new SessionNotCreatedException(
295+
"The underlying command executor returned a response without payload: " + response)
296+
);
297+
298+
// TODO: remove this workaround for Selenium API enforcing some legacy capability values in major version
299+
rawCapabilities.remove("platform");
300+
if (rawCapabilities.containsKey(CapabilityType.BROWSER_NAME)
301+
&& isNullOrEmpty((String) rawCapabilities.get(CapabilityType.BROWSER_NAME))) {
302+
rawCapabilities.remove(CapabilityType.BROWSER_NAME);
303+
}
304+
this.capabilities = new BaseOptions<>(rawCapabilities);
305+
this.initBiDi(capabilities);
306+
setSessionId(response.getSessionId());
307+
}
308+
309+
/**
310+
* Changes platform name if it is not set and returns merged capabilities.
311+
*
312+
* @param originalCapabilities the given {@link Capabilities}.
313+
* @param defaultName a platformName value which has to be set up
314+
* @return {@link Capabilities} with changed platform name value or the original capabilities
315+
*/
316+
protected static Capabilities ensurePlatformName(
317+
Capabilities originalCapabilities, String defaultName) {
318+
return originalCapabilities.getPlatformName() == null
319+
? originalCapabilities.merge(new ImmutableCapabilities(PLATFORM_NAME, defaultName))
320+
: originalCapabilities;
321+
}
322+
323+
/**
324+
* Changes automation name if it is not set and returns merged capabilities.
325+
*
326+
* @param originalCapabilities the given {@link Capabilities}.
327+
* @param defaultName a platformName value which has to be set up
328+
* @return {@link Capabilities} with changed mobile automation name value or the original capabilities
329+
*/
330+
protected static Capabilities ensureAutomationName(
331+
Capabilities originalCapabilities, String defaultName) {
332+
String currentAutomationName = CapabilityHelpers.getCapability(
333+
originalCapabilities, AUTOMATION_NAME_OPTION, String.class);
334+
if (isNullOrEmpty(currentAutomationName)) {
335+
String capabilityName = originalCapabilities.getCapabilityNames()
336+
.contains(AUTOMATION_NAME_OPTION) ? AUTOMATION_NAME_OPTION : APPIUM_PREFIX + AUTOMATION_NAME_OPTION;
337+
return originalCapabilities.merge(new ImmutableCapabilities(capabilityName, defaultName));
338+
}
339+
return originalCapabilities;
340+
}
341+
342+
/**
343+
* Changes platform and automation names if they are not set
344+
* and returns merged capabilities.
345+
*
346+
* @param originalCapabilities the given {@link Capabilities}.
347+
* @param defaultPlatformName a platformName value which has to be set up
348+
* @param defaultAutomationName The default automation name to set up for this class
349+
* @return {@link Capabilities} with changed platform/automation name value or the original capabilities
350+
*/
351+
protected static Capabilities ensurePlatformAndAutomationNames(
352+
Capabilities originalCapabilities, String defaultPlatformName, String defaultAutomationName) {
353+
Capabilities capsWithPlatformFixed = ensurePlatformName(originalCapabilities, defaultPlatformName);
354+
return ensureAutomationName(capsWithPlatformFixed, defaultAutomationName);
355+
}
356+
357+
private void initBiDi(Capabilities responseCaps) {
358+
var webSocketUrl = CapabilityHelpers.getCapability(responseCaps, "webSocketUrl", String.class);
359+
if (webSocketUrl == null) {
360+
return;
361+
}
362+
try {
363+
this.biDiUri = new URI(webSocketUrl);
364+
} catch (URISyntaxException e) {
365+
// no valid url -> no BiDi
366+
return;
367+
}
368+
var executor = getCommandExecutor();
369+
final HttpClient wsClient;
370+
if (executor instanceof AppiumCommandExecutor) {
371+
var wsConfig = ((AppiumCommandExecutor) executor).getAppiumClientConfig().baseUri(biDiUri);
372+
wsClient = ((AppiumCommandExecutor) executor).getHttpClientFactory().createClient(wsConfig);
373+
} else {
374+
var wsConfig = AppiumClientConfig.defaultConfig().baseUri(biDiUri);
375+
wsClient = HttpClient.Factory.createDefault().createClient(wsConfig);
376+
}
377+
var biDiConnection = new org.openqa.selenium.bidi.Connection(wsClient, biDiUri.toString());
378+
this.biDi = new BiDi(biDiConnection);
379+
}
343380
}

src/main/java/io/appium/java_client/remote/AppiumCommandExecutor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.common.base.Throwables;
2020
import io.appium.java_client.AppiumClientConfig;
2121
import io.appium.java_client.internal.ReflectionHelpers;
22+
import lombok.Getter;
2223
import org.openqa.selenium.SessionNotCreatedException;
2324
import org.openqa.selenium.WebDriverException;
2425
import org.openqa.selenium.remote.Command;
@@ -54,9 +55,9 @@
5455
public class AppiumCommandExecutor extends HttpCommandExecutor {
5556

5657
private final Optional<DriverService> serviceOptional;
57-
58+
@Getter
5859
private final HttpClient.Factory httpClientFactory;
59-
60+
@Getter
6061
private final AppiumClientConfig appiumClientConfig;
6162

6263
/**

0 commit comments

Comments
 (0)