detox 21.0.0-rc.5 → 21.0.0-rc.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. package/.eslintignore +1 -0
  2. package/Detox-android/com/wix/detox/{21.0.0-rc.5/detox-21.0.0-rc.5-javadoc.jar → 21.0.0-rc.7/detox-21.0.0-rc.7-javadoc.jar} +0 -0
  3. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7-javadoc.jar.md5 +1 -0
  4. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7-javadoc.jar.sha1 +1 -0
  5. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7-javadoc.jar.sha256 +1 -0
  6. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7-javadoc.jar.sha512 +1 -0
  7. package/Detox-android/com/wix/detox/{21.0.0-rc.5/detox-21.0.0-rc.5-sources.jar → 21.0.0-rc.7/detox-21.0.0-rc.7-sources.jar} +0 -0
  8. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7-sources.jar.md5 +1 -0
  9. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7-sources.jar.sha1 +1 -0
  10. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7-sources.jar.sha256 +1 -0
  11. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7-sources.jar.sha512 +1 -0
  12. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7.aar +0 -0
  13. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7.aar.md5 +1 -0
  14. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7.aar.sha1 +1 -0
  15. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7.aar.sha256 +1 -0
  16. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7.aar.sha512 +1 -0
  17. package/Detox-android/com/wix/detox/{21.0.0-rc.5/detox-21.0.0-rc.5.pom → 21.0.0-rc.7/detox-21.0.0-rc.7.pom} +1 -7
  18. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7.pom.md5 +1 -0
  19. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7.pom.sha1 +1 -0
  20. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7.pom.sha256 +1 -0
  21. package/Detox-android/com/wix/detox/21.0.0-rc.7/detox-21.0.0-rc.7.pom.sha512 +1 -0
  22. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  23. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  24. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  25. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  26. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  27. package/Detox-ios-framework.tbz +0 -0
  28. package/Detox-ios-src.tbz +0 -0
  29. package/Detox-ios-xcuitest.tbz +0 -0
  30. package/android/build.gradle +1 -1
  31. package/android/detox/build.gradle +13 -8
  32. package/android/detox/src/full/java/com/wix/detox/espresso/DetoxAssertion.java +44 -25
  33. package/android/detox/src/full/java/com/wix/detox/espresso/EspressoDetox.java +6 -7
  34. package/android/detox/src/full/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +2 -2
  35. package/android/detox/src/full/java/com/wix/detox/espresso/action/GetAttributesAction.kt +34 -35
  36. package/android/detox/src/full/java/com/wix/detox/espresso/common/MaterialSliderHelper.kt +21 -0
  37. package/android/detox/src/full/java/com/wix/detox/espresso/common/{SliderHelper.kt → ReactSliderHelper.kt} +6 -5
  38. package/android/detox/src/full/java/com/wix/detox/espresso/matcher/ViewMatchers.kt +2 -2
  39. package/android/detox/src/full/java/com/wix/detox/espresso/performer/MultipleViewsActionPerformer.kt +43 -0
  40. package/android/detox/src/full/java/com/wix/detox/espresso/performer/SingleViewActionPerformer.kt +19 -0
  41. package/android/detox/src/full/java/com/wix/detox/espresso/performer/ViewActionPerformer.kt +24 -0
  42. package/android/detox/src/full/java/com/wix/detox/espresso/web/WebElement.java +4 -4
  43. package/android/detox/src/full/java/com/wix/invoke/types/Invocation.java +7 -6
  44. package/android/detox/src/main/java/com/wix/detox/espresso/MultipleViewsAction.kt +4 -0
  45. package/android/detox/src/main/java/com/wix/detox/espresso/UiControllerSpy.kt +0 -1
  46. package/android/detox/src/main/java/com/wix/detox/espresso/ViewActionWithResult.kt +2 -1
  47. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/MotionEvents.kt +60 -4
  48. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/GetAttributesActionTest.kt +6 -5
  49. package/android/detox/src/testFull/java/com/wix/detox/espresso/common/MaterialSliderHelperTest.kt +33 -0
  50. package/android/detox/src/testFull/java/com/wix/detox/espresso/common/{SliderHelperTest.kt → ReactSliderHelperTest.kt} +3 -3
  51. package/android/detox/src/testFull/java/com/wix/detox/espresso/performer/ViewActionPerformerSpec.kt +37 -0
  52. package/android/detox/src/testFull/java/com/wix/invoke/JsonParserTest.java +23 -7
  53. package/android/detox/src/testFull/resources/targetInvocationEspressoWebDetoxScript.json +47 -0
  54. package/android/rninfo.gradle +3 -3
  55. package/detox.d.ts +1830 -0
  56. package/globals.d.ts +23 -0
  57. package/index.d.ts +2 -1823
  58. package/internals.d.ts +11 -1
  59. package/jest.config.js +108 -0
  60. package/local-cli/reset-lock-file.js +5 -9
  61. package/local-cli/testCommand/TestRunnerCommand.js +26 -3
  62. package/local-cli/utils/interruptListeners.js +15 -0
  63. package/package.json +2 -100
  64. package/runners/jest/reporter.js +21 -1
  65. package/runners/jest/reporters/DetoxIPCReporter.js +34 -0
  66. package/runners/jest/reporters/DetoxReporterDispatcher.js +144 -0
  67. package/runners/jest/reporters/DetoxSummaryReporter.js +16 -0
  68. package/runners/jest/reporters/DetoxVerboseReporter.js +16 -0
  69. package/runners/jest/reporters/index.js +6 -0
  70. package/runners/jest/testEnvironment/index.js +11 -0
  71. package/src/DetoxWorker.js +5 -11
  72. package/src/android/core/NativeElement.js +26 -29
  73. package/src/android/core/WebElement.js +24 -6
  74. package/src/android/espressoapi/DetoxAssertion.js +16 -14
  75. package/src/android/espressoapi/EspressoDetox.js +9 -2
  76. package/src/android/espressoapi/web/WebElement.js +1 -4
  77. package/src/android/interactions/native.js +2 -3
  78. package/src/artifacts/providers/index.js +3 -3
  79. package/src/artifacts/screenshot/SimulatorScreenshotPlugin.js +0 -17
  80. package/src/configuration/composeLoggerConfig.js +1 -0
  81. package/src/configuration/composeRunnerConfig.js +3 -1
  82. package/src/devices/allocation/DeviceAllocator.js +66 -20
  83. package/src/devices/allocation/DeviceList.js +44 -0
  84. package/src/devices/allocation/DeviceRegistry.js +189 -0
  85. package/src/devices/allocation/drivers/AllocationDriverBase.d.ts +15 -0
  86. package/src/devices/{common/drivers/android/tools → allocation/drivers/android}/FreeDeviceFinder.js +11 -10
  87. package/src/devices/allocation/drivers/android/attached/AttachedAndroidAllocDriver.js +22 -17
  88. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +97 -38
  89. package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +32 -45
  90. package/src/devices/allocation/drivers/android/emulator/FreeEmulatorFinder.js +1 -1
  91. package/src/devices/allocation/drivers/android/emulator/FreePortFinder.js +37 -0
  92. package/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js +3 -3
  93. package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +104 -32
  94. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceLauncher.js +40 -31
  95. package/src/devices/allocation/drivers/android/genycloud/GenyRegistry.js +121 -0
  96. package/src/devices/allocation/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +24 -0
  97. package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyRecipesService.js +1 -1
  98. package/src/devices/allocation/drivers/android/genycloud/services/dto/GenyInstance.js +83 -0
  99. package/src/devices/allocation/drivers/android/genycloud/services/dto/GenyRecipe.js +25 -0
  100. package/src/devices/allocation/drivers/ios/SimulatorAllocDriver.js +95 -54
  101. package/src/devices/allocation/drivers/ios/SimulatorQuery.js +24 -0
  102. package/src/devices/allocation/factories/android.js +29 -35
  103. package/src/devices/allocation/factories/ios.js +6 -7
  104. package/src/devices/common/drivers/DeviceCookie.d.ts +12 -0
  105. package/src/devices/common/drivers/android/cookies.d.ts +11 -0
  106. package/src/devices/common/drivers/android/emulator/exec/EmulatorExec.js +17 -5
  107. package/src/devices/common/drivers/android/exec/ADB.js +1 -0
  108. package/src/devices/common/drivers/android/tools/instrumentationArgs.js +7 -1
  109. package/src/devices/common/drivers/ios/cookies.d.ts +9 -0
  110. package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +3 -1
  111. package/src/devices/cookies/index.js +0 -6
  112. package/src/devices/runtime/drivers/android/genycloud/GenyCloudDriver.js +7 -6
  113. package/src/devices/runtime/drivers/ios/SimulatorDriver.js +5 -4
  114. package/src/devices/runtime/drivers/ios/XCUITestUtils.js +18 -7
  115. package/src/devices/runtime/factories/android.js +3 -11
  116. package/src/devices/runtime/factories/ios.js +3 -4
  117. package/src/{servicelocator → devices/servicelocator}/android/emulatorServiceLocator.js +1 -1
  118. package/src/devices/servicelocator/android/genycloudServiceLocator.js +17 -0
  119. package/src/devices/servicelocator/android/index.js +23 -0
  120. package/src/{validation → devices/validation}/EnvironmentValidatorBase.js +1 -0
  121. package/src/{validation → devices/validation}/android/GenycloudEnvValidator.js +2 -2
  122. package/src/{validation → devices/validation}/factories/index.js +1 -1
  123. package/src/{validation → devices/validation}/ios/IosSimulatorEnvValidator.js +2 -2
  124. package/src/environmentFactory.js +1 -11
  125. package/src/ios/web.js +21 -5
  126. package/src/ipc/IPCClient.js +22 -1
  127. package/src/ipc/IPCServer.js +42 -1
  128. package/src/ipc/SessionState.js +1 -0
  129. package/src/logger/DetoxLogger.js +2 -2
  130. package/src/realms/DetoxContext.js +8 -0
  131. package/src/realms/DetoxInternalsFacade.js +1 -0
  132. package/src/realms/DetoxPrimaryContext.js +48 -42
  133. package/src/realms/DetoxSecondaryContext.js +27 -0
  134. package/src/realms/symbols.js +6 -0
  135. package/src/utils/PIDService.js +27 -0
  136. package/src/utils/assertIsFunction.js +35 -0
  137. package/src/utils/environment.js +8 -15
  138. package/src/utils/errorUtils.js +3 -3
  139. package/src/utils/isArrowFunction.js +24 -0
  140. package/tsconfig.json +8 -3
  141. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5-javadoc.jar.md5 +0 -1
  142. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5-javadoc.jar.sha1 +0 -1
  143. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5-javadoc.jar.sha256 +0 -1
  144. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5-javadoc.jar.sha512 +0 -1
  145. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5-sources.jar.md5 +0 -1
  146. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5-sources.jar.sha1 +0 -1
  147. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5-sources.jar.sha256 +0 -1
  148. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5-sources.jar.sha512 +0 -1
  149. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5.aar +0 -0
  150. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5.aar.md5 +0 -1
  151. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5.aar.sha1 +0 -1
  152. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5.aar.sha256 +0 -1
  153. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5.aar.sha512 +0 -1
  154. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5.pom.md5 +0 -1
  155. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5.pom.sha1 +0 -1
  156. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5.pom.sha256 +0 -1
  157. package/Detox-android/com/wix/detox/21.0.0-rc.5/detox-21.0.0-rc.5.pom.sha512 +0 -1
  158. package/runners/jest/reporters/DetoxReporter.js +0 -36
  159. package/src/devices/DeviceRegistry.js +0 -176
  160. package/src/devices/allocation/drivers/AllocationDriverBase.js +0 -30
  161. package/src/devices/allocation/drivers/android/attached/AttachedAndroidLauncher.js +0 -13
  162. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocationHelper.js +0 -72
  163. package/src/devices/allocation/drivers/android/genycloud/GenyDeviceRegistryFactory.js +0 -16
  164. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceAllocationHelper.js +0 -65
  165. package/src/devices/allocation/drivers/ios/SimulatorLauncher.js +0 -21
  166. package/src/devices/common/drivers/DeviceAllocationHelper.js +0 -20
  167. package/src/devices/common/drivers/DeviceLauncher.js +0 -19
  168. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +0 -25
  169. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLookupService.js +0 -38
  170. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceNaming.js +0 -14
  171. package/src/devices/common/drivers/android/genycloud/services/dto/GenyInstance.js +0 -66
  172. package/src/devices/common/drivers/android/genycloud/services/dto/GenyRecipe.js +0 -13
  173. package/src/devices/cookies/AndroidDeviceCookie.js +0 -13
  174. package/src/devices/cookies/AndroidEmulatorCookie.js +0 -6
  175. package/src/devices/cookies/AttachedAndroidDeviceCookie.js +0 -12
  176. package/src/devices/cookies/DeviceCookie.js +0 -4
  177. package/src/devices/cookies/GenycloudEmulatorCookie.js +0 -20
  178. package/src/devices/cookies/IosCookie.js +0 -6
  179. package/src/devices/cookies/IosSimulatorCookie.js +0 -10
  180. package/src/devices/lifecycle/GenyGlobalLifecycleHandler.js +0 -71
  181. package/src/devices/lifecycle/factories/GenyGlobalLifecycleHandlerFactory.js +0 -18
  182. package/src/servicelocator/android/genycloudServiceLocator.js +0 -21
  183. package/src/servicelocator/android/index.js +0 -25
  184. package/src/servicelocator/ios.js +0 -7
  185. /package/src/devices/{common → allocation}/drivers/android/genycloud/exec/GenyCloudExec.js +0 -0
  186. /package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyAuthService.js +0 -0
@@ -1,22 +1,35 @@
1
- // @ts-nocheck
1
+ /**
2
+ * @typedef {import('../AllocationDriverBase').AllocationDriverBase} AllocationDriverBase
3
+ * @typedef {import('../AllocationDriverBase').DeallocOptions} DeallocOptions
4
+ * @typedef {import('../../../common/drivers/ios/cookies').IosSimulatorCookie} IosSimulatorCookie
5
+ */
6
+
2
7
  const _ = require('lodash');
3
8
 
4
- const DetoxRuntimeError = require('../../../../errors/DetoxRuntimeError');
5
- const IosSimulatorCookie = require('../../../cookies/IosSimulatorCookie');
6
- const AllocationDriverBase = require('../AllocationDriverBase');
9
+ const { DetoxRuntimeError } = require('../../../../errors');
10
+ const log = require('../../../../utils/logger').child({ cat: 'device,device-allocation' });
11
+
12
+ const SimulatorQuery = require('./SimulatorQuery');
7
13
 
8
- class SimulatorAllocDriver extends AllocationDriverBase {
14
+ /**
15
+ * @implements {AllocationDriverBase}
16
+ */
17
+ class SimulatorAllocDriver {
9
18
  /**
10
- * @param deviceRegistry { DeviceRegistry }
11
- * @param applesimutils { AppleSimUtils }
12
- * @param simulatorLauncher { SimulatorLauncher }
19
+ * @param {object} options
20
+ * @param {import('../../DeviceRegistry')} options.deviceRegistry
21
+ * @param {DetoxInternals.RuntimeConfig} options.detoxConfig
22
+ * @param {import('../../../common/drivers/ios/tools/AppleSimUtils')} options.applesimutils
13
23
  */
14
- constructor({ deviceRegistry, applesimutils, simulatorLauncher }) {
15
- super();
24
+ constructor({ detoxConfig, deviceRegistry, applesimutils }) {
16
25
  this._deviceRegistry = deviceRegistry;
17
26
  this._applesimutils = applesimutils;
18
- this._simulatorLauncher = simulatorLauncher;
19
27
  this._launchInfo = {};
28
+ this._shouldShutdown = detoxConfig.behavior.cleanup.shutdownDevice;
29
+ }
30
+
31
+ async init() {
32
+ await this._deviceRegistry.unregisterZombieDevices();
20
33
  }
21
34
 
22
35
  /**
@@ -24,30 +37,37 @@ class SimulatorAllocDriver extends AllocationDriverBase {
24
37
  * @return {Promise<IosSimulatorCookie>}
25
38
  */
26
39
  async allocate(deviceConfig) {
27
- const deviceQuery = this._adaptQuery(deviceConfig.device);
40
+ const deviceQuery = new SimulatorQuery(deviceConfig.device);
28
41
 
29
42
  // TODO Delegate this onto a well tested allocator class
30
- const udid = await this._deviceRegistry.allocateDevice(async () => {
43
+ const udid = await this._deviceRegistry.registerDevice(async () => {
31
44
  return await this._findOrCreateDevice(deviceQuery);
32
45
  });
33
46
 
34
- const deviceComment = this._commentDevice(deviceQuery);
35
47
  if (!udid) {
36
- throw new DetoxRuntimeError(`Failed to find device matching ${deviceComment}`);
48
+ throw new DetoxRuntimeError(`Failed to find device matching ${deviceQuery.getDeviceComment()}`);
37
49
  }
38
50
 
39
51
  this._launchInfo[udid] = { deviceConfig };
40
- return new IosSimulatorCookie(udid);
52
+ return { id: udid, udid };
41
53
  }
42
54
 
43
55
  /**
44
56
  * @param {IosSimulatorCookie} deviceCookie
45
- * @returns {Promise<void>}
57
+ * @returns {Promise<IosSimulatorCookie>}
46
58
  */
47
59
  async postAllocate(deviceCookie) {
48
60
  const { udid } = deviceCookie;
49
61
  const { deviceConfig } = this._launchInfo[udid];
50
- await this._simulatorLauncher.launch(udid, deviceConfig.type, deviceConfig.bootArgs, deviceConfig.headless);
62
+ await this._applesimutils.boot(udid, deviceConfig.bootArgs, deviceConfig.headless);
63
+
64
+ return {
65
+ id: udid,
66
+ udid,
67
+ type: deviceConfig.type,
68
+ bootArgs: deviceConfig.bootArgs,
69
+ headless: deviceConfig.headless,
70
+ };
51
71
  }
52
72
 
53
73
  /**
@@ -58,21 +78,40 @@ class SimulatorAllocDriver extends AllocationDriverBase {
58
78
  async free(cookie, options = {}) {
59
79
  const { udid } = cookie;
60
80
 
61
- await this._deviceRegistry.disposeDevice(udid);
62
-
63
81
  if (options.shutdown) {
64
- await this._simulatorLauncher.shutdown(udid);
82
+ await this._doShutdown(udid);
83
+ await this._deviceRegistry.unregisterDevice(udid);
84
+ } else {
85
+ await this._deviceRegistry.releaseDevice(udid);
86
+ }
87
+ }
88
+
89
+ async cleanup() {
90
+ if (this._shouldShutdown) {
91
+ const sessionDevices = await this._deviceRegistry.readSessionDevices();
92
+ const shutdownPromises = sessionDevices.getIds().map((udid) => this._doShutdown(udid));
93
+ await Promise.all(shutdownPromises);
94
+ }
95
+
96
+ await this._deviceRegistry.unregisterSessionDevices();
97
+ }
98
+
99
+ /**
100
+ * @param {string} udid
101
+ * @returns {Promise<void>}
102
+ * @private
103
+ */
104
+ async _doShutdown(udid) {
105
+ try {
106
+ await this._applesimutils.shutdown(udid);
107
+ } catch (err) {
108
+ log.warn({ err }, `Failed to shutdown simulator ${udid}`);
65
109
  }
66
110
  }
67
111
 
68
112
  /***
69
113
  * @private
70
- * @param deviceQuery {{
71
- * byId?: string;
72
- * byName?: string;
73
- * byType?: string;
74
- * byOS?: string;
75
- * }}
114
+ * @param {SimulatorQuery} deviceQuery
76
115
  * @returns {Promise<String>}
77
116
  */
78
117
  async _findOrCreateDevice(deviceQuery) {
@@ -83,6 +122,7 @@ class SimulatorAllocDriver extends AllocationDriverBase {
83
122
  if (_.isEmpty(free)) {
84
123
  const prototypeDevice = taken[0];
85
124
  udid = this._applesimutils.create(prototypeDevice);
125
+ await this._runScreenshotWorkaround(udid);
86
126
  } else {
87
127
  udid = free[0].udid;
88
128
  }
@@ -90,11 +130,30 @@ class SimulatorAllocDriver extends AllocationDriverBase {
90
130
  return udid;
91
131
  }
92
132
 
133
+ async _runScreenshotWorkaround(udid) {
134
+ await this._applesimutils.takeScreenshot(udid, '/dev/null').catch(() => {
135
+ log.debug({}, `
136
+ NOTE: For an unknown yet reason, taking the first screenshot is apt
137
+ to fail when booting iOS Simulator in a hidden window mode (or on CI).
138
+ Detox applies a workaround by taking a dummy screenshot to ensure
139
+ that the future ones are going to work fine. This screenshot is not
140
+ saved anywhere, and the error above is suppressed for all log levels
141
+ except for "debug" and "trace."
142
+ `.trim());
143
+ });
144
+ }
145
+
146
+ /**
147
+ * @private
148
+ * @param {SimulatorQuery} deviceQuery
149
+ */
93
150
  async _groupDevicesByStatus(deviceQuery) {
94
151
  const searchResults = await this._queryDevices(deviceQuery);
95
- const { rawDevices: takenDevices } = this._deviceRegistry.getRegisteredDevices();
96
- const takenUDIDs = new Set(_.map(takenDevices, 'id'));
97
- const { taken, free } = _.groupBy(searchResults, ({ udid }) => takenUDIDs.has(udid) ? 'taken' : 'free');
152
+ const takenDevices = this._deviceRegistry.getTakenDevicesSync();
153
+
154
+ const { taken, free } = _.groupBy(searchResults, ({ udid }) => {
155
+ return takenDevices.includes(udid) ? 'taken' : 'free';
156
+ });
98
157
 
99
158
  const targetOS = _.get(taken, '0.os.identifier');
100
159
  const isMatching = targetOS && { os: { identifier: targetOS } };
@@ -105,43 +164,25 @@ class SimulatorAllocDriver extends AllocationDriverBase {
105
164
  };
106
165
  }
107
166
 
167
+ /**
168
+ * @private
169
+ * @param {SimulatorQuery} deviceQuery
170
+ */
108
171
  async _queryDevices(deviceQuery) {
109
172
  const result = await this._applesimutils.list(
110
173
  deviceQuery,
111
- `Searching for device ${this._commentQuery(deviceQuery)} ...`
174
+ `Searching for device ${deviceQuery} ...`
112
175
  );
113
176
 
114
177
  if (_.isEmpty(result)) {
115
178
  throw new DetoxRuntimeError({
116
- message: `Failed to find a device ${this._commentQuery(deviceQuery)}`,
179
+ message: `Failed to find a device ${deviceQuery}`,
117
180
  hint: `Run 'applesimutils --list' to list your supported devices. ` +
118
181
  `It is advised only to specify a device type, e.g., "iPhone Xʀ" and avoid explicit search by OS version.`
119
182
  });
120
183
  }
121
184
  return result;
122
185
  }
123
-
124
- _adaptQuery({ id, name, os, type }) {
125
- return _.omitBy({
126
- byId: id,
127
- byName: name,
128
- byOS: os,
129
- byType: type,
130
- }, _.isUndefined);
131
- }
132
-
133
- _commentQuery({ byId, byName, byOS, byType }) {
134
- return _.compact([
135
- byId && `by UDID = ${JSON.stringify(byId)}`,
136
- byName && `by name = ${JSON.stringify(byName)}`,
137
- byType && `by type = ${JSON.stringify(byType)}`,
138
- byOS && `by OS = ${JSON.stringify(byOS)}`,
139
- ]).join(' and ');
140
- }
141
-
142
- _commentDevice({ byId, byName, byOS, byType }) {
143
- return byId || _.compact([byName, byType, byOS]).join(', ');
144
- }
145
186
  }
146
187
 
147
188
  module.exports = SimulatorAllocDriver;
@@ -0,0 +1,24 @@
1
+ class SimulatorQuery {
2
+ /** @param {Partial<Detox.IosSimulatorQuery>} query */
3
+ constructor({ id, name, os, type }) {
4
+ if (id != null) this.byId = id;
5
+ if (name != null) this.byName = name;
6
+ if (os != null) this.byOS = os;
7
+ if (type != null) this.byType = type;
8
+ }
9
+
10
+ getDeviceComment() {
11
+ return this.byId || [this.byName, this.byType, this.byOS].filter(Boolean).join(', ');
12
+ }
13
+
14
+ toString() {
15
+ return [
16
+ this.byId && `by UDID = ${JSON.stringify(this.byId)}`,
17
+ this.byName && `by name = ${JSON.stringify(this.byName)}`,
18
+ this.byType && `by type = ${JSON.stringify(this.byType)}`,
19
+ this.byOS && `by OS = ${JSON.stringify(this.byOS)}`,
20
+ ].filter(Boolean).join(' and ');
21
+ }
22
+ }
23
+
24
+ module.exports = SimulatorQuery;
@@ -2,11 +2,12 @@
2
2
  const DeviceAllocatorFactory = require('./base');
3
3
 
4
4
  class AndroidEmulator extends DeviceAllocatorFactory {
5
- _createDriver({ eventEmitter }) {
6
- const serviceLocator = require('../../../servicelocator/android');
5
+ _createDriver({ detoxSession, detoxConfig }) {
6
+ const serviceLocator = require('../../servicelocator/android');
7
7
  const adb = serviceLocator.adb;
8
8
  const emulatorExec = serviceLocator.emulator.exec;
9
- const deviceRegistry = serviceLocator.deviceRegistry;
9
+ const DeviceRegistry = require('../../allocation/DeviceRegistry');
10
+ const deviceRegistry = new DeviceRegistry({ sessionId: detoxSession.id });
10
11
 
11
12
  const AVDsResolver = require('../drivers/android/emulator/AVDsResolver');
12
13
  const avdsResolver = new AVDsResolver(emulatorExec);
@@ -20,74 +21,67 @@ class AndroidEmulator extends DeviceAllocatorFactory {
20
21
  const FreeEmulatorFinder = require('../drivers/android/emulator/FreeEmulatorFinder');
21
22
  const freeEmulatorFinder = new FreeEmulatorFinder(adb, deviceRegistry);
22
23
 
23
- const EmulatorLauncher = require('../drivers/android/emulator/EmulatorLauncher');
24
- const emulatorLauncher = new EmulatorLauncher({ adb, emulatorExec, eventEmitter });
24
+ const FreePortFinder = require('../drivers/android/emulator/FreePortFinder');
25
+ const freePortFinder = new FreePortFinder();
25
26
 
26
- const EmulatorAllocationHelper = require('../drivers/android/emulator/EmulatorAllocationHelper');
27
- const allocationHelper = new EmulatorAllocationHelper(deviceRegistry, freeEmulatorFinder);
27
+ const EmulatorLauncher = require('../drivers/android/emulator/EmulatorLauncher');
28
+ const emulatorLauncher = new EmulatorLauncher({ adb, emulatorExec });
28
29
 
29
30
  const EmulatorAllocDriver = require('../drivers/android/emulator/EmulatorAllocDriver');
30
31
  return new EmulatorAllocDriver({
31
32
  adb,
32
33
  avdValidator,
34
+ detoxConfig,
35
+ deviceRegistry,
33
36
  emulatorVersionResolver,
34
37
  emulatorLauncher,
35
- allocationHelper,
38
+ freeDeviceFinder: freeEmulatorFinder,
39
+ freePortFinder,
36
40
  });
37
41
  }
38
42
  }
39
43
 
40
44
  class AndroidAttached extends DeviceAllocatorFactory {
41
- _createDriver({ eventEmitter }) {
42
- const serviceLocator = require('../../../servicelocator/android');
45
+ _createDriver({ detoxSession, detoxConfig }) {
46
+ const serviceLocator = require('../../servicelocator/android');
43
47
  const adb = serviceLocator.adb;
44
- const deviceRegistry = serviceLocator.deviceRegistry;
48
+ const DeviceRegistry = require('../../allocation/DeviceRegistry');
49
+ const deviceRegistry = new DeviceRegistry({ sessionId: detoxSession.id });
45
50
 
46
- const FreeDeviceFinder = require('../../common/drivers/android/tools/FreeDeviceFinder');
51
+ const FreeDeviceFinder = require('../drivers/android/FreeDeviceFinder');
47
52
  const freeDeviceFinder = new FreeDeviceFinder(adb, deviceRegistry);
48
53
 
49
- const AttachedAndroidLauncher = require('../drivers/android/attached/AttachedAndroidLauncher');
50
- const attachedAndroidLauncher = new AttachedAndroidLauncher(eventEmitter);
51
-
52
54
  const AttachedAndroidAllocDriver = require('../drivers/android/attached/AttachedAndroidAllocDriver');
53
- return new AttachedAndroidAllocDriver({ adb, deviceRegistry, freeDeviceFinder, attachedAndroidLauncher });
55
+ return new AttachedAndroidAllocDriver({ adb, deviceRegistry, freeDeviceFinder });
54
56
  }
55
57
  }
56
58
 
57
59
  class Genycloud extends DeviceAllocatorFactory {
58
- _createDriver({ eventEmitter }) {
59
- const serviceLocator = require('../../../servicelocator/android');
60
+ _createDriver(deps) {
61
+ const serviceLocator = require('../../servicelocator/android');
60
62
  const adb = serviceLocator.adb;
61
63
  const exec = serviceLocator.genycloud.exec;
62
- const deviceRegistry = serviceLocator.genycloud.runtimeDeviceRegistry;
63
- const deviceCleanupRegistry = serviceLocator.genycloud.cleanupDeviceRegistry;
64
64
 
65
- const InstanceNaming = require('../../common/drivers/android/genycloud/services/GenyInstanceNaming');
66
- const instanceNaming = new InstanceNaming(); // TODO should consider a permissive impl for debug/dev mode. Maybe even a custom arg in package.json (Detox > ... > genycloud > sharedAccount: false)
67
-
68
- const RecipesService = require('../../common/drivers/android/genycloud/services/GenyRecipesService');
65
+ const RecipesService = require('../drivers/android/genycloud/services/GenyRecipesService');
69
66
  const recipeService = new RecipesService(exec);
70
67
 
71
- const InstanceLookupService = require('../../common/drivers/android/genycloud/services/GenyInstanceLookupService');
72
- const instanceLookupService = new InstanceLookupService(exec, instanceNaming, deviceRegistry);
73
-
74
- const InstanceLifecycleService = require('../../common/drivers/android/genycloud/services/GenyInstanceLifecycleService');
75
- const instanceLifecycleService = new InstanceLifecycleService(exec, instanceNaming);
68
+ const InstanceLifecycleService = require('../drivers/android/genycloud/services/GenyInstanceLifecycleService');
69
+ const instanceLifecycleService = new InstanceLifecycleService(exec);
76
70
 
77
71
  const RecipeQuerying = require('../drivers/android/genycloud/GenyRecipeQuerying');
78
72
  const recipeQuerying = new RecipeQuerying(recipeService);
79
73
 
80
- const InstanceAllocationHelper = require('../drivers/android/genycloud/GenyInstanceAllocationHelper');
81
- const allocationHelper = new InstanceAllocationHelper({ deviceRegistry, instanceLookupService, instanceLifecycleService });
82
-
83
74
  const InstanceLauncher = require('../drivers/android/genycloud/GenyInstanceLauncher');
75
+ const instanceLauncher = new InstanceLauncher({ genyCloudExec: exec, instanceLifecycleService });
76
+
84
77
  const GenyAllocDriver = require('../drivers/android/genycloud/GenyAllocDriver');
85
- const instanceLauncher = new InstanceLauncher({ instanceLifecycleService, instanceLookupService, deviceCleanupRegistry, eventEmitter });
78
+
86
79
  return new GenyAllocDriver({
87
80
  adb,
88
- recipeQuerying,
89
- allocationHelper,
90
81
  instanceLauncher,
82
+ instanceLifecycleService,
83
+ recipeQuerying,
84
+ ...deps,
91
85
  });
92
86
  }
93
87
  }
@@ -2,16 +2,15 @@
2
2
  const DeviceAllocatorFactory = require('./base');
3
3
 
4
4
  class IosSimulator extends DeviceAllocatorFactory {
5
- _createDriver({ eventEmitter }) {
6
- const serviceLocator = require('../../../servicelocator/ios');
7
- const applesimutils = serviceLocator.appleSimUtils;
8
- const deviceRegistry = serviceLocator.deviceRegistry;
5
+ _createDriver({ detoxConfig, detoxSession, eventEmitter }) {
6
+ const AppleSimUtils = require('../../../devices/common/drivers/ios/tools/AppleSimUtils');
7
+ const applesimutils = new AppleSimUtils();
9
8
 
10
- const SimulatorLauncher = require('../drivers/ios/SimulatorLauncher');
11
- const simulatorLauncher = new SimulatorLauncher({ applesimutils, eventEmitter });
9
+ const DeviceRegistry = require('../../../devices/allocation/DeviceRegistry');
10
+ const deviceRegistry = new DeviceRegistry({ sessionId: detoxSession.id });
12
11
 
13
12
  const SimulatorAllocDriver = require('../drivers/ios/SimulatorAllocDriver');
14
- return new SimulatorAllocDriver({ deviceRegistry, applesimutils, simulatorLauncher });
13
+ return new SimulatorAllocDriver({ detoxConfig, deviceRegistry, applesimutils });
15
14
  }
16
15
  }
17
16
 
@@ -0,0 +1,12 @@
1
+ /* eslint-disable import/no-unresolved,node/no-missing-import,node/no-unsupported-features/es-syntax */
2
+
3
+ /**
4
+ * A serializable object that represents a device.
5
+ */
6
+ export interface DeviceCookie {
7
+ /** The device's unique identifier. */
8
+ id: string;
9
+ /** The display name of the device. */
10
+ name?: string;
11
+ }
12
+
@@ -0,0 +1,11 @@
1
+ /* eslint-disable import/no-unresolved,node/no-missing-import,node/no-unsupported-features/es-syntax */
2
+ import GenyInstance from '../../../allocation/drivers/android/genycloud/services/dto/GenyInstance';
3
+ import { DeviceCookie } from '../DeviceCookie';
4
+
5
+ interface AndroidDeviceCookie extends DeviceCookie {
6
+ adbName: string;
7
+ }
8
+
9
+ interface GenycloudEmulatorCookie extends AndroidDeviceCookie {
10
+ instance: GenyInstance;
11
+ }
@@ -32,19 +32,31 @@ class QueryVersionCommand extends ExecCommand {
32
32
  }
33
33
 
34
34
  class LaunchCommand extends ExecCommand {
35
- constructor(emulatorName, options) {
35
+ constructor(options) {
36
36
  super();
37
37
  this._options = options;
38
- this._args = this._getEmulatorArgs(emulatorName);
39
- this.port = options.port;
38
+ this._args = this._getEmulatorArgs();
39
+ }
40
+
41
+ get adbName() {
42
+ return this._options.adbName;
43
+ }
44
+
45
+ get avdName() {
46
+ return this._options.avdName;
47
+ }
48
+
49
+ get port() {
50
+ return this._options.port;
40
51
  }
41
52
 
42
53
  _getArgs() {
43
54
  return this._args;
44
55
  }
45
56
 
46
- _getEmulatorArgs(emulatorName) {
57
+ _getEmulatorArgs() {
47
58
  const {
59
+ avdName,
48
60
  bootArgs,
49
61
  gpuMode = this._getDefaultGPUMode(),
50
62
  headless,
@@ -64,7 +76,7 @@ class LaunchCommand extends ExecCommand {
64
76
  port ? '-port' : '',
65
77
  port ? `${port}` : '',
66
78
  ...deviceBootArgs,
67
- `@${emulatorName}`
79
+ `@${avdName}`
68
80
  ]);
69
81
 
70
82
  return emulatorArgs;
@@ -18,6 +18,7 @@ class ADB {
18
18
 
19
19
  async devices() {
20
20
  const { stdout } = await this.adbCmd('', 'devices', { verbosity: 'high' });
21
+ /** @type {DeviceHandle[]} */
21
22
  const devices = _.chain(stdout)
22
23
  .trim()
23
24
  .split('\n')
@@ -1,6 +1,7 @@
1
1
  const _ = require('lodash');
2
2
 
3
3
  const { encodeBase64 } = require('../../../../../utils/encoding');
4
+ const { autoEscape } = require('../../../../../utils/shellUtils');
4
5
 
5
6
  const reservedInstrumentationArgs = new Set(['class', 'package', 'func', 'unit', 'size', 'perf', 'debug', 'log', 'emma', 'coverageFile']);
6
7
  const isReservedInstrumentationArg = (arg) => reservedInstrumentationArgs.has(arg);
@@ -17,7 +18,8 @@ function prepareInstrumentationArgs(args) {
17
18
  valueEncoded = encodeBase64(valueAsString);
18
19
  }
19
20
 
20
- result.push('-e', key, valueEncoded);
21
+ const valueEscaped = hasLegacyIssues(key) ? valueEncoded : autoEscape.shell(valueEncoded);
22
+ result.push('-e', key, valueEscaped);
21
23
  return result;
22
24
  }, []);
23
25
 
@@ -27,6 +29,10 @@ function prepareInstrumentationArgs(args) {
27
29
  };
28
30
  }
29
31
 
32
+ function hasLegacyIssues(key) {
33
+ return key === 'detoxURLBlacklistRegex';
34
+ }
35
+
30
36
  module.exports = {
31
37
  prepareInstrumentationArgs,
32
38
  };
@@ -0,0 +1,9 @@
1
+ /* eslint-disable import/no-unresolved,node/no-missing-import,node/no-unsupported-features/es-syntax */
2
+ import { DeviceCookie } from '../DeviceCookie';
3
+
4
+ interface IosSimulatorCookie extends DeviceCookie {
5
+ udid: string;
6
+ type?: string;
7
+ bootArgs?: string;
8
+ headless?: boolean;
9
+ }
@@ -288,7 +288,7 @@ class AppleSimUtils {
288
288
  // want to make sure it isn't now.
289
289
  if (err.code === 3 &&
290
290
  (err.stderr.includes(`the app is not currently running`) ||
291
- err.stderr.includes(`The operation couldn’t be completed. found nothing to terminate`))) {
291
+ err.stderr.includes(`found nothing to terminate`))) {
292
292
  return;
293
293
  }
294
294
 
@@ -446,6 +446,8 @@ class AppleSimUtils {
446
446
  overrides.push(`--cellularMode "${flags.cellularMode}"`);
447
447
  if (flags.cellularBars)
448
448
  overrides.push(`--cellularBars "${flags.cellularBars}"`);
449
+ if (flags.operatorName)
450
+ overrides.push(`--operatorName "${flags.operatorName}"`);
449
451
  if (flags.batteryState)
450
452
  overrides.push(`--batteryState "${flags.batteryState}"`);
451
453
  if (flags.batteryLevel)
@@ -1,6 +0,0 @@
1
- module.exports = {
2
- IosSimulatorCookie: require('./IosSimulatorCookie'),
3
- AttachedAndroidDeviceCookie: require('./AttachedAndroidDeviceCookie'),
4
- AndroidEmulatorCookie: require('./AndroidEmulatorCookie'),
5
- GenycloudEmulatorCookie: require('./GenycloudEmulatorCookie'),
6
- };
@@ -8,21 +8,22 @@ const AndroidDriver = require('../AndroidDriver');
8
8
 
9
9
  /**
10
10
  * @typedef GenycloudDriverProps
11
- * @property instance { GenyInstance } The DTO associated with the cloud instance
11
+ * @property adbName { GenyInstance } The DTO associated with the cloud instance
12
12
  */
13
13
 
14
14
  class GenyCloudDriver extends AndroidDriver {
15
15
  /**
16
16
  * @param deps { GenycloudDriverDeps }
17
- * @param props { GenycloudDriverProps }
17
+ * @param props { GenycloudEmulatorCookie }
18
18
  */
19
- constructor(deps, { instance }) {
20
- super(deps, { adbName: instance.adbName });
21
- this.instance = instance;
19
+ constructor(deps, { adbName, name }) {
20
+ super(deps, { adbName });
21
+
22
+ this._instanceName = name;
22
23
  }
23
24
 
24
25
  getDeviceName() {
25
- return this.instance.toString();
26
+ return this._instanceName;
26
27
  }
27
28
 
28
29
  async setLocation(lat, lon) {
@@ -17,7 +17,6 @@ const { launchXCUITest } = require('./XCUITestUtils');
17
17
 
18
18
  /**
19
19
  * @typedef SimulatorDriverDeps { DeviceDriverDeps }
20
- * @property simulatorLauncher { SimulatorLauncher }
21
20
  * @property applesimutils { AppleSimUtils }
22
21
  */
23
22
 
@@ -41,7 +40,6 @@ class SimulatorDriver extends IosDriver {
41
40
  this._bootArgs = bootArgs;
42
41
  this._headless = headless;
43
42
  this._deviceName = `${udid} (${this._type})`;
44
- this._simulatorLauncher = deps.simulatorLauncher;
45
43
  this._applesimutils = deps.applesimutils;
46
44
  // TODO: allocate unique-per-worker available port.
47
45
  this._testTargetServerPort = 8997 + _.random(0, 1000);
@@ -190,9 +188,12 @@ class SimulatorDriver extends IosDriver {
190
188
  }
191
189
 
192
190
  async resetContentAndSettings() {
193
- await this._simulatorLauncher.shutdown(this.udid);
191
+ await this.emitter.emit('beforeShutdownDevice', { deviceId: this.udid });
192
+ await this._applesimutils.shutdown(this.udid);
193
+ await this.emitter.emit('shutdownDevice', { deviceId: this.udid });
194
194
  await this._applesimutils.resetContentAndSettings(this.udid);
195
- await this._simulatorLauncher.launch(this.udid, this._type, this._bootArgs, this._headless);
195
+ await this._applesimutils.boot(this.udid, this._bootArgs, this._headless);
196
+ await this.emitter.emit('bootDevice', { deviceId: this.udid });
196
197
  }
197
198
 
198
199
  getLogsPaths() {