* iOS

* Android

* update

* RN -0.84 android

* update for android

* test update for hardcoded height - from 412 to 394

* revert test update

* all tests were running on latest RN instead of the right onw

* yarn change

* pod install update to run only once

* patch for codegen for Android webview

* some minor changes in regex and iOS install script

* regex update

* test update for android

* another test update for android

* very flaky test removed

* removal of rules

* reverted the removed test

* API 36 fix for button test

* safety added to BridgeIdlingResource.kt
This commit is contained in:
mark.dev
2026-04-19 09:29:53 +03:00
committed by GitHub
parent 428e458935
commit b8e1c7ebb1
36 changed files with 417 additions and 186 deletions
@@ -5,7 +5,6 @@
env: env:
REACT_NATIVE_VERSION: 0.83.0 REACT_NATIVE_VERSION: 0.83.0
RCT_NEW_ARCH_ENABLED: 1 RCT_NEW_ARCH_ENABLED: 1
REACT_NATIVE_COMPAT_TEST: true # Only set 'true' in jobs with the latest supported RN
DETOX_DISABLE_POD_INSTALL: true DETOX_DISABLE_POD_INSTALL: true
DETOX_DISABLE_POSTINSTALL: true DETOX_DISABLE_POSTINSTALL: true
artifact_paths: artifact_paths:
@@ -0,0 +1,13 @@
- label: ":android::react: RN .84 + Android: Demo app"
command:
- "nvm install"
- "./scripts/demo-projects.android.sh"
env:
REACT_NATIVE_VERSION: 0.84.0
RCT_NEW_ARCH_ENABLED: 1
REACT_NATIVE_COMPAT_TEST: true
DETOX_DISABLE_POD_INSTALL: true
DETOX_DISABLE_POSTINSTALL: true
artifact_paths:
- "/Users/builder/uibuilder/work/coverage/**/*.lcov"
- "/Users/builder/uibuilder/work/artifacts*.tar.gz"
@@ -0,0 +1,13 @@
- label: ":android::detox: RN .84 + Android: Tests app"
command:
- "nvm install"
- "./scripts/ci.android.sh"
env:
REACT_NATIVE_VERSION: 0.84.0
RCT_NEW_ARCH_ENABLED: 1
DETOX_DISABLE_POD_INSTALL: true
DETOX_DISABLE_POSTINSTALL: true
artifact_paths:
- "/Users/builder/uibuilder/work/coverage/**/*.lcov"
- "/Users/builder/uibuilder/work/**/allure-report-*.html"
- "/Users/builder/uibuilder/work/artifacts*.tar.gz"
@@ -5,7 +5,6 @@
env: env:
REACT_NATIVE_VERSION: 0.83.0 REACT_NATIVE_VERSION: 0.83.0
RCT_NEW_ARCH_ENABLED: 1 RCT_NEW_ARCH_ENABLED: 1
REACT_NATIVE_COMPAT_TEST: true # Only set 'true' in jobs with the latest supported RN
artifact_paths: artifact_paths:
- "/Users/builder/uibuilder/work/coverage/**/*.lcov" - "/Users/builder/uibuilder/work/coverage/**/*.lcov"
- "/Users/builder/uibuilder/work/artifacts*.tar.gz" - "/Users/builder/uibuilder/work/artifacts*.tar.gz"
@@ -0,0 +1,11 @@
- label: ":ios::react: RN .84 + iOS: Demo app"
command:
- "nvm install"
- "./scripts/demo-projects.ios.sh"
env:
REACT_NATIVE_VERSION: 0.84.0
RCT_NEW_ARCH_ENABLED: 1
REACT_NATIVE_COMPAT_TEST: true
artifact_paths:
- "/Users/builder/uibuilder/work/coverage/**/*.lcov"
- "/Users/builder/uibuilder/work/artifacts*.tar.gz"
+11
View File
@@ -0,0 +1,11 @@
- label: ":ios::detox: RN .84 + iOS: Tests app"
command:
- "nvm install"
- "./scripts/ci.ios.sh"
env:
REACT_NATIVE_VERSION: 0.84.0
RCT_NEW_ARCH_ENABLED: 1
artifact_paths:
- "/Users/builder/uibuilder/work/coverage/**/*.lcov"
- "/Users/builder/uibuilder/work/**/allure-report-*.html"
- "/Users/builder/uibuilder/work/artifacts*.tar.gz"
+4 -2
View File
@@ -2,10 +2,12 @@
echo "steps:" echo "steps:"
cat .buildkite/jobs/pipeline.android_demo_app_rn_83.yml cat .buildkite/jobs/pipeline.android_demo_app_rn_84.yml
cat .buildkite/jobs/pipeline.android_rn_77.yml cat .buildkite/jobs/pipeline.android_rn_77.yml
cat .buildkite/jobs/pipeline.android_rn_83.yml cat .buildkite/jobs/pipeline.android_rn_83.yml
cat .buildkite/jobs/pipeline.ios_demo_app_rn_83.yml cat .buildkite/jobs/pipeline.android_rn_84.yml
cat .buildkite/jobs/pipeline.ios_demo_app_rn_84.yml
cat .buildkite/jobs/pipeline.ios_rn_77.yml cat .buildkite/jobs/pipeline.ios_rn_77.yml
cat .buildkite/jobs/pipeline.ios_rn_83.yml cat .buildkite/jobs/pipeline.ios_rn_83.yml
cat .buildkite/jobs/pipeline.ios_rn_84.yml
cat .buildkite/pipeline.post_processing.yml cat .buildkite/pipeline.post_processing.yml
-8
View File
@@ -1,8 +0,0 @@
---
description: Rules to help Cursor write code that is more Detox idomatic
globs: *.js
alwaysApply: false
---
- Whenever an Error needs to be thrown, prefer throwing a DetoxRuntimeError with a proper message and hint.
- Minimize comments usage, especially in-line ones. Leave comments where things are exceptionally unclear for some reason.
-62
View File
@@ -1,62 +0,0 @@
---
Description: Rules to help Cursor write unit tests code that aligns with the project's standards
globs: *.test.js,*.test.ts
alwaysApply: false
---
# Project Rules
## Jest Unit Testing
In Jest unit test files, **never perform mocking globally**. Instead, perform all mocks inside the `beforeEach()` section.
### Rationale
- Keeps test setup centralized and visible
- Ensures mocks are reset between tests
- Makes test dependencies explicit
- Prevents test pollution from global mocks
### Example
❌ **Don't do this:**
```javascript
const mockSleep = jest.fn().mockResolvedValue(undefined);
jest.mock('../../../../utils/sleep', () => mockSleep);
describe('MyClass', () => {
// ...
});
```
✅ **Do this instead:**
```javascript
describe('MyClass', () => {
let mockSleep;
beforeEach(() => {
jest.mock('../../../../utils/sleep');
mockSleep = require('../../../../utils/sleep');
mockSleep.mockResolvedValue(undefined);
// ... other setup
});
});
```
Alternatively, in cases where jest.mock() doesn't work well out of the box, this can be performed too:
```javascript
beforeEach(() => {
jest.mock('../../../../utils/sleep', () => jest.fn().mockResolvedValue(undefined));
mockSleep = require('../../../../utils/sleep');
});
// Or if the sleep js module actually exports an object:
beforeEach(() => {
jest.mock('../../../../utils/sleep', () => ({
sleep: jest.fn().mockResolvedValue(undefined),
}));
mockSleep = require('../../../../utils/sleep');
});
```
Note: `jest.mock()` must still be called at the module level (for hoisting), but all mock configuration should happen in `beforeEach()`.
+1
View File
@@ -49,6 +49,7 @@ jspm_packages
################ ################
.idea .idea
.vscode .vscode
.cursor
## File-based project format: ## File-based project format:
*.iws *.iws
+1 -1
View File
@@ -63,7 +63,7 @@ Detox was built from the ground up to support React Native projects.
While Detox should work out of the box with any React Native version of the latest minor releases, While Detox should work out of the box with any React Native version of the latest minor releases,
the _official_ versions compatibility is provided according to the following: the _official_ versions compatibility is provided according to the following:
- **RN `v0.77.x` - `v0.83.x`:** Fully compatible with React Native's ["New Architecture"](https://reactnative.dev/docs/the-new-architecture/landing-page). - **RN `v0.77.x` - `v0.84.x`:** Fully compatible with React Native's ["New Architecture"](https://reactnative.dev/docs/the-new-architecture/landing-page).
Newer RN versions might work with Detox, but they've not been thoroughly tested by the Detox team yet. Newer RN versions might work with Detox, but they've not been thoroughly tested by the Detox team yet.
Although we do not officially support older React Native versions, we do our best to keep Detox compatible with them. Although we do not officially support older React Native versions, we do our best to keep Detox compatible with them.
@@ -14,7 +14,7 @@ fun ReactApplication.getInstanceManagerSafe(): ReactInstanceManager {
@SuppressLint("VisibleForTests") @SuppressLint("VisibleForTests")
fun ReactApplication.getCurrentReactContext(): ReactContext? { fun ReactApplication.getCurrentReactContext(): ReactContext? {
return if (isFabricEnabled()) { return if (isFabricEnabled() || ReactNativeInfo.isNewArchitectureOnlyVersion()) {
reactHost?.currentReactContext reactHost?.currentReactContext
} else { } else {
getInstanceManagerSafe().currentReactContext getInstanceManagerSafe().currentReactContext
@@ -2,10 +2,16 @@ package com.wix.detox.reactnative
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import com.facebook.react.ReactApplication import com.facebook.react.ReactApplication
import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.ReactContext
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.wix.detox.LaunchArgs import com.wix.detox.LaunchArgs
import com.wix.detox.reactnative.idlingresources.ReactNativeIdlingResources import com.wix.detox.reactnative.idlingresources.ReactNativeIdlingResources
import com.wix.detox.reactnative.reloader.ReactNativeReloaderFactory import com.wix.detox.reactnative.reloader.ReactNativeReloaderFactory
@@ -14,6 +20,7 @@ private const val LOG_TAG = "DetoxRNExt"
object ReactNativeExtension { object ReactNativeExtension {
private var rnIdlingResources: ReactNativeIdlingResources? = null private var rnIdlingResources: ReactNativeIdlingResources? = null
private var backPressCallbackRegistered = false
fun initIfNeeded() { fun initIfNeeded() {
if (!ReactNativeInfo.isReactNativeApp()) { if (!ReactNativeInfo.isReactNativeApp()) {
@@ -37,6 +44,7 @@ object ReactNativeExtension {
awaitNewReactNativeContext(it, null) awaitNewReactNativeContext(it, null)
enableOrDisableSynchronization(it) enableOrDisableSynchronization(it)
registerBackPressCallbackIfNeeded(applicationContext, it)
} }
} }
@@ -58,6 +66,7 @@ object ReactNativeExtension {
(applicationContext as ReactApplication).let { (applicationContext as ReactApplication).let {
clearIdlingResources() clearIdlingResources()
backPressCallbackRegistered = false
val previousReactContext = it.getCurrentReactContext() val previousReactContext = it.getCurrentReactContext()
@@ -65,6 +74,7 @@ object ReactNativeExtension {
awaitNewReactNativeContext(it, previousReactContext) awaitNewReactNativeContext(it, previousReactContext)
enableOrDisableSynchronization(it) enableOrDisableSynchronization(it)
registerBackPressCallbackIfNeeded(applicationContext, it)
} }
} }
@@ -110,6 +120,43 @@ object ReactNativeExtension {
} }
} }
/**
* On API 35+, the system routes back presses through [OnBackPressedDispatcher] instead of
* calling [Activity.onBackPressed]. Older RN versions (< 0.83) don't register an
* [OnBackPressedCallback], so the back press bypasses React Native's BackHandler entirely,
* causing the activity to finish instead of letting JS handle it.
*
* This registers a callback that routes back presses through [ReactInstanceManager.onBackPressed],
* restoring the expected behavior.
*/
private fun registerBackPressCallbackIfNeeded(context: Context, reactApp: ReactApplication) {
if (backPressCallbackRegistered) return
if (Build.VERSION.SDK_INT < 35) return
if (ReactNativeInfo.isNewArchitectureOnlyVersion()) return
val activity = getRNActivity(context) as? ComponentActivity ?: return
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
try {
val reactContext = reactApp.getCurrentReactContext()
reactContext
?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
?.emit("hardwareBackPress", null)
?: Log.w(LOG_TAG, "ReactContext is null, cannot emit hardwareBackPress")
} catch (e: Exception) {
Log.w(LOG_TAG, "Failed to emit hardwareBackPress", e)
}
}
}
backPressCallbackRegistered = true
Handler(Looper.getMainLooper()).post {
activity.onBackPressedDispatcher.addCallback(activity, callback)
Log.i(LOG_TAG, "Registered OnBackPressedCallback for legacy RN back press handling")
}
}
private fun reloadReactNativeInBackground(reactApplication: ReactApplication) { private fun reloadReactNativeInBackground(reactApplication: ReactApplication) {
val rnReloader = ReactNativeReloaderFactory(InstrumentationRegistry.getInstrumentation(), reactApplication).create() val rnReloader = ReactNativeReloaderFactory(InstrumentationRegistry.getInstrumentation(), reactApplication).create()
rnReloader.reloadInBackground() rnReloader.reloadInBackground()
@@ -12,6 +12,13 @@ object ReactNativeInfo {
@JvmStatic @JvmStatic
fun rnVersion() = rnVersion fun rnVersion() = rnVersion
@JvmStatic
fun isNewArchitectureOnlyVersion(): Boolean {
return rnVersion.run {
major > 0 || (major == 0 && minor >= 84)
}
}
fun isReactNativeApp(): Boolean = try { fun isReactNativeApp(): Boolean = try {
Class.forName("com.facebook.react.ReactApplication") Class.forName("com.facebook.react.ReactApplication")
true true
@@ -83,8 +83,8 @@ open class ReactNativeLoadingMonitor(
reactContext != null && reactContext !== previousReactContext && reactContext.hasActiveReactInstance() reactContext != null && reactContext !== previousReactContext && reactContext.hasActiveReactInstance()
private fun subscribeAsyncRNContextHandler(onReactContextInitialized: () -> Any) { private fun subscribeAsyncRNContextHandler(onReactContextInitialized: () -> Any) {
val isFabric = isFabricEnabled() val useNewArchitectureListener = isFabricEnabled() || ReactNativeInfo.isNewArchitectureOnlyVersion()
if (isFabric) { if (useNewArchitectureListener) {
// We do a casting for supporting RN 0.75 and above // We do a casting for supporting RN 0.75 and above
val host = rnApplication.reactHost as ReactHostImpl? val host = rnApplication.reactHost as ReactHostImpl?
host?.addReactInstanceEventListener(object : ReactInstanceEventListener { host?.addReactInstanceEventListener(object : ReactInstanceEventListener {
@@ -1,9 +1,11 @@
package com.wix.detox.reactnative.idlingresources.bridge package com.wix.detox.reactnative.idlingresources.bridge
import android.util.Log import android.util.Log
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener
import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.ReactContext
import com.wix.detox.reactnative.idlingresources.DetoxIdlingResource import com.wix.detox.reactnative.idlingresources.DetoxIdlingResource
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
/** /**
@@ -16,16 +18,25 @@ import java.util.concurrent.atomic.AtomicBoolean
* React Native's JS bridge. * React Native's JS bridge.
* *
*/ */
class BridgeIdlingResource(private val reactContext: ReactContext) : DetoxIdlingResource(), class BridgeIdlingResource(private val reactContext: ReactContext) : DetoxIdlingResource() {
NotThreadSafeBridgeIdleDebugListener {
private val idleNow = AtomicBoolean(true) private val idleNow = AtomicBoolean(true)
private val bridgeIdleListenerApi = BridgeIdleListenerApi(reactContext)
init { init {
reactContext.catalystInstance.addBridgeIdleDebugListener(this) bridgeIdleListenerApi.attach(
onBridgeIdle = {
idleNow.set(true)
notifyIdle()
},
onBridgeBusy = {
idleNow.set(false)
},
onBridgeDestroyed = {}
)
} }
fun onDetach() { fun onDetach() {
reactContext.catalystInstance.removeBridgeIdleDebugListener(this) bridgeIdleListenerApi.detach()
} }
override fun getName(): String { override fun getName(): String {
@@ -48,19 +59,6 @@ class BridgeIdlingResource(private val reactContext: ReactContext) : DetoxIdling
return ret return ret
} }
override fun onTransitionToBridgeIdle() {
idleNow.set(true)
notifyIdle()
}
override fun onTransitionToBridgeBusy() {
idleNow.set(false)
// Log.i(LOG_TAG, "JS Bridge transitions to busy.");
}
override fun onBridgeDestroyed() {
}
override fun onUnregistered() { override fun onUnregistered() {
super.onUnregistered() super.onUnregistered()
onDetach() onDetach()
@@ -70,3 +68,72 @@ class BridgeIdlingResource(private val reactContext: ReactContext) : DetoxIdling
private const val LOG_TAG = "Detox" private const val LOG_TAG = "Detox"
} }
} }
private class BridgeIdleListenerApi(private val reactContext: ReactContext) {
private var listenerProxy: Any? = null
private var removeListenerMethod: Method? = null
fun attach(
onBridgeIdle: () -> Unit,
onBridgeBusy: () -> Unit,
onBridgeDestroyed: () -> Unit,
) {
try {
val listenerClass = Class.forName(BRIDGE_IDLE_LISTENER_CLASS_NAME)
val catalystInstance = reactContext.catalystInstance
val addListenerMethod = catalystInstance.javaClass.methods.firstOrNull { method ->
method.name == ADD_LISTENER_METHOD && method.parameterTypes.contentEquals(arrayOf(listenerClass))
} ?: run {
Log.i(LOG_TAG, "RN bridge idle debug listener API is unavailable.")
return
}
val proxy = Proxy.newProxyInstance(
listenerClass.classLoader,
arrayOf(listenerClass),
InvocationHandler { proxy, method, args ->
when (method.name) {
ON_BRIDGE_IDLE_METHOD -> onBridgeIdle()
ON_BRIDGE_BUSY_METHOD -> onBridgeBusy()
ON_BRIDGE_DESTROYED_METHOD -> onBridgeDestroyed()
"equals" -> proxy === args?.firstOrNull()
"hashCode" -> System.identityHashCode(proxy)
"toString" -> "BridgeIdleListenerProxy"
}
}
)
addListenerMethod.invoke(catalystInstance, proxy)
listenerProxy = proxy
removeListenerMethod = catalystInstance.javaClass.methods.firstOrNull { method ->
method.name == REMOVE_LISTENER_METHOD && method.parameterTypes.contentEquals(arrayOf(listenerClass))
}
} catch (e: Throwable) {
Log.w(LOG_TAG, "Could not attach RN bridge idle debug listener.", e)
}
}
fun detach() {
val listener = listenerProxy ?: return
val removeMethod = removeListenerMethod ?: return
try {
removeMethod.invoke(reactContext.catalystInstance, listener)
} catch (e: Throwable) {
Log.w(LOG_TAG, "Could not detach RN bridge idle debug listener.", e)
} finally {
listenerProxy = null
removeListenerMethod = null
}
}
companion object {
private const val LOG_TAG = "Detox"
private const val BRIDGE_IDLE_LISTENER_CLASS_NAME =
"com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener"
private const val ADD_LISTENER_METHOD = "addBridgeIdleDebugListener"
private const val REMOVE_LISTENER_METHOD = "removeBridgeIdleDebugListener"
private const val ON_BRIDGE_IDLE_METHOD = "onTransitionToBridgeIdle"
private const val ON_BRIDGE_BUSY_METHOD = "onTransitionToBridgeBusy"
private const val ON_BRIDGE_DESTROYED_METHOD = "onBridgeDestroyed"
}
}
@@ -1,6 +1,7 @@
package com.wix.detox.reactnative.idlingresources.factory package com.wix.detox.reactnative.idlingresources.factory
import com.facebook.react.ReactApplication import com.facebook.react.ReactApplication
import com.wix.detox.reactnative.ReactNativeInfo
import com.wix.detox.reactnative.idlingresources.DetoxIdlingResource import com.wix.detox.reactnative.idlingresources.DetoxIdlingResource
import com.wix.detox.reactnative.isFabricEnabled import com.wix.detox.reactnative.isFabricEnabled
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -9,7 +10,8 @@ import kotlinx.coroutines.withContext
class DetoxIdlingResourceFactory(private val reactApplication: ReactApplication) { class DetoxIdlingResourceFactory(private val reactApplication: ReactApplication) {
suspend fun create(): Map<IdlingResourcesName, DetoxIdlingResource> = withContext(Dispatchers.Main) { suspend fun create(): Map<IdlingResourcesName, DetoxIdlingResource> = withContext(Dispatchers.Main) {
val strategy = if (isFabricEnabled()) { val useNewArchitectureStrategy = isFabricEnabled() || ReactNativeInfo.isNewArchitectureOnlyVersion()
val strategy = if (useNewArchitectureStrategy) {
FabricDetoxIdlingResourceFactoryStrategy(reactApplication) FabricDetoxIdlingResourceFactoryStrategy(reactApplication)
} else { } else {
OldArchitectureDetoxIdlingResourceFactoryStrategy(reactApplication) OldArchitectureDetoxIdlingResourceFactoryStrategy(reactApplication)
@@ -1,5 +1,6 @@
package com.wix.detox.reactnative.idlingresources.uimodule.fabric package com.wix.detox.reactnative.idlingresources.uimodule.fabric
import android.os.SystemClock
import android.view.Choreographer import android.view.Choreographer
import androidx.test.espresso.IdlingResource import androidx.test.espresso.IdlingResource
import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.ReactContext
@@ -15,14 +16,51 @@ class FabricUIManagerIdlingResources(
private val reactContext: ReactContext private val reactContext: ReactContext
) : DetoxIdlingResource(), Choreographer.FrameCallback { ) : DetoxIdlingResource(), Choreographer.FrameCallback {
private var firstBusyTimestamp: Long = 0
private var isSteadyState: Boolean = false
override fun checkIdle(): Boolean { override fun checkIdle(): Boolean {
return if (getViewCommandMountItemsSize() == 0 && getMountItemsSize() == 0) { val mountItemsCount = getMountItemsSize()
val viewCommandMountItemsCount = getViewCommandMountItemsSize()
if (mountItemsCount == 0 && viewCommandMountItemsCount == 0) {
firstBusyTimestamp = 0
isSteadyState = false
notifyIdle() notifyIdle()
true return true
} else {
Choreographer.getInstance().postFrameCallback(this)
false
} }
// Once we've determined this is a steady-state (a stuck mount item that never
// resolves), keep reporting idle as long as the count stays low.
if (isSteadyState && mountItemsCount <= 1 && viewCommandMountItemsCount == 0) {
notifyIdle()
return true
}
// Count increased beyond steady-state threshold — reset and treat as genuinely busy.
if (isSteadyState) {
isSteadyState = false
firstBusyTimestamp = 0
}
val now = SystemClock.uptimeMillis()
if (firstBusyTimestamp == 0L) {
firstBusyTimestamp = now
}
// On API 36+, edge-to-edge enforcement can cause a single mount item to remain
// permanently in the queue on older RN versions. If the count stays at 1 for over
// 1.5s, treat it as a steady-state condition rather than a genuinely busy UI.
if (now - firstBusyTimestamp >= BUSY_TOLERANCE_MS
&& mountItemsCount <= 1
&& viewCommandMountItemsCount == 0) {
isSteadyState = true
notifyIdle()
return true
}
Choreographer.getInstance().postFrameCallback(this)
return false
} }
override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) { override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
@@ -78,4 +116,7 @@ class FabricUIManagerIdlingResources(
return viewCommandMountItems.size return viewCommandMountItems.size
} }
companion object {
private const val BUSY_TOLERANCE_MS = 1500L
}
} }
@@ -2,6 +2,7 @@ package com.wix.detox.reactnative.reloader
import android.app.Instrumentation import android.app.Instrumentation
import com.facebook.react.ReactApplication import com.facebook.react.ReactApplication
import com.wix.detox.reactnative.ReactNativeInfo
import com.wix.detox.reactnative.isFabricEnabled import com.wix.detox.reactnative.isFabricEnabled
class ReactNativeReloaderFactory( class ReactNativeReloaderFactory(
@@ -11,7 +12,8 @@ class ReactNativeReloaderFactory(
fun create(): ReactNativeReLoader { fun create(): ReactNativeReLoader {
return when { return when {
isFabricEnabled() -> NewArchitectureNativeReLoader(instrumentation, rnApplication) isFabricEnabled() || ReactNativeInfo.isNewArchitectureOnlyVersion() ->
NewArchitectureNativeReLoader(instrumentation, rnApplication)
else -> OldArchReactNativeReLoader(instrumentation, rnApplication) else -> OldArchReactNativeReLoader(instrumentation, rnApplication)
} }
} }
+1
View File
@@ -48,4 +48,5 @@ ext.rnInfo = [
isRN79OrHigher: rnMajorVer >= 79, isRN79OrHigher: rnMajorVer >= 79,
isRN80OrHigher: rnMajorVer >= 80, isRN80OrHigher: rnMajorVer >= 80,
isRN81OrHigher: rnMajorVer >= 81, isRN81OrHigher: rnMajorVer >= 81,
isRN84OrHigher: rnMajorVer >= 84,
] ]
+4
View File
@@ -275,6 +275,10 @@ class Element : NSObject {
@objc @objc
var value: String? { var value: String? {
if let slider = view.dtx_sliderView {
return slider.accessibilityValue
}
return view.accessibilityValue return view.accessibilityValue
} }
+1 -1
View File
@@ -82,7 +82,7 @@
"jest-allure2-reporter": "^2.2.6", "jest-allure2-reporter": "^2.2.6",
"metro-react-native-babel-preset": "0.76.8", "metro-react-native-babel-preset": "0.76.8",
"prettier": "^3.1.1", "prettier": "^3.1.1",
"react-native": "0.83.0", "react-native": "0.84.0",
"react-native-codegen": "^0.0.8", "react-native-codegen": "^0.0.8",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"wtfnode": "^0.9.1" "wtfnode": "^0.9.1"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 81 KiB

@@ -701,7 +701,10 @@
); );
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "$(inherited)"; OTHER_CFLAGS = (
"$(inherited)",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
OTHER_CPLUSPLUSFLAGS = ( OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)", "$(OTHER_CFLAGS)",
"-DFOLLY_NO_CONFIG", "-DFOLLY_NO_CONFIG",
@@ -709,6 +712,7 @@
"-DFOLLY_USE_LIBCPP=1", "-DFOLLY_USE_LIBCPP=1",
"-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_CFG_NO_COROUTINES=1",
"-DFOLLY_HAVE_CLOCK_GETTIME=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1",
"-DRCT_REMOVE_LEGACY_ARCH=1",
); );
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
@@ -717,6 +721,7 @@
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
SWIFT_ENABLE_EXPLICIT_MODULES = NO;
USE_HERMES = true; USE_HERMES = true;
}; };
name = Debug; name = Debug;
@@ -774,7 +779,10 @@
"\"$(inherited)\"", "\"$(inherited)\"",
); );
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "$(inherited)"; OTHER_CFLAGS = (
"$(inherited)",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
OTHER_CPLUSPLUSFLAGS = ( OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)", "$(OTHER_CFLAGS)",
"-DFOLLY_NO_CONFIG", "-DFOLLY_NO_CONFIG",
@@ -782,6 +790,7 @@
"-DFOLLY_USE_LIBCPP=1", "-DFOLLY_USE_LIBCPP=1",
"-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_CFG_NO_COROUTINES=1",
"-DFOLLY_HAVE_CLOCK_GETTIME=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1",
"-DRCT_REMOVE_LEGACY_ARCH=1",
); );
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
@@ -789,6 +798,7 @@
); );
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_ENABLE_EXPLICIT_MODULES = NO;
USE_HERMES = true; USE_HERMES = true;
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
+2 -2
View File
@@ -40,8 +40,8 @@
"@react-native-picker/picker": "^2.11.2", "@react-native-picker/picker": "^2.11.2",
"@react-native-segmented-control/segmented-control": "^2.5.6", "@react-native-segmented-control/segmented-control": "^2.5.6",
"moment": "^2.30.1", "moment": "^2.30.1",
"react": "19.2.0", "react": "19.2.3",
"react-native": "0.83.0", "react-native": "0.84.0",
"react-native-launch-arguments": "^4.0.2", "react-native-launch-arguments": "^4.0.2",
"react-native-permissions": "^5.2.1", "react-native-permissions": "^5.2.1",
"react-native-webview": "^13.13.1", "react-native-webview": "^13.13.1",
@@ -0,0 +1,34 @@
const fs = require('fs');
const path = require('path');
const log = message => console.log(`[PATCH-WEBVIEW] ${message}`);
const testAppRoot = path.resolve(__dirname, '..');
const reactNativePackageJsonPath = path.join(testAppRoot, 'node_modules/react-native/package.json');
const webViewSpecPath = path.join(testAppRoot, 'node_modules/react-native-webview/src/RNCWebViewNativeComponent.ts');
if (!fs.existsSync(reactNativePackageJsonPath) || !fs.existsSync(webViewSpecPath)) {
log('react-native or react-native-webview is missing, skipping.');
process.exit(0);
}
const reactNativeVersion = JSON.parse(fs.readFileSync(reactNativePackageJsonPath, 'utf8')).version;
const [major, minor] = reactNativeVersion.split('.').map(Number);
if (major !== 0 || minor < 84) {
log(`react-native@${reactNativeVersion} does not need the WebView patch, skipping.`);
process.exit(0);
}
let webViewSpec = fs.readFileSync(webViewSpecPath, 'utf8');
const navigationTypeUnion = /navigationType:\s*\|?\s*'click'\s*\|\s*'formsubmit'\s*\|\s*'backforward'\s*\|\s*'reload'\s*\|\s*'formresubmit'\s*\|\s*'other';/g;
if (!navigationTypeUnion.test(webViewSpec)) {
log('Spec already patched or changed, skipping.');
process.exit(0);
}
navigationTypeUnion.lastIndex = 0;
log('Patching react-native-webview navigationType union → string for Android codegen compatibility...');
webViewSpec = webViewSpec.replace(navigationTypeUnion, 'navigationType: string;');
fs.writeFileSync(webViewSpecPath, webViewSpec, 'utf8');
log('Done.');
@@ -7,7 +7,7 @@ Detox was built from the ground up to support "classic" React Native projects.
While Detox should work out of the box with any React Native version of the latest minor releases, While Detox should work out of the box with any React Native version of the latest minor releases,
the _official_ versions compatibility is provided according to the following: the _official_ versions compatibility is provided according to the following:
- **RN `v0.77.x` - `v0.82.x`:** Fully compatible with React Native's ["New Architecture"](https://reactnative.dev/docs/the-new-architecture/landing-page). - **RN `v0.77.x` - `v0.84.x`:** Fully compatible with React Native's ["New Architecture"](https://reactnative.dev/docs/the-new-architecture/landing-page).
Newer RN versions might work with Detox, but they've not been thoroughly tested by the Detox team yet. Newer RN versions might work with Detox, but they've not been thoroughly tested by the Detox team yet.
Although we do not officially support older React Native versions, we do our best to keep Detox compatible with them. Although we do not officially support older React Native versions, we do our best to keep Detox compatible with them.
@@ -228,10 +228,14 @@
); );
inputPaths = ( inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh", "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/React-Core-prebuilt/React.framework/React",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ReactNativeDependencies/ReactNativeDependencies.framework/ReactNativeDependencies",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermesvm.framework/hermesvm", "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermesvm.framework/hermesvm",
); );
name = "[CP] Embed Pods Frameworks"; name = "[CP] Embed Pods Frameworks";
outputPaths = ( outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/React.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReactNativeDependencies.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermesvm.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermesvm.framework",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -246,21 +250,15 @@
); );
inputPaths = ( inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh", "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
); );
name = "[CP] Copy Pods Resources"; name = "[CP] Copy Pods Resources";
outputPaths = ( outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
@@ -409,12 +407,19 @@
LDPLUSPLUS = ""; LDPLUSPLUS = "";
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "$(inherited)"; OTHER_CFLAGS = (
OTHER_CPLUSPLUSFLAGS = "$(inherited)"; "$(inherited)",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
OTHER_CPLUSPLUSFLAGS = (
"$(inherited)",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
OTHER_LDFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
SWIFT_ENABLE_EXPLICIT_MODULES = NO;
USE_HERMES = true; USE_HERMES = true;
}; };
name = Debug; name = Debug;
@@ -462,11 +467,18 @@
LD = ""; LD = "";
LDPLUSPLUS = ""; LDPLUSPLUS = "";
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "$(inherited)"; OTHER_CFLAGS = (
OTHER_CPLUSPLUSFLAGS = "$(inherited)"; "$(inherited)",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
OTHER_CPLUSPLUSFLAGS = (
"$(inherited)",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
OTHER_LDFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_ENABLE_EXPLICIT_MODULES = NO;
USE_HERMES = true; USE_HERMES = true;
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
+2 -2
View File
@@ -23,8 +23,8 @@
}, },
"dependencies": { "dependencies": {
"@react-native-async-storage/async-storage": "^2.1.0", "@react-native-async-storage/async-storage": "^2.1.0",
"react": "19.2.0", "react": "19.2.3",
"react-native": "0.83.0", "react-native": "0.84.0",
"tslib": "^2.0.3" "tslib": "^2.0.3"
}, },
"devDependencies": { "devDependencies": {
+1 -2
View File
@@ -5,6 +5,5 @@ if [ ! -z ${REACT_NATIVE_VERSION} ]; then
node scripts/change_react_native_version.js "detox/test" ${REACT_NATIVE_VERSION} "dependencies" node scripts/change_react_native_version.js "detox/test" ${REACT_NATIVE_VERSION} "dependencies"
node scripts/change_react_native_version.js "detox" ${REACT_NATIVE_VERSION} "devDependencies" node scripts/change_react_native_version.js "detox" ${REACT_NATIVE_VERSION} "devDependencies"
# Update lockfile for the new RN version YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install
yarn install --mode update-lockfile
fi fi
+1
View File
@@ -23,6 +23,7 @@ mkdir -p coverage
### E2E self-tests ### E2E self-tests
pushd detox/test pushd detox/test
run_f "node scripts/patch-webview-codegen.js"
run_f "yarn build:android-debug" # Workaround for asyncstorage issue https://github.com/react-native-async-storage/async-storage/issues/1216. Can be removed after fixing it run_f "yarn build:android-debug" # Workaround for asyncstorage issue https://github.com/react-native-async-storage/async-storage/issues/1216. Can be removed after fixing it
run_f "yarn build:android" run_f "yarn build:android"
@@ -7,7 +7,7 @@ Detox was built from the ground up to support "classic" React Native projects.
While Detox should work out of the box with any React Native version of the latest minor releases, While Detox should work out of the box with any React Native version of the latest minor releases,
the _official_ versions compatibility is provided according to the following: the _official_ versions compatibility is provided according to the following:
- **RN `v0.77.x` - `v0.82.x`:** Fully compatible with React Native's ["New Architecture"](https://reactnative.dev/docs/the-new-architecture/landing-page). - **RN `v0.77.x` - `v0.84.x`:** Fully compatible with React Native's ["New Architecture"](https://reactnative.dev/docs/the-new-architecture/landing-page).
Newer RN versions might work with Detox, but they've not been thoroughly tested by the Detox team yet. Newer RN versions might work with Detox, but they've not been thoroughly tested by the Detox team yet.
Although we do not officially support older React Native versions, we do our best to keep Detox compatible with them. Although we do not officially support older React Native versions, we do our best to keep Detox compatible with them.
+87 -62
View File
@@ -3966,10 +3966,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@react-native/assets-registry@npm:0.83.0": "@react-native/assets-registry@npm:0.84.0":
version: 0.83.0 version: 0.84.0
resolution: "@react-native/assets-registry@npm:0.83.0" resolution: "@react-native/assets-registry@npm:0.84.0"
checksum: 10/9c314a311739e5ea555155df54821f888f3c61de6bcd71db60a253b27aefb42116892d487efb57a5ba99998c9835e1b123019bfe4397c087d0bcef38bad43c8c checksum: 10/9f972a0e46494aafddbed140f3256615dd2d2675055b8a3dbe05ca0c30d2d89a7a960fbe6580e327a850d870203b8e89247adfd3ed629c0b54d905c6fd5dbcb3
languageName: node languageName: node
linkType: hard linkType: hard
@@ -4055,11 +4055,28 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@react-native/community-cli-plugin@npm:0.83.0": "@react-native/codegen@npm:0.84.0":
version: 0.83.0 version: 0.84.0
resolution: "@react-native/community-cli-plugin@npm:0.83.0" resolution: "@react-native/codegen@npm:0.84.0"
dependencies: dependencies:
"@react-native/dev-middleware": "npm:0.83.0" "@babel/core": "npm:^7.25.2"
"@babel/parser": "npm:^7.25.3"
hermes-parser: "npm:0.32.0"
invariant: "npm:^2.2.4"
nullthrows: "npm:^1.1.1"
tinyglobby: "npm:^0.2.15"
yargs: "npm:^17.6.2"
peerDependencies:
"@babel/core": "*"
checksum: 10/1b81de2bcccbf570efb42cf8aaa4a551ececdc510ce9c9c2230f8eafdd0eeec25729307340993a769eca94c99e343499ba0aeec4427e3e7976914c01055732f7
languageName: node
linkType: hard
"@react-native/community-cli-plugin@npm:0.84.0":
version: 0.84.0
resolution: "@react-native/community-cli-plugin@npm:0.84.0"
dependencies:
"@react-native/dev-middleware": "npm:0.84.0"
debug: "npm:^4.4.0" debug: "npm:^4.4.0"
invariant: "npm:^2.2.4" invariant: "npm:^2.2.4"
metro: "npm:^0.83.3" metro: "npm:^0.83.3"
@@ -4074,34 +4091,35 @@ __metadata:
optional: true optional: true
"@react-native/metro-config": "@react-native/metro-config":
optional: true optional: true
checksum: 10/b0a664d828270c7be320c84b6f6f37dc08682d899157865cd8655f5bac99d2b990c9cc4dcaf57f4c284c698af41dd97bc4acd34fec5fc052f535c83a74cf0cb9 checksum: 10/3d1d0fa6e5d904b9d3fc55c417e2f561daa8ace734ba0e4fd4cd6abb73967fe0756ec34f7a04b2f98b21eb834cffba869a79e26c57612c66c67054f6995d44b2
languageName: node languageName: node
linkType: hard linkType: hard
"@react-native/debugger-frontend@npm:0.83.0": "@react-native/debugger-frontend@npm:0.84.0":
version: 0.83.0 version: 0.84.0
resolution: "@react-native/debugger-frontend@npm:0.83.0" resolution: "@react-native/debugger-frontend@npm:0.84.0"
checksum: 10/3f52e5400c24173f7619d35be95b6899f15e087e218d8ad580a5a931e908c2d0ef0e752619a73752bc5ed18f35a96fd38723c1967fa65f68e9f8cc6bd43972dc checksum: 10/93435f6bb8cc69195846a25453639eeaa6b261113fae8957e3c7f28c0302e25ea89fc757bf7bb971e2e5caa0672b228fbaddbf5ab2c844d2a6365522e3fa7edd
languageName: node languageName: node
linkType: hard linkType: hard
"@react-native/debugger-shell@npm:0.83.0": "@react-native/debugger-shell@npm:0.84.0":
version: 0.83.0 version: 0.84.0
resolution: "@react-native/debugger-shell@npm:0.83.0" resolution: "@react-native/debugger-shell@npm:0.84.0"
dependencies: dependencies:
cross-spawn: "npm:^7.0.6" cross-spawn: "npm:^7.0.6"
debug: "npm:^4.4.0"
fb-dotslash: "npm:0.5.8" fb-dotslash: "npm:0.5.8"
checksum: 10/51b61131b90a9e09f259a5999759b77d9384cf9b0f38e4c4da1b63a1a729126349f9d20f51d599e527ebe5430945e2e1ddde17d933aa30c0775e2437694db50f checksum: 10/5e29a717ce5384735f9db32fb3f073ec1bf53d7fd136780433befa04f0d31275ef5aaebc91e1e923518add0e77a70cfbcc9dc374b01a8bcf71944747d2f7bbde
languageName: node languageName: node
linkType: hard linkType: hard
"@react-native/dev-middleware@npm:0.83.0": "@react-native/dev-middleware@npm:0.84.0":
version: 0.83.0 version: 0.84.0
resolution: "@react-native/dev-middleware@npm:0.83.0" resolution: "@react-native/dev-middleware@npm:0.84.0"
dependencies: dependencies:
"@isaacs/ttlcache": "npm:^1.4.1" "@isaacs/ttlcache": "npm:^1.4.1"
"@react-native/debugger-frontend": "npm:0.83.0" "@react-native/debugger-frontend": "npm:0.84.0"
"@react-native/debugger-shell": "npm:0.83.0" "@react-native/debugger-shell": "npm:0.84.0"
chrome-launcher: "npm:^0.15.2" chrome-launcher: "npm:^0.15.2"
chromium-edge-launcher: "npm:^0.2.0" chromium-edge-launcher: "npm:^0.2.0"
connect: "npm:^3.6.5" connect: "npm:^3.6.5"
@@ -4111,7 +4129,7 @@ __metadata:
open: "npm:^7.0.3" open: "npm:^7.0.3"
serve-static: "npm:^1.16.2" serve-static: "npm:^1.16.2"
ws: "npm:^7.5.10" ws: "npm:^7.5.10"
checksum: 10/524df13d559e4593887b6183cedafb43ea0d6b07e4b04b527549efcce03e2809044937c7fc0c6483fc14fb237d74b0c706c2fd01825e3abb5a3cc009a27aba8b checksum: 10/ac061225f248821e2d90f201423e51b23431444ef76ea5bd5cc7a5467cee40fa6c780a1d80a0bb926cb9c25d79811eb1e4de663e42202fd3776a6ea43cda8ea4
languageName: node languageName: node
linkType: hard linkType: hard
@@ -4145,10 +4163,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@react-native/gradle-plugin@npm:0.83.0": "@react-native/gradle-plugin@npm:0.84.0":
version: 0.83.0 version: 0.84.0
resolution: "@react-native/gradle-plugin@npm:0.83.0" resolution: "@react-native/gradle-plugin@npm:0.84.0"
checksum: 10/dcd7a47b21629a78a6f079a16a87d85ed78214dd19efafb054e7718624774b07e8c10572d054b9e31ac80c96148127a40da164b44e04e8aa434cfb93ead258fc checksum: 10/161c956b010a63f495c6d5fbe90446f7f4b71a584e315b0e2244cc8084f43241445650e90ab816187071fff8253481156c20d94251620786ad5076a7bf9cf622
languageName: node languageName: node
linkType: hard linkType: hard
@@ -4159,6 +4177,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@react-native/js-polyfills@npm:0.84.0":
version: 0.84.0
resolution: "@react-native/js-polyfills@npm:0.84.0"
checksum: 10/5b2927aee2f7106cfe2531f9fce4ef1563c207223a34a6ec2aec886b475b78e507a8dd23a9e2c9a0afafbaff56a53e23dec93c6b4dc14b224d026bbed4304d97
languageName: node
linkType: hard
"@react-native/metro-babel-transformer@npm:0.83.0": "@react-native/metro-babel-transformer@npm:0.83.0":
version: 0.83.0 version: 0.83.0
resolution: "@react-native/metro-babel-transformer@npm:0.83.0" resolution: "@react-native/metro-babel-transformer@npm:0.83.0"
@@ -4185,10 +4210,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@react-native/normalize-colors@npm:0.83.0": "@react-native/normalize-colors@npm:0.84.0":
version: 0.83.0 version: 0.84.0
resolution: "@react-native/normalize-colors@npm:0.83.0" resolution: "@react-native/normalize-colors@npm:0.84.0"
checksum: 10/84e3122bd0292ae9ee61a6d2997119196a5dc128ede7770a2a70e3b9de1b78f5f2a10b44dc683ce2652ad8f45767f249a74d1a86f7657a5dae7a377790512f68 checksum: 10/038a4687c023b4799126efc4b2d8b91abdb806fdc90af5b749b552b2c311d7459893f6c583941c1baed1c906ab437804916da2cdfc666a431c98482ddb62672a
languageName: node languageName: node
linkType: hard linkType: hard
@@ -4199,9 +4224,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@react-native/virtualized-lists@npm:0.83.0": "@react-native/virtualized-lists@npm:0.84.0":
version: 0.83.0 version: 0.84.0
resolution: "@react-native/virtualized-lists@npm:0.83.0" resolution: "@react-native/virtualized-lists@npm:0.84.0"
dependencies: dependencies:
invariant: "npm:^2.2.4" invariant: "npm:^2.2.4"
nullthrows: "npm:^1.1.1" nullthrows: "npm:^1.1.1"
@@ -4212,7 +4237,7 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
"@types/react": "@types/react":
optional: true optional: true
checksum: 10/b51a3c0c110b6d259b6cf25fa4d143b03cbd6b637e5b33ea07453c0aa12a061e2f861a67c1b4089f96817ef46a1bd3709ecae3133026e92b77979b96a08bff06 checksum: 10/9a701e31a2912610fe6ba38d8d0aa255e559719f1c4a416073cef2b6c47ea0b9795fdef29738e9faeaaa670788a50d7a4f7db3704954b4a24e99111d16fedcc5
languageName: node languageName: node
linkType: hard linkType: hard
@@ -10216,8 +10241,8 @@ __metadata:
nyc: "npm:^15.1.0" nyc: "npm:^15.1.0"
p-iteration: "npm:^1.1.8" p-iteration: "npm:^1.1.8"
pngjs: "npm:^3.4.0" pngjs: "npm:^3.4.0"
react: "npm:19.2.0" react: "npm:19.2.3"
react-native: "npm:0.83.0" react-native: "npm:0.84.0"
react-native-launch-arguments: "npm:^4.0.2" react-native-launch-arguments: "npm:^4.0.2"
react-native-permissions: "npm:^5.2.1" react-native-permissions: "npm:^5.2.1"
react-native-webview: "npm:^13.13.1" react-native-webview: "npm:^13.13.1"
@@ -10279,7 +10304,7 @@ __metadata:
prettier: "npm:^3.1.1" prettier: "npm:^3.1.1"
promisify-child-process: "npm:^4.1.2" promisify-child-process: "npm:^4.1.2"
proper-lockfile: "npm:^3.0.2" proper-lockfile: "npm:^3.0.2"
react-native: "npm:0.83.0" react-native: "npm:0.84.0"
react-native-codegen: "npm:^0.0.8" react-native-codegen: "npm:^0.0.8"
resolve-from: "npm:^5.0.0" resolve-from: "npm:^5.0.0"
sanitize-filename: "npm:^1.6.1" sanitize-filename: "npm:^1.6.1"
@@ -11743,8 +11768,8 @@ __metadata:
detox: "workspace:*" detox: "workspace:*"
fs-extra: "npm:^9.1.0" fs-extra: "npm:^9.1.0"
jest: "npm:^30.0.3" jest: "npm:^30.0.3"
react: "npm:19.2.0" react: "npm:19.2.3"
react-native: "npm:0.83.0" react-native: "npm:0.84.0"
react-test-renderer: "npm:19.2.0" react-test-renderer: "npm:19.2.0"
ts-jest: "npm:^29.4.0" ts-jest: "npm:^29.4.0"
tslib: "npm:^2.0.3" tslib: "npm:^2.0.3"
@@ -13814,10 +13839,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"hermes-compiler@npm:0.14.0": "hermes-compiler@npm:250829098.0.7":
version: 0.14.0 version: 250829098.0.7
resolution: "hermes-compiler@npm:0.14.0" resolution: "hermes-compiler@npm:250829098.0.7"
checksum: 10/880a5570770a977731f98fe2bedf5b7079e2e851a16e08ec89aeaa1de552e885734de3be513bc5633e5415fd1ac497b7b630aab75a46c359a5d30a243a1dcf11 checksum: 10/229cfee06c0e3205f93e1b8ac1782b852cd8ef190b4e3a2ad69f953b470facf0cca3c5986aa499a752802b714a82960d69b9a43dacaa056bd3998db7553b6c36
languageName: node languageName: node
linkType: hard linkType: hard
@@ -22230,18 +22255,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-native@npm:0.83.0": "react-native@npm:0.84.0":
version: 0.83.0 version: 0.84.0
resolution: "react-native@npm:0.83.0" resolution: "react-native@npm:0.84.0"
dependencies: dependencies:
"@jest/create-cache-key-function": "npm:^29.7.0" "@jest/create-cache-key-function": "npm:^29.7.0"
"@react-native/assets-registry": "npm:0.83.0" "@react-native/assets-registry": "npm:0.84.0"
"@react-native/codegen": "npm:0.83.0" "@react-native/codegen": "npm:0.84.0"
"@react-native/community-cli-plugin": "npm:0.83.0" "@react-native/community-cli-plugin": "npm:0.84.0"
"@react-native/gradle-plugin": "npm:0.83.0" "@react-native/gradle-plugin": "npm:0.84.0"
"@react-native/js-polyfills": "npm:0.83.0" "@react-native/js-polyfills": "npm:0.84.0"
"@react-native/normalize-colors": "npm:0.83.0" "@react-native/normalize-colors": "npm:0.84.0"
"@react-native/virtualized-lists": "npm:0.83.0" "@react-native/virtualized-lists": "npm:0.84.0"
abort-controller: "npm:^3.0.0" abort-controller: "npm:^3.0.0"
anser: "npm:^1.4.9" anser: "npm:^1.4.9"
ansi-regex: "npm:^5.0.0" ansi-regex: "npm:^5.0.0"
@@ -22250,8 +22275,7 @@ __metadata:
base64-js: "npm:^1.5.1" base64-js: "npm:^1.5.1"
commander: "npm:^12.0.0" commander: "npm:^12.0.0"
flow-enums-runtime: "npm:^0.0.6" flow-enums-runtime: "npm:^0.0.6"
glob: "npm:^7.1.1" hermes-compiler: "npm:250829098.0.7"
hermes-compiler: "npm:0.14.0"
invariant: "npm:^2.2.4" invariant: "npm:^2.2.4"
jest-environment-node: "npm:^29.7.0" jest-environment-node: "npm:^29.7.0"
memoize-one: "npm:^5.0.0" memoize-one: "npm:^5.0.0"
@@ -22266,18 +22290,19 @@ __metadata:
scheduler: "npm:0.27.0" scheduler: "npm:0.27.0"
semver: "npm:^7.1.3" semver: "npm:^7.1.3"
stacktrace-parser: "npm:^0.1.10" stacktrace-parser: "npm:^0.1.10"
tinyglobby: "npm:^0.2.15"
whatwg-fetch: "npm:^3.0.0" whatwg-fetch: "npm:^3.0.0"
ws: "npm:^7.5.10" ws: "npm:^7.5.10"
yargs: "npm:^17.6.2" yargs: "npm:^17.6.2"
peerDependencies: peerDependencies:
"@types/react": ^19.1.1 "@types/react": ^19.1.1
react: ^19.2.0 react: ^19.2.3
peerDependenciesMeta: peerDependenciesMeta:
"@types/react": "@types/react":
optional: true optional: true
bin: bin:
react-native: cli.js react-native: cli.js
checksum: 10/78454fa89c2f5c8794d8d04c099c6d5f43f569696b6e04afb927181bcdcc73646921e428bd29b32a79c94678d572ec00c5d3355e7a43fa94d58065eed4ba78f2 checksum: 10/f1ae3cd7f9581d0ee516bfbbdc705ea7099d025a5701d997bf21e0a158c630ab30edf03140e64128f33e1a99be7f607e53a9d4277723d73239e14a7cd7193eaf
languageName: node languageName: node
linkType: hard linkType: hard
@@ -22382,10 +22407,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react@npm:19.2.0": "react@npm:19.2.3":
version: 19.2.0 version: 19.2.3
resolution: "react@npm:19.2.0" resolution: "react@npm:19.2.3"
checksum: 10/e13bcdb8e994c3cfa922743cb75ca8deb60531bf02f584d2d8dab940a8132ce8a2e6ef16f8ed7f372b4072e7a7eeff589b2812dabbedfa73e6e46201dac8a9d0 checksum: 10/d16b7f35c0d35a56f63d9d1693819762e4abc479c57dd6310298920bed3820fcec7e17a99d44983416d8f5049143ea45b8005d3ab8324bae8973224400502b08
languageName: node languageName: node
linkType: hard linkType: hard