detox 20.14.1 → 20.14.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. package/Detox-android/com/wix/detox/{20.14.1/detox-20.14.1-javadoc.jar → 20.14.2/detox-20.14.2-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2-javadoc.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.14.1/detox-20.14.1-sources.jar → 20.14.2/detox-20.14.2-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2.aar +0 -0
  12. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2.aar.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2.aar.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2.aar.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2.aar.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/{20.14.1/detox-20.14.1.pom → 20.14.2/detox-20.14.2.pom} +1 -1
  17. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2.pom.md5 +1 -0
  18. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2.pom.sha1 +1 -0
  19. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2.pom.sha256 +1 -0
  20. package/Detox-android/com/wix/detox/20.14.2/detox-20.14.2.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/Detox.java +70 -8
  29. package/android/detox/src/full/java/com/wix/detox/DetoxMain.kt +44 -69
  30. package/android/detox/src/full/java/com/wix/detox/LaunchIntentsFactory.kt +1 -1
  31. package/android/detox/src/full/java/com/wix/detox/adapters/server/DetoxActionHandlers.kt +4 -0
  32. package/android/detox/src/full/java/com/wix/detox/adapters/server/DetoxActionsDispatcher.kt +6 -16
  33. package/android/detox/src/full/java/com/wix/detox/adapters/server/DetoxServerAdapter.kt +2 -0
  34. package/android/detox/src/full/java/com/wix/detox/adapters/server/DetoxServerInfo.kt +2 -4
  35. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeExtension.kt +32 -25
  36. package/android/detox/src/main/java/com/wix/detox/espresso/UiControllerSpy.kt +3 -0
  37. package/package.json +2 -2
  38. package/src/android/espressoapi/Detox.js +11 -0
  39. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1-javadoc.jar.md5 +0 -1
  40. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1-javadoc.jar.sha1 +0 -1
  41. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1-javadoc.jar.sha256 +0 -1
  42. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1-javadoc.jar.sha512 +0 -1
  43. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1-sources.jar.md5 +0 -1
  44. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1-sources.jar.sha1 +0 -1
  45. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1-sources.jar.sha256 +0 -1
  46. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1-sources.jar.sha512 +0 -1
  47. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1.aar +0 -0
  48. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1.aar.md5 +0 -1
  49. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1.aar.sha1 +0 -1
  50. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1.aar.sha256 +0 -1
  51. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1.aar.sha512 +0 -1
  52. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1.pom.md5 +0 -1
  53. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1.pom.sha1 +0 -1
  54. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1.pom.sha256 +0 -1
  55. package/Detox-android/com/wix/detox/20.14.1/detox-20.14.1.pom.sha512 +0 -1
  56. package/android/detox/src/full/java/com/wix/detox/ActivityLaunchHelper.kt +0 -76
  57. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactMarkersLogger.kt +0 -44
@@ -0,0 +1 @@
1
+ 83e2c044c4a8a80da67d2142bbf15047
@@ -0,0 +1 @@
1
+ 8ccd5b34260ca721f645624dfcc00f79562b56c7
@@ -0,0 +1 @@
1
+ 084644c2e162c2735fa8a1402ca042202859f58c8596d49f484634b13e583763
@@ -0,0 +1 @@
1
+ 85c49763108219fee733c5a84e44aac4a5b2ebcaec9d969cb743f9628d9411050683dd464b64d126b2275f607da7f8da8c98199246538c55ef805361f1f054b1
@@ -0,0 +1 @@
1
+ 72c8a46b258d5502226964690e6f2124
@@ -0,0 +1 @@
1
+ 6b00e943e255c1a1522f2b3249a639ea815a18ed
@@ -0,0 +1 @@
1
+ 716fe252a3e939dfa01dfaeedc483b67ee7d42786cbdc920dd7140b12d575af7
@@ -0,0 +1 @@
1
+ 8187b89fb5e5c5100ce26d97cd4103342fc9ebb2f2583b12826d02bf17c8a7f34be016037624d6f8992e8b78ab25435da145d5404bc2796fc741dd3e04272ce9
@@ -0,0 +1 @@
1
+ 5037d89ac5d98e47d69d63b8731e7e3a
@@ -0,0 +1 @@
1
+ b4a0f9db575d92cb2da51c617e0ff531330cc2c6
@@ -0,0 +1 @@
1
+ 1fa253115965db1c97bc6c8b0c1ab0dff9ac9f8714e063dc972332cc6798b63a
@@ -0,0 +1 @@
1
+ 8174914ad6abe68c79f4812a32d292e80d922239045a30401bc39f43801f3cd2ea2807786248ffb58e84dfcdb8eddc00c7f606a874eb216def9350d325442af3
@@ -3,7 +3,7 @@
3
3
  <modelVersion>4.0.0</modelVersion>
4
4
  <groupId>com.wix</groupId>
5
5
  <artifactId>detox</artifactId>
6
- <version>20.14.1</version>
6
+ <version>20.14.2</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
+ d66f5511d730ae3148f66ce7e2c16c76
@@ -0,0 +1 @@
1
+ 2f6d1547f9c0706d24c68995162ae99bda38cb67
@@ -0,0 +1 @@
1
+ 6006602d31121b42160842f61b249dd3696c81c9552681709aae5a123b22699d
@@ -0,0 +1 @@
1
+ 52f8b1ab28ac1bf3970a19aba1930ffbd310836e8e480a066a9bd4ff99727baf5f7fc1ac882019b59245dff846fe2d39bc9173e26d3ff5d1a77c442c00782ee2
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>20.14.1</latest>
7
- <release>20.14.1</release>
6
+ <latest>20.14.2</latest>
7
+ <release>20.14.2</release>
8
8
  <versions>
9
- <version>20.14.1</version>
9
+ <version>20.14.2</version>
10
10
  </versions>
11
- <lastUpdated>20231212155320</lastUpdated>
11
+ <lastUpdated>20231218160804</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 3e4214cc64c1e5edc2a3032be61a5545
1
+ c12d39148c8df6c732259787182f39c9
@@ -1 +1 @@
1
- 0196bb30f6b15f7ca321049999448d9242cc34fb
1
+ c87af74eb1bb58f0acc0c0b95cb000d33ff185db
@@ -1 +1 @@
1
- 00f7fa47240f24b4188f773b9652d64f1e08a0dd32fdf918ab48b7e56215909b
1
+ f7f6fbf308504c7b02ec391f800e5d0f816b2e1be07a5fca83164947c3a80b2a
@@ -1 +1 @@
1
- ac2fac1e5878e4d303daa66ed9174cb6bbb4304fcc1ddbc30dad48e47a9ac87106e849fb1d85bdcbb32a6d5955cff4b7a47a341a0203ca040d40bb852019e312
1
+ 7bbcaf76b676ecae74a22f94447d62fe9212b2ead9c5dafe0fd5dfaed470b914948e58fb6dd898a9ea90ac9da3ae2bf08e19b88d626220d4baa6f218efc2d872
package/Detox-ios-src.tbz CHANGED
Binary file
package/Detox-ios.tbz CHANGED
Binary file
@@ -1,13 +1,18 @@
1
1
  package com.wix.detox;
2
2
 
3
+ import android.app.Activity;
4
+ import android.app.Instrumentation;
3
5
  import android.content.Context;
6
+ import android.content.Intent;
7
+ import android.os.Bundle;
8
+
9
+ import com.wix.detox.config.DetoxConfig;
10
+ import com.wix.detox.espresso.UiControllerSpy;
4
11
 
5
12
  import androidx.annotation.NonNull;
6
13
  import androidx.test.platform.app.InstrumentationRegistry;
7
14
  import androidx.test.rule.ActivityTestRule;
8
15
 
9
- import com.wix.detox.config.DetoxConfig;
10
-
11
16
  /**
12
17
  * <p>Static class.</p>
13
18
  *
@@ -62,7 +67,12 @@ import com.wix.detox.config.DetoxConfig;
62
67
  * <p>If not set, then Detox tests are no ops. So it's safe to mix it with other tests.</p>
63
68
  */
64
69
  public final class Detox {
65
- private static ActivityLaunchHelper sActivityLaunchHelper;
70
+ private static final String INTENT_LAUNCH_ARGS_KEY = "launchArgs";
71
+ private static final long ACTIVITY_LAUNCH_TIMEOUT = 10000L;
72
+
73
+ private static final LaunchArgs sLaunchArgs = new LaunchArgs();
74
+ private static final LaunchIntentsFactory sIntentsFactory = new LaunchIntentsFactory();
75
+ private static ActivityTestRule sActivityTestRule;
66
76
 
67
77
  private Detox() {
68
78
  }
@@ -122,20 +132,72 @@ public final class Detox {
122
132
  DetoxConfig.CONFIG = detoxConfig;
123
133
  DetoxConfig.CONFIG.apply();
124
134
 
125
- sActivityLaunchHelper = new ActivityLaunchHelper(activityTestRule);
126
- DetoxMain.run(context, sActivityLaunchHelper);
135
+ sActivityTestRule = activityTestRule;
136
+
137
+ UiControllerSpy.attachThroughProxy();
138
+
139
+ Intent intent = extractInitialIntent();
140
+ sActivityTestRule.launchActivity(intent);
141
+
142
+ try {
143
+ DetoxMain.run(context);
144
+ } catch (Exception e) {
145
+ Thread.currentThread().interrupt();
146
+ throw new RuntimeException("Detox got interrupted prematurely", e);
147
+ }
127
148
  }
128
149
 
129
150
  public static void launchMainActivity() {
130
- sActivityLaunchHelper.launchMainActivity();
151
+ final Activity activity = sActivityTestRule.getActivity();
152
+ launchActivitySync(sIntentsFactory.activityLaunchIntent(activity));
131
153
  }
132
154
 
133
155
  public static void startActivityFromUrl(String url) {
134
- sActivityLaunchHelper.startActivityFromUrl(url);
156
+ launchActivitySync(sIntentsFactory.intentWithUrl(url, false));
135
157
  }
136
158
 
137
159
  public static void startActivityFromNotification(String dataFilePath) {
138
- sActivityLaunchHelper.startActivityFromNotification(dataFilePath);
160
+ Bundle notificationData = new NotificationDataParser(dataFilePath).toBundle();
161
+ Intent intent = sIntentsFactory.intentWithNotificationData(getAppContext(), notificationData, false);
162
+ launchActivitySync(intent);
163
+ }
164
+
165
+ private static Intent extractInitialIntent() {
166
+ Intent intent;
167
+
168
+ if (sLaunchArgs.hasUrlOverride()) {
169
+ intent = sIntentsFactory.intentWithUrl(sLaunchArgs.getUrlOverride(), true);
170
+ } else if (sLaunchArgs.hasNotificationPath()) {
171
+ Bundle notificationData = new NotificationDataParser(sLaunchArgs.getNotificationPath()).toBundle();
172
+ intent = sIntentsFactory.intentWithNotificationData(getAppContext(), notificationData, true);
173
+ } else {
174
+ intent = sIntentsFactory.cleanIntent();
175
+ }
176
+ intent.putExtra(INTENT_LAUNCH_ARGS_KEY, sLaunchArgs.asIntentBundle());
177
+ return intent;
178
+ }
179
+
180
+ private static void launchActivitySync(Intent intent) {
181
+ // Ideally, we would just call sActivityTestRule.launchActivity(intent) and get it over with.
182
+ // BUT!!! as it turns out, Espresso has an issue where doing this for an activity running in the background
183
+ // would have Espresso set up an ActivityMonitor which will spend its time waiting for the activity to load, *without
184
+ // ever being released*. It will finally fail after a 45 seconds timeout.
185
+ // Without going into full details, it seems that activity test rules were not meant to be used this way. However,
186
+ // the all-new ActivityScenario implementation introduced in androidx could probably support this (e.g. by using
187
+ // dedicated methods such as moveToState(), which give better control over the lifecycle).
188
+ // In any case, this is the core reason for this issue: https://github.com/wix/Detox/issues/1125
189
+ // What it forces us to do, then, is this -
190
+ // 1. Launch the activity by "ourselves" from the OS (i.e. using context.startActivity()).
191
+ // 2. Set up an activity monitor by ourselves -- such that it would block until the activity is ready.
192
+ // ^ Hence the code below.
193
+
194
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
195
+ final Activity activity = sActivityTestRule.getActivity();
196
+ final Instrumentation.ActivityMonitor activityMonitor = new Instrumentation.ActivityMonitor(activity.getClass().getName(), null, true);
197
+
198
+ activity.startActivity(intent);
199
+ instrumentation.addMonitor(activityMonitor);
200
+ instrumentation.waitForMonitorWithTimeout(activityMonitor, ACTIVITY_LAUNCH_TIMEOUT);
139
201
  }
140
202
 
141
203
  private static Context getAppContext() {
@@ -3,62 +3,35 @@ package com.wix.detox
3
3
  import android.content.Context
4
4
  import android.util.Log
5
5
  import com.wix.detox.adapters.server.*
6
- import com.wix.detox.common.DetoxLog
7
- import com.wix.detox.espresso.UiControllerSpy
6
+ import com.wix.detox.common.DetoxLog.Companion.LOG_TAG
8
7
  import com.wix.detox.instruments.DetoxInstrumentsManager
9
8
  import com.wix.detox.reactnative.ReactNativeExtension
10
9
  import com.wix.invoke.MethodInvocation
11
- import java.util.concurrent.CountDownLatch
12
10
 
11
+ private const val INIT_ACTION = "_init"
12
+ private const val IS_READY_ACTION = "isReady"
13
13
  private const val TERMINATION_ACTION = "_terminate"
14
14
 
15
15
  object DetoxMain {
16
- private val handshakeLock = CountDownLatch(1)
17
-
18
16
  @JvmStatic
19
- fun run(rnHostHolder: Context, activityLaunchHelper: ActivityLaunchHelper) {
17
+ fun run(rnHostHolder: Context) {
20
18
  val detoxServerInfo = DetoxServerInfo()
19
+ Log.i(LOG_TAG, "Detox server connection details: $detoxServerInfo")
20
+
21
21
  val testEngineFacade = TestEngineFacade()
22
22
  val actionsDispatcher = DetoxActionsDispatcher()
23
- val serverAdapter = DetoxServerAdapter(actionsDispatcher, detoxServerInfo, TERMINATION_ACTION)
24
-
25
- initCrashHandler(serverAdapter)
26
- initANRListener(serverAdapter)
27
- initEspresso()
28
- initReactNative()
29
-
30
- setupActionHandlers(actionsDispatcher, serverAdapter, testEngineFacade, rnHostHolder)
31
- serverAdapter.connect()
32
-
33
- launchActivityOnCue(rnHostHolder, activityLaunchHelper)
23
+ val externalAdapter = DetoxServerAdapter(actionsDispatcher, detoxServerInfo, IS_READY_ACTION, TERMINATION_ACTION)
24
+ initActionHandlers(actionsDispatcher, externalAdapter, testEngineFacade, rnHostHolder)
25
+ actionsDispatcher.dispatchAction(INIT_ACTION, "", 0)
34
26
  actionsDispatcher.join()
35
27
  }
36
28
 
37
- /**
38
- * Launch the tested activity "on cue", namely, right after a connection is established and the handshake
39
- * completes successfully.
40
- *
41
- * This has to be synchronized so that an `isReady` isn't handled *before* the activity is launched (albeit not fully
42
- * initialized - all native modules and everything) and a react context is available.
43
- *
44
- * As a better alternative, it would make sense to execute this as a simple action from within the actions
45
- * dispatcher (i.e. handler of `loginSuccess`), in which case, no inter-thread locking would be required
46
- * thanks to the usage of Handlers. However, in this type of a solution, errors / crashes would be reported
47
- * not by instrumentation itself, but based on the `AppWillTerminateWithError` message; In it's own, it is a good
48
- * thing, but for a reason we're not sure of yet, it is ignored by the test runner at this point in the flow.
49
- */
50
- @Synchronized
51
- private fun launchActivityOnCue(rnHostHolder: Context, activityLaunchHelper: ActivityLaunchHelper) {
52
- awaitHandshake()
53
- launchActivity(rnHostHolder, activityLaunchHelper)
54
- }
55
-
56
- private fun awaitHandshake() {
57
- handshakeLock.await()
58
- }
29
+ private fun doInit(externalAdapter: DetoxServerAdapter, rnHostHolder: Context) {
30
+ externalAdapter.connect()
59
31
 
60
- private fun onLoginSuccess() {
61
- handshakeLock.countDown()
32
+ initCrashHandler(externalAdapter)
33
+ initANRListener(externalAdapter)
34
+ initReactNativeIfNeeded(rnHostHolder)
62
35
  }
63
36
 
64
37
  private fun doTeardown(serverAdapter: DetoxServerAdapter, actionsDispatcher: DetoxActionsDispatcher, testEngineFacade: TestEngineFacade) {
@@ -68,28 +41,35 @@ object DetoxMain {
68
41
  actionsDispatcher.teardown()
69
42
  }
70
43
 
71
- private fun setupActionHandlers(actionsDispatcher: DetoxActionsDispatcher, serverAdapter: DetoxServerAdapter, testEngineFacade: TestEngineFacade, rnHostHolder: Context) {
72
- class SynchronizedActionHandler(private val actionHandler: DetoxActionHandler): DetoxActionHandler {
73
- override fun handle(params: String, messageId: Long) {
74
- synchronized(this@DetoxMain) {
75
- actionHandler.handle(params, messageId)
76
- }
77
- }
78
- }
79
-
44
+ private fun initActionHandlers(actionsDispatcher: DetoxActionsDispatcher, serverAdapter: DetoxServerAdapter, testEngineFacade: TestEngineFacade, rnHostHolder: Context) {
80
45
  // Primary actions
81
46
  with(actionsDispatcher) {
82
- val readyHandler = SynchronizedActionHandler( ReadyActionHandler(serverAdapter, testEngineFacade) )
83
- val rnReloadHandler = SynchronizedActionHandler( ReactNativeReloadActionHandler(rnHostHolder, serverAdapter, testEngineFacade) )
47
+ val rnReloadHandler = ReactNativeReloadActionHandler(rnHostHolder, serverAdapter, testEngineFacade)
84
48
 
85
- associateActionHandler("loginSuccess", ::onLoginSuccess)
86
- associateActionHandler("isReady", readyHandler)
87
- associateActionHandler("reactNativeReload", rnReloadHandler)
49
+ associateActionHandler(INIT_ACTION, object : DetoxActionHandler {
50
+ override fun handle(params: String, messageId: Long) =
51
+ synchronized(this@DetoxMain) {
52
+ this@DetoxMain.doInit(serverAdapter, rnHostHolder)
53
+ }
54
+ })
55
+ associateActionHandler(IS_READY_ACTION, ReadyActionHandler(serverAdapter, testEngineFacade))
56
+
57
+ associateActionHandler("loginSuccess", ScarceActionHandler())
58
+ associateActionHandler("reactNativeReload", object: DetoxActionHandler {
59
+ override fun handle(params: String, messageId: Long) =
60
+ synchronized(this@DetoxMain) {
61
+ rnReloadHandler.handle(params, messageId)
62
+ }
63
+ })
88
64
  associateActionHandler("invoke", InvokeActionHandler(MethodInvocation(), serverAdapter))
89
65
  associateActionHandler("cleanup", CleanupActionHandler(serverAdapter, testEngineFacade) {
90
66
  dispatchAction(TERMINATION_ACTION, "", 0)
91
67
  })
92
- associateActionHandler(TERMINATION_ACTION) { -> doTeardown(serverAdapter, actionsDispatcher, testEngineFacade) }
68
+ associateActionHandler(TERMINATION_ACTION, object: DetoxActionHandler {
69
+ override fun handle(params: String, messageId: Long) {
70
+ this@DetoxMain.doTeardown(serverAdapter, actionsDispatcher, testEngineFacade)
71
+ }
72
+ })
93
73
 
94
74
  if (DetoxInstrumentsManager.supports()) {
95
75
  val instrumentsManager = DetoxInstrumentsManager(rnHostHolder)
@@ -100,8 +80,13 @@ object DetoxMain {
100
80
 
101
81
  // Secondary actions
102
82
  with(actionsDispatcher) {
103
- val queryStatusHandler = SynchronizedActionHandler( QueryStatusActionHandler(serverAdapter, testEngineFacade) )
104
- associateSecondaryActionHandler("currentStatus", queryStatusHandler)
83
+ val queryStatusHandler = QueryStatusActionHandler(serverAdapter, testEngineFacade)
84
+ associateActionHandler("currentStatus", object: DetoxActionHandler {
85
+ override fun handle(params: String, messageId: Long) =
86
+ synchronized(this@DetoxMain) {
87
+ queryStatusHandler.handle(params, messageId)
88
+ }
89
+ }, false)
105
90
  }
106
91
  }
107
92
 
@@ -113,17 +98,7 @@ object DetoxMain {
113
98
  DetoxANRHandler(outboundServerAdapter).attach()
114
99
  }
115
100
 
116
- private fun initEspresso() {
117
- UiControllerSpy.attachThroughProxy()
118
- }
119
-
120
- private fun initReactNative() {
121
- ReactNativeExtension.initIfNeeded()
122
- }
123
-
124
- private fun launchActivity(rnHostHolder: Context, activityLaunchHelper: ActivityLaunchHelper) {
125
- Log.i(DetoxLog.LOG_TAG, "Launching the tested activity!")
126
- activityLaunchHelper.launchActivityUnderTest()
101
+ private fun initReactNativeIfNeeded(rnHostHolder: Context) {
127
102
  ReactNativeExtension.waitForRNBootstrap(rnHostHolder)
128
103
  }
129
104
  }
@@ -6,7 +6,7 @@ import android.content.Intent
6
6
  import android.net.Uri
7
7
  import android.os.Bundle
8
8
 
9
- class LaunchIntentsFactory {
9
+ internal class LaunchIntentsFactory {
10
10
 
11
11
  /**
12
12
  * Constructs an intent tightly associated with a specific activity.
@@ -152,3 +152,7 @@ class InstrumentsEventsActionsHandler(
152
152
  outboundServerAdapter.sendMessage("eventDone", emptyMap<String, Any>(), messageId)
153
153
  }
154
154
  }
155
+
156
+ class ScarceActionHandler: DetoxActionHandler {
157
+ override fun handle(params: String, messageId: Long) {}
158
+ }
@@ -11,18 +11,11 @@ class DetoxActionsDispatcher {
11
11
  private val primaryExec = ActionsExecutor("detox.primary")
12
12
  private val secondaryExec = ActionsExecutor("detox.secondary")
13
13
 
14
- fun associateActionHandler(type: String, actionHandler: DetoxActionHandler) =
15
- associateActionHandler(type, actionHandler, true)
16
-
17
- fun associateActionHandler(type: String, handlerFunc: () -> Unit) {
18
- associateActionHandler(type, object: DetoxActionHandler {
19
- override fun handle(params: String, messageId: Long) = handlerFunc()
20
- })
14
+ fun associateActionHandler(type: String, actionHandler: DetoxActionHandler, isPrimary: Boolean = true) {
15
+ val actionsExecutor = (if (isPrimary) primaryExec else secondaryExec)
16
+ actionsExecutor.associateHandler(type, actionHandler)
21
17
  }
22
18
 
23
- fun associateSecondaryActionHandler(type: String, actionHandler: DetoxActionHandler) =
24
- associateActionHandler(type, actionHandler, false)
25
-
26
19
  fun dispatchAction(type: String, params: String, messageId: Long) {
27
20
  (primaryExec.executeAction(type, params, messageId) ||
28
21
  secondaryExec.executeAction(type, params, messageId))
@@ -40,11 +33,6 @@ class DetoxActionsDispatcher {
40
33
  primaryExec.join()
41
34
  secondaryExec.join()
42
35
  }
43
-
44
- private fun associateActionHandler(type: String, actionHandler: DetoxActionHandler, isPrimary: Boolean = true) {
45
- val actionsExecutor = (if (isPrimary) primaryExec else secondaryExec)
46
- actionsExecutor.associateHandler(type, actionHandler)
47
- }
48
36
  }
49
37
 
50
38
  private class ActionsExecutor(name: String) {
@@ -86,5 +74,7 @@ private class ActionsExecutor(name: String) {
86
74
  handler.looper.quit()
87
75
  }
88
76
 
89
- fun join() = thread.join()
77
+ fun join() {
78
+ thread.join()
79
+ }
90
80
  }
@@ -10,6 +10,7 @@ interface OutboundServerAdapter {
10
10
  class DetoxServerAdapter(
11
11
  private val actionsDispatcher: DetoxActionsDispatcher,
12
12
  private val detoxServerInfo: DetoxServerInfo,
13
+ private val readyActionType: String,
13
14
  private val terminationActionType: String)
14
15
  : WebSocketClient.WSEventsHandler, OutboundServerAdapter {
15
16
 
@@ -26,6 +27,7 @@ class DetoxServerAdapter(
26
27
 
27
28
  override fun onConnect() {
28
29
  Log.i(DetoxLog.LOG_TAG, "Connected to server!")
30
+ actionsDispatcher.dispatchAction(readyActionType, "", -1000L)
29
31
  }
30
32
 
31
33
  override fun onClosed() {
@@ -1,9 +1,7 @@
1
1
  package com.wix.detox.adapters.server
2
2
 
3
- import android.util.Log
4
3
  import androidx.test.platform.app.InstrumentationRegistry
5
4
  import com.wix.detox.LaunchArgs
6
- import com.wix.detox.common.DetoxLog
7
5
 
8
6
  private const val DEFAULT_URL = "ws://localhost:8099"
9
7
 
@@ -11,7 +9,7 @@ class DetoxServerInfo internal constructor(launchArgs: LaunchArgs = LaunchArgs()
11
9
  val serverUrl: String = launchArgs.detoxServerUrl ?: DEFAULT_URL
12
10
  val sessionId: String = launchArgs.detoxSessionId ?: InstrumentationRegistry.getInstrumentation().targetContext.applicationInfo.packageName
13
11
 
14
- init {
15
- Log.i(DetoxLog.LOG_TAG, "Detox server connection details: url=$serverUrl, sessionId=$sessionId")
12
+ override fun toString(): String {
13
+ return "url=$serverUrl, sessionId=$sessionId"
16
14
  }
17
15
  }
@@ -14,31 +14,6 @@ private const val LOG_TAG = "DetoxRNExt"
14
14
  object ReactNativeExtension {
15
15
  private var rnIdlingResources: ReactNativeIdlingResources? = null
16
16
 
17
- fun initIfNeeded() {
18
- if (!ReactNativeInfo.isReactNativeApp()) {
19
- return
20
- }
21
-
22
- ReactMarkersLogger.attach()
23
- }
24
-
25
- /**
26
- * Wait for React-Native to finish loading (i.e. make RN context available).
27
- *
28
- * @param applicationContext The app context, implicitly assumed to be a [ReactApplication] instance.
29
- */
30
- fun waitForRNBootstrap(applicationContext: Context) {
31
- if (!ReactNativeInfo.isReactNativeApp()) {
32
- return
33
- }
34
-
35
- (applicationContext as ReactApplication).let {
36
- val reactContext = awaitNewReactNativeContext(it, null)
37
-
38
- enableOrDisableSynchronization(reactContext)
39
- }
40
- }
41
-
42
17
  /**
43
18
  * Reloads the React Native context and thus all javascript code.
44
19
  *
@@ -65,6 +40,26 @@ object ReactNativeExtension {
65
40
  val reactContext = awaitNewReactNativeContext(it, previousReactContext)
66
41
 
67
42
  enableOrDisableSynchronization(reactContext, networkSyncEnabled)
43
+ hackRN50OrHigherWaitForReady()
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Wait for React-Native to finish loading (i.e. make RN context available).
49
+ *
50
+ * @param applicationContext The app context, implicitly assumed to be a [ReactApplication] instance.
51
+ */
52
+ @JvmStatic
53
+ fun waitForRNBootstrap(applicationContext: Context) {
54
+ if (!ReactNativeInfo.isReactNativeApp()) {
55
+ return
56
+ }
57
+
58
+ (applicationContext as ReactApplication).let {
59
+ val reactContext = awaitNewReactNativeContext(it, null)
60
+
61
+ enableOrDisableSynchronization(reactContext)
62
+ hackRN50OrHigherWaitForReady()
68
63
  }
69
64
  }
70
65
 
@@ -150,6 +145,18 @@ object ReactNativeExtension {
150
145
  }
151
146
  }
152
147
 
148
+ private fun hackRN50OrHigherWaitForReady() {
149
+ if (ReactNativeInfo.rnVersion().minor in 50..62) {
150
+ try {
151
+ //TODO- Temp hack to make Detox usable for RN>=50 till we find a better sync solution.
152
+ Thread.sleep(1000)
153
+ } catch (e: InterruptedException) {
154
+ e.printStackTrace()
155
+ }
156
+
157
+ }
158
+ }
159
+
153
160
  private fun clearIdlingResources() {
154
161
  rnIdlingResources?.unregisterAll()
155
162
  rnIdlingResources = null
@@ -11,8 +11,11 @@ class UiControllerSpy: MethodsSpy("uiController") {
11
11
  fun eventInjectionsIterator(): Iterator<CallInfo?> = historyOf("injectMotionEvent").iterator()
12
12
 
13
13
  companion object {
14
+ @JvmStatic
14
15
  val instance = UiControllerSpy()
15
16
 
17
+ @JvmStatic
18
+ @JvmOverloads
16
19
  fun attachThroughProxy(spy: UiControllerSpy = instance) {
17
20
  val eventsInjectorReflected = EventsInjectorReflected(getUiController())
18
21
 
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": "20.14.1",
4
+ "version": "20.14.2",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -109,5 +109,5 @@
109
109
  "browserslist": [
110
110
  "node 14"
111
111
  ],
112
- "gitHead": "1512e9aa473d2c8fccdbaa11cc6b642c5eb17cfe"
112
+ "gitHead": "281536e93191a2d88bf7bd591b6ee95c316f817b"
113
113
  }
@@ -58,6 +58,17 @@ class Detox {
58
58
  };
59
59
  }
60
60
 
61
+ static extractInitialIntent() {
62
+ return {
63
+ target: {
64
+ type: "Class",
65
+ value: "com.wix.detox.Detox"
66
+ },
67
+ method: "extractInitialIntent",
68
+ args: []
69
+ };
70
+ }
71
+
61
72
  static getAppContext() {
62
73
  return {
63
74
  target: {
@@ -1 +0,0 @@
1
- df336477c5bb07869456e1a65d69d5c7
@@ -1 +0,0 @@
1
- aaa63a4199f17a89c7e884202ca33554cf0c4410
@@ -1 +0,0 @@
1
- fe1c75ae0cbabeeb848ffb9e80b591b6f6f2d37c899e05717a58d967748f4860
@@ -1 +0,0 @@
1
- 4e2f5bc2efecc31716ae9e70a2e87da45ebcda42a0f4d8d192ae7f2c68ad1f801e3758590be3d2bb3f8bd4a44dbed255e2a4f95abc2f9c44bd9d4879520ac51d
@@ -1 +0,0 @@
1
- 3ee1d9a4a119b7c6627434ad73e41638
@@ -1 +0,0 @@
1
- 4a270aaa691aa23b7b14ae32e77cb56de9cfdc95
@@ -1 +0,0 @@
1
- 47099e074808fb5626c109b5b09c464cabd9cad5c2844cdc98a1868e2b10238a
@@ -1 +0,0 @@
1
- c4c1f64a63da6e106ca90cfcab4e8d095d0f97c4341e6f3a54203116438264e1e2e262f3e2278b064b3283d4bbbddad88afc48c8d9a5f8bf410e0cc653c307f0
@@ -1 +0,0 @@
1
- 5802af204da6a4e44339eb5b544e816d
@@ -1 +0,0 @@
1
- 530bd1ee5be0479b9d48b33722b521ef408d82b7
@@ -1 +0,0 @@
1
- 4efd5d50625590c576c69a3a722d8df776fa213cff6e1fa254ec429d4db6fb58
@@ -1 +0,0 @@
1
- 19b779508bf7d9059f23fd638114bb817eee061bf018fcf793b451f63b3c5d8519e20f85fceffa61863d59244f168692d1e4e8d861be979200a2130c4486b9e8
@@ -1 +0,0 @@
1
- fdbbf729d55d7657238f4ffdfdf21b18
@@ -1 +0,0 @@
1
- 2ee22e6f82bbbd550d383530de9ac1403e716dbc
@@ -1 +0,0 @@
1
- 9522ff285942a4332b1602849b17fa910d603dd019c4cbcc454e8c2a64b0859d
@@ -1 +0,0 @@
1
- 1ff01de68f6d101e206f63bab0e67e93492a2c3661d3f763f08d4acbf647c207e095f74a9bfbb7b55a7faacf6545ed130037085258c7869fee01f80a45ecf7f1
@@ -1,76 +0,0 @@
1
- package com.wix.detox
2
-
3
- import android.app.Instrumentation.ActivityMonitor
4
- import android.content.Context
5
- import android.content.Intent
6
- import androidx.test.platform.app.InstrumentationRegistry
7
- import androidx.test.rule.ActivityTestRule
8
-
9
- class ActivityLaunchHelper(private val activityTestRule: ActivityTestRule<*>) {
10
-
11
- private val launchArgs = LaunchArgs()
12
- private val intentsFactory = LaunchIntentsFactory()
13
-
14
- fun launchActivityUnderTest() {
15
- val intent = extractInitialIntent()
16
- activityTestRule.launchActivity(intent)
17
- }
18
-
19
- fun launchMainActivity() {
20
- val activity = activityTestRule.activity
21
- launchActivitySync(intentsFactory.activityLaunchIntent(activity))
22
- }
23
-
24
- fun startActivityFromUrl(url: String) {
25
- launchActivitySync(intentsFactory.intentWithUrl(url, false))
26
- }
27
-
28
- fun startActivityFromNotification(dataFilePath: String) {
29
- val notificationData = NotificationDataParser(dataFilePath!!).toBundle()
30
- val intent = intentsFactory.intentWithNotificationData(appContext, notificationData, false)
31
- launchActivitySync(intent)
32
- }
33
-
34
- private fun extractInitialIntent(): Intent =
35
- if (launchArgs.hasUrlOverride()) {
36
- intentsFactory.intentWithUrl(launchArgs.urlOverride, true)
37
- } else if (launchArgs.hasNotificationPath()) {
38
- val notificationData = NotificationDataParser(launchArgs.notificationPath).toBundle()
39
- intentsFactory.intentWithNotificationData(appContext, notificationData, true)
40
- } else {
41
- intentsFactory.cleanIntent()
42
- }.also {
43
- it.putExtra(INTENT_LAUNCH_ARGS_KEY, launchArgs.asIntentBundle())
44
- }
45
-
46
- private fun launchActivitySync(intent: Intent) {
47
- // Ideally, we would just call sActivityTestRule.launchActivity(intent) and get it over with.
48
- // BUT!!! as it turns out, Espresso has an issue where doing this for an activity running in the background
49
- // would have Espresso set up an ActivityMonitor which will spend its time waiting for the activity to load, *without
50
- // ever being released*. It will finally fail after a 45 seconds timeout.
51
- // Without going into full details, it seems that activity test rules were not meant to be used this way. However,
52
- // the all-new ActivityScenario implementation introduced in androidx could probably support this (e.g. by using
53
- // dedicated methods such as moveToState(), which give better control over the lifecycle).
54
- // In any case, this is the core reason for this issue: https://github.com/wix/Detox/issues/1125
55
- // What it forces us to do, then, is this -
56
- // 1. Launch the activity by "ourselves" from the OS (i.e. using context.startActivity()).
57
- // 2. Set up an activity monitor by ourselves -- such that it would block until the activity is ready.
58
- // ^ Hence the code below.
59
- val activity = activityTestRule.activity
60
- val activityMonitor = ActivityMonitor(activity.javaClass.name, null, true)
61
- activity.startActivity(intent)
62
-
63
- InstrumentationRegistry.getInstrumentation().run {
64
- addMonitor(activityMonitor)
65
- waitForMonitorWithTimeout(activityMonitor, ACTIVITY_LAUNCH_TIMEOUT)
66
- }
67
- }
68
-
69
- private val appContext: Context
70
- get() = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
71
-
72
- companion object {
73
- private const val INTENT_LAUNCH_ARGS_KEY = "launchArgs"
74
- private const val ACTIVITY_LAUNCH_TIMEOUT = 10000L
75
- }
76
- }
@@ -1,44 +0,0 @@
1
- package com.wix.detox.reactnative
2
-
3
- import android.util.Log
4
- import com.facebook.react.bridge.ReactMarker
5
- import com.facebook.react.bridge.ReactMarkerConstants
6
- import com.facebook.react.bridge.ReactMarkerConstants.*
7
-
8
- object ReactMarkersLogger : ReactMarker.MarkerListener {
9
-
10
- fun attach() {
11
- ReactMarker.addListener(this)
12
- }
13
-
14
- override fun logMarker(marker: ReactMarkerConstants, p1: String?, p2: Int) {
15
- when {
16
- marker == DOWNLOAD_START ||
17
- marker == DOWNLOAD_END ||
18
- marker == BUILD_REACT_INSTANCE_MANAGER_START ||
19
- marker == BUILD_REACT_INSTANCE_MANAGER_END ||
20
- marker == REACT_BRIDGE_LOADING_START ||
21
- marker == REACT_BRIDGE_LOADING_END ||
22
- marker == REACT_BRIDGELESS_LOADING_START ||
23
- marker == REACT_BRIDGELESS_LOADING_END ||
24
- marker == CREATE_MODULE_START ||
25
- marker == CREATE_MODULE_END ||
26
- marker == NATIVE_MODULE_SETUP_START ||
27
- marker == NATIVE_MODULE_SETUP_END ||
28
- marker == PRE_RUN_JS_BUNDLE_START ||
29
- marker == RUN_JS_BUNDLE_START ||
30
- marker == RUN_JS_BUNDLE_END ||
31
- marker == CONTENT_APPEARED ||
32
- marker == CREATE_CATALYST_INSTANCE_START ||
33
- marker == CREATE_CATALYST_INSTANCE_END ||
34
- marker == DESTROY_CATALYST_INSTANCE_START ||
35
- marker == DESTROY_CATALYST_INSTANCE_END ||
36
- marker == CREATE_REACT_CONTEXT_START ||
37
- marker == CREATE_REACT_CONTEXT_END ||
38
- marker == PROCESS_PACKAGES_START ||
39
- marker == PROCESS_PACKAGES_END ||
40
- false ->
41
- Log.d("Detox.RNMarker", "$marker ($p1)")
42
- }
43
- }
44
- }