detox 19.10.0 → 19.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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