@limrun/appium-xcuitest-driver 10.4.3-lim.1 → 10.10.1-lim.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.
- package/CHANGELOG.md +84 -0
- package/build/lib/app-utils.d.ts +2 -2
- package/build/lib/app-utils.d.ts.map +1 -1
- package/build/lib/app-utils.js +4 -1
- package/build/lib/app-utils.js.map +1 -1
- package/build/lib/commands/app-management.js +2 -2
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/appearance.js +2 -2
- package/build/lib/commands/appearance.js.map +1 -1
- package/build/lib/commands/bidi/models.d.ts.map +1 -1
- package/build/lib/commands/bidi/models.js +1 -0
- package/build/lib/commands/bidi/models.js.map +1 -1
- package/build/lib/commands/bidi/types.d.ts +1 -0
- package/build/lib/commands/bidi/types.d.ts.map +1 -1
- package/build/lib/commands/biometric.js +3 -3
- package/build/lib/commands/biometric.js.map +1 -1
- package/build/lib/commands/certificate.d.ts.map +1 -1
- package/build/lib/commands/certificate.js +9 -3
- package/build/lib/commands/certificate.js.map +1 -1
- package/build/lib/commands/condition.d.ts +2 -0
- package/build/lib/commands/condition.d.ts.map +1 -1
- package/build/lib/commands/condition.js +75 -2
- package/build/lib/commands/condition.js.map +1 -1
- package/build/lib/commands/context.d.ts +5 -5
- package/build/lib/commands/context.d.ts.map +1 -1
- package/build/lib/commands/context.js +6 -6
- package/build/lib/commands/context.js.map +1 -1
- package/build/lib/commands/file-movement.d.ts.map +1 -1
- package/build/lib/commands/file-movement.js +7 -7
- package/build/lib/commands/file-movement.js.map +1 -1
- package/build/lib/commands/general.js +1 -1
- package/build/lib/commands/general.js.map +1 -1
- package/build/lib/commands/gesture.js +1 -1
- package/build/lib/commands/gesture.js.map +1 -1
- package/build/lib/commands/keychains.js +1 -1
- package/build/lib/commands/keychains.js.map +1 -1
- package/build/lib/commands/localization.js +1 -1
- package/build/lib/commands/localization.js.map +1 -1
- package/build/lib/commands/location.d.ts +3 -2
- package/build/lib/commands/location.d.ts.map +1 -1
- package/build/lib/commands/location.js +10 -4
- package/build/lib/commands/location.js.map +1 -1
- package/build/lib/commands/log.js +9 -9
- package/build/lib/commands/log.js.map +1 -1
- package/build/lib/commands/memory.js +1 -1
- package/build/lib/commands/memory.js.map +1 -1
- package/build/lib/commands/notifications.js +1 -1
- package/build/lib/commands/notifications.js.map +1 -1
- package/build/lib/commands/pasteboard.js +2 -2
- package/build/lib/commands/pasteboard.js.map +1 -1
- package/build/lib/commands/pcap.js +1 -1
- package/build/lib/commands/pcap.js.map +1 -1
- package/build/lib/commands/performance.d.ts.map +1 -1
- package/build/lib/commands/performance.js +13 -4
- package/build/lib/commands/performance.js.map +1 -1
- package/build/lib/commands/permissions.js +2 -2
- package/build/lib/commands/permissions.js.map +1 -1
- package/build/lib/commands/proxy-helper.d.ts.map +1 -1
- package/build/lib/commands/proxy-helper.js +0 -3
- package/build/lib/commands/proxy-helper.js.map +1 -1
- package/build/lib/commands/screenshots.js +1 -1
- package/build/lib/commands/screenshots.js.map +1 -1
- package/build/lib/commands/simctl.d.ts +1 -1
- package/build/lib/commands/simctl.d.ts.map +1 -1
- package/build/lib/commands/simctl.js +1 -1
- package/build/lib/commands/simctl.js.map +1 -1
- package/build/lib/commands/web.js +1 -1
- package/build/lib/commands/web.js.map +1 -1
- package/build/lib/commands/xctest-record-screen.js +2 -2
- package/build/lib/commands/xctest-record-screen.js.map +1 -1
- package/build/lib/desired-caps.d.ts +392 -505
- package/build/lib/desired-caps.d.ts.map +1 -1
- package/build/lib/desired-caps.js +19 -10
- package/build/lib/desired-caps.js.map +1 -1
- package/build/lib/device/clients/base-device-client.d.ts +22 -0
- package/build/lib/device/clients/base-device-client.d.ts.map +1 -0
- package/build/lib/device/clients/base-device-client.js +14 -0
- package/build/lib/device/clients/base-device-client.js.map +1 -0
- package/build/lib/device/clients/py-ios-device-client.d.ts +21 -0
- package/build/lib/device/clients/py-ios-device-client.d.ts.map +1 -0
- package/build/lib/device/clients/py-ios-device-client.js +125 -0
- package/build/lib/device/clients/py-ios-device-client.js.map +1 -0
- package/build/lib/device/device-connections-factory.d.ts +18 -0
- package/build/lib/device/device-connections-factory.d.ts.map +1 -0
- package/build/lib/device/device-connections-factory.js +260 -0
- package/build/lib/device/device-connections-factory.js.map +1 -0
- package/build/lib/device/log/helpers.d.ts +10 -0
- package/build/lib/device/log/helpers.d.ts.map +1 -0
- package/build/lib/device/log/helpers.js +37 -0
- package/build/lib/device/log/helpers.js.map +1 -0
- package/build/lib/device/log/ios-crash-log.d.ts +34 -0
- package/build/lib/device/log/ios-crash-log.d.ts.map +1 -0
- package/build/lib/device/log/ios-crash-log.js +141 -0
- package/build/lib/device/log/ios-crash-log.js.map +1 -0
- package/build/lib/device/log/ios-device-log.d.ts +19 -0
- package/build/lib/device/log/ios-device-log.d.ts.map +1 -0
- package/build/lib/device/log/ios-device-log.js +42 -0
- package/build/lib/device/log/ios-device-log.js.map +1 -0
- package/build/lib/device/log/ios-log.d.ts +24 -0
- package/build/lib/device/log/ios-log.d.ts.map +1 -0
- package/build/lib/device/log/ios-log.js +50 -0
- package/build/lib/device/log/ios-log.js.map +1 -0
- package/build/lib/device/log/ios-performance-log.d.ts +18 -0
- package/build/lib/device/log/ios-performance-log.d.ts.map +1 -0
- package/build/lib/device/log/ios-performance-log.js +43 -0
- package/build/lib/device/log/ios-performance-log.js.map +1 -0
- package/build/lib/device/log/ios-simulator-log.d.ts +38 -0
- package/build/lib/device/log/ios-simulator-log.d.ts.map +1 -0
- package/build/lib/device/log/ios-simulator-log.js +184 -0
- package/build/lib/device/log/ios-simulator-log.js.map +1 -0
- package/build/lib/device/log/line-consuming-log.d.ts +9 -0
- package/build/lib/device/log/line-consuming-log.d.ts.map +1 -0
- package/build/lib/device/log/line-consuming-log.js +16 -0
- package/build/lib/device/log/line-consuming-log.js.map +1 -0
- package/build/lib/device/log/safari-console-log.d.ts +67 -0
- package/build/lib/device/log/safari-console-log.d.ts.map +1 -0
- package/build/lib/device/log/safari-console-log.js +81 -0
- package/build/lib/device/log/safari-console-log.js.map +1 -0
- package/build/lib/device/log/safari-network-log.d.ts +75 -0
- package/build/lib/device/log/safari-network-log.d.ts.map +1 -0
- package/build/lib/device/log/safari-network-log.js +47 -0
- package/build/lib/device/log/safari-network-log.js.map +1 -0
- package/build/lib/device/real-device-management.d.ts +146 -0
- package/build/lib/device/real-device-management.d.ts.map +1 -0
- package/build/lib/device/real-device-management.js +740 -0
- package/build/lib/device/real-device-management.js.map +1 -0
- package/build/lib/device/simulator-management.d.ts +65 -0
- package/build/lib/device/simulator-management.d.ts.map +1 -0
- package/build/lib/device/simulator-management.js +261 -0
- package/build/lib/device/simulator-management.js.map +1 -0
- package/build/lib/device-log/ios-crash-log.d.ts +1 -1
- package/build/lib/device-log/ios-crash-log.d.ts.map +1 -1
- package/build/lib/device-log/ios-simulator-log.d.ts +1 -1
- package/build/lib/device-log/ios-simulator-log.d.ts.map +1 -1
- package/build/lib/doctor/required-checks.js +1 -1
- package/build/lib/doctor/required-checks.js.map +1 -1
- package/build/lib/driver.d.ts +129 -1377
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +461 -573
- package/build/lib/driver.js.map +1 -1
- package/build/lib/method-map.d.ts +1 -1
- package/build/lib/method-map.d.ts.map +1 -1
- package/build/lib/method-map.js +2 -2
- package/build/lib/method-map.js.map +1 -1
- package/build/lib/simulator-management.d.ts +10 -0
- package/build/lib/simulator-management.d.ts.map +1 -1
- package/build/lib/simulator-management.js +9 -5
- package/build/lib/simulator-management.js.map +1 -1
- package/build/lib/utils.d.ts +2 -9
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +3 -47
- package/build/lib/utils.js.map +1 -1
- package/lib/app-utils.js +5 -1
- package/lib/commands/app-management.js +2 -2
- package/lib/commands/appearance.js +2 -2
- package/lib/commands/bidi/models.ts +1 -0
- package/lib/commands/bidi/types.ts +1 -0
- package/lib/commands/biometric.js +3 -3
- package/lib/commands/certificate.js +9 -3
- package/lib/commands/condition.js +85 -2
- package/lib/commands/context.js +6 -6
- package/lib/commands/file-movement.js +11 -7
- package/lib/commands/general.js +1 -1
- package/lib/commands/gesture.js +1 -1
- package/lib/commands/keychains.js +1 -1
- package/lib/commands/localization.js +1 -1
- package/lib/commands/location.js +11 -4
- package/lib/commands/log.js +9 -9
- package/lib/commands/memory.js +1 -1
- package/lib/commands/notifications.js +1 -1
- package/lib/commands/pasteboard.js +2 -2
- package/lib/commands/pcap.js +1 -1
- package/lib/commands/performance.js +12 -1
- package/lib/commands/permissions.js +2 -2
- package/lib/commands/proxy-helper.js +0 -3
- package/lib/commands/screenshots.js +1 -1
- package/lib/commands/simctl.js +1 -1
- package/lib/commands/web.js +1 -1
- package/lib/commands/xctest-record-screen.js +2 -2
- package/lib/{desired-caps.js → desired-caps.ts} +20 -6
- package/lib/{real-device-clients → device/clients}/py-ios-device-client.ts +1 -1
- package/lib/{device-connections-factory.js → device/device-connections-factory.ts} +96 -60
- package/lib/{device-log → device/log}/helpers.ts +1 -1
- package/lib/{device-log → device/log}/ios-crash-log.ts +4 -4
- package/lib/{device-log → device/log}/ios-log.ts +1 -1
- package/lib/{device-log → device/log}/ios-simulator-log.ts +1 -1
- package/lib/{device-log → device/log}/line-consuming-log.ts +1 -1
- package/lib/{device-log → device/log}/safari-console-log.ts +1 -1
- package/lib/device/real-device-management.ts +831 -0
- package/lib/{simulator-management.js → device/simulator-management.ts} +75 -64
- package/lib/doctor/required-checks.ts +1 -1
- package/lib/{driver.js → driver.ts} +623 -707
- package/lib/{method-map.js → method-map.ts} +5 -2
- package/lib/utils.js +3 -54
- package/package.json +17 -19
- package/scripts/build-wda.js +3 -3
- package/lib/ios-fs-helpers.js +0 -355
- package/lib/ios-generic-simulators.js +0 -11
- package/lib/real-device-management.js +0 -133
- package/lib/real-device.js +0 -347
- package/lib/xcrun.js +0 -16
- /package/lib/{real-device-clients → device/clients}/base-device-client.ts +0 -0
- /package/lib/{device-log → device/log}/ios-device-log.ts +0 -0
- /package/lib/{device-log → device/log}/ios-performance-log.ts +0 -0
- /package/lib/{device-log → device/log}/safari-network-log.ts +0 -0
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
|
-
import {buildSafariPreferences} from './app-utils';
|
|
3
|
-
import { getConnectedDevices } from './real-device';
|
|
4
|
-
|
|
5
|
-
const DEFAULT_APP_INSTALLATION_TIMEOUT_MS = 8 * 60 * 1000;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {Object} InstallOptions
|
|
9
|
-
*
|
|
10
|
-
* @property {boolean} [skipUninstall] Whether to skip app uninstall before installing it
|
|
11
|
-
* @property {number} [timeout=480000] App install timeout
|
|
12
|
-
* @property {boolean} [shouldEnforceUninstall] Whether to enforce the app uninstallation. e.g. fullReset, or enforceAppInstall is true
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @this {import('./driver').XCUITestDriver}
|
|
17
|
-
* @param {string} [app] The app to the path
|
|
18
|
-
* @param {string} [bundleId] The bundle id to ensure it is already installed and uninstall it
|
|
19
|
-
* @param {InstallOptions} [opts={}]
|
|
20
|
-
*/
|
|
21
|
-
export async function installToRealDevice(app, bundleId, opts = {}) {
|
|
22
|
-
const device = /** @type {RealDevice} */ (this.device);
|
|
23
|
-
|
|
24
|
-
if (!device.udid || !app || !bundleId) {
|
|
25
|
-
this.log.debug('No device id, app or bundle id, not installing to real device.');
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const {
|
|
30
|
-
skipUninstall,
|
|
31
|
-
timeout = DEFAULT_APP_INSTALLATION_TIMEOUT_MS,
|
|
32
|
-
} = opts;
|
|
33
|
-
|
|
34
|
-
if (!skipUninstall) {
|
|
35
|
-
this.log.info(`Reset requested. Removing app with id '${bundleId}' from the device`);
|
|
36
|
-
await device.remove(bundleId);
|
|
37
|
-
}
|
|
38
|
-
this.log.debug(`Installing '${app}' on the device with UUID '${device.udid}'`);
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
await device.install(app, bundleId, {
|
|
42
|
-
timeoutMs: timeout,
|
|
43
|
-
});
|
|
44
|
-
this.log.debug('The app has been installed successfully.');
|
|
45
|
-
} catch (e) {
|
|
46
|
-
// Want to clarify the device's application installation state in this situation.
|
|
47
|
-
|
|
48
|
-
if (!skipUninstall || !e.message.includes('MismatchedApplicationIdentifierEntitlement')) {
|
|
49
|
-
// Other error cases that could not be recoverable by here.
|
|
50
|
-
// Exact error will be in the log.
|
|
51
|
-
|
|
52
|
-
// We cannot recover 'ApplicationVerificationFailed' situation since this reason is clearly the app's provisioning profile was invalid.
|
|
53
|
-
// [XCUITest] Error installing app '/path/to.app': Unexpected data: {"Error":"ApplicationVerificationFailed","ErrorDetail":-402620395,"ErrorDescription":"Failed to verify code signature of /path/to.app : 0xe8008015 (A valid provisioning profile for this executable was not found.)"}
|
|
54
|
-
throw e;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// If the error was by below error case, we could recover the situation
|
|
58
|
-
// by uninstalling the device's app bundle id explicitly regard less the app exists on the device or not (e.g. offload app).
|
|
59
|
-
// [XCUITest] Error installing app '/path/to.app': Unexpected data: {"Error":"MismatchedApplicationIdentifierEntitlement","ErrorDescription":"Upgrade's application-identifier entitlement string (TEAM_ID.com.kazucocoa.example) does not match installed application's application-identifier string (ANOTHER_TEAM_ID.com.kazucocoa.example); rejecting upgrade."}
|
|
60
|
-
this.log.info(`The application identified by '${bundleId}' cannot be installed because it might ` +
|
|
61
|
-
`be already cached on the device, probably with a different signature. ` +
|
|
62
|
-
`Will try to remove it and install a new copy. Original error: ${e.message}`);
|
|
63
|
-
await device.remove(bundleId);
|
|
64
|
-
await device.install(app, bundleId, {
|
|
65
|
-
timeoutMs: timeout,
|
|
66
|
-
});
|
|
67
|
-
this.log.debug('The app has been installed after one retrial.');
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* @this {import('./driver').XCUITestDriver}
|
|
73
|
-
* @returns {Promise<void>}
|
|
74
|
-
*/
|
|
75
|
-
export async function runRealDeviceReset() {
|
|
76
|
-
if (!this.opts.noReset || this.opts.fullReset) {
|
|
77
|
-
this.log.debug('Reset: running ios real device reset flow');
|
|
78
|
-
if (!this.opts.noReset) {
|
|
79
|
-
await /** @type {RealDevice} */ (this.device).reset(this.opts);
|
|
80
|
-
}
|
|
81
|
-
} else {
|
|
82
|
-
this.log.debug('Reset: fullReset not set. Leaving as is');
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Configures Safari startup options based on the given session capabilities.
|
|
88
|
-
*
|
|
89
|
-
* !!! This method mutates driver options.
|
|
90
|
-
*
|
|
91
|
-
* @this {import('./driver').XCUITestDriver}
|
|
92
|
-
* @return {boolean} true if process arguments have been modified
|
|
93
|
-
*/
|
|
94
|
-
export function applySafariStartupArgs() {
|
|
95
|
-
const prefs = buildSafariPreferences(this.opts);
|
|
96
|
-
if (_.isEmpty(prefs)) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const args = _.toPairs(prefs)
|
|
101
|
-
.flatMap(([key, value]) => [_.startsWith(key, '-') ? key : `-${key}`, String(value)]);
|
|
102
|
-
this.log.debug(`Generated Safari command line arguments: ${args.join(' ')}`);
|
|
103
|
-
if (_.isPlainObject(this.opts.processArguments)) {
|
|
104
|
-
this.opts.processArguments.args = [...(this.opts.processArguments.args ?? []), ...args];
|
|
105
|
-
} else {
|
|
106
|
-
this.opts.processArguments = {args};
|
|
107
|
-
}
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* @this {XCUITestDriver}
|
|
113
|
-
* @returns {Promise<string>}
|
|
114
|
-
*/
|
|
115
|
-
export async function detectUdid() {
|
|
116
|
-
this.log.debug('Auto-detecting real device udid...');
|
|
117
|
-
const udids = await getConnectedDevices();
|
|
118
|
-
if (_.isEmpty(udids)) {
|
|
119
|
-
throw new Error('No real devices are connected to the host');
|
|
120
|
-
}
|
|
121
|
-
const udid = udids[udids.length - 1];
|
|
122
|
-
if (udids.length > 1) {
|
|
123
|
-
this.log.info(`Multiple devices found: ${udids.join(', ')}`);
|
|
124
|
-
this.log.info(`Choosing '${udid}'. Consider settings the 'udid' capability if another device must be selected`);
|
|
125
|
-
}
|
|
126
|
-
this.log.debug(`Detected real device udid: '${udid}'`);
|
|
127
|
-
return udid;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* @typedef {import('./real-device').RealDevice} RealDevice}
|
|
132
|
-
* @typedef {import('./driver').XCUITestDriver} XCUITestDriver
|
|
133
|
-
*/
|
package/lib/real-device.js
DELETED
|
@@ -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};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|