detox 20.15.1-prerelease.0 → 20.16.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.16.0/detox-20.16.0-javadoc.jar} +0 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0-javadoc.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0-javadoc.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0-javadoc.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.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.16.0/detox-20.16.0-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0.aar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.15.0-prerelease.0/detox-20.15.0-prerelease.0.pom → 20.16.0/detox-20.16.0.pom} +1 -1
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.0.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.16.0/detox-20.16.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 +26 -20
- 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 +111 -15
- 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
|
+
56f5ec5b7efbb230f3238c004a5fe715
|
@@ -0,0 +1 @@
|
|
1
|
+
64494abc41fe95ddfa2592069865471684bee013
|
@@ -0,0 +1 @@
|
|
1
|
+
a59fe4333df991b4188fe010b85fd8cd739ea7244d8feb096ed77b42ec178d63
|
@@ -0,0 +1 @@
|
|
1
|
+
623c51f747c437bd88a7421ac94e30d1c4d9c11f7aca8066746213c80e79eb7aba7a1d067dbf1033ae710deb021fdaf503304f387e43ae6333c763786a64bf9f
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
65e9a1e222c5bd2d0d8f1adbb87e81dc
|
@@ -0,0 +1 @@
|
|
1
|
+
a619619716feca26448febd692de8dac38314100
|
@@ -0,0 +1 @@
|
|
1
|
+
5aa3a4be5939728a238f7ed475ee6e6d55d0eef81d1f84fc842029a7ba597c58
|
@@ -0,0 +1 @@
|
|
1
|
+
61cf8afa304f860372a728279633e767070bddea071a2de58ff6b23fac2ef61e472ba4191704a8175cbd8cf1b10c526fae02db7205b8dcc5696d8f76e9441ab2
|
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.
|
6
|
+
<version>20.16.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
|
+
6e8623d17be32c1363d08aa89e5fd3b2
|
@@ -0,0 +1 @@
|
|
1
|
+
2b12affbd8b37c465ddc003dd59c224c83eb31fa
|
@@ -0,0 +1 @@
|
|
1
|
+
1c812b8cb14e877335790a3dbf0ffd4db6e85c311e56050aa14853f1c78a7c90
|
@@ -0,0 +1 @@
|
|
1
|
+
27641bf294e773373fa00156c73180d0260f0ee0611d5bb3f3e40001560a0a1537eed5807196397be5742d513974f7281bb682b0c09d2865888863080b305a05
|
@@ -3,11 +3,11 @@
|
|
3
3
|
<groupId>com.wix</groupId>
|
4
4
|
<artifactId>detox</artifactId>
|
5
5
|
<versioning>
|
6
|
-
<latest>20.
|
7
|
-
<release>20.
|
6
|
+
<latest>20.16.0</latest>
|
7
|
+
<release>20.16.0</release>
|
8
8
|
<versions>
|
9
|
-
<version>20.
|
9
|
+
<version>20.16.0</version>
|
10
10
|
</versions>
|
11
|
-
<lastUpdated>
|
11
|
+
<lastUpdated>20240114094649</lastUpdated>
|
12
12
|
</versioning>
|
13
13
|
</metadata>
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
3a7cd4da4ea3dd3e5cf31ee3a0c6ad4b
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
d32146f04f20a32ac69562f7fec718954e1f925d
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
9440532f25a30a2d2c025159b566348219724e16f627e1c2cf80e384c9b40141
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
ff53b142b83374497a6450075bf40da6df3996a77522efb5555d59b61b17e39c59b3891250a9c53789845add208bd28cfdbc975cd9e6a262ff47dead71e1dd74
|
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
|
+
}
|