detox 21.0.0-rc.1 → 21.0.0-rc.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (202) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.js +1 -40
  3. package/Detox-android/com/wix/detox/{21.0.0-rc.1/detox-21.0.0-rc.1-javadoc.jar → 21.0.0-rc.10/detox-21.0.0-rc.10-javadoc.jar} +0 -0
  4. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10-javadoc.jar.md5 +1 -0
  5. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10-javadoc.jar.sha1 +1 -0
  6. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10-javadoc.jar.sha256 +1 -0
  7. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10-javadoc.jar.sha512 +1 -0
  8. package/Detox-android/com/wix/detox/{21.0.0-rc.1/detox-21.0.0-rc.1-sources.jar → 21.0.0-rc.10/detox-21.0.0-rc.10-sources.jar} +0 -0
  9. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10-sources.jar.md5 +1 -0
  10. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10-sources.jar.sha1 +1 -0
  11. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10-sources.jar.sha256 +1 -0
  12. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10-sources.jar.sha512 +1 -0
  13. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10.aar +0 -0
  14. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10.aar.md5 +1 -0
  15. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10.aar.sha1 +1 -0
  16. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10.aar.sha256 +1 -0
  17. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10.aar.sha512 +1 -0
  18. package/Detox-android/com/wix/detox/{21.0.0-rc.1/detox-21.0.0-rc.1.pom → 21.0.0-rc.10/detox-21.0.0-rc.10.pom} +1 -7
  19. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10.pom.md5 +1 -0
  20. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10.pom.sha1 +1 -0
  21. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10.pom.sha256 +1 -0
  22. package/Detox-android/com/wix/detox/21.0.0-rc.10/detox-21.0.0-rc.10.pom.sha512 +1 -0
  23. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  24. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  25. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  26. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  27. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  28. package/Detox-ios-framework.tbz +0 -0
  29. package/Detox-ios-src.tbz +0 -0
  30. package/Detox-ios-xcuitest.tbz +0 -0
  31. package/android/build.gradle +20 -10
  32. package/android/detox/build.gradle +24 -12
  33. package/android/detox/src/full/java/com/wix/detox/espresso/DetoxAssertion.java +44 -25
  34. package/android/detox/src/full/java/com/wix/detox/espresso/DetoxMatcher.java +12 -12
  35. package/android/detox/src/full/java/com/wix/detox/espresso/EspressoDetox.java +6 -7
  36. package/android/detox/src/full/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +2 -2
  37. package/android/detox/src/full/java/com/wix/detox/espresso/action/GetAttributesAction.kt +34 -35
  38. package/android/detox/src/full/java/com/wix/detox/espresso/common/MaterialSliderHelper.kt +21 -0
  39. package/android/detox/src/full/java/com/wix/detox/espresso/common/{SliderHelper.kt → ReactSliderHelper.kt} +7 -6
  40. package/android/detox/src/full/java/com/wix/detox/espresso/matcher/RegexMatcher.kt +56 -0
  41. package/android/detox/src/full/java/com/wix/detox/espresso/matcher/ViewMatchers.kt +18 -6
  42. package/android/detox/src/full/java/com/wix/detox/espresso/performer/MultipleViewsActionPerformer.kt +43 -0
  43. package/android/detox/src/full/java/com/wix/detox/espresso/performer/SingleViewActionPerformer.kt +19 -0
  44. package/android/detox/src/full/java/com/wix/detox/espresso/performer/ViewActionPerformer.kt +24 -0
  45. package/android/detox/src/full/java/com/wix/detox/espresso/web/WebElement.java +4 -4
  46. package/android/detox/src/full/java/com/wix/invoke/types/Invocation.java +7 -6
  47. package/android/detox/src/main/java/com/wix/detox/espresso/MultipleViewsAction.kt +4 -0
  48. package/android/detox/src/main/java/com/wix/detox/espresso/UiControllerSpy.kt +0 -1
  49. package/android/detox/src/main/java/com/wix/detox/espresso/ViewActionWithResult.kt +2 -1
  50. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/MotionEvents.kt +60 -4
  51. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/GetAttributesActionTest.kt +6 -5
  52. package/android/detox/src/testFull/java/com/wix/detox/espresso/common/MaterialSliderHelperTest.kt +33 -0
  53. package/android/detox/src/testFull/java/com/wix/detox/espresso/common/{SliderHelperTest.kt → ReactSliderHelperTest.kt} +3 -3
  54. package/android/detox/src/testFull/java/com/wix/detox/espresso/matcher/RegexMatcherTest.kt +52 -0
  55. package/android/detox/src/testFull/java/com/wix/detox/espresso/performer/ViewActionPerformerSpec.kt +37 -0
  56. package/android/detox/src/testFull/java/com/wix/invoke/JsonParserTest.java +23 -7
  57. package/android/detox/src/testFull/resources/targetInvocationEspressoWebDetoxScript.json +47 -0
  58. package/android/gradle/wrapper/gradle-wrapper.properties +1 -1
  59. package/android/rninfo.gradle +25 -0
  60. package/android/settings.gradle +2 -1
  61. package/detox.d.ts +1840 -0
  62. package/globals.d.ts +23 -0
  63. package/index.d.ts +2 -1789
  64. package/internals.d.ts +11 -1
  65. package/jest.config.js +108 -0
  66. package/local-cli/reset-lock-file.js +5 -9
  67. package/local-cli/startCommand/AppStartCommand.js +4 -1
  68. package/local-cli/testCommand/TestRunnerCommand.js +26 -3
  69. package/local-cli/utils/interruptListeners.js +15 -0
  70. package/package.json +15 -108
  71. package/runners/jest/reporter.js +21 -1
  72. package/runners/jest/reporters/DetoxIPCReporter.js +34 -0
  73. package/runners/jest/reporters/DetoxReporterDispatcher.js +144 -0
  74. package/runners/jest/reporters/DetoxSummaryReporter.js +16 -0
  75. package/runners/jest/reporters/DetoxVerboseReporter.js +16 -0
  76. package/runners/jest/reporters/index.js +6 -0
  77. package/runners/jest/testEnvironment/index.js +11 -0
  78. package/src/DetoxWorker.js +5 -11
  79. package/src/android/core/NativeElement.js +26 -29
  80. package/src/android/core/WebElement.js +24 -6
  81. package/src/android/espressoapi/DetoxAssertion.js +16 -14
  82. package/src/android/espressoapi/DetoxMatcher.js +24 -8
  83. package/src/android/espressoapi/EspressoDetox.js +9 -2
  84. package/src/android/espressoapi/web/WebElement.js +1 -4
  85. package/src/android/interactions/native.js +2 -3
  86. package/src/android/matchers/index.js +4 -0
  87. package/src/android/matchers/native.js +9 -4
  88. package/src/android/matchers/web.js +26 -1
  89. package/src/artifacts/providers/index.js +3 -3
  90. package/src/artifacts/screenshot/SimulatorScreenshotPlugin.js +0 -17
  91. package/src/configuration/composeLoggerConfig.js +1 -0
  92. package/src/configuration/composeRunnerConfig.js +3 -1
  93. package/src/devices/allocation/DeviceAllocator.js +66 -20
  94. package/src/devices/allocation/DeviceList.js +44 -0
  95. package/src/devices/allocation/DeviceRegistry.js +189 -0
  96. package/src/devices/allocation/drivers/AllocationDriverBase.d.ts +15 -0
  97. package/src/devices/{common/drivers/android/tools → allocation/drivers/android}/FreeDeviceFinder.js +11 -10
  98. package/src/devices/allocation/drivers/android/attached/AttachedAndroidAllocDriver.js +22 -17
  99. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +97 -38
  100. package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +32 -45
  101. package/src/devices/allocation/drivers/android/emulator/FreeEmulatorFinder.js +1 -1
  102. package/src/devices/allocation/drivers/android/emulator/FreePortFinder.js +37 -0
  103. package/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js +3 -3
  104. package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +104 -32
  105. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceLauncher.js +40 -31
  106. package/src/devices/allocation/drivers/android/genycloud/GenyRegistry.js +121 -0
  107. package/src/devices/allocation/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +24 -0
  108. package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyRecipesService.js +1 -1
  109. package/src/devices/allocation/drivers/android/genycloud/services/dto/GenyInstance.js +83 -0
  110. package/src/devices/allocation/drivers/android/genycloud/services/dto/GenyRecipe.js +25 -0
  111. package/src/devices/allocation/drivers/ios/SimulatorAllocDriver.js +95 -54
  112. package/src/devices/allocation/drivers/ios/SimulatorQuery.js +24 -0
  113. package/src/devices/allocation/factories/android.js +29 -35
  114. package/src/devices/allocation/factories/ios.js +6 -7
  115. package/src/devices/common/drivers/DeviceCookie.d.ts +12 -0
  116. package/src/devices/common/drivers/android/cookies.d.ts +11 -0
  117. package/src/devices/common/drivers/android/emulator/exec/EmulatorExec.js +17 -5
  118. package/src/devices/common/drivers/android/exec/ADB.js +1 -0
  119. package/src/devices/common/drivers/android/tools/instrumentationArgs.js +7 -1
  120. package/src/devices/common/drivers/ios/cookies.d.ts +9 -0
  121. package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +3 -1
  122. package/src/devices/cookies/index.js +0 -6
  123. package/src/devices/runtime/drivers/android/genycloud/GenyCloudDriver.js +7 -6
  124. package/src/devices/runtime/drivers/ios/SimulatorDriver.js +5 -4
  125. package/src/devices/runtime/drivers/ios/XCUITestUtils.js +21 -11
  126. package/src/devices/runtime/factories/android.js +3 -11
  127. package/src/devices/runtime/factories/ios.js +3 -4
  128. package/src/{servicelocator → devices/servicelocator}/android/emulatorServiceLocator.js +1 -1
  129. package/src/devices/servicelocator/android/genycloudServiceLocator.js +17 -0
  130. package/src/devices/servicelocator/android/index.js +23 -0
  131. package/src/{validation → devices/validation}/EnvironmentValidatorBase.js +1 -0
  132. package/src/{validation → devices/validation}/android/GenycloudEnvValidator.js +2 -2
  133. package/src/{validation → devices/validation}/factories/index.js +1 -1
  134. package/src/{validation → devices/validation}/ios/IosSimulatorEnvValidator.js +2 -2
  135. package/src/environmentFactory.js +1 -11
  136. package/src/invoke.js +0 -2
  137. package/src/ios/expectTwo.js +28 -11
  138. package/src/ios/web.js +302 -0
  139. package/src/ipc/IPCClient.js +22 -1
  140. package/src/ipc/IPCServer.js +42 -1
  141. package/src/ipc/SessionState.js +1 -0
  142. package/src/logger/DetoxLogger.js +2 -2
  143. package/src/realms/DetoxContext.js +8 -0
  144. package/src/realms/DetoxInternalsFacade.js +1 -0
  145. package/src/realms/DetoxPrimaryContext.js +49 -44
  146. package/src/realms/DetoxSecondaryContext.js +27 -0
  147. package/src/realms/symbols.js +6 -0
  148. package/src/utils/PIDService.js +27 -0
  149. package/src/utils/assertIsFunction.js +35 -0
  150. package/src/utils/environment.js +8 -15
  151. package/src/utils/errorUtils.js +3 -3
  152. package/src/utils/invocationTraceDescriptions.js +16 -0
  153. package/src/utils/isArrowFunction.js +24 -0
  154. package/src/utils/isRegExp.js +7 -0
  155. package/tsconfig.json +8 -3
  156. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1-javadoc.jar.md5 +0 -1
  157. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1-javadoc.jar.sha1 +0 -1
  158. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1-javadoc.jar.sha256 +0 -1
  159. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1-javadoc.jar.sha512 +0 -1
  160. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1-sources.jar.md5 +0 -1
  161. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1-sources.jar.sha1 +0 -1
  162. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1-sources.jar.sha256 +0 -1
  163. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1-sources.jar.sha512 +0 -1
  164. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1.aar +0 -0
  165. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1.aar.md5 +0 -1
  166. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1.aar.sha1 +0 -1
  167. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1.aar.sha256 +0 -1
  168. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1.aar.sha512 +0 -1
  169. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1.pom.md5 +0 -1
  170. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1.pom.sha1 +0 -1
  171. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1.pom.sha256 +0 -1
  172. package/Detox-android/com/wix/detox/21.0.0-rc.1/detox-21.0.0-rc.1.pom.sha512 +0 -1
  173. package/runners/jest/reporters/DetoxReporter.js +0 -36
  174. package/src/devices/DeviceRegistry.js +0 -176
  175. package/src/devices/allocation/drivers/AllocationDriverBase.js +0 -30
  176. package/src/devices/allocation/drivers/android/attached/AttachedAndroidLauncher.js +0 -13
  177. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocationHelper.js +0 -72
  178. package/src/devices/allocation/drivers/android/genycloud/GenyDeviceRegistryFactory.js +0 -16
  179. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceAllocationHelper.js +0 -65
  180. package/src/devices/allocation/drivers/ios/SimulatorLauncher.js +0 -21
  181. package/src/devices/common/drivers/DeviceAllocationHelper.js +0 -20
  182. package/src/devices/common/drivers/DeviceLauncher.js +0 -19
  183. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +0 -25
  184. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLookupService.js +0 -38
  185. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceNaming.js +0 -14
  186. package/src/devices/common/drivers/android/genycloud/services/dto/GenyInstance.js +0 -66
  187. package/src/devices/common/drivers/android/genycloud/services/dto/GenyRecipe.js +0 -13
  188. package/src/devices/cookies/AndroidDeviceCookie.js +0 -13
  189. package/src/devices/cookies/AndroidEmulatorCookie.js +0 -6
  190. package/src/devices/cookies/AttachedAndroidDeviceCookie.js +0 -12
  191. package/src/devices/cookies/DeviceCookie.js +0 -4
  192. package/src/devices/cookies/GenycloudEmulatorCookie.js +0 -20
  193. package/src/devices/cookies/IosCookie.js +0 -6
  194. package/src/devices/cookies/IosSimulatorCookie.js +0 -10
  195. package/src/devices/lifecycle/GenyGlobalLifecycleHandler.js +0 -71
  196. package/src/devices/lifecycle/factories/GenyGlobalLifecycleHandlerFactory.js +0 -18
  197. package/src/invoke/EarlGrey.js +0 -8
  198. package/src/servicelocator/android/genycloudServiceLocator.js +0 -21
  199. package/src/servicelocator/android/index.js +0 -25
  200. package/src/servicelocator/ios.js +0 -7
  201. /package/src/devices/{common → allocation}/drivers/android/genycloud/exec/GenyCloudExec.js +0 -0
  202. /package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyAuthService.js +0 -0
@@ -7,9 +7,10 @@ import android.widget.CheckBox
7
7
  import android.widget.ProgressBar
8
8
  import android.widget.TextView
9
9
  import androidx.test.espresso.UiController
10
- import com.google.android.material.slider.Slider
11
10
  import com.wix.detox.espresso.ViewActionWithResult
12
- import com.wix.detox.espresso.common.SliderHelper
11
+ import com.wix.detox.espresso.MultipleViewsAction
12
+ import com.wix.detox.espresso.common.ReactSliderHelper
13
+ import com.wix.detox.espresso.common.MaterialSliderHelper
13
14
  import com.wix.detox.reactnative.ui.getAccessibilityLabel
14
15
  import org.hamcrest.Matcher
15
16
  import org.hamcrest.Matchers
@@ -17,26 +18,27 @@ import org.hamcrest.Matchers.allOf
17
18
  import org.hamcrest.Matchers.notNullValue
18
19
  import org.json.JSONObject
19
20
 
20
- class GetAttributesAction() : ViewActionWithResult<String?> {
21
- private val commonAttributes = CommonAttributes()
22
- private val textViewAttributes = TextViewAttributes()
23
- private val checkBoxAttributes = CheckBoxAttributes()
24
- private val progressBarAttributes = ProgressBarAttributes()
25
- private val sliderAttributes = SliderAttributes()
26
- private var result: String = ""
21
+ private interface AttributeExtractor {
22
+ fun extractAttributes(json: JSONObject, view: View)
23
+ }
24
+
25
+ class GetAttributesAction() : ViewActionWithResult<JSONObject?>, MultipleViewsAction {
26
+ private val attributeExtractors = listOf(
27
+ CommonAttributes(),
28
+ TextViewAttributes(),
29
+ CheckBoxAttributes(),
30
+ ProgressBarAttributes(),
31
+ MaterialSliderAttributes()
32
+ )
33
+ private var result: JSONObject? = null
27
34
 
28
35
  override fun perform(uiController: UiController?, view: View?) {
29
36
  view!!
30
37
 
31
38
  val json = JSONObject()
39
+ attributeExtractors.forEach { it.extractAttributes(json, view) }
32
40
 
33
- commonAttributes.get(json, view)
34
- textViewAttributes.get(json, view)
35
- checkBoxAttributes.get(json, view)
36
- progressBarAttributes.get(json, view)
37
- sliderAttributes.get(json, view)
38
-
39
- result = json.toString()
41
+ result = json
40
42
  }
41
43
 
42
44
  override fun getResult() = result
@@ -44,8 +46,8 @@ class GetAttributesAction() : ViewActionWithResult<String?> {
44
46
  override fun getConstraints(): Matcher<View> = allOf(notNullValue(), Matchers.isA(View::class.java))
45
47
  }
46
48
 
47
- private class CommonAttributes {
48
- fun get(json: JSONObject, view: View) {
49
+ private class CommonAttributes : AttributeExtractor {
50
+ override fun extractAttributes(json: JSONObject, view: View) {
49
51
  getId(json, view)
50
52
  getVisibility(json, view)
51
53
  getAccessibilityLabel(json, view)
@@ -89,8 +91,8 @@ private class CommonAttributes {
89
91
  }
90
92
  }
91
93
 
92
- private class TextViewAttributes {
93
- fun get(json: JSONObject, view: View) {
94
+ private class TextViewAttributes : AttributeExtractor {
95
+ override fun extractAttributes(json: JSONObject, view: View) {
94
96
  if (view is TextView) {
95
97
  getText(json, view)
96
98
  getLength(json, view)
@@ -118,8 +120,8 @@ private class TextViewAttributes {
118
120
  }
119
121
  }
120
122
 
121
- private class CheckBoxAttributes {
122
- fun get(json: JSONObject, view: View) {
123
+ private class CheckBoxAttributes : AttributeExtractor {
124
+ override fun extractAttributes(json: JSONObject, view: View) {
123
125
  if (view is CheckBox) {
124
126
  getCheckboxValue(json, view)
125
127
  }
@@ -133,31 +135,28 @@ private class CheckBoxAttributes {
133
135
  * Note: this applies also to [androidx.appcompat.widget.AppCompatSeekBar], which
134
136
  * is anything RN-slider-ish.
135
137
  */
136
- private class ProgressBarAttributes {
137
- fun get(json: JSONObject, view: View) {
138
+ private class ProgressBarAttributes : AttributeExtractor {
139
+ override fun extractAttributes(json: JSONObject, view: View) {
138
140
  if (view is ProgressBar) {
139
- SliderHelper.maybeCreate(view)?.let {
140
- getRNSliderValue(json, it)
141
+ ReactSliderHelper.maybeCreate(view)?.let {
142
+ getReactSliderValue(json, it)
141
143
  } ?:
142
144
  getProgressBarValue(json, view)
143
145
  }
144
146
  }
145
147
 
146
- private fun getRNSliderValue(rootObject: JSONObject, sliderHelper: SliderHelper) {
147
- rootObject.put("value", sliderHelper.getCurrentProgressPct())
148
+ private fun getReactSliderValue(rootObject: JSONObject, reactSliderHelper: ReactSliderHelper) {
149
+ rootObject.put("value", reactSliderHelper.getCurrentProgressPct())
148
150
  }
149
151
 
150
152
  private fun getProgressBarValue(rootObject: JSONObject, view: ProgressBar) =
151
153
  rootObject.put("value", view.progress)
152
154
  }
153
155
 
154
- private class SliderAttributes {
155
- fun get(json: JSONObject, view: View) {
156
- if (view is Slider) {
157
- getSliderValue(json, view)
156
+ private class MaterialSliderAttributes : AttributeExtractor {
157
+ override fun extractAttributes(json: JSONObject, view: View) {
158
+ MaterialSliderHelper(view).getValueIfSlider()?.let {
159
+ json.put("value", it)
158
160
  }
159
161
  }
160
-
161
- private fun getSliderValue(rootObject: JSONObject, view: Slider) =
162
- rootObject.put("value", view.value)
163
162
  }
@@ -0,0 +1,21 @@
1
+ package com.wix.detox.espresso.common
2
+
3
+ import android.view.View
4
+ import com.wix.detox.espresso.action.common.ReflectUtils
5
+ import org.joor.Reflect
6
+
7
+ private const val CLASS_MATERIAL_SLIDER = "com.google.android.material.slider.Slider"
8
+
9
+ open class MaterialSliderHelper(protected val view: View) {
10
+ fun getValueIfSlider(): Float? {
11
+ if (!isSlider()) {
12
+ return null
13
+ }
14
+
15
+ return getValue()
16
+ }
17
+
18
+ private fun isSlider() = ReflectUtils.isAssignableFrom(view, CLASS_MATERIAL_SLIDER)
19
+
20
+ private fun getValue() = Reflect.on(view).call("getValue").get() as Float
21
+ }
@@ -4,15 +4,16 @@ import android.view.View
4
4
  import androidx.appcompat.widget.AppCompatSeekBar
5
5
  import com.facebook.react.bridge.JavaOnlyMap
6
6
  import com.facebook.react.uimanager.ReactStylesDiffMap
7
- import com.facebook.react.views.slider.ReactSlider
8
7
  import com.wix.detox.common.DetoxErrors.DetoxIllegalStateException
9
8
  import com.wix.detox.espresso.action.common.ReflectUtils
9
+ import com.facebook.react.views.slider.ReactSlider
10
10
  import org.joor.Reflect
11
11
 
12
12
  private const val CLASS_REACT_SLIDER_LEGACY = "com.facebook.react.views.slider.ReactSlider"
13
13
  private const val CLASS_REACT_SLIDER_COMMUNITY = "com.reactnativecommunity.slider.ReactSlider"
14
+ private const val CLASS_REACT_SLIDER_COMMUNITY_MANAGER = "com.reactnativecommunity.slider.ReactSliderManager"
14
15
 
15
- abstract class SliderHelper(protected val slider: AppCompatSeekBar) {
16
+ abstract class ReactSliderHelper(protected val slider: AppCompatSeekBar) {
16
17
  fun getCurrentProgressPct(): Double {
17
18
  val nativeProgress = slider.progress.toDouble()
18
19
  val nativeMax = slider.max
@@ -46,7 +47,7 @@ abstract class SliderHelper(protected val slider: AppCompatSeekBar) {
46
47
  ?: throw DetoxIllegalStateException("Cannot handle this type of a seek-bar view (Class ${view.javaClass.canonicalName}). " +
47
48
  "Only React Native sliders are currently supported.")
48
49
 
49
- fun maybeCreate(view: View): SliderHelper? =
50
+ fun maybeCreate(view: View): ReactSliderHelper? =
50
51
  when {
51
52
  ReflectUtils.isAssignableFrom(view, CLASS_REACT_SLIDER_LEGACY)
52
53
  -> LegacySliderHelper(view as ReactSlider)
@@ -58,7 +59,7 @@ abstract class SliderHelper(protected val slider: AppCompatSeekBar) {
58
59
  }
59
60
  }
60
61
 
61
- private class LegacySliderHelper(slider: AppCompatSeekBar): SliderHelper(slider) {
62
+ private class LegacySliderHelper(slider: ReactSlider): ReactSliderHelper(slider) {
62
63
  override fun setProgressJS(valueJS: Double) {
63
64
  val reactSliderManager = com.facebook.react.views.slider.ReactSliderManager()
64
65
  reactSliderManager.updateProperties(slider as ReactSlider, buildStyles("value", valueJS))
@@ -67,9 +68,9 @@ private class LegacySliderHelper(slider: AppCompatSeekBar): SliderHelper(slider)
67
68
  private fun buildStyles(vararg keysAndValues: Any) = ReactStylesDiffMap(JavaOnlyMap.of(*keysAndValues))
68
69
  }
69
70
 
70
- private class CommunitySliderHelper(slider: AppCompatSeekBar): SliderHelper(slider) {
71
+ private class CommunitySliderHelper(slider: AppCompatSeekBar): ReactSliderHelper(slider) {
71
72
  override fun setProgressJS(valueJS: Double) {
72
- val reactSliderManager = Class.forName("com.reactnativecommunity.slider.ReactSliderManager").newInstance()
73
+ val reactSliderManager = Class.forName(CLASS_REACT_SLIDER_COMMUNITY_MANAGER).newInstance()
73
74
  Reflect.on(reactSliderManager).call("setValue", slider, valueJS)
74
75
  }
75
76
  }
@@ -0,0 +1,56 @@
1
+ package com.wix.detox.espresso.matcher
2
+
3
+ import org.hamcrest.Description
4
+ import org.hamcrest.TypeSafeMatcher
5
+
6
+ class RegexMatcher<T>(private val jsRegex: String) : TypeSafeMatcher<T>() {
7
+ override fun matchesSafely(item: T): Boolean {
8
+ val stringItem = item.toString()
9
+ return stringItem.matchesJSRegex(jsRegex)
10
+ }
11
+
12
+ override fun describeTo(description: Description) {
13
+ description.appendText("should match the pattern: $jsRegex")
14
+ }
15
+ }
16
+
17
+ // Returns whether the whole string matches the given `jsRegex`.
18
+ // JS flags has the format of `/<pattern>/<flags>`.
19
+ // Flags can be either:
20
+ // - i: With this flag the search is case-insensitive: no difference between A and a (see the example below).
21
+ // - s: Enables “dotall” mode, that allows a dot . to match newline character \n (covered in the chapter Character classes).
22
+ // - m: Multiline mode (covered in the chapter Multiline mode of anchors ^ $, flag "m").
23
+ // Other flags (e.g. g,u,s) are not supported as they do not have equivalents in Kotlin.
24
+ //
25
+ // - See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
26
+ fun String.matchesJSRegex(jsRegex: String): Boolean {
27
+ val flagsChars = getRegexFlags(jsRegex)
28
+ val options = getRegexOptions(flagsChars)
29
+ val pattern = getRegexPattern(jsRegex)
30
+ return Regex(pattern, options).matches(this)
31
+ }
32
+
33
+ private fun getRegexFlags(jsRegex: String): CharSequence {
34
+ return jsRegex.substringAfterLast("/")
35
+ }
36
+
37
+ private fun getRegexPattern(jsRegex: String): String {
38
+ val pattern = jsRegex.substringAfter("/")
39
+ return pattern.substringBeforeLast("/")
40
+ }
41
+
42
+ private fun getRegexOptions(flagsChars: CharSequence): MutableSet<RegexOption> {
43
+ val options = mutableSetOf<RegexOption>()
44
+
45
+ if (flagsChars.contains('i', ignoreCase = true)) {
46
+ options.add(RegexOption.IGNORE_CASE)
47
+ }
48
+ if (flagsChars.contains('s', ignoreCase = true)) {
49
+ options.add(RegexOption.DOT_MATCHES_ALL)
50
+ }
51
+ if (flagsChars.contains('m', ignoreCase = true)) {
52
+ options.add(RegexOption.MULTILINE)
53
+ }
54
+
55
+ return options
56
+ }
@@ -7,20 +7,32 @@ import androidx.appcompat.widget.AppCompatSeekBar
7
7
  import androidx.test.espresso.matcher.BoundedMatcher
8
8
  import androidx.test.espresso.matcher.ViewMatchers
9
9
  import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
10
- import com.wix.detox.espresso.common.SliderHelper
10
+ import com.wix.detox.espresso.common.ReactSliderHelper
11
11
  import org.hamcrest.*
12
12
  import org.hamcrest.Matchers.*
13
13
  import kotlin.math.abs
14
+ import org.hamcrest.CoreMatchers.`is`
14
15
 
15
16
  /*
16
17
  * An extension of [androidx.test.espresso.matcher.ViewMatchers].
17
18
  */
19
+ fun <T> getRelevantMatcher(value: T, isRegex: Boolean): Matcher<T> =
20
+ if (isRegex) RegexMatcher(value.toString()) else `is`(value)
18
21
 
19
- fun withAccessibilityLabel(text: String) =
20
- WithAccessibilityLabelMatcher(`is`(text))
22
+ fun withAccessibilityLabel(text: String, isRegex: Boolean): Matcher<View> =
23
+ WithAccessibilityLabelMatcher(getRelevantMatcher(text, isRegex))
21
24
 
22
- fun withShallowAccessibilityLabel(label: String): Matcher<View>
23
- = anyOf(ViewMatchers.withContentDescription(label), ViewMatchers.withText(label))
25
+ fun withShallowAccessibilityLabel(label: String, isRegex: Boolean): Matcher<View> =
26
+ anyOf(withContentDescription(label, isRegex), withText(label, isRegex))
27
+
28
+ fun withText(text: String, isRegex: Boolean): Matcher<View> =
29
+ ViewMatchers.withText(getRelevantMatcher(text, isRegex))
30
+
31
+ fun withContentDescription(label: String, isRegex: Boolean): Matcher<View> =
32
+ ViewMatchers.withContentDescription(getRelevantMatcher(label, isRegex))
33
+
34
+ fun withTagValue(testId: String, isRegex: Boolean): Matcher<View> =
35
+ ViewMatchers.withTagValue(getRelevantMatcher<Any>(testId, isRegex))
24
36
 
25
37
  fun isOfClassName(className: String): Matcher<View> {
26
38
  try {
@@ -48,7 +60,7 @@ fun toHaveSliderPosition(expectedValuePct: Double, tolerance: Double): Matcher<V
48
60
  }
49
61
 
50
62
  override fun matchesSafely(view: AppCompatSeekBar): Boolean {
51
- val sliderHelper = SliderHelper.create(view)
63
+ val sliderHelper = ReactSliderHelper.create(view)
52
64
  val progressPct = sliderHelper.getCurrentProgressPct()
53
65
  return (abs(progressPct - expectedValuePct) <= tolerance)
54
66
  }
@@ -0,0 +1,43 @@
1
+ package com.wix.detox.espresso.performer
2
+
3
+ import com.wix.detox.espresso.DetoxMatcher
4
+ import com.wix.detox.espresso.ViewActionWithResult
5
+
6
+ import android.view.View
7
+ import androidx.test.espresso.Espresso.onView
8
+ import androidx.test.espresso.NoMatchingViewException
9
+ import androidx.test.espresso.ViewAction
10
+ import org.hamcrest.Matcher
11
+
12
+ class MultipleViewsActionPerformer(
13
+ private val action: ViewAction
14
+ ) : ViewActionPerformer {
15
+ override fun performOn(matcher: Matcher<View>): Any? {
16
+ val results = mutableListOf<Any?>()
17
+ var index = 0
18
+
19
+ while (true) {
20
+ val indexedMatcher = DetoxMatcher.matcherForAtIndex(index, matcher)
21
+
22
+ try {
23
+ onView(indexedMatcher).perform(action)
24
+
25
+ (action as? ViewActionWithResult<*>)?.getResult()?.let { results.add(it) }
26
+
27
+ index++
28
+ } catch (e: NoMatchingViewException) {
29
+ if (index == 0) {
30
+ throw e
31
+ }
32
+
33
+ break
34
+ }
35
+ }
36
+
37
+ return when {
38
+ results.isEmpty() -> null
39
+ results.size == 1 -> results.first()
40
+ else -> mapOf("elements" to results)
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,19 @@
1
+ package com.wix.detox.espresso.performer
2
+
3
+ import com.wix.detox.espresso.ViewActionWithResult
4
+
5
+ import android.view.View
6
+ import androidx.test.espresso.Espresso.onView
7
+ import androidx.test.espresso.NoMatchingViewException
8
+ import androidx.test.espresso.ViewAction
9
+ import org.hamcrest.Matcher
10
+
11
+ class SingleViewActionPerformer(
12
+ private val action: ViewAction
13
+ ) : ViewActionPerformer {
14
+ override fun performOn(matcher: Matcher<View>): Any? {
15
+ onView(matcher).perform(action)
16
+
17
+ return (action as? ViewActionWithResult<*>)?.getResult()
18
+ }
19
+ }
@@ -0,0 +1,24 @@
1
+ package com.wix.detox.espresso.performer
2
+
3
+ import com.wix.detox.espresso.MultipleViewsAction
4
+
5
+ import android.view.View
6
+ import androidx.test.espresso.Espresso.onView
7
+ import androidx.test.espresso.NoMatchingViewException
8
+ import androidx.test.espresso.ViewAction
9
+ import org.hamcrest.Matcher
10
+
11
+ interface ViewActionPerformer {
12
+ fun performOn(matcher: Matcher<View>): Any?
13
+
14
+ companion object {
15
+ @JvmStatic
16
+ fun forAction(action: ViewAction): ViewActionPerformer {
17
+ return if (action is MultipleViewsAction) {
18
+ MultipleViewsActionPerformer(action)
19
+ } else {
20
+ SingleViewActionPerformer(action)
21
+ }
22
+ }
23
+ }
24
+ }
@@ -62,12 +62,12 @@ public class WebElement {
62
62
  return getWebViewInteraction().withElement(get()).perform(DriverAtoms.getText()).get();
63
63
  }
64
64
 
65
- public Evaluation runScript(String script) {
66
- return getWebViewInteraction().withElement(get()).perform(new SimpleAtom(script)).get();
65
+ public Object runScript(String script) {
66
+ return getWebViewInteraction().withElement(get()).perform(new SimpleAtom(script)).get().getValue();
67
67
  }
68
68
 
69
- public Evaluation runScriptWithArgs(String script, final ArrayList<Object> args) {
70
- return getWebViewInteraction().withElement(get()).perform(Atoms.scriptWithArgs(script, args)).get();
69
+ public Object runScriptWithArgs(String script, final ArrayList<Object> args) {
70
+ return getWebViewInteraction().withElement(get()).perform(Atoms.scriptWithArgs(script, args)).get().getValue();
71
71
  }
72
72
 
73
73
  public String getCurrentUrl() {
@@ -68,9 +68,9 @@ public class Invocation {
68
68
  argument = args.get(i);
69
69
  } else if(args.get(i).getClass() == JSONArray.class) {
70
70
  JSONArray jsonArray = (JSONArray) args.get(i);
71
- List<String> list = new ArrayList<>();
71
+ List<Object> list = new ArrayList<>();
72
72
  for (int j = 0; j < jsonArray.length(); j++) {
73
- list.add(jsonArray.getString(j));
73
+ list.add(jsonArray.get(j));
74
74
  }
75
75
  argument = list;
76
76
  } else {
@@ -92,9 +92,9 @@ public class Invocation {
92
92
  } else if (type.equals("boolean")) {
93
93
  argument = jsonArgument.optBoolean("value");
94
94
  } else if (type.equals("Invocation")) {
95
- argument = new Invocation(jsonArgument.optJSONObject("value"));
95
+ argument = new Invocation(jsonArgument.optJSONObject("value"));
96
96
  } else {
97
- throw new RuntimeException("Unhandled arg type" + type);
97
+ throw new RuntimeException("Unhandled arg type " + type);
98
98
  }
99
99
  }
100
100
  }
@@ -105,6 +105,8 @@ public class Invocation {
105
105
  }
106
106
 
107
107
  public void setArgs(Object[] args) {
108
+ JsonParser parser = new JsonParser();
109
+
108
110
  for (int i = 0; i < args.length; i++) {
109
111
  Object argument = args[i];
110
112
  if (argument instanceof HashMap && !((HashMap) argument).isEmpty()) {
@@ -125,10 +127,9 @@ public class Invocation {
125
127
  } else if (type.equals("boolean")) {
126
128
  argument = ((Boolean) value).booleanValue();
127
129
  } else if (type.equals("Invocation")) {
128
- JsonParser parser = new JsonParser();
129
130
  argument = parser.parse((String)value);
130
131
  } else {
131
- throw new RuntimeException("Unhandled arg type" + type);
132
+ throw new RuntimeException("Unhandled arg type " + type);
132
133
  }
133
134
 
134
135
  args[i] = argument;
@@ -0,0 +1,4 @@
1
+ package com.wix.detox.espresso
2
+
3
+ // Marker interface for actions that should be applied to all matching elements without ambiguity.
4
+ interface MultipleViewsAction
@@ -1,4 +1,3 @@
1
-
2
1
  package com.wix.detox.espresso
3
2
 
4
3
  import androidx.test.espresso.UiController
@@ -2,6 +2,7 @@ package com.wix.detox.espresso
2
2
 
3
3
  import androidx.test.espresso.ViewAction
4
4
 
5
- interface ViewActionWithResult<R: Any?>: ViewAction {
5
+ // Interface for actions that return a result.
6
+ interface ViewActionWithResult<R: Any?> : ViewAction {
6
7
  fun getResult(): R
7
8
  }
@@ -9,8 +9,37 @@ import androidx.test.espresso.action.MotionEvents
9
9
  private val PRECISION = floatArrayOf(16f, 16f)
10
10
 
11
11
  class MotionEvents {
12
- fun obtainMoveEvent(downEvent: MotionEvent, eventTime: Long, x: Float, y: Float): MotionEvent
13
- = MotionEvents.obtainMovement(downEvent.downTime, eventTime, floatArrayOf(x, y))!!
12
+ fun obtainMoveEvent(downEvent: MotionEvent, eventTime: Long, x: Float, y: Float): MotionEvent {
13
+ val pointerProperties = MotionEvent.PointerProperties().apply {
14
+ clear()
15
+ id = 0
16
+ toolType = MotionEvent.TOOL_TYPE_UNKNOWN
17
+ }
18
+ val pointerCoords = MotionEvent.PointerCoords().apply {
19
+ clear()
20
+ this.x = x
21
+ this.y = y
22
+ this.pressure = 0f
23
+ this.size = 1f
24
+ }
25
+
26
+ return MotionEvent.obtain(
27
+ downEvent.downTime,
28
+ eventTime,
29
+ MotionEvent.ACTION_MOVE,
30
+ 1, // pointerCounts
31
+ arrayOf(pointerProperties),
32
+ arrayOf(pointerCoords),
33
+ 0, // metaState
34
+ downEvent.buttonState,
35
+ downEvent.xPrecision,
36
+ downEvent.yPrecision,
37
+ 0, // deviceId
38
+ 0, // edgeFlags
39
+ downEvent.source,
40
+ 0
41
+ )
42
+ }
14
43
 
15
44
  fun obtainDownEvent(x: Float, y: Float, precision: FloatArray = PRECISION)
16
45
  = obtainDownEvent(x, y, precision, null)
@@ -46,8 +75,35 @@ class MotionEvents {
46
75
  0)
47
76
  }
48
77
 
49
- fun obtainUpEvent(downEvent: MotionEvent, eventTime: Long, x: Float, y: Float): MotionEvent
50
- = MotionEvent.obtain(downEvent.downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0)!!
78
+ fun obtainUpEvent(downEvent: MotionEvent, eventTime: Long, x: Float, y: Float): MotionEvent {
79
+ val pointerProperties = MotionEvent.PointerProperties().apply {
80
+ id = 0
81
+ toolType = MotionEvent.TOOL_TYPE_UNKNOWN
82
+ }
83
+ val pointerCoords = MotionEvent.PointerCoords().apply {
84
+ clear()
85
+ this.x = x
86
+ this.y = y
87
+ this.pressure = 0f
88
+ this.size = 1f
89
+ }
90
+ return MotionEvent.obtain(
91
+ downEvent.downTime,
92
+ eventTime,
93
+ MotionEvent.ACTION_UP,
94
+ 1, // pointerCounts
95
+ arrayOf(pointerProperties),
96
+ arrayOf(pointerCoords),
97
+ 0, // metaState
98
+ downEvent.buttonState,
99
+ downEvent.xPrecision,
100
+ downEvent.yPrecision,
101
+ 0, // deviceId
102
+ 0, // edgeFlags
103
+ downEvent.source,
104
+ 0
105
+ )
106
+ }
51
107
 
52
108
  fun sendDownAsync(uiController: UiController, x: Float, y: Float, precision: FloatArray = PRECISION): MotionEvent {
53
109
  val downEvent = obtainDownEvent(x, y, precision, null)
@@ -37,7 +37,7 @@ class GetAttributesActionTest {
37
37
 
38
38
  private fun perform(v: View = view): JSONObject {
39
39
  uut.perform(null, v)
40
- return JSONObject(uut.getResult())
40
+ return uut.getResult()!!
41
41
  }
42
42
 
43
43
  @Test
@@ -135,10 +135,10 @@ class GetAttributesActionTest {
135
135
  }
136
136
 
137
137
  val resultJson = perform()
138
- assertThat(resultJson.opt("alpha")).isEqualTo(0.42)
138
+ assertThat(resultJson.opt("alpha")).isEqualTo(0.42f)
139
139
  assertThat(resultJson.opt("width")).isEqualTo(123)
140
140
  assertThat(resultJson.opt("height")).isEqualTo(456)
141
- assertThat(resultJson.opt("elevation")).isEqualTo(0.314)
141
+ assertThat(resultJson.opt("elevation")).isEqualTo(0.314f)
142
142
  }
143
143
 
144
144
  @Test
@@ -208,7 +208,8 @@ class GetAttributesActionTest {
208
208
  }
209
209
 
210
210
  val resultJson = perform(slider)
211
- assertThat(resultJson.opt("value")).isEqualTo(0.42)
211
+ android.util.Log.i("TESTS", "should return material-Slider state through value attribute: "+ resultJson)
212
+ assertThat(resultJson.opt("value")).isEqualTo(0.42f)
212
213
  }
213
214
 
214
215
  @Test
@@ -221,7 +222,7 @@ class GetAttributesActionTest {
221
222
 
222
223
  val resultJson = perform(textView)
223
224
  assertThat(resultJson.opt("text")).isEqualTo("mock-text")
224
- assertThat(resultJson.opt("textSize")).isEqualTo(24)
225
+ assertThat(resultJson.opt("textSize")).isEqualTo(24f)
225
226
  assertThat(resultJson.opt("length")).isEqualTo(111)
226
227
  }
227
228
 
@@ -0,0 +1,33 @@
1
+ package com.wix.detox.espresso.common
2
+
3
+ import android.view.View
4
+ import com.google.android.material.slider.Slider
5
+ import org.assertj.core.api.Assertions.assertThat
6
+ import org.junit.Test
7
+ import org.junit.runner.RunWith
8
+ import org.mockito.kotlin.doReturn
9
+ import org.mockito.kotlin.mock
10
+ import org.robolectric.RobolectricTestRunner
11
+
12
+ @RunWith(RobolectricTestRunner::class)
13
+ class MaterialSliderHelperTest {
14
+ @Test
15
+ fun `should return value if view is a slider`() {
16
+ val slider: Slider = mock {
17
+ on { value } doReturn 0.2f
18
+ }
19
+
20
+ val uut = MaterialSliderHelper(slider)
21
+
22
+ assertThat(uut.getValueIfSlider()).isEqualTo(0.2f)
23
+ }
24
+
25
+ @Test
26
+ fun `should return null if view is not a slider`() {
27
+ val view: View = mock()
28
+
29
+ val uut = MaterialSliderHelper(view)
30
+
31
+ assertThat(uut.getValueIfSlider()).isNull()
32
+ }
33
+ }
@@ -15,14 +15,14 @@ import org.robolectric.RobolectricTestRunner
15
15
  * to avoid having to install the community slider under node_modules just for this.
16
16
  */
17
17
  @RunWith(RobolectricTestRunner::class)
18
- class SliderHelperTest {
18
+ class ReactSliderHelperTest {
19
19
  lateinit var slider: ReactSlider
20
- lateinit var uut: SliderHelper
20
+ lateinit var uut: ReactSliderHelper
21
21
 
22
22
  @Before
23
23
  fun setup() {
24
24
  slider = mock()
25
- uut = SliderHelper.create(slider)
25
+ uut = ReactSliderHelper.create(slider)
26
26
  }
27
27
 
28
28
  private fun givenNativeProgressTraits(current: Int, max: Int) {