Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions detox/android/detox/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -123,31 +123,31 @@ dependencies {
// Versions are in-sync with the 'androidx-test-1.4.0' release/tag of the android-test github repo,
// used by the Detox generator. See https://github.com/android/android-test/releases/tag/androidx-test-1.4.0
// Important: Should remain so when generator tag is replaced!
api('androidx.test.espresso:espresso-core:3.6.1') {
api('androidx.test.espresso:espresso-core:3.7.0') {
because 'Needed all across Detox but also makes Espresso seamlessly provided to Detox users with hybrid apps/E2E-tests.'
}
api('androidx.test.espresso:espresso-web:3.6.1') {
api('androidx.test.espresso:espresso-web:3.7.0') {
because 'Web-View testing'
}
api('androidx.test.espresso:espresso-contrib:3.6.1') {
api('androidx.test.espresso:espresso-contrib:3.7.0') {
because 'Android datepicker support'
exclude group: "org.checkerframework", module: "checker"
}
api('org.hamcrest:hamcrest:2.2') {
because 'See https://github.com/wix/Detox/issues/3920. Need to force hamcrest 2.2 win in battle of 2.2 vs. 1.3 (specified by Espresso).'
}
api('androidx.test:rules:1.6.1') {
api('androidx.test:rules:1.7.0') {
because 'of ActivityTestRule. Needed by users *and* internally used by Detox.'
}
api('androidx.test.ext:junit:1.2.1') {
api('androidx.test.ext:junit:1.3.0') {
because 'Needed so as to seamlessly provide AndroidJUnit4 to Detox users. Depends on junit core.'
}
// Version is the latest; Cannot sync with the Github repo (e.g. android/android-test) because the androidx
// packaging version of associated classes is simply not there...
api('androidx.test.uiautomator:uiautomator:2.2.0') {
api('androidx.test.uiautomator:uiautomator:2.3.0') {
because 'Needed by Detox but also makes UIAutomator seamlessly provided to Detox users with hybrid apps/E2E-tests.'
}
api('androidx.test:core-ktx:1.6.1') {
api('androidx.test:core-ktx:1.7.0') {
because 'Needed by Detox but also makes AndroidX test core seamlessly provided to Detox users with hybrid apps/E2E-tests.'
}
implementation("org.jetbrains.kotlin:kotlin-reflect:$_kotlinVersion") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.wix.detox.reactnative.idlingresources.looper.MQThreadsReflector
import com.wix.detox.reactnative.idlingresources.network.NetworkIdlingResource
import kotlinx.coroutines.runBlocking
import org.joor.Reflect
import java.util.concurrent.ConcurrentHashMap

private const val LOG_TAG = "DetoxRNIdleRes"

Expand Down Expand Up @@ -43,6 +44,7 @@ class ReactNativeIdlingResources(
fun unregisterAll() {
unregisterMQThreadsInterrogators()
unregisterIdlingResources()
syncIdlingResources()
}

fun pauseNetworkSynchronization() = pauseIdlingResource(IdlingResourcesName.Network)
Expand Down Expand Up @@ -77,8 +79,8 @@ class ReactNativeIdlingResources(
}

private fun setupMQThreadsInterrogator(looperName: LooperName) {
reactApplication.getCurrentReactContext()?.let {
val mqThreadsReflector = MQThreadsReflector(it)
reactApplication.getCurrentReactContext()?.let { reactContext ->
val mqThreadsReflector = MQThreadsReflector(reactContext)
val looper = when (looperName) {
LooperName.JS -> mqThreadsReflector.getJSMQueue()?.getLooper()
LooperName.NativeModules -> mqThreadsReflector.getNativeModulesQueue()?.getLooper()
Expand Down Expand Up @@ -108,12 +110,39 @@ class ReactNativeIdlingResources(
}

private fun unregisterMQThreadsInterrogators() {
loopers.values.forEach {
IdlingRegistry.getInstance().unregisterLooperAsIdlingResource(it)
loopers.values.forEach { looper ->
IdlingRegistry.getInstance().unregisterLooperAsIdlingResource(looper)
clearLooperHandlerCache(looper)
}
loopers.clear()
}

/**
* Workaround for Espresso 3.7.0+ where LooperIdlingResourceInterrogationHandler
* caches handlers in a static map but doesn't clear them on release().
* This causes re-registered loopers to get stale handlers with releasing=true.
*
* For older Espresso versions without this static cache, this is a no-op.
*/
private fun clearLooperHandlerCache(looper: Looper) {
try {
val insts: ConcurrentHashMap<String, Any> =
Reflect.on("androidx.test.espresso.base.LooperIdlingResourceInterrogationHandler")
.field("insts")
.get()

for ((key, handler) in insts) {
val handlerLooper: Looper? = Reflect.on(handler).field("looper").get()
if (handlerLooper === looper) {
insts.remove(key)
break
}
}
} catch (e: Exception) {
// Expected for older Espresso versions or different implementations - silently ignore
}
}

@OptIn(ExperimentalStdlibApi::class)
private fun unregisterIdlingResources() {
IdlingResourcesName.entries.forEach {
Expand Down
2 changes: 1 addition & 1 deletion detox/test/e2e/detox.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const config = {
type: 'android.emulator',
headless: Boolean(process.env.CI),
device: {
avdName: 'Pixel_3a_API_35'
avdName: 'Pixel_3a_API_36'
},
utilBinaryPaths: ["e2e/util-binary/detoxbutler-1.1.0-aosp-release.apk"],
systemUI: {
Expand Down