21
21
import io .appium .java_client .remote .AppiumCommandExecutor ;
22
22
import io .appium .java_client .remote .AppiumW3CHttpCommandCodec ;
23
23
import io .appium .java_client .remote .options .BaseOptions ;
24
+ import io .appium .java_client .remote .options .SupportsWebSocketUrlOption ;
24
25
import io .appium .java_client .service .local .AppiumDriverLocalService ;
25
26
import io .appium .java_client .service .local .AppiumServiceBuilder ;
26
27
import lombok .Getter ;
31
32
import org .openqa .selenium .UnsupportedCommandException ;
32
33
import org .openqa .selenium .WebDriverException ;
33
34
import org .openqa .selenium .bidi .BiDi ;
35
+ import org .openqa .selenium .bidi .BiDiException ;
34
36
import org .openqa .selenium .bidi .HasBiDi ;
35
37
import org .openqa .selenium .remote .CapabilityType ;
36
38
import org .openqa .selenium .remote .DriverCommand ;
44
46
import org .openqa .selenium .remote .http .HttpClient ;
45
47
import org .openqa .selenium .remote .http .HttpMethod ;
46
48
49
+ import javax .annotation .Nonnull ;
47
50
import java .net .URI ;
48
51
import java .net .URISyntaxException ;
49
52
import java .net .URL ;
@@ -152,8 +155,8 @@ public AppiumDriver(Capabilities capabilities) {
152
155
* !!! This API is supposed to be used for **debugging purposes only**.
153
156
*
154
157
* @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.
157
160
*/
158
161
public AppiumDriver (URL remoteSessionAddress , String platformName , String automationName ) {
159
162
super ();
@@ -268,19 +271,46 @@ public Optional<BiDi> maybeGetBiDi() {
268
271
return Optional .ofNullable (this .biDi );
269
272
}
270
273
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
+
271
301
protected HttpClient getHttpClient () {
272
302
return ((HttpCommandExecutor ) getCommandExecutor ()).client ;
273
303
}
274
304
275
305
@ Override
276
- protected void startSession (Capabilities capabilities ) {
306
+ protected void startSession (Capabilities requestCapabilities ) {
277
307
var response = Optional .ofNullable (
278
- execute (DriverCommand .NEW_SESSION (singleton (capabilities )))
308
+ execute (DriverCommand .NEW_SESSION (singleton (requestCapabilities )))
279
309
).orElseThrow (() -> new SessionNotCreatedException (
280
310
"The underlying command executor returned a null response."
281
311
));
282
312
283
- var rawCapabilities = Optional .ofNullable (response .getValue ())
313
+ var rawResponseCapabilities = Optional .ofNullable (response .getValue ())
284
314
.map (value -> {
285
315
if (!(value instanceof Map )) {
286
316
throw new SessionNotCreatedException (String .format (
@@ -296,13 +326,15 @@ protected void startSession(Capabilities capabilities) {
296
326
);
297
327
298
328
// 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 );
303
337
}
304
- this .capabilities = new BaseOptions <>(rawCapabilities );
305
- this .initBiDi (capabilities );
306
338
setSessionId (response .getSessionId ());
307
339
}
308
340
@@ -343,8 +375,8 @@ protected static Capabilities ensureAutomationName(
343
375
* Changes platform and automation names if they are not set
344
376
* and returns merged capabilities.
345
377
*
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
348
380
* @param defaultAutomationName The default automation name to set up for this class
349
381
* @return {@link Capabilities} with changed platform/automation name value or the original capabilities
350
382
*/
@@ -354,16 +386,27 @@ protected static Capabilities ensurePlatformAndAutomationNames(
354
386
return ensureAutomationName (capsWithPlatformFixed , defaultAutomationName );
355
387
}
356
388
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 () ) {
360
392
return ;
361
393
}
394
+ URISyntaxException uriSyntaxError = null ;
362
395
try {
363
- this .biDiUri = new URI (webSocketUrl );
396
+ this .biDiUri = new URI (String . valueOf ( webSocketUrl . get ()) );
364
397
} 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 );
367
410
}
368
411
var executor = getCommandExecutor ();
369
412
final HttpClient wsClient ;
0 commit comments