detox 20.44.0-smoke.1 → 20.44.0-smoke.3

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.
Files changed (47) hide show
  1. package/Detox-android/com/wix/detox/{20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar → 20.44.0-smoke.3/detox-20.44.0-smoke.3-sources.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3-sources.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3-sources.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3-sources.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3-sources.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3.aar +0 -0
  7. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3.aar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3.aar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3.aar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3.aar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/{20.44.0-smoke.1/detox-20.44.0-smoke.1.pom → 20.44.0-smoke.3/detox-20.44.0-smoke.3.pom} +1 -1
  12. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3.pom.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3.pom.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3.pom.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.44.0-smoke.3/detox-20.44.0-smoke.3.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/src/full/java/com/wix/detox/adapters/server/DetoxActionHandlers.kt +1 -7
  25. package/android/detox/src/full/java/com/wix/detox/espresso/hierarchy/ViewHierarchyGenerator.kt +14 -63
  26. package/android/detox/src/full/java/com/wix/detox/inquiry/FabricAnimationsInquirer.kt +440 -0
  27. package/android/detox/src/full/java/com/wix/detox/inquiry/ViewLifecycleRegistry.kt +83 -173
  28. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/animations/AnimatedModuleIdlingResource.kt +7 -2
  29. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/fabric/FabricUIManagerIdlingResources.kt +1 -170
  30. package/package.json +2 -2
  31. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.md5 +0 -1
  32. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.sha1 +0 -1
  33. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.sha256 +0 -1
  34. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1-sources.jar.sha512 +0 -1
  35. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar +0 -0
  36. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar.md5 +0 -1
  37. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar.sha1 +0 -1
  38. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar.sha256 +0 -1
  39. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.aar.sha512 +0 -1
  40. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.pom.md5 +0 -1
  41. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.pom.sha1 +0 -1
  42. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.pom.sha256 +0 -1
  43. package/Detox-android/com/wix/detox/20.44.0-smoke.1/detox-20.44.0-smoke.1.pom.sha512 +0 -1
  44. package/android/detox/src/full/java/com/wix/detox/inquiry/DetoxAnimationTracker.kt +0 -70
  45. package/android/detox/src/full/java/com/wix/detox/inquiry/DetoxFabricAnimationHook.kt +0 -83
  46. package/android/detox/src/full/java/com/wix/detox/inquiry/DetoxFabricIntegration.kt +0 -99
  47. 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.concurrent.atomic.AtomicLong
6
+ import java.util.Date
6
7
 
7
8
  /**
8
- * Registry to track various lifecycle events of Android Views.
9
- * Uses WeakHashMap to prevent memory leaks and allows tracking of:
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 viewLifecycleInfo = ConcurrentHashMap<View, ViewLifecycleInfo>()
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
- * Data class to hold lifecycle information for a view
22
- */
23
- data class ViewLifecycleInfo(
24
- val view: View,
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 = System.currentTimeMillis()
58
- viewLifecycleInfo.compute(view) { _, existing ->
59
- existing?.copy(mountTime = now) ?: ViewLifecycleInfo(view, mountTime = now)
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 animated
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 = System.currentTimeMillis()
85
- viewLifecycleInfo.compute(view) { _, existing ->
86
- val info = existing ?: ViewLifecycleInfo(view)
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
- * Mark a custom event for a view
40
+ * Clear animated views older than 5 seconds (called at start of each inquiry)
97
41
  */
98
- fun markCustomEvent(view: View, eventType: String) {
42
+ fun clearAnimatedViews() {
99
43
  val now = System.currentTimeMillis()
100
- viewLifecycleInfo.compute(view) { _, existing ->
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
- * Check if a view was recently animated
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
- * Check if a view was recently updated
117
- */
118
- fun wasRecentlyUpdated(view: View, windowMs: Long = 1000): Boolean {
119
- return viewLifecycleInfo[view]?.wasRecentlyUpdated(windowMs) ?: false
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
- * Check if a view has a custom event within the time window
61
+ * Mark a view as currently animating
131
62
  */
132
- fun hasCustomEvent(view: View, eventType: String, windowMs: Long = 2000): Boolean {
133
- return viewLifecycleInfo[view]?.hasCustomEvent(eventType, windowMs) ?: false
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
- * Get lifecycle info for a view
70
+ * Mark a custom event on a view (e.g., specific animated properties)
138
71
  */
139
- fun getLifecycleInfo(view: View): ViewLifecycleInfo? {
140
- return viewLifecycleInfo[view]
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 all views that were recently animated
79
+ * Get animation metadata for a view
145
80
  */
146
- fun getRecentlyAnimatedViews(windowMs: Long = 1500): List<View> {
147
- return viewLifecycleInfo.keys.filter { wasRecentlyAnimated(it, windowMs) }
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
- * Get all views that were recently updated
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
- * Get all views that were recently mounted
159
- */
160
- fun getRecentlyMountedViews(windowMs: Long = 5000): List<View> {
161
- return viewLifecycleInfo.keys.filter { wasRecentlyMounted(it, windowMs) }
91
+ return AnimationMetadata(
92
+ mounted = mounted,
93
+ updated = updated,
94
+ animated = animated,
95
+ events = events
96
+ )
162
97
  }
163
98
 
164
99
  /**
165
- * Get all views with a specific custom event
100
+ * Clear all data (useful for testing)
166
101
  */
167
- fun getViewsWithCustomEvent(eventType: String, windowMs: Long = 2000): List<View> {
168
- return viewLifecycleInfo.keys.filter { hasCustomEvent(it, eventType, windowMs) }
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 statistics about the registry
111
+ * Get all currently animated views
173
112
  */
174
- fun getStats(): Map<String, Any> {
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
- * Clear all data (useful for testing)
116
+ * Check if a view is currently animating
190
117
  */
191
- fun clear() {
192
- viewLifecycleInfo.clear()
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
- * Perform periodic cleanup to remove stale entries
131
+ * Calculate time since animation started in milliseconds
197
132
  */
198
- private fun performPeriodicCleanup() {
199
- val now = System.currentTimeMillis()
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
- * Check if a view is still valid (not garbage collected)
138
+ * Calculate time since last update in milliseconds
224
139
  */
225
- private fun isViewValid(view: View): Boolean {
226
- return try {
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
- val mountItemsSize = getMountItemsSize()
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.1",
4
+ "version": "20.44.0-smoke.3",
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": "5e0b38a97eccb64d73369b1a3ccbd9c31ecde284"
123
+ "gitHead": "69e5e1f5fadd16c25e4303c7bda877f556527c5e"
124
124
  }
@@ -1 +0,0 @@
1
- bdf9863f8c4afcf4541218e9b3bed4a3
@@ -1 +0,0 @@
1
- 760a64eb6cdd8807b45b398d53de460c48df99a5
@@ -1 +0,0 @@
1
- 8e42be451e2bd115b75216749965d28317bddb694a2e880adab3abb709ba1d08
@@ -1 +0,0 @@
1
- 5cb65dc5c27e0964f4fd77c1cb638cd071b43db67100cae210c0205ea2e87b3268bfb8543c06a8baf10aa748d4eb49302db99390464b639bc4e9c8a2eeabd278
@@ -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