detox 20.0.0-breaking.new-global-lifecycle.0 → 20.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/Detox-android/com/wix/detox/{20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-javadoc.jar → 20.0.0/detox-20.0.0-javadoc.jar} +0 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0-javadoc.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0-javadoc.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0-javadoc.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0-javadoc.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-sources.jar → 20.0.0/detox-20.0.0-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0.aar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.pom → 20.0.0/detox-20.0.0.pom} +2 -8
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.0/detox-20.0.0.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-src.tbz +0 -0
- package/Detox-ios.tbz +0 -0
- package/README.md +1 -1
- package/android/build.gradle +13 -8
- package/android/detox/build.gradle +14 -10
- package/android/detox/proguard-rules-app.pro +6 -0
- package/android/detox/proguard-rules.pro +3 -0
- package/android/detox/publish-pom.gradle +5 -1
- package/android/detox/publishing.gradle +35 -33
- package/android/detox/src/full/java/com/wix/detox/DetoxCrashHandler.kt +1 -1
- package/android/detox/src/full/java/com/wix/detox/LaunchArgs.java +9 -0
- package/android/detox/src/full/java/com/wix/detox/TestEngineFacade.kt +1 -1
- package/android/detox/src/full/java/com/wix/detox/adapters/server/WebSocketClient.java +3 -1
- package/android/detox/src/full/java/com/wix/detox/espresso/DetoxAction.java +1 -3
- package/android/detox/src/full/java/com/wix/detox/espresso/UiAutomatorHelper.java +1 -1
- package/android/detox/src/full/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +22 -0
- package/android/detox/src/{main → full}/java/com/wix/detox/espresso/action/GetAttributesAction.kt +13 -1
- package/android/detox/src/full/java/com/wix/detox/espresso/common/SliderHelper.kt +75 -0
- package/android/detox/src/full/java/com/wix/detox/espresso/matcher/ViewMatchers.kt +16 -23
- package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeExtension.kt +15 -2
- package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeIdlingResources.kt +43 -38
- package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeLoadingMonitor.kt +54 -8
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/IdlingResourceDescription.kt +19 -13
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/NetworkIdlingResource.java +33 -30
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/DelegatedIdleInterrogationStrategy.kt +7 -27
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/IdleInterrogationStrategy.kt +1 -11
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/RN66Workaround.kt +13 -4
- package/android/detox/src/main/java/com/wix/detox/common/DetoxErrors.java +4 -1
- package/android/detox/src/main/java/com/wix/detox/common/TextFileReader.kt +1 -1
- package/android/detox/src/main/java/com/wix/detox/espresso/UiControllerSpy.kt +2 -1
- package/android/detox/src/main/java/com/wix/detox/espresso/action/common/ReflectUtils.kt +10 -0
- package/android/detox/src/main/java/com/wix/detox/espresso/action/common/utils/UiControllerUtils.kt +1 -1
- package/android/detox/src/testFull/java/com/wix/detox/espresso/action/DetoxMultiTapSpec.kt +4 -3
- package/android/detox/src/testFull/java/com/wix/detox/espresso/action/GetAttributesActionTest.kt +15 -3
- package/android/detox/src/testFull/java/com/wix/detox/espresso/common/SliderHelperTest.kt +39 -0
- package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/NetworkIdlingResourcesTest.kt +61 -0
- package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/timers/DelegatedIdleInterrogationStrategySpec.kt +3 -11
- package/android/gradle/wrapper/gradle-wrapper.properties +2 -1
- package/android/gradlew +181 -107
- package/index.d.ts +198 -57
- package/internals.d.ts +220 -51
- package/local-cli/build.js +2 -2
- package/local-cli/build.test.js +14 -14
- package/local-cli/cli.js +8 -6
- package/local-cli/init.js +61 -21
- package/local-cli/rebuild-framework-cache.js +1 -1
- package/local-cli/reset-lock-file.js +16 -0
- package/local-cli/templates/jest.js +13 -10
- package/local-cli/test.js +14 -8
- package/local-cli/test.test.js +148 -61
- package/local-cli/testCommand/TestRunnerCommand.js +78 -71
- package/local-cli/testCommand/builder.js +0 -1
- package/local-cli/testCommand/middlewares.js +4 -13
- package/local-cli/testCommand/warnings.js +0 -3
- package/local-cli/utils/jestInternals.js +4 -1
- package/package.json +22 -15
- package/runners/deprecation.js +42 -44
- package/runners/jest/globalSetup.js +1 -1
- package/runners/jest/globalTeardown.js +1 -1
- package/runners/jest/index.d.ts +60 -0
- package/runners/jest/index.js +3 -8
- package/runners/jest/index.test.js +13 -0
- package/runners/jest/reporters/DetoxReporter.js +33 -2
- package/runners/jest/testEnvironment/index.js +119 -69
- package/runners/jest/testEnvironment/listeners/DetoxCoreListener.js +94 -51
- package/runners/jest/testEnvironment/listeners/DetoxPlatformFilterListener.js +1 -1
- package/runners/jest/testEnvironment/listeners/SpecReporter.js +14 -16
- package/runners/jest/testEnvironment/listeners/WorkerAssignReporter.js +2 -6
- package/runners/jest/testEnvironment/utils/assertJestCircus27.js +17 -3
- package/runners/jest/testEnvironment/utils/assertJestCircus27.test.js +0 -1
- package/src/DetoxWorker.js +107 -59
- package/src/android/core/NativeElement.js +56 -20
- package/src/android/core/NativeExpect.js +28 -9
- package/src/android/interactions/native.js +25 -18
- package/src/artifacts/ArtifactsManager.js +14 -47
- package/src/artifacts/instruments/ios/SimulatorInstrumentsRecording.js +3 -3
- package/src/artifacts/log/android/ADBLogcatRecording.js +11 -28
- package/src/artifacts/log/ios/SimulatorLogRecording.js +1 -1
- package/src/artifacts/screenshot/SimulatorScreenshotPlugin.js +1 -1
- package/src/artifacts/templates/artifact/Artifact.js +1 -1
- package/src/artifacts/templates/plugin/ArtifactPlugin.js +1 -1
- package/src/artifacts/timeline/TimelineContextTypes.js +7 -0
- package/src/artifacts/utils/temporaryPath.js +18 -7
- package/src/artifacts/video/SimulatorRecordVideoPlugin.js +1 -1
- package/src/client/AsyncWebSocket.js +8 -17
- package/src/client/Client.js +19 -2
- package/src/client/actions/formatters/sync-resources/NetworkFormatter.js +1 -1
- package/src/configuration/collectCliConfig.js +1 -12
- package/src/configuration/composeAppsConfig.js +5 -1
- package/src/configuration/composeDeviceConfig.js +1 -1
- package/src/configuration/composeLoggerConfig.js +19 -10
- package/src/configuration/composeRunnerConfig.js +62 -9
- package/src/configuration/index.js +14 -9
- package/src/configuration/loadExternalConfig.js +1 -1
- package/src/devices/allocation/DeviceAllocator.js +3 -2
- package/src/devices/allocation/drivers/android/emulator/AVDValidator.js +5 -5
- package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +4 -3
- package/src/devices/allocation/drivers/android/emulator/EmulatorAllocationHelper.js +1 -1
- package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +3 -2
- package/src/devices/allocation/drivers/android/emulator/EmulatorVersionResolver.js +4 -6
- package/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js +1 -1
- package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +1 -0
- package/src/devices/allocation/drivers/android/genycloud/GenyInstanceAllocationHelper.js +1 -1
- package/src/devices/common/drivers/android/exec/ADB.js +5 -0
- package/src/devices/common/drivers/android/genycloud/services/GenyInstanceNaming.js +3 -3
- package/src/devices/common/drivers/android/genycloud/services/GenyRecipesService.js +1 -1
- package/src/devices/common/drivers/android/tools/EmulatorTelnet.js +1 -1
- package/src/devices/common/drivers/android/tools/FreeDeviceFinder.js +1 -1
- package/src/devices/common/drivers/android/tools/MonitoredInstrumentation.js +1 -1
- package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +29 -3
- package/src/devices/lifecycle/GenyGlobalLifecycleHandler.js +13 -15
- package/src/devices/runtime/RuntimeDevice.js +19 -12
- package/src/devices/runtime/drivers/DeviceDriverBase.js +1 -1
- package/src/devices/runtime/drivers/android/AndroidDriver.js +10 -2
- package/src/devices/runtime/drivers/ios/SimulatorDriver.js +1 -1
- package/src/errors/DetoxConfigErrorComposer.js +18 -3
- package/src/ios/expectTwo.js +153 -67
- package/src/ipc/IPCClient.js +12 -12
- package/src/ipc/IPCServer.js +34 -17
- package/src/ipc/{state.js → SessionState.js} +26 -31
- package/src/logger/DetoxLogger.js +261 -157
- package/src/logger/index.js +5 -0
- package/src/logger/utils/BunyanLogger.js +76 -0
- package/src/logger/utils/CategoryThreadDispatcher.js +36 -0
- package/src/logger/utils/DetoxLogFinalizer.js +140 -0
- package/src/logger/utils/MessageStack.js +24 -0
- package/src/logger/utils/ThreadDispatcher.js +61 -0
- package/src/logger/{customConsoleLogger.js → utils/customConsoleLogger.js} +5 -4
- package/src/logger/utils/sanitizeBunyanContext.js +30 -0
- package/src/{utils → logger/utils}/streamUtils.js +51 -17
- package/src/logger/utils/tracerLegacy.js +37 -0
- package/src/realms/DetoxContext.js +78 -65
- package/src/realms/DetoxInternalsFacade.js +8 -12
- package/src/realms/DetoxPrimaryContext.js +106 -73
- package/src/realms/DetoxSecondaryContext.js +29 -31
- package/src/server/DetoxConnection.js +18 -23
- package/src/server/DetoxServer.js +7 -10
- package/src/server/DetoxSession.js +6 -6
- package/src/server/DetoxSessionManager.js +1 -1
- package/src/server/handlers/RegisteredConnectionHandler.js +1 -2
- package/src/symbols.js +16 -22
- package/src/utils/Timer.js +55 -38
- package/src/utils/argparse.js +11 -0
- package/src/utils/childProcess/exec.js +1 -1
- package/src/utils/childProcess/spawn.js +1 -1
- package/src/utils/environment.js +30 -15
- package/src/utils/errorUtils.js +24 -3
- package/src/utils/invocationTraceDescriptions.js +43 -0
- package/src/utils/logger.js +1 -1
- package/src/utils/traceInvocationCall.js +21 -0
- package/src/utils/traceMethods.js +15 -0
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-javadoc.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-javadoc.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-javadoc.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-javadoc.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.aar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.pom.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.pom.sha512 +0 -1
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/DefaultIdleInterrogationStrategy.kt +0 -84
- package/android/detox/src/main/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +0 -36
- package/android/detox/src/testFull/java/com/wix/detox/espresso/action/AdjustSliderToPositionActionTest.kt +0 -59
- package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/timers/DefaultIdleInterrogationStrategySpec.kt +0 -115
- package/runners/jest/deprecation.js +0 -25
- package/src/configuration/utils/warnings.js +0 -12
- package/src/logger/DetoxTraceEventBuilder.js +0 -21
- package/src/logger/DetoxTracer.js +0 -133
- package/src/logger/TraceThreadDispatcher.js +0 -52
- package/src/utils/ChromeTracingExporter.js +0 -53
- package/src/utils/trace.js +0 -19
@@ -3,15 +3,17 @@
|
|
3
3
|
package com.wix.detox.espresso.matcher
|
4
4
|
|
5
5
|
import android.view.View
|
6
|
+
import androidx.appcompat.widget.AppCompatSeekBar
|
6
7
|
import androidx.test.espresso.matcher.BoundedMatcher
|
7
8
|
import androidx.test.espresso.matcher.ViewMatchers
|
8
9
|
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
9
|
-
import com.
|
10
|
+
import com.wix.detox.espresso.common.SliderHelper
|
10
11
|
import org.hamcrest.BaseMatcher
|
11
12
|
import org.hamcrest.Description
|
12
13
|
import org.hamcrest.Matcher
|
13
14
|
import org.hamcrest.Matchers.allOf
|
14
15
|
import org.hamcrest.TypeSafeMatcher
|
16
|
+
import kotlin.math.abs
|
15
17
|
|
16
18
|
/*
|
17
19
|
* An extension of [androidx.test.espresso.matcher.ViewMatchers].
|
@@ -36,6 +38,19 @@ fun isOfClassName(className: String): Matcher<View> {
|
|
36
38
|
fun isMatchingAtIndex(index: Int, innerMatcher: Matcher<View>): Matcher<View> =
|
37
39
|
ViewAtIndexMatcher(index, innerMatcher)
|
38
40
|
|
41
|
+
fun toHaveSliderPosition(expectedValuePct: Double, tolerance: Double): Matcher<View?> =
|
42
|
+
object: BoundedMatcher<View?, AppCompatSeekBar>(AppCompatSeekBar::class.java) {
|
43
|
+
override fun describeTo(description: Description) {
|
44
|
+
description.appendText("sliderPositionPercent($expectedValuePct)")
|
45
|
+
}
|
46
|
+
|
47
|
+
override fun matchesSafely(view: AppCompatSeekBar): Boolean {
|
48
|
+
val sliderHelper = SliderHelper.create(view)
|
49
|
+
val progressPct = sliderHelper.getCurrentProgressPct()
|
50
|
+
return (abs(progressPct - expectedValuePct) <= tolerance)
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
39
54
|
/**
|
40
55
|
* Same as [androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom], but accepts any class. Needed
|
41
56
|
* in order to avoid warning when passing 'any' class.
|
@@ -47,25 +62,3 @@ private class IsAssignableFromMatcher(private val clazz: Class<*>) : TypeSafeMat
|
|
47
62
|
description.appendText("is assignable from class: $clazz")
|
48
63
|
}
|
49
64
|
}
|
50
|
-
|
51
|
-
fun toHaveSliderPosition(expectedValue: Double, tolerance: Double): Matcher<View?> {
|
52
|
-
return object : BoundedMatcher<View?, ReactSlider>(ReactSlider::class.java) {
|
53
|
-
override fun describeTo(description: Description) {
|
54
|
-
description.appendText("expected: $expectedValue")
|
55
|
-
}
|
56
|
-
|
57
|
-
override fun matchesSafely(slider: ReactSlider?): Boolean {
|
58
|
-
val currentProgress = slider?.progress
|
59
|
-
|
60
|
-
if (currentProgress != null) {
|
61
|
-
val realProgress = slider.toRealProgress(currentProgress)
|
62
|
-
val currentPctFactor = slider.max / currentProgress.toDouble()
|
63
|
-
val realTotal = realProgress * currentPctFactor
|
64
|
-
val actualValue = realProgress / realTotal
|
65
|
-
return Math.abs(actualValue - expectedValue) <= tolerance
|
66
|
-
}
|
67
|
-
|
68
|
-
return false
|
69
|
-
}
|
70
|
-
}
|
71
|
-
}
|
@@ -39,7 +39,7 @@ object ReactNativeExtension {
|
|
39
39
|
reloadReactNativeInBackground(it)
|
40
40
|
val reactContext = awaitNewReactNativeContext(it, previousReactContext)
|
41
41
|
|
42
|
-
|
42
|
+
enableOrDisableSynchronization(reactContext, networkSyncEnabled)
|
43
43
|
hackRN50OrHigherWaitForReady()
|
44
44
|
}
|
45
45
|
}
|
@@ -58,7 +58,7 @@ object ReactNativeExtension {
|
|
58
58
|
(applicationContext as ReactApplication).let {
|
59
59
|
val reactContext = awaitNewReactNativeContext(it, null)
|
60
60
|
|
61
|
-
|
61
|
+
enableOrDisableSynchronization(reactContext)
|
62
62
|
hackRN50OrHigherWaitForReady()
|
63
63
|
}
|
64
64
|
}
|
@@ -124,6 +124,19 @@ object ReactNativeExtension {
|
|
124
124
|
return rnLoadingMonitor.getNewContext()!!
|
125
125
|
}
|
126
126
|
|
127
|
+
private fun enableOrDisableSynchronization(reactContext: ReactContext, networkSyncEnabled: Boolean = true) {
|
128
|
+
if (shouldDisableSynchronization()) {
|
129
|
+
clearAllSynchronization()
|
130
|
+
} else {
|
131
|
+
setupIdlingResources(reactContext, networkSyncEnabled)
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
private fun shouldDisableSynchronization(): Boolean {
|
136
|
+
val launchArgs = LaunchArgs()
|
137
|
+
return launchArgs.hasEnableSynchronization() && launchArgs.enableSynchronization.equals("0")
|
138
|
+
}
|
139
|
+
|
127
140
|
private fun setupIdlingResources(reactContext: ReactContext, networkSyncEnabled: Boolean = true) {
|
128
141
|
val launchArgs = LaunchArgs()
|
129
142
|
|
@@ -2,6 +2,7 @@ package com.wix.detox.reactnative
|
|
2
2
|
|
3
3
|
import android.os.Looper
|
4
4
|
import android.util.Log
|
5
|
+
import androidx.test.espresso.Espresso
|
5
6
|
import androidx.test.espresso.IdlingRegistry
|
6
7
|
import androidx.test.espresso.base.IdlingResourceRegistry
|
7
8
|
import com.facebook.react.bridge.ReactContext
|
@@ -12,7 +13,6 @@ import com.wix.detox.reactnative.idlingresources.timers.getInterrogationStrategy
|
|
12
13
|
import com.wix.detox.reactnative.idlingresources.uimodule.UIModuleIdlingResource
|
13
14
|
import org.joor.Reflect
|
14
15
|
import org.joor.ReflectException
|
15
|
-
import java.util.Set
|
16
16
|
|
17
17
|
private const val LOG_TAG = "DetoxRNIdleRes"
|
18
18
|
|
@@ -46,15 +46,11 @@ private class MQThreadReflected(private val queue: Any?, private val queueName:
|
|
46
46
|
}
|
47
47
|
|
48
48
|
class ReactNativeIdlingResources constructor(
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
{
|
55
|
-
|
49
|
+
private val reactContext: ReactContext,
|
50
|
+
private var launchArgs: LaunchArgs,
|
51
|
+
internal var networkSyncEnabled: Boolean = true
|
52
|
+
) {
|
56
53
|
companion object {
|
57
|
-
private const val FIELD_UI_BG_MSG_QUEUE = "mUiBackgroundMessageQueueThread"
|
58
54
|
private const val FIELD_NATIVE_MODULES_MSG_QUEUE = "mNativeModulesMessageQueueThread"
|
59
55
|
private const val FIELD_JS_MSG_QUEUE = "mJSMessageQueueThread"
|
60
56
|
}
|
@@ -69,17 +65,11 @@ class ReactNativeIdlingResources constructor(
|
|
69
65
|
|
70
66
|
fun registerAll() {
|
71
67
|
Log.i(LOG_TAG, "Setting up Espresso Idling Resources for React Native")
|
72
|
-
|
73
68
|
unregisterAll()
|
74
69
|
|
75
|
-
|
76
|
-
val blacklistUrls = launchArgs.getURLBlacklist()
|
77
|
-
setBlacklistUrls(blacklistUrls)
|
78
|
-
}
|
79
|
-
|
70
|
+
setupUrlBlacklist()
|
80
71
|
setupMQThreadsInterrogators()
|
81
72
|
syncIdlingResources()
|
82
|
-
|
83
73
|
setupCustomRNIdlingResources()
|
84
74
|
syncIdlingResources()
|
85
75
|
}
|
@@ -108,28 +98,42 @@ class ReactNativeIdlingResources constructor(
|
|
108
98
|
networkIdlingResource?.resume()
|
109
99
|
}
|
110
100
|
}
|
101
|
+
|
111
102
|
fun pauseRNTimersIdlingResource() = timersIdlingResource?.pause()
|
112
103
|
fun resumeRNTimersIdlingResource() = timersIdlingResource?.resume()
|
113
104
|
fun pauseUIIdlingResource() = uiModuleIdlingResource?.pause()
|
114
105
|
fun resumeUIIdlingResource() = uiModuleIdlingResource?.resume()
|
115
|
-
|
116
|
-
fun
|
106
|
+
|
107
|
+
fun setBlacklistUrls(urlList: String) {
|
108
|
+
setIldingResourceBlacklist(urlList)
|
109
|
+
}
|
110
|
+
|
111
|
+
private fun setIldingResourceBlacklist(urlList: String) {
|
112
|
+
val urlArray = toFormattedUrlArray(urlList)
|
113
|
+
NetworkIdlingResource.setURLBlacklist(urlArray)
|
114
|
+
}
|
117
115
|
|
118
116
|
private fun setupMQThreadsInterrogators() {
|
119
117
|
if (IdlingRegistry.getInstance().loopers.isEmpty()) {
|
120
118
|
val mqThreadsReflector = MQThreadsReflector(reactContext)
|
121
|
-
// val mqUIBackground = mqThreadsReflector.getQueue(FIELD_UI_BG_MSG_QUEUE)?.getLooper() TODO
|
122
119
|
val mqJS = mqThreadsReflector.getQueue(FIELD_JS_MSG_QUEUE)?.getLooper()
|
123
|
-
val mqNativeModules =
|
120
|
+
val mqNativeModules =
|
121
|
+
mqThreadsReflector.getQueue(FIELD_NATIVE_MODULES_MSG_QUEUE)?.getLooper()
|
124
122
|
|
125
123
|
IdlingRegistry.getInstance().apply {
|
126
|
-
// registerLooperAsIdlingResource(mqUIBackground)
|
127
124
|
registerLooperAsIdlingResource(mqJS)
|
128
125
|
registerLooperAsIdlingResource(mqNativeModules)
|
129
126
|
}
|
130
127
|
}
|
131
128
|
}
|
132
129
|
|
130
|
+
private fun setupUrlBlacklist() {
|
131
|
+
if (launchArgs.hasURLBlacklist()) {
|
132
|
+
val blacklistUrls = launchArgs.urlBlacklist
|
133
|
+
setIldingResourceBlacklist(blacklistUrls)
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
133
137
|
private fun setupCustomRNIdlingResources() {
|
134
138
|
rnBridgeIdlingResource = BridgeIdlingResource(reactContext)
|
135
139
|
timersIdlingResource = TimersIdlingResource(getInterrogationStrategy(reactContext)!!)
|
@@ -151,13 +155,16 @@ class ReactNativeIdlingResources constructor(
|
|
151
155
|
|
152
156
|
private fun syncIdlingResources() {
|
153
157
|
IdlingRegistry.getInstance().apply {
|
154
|
-
val irr: IdlingResourceRegistry =
|
158
|
+
val irr: IdlingResourceRegistry =
|
159
|
+
Reflect.on(Espresso::class.java).field("baseRegistry").get()
|
155
160
|
irr.sync(this.resources, this.loopers)
|
156
161
|
}
|
157
162
|
}
|
158
163
|
|
159
164
|
private fun unregisterMQThreadsInterrogators() {
|
160
|
-
|
165
|
+
val idlingResourceInstance = IdlingRegistry.getInstance()
|
166
|
+
val loopersField = Reflect.on(idlingResourceInstance).field("loopers")
|
167
|
+
loopersField.get<MutableSet<Any>>().clear()
|
161
168
|
}
|
162
169
|
|
163
170
|
private fun unregisterCustomRNIdlingResources() {
|
@@ -166,7 +173,8 @@ class ReactNativeIdlingResources constructor(
|
|
166
173
|
timersIdlingResource,
|
167
174
|
rnBridgeIdlingResource,
|
168
175
|
uiModuleIdlingResource,
|
169
|
-
animIdlingResource
|
176
|
+
animIdlingResource
|
177
|
+
)
|
170
178
|
rnBridgeIdlingResource?.onDetach()
|
171
179
|
|
172
180
|
removeNetworkIdlingResource()
|
@@ -174,13 +182,15 @@ class ReactNativeIdlingResources constructor(
|
|
174
182
|
}
|
175
183
|
|
176
184
|
private fun setupAsyncStorageIdlingResource() {
|
177
|
-
asyncStorageIdlingResource =
|
178
|
-
|
179
|
-
|
185
|
+
asyncStorageIdlingResource =
|
186
|
+
AsyncStorageIdlingResource.createIfNeeded(reactContext, false)?.also {
|
187
|
+
IdlingRegistry.getInstance().register(it)
|
188
|
+
}
|
180
189
|
|
181
|
-
legacyAsyncStorageIdlingResource =
|
182
|
-
|
183
|
-
|
190
|
+
legacyAsyncStorageIdlingResource =
|
191
|
+
AsyncStorageIdlingResource.createIfNeeded(reactContext, true)?.also {
|
192
|
+
IdlingRegistry.getInstance().register(it)
|
193
|
+
}
|
184
194
|
}
|
185
195
|
|
186
196
|
private fun removeAsyncStorageIdlingResource() {
|
@@ -212,13 +222,8 @@ class ReactNativeIdlingResources constructor(
|
|
212
222
|
|
213
223
|
private fun toFormattedUrlArray(urlList: String): List<String> {
|
214
224
|
var formattedUrls = urlList
|
215
|
-
formattedUrls = formattedUrls.replace(Regex("""[()"]"""), "")
|
216
|
-
formattedUrls = formattedUrls.trim()
|
217
|
-
return formattedUrls.split(',')
|
218
|
-
}
|
219
|
-
|
220
|
-
fun setBlacklistUrls(urlList: String) {
|
221
|
-
val urlArray = toFormattedUrlArray(urlList)
|
222
|
-
NetworkIdlingResource.setURLBlacklist(urlArray);
|
225
|
+
formattedUrls = formattedUrls.replace(Regex("""[()"]"""), "")
|
226
|
+
formattedUrls = formattedUrls.trim()
|
227
|
+
return formattedUrls.split(',')
|
223
228
|
}
|
224
229
|
}
|
@@ -7,17 +7,22 @@ import com.facebook.react.ReactInstanceManager
|
|
7
7
|
import com.facebook.react.bridge.ReactContext
|
8
8
|
import com.wix.detox.common.DetoxErrors
|
9
9
|
import com.wix.detox.config.DetoxConfig
|
10
|
+
import org.joor.Reflect
|
11
|
+
import java.lang.reflect.Proxy
|
10
12
|
import java.util.concurrent.CountDownLatch
|
11
13
|
import java.util.concurrent.TimeUnit
|
12
14
|
|
13
15
|
private const val LOG_TAG = "DetoxRNLoading"
|
14
16
|
|
17
|
+
private const val REACT_INSTANCE_EVENT_LISTENER_CLASS = "com.facebook.react.ReactInstanceEventListener"
|
18
|
+
private const val REACT_INSTANCE_EVENT_LISTENER_CLASS_COMPAT = "com.facebook.react.ReactInstanceManager\$ReactInstanceEventListener"
|
19
|
+
|
15
20
|
open class ReactNativeLoadingMonitor(
|
16
21
|
private val instrumentation: Instrumentation,
|
17
22
|
private val rnApplication: ReactApplication,
|
18
23
|
private val previousReactContext: ReactContext?,
|
19
24
|
private val config: DetoxConfig = DetoxConfig.CONFIG) {
|
20
|
-
val countDownLatch = CountDownLatch(1)
|
25
|
+
private val countDownLatch = CountDownLatch(1)
|
21
26
|
|
22
27
|
fun getNewContext(): ReactContext? {
|
23
28
|
subscribeToNewRNContextUpdates()
|
@@ -35,13 +40,9 @@ open class ReactNativeLoadingMonitor(
|
|
35
40
|
return@Runnable
|
36
41
|
}
|
37
42
|
|
38
|
-
rnInstanceManager
|
39
|
-
|
40
|
-
|
41
|
-
rnInstanceManager.removeReactInstanceEventListener(this)
|
42
|
-
countDownLatch.countDown()
|
43
|
-
}
|
44
|
-
})
|
43
|
+
subscribeAsyncRNContextHandler(rnInstanceManager) {
|
44
|
+
countDownLatch.countDown()
|
45
|
+
}
|
45
46
|
})
|
46
47
|
}
|
47
48
|
|
@@ -81,3 +82,48 @@ open class ReactNativeLoadingMonitor(
|
|
81
82
|
return rnInstanceManager.currentReactContext
|
82
83
|
}
|
83
84
|
}
|
85
|
+
|
86
|
+
private interface DummyListenerIdentifier
|
87
|
+
|
88
|
+
/**
|
89
|
+
* This baby bridges over RN's breaking change introduced in version 0.68:
|
90
|
+
* `ReactInstanceManager$ReactInstanceEventListener` was extracted onto a separate interface, having
|
91
|
+
* `ReactInstanceManager.{add|remove}ReactInstanceEventLister()` changing their signature to use it, accordingly.
|
92
|
+
*
|
93
|
+
* This made us resort to a solution based on dynamic proxies, because - depending on RN's version (at runtime), we
|
94
|
+
* need to dynamically decide what interface we "extend" (or actually shadow) via the proxy.
|
95
|
+
*
|
96
|
+
* @see RNDiff https://github.com/facebook/react-native/compare/v0.67.4..v0.68.0#diff-2f01f0cd7ff8c9ea58f12ef0eff5fa8250cad144dd5490598d80d8e9e743458aR1009
|
97
|
+
* @see DynamicProxies https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html
|
98
|
+
*/
|
99
|
+
private fun subscribeAsyncRNContextHandler(rnInstanceManager: ReactInstanceManager, onReactContextInitialized: () -> Any) {
|
100
|
+
val listenerClass = resolveListenerClass()
|
101
|
+
val proxyInterfaces = arrayOf(
|
102
|
+
listenerClass,
|
103
|
+
DummyListenerIdentifier::class.java // In order to be able to implement equals()
|
104
|
+
)
|
105
|
+
val listener = Proxy.newProxyInstance(listenerClass.classLoader, proxyInterfaces) { listener, method, args ->
|
106
|
+
Log.d(LOG_TAG, "Listener-proxy method called: ${method.name}")
|
107
|
+
|
108
|
+
val result = when (method.name) {
|
109
|
+
"onReactContextInitialized" -> {
|
110
|
+
Log.i(LOG_TAG, "Got new RN-context async'ly through listener")
|
111
|
+
Reflect.on(rnInstanceManager).call("removeReactInstanceEventListener", listener)
|
112
|
+
onReactContextInitialized()
|
113
|
+
}
|
114
|
+
"equals" -> {
|
115
|
+
val candidate = args[0]
|
116
|
+
candidate is DummyListenerIdentifier
|
117
|
+
}
|
118
|
+
else -> Any()
|
119
|
+
}
|
120
|
+
|
121
|
+
result
|
122
|
+
}
|
123
|
+
Reflect.on(rnInstanceManager).call("addReactInstanceEventListener", listener)
|
124
|
+
}
|
125
|
+
|
126
|
+
private fun resolveListenerClass(): Class<*> {
|
127
|
+
val className = if (ReactNativeInfo.rnVersion().minor >= 68) REACT_INSTANCE_EVENT_LISTENER_CLASS else REACT_INSTANCE_EVENT_LISTENER_CLASS_COMPAT
|
128
|
+
return Class.forName(className)
|
129
|
+
}
|
@@ -1,20 +1,26 @@
|
|
1
1
|
package com.wix.detox.reactnative.idlingresources
|
2
2
|
|
3
3
|
class IdlingResourceDescription private constructor(
|
4
|
-
|
5
|
-
|
6
|
-
fun json(): Map<String, Any> = mutableMapOf<String, Any>("name" to name)
|
7
|
-
.apply { if (description.isNotEmpty()) put("description", description) }
|
4
|
+
private val name: String,
|
5
|
+
private val description: Map<String, Any>) {
|
8
6
|
|
9
|
-
|
10
|
-
|
7
|
+
fun json() =
|
8
|
+
mutableMapOf<String, Any>("name" to name)
|
9
|
+
.apply {
|
10
|
+
if (description.isNotEmpty()) put("description", description)
|
11
|
+
}.toMap()
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
override fun equals(other: Any?) =
|
14
|
+
(other is IdlingResourceDescription && other.json() == this.json())
|
15
|
+
|
16
|
+
override fun hashCode(): Int =
|
17
|
+
(31 * name.hashCode() + description.hashCode())
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
19
|
+
data class Builder(
|
20
|
+
var name: String = "unknown",
|
21
|
+
var description: MutableMap<String, Any> = mutableMapOf()) {
|
22
|
+
fun name(value: String) = apply { name = value }
|
23
|
+
fun addDescription(key: String, value: Any) = apply { description[key] = value }
|
24
|
+
fun build() = IdlingResourceDescription(name, description)
|
25
|
+
}
|
20
26
|
}
|
@@ -69,42 +69,13 @@ public class NetworkIdlingResource extends DetoxBaseIdlingResource implements Ch
|
|
69
69
|
|
70
70
|
@NotNull
|
71
71
|
@Override
|
72
|
-
public IdlingResourceDescription getDescription() {
|
72
|
+
public synchronized IdlingResourceDescription getDescription() {
|
73
73
|
return new IdlingResourceDescription.Builder()
|
74
74
|
.name("network")
|
75
75
|
.addDescription("urls", new ArrayList<>(busyResources))
|
76
76
|
.build();
|
77
77
|
}
|
78
78
|
|
79
|
-
@Override
|
80
|
-
protected boolean checkIdle() {
|
81
|
-
boolean idle = true;
|
82
|
-
busyResources.clear();
|
83
|
-
List<Call> calls = dispatcher.runningCalls();
|
84
|
-
for (Call call : calls) {
|
85
|
-
idle = false;
|
86
|
-
String url = call.request().url().toString();
|
87
|
-
for (Pattern pattern : blacklist) {
|
88
|
-
if (pattern.matcher(url).matches()) {
|
89
|
-
idle = true;
|
90
|
-
break;
|
91
|
-
}
|
92
|
-
}
|
93
|
-
if (!idle) {
|
94
|
-
busyResources.add(call.request().url().toString());
|
95
|
-
}
|
96
|
-
}
|
97
|
-
if (!idle) {
|
98
|
-
Choreographer.getInstance().postFrameCallback(this);
|
99
|
-
Log.i(LOG_TAG, "Network is busy");
|
100
|
-
} else {
|
101
|
-
if (callback != null) {
|
102
|
-
callback.onTransitionToIdle();
|
103
|
-
}
|
104
|
-
}
|
105
|
-
return idle;
|
106
|
-
}
|
107
|
-
|
108
79
|
@Override
|
109
80
|
public void registerIdleTransitionCallback(ResourceCallback callback) {
|
110
81
|
this.callback = callback;
|
@@ -116,10 +87,42 @@ public class NetworkIdlingResource extends DetoxBaseIdlingResource implements Ch
|
|
116
87
|
isIdleNow();
|
117
88
|
}
|
118
89
|
|
90
|
+
@Override
|
91
|
+
protected synchronized boolean checkIdle() {
|
92
|
+
busyResources.clear();
|
93
|
+
|
94
|
+
List<Call> calls = dispatcher.runningCalls();
|
95
|
+
for (Call call: calls) {
|
96
|
+
final String url = call.request().url().toString();
|
97
|
+
|
98
|
+
if (!isUrlBlacklisted(url)) {
|
99
|
+
busyResources.add(url);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
if (!busyResources.isEmpty()) {
|
104
|
+
Log.i(LOG_TAG, "Network is busy, with " + busyResources.size() + " in-flight calls");
|
105
|
+
Choreographer.getInstance().postFrameCallback(this);
|
106
|
+
return false;
|
107
|
+
}
|
108
|
+
|
109
|
+
notifyIdle();
|
110
|
+
return true;
|
111
|
+
}
|
112
|
+
|
119
113
|
@Override
|
120
114
|
protected void notifyIdle() {
|
121
115
|
if (callback != null) {
|
122
116
|
callback.onTransitionToIdle();
|
123
117
|
}
|
124
118
|
}
|
119
|
+
|
120
|
+
private boolean isUrlBlacklisted(String url) {
|
121
|
+
for (Pattern pattern: blacklist) {
|
122
|
+
if (pattern.matcher(url).matches()) {
|
123
|
+
return true;
|
124
|
+
}
|
125
|
+
}
|
126
|
+
return false;
|
127
|
+
}
|
125
128
|
}
|
@@ -1,43 +1,23 @@
|
|
1
|
+
|
1
2
|
package com.wix.detox.reactnative.idlingresources.timers
|
2
3
|
|
3
|
-
import com.facebook.react.bridge.NativeModule
|
4
4
|
import com.facebook.react.bridge.ReactContext
|
5
|
-
import com.
|
6
|
-
import com.wix.detox.reactnative.helpers.RNHelpers
|
7
|
-
import org.joor.Reflect
|
5
|
+
import com.facebook.react.modules.core.TimingModule
|
8
6
|
|
9
7
|
private const val BUSY_WINDOW_THRESHOLD = 1500L
|
10
8
|
|
11
|
-
private class RN62TimingModuleReflected(private val timingModule: NativeModule) {
|
12
|
-
fun hasActiveTimers(): Boolean = Reflect.on(timingModule).call("hasActiveTimersInRange", BUSY_WINDOW_THRESHOLD).get()
|
13
|
-
}
|
14
|
-
|
15
9
|
/**
|
16
10
|
* Delegates the interrogation to the native module itself, added
|
17
11
|
* [here](https://github.com/facebook/react-native/pull/27539) in the context
|
18
12
|
* of RN v0.62 (followed by a previous refactor and rename of the class).
|
19
13
|
*/
|
20
|
-
|
21
|
-
|
22
|
-
should) remove any usage of reflection here.
|
23
|
-
That includes the unit test's stub being used for that reason in particular.
|
24
|
-
""")
|
25
|
-
class DelegatedIdleInterrogationStrategy(timingModule: NativeModule): IdleInterrogationStrategy {
|
26
|
-
private val timingModuleReflected = RN62TimingModuleReflected(timingModule)
|
27
|
-
|
28
|
-
override fun isIdleNow(): Boolean = !timingModuleReflected.hasActiveTimers()
|
14
|
+
class DelegatedIdleInterrogationStrategy(private val timingModule: TimingModule): IdleInterrogationStrategy {
|
15
|
+
override fun isIdleNow(): Boolean = !timingModule.hasActiveTimersInRange(BUSY_WINDOW_THRESHOLD)
|
29
16
|
|
30
17
|
companion object {
|
31
|
-
fun
|
32
|
-
val
|
33
|
-
|
34
|
-
|
35
|
-
return try {
|
36
|
-
module.javaClass.getDeclaredMethod("hasActiveTimersInRange", Long::class.java)
|
37
|
-
DelegatedIdleInterrogationStrategy(module)
|
38
|
-
} catch (ex: Exception) {
|
39
|
-
null
|
40
|
-
}
|
18
|
+
fun create(reactContext: ReactContext): DelegatedIdleInterrogationStrategy {
|
19
|
+
val timingModule = reactContext.getNativeModule(TimingModule::class.java)!!
|
20
|
+
return DelegatedIdleInterrogationStrategy(timingModule)
|
41
21
|
}
|
42
22
|
}
|
43
23
|
}
|
@@ -1,6 +1,5 @@
|
|
1
1
|
package com.wix.detox.reactnative.idlingresources.timers
|
2
2
|
|
3
|
-
import android.util.Log
|
4
3
|
import com.facebook.react.bridge.ReactContext
|
5
4
|
import com.wix.detox.common.UIThread
|
6
5
|
import java.util.concurrent.Callable
|
@@ -13,14 +12,5 @@ fun getInterrogationStrategy(reactContext: ReactContext): IdleInterrogationStrat
|
|
13
12
|
// Getting a native-module (inside) also initializes it if needed. That has to run on a
|
14
13
|
// looper thread, and the easiest to make sure that happens is to use the main thread.
|
15
14
|
UIThread.runSync(Callable {
|
16
|
-
DelegatedIdleInterrogationStrategy.
|
17
|
-
return@Callable it
|
18
|
-
}
|
19
|
-
|
20
|
-
DefaultIdleInterrogationStrategy.createIfSupported(reactContext)?.let {
|
21
|
-
return@Callable it
|
22
|
-
}
|
23
|
-
|
24
|
-
Log.e(TimersIdlingResource.LOG_TAG, "Failed to determine proper implementation-strategy for timers idling resource")
|
25
|
-
null
|
15
|
+
return@Callable DelegatedIdleInterrogationStrategy.create(reactContext)
|
26
16
|
})
|
@@ -2,6 +2,7 @@ package com.wix.detox.reactnative.idlingresources.uimodule
|
|
2
2
|
|
3
3
|
import android.util.Log
|
4
4
|
import android.view.View
|
5
|
+
import com.facebook.react.uimanager.IllegalViewOperationException
|
5
6
|
import com.wix.detox.common.DetoxLog.Companion.LOG_TAG
|
6
7
|
import com.wix.detox.reactnative.ReactNativeInfo
|
7
8
|
import java.lang.ref.WeakReference
|
@@ -19,9 +20,7 @@ class RN66Workaround {
|
|
19
20
|
fun isScarceUISwitchCommandStuckInQueue(uiManagerModuleReflected: UIManagerModuleReflected): Boolean {
|
20
21
|
var isStuckSwitchOperation = false
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
if (rnVersion.minor >= 66 && uiManagerModuleReflected.getUIOpsCount() >= 1) {
|
23
|
+
if (isRelevantRNVersion() && uiManagerModuleReflected.getUIOpsCount() >= 1) {
|
25
24
|
val nextUIOperation = uiManagerModuleReflected.getNextUIOpReflected()
|
26
25
|
val view = getUIOpView(uiManagerModuleReflected, nextUIOperation)
|
27
26
|
val isReactSwitch = isReactSwitch(view)
|
@@ -46,10 +45,20 @@ class RN66Workaround {
|
|
46
45
|
return isStuckSwitchOperation
|
47
46
|
}
|
48
47
|
|
48
|
+
private fun isRelevantRNVersion(): Boolean {
|
49
|
+
val rnVersion = ReactNativeInfo.rnVersion()
|
50
|
+
return rnVersion.minor == 66 || (rnVersion.minor == 67 && rnVersion.patch < 4)
|
51
|
+
}
|
52
|
+
|
49
53
|
private fun getUIOpView(uiManagerModuleReflected: UIManagerModuleReflected, uiOperation: DispatchCommandOperationReflected?): View? {
|
50
54
|
val nativeViewHierarchyManager = uiManagerModuleReflected.nativeViewHierarchyManager() ?: return null
|
51
55
|
val tag = uiOperation?.tag ?: return null
|
52
|
-
return
|
56
|
+
return try {
|
57
|
+
nativeViewHierarchyManager.getViewClass(tag)
|
58
|
+
} catch(e: IllegalViewOperationException) {
|
59
|
+
Log.e(LOG_TAG, "failed to get view from tag ", e.cause)
|
60
|
+
null
|
61
|
+
}
|
53
62
|
}
|
54
63
|
|
55
64
|
private fun isReactSwitch(view: View?) = try {
|
@@ -5,7 +5,6 @@ public interface DetoxErrors {
|
|
5
5
|
public DetoxRuntimeException(Throwable cause) {
|
6
6
|
super(cause);
|
7
7
|
}
|
8
|
-
|
9
8
|
public DetoxRuntimeException(String message) {
|
10
9
|
super(message);
|
11
10
|
}
|
@@ -31,5 +30,9 @@ public interface DetoxErrors {
|
|
31
30
|
public DetoxIllegalArgumentException(String message) {
|
32
31
|
super(message);
|
33
32
|
}
|
33
|
+
|
34
|
+
public DetoxIllegalArgumentException(Throwable cause) {
|
35
|
+
super(cause);
|
36
|
+
}
|
34
37
|
}
|
35
38
|
}
|
@@ -1,10 +1,11 @@
|
|
1
|
+
|
1
2
|
package com.wix.detox.espresso
|
2
3
|
|
3
4
|
import androidx.test.espresso.UiController
|
4
5
|
import com.wix.detox.common.proxy.CallInfo
|
5
6
|
import com.wix.detox.common.proxy.SpyingInvocationHandler
|
6
7
|
import com.wix.detox.common.proxy.MethodsSpy
|
7
|
-
import com.wix.detox.espresso.common.utils.getUiController
|
8
|
+
import com.wix.detox.espresso.action.common.utils.getUiController
|
8
9
|
import org.joor.Reflect
|
9
10
|
|
10
11
|
class UiControllerSpy: MethodsSpy("uiController") {
|