detox 20.19.5 → 20.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/Detox-android/com/wix/detox/{20.19.5/detox-20.19.5-sources.jar → 20.20.0/detox-20.20.0-sources.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0-sources.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0-sources.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0-sources.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0-sources.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0.aar +0 -0
  7. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0.aar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0.aar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0.aar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0.aar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/{20.19.5/detox-20.19.5.pom → 20.20.0/detox-20.20.0.pom} +1 -1
  12. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0.pom.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0.pom.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0.pom.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.20.0/detox-20.20.0.pom.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  17. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  18. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  19. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  20. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  21. package/Detox-android/com/wix/detox-legacy/{20.19.5/detox-legacy-20.19.5-sources.jar → 20.20.0/detox-legacy-20.20.0-sources.jar} +0 -0
  22. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0-sources.jar.md5 +1 -0
  23. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0-sources.jar.sha1 +1 -0
  24. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0-sources.jar.sha256 +1 -0
  25. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0-sources.jar.sha512 +1 -0
  26. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0.aar +0 -0
  27. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0.aar.md5 +1 -0
  28. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0.aar.sha1 +1 -0
  29. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0.aar.sha256 +1 -0
  30. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0.aar.sha512 +1 -0
  31. package/Detox-android/com/wix/detox-legacy/{20.19.5/detox-legacy-20.19.5.pom → 20.20.0/detox-legacy-20.20.0.pom} +1 -1
  32. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0.pom.md5 +1 -0
  33. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0.pom.sha1 +1 -0
  34. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0.pom.sha256 +1 -0
  35. package/Detox-android/com/wix/detox-legacy/20.20.0/detox-legacy-20.20.0.pom.sha512 +1 -0
  36. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml +4 -4
  37. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.md5 +1 -1
  38. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha1 +1 -1
  39. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha256 +1 -1
  40. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha512 +1 -1
  41. package/Detox-ios-src.tbz +0 -0
  42. package/Detox-ios.tbz +0 -0
  43. package/android/detox/src/full/java/com/wix/detox/espresso/DetoxAction.java +47 -21
  44. package/android/detox/src/full/java/com/wix/detox/espresso/action/GetAttributesAction.kt +12 -0
  45. package/android/detox/src/main/java/com/wix/detox/espresso/action/DetoxSwipeWithLongPress.kt +44 -0
  46. package/android/detox/src/main/java/com/wix/detox/espresso/action/LongPressAndDragAction.kt +89 -0
  47. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/utils/ViewInteractionExt.kt +32 -0
  48. package/android/detox/src/main/java/com/wix/detox/espresso/scroll/DetoxSwiper.kt +66 -4
  49. package/android/detox/src/main/java/com/wix/detox/espresso/scroll/FlinglessSwiper.kt +6 -54
  50. package/android/detox/src/main/java/com/wix/detox/espresso/scroll/LinearSwiper.kt +26 -0
  51. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/GetAttributesActionTest.kt +11 -0
  52. package/detox.d.ts +10 -2
  53. package/package.json +2 -2
  54. package/src/android/actions/native.js +27 -0
  55. package/src/android/core/NativeElement.js +22 -0
  56. package/src/android/espressoapi/DetoxAction.js +42 -0
  57. package/src/utils/rn-consts/rn-consts.js +12 -4
  58. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5-sources.jar.md5 +0 -1
  59. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5-sources.jar.sha1 +0 -1
  60. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5-sources.jar.sha256 +0 -1
  61. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5-sources.jar.sha512 +0 -1
  62. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5.aar +0 -0
  63. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5.aar.md5 +0 -1
  64. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5.aar.sha1 +0 -1
  65. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5.aar.sha256 +0 -1
  66. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5.aar.sha512 +0 -1
  67. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5.pom.md5 +0 -1
  68. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5.pom.sha1 +0 -1
  69. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5.pom.sha256 +0 -1
  70. package/Detox-android/com/wix/detox/20.19.5/detox-20.19.5.pom.sha512 +0 -1
  71. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5-sources.jar.md5 +0 -1
  72. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5-sources.jar.sha1 +0 -1
  73. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5-sources.jar.sha256 +0 -1
  74. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5-sources.jar.sha512 +0 -1
  75. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5.aar +0 -0
  76. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5.aar.md5 +0 -1
  77. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5.aar.sha1 +0 -1
  78. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5.aar.sha256 +0 -1
  79. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5.aar.sha512 +0 -1
  80. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5.pom.md5 +0 -1
  81. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5.pom.sha1 +0 -1
  82. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5.pom.sha256 +0 -1
  83. package/Detox-android/com/wix/detox-legacy/20.19.5/detox-legacy-20.19.5.pom.sha512 +0 -1
@@ -0,0 +1 @@
1
+ 06c04df2d24757bd08bfc0fb5fc8d83e
@@ -0,0 +1 @@
1
+ 4c33f3a70d19672e881481cf7b96a33455ce6617
@@ -0,0 +1 @@
1
+ f155c86b66a87ba0ac92dd2d563b9a521283527b267b1b15ab0c299a830fe1dd
@@ -0,0 +1 @@
1
+ 2a468f22ad46429526c351cd93a78b11ffca19976b4c125de6e0bcb567e8a3187cf44d1937fc0a3ad6c44373a17050e390c9be633dcd637adfbb2506b32d9524
@@ -0,0 +1 @@
1
+ 96e530b8d58b0393d98c0e2247e1a60f
@@ -0,0 +1 @@
1
+ 2cb5d2fda3e4957c55e3b00e993a430ac3d213e9
@@ -0,0 +1 @@
1
+ 4d96c2710e83ed6a596c3c97898d70d2b685c8a7e97ba8aa510feed72d7b5b1f
@@ -0,0 +1 @@
1
+ 62e8a9348d35ac705353da85b95bb1ae85854b4e60646c78524e03258cc173540a08d1372f2b5a8a105b180373578596d0da5ef42199d218b7bdfef844f00269
@@ -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.19.5</version>
6
+ <version>20.20.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
+ 3d130211f53462c2d621c933568bdc94
@@ -0,0 +1 @@
1
+ 07ced3a0e52a803220fd2553f6907c09e162df46
@@ -0,0 +1 @@
1
+ b49710a0ec44f42ab8ba63fa81361ae91b4cd71d3e1c46965cc3c6af4ec90d1e
@@ -0,0 +1 @@
1
+ a8d8afe9f4df9f4ab07c02ae9dbcae81e86e21ea378e41a4df6343de08d9eea626e2c80206de8f981b158ca16d709b665bd8d4c181b6a89cf0b6df8c016f9dde
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>20.19.5</latest>
7
- <release>20.19.5</release>
6
+ <latest>20.20.0</latest>
7
+ <release>20.20.0</release>
8
8
  <versions>
9
- <version>20.19.5</version>
9
+ <version>20.20.0</version>
10
10
  </versions>
11
- <lastUpdated>20240331063337</lastUpdated>
11
+ <lastUpdated>20240402121112</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 8005f31eb8f340e2f9f41011bc748032
1
+ b69c7a4f9b9179cc48026542d7d7ca21
@@ -1 +1 @@
1
- d8e1b9de2f86083e380e860b6c279456c3115923
1
+ 42ab3ad77518bab1b07ecbb5100b9d59b50ff6ed
@@ -1 +1 @@
1
- 2f2903c4708c57eaf86efc12be7e381e0781a7109a231c951e8d9108f1f8c33f
1
+ c71a6efdc97efcb4fc1feb04c35e13062273be1f02367d2388dabbd6addbb94c
@@ -1 +1 @@
1
- 1dba2cd72ebc4e09b773cec88093b69afe22d36939d435b0845a43c541b18efe54a8920daabd4ff6cac6758ee50a5f1c60d42482ba5b6aa45ddce78ad8e123f0
1
+ 5c498fd316bd028482c25d65c899e24b63a1363ae9591df2fb51de82da4b30e5609527c6792fa4cf1a2533a539cac068fb04ed72fdbbc2f5eeafdf2fcebd5aaa
@@ -0,0 +1 @@
1
+ 24c25326aec65db700f9112956c3ee87
@@ -0,0 +1 @@
1
+ 82f39d382db703e29d470b276e048777dd66144f
@@ -0,0 +1 @@
1
+ 69c5b169569a5f7b54257e49ee3314aef1ee50c698783776e1d61eb825f237fe
@@ -0,0 +1 @@
1
+ 379ca59ec57000fa2962779117e1e5531168dd84cc22fc684c204697d0ac22d9cd8c27c764996b5a4bf7dfef9c494872484e8daa9bf570629675532f7c65d98d
@@ -0,0 +1 @@
1
+ 46e8e8822d7864d2c5e20be3c3bba027
@@ -0,0 +1 @@
1
+ d7a82a0126cb27386c4bb29607214424615b5ee3
@@ -0,0 +1 @@
1
+ 4438c339d4c6a810e76d007a755b1b4bbdacbaa92eab840ec5090f8207d4fc41
@@ -0,0 +1 @@
1
+ 6d960c8e53a8df4c5c4139abbd2ce44f7fab6855ebf44908e82e8eb8f90bcbe3392b6fbc421e1782811b3227613f4f5923e5ed29341d08fc77605b1f80b31d15
@@ -3,7 +3,7 @@
3
3
  <modelVersion>4.0.0</modelVersion>
4
4
  <groupId>com.wix</groupId>
5
5
  <artifactId>detox-legacy</artifactId>
6
- <version>20.19.5</version>
6
+ <version>20.20.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
+ a55835cbc1f84fec7901d1f9ab16271e
@@ -0,0 +1 @@
1
+ 313d55b91dafea72d29ffc5c62e64d767b1aa840
@@ -0,0 +1 @@
1
+ e2e5a32d53c8de1088462f1a77e0b0f95d12e6a28a4d8f2621334427b2f1fa81
@@ -0,0 +1 @@
1
+ 4188771a4703ce3adcefa0a5b07faee21e76a4ebded28d056b1f2691681aaacf8acc7863552a8cf7d8243d04a94f99e36e1c34b21cdb3117fd4941a6b41e36d5
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox-legacy</artifactId>
5
5
  <versioning>
6
- <latest>20.19.5</latest>
7
- <release>20.19.5</release>
6
+ <latest>20.20.0</latest>
7
+ <release>20.20.0</release>
8
8
  <versions>
9
- <version>20.19.5</version>
9
+ <version>20.20.0</version>
10
10
  </versions>
11
- <lastUpdated>20240331063405</lastUpdated>
11
+ <lastUpdated>20240402121223</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- aad1f021e2ebf55e087746f12606d813
1
+ 5b288a143f63e3103a7b388e7204ee29
@@ -1 +1 @@
1
- fd6fdf7b93e5f0c3dbe2b6bb933d3ea55f75afb6
1
+ 927f092fe8186e38b20b049af1f013c6a986e6d0
@@ -1 +1 @@
1
- 97531b4bf2d65744e8b16c1ee85d8f54fb86189602ba38c38809bee92e9638f8
1
+ 348dc12d41676e31072d4e02a855ec781956b08c51e8a9409bdaf00728cda685
@@ -1 +1 @@
1
- c16b930dae97e25465562c6cead2190f258873ea71c47186f6acf99552f2d6e971418929370a9447ee025d03e580cbfdf65cc86a85bd7dde57c208d238b875de
1
+ 150ea0094b15ae726bd08d78de639efa60177284d26397fd42127bcdf93ca78e7d390d025d95ce1f2ae387dce55a706cb2ef843ebc7701f74625d03d32bb6b70
package/Detox-ios-src.tbz CHANGED
Binary file
package/Detox-ios.tbz CHANGED
Binary file
@@ -1,19 +1,34 @@
1
1
  package com.wix.detox.espresso;
2
2
 
3
+ import static androidx.test.espresso.action.ViewActions.actionWithAssertions;
4
+ import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
5
+ import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
6
+ import static org.hamcrest.Matchers.allOf;
7
+
3
8
  import android.view.View;
4
- import android.os.Build;
5
9
 
10
+ import androidx.test.espresso.UiController;
11
+ import androidx.test.espresso.ViewAction;
12
+ import androidx.test.espresso.ViewInteraction;
13
+ import androidx.test.espresso.action.CoordinatesProvider;
14
+ import androidx.test.espresso.action.GeneralClickAction;
15
+ import androidx.test.espresso.action.GeneralLocation;
16
+ import androidx.test.espresso.action.Press;
17
+ import androidx.test.espresso.contrib.PickerActions;
18
+
19
+ import com.wix.detox.action.common.MotionDir;
6
20
  import com.wix.detox.common.DetoxErrors.DetoxRuntimeException;
7
21
  import com.wix.detox.common.DetoxErrors.StaleActionException;
8
- import com.wix.detox.espresso.action.RNDetoxAccessibilityAction;
9
22
  import com.wix.detox.espresso.action.AdjustSliderToPositionAction;
10
23
  import com.wix.detox.espresso.action.DetoxMultiTap;
24
+ import com.wix.detox.espresso.action.GetAttributesAction;
25
+ import com.wix.detox.espresso.action.LongPressAndDragAction;
11
26
  import com.wix.detox.espresso.action.RNClickAction;
27
+ import com.wix.detox.espresso.action.RNDetoxAccessibilityAction;
12
28
  import com.wix.detox.espresso.action.ScreenshotResult;
13
29
  import com.wix.detox.espresso.action.ScrollToIndexAction;
14
30
  import com.wix.detox.espresso.action.TakeViewScreenshotAction;
15
- import com.wix.detox.espresso.action.GetAttributesAction;
16
- import com.wix.detox.action.common.MotionDir;
31
+ import com.wix.detox.espresso.action.common.utils.ViewInteractionExt;
17
32
  import com.wix.detox.espresso.scroll.DetoxScrollAction;
18
33
  import com.wix.detox.espresso.scroll.DetoxScrollActionStaleAtEdge;
19
34
  import com.wix.detox.espresso.scroll.ScrollEdgeException;
@@ -21,26 +36,12 @@ import com.wix.detox.espresso.scroll.ScrollHelper;
21
36
  import com.wix.detox.espresso.scroll.SwipeHelper;
22
37
 
23
38
  import org.hamcrest.Matcher;
39
+
24
40
  import java.text.ParseException;
25
41
  import java.text.SimpleDateFormat;
26
- import java.time.ZonedDateTime;
27
42
  import java.util.Calendar;
28
43
  import java.util.Date;
29
44
 
30
- import androidx.test.espresso.UiController;
31
- import androidx.test.espresso.ViewAction;
32
- import androidx.test.espresso.action.CoordinatesProvider;
33
- import androidx.test.espresso.action.GeneralClickAction;
34
- import androidx.test.espresso.action.GeneralLocation;
35
- import androidx.test.espresso.action.Press;
36
- import androidx.test.espresso.contrib.PickerActions;
37
-
38
- import static androidx.test.espresso.action.ViewActions.actionWithAssertions;
39
- import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
40
- import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
41
-
42
- import static org.hamcrest.Matchers.allOf;
43
-
44
45
 
45
46
  /**
46
47
  * Created by simonracz on 10/07/2017.
@@ -69,7 +70,7 @@ public class DetoxAction {
69
70
  view.getLocationOnScreen(xy);
70
71
  final float fx = xy[0] + px;
71
72
  final float fy = xy[1] + py;
72
- return new float[] {fx, fy};
73
+ return new float[]{fx, fy};
73
74
  }
74
75
  };
75
76
  return actionWithAssertions(new RNClickAction(c));
@@ -78,7 +79,7 @@ public class DetoxAction {
78
79
  /**
79
80
  * Scrolls to the edge of the given scrollable view.
80
81
  *
81
- * @param edge Direction to scroll (see {@link MotionDir})
82
+ * @param edge Direction to scroll (see {@link MotionDir})
82
83
  * @param startOffsetPercentX Percentage denoting where the scroll should start from on the X-axis, with respect to the scrollable view.
83
84
  * @param startOffsetPercentY Percentage denoting where the scroll should start from on the Y-axis, with respect to the scrollable view.
84
85
  * @return ViewAction
@@ -182,6 +183,31 @@ public class DetoxAction {
182
183
  return new AdjustSliderToPositionAction(newPosition);
183
184
  }
184
185
 
186
+ public static ViewAction longPressAndDrag(Integer duration,
187
+ Double normalizedPositionX,
188
+ Double normalizedPositionY,
189
+ ViewInteraction targetElement,
190
+ Double normalizedTargetPositionX,
191
+ Double normalizedTargetPositionY,
192
+ boolean isFast,
193
+ Integer holdDuration) {
194
+
195
+ // We receive a ViewInteraction which represents an interactions of the target view. We need to extract the view
196
+ // from it in order to get the coordinates of the target view.
197
+ View targetView = ViewInteractionExt.getView(targetElement);
198
+
199
+ return actionWithAssertions(new LongPressAndDragAction(
200
+ duration,
201
+ normalizedPositionX,
202
+ normalizedPositionY,
203
+ targetView,
204
+ normalizedTargetPositionX,
205
+ normalizedTargetPositionY,
206
+ isFast,
207
+ holdDuration
208
+ ));
209
+ }
210
+
185
211
  public static ViewAction takeViewScreenshot() {
186
212
  return new ViewActionWithResult<String>() {
187
213
  private final TakeViewScreenshotAction action = new TakeViewScreenshotAction();
@@ -53,6 +53,7 @@ private class CommonAttributes : AttributeExtractor {
53
53
  getAccessibilityLabel(json, view)
54
54
  getAlpha(json, view)
55
55
  getElevation(json, view)
56
+ getFrame(json, view)
56
57
  getHeight(json, view)
57
58
  getWidth(json, view)
58
59
  getHasFocus(json, view)
@@ -64,6 +65,17 @@ private class CommonAttributes : AttributeExtractor {
64
65
  json.put("identifier", it.toString())
65
66
  }
66
67
 
68
+ private fun getFrame(json: JSONObject, view: View) {
69
+ val location = IntArray(2)
70
+ view.getLocationOnScreen(location)
71
+ json.put("frame", JSONObject().apply {
72
+ put("x", location[0])
73
+ put("y", location[1])
74
+ put("width", view.width)
75
+ put("height", view.height)
76
+ })
77
+ }
78
+
67
79
  private fun getVisibility(json: JSONObject, view: View) {
68
80
  json.put("visibility", visibilityMap[view.visibility])
69
81
  json.put("visible", view.getLocalVisibleRect(Rect()))
@@ -0,0 +1,44 @@
1
+ package com.wix.detox.espresso.action
2
+
3
+ import com.wix.detox.espresso.scroll.DetoxSwiper
4
+
5
+ /**
6
+ * Implementation of the Detox swipe action with long press in the beginning of the swipe and at the end
7
+ */
8
+ class DetoxSwipeWithLongPress(
9
+ private val durationStart: Int,
10
+ private val durationEnd: Int,
11
+ private val startX: Float,
12
+ private val startY: Float,
13
+ private val endX: Float,
14
+ private val endY: Float,
15
+ private val motionCount: Int,
16
+ private val swiper: DetoxSwiper
17
+ ) {
18
+
19
+ fun perform() {
20
+ with(swiper) {
21
+ startAt(startX, startY)
22
+ wait(durationStart)
23
+ try {
24
+ val stepSizeX = (endX - startX) / (motionCount + 2f)
25
+ val stepSizeY = (endY - startY) / (motionCount + 2f)
26
+
27
+ var targetX = startX
28
+ var targetY = startY
29
+ for (step in 1..motionCount) {
30
+ targetX += stepSizeX
31
+ targetY += stepSizeY
32
+
33
+ if (!moveTo(targetX, targetY)) {
34
+ return
35
+ }
36
+ }
37
+ } finally {
38
+ moveTo(endX, endY)
39
+ wait(durationEnd)
40
+ finishAt(endX, endY)
41
+ }
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,89 @@
1
+ package com.wix.detox.espresso.action
2
+
3
+ import android.graphics.Point
4
+ import android.util.Log
5
+ import android.view.View
6
+ import androidx.test.espresso.UiController
7
+ import androidx.test.espresso.ViewAction
8
+ import androidx.test.espresso.matcher.ViewMatchers
9
+ import com.wix.detox.espresso.scroll.LinearSwiper
10
+ import org.hamcrest.Matcher
11
+ import kotlin.math.ceil
12
+
13
+
14
+ private const val SLOW_SCROLL_MOTIONS = 50
15
+ private const val FAST_SCROLL_MOTIONS = 20
16
+
17
+ class LongPressAndDragAction(
18
+ private val duration: Int,
19
+ private val normalizedPositionX: Double,
20
+ private val normalizedPositionY: Double,
21
+ private val targetView: View,
22
+ private val normalizedTargetPositionX: Double,
23
+ private val normalizedTargetPositionY: Double,
24
+ isFast: Boolean,
25
+ private val holdDuration: Int
26
+ ) : ViewAction {
27
+
28
+ private val scrollMotions = if (isFast) FAST_SCROLL_MOTIONS else SLOW_SCROLL_MOTIONS
29
+
30
+ override fun getDescription(): String {
31
+ return "longPressAndDrag"
32
+ }
33
+
34
+ override fun getConstraints(): Matcher<View> {
35
+ return ViewMatchers.isAssignableFrom(
36
+ View::class.java
37
+ )
38
+ }
39
+
40
+ override fun perform(uiController: UiController, view: View) {
41
+ performSwipe(uiController, view, targetView)
42
+ }
43
+
44
+ private fun performSwipe(uiController: UiController, sourceView: View, targetView: View) {
45
+ val xy = IntArray(2)
46
+
47
+ // Get start coordinates
48
+ sourceView.getLocationOnScreen(xy)
49
+ val sourceViewPoint = Point(xy[0], xy[1])
50
+ val startPoint = Point(
51
+ ceil(sourceViewPoint.x + sourceView.width * normalizedPositionX).toInt(),
52
+ ceil(sourceViewPoint.y + sourceView.height * normalizedPositionY).toInt()
53
+ )
54
+
55
+ // Get end coordinates
56
+ targetView.getLocationOnScreen(xy)
57
+ val targetViewPoint = Point(xy[0], xy[1])
58
+ val endPoint = Point(
59
+ ceil(targetViewPoint.x + targetView.width * normalizedTargetPositionX).toInt(),
60
+ ceil(targetViewPoint.y + targetView.height * normalizedTargetPositionY).toInt()
61
+ )
62
+
63
+ Log.d(
64
+ "LongPressAndDragAction",
65
+ "start:$startPoint, end:$endPoint duration: $duration, holdDuration: $holdDuration, scrollMotions: $scrollMotions, source:[$sourceViewPoint,${sourceView.width}x${sourceView.height}], target:[$targetViewPoint,${targetView.width}x${targetView.height}]"
66
+ )
67
+
68
+ val swiper = LinearSwiper(uiController)
69
+ val swipe = DetoxSwipeWithLongPress(
70
+ duration,
71
+ holdDuration,
72
+ startPoint.x.toFloat(),
73
+ startPoint.y.toFloat(),
74
+ endPoint.x.toFloat(),
75
+ endPoint.y.toFloat(),
76
+ scrollMotions,
77
+ swiper
78
+ )
79
+ swipe.perform()
80
+
81
+ sourceView.getLocationOnScreen(xy)
82
+
83
+ // Please note that the actual coordinates are not the same as the end coordinates.
84
+ Log.d(
85
+ "LongPressAndDragAction",
86
+ "Performed swipe. Actual coordinates x=${xy[0]}, y=${xy[1]}. Normalized position x=${xy[0] + sourceView.width * normalizedTargetPositionX}, y=${xy[1] + sourceView.height * normalizedTargetPositionX}"
87
+ )
88
+ }
89
+ }
@@ -0,0 +1,32 @@
1
+ @file:JvmName("ViewInteractionExt")
2
+ package com.wix.detox.espresso.action.common.utils
3
+
4
+ import android.view.View
5
+ import androidx.test.espresso.ViewAction
6
+ import androidx.test.espresso.ViewInteraction
7
+ import org.hamcrest.Matcher
8
+ import org.hamcrest.Matchers
9
+
10
+
11
+ fun ViewInteraction.getView(): View {
12
+ var result: View? = null
13
+
14
+ val viewAction = object : ViewAction {
15
+ override fun getDescription(): String {
16
+ return "Get View"
17
+ }
18
+
19
+ override fun getConstraints(): Matcher<View> {
20
+ return Matchers.any(View::class.java)
21
+ }
22
+
23
+ override fun perform(uiController: androidx.test.espresso.UiController, view: View) {
24
+ result = view
25
+ }
26
+ }
27
+
28
+ perform(viewAction)
29
+
30
+ return result ?: throw IllegalStateException("Failed to get view")
31
+ }
32
+
@@ -1,7 +1,69 @@
1
1
  package com.wix.detox.espresso.scroll
2
2
 
3
- interface DetoxSwiper {
4
- fun startAt(touchX: Float, touchY: Float)
5
- fun moveTo(targetX: Float, targetY: Float): Boolean
6
- fun finishAt(releaseX: Float, releaseY: Float)
3
+ import android.view.MotionEvent
4
+ import androidx.test.espresso.UiController
5
+ import com.wix.detox.espresso.action.common.MotionEvents
6
+
7
+ abstract class DetoxSwiper(
8
+ private val uiController: UiController,
9
+ private val motionEvents: MotionEvents
10
+ ) {
11
+
12
+ private var downEvent: MotionEvent? = null
13
+
14
+ protected val events = mutableListOf<MotionEvent>()
15
+
16
+ fun startAt(touchX: Float, touchY: Float) {
17
+ assertNotStarted()
18
+
19
+ downEvent = motionEvents.obtainDownEvent(touchX, touchY)
20
+ events.add(downEvent!!)
21
+ }
22
+
23
+ fun moveTo(targetX: Float, targetY: Float): Boolean {
24
+ assertStarted()
25
+
26
+ val moveEvent = motionEvents.obtainMoveEvent(downEvent!!, calcEventTime(targetX, targetY), targetX, targetY)
27
+ events.add(moveEvent)
28
+ return true
29
+ }
30
+
31
+ fun wait(duration: Int) {
32
+ assertStarted()
33
+
34
+ val lastEvent = events.last()
35
+ // Insert a fake move event without actually moving, just to wait for the given duration.
36
+ val waitEvent = motionEvents.obtainMoveEvent(downEvent!!, lastEvent.eventTime + duration, lastEvent.x, lastEvent.y)
37
+ events.add(waitEvent)
38
+ }
39
+
40
+ fun finishAt(releaseX: Float, releaseY: Float) {
41
+ assertStarted()
42
+
43
+ try {
44
+ val upEvent = motionEvents.obtainUpEvent(downEvent!!, calcEventTime(releaseX, releaseY), releaseX, releaseY)
45
+ events.add(upEvent)
46
+
47
+ // Flush!
48
+ uiController.injectMotionEventSequence(events)
49
+ } finally {
50
+ events.forEach { event -> event.recycle() }
51
+ downEvent = null
52
+ }
53
+ }
54
+
55
+
56
+ private fun assertStarted() {
57
+ if (downEvent == null) {
58
+ throw IllegalStateException("Swiper not initialized - did you forget to call startAt()?")
59
+ }
60
+ }
61
+
62
+ private fun assertNotStarted() {
63
+ if (downEvent != null) {
64
+ throw IllegalStateException("Swiper already started")
65
+ }
66
+ }
67
+
68
+ protected abstract fun calcEventTime(targetX: Float, targetY: Float): Long
7
69
  }
@@ -1,6 +1,5 @@
1
1
  package com.wix.detox.espresso.scroll
2
2
 
3
- import android.view.MotionEvent
4
3
  import android.view.ViewConfiguration
5
4
  import androidx.test.espresso.UiController
6
5
  import com.wix.detox.espresso.action.common.MotionEvents
@@ -15,56 +14,20 @@ import com.wix.detox.espresso.action.common.MotionEvents
15
14
  */
16
15
  class FlinglessSwiper @JvmOverloads constructor(
17
16
  expectedMotions: Int,
18
- private val uiController: UiController,
17
+ uiController: UiController,
19
18
  viewConfig: ViewConfiguration,
20
- private val motionEvents: MotionEvents = MotionEvents())
21
- : DetoxSwiper {
19
+ motionEvents: MotionEvents = MotionEvents())
20
+ : DetoxSwiper(uiController, motionEvents) {
22
21
 
23
22
  private val pixelsPerSecond = viewConfig.scaledMinimumFlingVelocity * VELOCITY_SAFETY_RATIO
24
23
  private val fastEventsCountLimit = expectedMotions * FAST_EVENTS_RATIO
25
24
 
26
- private var downEvent: MotionEvent? = null
27
-
28
- private var events = mutableListOf<MotionEvent>()
29
- private var motionsCount = 0
30
-
31
- override fun startAt(touchX: Float, touchY: Float) {
32
- assertNotStarted()
33
-
34
- downEvent = motionEvents.obtainDownEvent(touchX, touchY)
35
- events.add(downEvent!!)
36
- }
37
-
38
- override fun moveTo(targetX: Float, targetY: Float): Boolean {
39
- assertStarted()
40
-
41
- val moveEvent = motionEvents.obtainMoveEvent(downEvent!!, calcEventTime(targetX, targetY), targetX, targetY)
42
- events.add(moveEvent)
43
-
44
- motionsCount++
45
- return true
46
- }
47
-
48
- override fun finishAt(releaseX: Float, releaseY: Float) {
49
- assertStarted()
50
-
51
- try {
52
- val upEvent = motionEvents.obtainUpEvent(downEvent!!, calcEventTime(releaseX, releaseY), releaseX, releaseY)
53
- events.add(upEvent)
54
-
55
- // Flush!
56
- uiController.injectMotionEventSequence(events)
57
- } finally {
58
- events.forEach { event -> event.recycle() }
59
- downEvent = null
60
- motionsCount = 0
61
- }
62
- }
63
-
64
- private fun calcEventTime(targetX: Float, targetY: Float): Long {
25
+ override fun calcEventTime(targetX: Float, targetY: Float): Long {
65
26
  val lastEvent = events.last()
66
27
  var dt = 10
67
28
 
29
+ val motionsCount = events.size
30
+
68
31
  if (motionsCount >= fastEventsCountLimit) {
69
32
  val dx = Math.abs((targetX - lastEvent.x))
70
33
  val dy = Math.abs((targetY - lastEvent.y))
@@ -78,17 +41,6 @@ class FlinglessSwiper @JvmOverloads constructor(
78
41
  return lastEvent.eventTime + Math.max(dt, 10)
79
42
  }
80
43
 
81
- private fun assertStarted() {
82
- if (downEvent == null) {
83
- throw IllegalStateException("Swiper not initialized - did you forget to call startAt()?")
84
- }
85
- }
86
-
87
- private fun assertNotStarted() {
88
- if (downEvent != null) {
89
- throw IllegalStateException("Swiper already started")
90
- }
91
- }
92
44
 
93
45
  companion object {
94
46
  // private const val LOG_TAG = "DetoxBatchedSwiper"
@@ -0,0 +1,26 @@
1
+ package com.wix.detox.espresso.scroll
2
+
3
+ import androidx.test.espresso.UiController
4
+ import com.wix.detox.espresso.action.common.MotionEvents
5
+
6
+ /**
7
+ * The delay between each motion event.
8
+ * Reducing this value may fail the swipe on different devices. Please change with caution.
9
+ */
10
+ private const val EVENT_DELAY = 25L
11
+
12
+ /**
13
+ * Implementation of @see DetoxSwiper that swipes in a linear fashion uses const delay between events.
14
+ */
15
+ class LinearSwiper @JvmOverloads constructor(
16
+ uiController: UiController,
17
+ motionEvents: MotionEvents = MotionEvents()
18
+ ) : DetoxSwiper(uiController, motionEvents) {
19
+
20
+ override fun calcEventTime(targetX: Float, targetY: Float): Long {
21
+ val lastEvent = events.last()
22
+
23
+ return lastEvent.eventTime + EVENT_DELAY
24
+ }
25
+
26
+ }
@@ -12,6 +12,7 @@ import org.junit.Before
12
12
  import org.junit.Test
13
13
  import org.junit.runner.RunWith
14
14
  import org.mockito.kotlin.any
15
+ import org.mockito.kotlin.doAnswer
15
16
  import org.mockito.kotlin.doReturn
16
17
  import org.mockito.kotlin.mock
17
18
  import org.mockito.kotlin.whenever
@@ -133,11 +134,21 @@ class GetAttributesActionTest {
133
134
  on { elevation } doReturn 0.314f
134
135
  }
135
136
 
137
+ doAnswer { invocation ->
138
+ val location = invocation.getArgument<IntArray>(0)
139
+ location[0] = 10
140
+ location[1] = 20
141
+ }.whenever(view).getLocationOnScreen(any())
142
+
136
143
  val resultJson = perform()
137
144
  assertThat(resultJson.opt("alpha")).isEqualTo(0.42f)
138
145
  assertThat(resultJson.opt("width")).isEqualTo(123)
139
146
  assertThat(resultJson.opt("height")).isEqualTo(456)
140
147
  assertThat(resultJson.opt("elevation")).isEqualTo(0.314f)
148
+ assertThat(resultJson.optJSONObject("frame")?.opt("x")).isEqualTo(10)
149
+ assertThat(resultJson.optJSONObject("frame")?.opt("y")).isEqualTo(20)
150
+ assertThat(resultJson.optJSONObject("frame")?.opt("width")).isEqualTo(123)
151
+ assertThat(resultJson.optJSONObject("frame")?.opt("height")).isEqualTo(456)
141
152
  }
142
153
 
143
154
  @Test
package/detox.d.ts CHANGED
@@ -1740,9 +1740,11 @@ declare global {
1740
1740
  value?: unknown;
1741
1741
  }
1742
1742
 
1743
- interface IosElementAttributeFrame {
1744
- y: number;
1743
+ interface IosElementAttributeFrame extends ElementAttributeFrame { }
1744
+
1745
+ interface ElementAttributeFrame {
1745
1746
  x: number;
1747
+ y: number;
1746
1748
  width: number;
1747
1749
  height: number;
1748
1750
  }
@@ -1821,10 +1823,12 @@ declare global {
1821
1823
  */
1822
1824
  visibility: 'visible' | 'invisible' | 'gone';
1823
1825
  /**
1826
+ * @deprecated
1824
1827
  * Width of the element, in pixels.
1825
1828
  */
1826
1829
  width: number;
1827
1830
  /**
1831
+ * @deprecated
1828
1832
  * Height of the element, in pixels.
1829
1833
  */
1830
1834
  height: number;
@@ -1840,6 +1844,10 @@ declare global {
1840
1844
  * Whether the element is the one currently in focus.
1841
1845
  */
1842
1846
  focused: boolean;
1847
+ /**
1848
+ * The frame of the element, in screen coordinate space.
1849
+ */
1850
+ frame: ElementAttributeFrame;
1843
1851
  /**
1844
1852
  * The text size for the text element.
1845
1853
  */
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.19.5",
4
+ "version": "20.20.0",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -115,5 +115,5 @@
115
115
  "browserslist": [
116
116
  "node 14"
117
117
  ],
118
- "gitHead": "60a56b0394b4a3a2aa99ce0a82b03b19b6116e54"
118
+ "gitHead": "b5821206730e4541260ab98eecaf77a77c569e59"
119
119
  }
@@ -45,6 +45,32 @@ class PressKeyAction extends Action {
45
45
  }
46
46
  }
47
47
 
48
+ class LongPressAndDragAction extends Action {
49
+ constructor(duration, normalizedPositionX, normalizedPositionY, targetElement, normalizedTargetPositionX, normalizedTargetPositionY, speed, holdDuration) {
50
+ super();
51
+
52
+ assertNormalized({ normalizedPositionX });
53
+ assertNormalized({ normalizedPositionY });
54
+ assertNormalized({ normalizedTargetPositionX });
55
+ assertNormalized({ normalizedTargetPositionY });
56
+ assertSpeed({ speed });
57
+
58
+ this._call = invoke.callDirectly(
59
+ DetoxActionApi.longPressAndDrag(
60
+ duration,
61
+ normalizedPositionX,
62
+ normalizedPositionY,
63
+ targetElement._call(),
64
+ normalizedTargetPositionX,
65
+ normalizedTargetPositionY,
66
+ speed === 'fast',
67
+ holdDuration
68
+ )
69
+ );
70
+ }
71
+
72
+ }
73
+
48
74
  class TypeTextAction extends Action {
49
75
  constructor(value) {
50
76
  super();
@@ -157,6 +183,7 @@ module.exports = {
157
183
  TapAction,
158
184
  TapAtPointAction,
159
185
  LongPressAction,
186
+ LongPressAndDragAction,
160
187
  MultiClickAction,
161
188
  PressKeyAction,
162
189
  TypeTextAction,
@@ -48,6 +48,28 @@ class NativeElement {
48
48
  return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
49
49
  }
50
50
 
51
+ async longPressAndDrag(duration, normalizedPositionX, normalizedPositionY, targetElement, normalizedTargetPositionX, normalizedTargetPositionY, speed, holdDuration) {
52
+ const action = new actions.LongPressAndDragAction(
53
+ duration,
54
+ normalizedPositionX,
55
+ normalizedPositionY,
56
+ targetElement,
57
+ normalizedTargetPositionX,
58
+ normalizedTargetPositionY,
59
+ speed,
60
+ holdDuration);
61
+ const traceDescription = actionDescription.longPressAndDrag(
62
+ duration,
63
+ normalizedPositionX,
64
+ normalizedPositionY,
65
+ targetElement,
66
+ normalizedTargetPositionX,
67
+ normalizedTargetPositionY,
68
+ speed,
69
+ holdDuration);
70
+ return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
71
+ }
72
+
51
73
  async multiTap(times) {
52
74
  if (typeof times !== 'number') throw new Error('times should be a number, but got ' + (times + (' (' + (typeof times + ')'))));
53
75
  if (times < 1) throw new Error('times should be greater than 0, but got ' + times);
@@ -229,6 +229,48 @@ class DetoxAction {
229
229
  };
230
230
  }
231
231
 
232
+ static longPressAndDrag(duration, normalizedPositionX, normalizedPositionY, targetElement, normalizedTargetPositionX, normalizedTargetPositionY, isFast, holdDuration) {
233
+ if (typeof duration !== "number") throw new Error("duration should be a number, but got " + (duration + (" (" + (typeof duration + ")"))));
234
+ if (typeof normalizedPositionX !== "number") throw new Error("normalizedPositionX should be a number, but got " + (normalizedPositionX + (" (" + (typeof normalizedPositionX + ")"))));
235
+ if (typeof normalizedPositionY !== "number") throw new Error("normalizedPositionY should be a number, but got " + (normalizedPositionY + (" (" + (typeof normalizedPositionY + ")"))));
236
+ if (typeof normalizedTargetPositionX !== "number") throw new Error("normalizedTargetPositionX should be a number, but got " + (normalizedTargetPositionX + (" (" + (typeof normalizedTargetPositionX + ")"))));
237
+ if (typeof normalizedTargetPositionY !== "number") throw new Error("normalizedTargetPositionY should be a number, but got " + (normalizedTargetPositionY + (" (" + (typeof normalizedTargetPositionY + ")"))));
238
+ if (typeof isFast !== "boolean") throw new Error("isFast should be a boolean, but got " + (isFast + (" (" + (typeof isFast + ")"))));
239
+ if (typeof holdDuration !== "number") throw new Error("holdDuration should be a number, but got " + (holdDuration + (" (" + (typeof holdDuration + ")"))));
240
+ return {
241
+ target: {
242
+ type: "Class",
243
+ value: "com.wix.detox.espresso.DetoxAction"
244
+ },
245
+ method: "longPressAndDrag",
246
+ args: [{
247
+ type: "Integer",
248
+ value: duration
249
+ }, {
250
+ type: "Double",
251
+ value: normalizedPositionX
252
+ }, {
253
+ type: "Double",
254
+ value: normalizedPositionY
255
+ }, {
256
+ type: "Invocation",
257
+ value: targetElement
258
+ }, {
259
+ type: "Double",
260
+ value: normalizedTargetPositionX
261
+ }, {
262
+ type: "Double",
263
+ value: normalizedTargetPositionY
264
+ }, {
265
+ type: "boolean",
266
+ value: isFast
267
+ }, {
268
+ type: "Integer",
269
+ value: holdDuration
270
+ }]
271
+ };
272
+ }
273
+
232
274
  static takeViewScreenshot() {
233
275
  return {
234
276
  target: {
@@ -1,15 +1,23 @@
1
+ const DEFAULT_RN_VERSION = '99.9999.9999';
2
+
1
3
  const rnVersion = (function parseRNVersion() {
2
- const packageJson = require('react-native/package.json');
3
- const raw = packageJson.version;
4
+ let raw;
5
+ try {
6
+ const packageJson = require('react-native/package.json');
7
+ raw = packageJson.version;
8
+ } catch {
9
+ // Default version for RN
10
+ raw = DEFAULT_RN_VERSION;
11
+ }
4
12
  const [major, minor, patch] = raw.split('.');
5
13
  return {
6
14
  major,
7
15
  minor,
8
16
  patch,
9
- raw,
17
+ raw
10
18
  };
11
19
  })();
12
20
 
13
21
  module.exports = {
14
- rnVersion,
22
+ rnVersion
15
23
  };
@@ -1 +0,0 @@
1
- ef5f4d624c42d1cfc1c35eab0b6fc669
@@ -1 +0,0 @@
1
- 828c0678db282e6fcaa57531a9df8201bc372cbb
@@ -1 +0,0 @@
1
- 1e0dd6e73697f2ac094b28fc9bdecf52c1d20e3004ec4876fa4e228e53a71d60
@@ -1 +0,0 @@
1
- 1a93e1c2319c44078780d66a26d1ebc78ef988a8a0f69cbd81e4cc79149e0b300f0c33f474190678cddad86d54e79416f4c3944ffb5f0b6164aa9e29773e0998
@@ -1 +0,0 @@
1
- 221afe495c4ff28f344cff35e41e566a
@@ -1 +0,0 @@
1
- 9657a291e6e1725adb2a3e333248ad4a05892015
@@ -1 +0,0 @@
1
- 23a9f6a86b0fb26b426f6188be04dc98eed5edeaa7f77c6e682bae9b80906312
@@ -1 +0,0 @@
1
- a43c0a36b2b519a777662f087ba88e2bf781613ec86633434499ba70483fe072ebbbff6507f3ec3cb1cff7bfd5326af172d6fe7dcdc7990790796fe97d9c1519
@@ -1 +0,0 @@
1
- 8446c7127a92b93db77c0f1088d7e876
@@ -1 +0,0 @@
1
- 941870c07884db2324fb5f1b9dfa0c7dc9929a06
@@ -1 +0,0 @@
1
- 2228df1b6a06c8c07b1008ab1da7a2eb743b186d05d5d797fd4151b55df08df8
@@ -1 +0,0 @@
1
- 2a131492a23f98f4c45dbcf3f26a97c62e842a38cd03c21717be83fae18ccbbf41c6fc6d613e7d4308939111c2c02ab00e65001e1adaec2202bcd9d899c9a883
@@ -1 +0,0 @@
1
- aab82e5a9267a9ccdf45b6c013aaa8ca
@@ -1 +0,0 @@
1
- 5a4bdd3ab91c7d509328e6c4f610c2ce9a5bbb0c
@@ -1 +0,0 @@
1
- a4bae311cb2922a8d3e5e75e855ca23f1d49846e19d203514c9c55bcad3aa332
@@ -1 +0,0 @@
1
- a26ce58824701b283041b74fe589d5648faf0acf85f675b5724af6dc8099be837000975787315abfb9b2f8bcc0c3b150c87df2be33339f7a08655f2cfef18b9b
@@ -1 +0,0 @@
1
- 2ccac07d6c4079ce6a948d8cb8e78985
@@ -1 +0,0 @@
1
- e3b8881d1f3a9ff8c71cdfa84a7cc2a69b025401
@@ -1 +0,0 @@
1
- 73266b3affb9ce645c35133ef28db777e3e321343d9b7d946610b2178b5b3863
@@ -1 +0,0 @@
1
- d9032a388871d30165507ca248712125a3cf6f5443b2a74eed35ab3a4a075d9a6c5c631e0d80395b66b465a464e4ee7f795a65acbeb3b6082177eabafe29e1b5
@@ -1 +0,0 @@
1
- 371fa89c30de7e470906ae0a9d5ddc9b
@@ -1 +0,0 @@
1
- abb73698e3227e38e2261f5cea3d3f87e4595d64
@@ -1 +0,0 @@
1
- 05a6d9e21b708659310e449ffd61918cf4bdc1c1614d8e2a53b89f78cc9b252e
@@ -1 +0,0 @@
1
- 6a1bb4db5e330d5e1c51af689b97bdff2bef36c4c1e0c6f0ce35e90b32ac5e288321a8fa8ca9d8967adb786c43ab11f62f2b60fb24796a760269b9ae5a873a43