appium-ios-simulator 8.0.13 → 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.
- package/CHANGELOG.md +6 -0
- package/build/lib/defaults-utils.d.ts +24 -24
- package/build/lib/defaults-utils.d.ts.map +1 -1
- package/build/lib/defaults-utils.js +61 -65
- package/build/lib/defaults-utils.js.map +1 -1
- package/build/lib/extensions/applications.d.ts.map +1 -1
- package/build/lib/extensions/applications.js +6 -7
- package/build/lib/extensions/applications.js.map +1 -1
- package/build/lib/extensions/biometric.d.ts.map +1 -1
- package/build/lib/extensions/biometric.js +3 -6
- package/build/lib/extensions/biometric.js.map +1 -1
- package/build/lib/extensions/keychain.d.ts.map +1 -1
- package/build/lib/extensions/keychain.js +7 -6
- package/build/lib/extensions/keychain.js.map +1 -1
- package/build/lib/extensions/permissions.d.ts.map +1 -1
- package/build/lib/extensions/permissions.js +16 -18
- package/build/lib/extensions/permissions.js.map +1 -1
- package/build/lib/extensions/safari.d.ts.map +1 -1
- package/build/lib/extensions/safari.js +2 -4
- package/build/lib/extensions/safari.js.map +1 -1
- package/build/lib/extensions/settings.d.ts.map +1 -1
- package/build/lib/extensions/settings.js +52 -50
- package/build/lib/extensions/settings.js.map +1 -1
- package/build/lib/simulator-xcode-14.d.ts +37 -37
- package/build/lib/simulator-xcode-14.d.ts.map +1 -1
- package/build/lib/simulator-xcode-14.js +57 -59
- package/build/lib/simulator-xcode-14.js.map +1 -1
- package/build/lib/simulator-xcode-15.d.ts.map +1 -1
- package/build/lib/simulator-xcode-15.js +3 -5
- package/build/lib/simulator-xcode-15.js.map +1 -1
- package/build/lib/types.d.ts +8 -8
- package/build/lib/types.d.ts.map +1 -1
- package/build/lib/utils.d.ts +11 -3
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +50 -35
- package/build/lib/utils.js.map +1 -1
- package/lib/defaults-utils.ts +69 -68
- package/lib/extensions/applications.ts +6 -7
- package/lib/extensions/biometric.ts +3 -3
- package/lib/extensions/keychain.ts +11 -6
- package/lib/extensions/permissions.ts +17 -19
- package/lib/extensions/safari.ts +2 -4
- package/lib/extensions/settings.ts +71 -62
- package/lib/simulator-xcode-14.ts +70 -71
- package/lib/simulator-xcode-15.ts +3 -5
- package/lib/types.ts +9 -9
- package/lib/utils.ts +52 -37
- package/package.json +1 -5
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import {NSUserDefaults, generateDefaultsCommandArgs} from '../defaults-utils';
|
|
3
|
-
import B from 'bluebird';
|
|
4
2
|
import path from 'node:path';
|
|
5
3
|
import {exec} from 'teen_process';
|
|
6
4
|
import AsyncLock from 'async-lock';
|
|
7
5
|
import {fs} from '@appium/support';
|
|
6
|
+
import {isPlainObject} from '../utils';
|
|
8
7
|
import type {
|
|
9
8
|
CoreSimulator,
|
|
10
9
|
HasSettings,
|
|
@@ -90,12 +89,12 @@ export async function updateSettings(
|
|
|
90
89
|
domain: string,
|
|
91
90
|
updates: StringRecord,
|
|
92
91
|
): Promise<boolean> {
|
|
93
|
-
if (
|
|
92
|
+
if (Object.keys(updates).length === 0) {
|
|
94
93
|
return false;
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
const argChunks = generateDefaultsCommandArgs(updates);
|
|
98
|
-
await
|
|
97
|
+
await Promise.all(
|
|
99
98
|
argChunks.map((args) => this.simctl.spawnProcess(['defaults', 'write', domain, ...args])),
|
|
100
99
|
);
|
|
101
100
|
return true;
|
|
@@ -111,7 +110,7 @@ export async function updateSettings(
|
|
|
111
110
|
* @since Xcode SDK 11.4
|
|
112
111
|
*/
|
|
113
112
|
export async function setAppearance(this: CoreSimulatorWithSettings, value: string): Promise<void> {
|
|
114
|
-
await this.simctl.setAppearance(
|
|
113
|
+
await this.simctl.setAppearance(value.toLowerCase());
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
/**
|
|
@@ -203,14 +202,14 @@ export async function configureLocalization(
|
|
|
203
202
|
this: CoreSimulatorWithSettings,
|
|
204
203
|
opts: LocalizationOptions = {},
|
|
205
204
|
): Promise<boolean> {
|
|
206
|
-
if (
|
|
205
|
+
if (Object.keys(opts).length === 0) {
|
|
207
206
|
return false;
|
|
208
207
|
}
|
|
209
208
|
|
|
210
209
|
const {language, locale, keyboard} = opts;
|
|
211
210
|
const globalPrefs: Record<string, any> = {};
|
|
212
211
|
let keyboardId: string | null = null;
|
|
213
|
-
if (
|
|
212
|
+
if (isPlainObject(keyboard)) {
|
|
214
213
|
const {name, layout, hardware} = keyboard;
|
|
215
214
|
if (!name) {
|
|
216
215
|
throw new Error(`The 'keyboard' field must have a valid name set`);
|
|
@@ -224,14 +223,14 @@ export async function configureLocalization(
|
|
|
224
223
|
}
|
|
225
224
|
globalPrefs.AppleKeyboards = [keyboardId];
|
|
226
225
|
}
|
|
227
|
-
if (
|
|
226
|
+
if (isPlainObject(language)) {
|
|
228
227
|
const {name} = language;
|
|
229
228
|
if (!name) {
|
|
230
229
|
throw new Error(`The 'language' field must have a valid name set`);
|
|
231
230
|
}
|
|
232
231
|
globalPrefs.AppleLanguages = [name];
|
|
233
232
|
}
|
|
234
|
-
if (
|
|
233
|
+
if (isPlainObject(locale)) {
|
|
235
234
|
const {name, calendar} = locale;
|
|
236
235
|
if (!name) {
|
|
237
236
|
throw new Error(`The 'locale' field must have a valid name set`);
|
|
@@ -242,7 +241,7 @@ export async function configureLocalization(
|
|
|
242
241
|
}
|
|
243
242
|
globalPrefs.AppleLocale = localeId;
|
|
244
243
|
}
|
|
245
|
-
if (
|
|
244
|
+
if (Object.keys(globalPrefs).length === 0) {
|
|
246
245
|
return false;
|
|
247
246
|
}
|
|
248
247
|
|
|
@@ -265,7 +264,7 @@ export async function configureLocalization(
|
|
|
265
264
|
}
|
|
266
265
|
|
|
267
266
|
const argChunks = generateDefaultsCommandArgs(globalPrefs, true);
|
|
268
|
-
await
|
|
267
|
+
await Promise.all(
|
|
269
268
|
argChunks.map((args) =>
|
|
270
269
|
this.simctl.spawnProcess(['defaults', 'write', GLOBAL_PREFS_PLIST, ...args]),
|
|
271
270
|
),
|
|
@@ -280,7 +279,7 @@ export async function configureLocalization(
|
|
|
280
279
|
},
|
|
281
280
|
true,
|
|
282
281
|
);
|
|
283
|
-
await
|
|
282
|
+
await Promise.all(
|
|
284
283
|
argChunks.map((args) =>
|
|
285
284
|
this.simctl.spawnProcess(['defaults', 'write', 'com.apple.Preferences', ...args]),
|
|
286
285
|
),
|
|
@@ -288,7 +287,7 @@ export async function configureLocalization(
|
|
|
288
287
|
}
|
|
289
288
|
|
|
290
289
|
if (globalPrefs.AppleLanguages) {
|
|
291
|
-
if (
|
|
290
|
+
if (JSON.stringify(previousAppleLanguages) === JSON.stringify(globalPrefs.AppleLanguages)) {
|
|
292
291
|
this.log.info(
|
|
293
292
|
`The 'AppleLanguages' preference is already set to '${globalPrefs.AppleLanguages}'. ` +
|
|
294
293
|
`Skipping services reset`,
|
|
@@ -303,7 +302,7 @@ export async function configureLocalization(
|
|
|
303
302
|
`${SERVICES_FOR_TRANSLATION}. This might have unexpected side effects, ` +
|
|
304
303
|
`see https://github.com/appium/appium/issues/19440 for more details`,
|
|
305
304
|
);
|
|
306
|
-
await
|
|
305
|
+
await Promise.all(
|
|
307
306
|
SERVICES_FOR_TRANSLATION.map((arg) => this.simctl.spawnProcess(['launchctl', 'stop', arg])),
|
|
308
307
|
);
|
|
309
308
|
}
|
|
@@ -343,12 +342,12 @@ export async function updatePreferences(
|
|
|
343
342
|
devicePrefs: DevicePreferences = {},
|
|
344
343
|
commonPrefs: CommonPreferences = {},
|
|
345
344
|
): Promise<boolean> {
|
|
346
|
-
if (
|
|
345
|
+
if (Object.keys(devicePrefs).length > 0) {
|
|
347
346
|
this.log.debug(
|
|
348
347
|
`Setting preferences of ${this.udid} Simulator to ${JSON.stringify(devicePrefs)}`,
|
|
349
348
|
);
|
|
350
349
|
}
|
|
351
|
-
if (
|
|
350
|
+
if (Object.keys(commonPrefs).length > 0) {
|
|
352
351
|
this.log.debug(`Setting common Simulator preferences to ${JSON.stringify(commonPrefs)}`);
|
|
353
352
|
}
|
|
354
353
|
const homeFolderPath = process.env.HOME;
|
|
@@ -368,16 +367,16 @@ export async function updatePreferences(
|
|
|
368
367
|
);
|
|
369
368
|
return await PREFERENCES_PLIST_GUARD.acquire(this.constructor.name, async () => {
|
|
370
369
|
const defaults = new NSUserDefaults(plistPath);
|
|
371
|
-
const prefsToUpdate =
|
|
370
|
+
const prefsToUpdate = {...commonPrefs};
|
|
372
371
|
try {
|
|
373
|
-
if (
|
|
372
|
+
if (Object.keys(devicePrefs).length > 0) {
|
|
374
373
|
let existingDevicePrefs: any;
|
|
375
374
|
const udidKey = this.udid.toUpperCase();
|
|
376
375
|
if (await fs.exists(plistPath)) {
|
|
377
376
|
const currentPlistContent = await defaults.asJson();
|
|
378
377
|
if (
|
|
379
|
-
|
|
380
|
-
|
|
378
|
+
isPlainObject(currentPlistContent.DevicePreferences) &&
|
|
379
|
+
isPlainObject(currentPlistContent.DevicePreferences[udidKey])
|
|
381
380
|
) {
|
|
382
381
|
existingDevicePrefs = currentPlistContent.DevicePreferences[udidKey];
|
|
383
382
|
}
|
|
@@ -427,22 +426,26 @@ export function compileSimulatorPreferences(
|
|
|
427
426
|
AttachBootedOnStart: true,
|
|
428
427
|
};
|
|
429
428
|
const devicePreferences: DevicePreferences = opts.devicePreferences
|
|
430
|
-
?
|
|
429
|
+
? structuredClone(opts.devicePreferences)
|
|
431
430
|
: {};
|
|
432
431
|
if (scaleFactor) {
|
|
433
432
|
devicePreferences.SimulatorWindowLastScale = parseFloat(scaleFactor);
|
|
434
433
|
}
|
|
435
|
-
if (
|
|
434
|
+
if (
|
|
435
|
+
typeof connectHardwareKeyboard === 'boolean' ||
|
|
436
|
+
connectHardwareKeyboard === null ||
|
|
437
|
+
connectHardwareKeyboard === undefined
|
|
438
|
+
) {
|
|
436
439
|
devicePreferences.ConnectHardwareKeyboard = connectHardwareKeyboard ?? false;
|
|
437
440
|
commonPreferences.ConnectHardwareKeyboard = connectHardwareKeyboard ?? false;
|
|
438
441
|
}
|
|
439
|
-
if (
|
|
442
|
+
if (typeof tracePointer === 'boolean') {
|
|
440
443
|
commonPreferences.ShowSingleTouches = tracePointer;
|
|
441
444
|
commonPreferences.ShowPinches = tracePointer;
|
|
442
445
|
commonPreferences.ShowPinchPivotPoint = tracePointer;
|
|
443
446
|
commonPreferences.HighlightEdgeGestures = tracePointer;
|
|
444
447
|
}
|
|
445
|
-
switch (
|
|
448
|
+
switch ((pasteboardAutomaticSync || '').toLowerCase()) {
|
|
446
449
|
case 'on':
|
|
447
450
|
commonPreferences.PasteboardAutomaticSync = true;
|
|
448
451
|
break;
|
|
@@ -475,52 +478,58 @@ export function verifyDevicePreferences(
|
|
|
475
478
|
this: CoreSimulatorWithSettings,
|
|
476
479
|
prefs: DevicePreferences = {},
|
|
477
480
|
): void {
|
|
478
|
-
if (
|
|
481
|
+
if (Object.keys(prefs).length === 0) {
|
|
479
482
|
return;
|
|
480
483
|
}
|
|
481
484
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
485
|
+
// https://regex101.com/r/2ZXOij/2
|
|
486
|
+
const simulatorWindowCenterPattern = /{-?\d+(\.\d+)?,-?\d+(\.\d+)?}/;
|
|
487
|
+
const acceptableSimulatorWindowOrientations = [
|
|
488
|
+
'Portrait',
|
|
489
|
+
'LandscapeLeft',
|
|
490
|
+
'PortraitUpsideDown',
|
|
491
|
+
'LandscapeRight',
|
|
492
|
+
];
|
|
493
|
+
|
|
494
|
+
if (
|
|
495
|
+
prefs.SimulatorWindowLastScale !== undefined &&
|
|
496
|
+
(typeof prefs.SimulatorWindowLastScale !== 'number' || prefs.SimulatorWindowLastScale <= 0)
|
|
497
|
+
) {
|
|
498
|
+
throw this.log.errorWithException(
|
|
499
|
+
`SimulatorWindowLastScale is expected to be a positive float value. ` +
|
|
500
|
+
`'${prefs.SimulatorWindowLastScale}' is assigned instead.`,
|
|
501
|
+
);
|
|
489
502
|
}
|
|
490
503
|
|
|
491
|
-
if (
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
`'${prefs.SimulatorWindowCenter}' is assigned instead.`,
|
|
501
|
-
);
|
|
502
|
-
}
|
|
504
|
+
if (
|
|
505
|
+
prefs.SimulatorWindowCenter !== undefined &&
|
|
506
|
+
(typeof prefs.SimulatorWindowCenter !== 'string' ||
|
|
507
|
+
!simulatorWindowCenterPattern.test(prefs.SimulatorWindowCenter))
|
|
508
|
+
) {
|
|
509
|
+
throw this.log.errorWithException(
|
|
510
|
+
`SimulatorWindowCenter is expected to match "{floatXPosition,floatYPosition}" format (without spaces). ` +
|
|
511
|
+
`'${prefs.SimulatorWindowCenter}' is assigned instead.`,
|
|
512
|
+
);
|
|
503
513
|
}
|
|
504
514
|
|
|
505
|
-
if (
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
!prefs.SimulatorWindowOrientation
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
`SimulatorWindowOrientation is
|
|
513
|
-
|
|
514
|
-
);
|
|
515
|
-
}
|
|
515
|
+
if (
|
|
516
|
+
prefs.SimulatorWindowOrientation !== undefined &&
|
|
517
|
+
(!prefs.SimulatorWindowOrientation ||
|
|
518
|
+
!acceptableSimulatorWindowOrientations.includes(prefs.SimulatorWindowOrientation))
|
|
519
|
+
) {
|
|
520
|
+
throw this.log.errorWithException(
|
|
521
|
+
`SimulatorWindowOrientation is expected to be one of ${acceptableSimulatorWindowOrientations}. ` +
|
|
522
|
+
`'${prefs.SimulatorWindowOrientation}' is assigned instead.`,
|
|
523
|
+
);
|
|
516
524
|
}
|
|
517
525
|
|
|
518
|
-
if (
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
526
|
+
if (
|
|
527
|
+
prefs.SimulatorWindowRotationAngle !== undefined &&
|
|
528
|
+
typeof prefs.SimulatorWindowRotationAngle !== 'number'
|
|
529
|
+
) {
|
|
530
|
+
throw this.log.errorWithException(
|
|
531
|
+
`SimulatorWindowRotationAngle is expected to be a valid number. ` +
|
|
532
|
+
`'${prefs.SimulatorWindowRotationAngle}' is assigned instead.`,
|
|
533
|
+
);
|
|
525
534
|
}
|
|
526
535
|
}
|
|
@@ -5,9 +5,7 @@ import {exec} from 'teen_process';
|
|
|
5
5
|
import {log as defaultLog} from './logger';
|
|
6
6
|
import EventEmitter from 'node:events';
|
|
7
7
|
import AsyncLock from 'async-lock';
|
|
8
|
-
import _ from 'lodash';
|
|
9
8
|
import path from 'node:path';
|
|
10
|
-
import B from 'bluebird';
|
|
11
9
|
import {getPath as getXcodePath} from 'appium-xcode';
|
|
12
10
|
import {Simctl} from 'node-simctl';
|
|
13
11
|
import * as appExtensions from './extensions/applications';
|
|
@@ -57,6 +55,57 @@ export class SimulatorXcode14
|
|
|
57
55
|
_keychainsBackupPath: string | null | undefined;
|
|
58
56
|
_platformVersion: string | null | undefined;
|
|
59
57
|
_webInspectorSocket: string | null | undefined;
|
|
58
|
+
|
|
59
|
+
// Extension methods
|
|
60
|
+
installApp = appExtensions.installApp;
|
|
61
|
+
getUserInstalledBundleIdsByBundleName = appExtensions.getUserInstalledBundleIdsByBundleName;
|
|
62
|
+
isAppInstalled = appExtensions.isAppInstalled;
|
|
63
|
+
removeApp = appExtensions.removeApp;
|
|
64
|
+
launchApp = appExtensions.launchApp;
|
|
65
|
+
terminateApp = appExtensions.terminateApp;
|
|
66
|
+
isAppRunning = appExtensions.isAppRunning;
|
|
67
|
+
scrubApp = appExtensions.scrubApp;
|
|
68
|
+
|
|
69
|
+
openUrl = safariExtensions.openUrl;
|
|
70
|
+
scrubSafari = safariExtensions.scrubSafari;
|
|
71
|
+
updateSafariSettings = safariExtensions.updateSafariSettings;
|
|
72
|
+
getWebInspectorSocket = safariExtensions.getWebInspectorSocket as unknown as () => Promise<
|
|
73
|
+
string | null
|
|
74
|
+
>;
|
|
75
|
+
|
|
76
|
+
isBiometricEnrolled = biometricExtensions.isBiometricEnrolled;
|
|
77
|
+
enrollBiometric = biometricExtensions.enrollBiometric;
|
|
78
|
+
sendBiometricMatch = biometricExtensions.sendBiometricMatch;
|
|
79
|
+
|
|
80
|
+
backupKeychains = keychainExtensions.backupKeychains as unknown as () => Promise<boolean>;
|
|
81
|
+
restoreKeychains = keychainExtensions.restoreKeychains as unknown as (
|
|
82
|
+
excludePatterns: string[],
|
|
83
|
+
) => Promise<boolean>;
|
|
84
|
+
clearKeychains = keychainExtensions.clearKeychains;
|
|
85
|
+
|
|
86
|
+
setGeolocation = geolocationExtensions.setGeolocation;
|
|
87
|
+
|
|
88
|
+
shake = miscExtensions.shake;
|
|
89
|
+
addCertificate = miscExtensions.addCertificate;
|
|
90
|
+
pushNotification = miscExtensions.pushNotification;
|
|
91
|
+
|
|
92
|
+
setPermission = permissionsExtensions.setPermission;
|
|
93
|
+
setPermissions = permissionsExtensions.setPermissions;
|
|
94
|
+
getPermission = permissionsExtensions.getPermission;
|
|
95
|
+
|
|
96
|
+
updateSettings = settingsExtensions.updateSettings;
|
|
97
|
+
setAppearance = settingsExtensions.setAppearance;
|
|
98
|
+
getAppearance = settingsExtensions.getAppearance;
|
|
99
|
+
setIncreaseContrast = settingsExtensions.setIncreaseContrast;
|
|
100
|
+
getIncreaseContrast = settingsExtensions.getIncreaseContrast;
|
|
101
|
+
setContentSize = settingsExtensions.setContentSize;
|
|
102
|
+
getContentSize = settingsExtensions.getContentSize;
|
|
103
|
+
configureLocalization = settingsExtensions.configureLocalization;
|
|
104
|
+
setAutoFillPasswords = settingsExtensions.setAutoFillPasswords;
|
|
105
|
+
setReduceMotion = settingsExtensions.setReduceMotion;
|
|
106
|
+
setReduceTransparency = settingsExtensions.setReduceTransparency;
|
|
107
|
+
disableKeyboardIntroduction = settingsExtensions.disableKeyboardIntroduction;
|
|
108
|
+
|
|
60
109
|
private readonly _udid: string;
|
|
61
110
|
private readonly _simctl: Simctl;
|
|
62
111
|
private readonly _xcodeVersion: XcodeVersion;
|
|
@@ -193,7 +242,7 @@ export class SimulatorXcode14
|
|
|
193
242
|
*/
|
|
194
243
|
async stat(): Promise<DeviceStat | StringRecord<never>> {
|
|
195
244
|
const devices = await this.simctl.getDevices();
|
|
196
|
-
for (const [sdk, deviceArr] of
|
|
245
|
+
for (const [sdk, deviceArr] of Object.entries(devices)) {
|
|
197
246
|
for (const device of deviceArr as any[]) {
|
|
198
247
|
if (device.udid === this.udid) {
|
|
199
248
|
device.sdk = sdk;
|
|
@@ -246,7 +295,7 @@ export class SimulatorXcode14
|
|
|
246
295
|
await this.simctl.getEnv('dummy');
|
|
247
296
|
return false;
|
|
248
297
|
} catch (e: any) {
|
|
249
|
-
return
|
|
298
|
+
return String(e.stderr).includes('Current state: Shutdown');
|
|
250
299
|
}
|
|
251
300
|
}
|
|
252
301
|
|
|
@@ -276,7 +325,7 @@ export class SimulatorXcode14
|
|
|
276
325
|
* @returns True if UI client is running or false otherwise.
|
|
277
326
|
*/
|
|
278
327
|
async isUIClientRunning(): Promise<boolean> {
|
|
279
|
-
return
|
|
328
|
+
return (await this.getUIClientPid()) !== null;
|
|
280
329
|
}
|
|
281
330
|
|
|
282
331
|
/**
|
|
@@ -307,14 +356,14 @@ export class SimulatorXcode14
|
|
|
307
356
|
shouldPreboot: true,
|
|
308
357
|
});
|
|
309
358
|
try {
|
|
310
|
-
await new
|
|
359
|
+
await new Promise<void>((resolve, reject) => {
|
|
311
360
|
// Historically this call was always asynchronous,
|
|
312
361
|
// e.g. it was not waiting until Simulator is fully booted.
|
|
313
362
|
// So we preserve that behavior, and if no errors are received for a while
|
|
314
363
|
// then we assume the Simulator booting is still in progress.
|
|
315
364
|
setTimeout(resolve, 3000);
|
|
316
365
|
bootEventsEmitter.once('failure', (err: Error) => {
|
|
317
|
-
if (
|
|
366
|
+
if (String(err?.message).includes('state: Booted')) {
|
|
318
367
|
resolve();
|
|
319
368
|
} else {
|
|
320
369
|
reject(err);
|
|
@@ -402,16 +451,16 @@ export class SimulatorXcode14
|
|
|
402
451
|
* @param opts - Simulator startup options.
|
|
403
452
|
*/
|
|
404
453
|
async startUIClient(opts: StartUiClientOptions = {}): Promise<void> {
|
|
405
|
-
|
|
406
|
-
_.defaultsDeep(opts, {
|
|
454
|
+
const startUiOpts = {
|
|
407
455
|
startupTimeout: this.startupTimeout,
|
|
408
|
-
|
|
456
|
+
...opts,
|
|
457
|
+
};
|
|
409
458
|
|
|
410
459
|
const simulatorApp = path.resolve(await getXcodePath(), 'Applications', SIMULATOR_APP_NAME);
|
|
411
460
|
const args = ['-Fn', simulatorApp];
|
|
412
461
|
this.log.info(`Starting Simulator UI: ${util.quote(['open', ...args])}`);
|
|
413
462
|
try {
|
|
414
|
-
await exec('open', args, {timeout:
|
|
463
|
+
await exec('open', args, {timeout: startUiOpts.startupTimeout});
|
|
415
464
|
} catch (err: any) {
|
|
416
465
|
throw new Error(
|
|
417
466
|
`Got an unexpected error while opening Simulator UI: ` + err.stderr ||
|
|
@@ -428,21 +477,21 @@ export class SimulatorXcode14
|
|
|
428
477
|
* @param opts - One or more of available Simulator options.
|
|
429
478
|
*/
|
|
430
479
|
async run(opts: RunOptions = {}): Promise<void> {
|
|
431
|
-
|
|
432
|
-
_.defaultsDeep(opts, {
|
|
480
|
+
const runOpts: RunOptions = {
|
|
433
481
|
isHeadless: false,
|
|
434
482
|
startupTimeout: this.startupTimeout,
|
|
435
|
-
|
|
483
|
+
...structuredClone(opts),
|
|
484
|
+
};
|
|
436
485
|
|
|
437
486
|
const [devicePreferences, commonPreferences] =
|
|
438
|
-
settingsExtensions.compileSimulatorPreferences.bind(this)(
|
|
487
|
+
settingsExtensions.compileSimulatorPreferences.bind(this)(runOpts);
|
|
439
488
|
await settingsExtensions.updatePreferences.bind(this)(devicePreferences, commonPreferences);
|
|
440
489
|
|
|
441
490
|
const timer = new timing.Timer().start();
|
|
442
491
|
const shouldWaitForBoot = await STARTUP_LOCK.acquire(this.uiClientBundleId, async () => {
|
|
443
492
|
const isServerRunning = await this.isRunning();
|
|
444
493
|
const uiClientPid = await this.getUIClientPid();
|
|
445
|
-
if (
|
|
494
|
+
if (runOpts.isHeadless) {
|
|
446
495
|
if (isServerRunning && !uiClientPid) {
|
|
447
496
|
this.log.info(`Simulator with UDID '${this.udid}' is already booted in headless mode.`);
|
|
448
497
|
return false;
|
|
@@ -485,19 +534,19 @@ export class SimulatorXcode14
|
|
|
485
534
|
);
|
|
486
535
|
await this.shutdown({timeout: SIMULATOR_SHUTDOWN_TIMEOUT});
|
|
487
536
|
}
|
|
488
|
-
await this.launchWindow(Boolean(uiClientPid),
|
|
537
|
+
await this.launchWindow(Boolean(uiClientPid), runOpts);
|
|
489
538
|
}
|
|
490
539
|
return true;
|
|
491
540
|
});
|
|
492
541
|
|
|
493
|
-
if (shouldWaitForBoot &&
|
|
494
|
-
await this.waitForBoot(
|
|
542
|
+
if (shouldWaitForBoot && runOpts.startupTimeout) {
|
|
543
|
+
await this.waitForBoot(runOpts.startupTimeout);
|
|
495
544
|
this.log.info(
|
|
496
545
|
`Simulator with UDID ${this.udid} booted in ${timer.getDuration().asSeconds.toFixed(3)}s`,
|
|
497
546
|
);
|
|
498
547
|
}
|
|
499
548
|
|
|
500
|
-
(async () => {
|
|
549
|
+
void (async () => {
|
|
501
550
|
try {
|
|
502
551
|
await this.disableKeyboardIntroduction();
|
|
503
552
|
} catch (e: any) {
|
|
@@ -573,7 +622,7 @@ export class SimulatorXcode14
|
|
|
573
622
|
|
|
574
623
|
const result: ProcessInfo[] = [];
|
|
575
624
|
for (const line of stdout.split('\n')) {
|
|
576
|
-
const trimmedLine =
|
|
625
|
+
const trimmedLine = line.trim();
|
|
577
626
|
if (!trimmedLine) {
|
|
578
627
|
continue;
|
|
579
628
|
}
|
|
@@ -616,54 +665,4 @@ export class SimulatorXcode14
|
|
|
616
665
|
'LaunchDaemons',
|
|
617
666
|
);
|
|
618
667
|
}
|
|
619
|
-
|
|
620
|
-
// Extension methods
|
|
621
|
-
installApp = appExtensions.installApp;
|
|
622
|
-
getUserInstalledBundleIdsByBundleName = appExtensions.getUserInstalledBundleIdsByBundleName;
|
|
623
|
-
isAppInstalled = appExtensions.isAppInstalled;
|
|
624
|
-
removeApp = appExtensions.removeApp;
|
|
625
|
-
launchApp = appExtensions.launchApp;
|
|
626
|
-
terminateApp = appExtensions.terminateApp;
|
|
627
|
-
isAppRunning = appExtensions.isAppRunning;
|
|
628
|
-
scrubApp = appExtensions.scrubApp;
|
|
629
|
-
|
|
630
|
-
openUrl = safariExtensions.openUrl;
|
|
631
|
-
scrubSafari = safariExtensions.scrubSafari;
|
|
632
|
-
updateSafariSettings = safariExtensions.updateSafariSettings;
|
|
633
|
-
getWebInspectorSocket = safariExtensions.getWebInspectorSocket as unknown as () => Promise<
|
|
634
|
-
string | null
|
|
635
|
-
>;
|
|
636
|
-
|
|
637
|
-
isBiometricEnrolled = biometricExtensions.isBiometricEnrolled;
|
|
638
|
-
enrollBiometric = biometricExtensions.enrollBiometric;
|
|
639
|
-
sendBiometricMatch = biometricExtensions.sendBiometricMatch;
|
|
640
|
-
|
|
641
|
-
backupKeychains = keychainExtensions.backupKeychains as unknown as () => Promise<boolean>;
|
|
642
|
-
restoreKeychains = keychainExtensions.restoreKeychains as unknown as (
|
|
643
|
-
excludePatterns: string[],
|
|
644
|
-
) => Promise<boolean>;
|
|
645
|
-
clearKeychains = keychainExtensions.clearKeychains;
|
|
646
|
-
|
|
647
|
-
setGeolocation = geolocationExtensions.setGeolocation;
|
|
648
|
-
|
|
649
|
-
shake = miscExtensions.shake;
|
|
650
|
-
addCertificate = miscExtensions.addCertificate;
|
|
651
|
-
pushNotification = miscExtensions.pushNotification;
|
|
652
|
-
|
|
653
|
-
setPermission = permissionsExtensions.setPermission;
|
|
654
|
-
setPermissions = permissionsExtensions.setPermissions;
|
|
655
|
-
getPermission = permissionsExtensions.getPermission;
|
|
656
|
-
|
|
657
|
-
updateSettings = settingsExtensions.updateSettings;
|
|
658
|
-
setAppearance = settingsExtensions.setAppearance;
|
|
659
|
-
getAppearance = settingsExtensions.getAppearance;
|
|
660
|
-
setIncreaseContrast = settingsExtensions.setIncreaseContrast;
|
|
661
|
-
getIncreaseContrast = settingsExtensions.getIncreaseContrast;
|
|
662
|
-
setContentSize = settingsExtensions.setContentSize;
|
|
663
|
-
getContentSize = settingsExtensions.getContentSize;
|
|
664
|
-
configureLocalization = settingsExtensions.configureLocalization;
|
|
665
|
-
setAutoFillPasswords = settingsExtensions.setAutoFillPasswords;
|
|
666
|
-
setReduceMotion = settingsExtensions.setReduceMotion;
|
|
667
|
-
setReduceTransparency = settingsExtensions.setReduceTransparency;
|
|
668
|
-
disableKeyboardIntroduction = settingsExtensions.disableKeyboardIntroduction;
|
|
669
668
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {fs} from '@appium/support';
|
|
2
2
|
import {exec} from 'teen_process';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import _ from 'lodash';
|
|
5
|
-
import B from 'bluebird';
|
|
6
4
|
import {SimulatorXcode14} from './simulator-xcode-14';
|
|
7
5
|
|
|
8
6
|
export class SimulatorXcode15 extends SimulatorXcode14 {
|
|
@@ -102,7 +100,7 @@ export class SimulatorXcode15 extends SimulatorXcode14 {
|
|
|
102
100
|
if (!simRoot) {
|
|
103
101
|
throw new Error('The IPHONE_SIMULATOR_ROOT environment variable value cannot be retrieved');
|
|
104
102
|
}
|
|
105
|
-
return
|
|
103
|
+
return simRoot.trim();
|
|
106
104
|
}
|
|
107
105
|
|
|
108
106
|
/**
|
|
@@ -124,7 +122,7 @@ export class SimulatorXcode15 extends SimulatorXcode14 {
|
|
|
124
122
|
'print CFBundleIdentifier',
|
|
125
123
|
infoPlistPath,
|
|
126
124
|
]);
|
|
127
|
-
return
|
|
125
|
+
return stdout.trim();
|
|
128
126
|
} catch {
|
|
129
127
|
return null;
|
|
130
128
|
}
|
|
@@ -132,7 +130,7 @@ export class SimulatorXcode15 extends SimulatorXcode14 {
|
|
|
132
130
|
const allApps = (await fs.readdir(appsRoot))
|
|
133
131
|
.filter((x) => x.endsWith('.app'))
|
|
134
132
|
.map((x) => path.join(appsRoot, x));
|
|
135
|
-
const bundleIds = await
|
|
133
|
+
const bundleIds = await Promise.all(allApps.map(fetchBundleId));
|
|
136
134
|
this._systemAppBundleIds = new Set(bundleIds.filter((x): x is string => x !== null));
|
|
137
135
|
return this._systemAppBundleIds;
|
|
138
136
|
}
|
package/lib/types.ts
CHANGED
|
@@ -212,15 +212,6 @@ export interface InteractsWithSafariBrowser {
|
|
|
212
212
|
getWebInspectorSocket(): Promise<string | null>;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
interface KeyboardOptions {
|
|
216
|
-
/** The name of the keyboard locale, for example `en_US` or `de_CH` */
|
|
217
|
-
name: string;
|
|
218
|
-
/** The keyboard layout, for example `QUERTY` or `Ukrainian` */
|
|
219
|
-
layout: string;
|
|
220
|
-
/** hardware Could either be `Automatic` or `null` */
|
|
221
|
-
hardware?: string | null;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
215
|
export interface LanguageOptions {
|
|
225
216
|
/** The name of the language, for example `de` or `zh-Hant-CN` */
|
|
226
217
|
name: string;
|
|
@@ -298,3 +289,12 @@ export type Simulator = CoreSimulator &
|
|
|
298
289
|
InteractsWithKeychain &
|
|
299
290
|
SupportsAppPermissions &
|
|
300
291
|
HasMiscFeatures;
|
|
292
|
+
|
|
293
|
+
interface KeyboardOptions {
|
|
294
|
+
/** The name of the keyboard locale, for example `en_US` or `de_CH` */
|
|
295
|
+
name: string;
|
|
296
|
+
/** The keyboard layout, for example `QUERTY` or `Ukrainian` */
|
|
297
|
+
layout: string;
|
|
298
|
+
/** hardware Could either be `Automatic` or `null` */
|
|
299
|
+
hardware?: string | null;
|
|
300
|
+
}
|