detox 20.32.0 → 20.33.0-prerelease.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. package/Detox-android/com/wix/detox/{20.32.0/detox-20.32.0-sources.jar → 20.33.0-prerelease.0/detox-20.33.0-prerelease.0-sources.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0-sources.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0-sources.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0-sources.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0-sources.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0.aar +0 -0
  7. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0.aar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0.aar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0.aar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0.aar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/{20.32.0/detox-20.32.0.pom → 20.33.0-prerelease.0/detox-20.33.0-prerelease.0.pom} +13 -1
  12. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0.pom.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0.pom.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0.pom.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.33.0-prerelease.0/detox-20.33.0-prerelease.0.pom.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  17. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  18. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  19. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  20. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  21. package/Detox-ios-framework.tbz +0 -0
  22. package/Detox-ios-src.tbz +0 -0
  23. package/Detox-ios-xcuitest.tbz +0 -0
  24. package/android/detox/build.gradle +6 -1
  25. package/android/detox/proguard-rules-app.pro +12 -0
  26. package/android/detox/src/full/java/com/wix/detox/DetoxMain.kt +4 -3
  27. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactApplicationExt.kt +34 -0
  28. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeExtension.kt +22 -27
  29. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeLoadingMonitor.kt +51 -71
  30. package/android/detox/src/full/java/com/wix/detox/reactnative/helpers/RNHelpers.kt +1 -1
  31. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/DetoxIdlingResource.kt +4 -2
  32. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/ReactNativeIdlingResources.kt +17 -13
  33. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/animations/AnimatedModuleIdlingResource.kt +5 -2
  34. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/DetoxIdlingResourceFactory.kt +9 -20
  35. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/DetoxIdlingResourceFactoryStrategy.kt +7 -0
  36. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/FabricDetoxIdlingResourceFactoryStrategy.kt +31 -0
  37. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/IdlingResourcesName.kt +1 -1
  38. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/OldArchitectureDetoxIdlingResourceFactoryStrategy.kt +33 -0
  39. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/network/NetworkIdlingResource.kt +5 -0
  40. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/network/NetworkingModuleReflected.kt +11 -14
  41. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/storage/AsyncStorageIdlingResource.kt +31 -29
  42. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/FabricTimersIdlingResource.kt +50 -0
  43. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/JavaTimersReflected.kt +26 -0
  44. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/TimersIdlingResource.kt +6 -5
  45. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/fabric/FabricUIManagerIdlingResources.kt +70 -0
  46. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/{DispatchCommandOperationReflected.kt → paper/DispatchCommandOperationReflected.kt} +1 -1
  47. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/{NativeHierarchyManagerReflected.kt → paper/NativeHierarchyManagerReflected.kt} +5 -3
  48. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/{UIManagerModuleReflected.kt → paper/UIManagerModuleReflected.kt} +5 -3
  49. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/{UIModuleIdlingResource.kt → paper/UIModuleIdlingResource.kt} +12 -4
  50. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/{ViewCommandOpsQueueReflected.kt → paper/ViewCommandOpsQueueReflected.kt} +3 -3
  51. package/android/detox/src/full/java/com/wix/detox/reactnative/reloader/ReactNativeReloader.kt +34 -0
  52. package/android/detox/src/full/java/com/wix/detox/reactnative/reloader/ReactNativeReloaderFactory.kt +18 -0
  53. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/AsyncStorageIdlingResourceTest.kt +9 -2
  54. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/timers/TimersIdlingResourceTest.kt +9 -3
  55. package/package.json +2 -2
  56. package/scripts/updateGradle.js +37 -0
  57. package/src/DetoxWorker.js +1 -2
  58. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0-sources.jar.md5 +0 -1
  59. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0-sources.jar.sha1 +0 -1
  60. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0-sources.jar.sha256 +0 -1
  61. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0-sources.jar.sha512 +0 -1
  62. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.aar +0 -0
  63. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.aar.md5 +0 -1
  64. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.aar.sha1 +0 -1
  65. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.aar.sha256 +0 -1
  66. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.aar.sha512 +0 -1
  67. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.pom.md5 +0 -1
  68. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.pom.sha1 +0 -1
  69. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.pom.sha256 +0 -1
  70. package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.pom.sha512 +0 -1
  71. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeReloader.kt +0 -16
@@ -0,0 +1 @@
1
+ f8170fd9458971b52edf5cac9c405b006897dfda
@@ -0,0 +1 @@
1
+ a8cf832ed05547b999c437611e921271ae2aaafe7a19bfda8bc6737e4d3ba7d0
@@ -0,0 +1 @@
1
+ 8bd107bedd83c2784c3d19782e9a1f8ee6d7d66eb8bd4fe04a2a7603a23d257ac6cc516552a8238ec8e13fbabcd406cb1a458ae270d18f1b45094f22a69b3e6a
@@ -0,0 +1 @@
1
+ 4ab2c960e9f1d12a9daa555f3db6554c
@@ -0,0 +1 @@
1
+ 4068d06887f8949b32a0cf36fed6f3e70bb4f922
@@ -0,0 +1 @@
1
+ 189187fc397bdb111df5485b64ff7154c84ce232240594646896f11c6f433ef6
@@ -0,0 +1 @@
1
+ 3906c9b7a0dc913a2697a735819c907f62c4eb516f40e492bba0df74e54ee99927b36d5771f942e29054a94dd03c573bfed9a372756c5fcb42f0d3f0b53f85df
@@ -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.32.0</version>
6
+ <version>20.33.0-prerelease.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>
@@ -84,6 +84,18 @@
84
84
  <version>2.2.0</version>
85
85
  <scope>compile</scope>
86
86
  </dependency>
87
+ <dependency>
88
+ <groupId>androidx.test</groupId>
89
+ <artifactId>core-ktx</artifactId>
90
+ <version>1.6.1</version>
91
+ <scope>compile</scope>
92
+ </dependency>
93
+ <dependency>
94
+ <groupId>org.jetbrains.kotlin</groupId>
95
+ <artifactId>kotlin-reflect</artifactId>
96
+ <version>1.9.24</version>
97
+ <scope>runtime</scope>
98
+ </dependency>
87
99
  <dependency>
88
100
  <groupId>org.apache.commons</groupId>
89
101
  <artifactId>commons-lang3</artifactId>
@@ -0,0 +1 @@
1
+ 92a1842fd8b468f7d2617fdd5818460d
@@ -0,0 +1 @@
1
+ 53fac1636a7713666c932f139c9ee11e57e1ebc2
@@ -0,0 +1 @@
1
+ 798ac5edd34d8550949f1ccd01997146980586ba5d2ddb898f55352119dd0cdc
@@ -0,0 +1 @@
1
+ ad694f20be729a5ee85ce82a6c40c38d00519123e4fdba9147f460ee13e5fdde200007689394e1f358f66df81951153f3b25d2f99e1d9af7c504f16e14174dcd
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>20.32.0</latest>
7
- <release>20.32.0</release>
6
+ <latest>20.33.0-prerelease.0</latest>
7
+ <release>20.33.0-prerelease.0</release>
8
8
  <versions>
9
- <version>20.32.0</version>
9
+ <version>20.33.0-prerelease.0</version>
10
10
  </versions>
11
- <lastUpdated>20250106191456</lastUpdated>
11
+ <lastUpdated>20250128142241</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- a21a21f5f33f0521ea5b27a6b7c9a53d
1
+ f16f4bdb9f7dde7c65d8b063da273ae1
@@ -1 +1 @@
1
- 3c0a9c339f4d397ac37a6779311c1c121ab01863
1
+ da85452d2eeb7463aece46e69ccb689c12c06844
@@ -1 +1 @@
1
- fa21aff43ff2f349f95760a2467987cef206152f4277db7d005193124667457b
1
+ 223da53f8133c03f92bd33fc684cff73fe42cedf782a035c6e54c885a8c6cae7
@@ -1 +1 @@
1
- 91de105f632722548240b0ce79abfa9758dfb44827badc8828bdb09404f917038e5e2157757003141f2d676cf641b9734f063ea0f950e01842d491d3eb8f04fc
1
+ 5537372f15749a8bc0c294de80fb75a785a209fa10e7bd897696ee58947b316c17508ab02c7160f223ebc956a9d994f2e4a448d6bab473f074b682bcda309d0c
Binary file
package/Detox-ios-src.tbz CHANGED
Binary file
Binary file
@@ -147,6 +147,12 @@ dependencies {
147
147
  api('androidx.test.uiautomator:uiautomator:2.2.0') {
148
148
  because 'Needed by Detox but also makes UIAutomator seamlessly provided to Detox users with hybrid apps/E2E-tests.'
149
149
  }
150
+ api('androidx.test:core-ktx:1.6.1') {
151
+ because 'Needed by Detox but also makes AndroidX test core seamlessly provided to Detox users with hybrid apps/E2E-tests.'
152
+ }
153
+ implementation("org.jetbrains.kotlin:kotlin-reflect:$_kotlinVersion") {
154
+ because('Needed by Detox for kotlin reflection')
155
+ }
150
156
  }
151
157
 
152
158
  // Third-party/extension deps.
@@ -199,7 +205,6 @@ if (rootProject.hasProperty('isOfficialDetoxLib') ||
199
205
  dependencies {
200
206
  testImplementation 'org.spekframework.spek2:spek-dsl-jvm:2.0.15'
201
207
  testImplementation 'org.spekframework.spek2:spek-runner-junit5:2.0.15'
202
- testImplementation "org.jetbrains.kotlin:kotlin-reflect:$_kotlinVersion"
203
208
  }
204
209
  }
205
210
 
@@ -1,10 +1,18 @@
1
1
  -keepattributes InnerClasses, Exceptions
2
2
 
3
+ -keep class com.facebook.react.fabric.FabricUIManager { *; }
4
+ -keep class com.facebook.react.fabric.mounting.MountItemDispatcher { *; }
3
5
  -keep class com.facebook.react.modules.** { *; }
4
6
  -keep class com.facebook.react.uimanager.** { *; }
5
7
  -keep class com.facebook.react.animated.** { *; }
6
8
  -keep class com.facebook.react.ReactApplication { *; }
7
9
  -keep class com.facebook.react.ReactNativeHost { *; }
10
+ -keep class com.facebook.react.ReactHost { *; }
11
+ -keep class com.facebook.react.runtime.ReactHostImpl { *; }
12
+ -keep class com.facebook.react.runtime.BridgelessReactContext { *; }
13
+ -keep class com.facebook.react.runtime.ReactInstance { *; }
14
+ -keep class com.facebook.react.modules.core.JavaTimerManager { *; }
15
+
8
16
  -keep class com.facebook.react.ReactInstanceManager { *; }
9
17
  -keep class com.facebook.react.ReactInstanceManager** { *; }
10
18
  -keep class com.facebook.react.ReactInstanceEventListener { *; }
@@ -18,6 +26,10 @@
18
26
  -keep class com.reactnativecommunity.asyncstorage.** { *; }
19
27
 
20
28
  -keep class kotlin.reflect.** { *; }
29
+ -keep class kotlin.KotlinVersion { *; }
30
+ -keep class kotlin.sequences.** { *; }
31
+ -keep class kotlin.Triple { *; }
32
+ -keep class kotlin.properties.** { *; }
21
33
  -keep class kotlin.coroutines.CoroutineDispatcher { *; }
22
34
  -keep class kotlin.coroutines.CoroutineScope { *; }
23
35
  -keep class kotlin.coroutines.CoroutineContext { *; }
@@ -47,10 +47,11 @@ object DetoxMain {
47
47
  * not by instrumentation itself, but based on the `AppWillTerminateWithError` message; In it's own, it is a good
48
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
49
  */
50
- @Synchronized
51
50
  private fun launchActivityOnCue(rnHostHolder: Context, activityLaunchHelper: ActivityLaunchHelper) {
52
- awaitHandshake()
53
- launchActivity(rnHostHolder, activityLaunchHelper)
51
+ synchronized(this) {
52
+ awaitHandshake()
53
+ launchActivity(rnHostHolder, activityLaunchHelper)
54
+ }
54
55
  }
55
56
 
56
57
  private fun awaitHandshake() {
@@ -0,0 +1,34 @@
1
+ package com.wix.detox.reactnative
2
+
3
+ import android.annotation.SuppressLint
4
+ import com.facebook.react.ReactApplication
5
+ import com.facebook.react.ReactInstanceManager
6
+ import com.facebook.react.bridge.ReactContext
7
+ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
8
+
9
+
10
+ fun ReactApplication.getInstanceManagerSafe(): ReactInstanceManager {
11
+ return reactNativeHost.reactInstanceManager
12
+ ?: throw RuntimeException("ReactInstanceManager is null!")
13
+ }
14
+
15
+ @SuppressLint("VisibleForTests")
16
+ fun ReactApplication.getCurrentReactContext(): ReactContext? {
17
+ return if (isFabricEnabled()) {
18
+ reactHost?.currentReactContext
19
+ } else {
20
+ getInstanceManagerSafe().currentReactContext
21
+ }
22
+ }
23
+
24
+ fun ReactApplication.getCurrentReactContextSafe(): ReactContext {
25
+ return getCurrentReactContext()
26
+ ?: throw RuntimeException("ReactContext is null!")
27
+ }
28
+
29
+ /**
30
+ * A method to check if Fabric is enabled in the React Native application.
31
+ */
32
+ fun isFabricEnabled(): Boolean {
33
+ return DefaultNewArchitectureEntryPoint.fabricEnabled
34
+ }
@@ -5,10 +5,10 @@ import android.content.Context
5
5
  import android.util.Log
6
6
  import androidx.test.platform.app.InstrumentationRegistry
7
7
  import com.facebook.react.ReactApplication
8
- import com.facebook.react.ReactInstanceManager
9
8
  import com.facebook.react.bridge.ReactContext
10
9
  import com.wix.detox.LaunchArgs
11
10
  import com.wix.detox.reactnative.idlingresources.ReactNativeIdlingResources
11
+ import com.wix.detox.reactnative.reloader.ReactNativeReloaderFactory
12
12
 
13
13
  private const val LOG_TAG = "DetoxRNExt"
14
14
 
@@ -34,9 +34,9 @@ object ReactNativeExtension {
34
34
  }
35
35
 
36
36
  (applicationContext as ReactApplication).let {
37
- val reactContext = awaitNewReactNativeContext(it, null)
37
+ awaitNewReactNativeContext(it, null)
38
38
 
39
- enableOrDisableSynchronization(reactContext)
39
+ enableOrDisableSynchronization(it)
40
40
  }
41
41
  }
42
42
 
@@ -59,12 +59,12 @@ object ReactNativeExtension {
59
59
  (applicationContext as ReactApplication).let {
60
60
  clearIdlingResources()
61
61
 
62
- val previousReactContext = getCurrentReactContextSafe(it)
62
+ val previousReactContext = it.getCurrentReactContext()
63
63
 
64
64
  reloadReactNativeInBackground(it)
65
- val reactContext = awaitNewReactNativeContext(it, previousReactContext)
65
+ awaitNewReactNativeContext(it, previousReactContext)
66
66
 
67
- enableOrDisableSynchronization(reactContext)
67
+ enableOrDisableSynchronization(it)
68
68
  }
69
69
  }
70
70
 
@@ -75,11 +75,7 @@ object ReactNativeExtension {
75
75
 
76
76
  @JvmStatic
77
77
  fun enableAllSynchronization(applicationContext: ReactApplication) {
78
- val reactContext = getCurrentReactContextSafe(applicationContext)
79
-
80
- if (reactContext != null) {
81
- setupIdlingResources(reactContext)
82
- }
78
+ setupIdlingResources(applicationContext)
83
79
  }
84
80
 
85
81
  @JvmStatic
@@ -88,7 +84,7 @@ object ReactNativeExtension {
88
84
  @JvmStatic
89
85
  fun getRNActivity(applicationContext: Context): Activity? {
90
86
  if (ReactNativeInfo.isReactNativeApp()) {
91
- return getCurrentReactContextSafe(applicationContext as ReactApplication)?.currentActivity
87
+ return (applicationContext as ReactApplication).getCurrentReactContext()?.currentActivity
92
88
  }
93
89
  return null
94
90
  }
@@ -115,20 +111,27 @@ object ReactNativeExtension {
115
111
  }
116
112
 
117
113
  private fun reloadReactNativeInBackground(reactApplication: ReactApplication) {
118
- val rnReloader = ReactNativeReLoader(InstrumentationRegistry.getInstrumentation(), reactApplication)
114
+ val rnReloader = ReactNativeReloaderFactory(InstrumentationRegistry.getInstrumentation(), reactApplication).create()
119
115
  rnReloader.reloadInBackground()
120
116
  }
121
117
 
122
- private fun awaitNewReactNativeContext(reactApplication: ReactApplication, previousReactContext: ReactContext?): ReactContext {
123
- val rnLoadingMonitor = ReactNativeLoadingMonitor(InstrumentationRegistry.getInstrumentation(), reactApplication, previousReactContext)
118
+ private fun awaitNewReactNativeContext(
119
+ reactApplication: ReactApplication,
120
+ previousReactContext: ReactContext?
121
+ ): ReactContext {
122
+ val rnLoadingMonitor = ReactNativeLoadingMonitor(
123
+ InstrumentationRegistry.getInstrumentation(),
124
+ reactApplication,
125
+ previousReactContext
126
+ )
124
127
  return rnLoadingMonitor.getNewContext()!!
125
128
  }
126
129
 
127
- private fun enableOrDisableSynchronization(reactContext: ReactContext) {
130
+ private fun enableOrDisableSynchronization(reactApplication: ReactApplication) {
128
131
  if (shouldDisableSynchronization()) {
129
132
  clearAllSynchronization()
130
133
  } else {
131
- setupIdlingResources(reactContext)
134
+ setupIdlingResources(reactApplication)
132
135
  }
133
136
  }
134
137
 
@@ -137,10 +140,10 @@ object ReactNativeExtension {
137
140
  return launchArgs.hasEnableSynchronization() && launchArgs.enableSynchronization.equals("0")
138
141
  }
139
142
 
140
- private fun setupIdlingResources(reactContext: ReactContext) {
143
+ private fun setupIdlingResources(reactApplication: ReactApplication) {
141
144
  val launchArgs = LaunchArgs()
142
145
 
143
- rnIdlingResources = ReactNativeIdlingResources(reactContext, launchArgs).apply {
146
+ rnIdlingResources = ReactNativeIdlingResources(reactApplication, launchArgs).apply {
144
147
  registerAll()
145
148
  }
146
149
  }
@@ -150,12 +153,4 @@ object ReactNativeExtension {
150
153
  rnIdlingResources = null
151
154
  }
152
155
 
153
- private fun getInstanceManagerSafe(reactApplication: ReactApplication): ReactInstanceManager {
154
- return reactApplication.reactNativeHost.reactInstanceManager
155
- ?: throw RuntimeException("ReactInstanceManager is null!")
156
- }
157
-
158
- private fun getCurrentReactContextSafe(reactApplication: ReactApplication): ReactContext? {
159
- return getInstanceManagerSafe(reactApplication).currentReactContext
160
- }
161
156
  }
@@ -3,25 +3,22 @@ package com.wix.detox.reactnative
3
3
  import android.app.Instrumentation
4
4
  import android.util.Log
5
5
  import com.facebook.react.ReactApplication
6
- import com.facebook.react.ReactInstanceManager
6
+ import com.facebook.react.ReactInstanceEventListener
7
7
  import com.facebook.react.bridge.ReactContext
8
+ import com.facebook.react.runtime.ReactHostImpl
8
9
  import com.wix.detox.common.DetoxErrors
9
10
  import com.wix.detox.config.DetoxConfig
10
- import org.joor.Reflect
11
- import java.lang.reflect.Proxy
12
11
  import java.util.concurrent.CountDownLatch
13
12
  import java.util.concurrent.TimeUnit
14
13
 
15
14
  private const val LOG_TAG = "DetoxRNLoading"
16
15
 
17
- private const val REACT_INSTANCE_EVENT_LISTENER_CLASS = "com.facebook.react.ReactInstanceEventListener"
18
- private const val REACT_INSTANCE_EVENT_LISTENER_CLASS_COMPAT = "com.facebook.react.ReactInstanceManager\$ReactInstanceEventListener"
19
-
20
16
  open class ReactNativeLoadingMonitor(
21
- private val instrumentation: Instrumentation,
22
- private val rnApplication: ReactApplication,
23
- private val previousReactContext: ReactContext?,
24
- private val config: DetoxConfig = DetoxConfig.CONFIG) {
17
+ private val instrumentation: Instrumentation,
18
+ private val rnApplication: ReactApplication,
19
+ private val previousReactContext: ReactContext?,
20
+ private val config: DetoxConfig = DetoxConfig.CONFIG
21
+ ) {
25
22
  private val countDownLatch = CountDownLatch(1)
26
23
 
27
24
  fun getNewContext(): ReactContext? {
@@ -31,24 +28,21 @@ open class ReactNativeLoadingMonitor(
31
28
 
32
29
  private fun subscribeToNewRNContextUpdates() {
33
30
  instrumentation.runOnMainSync(
34
- Runnable {
35
- val rnInstanceManager = rnApplication.reactNativeHost.reactInstanceManager
36
- val reactContext = rnInstanceManager.currentReactContext
37
- if (reactContext != null && reactContext !== previousReactContext) {
38
- Log.d(LOG_TAG, "Got new RN-context directly and immediately")
39
- countDownLatch.countDown()
40
- return@Runnable
41
- }
31
+ Runnable {
32
+ val reactContext = rnApplication.getCurrentReactContext()
33
+ if (isReactNativeLoaded(reactContext)) {
34
+ Log.d(LOG_TAG, "Got new RN-context directly and immediately")
35
+ countDownLatch.countDown()
36
+ return@Runnable
37
+ }
42
38
 
43
- subscribeAsyncRNContextHandler(rnInstanceManager) {
44
- countDownLatch.countDown()
45
- }
46
- })
39
+ subscribeAsyncRNContextHandler() {
40
+ countDownLatch.countDown()
41
+ }
42
+ })
47
43
  }
48
44
 
49
45
  private fun awaitNewRNContext(): ReactContext? {
50
- val rnInstanceManager = rnApplication.reactNativeHost.reactInstanceManager
51
-
52
46
  var i = 0
53
47
  while (true) {
54
48
  try {
@@ -58,19 +52,22 @@ open class ReactNativeLoadingMonitor(
58
52
  // First load can take a lot of time. (packager)
59
53
  // Loads afterwards should take less than a second.
60
54
  throw DetoxErrors.DetoxRuntimeException(
61
- """Waited for the new RN-context for too long! (${config.rnContextLoadTimeoutSec} seconds)
55
+ """Waited for the new RN-context for too long! (${config.rnContextLoadTimeoutSec} seconds)
62
56
  |If you think that's not long enough, consider applying a custom Detox runtime-config in DetoxTest.runTests()."""
63
- .trimMargin())
57
+ .trimMargin()
58
+ )
64
59
  }
65
60
  } else {
66
61
  break
67
62
  }
68
63
 
69
- // Due to an ugly timing issue in RN
64
+ // Due to timing in RN
70
65
  // it is possible that our listener won't be ever called
71
66
  // That's why we have to check the reactContext regularly.
72
- val reactContext = rnInstanceManager.currentReactContext
73
- if (reactContext != null && reactContext !== previousReactContext) {
67
+ val reactContext = rnApplication.getCurrentReactContext()
68
+
69
+ // We also need to wait for rect native instance to be initialized
70
+ if (isReactNativeLoaded(reactContext)) {
74
71
  Log.d(LOG_TAG, "Got new RN-context explicitly while polling (#iteration=$i)")
75
72
  break
76
73
  }
@@ -79,51 +76,34 @@ open class ReactNativeLoadingMonitor(
79
76
  }
80
77
  }
81
78
 
82
- return rnInstanceManager.currentReactContext
79
+ return rnApplication.getCurrentReactContext()
83
80
  }
84
- }
85
81
 
86
- private interface DummyListenerIdentifier
87
-
88
- /**
89
- * This baby bridges over RN's breaking change introduced in version 0.68:
90
- * `ReactInstanceManager$ReactInstanceEventListener` was extracted onto a separate interface, having
91
- * `ReactInstanceManager.{add|remove}ReactInstanceEventLister()` changing their signature to use it, accordingly.
92
- *
93
- * This made us resort to a solution based on dynamic proxies, because - depending on RN's version (at runtime), we
94
- * need to dynamically decide what interface we "extend" (or actually shadow) via the proxy.
95
- *
96
- * @see RNDiff https://github.com/facebook/react-native/compare/v0.67.4..v0.68.0#diff-2f01f0cd7ff8c9ea58f12ef0eff5fa8250cad144dd5490598d80d8e9e743458aR1009
97
- * @see DynamicProxies https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html
98
- */
99
- private fun subscribeAsyncRNContextHandler(rnInstanceManager: ReactInstanceManager, onReactContextInitialized: () -> Any) {
100
- val listenerClass = resolveListenerClass()
101
- val proxyInterfaces = arrayOf(
102
- listenerClass,
103
- DummyListenerIdentifier::class.java // In order to be able to implement equals()
104
- )
105
- val listener = Proxy.newProxyInstance(listenerClass.classLoader, proxyInterfaces) { listener, method, args ->
106
- Log.d(LOG_TAG, "Listener-proxy method called: ${method.name}")
107
-
108
- val result = when (method.name) {
109
- "onReactContextInitialized" -> {
110
- Log.i(LOG_TAG, "Got new RN-context async'ly through listener")
111
- Reflect.on(rnInstanceManager).call("removeReactInstanceEventListener", listener)
112
- onReactContextInitialized()
113
- }
114
- "equals" -> {
115
- val candidate = args[0]
116
- candidate is DummyListenerIdentifier
117
- }
118
- else -> Any()
82
+ private fun isReactNativeLoaded(reactContext: ReactContext?) =
83
+ reactContext != null && reactContext !== previousReactContext && reactContext.hasActiveReactInstance()
84
+
85
+ private fun subscribeAsyncRNContextHandler(onReactContextInitialized: () -> Any) {
86
+ val isFabric = isFabricEnabled()
87
+ if (isFabric) {
88
+ // We do a casting for supporting RN 0.75 and above
89
+ val host = rnApplication.reactHost as ReactHostImpl?
90
+ host?.addReactInstanceEventListener(object : ReactInstanceEventListener {
91
+ override fun onReactContextInitialized(context: ReactContext) {
92
+ Log.i(LOG_TAG, "Got new RN-context through listener")
93
+ onReactContextInitialized()
94
+ host.removeReactInstanceEventListener(this)
95
+ }
96
+ })
97
+ } else {
98
+ val rnInstanceManager = rnApplication.getInstanceManagerSafe()
99
+ rnInstanceManager.addReactInstanceEventListener(object : ReactInstanceEventListener {
100
+ override fun onReactContextInitialized(context: ReactContext) {
101
+ Log.i(LOG_TAG, "Got new RN-context directly through listener")
102
+ onReactContextInitialized()
103
+ rnInstanceManager.removeReactInstanceEventListener(this)
104
+ }
105
+ })
119
106
  }
120
-
121
- result
122
107
  }
123
- Reflect.on(rnInstanceManager).call("addReactInstanceEventListener", listener)
124
108
  }
125
109
 
126
- private fun resolveListenerClass(): Class<*> {
127
- val className = if (ReactNativeInfo.rnVersion().minor >= 68) REACT_INSTANCE_EVENT_LISTENER_CLASS else REACT_INSTANCE_EVENT_LISTENER_CLASS_COMPAT
128
- return Class.forName(className)
129
- }
@@ -6,7 +6,7 @@ import com.facebook.react.bridge.ReactContext
6
6
 
7
7
  private const val LOG_TAG = "DetoxRNHelpers"
8
8
 
9
- object RNHelpers {
9
+ class RNHelpers {
10
10
  fun getNativeModule(reactContext: ReactContext, className: String): NativeModule? =
11
11
  try {
12
12
  val moduleClass = Class.forName(className) as Class<NativeModule>
@@ -1,12 +1,14 @@
1
1
  package com.wix.detox.reactnative.idlingresources
2
2
 
3
+ import androidx.annotation.VisibleForTesting
3
4
  import androidx.test.espresso.IdlingResource
4
5
  import com.wix.detox.espresso.idlingresources.DescriptiveIdlingResource
5
6
  import java.util.concurrent.atomic.AtomicBoolean
6
7
 
7
8
  abstract class DetoxIdlingResource : DescriptiveIdlingResource {
8
9
  private var callback: IdlingResource.ResourceCallback? = null
9
- private var paused: AtomicBoolean = AtomicBoolean(false)
10
+ @VisibleForTesting
11
+ internal var paused: AtomicBoolean = AtomicBoolean(false)
10
12
 
11
13
  fun pause() {
12
14
  paused.set(true)
@@ -30,7 +32,7 @@ abstract class DetoxIdlingResource : DescriptiveIdlingResource {
30
32
  }
31
33
 
32
34
  open fun onUnregistered() {
33
- // no-op
35
+ pause()
34
36
  }
35
37
 
36
38
  protected abstract fun checkIdle(): Boolean
@@ -5,8 +5,9 @@ import android.util.Log
5
5
  import androidx.test.espresso.Espresso
6
6
  import androidx.test.espresso.IdlingRegistry
7
7
  import androidx.test.espresso.base.IdlingResourceRegistry
8
- import com.facebook.react.bridge.ReactContext
8
+ import com.facebook.react.ReactApplication
9
9
  import com.wix.detox.LaunchArgs
10
+ import com.wix.detox.reactnative.getCurrentReactContext
10
11
  import com.wix.detox.reactnative.idlingresources.factory.DetoxIdlingResourceFactory
11
12
  import com.wix.detox.reactnative.idlingresources.factory.IdlingResourcesName
12
13
  import com.wix.detox.reactnative.idlingresources.factory.LooperName
@@ -19,9 +20,9 @@ import org.joor.Reflect
19
20
  private const val LOG_TAG = "DetoxRNIdleRes"
20
21
 
21
22
  class ReactNativeIdlingResources(
22
- private val reactContext: ReactContext,
23
+ private val reactApplication: ReactApplication,
23
24
  private var launchArgs: LaunchArgs,
24
- private val idlingResourcesFactory: DetoxIdlingResourceFactory = DetoxIdlingResourceFactory(reactContext)
25
+ private val idlingResourcesFactory: DetoxIdlingResourceFactory = DetoxIdlingResourceFactory(reactApplication)
25
26
  ) {
26
27
 
27
28
  private val idlingResources = mutableMapOf<IdlingResourcesName, DetoxIdlingResource>()
@@ -52,8 +53,8 @@ class ReactNativeIdlingResources(
52
53
 
53
54
  fun pauseRNTimersIdlingResource() = pauseIdlingResource(IdlingResourcesName.Timers)
54
55
  fun resumeRNTimersIdlingResource() = resumeIdlingResource(IdlingResourcesName.Timers)
55
- fun pauseUIIdlingResource() = pauseIdlingResource(IdlingResourcesName.UIModule)
56
- fun resumeUIIdlingResource() = resumeIdlingResource(IdlingResourcesName.UIModule)
56
+ fun pauseUIIdlingResource() = pauseIdlingResource(IdlingResourcesName.UI)
57
+ fun resumeUIIdlingResource() = resumeIdlingResource(IdlingResourcesName.UI)
57
58
 
58
59
  fun setBlacklistUrls(urlList: String) {
59
60
  setIdlingResourceBlacklist(urlList)
@@ -77,16 +78,19 @@ class ReactNativeIdlingResources(
77
78
  }
78
79
 
79
80
  private fun setupMQThreadsInterrogator(looperName: LooperName) {
80
- val mqThreadsReflector = MQThreadsReflector(reactContext)
81
- val looper = when (looperName) {
82
- LooperName.JS -> mqThreadsReflector.getJSMQueue()?.getLooper()
83
- LooperName.NativeModules -> mqThreadsReflector.getNativeModulesQueue()?.getLooper()
81
+ reactApplication.getCurrentReactContext()?.let {
82
+ val mqThreadsReflector = MQThreadsReflector(it)
83
+ val looper = when (looperName) {
84
+ LooperName.JS -> mqThreadsReflector.getJSMQueue()?.getLooper()
85
+ LooperName.NativeModules -> mqThreadsReflector.getNativeModulesQueue()?.getLooper()
86
+ }
87
+
88
+ looper?.let {
89
+ IdlingRegistry.getInstance().registerLooperAsIdlingResource(it)
90
+ loopers[looperName] = it
91
+ }
84
92
  }
85
93
 
86
- looper?.let {
87
- IdlingRegistry.getInstance().registerLooperAsIdlingResource(it)
88
- loopers[looperName] = it
89
- }
90
94
  }
91
95
 
92
96
  private suspend fun setupIdlingResources() {
@@ -53,9 +53,12 @@ class AnimatedModuleIdlingResource(private val reactContext: ReactContext) : Det
53
53
  Choreographer.getInstance().postFrameCallback(this)
54
54
  }
55
55
 
56
+ override fun onUnregistered() {
57
+ super.onUnregistered()
58
+ Choreographer.getInstance().removeFrameCallback(this)
59
+ }
60
+
56
61
  override fun doFrame(frameTimeNanos: Long) {
57
62
  isIdleNow
58
63
  }
59
64
  }
60
-
61
-