detox 20.44.0-smoke.1 → 20.44.0-smoke.2
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.1/detox-20.44.0-smoke.1-sources.jar → 20.44.0-smoke.2/detox-20.44.0-smoke.2-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2.aar +0 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2.aar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.44.0-smoke.1/detox-20.44.0-smoke.1.pom → 20.44.0-smoke.2/detox-20.44.0-smoke.2.pom} +1 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.44.0-smoke.2/detox-20.44.0-smoke.2.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/adapters/server/DetoxActionHandlers.kt +1 -7
- package/android/detox/src/full/java/com/wix/detox/espresso/hierarchy/ViewHierarchyGenerator.kt +14 -63
- package/android/detox/src/full/java/com/wix/detox/inquiry/FabricAnimationsInquirer.kt +430 -0
- package/android/detox/src/full/java/com/wix/detox/inquiry/ViewLifecycleRegistry.kt +83 -173
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/animations/AnimatedModuleIdlingResource.kt +7 -2
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/fabric/FabricUIManagerIdlingResources.kt +1 -170
- package/package.json +2 -2
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/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 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.pom.md5 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.pom.sha512 +0 -1
- package/android/detox/src/full/java/com/wix/detox/inquiry/DetoxAnimationTracker.kt +0 -70
- package/android/detox/src/full/java/com/wix/detox/inquiry/DetoxFabricAnimationHook.kt +0 -83
- package/android/detox/src/full/java/com/wix/detox/inquiry/DetoxFabricIntegration.kt +0 -99
- package/android/detox/src/full/java/com/wix/detox/inquiry/DetoxFabricUIManagerWrapper.kt +0 -37
|
@@ -1,233 +1,143 @@
|
|
|
1
1
|
package com.wix.detox.inquiry
|
|
2
2
|
|
|
3
|
+
import android.util.Log
|
|
3
4
|
import android.view.View
|
|
4
5
|
import java.util.concurrent.ConcurrentHashMap
|
|
5
|
-
import java.util.
|
|
6
|
+
import java.util.Date
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
* Registry to track
|
|
9
|
-
*
|
|
10
|
-
* - Mount events (when views are created/mounted)
|
|
11
|
-
* - Animation events (when views are animated)
|
|
12
|
-
* - Update events (when views are updated)
|
|
13
|
-
* - Custom events (extensible for future needs)
|
|
9
|
+
* Registry to track view lifecycle events like mounting, updating, and animating.
|
|
10
|
+
* This data is used to inject metadata into the XML hierarchy for debugging.
|
|
14
11
|
*/
|
|
15
12
|
object ViewLifecycleRegistry {
|
|
16
|
-
private val
|
|
17
|
-
private val lastCleanupTime = AtomicLong(0)
|
|
18
|
-
private val cleanupIntervalMs = 30_000L // Clean up every 30 seconds
|
|
13
|
+
private const val LOG_TAG = "ViewLifecycleRegistry"
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
val mountTime: Long = 0,
|
|
26
|
-
val lastAnimateTime: Long = 0,
|
|
27
|
-
val lastUpdateTime: Long = 0,
|
|
28
|
-
val animationCount: Int = 0,
|
|
29
|
-
val updateCount: Int = 0,
|
|
30
|
-
val customEvents: MutableMap<String, Long> = mutableMapOf()
|
|
31
|
-
) {
|
|
32
|
-
fun wasRecentlyAnimated(windowMs: Long = 1500): Boolean {
|
|
33
|
-
if (lastAnimateTime == 0L) return false
|
|
34
|
-
return System.currentTimeMillis() - lastAnimateTime <= windowMs
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
fun wasRecentlyUpdated(windowMs: Long = 1000): Boolean {
|
|
38
|
-
if (lastUpdateTime == 0L) return false
|
|
39
|
-
return System.currentTimeMillis() - lastUpdateTime <= windowMs
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
fun wasRecentlyMounted(windowMs: Long = 5000): Boolean {
|
|
43
|
-
if (mountTime == 0L) return false
|
|
44
|
-
return System.currentTimeMillis() - mountTime <= windowMs
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
fun hasCustomEvent(eventType: String, windowMs: Long = 2000): Boolean {
|
|
48
|
-
val eventTime = customEvents[eventType] ?: return false
|
|
49
|
-
return System.currentTimeMillis() - eventTime <= windowMs
|
|
50
|
-
}
|
|
51
|
-
}
|
|
15
|
+
// Thread-safe maps to store view lifecycle data
|
|
16
|
+
private val mountedViews = ConcurrentHashMap<View, Date>()
|
|
17
|
+
private val updatedViews = ConcurrentHashMap<View, Date>()
|
|
18
|
+
private val animatedViews = ConcurrentHashMap<View, Date>()
|
|
19
|
+
private val customEvents = ConcurrentHashMap<View, MutableList<Pair<String, Date>>>()
|
|
52
20
|
|
|
53
21
|
/**
|
|
54
|
-
* Mark a view as mounted
|
|
22
|
+
* Mark a view as mounted (created/attached)
|
|
55
23
|
*/
|
|
56
24
|
fun markMounted(view: View) {
|
|
57
|
-
val now =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
performPeriodicCleanup()
|
|
25
|
+
val now = Date()
|
|
26
|
+
mountedViews[view] = now
|
|
27
|
+
Log.d(LOG_TAG, "View mounted: ${view.javaClass.simpleName} at $now")
|
|
62
28
|
}
|
|
63
29
|
|
|
64
30
|
/**
|
|
65
|
-
* Mark a view as
|
|
66
|
-
*/
|
|
67
|
-
fun markAnimated(view: View) {
|
|
68
|
-
val now = System.currentTimeMillis()
|
|
69
|
-
|
|
70
|
-
viewLifecycleInfo.compute(view) { _, existing ->
|
|
71
|
-
val info = existing ?: ViewLifecycleInfo(view)
|
|
72
|
-
info.copy(
|
|
73
|
-
lastAnimateTime = now,
|
|
74
|
-
animationCount = info.animationCount + 1
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
performPeriodicCleanup()
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Mark a view as updated
|
|
31
|
+
* Mark a view as updated (props changed)
|
|
82
32
|
*/
|
|
83
33
|
fun markUpdated(view: View) {
|
|
84
|
-
val now =
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
info.copy(
|
|
88
|
-
lastUpdateTime = now,
|
|
89
|
-
updateCount = info.updateCount + 1
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
performPeriodicCleanup()
|
|
34
|
+
val now = Date()
|
|
35
|
+
updatedViews[view] = now
|
|
36
|
+
Log.d(LOG_TAG, "View updated: ${view.javaClass.simpleName} at $now")
|
|
93
37
|
}
|
|
94
38
|
|
|
95
39
|
/**
|
|
96
|
-
*
|
|
40
|
+
* Clear animated views older than 5 seconds (called at start of each inquiry)
|
|
97
41
|
*/
|
|
98
|
-
fun
|
|
42
|
+
fun clearAnimatedViews() {
|
|
99
43
|
val now = System.currentTimeMillis()
|
|
100
|
-
|
|
101
|
-
val info = existing ?: ViewLifecycleInfo(view)
|
|
102
|
-
info.customEvents[eventType] = now
|
|
103
|
-
info
|
|
104
|
-
}
|
|
105
|
-
performPeriodicCleanup()
|
|
106
|
-
}
|
|
44
|
+
val fiveSecondsAgo = now - 5000
|
|
107
45
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
*/
|
|
111
|
-
fun wasRecentlyAnimated(view: View, windowMs: Long = 1500): Boolean {
|
|
112
|
-
return viewLifecycleInfo[view]?.wasRecentlyAnimated(windowMs) ?: false
|
|
113
|
-
}
|
|
46
|
+
val iterator = animatedViews.iterator()
|
|
47
|
+
var clearedCount = 0
|
|
114
48
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
49
|
+
while (iterator.hasNext()) {
|
|
50
|
+
val entry = iterator.next()
|
|
51
|
+
if (entry.value.time < fiveSecondsAgo) {
|
|
52
|
+
iterator.remove()
|
|
53
|
+
clearedCount++
|
|
54
|
+
}
|
|
55
|
+
}
|
|
121
56
|
|
|
122
|
-
|
|
123
|
-
* Check if a view was recently mounted
|
|
124
|
-
*/
|
|
125
|
-
fun wasRecentlyMounted(view: View, windowMs: Long = 5000): Boolean {
|
|
126
|
-
return viewLifecycleInfo[view]?.wasRecentlyMounted(windowMs) ?: false
|
|
57
|
+
Log.d(LOG_TAG, "Cleared $clearedCount animated views older than 5s, ${animatedViews.size} remaining")
|
|
127
58
|
}
|
|
128
59
|
|
|
129
60
|
/**
|
|
130
|
-
*
|
|
61
|
+
* Mark a view as currently animating
|
|
131
62
|
*/
|
|
132
|
-
fun
|
|
133
|
-
|
|
63
|
+
fun markAnimated(view: View) {
|
|
64
|
+
val now = Date()
|
|
65
|
+
animatedViews[view] = now
|
|
66
|
+
Log.d(LOG_TAG, "View animating: ${view.javaClass.simpleName} at $now")
|
|
134
67
|
}
|
|
135
68
|
|
|
136
69
|
/**
|
|
137
|
-
*
|
|
70
|
+
* Mark a custom event on a view (e.g., specific animated properties)
|
|
138
71
|
*/
|
|
139
|
-
fun
|
|
140
|
-
|
|
72
|
+
fun markCustomEvent(view: View, event: String) {
|
|
73
|
+
val now = Date()
|
|
74
|
+
customEvents.computeIfAbsent(view) { mutableListOf() }.add(event to now)
|
|
75
|
+
Log.d(LOG_TAG, "Custom event '$event' on view: ${view.javaClass.simpleName} at $now")
|
|
141
76
|
}
|
|
142
77
|
|
|
143
78
|
/**
|
|
144
|
-
* Get
|
|
79
|
+
* Get animation metadata for a view
|
|
145
80
|
*/
|
|
146
|
-
fun
|
|
147
|
-
|
|
148
|
-
|
|
81
|
+
fun getAnimationMetadata(view: View): AnimationMetadata? {
|
|
82
|
+
val mounted = mountedViews[view]
|
|
83
|
+
val updated = updatedViews[view]
|
|
84
|
+
val animated = animatedViews[view]
|
|
85
|
+
val events = customEvents[view] ?: emptyList()
|
|
149
86
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
fun getRecentlyUpdatedViews(windowMs: Long = 1000): List<View> {
|
|
154
|
-
return viewLifecycleInfo.keys.filter { wasRecentlyUpdated(it, windowMs) }
|
|
155
|
-
}
|
|
87
|
+
if (mounted == null && updated == null && animated == null && events.isEmpty()) {
|
|
88
|
+
return null
|
|
89
|
+
}
|
|
156
90
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
91
|
+
return AnimationMetadata(
|
|
92
|
+
mounted = mounted,
|
|
93
|
+
updated = updated,
|
|
94
|
+
animated = animated,
|
|
95
|
+
events = events
|
|
96
|
+
)
|
|
162
97
|
}
|
|
163
98
|
|
|
164
99
|
/**
|
|
165
|
-
*
|
|
100
|
+
* Clear all data (useful for testing)
|
|
166
101
|
*/
|
|
167
|
-
fun
|
|
168
|
-
|
|
102
|
+
fun clear() {
|
|
103
|
+
mountedViews.clear()
|
|
104
|
+
updatedViews.clear()
|
|
105
|
+
animatedViews.clear()
|
|
106
|
+
customEvents.clear()
|
|
107
|
+
Log.d(LOG_TAG, "ViewLifecycleRegistry cleared")
|
|
169
108
|
}
|
|
170
109
|
|
|
171
110
|
/**
|
|
172
|
-
* Get
|
|
111
|
+
* Get all currently animated views
|
|
173
112
|
*/
|
|
174
|
-
fun
|
|
175
|
-
val totalViews = viewLifecycleInfo.size
|
|
176
|
-
val recentlyAnimated = getRecentlyAnimatedViews().size
|
|
177
|
-
val recentlyUpdated = getRecentlyUpdatedViews().size
|
|
178
|
-
val recentlyMounted = getRecentlyMountedViews().size
|
|
179
|
-
|
|
180
|
-
return mapOf(
|
|
181
|
-
"totalViews" to totalViews,
|
|
182
|
-
"recentlyAnimated" to recentlyAnimated,
|
|
183
|
-
"recentlyUpdated" to recentlyUpdated,
|
|
184
|
-
"recentlyMounted" to recentlyMounted
|
|
185
|
-
)
|
|
186
|
-
}
|
|
113
|
+
fun getAnimatedViews(): Map<View, Date> = animatedViews.toMap()
|
|
187
114
|
|
|
188
115
|
/**
|
|
189
|
-
*
|
|
116
|
+
* Check if a view is currently animating
|
|
190
117
|
*/
|
|
191
|
-
fun
|
|
192
|
-
|
|
193
|
-
}
|
|
118
|
+
fun isAnimating(view: View): Boolean = animatedViews.containsKey(view)
|
|
119
|
+
}
|
|
194
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Data class to hold animation metadata for a view
|
|
123
|
+
*/
|
|
124
|
+
data class AnimationMetadata(
|
|
125
|
+
val mounted: Date?,
|
|
126
|
+
val updated: Date?,
|
|
127
|
+
val animated: Date?,
|
|
128
|
+
val events: List<Pair<String, Date>>
|
|
129
|
+
) {
|
|
195
130
|
/**
|
|
196
|
-
*
|
|
131
|
+
* Calculate time since animation started in milliseconds
|
|
197
132
|
*/
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
val lastCleanup = lastCleanupTime.get()
|
|
201
|
-
|
|
202
|
-
if (now - lastCleanup > cleanupIntervalMs &&
|
|
203
|
-
lastCleanupTime.compareAndSet(lastCleanup, now)) {
|
|
204
|
-
|
|
205
|
-
// Remove views that are no longer valid or haven't been accessed recently
|
|
206
|
-
val iterator = viewLifecycleInfo.iterator()
|
|
207
|
-
while (iterator.hasNext()) {
|
|
208
|
-
val entry = iterator.next()
|
|
209
|
-
val view = entry.key
|
|
210
|
-
val info = entry.value
|
|
211
|
-
|
|
212
|
-
// Remove if view is no longer valid or hasn't been accessed in 5 minutes
|
|
213
|
-
if (!isViewValid(view) || (now - info.lastAnimateTime > 300_000 &&
|
|
214
|
-
now - info.lastUpdateTime > 300_000 &&
|
|
215
|
-
now - info.mountTime > 300_000)) {
|
|
216
|
-
iterator.remove()
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
133
|
+
fun getAnimationDurationMs(): Long? {
|
|
134
|
+
return animated?.let { System.currentTimeMillis() - it.time }
|
|
220
135
|
}
|
|
221
136
|
|
|
222
137
|
/**
|
|
223
|
-
*
|
|
138
|
+
* Calculate time since last update in milliseconds
|
|
224
139
|
*/
|
|
225
|
-
|
|
226
|
-
return
|
|
227
|
-
view.javaClass // Try to access the view
|
|
228
|
-
true
|
|
229
|
-
} catch (e: Exception) {
|
|
230
|
-
false
|
|
231
|
-
}
|
|
140
|
+
fun getUpdateDurationMs(): Long? {
|
|
141
|
+
return updated?.let { System.currentTimeMillis() - it.time }
|
|
232
142
|
}
|
|
233
143
|
}
|
|
@@ -6,9 +6,11 @@ import androidx.annotation.UiThread
|
|
|
6
6
|
import androidx.test.espresso.IdlingResource.ResourceCallback
|
|
7
7
|
import com.facebook.react.animated.NativeAnimatedModule
|
|
8
8
|
import com.facebook.react.animated.NativeAnimatedNodesManager
|
|
9
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
9
10
|
import com.facebook.react.bridge.ReactContext
|
|
10
11
|
import com.wix.detox.common.DetoxErrors
|
|
11
12
|
import com.wix.detox.common.DetoxLog.Companion.LOG_TAG
|
|
13
|
+
import com.wix.detox.inquiry.FabricAnimationsInquirer
|
|
12
14
|
import com.wix.detox.reactnative.ReactNativeInfo
|
|
13
15
|
import com.wix.detox.reactnative.idlingresources.DetoxIdlingResource
|
|
14
16
|
import kotlin.reflect.KProperty1
|
|
@@ -34,6 +36,9 @@ class AnimatedModuleIdlingResource(private val reactContext: ReactContext) : Det
|
|
|
34
36
|
|
|
35
37
|
if (animatedModule.hasQueuedAnimations() ||
|
|
36
38
|
animatedModule.hasActiveAnimations()) {
|
|
39
|
+
if (reactContext is ReactApplicationContext) {
|
|
40
|
+
FabricAnimationsInquirer.logAnimatingViews(reactContext)
|
|
41
|
+
}
|
|
37
42
|
Choreographer.getInstance().postFrameCallback(this)
|
|
38
43
|
return false
|
|
39
44
|
}
|
|
@@ -115,7 +120,7 @@ class OperationsQueueReflected(private val operationsQueue: Any) {
|
|
|
115
120
|
isEmptyMethod.isAccessible = true
|
|
116
121
|
return isEmptyMethod.call(operationsQueue) as Boolean
|
|
117
122
|
}
|
|
118
|
-
|
|
123
|
+
|
|
119
124
|
// Fallback to property (works in debug builds for RN 0.80+)
|
|
120
125
|
val isEmptyProperty = operationsQueue::class.memberProperties.find { it.name == "isEmpty" }
|
|
121
126
|
if (isEmptyProperty != null) {
|
|
@@ -123,7 +128,7 @@ class OperationsQueueReflected(private val operationsQueue: Any) {
|
|
|
123
128
|
@Suppress("UNCHECKED_CAST")
|
|
124
129
|
return (isEmptyProperty as KProperty1<Any, *>).get(operationsQueue) as Boolean
|
|
125
130
|
}
|
|
126
|
-
|
|
131
|
+
|
|
127
132
|
throw DetoxErrors.DetoxIllegalStateException("isEmpty method/property cannot be reached")
|
|
128
133
|
}
|
|
129
134
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
package com.wix.detox.reactnative.idlingresources.uimodule.fabric
|
|
2
2
|
|
|
3
|
-
import android.util.Log
|
|
4
3
|
import android.view.Choreographer
|
|
5
4
|
import androidx.test.espresso.IdlingResource
|
|
6
5
|
import com.facebook.react.bridge.ReactContext
|
|
7
6
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
8
7
|
import com.facebook.react.uimanager.common.UIManagerType
|
|
9
8
|
import com.wix.detox.reactnative.idlingresources.DetoxIdlingResource
|
|
10
|
-
import com.wix.detox.inquiry.ViewLifecycleRegistry
|
|
11
9
|
import org.joor.Reflect
|
|
12
10
|
import java.util.concurrent.ConcurrentLinkedQueue
|
|
13
11
|
|
|
@@ -17,15 +15,10 @@ class FabricUIManagerIdlingResources(
|
|
|
17
15
|
) : DetoxIdlingResource(), Choreographer.FrameCallback {
|
|
18
16
|
|
|
19
17
|
override fun checkIdle(): Boolean {
|
|
20
|
-
|
|
21
|
-
val viewCommandMountItemsSize = getViewCommandMountItemsSize()
|
|
22
|
-
|
|
23
|
-
return if (mountItemsSize == 0 && viewCommandMountItemsSize == 0) {
|
|
18
|
+
return if (getViewCommandMountItemsSize() == 0 && getMountItemsSize() == 0) {
|
|
24
19
|
notifyIdle()
|
|
25
20
|
true
|
|
26
21
|
} else {
|
|
27
|
-
// Track mount items to identify animated views
|
|
28
|
-
trackMountItems()
|
|
29
22
|
Choreographer.getInstance().postFrameCallback(this)
|
|
30
23
|
false
|
|
31
24
|
}
|
|
@@ -74,166 +67,4 @@ class FabricUIManagerIdlingResources(
|
|
|
74
67
|
return viewCommandMountItems.size
|
|
75
68
|
}
|
|
76
69
|
|
|
77
|
-
/**
|
|
78
|
-
* Track mount items to identify animated views.
|
|
79
|
-
* This is called when the UI manager is busy processing mount items.
|
|
80
|
-
*/
|
|
81
|
-
private fun trackMountItems() {
|
|
82
|
-
try {
|
|
83
|
-
val mountItemDispatcher = getMountItemDispatcher()
|
|
84
|
-
val mountItems = Reflect.on(mountItemDispatcher).field("mMountItems").get<ConcurrentLinkedQueue<*>>()
|
|
85
|
-
|
|
86
|
-
// Track each mount item to identify animated views
|
|
87
|
-
mountItems.forEach { mountItem ->
|
|
88
|
-
trackMountItem(mountItem)
|
|
89
|
-
}
|
|
90
|
-
} catch (e: Exception) {
|
|
91
|
-
// Silently ignore errors to avoid breaking the idling resource
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Track individual mount item to identify animated views.
|
|
97
|
-
*/
|
|
98
|
-
private fun trackMountItem(mountItem: Any) {
|
|
99
|
-
try {
|
|
100
|
-
val mountItemClass = mountItem.javaClass.simpleName
|
|
101
|
-
Log.i("DetoxFabricDebug", "Processing mount item: $mountItemClass")
|
|
102
|
-
|
|
103
|
-
when (mountItemClass) {
|
|
104
|
-
"IntBufferBatchMountItem" -> {
|
|
105
|
-
Log.i("DetoxFabricDebug", "Found IntBufferBatchMountItem - processing animated props")
|
|
106
|
-
// This is where animated props get applied
|
|
107
|
-
trackIntBufferBatchMountItem(mountItem)
|
|
108
|
-
}
|
|
109
|
-
"CreateMountItem" -> {
|
|
110
|
-
Log.i("DetoxFabricDebug", "Found CreateMountItem - processing view creation")
|
|
111
|
-
// Track view creation
|
|
112
|
-
trackCreateMountItem(mountItem)
|
|
113
|
-
}
|
|
114
|
-
else -> {
|
|
115
|
-
Log.i("DetoxFabricDebug", "Unknown mount item type: $mountItemClass")
|
|
116
|
-
}
|
|
117
|
-
// Add other mount item types as needed
|
|
118
|
-
}
|
|
119
|
-
} catch (e: Exception) {
|
|
120
|
-
Log.e("DetoxFabricDebug", "Error processing mount item", e)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Track IntBufferBatchMountItem which contains animated prop updates.
|
|
126
|
-
*/
|
|
127
|
-
private fun trackIntBufferBatchMountItem(mountItem: Any) {
|
|
128
|
-
try {
|
|
129
|
-
// Use reflection to access the mount item's data
|
|
130
|
-
val intBuffer = Reflect.on(mountItem).field("mIntBuffer").get<IntArray>()
|
|
131
|
-
val objBuffer = Reflect.on(mountItem).field("mObjBuffer").get<Array<Any>>()
|
|
132
|
-
|
|
133
|
-
var i = 0
|
|
134
|
-
var j = 0
|
|
135
|
-
|
|
136
|
-
while (i < intBuffer.size) {
|
|
137
|
-
val instruction = intBuffer[i++]
|
|
138
|
-
|
|
139
|
-
when (instruction) {
|
|
140
|
-
32 -> { // INSTRUCTION_UPDATE_PROPS
|
|
141
|
-
val viewTag = intBuffer[i++]
|
|
142
|
-
val props = objBuffer[j++] as? com.facebook.react.bridge.ReadableMap
|
|
143
|
-
|
|
144
|
-
// Track animated view update
|
|
145
|
-
trackAnimatedViewUpdate(viewTag, props)
|
|
146
|
-
}
|
|
147
|
-
2 -> { // INSTRUCTION_CREATE
|
|
148
|
-
val viewTag = intBuffer[i++]
|
|
149
|
-
// Mark as mounted
|
|
150
|
-
val fabricUIManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.FABRIC)
|
|
151
|
-
val view = getViewByTag(fabricUIManager as Any, viewTag)
|
|
152
|
-
view?.let { ViewLifecycleRegistry.markMounted(it) }
|
|
153
|
-
}
|
|
154
|
-
8 -> { // INSTRUCTION_INSERT
|
|
155
|
-
val parentTag = intBuffer[i++]
|
|
156
|
-
val viewTag = intBuffer[i++]
|
|
157
|
-
val index = intBuffer[i++]
|
|
158
|
-
// Mark as mounted
|
|
159
|
-
val fabricUIManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.FABRIC)
|
|
160
|
-
val view = getViewByTag(fabricUIManager as Any, viewTag)
|
|
161
|
-
view?.let { ViewLifecycleRegistry.markMounted(it) }
|
|
162
|
-
}
|
|
163
|
-
16 -> { // INSTRUCTION_REMOVE
|
|
164
|
-
val parentTag = intBuffer[i++]
|
|
165
|
-
val viewTag = intBuffer[i++]
|
|
166
|
-
val index = intBuffer[i++]
|
|
167
|
-
// View is being removed, no need to track
|
|
168
|
-
}
|
|
169
|
-
128 -> { // INSTRUCTION_UPDATE_LAYOUT
|
|
170
|
-
val viewTag = intBuffer[i++]
|
|
171
|
-
// Mark as updated
|
|
172
|
-
val fabricUIManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.FABRIC)
|
|
173
|
-
val view = getViewByTag(fabricUIManager as Any, viewTag)
|
|
174
|
-
view?.let { ViewLifecycleRegistry.markUpdated(it) }
|
|
175
|
-
}
|
|
176
|
-
// Skip other instruction types
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
} catch (e: Exception) {
|
|
180
|
-
// Silently ignore errors to avoid breaking the idling resource
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Track CreateMountItem for view creation.
|
|
186
|
-
*/
|
|
187
|
-
private fun trackCreateMountItem(mountItem: Any) {
|
|
188
|
-
try {
|
|
189
|
-
val viewTag = Reflect.on(mountItem).field("mReactTag").get<Int>()
|
|
190
|
-
// We can't get the actual View here, but we can track the tag
|
|
191
|
-
// The actual View will be available when it's mounted
|
|
192
|
-
} catch (e: Exception) {
|
|
193
|
-
// Silently ignore errors
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Track animated view update.
|
|
199
|
-
*/
|
|
200
|
-
private fun trackAnimatedViewUpdate(viewTag: Int, props: com.facebook.react.bridge.ReadableMap?) {
|
|
201
|
-
try {
|
|
202
|
-
// Get the actual Android View
|
|
203
|
-
val fabricUIManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.FABRIC)
|
|
204
|
-
val view = getViewByTag(fabricUIManager as Any, viewTag)
|
|
205
|
-
|
|
206
|
-
if (view != null) {
|
|
207
|
-
com.wix.detox.inquiry.DetoxFabricAnimationHook.hookSynchronouslyUpdateViewOnUIThread(viewTag, props, fabricUIManager as com.facebook.react.fabric.FabricUIManager)
|
|
208
|
-
}
|
|
209
|
-
} catch (e: Exception) {
|
|
210
|
-
// Silently ignore errors to avoid breaking the idling resource
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Get Android View by React Native view tag using correct Fabric APIs.
|
|
216
|
-
*/
|
|
217
|
-
private fun getViewByTag(fabricUIManager: Any, viewTag: Int): android.view.View? {
|
|
218
|
-
return try {
|
|
219
|
-
// Get MountingManager from FabricUIManager
|
|
220
|
-
val mountingManager = Reflect.on(fabricUIManager).field("mMountingManager").get<Any>()
|
|
221
|
-
|
|
222
|
-
// Get SurfaceMountingManager for the view
|
|
223
|
-
val getSurfaceManagerMethod = mountingManager.javaClass.getMethod("getSurfaceManagerForView", Int::class.java)
|
|
224
|
-
val surfaceMountingManager = getSurfaceManagerMethod.invoke(mountingManager, viewTag)
|
|
225
|
-
|
|
226
|
-
if (surfaceMountingManager != null) {
|
|
227
|
-
// Get the actual Android View
|
|
228
|
-
val getViewMethod = surfaceMountingManager.javaClass.getMethod("getView", Int::class.java)
|
|
229
|
-
getViewMethod.invoke(surfaceMountingManager, viewTag) as? android.view.View
|
|
230
|
-
} else {
|
|
231
|
-
null
|
|
232
|
-
}
|
|
233
|
-
} catch (e: Exception) {
|
|
234
|
-
null
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
70
|
}
|
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.2",
|
|
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": "e8582f26e8814a014881f63334fa04d1462fa7d2"
|
|
124
124
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
bdf9863f8c4afcf4541218e9b3bed4a3
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
760a64eb6cdd8807b45b398d53de460c48df99a5
|
package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.sha256
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
8e42be451e2bd115b75216749965d28317bddb694a2e880adab3abb709ba1d08
|
package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.sha512
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
5cb65dc5c27e0964f4fd77c1cb638cd071b43db67100cae210c0205ea2e87b3268bfb8543c06a8baf10aa748d4eb49302db99390464b639bc4e9c8a2eeabd278
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1a39cefde2e8dde2ca54d70b0c5e5b0d
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
39e35796a4308c0a15457b1613b96b090f721432
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
76090af6a690fc00138ac3c3a307fe3cc49dfde32789bbc2d8066fa53e67805b
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
12b0c9b12325ad848ba52cdbbea6a37d837b341d5f98d6c4c34c45f357cebbfc767466df76479ebde2f1546cdc2211cfe02a3bf383371ac24b1e3de43df568b5
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
aae3ffc3cd9babca3385c2de23ca2be0
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
b6de9c93a0a4fdc91a1e1335dc19a90d6fe592bd
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
305c59339a78c67c7fa2b6ab908c7f455d6faeb039827af94e5426238d4f464e
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3e6d2c931c52d22342ae2c2cbc0cbd4c38c74e3240a48d95cfe69a1561eb76337dbfdd4dd2753414c19f02b42e16c1fa8f2965efe9fffafce9122cd798eecf22
|