detox 20.27.7-smoke.0 → 20.31.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. package/Detox-android/com/wix/detox/{20.27.7-smoke.0/detox-20.27.7-smoke.0-sources.jar → 20.30.0/detox-20.30.0-sources.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0-sources.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0-sources.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0-sources.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0-sources.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0.aar +0 -0
  7. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0.aar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0.aar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0.aar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0.aar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/{20.27.7-smoke.0/detox-20.27.7-smoke.0.pom → 20.30.0/detox-20.30.0.pom} +2 -2
  12. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0.pom.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0.pom.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0.pom.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.30.0/detox-20.30.0.pom.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  17. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  18. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  19. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  20. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  21. package/Detox-ios-framework.tbz +0 -0
  22. package/Detox-ios-src.tbz +0 -0
  23. package/Detox-ios-xcuitest.tbz +0 -0
  24. package/android/build.gradle +5 -5
  25. package/android/detox/proguard-rules-app.pro +3 -0
  26. package/android/detox/src/full/java/com/wix/detox/espresso/EspressoDetox.java +54 -3
  27. package/android/detox/src/full/java/com/wix/detox/espresso/UiAutomatorHelper.java +11 -0
  28. package/android/detox/src/full/java/com/wix/detox/espresso/matcher/IsDisplayingAtLeastDetoxMatcher.kt +2 -2
  29. package/android/detox/src/full/java/com/wix/detox/espresso/web/DetoxWebAtomMatcher.java +4 -7
  30. package/android/detox/src/full/java/com/wix/detox/espresso/web/WebElement.java +0 -5
  31. package/android/detox/src/full/java/com/wix/detox/espresso/web/WebViewElement.java +33 -8
  32. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeExtension.kt +6 -11
  33. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeInfo.kt +4 -11
  34. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/DetoxIdlingResource.kt +42 -0
  35. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/ReactNativeIdlingResources.kt +145 -0
  36. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/animations/AnimatedModuleIdlingResource.kt +61 -0
  37. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/bridge/BridgeIdlingResource.kt +72 -0
  38. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/DetoxIdlingResourceFactory.kt +32 -0
  39. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/IdlingResourcesName.kt +10 -0
  40. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/factory/LooperName.kt +6 -0
  41. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/looper/MQThreadsReflector.kt +47 -0
  42. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/network/NetworkIdlingResource.kt +105 -0
  43. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/{NetworkingModuleReflected.kt → network/NetworkingModuleReflected.kt} +1 -1
  44. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/{AsyncStorageIdlingResource.kt → storage/AsyncStorageIdlingResource.kt} +33 -35
  45. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/{SerialExecutorReflected.kt → storage/SerialExecutorReflected.kt} +1 -1
  46. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/TimersIdlingResource.kt +21 -19
  47. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/UIManagerModuleReflected.kt +19 -27
  48. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/UIModuleIdlingResource.kt +5 -17
  49. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/AsyncStorageIdlingResourceTest.kt +248 -0
  50. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/NetworkIdlingResourcesTest.kt +5 -1
  51. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/SerialExecutorReflectedSpec.kt +1 -0
  52. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/timers/TimersIdlingResourceTest.kt +212 -0
  53. package/android/gradle/wrapper/gradle-wrapper.properties +1 -1
  54. package/android/rninfo.gradle +31 -24
  55. package/android/settings.gradle +11 -6
  56. package/detox.d.ts +39 -0
  57. package/package.json +11 -8
  58. package/scripts/postinstall.js +2 -2
  59. package/scripts/updateGradle.js +41 -8
  60. package/src/DetoxWorker.js +11 -6
  61. package/src/android/espressoapi/EspressoDetox.js +83 -0
  62. package/src/copilot/DetoxCopilot.js +3 -15
  63. package/src/copilot/detoxCopilotFrameworkDriver.js +27 -15
  64. package/src/devices/runtime/RuntimeDevice.js +11 -0
  65. package/src/devices/runtime/drivers/DeviceDriverBase.js +8 -0
  66. package/src/devices/runtime/drivers/android/AndroidDriver.js +16 -0
  67. package/src/devices/runtime/drivers/ios/SimulatorDriver.js +35 -0
  68. package/src/utils/assertArgument.js +9 -0
  69. package/src/utils/invocationTraceDescriptions.js +1 -0
  70. package/src/utils/mapDeviceLongPressArguments.js +56 -0
  71. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0-sources.jar.md5 +0 -1
  72. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0-sources.jar.sha1 +0 -1
  73. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0-sources.jar.sha256 +0 -1
  74. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0-sources.jar.sha512 +0 -1
  75. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0.aar +0 -0
  76. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0.aar.md5 +0 -1
  77. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0.aar.sha1 +0 -1
  78. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0.aar.sha256 +0 -1
  79. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0.aar.sha512 +0 -1
  80. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0.pom.md5 +0 -1
  81. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0.pom.sha1 +0 -1
  82. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0.pom.sha256 +0 -1
  83. package/Detox-android/com/wix/detox/20.27.7-smoke.0/detox-20.27.7-smoke.0.pom.sha512 +0 -1
  84. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0-sources.jar +0 -0
  85. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0-sources.jar.md5 +0 -1
  86. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0-sources.jar.sha1 +0 -1
  87. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0-sources.jar.sha256 +0 -1
  88. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0-sources.jar.sha512 +0 -1
  89. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0.aar +0 -0
  90. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0.aar.md5 +0 -1
  91. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0.aar.sha1 +0 -1
  92. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0.aar.sha256 +0 -1
  93. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0.aar.sha512 +0 -1
  94. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0.pom +0 -100
  95. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0.pom.md5 +0 -1
  96. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0.pom.sha1 +0 -1
  97. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0.pom.sha256 +0 -1
  98. package/Detox-android/com/wix/detox-legacy/20.27.7-smoke.0/detox-legacy-20.27.7-smoke.0.pom.sha512 +0 -1
  99. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml +0 -13
  100. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.md5 +0 -1
  101. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha1 +0 -1
  102. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha256 +0 -1
  103. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha512 +0 -1
  104. package/android/detox/src/full/java/com/wix/detox/espresso/web/DetoxDriverAtoms.java +0 -642
  105. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeIdlingResources.kt +0 -229
  106. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/AnimatedModuleIdlingResource.java +0 -215
  107. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/BridgeIdlingResource.java +0 -94
  108. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/DetoxBaseIdlingResource.java +0 -29
  109. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/NetworkIdlingResource.java +0 -134
  110. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/DelegatedIdleInterrogationStrategy.kt +0 -23
  111. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/IdleInterrogationStrategy.kt +0 -16
  112. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/RN66Workaround.kt +0 -71
  113. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/AsyncStorageIdlingResourceSpec.kt +0 -227
  114. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/timers/DelegatedIdleInterrogationStrategySpec.kt +0 -47
  115. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/timers/TimersIdlingResourceSpec.kt +0 -189
@@ -0,0 +1,145 @@
1
+ package com.wix.detox.reactnative.idlingresources
2
+
3
+ import android.os.Looper
4
+ import android.util.Log
5
+ import androidx.test.espresso.Espresso
6
+ import androidx.test.espresso.IdlingRegistry
7
+ import androidx.test.espresso.base.IdlingResourceRegistry
8
+ import com.facebook.react.bridge.ReactContext
9
+ import com.wix.detox.LaunchArgs
10
+ import com.wix.detox.reactnative.idlingresources.factory.DetoxIdlingResourceFactory
11
+ import com.wix.detox.reactnative.idlingresources.factory.IdlingResourcesName
12
+ import com.wix.detox.reactnative.idlingresources.factory.LooperName
13
+ import com.wix.detox.reactnative.idlingresources.looper.MQThreadsReflector
14
+ import com.wix.detox.reactnative.idlingresources.network.NetworkIdlingResource
15
+ import kotlinx.coroutines.runBlocking
16
+ import org.joor.Reflect
17
+
18
+
19
+ private const val LOG_TAG = "DetoxRNIdleRes"
20
+
21
+ class ReactNativeIdlingResources(
22
+ private val reactContext: ReactContext,
23
+ private var launchArgs: LaunchArgs,
24
+ private val idlingResourcesFactory: DetoxIdlingResourceFactory = DetoxIdlingResourceFactory(reactContext)
25
+ ) {
26
+
27
+ private val idlingResources = mutableMapOf<IdlingResourcesName, DetoxIdlingResource>()
28
+ private val loopers = mutableMapOf<LooperName, Looper>()
29
+
30
+ fun registerAll() {
31
+ runBlocking {
32
+ Log.i(LOG_TAG, "Setting up Espresso Idling Resources for React Native")
33
+ unregisterAll()
34
+
35
+ setupUrlBlacklist()
36
+ setupMQThreadsInterrogators()
37
+ syncIdlingResources()
38
+ setupIdlingResources()
39
+ syncIdlingResources()
40
+ }
41
+ }
42
+
43
+ fun unregisterAll() {
44
+ unregisterMQThreadsInterrogators()
45
+ unregisterIdlingResources()
46
+ }
47
+
48
+ fun pauseNetworkSynchronization() = pauseIdlingResource(IdlingResourcesName.Network)
49
+ fun resumeNetworkSynchronization() {
50
+ resumeIdlingResource(IdlingResourcesName.Network)
51
+ }
52
+
53
+ fun pauseRNTimersIdlingResource() = pauseIdlingResource(IdlingResourcesName.Timers)
54
+ fun resumeRNTimersIdlingResource() = resumeIdlingResource(IdlingResourcesName.Timers)
55
+ fun pauseUIIdlingResource() = pauseIdlingResource(IdlingResourcesName.UIModule)
56
+ fun resumeUIIdlingResource() = resumeIdlingResource(IdlingResourcesName.UIModule)
57
+
58
+ fun setBlacklistUrls(urlList: String) {
59
+ setIdlingResourceBlacklist(urlList)
60
+ }
61
+
62
+ private fun setIdlingResourceBlacklist(urlList: String) {
63
+ val urlArray = toFormattedUrlArray(urlList)
64
+ NetworkIdlingResource.setURLBlacklist(urlArray)
65
+ }
66
+
67
+ private fun setupMQThreadsInterrogators() {
68
+ setupMQThreadsInterrogator(LooperName.JS)
69
+ setupMQThreadsInterrogator(LooperName.NativeModules)
70
+ }
71
+
72
+ private fun setupUrlBlacklist() {
73
+ if (launchArgs.hasURLBlacklist()) {
74
+ val blacklistUrls = launchArgs.urlBlacklist
75
+ setIdlingResourceBlacklist(blacklistUrls)
76
+ }
77
+ }
78
+
79
+ private fun setupMQThreadsInterrogator(looperName: LooperName) {
80
+ val mqThreadsReflector = MQThreadsReflector(reactContext)
81
+ val looper = when (looperName) {
82
+ LooperName.JS -> mqThreadsReflector.getJSMQueue()?.getLooper()
83
+ LooperName.NativeModules -> mqThreadsReflector.getNativeModulesQueue()?.getLooper()
84
+ }
85
+
86
+ looper?.let {
87
+ IdlingRegistry.getInstance().registerLooperAsIdlingResource(it)
88
+ loopers[looperName] = it
89
+ }
90
+ }
91
+
92
+ private suspend fun setupIdlingResources() {
93
+ idlingResources.putAll(idlingResourcesFactory.create())
94
+ idlingResources.forEach { (_, idlingResource) ->
95
+ IdlingRegistry.getInstance().register(idlingResource)
96
+ }
97
+ }
98
+
99
+ private fun syncIdlingResources() {
100
+ IdlingRegistry.getInstance().apply {
101
+ val irr: IdlingResourceRegistry =
102
+ Reflect.on(Espresso::class.java).field("baseRegistry").get()
103
+ irr.sync(this.resources, this.loopers)
104
+ }
105
+ }
106
+
107
+ private fun unregisterMQThreadsInterrogators() {
108
+ loopers.values.forEach {
109
+ IdlingRegistry.getInstance().unregisterLooperAsIdlingResource(it)
110
+ }
111
+ loopers.clear()
112
+ }
113
+
114
+ private fun unregisterIdlingResources() {
115
+ IdlingResourcesName.entries.forEach {
116
+ removeIdlingResource(it)
117
+ }
118
+ }
119
+
120
+ private fun pauseIdlingResource(idlingResourcesName: IdlingResourcesName) {
121
+ val idlingResource = idlingResources[idlingResourcesName]
122
+ idlingResource?.pause()
123
+ }
124
+
125
+ private fun resumeIdlingResource(idlingResourcesName: IdlingResourcesName) {
126
+ val idlingResource = idlingResources[idlingResourcesName]
127
+ idlingResource?.resume()
128
+ }
129
+
130
+ private fun removeIdlingResource(idlingResourcesName: IdlingResourcesName) {
131
+ val idlingResource = idlingResources[idlingResourcesName]
132
+ idlingResource?.let {
133
+ IdlingRegistry.getInstance().unregister(it)
134
+ idlingResource.onUnregistered()
135
+ idlingResources.remove(idlingResourcesName)
136
+ }
137
+ }
138
+
139
+ private fun toFormattedUrlArray(urlList: String): List<String> {
140
+ var formattedUrls = urlList
141
+ formattedUrls = formattedUrls.replace(Regex("""[()"]"""), "")
142
+ formattedUrls = formattedUrls.trim()
143
+ return formattedUrls.split(',')
144
+ }
145
+ }
@@ -0,0 +1,61 @@
1
+ package com.wix.detox.reactnative.idlingresources.animations
2
+
3
+ import android.util.Log
4
+ import android.view.Choreographer
5
+ import androidx.test.espresso.IdlingResource.ResourceCallback
6
+ import com.facebook.react.animated.NativeAnimatedModule
7
+ import com.facebook.react.bridge.ReactContext
8
+ import com.wix.detox.reactnative.idlingresources.DetoxIdlingResource
9
+
10
+ private const val LOG_TAG = "Detox"
11
+
12
+
13
+ /**
14
+ * Created by simonracz on 25/08/2017.
15
+ */
16
+ /**
17
+ *
18
+ * Espresso IdlingResource for React Native's Animated Module.
19
+ *
20
+ *
21
+ * Hooks up to React Native internals to monitor the state of the animations.
22
+ *
23
+ */
24
+ class AnimatedModuleIdlingResource(private val reactContext: ReactContext) : DetoxIdlingResource(),
25
+ Choreographer.FrameCallback {
26
+
27
+ override fun getName() = AnimatedModuleIdlingResource::class.java.name
28
+
29
+ override fun getDebugName(): String {
30
+ return "AnimatedModule"
31
+ }
32
+
33
+ override fun getBusyHint(): Map<String, Any> {
34
+ return mapOf("reason" to "Animations running on screen")
35
+ }
36
+
37
+ override fun checkIdle(): Boolean {
38
+ val animatedModule = reactContext.getNativeModule(NativeAnimatedModule::class.java)
39
+ val hasAnimations = animatedModule?.nodesManager?.hasActiveAnimations() ?: false
40
+
41
+ if (hasAnimations) {
42
+ Log.i(LOG_TAG, "AnimatedModule is busy.")
43
+ Choreographer.getInstance().postFrameCallback(this)
44
+ return false
45
+ }
46
+
47
+ notifyIdle()
48
+ return true
49
+ }
50
+
51
+ override fun registerIdleTransitionCallback(callback: ResourceCallback?) {
52
+ super.registerIdleTransitionCallback(callback)
53
+ Choreographer.getInstance().postFrameCallback(this)
54
+ }
55
+
56
+ override fun doFrame(frameTimeNanos: Long) {
57
+ isIdleNow
58
+ }
59
+ }
60
+
61
+
@@ -0,0 +1,72 @@
1
+ package com.wix.detox.reactnative.idlingresources.bridge
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener
5
+ import com.facebook.react.bridge.ReactContext
6
+ import com.wix.detox.reactnative.idlingresources.DetoxIdlingResource
7
+ import java.util.concurrent.atomic.AtomicBoolean
8
+
9
+ /**
10
+ * Created by simonracz on 01/06/2017.
11
+ */
12
+ /**
13
+ *
14
+ *
15
+ * IdlingResource for Espresso, which monitors the traffic of
16
+ * React Native's JS bridge.
17
+ *
18
+ */
19
+ class BridgeIdlingResource(private val reactContext: ReactContext) : DetoxIdlingResource(),
20
+ NotThreadSafeBridgeIdleDebugListener {
21
+ private val idleNow = AtomicBoolean(true)
22
+
23
+ init {
24
+ reactContext.catalystInstance.addBridgeIdleDebugListener(this)
25
+ }
26
+
27
+ fun onDetach() {
28
+ reactContext.catalystInstance.removeBridgeIdleDebugListener(this)
29
+ }
30
+
31
+ override fun getName(): String {
32
+ return BridgeIdlingResource::class.java.name
33
+ }
34
+
35
+ override fun getDebugName(): String {
36
+ return "bridge"
37
+ }
38
+
39
+ override fun getBusyHint(): Map<String, Any>? {
40
+ return null
41
+ }
42
+
43
+ override fun checkIdle(): Boolean {
44
+ val ret = idleNow.get()
45
+ if (!ret) {
46
+ Log.i(LOG_TAG, "JS Bridge is busy")
47
+ }
48
+ return ret
49
+ }
50
+
51
+ override fun onTransitionToBridgeIdle() {
52
+ idleNow.set(true)
53
+ notifyIdle()
54
+ }
55
+
56
+ override fun onTransitionToBridgeBusy() {
57
+ idleNow.set(false)
58
+ // Log.i(LOG_TAG, "JS Bridge transitions to busy.");
59
+ }
60
+
61
+ override fun onBridgeDestroyed() {
62
+ }
63
+
64
+ override fun onUnregistered() {
65
+ super.onUnregistered()
66
+ onDetach()
67
+ }
68
+
69
+ companion object {
70
+ private const val LOG_TAG = "Detox"
71
+ }
72
+ }
@@ -0,0 +1,32 @@
1
+ package com.wix.detox.reactnative.idlingresources.factory
2
+
3
+ import com.facebook.react.bridge.ReactContext
4
+ import com.wix.detox.reactnative.idlingresources.DetoxIdlingResource
5
+ import com.wix.detox.reactnative.idlingresources.animations.AnimatedModuleIdlingResource
6
+ import com.wix.detox.reactnative.idlingresources.bridge.BridgeIdlingResource
7
+ import com.wix.detox.reactnative.idlingresources.network.NetworkIdlingResource
8
+ import com.wix.detox.reactnative.idlingresources.storage.AsyncStorageIdlingResource
9
+ import com.wix.detox.reactnative.idlingresources.timers.TimersIdlingResource
10
+ import com.wix.detox.reactnative.idlingresources.uimodule.UIModuleIdlingResource
11
+ import kotlinx.coroutines.Dispatchers
12
+ import kotlinx.coroutines.withContext
13
+
14
+ class DetoxIdlingResourceFactory(private val reactContext: ReactContext) {
15
+ suspend fun create(): Map<IdlingResourcesName, DetoxIdlingResource> = withContext(Dispatchers.Main) {
16
+ val result = mutableMapOf(
17
+ IdlingResourcesName.Timers to TimersIdlingResource(reactContext),
18
+ IdlingResourcesName.RNBridge to BridgeIdlingResource(reactContext),
19
+ IdlingResourcesName.UIModule to UIModuleIdlingResource(reactContext),
20
+ IdlingResourcesName.Animations to AnimatedModuleIdlingResource(reactContext),
21
+ IdlingResourcesName.Network to NetworkIdlingResource(reactContext)
22
+ )
23
+
24
+ val asyncStorageIdlingResource = AsyncStorageIdlingResource.createIfNeeded(reactContext)
25
+ if (asyncStorageIdlingResource != null) {
26
+ result[IdlingResourcesName.AsyncStorage] = asyncStorageIdlingResource
27
+ }
28
+
29
+ return@withContext result
30
+ }
31
+ }
32
+
@@ -0,0 +1,10 @@
1
+ package com.wix.detox.reactnative.idlingresources.factory
2
+
3
+ enum class IdlingResourcesName {
4
+ Timers,
5
+ AsyncStorage,
6
+ RNBridge,
7
+ UIModule,
8
+ Animations,
9
+ Network
10
+ }
@@ -0,0 +1,6 @@
1
+ package com.wix.detox.reactnative.idlingresources.factory
2
+
3
+ enum class LooperName {
4
+ JS,
5
+ NativeModules
6
+ }
@@ -0,0 +1,47 @@
1
+ package com.wix.detox.reactnative.idlingresources.looper
2
+
3
+ import android.os.Looper
4
+ import android.util.Log
5
+ import com.facebook.react.bridge.ReactContext
6
+ import org.joor.Reflect
7
+ import org.joor.ReflectException
8
+
9
+
10
+ private const val LOG_TAG = "DetoxRNIdleRes"
11
+ private const val METHOD_GET_LOOPER = "getLooper"
12
+ private const val FIELD_NATIVE_MODULES_MSG_QUEUE = "mNativeModulesMessageQueueThread"
13
+ private const val FIELD_JS_MSG_QUEUE = "mJSMessageQueueThread"
14
+
15
+ internal class MQThreadsReflector(private val reactContext: ReactContext) {
16
+
17
+ fun getJSMQueue(): MQThreadReflected? {
18
+ return getQueue(FIELD_JS_MSG_QUEUE)
19
+ }
20
+
21
+ fun getNativeModulesQueue(): MQThreadReflected? {
22
+ return getQueue(FIELD_NATIVE_MODULES_MSG_QUEUE)
23
+ }
24
+
25
+ private fun getQueue(queueName: String): MQThreadReflected? {
26
+ try {
27
+ val queue = Reflect.on(reactContext).field(queueName).get() as Any?
28
+ return MQThreadReflected(queue, queueName)
29
+ } catch (e: ReflectException) {
30
+ Log.e(LOG_TAG, "Could not find queue: $queueName", e)
31
+ }
32
+ return null
33
+ }
34
+ }
35
+
36
+ internal class MQThreadReflected(private val queue: Any?, private val queueName: String) {
37
+ fun getLooper(): Looper? {
38
+ try {
39
+ if (queue != null) {
40
+ return Reflect.on(queue).call(METHOD_GET_LOOPER).get()
41
+ }
42
+ } catch (e: ReflectException) {
43
+ Log.e(LOG_TAG, "Could not find looper for queue: $queueName", e)
44
+ }
45
+ return null
46
+ }
47
+ }
@@ -0,0 +1,105 @@
1
+ package com.wix.detox.reactnative.idlingresources.network
2
+
3
+ import android.util.Log
4
+ import android.view.Choreographer
5
+ import androidx.test.espresso.IdlingResource.ResourceCallback
6
+ import com.facebook.react.bridge.ReactContext
7
+ import com.wix.detox.reactnative.idlingresources.DetoxIdlingResource
8
+ import okhttp3.Dispatcher
9
+ import java.util.regex.Pattern
10
+ import java.util.regex.PatternSyntaxException
11
+
12
+ /**
13
+ * Created by simonracz on 09/10/2017.
14
+ *
15
+ * Idling Resource which monitors React Native's OkHttpClient.
16
+ *
17
+ *
18
+ * Must call stop() on it, before removing it from Espresso.
19
+ */
20
+ class NetworkIdlingResource(private val dispatcher: Dispatcher) : DetoxIdlingResource(),
21
+ Choreographer.FrameCallback {
22
+ private val busyResources: MutableSet<String> = HashSet()
23
+
24
+ constructor(reactContext: ReactContext) : this(NetworkingModuleReflected(reactContext).getHttpClient()!!.dispatcher)
25
+
26
+ override fun getName(): String {
27
+ return NetworkIdlingResource::class.java.name
28
+ }
29
+
30
+ override fun getDebugName(): String {
31
+ return "network"
32
+ }
33
+
34
+ @Synchronized
35
+ override fun getBusyHint(): Map<String, Any> = mapOf("urls" to ArrayList(busyResources))
36
+
37
+ override fun registerIdleTransitionCallback(callback: ResourceCallback?) {
38
+ super.registerIdleTransitionCallback(callback)
39
+ Choreographer.getInstance().postFrameCallback(this)
40
+ }
41
+
42
+ override fun doFrame(frameTimeNanos: Long) {
43
+ isIdleNow
44
+ }
45
+
46
+ @Synchronized
47
+ override fun checkIdle(): Boolean {
48
+ busyResources.clear()
49
+
50
+ val calls = dispatcher.runningCalls()
51
+ for (call in calls) {
52
+ val url = call.request().url.toString()
53
+
54
+ if (!isUrlBlacklisted(url)) {
55
+ busyResources.add(url)
56
+ }
57
+ }
58
+
59
+ if (busyResources.isNotEmpty()) {
60
+ Log.i(LOG_TAG, "Network is busy, with " + busyResources.size + " in-flight calls")
61
+ Choreographer.getInstance().postFrameCallback(this)
62
+ return false
63
+ }
64
+
65
+ notifyIdle()
66
+ return true
67
+ }
68
+
69
+ private fun isUrlBlacklisted(url: String): Boolean {
70
+ for (pattern in blacklist) {
71
+ if (pattern.matcher(url).matches()) {
72
+ return true
73
+ }
74
+ }
75
+ return false
76
+ }
77
+
78
+ companion object {
79
+ private const val LOG_TAG = "Detox"
80
+
81
+ private val blacklist = ArrayList<Pattern>()
82
+
83
+ /**
84
+ * Must be called on the UI thread.
85
+ *
86
+ * @param urls list of regexes of blacklisted urls
87
+ */
88
+ @JvmStatic
89
+ fun setURLBlacklist(urls: List<String>?) {
90
+ blacklist.clear()
91
+ if (urls == null) return
92
+
93
+ for (url in urls) {
94
+ try {
95
+ blacklist.add(Pattern.compile(url))
96
+ } catch (e: PatternSyntaxException) {
97
+ Log.e(
98
+ LOG_TAG,
99
+ "Couldn't parse regular expression for Black list url: $url", e
100
+ )
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
@@ -1,4 +1,4 @@
1
- package com.wix.detox.reactnative.idlingresources
1
+ package com.wix.detox.reactnative.idlingresources.network
2
2
 
3
3
  import android.util.Log
4
4
  import com.facebook.react.bridge.ReactContext
@@ -1,16 +1,18 @@
1
- package com.wix.detox.reactnative.idlingresources
1
+ package com.wix.detox.reactnative.idlingresources.storage
2
2
 
3
3
  import android.util.Log
4
4
  import androidx.test.espresso.IdlingResource
5
5
  import com.facebook.react.bridge.NativeModule
6
6
  import com.facebook.react.bridge.ReactContext
7
- import com.wix.detox.espresso.idlingresources.DescriptiveIdlingResource
8
7
  import com.wix.detox.reactnative.helpers.RNHelpers
8
+ import com.wix.detox.reactnative.idlingresources.DetoxIdlingResource
9
9
  import org.joor.Reflect
10
10
  import java.util.concurrent.Executor
11
11
 
12
12
  private typealias SExecutorReflectedGenFnType = (executor: Executor) -> SerialExecutorReflected
13
- private val defaultSExecutorReflectedGenFn: SExecutorReflectedGenFnType = { executor: Executor -> SerialExecutorReflected(executor) }
13
+
14
+ private val defaultSExecutorReflectedGenFn: SExecutorReflectedGenFnType =
15
+ { executor: Executor -> SerialExecutorReflected(executor) }
14
16
 
15
17
  private class ModuleReflected(module: NativeModule, sexecutorReflectedGen: SExecutorReflectedGenFnType) {
16
18
  private val executorReflected: SerialExecutorReflected
@@ -25,17 +27,16 @@ private class ModuleReflected(module: NativeModule, sexecutorReflectedGen: SExec
25
27
  get() = executorReflected
26
28
  }
27
29
 
28
- open class AsyncStorageIdlingResource
29
- @JvmOverloads constructor(
30
- module: NativeModule,
31
- sexecutorReflectedGenFn: SExecutorReflectedGenFnType = defaultSExecutorReflectedGenFn)
32
- : DescriptiveIdlingResource {
30
+ class AsyncStorageIdlingResource
31
+ @JvmOverloads constructor(
32
+ module: NativeModule,
33
+ sexecutorReflectedGenFn: SExecutorReflectedGenFnType = defaultSExecutorReflectedGenFn
34
+ ) : DetoxIdlingResource() {
33
35
 
34
- open val logTag: String
36
+ val logTag: String
35
37
  get() = LOG_TAG
36
38
 
37
39
  private val moduleReflected = ModuleReflected(module, sexecutorReflectedGenFn)
38
- private var callback: IdlingResource.ResourceCallback? = null
39
40
  private var idleCheckTask: Runnable? = null
40
41
  private val idleCheckTaskImpl = Runnable {
41
42
  with(moduleReflected.sexecutor) {
@@ -44,7 +45,7 @@ open class AsyncStorageIdlingResource
44
45
  executeTask(idleCheckTask!!)
45
46
  } else {
46
47
  clearIdleCheckTask()
47
- callback?.onTransitionToIdle()
48
+ notifyIdle()
48
49
  }
49
50
  }
50
51
  }
@@ -54,22 +55,24 @@ open class AsyncStorageIdlingResource
54
55
  override fun getDebugName() = "io"
55
56
  override fun getBusyHint(): Map<String, Any>? = null
56
57
 
57
- override fun isIdleNow(): Boolean =
58
- checkIdle().also { idle ->
59
- if (!idle) {
60
- Log.d(logTag, "Async-storage is busy!")
61
- enqueueIdleCheckTask()
58
+
59
+ private fun checkIdleInternal(): Boolean =
60
+ with(moduleReflected.sexecutor) {
61
+ synchronized(executor()) {
62
+ !hasActiveTask() && !hasPendingTasks()
62
63
  }
63
64
  }
65
+
64
66
  override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
65
- this.callback = callback
67
+ super.registerIdleTransitionCallback(callback)
66
68
  enqueueIdleCheckTask()
67
69
  }
68
70
 
69
- private fun checkIdle(): Boolean =
70
- with(moduleReflected.sexecutor) {
71
- synchronized(executor()) {
72
- !hasActiveTask() && !hasPendingTasks()
71
+ override fun checkIdle(): Boolean =
72
+ checkIdleInternal().also { idle ->
73
+ if (!idle) {
74
+ Log.d(logTag, "Async-storage is busy!")
75
+ enqueueIdleCheckTask()
73
76
  }
74
77
  }
75
78
 
@@ -94,26 +97,21 @@ open class AsyncStorageIdlingResource
94
97
  companion object {
95
98
  private const val LOG_TAG = "AsyncStorageIR"
96
99
 
97
- fun createIfNeeded(reactContext: ReactContext, legacy: Boolean): AsyncStorageIdlingResource? {
98
- Log.d(LOG_TAG, "Checking whether a custom IR for Async-Storage is required... (legacy=$legacy)")
100
+ fun createIfNeeded(reactContext: ReactContext): AsyncStorageIdlingResource? {
101
+ Log.d(LOG_TAG, "Checking whether a custom IR for Async-Storage is required...")
99
102
 
100
- return RNHelpers.getNativeModule(reactContext, className(legacy))?.let { module ->
101
- Log.d(LOG_TAG, "IR for Async-Storage is required! (legacy=$legacy)")
102
- createInstance(module, legacy)
103
+ return RNHelpers.getNativeModule(reactContext, className())?.let { module ->
104
+ Log.d(LOG_TAG, "IR for Async-Storage is required!")
105
+ createInstance(module)
103
106
  }
104
107
  }
105
108
 
106
- private fun className(legacy: Boolean): String {
107
- val packageName = if (legacy) "com.facebook.react.modules.storage" else "com.reactnativecommunity.asyncstorage"
109
+ private fun className(): String {
110
+ val packageName = "com.reactnativecommunity.asyncstorage"
108
111
  return "$packageName.AsyncStorageModule"
109
112
  }
110
113
 
111
- private fun createInstance(module: NativeModule, legacy: Boolean) =
112
- if (legacy) AsyncStorageIdlingResourceLegacy(module) else AsyncStorageIdlingResource(module)
114
+ private fun createInstance(module: NativeModule) =
115
+ AsyncStorageIdlingResource(module)
113
116
  }
114
117
  }
115
-
116
- class AsyncStorageIdlingResourceLegacy(module: NativeModule): AsyncStorageIdlingResource(module) {
117
- override val logTag: String
118
- get() = super.logTag + "Legacy"
119
- }
@@ -1,4 +1,4 @@
1
- package com.wix.detox.reactnative.idlingresources
1
+ package com.wix.detox.reactnative.idlingresources.storage
2
2
 
3
3
  import org.joor.Reflect
4
4
  import java.util.concurrent.Executor