2121import io .appium .java_client .remote .AppiumCommandExecutor ;
2222import io .appium .java_client .remote .AppiumW3CHttpCommandCodec ;
2323import io .appium .java_client .remote .options .BaseOptions ;
24+ import io .appium .java_client .remote .options .SupportsWebSocketUrlOption ;
2425import io .appium .java_client .service .local .AppiumDriverLocalService ;
2526import io .appium .java_client .service .local .AppiumServiceBuilder ;
2627import lombok .Getter ;
3132import org .openqa .selenium .UnsupportedCommandException ;
3233import org .openqa .selenium .WebDriverException ;
3334import org .openqa .selenium .bidi .BiDi ;
35+ import org .openqa .selenium .bidi .BiDiException ;
3436import org .openqa .selenium .bidi .HasBiDi ;
3537import org .openqa .selenium .remote .CapabilityType ;
3638import org .openqa .selenium .remote .DriverCommand ;
4446import org .openqa .selenium .remote .http .HttpClient ;
4547import org .openqa .selenium .remote .http .HttpMethod ;
4648
49+ import javax .annotation .Nonnull ;
4750import java .net .URI ;
4851import java .net .URISyntaxException ;
4952import java .net .URL ;
@@ -152,8 +155,8 @@ public AppiumDriver(Capabilities capabilities) {
152155 * !!! This API is supposed to be used for **debugging purposes only**.
153156 *
154157 * @param remoteSessionAddress The address of the **running** session including the session identifier.
155- * @param platformName The name of the target platform.
156- * @param automationName The name of the target automation.
158+ * @param platformName The name of the target platform.
159+ * @param automationName The name of the target automation.
157160 */
158161 public AppiumDriver (URL remoteSessionAddress , String platformName , String automationName ) {
159162 super ();
@@ -268,19 +271,46 @@ public Optional<BiDi> maybeGetBiDi() {
268271 return Optional .ofNullable (this .biDi );
269272 }
270273
274+ @ Override
275+ @ Nonnull
276+ public BiDi getBiDi () {
277+ var webSocketUrl = ((BaseOptions <?>) this .capabilities ).getWebSocketUrl ().orElseThrow (
278+ () -> new BiDiException (
279+ String .format (
280+ "BiDi is not enabled for this driver session. " +
281+ "Did you set %s to true?" , SupportsWebSocketUrlOption .WEB_SOCKET_URL
282+ )
283+ )
284+ );
285+ if (this .biDiUri == null ) {
286+ throw new BiDiException (
287+ String .format (
288+ "BiDi is not enabled for this driver session. " +
289+ "Is the %s '%s' received from the create session response valid?" ,
290+ SupportsWebSocketUrlOption .WEB_SOCKET_URL , webSocketUrl
291+ )
292+ );
293+ }
294+ if (this .biDi == null ) {
295+ // This should not happen
296+ throw new IllegalStateException ();
297+ }
298+ return this .biDi ;
299+ }
300+
271301 protected HttpClient getHttpClient () {
272302 return ((HttpCommandExecutor ) getCommandExecutor ()).client ;
273303 }
274304
275305 @ Override
276- protected void startSession (Capabilities capabilities ) {
306+ protected void startSession (Capabilities requestCapabilities ) {
277307 var response = Optional .ofNullable (
278- execute (DriverCommand .NEW_SESSION (singleton (capabilities )))
308+ execute (DriverCommand .NEW_SESSION (singleton (requestCapabilities )))
279309 ).orElseThrow (() -> new SessionNotCreatedException (
280310 "The underlying command executor returned a null response."
281311 ));
282312
283- var rawCapabilities = Optional .ofNullable (response .getValue ())
313+ var rawResponseCapabilities = Optional .ofNullable (response .getValue ())
284314 .map (value -> {
285315 if (!(value instanceof Map )) {
286316 throw new SessionNotCreatedException (String .format (
@@ -296,13 +326,15 @@ protected void startSession(Capabilities capabilities) {
296326 );
297327
298328 // 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 );
329+ rawResponseCapabilities .remove ("platform" );
330+ if (rawResponseCapabilities .containsKey (CapabilityType .BROWSER_NAME )
331+ && isNullOrEmpty ((String ) rawResponseCapabilities .get (CapabilityType .BROWSER_NAME ))) {
332+ rawResponseCapabilities .remove (CapabilityType .BROWSER_NAME );
333+ }
334+ this .capabilities = new BaseOptions <>(rawResponseCapabilities );
335+ if (Boolean .TRUE .equals (requestCapabilities .getCapability (SupportsWebSocketUrlOption .WEB_SOCKET_URL ))) {
336+ this .initBiDi ((BaseOptions <?>) capabilities );
303337 }
304- this .capabilities = new BaseOptions <>(rawCapabilities );
305- this .initBiDi (capabilities );
306338 setSessionId (response .getSessionId ());
307339 }
308340
@@ -343,8 +375,8 @@ protected static Capabilities ensureAutomationName(
343375 * Changes platform and automation names if they are not set
344376 * and returns merged capabilities.
345377 *
346- * @param originalCapabilities the given {@link Capabilities}.
347- * @param defaultPlatformName a platformName value which has to be set up
378+ * @param originalCapabilities the given {@link Capabilities}.
379+ * @param defaultPlatformName a platformName value which has to be set up
348380 * @param defaultAutomationName The default automation name to set up for this class
349381 * @return {@link Capabilities} with changed platform/automation name value or the original capabilities
350382 */
@@ -354,16 +386,27 @@ protected static Capabilities ensurePlatformAndAutomationNames(
354386 return ensureAutomationName (capsWithPlatformFixed , defaultAutomationName );
355387 }
356388
357- private void initBiDi (Capabilities responseCaps ) {
358- var webSocketUrl = CapabilityHelpers . getCapability ( responseCaps , "webSocketUrl" , String . class );
359- if (webSocketUrl == null ) {
389+ private void initBiDi (BaseOptions <?> responseCaps ) {
390+ var webSocketUrl = responseCaps . getWebSocketUrl ( );
391+ if (webSocketUrl . isEmpty () ) {
360392 return ;
361393 }
394+ URISyntaxException uriSyntaxError = null ;
362395 try {
363- this .biDiUri = new URI (webSocketUrl );
396+ this .biDiUri = new URI (String . valueOf ( webSocketUrl . get ()) );
364397 } catch (URISyntaxException e ) {
365- // no valid url -> no BiDi
366- return ;
398+ uriSyntaxError = e ;
399+ }
400+ if (uriSyntaxError != null || this .biDiUri .getScheme () == null ) {
401+ var message = String .format (
402+ "BiDi cannot be enabled for this driver session. " +
403+ "Is the %s '%s' received from the create session response valid?" ,
404+ SupportsWebSocketUrlOption .WEB_SOCKET_URL , webSocketUrl .get ()
405+ );
406+ if (uriSyntaxError == null ) {
407+ throw new BiDiException (message );
408+ }
409+ throw new BiDiException (message , uriSyntaxError );
367410 }
368411 var executor = getCommandExecutor ();
369412 final HttpClient wsClient ;
0 commit comments