Skip to content

Commit f17d08d

Browse files
feat: Make the plugin compatible with Appium 3 (#35)
BREAKING CHANGE: The minimum required Appium server version is set to 3.0.0
1 parent 6941d91 commit f17d08d

File tree

16 files changed

+110
-114
lines changed

16 files changed

+110
-114
lines changed

.eslintignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

.eslintrc.json

Lines changed: 0 additions & 11 deletions
This file was deleted.

.github/workflows/functional-test.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,22 @@ jobs:
2222
APPIUM_TEST_SERVER_PORT: 4567
2323
APPIUM_TEST_SERVER_HOST: 127.0.0.1
2424
_FORCE_LOGS: 1
25-
# No hardware acceleration is available for emulators on Ubuntu:
26-
# https://github.com/marketplace/actions/android-emulator-runner#can-i-use-this-action-on-linux-vms
27-
runs-on: macos-latest
25+
runs-on: ubuntu-latest
2826
steps:
29-
- uses: actions/checkout@v3
27+
- uses: actions/checkout@v4
3028
- uses: actions/setup-node@v3
3129
with:
3230
node-version: lts/*
3331
check-latest: true
3432
- uses: actions/setup-java@v3
3533
with:
3634
distribution: 'temurin'
37-
java-version: '11'
35+
java-version: '17'
36+
- name: Enable KVM group perms
37+
run: |
38+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
39+
sudo udevadm control --reload-rules
40+
sudo udevadm trigger --name-match=kvm
3841
- run: npm install
3942
name: Install dev dependencies
4043
- run: |
@@ -64,6 +67,7 @@ jobs:
6467
sdcard-path-or-size: 1500M
6568
api-level: ${{ matrix.apiLevel }}
6669
disable-spellchecker: true
70+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim
6771
target: ${{ matrix.emuTag }}
6872
arch: ${{ matrix.arch }}
6973
ram-size: 4096M

.github/workflows/pr-title.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
name: Conventional Commits
22
on:
33
pull_request:
4+
types: [opened, edited, synchronize, reopened]
45

56

67
jobs:
78
lint:
89
name: https://www.conventionalcommits.org
910
runs-on: ubuntu-latest
1011
steps:
11-
- uses: beemojs/conventional-pr-action@v2
12-
with:
13-
config-preset: angular
14-
env:
15-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
12+
- uses: beemojs/conventional-pr-action@v3
13+
with:
14+
config-preset: angular
15+
env:
16+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/unit-test.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ jobs:
77
prepare_matrix:
88
runs-on: ubuntu-latest
99
outputs:
10-
versions: ${{ steps.generate-matrix.outputs.versions }}
10+
versions: ${{ steps.generate-matrix.outputs.lts }}
1111
steps:
12-
- name: Select 3 most recent LTS versions of Node.js
12+
- name: Select all current LTS versions of Node.js
1313
id: generate-matrix
14-
run: echo "versions=$(curl -s https://endoflife.date/api/nodejs.json | jq -c '[[.[] | select(.lts != false)][:3] | .[].cycle | tonumber]')" >> "$GITHUB_OUTPUT"
15-
14+
uses: msimerson/node-lts-versions@v1
1615

1716
test:
1817
needs:

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ This plugin is created to expose [Chrome Devtools Protocol](https://chromedevtoo
44
Afterwards this API could be used to establish a connection to it from an external client and to
55
perform an extended automation, like [performance metrics gathering](https://www.selenium.dev/documentation/webdriver/bidirectional/chrome_devtools/#collect-performance-metrics) or [geolocation emulation](https://www.selenium.dev/documentation/webdriver/bidirectional/chrome_devtools/#emulate-geo-location-with-the-remote-webdriver).
66

7+
> [!IMPORTANT]
8+
> Since major version *1.0.0*, this plugin is only compatible with Appium 3.
9+
710
## Features
811

912
* Adds the following execute methods:
@@ -26,7 +29,7 @@ perform an extended automation, like [performance metrics gathering](https://www
2629

2730
## Prerequisites
2831

29-
* Appium Server 2.0+
32+
* Appium Server 3.0+
3033
* [UIAutomator2](https://github.com/appium/appium-uiautomator2-driver) or [Espresso](https://github.com/appium/appium-espresso-driver) driver
3134

3235
## Installation - Server

eslint.config.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import appiumConfig from '@appium/eslint-config-appium-ts';
2+
3+
export default [
4+
...appiumConfig,
5+
{
6+
ignores: [
7+
'docs/**',
8+
],
9+
},
10+
];

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import DevtoolsPlugin from './lib/plugin';
1+
import { DevtoolsPlugin } from './lib/plugin';
22

33
export default DevtoolsPlugin;
44
export { DevtoolsPlugin };

lib/mixins/proxy.js

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function requireDriverProperties(driver) {
5555
*/
5656
async function getCandidateSocketNames() {
5757
const {adb} = requireDriverProperties(this.driver);
58-
this.logger.debug('Getting a list of candidate devtools sockets');
58+
this.log.debug('Getting a list of candidate devtools sockets');
5959
const out = await adb.shell(['cat', '/proc/net/unix']);
6060
const names = [];
6161
const allMatches = [];
@@ -78,12 +78,12 @@ async function getCandidateSocketNames() {
7878
names.push(sockPath);
7979
}
8080
if (_.isEmpty(names)) {
81-
this.logger.debug('Found no active devtools sockets');
81+
this.log.debug('Found no active devtools sockets');
8282
if (!_.isEmpty(allMatches)) {
83-
this.logger.debug(`Other sockets are: ${JSON.stringify(allMatches, null, 2)}`);
83+
this.log.debug(`Other sockets are: ${JSON.stringify(allMatches, null, 2)}`);
8484
}
8585
} else {
86-
this.logger.debug(
86+
this.log.debug(
8787
`Parsed ${names.length} active devtools ${util.pluralize('socket', names.length, false)}: ` +
8888
JSON.stringify(names)
8989
);
@@ -109,9 +109,9 @@ async function getCandidateSocketNames() {
109109
* @returns {Promise<WebviewProps>}
110110
*/
111111
async function collectSingleDetails(socketName, localPort) {
112-
this.logger.debug(`Collecting CDP data of '${socketName}'`);
112+
this.log.debug(`Collecting CDP data of '${socketName}'`);
113113
const [info, pages] = await B.all([cdpInfo(localPort), cdpList(localPort)]);
114-
this.logger.info(`Collected CDP details of '${socketName}'`);
114+
this.log.info(`Collected CDP details of '${socketName}'`);
115115
return {info, pages};
116116
}
117117

@@ -133,12 +133,12 @@ async function collectMultipleDetails(socketNames) {
133133
/** @type {Record<string, WebviewProps>} */
134134
const details = {};
135135
// Connect to each devtools socket and retrieve web view details
136-
this.logger.debug(`Collecting CDP data of ${util.pluralize('candidate webview', socketNames.length, true)}`);
136+
this.log.debug(`Collecting CDP data of ${util.pluralize('candidate webview', socketNames.length, true)}`);
137137
const [startPort, endPort] = DEVTOOLS_LOOKUP_PORTS_RANGE;
138138
let localPort;
139139
try {
140140
localPort = await findAPortNotInUse(startPort, endPort);
141-
} catch (e) {
141+
} catch {
142142
throw new Error(
143143
`Cannot find any free port to forward candidate Devtools sockets ` +
144144
`in range ${startPort}..${endPort}`
@@ -149,7 +149,7 @@ async function collectMultipleDetails(socketNames) {
149149
try {
150150
await adb.forwardAbstractPort(localPort, remotePort);
151151
} catch (e) {
152-
this.logger.debug(
152+
this.log.debug(
153153
`Could not create a port forward to fetch the details of '${socketName}' socket: ${e.message}`
154154
);
155155
continue;
@@ -158,16 +158,16 @@ async function collectMultipleDetails(socketNames) {
158158
try {
159159
details[socketName] = await collectSingleDetails.bind(this)(socketName, localPort);
160160
} catch (e) {
161-
this.logger.debug(`Could not fetch the CDP details of '${socketName}' socket: ${e.message}`);
161+
this.log.debug(`Could not fetch the CDP details of '${socketName}' socket: ${e.message}`);
162162
} finally {
163163
try {
164164
await adb.removePortForward(localPort);
165165
} catch (e) {
166-
this.logger.debug(e.message);
166+
this.log.debug(e.message);
167167
}
168168
}
169169
}
170-
this.logger.info(`Collected CDP details of ${util.pluralize('webview', _.size(details), true)}`);
170+
this.log.info(`Collected CDP details of ${util.pluralize('webview', _.size(details), true)}`);
171171
return details;
172172
}
173173

@@ -265,9 +265,9 @@ function prepareWebSocketForwarder (forwardToUrlPattern, entityIdPlaceholder) {
265265
/** @type {WebSocket} */ wsUpstream,
266266
/** @type {import('http').IncomingMessage} */ req
267267
) => {
268-
this.logger.debug(`Got a new websocket connection at ${req.url}`);
268+
this.log.debug(`Got a new websocket connection at ${req.url}`);
269269
if (!req.url) {
270-
this.logger.debug('The url is empty. Will ignore');
270+
this.log.debug('The url is empty. Will ignore');
271271
wsUpstream.close(WS_SERVER_POLICY_VIOLATION);
272272
return;
273273
}
@@ -276,7 +276,7 @@ function prepareWebSocketForwarder (forwardToUrlPattern, entityIdPlaceholder) {
276276
const dstUrl = entityId && forwardToUrlPattern.includes(entityIdPlaceholder)
277277
? forwardToUrlPattern.replace(entityIdPlaceholder, entityId)
278278
: forwardToUrlPattern;
279-
this.logger.debug(`Will forward upstream ${req.url} to downstream ${dstUrl}`);
279+
this.log.debug(`Will forward upstream ${req.url} to downstream ${dstUrl}`);
280280
const wsDownstream = new WebSocket(dstUrl);
281281
wsDownstream.on('message', (msg, binary) => {
282282
if (wsUpstream.readyState === WebSocket.OPEN) {
@@ -289,13 +289,13 @@ function prepareWebSocketForwarder (forwardToUrlPattern, entityIdPlaceholder) {
289289
}
290290
});
291291
wsDownstream.once('error', (e) => {
292-
this.logger.warn(`Got an error from the downstream ${dstUrl}: ${e.message}`);
292+
this.log.warn(`Got an error from the downstream ${dstUrl}: ${e.message}`);
293293
});
294294
wsUpstream.once('error', (e) => {
295-
this.logger.info(`Got an error from the upstream ${req.url}: ${e.message}`);
295+
this.log.info(`Got an error from the upstream ${req.url}: ${e.message}`);
296296
});
297297
wsDownstream.once('close', (code, reason) => {
298-
this.logger.info(
298+
this.log.info(
299299
`The downstream ${dstUrl} has been closed: ${code}, ` +
300300
`${reason || '(no reason given)'}`
301301
);
@@ -304,7 +304,7 @@ function prepareWebSocketForwarder (forwardToUrlPattern, entityIdPlaceholder) {
304304
}
305305
});
306306
wsUpstream.once('close', (code, reason) => {
307-
this.logger.info(
307+
this.log.info(
308308
`The upstream ${req.url} has been closed: ${code}, ` +
309309
`${reason || '(no reason given)'}`
310310
);
@@ -332,7 +332,7 @@ export async function proxyDevtoolsTarget (next, driver, name, port) {
332332
throw new Error(`The target '${name}' is already being proxied`);
333333
}
334334

335-
this.logger.debug(`Starting the proxy for the Devtools target '${name}'`);
335+
this.log.debug(`Starting the proxy for the Devtools target '${name}'`);
336336
let localPort = port;
337337
if (localPort) {
338338
if (await checkPortStatus(localPort) !== 'closed') {
@@ -345,7 +345,7 @@ export async function proxyDevtoolsTarget (next, driver, name, port) {
345345
const [startPort, endPort] = DEVTOOLS_BIND_PORTS_RANGE;
346346
try {
347347
localPort = await findAPortNotInUse(startPort, endPort);
348-
} catch (e) {
348+
} catch {
349349
throw new Error(
350350
`Cannot find any free port in range ${startPort}..${endPort} ` +
351351
`to forward the Devtools socket '${name}'. Try to provide a custom port ` +
@@ -358,7 +358,7 @@ export async function proxyDevtoolsTarget (next, driver, name, port) {
358358
try {
359359
await adb.removePortForward(localPort);
360360
} catch (e) {
361-
this.logger.debug(`Cannot remove the port forward. Original error: ${e.message}`);
361+
this.log.debug(`Cannot remove the port forward. Original error: ${e.message}`);
362362
}
363363
};
364364
try {
@@ -378,7 +378,7 @@ export async function proxyDevtoolsTarget (next, driver, name, port) {
378378
}
379379
const browserDebuggerUrl = details.info[DEBUGGER_URL_KEY];
380380
if (!browserDebuggerUrl) {
381-
this.logger.debug(JSON.stringify(details.info));
381+
this.log.debug(JSON.stringify(details.info));
382382
await removePortForward();
383383
throw new Error(
384384
`The target '${name}' cannot be proxied. ` +
@@ -400,7 +400,7 @@ export async function proxyDevtoolsTarget (next, driver, name, port) {
400400
let wdUrl;
401401
try {
402402
wdUrl = new URL(browserDebuggerUrl);
403-
} catch (e) {
403+
} catch {
404404
await removePortForward();
405405
throw new Error(
406406
`The target '${name}' cannot be proxied. ` +
@@ -453,7 +453,7 @@ export async function proxyDevtoolsTarget (next, driver, name, port) {
453453
port: localPort,
454454
rewrites,
455455
};
456-
this.logger.info(
456+
this.log.info(
457457
`Successfully started the proxy for the Devtools target '${name}' at ${forwardTo}: ` +
458458
JSON.stringify(this.proxiedSessions[alias], null, 2)
459459
);
@@ -474,7 +474,7 @@ export async function unproxyDevtoolsTarget (next, driver, name) {
474474
throw new Error(`The target '${name}' is not being proxied`);
475475
}
476476

477-
this.logger.debug(`Stopping the proxy for the Devtools target '${name}'`);
477+
this.log.debug(`Stopping the proxy for the Devtools target '${name}'`);
478478
const {
479479
browserDebuggerPathname,
480480
pageDebuggerPathname,
@@ -489,10 +489,10 @@ export async function unproxyDevtoolsTarget (next, driver, name) {
489489
try {
490490
await step();
491491
} catch (e) {
492-
this.logger.warn(e.message);
492+
this.log.warn(e.message);
493493
}
494494
}
495-
this.logger.info(
495+
this.log.info(
496496
`Successfully stopped the proxy for the Devtools target '${name}': ` +
497497
JSON.stringify(this.proxiedSessions[alias], null, 2)
498498
);

lib/plugin.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ export default class DevtoolsPlugin extends BasePlugin {
6969
});
7070

7171
/** @type {import('@appium/types').UpdateServerCallback} */
72-
// eslint-disable-next-line require-await
7372
static async updateServer(expressApp) {
7473
const buildHanlder = (
7574
/** @type {string} */methodName,
@@ -79,11 +78,12 @@ export default class DevtoolsPlugin extends BasePlugin {
7978
) => {
8079
const plugin = findPlugin(req.params.uuid);
8180
if (!plugin) {
82-
logger.debug(`Cannot find any plugin instance identified by ${req.params.uuid}. Is is still alive?`);
81+
logger.debug(`Cannot find any plugin instance identified by ${req.params.uuid}. Is it still alive?`);
8382
res.status(404).send();
8483
return;
8584
}
8685
try {
86+
// eslint-disable-next-line import/namespace
8787
const result = await cmdMethods[methodName].bind(plugin)(req);
8888
res.status(200).json(result);
8989
} catch (e) {
@@ -123,9 +123,8 @@ export default class DevtoolsPlugin extends BasePlugin {
123123
*/
124124
async handle(next, driver, cmdName, ...cmdArgs) {
125125
if (!this.driverRef && 'adb' in driver && driver.adb) {
126-
// eslint-disable-next-line no-undef
127126
this.driverRef = new WeakRef(driver);
128-
this.logger.info(`Successfully initialized with ${driver.constructor.name}`);
127+
this.log.info(`Successfully initialized with ${driver.constructor.name}`);
129128
}
130129

131130
switch (cmdName) {

0 commit comments

Comments
 (0)