detox 20.0.0-breaking.new-global-lifecycle.0 → 20.0.3-breaking.new-global-lifecycle.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  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 → 20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-javadoc.jar.sha512 +1 -0
  6. 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.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.aar +0 -0
  12. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.aar.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.aar.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.aar.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.aar.sha512 +1 -0
  16. 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.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.pom} +2 -2
  17. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.pom.md5 +1 -0
  18. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.pom.sha1 +1 -0
  19. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.pom.sha256 +1 -0
  20. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.pom.sha512 +1 -0
  21. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  22. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  23. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  24. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  25. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  26. package/Detox-ios-src.tbz +0 -0
  27. package/Detox-ios.tbz +0 -0
  28. package/android/build.gradle +2 -2
  29. package/android/detox/build.gradle +2 -2
  30. package/android/detox/publish-pom.gradle +5 -1
  31. package/android/detox/publishing.gradle +9 -7
  32. package/android/detox/src/full/java/com/wix/detox/adapters/server/WebSocketClient.java +3 -1
  33. package/android/detox/src/full/java/com/wix/detox/espresso/DetoxAction.java +1 -3
  34. package/android/detox/src/full/java/com/wix/detox/espresso/UiAutomatorHelper.java +1 -1
  35. package/android/detox/src/full/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +22 -0
  36. package/android/detox/src/{main → full}/java/com/wix/detox/espresso/action/GetAttributesAction.kt +13 -1
  37. package/android/detox/src/full/java/com/wix/detox/espresso/common/SliderHelper.kt +75 -0
  38. package/android/detox/src/full/java/com/wix/detox/espresso/matcher/ViewMatchers.kt +16 -23
  39. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeLoadingMonitor.kt +54 -8
  40. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/RN66Workaround.kt +13 -4
  41. package/android/detox/src/main/java/com/wix/detox/common/DetoxErrors.java +4 -1
  42. package/android/detox/src/main/java/com/wix/detox/espresso/UiControllerSpy.kt +2 -1
  43. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/ReflectUtils.kt +10 -0
  44. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/utils/UiControllerUtils.kt +1 -1
  45. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/GetAttributesActionTest.kt +15 -3
  46. package/android/detox/src/testFull/java/com/wix/detox/espresso/common/SliderHelperTest.kt +39 -0
  47. package/android/gradle/wrapper/gradle-wrapper.properties +2 -1
  48. package/android/gradlew +181 -107
  49. package/index.d.ts +5 -0
  50. package/internals.d.ts +25 -18
  51. package/local-cli/build.js +1 -1
  52. package/local-cli/build.test.js +14 -14
  53. package/local-cli/test.js +3 -3
  54. package/local-cli/test.test.js +20 -14
  55. package/local-cli/testCommand/TestRunnerCommand.js +6 -6
  56. package/package.json +4 -4
  57. package/runners/jest/testEnvironment/index.js +1 -1
  58. package/runners/jest/testEnvironment/listeners/DetoxCoreListener.js +5 -9
  59. package/runners/jest/testEnvironment/listeners/SpecReporter.js +1 -1
  60. package/runners/jest/testEnvironment/listeners/WorkerAssignReporter.js +1 -1
  61. package/src/DetoxWorker.js +12 -1
  62. package/src/artifacts/log/android/ADBLogcatRecording.js +11 -28
  63. package/src/configuration/composeAppsConfig.js +1 -1
  64. package/src/configuration/composeRunnerConfig.js +49 -1
  65. package/src/configuration/index.js +9 -8
  66. package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +1 -0
  67. package/src/devices/common/drivers/android/exec/ADB.js +5 -0
  68. package/src/errors/DetoxConfigErrorComposer.js +5 -3
  69. package/src/ipc/IPCClient.js +3 -2
  70. package/src/ipc/IPCServer.js +16 -3
  71. package/src/ipc/state.js +24 -2
  72. package/src/realms/DetoxContext.js +3 -3
  73. package/src/realms/DetoxPrimaryContext.js +13 -7
  74. package/src/realms/DetoxSecondaryContext.js +4 -3
  75. package/src/utils/ChromeTracingExporter.js +6 -5
  76. package/src/utils/environment.js +30 -15
  77. 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
  78. 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
  79. 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
  80. 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
  81. 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
  82. 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
  83. 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
  84. 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
  85. 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
  86. 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
  87. 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
  88. 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
  89. 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
  90. 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
  91. 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
  92. 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
  93. 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
  94. package/android/detox/src/main/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +0 -36
  95. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/AdjustSliderToPositionActionTest.kt +0 -59
@@ -0,0 +1 @@
1
+ 1fd68b232ab5d4668575c8d666cacb5c8ab02e50f7aed50d8d53d36d2c669eee
@@ -0,0 +1 @@
1
+ 3b59f3522d88982355734b5a6d74add724fdd37b569bfc4fb07a8a1f954aab82e054bb5348833b9c156479af8440af6aabb4c2f0f684ff10afe7ca330d2a17a1
@@ -0,0 +1 @@
1
+ 3ab48363cf0d353f3013e0a1157a8f1781e3ef91f86929c321c885aba8f0af52
@@ -0,0 +1 @@
1
+ b5034dce6f23e5356cc635d35c511314c074f1a4b01ae5cf15663f00d9b27711add721b3a9e9b318bacae16209e8474ccf3fd29533f4e591353169da7ad6c2c5
@@ -0,0 +1 @@
1
+ 7828ffdff493f8a856efad9b7db540e4e6b13b91260292efe98791013ba2db04
@@ -0,0 +1 @@
1
+ a57448b5eec5edfb2b45b99b9c8a6714b3384f71ade0f25da7b453d14c29597e818690b293cefa9be9362be29efb2c420664283cb1fc994883cd143d46506a2f
@@ -1,9 +1,9 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
2
+ <project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3
3
  <modelVersion>4.0.0</modelVersion>
4
4
  <groupId>com.wix</groupId>
5
5
  <artifactId>detox</artifactId>
6
- <version>20.0.0-breaking.new-global-lifecycle.0</version>
6
+ <version>20.0.3-breaking.new-global-lifecycle.0</version>
7
7
  <packaging>aar</packaging>
8
8
  <name>Detox</name>
9
9
  <description>Gray box end-to-end testing and automation library for mobile apps</description>
@@ -0,0 +1 @@
1
+ f05c7aab6a3e42433c2934f4b7617441cce2d9c1879319cd5999b765cce8d968
@@ -0,0 +1 @@
1
+ 1c0756ed5dd13915de4625248beffb25c78fdf56635b54b65df36c61116ae860a66bc421e14641630d165b1cc630f51a225221dbd8789214d108bc531eb51ef7
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>20.0.0-breaking.new-global-lifecycle.0</latest>
7
- <release>20.0.0-breaking.new-global-lifecycle.0</release>
6
+ <latest>20.0.3-breaking.new-global-lifecycle.0</latest>
7
+ <release>20.0.3-breaking.new-global-lifecycle.0</release>
8
8
  <versions>
9
- <version>20.0.0-breaking.new-global-lifecycle.0</version>
9
+ <version>20.0.3-breaking.new-global-lifecycle.0</version>
10
10
  </versions>
11
- <lastUpdated>20220719065707</lastUpdated>
11
+ <lastUpdated>20220815091331</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- d6bdfc52b842956498f8c9915246ef54
1
+ 2ef77b80bf45d2428c511a0c5723661e
@@ -1 +1 @@
1
- 7d7b975015b7c6dc6804b63b99522ffaca9d09ff
1
+ a0dbdca1e65a0410798600e9b701a189b1831aac
@@ -1 +1 @@
1
- e3b6dce4491bb1703968fd4b82d89bfdf8c25014eac1fda6a64c196b9a79cea2
1
+ ef9c05cfe345d3f50b63c73a2207a80d0a417bb2288059f987285188457c41eb
@@ -1 +1 @@
1
- 3167b1a219d343785c974586d46d5a19dbe6985dd444d70f5bd364b747272c3da434f60f743c5a40c809a4f9db5e3665403471e5ed2756c50439c841993856cf
1
+ 495f7993e651cf04a0cc907638214236249c2944faec87b77795d0eb923a602b414c550dbe0d82ad3555d1b7f55a62671bcdf81e5169f1f23a8fecd53f1f5cca
package/Detox-ios-src.tbz CHANGED
Binary file
package/Detox-ios.tbz CHANGED
Binary file
@@ -3,7 +3,7 @@ buildscript {
3
3
  isOfficialDetoxLib = true
4
4
  kotlinVersion = '1.2.0'
5
5
  dokkaVersion = '1.6.0'
6
- buildToolsVersion = '30.0.2'
6
+ buildToolsVersion = '31.0.0'
7
7
  compileSdkVersion = 31
8
8
  targetSdkVersion = 31
9
9
  minSdkVersion = 21
@@ -15,7 +15,7 @@ buildscript {
15
15
  google()
16
16
  }
17
17
  dependencies {
18
- classpath 'com.android.tools.build:gradle:4.2.2'
18
+ classpath 'com.android.tools.build:gradle:7.0.4'
19
19
  classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
20
20
  classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokkaVersion"
21
21
 
@@ -5,8 +5,8 @@ apply plugin: 'kotlin-android-extensions'
5
5
  def _ext = rootProject.ext
6
6
  def _compileSdkVersion = _ext.has('compileSdkVersion') ? _ext.compileSdkVersion : 31
7
7
  def _targetSdkVersion = _ext.has('targetSdkVersion') ? _ext.targetSdkVersion : 31
8
- def _buildToolsVersion = _ext.has('buildToolsVersion') ? _ext.buildToolsVersion : '30.0.2'
9
- def _minSdkVersion = _ext.has('minSdkVersion') ? _ext.minSdkVersion : 18
8
+ def _buildToolsVersion = _ext.has('buildToolsVersion') ? _ext.buildToolsVersion : "31.0.0"
9
+ def _minSdkVersion = _ext.has('minSdkVersion') ? _ext.minSdkVersion : 21
10
10
  def _kotlinVersion = _ext.has('detoxKotlinVersion') ? _ext.detoxKotlinVersion : '1.2.0'
11
11
  def _kotlinStdlib = _ext.has('detoxKotlinStdlib') ? _ext.detoxKotlinStdlib : 'kotlin-stdlib-jdk8'
12
12
 
@@ -4,8 +4,12 @@ project.ext.buildPomXmlDependencies = { pom, configurations ->
4
4
  pom.withXml {
5
5
  final rootNode = asNode().appendNode('dependencies')
6
6
  addConfigurationDependencies(rootNode, configurations.api, 'compile')
7
- addConfigurationDependencies(rootNode, configurations.compile, 'compile')
8
7
  addConfigurationDependencies(rootNode, configurations.implementation, 'runtime')
8
+
9
+ // Legacy syntax
10
+ if (configurations.hasProperty('compile')) {
11
+ addConfigurationDependencies(rootNode, configurations.compile, 'compile')
12
+ }
9
13
  }
10
14
  }
11
15
 
@@ -140,13 +140,15 @@ signing {
140
140
 
141
141
  project.afterEvaluate {
142
142
  project.tasks.all { Task task ->
143
- android.libraryVariants.all { variant ->
144
- String variantName = variant.name.capitalize()
145
- if (task.name == "publishMaven${variantName}AarPublicationToMavenRepository") {
146
- task.dependsOn "assemble${variantName}"
147
- task.dependsOn project.tasks.signArchives
148
- task.doFirst {
149
- onPrePublish()
143
+ android {
144
+ libraryVariants.all { variant ->
145
+ String variantName = variant.name.capitalize()
146
+ if (task.name == "publishMaven${variantName}AarPublicationToMavenRepository") {
147
+ task.dependsOn "assemble${variantName}"
148
+ task.dependsOn project.tasks.signArchives
149
+ task.doFirst {
150
+ onPrePublish()
151
+ }
150
152
  }
151
153
  }
152
154
  }
@@ -2,6 +2,8 @@ package com.wix.detox.adapters.server;
2
2
 
3
3
  import android.util.Log;
4
4
 
5
+ import com.wix.detox.common.DetoxErrors;
6
+
5
7
  import org.json.JSONException;
6
8
  import org.json.JSONObject;
7
9
 
@@ -84,7 +86,7 @@ public class WebSocketClient {
84
86
  wsEventsHandler.onAction(type, params.toString(), messageId);
85
87
  }
86
88
  } catch (JSONException e) {
87
- Log.e(LOG_TAG, "Detox Error: receiveAction decode - " + e.toString());
89
+ throw new DetoxErrors.DetoxIllegalArgumentException(e);
88
90
  }
89
91
  }
90
92
 
@@ -2,7 +2,6 @@ package com.wix.detox.espresso;
2
2
 
3
3
  import android.view.View;
4
4
 
5
- import com.facebook.react.views.slider.ReactSliderManager;
6
5
  import com.wix.detox.common.DetoxErrors.DetoxRuntimeException;
7
6
  import com.wix.detox.common.DetoxErrors.StaleActionException;
8
7
  import com.wix.detox.espresso.action.AdjustSliderToPositionAction;
@@ -151,8 +150,7 @@ public class DetoxAction {
151
150
  }
152
151
 
153
152
  public static ViewAction adjustSliderToPosition(final double newPosition) {
154
- ReactSliderManager reactSliderManager = new ReactSliderManager();
155
- return new AdjustSliderToPositionAction(newPosition, reactSliderManager);
153
+ return new AdjustSliderToPositionAction(newPosition);
156
154
  }
157
155
 
158
156
  public static ViewAction takeViewScreenshot() {
@@ -5,7 +5,7 @@ import android.util.Log;
5
5
  import android.view.Choreographer;
6
6
 
7
7
  import com.wix.detox.common.UIThread;
8
- import com.wix.detox.espresso.common.utils.UiControllerUtils;
8
+ import com.wix.detox.espresso.action.common.utils.UiControllerUtils;
9
9
 
10
10
  import org.joor.Reflect;
11
11
  import org.joor.ReflectException;
@@ -0,0 +1,22 @@
1
+ package com.wix.detox.espresso.action
2
+
3
+ import android.view.View
4
+ import androidx.appcompat.widget.AppCompatSeekBar
5
+ import androidx.test.espresso.UiController
6
+ import androidx.test.espresso.ViewAction
7
+ import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
8
+ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
9
+ import com.wix.detox.espresso.common.SliderHelper
10
+ import org.hamcrest.Matcher
11
+ import org.hamcrest.Matchers
12
+
13
+ class AdjustSliderToPositionAction(private val targetPositionPct: Double) : ViewAction {
14
+ override fun getDescription() = "adjustSliderToPosition"
15
+ override fun getConstraints(): Matcher<View?>? =
16
+ Matchers.allOf( isDisplayed(), isAssignableFrom(AppCompatSeekBar::class.java) )
17
+
18
+ override fun perform(uiController: UiController?, view: View) {
19
+ val sliderHelper = SliderHelper.create(view)
20
+ sliderHelper.setProgressPct(targetPositionPct)
21
+ }
22
+ }
@@ -9,6 +9,7 @@ import android.widget.TextView
9
9
  import androidx.test.espresso.UiController
10
10
  import com.google.android.material.slider.Slider
11
11
  import com.wix.detox.espresso.ViewActionWithResult
12
+ import com.wix.detox.espresso.common.SliderHelper
12
13
  import org.hamcrest.Matcher
13
14
  import org.hamcrest.Matchers
14
15
  import org.hamcrest.Matchers.allOf
@@ -127,13 +128,24 @@ private class CheckBoxAttributes {
127
128
  rootObject.put("value", view.isChecked)
128
129
  }
129
130
 
131
+ /**
132
+ * Note: this applies also to [androidx.appcompat.widget.AppCompatSeekBar], which
133
+ * is anything RN-slider-ish.
134
+ */
130
135
  private class ProgressBarAttributes {
131
136
  fun get(json: JSONObject, view: View) {
132
137
  if (view is ProgressBar) {
133
- getProgressBarValue(json, view)
138
+ SliderHelper.maybeCreate(view)?.let {
139
+ getRNSliderValue(json, it)
140
+ } ?:
141
+ getProgressBarValue(json, view)
134
142
  }
135
143
  }
136
144
 
145
+ private fun getRNSliderValue(rootObject: JSONObject, sliderHelper: SliderHelper) {
146
+ rootObject.put("value", sliderHelper.getCurrentProgressPct())
147
+ }
148
+
137
149
  private fun getProgressBarValue(rootObject: JSONObject, view: ProgressBar) =
138
150
  rootObject.put("value", view.progress)
139
151
  }
@@ -0,0 +1,75 @@
1
+ package com.wix.detox.espresso.common
2
+
3
+ import android.view.View
4
+ import androidx.appcompat.widget.AppCompatSeekBar
5
+ import com.facebook.react.bridge.JavaOnlyMap
6
+ import com.facebook.react.uimanager.ReactStylesDiffMap
7
+ import com.facebook.react.views.slider.ReactSlider
8
+ import com.wix.detox.common.DetoxErrors.DetoxIllegalStateException
9
+ import com.wix.detox.espresso.action.common.ReflectUtils
10
+ import org.joor.Reflect
11
+
12
+ private const val CLASS_REACT_SLIDER_LEGACY = "com.facebook.react.views.slider.ReactSlider"
13
+ private const val CLASS_REACT_SLIDER_COMMUNITY = "com.reactnativecommunity.slider.ReactSlider"
14
+
15
+ abstract class SliderHelper(protected val slider: AppCompatSeekBar) {
16
+ fun getCurrentProgressPct(): Double {
17
+ val nativeProgress = slider.progress.toDouble()
18
+ val nativeMax = slider.max
19
+ return nativeProgress / nativeMax
20
+ }
21
+
22
+ // TODO Make this more testable (e.g. by delegating the set action away)
23
+ fun setProgressPct(valuePct: Double) {
24
+ val maxJSProgress = calcMaxJSProgress()
25
+ val valueJS = valuePct * maxJSProgress
26
+ setProgressJS(valueJS)
27
+ }
28
+
29
+ protected abstract fun setProgressJS(valueJS: Double)
30
+
31
+ private fun calcMaxJSProgress(): Double {
32
+ val nativeProgress = slider.progress.toDouble()
33
+ val nativeMax = slider.max
34
+ val toMaxFactor = nativeMax / nativeProgress
35
+
36
+ val jsProgress = getJSProgress()
37
+ return jsProgress * toMaxFactor
38
+ }
39
+
40
+ private fun getJSProgress(): Double =
41
+ Reflect.on(slider).call("toRealProgress", slider.progress).get() as Double
42
+
43
+ companion object {
44
+ fun create(view: View) =
45
+ maybeCreate(view)
46
+ ?: throw DetoxIllegalStateException("Cannot handle this type of a seek-bar view (Class ${view.javaClass.canonicalName}). " +
47
+ "Only React Native sliders are currently supported.")
48
+
49
+ fun maybeCreate(view: View): SliderHelper? =
50
+ when {
51
+ ReflectUtils.isAssignableFrom(view, CLASS_REACT_SLIDER_LEGACY)
52
+ -> LegacySliderHelper(view as ReactSlider)
53
+ ReflectUtils.isAssignableFrom(view, CLASS_REACT_SLIDER_COMMUNITY)
54
+ -> CommunitySliderHelper(view as AppCompatSeekBar)
55
+ else
56
+ -> null
57
+ }
58
+ }
59
+ }
60
+
61
+ private class LegacySliderHelper(slider: AppCompatSeekBar): SliderHelper(slider) {
62
+ override fun setProgressJS(valueJS: Double) {
63
+ val reactSliderManager = com.facebook.react.views.slider.ReactSliderManager()
64
+ reactSliderManager.updateProperties(slider as ReactSlider, buildStyles("value", valueJS))
65
+ }
66
+
67
+ private fun buildStyles(vararg keysAndValues: Any) = ReactStylesDiffMap(JavaOnlyMap.of(*keysAndValues))
68
+ }
69
+
70
+ private class CommunitySliderHelper(slider: AppCompatSeekBar): SliderHelper(slider) {
71
+ override fun setProgressJS(valueJS: Double) {
72
+ val reactSliderManager = Class.forName("com.reactnativecommunity.slider.ReactSliderManager").newInstance()
73
+ Reflect.on(reactSliderManager).call("setValue", slider, valueJS)
74
+ }
75
+ }
@@ -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.facebook.react.views.slider.ReactSlider
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
- }
@@ -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.addReactInstanceEventListener(object : ReactInstanceManager.ReactInstanceEventListener {
39
- override fun onReactContextInitialized(context: ReactContext) {
40
- Log.i(LOG_TAG, "Got new RN-context async'ly through listener")
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
+ }
@@ -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
- val rnVersion = ReactNativeInfo.rnVersion()
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 nativeViewHierarchyManager.getViewClass(tag)
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") {
@@ -0,0 +1,10 @@
1
+ package com.wix.detox.espresso.action.common
2
+
3
+ object ReflectUtils {
4
+ fun isAssignableFrom(source: Any, className: String) =
5
+ try {
6
+ Class.forName(className).isAssignableFrom(source.javaClass)
7
+ } catch (ex: ClassNotFoundException) {
8
+ false
9
+ }
10
+ }
@@ -1,6 +1,6 @@
1
1
  @file:JvmName("UiControllerUtils")
2
2
 
3
- package com.wix.detox.espresso.common.utils
3
+ package com.wix.detox.espresso.action.common.utils
4
4
 
5
5
  import androidx.test.espresso.Espresso
6
6
  import androidx.test.espresso.UiController