Skip to content

Commit

Permalink
add a test
Browse files Browse the repository at this point in the history
  • Loading branch information
mykola-mokhnach committed Dec 14, 2024
1 parent 7b3ffc6 commit e2c20ee
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public static void beforeClass() {
.setDeviceName(DEVICE_NAME)
.setCommandTimeouts(Duration.ofSeconds(240))
.setApp(TEST_APP_ZIP)
.enableBiDi()
.setWdaLaunchTimeout(WDA_LAUNCH_TIMEOUT);
try {
driver = new IOSDriver(service.getUrl(), options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ public static void beforeClass() {
.setDeviceName(DEVICE_NAME)
.setWdaLaunchTimeout(WDA_LAUNCH_TIMEOUT)
.setCommandTimeouts(Duration.ofSeconds(240))
.setShowIosLog(true)
.setApp(VODQA_ZIP);
Supplier<IOSDriver> createDriver = () -> new IOSDriver(service.getUrl(), options);
try {
Expand Down
41 changes: 41 additions & 0 deletions src/e2eIosTest/java/io/appium/java_client/ios/IOSBiDiTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.ios;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.bidi.log.LogEntry;
import org.openqa.selenium.bidi.module.LogInspector;

import java.util.concurrent.CopyOnWriteArrayList;

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

public class IOSBiDiTest extends AppIOSTest {

@Test
@Disabled("Need to resolve compatibility issues")
public void listenForIosLogs() {
var logs = new CopyOnWriteArrayList<LogEntry>();
try (var logInspector = new LogInspector(driver)) {
logInspector.onLog(logs::add);
driver.getPageSource();
}
assertFalse(logs.isEmpty());
}

}
81 changes: 62 additions & 19 deletions src/main/java/io/appium/java_client/AppiumDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.appium.java_client.remote.AppiumCommandExecutor;
import io.appium.java_client.remote.AppiumW3CHttpCommandCodec;
import io.appium.java_client.remote.options.BaseOptions;
import io.appium.java_client.remote.options.SupportsWebSocketUrlOption;
import io.appium.java_client.service.local.AppiumDriverLocalService;
import io.appium.java_client.service.local.AppiumServiceBuilder;
import lombok.Getter;
Expand All @@ -31,6 +32,7 @@
import org.openqa.selenium.UnsupportedCommandException;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.bidi.BiDi;
import org.openqa.selenium.bidi.BiDiException;
import org.openqa.selenium.bidi.HasBiDi;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DriverCommand;
Expand All @@ -44,6 +46,7 @@
import org.openqa.selenium.remote.http.HttpClient;
import org.openqa.selenium.remote.http.HttpMethod;

import javax.annotation.Nonnull;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
Expand Down Expand Up @@ -152,8 +155,8 @@ public AppiumDriver(Capabilities capabilities) {
* !!! This API is supposed to be used for **debugging purposes only**.
*
* @param remoteSessionAddress The address of the **running** session including the session identifier.
* @param platformName The name of the target platform.
* @param automationName The name of the target automation.
* @param platformName The name of the target platform.
* @param automationName The name of the target automation.
*/
public AppiumDriver(URL remoteSessionAddress, String platformName, String automationName) {
super();
Expand Down Expand Up @@ -268,19 +271,46 @@ public Optional<BiDi> maybeGetBiDi() {
return Optional.ofNullable(this.biDi);
}

@Override
@Nonnull
public BiDi getBiDi() {
var webSocketUrl = ((BaseOptions<?>) this.capabilities).getWebSocketUrl().orElseThrow(
() -> new BiDiException(
String.format(
"BiDi is not enabled for this driver session. " +
"Did you set %s to true?", SupportsWebSocketUrlOption.WEB_SOCKET_URL
)
)
);
if (this.biDiUri == null) {
throw new BiDiException(
String.format(
"BiDi is not enabled for this driver session. " +
"Is the %s '%s' received from the create session response valid?",
SupportsWebSocketUrlOption.WEB_SOCKET_URL, webSocketUrl
)
);
}
if (this.biDi == null) {
// This should not happen
throw new IllegalStateException();
}
return this.biDi;
}

protected HttpClient getHttpClient() {
return ((HttpCommandExecutor) getCommandExecutor()).client;
}

@Override
protected void startSession(Capabilities capabilities) {
protected void startSession(Capabilities requestCapabilities) {
var response = Optional.ofNullable(
execute(DriverCommand.NEW_SESSION(singleton(capabilities)))
execute(DriverCommand.NEW_SESSION(singleton(requestCapabilities)))
).orElseThrow(() -> new SessionNotCreatedException(
"The underlying command executor returned a null response."
));

var rawCapabilities = Optional.ofNullable(response.getValue())
var rawResponseCapabilities = Optional.ofNullable(response.getValue())
.map(value -> {
if (!(value instanceof Map)) {
throw new SessionNotCreatedException(String.format(
Expand All @@ -296,13 +326,15 @@ protected void startSession(Capabilities capabilities) {
);

// TODO: remove this workaround for Selenium API enforcing some legacy capability values in major version
rawCapabilities.remove("platform");
if (rawCapabilities.containsKey(CapabilityType.BROWSER_NAME)
&& isNullOrEmpty((String) rawCapabilities.get(CapabilityType.BROWSER_NAME))) {
rawCapabilities.remove(CapabilityType.BROWSER_NAME);
rawResponseCapabilities.remove("platform");
if (rawResponseCapabilities.containsKey(CapabilityType.BROWSER_NAME)
&& isNullOrEmpty((String) rawResponseCapabilities.get(CapabilityType.BROWSER_NAME))) {
rawResponseCapabilities.remove(CapabilityType.BROWSER_NAME);
}
this.capabilities = new BaseOptions<>(rawResponseCapabilities);
if (Boolean.TRUE.equals(requestCapabilities.getCapability(SupportsWebSocketUrlOption.WEB_SOCKET_URL))) {
this.initBiDi((BaseOptions<?>) capabilities);
}
this.capabilities = new BaseOptions<>(rawCapabilities);
this.initBiDi(capabilities);
setSessionId(response.getSessionId());
}

Expand Down Expand Up @@ -343,8 +375,8 @@ protected static Capabilities ensureAutomationName(
* Changes platform and automation names if they are not set
* and returns merged capabilities.
*
* @param originalCapabilities the given {@link Capabilities}.
* @param defaultPlatformName a platformName value which has to be set up
* @param originalCapabilities the given {@link Capabilities}.
* @param defaultPlatformName a platformName value which has to be set up
* @param defaultAutomationName The default automation name to set up for this class
* @return {@link Capabilities} with changed platform/automation name value or the original capabilities
*/
Expand All @@ -354,16 +386,27 @@ protected static Capabilities ensurePlatformAndAutomationNames(
return ensureAutomationName(capsWithPlatformFixed, defaultAutomationName);
}

private void initBiDi(Capabilities responseCaps) {
var webSocketUrl = CapabilityHelpers.getCapability(responseCaps, "webSocketUrl", String.class);
if (webSocketUrl == null) {
private void initBiDi(BaseOptions<?> responseCaps) {
var webSocketUrl = responseCaps.getWebSocketUrl();
if (webSocketUrl.isEmpty()) {
return;
}
URISyntaxException uriSyntaxError = null;
try {
this.biDiUri = new URI(webSocketUrl);
this.biDiUri = new URI(String.valueOf(webSocketUrl.get()));
} catch (URISyntaxException e) {
// no valid url -> no BiDi
return;
uriSyntaxError = e;
}
if (uriSyntaxError != null || this.biDiUri.getScheme() == null) {
var message = String.format(
"BiDi cannot be enabled for this driver session. " +
"Is the %s '%s' received from the create session response valid?",
SupportsWebSocketUrlOption.WEB_SOCKET_URL, webSocketUrl.get()
);
if (uriSyntaxError == null) {
throw new BiDiException(message);
}
throw new BiDiException(message, uriSyntaxError);
}
var executor = getCommandExecutor();
final HttpClient wsClient;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public class BaseOptions<T extends BaseOptions<T>> extends MutableCapabilities i
SupportsFullResetOption<T>,
SupportsNewCommandTimeoutOption<T>,
SupportsBrowserNameOption<T>,
SupportsPlatformVersionOption<T> {
SupportsPlatformVersionOption<T>,
SupportsWebSocketUrlOption<T> {

/**
* Creates new instance with no preset capabilities.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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.remote.options;

import org.openqa.selenium.Capabilities;

import java.util.Optional;

public interface SupportsWebSocketUrlOption<T extends BaseOptions<T>> extends
Capabilities, CanSetCapability<T> {
String WEB_SOCKET_URL = "webSocketUrl";

/**
* Enable BiDi session support.
*
* @return self instance for chaining.
*/
default T enableBiDi() {
return amend(WEB_SOCKET_URL, true);
}

/**
* Whether to enable BiDi session support.
*
* @return self instance for chaining.
*/
default T setWebSocketUrl(boolean value) {
return amend(WEB_SOCKET_URL, value);
}

/**
* For input capabilities: whether enable BiDi session support is enabled.
* For session creation response capabilities: BiDi web socket URL.
*
* @return If called on request capabilities if BiDi support is enabled for the driver session
*/
default Optional<Object> getWebSocketUrl() {
return Optional.ofNullable(getCapability(WEB_SOCKET_URL));
}
}

0 comments on commit e2c20ee

Please sign in to comment.