detox 19.10.0 → 19.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. package/Detox-android/com/wix/detox/{19.10.0/detox-19.10.0-javadoc.jar → 19.11.0/detox-19.11.0-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0-javadoc.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{19.10.0/detox-19.10.0-sources.jar → 19.11.0/detox-19.11.0-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0.aar +0 -0
  12. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0.aar.md5 +1 -0
  13. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0.aar.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0.aar.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0.aar.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/{19.10.0/detox-19.10.0.pom → 19.11.0/detox-19.11.0.pom} +1 -1
  17. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0.pom.md5 +1 -0
  18. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0.pom.sha1 +1 -0
  19. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0.pom.sha256 +1 -0
  20. package/Detox-android/com/wix/detox/19.11.0/detox-19.11.0.pom.sha512 +1 -0
  21. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  22. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  23. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  24. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  25. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  26. package/Detox-ios-src.tbz +0 -0
  27. package/Detox-ios.tbz +0 -0
  28. package/android/detox/src/full/java/com/wix/detox/LaunchArgs.java +9 -0
  29. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeExtension.kt +15 -2
  30. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeIdlingResources.kt +43 -38
  31. package/package.json +2 -2
  32. package/runners/jest-circus/listeners/DetoxCoreListener.js +24 -15
  33. package/src/DetoxExportWrapper.js +1 -1
  34. package/src/android/core/NativeElement.js +56 -20
  35. package/src/android/core/NativeExpect.js +28 -9
  36. package/src/android/interactions/native.js +24 -18
  37. package/src/artifacts/timeline/TimelineArtifactPlugin.js +6 -9
  38. package/src/artifacts/timeline/TimelineContextTypes.js +7 -0
  39. package/src/devices/allocation/DeviceAllocator.js +1 -2
  40. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +1 -1
  41. package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +1 -2
  42. package/src/devices/runtime/RuntimeDevice.js +7 -11
  43. package/src/ios/expectTwo.js +152 -67
  44. package/src/utils/invocationTraceDescriptions.js +43 -0
  45. package/src/utils/trace.js +52 -10
  46. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0-javadoc.jar.md5 +0 -1
  47. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0-javadoc.jar.sha1 +0 -1
  48. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0-javadoc.jar.sha256 +0 -1
  49. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0-javadoc.jar.sha512 +0 -1
  50. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0-sources.jar.md5 +0 -1
  51. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0-sources.jar.sha1 +0 -1
  52. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0-sources.jar.sha256 +0 -1
  53. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0-sources.jar.sha512 +0 -1
  54. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0.aar +0 -0
  55. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0.aar.md5 +0 -1
  56. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0.aar.sha1 +0 -1
  57. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0.aar.sha256 +0 -1
  58. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0.aar.sha512 +0 -1
  59. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0.pom.md5 +0 -1
  60. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0.pom.sha1 +0 -1
  61. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0.pom.sha256 +0 -1
  62. package/Detox-android/com/wix/detox/19.10.0/detox-19.10.0.pom.sha512 +0 -1
@@ -0,0 +1 @@
1
+ 9aba5676c7298fb259def31abb2391c2
@@ -0,0 +1 @@
1
+ 8489941696f685e4d9ee0f2695e524a9ee882ff7
@@ -0,0 +1 @@
1
+ 5c074864be82defddfe6f03d23f1023718773a6caf867fd6de97306167b96c10
@@ -0,0 +1 @@
1
+ fc404aa3532e1bf13a1e12871b9a4d6c27575cacdfae5b76179308f321091b67d60f5a81e74bd0b3d6304a7930c3a9f79d15015fda37d4cdc5cb849274eb4b6b
@@ -0,0 +1 @@
1
+ c607efb999d878b919150d3c868094fe
@@ -0,0 +1 @@
1
+ 18641f7574235c6ec8c81ea52b75442ce919f213
@@ -0,0 +1 @@
1
+ 0e23270810340a40547ede0002fb741353646db1878d5825f67a2212a13d3d41
@@ -0,0 +1 @@
1
+ 440caa1e9ede22144e4f55d2c2c034c13e4b336f9f387f0fa0811bd651612c0bef7981c1b1ae826ab78519185c930a9287a6c6f3c76a554184282fdf8684a344
@@ -0,0 +1 @@
1
+ 88fff568e621751c02f293579cce453a
@@ -0,0 +1 @@
1
+ 9398e3ea8573a634ddc61abf4f6b21d9e17ba728
@@ -0,0 +1 @@
1
+ ae00e739f49017b43024ff3a508e03dafa7f002b406a746f43f742fa6c21cd15
@@ -0,0 +1 @@
1
+ 0239c96c8027c9274b6823bfadd5ddea7ca0803db694f53ee30d0d39b8ce5c4dd8d72f4e7d0af089db5d722fecf629e81b62759b5dc4cc434be86910b9b37f5d
@@ -3,7 +3,7 @@
3
3
  <modelVersion>4.0.0</modelVersion>
4
4
  <groupId>com.wix</groupId>
5
5
  <artifactId>detox</artifactId>
6
- <version>19.10.0</version>
6
+ <version>19.11.0</version>
7
7
  <packaging>aar</packaging>
8
8
  <name>Detox</name>
9
9
  <description>Gray box end-to-end testing and automation library for mobile apps</description>
@@ -0,0 +1 @@
1
+ 2300f079161364c78e01d8c27334ac7f
@@ -0,0 +1 @@
1
+ 1031ee5586ac864c9b0cbcaa5876dfbc07325f1f
@@ -0,0 +1 @@
1
+ 25d21ff3daef4e4da80808464fc808efbd4d0fa75f5e88ce9376fb1d85a922d8
@@ -0,0 +1 @@
1
+ c1fae4e858403138b44db05d47128f74c1ac8b14a9f55dcae90ef90800040507bb8bdd97af6c64d26811ddcdf14be1a341531e4c8a4fab0323f500167b4d3499
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>19.10.0</latest>
7
- <release>19.10.0</release>
6
+ <latest>19.11.0</latest>
7
+ <release>19.11.0</release>
8
8
  <versions>
9
- <version>19.10.0</version>
9
+ <version>19.11.0</version>
10
10
  </versions>
11
- <lastUpdated>20220818130424</lastUpdated>
11
+ <lastUpdated>20220831134632</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 38d31df24edd01e6923c2e3670359d6e
1
+ 63739c1fd16a9d1140a49b7857aa0fdf
@@ -1 +1 @@
1
- b4a03bfc096cae92a22fabe9ae811709c09c4b92
1
+ 0dcec1feec1b37fdf53e88ab4779a702631cd864
@@ -1 +1 @@
1
- 504204669f5f3377f1c42725d55e8dcf742dc0c38318209978991dfe96abf831
1
+ e1c67c307a105c7c7c82fae75e7d56f8b9718b1bb7dc9f604560909f39dce425
@@ -1 +1 @@
1
- a36b581589d46796551b3fccad8a460b3ef5e5ee40ba374a45b7b7801520d28d2fd14308233cd33ba93943cf1600f7f7f81d032166b1ada5bdd0b4450a139ec1
1
+ c5633deb1fbf020dd6bc03ba8796bd5bd85de50efadc76870c3e5f5cc2b27191bad2a59ea37abb50a96accc373810fa45799d1f60f4dd499637d3eef1f6d8601
package/Detox-ios-src.tbz CHANGED
Binary file
package/Detox-ios.tbz CHANGED
Binary file
@@ -14,6 +14,7 @@ public class LaunchArgs {
14
14
  private static final String DETOX_NOTIFICATION_PATH_ARG = "detoxUserNotificationDataURL";
15
15
  private static final String DETOX_BLACKLIST_URLS_ARG = "detoxURLBlacklistRegex";
16
16
  private static final String DETOX_URL_OVERRIDE_ARG = "detoxURLOverride";
17
+ private static final String DETOX_ENABLE_SYNCHRONIZATION = "detoxEnableSynchronization";
17
18
  private static final List<String> RESERVED_INSTRUMENTATION_ARGS = Arrays.asList("class", "package", "func", "unit", "size", "perf", "debug", "log", "emma", "coverageFile");
18
19
 
19
20
  public boolean hasNotificationPath() {
@@ -36,6 +37,14 @@ public class LaunchArgs {
36
37
  return InstrumentationRegistry.getArguments().containsKey(DETOX_BLACKLIST_URLS_ARG);
37
38
  }
38
39
 
40
+ public String getEnableSynchronization() {
41
+ return InstrumentationRegistry.getArguments().getString(DETOX_ENABLE_SYNCHRONIZATION);
42
+ }
43
+
44
+ public boolean hasEnableSynchronization() {
45
+ return InstrumentationRegistry.getArguments().containsKey(DETOX_ENABLE_SYNCHRONIZATION);
46
+ }
47
+
39
48
  public String getUrlOverride() {
40
49
  return InstrumentationRegistry.getArguments().getString(DETOX_URL_OVERRIDE_ARG);
41
50
  }
@@ -39,7 +39,7 @@ object ReactNativeExtension {
39
39
  reloadReactNativeInBackground(it)
40
40
  val reactContext = awaitNewReactNativeContext(it, previousReactContext)
41
41
 
42
- setupIdlingResources(reactContext, networkSyncEnabled)
42
+ enableOrDisableSynchronization(reactContext, networkSyncEnabled)
43
43
  hackRN50OrHigherWaitForReady()
44
44
  }
45
45
  }
@@ -58,7 +58,7 @@ object ReactNativeExtension {
58
58
  (applicationContext as ReactApplication).let {
59
59
  val reactContext = awaitNewReactNativeContext(it, null)
60
60
 
61
- setupIdlingResources(reactContext)
61
+ enableOrDisableSynchronization(reactContext)
62
62
  hackRN50OrHigherWaitForReady()
63
63
  }
64
64
  }
@@ -124,6 +124,19 @@ object ReactNativeExtension {
124
124
  return rnLoadingMonitor.getNewContext()!!
125
125
  }
126
126
 
127
+ private fun enableOrDisableSynchronization(reactContext: ReactContext, networkSyncEnabled: Boolean = true) {
128
+ if (shouldDisableSynchronization()) {
129
+ clearAllSynchronization()
130
+ } else {
131
+ setupIdlingResources(reactContext, networkSyncEnabled)
132
+ }
133
+ }
134
+
135
+ private fun shouldDisableSynchronization(): Boolean {
136
+ val launchArgs = LaunchArgs()
137
+ return launchArgs.hasEnableSynchronization() && launchArgs.enableSynchronization.equals("0")
138
+ }
139
+
127
140
  private fun setupIdlingResources(reactContext: ReactContext, networkSyncEnabled: Boolean = true) {
128
141
  val launchArgs = LaunchArgs()
129
142
 
@@ -2,6 +2,7 @@ package com.wix.detox.reactnative
2
2
 
3
3
  import android.os.Looper
4
4
  import android.util.Log
5
+ import androidx.test.espresso.Espresso
5
6
  import androidx.test.espresso.IdlingRegistry
6
7
  import androidx.test.espresso.base.IdlingResourceRegistry
7
8
  import com.facebook.react.bridge.ReactContext
@@ -12,7 +13,6 @@ import com.wix.detox.reactnative.idlingresources.timers.getInterrogationStrategy
12
13
  import com.wix.detox.reactnative.idlingresources.uimodule.UIModuleIdlingResource
13
14
  import org.joor.Reflect
14
15
  import org.joor.ReflectException
15
- import java.util.Set
16
16
 
17
17
  private const val LOG_TAG = "DetoxRNIdleRes"
18
18
 
@@ -46,15 +46,11 @@ private class MQThreadReflected(private val queue: Any?, private val queueName:
46
46
  }
47
47
 
48
48
  class ReactNativeIdlingResources constructor(
49
- private val reactContext: ReactContext,
50
- private var launchArgs: LaunchArgs,
51
- internal var networkSyncEnabled: Boolean = true
52
- )
53
-
54
- {
55
-
49
+ private val reactContext: ReactContext,
50
+ private var launchArgs: LaunchArgs,
51
+ internal var networkSyncEnabled: Boolean = true
52
+ ) {
56
53
  companion object {
57
- private const val FIELD_UI_BG_MSG_QUEUE = "mUiBackgroundMessageQueueThread"
58
54
  private const val FIELD_NATIVE_MODULES_MSG_QUEUE = "mNativeModulesMessageQueueThread"
59
55
  private const val FIELD_JS_MSG_QUEUE = "mJSMessageQueueThread"
60
56
  }
@@ -69,17 +65,11 @@ class ReactNativeIdlingResources constructor(
69
65
 
70
66
  fun registerAll() {
71
67
  Log.i(LOG_TAG, "Setting up Espresso Idling Resources for React Native")
72
-
73
68
  unregisterAll()
74
69
 
75
- if (launchArgs.hasURLBlacklist()) {
76
- val blacklistUrls = launchArgs.getURLBlacklist()
77
- setBlacklistUrls(blacklistUrls)
78
- }
79
-
70
+ setupUrlBlacklist()
80
71
  setupMQThreadsInterrogators()
81
72
  syncIdlingResources()
82
-
83
73
  setupCustomRNIdlingResources()
84
74
  syncIdlingResources()
85
75
  }
@@ -108,28 +98,42 @@ class ReactNativeIdlingResources constructor(
108
98
  networkIdlingResource?.resume()
109
99
  }
110
100
  }
101
+
111
102
  fun pauseRNTimersIdlingResource() = timersIdlingResource?.pause()
112
103
  fun resumeRNTimersIdlingResource() = timersIdlingResource?.resume()
113
104
  fun pauseUIIdlingResource() = uiModuleIdlingResource?.pause()
114
105
  fun resumeUIIdlingResource() = uiModuleIdlingResource?.resume()
115
- fun pauseJSBridgeIdlingResource() = rnBridgeIdlingResource?.pause()
116
- fun resumeJSBridgeIdlingResource() = rnBridgeIdlingResource?.resume()
106
+
107
+ fun setBlacklistUrls(urlList: String) {
108
+ setIldingResourceBlacklist(urlList)
109
+ }
110
+
111
+ private fun setIldingResourceBlacklist(urlList: String) {
112
+ val urlArray = toFormattedUrlArray(urlList)
113
+ NetworkIdlingResource.setURLBlacklist(urlArray)
114
+ }
117
115
 
118
116
  private fun setupMQThreadsInterrogators() {
119
117
  if (IdlingRegistry.getInstance().loopers.isEmpty()) {
120
118
  val mqThreadsReflector = MQThreadsReflector(reactContext)
121
- // val mqUIBackground = mqThreadsReflector.getQueue(FIELD_UI_BG_MSG_QUEUE)?.getLooper() TODO
122
119
  val mqJS = mqThreadsReflector.getQueue(FIELD_JS_MSG_QUEUE)?.getLooper()
123
- val mqNativeModules = mqThreadsReflector.getQueue(FIELD_NATIVE_MODULES_MSG_QUEUE)?.getLooper()
120
+ val mqNativeModules =
121
+ mqThreadsReflector.getQueue(FIELD_NATIVE_MODULES_MSG_QUEUE)?.getLooper()
124
122
 
125
123
  IdlingRegistry.getInstance().apply {
126
- // registerLooperAsIdlingResource(mqUIBackground)
127
124
  registerLooperAsIdlingResource(mqJS)
128
125
  registerLooperAsIdlingResource(mqNativeModules)
129
126
  }
130
127
  }
131
128
  }
132
129
 
130
+ private fun setupUrlBlacklist() {
131
+ if (launchArgs.hasURLBlacklist()) {
132
+ val blacklistUrls = launchArgs.urlBlacklist
133
+ setIldingResourceBlacklist(blacklistUrls)
134
+ }
135
+ }
136
+
133
137
  private fun setupCustomRNIdlingResources() {
134
138
  rnBridgeIdlingResource = BridgeIdlingResource(reactContext)
135
139
  timersIdlingResource = TimersIdlingResource(getInterrogationStrategy(reactContext)!!)
@@ -151,13 +155,16 @@ class ReactNativeIdlingResources constructor(
151
155
 
152
156
  private fun syncIdlingResources() {
153
157
  IdlingRegistry.getInstance().apply {
154
- val irr: IdlingResourceRegistry = Reflect.on(androidx.test.espresso.Espresso::class.java).field("baseRegistry").get()
158
+ val irr: IdlingResourceRegistry =
159
+ Reflect.on(Espresso::class.java).field("baseRegistry").get()
155
160
  irr.sync(this.resources, this.loopers)
156
161
  }
157
162
  }
158
163
 
159
164
  private fun unregisterMQThreadsInterrogators() {
160
- Reflect.on(IdlingRegistry.getInstance()).field("loopers").get<Set<Any>>().clear()
165
+ val idlingResourceInstance = IdlingRegistry.getInstance()
166
+ val loopersField = Reflect.on(idlingResourceInstance).field("loopers")
167
+ loopersField.get<MutableSet<Any>>().clear()
161
168
  }
162
169
 
163
170
  private fun unregisterCustomRNIdlingResources() {
@@ -166,7 +173,8 @@ class ReactNativeIdlingResources constructor(
166
173
  timersIdlingResource,
167
174
  rnBridgeIdlingResource,
168
175
  uiModuleIdlingResource,
169
- animIdlingResource)
176
+ animIdlingResource
177
+ )
170
178
  rnBridgeIdlingResource?.onDetach()
171
179
 
172
180
  removeNetworkIdlingResource()
@@ -174,13 +182,15 @@ class ReactNativeIdlingResources constructor(
174
182
  }
175
183
 
176
184
  private fun setupAsyncStorageIdlingResource() {
177
- asyncStorageIdlingResource = AsyncStorageIdlingResource.createIfNeeded(reactContext, false)?.also {
178
- IdlingRegistry.getInstance().register(it)
179
- }
185
+ asyncStorageIdlingResource =
186
+ AsyncStorageIdlingResource.createIfNeeded(reactContext, false)?.also {
187
+ IdlingRegistry.getInstance().register(it)
188
+ }
180
189
 
181
- legacyAsyncStorageIdlingResource = AsyncStorageIdlingResource.createIfNeeded(reactContext, true)?.also {
182
- IdlingRegistry.getInstance().register(it)
183
- }
190
+ legacyAsyncStorageIdlingResource =
191
+ AsyncStorageIdlingResource.createIfNeeded(reactContext, true)?.also {
192
+ IdlingRegistry.getInstance().register(it)
193
+ }
184
194
  }
185
195
 
186
196
  private fun removeAsyncStorageIdlingResource() {
@@ -212,13 +222,8 @@ class ReactNativeIdlingResources constructor(
212
222
 
213
223
  private fun toFormattedUrlArray(urlList: String): List<String> {
214
224
  var formattedUrls = urlList
215
- formattedUrls = formattedUrls.replace(Regex("""[()"]"""), "");
216
- formattedUrls = formattedUrls.trim();
217
- return formattedUrls.split(',');
218
- }
219
-
220
- fun setBlacklistUrls(urlList: String) {
221
- val urlArray = toFormattedUrlArray(urlList)
222
- NetworkIdlingResource.setURLBlacklist(urlArray);
225
+ formattedUrls = formattedUrls.replace(Regex("""[()"]"""), "")
226
+ formattedUrls = formattedUrls.trim()
227
+ return formattedUrls.split(',')
223
228
  }
224
229
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "detox",
3
3
  "description": "E2E tests and automation for mobile",
4
- "version": "19.10.0",
4
+ "version": "19.11.0",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -177,5 +177,5 @@
177
177
  }
178
178
  }
179
179
  },
180
- "gitHead": "6468b38c68bcac4c283ef005e9817f1d7ba8161b"
180
+ "gitHead": "900cdb17bd766869cb2d46915ff2c7821808c676"
181
181
  }
@@ -22,16 +22,6 @@ class DetoxCoreListener {
22
22
  this.detox = detox;
23
23
  }
24
24
 
25
- _getTestInvocations(test) {
26
- const { DETOX_RERUN_INDEX } = process.env;
27
-
28
- if (!isNaN(DETOX_RERUN_INDEX)) {
29
- return Number(DETOX_RERUN_INDEX) * this._testRunTimes + test.invocations;
30
- } else {
31
- return test.invocations;
32
- }
33
- }
34
-
35
25
  async run_describe_start({ describeBlock: { name, children } }) {
36
26
  if (children.length) {
37
27
  await this.detox[onRunDescribeStart]({ name });
@@ -80,20 +70,39 @@ class DetoxCoreListener {
80
70
  this._startedTests.add(test);
81
71
 
82
72
  await this.detox[onTestStart]({
73
+ ...this._getTestMetadata(test),
74
+ status: 'running',
75
+ });
76
+ }
77
+
78
+ _getTestMetadata(test) {
79
+ return {
83
80
  title: test.name,
81
+ parent: test.parent.name,
84
82
  fullName: getFullTestName(test),
85
83
  status: 'running',
84
+ functionCode: test.fn.toString(),
86
85
  invocations: this._getTestInvocations(test),
87
- });
86
+ };
87
+ }
88
+
89
+ _getTestInvocations(test) {
90
+ const { DETOX_RERUN_INDEX } = process.env;
91
+
92
+ if (!isNaN(DETOX_RERUN_INDEX)) {
93
+ return Number(DETOX_RERUN_INDEX) * this._testRunTimes + test.invocations;
94
+ } else {
95
+ return test.invocations;
96
+ }
88
97
  }
89
98
 
90
99
  async test_done({ test }) {
91
100
  if (this._startedTests.has(test)) {
92
101
  await this.detox[onTestDone]({
93
- title: test.name,
94
- fullName: getFullTestName(test),
95
- status: test.errors.length ? 'failed' : 'passed',
96
- invocations: this._getTestInvocations(test),
102
+ ...this._getTestMetadata(test),
103
+ status: _.isEmpty(test.errors) ? 'passed' : 'failed',
104
+ errors: _.isEmpty(test.errors) ? undefined : test.errors,
105
+ asyncError: _.isEmpty(test.asyncError) ? undefined : test.asyncError,
97
106
  timedOut: hasTimedOut(test)
98
107
  });
99
108
 
@@ -66,7 +66,7 @@ class DetoxExportWrapper {
66
66
  }
67
67
 
68
68
  this[_detox] = new Detox(resolvedConfig);
69
- await traceCall('detoxInit', () => this[_detox].init());
69
+ await traceCall('detoxInit', this[_detox].init());
70
70
  Detox.none.setError(null);
71
71
 
72
72
  return this[_detox];
@@ -5,6 +5,7 @@ const tempfile = require('tempfile');
5
5
 
6
6
  const DetoxRuntimeError = require('../../errors/DetoxRuntimeError');
7
7
  const invoke = require('../../invoke');
8
+ const { actionDescription } = require('../../utils/invocationTraceDescriptions');
8
9
  const actions = require('../actions/native');
9
10
  const DetoxMatcherApi = require('../espressoapi/DetoxMatcher');
10
11
  const { ActionInteraction } = require('../interactions/native');
@@ -18,12 +19,11 @@ class NativeElement {
18
19
  }
19
20
 
20
21
  _selectElementWithMatcher(matcher) {
21
- // if (!(matcher instanceof NativeMatcher)) throw new DetoxRuntimeError(`Element _selectElementWithMatcher argument must be a valid NativeMatcher, got ${typeof matcher}`);
22
22
  this._call = invoke.call(invoke.Espresso, 'onView', matcher._call);
23
23
  }
24
24
 
25
25
  atIndex(index) {
26
- if (typeof index !== 'number') throw new DetoxRuntimeError(`Element atIndex argument must be a number, got ${typeof index}`);
26
+ if (typeof index !== 'number') throw new DetoxRuntimeError({ message: `Element atIndex argument must be a number, got ${typeof index}` });
27
27
  const matcher = this._originalMatcher;
28
28
  this._originalMatcher._call = invoke.callDirectly(DetoxMatcherApi.matcherForAtIndex(index, matcher._call.value));
29
29
 
@@ -32,56 +32,84 @@ class NativeElement {
32
32
  }
33
33
 
34
34
  async tap(value) {
35
- return await new ActionInteraction(this._invocationManager, this, new actions.TapAction(value)).execute();
35
+ const action = new actions.TapAction(value);
36
+ const traceDescription = actionDescription.tapAtPoint(value);
37
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
36
38
  }
37
39
 
38
40
  async tapAtPoint(value) {
39
- return await new ActionInteraction(this._invocationManager, this, new actions.TapAtPointAction(value)).execute();
41
+ const action = new actions.TapAtPointAction(value);
42
+ const traceDescription = actionDescription.tapAtPoint(value);
43
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
40
44
  }
41
45
 
42
46
  async longPress() {
43
- return await new ActionInteraction(this._invocationManager, this, new actions.LongPressAction()).execute();
47
+ const action = new actions.LongPressAction();
48
+ const traceDescription = actionDescription.longPress();
49
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
44
50
  }
45
51
 
46
52
  async multiTap(times) {
47
- return await new ActionInteraction(this._invocationManager, this, new actions.MultiClickAction(times)).execute();
53
+ if (typeof times !== 'number') throw new Error('times should be a number, but got ' + (times + (' (' + (typeof times + ')'))));
54
+ if (times < 1) throw new Error('times should be greater than 0, but got ' + times);
55
+
56
+ const action = new actions.MultiClickAction(times);
57
+ const traceDescription = actionDescription.multiTap(times);
58
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
48
59
  }
49
60
 
50
61
  async tapBackspaceKey() {
51
- return await new ActionInteraction(this._invocationManager, this, new actions.PressKeyAction(67)).execute();
62
+ const action = new actions.PressKeyAction(67);
63
+ const traceDescription = actionDescription.tapBackspaceKey();
64
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
52
65
  }
53
66
 
54
67
  async tapReturnKey() {
55
- return await new ActionInteraction(this._invocationManager, this, new actions.TypeTextAction('\n')).execute();
68
+ const action = new actions.TypeTextAction('\n');
69
+ const traceDescription = actionDescription.tapReturnKey();
70
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
56
71
  }
57
72
 
58
73
  async typeText(value) {
59
- return await new ActionInteraction(this._invocationManager, this, new actions.TypeTextAction(value)).execute();
74
+ const action = new actions.TypeTextAction(value);
75
+ const traceDescription = actionDescription.typeText(value);
76
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
60
77
  }
61
78
 
62
79
  async replaceText(value) {
63
- return await new ActionInteraction(this._invocationManager, this, new actions.ReplaceTextAction(value)).execute();
80
+ const action = new actions.ReplaceTextAction(value);
81
+ const traceDescription = actionDescription.replaceText(value);
82
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
64
83
  }
65
84
 
66
85
  async clearText() {
67
- return await new ActionInteraction(this._invocationManager, this, new actions.ClearTextAction()).execute();
86
+ const action = new actions.ClearTextAction();
87
+ const traceDescription = actionDescription.clearText();
88
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
68
89
  }
69
90
 
70
91
  async scroll(amount, direction = 'down', startPositionX, startPositionY) {
71
- // override the user's element selection with an extended matcher that looks for UIScrollView children
72
- // this._selectElementWithMatcher(this._originalMatcher._extendToDescendantScrollViews());
73
- return await new ActionInteraction(this._invocationManager, this, new actions.ScrollAmountAction(direction, amount, startPositionX, startPositionY)).execute();
92
+ const action = new actions.ScrollAmountAction(direction, amount, startPositionX, startPositionY);
93
+ const traceDescription = actionDescription.scroll(amount, direction, startPositionX, startPositionY);
94
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
74
95
  }
75
96
 
76
97
  async scrollTo(edge) {
77
98
  // override the user's element selection with an extended matcher that looks for UIScrollView children
78
99
  this._selectElementWithMatcher(this._originalMatcher._extendToDescendantScrollViews());
79
- return await new ActionInteraction(this._invocationManager, this, new actions.ScrollEdgeAction(edge)).execute();
100
+
101
+ const action = new actions.ScrollEdgeAction(edge);
102
+ const traceDescription = actionDescription.scrollTo(edge);
103
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
80
104
  }
81
105
 
82
106
  async scrollToIndex(index) {
107
+ // override the user's element selection with an extended matcher that looks for UIScrollView children
83
108
  this._selectElementWithMatcher(this._originalMatcher._extendToDescendantScrollViews());
84
- return await new ActionInteraction(this._invocationManager, this, new actions.ScrollToIndex(index)).execute();
109
+
110
+ const action = new actions.ScrollToIndex(index);
111
+ const traceDescription = actionDescription.scrollToIndex(index);
112
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
85
113
  }
86
114
 
87
115
  /**
@@ -96,13 +124,17 @@ class NativeElement {
96
124
 
97
125
  // override the user's element selection with an extended matcher that avoids RN issues with RCTScrollView
98
126
  this._selectElementWithMatcher(this._originalMatcher._avoidProblematicReactNativeElements());
127
+
99
128
  const action = new actions.SwipeAction(direction, speed, normalizedSwipeOffset, normalizedStartingPointX, normalizedStartingPointY);
100
- return await new ActionInteraction(this._invocationManager, this, action).execute();
129
+ const traceDescription = actionDescription.swipe(direction, speed, normalizedSwipeOffset, normalizedStartingPointX, normalizedStartingPointY);
130
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
101
131
  }
102
132
 
103
133
  async takeScreenshot(screenshotName) {
104
134
  // TODO this should be moved to a lower-layer handler of this use-case
105
- const resultBase64 = await new ActionInteraction(this._invocationManager, this, new actions.TakeElementScreenshot()).execute();
135
+ const action = new actions.TakeElementScreenshot();
136
+ const traceDescription = actionDescription.takeScreenshot(screenshotName);
137
+ const resultBase64 = await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
106
138
  const filePath = tempfile('detox.element-screenshot.png');
107
139
  await fs.writeFile(filePath, resultBase64, 'base64');
108
140
 
@@ -115,12 +147,16 @@ class NativeElement {
115
147
  }
116
148
 
117
149
  async getAttributes() {
118
- const result = await new ActionInteraction(this._invocationManager, this, new actions.GetAttributes()).execute();
150
+ const action = new actions.GetAttributes();
151
+ const traceDescription = actionDescription.getAttributes();
152
+ const result = await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
119
153
  return JSON.parse(result);
120
154
  }
121
155
 
122
156
  async adjustSliderToPosition(newPosition) {
123
- return await new ActionInteraction(this._invocationManager, this, new actions.AdjustSliderToPosition(newPosition)).execute();
157
+ const action = new actions.AdjustSliderToPosition(newPosition);
158
+ const traceDescription = actionDescription.adjustSliderToPosition(newPosition);
159
+ return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
124
160
  }
125
161
  }
126
162