appium-xcuitest-driver 10.8.3 → 10.8.4

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 (66) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/lib/commands/app-management.js +1 -1
  3. package/build/lib/commands/app-management.js.map +1 -1
  4. package/build/lib/commands/file-movement.d.ts.map +1 -1
  5. package/build/lib/commands/file-movement.js +4 -4
  6. package/build/lib/commands/file-movement.js.map +1 -1
  7. package/build/lib/commands/memory.js +1 -1
  8. package/build/lib/commands/memory.js.map +1 -1
  9. package/build/lib/commands/performance.d.ts.map +1 -1
  10. package/build/lib/commands/performance.js +13 -4
  11. package/build/lib/commands/performance.js.map +1 -1
  12. package/build/lib/commands/xctest-record-screen.js +1 -1
  13. package/build/lib/commands/xctest-record-screen.js.map +1 -1
  14. package/build/lib/device/device-connections-factory.d.ts +18 -0
  15. package/build/lib/device/device-connections-factory.d.ts.map +1 -0
  16. package/build/lib/{device-connections-factory.js → device/device-connections-factory.js} +57 -41
  17. package/build/lib/device/device-connections-factory.js.map +1 -0
  18. package/build/lib/device/real-device-management.d.ts +146 -0
  19. package/build/lib/device/real-device-management.d.ts.map +1 -0
  20. package/build/lib/device/real-device-management.js +727 -0
  21. package/build/lib/device/real-device-management.js.map +1 -0
  22. package/build/lib/device/simulator-management.d.ts +65 -0
  23. package/build/lib/device/simulator-management.d.ts.map +1 -0
  24. package/build/lib/{simulator-management.js → device/simulator-management.js} +23 -42
  25. package/build/lib/device/simulator-management.js.map +1 -0
  26. package/build/lib/driver.d.ts +1 -1
  27. package/build/lib/driver.d.ts.map +1 -1
  28. package/build/lib/driver.js +5 -6
  29. package/build/lib/driver.js.map +1 -1
  30. package/lib/commands/app-management.js +1 -1
  31. package/lib/commands/file-movement.js +8 -4
  32. package/lib/commands/memory.js +1 -1
  33. package/lib/commands/performance.js +12 -1
  34. package/lib/commands/xctest-record-screen.js +1 -1
  35. package/lib/{device-connections-factory.js → device/device-connections-factory.ts} +96 -60
  36. package/lib/device/real-device-management.ts +818 -0
  37. package/lib/{simulator-management.js → device/simulator-management.ts} +68 -61
  38. package/lib/driver.js +3 -5
  39. package/npm-shrinkwrap.json +2 -2
  40. package/package.json +1 -1
  41. package/build/lib/device-connections-factory.d.ts +0 -13
  42. package/build/lib/device-connections-factory.d.ts.map +0 -1
  43. package/build/lib/device-connections-factory.js.map +0 -1
  44. package/build/lib/ios-fs-helpers.d.ts +0 -75
  45. package/build/lib/ios-fs-helpers.d.ts.map +0 -1
  46. package/build/lib/ios-fs-helpers.js +0 -370
  47. package/build/lib/ios-fs-helpers.js.map +0 -1
  48. package/build/lib/real-device-management.d.ts +0 -53
  49. package/build/lib/real-device-management.d.ts.map +0 -1
  50. package/build/lib/real-device-management.js +0 -128
  51. package/build/lib/real-device-management.js.map +0 -1
  52. package/build/lib/real-device.d.ts +0 -112
  53. package/build/lib/real-device.d.ts.map +0 -1
  54. package/build/lib/real-device.js +0 -352
  55. package/build/lib/real-device.js.map +0 -1
  56. package/build/lib/simulator-management.d.ts +0 -96
  57. package/build/lib/simulator-management.d.ts.map +0 -1
  58. package/build/lib/simulator-management.js.map +0 -1
  59. package/build/lib/xcrun.d.ts +0 -3
  60. package/build/lib/xcrun.d.ts.map +0 -1
  61. package/build/lib/xcrun.js +0 -17
  62. package/build/lib/xcrun.js.map +0 -1
  63. package/lib/ios-fs-helpers.js +0 -355
  64. package/lib/real-device-management.js +0 -133
  65. package/lib/real-device.js +0 -347
  66. package/lib/xcrun.js +0 -16
@@ -1,347 +0,0 @@
1
- import {timing, util, fs} from 'appium/support';
2
- import path from 'path';
3
- import {services, utilities, INSTRUMENT_CHANNEL} from 'appium-ios-device';
4
- import B, {TimeoutError} from 'bluebird';
5
- import defaultLogger from './logger';
6
- import _ from 'lodash';
7
- import {SAFARI_BUNDLE_ID} from './app-utils';
8
- import {pushFile, pushFolder, IO_TIMEOUT_MS} from './ios-fs-helpers';
9
- import { Devicectl } from 'node-devicectl';
10
-
11
- const APPLICATION_INSTALLED_NOTIFICATION = 'com.apple.mobile.application_installed';
12
- const APPLICATION_NOTIFICATION_TIMEOUT_MS = 30 * 1000;
13
- const INSTALLATION_STAGING_DIR = 'PublicStaging';
14
-
15
- /**
16
- * @returns {Promise<string[]>}
17
- */
18
- export async function getConnectedDevices() {
19
- if (['yes', 'true', '1'].includes(_.toLower(process.env.APPIUM_XCUITEST_PREFER_DEVICECTL))) {
20
- return (await new Devicectl('').listDevices())
21
- .map(({hardwareProperties}) => hardwareProperties?.udid)
22
- .filter(Boolean);
23
- }
24
- return await utilities.getConnectedDevices();
25
- }
26
-
27
- /**
28
- * @typedef {Object} InstallOptions
29
- * @param {number} [timeoutMs=240000] Application installation timeout in milliseconds
30
- */
31
-
32
- /**
33
- * @typedef {Object} InstallOrUpgradeOptions
34
- * @property {number} timeout Install/upgrade timeout in milliseconds
35
- * @property {boolean} isUpgrade Whether it is an app upgrade or a new install
36
- */
37
-
38
- export class RealDevice {
39
- /**
40
- * @param {string} udid
41
- * @param {import('@appium/types').AppiumLogger} [logger]
42
- */
43
- constructor(udid, logger) {
44
- this.udid = udid;
45
- this._log = logger ?? defaultLogger;
46
- this.devicectl = new Devicectl(this.udid);
47
- }
48
-
49
- /**
50
- * @returns {import('@appium/types').AppiumLogger}
51
- */
52
- get log() {
53
- return this._log;
54
- }
55
-
56
- /**
57
- * @param {string} bundleId
58
- */
59
- async remove(bundleId) {
60
- const service = await services.startInstallationProxyService(this.udid);
61
- try {
62
- await service.uninstallApplication(bundleId);
63
- } finally {
64
- service.close();
65
- }
66
- }
67
-
68
- /**
69
- * @param {string} bundleId
70
- */
71
- async removeApp(bundleId) {
72
- await this.remove(bundleId);
73
- }
74
-
75
- /**
76
- *
77
- * @param {string} appPath
78
- * @param {string} bundleId
79
- * @param {InstallOptions} [opts={}]
80
- */
81
- async install(appPath, bundleId, opts = {}) {
82
- const {
83
- timeoutMs = IO_TIMEOUT_MS,
84
- } = opts;
85
- const timer = new timing.Timer().start();
86
- const afcService = await services.startAfcService(this.udid);
87
- try {
88
- let bundlePathOnPhone;
89
- if ((await fs.stat(appPath)).isFile()) {
90
- // https://github.com/doronz88/pymobiledevice3/blob/6ff5001f5776e03b610363254e82d7fbcad4ef5f/pymobiledevice3/services/installation_proxy.py#L75
91
- bundlePathOnPhone = `/${path.basename(appPath)}`;
92
- await pushFile(afcService, appPath, bundlePathOnPhone, {
93
- timeoutMs,
94
- });
95
- } else {
96
- bundlePathOnPhone = `${INSTALLATION_STAGING_DIR}/${bundleId}`;
97
- await pushFolder(afcService, appPath, bundlePathOnPhone, {
98
- enableParallelPush: true,
99
- timeoutMs,
100
- });
101
- }
102
- await this.installOrUpgradeApplication(
103
- bundlePathOnPhone,
104
- {
105
- timeout: Math.max(timeoutMs - timer.getDuration().asMilliSeconds, 60000),
106
- isUpgrade: await this.isAppInstalled(bundleId),
107
- }
108
- );
109
- } catch (err) {
110
- this.log.debug(err.stack);
111
- let errMessage = `Cannot install the ${bundleId} application`;
112
- if (err instanceof TimeoutError) {
113
- errMessage += `. Consider increasing the value of 'appPushTimeout' capability (the current value equals to ${timeoutMs}ms)`;
114
- }
115
- errMessage += `. Original error: ${err.message}`;
116
- throw new Error(errMessage);
117
- } finally {
118
- afcService.close();
119
- }
120
- this.log.info(
121
- `The installation of '${bundleId}' succeeded after ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`
122
- );
123
- }
124
-
125
- /**
126
- * @param {string} bundlePathOnPhone
127
- * @param {InstallOrUpgradeOptions} opts
128
- */
129
- async installOrUpgradeApplication(bundlePathOnPhone, {isUpgrade, timeout}) {
130
- const notificationService = await services.startNotificationProxyService(this.udid);
131
- const installationService = await services.startInstallationProxyService(this.udid);
132
- const appInstalledNotification = new B((resolve) => {
133
- notificationService.observeNotification(APPLICATION_INSTALLED_NOTIFICATION, {
134
- notification: resolve,
135
- });
136
- });
137
- const clientOptions = {PackageType: 'Developer'};
138
- try {
139
- if (isUpgrade) {
140
- this.log.debug(
141
- `An upgrade of the existing application is going to be performed. ` +
142
- `Will timeout in ${timeout.toFixed(0)} ms`
143
- );
144
- await installationService.upgradeApplication(bundlePathOnPhone, clientOptions, timeout);
145
- } else {
146
- this.log.debug(
147
- `A new application installation is going to be performed. ` +
148
- `Will timeout in ${timeout.toFixed(0)} ms`
149
- );
150
- await installationService.installApplication(bundlePathOnPhone, clientOptions, timeout);
151
- }
152
- try {
153
- await appInstalledNotification.timeout(
154
- APPLICATION_NOTIFICATION_TIMEOUT_MS,
155
- `Could not get the application installed notification within ` +
156
- `${APPLICATION_NOTIFICATION_TIMEOUT_MS}ms but we will continue`,
157
- );
158
- } catch (e) {
159
- this.log.warn(e.message);
160
- }
161
- } finally {
162
- installationService.close();
163
- notificationService.close();
164
- }
165
- }
166
-
167
- /**
168
- * Alias for {@linkcode install}
169
- * @param {string} appPath
170
- * @param {string} bundleId
171
- * @param {InstallOptions} [opts={}]
172
- */
173
- async installApp(appPath, bundleId, opts = {}) {
174
- return await this.install(appPath, bundleId, opts);
175
- }
176
-
177
- /**
178
- * Return an application object if test app has 'bundleid'.
179
- * The target bundleid can be User and System apps.
180
- *
181
- * @param {string} bundleId The bundleId to ensure it is installed
182
- * @return {Promise<boolean>} Returns True if the app is installed
183
- * on the device under test.
184
- */
185
- async isAppInstalled(bundleId) {
186
- return Boolean(await this.fetchAppInfo(bundleId));
187
- }
188
-
189
- /**
190
- * Fetches various attributes, like bundle id, version, entitlements etc. of
191
- * an installed application.
192
- *
193
- * @param {string} bundleId the bundle identifier of an app to check
194
- * @param {string|string[]|undefined} returnAttributes If provided then
195
- * only fetches the requested attributes of the app into the resulting object.
196
- * Some apps may have too many attributes, so it makes sense to limit these
197
- * by default if you don't need all of them.
198
- * @returns {Promise<Object|undefined>} Either app info as an object or undefined
199
- * if the app is not found.
200
- */
201
- async fetchAppInfo(bundleId, returnAttributes = ['CFBundleIdentifier', 'CFBundleVersion']) {
202
- const service = await services.startInstallationProxyService(this.udid);
203
- try {
204
- return (
205
- await service.lookupApplications({
206
- bundleIds: bundleId,
207
- // https://github.com/appium/appium/issues/18753
208
- returnAttributes,
209
- })
210
- )[bundleId];
211
- } finally {
212
- service.close();
213
- }
214
- }
215
-
216
- /**
217
- * @param {string} bundleId
218
- * @param {string} platformVersion
219
- * @returns {Promise<boolean>}
220
- */
221
- async terminateApp(bundleId, platformVersion) {
222
- let instrumentService;
223
- let installProxyService;
224
- try {
225
- installProxyService = await services.startInstallationProxyService(this.udid);
226
- const apps = await installProxyService.listApplications({
227
- returnAttributes: ['CFBundleIdentifier', 'CFBundleExecutable']
228
- });
229
- if (!apps[bundleId]) {
230
- this.log.info(`The bundle id '${bundleId}' did not exist`);
231
- return false;
232
- }
233
- const executableName = apps[bundleId].CFBundleExecutable;
234
- this.log.debug(`The executable name for the bundle id '${bundleId}' was '${executableName}'`);
235
-
236
- // 'devicectl' has overhead (generally?) than the instrument service via appium-ios-device,
237
- // so hre uses the 'devicectl' only for iOS 17+.
238
- if (util.compareVersions(platformVersion, '>=', '17.0')) {
239
- this.log.debug(`Calling devicectl to kill the process`);
240
-
241
- const pids = (await this.devicectl.listProcesses())
242
- .filter(({executable}) => executable.endsWith(`/${executableName}`))
243
- .map(({processIdentifier}) => processIdentifier);
244
- if (_.isEmpty(pids)) {
245
- this.log.info(`The process of the bundle id '${bundleId}' was not running`);
246
- return false;
247
- }
248
- await this.devicectl.sendSignalToProcess(pids[0], 2);
249
- } else {
250
- instrumentService = await services.startInstrumentService(this.udid);
251
-
252
- // The result of "runningProcesses" includes `bundle_id` key in iOS 16+ (possibly a specific 16.x+)
253
- // then here may not be necessary to find a process with `CFBundleExecutable`
254
- // after dropping older iOS version support.
255
- const processes = await instrumentService.callChannel(
256
- INSTRUMENT_CHANNEL.DEVICE_INFO,
257
- 'runningProcesses',
258
- );
259
- const process = processes.selector.find((process) => process.name === executableName);
260
- if (!process) {
261
- this.log.info(`The process of the bundle id '${bundleId}' was not running`);
262
- return false;
263
- }
264
- await instrumentService.callChannel(
265
- INSTRUMENT_CHANNEL.PROCESS_CONTROL,
266
- 'killPid:',
267
- `${process.pid}`,
268
- );
269
- }
270
- } catch (err) {
271
- this.log.warn(`Failed to kill '${bundleId}'. Original error: ${err.stderr || err.message}`);
272
- return false;
273
- } finally {
274
- if (installProxyService) {
275
- installProxyService.close();
276
- }
277
- if (instrumentService) {
278
- instrumentService.close();
279
- }
280
- }
281
- return true;
282
- }
283
-
284
- /**
285
- * @param {string} bundleName The name of CFBundleName in Info.plist
286
- *
287
- * @returns {Promise<string[]>} A list of User level apps' bundle ids which has
288
- * 'CFBundleName' attribute as 'bundleName'.
289
- */
290
- async getUserInstalledBundleIdsByBundleName(bundleName) {
291
- const service = await services.startInstallationProxyService(this.udid);
292
- try {
293
- const applications = await service.listApplications({
294
- applicationType: 'User', returnAttributes: ['CFBundleIdentifier', 'CFBundleName']
295
- });
296
- return _.reduce(
297
- applications,
298
- (acc, {CFBundleName}, key) => {
299
- if (CFBundleName === bundleName) {
300
- acc.push(key);
301
- }
302
- return acc;
303
- },
304
- /** @type {string[]} */ ([]),
305
- );
306
- } finally {
307
- service.close();
308
- }
309
- }
310
-
311
- /**
312
- * @returns {Promise<string>}
313
- */
314
- async getPlatformVersion() {
315
- return await utilities.getOSVersion(this.udid);
316
- }
317
-
318
- /**
319
- * @param {import('./driver').XCUITestDriverOpts} opts
320
- * @returns {Promise<void>}
321
- */
322
- async reset({bundleId, fullReset}) {
323
- if (!bundleId || !fullReset || bundleId === SAFARI_BUNDLE_ID) {
324
- // Safari cannot be removed as system app.
325
- // Safari process handling will be managed by WDA
326
- // with noReset, forceAppLaunch or shouldTerminateApp capabilities.
327
- return;
328
- }
329
-
330
- this.log.debug(`Reset: fullReset requested. Will try to uninstall the app '${bundleId}'.`);
331
- if (!(await this.isAppInstalled(bundleId))) {
332
- this.log.debug('Reset: app not installed. No need to uninstall');
333
- return;
334
- }
335
-
336
- try {
337
- await this.remove(bundleId);
338
- } catch (err) {
339
- this.log.error(`Reset: could not remove '${bundleId}' from device: ${err.message}`);
340
- throw err;
341
- }
342
- this.log.debug(`Reset: removed '${bundleId}'`);
343
- }
344
-
345
- }
346
-
347
- export default RealDevice;
package/lib/xcrun.js DELETED
@@ -1,16 +0,0 @@
1
- import {fs} from 'appium/support';
2
-
3
- const XCRUN = 'xcrun';
4
-
5
- async function requireXcrun() {
6
- try {
7
- return await fs.which(XCRUN);
8
- } catch {
9
- throw new Error(
10
- `${XCRUN} has not been found in PATH. ` +
11
- `Please make sure XCode development tools are installed`,
12
- );
13
- }
14
- }
15
-
16
- export {requireXcrun, XCRUN};