detox 20.9.1 → 20.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.js +1 -40
  3. package/Detox-android/com/wix/detox/{20.9.1/detox-20.9.1-javadoc.jar → 20.11.0/detox-20.11.0-javadoc.jar} +0 -0
  4. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0-javadoc.jar.md5 +1 -0
  5. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0-javadoc.jar.sha1 +1 -0
  6. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0-javadoc.jar.sha256 +1 -0
  7. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0-javadoc.jar.sha512 +1 -0
  8. package/Detox-android/com/wix/detox/{20.9.1/detox-20.9.1-sources.jar → 20.11.0/detox-20.11.0-sources.jar} +0 -0
  9. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0-sources.jar.md5 +1 -0
  10. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0-sources.jar.sha1 +1 -0
  11. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0-sources.jar.sha256 +1 -0
  12. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0-sources.jar.sha512 +1 -0
  13. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0.aar +0 -0
  14. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0.aar.md5 +1 -0
  15. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0.aar.sha1 +1 -0
  16. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0.aar.sha256 +1 -0
  17. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0.aar.sha512 +1 -0
  18. package/Detox-android/com/wix/detox/{20.9.1/detox-20.9.1.pom → 20.11.0/detox-20.11.0.pom} +1 -1
  19. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0.pom.md5 +1 -0
  20. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0.pom.sha1 +1 -0
  21. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0.pom.sha256 +1 -0
  22. package/Detox-android/com/wix/detox/20.11.0/detox-20.11.0.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-src.tbz +0 -0
  29. package/Detox-ios.tbz +0 -0
  30. package/android/build.gradle +20 -10
  31. package/android/detox/build.gradle +11 -4
  32. package/android/detox/src/full/java/com/wix/detox/espresso/DetoxMatcher.java +12 -12
  33. package/android/detox/src/full/java/com/wix/detox/espresso/common/SliderHelper.kt +2 -2
  34. package/android/detox/src/full/java/com/wix/detox/espresso/matcher/RegexMatcher.kt +56 -0
  35. package/android/detox/src/full/java/com/wix/detox/espresso/matcher/ViewMatchers.kt +16 -4
  36. package/android/detox/src/testFull/java/com/wix/detox/espresso/matcher/RegexMatcherTest.kt +52 -0
  37. package/android/gradle/wrapper/gradle-wrapper.properties +1 -1
  38. package/android/rninfo.gradle +25 -0
  39. package/android/settings.gradle +2 -1
  40. package/index.d.ts +10 -5
  41. package/local-cli/startCommand/AppStartCommand.js +4 -1
  42. package/package.json +14 -10
  43. package/src/android/espressoapi/DetoxMatcher.js +24 -8
  44. package/src/android/matchers/native.js +9 -4
  45. package/src/ios/expectTwo.js +8 -7
  46. package/src/utils/isRegExp.js +7 -0
  47. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1-javadoc.jar.md5 +0 -1
  48. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1-javadoc.jar.sha1 +0 -1
  49. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1-javadoc.jar.sha256 +0 -1
  50. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1-javadoc.jar.sha512 +0 -1
  51. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1-sources.jar.md5 +0 -1
  52. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1-sources.jar.sha1 +0 -1
  53. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1-sources.jar.sha256 +0 -1
  54. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1-sources.jar.sha512 +0 -1
  55. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1.aar +0 -0
  56. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1.aar.md5 +0 -1
  57. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1.aar.sha1 +0 -1
  58. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1.aar.sha256 +0 -1
  59. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1.aar.sha512 +0 -1
  60. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1.pom.md5 +0 -1
  61. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1.pom.sha1 +0 -1
  62. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1.pom.sha256 +0 -1
  63. package/Detox-android/com/wix/detox/20.9.1/detox-20.9.1.pom.sha512 +0 -1
package/.eslintignore CHANGED
@@ -3,3 +3,5 @@
3
3
  /ios
4
4
  /android
5
5
  /test
6
+ /allure-*
7
+ /artifacts
package/.eslintrc.js CHANGED
@@ -4,7 +4,7 @@ module.exports = {
4
4
  'eslint:recommended',
5
5
  'plugin:import/recommended',
6
6
  'plugin:node/recommended',
7
- 'plugin:unicorn/recommended'
7
+ 'plugin:ecmascript-compat/recommended'
8
8
  ],
9
9
  parser: '@typescript-eslint/parser',
10
10
  plugins: [
@@ -73,45 +73,6 @@ module.exports = {
73
73
  allowWarningComments: false,
74
74
  }
75
75
  ],
76
- // TODO: enable some of unicorn rules
77
- 'unicorn/better-regex': 'off',
78
- 'unicorn/catch-error-name': 'off',
79
- 'unicorn/consistent-destructuring': 'off',
80
- 'unicorn/consistent-function-scoping': 'off',
81
- 'unicorn/empty-brace-spaces': 'off',
82
- 'unicorn/error-message': 'off',
83
- 'unicorn/explicit-length-check': 'off',
84
- 'unicorn/filename-case': 'off',
85
- 'unicorn/import-style': 'off',
86
- 'unicorn/new-for-builtins': 'off',
87
- 'unicorn/no-abusive-eslint-disable': 'off',
88
- 'unicorn/no-array-callback-reference': 'off',
89
- 'unicorn/no-array-for-each': 'off',
90
- 'unicorn/no-array-reduce': 'off',
91
- 'unicorn/no-await-expression-member': 'off',
92
- 'unicorn/no-lonely-if': 'off',
93
- 'unicorn/no-nested-ternary': 'off',
94
- 'unicorn/no-new-array': 'off',
95
- 'unicorn/no-null': 'off',
96
- 'unicorn/no-object-as-default-parameter': 'off',
97
- 'unicorn/no-useless-undefined': 'off',
98
- 'unicorn/number-literal-case': 'off',
99
- 'unicorn/numeric-separators-style': 'off',
100
- 'unicorn/prefer-add-event-listener': 'off',
101
- 'unicorn/prefer-array-some': 'off',
102
- 'unicorn/prefer-array-flat': 'off',
103
- 'unicorn/prefer-includes': 'off',
104
- 'unicorn/prefer-module': 'off',
105
- 'unicorn/prefer-number-properties': 'off',
106
- 'unicorn/prefer-object-from-entries': 'off',
107
- 'unicorn/prefer-optional-catch-binding': 'off',
108
- 'unicorn/prefer-regexp-test': 'off',
109
- 'unicorn/prefer-spread': 'off',
110
- 'unicorn/prefer-string-slice': 'off',
111
- 'unicorn/prefer-string-starts-ends-with': 'off',
112
- 'unicorn/prefer-string-trim-start-end': 'off',
113
- 'unicorn/prefer-ternary': 'off',
114
- 'unicorn/prevent-abbreviations': 'off',
115
76
  },
116
77
 
117
78
  overrides: [
@@ -0,0 +1 @@
1
+ f0cf6ad0def43fbab5ba6fae7b4e5441
@@ -0,0 +1 @@
1
+ 48b29f67227c7b45844172ffdc0b25c7e5bd4f05
@@ -0,0 +1 @@
1
+ 0dbfb89e22024b3c3f45e0941a9fd3421d6a4f4041b76f8f54edd4259a9092a8
@@ -0,0 +1 @@
1
+ a1a2706b699d0c9217252c287a3d82b679bb206423ebbee820a261cd5925970426596f99a16fdcd8d37107185c3d5ec80628dd48309d0c31c89a00a37f7bf292
@@ -0,0 +1 @@
1
+ 3579eb8ea896facf3fd7e2be627cdc03
@@ -0,0 +1 @@
1
+ 1fb720e2b0f0da39406134aabcdafec756f9cbe5
@@ -0,0 +1 @@
1
+ f1c18496d23e09cffc96ef1b3bcbf349b1ae0a020ed5a6707b64bf22e30d9180
@@ -0,0 +1 @@
1
+ ebfcaef97fb6aa02bbda14d12a26eaac823620766c7e3b84eee7fa0115edb17463ea59ac689d07ffc97e84d2dbdbbd7db3014fdd976d0b2dd207c6278494f523
@@ -0,0 +1 @@
1
+ 518f621bfc037203c5ddd30c3f62c8ad
@@ -0,0 +1 @@
1
+ 65065fce4ddace8e95d235b60cf5a27f93e1162b
@@ -0,0 +1 @@
1
+ cd516e30305ad672e7ed0e8b4f77e992482c9a2979ce92d88a074dc45524cbe9
@@ -0,0 +1 @@
1
+ 00ff273c46cb32923f574fd00189459e9dff6d4782ac561f30583c9610427e270450783485aece4e9ebeb52c12bd3eec1560521d5a0535965dd278c7b44f3848
@@ -3,7 +3,7 @@
3
3
  <modelVersion>4.0.0</modelVersion>
4
4
  <groupId>com.wix</groupId>
5
5
  <artifactId>detox</artifactId>
6
- <version>20.9.1</version>
6
+ <version>20.11.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
+ 72868177d685cfd9912ca8fe3c08da3a
@@ -0,0 +1 @@
1
+ ad0ed20f218ec25f055f5c668c9b6dcd875069bf
@@ -0,0 +1 @@
1
+ b1c48d9cf1df54b6cbe27c45a4c8834162b04a783601a3d4b030fd3ac12e723e
@@ -0,0 +1 @@
1
+ c68e22610d04ad949b65cffe6305e30bab93ca1a10d53d0cd3ded77dee8f64d7d14f1b250cbdde553a06a4fe5b50738af7ae363b5f7ad836f816f48ca845d189
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>20.9.1</latest>
7
- <release>20.9.1</release>
6
+ <latest>20.11.0</latest>
7
+ <release>20.11.0</release>
8
8
  <versions>
9
- <version>20.9.1</version>
9
+ <version>20.11.0</version>
10
10
  </versions>
11
- <lastUpdated>20230524200941</lastUpdated>
11
+ <lastUpdated>20230704204300</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 1d94c37700d4fbcc03221a27b3da67ed
1
+ e930da2d4686f5b8bbe2fe5638fab155
@@ -1 +1 @@
1
- 744c262d2fe22c39fafca012024e95592cdb583e
1
+ 7ec428486d1d978a16101aedc506e79483ccf63a
@@ -1 +1 @@
1
- 367b8367ac51f3b1268b86a694f294404cdb0dfe4786fb8267ea1c5f7e78e3cb
1
+ 469a65278a6ed368df597dfb505c150591e4ab587b190dbee2bb12c50b41a6ef
@@ -1 +1 @@
1
- 6de3736757b56504e83b488b7aabff2384a75d70ed303bac1ccb556615aef3adf491d9bd77287aa11a1cebfd88b0cda73e3d17c2498f53d04a65ba5b6678f882
1
+ dfc9bd1f96626dee848008639fc467b70d4653f57761c30cff16f41550d1f2a79dcd9aefd64c7313b8672c503e1a12dc360b1742faba6e2946878f6b2f8bbaac
package/Detox-ios-src.tbz CHANGED
Binary file
package/Detox-ios.tbz CHANGED
Binary file
@@ -1,11 +1,13 @@
1
1
  buildscript {
2
+ apply from: './rninfo.gradle'
3
+
2
4
  ext {
3
5
  isOfficialDetoxLib = true
4
- kotlinVersion = '1.6.10' // Aligned with RN .69's version bump
6
+ kotlinVersion = '1.8.22'
5
7
  dokkaVersion = '1.6.0'
6
- buildToolsVersion = '31.0.0'
7
- compileSdkVersion = 31
8
- targetSdkVersion = 31
8
+ buildToolsVersion = '33.0.0'
9
+ compileSdkVersion = 33
10
+ targetSdkVersion = 33
9
11
  minSdkVersion = 21
10
12
 
11
13
  if (System.properties['os.arch'] == "aarch64") {
@@ -19,11 +21,14 @@ buildscript {
19
21
  ext.detoxKotlinVersion = ext.kotlinVersion
20
22
 
21
23
  repositories {
22
- mavenCentral()
23
24
  google()
25
+ mavenCentral()
24
26
  }
25
27
  dependencies {
26
- classpath 'com.android.tools.build:gradle:7.2.1'
28
+ if (!rnInfo.isRN71OrNewer) {
29
+ classpath "com.facebook.react:react-native-gradle-plugin"
30
+ }
31
+ classpath 'com.android.tools.build:gradle:7.3.1'
27
32
  classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
28
33
  classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokkaVersion"
29
34
 
@@ -34,11 +39,16 @@ buildscript {
34
39
 
35
40
  allprojects {
36
41
  repositories {
37
- mavenLocal()
38
- mavenCentral()
39
42
  google()
40
- maven {
41
- url "$projectDir/../../node_modules/react-native/android"
43
+ mavenCentral()
44
+ mavenLocal()
45
+
46
+ // In RN's below 71, the native code comes from within node_modules/ rather
47
+ // than from maven-central.
48
+ if (!rnInfo.isRN71OrNewer) {
49
+ maven {
50
+ url "$projectDir/../../node_modules/react-native/android"
51
+ }
42
52
  }
43
53
  }
44
54
  }
@@ -1,5 +1,6 @@
1
1
  apply plugin: 'com.android.library'
2
2
  apply plugin: 'kotlin-android'
3
+ apply from: '../rninfo.gradle'
3
4
 
4
5
  def _kotlinMinVersion = '1.2.0'
5
6
  def _materialMinVersion = '1.2.1'
@@ -12,6 +13,13 @@ def _minSdkVersion = _ext.has('minSdkVersion') ? _ext.minSdkVersion : 21
12
13
  def _kotlinVersion = _ext.has('detoxKotlinVersion') ? _ext.detoxKotlinVersion : _kotlinMinVersion
13
14
  def _kotlinStdlib = _ext.has('detoxKotlinStdlib') ? _ext.detoxKotlinStdlib : 'kotlin-stdlib-jdk8'
14
15
 
16
+ // RN native code comes from either maven-central (in which case, need the *exact* version),
17
+ // or otherwise from node_modules/, where the version is already aligned, by definition.
18
+ // noinspection GradleDynamicVersion
19
+ def _rnNativeArtifact = rnInfo.isRN71OrHigher
20
+ ? "com.facebook.react:react-android:${rnInfo.version}"
21
+ : 'com.facebook.react:react-native:+'
22
+
15
23
  android {
16
24
  compileSdkVersion _compileSdkVersion
17
25
  buildToolsVersion _buildToolsVersion
@@ -78,8 +86,8 @@ android {
78
86
  // Fundamental deps.
79
87
  dependencies {
80
88
  implementation "org.jetbrains.kotlin:$_kotlinStdlib:$_kotlinMinVersion"
81
- //noinspection GradleDynamicVersion
82
- compileOnly 'com.facebook.react:react-native:+'
89
+
90
+ compileOnly "${_rnNativeArtifact}"
83
91
  }
84
92
 
85
93
  // androidx.test deps.
@@ -131,8 +139,7 @@ dependencies {
131
139
 
132
140
  // Unit-testing deps.
133
141
  dependencies {
134
- //noinspection GradleDynamicVersion
135
- testImplementation 'com.facebook.react:react-native:+'
142
+ testImplementation "${_rnNativeArtifact}"
136
143
  testImplementation 'org.json:json:20140107'
137
144
 
138
145
  // https://github.com/spekframework/spek/issues/232#issuecomment-610732158
@@ -14,10 +14,10 @@ import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
14
14
  import static androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
15
15
  import static androidx.test.espresso.matcher.ViewMatchers.isFocused;
16
16
  import static androidx.test.espresso.matcher.ViewMatchers.isNotChecked;
17
- import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
18
17
  import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
19
- import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
20
- import static androidx.test.espresso.matcher.ViewMatchers.withText;
18
+ import static com.wix.detox.espresso.matcher.ViewMatchers.withTagValue;
19
+ import static com.wix.detox.espresso.matcher.ViewMatchers.withContentDescription;
20
+ import static com.wix.detox.espresso.matcher.ViewMatchers.withText;
21
21
  import static com.wix.detox.espresso.matcher.ViewMatchers.isMatchingAtIndex;
22
22
  import static com.wix.detox.espresso.matcher.ViewMatchers.isOfClassName;
23
23
  import static com.wix.detox.espresso.matcher.ViewMatchers.toHaveSliderPosition;
@@ -40,25 +40,25 @@ public class DetoxMatcher {
40
40
  // static class
41
41
  }
42
42
 
43
- public static Matcher<View> matcherForText(String text) {
43
+ public static Matcher<View> matcherForText(String text, boolean isRegex) {
44
44
  // return anyOf(withText(text), withContentDescription(text));
45
- return allOf(withText(text), withEffectiveVisibility(Visibility.VISIBLE));
45
+ return allOf(withText(text, isRegex), withEffectiveVisibility(Visibility.VISIBLE));
46
46
  }
47
47
 
48
- public static Matcher<View> matcherForAccessibilityLabel(String label) {
49
- return allOf(withAccessibilityLabel(label), withEffectiveVisibility(Visibility.VISIBLE));
48
+ public static Matcher<View> matcherForAccessibilityLabel(String label, boolean isRegex) {
49
+ return allOf(withAccessibilityLabel(label, isRegex), withEffectiveVisibility(Visibility.VISIBLE));
50
50
  }
51
51
 
52
- public static Matcher<View> matcherForShallowAccessibilityLabel(String label) {
53
- return allOf(withShallowAccessibilityLabel(label), withEffectiveVisibility(Visibility.VISIBLE));
52
+ public static Matcher<View> matcherForShallowAccessibilityLabel(String label, boolean isRegex) {
53
+ return allOf(withShallowAccessibilityLabel(label, isRegex), withEffectiveVisibility(Visibility.VISIBLE));
54
54
  }
55
55
 
56
56
  public static Matcher<View> matcherForContentDescription(String contentDescription) {
57
- return allOf(withContentDescription(contentDescription), withEffectiveVisibility(Visibility.VISIBLE));
57
+ return allOf(withContentDescription(contentDescription, false), withEffectiveVisibility(Visibility.VISIBLE));
58
58
  }
59
59
 
60
- public static Matcher<View> matcherForTestId(String testId) {
61
- return allOf(withTagValue(is((Object) testId)), withEffectiveVisibility(Visibility.VISIBLE));
60
+ public static Matcher<View> matcherForTestId(String testId, boolean isRegex) {
61
+ return allOf(withTagValue(testId, isRegex), withEffectiveVisibility(Visibility.VISIBLE));
62
62
  }
63
63
 
64
64
  public static Matcher<View> matcherForToggleable(boolean value) {
@@ -4,9 +4,9 @@ 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"
@@ -58,7 +58,7 @@ abstract class SliderHelper(protected val slider: AppCompatSeekBar) {
58
58
  }
59
59
  }
60
60
 
61
- private class LegacySliderHelper(slider: AppCompatSeekBar): SliderHelper(slider) {
61
+ private class LegacySliderHelper(slider: ReactSlider): SliderHelper(slider) {
62
62
  override fun setProgressJS(valueJS: Double) {
63
63
  val reactSliderManager = com.facebook.react.views.slider.ReactSliderManager()
64
64
  reactSliderManager.updateProperties(slider as ReactSlider, buildStyles("value", valueJS))
@@ -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
+ }
@@ -11,16 +11,28 @@ import com.wix.detox.espresso.common.SliderHelper
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 {
@@ -0,0 +1,52 @@
1
+ package com.wix.detox.espresso.matcher
2
+
3
+ import org.junit.Test
4
+ import kotlin.test.assertFalse
5
+ import kotlin.test.assertTrue
6
+ import org.junit.runner.RunWith
7
+ import org.robolectric.RobolectricTestRunner
8
+
9
+ @RunWith(RobolectricTestRunner::class)
10
+ class RegexMatcherTest {
11
+ @Test
12
+ fun `should work with string matching regex`() {
13
+ val input = "Hello, world!"
14
+ val regex = "/[A-Z][a-z]+, world!/"
15
+ assertTrue(input.matchesJSRegex(regex))
16
+ }
17
+
18
+ @Test
19
+ fun `should work with string not matching regex`() {
20
+ val input = "Hello, world!"
21
+ val regex = "/[A-Z]+, world!/"
22
+ assertFalse(input.matchesJSRegex(regex))
23
+ }
24
+
25
+ @Test
26
+ fun `should work with the 'i' flag`() {
27
+ val input = "Hello, world!"
28
+ val regex = "/[A-Z]+, woRlD!/i"
29
+ assertTrue(input.matchesJSRegex(regex))
30
+ }
31
+
32
+ @Test
33
+ fun `should work with the 's' flag`() {
34
+ val input = "Hello,\nworld!"
35
+ val regex = "/Hello,\\sworld!/s"
36
+ assertTrue(input.matchesJSRegex(regex))
37
+ }
38
+
39
+ @Test
40
+ fun `should work with the 'm' flag`() {
41
+ val input = "Hello,\nworld!"
42
+ val regex = "/^Hello,\\s.*!$/m"
43
+ assertTrue(input.matchesJSRegex(regex))
44
+ }
45
+
46
+ @Test
47
+ fun `should work with multiple flags, ignore casing`() {
48
+ val input = "Hello,\nworld!"
49
+ val regex = "/^heLLo,\\swOrld!/ISM"
50
+ assertTrue(input.matchesJSRegex(regex))
51
+ }
52
+ }
@@ -3,5 +3,5 @@ distributionBase=GRADLE_USER_HOME
3
3
  distributionPath=wrapper/dists
4
4
  zipStoreBase=GRADLE_USER_HOME
5
5
  zipStorePath=wrapper/dists
6
- distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
6
+ distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
7
7
 
@@ -0,0 +1,25 @@
1
+ import groovy.json.JsonSlurper
2
+
3
+ def rnVersion = getRNVersion()
4
+ def rnMajorVer = getMajorVersion(rnVersion)
5
+ println "[$project] RNInfo: detected React Native version: $rnVersion (major=$rnMajorVer)"
6
+
7
+ project.ext.rnInfo = [
8
+ version: rnVersion,
9
+ majorVersion: rnMajorVer,
10
+ isRN69OrHigher: rnMajorVer >= 69,
11
+ isRN70OrHigher: rnMajorVer >= 70,
12
+ isRN71OrHigher: rnMajorVer >= 71,
13
+ ]
14
+
15
+ private static def getRNVersion() {
16
+ def jsonSlurper = new JsonSlurper()
17
+ Map<String, Object> packageJSON = jsonSlurper.parse(new File('../node_modules/react-native/package.json'))
18
+ String rnVersion = packageJSON.get('version')
19
+ return rnVersion
20
+ }
21
+
22
+ private static def getMajorVersion(semanticVersion) {
23
+ Integer rnVersionMajor = semanticVersion.split('\\.')[1].toInteger()
24
+ return rnVersionMajor
25
+ }
@@ -1 +1,2 @@
1
- include ':detox'
1
+ include ':detox'
2
+ includeBuild('../node_modules/react-native-gradle-plugin')
package/index.d.ts CHANGED
@@ -971,20 +971,25 @@ declare global {
971
971
  * <TouchableOpacity testID={'tap_me'}>
972
972
  * // Then match with by.id:
973
973
  * await element(by.id('tap_me'));
974
+ * await element(by.id(/^tap_[a-z]+$/));
974
975
  */
975
- id(id: string): NativeMatcher;
976
+ id(id: string | RegExp): NativeMatcher;
976
977
 
977
978
  /**
978
979
  * Find an element by text, useful for text fields, buttons.
979
- * @example await element(by.text('Tap Me'));
980
+ * @example
981
+ * await element(by.text('Tap Me'));
982
+ * await element(by.text(/^Tap .*$/));
980
983
  */
981
- text(text: string): NativeMatcher;
984
+ text(text: string | RegExp): NativeMatcher;
982
985
 
983
986
  /**
984
987
  * Find an element by accessibilityLabel on iOS, or by contentDescription on Android.
985
- * @example await element(by.label('Welcome'));
988
+ * @example
989
+ * await element(by.label('Welcome'));
990
+ * await element(by.label(/[a-z]+/i));
986
991
  */
987
- label(label: string): NativeMatcher;
992
+ label(label: string | RegExp): NativeMatcher;
988
993
 
989
994
  /**
990
995
  * Find an element by native view type.
@@ -36,7 +36,10 @@ class AppStartCommand {
36
36
  }
37
37
  };
38
38
 
39
- this._cpHandle = execa.command(cmd, { stdio: 'inherit', shell: true });
39
+ this._cpHandle = execa.command(cmd, {
40
+ stdio: ['ignore', 'inherit', 'inherit'],
41
+ shell: true
42
+ });
40
43
  this._cpHandle.on('error', onError);
41
44
  this._cpHandle.on('exit', (code, signal) => {
42
45
  const reason = code == null ? `signal ${signal}` : `code ${code}`;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "detox",
3
3
  "description": "E2E tests and automation for mobile",
4
- "version": "20.9.1",
4
+ "version": "20.11.0",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -41,19 +41,20 @@
41
41
  "@types/node": "^14.18.33",
42
42
  "@types/node-ipc": "^9.2.0",
43
43
  "@types/ws": "^7.4.0",
44
- "@typescript-eslint/eslint-plugin": "^5.4.0",
45
- "@typescript-eslint/parser": "^5.4.0",
44
+ "@typescript-eslint/eslint-plugin": "^5.59.8",
45
+ "@typescript-eslint/parser": "^5.59.8",
46
46
  "cross-env": "^7.0.3",
47
- "eslint": "^8.3.0",
48
- "eslint-plugin-import": "^2.23.3",
49
- "eslint-plugin-no-only-tests": "^2.6.0",
47
+ "eslint": "^8.41.0",
48
+ "eslint-plugin-ecmascript-compat": "^3.0.0",
49
+ "eslint-plugin-import": "^2.27.5",
50
+ "eslint-plugin-no-only-tests": "^3.1.0",
50
51
  "eslint-plugin-node": "^11.1.0",
51
- "eslint-plugin-unicorn": "^39.0.0",
52
+ "eslint-plugin-unicorn": "^47.0.0",
52
53
  "jest": "^28.1.3",
53
54
  "jest-allure2-reporter": "^1.2.1",
54
55
  "mockdate": "^2.0.1",
55
- "prettier": "1.7.0",
56
- "react-native": "0.70.7",
56
+ "prettier": "^2.4.1",
57
+ "react-native": "0.71.10",
57
58
  "react-native-codegen": "^0.0.8",
58
59
  "typescript": "^4.5.2",
59
60
  "wtfnode": "^0.9.1"
@@ -202,5 +203,8 @@
202
203
  }
203
204
  }
204
205
  },
205
- "gitHead": "354ee9919959b322d9907570aa57d0be65bdf8fc"
206
+ "browserslist": [
207
+ "node 14"
208
+ ],
209
+ "gitHead": "e132a02242f1a4ebcf1775ba4ceb782d5b1a855c"
206
210
  }
@@ -14,39 +14,51 @@ function sanitize_matcher(matcher) {
14
14
  return originalMatcher.type ? originalMatcher.value : originalMatcher;
15
15
  }
16
16
  class DetoxMatcher {
17
- static matcherForText(text) {
17
+ static matcherForText(text, isRegex) {
18
18
  if (typeof text !== "string") throw new Error("text should be a string, but got " + (text + (" (" + (typeof text + ")"))));
19
+ if (typeof isRegex !== "boolean") throw new Error("isRegex should be a boolean, but got " + (isRegex + (" (" + (typeof isRegex + ")"))));
19
20
  return {
20
21
  target: {
21
22
  type: "Class",
22
23
  value: "com.wix.detox.espresso.DetoxMatcher"
23
24
  },
24
25
  method: "matcherForText",
25
- args: [text]
26
+ args: [text, {
27
+ type: "boolean",
28
+ value: isRegex
29
+ }]
26
30
  };
27
31
  }
28
32
 
29
- static matcherForAccessibilityLabel(label) {
33
+ static matcherForAccessibilityLabel(label, isRegex) {
30
34
  if (typeof label !== "string") throw new Error("label should be a string, but got " + (label + (" (" + (typeof label + ")"))));
35
+ if (typeof isRegex !== "boolean") throw new Error("isRegex should be a boolean, but got " + (isRegex + (" (" + (typeof isRegex + ")"))));
31
36
  return {
32
37
  target: {
33
38
  type: "Class",
34
39
  value: "com.wix.detox.espresso.DetoxMatcher"
35
40
  },
36
41
  method: "matcherForAccessibilityLabel",
37
- args: [label]
42
+ args: [label, {
43
+ type: "boolean",
44
+ value: isRegex
45
+ }]
38
46
  };
39
47
  }
40
48
 
41
- static matcherForShallowAccessibilityLabel(label) {
49
+ static matcherForShallowAccessibilityLabel(label, isRegex) {
42
50
  if (typeof label !== "string") throw new Error("label should be a string, but got " + (label + (" (" + (typeof label + ")"))));
51
+ if (typeof isRegex !== "boolean") throw new Error("isRegex should be a boolean, but got " + (isRegex + (" (" + (typeof isRegex + ")"))));
43
52
  return {
44
53
  target: {
45
54
  type: "Class",
46
55
  value: "com.wix.detox.espresso.DetoxMatcher"
47
56
  },
48
57
  method: "matcherForShallowAccessibilityLabel",
49
- args: [label]
58
+ args: [label, {
59
+ type: "boolean",
60
+ value: isRegex
61
+ }]
50
62
  };
51
63
  }
52
64
 
@@ -62,15 +74,19 @@ class DetoxMatcher {
62
74
  };
63
75
  }
64
76
 
65
- static matcherForTestId(testId) {
77
+ static matcherForTestId(testId, isRegex) {
66
78
  if (typeof testId !== "string") throw new Error("testId should be a string, but got " + (testId + (" (" + (typeof testId + ")"))));
79
+ if (typeof isRegex !== "boolean") throw new Error("isRegex should be a boolean, but got " + (isRegex + (" (" + (typeof isRegex + ")"))));
67
80
  return {
68
81
  target: {
69
82
  type: "Class",
70
83
  value: "com.wix.detox.espresso.DetoxMatcher"
71
84
  },
72
85
  method: "matcherForTestId",
73
- args: [testId]
86
+ args: [testId, {
87
+ type: "boolean",
88
+ value: isRegex
89
+ }]
74
90
  };
75
91
  }
76
92
 
@@ -1,26 +1,30 @@
1
1
  const DetoxRuntimeError = require('../../errors/DetoxRuntimeError');
2
2
  const invoke = require('../../invoke');
3
+ const { isRegExp } = require('../../utils/isRegExp');
3
4
  const { NativeMatcher } = require('../core/NativeMatcher');
4
5
  const DetoxMatcherApi = require('../espressoapi/DetoxMatcher');
5
6
 
6
7
  class LabelMatcher extends NativeMatcher {
7
8
  constructor(value) {
8
9
  super();
9
- this._call = invoke.callDirectly(DetoxMatcherApi.matcherForAccessibilityLabel(value));
10
+ const isRegex = isRegExp(value);
11
+ this._call = invoke.callDirectly(DetoxMatcherApi.matcherForAccessibilityLabel(isRegex ? value.toString() : value, isRegex));
10
12
  }
11
13
  }
12
14
 
13
15
  class ShallowLabelMatcher extends NativeMatcher {
14
16
  constructor(value) {
15
17
  super();
16
- this._call = invoke.callDirectly(DetoxMatcherApi.matcherForShallowAccessibilityLabel(value));
18
+ const isRegex = isRegExp(value);
19
+ this._call = invoke.callDirectly(DetoxMatcherApi.matcherForShallowAccessibilityLabel(isRegex ? value.toString() : value, isRegex));
17
20
  }
18
21
  }
19
22
 
20
23
  class IdMatcher extends NativeMatcher {
21
24
  constructor(value) {
22
25
  super();
23
- this._call = invoke.callDirectly(DetoxMatcherApi.matcherForTestId(value));
26
+ const isRegex = isRegExp(value);
27
+ this._call = invoke.callDirectly(DetoxMatcherApi.matcherForTestId(isRegex ? value.toString() : value, isRegex));
24
28
  }
25
29
  }
26
30
 
@@ -53,7 +57,8 @@ class ExistsMatcher extends NativeMatcher {
53
57
  class TextMatcher extends NativeMatcher {
54
58
  constructor(value) {
55
59
  super();
56
- this._call = invoke.callDirectly(DetoxMatcherApi.matcherForText(value));
60
+ const isRegex = isRegExp(value);
61
+ this._call = invoke.callDirectly(DetoxMatcherApi.matcherForText(isRegex ? value.toString() : value, isRegex));
57
62
  }
58
63
  }
59
64
 
@@ -9,6 +9,7 @@ const tempfile = require('tempfile');
9
9
  const { assertEnum, assertNormalized } = require('../utils/assertArgument');
10
10
  const { removeMilliseconds } = require('../utils/dateUtils');
11
11
  const { actionDescription, expectDescription } = require('../utils/invocationTraceDescriptions');
12
+ const { isRegExp } = require('../utils/isRegExp');
12
13
  const log = require('../utils/logger').child({ cat: 'ws-client, ws' });
13
14
  const traceInvocationCall = require('../utils/traceInvocationCall').bind(null, log);
14
15
 
@@ -231,7 +232,7 @@ class Element {
231
232
 
232
233
  performAccessibilityAction(actionName) {
233
234
  if (typeof actionName !== 'string') throw new Error('actionName should be a string, but got ' + (actionName + (' (' + (typeof actionName + ')'))));
234
-
235
+
235
236
  const traceDescription = actionDescription.performAccessibilityAction(actionName);
236
237
  return this.withAction('accessibilityAction', traceDescription, actionName);
237
238
  }
@@ -409,14 +410,14 @@ class Matcher {
409
410
  }
410
411
 
411
412
  label(label) {
412
- if (typeof label !== 'string') throw new Error('label should be a string, but got ' + (label + (' (' + (typeof label + ')'))));
413
- this.predicate = { type: 'label', value: label };
413
+ if (typeof label !== 'string' && !isRegExp(label)) throw new Error('label should be a string or regex, but got ' + (label + (' (' + (typeof label + ')'))));
414
+ this.predicate = { type: 'label', value: label.toString(), isRegex: isRegExp(label) };
414
415
  return this;
415
416
  }
416
417
 
417
418
  id(id) {
418
- if (typeof id !== 'string') throw new Error('id should be a string, but got ' + (id + (' (' + (typeof id + ')'))));
419
- this.predicate = { type: 'id', value: id };
419
+ if (typeof id !== 'string' && !isRegExp(id)) throw new Error('id should be a string or regex, but got ' + (id + (' (' + (typeof id + ')'))));
420
+ this.predicate = { type: 'id', value: id.toString(), isRegex: isRegExp(id) };
420
421
  return this;
421
422
  }
422
423
 
@@ -439,8 +440,8 @@ class Matcher {
439
440
  }
440
441
 
441
442
  text(text) {
442
- if (typeof text !== 'string') throw new Error('text should be a string, but got ' + (text + (' (' + (typeof text + ')'))));
443
- this.predicate = { type: 'text', value: text };
443
+ if (typeof text !== 'string' && !isRegExp(text)) throw new Error(`text should be a string or regex, but got ` + (text + (' (' + (typeof text + ')'))));
444
+ this.predicate = { type: 'text', value: text.toString(), isRegex: isRegExp(text) };
444
445
  return this;
445
446
  }
446
447
 
@@ -0,0 +1,7 @@
1
+ function isRegExp(obj) {
2
+ return Object.prototype.toString.call(obj) === '[object RegExp]';
3
+ }
4
+
5
+ module.exports = {
6
+ isRegExp,
7
+ };
@@ -1 +0,0 @@
1
- e135691ef3c16000236576219ead8095
@@ -1 +0,0 @@
1
- 2ae996dffa140d6ef33003e472c5396bcf1fed97
@@ -1 +0,0 @@
1
- 489299047e3a1a1a10102b834874b119d5373263c8df9d010dd3696af0ca3cb5
@@ -1 +0,0 @@
1
- e5d017cb7ea0f9707dca69c80012edcd1320436c72a1bf922bcddf7526c885ba1a16e3bcbfc32122235ac2f122f2d40890f45b46d69eafb6d4bd612097a634c6
@@ -1 +0,0 @@
1
- 75020f38aa6029c7692b7a014159143f
@@ -1 +0,0 @@
1
- d0695ef9339d69af09a14e77c495339e5ff00700
@@ -1 +0,0 @@
1
- de56a9ce5199b6954621b9ab71b065882f50a50f7e723f78540bed54a17225fb
@@ -1 +0,0 @@
1
- 80684cd2db41f4c70e9a5795578b7eb7f528b39af836deec99a08e3ad38cf49c1e37bec6b98c8aedf0fdb03a6ca881b136149121639016dffe402f666152ca7b
@@ -1 +0,0 @@
1
- e89d3204dcda439e4deb0df61603394a
@@ -1 +0,0 @@
1
- eda5c226e954731b3dbd16becbab72f66888d5ce
@@ -1 +0,0 @@
1
- 4da9ab41881efc27c7268bff2bfbb7092d9dbdf6de8f43787b4df649f0291bff
@@ -1 +0,0 @@
1
- c63bf901eb0327ca3791b419f99dd67060a9b48a816da5c71e7e660e60fed607347ef8f76710969e9497054cc4f99add8db19f47b7e780ee3e6413df6d75e195
@@ -1 +0,0 @@
1
- c40ce1eb663c728fcb4997417bd2bad2
@@ -1 +0,0 @@
1
- 32a60d3d786cd018febcd30ae1a09a30772e1112
@@ -1 +0,0 @@
1
- de63c952930025cf608b8079311242c75647b0daa0f8ed18d4a5bf10f013f9c3
@@ -1 +0,0 @@
1
- 5a7090284861016e6589c5ddb404f8075ee1926ebfec73ce993e0f41048163e3b27876fe10c43f9c3f9dc80c842841eeb0d1b842737b87afba29334369e839c7