30
30
import org .openqa .selenium .SessionNotCreatedException ;
31
31
import org .openqa .selenium .UnsupportedCommandException ;
32
32
import org .openqa .selenium .WebDriverException ;
33
+ import org .openqa .selenium .bidi .BiDi ;
34
+ import org .openqa .selenium .bidi .HasBiDi ;
33
35
import org .openqa .selenium .remote .CapabilityType ;
34
36
import org .openqa .selenium .remote .DriverCommand ;
35
37
import org .openqa .selenium .remote .ErrorHandler ;
42
44
import org .openqa .selenium .remote .http .HttpClient ;
43
45
import org .openqa .selenium .remote .http .HttpMethod ;
44
46
47
+ import java .net .URI ;
48
+ import java .net .URISyntaxException ;
45
49
import java .net .URL ;
46
50
import java .util .Arrays ;
47
51
import java .util .Collections ;
@@ -66,7 +70,8 @@ public class AppiumDriver extends RemoteWebDriver implements
66
70
LogsEvents ,
67
71
HasBrowserCheck ,
68
72
CanRememberExtensionPresence ,
69
- HasSettings {
73
+ HasSettings ,
74
+ HasBiDi {
70
75
71
76
private static final ErrorHandler ERROR_HANDLER = new ErrorHandler (new ErrorCodesMobile (), true );
72
77
// frequently used command parameters
@@ -76,6 +81,8 @@ public class AppiumDriver extends RemoteWebDriver implements
76
81
protected final RemoteLocationContext locationContext ;
77
82
private final ExecuteMethod executeMethod ;
78
83
private final Set <String > absentExtensionNames = new HashSet <>();
84
+ protected URI biDiUri ;
85
+ protected BiDi biDi ;
79
86
80
87
/**
81
88
* Creates a new instance based on command {@code executor} and {@code capabilities}.
@@ -171,54 +178,6 @@ public AppiumDriver(URL remoteSessionAddress, String platformName, String automa
171
178
setSessionId (sessionAddress .getId ());
172
179
}
173
180
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
-
222
181
@ Override
223
182
public ExecuteMethod getExecuteMethod () {
224
183
return executeMethod ;
@@ -260,39 +219,6 @@ public void addCommand(HttpMethod httpMethod, String url, String methodName) {
260
219
((AppiumCommandExecutor ) getCommandExecutor ()).refreshAdditionalCommands ();
261
220
}
262
221
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
-
296
222
@ Override
297
223
public Response execute (String driverCommand , Map <String , ?> parameters ) {
298
224
return super .execute (driverCommand , parameters );
@@ -337,7 +263,118 @@ public AppiumDriver markExtensionAbsence(String extName) {
337
263
return this ;
338
264
}
339
265
266
+ @ Override
267
+ public Optional <BiDi > maybeGetBiDi () {
268
+ return Optional .ofNullable (this .biDi );
269
+ }
270
+
340
271
protected HttpClient getHttpClient () {
341
272
return ((HttpCommandExecutor ) getCommandExecutor ()).client ;
342
273
}
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
+ }
343
380
}
0 commit comments