appium-xcuitest-driver 7.20.2 → 7.21.1

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 (48) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/lib/commands/log.d.ts +0 -8
  3. package/build/lib/commands/log.d.ts.map +1 -1
  4. package/build/lib/commands/log.js +4 -13
  5. package/build/lib/commands/log.js.map +1 -1
  6. package/build/lib/commands/types.d.ts +5 -0
  7. package/build/lib/commands/types.d.ts.map +1 -1
  8. package/build/lib/device-log/helpers.d.ts +3 -0
  9. package/build/lib/device-log/helpers.d.ts.map +1 -0
  10. package/build/lib/device-log/helpers.js +11 -0
  11. package/build/lib/device-log/helpers.js.map +1 -0
  12. package/build/lib/device-log/ios-crash-log.d.ts +6 -17
  13. package/build/lib/device-log/ios-crash-log.d.ts.map +1 -1
  14. package/build/lib/device-log/ios-crash-log.js +4 -10
  15. package/build/lib/device-log/ios-crash-log.js.map +1 -1
  16. package/build/lib/device-log/ios-device-log.d.ts +5 -2
  17. package/build/lib/device-log/ios-device-log.d.ts.map +1 -1
  18. package/build/lib/device-log/ios-device-log.js +3 -0
  19. package/build/lib/device-log/ios-device-log.js.map +1 -1
  20. package/build/lib/device-log/ios-log.d.ts +36 -7
  21. package/build/lib/device-log/ios-log.d.ts.map +1 -1
  22. package/build/lib/device-log/ios-log.js +73 -25
  23. package/build/lib/device-log/ios-log.js.map +1 -1
  24. package/build/lib/device-log/ios-performance-log.d.ts +25 -8
  25. package/build/lib/device-log/ios-performance-log.d.ts.map +1 -1
  26. package/build/lib/device-log/ios-performance-log.js +40 -23
  27. package/build/lib/device-log/ios-performance-log.js.map +1 -1
  28. package/build/lib/device-log/ios-simulator-log.d.ts +11 -4
  29. package/build/lib/device-log/ios-simulator-log.d.ts.map +1 -1
  30. package/build/lib/device-log/ios-simulator-log.js +41 -47
  31. package/build/lib/device-log/ios-simulator-log.js.map +1 -1
  32. package/build/lib/driver.d.ts +13 -0
  33. package/build/lib/driver.d.ts.map +1 -1
  34. package/build/lib/execute-method-map.d.ts +13 -0
  35. package/build/lib/execute-method-map.d.ts.map +1 -1
  36. package/build/lib/execute-method-map.js +13 -0
  37. package/build/lib/execute-method-map.js.map +1 -1
  38. package/lib/commands/log.js +6 -14
  39. package/lib/commands/types.ts +6 -0
  40. package/lib/device-log/helpers.ts +9 -0
  41. package/lib/device-log/ios-crash-log.js +4 -11
  42. package/lib/device-log/ios-device-log.js +4 -2
  43. package/lib/device-log/ios-log.js +78 -27
  44. package/lib/device-log/ios-performance-log.js +40 -29
  45. package/lib/device-log/ios-simulator-log.js +45 -49
  46. package/lib/execute-method-map.ts +13 -0
  47. package/npm-shrinkwrap.json +81 -146
  48. package/package.json +2 -2
@@ -1,14 +1,22 @@
1
1
  import {EventEmitter} from 'events';
2
+ import { LRUCache } from 'lru-cache';
3
+ import { toLogEntry } from './helpers';
2
4
 
3
5
  // We keep only the most recent log entries to avoid out of memory error
4
6
  const MAX_LOG_ENTRIES_COUNT = 10000;
5
7
 
6
- class IOSLog extends EventEmitter {
7
- constructor() {
8
+ // TODO: Rewrite this class to typescript for better generic typing
9
+
10
+ export class IOSLog extends EventEmitter {
11
+ constructor(maxBufferSize = MAX_LOG_ENTRIES_COUNT) {
8
12
  super();
9
- this.logs = [];
10
- this.logIdxSinceLastRequest = -1;
11
- this.maxBufferSize = MAX_LOG_ENTRIES_COUNT;
13
+ this.maxBufferSize = maxBufferSize;
14
+ /** @type {LRUCache<number, any>} */
15
+ this.logs = new LRUCache({
16
+ max: this.maxBufferSize,
17
+ });
18
+ /** @type {number?} */
19
+ this.logIndexSinceLastRequest = null;
12
20
  }
13
21
 
14
22
  /** @returns {Promise<void>} */
@@ -28,38 +36,81 @@ class IOSLog extends EventEmitter {
28
36
  throw new Error(`Sub-classes need to implement a 'isCapturing' function`);
29
37
  }
30
38
 
31
- broadcast(logLine) {
32
- const logObj = {
33
- timestamp: Date.now(),
34
- level: 'ALL',
35
- message: logLine,
36
- };
37
- this.logs.push(logObj);
38
- this.emit('output', logObj);
39
- if (this.logs.length > this.maxBufferSize) {
40
- this.logs.shift();
41
- if (this.logIdxSinceLastRequest > 0) {
42
- --this.logIdxSinceLastRequest;
43
- }
39
+ /**
40
+ *
41
+ * @param {any} entry
42
+ * @returns {void}
43
+ */
44
+ broadcast(entry) {
45
+ let recentIndex = -1;
46
+ for (const key of this.logs.rkeys()) {
47
+ recentIndex = key;
48
+ break;
49
+ }
50
+ const serializedEntry = this._serializeEntry(entry);
51
+ this.logs.set(++recentIndex, serializedEntry);
52
+ if (this.listenerCount('output')) {
53
+ this.emit('output', this._deserializeEntry(serializedEntry));
44
54
  }
45
55
  }
46
56
 
57
+ /**
58
+ *
59
+ * @returns {import('../commands/types').LogEntry[]}
60
+ */
47
61
  getLogs() {
48
- if (this.logs.length && this.logIdxSinceLastRequest < this.logs.length) {
49
- let result = this.logs;
50
- if (this.logIdxSinceLastRequest > 0) {
51
- result = result.slice(this.logIdxSinceLastRequest);
62
+ /** @type {import('../commands/types').LogEntry[]} */
63
+ const result = [];
64
+ /** @type {number?} */
65
+ let recentLogIndex = null;
66
+ for (const [index, value] of this.logs.entries()) {
67
+ if (this.logIndexSinceLastRequest && index > this.logIndexSinceLastRequest
68
+ || !this.logIndexSinceLastRequest) {
69
+ recentLogIndex = index;
70
+ result.push(this._deserializeEntry(value));
52
71
  }
53
- this.logIdxSinceLastRequest = this.logs.length;
54
- return result;
55
72
  }
56
- return [];
73
+ if (recentLogIndex !== null) {
74
+ this.logIndexSinceLastRequest = recentLogIndex;
75
+ }
76
+ return result;
57
77
  }
58
78
 
79
+ /**
80
+ *
81
+ * @returns {import('../commands/types').LogEntry[]}
82
+ */
59
83
  getAllLogs() {
60
- return this.logs;
84
+ /** @type {import('../commands/types').LogEntry[]} */
85
+ const result = [];
86
+ for (const value of this.logs.values()) {
87
+ result.push(this._deserializeEntry(value));
88
+ }
89
+ return result;
90
+ }
91
+
92
+ /**
93
+ *
94
+ * @param {any} value
95
+ * @returns {any}
96
+ */
97
+ _serializeEntry(value) {
98
+ return [value, Date.now()];
99
+ }
100
+
101
+ /**
102
+ *
103
+ * @param {any} value
104
+ * @returns {any}
105
+ */
106
+ _deserializeEntry(value) {
107
+ const [message, timestamp] = value;
108
+ return toLogEntry(message, timestamp);
109
+ }
110
+
111
+ _clearEntries() {
112
+ this.logs.clear();
61
113
  }
62
114
  }
63
115
 
64
- export {IOSLog};
65
116
  export default IOSLog;
@@ -1,57 +1,68 @@
1
1
  import {logger} from 'appium/support';
2
2
  import _ from 'lodash';
3
+ import { IOSLog } from './ios-log';
3
4
 
4
5
  const log = logger.getLogger('IOSPerformanceLog');
5
6
  const MAX_EVENTS = 5000;
6
7
 
7
- class IOSPerformanceLog {
8
+ export class IOSPerformanceLog extends IOSLog {
8
9
  constructor(remoteDebugger, maxEvents = MAX_EVENTS) {
10
+ super(maxEvents);
9
11
  this.remoteDebugger = remoteDebugger;
10
12
  this.maxEvents = parseInt(String(maxEvents), 10);
11
-
12
- this.timelineEvents = [];
13
+ this._started = false;
13
14
  }
14
15
 
16
+ /**
17
+ * @override
18
+ */
15
19
  async startCapture() {
16
20
  log.debug('Starting performance (Timeline) log capture');
17
- this.timelineEvents = [];
18
- return await this.remoteDebugger.startTimeline(this.onTimelineEvent.bind(this));
21
+ this._clearEntries();
22
+ const result = await this.remoteDebugger.startTimeline(this.onTimelineEvent.bind(this));
23
+ this._started = true;
24
+ return result;
19
25
  }
20
26
 
27
+ /**
28
+ * @override
29
+ */
21
30
  async stopCapture() {
22
31
  log.debug('Stopping performance (Timeline) log capture');
23
- return await this.remoteDebugger.stopTimeline();
32
+ const result = await this.remoteDebugger.stopTimeline();
33
+ this._started = false;
34
+ return result;
24
35
  }
25
36
 
26
- onTimelineEvent(event) {
27
- log.debug(`Received Timeline event: ${_.truncate(JSON.stringify(event))}`);
28
- this.timelineEvents.push(event);
29
-
30
- // if we have too many, get rid of the oldest log line
31
- if (this.timelineEvents.length > this.maxEvents) {
32
- let removedEvent = this.timelineEvents.shift();
33
- log.warn(
34
- `Too many Timeline events, removing earliest: ${_.truncate(JSON.stringify(removedEvent))}`,
35
- );
36
- }
37
+ /**
38
+ * @override
39
+ */
40
+ get isCapturing() {
41
+ return this._started;
37
42
  }
38
43
 
39
- // eslint-disable-next-line require-await
40
- async getLogs() {
41
- let events = this.timelineEvents;
42
-
43
- // flush events
44
- log.debug('Flushing Timeline events');
45
- this.timelineEvents = [];
44
+ /**
45
+ * @override
46
+ */
47
+ _serializeEntry(value) {
48
+ return value;
49
+ }
46
50
 
47
- return events;
51
+ /**
52
+ * @override
53
+ */
54
+ _deserializeEntry(value) {
55
+ return value;
48
56
  }
49
57
 
50
- // eslint-disable-next-line require-await
51
- async getAllLogs() {
52
- return this.getLogs();
58
+ /**
59
+ *
60
+ * @param {import('../commands/types').LogEntry} event
61
+ */
62
+ onTimelineEvent(event) {
63
+ log.debug(`Received Timeline event: ${_.truncate(JSON.stringify(event))}`);
64
+ this.broadcast(event);
53
65
  }
54
66
  }
55
67
 
56
- export {IOSPerformanceLog};
57
68
  export default IOSPerformanceLog;
@@ -4,10 +4,11 @@ import {logger} from 'appium/support';
4
4
  import {exec} from 'teen_process';
5
5
 
6
6
  const log = logger.getLogger('IOSSimulatorLog');
7
+ const EXECVP_ERROR_PATTERN = /execvp\(\)/;
7
8
 
8
9
  const START_TIMEOUT = 10000;
9
10
 
10
- class IOSSimulatorLog extends IOSLog {
11
+ export class IOSSimulatorLog extends IOSLog {
11
12
  constructor({sim, showLogs, xcodeVersion, iosSimulatorLogsPredicate}) {
12
13
  super();
13
14
  this.sim = sim;
@@ -17,6 +18,9 @@ class IOSSimulatorLog extends IOSLog {
17
18
  this.proc = null;
18
19
  }
19
20
 
21
+ /**
22
+ * @override
23
+ */
20
24
  async startCapture() {
21
25
  if (_.isUndefined(this.sim.udid)) {
22
26
  throw new Error(`Log capture requires a sim udid`);
@@ -44,41 +48,9 @@ class IOSSimulatorLog extends IOSLog {
44
48
  }
45
49
  }
46
50
 
47
- async finishStartingLogCapture() {
48
- if (!this.proc) {
49
- log.errorAndThrow('Could not capture simulator log');
50
- }
51
- let firstLine = true;
52
- let logRow = '';
53
- this.proc.on('output', (stdout, stderr) => {
54
- if (stdout) {
55
- if (firstLine) {
56
- if (stdout.endsWith('\n')) {
57
- // don't store the first line of the log because it came before the sim was launched
58
- firstLine = false;
59
- }
60
- } else {
61
- logRow += stdout;
62
- if (stdout.endsWith('\n')) {
63
- this.onOutput(logRow);
64
- logRow = '';
65
- }
66
- }
67
- }
68
- if (stderr) {
69
- this.onOutput(logRow, 'STDERR');
70
- }
71
- });
72
-
73
- let sd = (stdout, stderr) => {
74
- if (/execvp\(\)/.test(stderr)) {
75
- throw new Error('iOS log capture process failed to start');
76
- }
77
- return stdout || stderr;
78
- };
79
- await this.proc.start(sd, START_TIMEOUT);
80
- }
81
-
51
+ /**
52
+ * @override
53
+ */
82
54
  async stopCapture() {
83
55
  if (!this.proc) {
84
56
  return;
@@ -87,6 +59,25 @@ class IOSSimulatorLog extends IOSLog {
87
59
  this.proc = null;
88
60
  }
89
61
 
62
+ /**
63
+ * @override
64
+ */
65
+ get isCapturing() {
66
+ return this.proc && this.proc.isRunning;
67
+ }
68
+
69
+ /**
70
+ * @param {string} logRow
71
+ * @param {string} [prefix='']
72
+ */
73
+ onOutput(logRow, prefix = '') {
74
+ this.broadcast(logRow);
75
+ if (this.showLogs) {
76
+ const space = prefix.length > 0 ? ' ' : '';
77
+ log.info(`[IOS_SYSLOG_ROW${space}${prefix}] ${logRow}`);
78
+ }
79
+ }
80
+
90
81
  async killLogSubProcess() {
91
82
  if (!this.proc.isRunning) {
92
83
  return;
@@ -103,22 +94,27 @@ class IOSSimulatorLog extends IOSLog {
103
94
  }
104
95
  }
105
96
 
106
- get isCapturing() {
107
- return this.proc && this.proc.isRunning;
108
- }
97
+ async finishStartingLogCapture() {
98
+ if (!this.proc) {
99
+ log.errorAndThrow('Could not capture simulator log');
100
+ }
109
101
 
110
- onOutput(logRow, prefix = '') {
111
- const logs = _.cloneDeep(logRow.split('\n'));
112
- for (const logLine of logs) {
113
- if (!logLine) continue; // eslint-disable-line curly
114
- this.broadcast(logLine);
115
- if (this.showLogs) {
116
- const space = prefix.length > 0 ? ' ' : '';
117
- log.info(`[IOS_SYSLOG_ROW${space}${prefix}] ${logLine}`);
118
- }
102
+ for (const streamName of ['stdout', 'stderr']) {
103
+ this.proc.on(`lines-${streamName}`, (/** @type {string[]} */ lines) => {
104
+ for (const line of lines) {
105
+ this.onOutput(line, ...(streamName === 'stderr' ? ['STDERR'] : []));
106
+ }
107
+ });
119
108
  }
109
+
110
+ const startDetector = (/** @type {string} */ stdout, /** @type {string} */ stderr) => {
111
+ if (EXECVP_ERROR_PATTERN.test(stderr)) {
112
+ throw new Error('iOS log capture process failed to start');
113
+ }
114
+ return stdout || stderr;
115
+ };
116
+ await this.proc.start(startDetector, START_TIMEOUT);
120
117
  }
121
118
  }
122
119
 
123
- export {IOSSimulatorLog};
124
120
  export default IOSSimulatorLog;
@@ -333,6 +333,19 @@ export const executeMethodMap = {
333
333
  required: ['style'],
334
334
  },
335
335
  },
336
+ 'mobile: getClipboard': {
337
+ command: 'getClipboard',
338
+ params: {
339
+ optional: ['contentType'],
340
+ },
341
+ },
342
+ 'mobile: setClipboard': {
343
+ command: 'setClipboard',
344
+ params: {
345
+ required: ['content'],
346
+ optional: ['contentType'],
347
+ },
348
+ },
336
349
  'mobile: siriCommand': {
337
350
  command: 'mobileSiriCommand',
338
351
  params: {