@limrun/appium-xcuitest-driver 10.4.3-lim.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2600 -0
- package/LICENSE +201 -0
- package/README.md +55 -0
- package/build/index.d.ts +5 -0
- package/build/index.js +41 -0
- package/build/lib/app-infos-cache.d.ts +62 -0
- package/build/lib/app-infos-cache.d.ts.map +1 -0
- package/build/lib/app-infos-cache.js +180 -0
- package/build/lib/app-infos-cache.js.map +1 -0
- package/build/lib/app-utils.d.ts +89 -0
- package/build/lib/app-utils.d.ts.map +1 -0
- package/build/lib/app-utils.js +657 -0
- package/build/lib/app-utils.js.map +1 -0
- package/build/lib/commands/active-app-info.d.ts +9 -0
- package/build/lib/commands/active-app-info.d.ts.map +1 -0
- package/build/lib/commands/active-app-info.js +14 -0
- package/build/lib/commands/active-app-info.js.map +1 -0
- package/build/lib/commands/advanced-battery-types.d.ts +444 -0
- package/build/lib/commands/advanced-battery-types.d.ts.map +1 -0
- package/build/lib/commands/advanced-battery-types.js +8 -0
- package/build/lib/commands/advanced-battery-types.js.map +1 -0
- package/build/lib/commands/alert.d.ts +45 -0
- package/build/lib/commands/alert.d.ts.map +1 -0
- package/build/lib/commands/alert.js +87 -0
- package/build/lib/commands/alert.js.map +1 -0
- package/build/lib/commands/app-management.d.ts +153 -0
- package/build/lib/commands/app-management.d.ts.map +1 -0
- package/build/lib/commands/app-management.js +323 -0
- package/build/lib/commands/app-management.js.map +1 -0
- package/build/lib/commands/app-strings.d.ts +16 -0
- package/build/lib/commands/app-strings.d.ts.map +1 -0
- package/build/lib/commands/app-strings.js +30 -0
- package/build/lib/commands/app-strings.js.map +1 -0
- package/build/lib/commands/appearance.d.ts +22 -0
- package/build/lib/commands/appearance.d.ts.map +1 -0
- package/build/lib/commands/appearance.js +74 -0
- package/build/lib/commands/appearance.js.map +1 -0
- package/build/lib/commands/audit.d.ts +43 -0
- package/build/lib/commands/audit.d.ts.map +1 -0
- package/build/lib/commands/audit.js +31 -0
- package/build/lib/commands/audit.js.map +1 -0
- package/build/lib/commands/battery.d.ts +13 -0
- package/build/lib/commands/battery.d.ts.map +1 -0
- package/build/lib/commands/battery.js +49 -0
- package/build/lib/commands/battery.js.map +1 -0
- package/build/lib/commands/bidi/constants.d.ts +6 -0
- package/build/lib/commands/bidi/constants.d.ts.map +1 -0
- package/build/lib/commands/bidi/constants.js +10 -0
- package/build/lib/commands/bidi/constants.js.map +1 -0
- package/build/lib/commands/bidi/models.d.ts +9 -0
- package/build/lib/commands/bidi/models.d.ts.map +1 -0
- package/build/lib/commands/bidi/models.js +54 -0
- package/build/lib/commands/bidi/models.js.map +1 -0
- package/build/lib/commands/bidi/types.d.ts +26 -0
- package/build/lib/commands/bidi/types.d.ts.map +1 -0
- package/build/lib/commands/bidi/types.js +4 -0
- package/build/lib/commands/bidi/types.js.map +1 -0
- package/build/lib/commands/biometric.d.ts +32 -0
- package/build/lib/commands/biometric.d.ts.map +1 -0
- package/build/lib/commands/biometric.js +54 -0
- package/build/lib/commands/biometric.js.map +1 -0
- package/build/lib/commands/certificate.d.ts +50 -0
- package/build/lib/commands/certificate.d.ts.map +1 -0
- package/build/lib/commands/certificate.js +454 -0
- package/build/lib/commands/certificate.js.map +1 -0
- package/build/lib/commands/clipboard.d.ts +21 -0
- package/build/lib/commands/clipboard.d.ts.map +1 -0
- package/build/lib/commands/clipboard.js +36 -0
- package/build/lib/commands/clipboard.js.map +1 -0
- package/build/lib/commands/condition.d.ts +102 -0
- package/build/lib/commands/condition.d.ts.map +1 -0
- package/build/lib/commands/condition.js +146 -0
- package/build/lib/commands/condition.js.map +1 -0
- package/build/lib/commands/content-size.d.ts +30 -0
- package/build/lib/commands/content-size.d.ts.map +1 -0
- package/build/lib/commands/content-size.js +67 -0
- package/build/lib/commands/content-size.js.map +1 -0
- package/build/lib/commands/context.d.ts +191 -0
- package/build/lib/commands/context.d.ts.map +1 -0
- package/build/lib/commands/context.js +625 -0
- package/build/lib/commands/context.js.map +1 -0
- package/build/lib/commands/deviceInfo.d.ts +12 -0
- package/build/lib/commands/deviceInfo.d.ts.map +1 -0
- package/build/lib/commands/deviceInfo.js +25 -0
- package/build/lib/commands/deviceInfo.js.map +1 -0
- package/build/lib/commands/element.d.ts +108 -0
- package/build/lib/commands/element.d.ts.map +1 -0
- package/build/lib/commands/element.js +395 -0
- package/build/lib/commands/element.js.map +1 -0
- package/build/lib/commands/enum.d.ts +105 -0
- package/build/lib/commands/enum.d.ts.map +1 -0
- package/build/lib/commands/enum.js +113 -0
- package/build/lib/commands/enum.js.map +1 -0
- package/build/lib/commands/execute.d.ts +33 -0
- package/build/lib/commands/execute.d.ts.map +1 -0
- package/build/lib/commands/execute.js +142 -0
- package/build/lib/commands/execute.js.map +1 -0
- package/build/lib/commands/file-movement.d.ts +90 -0
- package/build/lib/commands/file-movement.d.ts.map +1 -0
- package/build/lib/commands/file-movement.js +477 -0
- package/build/lib/commands/file-movement.js.map +1 -0
- package/build/lib/commands/find.d.ts +21 -0
- package/build/lib/commands/find.d.ts.map +1 -0
- package/build/lib/commands/find.js +199 -0
- package/build/lib/commands/find.js.map +1 -0
- package/build/lib/commands/general.d.ts +137 -0
- package/build/lib/commands/general.d.ts.map +1 -0
- package/build/lib/commands/general.js +270 -0
- package/build/lib/commands/general.js.map +1 -0
- package/build/lib/commands/geolocation.d.ts +57 -0
- package/build/lib/commands/geolocation.d.ts.map +1 -0
- package/build/lib/commands/geolocation.js +58 -0
- package/build/lib/commands/geolocation.js.map +1 -0
- package/build/lib/commands/gesture.d.ts +283 -0
- package/build/lib/commands/gesture.d.ts.map +1 -0
- package/build/lib/commands/gesture.js +565 -0
- package/build/lib/commands/gesture.js.map +1 -0
- package/build/lib/commands/hid-event.d.ts +2773 -0
- package/build/lib/commands/hid-event.d.ts.map +1 -0
- package/build/lib/commands/hid-event.js +1633 -0
- package/build/lib/commands/hid-event.js.map +1 -0
- package/build/lib/commands/increase-contrast.d.ts +24 -0
- package/build/lib/commands/increase-contrast.d.ts.map +1 -0
- package/build/lib/commands/increase-contrast.js +49 -0
- package/build/lib/commands/increase-contrast.js.map +1 -0
- package/build/lib/commands/iohid.d.ts +1372 -0
- package/build/lib/commands/iohid.d.ts.map +1 -0
- package/build/lib/commands/iohid.js +63 -0
- package/build/lib/commands/iohid.js.map +1 -0
- package/build/lib/commands/keyboard.d.ts +32 -0
- package/build/lib/commands/keyboard.d.ts.map +1 -0
- package/build/lib/commands/keyboard.js +67 -0
- package/build/lib/commands/keyboard.js.map +1 -0
- package/build/lib/commands/keychains.d.ts +10 -0
- package/build/lib/commands/keychains.d.ts.map +1 -0
- package/build/lib/commands/keychains.js +22 -0
- package/build/lib/commands/keychains.js.map +1 -0
- package/build/lib/commands/localization.d.ts +17 -0
- package/build/lib/commands/localization.d.ts.map +1 -0
- package/build/lib/commands/localization.js +34 -0
- package/build/lib/commands/localization.js.map +1 -0
- package/build/lib/commands/location.d.ts +40 -0
- package/build/lib/commands/location.d.ts.map +1 -0
- package/build/lib/commands/location.js +121 -0
- package/build/lib/commands/location.js.map +1 -0
- package/build/lib/commands/lock.d.ts +23 -0
- package/build/lib/commands/lock.d.ts.map +1 -0
- package/build/lib/commands/lock.js +49 -0
- package/build/lib/commands/lock.js.map +1 -0
- package/build/lib/commands/log.d.ts +68 -0
- package/build/lib/commands/log.d.ts.map +1 -0
- package/build/lib/commands/log.js +287 -0
- package/build/lib/commands/log.js.map +1 -0
- package/build/lib/commands/memory.d.ts +11 -0
- package/build/lib/commands/memory.d.ts.map +1 -0
- package/build/lib/commands/memory.js +49 -0
- package/build/lib/commands/memory.js.map +1 -0
- package/build/lib/commands/navigation.d.ts +44 -0
- package/build/lib/commands/navigation.d.ts.map +1 -0
- package/build/lib/commands/navigation.js +121 -0
- package/build/lib/commands/navigation.js.map +1 -0
- package/build/lib/commands/notifications.d.ts +28 -0
- package/build/lib/commands/notifications.d.ts.map +1 -0
- package/build/lib/commands/notifications.js +64 -0
- package/build/lib/commands/notifications.js.map +1 -0
- package/build/lib/commands/pasteboard.d.ts +23 -0
- package/build/lib/commands/pasteboard.d.ts.map +1 -0
- package/build/lib/commands/pasteboard.js +43 -0
- package/build/lib/commands/pasteboard.js.map +1 -0
- package/build/lib/commands/pcap.d.ts +54 -0
- package/build/lib/commands/pcap.d.ts.map +1 -0
- package/build/lib/commands/pcap.js +149 -0
- package/build/lib/commands/pcap.js.map +1 -0
- package/build/lib/commands/performance.d.ts +85 -0
- package/build/lib/commands/performance.d.ts.map +1 -0
- package/build/lib/commands/performance.js +331 -0
- package/build/lib/commands/performance.js.map +1 -0
- package/build/lib/commands/permissions.d.ts +36 -0
- package/build/lib/commands/permissions.d.ts.map +1 -0
- package/build/lib/commands/permissions.js +80 -0
- package/build/lib/commands/permissions.js.map +1 -0
- package/build/lib/commands/proxy-helper.d.ts +15 -0
- package/build/lib/commands/proxy-helper.d.ts.map +1 -0
- package/build/lib/commands/proxy-helper.js +117 -0
- package/build/lib/commands/proxy-helper.js.map +1 -0
- package/build/lib/commands/record-audio.d.ts +69 -0
- package/build/lib/commands/record-audio.d.ts.map +1 -0
- package/build/lib/commands/record-audio.js +228 -0
- package/build/lib/commands/record-audio.js.map +1 -0
- package/build/lib/commands/recordscreen.d.ts +89 -0
- package/build/lib/commands/recordscreen.d.ts.map +1 -0
- package/build/lib/commands/recordscreen.js +326 -0
- package/build/lib/commands/recordscreen.js.map +1 -0
- package/build/lib/commands/screenshots.d.ts +16 -0
- package/build/lib/commands/screenshots.d.ts.map +1 -0
- package/build/lib/commands/screenshots.js +129 -0
- package/build/lib/commands/screenshots.js.map +1 -0
- package/build/lib/commands/simctl.d.ts +27 -0
- package/build/lib/commands/simctl.d.ts.map +1 -0
- package/build/lib/commands/simctl.js +65 -0
- package/build/lib/commands/simctl.js.map +1 -0
- package/build/lib/commands/source.d.ts +16 -0
- package/build/lib/commands/source.d.ts.map +1 -0
- package/build/lib/commands/source.js +128 -0
- package/build/lib/commands/source.js.map +1 -0
- package/build/lib/commands/timeouts.d.ts +53 -0
- package/build/lib/commands/timeouts.d.ts.map +1 -0
- package/build/lib/commands/timeouts.js +71 -0
- package/build/lib/commands/timeouts.js.map +1 -0
- package/build/lib/commands/types.d.ts +539 -0
- package/build/lib/commands/types.d.ts.map +1 -0
- package/build/lib/commands/types.js +3 -0
- package/build/lib/commands/types.js.map +1 -0
- package/build/lib/commands/web.d.ts +297 -0
- package/build/lib/commands/web.d.ts.map +1 -0
- package/build/lib/commands/web.js +1029 -0
- package/build/lib/commands/web.js.map +1 -0
- package/build/lib/commands/xctest-record-screen.d.ts +92 -0
- package/build/lib/commands/xctest-record-screen.d.ts.map +1 -0
- package/build/lib/commands/xctest-record-screen.js +193 -0
- package/build/lib/commands/xctest-record-screen.js.map +1 -0
- package/build/lib/commands/xctest.d.ts +71 -0
- package/build/lib/commands/xctest.d.ts.map +1 -0
- package/build/lib/commands/xctest.js +257 -0
- package/build/lib/commands/xctest.js.map +1 -0
- package/build/lib/css-converter.d.ts +10 -0
- package/build/lib/css-converter.d.ts.map +1 -0
- package/build/lib/css-converter.js +258 -0
- package/build/lib/css-converter.js.map +1 -0
- package/build/lib/desired-caps.d.ts +506 -0
- package/build/lib/desired-caps.d.ts.map +1 -0
- package/build/lib/desired-caps.js +400 -0
- package/build/lib/desired-caps.js.map +1 -0
- package/build/lib/device-connections-factory.d.ts +13 -0
- package/build/lib/device-connections-factory.d.ts.map +1 -0
- package/build/lib/device-connections-factory.js +244 -0
- package/build/lib/device-connections-factory.js.map +1 -0
- package/build/lib/device-log/helpers.d.ts +10 -0
- package/build/lib/device-log/helpers.d.ts.map +1 -0
- package/build/lib/device-log/helpers.js +37 -0
- package/build/lib/device-log/helpers.js.map +1 -0
- package/build/lib/device-log/ios-crash-log.d.ts +34 -0
- package/build/lib/device-log/ios-crash-log.d.ts.map +1 -0
- package/build/lib/device-log/ios-crash-log.js +141 -0
- package/build/lib/device-log/ios-crash-log.js.map +1 -0
- package/build/lib/device-log/ios-device-log.d.ts +19 -0
- package/build/lib/device-log/ios-device-log.d.ts.map +1 -0
- package/build/lib/device-log/ios-device-log.js +42 -0
- package/build/lib/device-log/ios-device-log.js.map +1 -0
- package/build/lib/device-log/ios-log.d.ts +24 -0
- package/build/lib/device-log/ios-log.d.ts.map +1 -0
- package/build/lib/device-log/ios-log.js +50 -0
- package/build/lib/device-log/ios-log.js.map +1 -0
- package/build/lib/device-log/ios-performance-log.d.ts +18 -0
- package/build/lib/device-log/ios-performance-log.d.ts.map +1 -0
- package/build/lib/device-log/ios-performance-log.js +43 -0
- package/build/lib/device-log/ios-performance-log.js.map +1 -0
- package/build/lib/device-log/ios-simulator-log.d.ts +38 -0
- package/build/lib/device-log/ios-simulator-log.d.ts.map +1 -0
- package/build/lib/device-log/ios-simulator-log.js +184 -0
- package/build/lib/device-log/ios-simulator-log.js.map +1 -0
- package/build/lib/device-log/line-consuming-log.d.ts +9 -0
- package/build/lib/device-log/line-consuming-log.d.ts.map +1 -0
- package/build/lib/device-log/line-consuming-log.js +16 -0
- package/build/lib/device-log/line-consuming-log.js.map +1 -0
- package/build/lib/device-log/safari-console-log.d.ts +67 -0
- package/build/lib/device-log/safari-console-log.d.ts.map +1 -0
- package/build/lib/device-log/safari-console-log.js +81 -0
- package/build/lib/device-log/safari-console-log.js.map +1 -0
- package/build/lib/device-log/safari-network-log.d.ts +75 -0
- package/build/lib/device-log/safari-network-log.d.ts.map +1 -0
- package/build/lib/device-log/safari-network-log.js +47 -0
- package/build/lib/device-log/safari-network-log.js.map +1 -0
- package/build/lib/doctor/checks.d.ts +3 -0
- package/build/lib/doctor/checks.d.ts.map +1 -0
- package/build/lib/doctor/checks.js +39 -0
- package/build/lib/doctor/checks.js.map +1 -0
- package/build/lib/doctor/optional-checks.d.ts +46 -0
- package/build/lib/doctor/optional-checks.d.ts.map +1 -0
- package/build/lib/doctor/optional-checks.js +129 -0
- package/build/lib/doctor/optional-checks.js.map +1 -0
- package/build/lib/doctor/required-checks.d.ts +42 -0
- package/build/lib/doctor/required-checks.d.ts.map +1 -0
- package/build/lib/doctor/required-checks.js +94 -0
- package/build/lib/doctor/required-checks.js.map +1 -0
- package/build/lib/doctor/utils.d.ts +8 -0
- package/build/lib/doctor/utils.d.ts.map +1 -0
- package/build/lib/doctor/utils.js +21 -0
- package/build/lib/doctor/utils.js.map +1 -0
- package/build/lib/driver.d.ts +2429 -0
- package/build/lib/driver.d.ts.map +1 -0
- package/build/lib/driver.js +1967 -0
- package/build/lib/driver.js.map +1 -0
- package/build/lib/execute-method-map.d.ts +552 -0
- package/build/lib/execute-method-map.d.ts.map +1 -0
- package/build/lib/execute-method-map.js +586 -0
- package/build/lib/execute-method-map.js.map +1 -0
- package/build/lib/ios-fs-helpers.d.ts +75 -0
- package/build/lib/ios-fs-helpers.d.ts.map +1 -0
- package/build/lib/ios-fs-helpers.js +370 -0
- package/build/lib/ios-fs-helpers.js.map +1 -0
- package/build/lib/ios-generic-simulators.d.ts +6 -0
- package/build/lib/ios-generic-simulators.d.ts.map +1 -0
- package/build/lib/ios-generic-simulators.js +14 -0
- package/build/lib/ios-generic-simulators.js.map +1 -0
- package/build/lib/logger.d.ts +3 -0
- package/build/lib/logger.d.ts.map +1 -0
- package/build/lib/logger.js +6 -0
- package/build/lib/logger.js.map +1 -0
- package/build/lib/method-map.d.ts +229 -0
- package/build/lib/method-map.d.ts.map +1 -0
- package/build/lib/method-map.js +200 -0
- package/build/lib/method-map.js.map +1 -0
- package/build/lib/real-device-clients/base-device-client.d.ts +22 -0
- package/build/lib/real-device-clients/base-device-client.d.ts.map +1 -0
- package/build/lib/real-device-clients/base-device-client.js +14 -0
- package/build/lib/real-device-clients/base-device-client.js.map +1 -0
- package/build/lib/real-device-clients/py-ios-device-client.d.ts +21 -0
- package/build/lib/real-device-clients/py-ios-device-client.d.ts.map +1 -0
- package/build/lib/real-device-clients/py-ios-device-client.js +125 -0
- package/build/lib/real-device-clients/py-ios-device-client.js.map +1 -0
- package/build/lib/real-device-management.d.ts +53 -0
- package/build/lib/real-device-management.d.ts.map +1 -0
- package/build/lib/real-device-management.js +128 -0
- package/build/lib/real-device-management.js.map +1 -0
- package/build/lib/real-device.d.ts +112 -0
- package/build/lib/real-device.d.ts.map +1 -0
- package/build/lib/real-device.js +352 -0
- package/build/lib/real-device.js.map +1 -0
- package/build/lib/simulator-management.d.ts +96 -0
- package/build/lib/simulator-management.d.ts.map +1 -0
- package/build/lib/simulator-management.js +278 -0
- package/build/lib/simulator-management.js.map +1 -0
- package/build/lib/stubs.d.ts +3 -0
- package/build/lib/stubs.d.ts.map +1 -0
- package/build/lib/stubs.js +3 -0
- package/build/lib/stubs.js.map +1 -0
- package/build/lib/types.d.ts +31 -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/build/lib/utils.d.ts +191 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +549 -0
- package/build/lib/utils.js.map +1 -0
- package/build/lib/xcrun.d.ts +3 -0
- package/build/lib/xcrun.d.ts.map +1 -0
- package/build/lib/xcrun.js +17 -0
- package/build/lib/xcrun.js.map +1 -0
- package/index.js +7 -0
- package/lib/app-infos-cache.js +187 -0
- package/lib/app-utils.js +710 -0
- package/lib/commands/active-app-info.js +12 -0
- package/lib/commands/advanced-battery-types.ts +454 -0
- package/lib/commands/alert.js +88 -0
- package/lib/commands/app-management.js +346 -0
- package/lib/commands/app-strings.js +30 -0
- package/lib/commands/appearance.js +71 -0
- package/lib/commands/audit.js +31 -0
- package/lib/commands/battery.js +45 -0
- package/lib/commands/bidi/constants.ts +6 -0
- package/lib/commands/bidi/models.ts +55 -0
- package/lib/commands/bidi/types.ts +31 -0
- package/lib/commands/biometric.js +53 -0
- package/lib/commands/certificate.js +497 -0
- package/lib/commands/clipboard.js +35 -0
- package/lib/commands/condition.js +155 -0
- package/lib/commands/content-size.js +68 -0
- package/lib/commands/context.js +705 -0
- package/lib/commands/deviceInfo.js +27 -0
- package/lib/commands/element.js +423 -0
- package/lib/commands/enum.ts +108 -0
- package/lib/commands/execute.js +153 -0
- package/lib/commands/file-movement.js +510 -0
- package/lib/commands/find.js +205 -0
- package/lib/commands/general.js +278 -0
- package/lib/commands/geolocation.js +56 -0
- package/lib/commands/gesture.js +596 -0
- package/lib/commands/hid-event.ts +1634 -0
- package/lib/commands/increase-contrast.js +50 -0
- package/lib/commands/iohid.js +64 -0
- package/lib/commands/keyboard.js +62 -0
- package/lib/commands/keychains.js +18 -0
- package/lib/commands/localization.js +30 -0
- package/lib/commands/location.js +131 -0
- package/lib/commands/lock.js +46 -0
- package/lib/commands/log.js +327 -0
- package/lib/commands/memory.js +51 -0
- package/lib/commands/navigation.js +125 -0
- package/lib/commands/notifications.js +66 -0
- package/lib/commands/pasteboard.js +42 -0
- package/lib/commands/pcap.js +168 -0
- package/lib/commands/performance.js +392 -0
- package/lib/commands/permissions.js +85 -0
- package/lib/commands/proxy-helper.js +122 -0
- package/lib/commands/record-audio.js +264 -0
- package/lib/commands/recordscreen.js +391 -0
- package/lib/commands/screenshots.js +137 -0
- package/lib/commands/simctl.js +71 -0
- package/lib/commands/source.js +131 -0
- package/lib/commands/timeouts.js +68 -0
- package/lib/commands/types.ts +648 -0
- package/lib/commands/web.js +1113 -0
- package/lib/commands/xctest-record-screen.js +204 -0
- package/lib/commands/xctest.js +285 -0
- package/lib/css-converter.js +311 -0
- package/lib/desired-caps.js +396 -0
- package/lib/device-connections-factory.js +269 -0
- package/lib/device-log/helpers.ts +40 -0
- package/lib/device-log/ios-crash-log.ts +166 -0
- package/lib/device-log/ios-device-log.ts +51 -0
- package/lib/device-log/ios-log.ts +70 -0
- package/lib/device-log/ios-performance-log.ts +50 -0
- package/lib/device-log/ios-simulator-log.ts +202 -0
- package/lib/device-log/line-consuming-log.ts +16 -0
- package/lib/device-log/safari-console-log.ts +117 -0
- package/lib/device-log/safari-network-log.ts +120 -0
- package/lib/doctor/checks.ts +3 -0
- package/lib/doctor/optional-checks.ts +173 -0
- package/lib/doctor/required-checks.ts +120 -0
- package/lib/doctor/utils.ts +18 -0
- package/lib/driver.js +2316 -0
- package/lib/execute-method-map.ts +585 -0
- package/lib/ios-fs-helpers.js +355 -0
- package/lib/ios-generic-simulators.js +11 -0
- package/lib/logger.js +5 -0
- package/lib/method-map.js +196 -0
- package/lib/real-device-clients/base-device-client.ts +34 -0
- package/lib/real-device-clients/py-ios-device-client.ts +149 -0
- package/lib/real-device-management.js +133 -0
- package/lib/real-device.js +347 -0
- package/lib/simulator-management.js +324 -0
- package/lib/stubs.ts +3 -0
- package/lib/types.ts +33 -0
- package/lib/utils.js +551 -0
- package/lib/xcrun.js +16 -0
- package/package.json +175 -0
- package/scripts/build-docs.js +56 -0
- package/scripts/build-wda.js +42 -0
- package/scripts/download-wda-sim.mjs +68 -0
- package/scripts/image-mounter.mjs +239 -0
- package/scripts/open-wda.mjs +15 -0
- package/scripts/tunnel-creation.mjs +359 -0
- package/scripts/utils.js +16 -0
package/lib/driver.js
ADDED
|
@@ -0,0 +1,2316 @@
|
|
|
1
|
+
import IDB from 'appium-idb';
|
|
2
|
+
import {getSimulator} from '@limrun/appium-ios-simulator';
|
|
3
|
+
import {WebDriverAgent} from 'appium-webdriveragent';
|
|
4
|
+
import {BaseDriver, DeviceSettings, errors} from 'appium/driver';
|
|
5
|
+
import {fs, mjpeg, util, timing} from 'appium/support';
|
|
6
|
+
import AsyncLock from 'async-lock';
|
|
7
|
+
import {retryInterval} from 'asyncbox';
|
|
8
|
+
import B from 'bluebird';
|
|
9
|
+
import _ from 'lodash';
|
|
10
|
+
import {LRUCache} from 'lru-cache';
|
|
11
|
+
import EventEmitter from 'node:events';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import url from 'node:url';
|
|
14
|
+
import {
|
|
15
|
+
SUPPORTED_EXTENSIONS,
|
|
16
|
+
SAFARI_BUNDLE_ID,
|
|
17
|
+
onPostConfigureApp,
|
|
18
|
+
onDownloadApp,
|
|
19
|
+
verifyApplicationPlatform,
|
|
20
|
+
} from './app-utils';
|
|
21
|
+
import * as activeAppInfoCommands from './commands/active-app-info';
|
|
22
|
+
import * as alertCommands from './commands/alert';
|
|
23
|
+
import * as appManagementCommands from './commands/app-management';
|
|
24
|
+
import * as appearanceCommands from './commands/appearance';
|
|
25
|
+
import * as appStringsCommands from './commands/app-strings';
|
|
26
|
+
import * as auditCommands from './commands/audit';
|
|
27
|
+
import * as batteryCommands from './commands/battery';
|
|
28
|
+
import * as biometricCommands from './commands/biometric';
|
|
29
|
+
import * as certificateCommands from './commands/certificate';
|
|
30
|
+
import * as clipboardCommands from './commands/clipboard';
|
|
31
|
+
import * as conditionCommands from './commands/condition';
|
|
32
|
+
import * as contentSizeCommands from './commands/content-size';
|
|
33
|
+
import * as contextCommands from './commands/context';
|
|
34
|
+
import * as deviceInfoCommands from './commands/deviceInfo';
|
|
35
|
+
import * as elementCommands from './commands/element';
|
|
36
|
+
import * as executeCommands from './commands/execute';
|
|
37
|
+
import * as fileMovementCommands from './commands/file-movement';
|
|
38
|
+
import * as findCommands from './commands/find';
|
|
39
|
+
import * as generalCommands from './commands/general';
|
|
40
|
+
import * as geolocationCommands from './commands/geolocation';
|
|
41
|
+
import * as gestureCommands from './commands/gesture';
|
|
42
|
+
import * as iohidCommands from './commands/iohid';
|
|
43
|
+
import * as keychainsCommands from './commands/keychains';
|
|
44
|
+
import * as keyboardCommands from './commands/keyboard';
|
|
45
|
+
import * as localizationCommands from './commands/localization';
|
|
46
|
+
import * as locationCommands from './commands/location';
|
|
47
|
+
import * as lockCommands from './commands/lock';
|
|
48
|
+
import * as logCommands from './commands/log';
|
|
49
|
+
import * as memoryCommands from './commands/memory';
|
|
50
|
+
import * as navigationCommands from './commands/navigation';
|
|
51
|
+
import * as notificationsCommands from './commands/notifications';
|
|
52
|
+
import * as pasteboardCommands from './commands/pasteboard';
|
|
53
|
+
import * as pcapCommands from './commands/pcap';
|
|
54
|
+
import * as performanceCommands from './commands/performance';
|
|
55
|
+
import * as permissionsCommands from './commands/permissions';
|
|
56
|
+
import * as proxyHelperCommands from './commands/proxy-helper';
|
|
57
|
+
import * as recordAudioCommands from './commands/record-audio';
|
|
58
|
+
import * as recordScreenCommands from './commands/recordscreen';
|
|
59
|
+
import * as screenshotCommands from './commands/screenshots';
|
|
60
|
+
import * as sourceCommands from './commands/source';
|
|
61
|
+
import * as simctlCommands from './commands/simctl';
|
|
62
|
+
import * as timeoutCommands from './commands/timeouts';
|
|
63
|
+
import * as webCommands from './commands/web';
|
|
64
|
+
import * as xctestCommands from './commands/xctest';
|
|
65
|
+
import * as xctestRecordScreenCommands from './commands/xctest-record-screen';
|
|
66
|
+
import * as increaseContrastCommands from './commands/increase-contrast';
|
|
67
|
+
import {desiredCapConstraints} from './desired-caps';
|
|
68
|
+
import {DEVICE_CONNECTIONS_FACTORY} from './device-connections-factory';
|
|
69
|
+
import {executeMethodMap} from './execute-method-map';
|
|
70
|
+
import {newMethodMap} from './method-map';
|
|
71
|
+
import { Pyidevice } from './real-device-clients/py-ios-device-client';
|
|
72
|
+
import {
|
|
73
|
+
installToRealDevice,
|
|
74
|
+
runRealDeviceReset,
|
|
75
|
+
applySafariStartupArgs,
|
|
76
|
+
detectUdid,
|
|
77
|
+
} from './real-device-management';
|
|
78
|
+
import {
|
|
79
|
+
RealDevice,
|
|
80
|
+
getConnectedDevices,
|
|
81
|
+
} from './real-device';
|
|
82
|
+
import {
|
|
83
|
+
createSim,
|
|
84
|
+
getExistingSim,
|
|
85
|
+
installToSimulator,
|
|
86
|
+
runSimulatorReset,
|
|
87
|
+
setLocalizationPrefs,
|
|
88
|
+
setSafariPrefs,
|
|
89
|
+
shutdownOtherSimulators,
|
|
90
|
+
shutdownSimulator,
|
|
91
|
+
} from './simulator-management';
|
|
92
|
+
import {
|
|
93
|
+
DEFAULT_TIMEOUT_KEY,
|
|
94
|
+
UDID_AUTO,
|
|
95
|
+
checkAppPresent,
|
|
96
|
+
clearSystemFiles,
|
|
97
|
+
getAndCheckIosSdkVersion,
|
|
98
|
+
getAndCheckXcodeVersion,
|
|
99
|
+
getDriverInfo,
|
|
100
|
+
isLocalHost,
|
|
101
|
+
markSystemFilesForCleanup,
|
|
102
|
+
normalizeCommandTimeouts,
|
|
103
|
+
normalizePlatformVersion,
|
|
104
|
+
printUser,
|
|
105
|
+
removeAllSessionWebSocketHandlers,
|
|
106
|
+
shouldSetInitialSafariUrl,
|
|
107
|
+
translateDeviceName,
|
|
108
|
+
} from './utils';
|
|
109
|
+
import { AppInfosCache } from './app-infos-cache';
|
|
110
|
+
import { notifyBiDiContextChange } from './commands/context';
|
|
111
|
+
|
|
112
|
+
const SHUTDOWN_OTHER_FEAT_NAME = 'shutdown_other_sims';
|
|
113
|
+
const CUSTOMIZE_RESULT_BUNDLE_PATH = 'customize_result_bundle_path';
|
|
114
|
+
|
|
115
|
+
const defaultServerCaps = {
|
|
116
|
+
webStorageEnabled: false,
|
|
117
|
+
locationContextEnabled: false,
|
|
118
|
+
browserName: '',
|
|
119
|
+
platform: 'MAC',
|
|
120
|
+
javascriptEnabled: true,
|
|
121
|
+
databaseEnabled: false,
|
|
122
|
+
takesScreenshot: true,
|
|
123
|
+
networkConnectionEnabled: false,
|
|
124
|
+
};
|
|
125
|
+
const WDA_SIM_STARTUP_RETRIES = 2;
|
|
126
|
+
const WDA_REAL_DEV_STARTUP_RETRIES = 1;
|
|
127
|
+
const WDA_REAL_DEV_TUTORIAL_URL =
|
|
128
|
+
'https://appium.github.io/appium-xcuitest-driver/latest/preparation/real-device-config/';
|
|
129
|
+
const WDA_STARTUP_RETRY_INTERVAL = 10000;
|
|
130
|
+
const DEFAULT_SETTINGS = {
|
|
131
|
+
nativeWebTap: false,
|
|
132
|
+
nativeWebTapStrict: false,
|
|
133
|
+
useJSONSource: false,
|
|
134
|
+
webScreenshotMode: 'native',
|
|
135
|
+
shouldUseCompactResponses: true,
|
|
136
|
+
elementResponseAttributes: 'type,label',
|
|
137
|
+
// Read https://github.com/appium/WebDriverAgent/blob/master/WebDriverAgentLib/Utilities/FBConfiguration.m for following settings' values
|
|
138
|
+
mjpegServerScreenshotQuality: 25,
|
|
139
|
+
mjpegServerFramerate: 10,
|
|
140
|
+
screenshotQuality: 1,
|
|
141
|
+
mjpegScalingFactor: 100,
|
|
142
|
+
// set `reduceMotion` to `null` so that it will be verified but still set either true/false
|
|
143
|
+
reduceMotion: null,
|
|
144
|
+
pageSourceExcludedAttributes: ''
|
|
145
|
+
};
|
|
146
|
+
// This lock assures, that each driver session does not
|
|
147
|
+
// affect shared resources of the other parallel sessions
|
|
148
|
+
const SHARED_RESOURCES_GUARD = new AsyncLock();
|
|
149
|
+
const WEB_ELEMENTS_CACHE_SIZE = 500;
|
|
150
|
+
const SUPPORTED_ORIENATIONS = ['LANDSCAPE', 'PORTRAIT'];
|
|
151
|
+
const DEFAULT_MJPEG_SERVER_PORT = 9100;
|
|
152
|
+
|
|
153
|
+
/* eslint-disable no-useless-escape */
|
|
154
|
+
/** @type {import('@appium/types').RouteMatcher[]} */
|
|
155
|
+
const NO_PROXY_NATIVE_LIST = [
|
|
156
|
+
['DELETE', /window/],
|
|
157
|
+
['GET', /^\/session\/[^\/]+$/],
|
|
158
|
+
['GET', /alert_text/],
|
|
159
|
+
['GET', /alert\/[^\/]+/],
|
|
160
|
+
['GET', /appium/],
|
|
161
|
+
['GET', /attribute/],
|
|
162
|
+
['GET', /context/],
|
|
163
|
+
['GET', /location/],
|
|
164
|
+
['GET', /log/],
|
|
165
|
+
['GET', /screenshot/],
|
|
166
|
+
['GET', /size/],
|
|
167
|
+
['GET', /source/],
|
|
168
|
+
['GET', /timeouts$/],
|
|
169
|
+
['GET', /url/],
|
|
170
|
+
['GET', /window/],
|
|
171
|
+
['POST', /accept_alert/],
|
|
172
|
+
['POST', /actions$/],
|
|
173
|
+
['DELETE', /actions$/],
|
|
174
|
+
['POST', /alert_text/],
|
|
175
|
+
['POST', /alert\/[^\/]+/],
|
|
176
|
+
['POST', /appium/],
|
|
177
|
+
['POST', /appium\/device\/is_locked/],
|
|
178
|
+
['POST', /appium\/device\/lock/],
|
|
179
|
+
['POST', /appium\/device\/unlock/],
|
|
180
|
+
['POST', /back/],
|
|
181
|
+
['POST', /clear/],
|
|
182
|
+
['POST', /context/],
|
|
183
|
+
['POST', /dismiss_alert/],
|
|
184
|
+
['POST', /element\/active/], // MJSONWP get active element should proxy
|
|
185
|
+
['POST', /element$/],
|
|
186
|
+
['POST', /elements$/],
|
|
187
|
+
['POST', /execute/],
|
|
188
|
+
['POST', /keys/],
|
|
189
|
+
['POST', /log/],
|
|
190
|
+
['POST', /receive_async_response/], // always, in case context switches while waiting
|
|
191
|
+
['POST', /session\/[^\/]+\/location/], // geo location, but not element location
|
|
192
|
+
['POST', /shake/],
|
|
193
|
+
['POST', /timeouts/],
|
|
194
|
+
['POST', /url/],
|
|
195
|
+
['POST', /value/],
|
|
196
|
+
['POST', /window/],
|
|
197
|
+
['DELETE', /cookie/],
|
|
198
|
+
['GET', /cookie/],
|
|
199
|
+
['POST', /cookie/],
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
const NO_PROXY_WEB_LIST = /** @type {import('@appium/types').RouteMatcher[]} */ ([
|
|
203
|
+
['GET', /attribute/],
|
|
204
|
+
['GET', /element/],
|
|
205
|
+
['GET', /text/],
|
|
206
|
+
['GET', /title/],
|
|
207
|
+
['POST', /clear/],
|
|
208
|
+
['POST', /click/],
|
|
209
|
+
['POST', /element/],
|
|
210
|
+
['POST', /forward/],
|
|
211
|
+
['POST', /frame/],
|
|
212
|
+
['POST', /keys/],
|
|
213
|
+
['POST', /refresh/],
|
|
214
|
+
]).concat(NO_PROXY_NATIVE_LIST);
|
|
215
|
+
/* eslint-enable no-useless-escape */
|
|
216
|
+
|
|
217
|
+
const MEMOIZED_FUNCTIONS = ['getStatusBarHeight', 'getDevicePixelRatio', 'getScreenInfo'];
|
|
218
|
+
|
|
219
|
+
// Capabilities that do not have xcodebuild process
|
|
220
|
+
const CAP_NAMES_NO_XCODEBUILD_REQUIRED = ['webDriverAgentUrl', 'usePreinstalledWDA'];
|
|
221
|
+
|
|
222
|
+
const BUNDLE_VERSION_PATTERN = /CFBundleVersion\s+=\s+"?([^(;|")]+)/;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* @implements {ExternalDriver<XCUITestDriverConstraints, FullContext|string>}
|
|
226
|
+
* @extends {BaseDriver<XCUITestDriverConstraints>}
|
|
227
|
+
* @privateRemarks **This class should be considered "final"**. It cannot be extended
|
|
228
|
+
* due to use of public class field assignments. If extending this class becomes a hard requirement, refer to the implementation of `BaseDriver` on how to do so.
|
|
229
|
+
*/
|
|
230
|
+
export class XCUITestDriver extends BaseDriver {
|
|
231
|
+
static newMethodMap = newMethodMap;
|
|
232
|
+
|
|
233
|
+
static executeMethodMap = executeMethodMap;
|
|
234
|
+
|
|
235
|
+
/** @type {string|null|undefined} */
|
|
236
|
+
curWindowHandle;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* @type {boolean|undefined}
|
|
240
|
+
*/
|
|
241
|
+
selectingNewPage;
|
|
242
|
+
|
|
243
|
+
/** @type {string[]} */
|
|
244
|
+
contexts;
|
|
245
|
+
|
|
246
|
+
/** @type {string|null} */
|
|
247
|
+
curContext;
|
|
248
|
+
|
|
249
|
+
/** @type {string[]} */
|
|
250
|
+
curWebFrames;
|
|
251
|
+
|
|
252
|
+
/** @type {import('./types').CalibrationData|null} */
|
|
253
|
+
webviewCalibrationResult;
|
|
254
|
+
|
|
255
|
+
/** @type {import('./types').AsyncPromise|undefined} */
|
|
256
|
+
asyncPromise;
|
|
257
|
+
|
|
258
|
+
/** @type {number|undefined} */
|
|
259
|
+
asyncWaitMs;
|
|
260
|
+
|
|
261
|
+
/** @type {((logRecord: {message: string}) => void)|null} */
|
|
262
|
+
_syslogWebsocketListener;
|
|
263
|
+
|
|
264
|
+
/** @type {import('./commands/performance').PerfRecorder[]} */
|
|
265
|
+
_perfRecorders;
|
|
266
|
+
|
|
267
|
+
/** @type {LRUCache} */
|
|
268
|
+
webElementsCache;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* @type {any|null}
|
|
272
|
+
* @privateRemarks needs types
|
|
273
|
+
**/
|
|
274
|
+
_conditionInducerService;
|
|
275
|
+
|
|
276
|
+
/** @type {boolean|undefined} */
|
|
277
|
+
_isSafariIphone;
|
|
278
|
+
|
|
279
|
+
/** @type {boolean|undefined} */
|
|
280
|
+
_isSafariNotched;
|
|
281
|
+
|
|
282
|
+
/** @type {import('./commands/types').WaitingAtoms} */
|
|
283
|
+
_waitingAtoms;
|
|
284
|
+
|
|
285
|
+
/** @type {import('./types').LifecycleData} */
|
|
286
|
+
lifecycleData;
|
|
287
|
+
|
|
288
|
+
/** @type {import('./commands/record-audio').AudioRecorder|null} */
|
|
289
|
+
_audioRecorder;
|
|
290
|
+
|
|
291
|
+
/** @type {XcodeVersion|undefined} */
|
|
292
|
+
xcodeVersion;
|
|
293
|
+
|
|
294
|
+
/** @type {import('./commands/pcap').TrafficCapture|null} */
|
|
295
|
+
_trafficCapture;
|
|
296
|
+
|
|
297
|
+
/** @type {import('./commands/recordscreen').ScreenRecorder|null} */
|
|
298
|
+
_recentScreenRecorder;
|
|
299
|
+
|
|
300
|
+
/** @type {Simulator|RealDevice} */
|
|
301
|
+
_device;
|
|
302
|
+
|
|
303
|
+
/** @type {string|null} */
|
|
304
|
+
_iosSdkVersion;
|
|
305
|
+
|
|
306
|
+
/** @type {WebDriverAgent} */
|
|
307
|
+
wda;
|
|
308
|
+
|
|
309
|
+
/** @type {import('appium-remote-debugger').RemoteDebugger|null} */
|
|
310
|
+
remote;
|
|
311
|
+
|
|
312
|
+
/** @type {DriverLogs} */
|
|
313
|
+
logs;
|
|
314
|
+
|
|
315
|
+
/** @type {import('./commands/types').LogListener|undefined} */
|
|
316
|
+
_bidiServerLogListener;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
*
|
|
320
|
+
* @param {XCUITestDriverOpts} opts
|
|
321
|
+
* @param {boolean} shouldValidateCaps
|
|
322
|
+
*/
|
|
323
|
+
constructor(opts = /** @type {XCUITestDriverOpts} */ ({}), shouldValidateCaps = true) {
|
|
324
|
+
super(opts, shouldValidateCaps);
|
|
325
|
+
|
|
326
|
+
this.locatorStrategies = [
|
|
327
|
+
'xpath',
|
|
328
|
+
'id',
|
|
329
|
+
'name',
|
|
330
|
+
'class name',
|
|
331
|
+
'-ios predicate string',
|
|
332
|
+
'-ios class chain',
|
|
333
|
+
'accessibility id',
|
|
334
|
+
'css selector',
|
|
335
|
+
];
|
|
336
|
+
this.webLocatorStrategies = [
|
|
337
|
+
'link text',
|
|
338
|
+
'css selector',
|
|
339
|
+
'tag name',
|
|
340
|
+
'link text',
|
|
341
|
+
'partial link text',
|
|
342
|
+
];
|
|
343
|
+
this.curWebFrames = [];
|
|
344
|
+
this._perfRecorders = [];
|
|
345
|
+
this.desiredCapConstraints = desiredCapConstraints;
|
|
346
|
+
this.webElementsCache = new LRUCache({
|
|
347
|
+
max: WEB_ELEMENTS_CACHE_SIZE,
|
|
348
|
+
});
|
|
349
|
+
this.webviewCalibrationResult = null;
|
|
350
|
+
this._waitingAtoms = {
|
|
351
|
+
count: 0,
|
|
352
|
+
alertNotifier: new EventEmitter(),
|
|
353
|
+
alertMonitor: B.resolve(),
|
|
354
|
+
};
|
|
355
|
+
this.resetIos();
|
|
356
|
+
this.settings = new DeviceSettings(DEFAULT_SETTINGS, this.onSettingsUpdate.bind(this));
|
|
357
|
+
this.logs = {};
|
|
358
|
+
this._trafficCapture = null;
|
|
359
|
+
// memoize functions here, so that they are done on a per-instance basis
|
|
360
|
+
for (const fn of MEMOIZED_FUNCTIONS) {
|
|
361
|
+
this[fn] = _.memoize(this[fn]);
|
|
362
|
+
}
|
|
363
|
+
this.lifecycleData = {};
|
|
364
|
+
this._audioRecorder = null;
|
|
365
|
+
this.appInfosCache = new AppInfosCache(this.log);
|
|
366
|
+
this.remote = null;
|
|
367
|
+
this.doesSupportBidi = true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async onSettingsUpdate(key, value) {
|
|
371
|
+
// skip sending the update request to the WDA nor saving it in opts
|
|
372
|
+
// to not spend unnecessary time.
|
|
373
|
+
if (['pageSourceExcludedAttributes'].includes(key)) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (key !== 'nativeWebTap' && key !== 'nativeWebTapStrict') {
|
|
378
|
+
return await this.proxyCommand('/appium/settings', 'POST', {
|
|
379
|
+
settings: {[key]: value},
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
this.opts[key] = !!value;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
resetIos() {
|
|
386
|
+
this.opts = this.opts || {};
|
|
387
|
+
// @ts-ignore this is ok
|
|
388
|
+
this.wda = null;
|
|
389
|
+
this.jwpProxyActive = false;
|
|
390
|
+
this.proxyReqRes = null;
|
|
391
|
+
this.safari = false;
|
|
392
|
+
this.cachedWdaStatus = null;
|
|
393
|
+
|
|
394
|
+
this.curWebFrames = [];
|
|
395
|
+
this._currentUrl = null;
|
|
396
|
+
this.curContext = null;
|
|
397
|
+
this.xcodeVersion = undefined;
|
|
398
|
+
this.contexts = [];
|
|
399
|
+
this.implicitWaitMs = 0;
|
|
400
|
+
this.pageLoadMs = 6000;
|
|
401
|
+
this.landscapeWebCoordsOffset = 0;
|
|
402
|
+
this.remote = null;
|
|
403
|
+
this._conditionInducerService = null;
|
|
404
|
+
|
|
405
|
+
this.webElementsCache = new LRUCache({
|
|
406
|
+
max: WEB_ELEMENTS_CACHE_SIZE,
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
this._waitingAtoms = {
|
|
410
|
+
count: 0,
|
|
411
|
+
alertNotifier: new EventEmitter(),
|
|
412
|
+
alertMonitor: B.resolve(),
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
get driverData() {
|
|
417
|
+
// TODO fill out resource info here
|
|
418
|
+
return {};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async getStatus() {
|
|
422
|
+
const status = {
|
|
423
|
+
ready: true,
|
|
424
|
+
message: 'The driver is ready to accept new connections',
|
|
425
|
+
build: await getDriverInfo(),
|
|
426
|
+
};
|
|
427
|
+
if (this.cachedWdaStatus) {
|
|
428
|
+
status.wda = this.cachedWdaStatus;
|
|
429
|
+
}
|
|
430
|
+
return status;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
mergeCliArgsToOpts() {
|
|
434
|
+
let didMerge = false;
|
|
435
|
+
// this.cliArgs should never include anything we do not expect.
|
|
436
|
+
for (const [key, value] of Object.entries(this.cliArgs ?? {})) {
|
|
437
|
+
if (_.has(this.opts, key)) {
|
|
438
|
+
this.log.info(
|
|
439
|
+
`CLI arg '${key}' with value '${value}' overwrites value '${this.opts[key]}' sent in via caps)`,
|
|
440
|
+
);
|
|
441
|
+
didMerge = true;
|
|
442
|
+
}
|
|
443
|
+
this.opts[key] = value;
|
|
444
|
+
}
|
|
445
|
+
return didMerge;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* @returns {Simulator|RealDevice}
|
|
450
|
+
*/
|
|
451
|
+
get device() {
|
|
452
|
+
return this._device;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
isXcodebuildNeeded() {
|
|
456
|
+
return !(CAP_NAMES_NO_XCODEBUILD_REQUIRED.some((x) => Boolean(this.opts[x])));
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
async createSession(w3cCaps1, w3cCaps2, w3cCaps3, driverData) {
|
|
460
|
+
try {
|
|
461
|
+
let [sessionId, caps] = await super.createSession(w3cCaps1, w3cCaps2, w3cCaps3, driverData);
|
|
462
|
+
|
|
463
|
+
// merge cli args to opts, and if we did merge any, revalidate opts to ensure the final set
|
|
464
|
+
// is also consistent
|
|
465
|
+
if (this.mergeCliArgsToOpts()) {
|
|
466
|
+
this.validateDesiredCaps({...caps, ...this.cliArgs});
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
await this.start();
|
|
470
|
+
|
|
471
|
+
// merge server capabilities + desired capabilities
|
|
472
|
+
caps = { ...defaultServerCaps, ...caps };
|
|
473
|
+
// update the udid with what is actually used
|
|
474
|
+
caps.udid = this.opts.udid;
|
|
475
|
+
// ensure we track nativeWebTap capability as a setting as well
|
|
476
|
+
if (_.has(this.opts, 'nativeWebTap')) {
|
|
477
|
+
await this.updateSettings({nativeWebTap: this.opts.nativeWebTap});
|
|
478
|
+
}
|
|
479
|
+
// ensure we track nativeWebTapStrict capability as a setting as well
|
|
480
|
+
if (_.has(this.opts, 'nativeWebTapStrict')) {
|
|
481
|
+
await this.updateSettings({nativeWebTapStrict: this.opts.nativeWebTapStrict});
|
|
482
|
+
}
|
|
483
|
+
// ensure we track useJSONSource capability as a setting as well
|
|
484
|
+
if (_.has(this.opts, 'useJSONSource')) {
|
|
485
|
+
await this.updateSettings({useJSONSource: this.opts.useJSONSource});
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/** @type {import('appium-webdriveragent').WDASettings} */
|
|
489
|
+
let wdaSettings = {
|
|
490
|
+
elementResponseAttributes: DEFAULT_SETTINGS.elementResponseAttributes,
|
|
491
|
+
shouldUseCompactResponses: DEFAULT_SETTINGS.shouldUseCompactResponses,
|
|
492
|
+
};
|
|
493
|
+
if ('elementResponseAttributes' in this.opts && _.isString(this.opts.elementResponseAttributes)) {
|
|
494
|
+
wdaSettings.elementResponseAttributes = this.opts.elementResponseAttributes;
|
|
495
|
+
}
|
|
496
|
+
if ('shouldUseCompactResponses' in this.opts && _.isBoolean(this.opts.shouldUseCompactResponses)) {
|
|
497
|
+
wdaSettings.shouldUseCompactResponses = this.opts.shouldUseCompactResponses;
|
|
498
|
+
}
|
|
499
|
+
if ('mjpegServerScreenshotQuality' in this.opts && _.isNumber(this.opts.mjpegServerScreenshotQuality)) {
|
|
500
|
+
wdaSettings.mjpegServerScreenshotQuality = this.opts.mjpegServerScreenshotQuality;
|
|
501
|
+
}
|
|
502
|
+
if ('mjpegServerFramerate' in this.opts && _.isNumber(this.opts.mjpegServerFramerate)) {
|
|
503
|
+
wdaSettings.mjpegServerFramerate = this.opts.mjpegServerFramerate;
|
|
504
|
+
}
|
|
505
|
+
if (_.has(this.opts, 'screenshotQuality')) {
|
|
506
|
+
this.log.info(`Setting the quality of phone screenshot: '${this.opts.screenshotQuality}'`);
|
|
507
|
+
wdaSettings.screenshotQuality = this.opts.screenshotQuality;
|
|
508
|
+
}
|
|
509
|
+
// ensure WDA gets our defaults instead of whatever its own might be
|
|
510
|
+
await this.updateSettings(wdaSettings);
|
|
511
|
+
|
|
512
|
+
await this.handleMjpegOptions();
|
|
513
|
+
|
|
514
|
+
return /** @type {[string, import('@appium/types').DriverCaps<XCUITestDriverConstraints>]} */ ([
|
|
515
|
+
sessionId,
|
|
516
|
+
caps,
|
|
517
|
+
]);
|
|
518
|
+
} catch (e) {
|
|
519
|
+
this.log.error(JSON.stringify(e));
|
|
520
|
+
await this.deleteSession();
|
|
521
|
+
throw e;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Handles MJPEG server-related capabilities
|
|
527
|
+
* @returns {Promise<void>}
|
|
528
|
+
*/
|
|
529
|
+
async handleMjpegOptions() {
|
|
530
|
+
await this.allocateMjpegServerPort();
|
|
531
|
+
// turn on mjpeg stream reading if requested
|
|
532
|
+
if (this.opts.mjpegScreenshotUrl) {
|
|
533
|
+
this.log.info(`Starting MJPEG stream reading URL: '${this.opts.mjpegScreenshotUrl}'`);
|
|
534
|
+
this.mjpegStream = new mjpeg.MJpegStream(this.opts.mjpegScreenshotUrl);
|
|
535
|
+
await this.mjpegStream.start();
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Allocates and configures port forwarding for the MJPEG server
|
|
541
|
+
* @returns {Promise<void>}
|
|
542
|
+
* @throws {Error} If port forwarding fails and mjpegServerPort capability value is provided explicitly
|
|
543
|
+
*/
|
|
544
|
+
async allocateMjpegServerPort() {
|
|
545
|
+
const mjpegServerPort = this.opts.mjpegServerPort || DEFAULT_MJPEG_SERVER_PORT;
|
|
546
|
+
this.log.debug(
|
|
547
|
+
`Forwarding MJPEG server port ${mjpegServerPort} to local port ${mjpegServerPort}`,
|
|
548
|
+
);
|
|
549
|
+
try {
|
|
550
|
+
await DEVICE_CONNECTIONS_FACTORY.requestConnection(this.opts.udid, mjpegServerPort, {
|
|
551
|
+
devicePort: mjpegServerPort,
|
|
552
|
+
usePortForwarding: this.isRealDevice(),
|
|
553
|
+
});
|
|
554
|
+
} catch (error) {
|
|
555
|
+
if (_.isUndefined(this.opts.mjpegServerPort)) {
|
|
556
|
+
this.log.warn(
|
|
557
|
+
`Cannot forward the device port ${DEFAULT_MJPEG_SERVER_PORT} to the local port ${DEFAULT_MJPEG_SERVER_PORT}. ` +
|
|
558
|
+
`Certain features, like MJPEG-based screen recording, will be unavailable during this session. ` +
|
|
559
|
+
`Try to customize the value of 'mjpegServerPort' capability as a possible solution`,
|
|
560
|
+
);
|
|
561
|
+
} else {
|
|
562
|
+
this.log.debug(error.stack);
|
|
563
|
+
throw new Error(
|
|
564
|
+
`Cannot ensure MJPEG broadcast functionality by forwarding the local port ${mjpegServerPort} ` +
|
|
565
|
+
`requested by the 'mjpegServerPort' capability to the device port ${mjpegServerPort}. ` +
|
|
566
|
+
`Original error: ${error}`,
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Returns the default URL for Safari browser
|
|
574
|
+
* @returns {string} The default URL
|
|
575
|
+
*/
|
|
576
|
+
getDefaultUrl() {
|
|
577
|
+
// Setting this to some external URL slows down the session init
|
|
578
|
+
return `${this.getWdaLocalhostRoot()}/health`;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
async start() {
|
|
582
|
+
this.opts.noReset = !!this.opts.noReset;
|
|
583
|
+
this.opts.fullReset = !!this.opts.fullReset;
|
|
584
|
+
|
|
585
|
+
await printUser();
|
|
586
|
+
this._iosSdkVersion = null; // For WDA and xcodebuild
|
|
587
|
+
const {device, udid, realDevice} = await this.determineDevice();
|
|
588
|
+
this.log.info(
|
|
589
|
+
`Determining device to run tests on: udid: '${udid}', real device: ${realDevice}`,
|
|
590
|
+
);
|
|
591
|
+
this._device = device;
|
|
592
|
+
this.opts.udid = udid;
|
|
593
|
+
|
|
594
|
+
if (this.opts.simulatorDevicesSetPath) {
|
|
595
|
+
if (realDevice) {
|
|
596
|
+
this.log.info(
|
|
597
|
+
`The 'simulatorDevicesSetPath' capability is only supported for Simulator devices`,
|
|
598
|
+
);
|
|
599
|
+
} else {
|
|
600
|
+
this.log.info(
|
|
601
|
+
`Setting simulator devices set path to '${this.opts.simulatorDevicesSetPath}'`,
|
|
602
|
+
);
|
|
603
|
+
(/** @type {Simulator} */ (this.device)).devicesSetPath = this.opts.simulatorDevicesSetPath;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// at this point if there is no platformVersion, get it from the device
|
|
608
|
+
if (!this.opts.platformVersion) {
|
|
609
|
+
this.opts.platformVersion = await this.device.getPlatformVersion();
|
|
610
|
+
this.log.info(
|
|
611
|
+
`No platformVersion specified. Using device version: '${this.opts.platformVersion}'`,
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const normalizedVersion = normalizePlatformVersion(this.opts.platformVersion);
|
|
616
|
+
if (this.opts.platformVersion !== normalizedVersion) {
|
|
617
|
+
this.log.info(
|
|
618
|
+
`Normalized platformVersion capability value '${this.opts.platformVersion}' to '${normalizedVersion}'`,
|
|
619
|
+
);
|
|
620
|
+
this.opts.platformVersion = normalizedVersion;
|
|
621
|
+
}
|
|
622
|
+
this.caps.platformVersion = this.opts.platformVersion;
|
|
623
|
+
|
|
624
|
+
if (_.isEmpty(this.xcodeVersion) && (this.isXcodebuildNeeded() || this.isSimulator())) {
|
|
625
|
+
// no `webDriverAgentUrl`, or on a simulator, so we need an Xcode version
|
|
626
|
+
this.xcodeVersion = await getAndCheckXcodeVersion();
|
|
627
|
+
}
|
|
628
|
+
this.logEvent('xcodeDetailsRetrieved');
|
|
629
|
+
|
|
630
|
+
if (_.toLower(this.opts.browserName) === 'safari') {
|
|
631
|
+
this.log.info('Safari test requested');
|
|
632
|
+
this.safari = true;
|
|
633
|
+
this.opts.app = undefined;
|
|
634
|
+
this.opts.processArguments = this.opts.processArguments || {};
|
|
635
|
+
applySafariStartupArgs.bind(this)();
|
|
636
|
+
this.opts.bundleId = SAFARI_BUNDLE_ID;
|
|
637
|
+
this._currentUrl = this.opts.safariInitialUrl || this.getDefaultUrl();
|
|
638
|
+
} else if (this.opts.app || this.opts.bundleId) {
|
|
639
|
+
await this.configureApp();
|
|
640
|
+
}
|
|
641
|
+
this.logEvent('appConfigured');
|
|
642
|
+
|
|
643
|
+
// fail very early if the app doesn't actually exist
|
|
644
|
+
// or if bundle id doesn't point to an installed app
|
|
645
|
+
if (this.opts.app) {
|
|
646
|
+
await checkAppPresent(this.opts.app);
|
|
647
|
+
|
|
648
|
+
if (!this.opts.bundleId) {
|
|
649
|
+
this.opts.bundleId = await this.appInfosCache.extractBundleId(this.opts.app);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
await this.runReset();
|
|
654
|
+
|
|
655
|
+
this.wda = new WebDriverAgent(
|
|
656
|
+
/** @type {import('@limrun/appium-xcode').XcodeVersion} */ (this.xcodeVersion),
|
|
657
|
+
{
|
|
658
|
+
...this.opts,
|
|
659
|
+
device: this.device,
|
|
660
|
+
realDevice: this.isRealDevice(),
|
|
661
|
+
iosSdkVersion: this._iosSdkVersion ?? undefined,
|
|
662
|
+
reqBasePath: this.basePath,
|
|
663
|
+
},
|
|
664
|
+
// @ts-ignore this is ok
|
|
665
|
+
this.log,
|
|
666
|
+
);
|
|
667
|
+
// Derived data path retrieval is an expensive operation
|
|
668
|
+
// We could start that now in background and get the cached result
|
|
669
|
+
// whenever it is needed
|
|
670
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
671
|
+
this.wda.retrieveDerivedDataPath().catch((e) => this.log.debug(e));
|
|
672
|
+
|
|
673
|
+
const memoizedLogInfo = _.memoize(() => {
|
|
674
|
+
this.log.info(
|
|
675
|
+
"'skipLogCapture' is set. Skipping starting logs such as crash, system, safari console and safari network.",
|
|
676
|
+
);
|
|
677
|
+
});
|
|
678
|
+
const startLogCapture = async () => {
|
|
679
|
+
if (this.opts.skipLogCapture) {
|
|
680
|
+
memoizedLogInfo();
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const result = await this.startLogCapture();
|
|
685
|
+
if (result) {
|
|
686
|
+
this.logEvent('logCaptureStarted');
|
|
687
|
+
}
|
|
688
|
+
return result;
|
|
689
|
+
};
|
|
690
|
+
const isLogCaptureStarted = await startLogCapture();
|
|
691
|
+
|
|
692
|
+
this.log.info(`Setting up ${this.isRealDevice() ? 'real device' : 'simulator'}`);
|
|
693
|
+
|
|
694
|
+
if (this.isSimulator()) {
|
|
695
|
+
await this.initSimulator();
|
|
696
|
+
if (!isLogCaptureStarted) {
|
|
697
|
+
// Retry log capture if Simulator was not running before
|
|
698
|
+
await startLogCapture();
|
|
699
|
+
}
|
|
700
|
+
} else if (this.opts.customSSLCert) {
|
|
701
|
+
await new Pyidevice({
|
|
702
|
+
udid,
|
|
703
|
+
log: this.log,
|
|
704
|
+
}).installProfile({payload: this.opts.customSSLCert});
|
|
705
|
+
this.logEvent('customCertInstalled');
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
await this.installAUT();
|
|
709
|
+
|
|
710
|
+
// if we only have bundle identifier and no app, fail if it is not already installed
|
|
711
|
+
if (
|
|
712
|
+
!this.opts.app &&
|
|
713
|
+
this.opts.bundleId &&
|
|
714
|
+
!this.isSafari() &&
|
|
715
|
+
!(await this.device.isAppInstalled(this.opts.bundleId))
|
|
716
|
+
) {
|
|
717
|
+
throw this.log.errorWithException(`App with bundle identifier '${this.opts.bundleId}' unknown`);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (this.isSimulator()) {
|
|
721
|
+
if (this.opts.permissions) {
|
|
722
|
+
this.log.debug('Setting the requested permissions before WDA is started');
|
|
723
|
+
for (const [bundleId, permissionsMapping] of _.toPairs(JSON.parse(this.opts.permissions))) {
|
|
724
|
+
await /** @type {Simulator} */ (this.device).setPermissions(bundleId, permissionsMapping);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// TODO: Deprecate and remove this block together with calendarAccessAuthorized capability
|
|
729
|
+
if (_.isBoolean(this.opts.calendarAccessAuthorized)) {
|
|
730
|
+
this.log.warn(
|
|
731
|
+
`The 'calendarAccessAuthorized' capability is deprecated and will be removed soon. ` +
|
|
732
|
+
`Consider using 'permissions' one instead with 'calendar' key`,
|
|
733
|
+
);
|
|
734
|
+
const methodName = `${
|
|
735
|
+
this.opts.calendarAccessAuthorized ? 'enable' : 'disable'
|
|
736
|
+
}CalendarAccess`;
|
|
737
|
+
await this.device[methodName](this.opts.bundleId);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
await this.startWda();
|
|
742
|
+
|
|
743
|
+
if (_.isString(this.opts.orientation)) {
|
|
744
|
+
await this.setInitialOrientation(this.opts.orientation);
|
|
745
|
+
this.logEvent('orientationSet');
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (this.isSafari() || this.opts.autoWebview) {
|
|
749
|
+
await this.activateRecentWebview();
|
|
750
|
+
} else {
|
|
751
|
+
// We want to always setup the initial context value upon session startup
|
|
752
|
+
await notifyBiDiContextChange.bind(this)();
|
|
753
|
+
}
|
|
754
|
+
if (this.isSafari()) {
|
|
755
|
+
if (shouldSetInitialSafariUrl(this.opts)) {
|
|
756
|
+
this.log.info(`About to set the initial Safari URL to '${this.getCurrentUrl()}'`);
|
|
757
|
+
if (_.isNil(this.opts.safariInitialUrl) && _.isNil(this.opts.initialDeeplinkUrl)) {
|
|
758
|
+
this.log.info(`Use the 'safariInitialUrl' capability to customize it`);
|
|
759
|
+
};
|
|
760
|
+
await this.setUrl(this.getCurrentUrl() || this.getDefaultUrl());
|
|
761
|
+
} else {
|
|
762
|
+
const currentUrl = await this.getUrl();
|
|
763
|
+
this.log.info(`Current URL: ${currentUrl}`);
|
|
764
|
+
this.setCurrentUrl(currentUrl);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Start the simulator and initialize based on capabilities
|
|
771
|
+
*/
|
|
772
|
+
async initSimulator() {
|
|
773
|
+
const device = /** @type {Simulator} */ (this.device);
|
|
774
|
+
|
|
775
|
+
if (this.opts.shutdownOtherSimulators) {
|
|
776
|
+
this.assertFeatureEnabled(SHUTDOWN_OTHER_FEAT_NAME);
|
|
777
|
+
await shutdownOtherSimulators.bind(this)();
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
await this.startSim();
|
|
781
|
+
|
|
782
|
+
if (this.opts.customSSLCert) {
|
|
783
|
+
// Simulator must be booted in order to call this helper
|
|
784
|
+
await device.addCertificate(this.opts.customSSLCert);
|
|
785
|
+
this.logEvent('customCertInstalled');
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (await setSafariPrefs.bind(this)()) {
|
|
789
|
+
this.log.debug('Safari preferences have been updated');
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
if (await setLocalizationPrefs.bind(this)()) {
|
|
793
|
+
this.log.debug('Localization preferences have been updated');
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/** @type {Promise[]} */
|
|
797
|
+
const promises = ['reduceMotion', 'reduceTransparency', 'autoFillPasswords']
|
|
798
|
+
.filter((optName) => _.isBoolean(this.opts[optName]))
|
|
799
|
+
.map((optName) => {
|
|
800
|
+
this.log.info(`Setting ${optName} to ${this.opts[optName]}`);
|
|
801
|
+
return device[`set${_.upperFirst(optName)}`](this.opts[optName]);
|
|
802
|
+
});
|
|
803
|
+
await B.all(promises);
|
|
804
|
+
|
|
805
|
+
if (this.opts.launchWithIDB) {
|
|
806
|
+
try {
|
|
807
|
+
const idb = new IDB({udid: this.opts.udid});
|
|
808
|
+
await idb.connect();
|
|
809
|
+
device.idb = idb;
|
|
810
|
+
} catch (e) {
|
|
811
|
+
this.log.debug(e.stack);
|
|
812
|
+
this.log.warn(
|
|
813
|
+
`idb will not be used for Simulator interaction. Original error: ${e.message}`,
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
this.logEvent('simStarted');
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Start WebDriverAgentRunner
|
|
823
|
+
*/
|
|
824
|
+
async startWda() {
|
|
825
|
+
// Don't cleanup the processes if webDriverAgentUrl is set
|
|
826
|
+
if (!util.hasValue(this.wda.webDriverAgentUrl)) {
|
|
827
|
+
await this.wda.cleanupObsoleteProcesses();
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const usePortForwarding =
|
|
831
|
+
this.isRealDevice() && !this.wda.webDriverAgentUrl && isLocalHost(this.wda.wdaBaseUrl);
|
|
832
|
+
await DEVICE_CONNECTIONS_FACTORY.requestConnection(this.opts.udid, this.wda.url.port, {
|
|
833
|
+
devicePort: usePortForwarding ? this.wda.wdaRemotePort : null,
|
|
834
|
+
usePortForwarding,
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
// Let multiple WDA binaries with different derived data folders be built in parallel
|
|
838
|
+
// Concurrent WDA builds from the same source will cause xcodebuild synchronization errors
|
|
839
|
+
let synchronizationKey = XCUITestDriver.name;
|
|
840
|
+
if (this.opts.useXctestrunFile || !(await this.wda.isSourceFresh())) {
|
|
841
|
+
// First-time compilation is an expensive operation, which is done faster if executed
|
|
842
|
+
// sequentially. Xcodebuild spreads the load caused by the clang compiler to all available CPU cores
|
|
843
|
+
const derivedDataPath = await this.wda.retrieveDerivedDataPath();
|
|
844
|
+
if (derivedDataPath) {
|
|
845
|
+
synchronizationKey = path.normalize(derivedDataPath);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
this.log.debug(
|
|
849
|
+
`Starting WebDriverAgent initialization with the synchronization key '${synchronizationKey}'`,
|
|
850
|
+
);
|
|
851
|
+
if (SHARED_RESOURCES_GUARD.isBusy() && !this.opts.derivedDataPath && !this.opts.bootstrapPath) {
|
|
852
|
+
this.log.debug(
|
|
853
|
+
`Consider setting a unique 'derivedDataPath' capability value for each parallel driver instance ` +
|
|
854
|
+
`to avoid conflicts and speed up the building process`,
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (this.opts.usePreinstalledWDA && this.opts.prebuiltWDAPath && !(await fs.exists(this.opts.prebuiltWDAPath))) {
|
|
859
|
+
throw new Error(
|
|
860
|
+
`The prebuilt WebDriverAgent app at '${this.opts.prebuiltWDAPath}' provided as 'prebuiltWDAPath' ` +
|
|
861
|
+
`capability value does not exist or is not accessible`
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
return await SHARED_RESOURCES_GUARD.acquire(synchronizationKey, async () => {
|
|
866
|
+
if (this.opts.useNewWDA) {
|
|
867
|
+
this.log.debug(`Capability 'useNewWDA' set to true, so uninstalling WDA before proceeding`);
|
|
868
|
+
await this.wda.quitAndUninstall();
|
|
869
|
+
this.logEvent('wdaUninstalled');
|
|
870
|
+
} else if (!util.hasValue(this.wda.webDriverAgentUrl) && this.isXcodebuildNeeded()) {
|
|
871
|
+
await this.wda.setupCaching();
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// local helper for the two places we need to uninstall wda and re-start it
|
|
875
|
+
const quitAndUninstall = async (msg) => {
|
|
876
|
+
this.log.debug(msg);
|
|
877
|
+
if (!this.isXcodebuildNeeded()) {
|
|
878
|
+
this.log.debug(
|
|
879
|
+
`Not quitting/uninstalling WebDriverAgent since at least one of ${CAP_NAMES_NO_XCODEBUILD_REQUIRED} capabilities is provided`,
|
|
880
|
+
);
|
|
881
|
+
throw new Error(msg);
|
|
882
|
+
}
|
|
883
|
+
this.log.warn('Quitting and uninstalling WebDriverAgent');
|
|
884
|
+
await this.wda.quitAndUninstall();
|
|
885
|
+
|
|
886
|
+
throw new Error(msg);
|
|
887
|
+
};
|
|
888
|
+
|
|
889
|
+
// Used in the following WDA build
|
|
890
|
+
if (this.opts.resultBundlePath) {
|
|
891
|
+
this.assertFeatureEnabled(CUSTOMIZE_RESULT_BUNDLE_PATH);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
let startupRetries =
|
|
895
|
+
this.opts.wdaStartupRetries ||
|
|
896
|
+
(this.isRealDevice() ? WDA_REAL_DEV_STARTUP_RETRIES : WDA_SIM_STARTUP_RETRIES);
|
|
897
|
+
const startupRetryInterval = this.opts.wdaStartupRetryInterval || WDA_STARTUP_RETRY_INTERVAL;
|
|
898
|
+
|
|
899
|
+
// These values help only xcodebuild.
|
|
900
|
+
if (this.isXcodebuildNeeded()) {
|
|
901
|
+
this.log.debug(
|
|
902
|
+
`Trying to start WebDriverAgent ${startupRetries} times with ${startupRetryInterval}ms interval`,
|
|
903
|
+
);
|
|
904
|
+
if (
|
|
905
|
+
!util.hasValue(this.opts.wdaStartupRetries) &&
|
|
906
|
+
!util.hasValue(this.opts.wdaStartupRetryInterval)
|
|
907
|
+
) {
|
|
908
|
+
this.log.debug(
|
|
909
|
+
`These values can be customized by changing wdaStartupRetries/wdaStartupRetryInterval capabilities`,
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
} else {
|
|
913
|
+
// The startup retry will be one time if the session does not need WDA build
|
|
914
|
+
this.log.debug(`Trying to start WebDriverAgent once since at least one of ${CAP_NAMES_NO_XCODEBUILD_REQUIRED} capabilities is provided`);
|
|
915
|
+
startupRetries = 1;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/** @type {Error|null} */
|
|
919
|
+
let shortCircuitError = null;
|
|
920
|
+
let retryCount = 0;
|
|
921
|
+
await retryInterval(startupRetries, startupRetryInterval, async () => {
|
|
922
|
+
this.logEvent('wdaStartAttempted');
|
|
923
|
+
if (retryCount > 0) {
|
|
924
|
+
this.log.info(`Retrying WDA startup (${retryCount + 1} of ${startupRetries})`);
|
|
925
|
+
}
|
|
926
|
+
try {
|
|
927
|
+
if (this.opts.usePreinstalledWDA) {
|
|
928
|
+
await this.preparePreinstalledWda();
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
this.cachedWdaStatus = await this.wda.launch(/** @type {string} */ (this.sessionId));
|
|
932
|
+
} catch (err) {
|
|
933
|
+
this.logEvent('wdaStartFailed');
|
|
934
|
+
this.log.debug(err.stack);
|
|
935
|
+
retryCount++;
|
|
936
|
+
let errorMsg = `Unable to launch WebDriverAgent. Original error: ${err.message}`;
|
|
937
|
+
if (this.isRealDevice()) {
|
|
938
|
+
errorMsg += `. Make sure you follow the tutorial at ${WDA_REAL_DEV_TUTORIAL_URL}`;
|
|
939
|
+
}
|
|
940
|
+
if (this.opts.usePreinstalledWDA) {
|
|
941
|
+
try {
|
|
942
|
+
// In case the bundle id process start got failed because of
|
|
943
|
+
// auth popup in the device. Then, the bundle id process itself started. It is safe to stop it here.
|
|
944
|
+
await this.mobileKillApp(this.wda.bundleIdForXctest);
|
|
945
|
+
} catch {};
|
|
946
|
+
// Mostly it failed to start the WDA process as no the bundle id
|
|
947
|
+
// e.g. '<bundle id of WDA> not found on device <udid>'
|
|
948
|
+
|
|
949
|
+
errorMsg = `Unable to launch WebDriverAgent. Original error: ${err.message}. ` +
|
|
950
|
+
`Make sure the application ${this.wda.bundleIdForXctest} exists and it is launchable.`;
|
|
951
|
+
if (this.isRealDevice()) {
|
|
952
|
+
errorMsg += ` ${WDA_REAL_DEV_TUTORIAL_URL} may help to complete the preparation.`;
|
|
953
|
+
};
|
|
954
|
+
throw new Error(errorMsg);
|
|
955
|
+
} else {
|
|
956
|
+
await quitAndUninstall(errorMsg);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
this.proxyReqRes = this.wda.proxyReqRes.bind(this.wda);
|
|
961
|
+
this.jwpProxyActive = true;
|
|
962
|
+
|
|
963
|
+
try {
|
|
964
|
+
this.logEvent('wdaSessionAttempted');
|
|
965
|
+
this.log.debug('Sending createSession command to WDA');
|
|
966
|
+
this.cachedWdaStatus = this.cachedWdaStatus || (await this.proxyCommand('/status', 'GET'));
|
|
967
|
+
await this.startWdaSession(this.opts.bundleId, this.opts.processArguments);
|
|
968
|
+
this.logEvent('wdaSessionStarted');
|
|
969
|
+
} catch (err) {
|
|
970
|
+
this.logEvent('wdaSessionFailed');
|
|
971
|
+
this.log.debug(err.stack);
|
|
972
|
+
if (err instanceof errors.TimeoutError) {
|
|
973
|
+
// Session startup timed out. There is no point to retry
|
|
974
|
+
shortCircuitError = err;
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
let errorMsg = `Unable to start WebDriverAgent session. Original error: ${err.message}`;
|
|
978
|
+
if (this.isRealDevice() && _.includes(err.message, 'xcodebuild')) {
|
|
979
|
+
errorMsg += ` Make sure you follow the tutorial at ${WDA_REAL_DEV_TUTORIAL_URL}.`;
|
|
980
|
+
}
|
|
981
|
+
throw new Error(errorMsg);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
if (this.opts.clearSystemFiles && this.isXcodebuildNeeded()) {
|
|
985
|
+
await markSystemFilesForCleanup(this.wda);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// We don't restrict the version, but show what version of WDA is running on the device for debugging purposes.
|
|
989
|
+
if (this.cachedWdaStatus?.build) {
|
|
990
|
+
this.log.info(`WebDriverAgent version: '${this.cachedWdaStatus.build.version}'`);
|
|
991
|
+
} else {
|
|
992
|
+
this.log.warn(
|
|
993
|
+
`WebDriverAgent does not provide any version information. ` +
|
|
994
|
+
`This might indicate either a custom or an outdated build.`
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// we expect certain socket errors until this point, but now
|
|
999
|
+
// mark things as fully working
|
|
1000
|
+
this.wda.fullyStarted = true;
|
|
1001
|
+
this.logEvent('wdaStarted');
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
if (shortCircuitError) {
|
|
1005
|
+
throw shortCircuitError;
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
*
|
|
1012
|
+
* @param {boolean} [enforceSimulatorShutdown=false]
|
|
1013
|
+
*/
|
|
1014
|
+
async runReset(enforceSimulatorShutdown = false) {
|
|
1015
|
+
this.logEvent('resetStarted');
|
|
1016
|
+
if (this.isRealDevice()) {
|
|
1017
|
+
await runRealDeviceReset.bind(this)();
|
|
1018
|
+
} else {
|
|
1019
|
+
await runSimulatorReset.bind(this)(enforceSimulatorShutdown);
|
|
1020
|
+
}
|
|
1021
|
+
this.logEvent('resetComplete');
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
async deleteSession(sessionId) {
|
|
1025
|
+
await removeAllSessionWebSocketHandlers.bind(this)();
|
|
1026
|
+
|
|
1027
|
+
for (const recorder of _.compact([
|
|
1028
|
+
this._recentScreenRecorder,
|
|
1029
|
+
this._audioRecorder,
|
|
1030
|
+
this._trafficCapture,
|
|
1031
|
+
])) {
|
|
1032
|
+
await recorder.interrupt(true);
|
|
1033
|
+
await recorder.cleanup();
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
if (!_.isEmpty(this._perfRecorders)) {
|
|
1037
|
+
await B.all(this._perfRecorders.map((x) => x.stop(true)));
|
|
1038
|
+
this._perfRecorders = [];
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
if (this._conditionInducerService) {
|
|
1042
|
+
this.disableConditionInducer();
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
await this.stop();
|
|
1046
|
+
|
|
1047
|
+
if (this.wda && this.isXcodebuildNeeded()) {
|
|
1048
|
+
if (this.opts.clearSystemFiles) {
|
|
1049
|
+
let synchronizationKey = XCUITestDriver.name;
|
|
1050
|
+
const derivedDataPath = await this.wda.retrieveDerivedDataPath();
|
|
1051
|
+
if (derivedDataPath) {
|
|
1052
|
+
synchronizationKey = path.normalize(derivedDataPath);
|
|
1053
|
+
}
|
|
1054
|
+
await SHARED_RESOURCES_GUARD.acquire(synchronizationKey, async () => {
|
|
1055
|
+
await clearSystemFiles(this.wda);
|
|
1056
|
+
});
|
|
1057
|
+
} else {
|
|
1058
|
+
this.log.debug('Not clearing log files. Use `clearSystemFiles` capability to turn on.');
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
if (this.remote) {
|
|
1063
|
+
this.log.debug('Found a remote debugger session. Removing...');
|
|
1064
|
+
await this.stopRemote();
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
if (this.opts.resetOnSessionStartOnly === false) {
|
|
1068
|
+
await this.runReset(true);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
const simulatorDevice = this.isSimulator() ? /** @type {Simulator} */ (this.device) : null;
|
|
1072
|
+
if (simulatorDevice && this.lifecycleData.createSim) {
|
|
1073
|
+
this.log.debug(`Deleting simulator created for this run (udid: '${simulatorDevice.udid}')`);
|
|
1074
|
+
await shutdownSimulator.bind(this)();
|
|
1075
|
+
await simulatorDevice.delete();
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
const shouldResetLocationService = this.isRealDevice() && !!this.opts.resetLocationService;
|
|
1079
|
+
if (shouldResetLocationService) {
|
|
1080
|
+
try {
|
|
1081
|
+
await this.mobileResetLocationService();
|
|
1082
|
+
} catch {
|
|
1083
|
+
/* Ignore this error since mobileResetLocationService already logged the error */
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
await this.logs.syslog?.stopCapture();
|
|
1088
|
+
_.values(this.logs).forEach((x) => x.removeAllListeners());
|
|
1089
|
+
if (this._bidiServerLogListener) {
|
|
1090
|
+
this.log.unwrap().off('log', this._bidiServerLogListener);
|
|
1091
|
+
}
|
|
1092
|
+
this.logs = {};
|
|
1093
|
+
|
|
1094
|
+
if (this.mjpegStream) {
|
|
1095
|
+
this.log.info('Closing MJPEG stream');
|
|
1096
|
+
this.mjpegStream.stop();
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
this.resetIos();
|
|
1100
|
+
|
|
1101
|
+
await super.deleteSession(sessionId);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
async stop() {
|
|
1105
|
+
this.jwpProxyActive = false;
|
|
1106
|
+
this.proxyReqRes = null;
|
|
1107
|
+
|
|
1108
|
+
if (this.wda?.fullyStarted) {
|
|
1109
|
+
if (this.wda.jwproxy) {
|
|
1110
|
+
try {
|
|
1111
|
+
await this.proxyCommand(`/session/${this.sessionId}`, 'DELETE');
|
|
1112
|
+
} catch (err) {
|
|
1113
|
+
// an error here should not short-circuit the rest of clean up
|
|
1114
|
+
this.log.debug(`Unable to DELETE session on WDA: '${err.message}'. Continuing shutdown.`);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
// The former could cache the xcodebuild, so should not quit the process.
|
|
1118
|
+
// If the session skipped the xcodebuild (this.wda.canSkipXcodebuild), the this.wda instance
|
|
1119
|
+
// should quit properly.
|
|
1120
|
+
if ((!this.wda.webDriverAgentUrl && this.opts.useNewWDA) || this.wda.canSkipXcodebuild) {
|
|
1121
|
+
await this.wda.quit();
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
DEVICE_CONNECTIONS_FACTORY.releaseConnection(this.opts.udid);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/**
|
|
1128
|
+
*
|
|
1129
|
+
* @param {string} cmd
|
|
1130
|
+
* @param {...any} args
|
|
1131
|
+
* @returns {Promise<any>}
|
|
1132
|
+
*/
|
|
1133
|
+
async executeCommand(cmd, ...args) {
|
|
1134
|
+
this.log.debug(`Executing command '${cmd}'`);
|
|
1135
|
+
|
|
1136
|
+
if (cmd === 'receiveAsyncResponse') {
|
|
1137
|
+
return await this.receiveAsyncResponse(...args);
|
|
1138
|
+
}
|
|
1139
|
+
// TODO: once this fix gets into base driver remove from here
|
|
1140
|
+
if (cmd === 'getStatus') {
|
|
1141
|
+
return await this.getStatus();
|
|
1142
|
+
}
|
|
1143
|
+
return await super.executeCommand(cmd, ...args);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
async configureApp() {
|
|
1147
|
+
function appIsPackageOrBundle(app) {
|
|
1148
|
+
return /^([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)+$/.test(app);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// the app name is a bundleId assign it to the bundleId property
|
|
1152
|
+
if (!this.opts.bundleId && appIsPackageOrBundle(this.opts.app)) {
|
|
1153
|
+
this.opts.bundleId = this.opts.app;
|
|
1154
|
+
this.opts.app = '';
|
|
1155
|
+
}
|
|
1156
|
+
// we have a bundle ID, but no app, or app is also a bundle
|
|
1157
|
+
if (
|
|
1158
|
+
this.opts.bundleId &&
|
|
1159
|
+
appIsPackageOrBundle(this.opts.bundleId) &&
|
|
1160
|
+
(this.opts.app === '' || appIsPackageOrBundle(this.opts.app))
|
|
1161
|
+
) {
|
|
1162
|
+
this.log.debug('App is an iOS bundle, will attempt to run as pre-existing');
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// check for supported build-in apps
|
|
1167
|
+
switch (_.toLower(this.opts.app)) {
|
|
1168
|
+
case 'settings':
|
|
1169
|
+
this.opts.bundleId = 'com.apple.Preferences';
|
|
1170
|
+
this.opts.app = null;
|
|
1171
|
+
return;
|
|
1172
|
+
case 'calendar':
|
|
1173
|
+
this.opts.bundleId = 'com.apple.mobilecal';
|
|
1174
|
+
this.opts.app = null;
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
this.opts.app = await this.helpers.configureApp(this.opts.app, {
|
|
1179
|
+
onPostProcess: onPostConfigureApp.bind(this),
|
|
1180
|
+
onDownload: onDownloadApp.bind(this),
|
|
1181
|
+
supportedExtensions: SUPPORTED_EXTENSIONS,
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
async determineDevice() {
|
|
1186
|
+
// in the one case where we create a sim, we will set this state
|
|
1187
|
+
this.lifecycleData.createSim = false;
|
|
1188
|
+
|
|
1189
|
+
// if we get generic names, translate them
|
|
1190
|
+
this.opts.deviceName = translateDeviceName(this.opts.platformVersion ?? '', this.opts.deviceName);
|
|
1191
|
+
|
|
1192
|
+
const setupVersionCaps = async () => {
|
|
1193
|
+
this._iosSdkVersion = await getAndCheckIosSdkVersion();
|
|
1194
|
+
this.log.info(`iOS SDK Version set to '${this._iosSdkVersion}'`);
|
|
1195
|
+
if (!this.opts.platformVersion && this._iosSdkVersion) {
|
|
1196
|
+
this.log.info(
|
|
1197
|
+
`No platformVersion specified. Using the latest version Xcode supports: '${this._iosSdkVersion}'. ` +
|
|
1198
|
+
`This may cause problems if a simulator does not exist for this platform version.`,
|
|
1199
|
+
);
|
|
1200
|
+
this.opts.platformVersion = normalizePlatformVersion(this._iosSdkVersion);
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
|
|
1204
|
+
if (this.opts.udid) {
|
|
1205
|
+
if (this.opts.udid.toLowerCase() === UDID_AUTO) {
|
|
1206
|
+
try {
|
|
1207
|
+
this.opts.udid = await detectUdid.bind(this)();
|
|
1208
|
+
} catch (err) {
|
|
1209
|
+
// Trying to find matching UDID for Simulator
|
|
1210
|
+
this.log.warn(
|
|
1211
|
+
`Cannot detect any connected real devices. Falling back to Simulator. Original error: ${err.message}`,
|
|
1212
|
+
);
|
|
1213
|
+
await setupVersionCaps();
|
|
1214
|
+
|
|
1215
|
+
const device = await getExistingSim.bind(this)();
|
|
1216
|
+
if (!device) {
|
|
1217
|
+
// No matching Simulator is found. Throw an error
|
|
1218
|
+
throw this.log.errorWithException(
|
|
1219
|
+
`Cannot detect udid for ${this.opts.deviceName} Simulator running iOS ${this.opts.platformVersion}`,
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
this.opts.udid = device.udid;
|
|
1223
|
+
return {device, realDevice: false, udid: device.udid};
|
|
1224
|
+
}
|
|
1225
|
+
} else {
|
|
1226
|
+
// If the session specified this.opts.webDriverAgentUrl with a real device,
|
|
1227
|
+
// we can assume the user prepared the device properly already.
|
|
1228
|
+
let isRealDeviceUdid = false;
|
|
1229
|
+
const shouldCheckAvailableRealDevices = !this.opts.webDriverAgentUrl;
|
|
1230
|
+
if (shouldCheckAvailableRealDevices) {
|
|
1231
|
+
const devices = await getConnectedDevices();
|
|
1232
|
+
this.log.debug(`Available real devices: ${devices.join(', ')}`);
|
|
1233
|
+
isRealDeviceUdid = devices.includes(this.opts.udid);
|
|
1234
|
+
}
|
|
1235
|
+
if (!isRealDeviceUdid) {
|
|
1236
|
+
try {
|
|
1237
|
+
const device = await getSimulator(this.opts.udid, {
|
|
1238
|
+
devicesSetPath: this.opts.simulatorDevicesSetPath,
|
|
1239
|
+
// @ts-ignore This is ok
|
|
1240
|
+
logger: this.log,
|
|
1241
|
+
});
|
|
1242
|
+
return {device, realDevice: false, udid: this.opts.udid};
|
|
1243
|
+
} catch {
|
|
1244
|
+
if (shouldCheckAvailableRealDevices) {
|
|
1245
|
+
throw new Error(`Unknown device or simulator UDID: '${this.opts.udid}'`);
|
|
1246
|
+
}
|
|
1247
|
+
this.log.debug(
|
|
1248
|
+
'Skipping checking of the real devices availability since the session specifies appium:webDriverAgentUrl'
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
this.log.debug(`Creating iDevice object with udid '${this.opts.udid}'`);
|
|
1255
|
+
const device = new RealDevice(this.opts.udid, this.log);
|
|
1256
|
+
return {device, realDevice: true, udid: this.opts.udid};
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
this.log.info(
|
|
1260
|
+
`No real device udid has been provided in capabilities. ` +
|
|
1261
|
+
`Will select a matching simulator to run the test.`,
|
|
1262
|
+
);
|
|
1263
|
+
await setupVersionCaps();
|
|
1264
|
+
if (this.opts.enforceFreshSimulatorCreation) {
|
|
1265
|
+
this.log.debug(
|
|
1266
|
+
`New simulator is requested. If this is not wanted, set 'enforceFreshSimulatorCreation' capability to false`,
|
|
1267
|
+
);
|
|
1268
|
+
} else {
|
|
1269
|
+
// figure out the correct simulator to use, given the desired capabilities
|
|
1270
|
+
const device = await getExistingSim.bind(this)();
|
|
1271
|
+
// check for an existing simulator
|
|
1272
|
+
if (device) {
|
|
1273
|
+
return {device, realDevice: false, udid: device.udid};
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
// no device of this type exists, or they request new sim, so create one
|
|
1278
|
+
this.log.info('Using desired caps to create a new simulator');
|
|
1279
|
+
const device = await this.createSim();
|
|
1280
|
+
return {device, realDevice: false, udid: device.udid};
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
async startSim() {
|
|
1284
|
+
/** @type {import('@limrun/appium-ios-simulator').DevicePreferences} */
|
|
1285
|
+
const devicePreferences = {};
|
|
1286
|
+
/** @type {import('@limrun/appium-ios-simulator').RunOptions} */
|
|
1287
|
+
const runOpts = {
|
|
1288
|
+
scaleFactor: this.opts.scaleFactor,
|
|
1289
|
+
connectHardwareKeyboard: !!this.opts.connectHardwareKeyboard,
|
|
1290
|
+
pasteboardAutomaticSync: this.opts.simulatorPasteboardAutomaticSync ?? 'off',
|
|
1291
|
+
isHeadless: !!this.opts.isHeadless,
|
|
1292
|
+
tracePointer: this.opts.simulatorTracePointer,
|
|
1293
|
+
devicePreferences,
|
|
1294
|
+
};
|
|
1295
|
+
|
|
1296
|
+
// add the window center, if it is specified
|
|
1297
|
+
if (this.opts.simulatorWindowCenter) {
|
|
1298
|
+
devicePreferences.SimulatorWindowCenter = this.opts.simulatorWindowCenter;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
if (_.isInteger(this.opts.simulatorStartupTimeout)) {
|
|
1302
|
+
runOpts.startupTimeout = this.opts.simulatorStartupTimeout;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
// This is to workaround XCTest bug about changing Simulator
|
|
1306
|
+
// orientation is not synchronized to the actual window orientation
|
|
1307
|
+
const orientation = _.isString(this.opts.orientation) && this.opts.orientation.toUpperCase();
|
|
1308
|
+
switch (orientation) {
|
|
1309
|
+
case 'LANDSCAPE':
|
|
1310
|
+
devicePreferences.SimulatorWindowOrientation = 'LandscapeLeft';
|
|
1311
|
+
devicePreferences.SimulatorWindowRotationAngle = 90;
|
|
1312
|
+
break;
|
|
1313
|
+
case 'PORTRAIT':
|
|
1314
|
+
devicePreferences.SimulatorWindowOrientation = 'Portrait';
|
|
1315
|
+
devicePreferences.SimulatorWindowRotationAngle = 0;
|
|
1316
|
+
break;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
await /** @type {Simulator} */ (this.device).run(runOpts);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
async createSim() {
|
|
1323
|
+
this.lifecycleData.createSim = true;
|
|
1324
|
+
// create sim for caps
|
|
1325
|
+
const sim = await createSim.bind(this)();
|
|
1326
|
+
this.log.info(`Created simulator with udid '${sim.udid}'.`);
|
|
1327
|
+
return sim;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
async startWdaSession(bundleId, processArguments) {
|
|
1331
|
+
const args = processArguments ? _.cloneDeep(processArguments.args) || [] : [];
|
|
1332
|
+
if (!_.isArray(args)) {
|
|
1333
|
+
throw new Error(
|
|
1334
|
+
`processArguments.args capability is expected to be an array. ` +
|
|
1335
|
+
`${JSON.stringify(args)} is given instead`,
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
const env = processArguments ? _.cloneDeep(processArguments.env) || {} : {};
|
|
1339
|
+
if (!_.isPlainObject(env)) {
|
|
1340
|
+
throw new Error(
|
|
1341
|
+
`processArguments.env capability is expected to be a dictionary. ` +
|
|
1342
|
+
`${JSON.stringify(env)} is given instead`,
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
if (util.hasValue(this.opts.language)) {
|
|
1347
|
+
args.push('-AppleLanguages', `(${this.opts.language})`);
|
|
1348
|
+
args.push('-NSLanguages', `(${this.opts.language})`);
|
|
1349
|
+
}
|
|
1350
|
+
if (util.hasValue(this.opts.locale)) {
|
|
1351
|
+
args.push('-AppleLocale', this.opts.locale);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
if (this.opts.noReset) {
|
|
1355
|
+
if (_.isNil(this.opts.shouldTerminateApp)) {
|
|
1356
|
+
this.opts.shouldTerminateApp = false;
|
|
1357
|
+
}
|
|
1358
|
+
if (_.isNil(this.opts.forceAppLaunch)) {
|
|
1359
|
+
this.opts.forceAppLaunch = false;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
if (util.hasValue(this.opts.appTimeZone)) {
|
|
1364
|
+
// https://developer.apple.com/forums/thread/86951?answerId=263395022#263395022
|
|
1365
|
+
env.TZ = this.opts.appTimeZone;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/** @type {import('appium-webdriveragent').WDACapabilities} */
|
|
1369
|
+
const wdaCaps = {
|
|
1370
|
+
bundleId: this.opts.autoLaunch === false ? undefined : bundleId,
|
|
1371
|
+
arguments: args,
|
|
1372
|
+
environment: env,
|
|
1373
|
+
eventloopIdleDelaySec: this.opts.wdaEventloopIdleDelay ?? 0,
|
|
1374
|
+
shouldWaitForQuiescence: this.opts.waitForQuiescence ?? true,
|
|
1375
|
+
shouldUseTestManagerForVisibilityDetection: this.opts.simpleIsVisibleCheck ?? false,
|
|
1376
|
+
maxTypingFrequency: this.opts.maxTypingFrequency ?? 60,
|
|
1377
|
+
shouldUseSingletonTestManager: this.opts.shouldUseSingletonTestManager ?? true,
|
|
1378
|
+
waitForIdleTimeout: this.opts.waitForIdleTimeout,
|
|
1379
|
+
// @ts-expect-error - do not assign arbitrary properties to `this.opts`
|
|
1380
|
+
shouldUseCompactResponses: this.opts.shouldUseCompactResponses,
|
|
1381
|
+
// @ts-expect-error - do not assign arbitrary properties to `this.opts`
|
|
1382
|
+
elementResponseFields: this.opts.elementResponseFields,
|
|
1383
|
+
disableAutomaticScreenshots: this.opts.disableAutomaticScreenshots,
|
|
1384
|
+
shouldTerminateApp: this.opts.shouldTerminateApp ?? true,
|
|
1385
|
+
forceAppLaunch: this.opts.forceAppLaunch ?? true,
|
|
1386
|
+
appLaunchStateTimeoutSec: this.opts.appLaunchStateTimeoutSec,
|
|
1387
|
+
useNativeCachingStrategy: this.opts.useNativeCachingStrategy ?? true,
|
|
1388
|
+
forceSimulatorSoftwareKeyboardPresence:
|
|
1389
|
+
this.opts.forceSimulatorSoftwareKeyboardPresence ??
|
|
1390
|
+
(this.opts.connectHardwareKeyboard === true ? false : true),
|
|
1391
|
+
};
|
|
1392
|
+
if (this.opts.autoAcceptAlerts) {
|
|
1393
|
+
wdaCaps.defaultAlertAction = 'accept';
|
|
1394
|
+
} else if (this.opts.autoDismissAlerts) {
|
|
1395
|
+
wdaCaps.defaultAlertAction = 'dismiss';
|
|
1396
|
+
}
|
|
1397
|
+
if (this.opts.initialDeeplinkUrl) {
|
|
1398
|
+
this.log.info(`The deeplink URL will be set to ${this.opts.initialDeeplinkUrl}`);
|
|
1399
|
+
wdaCaps.initialUrl = this.opts.initialDeeplinkUrl;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
const timer = new timing.Timer().start();
|
|
1403
|
+
await this.proxyCommand('/session', 'POST', {
|
|
1404
|
+
capabilities: {
|
|
1405
|
+
firstMatch: [wdaCaps],
|
|
1406
|
+
alwaysMatch: {},
|
|
1407
|
+
},
|
|
1408
|
+
});
|
|
1409
|
+
this.log.info(`WDA session startup took ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// Override Proxy methods from BaseDriver
|
|
1413
|
+
proxyActive() {
|
|
1414
|
+
return Boolean(this.jwpProxyActive);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
getProxyAvoidList() {
|
|
1418
|
+
if (this.isWebview()) {
|
|
1419
|
+
return NO_PROXY_WEB_LIST;
|
|
1420
|
+
}
|
|
1421
|
+
return NO_PROXY_NATIVE_LIST;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
canProxy() {
|
|
1425
|
+
return true;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
/**
|
|
1429
|
+
* @returns {boolean}
|
|
1430
|
+
*/
|
|
1431
|
+
isSafari() {
|
|
1432
|
+
return !!this.safari;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
/**
|
|
1436
|
+
* @returns {boolean}
|
|
1437
|
+
*/
|
|
1438
|
+
isRealDevice() {
|
|
1439
|
+
return 'devicectl' in (this.device ?? {});
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
/**
|
|
1443
|
+
* @returns {boolean}
|
|
1444
|
+
*/
|
|
1445
|
+
isSimulator() {
|
|
1446
|
+
return 'simctl' in (this.device ?? {});
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* @param {string} strategy
|
|
1451
|
+
*/
|
|
1452
|
+
validateLocatorStrategy(strategy) {
|
|
1453
|
+
super.validateLocatorStrategy(strategy, this.isWebContext());
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
/**
|
|
1457
|
+
* @param {any} caps
|
|
1458
|
+
* @returns {caps is import('@appium/types').DriverCaps<XCUITestDriverConstraints>}
|
|
1459
|
+
*/
|
|
1460
|
+
validateDesiredCaps(caps) {
|
|
1461
|
+
if (!super.validateDesiredCaps(caps)) {
|
|
1462
|
+
return false;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
// make sure that the capabilities have one of `app` or `bundleId`
|
|
1466
|
+
if (_.toLower(caps.browserName) !== 'safari' && !caps.app && !caps.bundleId) {
|
|
1467
|
+
this.log.info(
|
|
1468
|
+
'The desired capabilities include neither an app nor a bundleId. ' +
|
|
1469
|
+
'WebDriverAgent will be started without the default app',
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
if (!util.coerceVersion(String(caps.platformVersion), false)) {
|
|
1474
|
+
this.log.warn(
|
|
1475
|
+
`'platformVersion' capability ('${caps.platformVersion}') is not a valid version number. ` +
|
|
1476
|
+
`Consider fixing it or be ready to experience an inconsistent driver behavior.`,
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
let verifyProcessArgument = (processArguments) => {
|
|
1481
|
+
const {args, env} = processArguments;
|
|
1482
|
+
if (!_.isNil(args) && !_.isArray(args)) {
|
|
1483
|
+
throw this.log.errorWithException('processArguments.args must be an array of strings');
|
|
1484
|
+
}
|
|
1485
|
+
if (!_.isNil(env) && !_.isPlainObject(env)) {
|
|
1486
|
+
throw this.log.errorWithException(
|
|
1487
|
+
'processArguments.env must be an object <key,value> pair {a:b, c:d}',
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
|
|
1492
|
+
// `processArguments` should be JSON string or an object with arguments and/ environment details
|
|
1493
|
+
if (caps.processArguments) {
|
|
1494
|
+
if (_.isString(caps.processArguments)) {
|
|
1495
|
+
try {
|
|
1496
|
+
// try to parse the string as JSON
|
|
1497
|
+
caps.processArguments = JSON.parse(caps.processArguments);
|
|
1498
|
+
verifyProcessArgument(caps.processArguments);
|
|
1499
|
+
} catch (err) {
|
|
1500
|
+
throw this.log.errorWithException(
|
|
1501
|
+
`processArguments must be a JSON format or an object with format {args : [], env : {a:b, c:d}}. ` +
|
|
1502
|
+
`Both environment and argument can be null. Error: ${err}`,
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
} else if (_.isPlainObject(caps.processArguments)) {
|
|
1506
|
+
verifyProcessArgument(caps.processArguments);
|
|
1507
|
+
} else {
|
|
1508
|
+
throw this.log.errorWithException(
|
|
1509
|
+
`'processArguments must be an object, or a string JSON object with format {args : [], env : {a:b, c:d}}. ` +
|
|
1510
|
+
`Both environment and argument can be null.`,
|
|
1511
|
+
);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
// there is no point in having `keychainPath` without `keychainPassword`
|
|
1516
|
+
if (
|
|
1517
|
+
(caps.keychainPath && !caps.keychainPassword) ||
|
|
1518
|
+
(!caps.keychainPath && caps.keychainPassword)
|
|
1519
|
+
) {
|
|
1520
|
+
throw this.log.errorWithException(
|
|
1521
|
+
`If 'keychainPath' is set, 'keychainPassword' must also be set (and vice versa).`,
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
// `resetOnSessionStartOnly` should be set to true by default
|
|
1526
|
+
this.opts.resetOnSessionStartOnly =
|
|
1527
|
+
!util.hasValue(this.opts.resetOnSessionStartOnly) || this.opts.resetOnSessionStartOnly;
|
|
1528
|
+
this.opts.useNewWDA = util.hasValue(this.opts.useNewWDA) ? this.opts.useNewWDA : false;
|
|
1529
|
+
|
|
1530
|
+
if (caps.commandTimeouts) {
|
|
1531
|
+
caps.commandTimeouts = normalizeCommandTimeouts(caps.commandTimeouts);
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
if (_.isString(caps.webDriverAgentUrl)) {
|
|
1535
|
+
const {protocol, host} = url.parse(caps.webDriverAgentUrl);
|
|
1536
|
+
if (_.isEmpty(protocol) || _.isEmpty(host)) {
|
|
1537
|
+
throw this.log.errorWithException(
|
|
1538
|
+
`'webDriverAgentUrl' capability is expected to contain a valid WebDriverAgent server URL. ` +
|
|
1539
|
+
`'${caps.webDriverAgentUrl}' is given instead`,
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
if (caps.browserName) {
|
|
1545
|
+
if (caps.bundleId) {
|
|
1546
|
+
throw this.log.errorWithException(
|
|
1547
|
+
`'browserName' cannot be set together with 'bundleId' capability`
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
// warn if the capabilities have both `app` and `browser, although this
|
|
1551
|
+
// is common with selenium grid
|
|
1552
|
+
if (caps.app) {
|
|
1553
|
+
this.log.warn(
|
|
1554
|
+
`The capabilities should generally not include both an 'app' and a 'browserName'`,
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
if (caps.permissions) {
|
|
1560
|
+
try {
|
|
1561
|
+
for (const [bundleId, perms] of _.toPairs(JSON.parse(caps.permissions))) {
|
|
1562
|
+
if (!_.isString(bundleId)) {
|
|
1563
|
+
throw new Error(`'${JSON.stringify(bundleId)}' must be a string`);
|
|
1564
|
+
}
|
|
1565
|
+
if (!_.isPlainObject(perms)) {
|
|
1566
|
+
throw new Error(`'${JSON.stringify(perms)}' must be a JSON object`);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
} catch (e) {
|
|
1570
|
+
throw this.log.errorWithException(
|
|
1571
|
+
`'${caps.permissions}' is expected to be a valid object with format ` +
|
|
1572
|
+
`{"<bundleId1>": {"<serviceName1>": "<serviceStatus1>", ...}, ...}. Original error: ${e.message}`,
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
if (caps.platformVersion && !util.coerceVersion(caps.platformVersion, false)) {
|
|
1578
|
+
throw this.log.errorWithException(
|
|
1579
|
+
`'platformVersion' must be a valid version number. ` +
|
|
1580
|
+
`'${caps.platformVersion}' is given instead.`,
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// additionalWebviewBundleIds is an array, JSON array, or string
|
|
1585
|
+
if (caps.additionalWebviewBundleIds) {
|
|
1586
|
+
caps.additionalWebviewBundleIds = this.helpers.parseCapsArray(
|
|
1587
|
+
caps.additionalWebviewBundleIds,
|
|
1588
|
+
);
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
// finally, return true since the superclass check passed, as did this
|
|
1592
|
+
return true;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
/**
|
|
1596
|
+
* Check if the given app can be installed, or should uninstall before installing it.
|
|
1597
|
+
*
|
|
1598
|
+
* @param {AutInstallationStateOptions} [opts]
|
|
1599
|
+
* @returns {Promise<AutInstallationState>}
|
|
1600
|
+
*/
|
|
1601
|
+
async checkAutInstallationState(opts) {
|
|
1602
|
+
const {enforceAppInstall, fullReset, noReset, bundleId, app} = opts ?? this.opts;
|
|
1603
|
+
|
|
1604
|
+
const wasAppInstalled = await this.device.isAppInstalled(bundleId);
|
|
1605
|
+
if (wasAppInstalled) {
|
|
1606
|
+
this.log.info(`App '${bundleId}' is already installed`);
|
|
1607
|
+
if (noReset) {
|
|
1608
|
+
this.log.info('noReset is requested. The app will not be be (re)installed');
|
|
1609
|
+
return {
|
|
1610
|
+
install: false,
|
|
1611
|
+
skipUninstall: true,
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
} else {
|
|
1615
|
+
this.log.info(`App '${bundleId}' is not installed yet or it has an offload and ` +
|
|
1616
|
+
'cannot be detected, which might keep the local data.');
|
|
1617
|
+
}
|
|
1618
|
+
if (enforceAppInstall !== false || fullReset || !wasAppInstalled) {
|
|
1619
|
+
return {
|
|
1620
|
+
install: true,
|
|
1621
|
+
skipUninstall: !wasAppInstalled,
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
const candidateBundleVersion = await this.appInfosCache.extractBundleVersion(app);
|
|
1626
|
+
this.log.debug(`CFBundleVersion from Info.plist: ${candidateBundleVersion}`);
|
|
1627
|
+
if (!candidateBundleVersion) {
|
|
1628
|
+
return {
|
|
1629
|
+
install: true,
|
|
1630
|
+
skipUninstall: false,
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
const appBundleVersion = this.isRealDevice()
|
|
1635
|
+
? (await /** @type {RealDevice} */ (this.device).fetchAppInfo(bundleId))?.CFBundleVersion
|
|
1636
|
+
: BUNDLE_VERSION_PATTERN.exec(await /** @type {Simulator} */ (this.device).simctl.appInfo(bundleId))?.[1];
|
|
1637
|
+
this.log.debug(`CFBundleVersion from installed app info: ${appBundleVersion}`);
|
|
1638
|
+
if (!appBundleVersion) {
|
|
1639
|
+
return {
|
|
1640
|
+
install: true,
|
|
1641
|
+
skipUninstall: false,
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
let shouldUpgrade;
|
|
1646
|
+
try {
|
|
1647
|
+
shouldUpgrade = util.compareVersions(candidateBundleVersion, '>', appBundleVersion);
|
|
1648
|
+
} catch (err) {
|
|
1649
|
+
this.log.warn(`App versions comparison is not possible: ${err.message}`);
|
|
1650
|
+
return {
|
|
1651
|
+
install: true,
|
|
1652
|
+
skipUninstall: false,
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
if (shouldUpgrade) {
|
|
1656
|
+
this.log.info(
|
|
1657
|
+
`The installed version of ${bundleId} is lower than the candidate one ` +
|
|
1658
|
+
`(${candidateBundleVersion} > ${appBundleVersion}). The app will be upgraded.`,
|
|
1659
|
+
);
|
|
1660
|
+
} else {
|
|
1661
|
+
this.log.info(
|
|
1662
|
+
`The candidate version of ${bundleId} is lower than the installed one ` +
|
|
1663
|
+
`(${candidateBundleVersion} <= ${appBundleVersion}). The app won't be reinstalled.`,
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
return {
|
|
1667
|
+
install: shouldUpgrade,
|
|
1668
|
+
skipUninstall: true,
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
async installAUT() {
|
|
1673
|
+
// install any other apps
|
|
1674
|
+
if (this.opts.otherApps) {
|
|
1675
|
+
await this.installOtherApps(this.opts.otherApps);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
if (this.isSafari() || !this.opts.app) {
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
await verifyApplicationPlatform.bind(this)();
|
|
1683
|
+
|
|
1684
|
+
const {install, skipUninstall} = await this.checkAutInstallationState();
|
|
1685
|
+
if (install) {
|
|
1686
|
+
if (this.isRealDevice()) {
|
|
1687
|
+
await installToRealDevice.bind(this)(this.opts.app, this.opts.bundleId, {
|
|
1688
|
+
skipUninstall,
|
|
1689
|
+
timeout: this.opts.appPushTimeout,
|
|
1690
|
+
});
|
|
1691
|
+
} else {
|
|
1692
|
+
await installToSimulator.bind(this)(this.opts.app, this.opts.bundleId, {
|
|
1693
|
+
skipUninstall,
|
|
1694
|
+
newSimulator: this.lifecycleData?.createSim,
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
if (util.hasValue(this.opts.iosInstallPause)) {
|
|
1698
|
+
// https://github.com/appium/appium/issues/6889
|
|
1699
|
+
const pauseMs = parseInt(this.opts.iosInstallPause, 10);
|
|
1700
|
+
this.log.debug(`iosInstallPause set. Pausing ${pauseMs} ms before continuing`);
|
|
1701
|
+
await B.delay(pauseMs);
|
|
1702
|
+
}
|
|
1703
|
+
this.logEvent('appInstalled');
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
/**
|
|
1708
|
+
* @param {string|string[]} otherApps
|
|
1709
|
+
* @returns {Promise<void>}
|
|
1710
|
+
*/
|
|
1711
|
+
async installOtherApps(otherApps) {
|
|
1712
|
+
/** @type {string[]|undefined} */
|
|
1713
|
+
let appsList;
|
|
1714
|
+
try {
|
|
1715
|
+
appsList = this.helpers.parseCapsArray(otherApps);
|
|
1716
|
+
} catch (e) {
|
|
1717
|
+
throw this.log.errorWithException(`Could not parse "otherApps" capability: ${e.message}`);
|
|
1718
|
+
}
|
|
1719
|
+
if (!appsList?.length) {
|
|
1720
|
+
this.log.info(`Got zero apps from 'otherApps' capability value. Doing nothing`);
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
/** @type {string[]} */
|
|
1725
|
+
const appPaths = await B.all(appsList.map((app) => this.helpers.configureApp(app, {
|
|
1726
|
+
onPostProcess: onPostConfigureApp.bind(this),
|
|
1727
|
+
onDownload: onDownloadApp.bind(this),
|
|
1728
|
+
supportedExtensions: SUPPORTED_EXTENSIONS,
|
|
1729
|
+
})));
|
|
1730
|
+
/** @type {string[]} */
|
|
1731
|
+
const appIds = await B.all(appPaths.map((appPath) => this.appInfosCache.extractBundleId(appPath)));
|
|
1732
|
+
for (const [appId, appPath] of _.zip(appIds, appPaths)) {
|
|
1733
|
+
if (this.isRealDevice()) {
|
|
1734
|
+
await installToRealDevice.bind(this)(
|
|
1735
|
+
appPath,
|
|
1736
|
+
appId,
|
|
1737
|
+
{
|
|
1738
|
+
skipUninstall: true, // to make the behavior as same as UIA2
|
|
1739
|
+
timeout: this.opts.appPushTimeout,
|
|
1740
|
+
},
|
|
1741
|
+
);
|
|
1742
|
+
} else {
|
|
1743
|
+
await installToSimulator.bind(this)(
|
|
1744
|
+
appPath,
|
|
1745
|
+
appId,
|
|
1746
|
+
{
|
|
1747
|
+
newSimulator: this.lifecycleData.createSim,
|
|
1748
|
+
},
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
/**
|
|
1755
|
+
* @param {string} orientation
|
|
1756
|
+
* @returns {Promise<void>}
|
|
1757
|
+
*/
|
|
1758
|
+
async setInitialOrientation(orientation) {
|
|
1759
|
+
const dstOrientation = _.toUpper(orientation);
|
|
1760
|
+
if (!SUPPORTED_ORIENATIONS.includes(dstOrientation)) {
|
|
1761
|
+
this.log.debug(
|
|
1762
|
+
`The initial orientation value '${orientation}' is unknown. ` +
|
|
1763
|
+
`Only ${JSON.stringify(SUPPORTED_ORIENATIONS)} are supported.`,
|
|
1764
|
+
);
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
this.log.debug(`Setting initial orientation to '${dstOrientation}'`);
|
|
1769
|
+
try {
|
|
1770
|
+
await this.proxyCommand('/orientation', 'POST', {orientation: dstOrientation});
|
|
1771
|
+
} catch (err) {
|
|
1772
|
+
this.log.warn(`Setting initial orientation failed with: ${err.message}`);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
/**
|
|
1777
|
+
* @param {string} [cmdName]
|
|
1778
|
+
* @returns {number|undefined}
|
|
1779
|
+
*/
|
|
1780
|
+
_getCommandTimeout(cmdName) {
|
|
1781
|
+
if (this.opts.commandTimeouts) {
|
|
1782
|
+
if (cmdName && _.has(this.opts.commandTimeouts, cmdName)) {
|
|
1783
|
+
return this.opts.commandTimeouts[cmdName];
|
|
1784
|
+
}
|
|
1785
|
+
return this.opts.commandTimeouts[DEFAULT_TIMEOUT_KEY];
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
/**
|
|
1790
|
+
* Reset the current session (run the delete session and create session subroutines)
|
|
1791
|
+
*/
|
|
1792
|
+
async reset() {
|
|
1793
|
+
throw new Error(
|
|
1794
|
+
`The reset API has been deprecated and is not supported anymore. ` +
|
|
1795
|
+
`Consider using corresponding 'mobile:' extensions to manage the state of the app under test.`,
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
async preparePreinstalledWda() {
|
|
1800
|
+
if (this.isRealDevice()) {
|
|
1801
|
+
// Stop the existing process before starting a new one to start a fresh WDA process every session.
|
|
1802
|
+
await this.mobileKillApp(this.wda.bundleIdForXctest);
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
if (!this.opts.prebuiltWDAPath) {
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
const candidateBundleId = await this.appInfosCache.extractBundleId(this.opts.prebuiltWDAPath);
|
|
1810
|
+
this.wda.updatedWDABundleId = candidateBundleId.replace('.xctrunner', '');
|
|
1811
|
+
this.log.info(
|
|
1812
|
+
`Installing prebuilt WDA at '${this.opts.prebuiltWDAPath}'. ` +
|
|
1813
|
+
`Bundle identifier: ${candidateBundleId}.`
|
|
1814
|
+
);
|
|
1815
|
+
|
|
1816
|
+
// Note: The CFBundleVersion in the test bundle was always 1.
|
|
1817
|
+
// It may not be able to compare with the installed version.
|
|
1818
|
+
if (this.isRealDevice()) {
|
|
1819
|
+
await installToRealDevice.bind(this)(
|
|
1820
|
+
this.opts.prebuiltWDAPath,
|
|
1821
|
+
candidateBundleId,
|
|
1822
|
+
{
|
|
1823
|
+
skipUninstall: true,
|
|
1824
|
+
timeout: this.opts.appPushTimeout,
|
|
1825
|
+
},
|
|
1826
|
+
);
|
|
1827
|
+
} else {
|
|
1828
|
+
await installToSimulator.bind(this)(
|
|
1829
|
+
this.opts.prebuiltWDAPath,
|
|
1830
|
+
candidateBundleId
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
/*---------------+
|
|
1836
|
+
| ACTIVEAPPINFO |
|
|
1837
|
+
+---------------+*/
|
|
1838
|
+
|
|
1839
|
+
mobileGetActiveAppInfo = activeAppInfoCommands.mobileGetActiveAppInfo;
|
|
1840
|
+
|
|
1841
|
+
/*-------+
|
|
1842
|
+
| ALERT |
|
|
1843
|
+
+-------+*/
|
|
1844
|
+
getAlertText = alertCommands.getAlertText;
|
|
1845
|
+
setAlertText = alertCommands.setAlertText;
|
|
1846
|
+
postAcceptAlert = alertCommands.postAcceptAlert;
|
|
1847
|
+
postDismissAlert = alertCommands.postDismissAlert;
|
|
1848
|
+
getAlertButtons = alertCommands.getAlertButtons;
|
|
1849
|
+
mobileHandleAlert = alertCommands.mobileHandleAlert;
|
|
1850
|
+
|
|
1851
|
+
/*---------------+
|
|
1852
|
+
| APPMANAGEMENT |
|
|
1853
|
+
+---------------+*/
|
|
1854
|
+
|
|
1855
|
+
mobileInstallApp = appManagementCommands.mobileInstallApp;
|
|
1856
|
+
mobileIsAppInstalled = appManagementCommands.mobileIsAppInstalled;
|
|
1857
|
+
mobileRemoveApp = appManagementCommands.mobileRemoveApp;
|
|
1858
|
+
mobileLaunchApp = appManagementCommands.mobileLaunchApp;
|
|
1859
|
+
mobileTerminateApp = appManagementCommands.mobileTerminateApp;
|
|
1860
|
+
mobileActivateApp = appManagementCommands.mobileActivateApp;
|
|
1861
|
+
mobileKillApp = appManagementCommands.mobileKillApp;
|
|
1862
|
+
mobileQueryAppState = appManagementCommands.mobileQueryAppState;
|
|
1863
|
+
installApp = appManagementCommands.installApp;
|
|
1864
|
+
activateApp = appManagementCommands.activateApp;
|
|
1865
|
+
isAppInstalled = appManagementCommands.isAppInstalled;
|
|
1866
|
+
// @ts-ignore it must return boolean
|
|
1867
|
+
terminateApp = appManagementCommands.terminateApp;
|
|
1868
|
+
queryAppState = appManagementCommands.queryAppState;
|
|
1869
|
+
mobileListApps = appManagementCommands.mobileListApps;
|
|
1870
|
+
mobileClearApp = appManagementCommands.mobileClearApp;
|
|
1871
|
+
|
|
1872
|
+
/*------------+
|
|
1873
|
+
| APPEARANCE |
|
|
1874
|
+
+------------+*/
|
|
1875
|
+
|
|
1876
|
+
mobileSetAppearance = appearanceCommands.mobileSetAppearance;
|
|
1877
|
+
mobileGetAppearance = appearanceCommands.mobileGetAppearance;
|
|
1878
|
+
|
|
1879
|
+
/*------------+
|
|
1880
|
+
| INCREASE CONTRAST |
|
|
1881
|
+
+------------+*/
|
|
1882
|
+
|
|
1883
|
+
mobileSetIncreaseContrast = increaseContrastCommands.mobileSetIncreaseContrast;
|
|
1884
|
+
mobileGetIncreaseContrast = increaseContrastCommands.mobileGetIncreaseContrast;
|
|
1885
|
+
|
|
1886
|
+
/*------------+
|
|
1887
|
+
| CONTENT SIZE |
|
|
1888
|
+
+------------+*/
|
|
1889
|
+
|
|
1890
|
+
mobileSetContentSize = contentSizeCommands.mobileSetContentSize;
|
|
1891
|
+
mobileGetContentSize = contentSizeCommands.mobileGetContentSize;
|
|
1892
|
+
|
|
1893
|
+
/*------------+
|
|
1894
|
+
| AUDIT |
|
|
1895
|
+
+------------+*/
|
|
1896
|
+
|
|
1897
|
+
mobilePerformAccessibilityAudit = auditCommands.mobilePerformAccessibilityAudit;
|
|
1898
|
+
|
|
1899
|
+
/*---------+
|
|
1900
|
+
| BATTERY |
|
|
1901
|
+
+---------+*/
|
|
1902
|
+
mobileGetBatteryInfo = batteryCommands.mobileGetBatteryInfo;
|
|
1903
|
+
|
|
1904
|
+
/*-----------+
|
|
1905
|
+
| BIOMETRIC |
|
|
1906
|
+
+-----------+*/
|
|
1907
|
+
|
|
1908
|
+
mobileEnrollBiometric = biometricCommands.mobileEnrollBiometric;
|
|
1909
|
+
mobileSendBiometricMatch = biometricCommands.mobileSendBiometricMatch;
|
|
1910
|
+
mobileIsBiometricEnrolled = biometricCommands.mobileIsBiometricEnrolled;
|
|
1911
|
+
|
|
1912
|
+
/*-------------+
|
|
1913
|
+
| CERTIFICATE |
|
|
1914
|
+
+-------------+*/
|
|
1915
|
+
mobileInstallCertificate = certificateCommands.mobileInstallCertificate;
|
|
1916
|
+
mobileListCertificates = certificateCommands.mobileListCertificates;
|
|
1917
|
+
mobileRemoveCertificate = certificateCommands.mobileRemoveCertificate;
|
|
1918
|
+
|
|
1919
|
+
/*-----------+
|
|
1920
|
+
| CLIPBOARD |
|
|
1921
|
+
+-----------+*/
|
|
1922
|
+
|
|
1923
|
+
setClipboard = clipboardCommands.setClipboard;
|
|
1924
|
+
getClipboard = clipboardCommands.getClipboard;
|
|
1925
|
+
|
|
1926
|
+
/*-----------+
|
|
1927
|
+
| CONDITION |
|
|
1928
|
+
+-----------+*/
|
|
1929
|
+
|
|
1930
|
+
listConditionInducers = conditionCommands.listConditionInducers;
|
|
1931
|
+
enableConditionInducer = conditionCommands.enableConditionInducer;
|
|
1932
|
+
disableConditionInducer = conditionCommands.disableConditionInducer;
|
|
1933
|
+
|
|
1934
|
+
/*---------+
|
|
1935
|
+
| CONTEXT |
|
|
1936
|
+
+---------+*/
|
|
1937
|
+
|
|
1938
|
+
getContexts = contextCommands.getContexts;
|
|
1939
|
+
getCurrentContext = contextCommands.getCurrentContext;
|
|
1940
|
+
// @ts-ignore This is OK
|
|
1941
|
+
getWindowHandle = contextCommands.getWindowHandle;
|
|
1942
|
+
getWindowHandles = contextCommands.getWindowHandles;
|
|
1943
|
+
// @ts-ignore Type mismatch: function type vs method signature
|
|
1944
|
+
setContext = contextCommands.setContext;
|
|
1945
|
+
// @ts-ignore This is OK
|
|
1946
|
+
setWindow = contextCommands.setWindow;
|
|
1947
|
+
activateRecentWebview = contextCommands.activateRecentWebview;
|
|
1948
|
+
connectToRemoteDebugger = contextCommands.connectToRemoteDebugger;
|
|
1949
|
+
getContextsAndViews = contextCommands.getContextsAndViews;
|
|
1950
|
+
listWebFrames = contextCommands.listWebFrames;
|
|
1951
|
+
mobileGetContexts = contextCommands.mobileGetContexts;
|
|
1952
|
+
onPageChange = contextCommands.onPageChange;
|
|
1953
|
+
useNewSafari = contextCommands.useNewSafari;
|
|
1954
|
+
getCurrentUrl = contextCommands.getCurrentUrl;
|
|
1955
|
+
getNewRemoteDebugger = contextCommands.getNewRemoteDebugger;
|
|
1956
|
+
getRecentWebviewContextId = contextCommands.getRecentWebviewContextId;
|
|
1957
|
+
isWebContext = contextCommands.isWebContext;
|
|
1958
|
+
isWebview = contextCommands.isWebview;
|
|
1959
|
+
setCurrentUrl = contextCommands.setCurrentUrl;
|
|
1960
|
+
stopRemote = contextCommands.stopRemote;
|
|
1961
|
+
|
|
1962
|
+
/*------------+
|
|
1963
|
+
| DEVICEINFO |
|
|
1964
|
+
+------------+*/
|
|
1965
|
+
|
|
1966
|
+
mobileGetDeviceInfo = deviceInfoCommands.mobileGetDeviceInfo;
|
|
1967
|
+
|
|
1968
|
+
/*---------+
|
|
1969
|
+
| ELEMENT |
|
|
1970
|
+
+---------+*/
|
|
1971
|
+
|
|
1972
|
+
elementDisplayed = elementCommands.elementDisplayed;
|
|
1973
|
+
elementEnabled = elementCommands.elementEnabled;
|
|
1974
|
+
elementSelected = elementCommands.elementSelected;
|
|
1975
|
+
getName = elementCommands.getName;
|
|
1976
|
+
getNativeAttribute = elementCommands.getNativeAttribute;
|
|
1977
|
+
getAttribute = elementCommands.getAttribute;
|
|
1978
|
+
getProperty = elementCommands.getProperty;
|
|
1979
|
+
getText = elementCommands.getText;
|
|
1980
|
+
getElementRect = elementCommands.getElementRect;
|
|
1981
|
+
getLocation = elementCommands.getLocation;
|
|
1982
|
+
getLocationInView = elementCommands.getLocationInView;
|
|
1983
|
+
getSize = elementCommands.getSize;
|
|
1984
|
+
/** @deprecated */
|
|
1985
|
+
setValueImmediate = elementCommands.setValueImmediate;
|
|
1986
|
+
setValue = elementCommands.setValue;
|
|
1987
|
+
setValueWithWebAtom = elementCommands.setValueWithWebAtom;
|
|
1988
|
+
keys = elementCommands.keys;
|
|
1989
|
+
clear = elementCommands.clear;
|
|
1990
|
+
getContentSize = elementCommands.getContentSize;
|
|
1991
|
+
getNativeRect = elementCommands.getNativeRect;
|
|
1992
|
+
|
|
1993
|
+
/*---------+
|
|
1994
|
+
| EXECUTE |
|
|
1995
|
+
+---------+*/
|
|
1996
|
+
|
|
1997
|
+
receiveAsyncResponse = executeCommands.receiveAsyncResponse;
|
|
1998
|
+
execute = executeCommands.execute;
|
|
1999
|
+
// @ts-ignore Type mismatch: function type vs method signature
|
|
2000
|
+
executeAsync = executeCommands.executeAsync;
|
|
2001
|
+
// Note: executeMobile is handled internally via execute method
|
|
2002
|
+
mobileSimctl = simctlCommands.mobileSimctl;
|
|
2003
|
+
|
|
2004
|
+
/*--------------+
|
|
2005
|
+
| FILEMOVEMENT |
|
|
2006
|
+
+--------------+*/
|
|
2007
|
+
|
|
2008
|
+
pushFile = fileMovementCommands.pushFile;
|
|
2009
|
+
mobilePushFile = fileMovementCommands.mobilePushFile;
|
|
2010
|
+
pullFile = fileMovementCommands.pullFile;
|
|
2011
|
+
mobilePullFile = fileMovementCommands.mobilePullFile;
|
|
2012
|
+
mobileDeleteFolder = fileMovementCommands.mobileDeleteFolder;
|
|
2013
|
+
mobileDeleteFile = fileMovementCommands.mobileDeleteFile;
|
|
2014
|
+
pullFolder = fileMovementCommands.pullFolder;
|
|
2015
|
+
mobilePullFolder = fileMovementCommands.mobilePullFolder;
|
|
2016
|
+
|
|
2017
|
+
/*--------+
|
|
2018
|
+
| MEMORY |
|
|
2019
|
+
+--------+*/
|
|
2020
|
+
|
|
2021
|
+
mobileSendMemoryWarning = memoryCommands.mobileSendMemoryWarning;
|
|
2022
|
+
|
|
2023
|
+
/*------+
|
|
2024
|
+
| FIND |
|
|
2025
|
+
+------+*/
|
|
2026
|
+
|
|
2027
|
+
findElOrEls = findCommands.findElOrEls;
|
|
2028
|
+
findNativeElementOrElements = findCommands.findNativeElementOrElements;
|
|
2029
|
+
doNativeFind = findCommands.doNativeFind;
|
|
2030
|
+
getFirstVisibleChild = findCommands.getFirstVisibleChild;
|
|
2031
|
+
|
|
2032
|
+
/*---------+
|
|
2033
|
+
| GENERAL |
|
|
2034
|
+
+---------+*/
|
|
2035
|
+
|
|
2036
|
+
active = generalCommands.active;
|
|
2037
|
+
background = appManagementCommands.background;
|
|
2038
|
+
touchId = generalCommands.touchId;
|
|
2039
|
+
toggleEnrollTouchId = generalCommands.toggleEnrollTouchId;
|
|
2040
|
+
getWindowSize = generalCommands.getWindowSize;
|
|
2041
|
+
getDeviceTime = generalCommands.getDeviceTime;
|
|
2042
|
+
mobileGetDeviceTime = generalCommands.mobileGetDeviceTime;
|
|
2043
|
+
getWindowRect = generalCommands.getWindowRect;
|
|
2044
|
+
getStrings = appStringsCommands.getStrings;
|
|
2045
|
+
removeApp = generalCommands.removeApp;
|
|
2046
|
+
launchApp = generalCommands.launchApp;
|
|
2047
|
+
closeApp = generalCommands.closeApp;
|
|
2048
|
+
// @ts-ignore Type mismatch: function type vs method signature
|
|
2049
|
+
setUrl = generalCommands.setUrl;
|
|
2050
|
+
getViewportRect = generalCommands.getViewportRect;
|
|
2051
|
+
getScreenInfo = generalCommands.getScreenInfo;
|
|
2052
|
+
getStatusBarHeight = generalCommands.getStatusBarHeight;
|
|
2053
|
+
getDevicePixelRatio = generalCommands.getDevicePixelRatio;
|
|
2054
|
+
mobilePressButton = generalCommands.mobilePressButton;
|
|
2055
|
+
mobileSiriCommand = generalCommands.mobileSiriCommand;
|
|
2056
|
+
|
|
2057
|
+
/*-------------+
|
|
2058
|
+
| GEOLOCATION |
|
|
2059
|
+
+-------------+*/
|
|
2060
|
+
mobileGetSimulatedLocation = geolocationCommands.mobileGetSimulatedLocation;
|
|
2061
|
+
mobileSetSimulatedLocation = geolocationCommands.mobileSetSimulatedLocation;
|
|
2062
|
+
mobileResetSimulatedLocation = geolocationCommands.mobileResetSimulatedLocation;
|
|
2063
|
+
|
|
2064
|
+
/*---------+
|
|
2065
|
+
| GESTURE |
|
|
2066
|
+
+---------+*/
|
|
2067
|
+
mobileShake = gestureCommands.mobileShake;
|
|
2068
|
+
click = gestureCommands.click;
|
|
2069
|
+
releaseActions = gestureCommands.releaseActions;
|
|
2070
|
+
performActions = gestureCommands.performActions;
|
|
2071
|
+
nativeClick = gestureCommands.nativeClick;
|
|
2072
|
+
mobileScrollToElement = gestureCommands.mobileScrollToElement;
|
|
2073
|
+
mobileScroll = gestureCommands.mobileScroll;
|
|
2074
|
+
mobileSwipe = gestureCommands.mobileSwipe;
|
|
2075
|
+
mobilePinch = gestureCommands.mobilePinch;
|
|
2076
|
+
mobileDoubleTap = gestureCommands.mobileDoubleTap;
|
|
2077
|
+
mobileTwoFingerTap = gestureCommands.mobileTwoFingerTap;
|
|
2078
|
+
mobileTouchAndHold = gestureCommands.mobileTouchAndHold;
|
|
2079
|
+
mobileTap = gestureCommands.mobileTap;
|
|
2080
|
+
mobileDragFromToForDuration = gestureCommands.mobileDragFromToForDuration;
|
|
2081
|
+
mobileDragFromToWithVelocity = gestureCommands.mobileDragFromToWithVelocity;
|
|
2082
|
+
mobileTapWithNumberOfTaps = gestureCommands.mobileTapWithNumberOfTaps;
|
|
2083
|
+
mobileForcePress = gestureCommands.mobileForcePress;
|
|
2084
|
+
mobileSelectPickerWheelValue = gestureCommands.mobileSelectPickerWheelValue;
|
|
2085
|
+
mobileRotateElement = gestureCommands.mobileRotateElement;
|
|
2086
|
+
|
|
2087
|
+
/*-------+
|
|
2088
|
+
| IOHID |
|
|
2089
|
+
+-------+*/
|
|
2090
|
+
mobilePerformIoHidEvent = iohidCommands.mobilePerformIoHidEvent;
|
|
2091
|
+
|
|
2092
|
+
/*-----------+
|
|
2093
|
+
| KEYCHAINS |
|
|
2094
|
+
+-----------+*/
|
|
2095
|
+
|
|
2096
|
+
mobileClearKeychains = keychainsCommands.mobileClearKeychains;
|
|
2097
|
+
|
|
2098
|
+
/*----------+
|
|
2099
|
+
| KEYBOARD |
|
|
2100
|
+
+----------+*/
|
|
2101
|
+
|
|
2102
|
+
hideKeyboard = keyboardCommands.hideKeyboard;
|
|
2103
|
+
mobileHideKeyboard = keyboardCommands.mobileHideKeyboard;
|
|
2104
|
+
isKeyboardShown = keyboardCommands.isKeyboardShown;
|
|
2105
|
+
mobileKeys = keyboardCommands.mobileKeys;
|
|
2106
|
+
|
|
2107
|
+
/*--------------+
|
|
2108
|
+
| LOCALIZATION |
|
|
2109
|
+
+--------------+*/
|
|
2110
|
+
|
|
2111
|
+
mobileConfigureLocalization = localizationCommands.mobileConfigureLocalization;
|
|
2112
|
+
|
|
2113
|
+
/*----------+
|
|
2114
|
+
| LOCATION |
|
|
2115
|
+
+----------+*/
|
|
2116
|
+
|
|
2117
|
+
getGeoLocation = locationCommands.getGeoLocation;
|
|
2118
|
+
setGeoLocation = locationCommands.setGeoLocation;
|
|
2119
|
+
mobileResetLocationService = locationCommands.mobileResetLocationService;
|
|
2120
|
+
|
|
2121
|
+
/*------+
|
|
2122
|
+
| LOCK |
|
|
2123
|
+
+------+*/
|
|
2124
|
+
lock = lockCommands.lock;
|
|
2125
|
+
unlock = lockCommands.unlock;
|
|
2126
|
+
isLocked = lockCommands.isLocked;
|
|
2127
|
+
|
|
2128
|
+
/*-----+
|
|
2129
|
+
| LOG |
|
|
2130
|
+
+-----+*/
|
|
2131
|
+
|
|
2132
|
+
extractLogs = logCommands.extractLogs;
|
|
2133
|
+
supportedLogTypes = logCommands.supportedLogTypes;
|
|
2134
|
+
startLogCapture = logCommands.startLogCapture;
|
|
2135
|
+
mobileStartLogsBroadcast = logCommands.mobileStartLogsBroadcast;
|
|
2136
|
+
mobileStopLogsBroadcast = logCommands.mobileStopLogsBroadcast;
|
|
2137
|
+
|
|
2138
|
+
/*------------+
|
|
2139
|
+
| NAVIGATION |
|
|
2140
|
+
+------------+*/
|
|
2141
|
+
|
|
2142
|
+
back = navigationCommands.back;
|
|
2143
|
+
forward = navigationCommands.forward;
|
|
2144
|
+
closeWindow = navigationCommands.closeWindow;
|
|
2145
|
+
nativeBack = navigationCommands.nativeBack;
|
|
2146
|
+
mobileDeepLink = navigationCommands.mobileDeepLink;
|
|
2147
|
+
|
|
2148
|
+
/*---------------+
|
|
2149
|
+
| NOTIFICATIONS |
|
|
2150
|
+
+---------------+*/
|
|
2151
|
+
|
|
2152
|
+
mobilePushNotification = notificationsCommands.mobilePushNotification;
|
|
2153
|
+
mobileExpectNotification = notificationsCommands.mobileExpectNotification;
|
|
2154
|
+
|
|
2155
|
+
/*------------+
|
|
2156
|
+
| PASTEBOARD |
|
|
2157
|
+
+------------+*/
|
|
2158
|
+
|
|
2159
|
+
mobileSetPasteboard = pasteboardCommands.mobileSetPasteboard;
|
|
2160
|
+
mobileGetPasteboard = pasteboardCommands.mobileGetPasteboard;
|
|
2161
|
+
|
|
2162
|
+
/*------+
|
|
2163
|
+
| PCAP |
|
|
2164
|
+
+------+*/
|
|
2165
|
+
|
|
2166
|
+
mobileStartPcap = pcapCommands.mobileStartPcap;
|
|
2167
|
+
mobileStopPcap = pcapCommands.mobileStopPcap;
|
|
2168
|
+
|
|
2169
|
+
/*-------------+
|
|
2170
|
+
| PERFORMANCE |
|
|
2171
|
+
+-------------+*/
|
|
2172
|
+
mobileStartPerfRecord = performanceCommands.mobileStartPerfRecord;
|
|
2173
|
+
mobileStopPerfRecord = performanceCommands.mobileStopPerfRecord;
|
|
2174
|
+
|
|
2175
|
+
/*-------------+
|
|
2176
|
+
| PERMISSIONS |
|
|
2177
|
+
+-------------+*/
|
|
2178
|
+
|
|
2179
|
+
mobileResetPermission = permissionsCommands.mobileResetPermission;
|
|
2180
|
+
mobileGetPermission = permissionsCommands.mobileGetPermission;
|
|
2181
|
+
mobileSetPermissions = permissionsCommands.mobileSetPermissions;
|
|
2182
|
+
|
|
2183
|
+
/*-------------+
|
|
2184
|
+
| PROXYHELPER |
|
|
2185
|
+
+-------------+*/
|
|
2186
|
+
|
|
2187
|
+
proxyCommand = proxyHelperCommands.proxyCommand;
|
|
2188
|
+
|
|
2189
|
+
/*-------------+
|
|
2190
|
+
| RECORDAUDIO |
|
|
2191
|
+
+-------------+*/
|
|
2192
|
+
|
|
2193
|
+
startAudioRecording = recordAudioCommands.startAudioRecording;
|
|
2194
|
+
stopAudioRecording = recordAudioCommands.stopAudioRecording;
|
|
2195
|
+
|
|
2196
|
+
/*--------------+
|
|
2197
|
+
| RECORDSCREEN |
|
|
2198
|
+
+--------------+*/
|
|
2199
|
+
|
|
2200
|
+
// Note: _recentScreenRecorder is a property, not a function, so it's handled internally in recordscreen.js
|
|
2201
|
+
startRecordingScreen = recordScreenCommands.startRecordingScreen;
|
|
2202
|
+
stopRecordingScreen = recordScreenCommands.stopRecordingScreen;
|
|
2203
|
+
|
|
2204
|
+
/*-------------+
|
|
2205
|
+
| SCREENSHOTS |
|
|
2206
|
+
+-------------+*/
|
|
2207
|
+
getScreenshot = screenshotCommands.getScreenshot;
|
|
2208
|
+
getElementScreenshot = screenshotCommands.getElementScreenshot;
|
|
2209
|
+
getViewportScreenshot = screenshotCommands.getViewportScreenshot;
|
|
2210
|
+
|
|
2211
|
+
/*--------+
|
|
2212
|
+
| SOURCE |
|
|
2213
|
+
+--------+*/
|
|
2214
|
+
getPageSource = sourceCommands.getPageSource;
|
|
2215
|
+
mobileGetSource = sourceCommands.mobileGetSource;
|
|
2216
|
+
|
|
2217
|
+
/*----------+
|
|
2218
|
+
| TIMEOUTS |
|
|
2219
|
+
+----------+*/
|
|
2220
|
+
|
|
2221
|
+
pageLoadTimeoutW3C = timeoutCommands.pageLoadTimeoutW3C;
|
|
2222
|
+
pageLoadTimeoutMJSONWP = timeoutCommands.pageLoadTimeoutMJSONWP;
|
|
2223
|
+
scriptTimeoutW3C = timeoutCommands.scriptTimeoutW3C;
|
|
2224
|
+
scriptTimeoutMJSONWP = timeoutCommands.scriptTimeoutMJSONWP;
|
|
2225
|
+
asyncScriptTimeout = timeoutCommands.asyncScriptTimeout;
|
|
2226
|
+
setPageLoadTimeout = timeoutCommands.setPageLoadTimeout;
|
|
2227
|
+
setAsyncScriptTimeout = timeoutCommands.setAsyncScriptTimeout;
|
|
2228
|
+
|
|
2229
|
+
/*-----+
|
|
2230
|
+
| WEB |
|
|
2231
|
+
+-----+*/
|
|
2232
|
+
// @ts-ignore Type mismatch: function type vs method signature
|
|
2233
|
+
setFrame = webCommands.setFrame;
|
|
2234
|
+
getCssProperty = webCommands.getCssProperty;
|
|
2235
|
+
submit = webCommands.submit;
|
|
2236
|
+
refresh = webCommands.refresh;
|
|
2237
|
+
getUrl = webCommands.getUrl;
|
|
2238
|
+
title = webCommands.title;
|
|
2239
|
+
getCookies = webCommands.getCookies;
|
|
2240
|
+
setCookie = webCommands.setCookie;
|
|
2241
|
+
deleteCookie = webCommands.deleteCookie;
|
|
2242
|
+
deleteCookies = webCommands.deleteCookies;
|
|
2243
|
+
cacheWebElement = webCommands.cacheWebElement;
|
|
2244
|
+
cacheWebElements = webCommands.cacheWebElements;
|
|
2245
|
+
executeAtom = webCommands.executeAtom;
|
|
2246
|
+
executeAtomAsync = webCommands.executeAtomAsync;
|
|
2247
|
+
getAtomsElement = webCommands.getAtomsElement;
|
|
2248
|
+
convertElementsForAtoms = webCommands.convertElementsForAtoms;
|
|
2249
|
+
getElementId = webCommands.getElementId;
|
|
2250
|
+
hasElementId = webCommands.hasElementId;
|
|
2251
|
+
findWebElementOrElements = webCommands.findWebElementOrElements;
|
|
2252
|
+
clickWebCoords = webCommands.clickWebCoords;
|
|
2253
|
+
getSafariIsIphone = webCommands.getSafariIsIphone;
|
|
2254
|
+
getSafariDeviceSize = webCommands.getSafariDeviceSize;
|
|
2255
|
+
getSafariIsNotched = webCommands.getSafariIsNotched;
|
|
2256
|
+
getExtraTranslateWebCoordsOffset = webCommands.getExtraTranslateWebCoordsOffset;
|
|
2257
|
+
getExtraNativeWebTapOffset = webCommands.getExtraNativeWebTapOffset;
|
|
2258
|
+
nativeWebTap = webCommands.nativeWebTap;
|
|
2259
|
+
translateWebCoords = webCommands.translateWebCoords;
|
|
2260
|
+
checkForAlert = webCommands.checkForAlert;
|
|
2261
|
+
waitForAtom = webCommands.waitForAtom;
|
|
2262
|
+
mobileWebNav = webCommands.mobileWebNav;
|
|
2263
|
+
getWdaLocalhostRoot = webCommands.getWdaLocalhostRoot;
|
|
2264
|
+
mobileCalibrateWebToRealCoordinatesTranslation = webCommands.mobileCalibrateWebToRealCoordinatesTranslation;
|
|
2265
|
+
mobileUpdateSafariPreferences = webCommands.mobileUpdateSafariPreferences;
|
|
2266
|
+
|
|
2267
|
+
/*--------+
|
|
2268
|
+
| XCTEST |
|
|
2269
|
+
+--------+*/
|
|
2270
|
+
mobileRunXCTest = xctestCommands.mobileRunXCTest;
|
|
2271
|
+
mobileInstallXCTestBundle = xctestCommands.mobileInstallXCTestBundle;
|
|
2272
|
+
mobileListXCTestBundles = xctestCommands.mobileListXCTestBundles;
|
|
2273
|
+
mobileListXCTestsInTestBundle = xctestCommands.mobileListXCTestsInTestBundle;
|
|
2274
|
+
|
|
2275
|
+
/*----------------------+
|
|
2276
|
+
| XCTEST SCREEN RECORD |
|
|
2277
|
+
+---------------------+*/
|
|
2278
|
+
mobileStartXctestScreenRecording = xctestRecordScreenCommands.mobileStartXctestScreenRecording;
|
|
2279
|
+
mobileGetXctestScreenRecordingInfo = xctestRecordScreenCommands.mobileGetXctestScreenRecordingInfo;
|
|
2280
|
+
mobileStopXctestScreenRecording = xctestRecordScreenCommands.mobileStopXctestScreenRecording;
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
export default XCUITestDriver;
|
|
2284
|
+
|
|
2285
|
+
/**
|
|
2286
|
+
* @template {import('@appium/types').Constraints} C
|
|
2287
|
+
* @template [Ctx=string]
|
|
2288
|
+
* @typedef {import('@appium/types').ExternalDriver<C, Ctx>} ExternalDriver
|
|
2289
|
+
*/
|
|
2290
|
+
|
|
2291
|
+
/**
|
|
2292
|
+
* @typedef {Pick<XCUITestDriverOpts, 'enforceAppInstall' | 'fullReset' | 'noReset' | 'bundleId' | 'app'>} AutInstallationStateOptions
|
|
2293
|
+
*/
|
|
2294
|
+
|
|
2295
|
+
/**
|
|
2296
|
+
* @typedef {Object} AutInstallationState
|
|
2297
|
+
* @property {boolean} install - If the given app should install, or not need to install.
|
|
2298
|
+
* @property {boolean} skipUninstall - If the installed app should be uninstalled, or not.
|
|
2299
|
+
*/
|
|
2300
|
+
|
|
2301
|
+
/**
|
|
2302
|
+
* @typedef {typeof desiredCapConstraints} XCUITestDriverConstraints
|
|
2303
|
+
* @typedef {import('@appium/types').DriverOpts<XCUITestDriverConstraints>} XCUITestDriverOpts
|
|
2304
|
+
* @typedef {import('./commands/types').FullContext} FullContext
|
|
2305
|
+
* @typedef {import('@limrun/appium-xcode').XcodeVersion} XcodeVersion
|
|
2306
|
+
* @typedef {import('@limrun/appium-ios-simulator').Simulator} Simulator
|
|
2307
|
+
*/
|
|
2308
|
+
|
|
2309
|
+
/**
|
|
2310
|
+
* @typedef {Object} DriverLogs
|
|
2311
|
+
* @property {import('./device-log/ios-device-log').IOSDeviceLog|import('./device-log/ios-simulator-log').IOSSimulatorLog} [syslog]
|
|
2312
|
+
* @property {import('./device-log/ios-crash-log').IOSCrashLog} [crashlog]
|
|
2313
|
+
* @property {import('./device-log/safari-console-log').SafariConsoleLog} [safariConsole]
|
|
2314
|
+
* @property {import('./device-log/safari-network-log').SafariNetworkLog} [safariNetwork]
|
|
2315
|
+
* @property {import('./device-log/ios-performance-log').IOSPerformanceLog} [performance]
|
|
2316
|
+
*/
|