detox 20.44.0-smoke.0 → 20.44.0-smoke.1
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.44.0-smoke.0/detox-20.44.0-smoke.0-sources.jar → 20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.44.0-smoke.0/detox-20.44.0-smoke.0.aar → 20.44.0-smoke.1/detox-20.44.0-smoke.1.aar} +0 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.44.0-smoke.0/detox-20.44.0-smoke.0.pom → 20.44.0-smoke.1/detox-20.44.0-smoke.1.pom} +1 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.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/src/full/java/com/wix/detox/inquiry/DetoxAnimationTracker.kt +70 -0
- package/android/detox/src/full/java/com/wix/detox/inquiry/DetoxFabricAnimationHook.kt +16 -125
- package/android/detox/src/full/java/com/wix/detox/inquiry/DetoxFabricIntegration.kt +99 -0
- package/android/detox/src/full/java/com/wix/detox/inquiry/DetoxFabricUIManagerWrapper.kt +37 -0
- package/android/detox/src/full/java/com/wix/detox/inquiry/ViewLifecycleRegistry.kt +1 -1
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/fabric/FabricUIManagerIdlingResources.kt +4 -41
- package/package.json +2 -2
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0.aar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0.pom.md5 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0.pom.sha512 +0 -1
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bdf9863f8c4afcf4541218e9b3bed4a3
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
760a64eb6cdd8807b45b398d53de460c48df99a5
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
8e42be451e2bd115b75216749965d28317bddb694a2e880adab3abb709ba1d08
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5cb65dc5c27e0964f4fd77c1cb638cd071b43db67100cae210c0205ea2e87b3268bfb8543c06a8baf10aa748d4eb49302db99390464b639bc4e9c8a2eeabd278
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1a39cefde2e8dde2ca54d70b0c5e5b0d
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
39e35796a4308c0a15457b1613b96b090f721432
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
76090af6a690fc00138ac3c3a307fe3cc49dfde32789bbc2d8066fa53e67805b
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
12b0c9b12325ad848ba52cdbbea6a37d837b341d5f98d6c4c34c45f357cebbfc767466df76479ebde2f1546cdc2211cfe02a3bf383371ac24b1e3de43df568b5
|
|
@@ -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.44.0-smoke.
|
|
6
|
+
<version>20.44.0-smoke.1</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
|
+
aae3ffc3cd9babca3385c2de23ca2be0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
b6de9c93a0a4fdc91a1e1335dc19a90d6fe592bd
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
305c59339a78c67c7fa2b6ab908c7f455d6faeb039827af94e5426238d4f464e
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3e6d2c931c52d22342ae2c2cbc0cbd4c38c74e3240a48d95cfe69a1561eb76337dbfdd4dd2753414c19f02b42e16c1fa8f2965efe9fffafce9122cd798eecf22
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<groupId>com.wix</groupId>
|
|
4
4
|
<artifactId>detox</artifactId>
|
|
5
5
|
<versioning>
|
|
6
|
-
<latest>20.44.0-smoke.
|
|
7
|
-
<release>20.44.0-smoke.
|
|
6
|
+
<latest>20.44.0-smoke.1</latest>
|
|
7
|
+
<release>20.44.0-smoke.1</release>
|
|
8
8
|
<versions>
|
|
9
|
-
<version>20.44.0-smoke.
|
|
9
|
+
<version>20.44.0-smoke.1</version>
|
|
10
10
|
</versions>
|
|
11
|
-
<lastUpdated>
|
|
11
|
+
<lastUpdated>20251009104839</lastUpdated>
|
|
12
12
|
</versioning>
|
|
13
13
|
</metadata>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4baad9ede6680e5d2b46f1246a48c8f9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
b07f36a5c0ae07b6a7961b318ca15b8acc44d602
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
a95b41be51ad53267864aae6fc777db00648abe236c7bdbd46c645543e9c052f
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
03f466d7a8a4a9f3a5b82b78cc9fcfc0020dd9bb7e3c7d821096887eb241118db40ce09f2f4912a6de05bd2b4c4d75ad48133665e37f68c37bae4a3b227d08ca
|
package/Detox-ios-framework.tbz
CHANGED
|
Binary file
|
package/Detox-ios-src.tbz
CHANGED
|
Binary file
|
package/Detox-ios-xcuitest.tbz
CHANGED
|
Binary file
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
package com.wix.detox.inquiry
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.facebook.react.bridge.ReactContext
|
|
5
|
+
import com.facebook.react.fabric.FabricUIManager
|
|
6
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
7
|
+
// import com.facebook.react.uimanager.UIManagerType
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Main entry point for Detox animation tracking in Fabric.
|
|
11
|
+
* This provides a simple API to initialize and use the animation tracking system.
|
|
12
|
+
*/
|
|
13
|
+
object DetoxAnimationTracker {
|
|
14
|
+
private const val LOG_TAG = "DetoxAnimationTracker"
|
|
15
|
+
private var isInitialized = false
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initialize the animation tracking system.
|
|
19
|
+
* This should be called once when Detox starts up.
|
|
20
|
+
*/
|
|
21
|
+
fun initialize(reactContext: ReactContext) {
|
|
22
|
+
if (isInitialized) {
|
|
23
|
+
Log.d(LOG_TAG, "DetoxAnimationTracker already initialized")
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// Initialize the Fabric integration
|
|
29
|
+
DetoxFabricIntegration.initialize(reactContext)
|
|
30
|
+
isInitialized = true
|
|
31
|
+
Log.i(LOG_TAG, "DetoxAnimationTracker initialized successfully")
|
|
32
|
+
|
|
33
|
+
} catch (e: Exception) {
|
|
34
|
+
Log.e(LOG_TAG, "Failed to initialize DetoxAnimationTracker", e)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the current animation statistics
|
|
40
|
+
*/
|
|
41
|
+
fun getAnimationStats(): Map<String, Any> {
|
|
42
|
+
return ViewLifecycleRegistry.getStats()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get all recently animated views
|
|
47
|
+
*/
|
|
48
|
+
fun getRecentlyAnimatedViews(): List<android.view.View> {
|
|
49
|
+
return ViewLifecycleRegistry.getRecentlyAnimatedViews()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if a specific view was recently animated
|
|
54
|
+
*/
|
|
55
|
+
fun wasRecentlyAnimated(view: android.view.View): Boolean {
|
|
56
|
+
return ViewLifecycleRegistry.wasRecentlyAnimated(view)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Clear all animation tracking data
|
|
61
|
+
*/
|
|
62
|
+
fun clear() {
|
|
63
|
+
ViewLifecycleRegistry.clear()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if the tracker is initialized
|
|
68
|
+
*/
|
|
69
|
+
fun isInitialized(): Boolean = isInitialized
|
|
70
|
+
}
|
|
@@ -3,51 +3,37 @@ package com.wix.detox.inquiry
|
|
|
3
3
|
import android.util.Log
|
|
4
4
|
import android.view.View
|
|
5
5
|
import com.facebook.react.bridge.ReadableMap
|
|
6
|
-
import com.facebook.react.fabric.
|
|
6
|
+
import com.facebook.react.fabric.FabricUIManager
|
|
7
7
|
import com.wix.detox.inquiry.ViewLifecycleRegistry.markAnimated
|
|
8
8
|
import com.wix.detox.inquiry.ViewLifecycleRegistry.markMounted
|
|
9
9
|
import com.wix.detox.inquiry.ViewLifecycleRegistry.markUpdated
|
|
10
|
-
import com.wix.detox.inquiry.ViewLifecycleRegistry.markCustomEvent
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
|
-
* Hook into React Native's Fabric new architecture to track
|
|
14
|
-
* This
|
|
12
|
+
* Hook into React Native's Fabric new architecture to track animated views.
|
|
13
|
+
* This provides precise tracking by intercepting the exact points where animated
|
|
14
|
+
* properties are applied to views in Fabric.
|
|
15
15
|
*/
|
|
16
16
|
object DetoxFabricAnimationHook {
|
|
17
17
|
private const val LOG_TAG = "DetoxFabricHook"
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Hook into
|
|
21
|
-
* This
|
|
20
|
+
* Hook into FabricUIManager.synchronouslyUpdateViewOnUIThread to track animated updates.
|
|
21
|
+
* This marks views as animated whenever there's any animation activity, giving lots of false positives.
|
|
22
22
|
*/
|
|
23
|
-
fun
|
|
24
|
-
|
|
23
|
+
fun hookSynchronouslyUpdateViewOnUIThread(
|
|
24
|
+
reactTag: Int,
|
|
25
25
|
props: ReadableMap?,
|
|
26
|
-
|
|
26
|
+
fabricUIManager: FabricUIManager
|
|
27
27
|
) {
|
|
28
28
|
try {
|
|
29
29
|
// Get the actual Android View
|
|
30
|
-
val androidView =
|
|
30
|
+
val androidView = fabricUIManager.resolveView(reactTag)
|
|
31
31
|
if (androidView == null) {
|
|
32
|
-
Log.d(LOG_TAG, "View not found for tag: $
|
|
32
|
+
Log.d(LOG_TAG, "View not found for tag: $reactTag")
|
|
33
33
|
return
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
if (isAnimatedPropsUpdate(props)) {
|
|
38
|
-
Log.d(LOG_TAG, "Animated props update for view tag: $viewTag")
|
|
39
|
-
markAnimated(androidView)
|
|
40
|
-
|
|
41
|
-
// Log problematic animations
|
|
42
|
-
if (isProblematicAnimation(props)) {
|
|
43
|
-
Log.w(LOG_TAG, "Problematic animation detected for view tag: $viewTag")
|
|
44
|
-
markCustomEvent(androidView, "problematic_animation")
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
// Regular props update
|
|
48
|
-
markUpdated(androidView)
|
|
49
|
-
}
|
|
50
|
-
|
|
36
|
+
markAnimated(androidView)
|
|
51
37
|
} catch (e: Exception) {
|
|
52
38
|
Log.w(LOG_TAG, "Failed to hook animated view update", e)
|
|
53
39
|
}
|
|
@@ -57,13 +43,13 @@ object DetoxFabricAnimationHook {
|
|
|
57
43
|
* Hook into view mount operations to track when views are created.
|
|
58
44
|
*/
|
|
59
45
|
fun hookViewMount(
|
|
60
|
-
|
|
61
|
-
|
|
46
|
+
reactTag: Int,
|
|
47
|
+
fabricUIManager: FabricUIManager
|
|
62
48
|
) {
|
|
63
49
|
try {
|
|
64
|
-
val androidView =
|
|
50
|
+
val androidView = fabricUIManager.resolveView(reactTag)
|
|
65
51
|
if (androidView != null) {
|
|
66
|
-
Log.d(LOG_TAG, "View mounted with tag: $
|
|
52
|
+
Log.d(LOG_TAG, "View mounted with tag: $reactTag")
|
|
67
53
|
markMounted(androidView)
|
|
68
54
|
}
|
|
69
55
|
} catch (e: Exception) {
|
|
@@ -71,86 +57,6 @@ object DetoxFabricAnimationHook {
|
|
|
71
57
|
}
|
|
72
58
|
}
|
|
73
59
|
|
|
74
|
-
/**
|
|
75
|
-
* Get Android View by React Native view tag using reflection.
|
|
76
|
-
* This works around the fact that SurfaceMountingManager doesn't expose a direct getView method.
|
|
77
|
-
*/
|
|
78
|
-
private fun getViewByTag(
|
|
79
|
-
surfaceMountingManager: SurfaceMountingManager,
|
|
80
|
-
viewTag: Int
|
|
81
|
-
): View? {
|
|
82
|
-
return try {
|
|
83
|
-
// Use reflection to access the internal view registry
|
|
84
|
-
val viewRegistryField = surfaceMountingManager.javaClass.getDeclaredField("mViewRegistry")
|
|
85
|
-
viewRegistryField.isAccessible = true
|
|
86
|
-
val viewRegistry = viewRegistryField.get(surfaceMountingManager)
|
|
87
|
-
|
|
88
|
-
// Get the view from the registry
|
|
89
|
-
val getViewMethod = viewRegistry.javaClass.getMethod("getView", Int::class.java)
|
|
90
|
-
getViewMethod.invoke(viewRegistry, viewTag) as? View
|
|
91
|
-
} catch (e: Exception) {
|
|
92
|
-
Log.w(LOG_TAG, "Failed to get view by tag: $viewTag", e)
|
|
93
|
-
null
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Check if this is an animated props update by looking for animated properties.
|
|
99
|
-
*/
|
|
100
|
-
private fun isAnimatedPropsUpdate(props: ReadableMap?): Boolean {
|
|
101
|
-
if (props == null) return false
|
|
102
|
-
|
|
103
|
-
val animatedKeys = setOf(
|
|
104
|
-
"transform", "opacity", "scaleX", "scaleY", "scale",
|
|
105
|
-
"translateX", "translateY", "rotateX", "rotateY", "rotateZ",
|
|
106
|
-
"backgroundColor", "borderRadius", "borderWidth"
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
val iterator = props.keySetIterator()
|
|
110
|
-
while (iterator.hasNextKey()) {
|
|
111
|
-
val key = iterator.nextKey()
|
|
112
|
-
if (animatedKeys.any { key.contains(it, ignoreCase = true) }) {
|
|
113
|
-
return true
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return false
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Check if this animation might be problematic (infinite loops, conflicting animations, etc.).
|
|
122
|
-
*/
|
|
123
|
-
private fun isProblematicAnimation(props: ReadableMap?): Boolean {
|
|
124
|
-
if (props == null) return false
|
|
125
|
-
|
|
126
|
-
// Check for potential infinite loop patterns
|
|
127
|
-
val transformKeys = mutableSetOf<String>()
|
|
128
|
-
val iterator = props.keySetIterator()
|
|
129
|
-
|
|
130
|
-
while (iterator.hasNextKey()) {
|
|
131
|
-
val key = iterator.nextKey()
|
|
132
|
-
if (key.contains("transform", ignoreCase = true)) {
|
|
133
|
-
transformKeys.add(key)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Multiple transform properties might indicate conflicting animations
|
|
138
|
-
if (transformKeys.size > 3) {
|
|
139
|
-
Log.w(LOG_TAG, "Multiple transform properties detected: $transformKeys")
|
|
140
|
-
return true
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Check for opacity animations that might cause issues
|
|
144
|
-
if (props.hasKey("opacity")) {
|
|
145
|
-
val opacity = props.getDouble("opacity")
|
|
146
|
-
if (opacity < 0.0 || opacity > 1.0) {
|
|
147
|
-
Log.w(LOG_TAG, "Invalid opacity value: $opacity")
|
|
148
|
-
return true
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return false
|
|
153
|
-
}
|
|
154
60
|
|
|
155
61
|
/**
|
|
156
62
|
* Get view coordinates for highlighting
|
|
@@ -167,21 +73,6 @@ object DetoxFabricAnimationHook {
|
|
|
167
73
|
return coords
|
|
168
74
|
}
|
|
169
75
|
|
|
170
|
-
/**
|
|
171
|
-
* Get view coordinates relative to root view
|
|
172
|
-
*/
|
|
173
|
-
fun getViewCoordinatesRelativeToRoot(view: View, rootView: View): IntArray {
|
|
174
|
-
val viewCoords = getViewCoordinates(view)
|
|
175
|
-
val rootCoords = getViewCoordinates(rootView)
|
|
176
|
-
|
|
177
|
-
return intArrayOf(
|
|
178
|
-
viewCoords[0] - rootCoords[0], // Relative X
|
|
179
|
-
viewCoords[1] - rootCoords[1], // Relative Y
|
|
180
|
-
viewCoords[2], // Width
|
|
181
|
-
viewCoords[3] // Height
|
|
182
|
-
)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
76
|
/**
|
|
186
77
|
* Log current registry statistics
|
|
187
78
|
*/
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
package com.wix.detox.inquiry
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.facebook.react.bridge.ReactContext
|
|
5
|
+
import com.facebook.react.fabric.FabricUIManager
|
|
6
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
7
|
+
// import com.facebook.react.uimanager.UIManagerType
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Integration point for Detox with React Native's Fabric architecture.
|
|
11
|
+
* This provides hooks into Fabric's animation system to track animated views.
|
|
12
|
+
*/
|
|
13
|
+
object DetoxFabricIntegration {
|
|
14
|
+
private const val LOG_TAG = "DetoxFabricIntegration"
|
|
15
|
+
private var isInitialized = false
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initialize the Fabric animation hooks.
|
|
19
|
+
* This should be called once when Detox starts up.
|
|
20
|
+
*/
|
|
21
|
+
fun initialize(reactContext: ReactContext) {
|
|
22
|
+
if (isInitialized) {
|
|
23
|
+
Log.d(LOG_TAG, "DetoxFabricIntegration already initialized")
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// Get the FabricUIManager
|
|
29
|
+
val fabricUIManager = UIManagerHelper.getUIManager(reactContext, 1) as? FabricUIManager
|
|
30
|
+
if (fabricUIManager == null) {
|
|
31
|
+
Log.w(LOG_TAG, "FabricUIManager not available - Fabric animation tracking disabled")
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Hook into the FabricUIManager
|
|
36
|
+
hookFabricUIManager(fabricUIManager)
|
|
37
|
+
isInitialized = true
|
|
38
|
+
Log.i(LOG_TAG, "DetoxFabricIntegration initialized successfully")
|
|
39
|
+
|
|
40
|
+
} catch (e: Exception) {
|
|
41
|
+
Log.e(LOG_TAG, "Failed to initialize DetoxFabricIntegration", e)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Hook into FabricUIManager to track animated updates.
|
|
47
|
+
* This uses reflection to intercept synchronouslyUpdateViewOnUIThread calls.
|
|
48
|
+
*/
|
|
49
|
+
private fun hookFabricUIManager(fabricUIManager: FabricUIManager) {
|
|
50
|
+
try {
|
|
51
|
+
// Create a wrapper that intercepts calls to synchronouslyUpdateViewOnUIThread
|
|
52
|
+
val originalMethod = FabricUIManager::class.java.getDeclaredMethod(
|
|
53
|
+
"synchronouslyUpdateViewOnUIThread",
|
|
54
|
+
Int::class.java,
|
|
55
|
+
com.facebook.react.bridge.ReadableMap::class.java
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
// Note: In a real implementation, you would use bytecode manipulation
|
|
59
|
+
// or AOP to intercept this method. For now, we'll provide a manual hook
|
|
60
|
+
// that can be called from the application code.
|
|
61
|
+
|
|
62
|
+
Log.d(LOG_TAG, "FabricUIManager hook prepared (manual integration required)")
|
|
63
|
+
|
|
64
|
+
} catch (e: Exception) {
|
|
65
|
+
Log.w(LOG_TAG, "Failed to hook FabricUIManager", e)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Manual hook for synchronouslyUpdateViewOnUIThread.
|
|
71
|
+
* This should be called from the application's FabricUIManager wrapper.
|
|
72
|
+
*/
|
|
73
|
+
fun onSynchronouslyUpdateViewOnUIThread(
|
|
74
|
+
reactTag: Int,
|
|
75
|
+
props: com.facebook.react.bridge.ReadableMap?,
|
|
76
|
+
fabricUIManager: FabricUIManager
|
|
77
|
+
) {
|
|
78
|
+
DetoxFabricAnimationHook.hookSynchronouslyUpdateViewOnUIThread(reactTag, props, fabricUIManager)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Manual hook for view mount operations.
|
|
83
|
+
*/
|
|
84
|
+
fun onViewMount(reactTag: Int, fabricUIManager: FabricUIManager) {
|
|
85
|
+
DetoxFabricAnimationHook.hookViewMount(reactTag, fabricUIManager)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Check if the integration is initialized
|
|
90
|
+
*/
|
|
91
|
+
fun isInitialized(): Boolean = isInitialized
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get current animation statistics
|
|
95
|
+
*/
|
|
96
|
+
fun getAnimationStats(): Map<String, Any> {
|
|
97
|
+
return ViewLifecycleRegistry.getStats()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
package com.wix.detox.inquiry
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.facebook.react.bridge.ReadableMap
|
|
5
|
+
import com.facebook.react.fabric.FabricUIManager
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Wrapper for FabricUIManager that intercepts animation-related calls.
|
|
9
|
+
* This provides a clean way to hook into Fabric's animation system.
|
|
10
|
+
*/
|
|
11
|
+
class DetoxFabricUIManagerWrapper(
|
|
12
|
+
private val originalUIManager: FabricUIManager
|
|
13
|
+
) {
|
|
14
|
+
private val LOG_TAG = "DetoxFabricUIManagerWrapper"
|
|
15
|
+
|
|
16
|
+
fun synchronouslyUpdateViewOnUIThread(reactTag: Int, props: ReadableMap?) {
|
|
17
|
+
try {
|
|
18
|
+
// Call the original method first (only if props is not null)
|
|
19
|
+
if (props != null) {
|
|
20
|
+
originalUIManager.synchronouslyUpdateViewOnUIThread(reactTag, props)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Then hook our animation tracking
|
|
24
|
+
DetoxFabricAnimationHook.hookSynchronouslyUpdateViewOnUIThread(reactTag, props, originalUIManager)
|
|
25
|
+
|
|
26
|
+
} catch (e: Exception) {
|
|
27
|
+
Log.w(LOG_TAG, "Failed to process animated view update", e)
|
|
28
|
+
// Still call the original method to avoid breaking the app (only if props is not null)
|
|
29
|
+
if (props != null) {
|
|
30
|
+
originalUIManager.synchronouslyUpdateViewOnUIThread(reactTag, props)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Delegate all other methods to the original UIManager
|
|
36
|
+
fun resolveView(reactTag: Int) = originalUIManager.resolveView(reactTag)
|
|
37
|
+
}
|
|
@@ -140,7 +140,7 @@ class FabricUIManagerIdlingResources(
|
|
|
140
140
|
32 -> { // INSTRUCTION_UPDATE_PROPS
|
|
141
141
|
val viewTag = intBuffer[i++]
|
|
142
142
|
val props = objBuffer[j++] as? com.facebook.react.bridge.ReadableMap
|
|
143
|
-
|
|
143
|
+
|
|
144
144
|
// Track animated view update
|
|
145
145
|
trackAnimatedViewUpdate(viewTag, props)
|
|
146
146
|
}
|
|
@@ -204,14 +204,7 @@ class FabricUIManagerIdlingResources(
|
|
|
204
204
|
val view = getViewByTag(fabricUIManager as Any, viewTag)
|
|
205
205
|
|
|
206
206
|
if (view != null) {
|
|
207
|
-
|
|
208
|
-
val isAnimated = isAnimatedPropsUpdate(props)
|
|
209
|
-
|
|
210
|
-
if (isAnimated) {
|
|
211
|
-
ViewLifecycleRegistry.markAnimated(view)
|
|
212
|
-
} else {
|
|
213
|
-
ViewLifecycleRegistry.markUpdated(view)
|
|
214
|
-
}
|
|
207
|
+
com.wix.detox.inquiry.DetoxFabricAnimationHook.hookSynchronouslyUpdateViewOnUIThread(viewTag, props, fabricUIManager as com.facebook.react.fabric.FabricUIManager)
|
|
215
208
|
}
|
|
216
209
|
} catch (e: Exception) {
|
|
217
210
|
// Silently ignore errors to avoid breaking the idling resource
|
|
@@ -225,11 +218,11 @@ class FabricUIManagerIdlingResources(
|
|
|
225
218
|
return try {
|
|
226
219
|
// Get MountingManager from FabricUIManager
|
|
227
220
|
val mountingManager = Reflect.on(fabricUIManager).field("mMountingManager").get<Any>()
|
|
228
|
-
|
|
221
|
+
|
|
229
222
|
// Get SurfaceMountingManager for the view
|
|
230
223
|
val getSurfaceManagerMethod = mountingManager.javaClass.getMethod("getSurfaceManagerForView", Int::class.java)
|
|
231
224
|
val surfaceMountingManager = getSurfaceManagerMethod.invoke(mountingManager, viewTag)
|
|
232
|
-
|
|
225
|
+
|
|
233
226
|
if (surfaceMountingManager != null) {
|
|
234
227
|
// Get the actual Android View
|
|
235
228
|
val getViewMethod = surfaceMountingManager.javaClass.getMethod("getView", Int::class.java)
|
|
@@ -242,35 +235,5 @@ class FabricUIManagerIdlingResources(
|
|
|
242
235
|
}
|
|
243
236
|
}
|
|
244
237
|
|
|
245
|
-
/**
|
|
246
|
-
* Check if this is an animated props update.
|
|
247
|
-
*/
|
|
248
|
-
private fun isAnimatedPropsUpdate(props: com.facebook.react.bridge.ReadableMap?): Boolean {
|
|
249
|
-
if (props == null) {
|
|
250
|
-
Log.i("DetoxFabricDebug", "Props is null - not animated")
|
|
251
|
-
return false
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
val animatedKeys = setOf(
|
|
255
|
-
"transform", "opacity", "scaleX", "scaleY", "scale",
|
|
256
|
-
"translateX", "translateY", "rotateX", "rotateY", "rotateZ",
|
|
257
|
-
"backgroundColor", "borderRadius", "borderWidth"
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
Log.i("DetoxFabricDebug", "Checking props for animated keys...")
|
|
261
|
-
|
|
262
|
-
val iterator = props.keySetIterator()
|
|
263
|
-
while (iterator.hasNextKey()) {
|
|
264
|
-
val key = iterator.nextKey()
|
|
265
|
-
Log.i("DetoxFabricDebug", "Checking key: $key")
|
|
266
|
-
if (animatedKeys.any { key.contains(it, ignoreCase = true) }) {
|
|
267
|
-
Log.i("DetoxFabricDebug", "Found animated key: $key")
|
|
268
|
-
return true
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
Log.i("DetoxFabricDebug", "No animated keys found")
|
|
273
|
-
return false
|
|
274
|
-
}
|
|
275
238
|
|
|
276
239
|
}
|
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.44.0-smoke.
|
|
4
|
+
"version": "20.44.0-smoke.1",
|
|
5
5
|
"bin": {
|
|
6
6
|
"detox": "local-cli/cli.js"
|
|
7
7
|
},
|
|
@@ -120,5 +120,5 @@
|
|
|
120
120
|
"browserslist": [
|
|
121
121
|
"node 14"
|
|
122
122
|
],
|
|
123
|
-
"gitHead": "
|
|
123
|
+
"gitHead": "5e0b38a97eccb64d73369b1a3ccbd9c31ecde284"
|
|
124
124
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
a3db6cd0603cac8288674ad9a3500d8f
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1ad7d900ea972d2ee93f237abcc969e0ad522652
|
package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0-sources.jar.sha256
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
b0290a59c47efb8a80702a11a2dfb69498ddcd0da14d63d30f558574105fbe17
|
package/Detox-android/com/wix/detox/20.44.0-smoke.0/detox-20.44.0-smoke.0-sources.jar.sha512
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
81bcbcbc24ded6f8b469f8be58ad0195f72b39faadc1f3da271d646c93756dac15476d208b1424cde7f9966614a01e122452083b060094bde60ba64d080ff9c4
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
216f0dfc73c1b4f9ab0514607d5f5f2b
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
9d8dfd8b0ac4ebeaeb7f4af1d8ab1d650046662d
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
7c48b2a45b2deda2526d564ffc1472a9ec7dd5d123591113106b480281c5e9ce
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
4208bb610d9268ad130dba46fa3a08eb2dd39660b700b3218c3b784937b8248ff93c51443bce4cff4d339bf3cc8969f37159235d26b284b7accb00e1ca30722f
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
a4be7bd3d45ebb560642a7c0761625fd
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
285ed616044919523ccdcc037a962672de13646e
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0338db52bbfeb69095f0a1c31abb80acec835b1031e60a1047982260ab232ff8
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
b2bf4d188dfbeca23574936656a9e09936762f5d53263529414696aa294770c324d556d309a1e9887b1e56734eb706072ab4cfc880af4bdbfe9e9d9b26618930
|