appium-android-driver 5.0.6 → 5.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +0 -1
  2. package/build/lib/commands/actions.js +16 -20
  3. package/build/lib/commands/app-management.js +15 -17
  4. package/build/lib/commands/context.js +46 -66
  5. package/build/lib/commands/coverage.js +2 -6
  6. package/build/lib/commands/element.js +3 -9
  7. package/build/lib/commands/emu-console.js +3 -5
  8. package/build/lib/commands/execute.js +3 -6
  9. package/build/lib/commands/file-actions.js +22 -29
  10. package/build/lib/commands/general.js +18 -30
  11. package/build/lib/commands/ime.js +8 -21
  12. package/build/lib/commands/network.js +14 -27
  13. package/build/lib/commands/performance.js +3 -6
  14. package/build/lib/commands/recordscreen.js +27 -38
  15. package/build/lib/commands/shell.js +6 -7
  16. package/build/lib/commands/streamscreen.js +29 -43
  17. package/build/lib/commands/system-bars.js +10 -16
  18. package/build/lib/commands/touch.js +4 -10
  19. package/build/lib/driver.js +61 -67
  20. package/build/lib/webview-helpers.js +2 -7
  21. package/lib/commands/actions.js +18 -19
  22. package/lib/commands/app-management.js +13 -12
  23. package/lib/commands/context.js +36 -40
  24. package/lib/commands/coverage.js +1 -4
  25. package/lib/commands/element.js +2 -3
  26. package/lib/commands/emu-console.js +2 -2
  27. package/lib/commands/execute.js +2 -3
  28. package/lib/commands/file-actions.js +31 -25
  29. package/lib/commands/general.js +16 -18
  30. package/lib/commands/ime.js +7 -8
  31. package/lib/commands/network.js +15 -16
  32. package/lib/commands/performance.js +2 -3
  33. package/lib/commands/recordscreen.js +23 -22
  34. package/lib/commands/shell.js +6 -6
  35. package/lib/commands/streamscreen.js +22 -23
  36. package/lib/commands/system-bars.js +11 -10
  37. package/lib/commands/touch.js +3 -4
  38. package/lib/driver.js +45 -46
  39. package/lib/webview-helpers.js +1 -7
  40. package/package.json +2 -2
@@ -1,4 +1,3 @@
1
- import log from '../logger';
2
1
  import { errors } from '@appium/base-driver';
3
2
 
4
3
  let commands = {}, helpers = {}, extensions = {};
@@ -9,32 +8,32 @@ commands.isIMEActivated = async function isIMEActivated () { // eslint-disable-l
9
8
  };
10
9
 
11
10
  commands.availableIMEEngines = async function availableIMEEngines () {
12
- log.debug('Retrieving available IMEs');
11
+ this.log.debug('Retrieving available IMEs');
13
12
  let engines = await this.adb.availableIMEs();
14
- log.debug(`Engines: ${JSON.stringify(engines)}`);
13
+ this.log.debug(`Engines: ${JSON.stringify(engines)}`);
15
14
  return engines;
16
15
  };
17
16
 
18
17
  commands.getActiveIMEEngine = async function getActiveIMEEngine () {
19
- log.debug('Retrieving current default IME');
18
+ this.log.debug('Retrieving current default IME');
20
19
  return await this.adb.defaultIME();
21
20
  };
22
21
 
23
22
  commands.activateIMEEngine = async function activateIMEEngine (imeId) {
24
- log.debug(`Attempting to activate IME ${imeId}`);
23
+ this.log.debug(`Attempting to activate IME ${imeId}`);
25
24
  let availableEngines = await this.adb.availableIMEs();
26
25
  if (availableEngines.indexOf(imeId) === -1) {
27
- log.debug('IME not found, failing');
26
+ this.log.debug('IME not found, failing');
28
27
  throw new errors.IMENotAvailableError();
29
28
  }
30
- log.debug('Found installed IME, attempting to activate');
29
+ this.log.debug('Found installed IME, attempting to activate');
31
30
  await this.adb.enableIME(imeId);
32
31
  await this.adb.setIME(imeId);
33
32
  };
34
33
 
35
34
  commands.deactivateIMEEngine = async function deactivateIMEEngine () {
36
35
  let currentEngine = await this.getActiveIMEEngine();
37
- log.debug(`Attempting to deactivate ${currentEngine}`);
36
+ this.log.debug(`Attempting to deactivate ${currentEngine}`);
38
37
  await this.adb.disableIME(currentEngine);
39
38
  };
40
39
 
@@ -1,4 +1,3 @@
1
- import log from '../logger';
2
1
  import _ from 'lodash';
3
2
  import { errors } from '@appium/base-driver';
4
3
  import B from 'bluebird';
@@ -16,7 +15,7 @@ const DATA_MASK = 0b100;
16
15
  const GEO_EPSILON = Number.MIN_VALUE;
17
16
 
18
17
  commands.getNetworkConnection = async function getNetworkConnection () {
19
- log.info('Getting network connection');
18
+ this.log.info('Getting network connection');
20
19
  let airplaneModeOn = await this.adb.isAirplaneModeOn();
21
20
  let connection = airplaneModeOn ? AIRPLANE_MODE_MASK : 0;
22
21
 
@@ -39,7 +38,7 @@ commands.isWifiOn = async function isWifiOn () {
39
38
  };
40
39
 
41
40
  commands.setNetworkConnection = async function setNetworkConnection (type) {
42
- log.info('Setting network connection');
41
+ this.log.info('Setting network connection');
43
42
  // decode the input
44
43
  const shouldEnableAirplaneMode = (type & AIRPLANE_MODE_MASK) !== 0;
45
44
  const shouldEnableWifi = (type & WIFI_MASK) !== 0;
@@ -58,12 +57,12 @@ commands.setNetworkConnection = async function setNetworkConnection (type) {
58
57
  await this.adb.broadcastAirplaneMode(shouldEnableAirplaneMode);
59
58
  });
60
59
  } else {
61
- log.info(`Not changing airplane mode, since it is already ` +
60
+ this.log.info(`Not changing airplane mode, since it is already ` +
62
61
  `${shouldEnableAirplaneMode ? 'enabled' : 'disabled'}`);
63
62
  }
64
63
 
65
64
  if (shouldEnableWifi === isWiFiEnabled && shouldEnableDataConnection === isDataEnabled) {
66
- log.info('Not changing data connection/Wi-Fi states, since they are already set to expected values');
65
+ this.log.info('Not changing data connection/Wi-Fi states, since they are already set to expected values');
67
66
  if (await this.adb.isAirplaneModeOn()) {
68
67
  return AIRPLANE_MODE_MASK | currentState;
69
68
  }
@@ -74,15 +73,15 @@ commands.setNetworkConnection = async function setNetworkConnection (type) {
74
73
  if (shouldEnableWifi !== isWiFiEnabled) {
75
74
  await this.setWifiState(shouldEnableWifi);
76
75
  } else {
77
- log.info(`Not changing Wi-Fi state, since it is already ` +
78
- `${shouldEnableWifi ? 'enabled' : 'disabled'}`);
76
+ this.log.info(`Not changing Wi-Fi state, since it is already ` +
77
+ `${shouldEnableWifi ? 'enabled' : 'disabled'}`);
79
78
  }
80
79
 
81
80
  if (shouldEnableAirplaneMode) {
82
- log.info('Not changing data connection state, because airplane mode is enabled');
81
+ this.log.info('Not changing data connection state, because airplane mode is enabled');
83
82
  } else if (shouldEnableDataConnection === isDataEnabled) {
84
- log.info(`Not changing data connection state, since it is already ` +
85
- `${shouldEnableDataConnection ? 'enabled' : 'disabled'}`);
83
+ this.log.info(`Not changing data connection state, since it is already ` +
84
+ `${shouldEnableDataConnection ? 'enabled' : 'disabled'}`);
86
85
  } else {
87
86
  await this.adb.setDataState(shouldEnableDataConnection, this.isEmulator());
88
87
  }
@@ -100,7 +99,7 @@ commands.setWifiState = async function setWifiState (wifi) {
100
99
 
101
100
  commands.toggleData = async function toggleData () {
102
101
  let data = !(await this.adb.isDataOn());
103
- log.info(`Turning network data ${data ? 'on' : 'off'}`);
102
+ this.log.info(`Turning network data ${data ? 'on' : 'off'}`);
104
103
  await this.wrapBootstrapDisconnect(async () => {
105
104
  await this.adb.setWifiAndData({data}, this.isEmulator());
106
105
  });
@@ -108,7 +107,7 @@ commands.toggleData = async function toggleData () {
108
107
 
109
108
  commands.toggleWiFi = async function toggleWiFi () {
110
109
  let wifi = !(await this.adb.isWifiOn());
111
- log.info(`Turning WiFi ${wifi ? 'on' : 'off'}`);
110
+ this.log.info(`Turning WiFi ${wifi ? 'on' : 'off'}`);
112
111
  await this.wrapBootstrapDisconnect(async () => {
113
112
  await this.adb.setWifiAndData({wifi}, this.isEmulator());
114
113
  });
@@ -120,7 +119,7 @@ commands.toggleFlightMode = async function toggleFlightMode () {
120
119
  * real devices, it should throw a NotYetImplementedError
121
120
  */
122
121
  let flightMode = !(await this.adb.isAirplaneModeOn());
123
- log.info(`Turning flight mode ${flightMode ? 'on' : 'off'}`);
122
+ this.log.info(`Turning flight mode ${flightMode ? 'on' : 'off'}`);
124
123
  await this.wrapBootstrapDisconnect(async () => {
125
124
  await this.adb.setAirplaneMode(flightMode);
126
125
  });
@@ -134,8 +133,8 @@ commands.setGeoLocation = async function setGeoLocation (location) {
134
133
  try {
135
134
  return await this.getGeoLocation();
136
135
  } catch (e) {
137
- log.warn(`Could not get the current geolocation info: ${e.message}`);
138
- log.warn(`Returning the default zero'ed values`);
136
+ this.log.warn(`Could not get the current geolocation info: ${e.message}`);
137
+ this.log.warn(`Returning the default zero'ed values`);
139
138
  return {
140
139
  latitude: GEO_EPSILON,
141
140
  longitude: GEO_EPSILON,
@@ -182,7 +181,7 @@ const KeyCode = {
182
181
  CENTER: 23
183
182
  };
184
183
  commands.toggleLocationServices = async function toggleLocationServices () {
185
- log.info('Toggling location services');
184
+ this.log.info('Toggling location services');
186
185
  let api = await this.adb.getApiLevel();
187
186
  if (this.isEmulator()) {
188
187
  let providers = await this.adb.getLocationProviders();
@@ -1,6 +1,5 @@
1
1
  import _ from 'lodash';
2
2
  import { retryInterval } from 'asyncbox';
3
- import log from '../logger';
4
3
 
5
4
  const commands = {}, helpers = {}, extensions = {};
6
5
 
@@ -150,7 +149,7 @@ helpers.getCPUInfo = async function getCPUInfo (packageName, retries = 2) {
150
149
  output = await this.adb.shell(['dumpsys', 'cpuinfo']);
151
150
  } catch (e) {
152
151
  if (e.stderr) {
153
- log.info(e.stderr);
152
+ this.log.info(e.stderr);
154
153
  }
155
154
  throw e;
156
155
  }
@@ -160,7 +159,7 @@ helpers.getCPUInfo = async function getCPUInfo (packageName, retries = 2) {
160
159
  new RegExp(`^.+\\/${_.escapeRegExp(packageName)}:\\D+([\\d.]+)%\\s+user\\s+\\+\\s+([\\d.]+)%\\s+kernel`, 'm');
161
160
  const match = usagesPattern.exec(output);
162
161
  if (!match) {
163
- log.debug(output);
162
+ this.log.debug(output);
164
163
  throw new Error(`Unable to parse cpu usage data for '${packageName}'. Check the server log for more details`);
165
164
  }
166
165
  return [CPU_KEYS, [match[1], match[2]]];
@@ -1,7 +1,6 @@
1
1
  import _ from 'lodash';
2
2
  import { waitForCondition } from 'asyncbox';
3
3
  import { util, fs, net, tempDir, system, timing } from '@appium/support';
4
- import log from '../logger';
5
4
  import { exec } from 'teen_process';
6
5
  import path from 'path';
7
6
 
@@ -21,8 +20,6 @@ const FFMPEG_BINARY = `ffmpeg${system.isWindows() ? '.exe' : ''}`;
21
20
 
22
21
  async function uploadRecordedMedia (localFile, remotePath = null, uploadOptions = {}) {
23
22
  if (_.isEmpty(remotePath)) {
24
- const {size} = await fs.stat(localFile);
25
- log.debug(`The size of the resulting screen recording is ${util.toReadableSizeString(size)}`);
26
23
  return (await util.toInMemoryBase64(localFile)).toString();
27
24
  }
28
25
 
@@ -50,7 +47,7 @@ async function verifyScreenRecordIsSupported (adb, isEmulator) {
50
47
  }
51
48
  }
52
49
 
53
- async function scheduleScreenRecord (adb, recordingProperties) {
50
+ async function scheduleScreenRecord (adb, recordingProperties, log = null) {
54
51
  if (recordingProperties.stopped) {
55
52
  return;
56
53
  }
@@ -83,10 +80,10 @@ async function scheduleScreenRecord (adb, recordingProperties) {
83
80
  return;
84
81
  }
85
82
  const currentDuration = timer.getDuration().asSeconds.toFixed(0);
86
- log.debug(`The overall screen recording duration is ${currentDuration}s so far`);
83
+ log?.debug(`The overall screen recording duration is ${currentDuration}s so far`);
87
84
  const timeLimitInt = parseInt(timeLimit, 10);
88
85
  if (isNaN(timeLimitInt) || currentDuration >= timeLimitInt) {
89
- log.debug('There is no need to start the next recording chunk');
86
+ log?.debug('There is no need to start the next recording chunk');
90
87
  return;
91
88
  }
92
89
 
@@ -94,11 +91,11 @@ async function scheduleScreenRecord (adb, recordingProperties) {
94
91
  const chunkDuration = recordingProperties.currentTimeLimit < MAX_RECORDING_TIME_SEC
95
92
  ? recordingProperties.currentTimeLimit
96
93
  : MAX_RECORDING_TIME_SEC;
97
- log.debug(`Starting the next ${chunkDuration}s-chunk ` +
94
+ log?.debug(`Starting the next ${chunkDuration}s-chunk ` +
98
95
  `of screen recording in order to achieve ${timeLimitInt}s total duration`);
99
- scheduleScreenRecord(adb, recordingProperties)
96
+ scheduleScreenRecord(adb, recordingProperties, log)
100
97
  .catch((e) => {
101
- log.error(e.stack);
98
+ log?.error(e.stack);
102
99
  recordingProperties.stopped = true;
103
100
  });
104
101
  });
@@ -116,7 +113,7 @@ async function scheduleScreenRecord (adb, recordingProperties) {
116
113
  recordingProperties.recordingProcess = recordingProc;
117
114
  }
118
115
 
119
- async function mergeScreenRecords (mediaFiles) {
116
+ async function mergeScreenRecords (mediaFiles, log = null) {
120
117
  try {
121
118
  await fs.which(FFMPEG_BINARY);
122
119
  } catch (e) {
@@ -127,10 +124,10 @@ async function mergeScreenRecords (mediaFiles) {
127
124
  .join('\n');
128
125
  const configFile = path.resolve(path.dirname(mediaFiles[0]), 'config.txt');
129
126
  await fs.writeFile(configFile, configContent, 'utf8');
130
- log.debug(`Generated ffmpeg merging config '${configFile}' with items:\n${configContent}`);
127
+ log?.debug(`Generated ffmpeg merging config '${configFile}' with items:\n${configContent}`);
131
128
  const result = path.resolve(path.dirname(mediaFiles[0]), `merge_${Math.floor(new Date())}${DEFAULT_EXT}`);
132
129
  const args = ['-safe', '0', '-f', 'concat', '-i', configFile, '-c', 'copy', result];
133
- log.info(`Initiating screen records merging using the command '${FFMPEG_BINARY} ${args.join(' ')}'`);
130
+ log?.info(`Initiating screen records merging using the command '${FFMPEG_BINARY} ${args.join(' ')}'`);
134
131
  await exec(FFMPEG_BINARY, args);
135
132
  return result;
136
133
  }
@@ -217,7 +214,7 @@ commands.startRecordingScreen = async function startRecordingScreen (options = {
217
214
  }
218
215
 
219
216
  if (await terminateBackgroundScreenRecording(this.adb, true)) {
220
- log.warn(`There were some ${SCREENRECORD_BINARY} process leftovers running ` +
217
+ this.log.warn(`There were some ${SCREENRECORD_BINARY} process leftovers running ` +
221
218
  `in the background. Make sure you stop screen recording each time after it is started, ` +
222
219
  `otherwise the recorded media might quickly exceed all the free space on the device under test.`);
223
220
  }
@@ -246,7 +243,7 @@ commands.startRecordingScreen = async function startRecordingScreen (options = {
246
243
  recordingProcess: null,
247
244
  stopped: false,
248
245
  };
249
- await scheduleScreenRecord(this.adb, this._screenRecordingProperties);
246
+ await scheduleScreenRecord(this.adb, this._screenRecordingProperties, this.log);
250
247
  return result;
251
248
  };
252
249
 
@@ -289,14 +286,14 @@ commands.stopRecordingScreen = async function stopRecordingScreen (options = {})
289
286
  try {
290
287
  await terminateBackgroundScreenRecording(this.adb, false);
291
288
  } catch (err) {
292
- log.warn(err.message);
289
+ this.log.warn(err.message);
293
290
  if (!_.isEmpty(this._screenRecordingProperties)) {
294
- log.warn('The resulting video might be corrupted');
291
+ this.log.warn('The resulting video might be corrupted');
295
292
  }
296
293
  }
297
294
 
298
295
  if (_.isEmpty(this._screenRecordingProperties)) {
299
- log.info(`Screen recording has not been previously started by Appium. There is nothing to stop`);
296
+ this.log.info(`Screen recording has not been previously started by Appium. There is nothing to stop`);
300
297
  return '';
301
298
  }
302
299
 
@@ -304,13 +301,13 @@ commands.stopRecordingScreen = async function stopRecordingScreen (options = {})
304
301
  try {
305
302
  await this._screenRecordingProperties.recordingProcess.stop('SIGINT', PROCESS_SHUTDOWN_TIMEOUT);
306
303
  } catch (e) {
307
- log.errorAndThrow(`Unable to stop screen recording within ${PROCESS_SHUTDOWN_TIMEOUT}ms`);
304
+ this.log.errorAndThrow(`Unable to stop screen recording within ${PROCESS_SHUTDOWN_TIMEOUT}ms`);
308
305
  }
309
306
  this._screenRecordingProperties.recordingProcess = null;
310
307
  }
311
308
 
312
309
  if (_.isEmpty(this._screenRecordingProperties.records)) {
313
- log.errorAndThrow(`No screen recordings have been stored on the device so far. ` +
310
+ this.log.errorAndThrow(`No screen recordings have been stored on the device so far. ` +
314
311
  `Are you sure the ${SCREENRECORD_BINARY} utility works as expected?`);
315
312
  }
316
313
 
@@ -324,14 +321,18 @@ commands.stopRecordingScreen = async function stopRecordingScreen (options = {})
324
321
  }
325
322
  let resultFilePath = _.last(localRecords);
326
323
  if (localRecords.length > 1) {
327
- log.info(`Got ${localRecords.length} screen recordings. Trying to merge them`);
324
+ this.log.info(`Got ${localRecords.length} screen recordings. Trying to merge them`);
328
325
  try {
329
- resultFilePath = await mergeScreenRecords(localRecords);
326
+ resultFilePath = await mergeScreenRecords(localRecords, this.log);
330
327
  } catch (e) {
331
- log.warn(`Cannot merge the recorded files. The most recent screen recording is going to be returned as the result. ` +
328
+ this.log.warn(`Cannot merge the recorded files. The most recent screen recording is going to be returned as the result. ` +
332
329
  `Original error: ${e.message}`);
333
330
  }
334
331
  }
332
+ if (_.isEmpty(options.remotePath)) {
333
+ const {size} = await fs.stat(resultFilePath);
334
+ this.log.debug(`The size of the resulting screen recording is ${util.toReadableSizeString(size)}`);
335
+ }
335
336
  return await uploadRecordedMedia(resultFilePath, options.remotePath, options);
336
337
  } finally {
337
338
  await fs.rimraf(tmpRoot);
@@ -1,7 +1,7 @@
1
- import log from '../logger';
2
1
  import _ from 'lodash';
3
2
  import { exec } from 'teen_process';
4
3
  import { util } from '@appium/support';
4
+ import { errors } from '@appium/base-driver';
5
5
 
6
6
  const ADB_SHELL_FEATURE = 'adb_shell';
7
7
 
@@ -18,7 +18,7 @@ commands.mobileShell = async function mobileShell (opts = {}) {
18
18
  } = opts;
19
19
 
20
20
  if (!_.isString(command)) {
21
- log.errorAndThrow(`The 'command' argument is mandatory`);
21
+ throw new errors.InvalidArgumentError(`The 'command' argument is mandatory`);
22
22
  }
23
23
 
24
24
  const adbArgs = [
@@ -27,7 +27,7 @@ commands.mobileShell = async function mobileShell (opts = {}) {
27
27
  command,
28
28
  ...(_.isArray(args) ? args : [args])
29
29
  ];
30
- log.debug(`Running '${this.adb.executable.path} ${util.quote(adbArgs)}'`);
30
+ this.log.debug(`Running '${this.adb.executable.path} ${util.quote(adbArgs)}'`);
31
31
  try {
32
32
  const {stdout, stderr} = await exec(this.adb.executable.path, adbArgs, {timeout});
33
33
  if (includeStderr) {
@@ -38,9 +38,9 @@ commands.mobileShell = async function mobileShell (opts = {}) {
38
38
  }
39
39
  return stdout;
40
40
  } catch (err) {
41
- log.errorAndThrow(`Cannot execute the '${command}' shell command. ` +
42
- `Original error: ${err.message}. ` +
43
- `StdOut: ${err.stdout}. StdErr: ${err.stderr}`);
41
+ this.log.errorAndThrow(`Cannot execute the '${command}' shell command. ` +
42
+ `Original error: ${err.message}. ` +
43
+ `StdOut: ${err.stdout}. StdErr: ${err.stderr}`);
44
44
  }
45
45
  };
46
46
 
@@ -1,6 +1,5 @@
1
1
  import _ from 'lodash';
2
2
  import { fs, system, logger, util } from '@appium/support';
3
- import log from '../logger';
4
3
  import { exec, SubProcess } from 'teen_process';
5
4
  import { checkPortStatus } from 'portscanner';
6
5
  import http from 'http';
@@ -74,7 +73,7 @@ async function verifyStreamingRequirements (adb) {
74
73
  await B.all(moduleCheckPromises);
75
74
  }
76
75
 
77
- async function getDeviceInfo (adb) {
76
+ async function getDeviceInfo (adb, log = null) {
78
77
  const output = await adb.shell(['dumpsys', 'display']);
79
78
  const result = {};
80
79
  for (const [key, pattern] of [
@@ -84,7 +83,7 @@ async function getDeviceInfo (adb) {
84
83
  ]) {
85
84
  const match = pattern.exec(output);
86
85
  if (!match) {
87
- log.debug(output);
86
+ log?.debug(output);
88
87
  throw new Error(`Cannot parse the device ${key} from the adb command output. ` +
89
88
  `Check the server log for more details.`);
90
89
  }
@@ -94,7 +93,7 @@ async function getDeviceInfo (adb) {
94
93
  return result;
95
94
  }
96
95
 
97
- async function initDeviceStreamingProc (adb, deviceInfo, opts = {}) {
96
+ async function initDeviceStreamingProc (adb, log, deviceInfo, opts = {}) {
98
97
  const {
99
98
  width,
100
99
  height,
@@ -158,7 +157,7 @@ async function initDeviceStreamingProc (adb, deviceInfo, opts = {}) {
158
157
  return deviceStreaming;
159
158
  }
160
159
 
161
- async function initGstreamerPipeline (deviceStreamingProc, deviceInfo, opts = {}) {
160
+ async function initGstreamerPipeline (deviceStreamingProc, deviceInfo, log, opts = {}) {
162
161
  const {
163
162
  width,
164
163
  height,
@@ -291,30 +290,30 @@ commands.mobileStartScreenStreaming = async function mobileStartScreenStreaming
291
290
  await verifyStreamingRequirements(this.adb);
292
291
  }
293
292
  if (!_.isEmpty(this._screenStreamingProps)) {
294
- log.info(`The screen streaming session is already running. ` +
293
+ this.log.info(`The screen streaming session is already running. ` +
295
294
  `Stop it first in order to start a new one.`);
296
295
  return;
297
296
  }
298
297
  if ((await checkPortStatus(port, host)) === 'open') {
299
- log.info(`The port #${port} at ${host} is busy. ` +
298
+ this.log.info(`The port #${port} at ${host} is busy. ` +
300
299
  `Assuming the screen streaming is already running`);
301
300
  return;
302
301
  }
303
302
  if ((await checkPortStatus(tcpPort, TCP_HOST)) === 'open') {
304
- log.errorAndThrow(`The port #${tcpPort} at ${TCP_HOST} is busy. ` +
303
+ this.log.errorAndThrow(`The port #${tcpPort} at ${TCP_HOST} is busy. ` +
305
304
  `Make sure there are no leftovers from previous sessions.`);
306
305
  }
307
306
  this._screenStreamingProps = null;
308
307
 
309
- const deviceInfo = await getDeviceInfo(this.adb);
310
- const deviceStreamingProc = await initDeviceStreamingProc(this.adb, deviceInfo, {
308
+ const deviceInfo = await getDeviceInfo(this.adb, this.log);
309
+ const deviceStreamingProc = await initDeviceStreamingProc(this.adb, this.log, deviceInfo, {
311
310
  width,
312
311
  height,
313
312
  bitRate,
314
313
  });
315
314
  let gstreamerPipeline;
316
315
  try {
317
- gstreamerPipeline = await initGstreamerPipeline(deviceStreamingProc, deviceInfo, {
316
+ gstreamerPipeline = await initGstreamerPipeline(deviceStreamingProc, deviceInfo, this.log, {
318
317
  width,
319
318
  height,
320
319
  quality,
@@ -334,15 +333,15 @@ commands.mobileStartScreenStreaming = async function mobileStartScreenStreaming
334
333
  try {
335
334
  await new B((resolve, reject) => {
336
335
  mjpegSocket = net.createConnection(tcpPort, TCP_HOST, () => {
337
- log.info(`Successfully connected to MJPEG stream at tcp://${TCP_HOST}:${tcpPort}`);
336
+ this.log.info(`Successfully connected to MJPEG stream at tcp://${TCP_HOST}:${tcpPort}`);
338
337
  mjpegServer = http.createServer((req, res) => {
339
338
  const remoteAddress = extractRemoteAddress(req);
340
339
  const currentPathname = url.parse(req.url).pathname;
341
- log.info(`Got an incoming screen bradcasting request from ${remoteAddress} ` +
340
+ this.log.info(`Got an incoming screen bradcasting request from ${remoteAddress} ` +
342
341
  `(${req.headers['user-agent'] || 'User Agent unknown'}) at ${currentPathname}`);
343
342
 
344
343
  if (pathname && currentPathname !== pathname) {
345
- log.info('Rejecting the broadcast request since it does not match the given pathname');
344
+ this.log.info('Rejecting the broadcast request since it does not match the given pathname');
346
345
  res.writeHead(404, {
347
346
  Connection: 'close',
348
347
  'Content-Type': 'text/plain; charset=utf-8',
@@ -352,7 +351,7 @@ commands.mobileStartScreenStreaming = async function mobileStartScreenStreaming
352
351
  return;
353
352
  }
354
353
 
355
- log.info('Starting MJPEG broadcast');
354
+ this.log.info('Starting MJPEG broadcast');
356
355
  res.writeHead(200, {
357
356
  'Cache-Control': 'no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0',
358
357
  Pragma: 'no-cache',
@@ -363,20 +362,20 @@ commands.mobileStartScreenStreaming = async function mobileStartScreenStreaming
363
362
  mjpegSocket.pipe(res);
364
363
  });
365
364
  mjpegServer.on('error', (e) => {
366
- log.warn(e);
365
+ this.log.warn(e);
367
366
  reject(e);
368
367
  });
369
368
  mjpegServer.on('close', () => {
370
- log.debug(`MJPEG server at http://${host}:${port} has been closed`);
369
+ this.log.debug(`MJPEG server at http://${host}:${port} has been closed`);
371
370
  });
372
371
  mjpegServer.on('listening', () => {
373
- log.info(`Successfully started MJPEG server at http://${host}:${port}`);
372
+ this.log.info(`Successfully started MJPEG server at http://${host}:${port}`);
374
373
  resolve();
375
374
  });
376
375
  mjpegServer.listen(port, host);
377
376
  });
378
377
  mjpegSocket.on('error', (e) => {
379
- log.error(e);
378
+ this.log.error(e);
380
379
  reject(e);
381
380
  });
382
381
  }).timeout(STREAMING_STARTUP_TIMEOUT_MS,
@@ -412,7 +411,7 @@ commands.mobileStartScreenStreaming = async function mobileStartScreenStreaming
412
411
  commands.mobileStopScreenStreaming = async function mobileStopScreenStreaming (/* options = {} */) {
413
412
  if (_.isEmpty(this._screenStreamingProps)) {
414
413
  if (!_.isUndefined(this._screenStreamingProps)) {
415
- log.debug(`Screen streaming is not running. There is nothing to stop`);
414
+ this.log.debug(`Screen streaming is not running. There is nothing to stop`);
416
415
  }
417
416
  return;
418
417
  }
@@ -436,15 +435,15 @@ commands.mobileStopScreenStreaming = async function mobileStopScreenStreaming (/
436
435
  try {
437
436
  await gstreamerPipeline.stop('SIGINT');
438
437
  } catch (e) {
439
- log.warn(e);
438
+ this.log.warn(e);
440
439
  try {
441
440
  await gstreamerPipeline.stop('SIGKILL');
442
441
  } catch (e1) {
443
- log.error(e1);
442
+ this.log.error(e1);
444
443
  }
445
444
  }
446
445
  }
447
- log.info(`Successfully terminated the screen streaming MJPEG server`);
446
+ this.log.info(`Successfully terminated the screen streaming MJPEG server`);
448
447
  } finally {
449
448
  this._screenStreamingProps = null;
450
449
  }
@@ -1,5 +1,4 @@
1
1
  import _ from 'lodash';
2
- import log from '../logger';
3
2
 
4
3
  const WINDOW_TITLE_PATTERN = /^\s+Window\s#\d+\sWindow\{[0-9a-f]+\s\w+\s([\w-]+)\}:$/;
5
4
  const FRAME_PATTERN = /^\s+mFrame=\[([0-9.-]+),([0-9.-]+)\]\[([0-9.-]+),([0-9.-]+)\]/m;
@@ -28,15 +27,16 @@ const commands = {};
28
27
  * @param {string} name The name of the window whose properties are being parsed
29
28
  * @param {Array<string>} props The list of particular window property lines.
30
29
  * Check the corresponding unit tests for more details on the input format.
30
+ * @param {Object?} log Logger instance
31
31
  * @returns {WindowProperties} Parsed properties object
32
32
  * @throws {Error} If there was an issue while parsing the properties string
33
33
  */
34
- function parseWindowProperties (name, props) {
34
+ function parseWindowProperties (name, props, log = null) {
35
35
  const result = _.cloneDeep(DEFAULT_WINDOW_PROPERTIES);
36
36
  const propLines = props.join('\n');
37
37
  const frameMatch = FRAME_PATTERN.exec(propLines);
38
38
  if (!frameMatch) {
39
- log.debug(propLines);
39
+ log?.debug(propLines);
40
40
  throw new Error(`Cannot parse the frame size from '${name}' window properties`);
41
41
  }
42
42
  result.x = parseFloat(frameMatch[1]);
@@ -47,7 +47,7 @@ function parseWindowProperties (name, props) {
47
47
  result.height = top - result.y;
48
48
  const visibilityMatch = SURFACE_PATTERN.exec(propLines);
49
49
  if (!visibilityMatch) {
50
- log.debug(propLines);
50
+ log?.debug(propLines);
51
51
  throw new Error(`Cannot parse the visibility value from '${name}' window properties`);
52
52
  }
53
53
  result.visible = visibilityMatch[1] === 'true';
@@ -59,11 +59,12 @@ function parseWindowProperties (name, props) {
59
59
  *
60
60
  * @param {Array<string>} lines Output from dumpsys command.
61
61
  * Check the corresponding unit tests for more details on the input format.
62
+ * @param {Object?} log Logger instance
62
63
  * @return {Object} An object containing two items where keys are statusBar and navigationBar,
63
64
  * and values are corresponding WindowProperties objects
64
65
  * @throws {Error} If no window properties could be parsed
65
66
  */
66
- function parseWindows (lines) {
67
+ function parseWindows (lines, log = null) {
67
68
  const windows = {};
68
69
  let currentWindowName = null;
69
70
  let windowNameRowIndent = null;
@@ -91,16 +92,16 @@ function parseWindows (lines) {
91
92
  windows[currentWindowName].push(line);
92
93
  }
93
94
  if (_.isEmpty(windows)) {
94
- log.debug(lines.join('\n'));
95
+ log?.debug(lines.join('\n'));
95
96
  throw new Error('Cannot parse any window information from the dumpsys output');
96
97
  }
97
98
 
98
99
  const result = {statusBar: null, navigationBar: null};
99
100
  for (const [name, props] of _.toPairs(windows)) {
100
101
  if (name.startsWith(STATUS_BAR_WINDOW_NAME_PREFIX)) {
101
- result.statusBar = parseWindowProperties(name, props);
102
+ result.statusBar = parseWindowProperties(name, props, log);
102
103
  } else if (name.startsWith(NAVIGATION_BAR_WINDOW_NAME_PREFIX)) {
103
- result.navigationBar = parseWindowProperties(name, props);
104
+ result.navigationBar = parseWindowProperties(name, props, log);
104
105
  }
105
106
  }
106
107
  const unmatchedWindows = [
@@ -108,7 +109,7 @@ function parseWindows (lines) {
108
109
  ['navigationBar', NAVIGATION_BAR_WINDOW_NAME_PREFIX]
109
110
  ].filter(([name]) => _.isNil(result[name]));
110
111
  for (const [window, namePrefix] of unmatchedWindows) {
111
- log.info(`No windows have been found whose title matches to ` +
112
+ log?.info(`No windows have been found whose title matches to ` +
112
113
  `'${namePrefix}'. Assuming it is invisible. ` +
113
114
  `Only the following windows are available: ${_.keys(windows)}`);
114
115
  result[window] = _.cloneDeep(DEFAULT_WINDOW_PROPERTIES);
@@ -123,7 +124,7 @@ commands.getSystemBars = async function getSystemBars () {
123
124
  } catch (e) {
124
125
  throw new Error(`Cannot retrieve system bars details. Original error: ${e.message}`);
125
126
  }
126
- return parseWindows(stdout);
127
+ return parseWindows(stdout, this.log);
127
128
  };
128
129
 
129
130
  // for unit tests
@@ -1,4 +1,3 @@
1
- import log from '../logger';
2
1
  import _ from 'lodash';
3
2
  import androidHelpers from '../android-helpers';
4
3
  import B from 'bluebird';
@@ -49,10 +48,10 @@ commands.doTouchAction = async function doTouchAction (action, opts = {}) {
49
48
  return await this.touchLongClick(null, x, y, duration || 1000);
50
49
  case 'cancel':
51
50
  // TODO: clarify behavior of 'cancel' action and fix this
52
- log.warn('Cancel action currently has no effect');
51
+ this.log.warn('Cancel action currently has no effect');
53
52
  break;
54
53
  default:
55
- log.errorAndThrow(`unknown action ${action}`);
54
+ this.log.errorAndThrow(`unknown action ${action}`);
56
55
  }
57
56
  };
58
57
 
@@ -146,7 +145,7 @@ helpers.performGesture = async function performGesture (gesture) {
146
145
  if (isErrorType(e, errors.NoSuchElementError) && gesture.action === 'release' &&
147
146
  gesture.options.element) {
148
147
  delete gesture.options.element;
149
- log.debug(`retrying release without element opts: ${gesture.options}.`);
148
+ this.log.debug(`retrying release without element opts: ${gesture.options}.`);
150
149
  return await this.doTouchAction(gesture.action, gesture.options || {});
151
150
  }
152
151
  throw e;