detox 20.15.0-prerelease.0 → 20.15.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.eslintrc.js +0 -4
- package/Detox-android/com/wix/detox/{20.15.0-prerelease.0/detox-20.15.0-prerelease.0-javadoc.jar → 20.15.0/detox-20.15.0-javadoc.jar} +0 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0-javadoc.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0-javadoc.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0-javadoc.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0-javadoc.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.15.0-prerelease.0/detox-20.15.0-prerelease.0-sources.jar → 20.15.0/detox-20.15.0-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0.aar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.15.0-prerelease.0/detox-20.15.0-prerelease.0.pom → 20.15.0/detox-20.15.0.pom} +1 -1
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.15.0/detox-20.15.0.pom.sha512 +1 -0
- package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
- package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
- package/Detox-ios-src.tbz +0 -0
- package/Detox-ios.tbz +0 -0
- package/android/detox/proguard-rules-app.pro +1 -2
- package/android/detox/src/full/java/com/wix/detox/espresso/DetoxAction.java +11 -6
- package/android/detox/src/main/java/com/wix/detox/espresso/DeviceDisplay.kt +1 -1
- package/android/detox/src/main/java/com/wix/detox/espresso/scroll/ScrollHelper.java +113 -16
- package/android/detox/src/testFull/java/com/wix/detox/espresso/scroll/ScrollHelperTest.kt +188 -0
- package/detox.d.ts +6 -3
- package/jest.config.js +1 -1
- package/local-cli/init.js +1 -1
- package/package.json +16 -16
- package/runners/jest/testEnvironment/index.js +24 -9
- package/src/DetoxWorker.js +1 -1
- package/src/android/actions/native.js +2 -2
- package/src/android/core/NativeElement.js +3 -4
- package/src/android/espressoapi/DetoxAction.js +9 -1
- package/src/android/interactions/native.js +2 -2
- package/src/android/matchers/native.js +1 -1
- package/src/artifacts/providers/index.js +1 -1
- package/src/artifacts/screenshot/SimulatorScreenshotPlugin.js +0 -1
- package/src/artifacts/templates/artifact/Artifact.js +1 -1
- package/src/client/AsyncWebSocket.js +2 -2
- package/src/client/Client.js +2 -2
- package/src/devices/allocation/drivers/android/attached/AttachedAndroidAllocDriver.js +0 -1
- package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +0 -1
- package/src/devices/allocation/drivers/android/emulator/EmulatorVersionResolver.js +1 -1
- package/src/devices/allocation/drivers/ios/SimulatorAllocDriver.js +0 -1
- package/src/devices/allocation/factories/android.js +1 -1
- package/src/devices/allocation/factories/ios.js +1 -1
- package/src/devices/common/drivers/android/exec/ADB.js +1 -2
- package/src/devices/common/drivers/android/tools/ApkValidator.js +1 -1
- package/src/devices/common/drivers/android/tools/AppInstallHelper.js +0 -2
- package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +1 -0
- package/src/devices/runtime/drivers/android/AndroidDriver.js +1 -1
- package/src/devices/runtime/drivers/android/emulator/EmulatorDriver.js +0 -1
- package/src/devices/runtime/factories/ios.js +0 -2
- package/src/ios/expectTwo.js +6 -3
- package/src/ipc/IPCClient.js +2 -2
- package/src/ipc/IPCServer.js +4 -4
- package/src/realms/DetoxContext.js +3 -3
- package/src/realms/DetoxPrimaryContext.js +74 -29
- package/src/realms/DetoxSecondaryContext.js +2 -2
- package/src/utils/childProcess/exec.js +3 -2
- package/src/utils/invocationTraceDescriptions.js +3 -2
- package/tsconfig.json +1 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0-javadoc.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0-javadoc.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0-javadoc.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0-javadoc.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0.aar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0.pom.md5 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.15.0-prerelease.0/detox-20.15.0-prerelease.0.pom.sha512 +0 -1
package/.eslintrc.js
CHANGED
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
6af6a1128b61c579b4a5c82d2865f585
|
@@ -0,0 +1 @@
|
|
1
|
+
4dba1aa3f352698d234cccc7f69d8d3e69350458
|
@@ -0,0 +1 @@
|
|
1
|
+
0d73e6570afca6e11cefdb63125dd01bdd0f33143089402c752c506b5acf85a3
|
@@ -0,0 +1 @@
|
|
1
|
+
cda5ab07eac2a8371056647e4b6391a1c42e2d4a5b1a6a7a810a398ed4f29946441ea9da132a236cee7e523b09b6b6ea8f1510e5faaec0061193c84767e1d849
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
b912691ddb7318d42de9431f7435bc9a
|
@@ -0,0 +1 @@
|
|
1
|
+
5c90afac3b3a7d5fa2ba83e650604f6f9bc00682
|
@@ -0,0 +1 @@
|
|
1
|
+
5eabd613ae0284bc97755f7e29cf0d233e281ae7635afeeb0875437401c3ddb8
|
@@ -0,0 +1 @@
|
|
1
|
+
5ef628faf38474b46638efc54f932b15824f5735497279f0153fe9c6afc2f7de9a151ee46e51724ce93ff22515bf43b86147f8330c67a49b34dd45ce3b129c5d
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
8a77aaff1489b4887d6873aaae1357a3
|
@@ -0,0 +1 @@
|
|
1
|
+
d5f06bdfb31d314d08ef800ab5d9148502702d2a
|
@@ -0,0 +1 @@
|
|
1
|
+
7f45586ecc61fe0f158c8701000529d566299aa4b24f9539e4cc4ea2d29d898e
|
@@ -0,0 +1 @@
|
|
1
|
+
b00a7385379509967eb9ffed2c619e19008f03fc2d5534fb80bd08c31a6f9d937d9269858a4f3d03878d4979259d43da7ccec3173954454ff50c9a18590f1d20
|
@@ -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.15.0
|
6
|
+
<version>20.15.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
|
+
b214c6ec949dbb966c2ab104243efbb5
|
@@ -0,0 +1 @@
|
|
1
|
+
91c5ac02c59420f8a6eaca4af5fa5a5d40901b92
|
@@ -0,0 +1 @@
|
|
1
|
+
8e39181294f58719e0d42ea4b6220526012c229b47ab6db988acd904a8bba64c
|
@@ -0,0 +1 @@
|
|
1
|
+
c919a336c80ca89a3d41975a6b8574539f93234105f7762870afdd1540ef44763b00ecf7860b6cb22457ff9c714047f5c78f9a341c9ad923fc8beb25fd1d1331
|
@@ -3,11 +3,11 @@
|
|
3
3
|
<groupId>com.wix</groupId>
|
4
4
|
<artifactId>detox</artifactId>
|
5
5
|
<versioning>
|
6
|
-
<latest>20.15.0
|
7
|
-
<release>20.15.0
|
6
|
+
<latest>20.15.0</latest>
|
7
|
+
<release>20.15.0</release>
|
8
8
|
<versions>
|
9
|
-
<version>20.15.0
|
9
|
+
<version>20.15.0</version>
|
10
10
|
</versions>
|
11
|
-
<lastUpdated>
|
11
|
+
<lastUpdated>20240111111712</lastUpdated>
|
12
12
|
</versioning>
|
13
13
|
</metadata>
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
9e22bc0ea12d6e5775b68bde874bdf22
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
add0ba9ace5604f9bad42acfe204f32728323ce4
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
8d6d198043d40230347ae63f85625557fd6b2f45494d80c14e19e7e8e2f19fd2
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
7411bfbd2cbeb1e65e91618e520b083f54253f8b5e8f17f8b187de2716d3bf809c45246eb2bca80a9011f439f65f08053442daa846c4833bcb28f378a42e18b6
|
package/Detox-ios-src.tbz
CHANGED
Binary file
|
package/Detox-ios.tbz
CHANGED
Binary file
|
@@ -20,5 +20,4 @@
|
|
20
20
|
-keep class kotlin.io.** { *; }
|
21
21
|
-keep class okhttp3.** { *; }
|
22
22
|
|
23
|
-
-keep class androidx.concurrent.futures
|
24
|
-
-keep class androidx.concurrent.futures.CallbackToFutureAdapter { *; }
|
23
|
+
-keep class androidx.concurrent.futures.** { *; }
|
@@ -79,9 +79,14 @@ public class DetoxAction {
|
|
79
79
|
* Scrolls to the edge of the given scrollable view.
|
80
80
|
*
|
81
81
|
* @param edge Direction to scroll (see {@link MotionDir})
|
82
|
+
* @param startOffsetPercentX Percentage denoting where the scroll should start from on the X-axis, with respect to the scrollable view.
|
83
|
+
* @param startOffsetPercentY Percentage denoting where the scroll should start from on the Y-axis, with respect to the scrollable view.
|
82
84
|
* @return ViewAction
|
83
85
|
*/
|
84
|
-
public static ViewAction scrollToEdge(final int edge) {
|
86
|
+
public static ViewAction scrollToEdge(final int edge, double startOffsetPercentX, double startOffsetPercentY) {
|
87
|
+
final Float _startOffsetPercentX = startOffsetPercentX < 0 ? null : (float) startOffsetPercentX;
|
88
|
+
final Float _startOffsetPercentY = startOffsetPercentY < 0 ? null : (float) startOffsetPercentY;
|
89
|
+
|
85
90
|
return actionWithAssertions(new ViewAction() {
|
86
91
|
@Override
|
87
92
|
public Matcher<View> getConstraints() {
|
@@ -97,7 +102,7 @@ public class DetoxAction {
|
|
97
102
|
public void perform(UiController uiController, View view) {
|
98
103
|
try {
|
99
104
|
for (int i = 0; i < 100; i++) {
|
100
|
-
ScrollHelper.performOnce(uiController, view, edge);
|
105
|
+
ScrollHelper.performOnce(uiController, view, edge, _startOffsetPercentX, _startOffsetPercentY);
|
101
106
|
}
|
102
107
|
throw new DetoxRuntimeException("Scrolling a lot without reaching the edge: force-breaking the loop");
|
103
108
|
} catch (ScrollEdgeException e) {
|
@@ -112,8 +117,8 @@ public class DetoxAction {
|
|
112
117
|
*
|
113
118
|
* @param direction Direction to scroll (see {@link MotionDir})
|
114
119
|
* @param amountInDP Density Independent Pixels
|
115
|
-
* @param startOffsetPercentX Percentage denoting where
|
116
|
-
* @param startOffsetPercentY Percentage denoting where
|
120
|
+
* @param startOffsetPercentX Percentage denoting where the scroll should start from on the X-axis, with respect to the scrollable view.
|
121
|
+
* @param startOffsetPercentY Percentage denoting where the scroll should start from on the Y-axis, with respect to the scrollable view.
|
117
122
|
*/
|
118
123
|
public static ViewAction scrollInDirection(final int direction, final double amountInDP, double startOffsetPercentX, double startOffsetPercentY) {
|
119
124
|
final Float _startOffsetPercentX = startOffsetPercentX < 0 ? null : (float) startOffsetPercentX;
|
@@ -129,8 +134,8 @@ public class DetoxAction {
|
|
129
134
|
*
|
130
135
|
* @param direction Direction to scroll (see {@link MotionDir})
|
131
136
|
* @param amountInDP Density Independent Pixels
|
132
|
-
* @param startOffsetPercentX Percentage denoting where
|
133
|
-
* @param startOffsetPercentY Percentage denoting where
|
137
|
+
* @param startOffsetPercentX Percentage denoting where the scroll should start from on the X-axis, with respect to the scrollable view.
|
138
|
+
* @param startOffsetPercentY Percentage denoting where the scroll should start from on the Y-axis, with respect to the scrollable view.
|
134
139
|
*/
|
135
140
|
public static ViewAction scrollInDirectionStaleAtEdge(final int direction, final double amountInDP, double startOffsetPercentX, double startOffsetPercentY) {
|
136
141
|
final Float _startOffsetPercentX = startOffsetPercentX < 0 ? null : (float) startOffsetPercentX;
|
@@ -1,14 +1,18 @@
|
|
1
1
|
package com.wix.detox.espresso.scroll;
|
2
2
|
|
3
3
|
import android.content.Context;
|
4
|
+
import android.graphics.Insets;
|
4
5
|
import android.graphics.Point;
|
6
|
+
import android.os.Build;
|
5
7
|
import android.util.Log;
|
6
8
|
import android.view.View;
|
7
9
|
import android.view.ViewConfiguration;
|
10
|
+
import android.view.WindowInsets;
|
8
11
|
|
9
12
|
import com.wix.detox.action.common.MotionDir;
|
10
13
|
import com.wix.detox.espresso.DeviceDisplay;
|
11
14
|
|
15
|
+
import androidx.annotation.VisibleForTesting;
|
12
16
|
import androidx.test.espresso.UiController;
|
13
17
|
import androidx.test.platform.app.InstrumentationRegistry;
|
14
18
|
|
@@ -42,8 +46,8 @@ public class ScrollHelper {
|
|
42
46
|
*
|
43
47
|
* @param direction Direction to scroll (see {@link MotionDir})
|
44
48
|
* @param amountInDP Density Independent Pixels
|
45
|
-
* @param startOffsetPercentX Percentage denoting where
|
46
|
-
* @param startOffsetPercentY Percentage denoting where
|
49
|
+
* @param startOffsetPercentX Percentage denoting where the scroll should start from on the X-axis, with respect to the scrollable view. Null means select automatically.
|
50
|
+
* @param startOffsetPercentY Percentage denoting where the scroll should start from on the Y-axis, with respect to the scrollable view. Null means select automatically.
|
47
51
|
*/
|
48
52
|
public static void perform(UiController uiController, View view, @MotionDir int direction, double amountInDP, Float startOffsetPercentX, Float startOffsetPercentY) throws ScrollEdgeException {
|
49
53
|
final int amountInPx = DeviceDisplay.convertDpiToPx(amountInDP);
|
@@ -51,7 +55,7 @@ public class ScrollHelper {
|
|
51
55
|
final int times = amountInPx / safeScrollableRangePx;
|
52
56
|
final int remainder = amountInPx % safeScrollableRangePx;
|
53
57
|
|
54
|
-
Log.d(LOG_TAG, "prescroll amountDP="+amountInDP + " amountPx="+amountInPx + " scrollableRangePx="+safeScrollableRangePx + " times="+times + " remainder="+remainder);
|
58
|
+
Log.d(LOG_TAG, "prescroll amountDP=" + amountInDP + " amountPx=" + amountInPx + " scrollableRangePx=" + safeScrollableRangePx + " times=" + times + " remainder=" + remainder);
|
55
59
|
|
56
60
|
for (int i = 0; i < times; ++i) {
|
57
61
|
scrollOnce(uiController, view, direction, safeScrollableRangePx, startOffsetPercentX, startOffsetPercentY);
|
@@ -64,10 +68,12 @@ public class ScrollHelper {
|
|
64
68
|
* of the screen.)
|
65
69
|
*
|
66
70
|
* @param direction Direction to scroll (see {@link @MotionDir})
|
71
|
+
* @param startOffsetPercentX Percentage denoting where the scroll should start from on the X-axis, with respect to the scrollable view. Null means select automatically.
|
72
|
+
* @param startOffsetPercentY Percentage denoting where the scroll should start from on the Y-axis, with respect to the scrollable view. Null means select automatically.
|
67
73
|
*/
|
68
|
-
public static void performOnce(UiController uiController, View view, @MotionDir int direction) throws ScrollEdgeException {
|
74
|
+
public static void performOnce(UiController uiController, View view, @MotionDir int direction, Float startOffsetPercentX, Float startOffsetPercentY) throws ScrollEdgeException {
|
69
75
|
final int scrollableRangePx = getViewSafeScrollableRangePix(view, direction);
|
70
|
-
scrollOnce(uiController, view, direction, scrollableRangePx,
|
76
|
+
scrollOnce(uiController, view, direction, scrollableRangePx, startOffsetPercentX, startOffsetPercentY);
|
71
77
|
}
|
72
78
|
|
73
79
|
private static void scrollOnce(UiController uiController, View view, @MotionDir int direction, int userAmountPx, Float startOffsetPercentX, Float startOffsetPercentY) throws ScrollEdgeException {
|
@@ -113,25 +119,32 @@ public class ScrollHelper {
|
|
113
119
|
}
|
114
120
|
}
|
115
121
|
|
116
|
-
|
122
|
+
@VisibleForTesting
|
123
|
+
public static int getViewSafeScrollableRangePix(View view, @MotionDir int direction) {
|
117
124
|
final float[] screenSize = DeviceDisplay.getScreenSizeInPX();
|
118
125
|
final int[] pos = new int[2];
|
119
126
|
view.getLocationInWindow(pos);
|
120
127
|
|
121
128
|
int range;
|
122
129
|
switch (direction) {
|
123
|
-
case MOTION_DIR_LEFT:
|
124
|
-
|
125
|
-
|
126
|
-
|
130
|
+
case MOTION_DIR_LEFT:
|
131
|
+
range = (int) ((screenSize[0] - pos[0]) * SCROLL_RANGE_SAFE_PERCENT);
|
132
|
+
break;
|
133
|
+
case MOTION_DIR_RIGHT:
|
134
|
+
range = (int) ((pos[0] + view.getWidth()) * SCROLL_RANGE_SAFE_PERCENT);
|
135
|
+
break;
|
136
|
+
case MOTION_DIR_UP:
|
137
|
+
range = (int) ((screenSize[1] - pos[1]) * SCROLL_RANGE_SAFE_PERCENT);
|
138
|
+
break;
|
139
|
+
default:
|
140
|
+
range = (int) ((pos[1] + view.getHeight()) * SCROLL_RANGE_SAFE_PERCENT);
|
141
|
+
break;
|
127
142
|
}
|
128
143
|
return range;
|
129
144
|
}
|
130
145
|
|
131
|
-
private static
|
146
|
+
private static int[] getScrollStartOffsetInView(View view, @MotionDir int direction, Float startOffsetPercentX, Float startOffsetPercentY) {
|
132
147
|
final int safetyOffset = DeviceDisplay.convertDpiToPx(1);
|
133
|
-
|
134
|
-
Point point = getGlobalViewLocation(view);
|
135
148
|
float offsetFactorX;
|
136
149
|
float offsetFactorY;
|
137
150
|
int safetyOffsetX;
|
@@ -169,8 +182,87 @@ public class ScrollHelper {
|
|
169
182
|
int offsetX = ((int) (view.getWidth() * offsetFactorX) + safetyOffsetX);
|
170
183
|
int offsetY = ((int) (view.getHeight() * offsetFactorY) + safetyOffsetY);
|
171
184
|
|
172
|
-
|
173
|
-
|
185
|
+
return new int[]{offsetX, offsetY};
|
186
|
+
}
|
187
|
+
|
188
|
+
/**
|
189
|
+
* Calculates the scroll start point, with respect to the global screen coordinates and gesture insets.
|
190
|
+
* @param view The view to scroll.
|
191
|
+
* @param direction The scroll direction.
|
192
|
+
* @param startOffsetPercentX The scroll start offset, as a percentage of the view's width. Null means select automatically.
|
193
|
+
* @param startOffsetPercentY The scroll start offset, as a percentage of the view's height. Null means select automatically.
|
194
|
+
* @return a Point object, denoting the scroll start point.
|
195
|
+
*/
|
196
|
+
private static Point getScrollStartPoint(View view, @MotionDir int direction, Float startOffsetPercentX, Float startOffsetPercentY) {
|
197
|
+
Point result = getGlobalViewLocation(view);
|
198
|
+
|
199
|
+
// 1. Calculate the scroll start point, with respect to the view's location.
|
200
|
+
int[] coordinates = getScrollStartOffsetInView(view, direction, startOffsetPercentX, startOffsetPercentY);
|
201
|
+
|
202
|
+
// 2. Make sure that the start point is within the scrollable area, taking into account the system gesture insets.
|
203
|
+
coordinates = applyScreenInsets(view, direction, coordinates[0], coordinates[1]);
|
204
|
+
|
205
|
+
result.offset(coordinates[0], coordinates[1]);
|
206
|
+
return result;
|
207
|
+
}
|
208
|
+
|
209
|
+
/**
|
210
|
+
* Calculates the scroll start point, with respect to the system gesture insets.
|
211
|
+
* @param view
|
212
|
+
* @param direction The scroll direction.
|
213
|
+
* @param x The scroll start point, with respect to the view's location.
|
214
|
+
* @param y The scroll start point, with respect to the view's location.
|
215
|
+
* @return an array of two integers, denoting the scroll start point, with respect to the system gesture insets.
|
216
|
+
*/
|
217
|
+
private static int[] applyScreenInsets(View view, int direction, int x, int y) {
|
218
|
+
// System gesture insets are only available on Android Q (29) and above.
|
219
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
220
|
+
return new int[]{x, y};
|
221
|
+
}
|
222
|
+
|
223
|
+
final float[] displaySize = DeviceDisplay.getScreenSizeInPX();
|
224
|
+
|
225
|
+
// Calculate the min/max scrollable area, taking into account the system gesture insets.
|
226
|
+
// By default we assume the scrollable area is the entire screen.
|
227
|
+
// 2dp is a safety offset to make sure we don't hit the system gesture area.
|
228
|
+
int gestureSafeOffset = DeviceDisplay.convertDpiToPx(2);
|
229
|
+
int minX = gestureSafeOffset;
|
230
|
+
int minY = gestureSafeOffset;
|
231
|
+
float maxX = displaySize[0] - gestureSafeOffset;
|
232
|
+
float maxY = displaySize[1] - gestureSafeOffset;
|
233
|
+
|
234
|
+
// Try to get the root window insets, and if available, use them to calculate the scrollable area.
|
235
|
+
WindowInsets rootWindowInsets = view.getRootWindowInsets();
|
236
|
+
if (rootWindowInsets == null) {
|
237
|
+
Log.w(LOG_TAG, "Could not get root window insets");
|
238
|
+
} else {
|
239
|
+
Insets gestureInsets = rootWindowInsets.getSystemGestureInsets();
|
240
|
+
minX = gestureInsets.left;
|
241
|
+
minY = gestureInsets.top;
|
242
|
+
maxX -= gestureInsets.right;
|
243
|
+
maxY -= gestureInsets.bottom;
|
244
|
+
|
245
|
+
Log.d(LOG_TAG,
|
246
|
+
"System gesture insets: " +
|
247
|
+
gestureInsets + " minX=" + minX + " minY=" + minY + " maxX=" + maxX + " maxY=" + maxY + " currentX=" + x + " currentY=" + y);
|
248
|
+
}
|
249
|
+
|
250
|
+
switch (direction) {
|
251
|
+
case MOTION_DIR_UP:
|
252
|
+
y = (int) Math.max(y, minY);
|
253
|
+
break;
|
254
|
+
case MOTION_DIR_DOWN:
|
255
|
+
y = (int) Math.min(y, maxY);
|
256
|
+
break;
|
257
|
+
case MOTION_DIR_LEFT:
|
258
|
+
x = (int) Math.max(x, minX);
|
259
|
+
break;
|
260
|
+
case MOTION_DIR_RIGHT:
|
261
|
+
x = (int) Math.min(x, maxX);
|
262
|
+
break;
|
263
|
+
}
|
264
|
+
|
265
|
+
return new int[]{x, y};
|
174
266
|
}
|
175
267
|
|
176
268
|
private static Point getScrollEndPoint(Point startPoint, @MotionDir int direction, int userAmountPx, Float startOffsetPercentX, Float startOffsetPercentY) {
|
@@ -209,13 +301,18 @@ public class ScrollHelper {
|
|
209
301
|
return point;
|
210
302
|
}
|
211
303
|
|
304
|
+
/**
|
305
|
+
* Calculates the global location of the view on the screen.
|
306
|
+
* @param view The view to calculate.
|
307
|
+
* @return a Point object, denoting the global location of the view.
|
308
|
+
*/
|
212
309
|
private static Point getGlobalViewLocation(View view) {
|
213
310
|
int[] pos = new int[2];
|
214
311
|
view.getLocationInWindow(pos);
|
215
312
|
return new Point(pos[0], pos[1]);
|
216
313
|
}
|
217
314
|
|
218
|
-
|
315
|
+
public static ViewConfiguration getViewConfiguration() {
|
219
316
|
if (viewConfiguration == null) {
|
220
317
|
final Context applicationContext = InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
|
221
318
|
viewConfiguration = ViewConfiguration.get(applicationContext);
|
@@ -0,0 +1,188 @@
|
|
1
|
+
package com.wix.detox.espresso.scroll
|
2
|
+
|
3
|
+
import android.graphics.Insets
|
4
|
+
import android.view.MotionEvent
|
5
|
+
import android.view.View
|
6
|
+
import android.view.WindowInsets
|
7
|
+
import androidx.test.espresso.UiController
|
8
|
+
import androidx.test.platform.app.InstrumentationRegistry
|
9
|
+
import com.wix.detox.action.common.MOTION_DIR_DOWN
|
10
|
+
import com.wix.detox.action.common.MOTION_DIR_LEFT
|
11
|
+
import com.wix.detox.action.common.MOTION_DIR_RIGHT
|
12
|
+
import com.wix.detox.action.common.MOTION_DIR_UP
|
13
|
+
import com.wix.detox.espresso.DeviceDisplay
|
14
|
+
import org.junit.Test
|
15
|
+
import org.junit.runner.RunWith
|
16
|
+
import org.mockito.kotlin.any
|
17
|
+
import org.mockito.kotlin.argumentCaptor
|
18
|
+
import org.mockito.kotlin.mock
|
19
|
+
import org.mockito.kotlin.verify
|
20
|
+
import org.mockito.kotlin.whenever
|
21
|
+
import org.robolectric.RobolectricTestRunner
|
22
|
+
import org.robolectric.annotation.Config
|
23
|
+
import kotlin.test.assertEquals
|
24
|
+
|
25
|
+
private const val INSETS_SIZE = 100
|
26
|
+
private const val SCROLL_RANGE_SAFE_PERCENT = 0.9f // ScrollHelper.SCROLL_RANGE_SAFE_PERCENT
|
27
|
+
|
28
|
+
@Config(
|
29
|
+
qualifiers = "xxxhdpi", // 1280x1880
|
30
|
+
sdk = [33]
|
31
|
+
)
|
32
|
+
@RunWith(RobolectricTestRunner::class)
|
33
|
+
class ScrollHelperTest {
|
34
|
+
|
35
|
+
private val display = DeviceDisplay.getScreenSizeInPX()
|
36
|
+
private val displayWidth = display[0].toInt()
|
37
|
+
private val displayHeight = display[1].toInt()
|
38
|
+
private val touchSlopPx = ScrollHelper.getViewConfiguration().scaledTouchSlop
|
39
|
+
private val safetyMarginPx = DeviceDisplay.convertDpiToPx(2.0)
|
40
|
+
|
41
|
+
private val uiControllerMock = mock<UiController>()
|
42
|
+
private val viewMock = mockViewWithGestureNavigation(displayWidth, displayHeight)
|
43
|
+
|
44
|
+
@Test
|
45
|
+
fun `should scrolling down by 200 when gesture navigation enabled`() {
|
46
|
+
val amountInDp = 200.0
|
47
|
+
val amountInPx = amountInDp * DeviceDisplay.getDensity()
|
48
|
+
|
49
|
+
ScrollHelper.perform(uiControllerMock, viewMock, MOTION_DIR_DOWN, amountInDp, null, null)
|
50
|
+
|
51
|
+
val upEvent = getUpEvent()
|
52
|
+
// Verify that the scroll started at the center of the view
|
53
|
+
assertEquals(displayWidth / 2.0, upEvent.x.toDouble(), 0.0)
|
54
|
+
// Verify that the scroll ended at the center of the view minus the requested amount
|
55
|
+
assertEquals(displayHeight - amountInPx - touchSlopPx - safetyMarginPx - INSETS_SIZE, upEvent.y.toDouble(), 0.0)
|
56
|
+
}
|
57
|
+
|
58
|
+
@Test
|
59
|
+
fun `should scrolling down by 200 when gesture navigation disabled`() {
|
60
|
+
val amountInDp = 200.0
|
61
|
+
val amountInPx = amountInDp * DeviceDisplay.getDensity()
|
62
|
+
|
63
|
+
val viewMock = mockViewWithoutGestureNavigation(displayWidth, displayHeight)
|
64
|
+
ScrollHelper.perform(uiControllerMock, viewMock, MOTION_DIR_DOWN, amountInDp, null, null)
|
65
|
+
|
66
|
+
val upEvent = getUpEvent()
|
67
|
+
// Verify that the scroll started at the center of the view
|
68
|
+
assertEquals(displayWidth / 2.0, upEvent.x.toDouble(), 0.0)
|
69
|
+
// Verify that the scroll ended at the center of the view minus the requested amount
|
70
|
+
assertEquals(displayHeight - amountInPx - touchSlopPx - safetyMarginPx, upEvent.y.toDouble(), 0.0)
|
71
|
+
}
|
72
|
+
|
73
|
+
@Test
|
74
|
+
fun `should scroll down to edge on full screen view when gesture navigation enabled`() {
|
75
|
+
ScrollHelper.performOnce(uiControllerMock, viewMock, MOTION_DIR_DOWN, null, null)
|
76
|
+
val upEvent = getUpEvent()
|
77
|
+
val amountInPx = displayHeight * SCROLL_RANGE_SAFE_PERCENT
|
78
|
+
|
79
|
+
// Calculate where the scroll should end
|
80
|
+
val targetY = displayHeight - amountInPx -
|
81
|
+
touchSlopPx -
|
82
|
+
safetyMarginPx -
|
83
|
+
INSETS_SIZE
|
84
|
+
|
85
|
+
assertEquals(displayWidth / 2.0, upEvent.x.toDouble(), 0.0)
|
86
|
+
assertEquals(targetY, upEvent.y, 0.0f)
|
87
|
+
}
|
88
|
+
|
89
|
+
@Test
|
90
|
+
fun `should scroll left to edge on full screen view when gesture navigation enabled`() {
|
91
|
+
ScrollHelper.performOnce(uiControllerMock, viewMock, MOTION_DIR_LEFT, null, null)
|
92
|
+
val upEvent = getUpEvent()
|
93
|
+
val amountInPx = displayWidth * SCROLL_RANGE_SAFE_PERCENT
|
94
|
+
|
95
|
+
// Calculate where the scroll should end
|
96
|
+
val targetX = amountInPx +
|
97
|
+
touchSlopPx +
|
98
|
+
INSETS_SIZE
|
99
|
+
|
100
|
+
assertEquals(targetX, upEvent.x, 0.0f)
|
101
|
+
assertEquals(displayHeight / 2.0, upEvent.y.toDouble(), 0.0)
|
102
|
+
}
|
103
|
+
|
104
|
+
@Test
|
105
|
+
fun `should scroll up to edge on full screen view when gesture navigation enabled`() {
|
106
|
+
ScrollHelper.performOnce(uiControllerMock, viewMock, MOTION_DIR_UP,null, null)
|
107
|
+
val upEvent = getUpEvent()
|
108
|
+
val amountInPx = displayHeight * SCROLL_RANGE_SAFE_PERCENT
|
109
|
+
|
110
|
+
// Calculate where the scroll should end
|
111
|
+
val targetY = amountInPx +
|
112
|
+
touchSlopPx +
|
113
|
+
INSETS_SIZE
|
114
|
+
|
115
|
+
assertEquals(displayWidth / 2.0, upEvent.x.toDouble(), 0.0)
|
116
|
+
assertEquals(targetY, upEvent.y, 0.0f)
|
117
|
+
}
|
118
|
+
|
119
|
+
@Test
|
120
|
+
fun `should scroll right to edge on full screen view when gesture navigation enabled`() {
|
121
|
+
ScrollHelper.performOnce(uiControllerMock, viewMock, MOTION_DIR_RIGHT, null, null)
|
122
|
+
val upEvent = getUpEvent()
|
123
|
+
val amountInPx = displayWidth * SCROLL_RANGE_SAFE_PERCENT
|
124
|
+
|
125
|
+
// Calculate where the scroll should end
|
126
|
+
val targetX = displayWidth - amountInPx -
|
127
|
+
touchSlopPx -
|
128
|
+
safetyMarginPx -
|
129
|
+
INSETS_SIZE
|
130
|
+
|
131
|
+
assertEquals(targetX, upEvent.x, 0.0f)
|
132
|
+
assertEquals(displayHeight / 2.0, upEvent.y.toDouble(), 0.0)
|
133
|
+
}
|
134
|
+
|
135
|
+
/**
|
136
|
+
* Get the performed UP event from the ui controller
|
137
|
+
*/
|
138
|
+
private fun getUpEvent(): MotionEvent {
|
139
|
+
val capture = argumentCaptor<Iterable<MotionEvent>>()
|
140
|
+
// Capture the events from the ui controller
|
141
|
+
verify(uiControllerMock).injectMotionEventSequence(capture.capture())
|
142
|
+
|
143
|
+
val listOfCapturedEvents = capture.firstValue.toList()
|
144
|
+
// The last event is the UP event with the target coordinates. All of the rest are not interesting
|
145
|
+
return listOfCapturedEvents.last()
|
146
|
+
}
|
147
|
+
|
148
|
+
private fun mockViewWithoutGestureNavigation(displayWidth: Int, displayHeight: Int): View {
|
149
|
+
// This is how we disable gesture navigation
|
150
|
+
val windowInsets = mock<WindowInsets>() {
|
151
|
+
whenever(it.systemGestureInsets).thenReturn(
|
152
|
+
Insets.of(0, 0, 0, 0)
|
153
|
+
)
|
154
|
+
}
|
155
|
+
|
156
|
+
return mockView(displayWidth, displayHeight, windowInsets)
|
157
|
+
}
|
158
|
+
|
159
|
+
/**
|
160
|
+
* Mock a view with gesture navigation enabled
|
161
|
+
*/
|
162
|
+
private fun mockViewWithGestureNavigation(displayWidth: Int, displayHeight: Int): View {
|
163
|
+
// This is how we enable gesture navigation
|
164
|
+
val windowInsets = mock<WindowInsets>() {
|
165
|
+
whenever(it.systemGestureInsets).thenReturn(
|
166
|
+
Insets.of(INSETS_SIZE, INSETS_SIZE, INSETS_SIZE, INSETS_SIZE)
|
167
|
+
)
|
168
|
+
}
|
169
|
+
|
170
|
+
return mockView(displayWidth, displayHeight, windowInsets)
|
171
|
+
}
|
172
|
+
|
173
|
+
private fun mockView(
|
174
|
+
displayWidth: Int,
|
175
|
+
displayHeight: Int,
|
176
|
+
windowInsets: WindowInsets
|
177
|
+
): View {
|
178
|
+
val view = mock<View>() {
|
179
|
+
whenever(it.width).thenReturn(displayWidth)
|
180
|
+
whenever(it.height).thenReturn(displayHeight)
|
181
|
+
whenever(it.canScrollVertically(any())).thenReturn(true) // We allow endless scroll
|
182
|
+
whenever(it.canScrollHorizontally(any())).thenReturn(true) // We allow endless scroll
|
183
|
+
whenever(it.context).thenReturn(InstrumentationRegistry.getInstrumentation().targetContext)
|
184
|
+
whenever(it.rootWindowInsets).thenReturn(windowInsets)
|
185
|
+
}
|
186
|
+
return view
|
187
|
+
}
|
188
|
+
}
|
package/detox.d.ts
CHANGED
@@ -214,7 +214,7 @@ declare global {
|
|
214
214
|
* This flag tells Detox to print messages like these every time the device gets assigned to a specific suite:
|
215
215
|
*
|
216
216
|
* ```plain text
|
217
|
-
* 18:03:29.869 detox[40125] i starter.test.js is assigned to 4EC84833-C7EA-4CA3-A6E9-5C30A29EA596 (iPhone
|
217
|
+
* 18:03:29.869 detox[40125] i starter.test.js is assigned to 4EC84833-C7EA-4CA3-A6E9-5C30A29EA596 (iPhone 15)
|
218
218
|
* ```
|
219
219
|
*
|
220
220
|
* @default true
|
@@ -1363,10 +1363,13 @@ declare global {
|
|
1363
1363
|
|
1364
1364
|
/**
|
1365
1365
|
* Scroll to edge.
|
1366
|
-
* @
|
1366
|
+
* @param edge - left|right|top|bottom
|
1367
|
+
* @param startPositionX - the X starting scroll position, in percentage; valid input: `[0.0, 1.0]`, `NaN`; default: `NaN`—choose the best value automatically
|
1368
|
+
* @param startPositionY - the Y starting scroll position, in percentage; valid input: `[0.0, 1.0]`, `NaN`; default: `NaN`—choose the best value automatically
|
1369
|
+
* @example await element(by.id('scrollView')).scrollTo('bottom', NaN, 0.85);
|
1367
1370
|
* @example await element(by.id('scrollView')).scrollTo('top');
|
1368
1371
|
*/
|
1369
|
-
scrollTo(edge: Direction): Promise<void>;
|
1372
|
+
scrollTo(edge: Direction, startPositionX?: number, startPositionY?: number): Promise<void>;
|
1370
1373
|
|
1371
1374
|
/**
|
1372
1375
|
* Adjust slider to position.
|
package/jest.config.js
CHANGED