detox 20.19.5 → 20.20.0

Sign up to get free protection for your applications and to get access to all the features.
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