Skip to content

Commit fa8b92e

Browse files
feat: Add chromedriverForwardBiDi capability (#1025)
1 parent 18afa8e commit fa8b92e

File tree

5 files changed

+50
-24
lines changed

5 files changed

+50
-24
lines changed

lib/commands/context/exports.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export function assignContexts(webviewsMapping) {
107107
* @returns {Promise<void>}
108108
*/
109109
export async function switchContext(name, webviewsMapping) {
110+
this._bidiProxyUrl = null;
110111
// We have some options when it comes to webviews. If we want a
111112
// Chromedriver webview, we can only control one at a time.
112113
if (this.isChromedriverContext(name)) {
@@ -215,7 +216,7 @@ export async function startChromedriverProxy(context, webviewsMapping) {
215216
// we want to reconnect to it, not create a whole new one
216217
this.log.debug(`Found existing Chromedriver for context '${context}'. Using it.`);
217218
cd = this.sessionChromedrivers[context];
218-
await setupExistingChromedriver.bind(this)(cd);
219+
await setupExistingChromedriver.bind(this)(cd, context);
219220
} else {
220221
// XXX: this suppresses errors about putting arbitrary stuff on opts
221222
const opts = /** @type {any} */ (_.cloneDeep(this.opts));

lib/commands/context/helpers.js

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,10 @@ function createChromedriverCaps(opts, deviceId, webViewDetails) {
222222
delete opts.chromeOptions.Arguments;
223223
}
224224

225+
if (opts.webSocketUrl && _.isNil(caps.chromeOptions.webSocketUrl)) {
226+
caps.chromeOptions.webSocketUrl = opts.webSocketUrl;
227+
}
228+
225229
this.log.debug(
226230
'Precalculated Chromedriver capabilities: ' + JSON.stringify(caps.chromeOptions, null, 2),
227231
);
@@ -763,26 +767,52 @@ export async function setupNewChromedriver(opts, curDeviceId, context) {
763767
this.log.debug(
764768
`Before starting chromedriver, androidPackage is '${caps.chromeOptions.androidPackage}'`,
765769
);
766-
await chromedriver.start(caps);
770+
const sessionCaps = await chromedriver.start(caps);
771+
updateBidiProxyUrl.bind(this)(sessionCaps, context);
767772
return chromedriver;
768773
}
769774

770775
/**
771776
* @this {import('../../driver').AndroidDriver}
772777
* @template {Chromedriver} T
773778
* @param {T} chromedriver
779+
* @param {string} context
774780
* @returns {Promise<T>}
775781
*/
776-
export async function setupExistingChromedriver(chromedriver) {
782+
export async function setupExistingChromedriver(chromedriver, context) {
777783
// check the status by sending a simple window-based command to ChromeDriver
778784
// if there is an error, we want to recreate the ChromeDriver session
779-
if (!(await chromedriver.hasWorkingWebview())) {
785+
if (await chromedriver.hasWorkingWebview()) {
786+
const cachedBidiProxyUrl = this._bidiProxyUrlCache.get(context);
787+
if (cachedBidiProxyUrl) {
788+
updateBidiProxyUrl.bind(this)({webSocketUrl: cachedBidiProxyUrl}, context);
789+
}
790+
} else {
780791
this.log.debug('ChromeDriver is not associated with a window. Re-initializing the session.');
781-
await chromedriver.restart();
792+
const sessionCaps = await chromedriver.restart();
793+
updateBidiProxyUrl.bind(this)(sessionCaps, context);
782794
}
783795
return chromedriver;
784796
}
785797

798+
/**
799+
* @this {import('../../driver').AndroidDriver}
800+
* @param {Record<string, any>} sessionCaps
801+
* @param {string} context
802+
* @returns {void}
803+
*/
804+
function updateBidiProxyUrl(sessionCaps, context) {
805+
if (!this.opts?.chromedriverForwardBiDi || !sessionCaps?.webSocketUrl) {
806+
return;
807+
}
808+
809+
this._bidiProxyUrlCache.set(context, sessionCaps.webSocketUrl);
810+
if (this._bidiProxyUrl !== sessionCaps.webSocketUrl) {
811+
this._bidiProxyUrl = sessionCaps.webSocketUrl;
812+
this.log.debug(`Updated Bidi Proxy URL to ${this._bidiProxyUrl}`);
813+
}
814+
}
815+
786816
/**
787817
* @this {import('../../driver').AndroidDriver}
788818
* @returns {boolean}

lib/constraints.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ export const ANDROID_DRIVER_CONSTRAINTS = {
9393
chromedriverArgs: {
9494
isObject: true,
9595
},
96+
chromedriverForwardBiDi: {
97+
isBoolean: true,
98+
},
9699
chromedriverExecutable: {
97100
isString: true,
98101
},

lib/driver.ts

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ import {mobileStartScreenStreaming, mobileStopScreenStreaming} from './commands/
208208
import {getSystemBars, mobilePerformStatusBarCommand} from './commands/system-bars';
209209
import {getDeviceTime, mobileGetDeviceTime} from './commands/time';
210210
import { executeMethodMap } from './execute-method-map';
211+
import { LRUCache } from 'lru-cache';
211212

212213
export type AndroidDriverCaps = DriverCaps<AndroidDriverConstraints>;
213214
export type W3CAndroidDriverCaps = W3CDriverCaps<AndroidDriverConstraints>;
@@ -224,39 +225,28 @@ class AndroidDriver
224225
static executeMethodMap = executeMethodMap;
225226

226227
jwpProxyAvoid: RouteMatcher[];
227-
228228
adb: ADB;
229-
230229
_settingsApp: SettingsApp;
231-
232230
proxyReqRes?: (...args: any) => any;
233-
234231
contexts?: string[];
235-
236232
sessionChromedrivers: StringRecord<AppiumChromedriver>;
237-
238233
chromedriver?: AppiumChromedriver;
239-
240234
proxyCommand?: AndroidExternalDriver['proxyCommand'];
241235
jwpProxyActive: boolean;
242236
curContext: string;
243-
244237
useUnlockHelperApp?: boolean;
245-
246238
defaultIME?: string;
247-
248239
_wasWindowAnimationDisabled?: boolean;
249-
250240
_cachedActivityArgs: StringRecord;
251-
252241
_screenStreamingProps?: StringRecord;
253-
254242
_screenRecordingProperties?: StringRecord;
255-
256243
_logcatWebsocketListener?: LogcatListener;
257-
258244
_bidiServerLogListener?: (...args: any[]) => void;
259-
245+
_bidiProxyUrl: string | null = null;
246+
_bidiProxyUrlCache: LRUCache<string, string> = new LRUCache({
247+
max: 20,
248+
updateAgeOnGet: true,
249+
});
260250
opts: AndroidDriverOpts;
261251

262252
constructor(opts: InitialOpts = {} as InitialOpts, shouldValidateCaps = true) {
@@ -276,8 +266,10 @@ class AndroidDriver
276266
this.curContext = this.defaultContextName();
277267
this.opts = opts as AndroidDriverOpts;
278268
this._cachedActivityArgs = {};
279-
// @ts-ignore Remove it after min supported server version is ^2.14.0
280-
this.doesSupportBidi = true;
269+
}
270+
271+
get bidiProxyUrl(): string | null {
272+
return this.opts.chromedriverForwardBiDi ? this._bidiProxyUrl : null;
281273
}
282274

283275
get settingsApp(): SettingsApp {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"@appium/support": "^7.0.0-rc.1",
5252
"@colors/colors": "^1.6.0",
5353
"appium-adb": "^14.0.0",
54-
"appium-chromedriver": "^8.0.16",
54+
"appium-chromedriver": "^8.0.18",
5555
"asyncbox": "^3.0.0",
5656
"axios": "^1.x",
5757
"bluebird": "^3.4.7",

0 commit comments

Comments
 (0)