detox 20.32.0 → 20.33.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.
- package/Detox-android/com/wix/detox/{20.32.0/detox-20.32.0-sources.jar → 20.33.0/detox-20.33.0-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0.aar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.32.0/detox-20.32.0.pom → 20.33.0/detox-20.33.0.pom} +13 -1
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.33.0/detox-20.33.0.pom.sha512 +1 -0
- package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
- package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
- package/Detox-ios-framework.tbz +0 -0
- package/Detox-ios-src.tbz +0 -0
- package/Detox-ios-xcuitest.tbz +0 -0
- package/android/detox/build.gradle +6 -1
- package/android/detox/proguard-rules-app.pro +12 -0
- package/android/detox/src/full/java/com/wix/detox/DetoxMain.kt +4 -3
- package/android/detox/src/full/java/com/wix/detox/reactnative/ReactApplicationExt.kt +34 -0
- package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeExtension.kt +22 -27
- package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeLoadingMonitor.kt +51 -71
- package/android/detox/src/full/java/com/wix/detox/reactnative/helpers/RNHelpers.kt +1 -1
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/DetoxIdlingResource.kt +4 -2
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/ReactNativeIdlingResources.kt +17 -13
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/animations/AnimatedModuleIdlingResource.kt +5 -2
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/DetoxIdlingResourceFactory.kt +9 -20
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/DetoxIdlingResourceFactoryStrategy.kt +7 -0
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/FabricDetoxIdlingResourceFactoryStrategy.kt +31 -0
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/IdlingResourcesName.kt +1 -1
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/OldArchitectureDetoxIdlingResourceFactoryStrategy.kt +33 -0
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/network/NetworkIdlingResource.kt +5 -0
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/network/NetworkingModuleReflected.kt +11 -14
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/storage/AsyncStorageIdlingResource.kt +31 -29
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/FabricTimersIdlingResource.kt +50 -0
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/JavaTimersReflected.kt +26 -0
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/TimersIdlingResource.kt +6 -5
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/fabric/FabricUIManagerIdlingResources.kt +70 -0
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/{DispatchCommandOperationReflected.kt → paper/DispatchCommandOperationReflected.kt} +1 -1
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/{NativeHierarchyManagerReflected.kt → paper/NativeHierarchyManagerReflected.kt} +5 -3
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/{UIManagerModuleReflected.kt → paper/UIManagerModuleReflected.kt} +5 -3
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/{UIModuleIdlingResource.kt → paper/UIModuleIdlingResource.kt} +12 -4
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/{ViewCommandOpsQueueReflected.kt → paper/ViewCommandOpsQueueReflected.kt} +3 -3
- package/android/detox/src/full/java/com/wix/detox/reactnative/reloader/ReactNativeReloader.kt +34 -0
- package/android/detox/src/full/java/com/wix/detox/reactnative/reloader/ReactNativeReloaderFactory.kt +18 -0
- package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/AsyncStorageIdlingResourceTest.kt +9 -2
- package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/timers/TimersIdlingResourceTest.kt +9 -3
- package/package.json +2 -2
- package/scripts/updateGradle.js +37 -0
- package/src/DetoxWorker.js +1 -2
- package/src/copilot/detoxCopilotFrameworkDriver.js +2 -0
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.aar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.pom.md5 +0 -1
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.32.0/detox-20.32.0.pom.sha512 +0 -1
- package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeReloader.kt +0 -16
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
498f29f164887f0f2274e4abd874c003
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
9fa9b37f66d8ab4ce655492c5cae5c689600ba32
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4f832f9e782b74e2dd8cc9e0dc7cce16d8237787ba41c4959766c8dfb1eff3ff
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
26c7411525f22292763035d43009a695d5d83f15683d3d25cac36bf81fa64403a9b10d748b1064b7ce9922b43ac0e6ceeb3af1cf254d362471a6e4d334877bb7
|
|
Binary file
|
|
@@ -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.
|
|
6
|
+
<version>20.33.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
|
+
855397c71ecf7f34e546fbd6470004e6
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1775da0d745f62fb048530ec154f391cae1bf4d0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2e0b47850ee572f6e29957534156cec647ce6e526b137cef2a8108c4627a7a6c
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ebf42a136cb4a15b368e79a2acefaf7a36a17dba77ef24f84e5abc8f63067cc18558256690ab8b6a37858d0e4bc203ea5e93875d51a490089f2bf344359696b3
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<groupId>com.wix</groupId>
|
|
4
4
|
<artifactId>detox</artifactId>
|
|
5
5
|
<versioning>
|
|
6
|
-
<latest>20.
|
|
7
|
-
<release>20.
|
|
6
|
+
<latest>20.33.0</latest>
|
|
7
|
+
<release>20.33.0</release>
|
|
8
8
|
<versions>
|
|
9
|
-
<version>20.
|
|
9
|
+
<version>20.33.0</version>
|
|
10
10
|
</versions>
|
|
11
|
-
<lastUpdated>
|
|
11
|
+
<lastUpdated>20250205141419</lastUpdated>
|
|
12
12
|
</versioning>
|
|
13
13
|
</metadata>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2079b2c121b2426b536bb1a82d453023
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
9404bd86287244a491d5a33e36f0ff8c6f3c167c
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
0e1c8604589283e252ed7e050af9fd4b7523e6ea049676e160cc9c62c682cbb0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
904d92790c6064bdef677e74ae821a5a02b37d86c16a28f29847779b936ba62142be465c13800c475360491906348a27925ca807d34e0cd322a7052ed6943756
|
package/Detox-ios-framework.tbz
CHANGED
|
Binary file
|
package/Detox-ios-src.tbz
CHANGED
|
Binary file
|
package/Detox-ios-xcuitest.tbz
CHANGED
|
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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
37
|
+
awaitNewReactNativeContext(it, null)
|
|
38
38
|
|
|
39
|
-
enableOrDisableSynchronization(
|
|
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 =
|
|
62
|
+
val previousReactContext = it.getCurrentReactContext()
|
|
63
63
|
|
|
64
64
|
reloadReactNativeInBackground(it)
|
|
65
|
-
|
|
65
|
+
awaitNewReactNativeContext(it, previousReactContext)
|
|
66
66
|
|
|
67
|
-
enableOrDisableSynchronization(
|
|
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
|
-
|
|
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
|
|
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 =
|
|
114
|
+
val rnReloader = ReactNativeReloaderFactory(InstrumentationRegistry.getInstrumentation(), reactApplication).create()
|
|
119
115
|
rnReloader.reloadInBackground()
|
|
120
116
|
}
|
|
121
117
|
|
|
122
|
-
private fun awaitNewReactNativeContext(
|
|
123
|
-
|
|
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(
|
|
130
|
+
private fun enableOrDisableSynchronization(reactApplication: ReactApplication) {
|
|
128
131
|
if (shouldDisableSynchronization()) {
|
|
129
132
|
clearAllSynchronization()
|
|
130
133
|
} else {
|
|
131
|
-
setupIdlingResources(
|
|
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(
|
|
143
|
+
private fun setupIdlingResources(reactApplication: ReactApplication) {
|
|
141
144
|
val launchArgs = LaunchArgs()
|
|
142
145
|
|
|
143
|
-
rnIdlingResources = ReactNativeIdlingResources(
|
|
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.
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
73
|
-
|
|
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
|
|
79
|
+
return rnApplication.getCurrentReactContext()
|
|
83
80
|
}
|
|
84
|
-
}
|
|
85
81
|
|
|
86
|
-
private
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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>
|
package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/DetoxIdlingResource.kt
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
23
|
+
private val reactApplication: ReactApplication,
|
|
23
24
|
private var launchArgs: LaunchArgs,
|
|
24
|
-
private val idlingResourcesFactory: DetoxIdlingResourceFactory = DetoxIdlingResourceFactory(
|
|
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.
|
|
56
|
-
fun resumeUIIdlingResource() = resumeIdlingResource(IdlingResourcesName.
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|