node-simctl 8.0.9 → 8.1.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 +12 -0
- package/build/index.d.ts +2 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/build/lib/helpers.d.ts +17 -9
- package/build/lib/helpers.d.ts.map +1 -1
- package/build/lib/helpers.js +50 -4
- package/build/lib/helpers.js.map +1 -1
- package/build/lib/logger.d.ts +2 -3
- package/build/lib/logger.d.ts.map +1 -1
- package/build/lib/logger.js +3 -5
- package/build/lib/logger.js.map +1 -1
- package/build/lib/simctl.d.ts +82 -188
- package/build/lib/simctl.d.ts.map +1 -1
- package/build/lib/simctl.js +98 -150
- package/build/lib/simctl.js.map +1 -1
- package/build/lib/subcommands/addmedia.d.ts +14 -16
- package/build/lib/subcommands/addmedia.d.ts.map +1 -1
- package/build/lib/subcommands/addmedia.js +5 -7
- package/build/lib/subcommands/addmedia.js.map +1 -1
- package/build/lib/subcommands/appinfo.d.ts +13 -45
- package/build/lib/subcommands/appinfo.d.ts.map +1 -1
- package/build/lib/subcommands/appinfo.js +31 -42
- package/build/lib/subcommands/appinfo.js.map +1 -1
- package/build/lib/subcommands/boot.d.ts +9 -12
- package/build/lib/subcommands/boot.d.ts.map +1 -1
- package/build/lib/subcommands/boot.js +5 -40
- package/build/lib/subcommands/boot.js.map +1 -1
- package/build/lib/subcommands/bootstatus.d.ts +16 -56
- package/build/lib/subcommands/bootstatus.d.ts.map +1 -1
- package/build/lib/subcommands/bootstatus.js +8 -22
- package/build/lib/subcommands/bootstatus.js.map +1 -1
- package/build/lib/subcommands/create.d.ts +15 -34
- package/build/lib/subcommands/create.d.ts.map +1 -1
- package/build/lib/subcommands/create.js +23 -66
- package/build/lib/subcommands/create.js.map +1 -1
- package/build/lib/subcommands/delete.d.ts +9 -12
- package/build/lib/subcommands/delete.d.ts.map +1 -1
- package/build/lib/subcommands/delete.js +3 -5
- package/build/lib/subcommands/delete.js.map +1 -1
- package/build/lib/subcommands/erase.d.ts +12 -15
- package/build/lib/subcommands/erase.d.ts.map +1 -1
- package/build/lib/subcommands/erase.js +4 -6
- package/build/lib/subcommands/erase.js.map +1 -1
- package/build/lib/subcommands/get_app_container.d.ts +19 -22
- package/build/lib/subcommands/get_app_container.d.ts.map +1 -1
- package/build/lib/subcommands/get_app_container.js +6 -8
- package/build/lib/subcommands/get_app_container.js.map +1 -1
- package/build/lib/subcommands/getenv.d.ts +11 -14
- package/build/lib/subcommands/getenv.d.ts.map +1 -1
- package/build/lib/subcommands/getenv.js +5 -7
- package/build/lib/subcommands/getenv.js.map +1 -1
- package/build/lib/subcommands/install.d.ts +12 -15
- package/build/lib/subcommands/install.d.ts.map +1 -1
- package/build/lib/subcommands/install.js +4 -6
- package/build/lib/subcommands/install.js.map +1 -1
- package/build/lib/subcommands/io.d.ts +12 -15
- package/build/lib/subcommands/io.d.ts.map +1 -1
- package/build/lib/subcommands/io.js +4 -6
- package/build/lib/subcommands/io.js.map +1 -1
- package/build/lib/subcommands/keychain.d.ts +37 -55
- package/build/lib/subcommands/keychain.d.ts.map +1 -1
- package/build/lib/subcommands/keychain.js +16 -25
- package/build/lib/subcommands/keychain.js.map +1 -1
- package/build/lib/subcommands/launch.d.ts +16 -19
- package/build/lib/subcommands/launch.d.ts.map +1 -1
- package/build/lib/subcommands/launch.js +9 -11
- package/build/lib/subcommands/launch.js.map +1 -1
- package/build/lib/subcommands/list.d.ts +94 -125
- package/build/lib/subcommands/list.d.ts.map +1 -1
- package/build/lib/subcommands/list.js +34 -92
- package/build/lib/subcommands/list.js.map +1 -1
- package/build/lib/subcommands/location.d.ts +18 -22
- package/build/lib/subcommands/location.d.ts.map +1 -1
- package/build/lib/subcommands/location.js +11 -13
- package/build/lib/subcommands/location.js.map +1 -1
- package/build/lib/subcommands/openurl.d.ts +15 -17
- package/build/lib/subcommands/openurl.d.ts.map +1 -1
- package/build/lib/subcommands/openurl.js +5 -7
- package/build/lib/subcommands/openurl.js.map +1 -1
- package/build/lib/subcommands/pbcopy.d.ts +14 -17
- package/build/lib/subcommands/pbcopy.d.ts.map +1 -1
- package/build/lib/subcommands/pbcopy.js +5 -7
- package/build/lib/subcommands/pbcopy.js.map +1 -1
- package/build/lib/subcommands/pbpaste.d.ts +14 -17
- package/build/lib/subcommands/pbpaste.d.ts.map +1 -1
- package/build/lib/subcommands/pbpaste.js +5 -7
- package/build/lib/subcommands/pbpaste.js.map +1 -1
- package/build/lib/subcommands/privacy.d.ts +52 -57
- package/build/lib/subcommands/privacy.d.ts.map +1 -1
- package/build/lib/subcommands/privacy.js +15 -17
- package/build/lib/subcommands/privacy.js.map +1 -1
- package/build/lib/subcommands/push.d.ts +22 -25
- package/build/lib/subcommands/push.d.ts.map +1 -1
- package/build/lib/subcommands/push.js +5 -7
- package/build/lib/subcommands/push.js.map +1 -1
- package/build/lib/subcommands/shutdown.d.ts +9 -12
- package/build/lib/subcommands/shutdown.d.ts.map +1 -1
- package/build/lib/subcommands/shutdown.js +5 -40
- package/build/lib/subcommands/shutdown.js.map +1 -1
- package/build/lib/subcommands/spawn.d.ts +24 -27
- package/build/lib/subcommands/spawn.d.ts.map +1 -1
- package/build/lib/subcommands/spawn.js +12 -14
- package/build/lib/subcommands/spawn.js.map +1 -1
- package/build/lib/subcommands/terminate.d.ts +12 -15
- package/build/lib/subcommands/terminate.d.ts.map +1 -1
- package/build/lib/subcommands/terminate.js +4 -6
- package/build/lib/subcommands/terminate.js.map +1 -1
- package/build/lib/subcommands/ui.d.ts +97 -105
- package/build/lib/subcommands/ui.d.ts.map +1 -1
- package/build/lib/subcommands/ui.js +24 -26
- package/build/lib/subcommands/ui.js.map +1 -1
- package/build/lib/subcommands/uninstall.d.ts +13 -16
- package/build/lib/subcommands/uninstall.d.ts.map +1 -1
- package/build/lib/subcommands/uninstall.js +4 -6
- package/build/lib/subcommands/uninstall.js.map +1 -1
- package/build/lib/types.d.ts +231 -0
- package/build/lib/types.d.ts.map +1 -0
- package/build/lib/types.js +3 -0
- package/build/lib/types.js.map +1 -0
- package/index.ts +13 -0
- package/lib/helpers.ts +81 -0
- package/lib/{logger.js → logger.ts} +2 -5
- package/lib/simctl.ts +221 -0
- package/lib/subcommands/{addmedia.js → addmedia.ts} +6 -8
- package/lib/subcommands/appinfo.ts +39 -0
- package/lib/subcommands/{boot.js → boot.ts} +5 -10
- package/lib/subcommands/bootstatus.ts +108 -0
- package/lib/subcommands/{create.js → create.ts} +29 -35
- package/lib/subcommands/{delete.js → delete.ts} +3 -5
- package/lib/subcommands/{erase.js → erase.ts} +4 -7
- package/lib/subcommands/{get_app_container.js → get_app_container.ts} +10 -8
- package/lib/subcommands/{getenv.js → getenv.ts} +5 -7
- package/lib/subcommands/{install.js → install.ts} +4 -6
- package/lib/subcommands/{io.js → io.ts} +4 -7
- package/lib/subcommands/{keychain.js → keychain.ts} +30 -32
- package/lib/subcommands/{launch.js → launch.ts} +9 -12
- package/lib/subcommands/{list.js → list.ts} +67 -60
- package/lib/subcommands/{location.js → location.ts} +15 -14
- package/lib/subcommands/{openurl.js → openurl.ts} +6 -7
- package/lib/subcommands/{pbcopy.js → pbcopy.ts} +11 -8
- package/lib/subcommands/{pbpaste.js → pbpaste.ts} +5 -7
- package/lib/subcommands/{privacy.js → privacy.ts} +13 -17
- package/lib/subcommands/{push.js → push.ts} +5 -8
- package/lib/subcommands/{shutdown.js → shutdown.ts} +5 -8
- package/lib/subcommands/{spawn.js → spawn.ts} +21 -17
- package/lib/subcommands/{terminate.js → terminate.ts} +4 -6
- package/lib/subcommands/{ui.js → ui.ts} +19 -28
- package/lib/subcommands/{uninstall.js → uninstall.ts} +4 -6
- package/lib/types.ts +243 -0
- package/package.json +2 -3
- package/index.js +0 -4
- package/lib/helpers.js +0 -37
- package/lib/simctl.js +0 -299
- package/lib/subcommands/appinfo.js +0 -51
- package/lib/subcommands/bootstatus.js +0 -119
package/lib/simctl.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import which from 'which';
|
|
3
|
+
import { log, LOG_PREFIX } from './logger';
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_EXEC_TIMEOUT, getXcrunBinary,
|
|
6
|
+
} from './helpers';
|
|
7
|
+
import { exec as tpExec, SubProcess } from 'teen_process';
|
|
8
|
+
import * as addmediaCommands from './subcommands/addmedia';
|
|
9
|
+
import * as appinfoCommands from './subcommands/appinfo';
|
|
10
|
+
import * as bootCommands from './subcommands/boot';
|
|
11
|
+
import * as bootstatusCommands from './subcommands/bootstatus';
|
|
12
|
+
import * as createCommands from './subcommands/create';
|
|
13
|
+
import * as deleteCommands from './subcommands/delete';
|
|
14
|
+
import * as eraseCommands from './subcommands/erase';
|
|
15
|
+
import * as getappcontainerCommands from './subcommands/get_app_container';
|
|
16
|
+
import * as installCommands from './subcommands/install';
|
|
17
|
+
import * as ioCommands from './subcommands/io';
|
|
18
|
+
import * as keychainCommands from './subcommands/keychain';
|
|
19
|
+
import * as launchCommands from './subcommands/launch';
|
|
20
|
+
import * as listCommands from './subcommands/list';
|
|
21
|
+
import * as openurlCommands from './subcommands/openurl';
|
|
22
|
+
import * as pbcopyCommands from './subcommands/pbcopy';
|
|
23
|
+
import * as pbpasteCommands from './subcommands/pbpaste';
|
|
24
|
+
import * as privacyCommands from './subcommands/privacy';
|
|
25
|
+
import * as pushCommands from './subcommands/push';
|
|
26
|
+
import * as envCommands from './subcommands/getenv';
|
|
27
|
+
import * as shutdownCommands from './subcommands/shutdown';
|
|
28
|
+
import * as spawnCommands from './subcommands/spawn';
|
|
29
|
+
import * as terminateCommands from './subcommands/terminate';
|
|
30
|
+
import * as uiCommands from './subcommands/ui';
|
|
31
|
+
import * as uninstallCommands from './subcommands/uninstall';
|
|
32
|
+
import * as locationCommands from './subcommands/location';
|
|
33
|
+
import type {
|
|
34
|
+
XCRun, ExecOpts, SimctlOpts, ExecResult,
|
|
35
|
+
} from './types';
|
|
36
|
+
|
|
37
|
+
const SIMCTL_ENV_PREFIX = 'SIMCTL_CHILD_';
|
|
38
|
+
|
|
39
|
+
export class Simctl {
|
|
40
|
+
private xcrun: XCRun;
|
|
41
|
+
private execTimeout: number;
|
|
42
|
+
private logErrors: boolean;
|
|
43
|
+
private _udid: string | null;
|
|
44
|
+
private _devicesSetPath: string | null;
|
|
45
|
+
|
|
46
|
+
constructor (opts: SimctlOpts = {}) {
|
|
47
|
+
this.xcrun = _.cloneDeep(opts.xcrun ?? { path: null });
|
|
48
|
+
this.execTimeout = opts.execTimeout ?? DEFAULT_EXEC_TIMEOUT;
|
|
49
|
+
this.logErrors = opts.logErrors ?? true;
|
|
50
|
+
this._udid = opts.udid ?? null;
|
|
51
|
+
this._devicesSetPath = opts.devicesSetPath ?? null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
set udid (value: string | null) {
|
|
55
|
+
this._udid = value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get udid (): string | null {
|
|
59
|
+
return this._udid;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
set devicesSetPath (value: string | null) {
|
|
63
|
+
this._devicesSetPath = value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get devicesSetPath (): string | null {
|
|
67
|
+
return this._devicesSetPath;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param commandName - Optional command name for error message
|
|
72
|
+
* @returns The UDID string
|
|
73
|
+
* @throws {Error} If UDID is not set
|
|
74
|
+
*/
|
|
75
|
+
requireUdid (commandName: string | null = null): string {
|
|
76
|
+
if (!this.udid) {
|
|
77
|
+
throw new Error(`udid is required to be set for ` +
|
|
78
|
+
(commandName ? `the '${commandName}' command` : 'this simctl command'));
|
|
79
|
+
}
|
|
80
|
+
return this.udid;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @returns Promise resolving to the xcrun binary path
|
|
85
|
+
*/
|
|
86
|
+
async requireXcrun (): Promise<string> {
|
|
87
|
+
const xcrunBinary = getXcrunBinary();
|
|
88
|
+
|
|
89
|
+
if (!this.xcrun.path) {
|
|
90
|
+
try {
|
|
91
|
+
this.xcrun.path = await which(xcrunBinary);
|
|
92
|
+
} catch {
|
|
93
|
+
throw new Error(`${xcrunBinary} tool has not been found in PATH. ` +
|
|
94
|
+
`Are Xcode developers tools installed?`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (!this.xcrun.path) {
|
|
98
|
+
throw new Error(`${xcrunBinary} tool path is not set`);
|
|
99
|
+
}
|
|
100
|
+
return this.xcrun.path;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Execute the particular simctl command.
|
|
105
|
+
*
|
|
106
|
+
* @param subcommand - One of available simctl subcommands.
|
|
107
|
+
* Execute `xcrun simctl` in Terminal to see the full list of available subcommands.
|
|
108
|
+
* @param opts - Execution options
|
|
109
|
+
* @return Either the result of teen process's `exec` or
|
|
110
|
+
* `SubProcess` instance depending of `opts.asynchronous` value.
|
|
111
|
+
* @throws {Error} If the simctl subcommand command returns non-zero return code.
|
|
112
|
+
*/
|
|
113
|
+
async exec<T extends ExecOpts> (
|
|
114
|
+
subcommand: string,
|
|
115
|
+
opts?: T
|
|
116
|
+
): Promise<ExecResult<T>> {
|
|
117
|
+
const {
|
|
118
|
+
args: initialArgs = [],
|
|
119
|
+
env: initialEnv = {},
|
|
120
|
+
asynchronous = false,
|
|
121
|
+
encoding,
|
|
122
|
+
logErrors = true,
|
|
123
|
+
architectures,
|
|
124
|
+
timeout,
|
|
125
|
+
} = opts ?? {} as T;
|
|
126
|
+
// run a particular simctl command
|
|
127
|
+
const args = [
|
|
128
|
+
'simctl',
|
|
129
|
+
...(this.devicesSetPath ? ['--set', this.devicesSetPath] : []),
|
|
130
|
+
subcommand,
|
|
131
|
+
...initialArgs
|
|
132
|
+
];
|
|
133
|
+
// Prefix all passed in environment variables with 'SIMCTL_CHILD_', simctl
|
|
134
|
+
// will then pass these to the child (spawned) process.
|
|
135
|
+
const env = _.defaults(
|
|
136
|
+
_.mapKeys(initialEnv,
|
|
137
|
+
(value, key) => _.startsWith(key, SIMCTL_ENV_PREFIX) ? key : `${SIMCTL_ENV_PREFIX}${key}`),
|
|
138
|
+
process.env
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const execOpts: any = {
|
|
142
|
+
env,
|
|
143
|
+
encoding,
|
|
144
|
+
};
|
|
145
|
+
if (!asynchronous) {
|
|
146
|
+
execOpts.timeout = timeout || this.execTimeout;
|
|
147
|
+
}
|
|
148
|
+
const xcrun = await this.requireXcrun();
|
|
149
|
+
try {
|
|
150
|
+
let execArgs: [string, string[], any];
|
|
151
|
+
if (architectures?.length) {
|
|
152
|
+
const archArgs = _.flatMap(
|
|
153
|
+
(_.isArray(architectures) ? architectures : [architectures]).map((arch) => ['-arch', arch])
|
|
154
|
+
);
|
|
155
|
+
execArgs = ['arch', [...archArgs, xcrun, ...args], execOpts];
|
|
156
|
+
} else {
|
|
157
|
+
execArgs = [xcrun, args, execOpts];
|
|
158
|
+
}
|
|
159
|
+
// We know what we are doing here - the type system can't handle the dynamic nature
|
|
160
|
+
return (asynchronous ? new SubProcess(...execArgs) : await tpExec(...execArgs)) as ExecResult<T>;
|
|
161
|
+
} catch (e: any) {
|
|
162
|
+
if (!this.logErrors || !logErrors) {
|
|
163
|
+
// if we don't want to see the errors, just throw and allow the calling
|
|
164
|
+
// code do what it wants
|
|
165
|
+
} else if (e.stderr) {
|
|
166
|
+
const msg = `Error running '${subcommand}': ${e.stderr.trim()}`;
|
|
167
|
+
log.debug(LOG_PREFIX, msg);
|
|
168
|
+
e.message = msg;
|
|
169
|
+
} else {
|
|
170
|
+
log.debug(LOG_PREFIX, e.message);
|
|
171
|
+
}
|
|
172
|
+
throw e;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Extension methods
|
|
177
|
+
addMedia = addmediaCommands.addMedia;
|
|
178
|
+
appInfo = appinfoCommands.appInfo;
|
|
179
|
+
bootDevice = bootCommands.bootDevice;
|
|
180
|
+
startBootMonitor = bootstatusCommands.startBootMonitor;
|
|
181
|
+
createDevice = createCommands.createDevice;
|
|
182
|
+
deleteDevice = deleteCommands.deleteDevice;
|
|
183
|
+
eraseDevice = eraseCommands.eraseDevice;
|
|
184
|
+
getAppContainer = getappcontainerCommands.getAppContainer;
|
|
185
|
+
getEnv = envCommands.getEnv;
|
|
186
|
+
installApp = installCommands.installApp;
|
|
187
|
+
getScreenshot = ioCommands.getScreenshot;
|
|
188
|
+
addRootCertificate = keychainCommands.addRootCertificate;
|
|
189
|
+
addCertificate = keychainCommands.addCertificate;
|
|
190
|
+
resetKeychain = keychainCommands.resetKeychain;
|
|
191
|
+
launchApp = launchCommands.launchApp;
|
|
192
|
+
getDevicesByParsing = listCommands.getDevicesByParsing;
|
|
193
|
+
getDevices = listCommands.getDevices;
|
|
194
|
+
getRuntimeForPlatformVersionViaJson = listCommands.getRuntimeForPlatformVersionViaJson;
|
|
195
|
+
getRuntimeForPlatformVersion = listCommands.getRuntimeForPlatformVersion;
|
|
196
|
+
getDeviceTypes = listCommands.getDeviceTypes;
|
|
197
|
+
list = listCommands.list;
|
|
198
|
+
setLocation = locationCommands.setLocation;
|
|
199
|
+
clearLocation = locationCommands.clearLocation;
|
|
200
|
+
openUrl = openurlCommands.openUrl;
|
|
201
|
+
setPasteboard = pbcopyCommands.setPasteboard;
|
|
202
|
+
getPasteboard = pbpasteCommands.getPasteboard;
|
|
203
|
+
grantPermission = privacyCommands.grantPermission;
|
|
204
|
+
revokePermission = privacyCommands.revokePermission;
|
|
205
|
+
resetPermission = privacyCommands.resetPermission;
|
|
206
|
+
pushNotification = pushCommands.pushNotification;
|
|
207
|
+
shutdownDevice = shutdownCommands.shutdownDevice;
|
|
208
|
+
spawnProcess = spawnCommands.spawnProcess;
|
|
209
|
+
spawnSubProcess = spawnCommands.spawnSubProcess;
|
|
210
|
+
terminateApp = terminateCommands.terminateApp;
|
|
211
|
+
getAppearance = uiCommands.getAppearance;
|
|
212
|
+
setAppearance = uiCommands.setAppearance;
|
|
213
|
+
getIncreaseContrast = uiCommands.getIncreaseContrast;
|
|
214
|
+
setIncreaseContrast = uiCommands.setIncreaseContrast;
|
|
215
|
+
getContentSize = uiCommands.getContentSize;
|
|
216
|
+
setContentSize = uiCommands.setContentSize;
|
|
217
|
+
removeApp = uninstallCommands.removeApp;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export default Simctl;
|
|
221
|
+
|
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Simctl } from '../simctl';
|
|
2
|
+
import type { TeenProcessExecResult } from 'teen_process';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Add the particular media file to Simulator's library.
|
|
5
6
|
* It is required that Simulator is in _booted_ state.
|
|
6
7
|
*
|
|
7
|
-
* @
|
|
8
|
-
* @param {string} filePath - Full path to a media file on the local
|
|
8
|
+
* @param filePath - Full path to a media file on the local
|
|
9
9
|
* file system.
|
|
10
|
-
* @return
|
|
10
|
+
* @return Command execution result.
|
|
11
11
|
* @throws {Error} If the corresponding simctl subcommand command
|
|
12
12
|
* returns non-zero return code.
|
|
13
13
|
* @throws {Error} If the `udid` instance property is unset
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
export async function addMedia (this: Simctl, filePath: string): Promise<TeenProcessExecResult<string>> {
|
|
16
16
|
return await this.exec('addmedia', {
|
|
17
17
|
args: [this.requireUdid('addmedia'), filePath],
|
|
18
18
|
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export default commands;
|
|
19
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Simctl } from '../simctl';
|
|
2
|
+
import type { AppInfo } from '../types';
|
|
3
|
+
import { convertPlistToJson } from '../helpers';
|
|
4
|
+
import _ from 'lodash';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get information about an app installed on the simulator
|
|
8
|
+
*
|
|
9
|
+
* @param bundleId - Bundle identifier of the application
|
|
10
|
+
* @return App info object
|
|
11
|
+
* @throws {Error} If the app is not found
|
|
12
|
+
* @throws {Error} If the corresponding simctl subcommand command
|
|
13
|
+
* returns non-zero return code.
|
|
14
|
+
* @throws {Error} If the `udid` instance property is unset
|
|
15
|
+
*/
|
|
16
|
+
export async function appInfo (this: Simctl, bundleId: string): Promise<AppInfo> {
|
|
17
|
+
const {stdout} = await this.exec('appinfo', {
|
|
18
|
+
args: [this.requireUdid('appinfo'), bundleId],
|
|
19
|
+
});
|
|
20
|
+
let result: any;
|
|
21
|
+
try {
|
|
22
|
+
result = JSON.parse(stdout);
|
|
23
|
+
} catch {
|
|
24
|
+
// If JSON parsing fails, use plutil to convert plist-style output to JSON
|
|
25
|
+
try {
|
|
26
|
+
result = await convertPlistToJson(stdout);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Cannot retrieve app info for ${bundleId}: ${err instanceof Error ? err.message : String(err)}`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!_.isPlainObject(result) || !('ApplicationType' in result)) {
|
|
35
|
+
throw new Error(`App with bundle identifier "${bundleId}" not found. Is it installed?`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return result as AppInfo;
|
|
39
|
+
}
|
|
@@ -1,28 +1,23 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import log,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const commands = {};
|
|
2
|
+
import { log, LOG_PREFIX } from '../logger';
|
|
3
|
+
import type { Simctl } from '../simctl';
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* Boot the particular Simulator if it is not running.
|
|
9
7
|
*
|
|
10
|
-
* @this {import('../simctl').Simctl}
|
|
11
8
|
* @throws {Error} If the corresponding simctl subcommand command
|
|
12
9
|
* returns non-zero return code.
|
|
13
10
|
* @throws {Error} If the `udid` instance property is unset
|
|
14
11
|
*/
|
|
15
|
-
|
|
12
|
+
export async function bootDevice (this: Simctl): Promise<void> {
|
|
16
13
|
try {
|
|
17
14
|
await this.exec('boot', {
|
|
18
15
|
args: [this.requireUdid('boot')]
|
|
19
16
|
});
|
|
20
|
-
} catch (e) {
|
|
17
|
+
} catch (e: any) {
|
|
21
18
|
if (_.includes(e.message, 'Unable to boot device in current state: Booted')) {
|
|
22
19
|
throw e;
|
|
23
20
|
}
|
|
24
21
|
log.debug(LOG_PREFIX, `Simulator already in 'Booted' state. Continuing`);
|
|
25
22
|
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export default commands;
|
|
23
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { log } from '../logger';
|
|
2
|
+
import { waitForCondition } from 'asyncbox';
|
|
3
|
+
import _ from 'lodash';
|
|
4
|
+
import type { Simctl } from '../simctl';
|
|
5
|
+
import type { BootMonitorOptions } from '../types';
|
|
6
|
+
import type { SubProcess } from 'teen_process';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Start monitoring for boot status of the particular Simulator.
|
|
10
|
+
* If onFinished property is not set then the method will block
|
|
11
|
+
* until Simulator booting is completed.
|
|
12
|
+
* The method is only available since Xcode8.
|
|
13
|
+
*
|
|
14
|
+
* @param opts - Monitoring options.
|
|
15
|
+
* @returns The instance of the corresponding monitoring process.
|
|
16
|
+
* @throws {Error} If the Simulator fails to finish booting within the given timeout and onFinished
|
|
17
|
+
* property is not set.
|
|
18
|
+
* @throws {Error} If the `udid` instance property is unset
|
|
19
|
+
*/
|
|
20
|
+
export async function startBootMonitor (
|
|
21
|
+
this: Simctl,
|
|
22
|
+
opts: BootMonitorOptions = {}
|
|
23
|
+
): Promise<SubProcess> {
|
|
24
|
+
const {
|
|
25
|
+
timeout = 240000,
|
|
26
|
+
onWaitingDataMigration,
|
|
27
|
+
onWaitingSystemApp,
|
|
28
|
+
onFinished,
|
|
29
|
+
onError,
|
|
30
|
+
shouldPreboot,
|
|
31
|
+
} = opts;
|
|
32
|
+
const udid = this.requireUdid('bootstatus');
|
|
33
|
+
|
|
34
|
+
const status: string[] = [];
|
|
35
|
+
let isBootingFinished = false;
|
|
36
|
+
let error: Error | null = null;
|
|
37
|
+
let timeoutHandler: NodeJS.Timeout | null = null;
|
|
38
|
+
const args = [udid];
|
|
39
|
+
if (shouldPreboot) {
|
|
40
|
+
args.push('-b');
|
|
41
|
+
}
|
|
42
|
+
const bootMonitor = await this.exec('bootstatus', {
|
|
43
|
+
args,
|
|
44
|
+
asynchronous: true,
|
|
45
|
+
});
|
|
46
|
+
const onStreamLine = (line: string) => {
|
|
47
|
+
status.push(line);
|
|
48
|
+
if (onWaitingDataMigration && line.includes('Waiting on Data Migration')) {
|
|
49
|
+
onWaitingDataMigration();
|
|
50
|
+
} else if (onWaitingSystemApp && line.includes('Waiting on System App')) {
|
|
51
|
+
onWaitingSystemApp();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
for (const streamName of ['stdout', 'stderr']) {
|
|
55
|
+
bootMonitor.on(`line-${streamName}`, onStreamLine);
|
|
56
|
+
}
|
|
57
|
+
bootMonitor.once('exit', (code, signal) => {
|
|
58
|
+
if (timeoutHandler) {
|
|
59
|
+
clearTimeout(timeoutHandler);
|
|
60
|
+
}
|
|
61
|
+
if (code === 0) {
|
|
62
|
+
if (onFinished) {
|
|
63
|
+
onFinished();
|
|
64
|
+
}
|
|
65
|
+
isBootingFinished = true;
|
|
66
|
+
} else {
|
|
67
|
+
const errMessage = _.isEmpty(status)
|
|
68
|
+
? `The simulator booting process has exited with code ${code} by signal ${signal}`
|
|
69
|
+
: status.join('\n');
|
|
70
|
+
error = new Error(errMessage);
|
|
71
|
+
if (onError) {
|
|
72
|
+
onError(error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
await bootMonitor.start(0);
|
|
77
|
+
const stopMonitor = async () => {
|
|
78
|
+
if (bootMonitor.isRunning) {
|
|
79
|
+
try {
|
|
80
|
+
await bootMonitor.stop();
|
|
81
|
+
} catch (e: any) {
|
|
82
|
+
log.warn(e.message);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const start = process.hrtime();
|
|
87
|
+
if (onFinished) {
|
|
88
|
+
timeoutHandler = setTimeout(stopMonitor, timeout);
|
|
89
|
+
} else {
|
|
90
|
+
try {
|
|
91
|
+
await waitForCondition(() => {
|
|
92
|
+
if (error) {
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
return isBootingFinished;
|
|
96
|
+
}, {waitMs: timeout, intervalMs: 500});
|
|
97
|
+
} catch {
|
|
98
|
+
await stopMonitor();
|
|
99
|
+
const [seconds] = process.hrtime(start);
|
|
100
|
+
throw new Error(
|
|
101
|
+
`The simulator ${udid} has failed to finish booting after ${seconds}s. ` +
|
|
102
|
+
`Original status: ${status.join('\n')}`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return bootMonitor;
|
|
107
|
+
}
|
|
108
|
+
|
|
@@ -1,41 +1,38 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import log,
|
|
2
|
+
import { log, LOG_PREFIX } from '../logger';
|
|
3
3
|
import { retryInterval } from 'asyncbox';
|
|
4
4
|
import { SIM_RUNTIME_NAME, normalizeVersion } from '../helpers';
|
|
5
|
-
|
|
5
|
+
import type { Simctl } from '../simctl';
|
|
6
|
+
import type { SimCreationOpts } from '../types';
|
|
6
7
|
|
|
7
8
|
const SIM_RUNTIME_NAME_SUFFIX_IOS = 'iOS';
|
|
8
9
|
const DEFAULT_CREATE_SIMULATOR_TIMEOUT = 10000;
|
|
9
10
|
|
|
10
|
-
const commands = {};
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @typedef {Object} SimCreationOpts
|
|
14
|
-
* @property {string} [platform='iOS'] - Platform name in order to specify runtime such as 'iOS', 'tvOS', 'watchOS'
|
|
15
|
-
* @property {number} [timeout=10000] - The maximum number of milliseconds to wait
|
|
16
|
-
* unit device creation is completed.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
11
|
/**
|
|
20
12
|
* Create Simulator device with given name for the particular
|
|
21
13
|
* platform type and version.
|
|
22
14
|
*
|
|
23
|
-
* @
|
|
24
|
-
* @param
|
|
25
|
-
* @param
|
|
26
|
-
* @param
|
|
27
|
-
* @
|
|
28
|
-
* @return {Promise<string>} The UDID of the newly created device.
|
|
15
|
+
* @param name - The device name to be created.
|
|
16
|
+
* @param deviceTypeId - Device type, for example 'iPhone 6'.
|
|
17
|
+
* @param platformVersion - Platform version, for example '10.3'.
|
|
18
|
+
* @param opts - Simulator options for creating devices.
|
|
19
|
+
* @return The UDID of the newly created device.
|
|
29
20
|
* @throws {Error} If the corresponding simctl subcommand command
|
|
30
21
|
* returns non-zero return code.
|
|
31
22
|
*/
|
|
32
|
-
|
|
23
|
+
export async function createDevice (
|
|
24
|
+
this: Simctl,
|
|
25
|
+
name: string,
|
|
26
|
+
deviceTypeId: string,
|
|
27
|
+
platformVersion: string,
|
|
28
|
+
opts: SimCreationOpts = {}
|
|
29
|
+
): Promise<string> {
|
|
33
30
|
const {
|
|
34
31
|
platform = SIM_RUNTIME_NAME_SUFFIX_IOS,
|
|
35
32
|
timeout = DEFAULT_CREATE_SIMULATOR_TIMEOUT
|
|
36
33
|
} = opts;
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
const runtimeIds: string[] = [];
|
|
39
36
|
|
|
40
37
|
// Try getting runtimeId using JSON flag
|
|
41
38
|
try {
|
|
@@ -45,7 +42,7 @@ commands.createDevice = async function createDevice (name, deviceTypeId, platfor
|
|
|
45
42
|
if (_.isEmpty(runtimeIds)) {
|
|
46
43
|
// at first make sure that the runtime id is the right one
|
|
47
44
|
// in some versions of Xcode it will be a patch version
|
|
48
|
-
let runtimeId;
|
|
45
|
+
let runtimeId: string;
|
|
49
46
|
try {
|
|
50
47
|
runtimeId = await this.getRuntimeForPlatformVersion(platformVersion, platform);
|
|
51
48
|
} catch {
|
|
@@ -56,7 +53,7 @@ commands.createDevice = async function createDevice (name, deviceTypeId, platfor
|
|
|
56
53
|
// get the possible runtimes, which will be iterated over
|
|
57
54
|
|
|
58
55
|
// start with major-minor version
|
|
59
|
-
|
|
56
|
+
const potentialRuntimeIds = [normalizeVersion(runtimeId)];
|
|
60
57
|
if (runtimeId.split('.').length === 3) {
|
|
61
58
|
// add patch version if it exists
|
|
62
59
|
potentialRuntimeIds.push(runtimeId);
|
|
@@ -71,7 +68,7 @@ commands.createDevice = async function createDevice (name, deviceTypeId, platfor
|
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
// go through the runtime ids and try to create a simulator with each
|
|
74
|
-
let udid;
|
|
71
|
+
let udid: string | undefined;
|
|
75
72
|
for (const runtimeId of runtimeIds) {
|
|
76
73
|
log.debug(LOG_PREFIX,
|
|
77
74
|
`Creating simulator with name '${name}', device type id '${deviceTypeId}' and runtime id '${runtimeId}'`);
|
|
@@ -95,17 +92,15 @@ commands.createDevice = async function createDevice (name, deviceTypeId, platfor
|
|
|
95
92
|
// make sure that it gets out of the "Creating" state
|
|
96
93
|
const retries = parseInt(`${timeout / 1000}`, 10);
|
|
97
94
|
await retryInterval(retries, 1000, async () => {
|
|
98
|
-
const devices = _.values(await this.getDevices());
|
|
99
|
-
for (const
|
|
100
|
-
|
|
101
|
-
if (device.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
95
|
+
const devices = _.flatMap(_.values(await this.getDevices()));
|
|
96
|
+
for (const device of devices) {
|
|
97
|
+
if (device.udid === udid) {
|
|
98
|
+
if (device.state === 'Creating') {
|
|
99
|
+
// need to retry
|
|
100
|
+
throw new Error(`Device with udid '${udid}' still being created`);
|
|
101
|
+
} else {
|
|
102
|
+
// stop looking, we're done
|
|
103
|
+
return;
|
|
109
104
|
}
|
|
110
105
|
}
|
|
111
106
|
}
|
|
@@ -113,6 +108,5 @@ commands.createDevice = async function createDevice (name, deviceTypeId, platfor
|
|
|
113
108
|
});
|
|
114
109
|
|
|
115
110
|
return udid;
|
|
116
|
-
}
|
|
111
|
+
}
|
|
117
112
|
|
|
118
|
-
export default commands;
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Simctl } from '../simctl';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Delete the particular Simulator from available devices list.
|
|
5
5
|
*
|
|
6
|
-
* @this {import('../simctl').Simctl}
|
|
7
6
|
* @throws {Error} If the corresponding simctl subcommand command
|
|
8
7
|
* returns non-zero return code.
|
|
9
8
|
* @throws {Error} If the `udid` instance property is unset
|
|
10
9
|
*/
|
|
11
|
-
|
|
10
|
+
export async function deleteDevice (this: Simctl): Promise<void> {
|
|
12
11
|
await this.exec('delete', {
|
|
13
12
|
args: [this.requireUdid('delete')]
|
|
14
13
|
});
|
|
15
|
-
}
|
|
14
|
+
}
|
|
16
15
|
|
|
17
|
-
export default commands;
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import { retryInterval } from 'asyncbox';
|
|
2
|
-
|
|
3
|
-
const commands = {};
|
|
2
|
+
import type { Simctl } from '../simctl';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Reset the content and settings of the particular Simulator.
|
|
7
6
|
* It is required that Simulator is in _shutdown_ state.
|
|
8
7
|
*
|
|
9
|
-
* @
|
|
10
|
-
* @param {number} [timeout=10000] - The maximum number of milliseconds to wait
|
|
8
|
+
* @param timeout - The maximum number of milliseconds to wait
|
|
11
9
|
* unit device reset is completed.
|
|
12
10
|
* @throws {Error} If the corresponding simctl subcommand command
|
|
13
11
|
* returns non-zero return code.
|
|
14
12
|
* @throws {Error} If the `udid` instance property is unset
|
|
15
13
|
*/
|
|
16
|
-
|
|
14
|
+
export async function eraseDevice (this: Simctl, timeout: number = 1000): Promise<void> {
|
|
17
15
|
// retry erase with a sleep in between because it's flakey
|
|
18
16
|
const retries = parseInt(`${timeout / 200}`, 10);
|
|
19
17
|
await retryInterval(retries, 200,
|
|
@@ -21,6 +19,5 @@ commands.eraseDevice = async function eraseDevice (timeout = 1000) {
|
|
|
21
19
|
args: [this.requireUdid('erase')]
|
|
22
20
|
})
|
|
23
21
|
);
|
|
24
|
-
}
|
|
22
|
+
}
|
|
25
23
|
|
|
26
|
-
export default commands;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Simctl } from '../simctl';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Get the full path to the particular application container
|
|
@@ -7,24 +7,26 @@ const commands = {};
|
|
|
7
7
|
* like 'com.apple.springboard'.
|
|
8
8
|
* It is required that Simulator is in _booted_ state.
|
|
9
9
|
*
|
|
10
|
-
* @
|
|
11
|
-
* @param
|
|
12
|
-
* @param {string?} [containerType=null] - Which container type to return. Possible values
|
|
10
|
+
* @param bundleId - Bundle identifier of an application.
|
|
11
|
+
* @param containerType - Which container type to return. Possible values
|
|
13
12
|
* are 'app', 'data', 'groups', '<A specific App Group container>'.
|
|
14
13
|
* The default value is 'app'.
|
|
15
|
-
* @return
|
|
14
|
+
* @return Full path to the given application container on the local
|
|
16
15
|
* file system.
|
|
17
16
|
* @throws {Error} If the corresponding simctl subcommand command
|
|
18
17
|
* returns non-zero return code.
|
|
19
18
|
* @throws {Error} If the `udid` instance property is unset
|
|
20
19
|
*/
|
|
21
|
-
|
|
20
|
+
export async function getAppContainer (
|
|
21
|
+
this: Simctl,
|
|
22
|
+
bundleId: string,
|
|
23
|
+
containerType: string | null = null
|
|
24
|
+
): Promise<string> {
|
|
22
25
|
const args = [this.requireUdid('get_app_container'), bundleId];
|
|
23
26
|
if (containerType) {
|
|
24
27
|
args.push(containerType);
|
|
25
28
|
}
|
|
26
29
|
const {stdout} = await this.exec('get_app_container', {args});
|
|
27
30
|
return (stdout || '').trim();
|
|
28
|
-
}
|
|
31
|
+
}
|
|
29
32
|
|
|
30
|
-
export default commands;
|
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Simctl } from '../simctl';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Retrieves the value of a Simulator environment variable
|
|
5
5
|
*
|
|
6
|
-
* @
|
|
7
|
-
* @
|
|
8
|
-
* @returns {Promise<string|null>} The value of the variable or null if the given variable
|
|
6
|
+
* @param varName - The name of the variable to be retrieved
|
|
7
|
+
* @returns The value of the variable or null if the given variable
|
|
9
8
|
* is not present in the Simulator environment
|
|
10
9
|
* @throws {Error} If there was an error while running the command
|
|
11
10
|
* @throws {Error} If the `udid` instance property is unset
|
|
12
11
|
*/
|
|
13
|
-
|
|
12
|
+
export async function getEnv (this: Simctl, varName: string): Promise<string | null> {
|
|
14
13
|
const {stdout, stderr} = await this.exec('getenv', {
|
|
15
14
|
args: [this.requireUdid('getenv'), varName],
|
|
16
15
|
logErrors: false,
|
|
17
16
|
});
|
|
18
17
|
return stderr ? null : stdout;
|
|
19
|
-
}
|
|
18
|
+
}
|
|
20
19
|
|
|
21
|
-
export default commands;
|