node-simctl 8.0.9 → 8.1.0

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 (156) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/index.d.ts +2 -1
  3. package/build/index.d.ts.map +1 -1
  4. package/build/index.js.map +1 -1
  5. package/build/lib/helpers.d.ts +17 -9
  6. package/build/lib/helpers.d.ts.map +1 -1
  7. package/build/lib/helpers.js +50 -4
  8. package/build/lib/helpers.js.map +1 -1
  9. package/build/lib/logger.d.ts +2 -3
  10. package/build/lib/logger.d.ts.map +1 -1
  11. package/build/lib/logger.js +3 -5
  12. package/build/lib/logger.js.map +1 -1
  13. package/build/lib/simctl.d.ts +82 -188
  14. package/build/lib/simctl.d.ts.map +1 -1
  15. package/build/lib/simctl.js +98 -150
  16. package/build/lib/simctl.js.map +1 -1
  17. package/build/lib/subcommands/addmedia.d.ts +14 -16
  18. package/build/lib/subcommands/addmedia.d.ts.map +1 -1
  19. package/build/lib/subcommands/addmedia.js +5 -7
  20. package/build/lib/subcommands/addmedia.js.map +1 -1
  21. package/build/lib/subcommands/appinfo.d.ts +13 -45
  22. package/build/lib/subcommands/appinfo.d.ts.map +1 -1
  23. package/build/lib/subcommands/appinfo.js +31 -42
  24. package/build/lib/subcommands/appinfo.js.map +1 -1
  25. package/build/lib/subcommands/boot.d.ts +9 -12
  26. package/build/lib/subcommands/boot.d.ts.map +1 -1
  27. package/build/lib/subcommands/boot.js +5 -40
  28. package/build/lib/subcommands/boot.js.map +1 -1
  29. package/build/lib/subcommands/bootstatus.d.ts +16 -56
  30. package/build/lib/subcommands/bootstatus.d.ts.map +1 -1
  31. package/build/lib/subcommands/bootstatus.js +8 -22
  32. package/build/lib/subcommands/bootstatus.js.map +1 -1
  33. package/build/lib/subcommands/create.d.ts +15 -34
  34. package/build/lib/subcommands/create.d.ts.map +1 -1
  35. package/build/lib/subcommands/create.js +23 -66
  36. package/build/lib/subcommands/create.js.map +1 -1
  37. package/build/lib/subcommands/delete.d.ts +9 -12
  38. package/build/lib/subcommands/delete.d.ts.map +1 -1
  39. package/build/lib/subcommands/delete.js +3 -5
  40. package/build/lib/subcommands/delete.js.map +1 -1
  41. package/build/lib/subcommands/erase.d.ts +12 -15
  42. package/build/lib/subcommands/erase.d.ts.map +1 -1
  43. package/build/lib/subcommands/erase.js +4 -6
  44. package/build/lib/subcommands/erase.js.map +1 -1
  45. package/build/lib/subcommands/get_app_container.d.ts +19 -22
  46. package/build/lib/subcommands/get_app_container.d.ts.map +1 -1
  47. package/build/lib/subcommands/get_app_container.js +6 -8
  48. package/build/lib/subcommands/get_app_container.js.map +1 -1
  49. package/build/lib/subcommands/getenv.d.ts +11 -14
  50. package/build/lib/subcommands/getenv.d.ts.map +1 -1
  51. package/build/lib/subcommands/getenv.js +5 -7
  52. package/build/lib/subcommands/getenv.js.map +1 -1
  53. package/build/lib/subcommands/install.d.ts +12 -15
  54. package/build/lib/subcommands/install.d.ts.map +1 -1
  55. package/build/lib/subcommands/install.js +4 -6
  56. package/build/lib/subcommands/install.js.map +1 -1
  57. package/build/lib/subcommands/io.d.ts +12 -15
  58. package/build/lib/subcommands/io.d.ts.map +1 -1
  59. package/build/lib/subcommands/io.js +4 -6
  60. package/build/lib/subcommands/io.js.map +1 -1
  61. package/build/lib/subcommands/keychain.d.ts +37 -55
  62. package/build/lib/subcommands/keychain.d.ts.map +1 -1
  63. package/build/lib/subcommands/keychain.js +16 -25
  64. package/build/lib/subcommands/keychain.js.map +1 -1
  65. package/build/lib/subcommands/launch.d.ts +16 -19
  66. package/build/lib/subcommands/launch.d.ts.map +1 -1
  67. package/build/lib/subcommands/launch.js +9 -11
  68. package/build/lib/subcommands/launch.js.map +1 -1
  69. package/build/lib/subcommands/list.d.ts +94 -125
  70. package/build/lib/subcommands/list.d.ts.map +1 -1
  71. package/build/lib/subcommands/list.js +34 -92
  72. package/build/lib/subcommands/list.js.map +1 -1
  73. package/build/lib/subcommands/location.d.ts +18 -22
  74. package/build/lib/subcommands/location.d.ts.map +1 -1
  75. package/build/lib/subcommands/location.js +11 -13
  76. package/build/lib/subcommands/location.js.map +1 -1
  77. package/build/lib/subcommands/openurl.d.ts +15 -17
  78. package/build/lib/subcommands/openurl.d.ts.map +1 -1
  79. package/build/lib/subcommands/openurl.js +5 -7
  80. package/build/lib/subcommands/openurl.js.map +1 -1
  81. package/build/lib/subcommands/pbcopy.d.ts +14 -17
  82. package/build/lib/subcommands/pbcopy.d.ts.map +1 -1
  83. package/build/lib/subcommands/pbcopy.js +5 -7
  84. package/build/lib/subcommands/pbcopy.js.map +1 -1
  85. package/build/lib/subcommands/pbpaste.d.ts +14 -17
  86. package/build/lib/subcommands/pbpaste.d.ts.map +1 -1
  87. package/build/lib/subcommands/pbpaste.js +5 -7
  88. package/build/lib/subcommands/pbpaste.js.map +1 -1
  89. package/build/lib/subcommands/privacy.d.ts +52 -57
  90. package/build/lib/subcommands/privacy.d.ts.map +1 -1
  91. package/build/lib/subcommands/privacy.js +15 -17
  92. package/build/lib/subcommands/privacy.js.map +1 -1
  93. package/build/lib/subcommands/push.d.ts +22 -25
  94. package/build/lib/subcommands/push.d.ts.map +1 -1
  95. package/build/lib/subcommands/push.js +5 -7
  96. package/build/lib/subcommands/push.js.map +1 -1
  97. package/build/lib/subcommands/shutdown.d.ts +9 -12
  98. package/build/lib/subcommands/shutdown.d.ts.map +1 -1
  99. package/build/lib/subcommands/shutdown.js +5 -40
  100. package/build/lib/subcommands/shutdown.js.map +1 -1
  101. package/build/lib/subcommands/spawn.d.ts +24 -27
  102. package/build/lib/subcommands/spawn.d.ts.map +1 -1
  103. package/build/lib/subcommands/spawn.js +12 -14
  104. package/build/lib/subcommands/spawn.js.map +1 -1
  105. package/build/lib/subcommands/terminate.d.ts +12 -15
  106. package/build/lib/subcommands/terminate.d.ts.map +1 -1
  107. package/build/lib/subcommands/terminate.js +4 -6
  108. package/build/lib/subcommands/terminate.js.map +1 -1
  109. package/build/lib/subcommands/ui.d.ts +97 -105
  110. package/build/lib/subcommands/ui.d.ts.map +1 -1
  111. package/build/lib/subcommands/ui.js +24 -26
  112. package/build/lib/subcommands/ui.js.map +1 -1
  113. package/build/lib/subcommands/uninstall.d.ts +13 -16
  114. package/build/lib/subcommands/uninstall.d.ts.map +1 -1
  115. package/build/lib/subcommands/uninstall.js +4 -6
  116. package/build/lib/subcommands/uninstall.js.map +1 -1
  117. package/build/lib/types.d.ts +231 -0
  118. package/build/lib/types.d.ts.map +1 -0
  119. package/build/lib/types.js +3 -0
  120. package/build/lib/types.js.map +1 -0
  121. package/index.ts +13 -0
  122. package/lib/helpers.ts +81 -0
  123. package/lib/{logger.js → logger.ts} +2 -5
  124. package/lib/simctl.ts +221 -0
  125. package/lib/subcommands/{addmedia.js → addmedia.ts} +6 -8
  126. package/lib/subcommands/appinfo.ts +39 -0
  127. package/lib/subcommands/{boot.js → boot.ts} +5 -10
  128. package/lib/subcommands/bootstatus.ts +108 -0
  129. package/lib/subcommands/{create.js → create.ts} +29 -35
  130. package/lib/subcommands/{delete.js → delete.ts} +3 -5
  131. package/lib/subcommands/{erase.js → erase.ts} +4 -7
  132. package/lib/subcommands/{get_app_container.js → get_app_container.ts} +10 -8
  133. package/lib/subcommands/{getenv.js → getenv.ts} +5 -7
  134. package/lib/subcommands/{install.js → install.ts} +4 -6
  135. package/lib/subcommands/{io.js → io.ts} +4 -7
  136. package/lib/subcommands/{keychain.js → keychain.ts} +30 -32
  137. package/lib/subcommands/{launch.js → launch.ts} +9 -12
  138. package/lib/subcommands/{list.js → list.ts} +67 -60
  139. package/lib/subcommands/{location.js → location.ts} +15 -14
  140. package/lib/subcommands/{openurl.js → openurl.ts} +6 -7
  141. package/lib/subcommands/{pbcopy.js → pbcopy.ts} +11 -8
  142. package/lib/subcommands/{pbpaste.js → pbpaste.ts} +5 -7
  143. package/lib/subcommands/{privacy.js → privacy.ts} +13 -17
  144. package/lib/subcommands/{push.js → push.ts} +5 -8
  145. package/lib/subcommands/{shutdown.js → shutdown.ts} +5 -8
  146. package/lib/subcommands/{spawn.js → spawn.ts} +21 -17
  147. package/lib/subcommands/{terminate.js → terminate.ts} +4 -6
  148. package/lib/subcommands/{ui.js → ui.ts} +19 -28
  149. package/lib/subcommands/{uninstall.js → uninstall.ts} +4 -6
  150. package/lib/types.ts +243 -0
  151. package/package.json +2 -2
  152. package/index.js +0 -4
  153. package/lib/helpers.js +0 -37
  154. package/lib/simctl.js +0 -299
  155. package/lib/subcommands/appinfo.js +0 -51
  156. 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
- const commands = {};
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
- * @this {import('../simctl').Simctl}
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 {Promise<import('teen_process').TeenProcessExecResult>} Command execution result.
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
- commands.addMedia = async function addMedia (filePath) {
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, { LOG_PREFIX } from '../logger';
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
- commands.bootDevice = async function bootDevice () {
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, { LOG_PREFIX } from '../logger';
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
- * @this {import('../simctl').Simctl}
24
- * @param {string} name - The device name to be created.
25
- * @param {string} deviceTypeId - Device type, for example 'iPhone 6'.
26
- * @param {string} platformVersion - Platform version, for example '10.3'.
27
- * @param {SimCreationOpts} [opts={}] - Simulator options for creating devices.
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
- commands.createDevice = async function createDevice (name, deviceTypeId, platformVersion, opts = {}) {
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
- let runtimeIds = [];
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
- let potentialRuntimeIds = [normalizeVersion(runtimeId)];
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 deviceArr of _.values(devices)) {
100
- for (const device of deviceArr) {
101
- if (device.udid === udid) {
102
- if (device.state === 'Creating') {
103
- // need to retry
104
- throw new Error(`Device with udid '${udid}' still being created`);
105
- } else {
106
- // stop looking, we're done
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
- const commands = {};
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
- commands.deleteDevice = async function deleteDevice () {
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
- * @this {import('../simctl').Simctl}
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
- commands.eraseDevice = async function eraseDevice (timeout = 1000) {
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
- const commands = {};
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
- * @this {import('../simctl').Simctl}
11
- * @param {string} bundleId - Bundle identifier of an application.
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 {Promise<string>} Full path to the given application container on the local
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
- commands.getAppContainer = async function getAppContainer (bundleId, containerType = null) {
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
- const commands = {};
1
+ import type { Simctl } from '../simctl';
2
2
 
3
3
  /**
4
4
  * Retrieves the value of a Simulator environment variable
5
5
  *
6
- * @this {import('../simctl').Simctl}
7
- * @param {string} varName - The name of the variable to be retrieved
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
- commands.getEnv = async function getEnv (varName) {
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;