detox 20.27.6 → 20.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Detox-android/com/wix/detox/{20.27.6/detox-20.27.6-sources.jar → 20.28.0/detox-20.28.0-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0.aar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.27.6/detox-20.27.6.pom → 20.28.0/detox-20.28.0.pom} +1 -1
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.0.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.28.0/detox-20.28.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-android/com/wix/detox-legacy/{20.27.6/detox-legacy-20.27.6-sources.jar → 20.28.0/detox-legacy-20.28.0-sources.jar} +0 -0
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox-legacy/{20.27.6/detox-legacy-20.27.6.aar → 20.28.0/detox-legacy-20.28.0.aar} +0 -0
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0.aar.md5 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox-legacy/{20.27.6/detox-legacy-20.27.6.pom → 20.28.0/detox-legacy-20.28.0.pom} +1 -1
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0.pom.md5 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.28.0/detox-legacy-20.28.0.pom.sha512 +1 -0
- package/Detox-android/com/wix/detox-legacy/maven-metadata.xml +4 -4
- package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.md5 +1 -1
- package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha1 +1 -1
- package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha256 +1 -1
- package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha512 +1 -1
- package/Detox-ios-framework.tbz +0 -0
- package/Detox-ios-src.tbz +0 -0
- package/Detox-ios-xcuitest.tbz +0 -0
- package/android/detox/src/full/java/com/wix/detox/espresso/EspressoDetox.java +53 -2
- package/android/detox/src/full/java/com/wix/detox/espresso/UiAutomatorHelper.java +11 -0
- package/android/detox/src/full/java/com/wix/detox/espresso/matcher/IsDisplayingAtLeastDetoxMatcher.kt +2 -2
- package/detox.d.ts +39 -0
- package/package.json +3 -3
- package/src/android/espressoapi/EspressoDetox.js +83 -0
- package/src/copilot/detoxCopilotFrameworkDriver.js +27 -15
- package/src/devices/runtime/RuntimeDevice.js +11 -0
- package/src/devices/runtime/drivers/DeviceDriverBase.js +8 -0
- package/src/devices/runtime/drivers/android/AndroidDriver.js +16 -0
- package/src/devices/runtime/drivers/ios/SimulatorDriver.js +35 -0
- package/src/utils/assertArgument.js +9 -0
- package/src/utils/invocationTraceDescriptions.js +1 -0
- package/src/utils/mapDeviceLongPressArguments.js +56 -0
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6.aar +0 -0
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6.aar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6.pom.md5 +0 -1
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.27.6/detox-20.27.6.pom.sha512 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6.aar.md5 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6.pom.md5 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.6/detox-legacy-20.27.6.pom.sha512 +0 -1
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5f9c61888ac541880ca966b9cf54f996
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
905d3c70363af1ef378373c1337b2a02fb085847
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
9da6d1b24a67e2c3f54e4f85b76d2f308df4f4783a2ead715cacae4078d54198
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
488ca79bd9330a788fe0659818e9256d06908d3c9ff765ddf9ee41e253b78dbbb0db1409f794532019fa39ca3395c8a27396398cbe56d203989afb258c31f1b8
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2d6e006f6dc0428fd9be3efb971433cf
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
7e71f1e0bd4e0beea5f2c8db164c560255332a30
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
d0d12c5ecb2c4d760332b168a9237aa1948ac10ea1736a4b75067de6ebaaf6cb
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1ebe46cd76b0bcfdcab9fc6b1a863b257da71ee05bac63f538fab1351f55ba858d97b81aba1d040d5db74c9e27508b12dbeb6ae2e6c09ef469a184516fc82c00
|
|
@@ -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.28.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
|
+
72dd9fb9e071a00dfb6b7731da5a8268
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5ea89a7c06c7d3e3ebee78b290422c25471ffccf
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
48839af705f5ce69be3f8075367e012c822f2cb9b4d5d66d1466c718f7adecd4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
01b1f663413f39c8f25e81f70669ddce75f2f332f154c531cb5289a646d25e3cc80120580202da5bc3cf1e4fd865a61b0551126be9da37b61eeec6f75fc2b9cf
|
|
@@ -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.28.0</latest>
|
|
7
|
+
<release>20.28.0</release>
|
|
8
8
|
<versions>
|
|
9
|
-
<version>20.
|
|
9
|
+
<version>20.28.0</version>
|
|
10
10
|
</versions>
|
|
11
|
-
<lastUpdated>
|
|
11
|
+
<lastUpdated>20241114182711</lastUpdated>
|
|
12
12
|
</versioning>
|
|
13
13
|
</metadata>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4409ef64d1213179048bfd9fed283361
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
112cd653d1f7777391df1fb10e9a33619f5d0c1b
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2177b496977ac272c3b7333fa2a4505f8756dca0bc265970801d60985a6a8dfd
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
da830892bc25886891e341fe7e167f4aa1138c8fc2c28246d31409c39e82aa64f13c0a0a39bb6394a260ee2f449bf16e5378be6026cf1862759d57cec40fd053
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
d01d2e826e3b99f843a7e444d309d6d2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3cf94f158d84d98241e421c64cc5c06142fbf4c0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cb302fd2cd81b8ba3a03115ab529d073bb8f223af0088e89e22af90120117412
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
37109f12c35e795d76f25a7c462c143dcedbbd0cb66bdde25364f237754da179307e87b73fae67e5432b7724903abe485c5105ac5667a7f6f26806b6c5229d88
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
f3e0e6447e243a7a93b9425d8ca2c34d
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
135215a6c872a80b0020ecface42fd07fde6e8a2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
67f6e9df85d01f17887b135aa64b3db8b10afce941b70314fa1cc15580699102
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
a71be556721d9c7b600914bcbeaff171d0d959a3dbb816d4467c8f52caa5703d9b241c0a76c13e4010c57036fff63b0c157eb126c58a96fe593ef1a71dcd3778
|
|
@@ -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.
|
|
6
|
+
<version>20.28.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
|
+
2c9a5efdc4cc9949403de29543d189c3
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dd1b295394376809aa40565060e7f0068758476d
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
28a059a24a50d3e94b75af039c1aee8dd403bb862525cce01876a4a188313eb1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
252cf3d47cd05805ab38367c8b33793b5b7e210a5f7f88717b1439127f168181bd5b3a05fa020c4c444aad0dfb6ad81547a7bc05ce6dc5f8043aff2eea3096f1
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<groupId>com.wix</groupId>
|
|
4
4
|
<artifactId>detox-legacy</artifactId>
|
|
5
5
|
<versioning>
|
|
6
|
-
<latest>20.
|
|
7
|
-
<release>20.
|
|
6
|
+
<latest>20.28.0</latest>
|
|
7
|
+
<release>20.28.0</release>
|
|
8
8
|
<versions>
|
|
9
|
-
<version>20.
|
|
9
|
+
<version>20.28.0</version>
|
|
10
10
|
</versions>
|
|
11
|
-
<lastUpdated>
|
|
11
|
+
<lastUpdated>20241114182808</lastUpdated>
|
|
12
12
|
</versioning>
|
|
13
13
|
</metadata>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
dbb9351f570b2e9f79cd97583cda0e41
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
e043135169acc282528cb1990f80ae8be7fd89b2
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
723c473ca26e2ced79ca5d707aa4e96f068d3bb86577a66d54ae5c41231127f2
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
5061e8696bf7b07237dd2b8fc02f65054eaaa87884723ca0f706b3b1de63e153e636db8077c6fdfd29ad8d0ca67433ff6a327bf6ffaddf040e671a2cafb4106e
|
package/Detox-ios-framework.tbz
CHANGED
|
Binary file
|
package/Detox-ios-src.tbz
CHANGED
|
Binary file
|
package/Detox-ios-xcuitest.tbz
CHANGED
|
Binary file
|
|
@@ -21,12 +21,11 @@ import java.util.ArrayList;
|
|
|
21
21
|
|
|
22
22
|
import androidx.test.espresso.UiController;
|
|
23
23
|
import androidx.test.espresso.ViewAction;
|
|
24
|
-
import androidx.test.espresso.ViewInteraction;
|
|
25
|
-
import androidx.test.espresso.NoMatchingViewException;
|
|
26
24
|
import androidx.test.platform.app.InstrumentationRegistry;
|
|
27
25
|
|
|
28
26
|
import static androidx.test.espresso.Espresso.onView;
|
|
29
27
|
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
|
28
|
+
import static com.wix.detox.espresso.UiAutomatorHelper.getStatusBarHeightDps;
|
|
30
29
|
|
|
31
30
|
/**
|
|
32
31
|
* Created by rotemm on 26/12/2016.
|
|
@@ -34,6 +33,10 @@ import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
|
|
34
33
|
public class EspressoDetox {
|
|
35
34
|
private static final String LOG_TAG = "detox";
|
|
36
35
|
|
|
36
|
+
private static int calculateAdjustedY(View view, Integer y, boolean shouldIgnoreStatusBar) {
|
|
37
|
+
return shouldIgnoreStatusBar ? y + getStatusBarHeightDps(view) : y;
|
|
38
|
+
}
|
|
39
|
+
|
|
37
40
|
public static Object perform(Matcher<View> matcher, ViewAction action) {
|
|
38
41
|
ViewActionPerformer performer = ViewActionPerformer.forAction(action);
|
|
39
42
|
return performer.performOn(matcher);
|
|
@@ -121,5 +124,53 @@ public class EspressoDetox {
|
|
|
121
124
|
}
|
|
122
125
|
});
|
|
123
126
|
}
|
|
127
|
+
|
|
128
|
+
public static void tap(Integer x, Integer y, boolean shouldIgnoreStatusBar) {
|
|
129
|
+
onView(isRoot()).perform(new ViewAction() {
|
|
130
|
+
@Override
|
|
131
|
+
public Matcher<View> getConstraints() {
|
|
132
|
+
return isRoot();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@Override
|
|
136
|
+
public String getDescription() {
|
|
137
|
+
return "tap on screen";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@Override
|
|
141
|
+
public void perform(UiController uiController, View view) {
|
|
142
|
+
int adjustedY = calculateAdjustedY(view, y, shouldIgnoreStatusBar);
|
|
143
|
+
ViewAction action = DetoxAction.tapAtLocation(x, adjustedY);
|
|
144
|
+
action.perform(uiController, view);
|
|
145
|
+
uiController.loopMainThreadUntilIdle();
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public static void longPress(Integer x, Integer y, boolean shouldIgnoreStatusBar) {
|
|
151
|
+
longPress(x, y, null, shouldIgnoreStatusBar);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public static void longPress(Integer x, Integer y, Integer duration, boolean shouldIgnoreStatusBar) {
|
|
155
|
+
onView(isRoot()).perform(new ViewAction() {
|
|
156
|
+
@Override
|
|
157
|
+
public Matcher<View> getConstraints() {
|
|
158
|
+
return isRoot();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@Override
|
|
162
|
+
public String getDescription() {
|
|
163
|
+
return "long press on screen";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@Override
|
|
167
|
+
public void perform(UiController uiController, View view) {
|
|
168
|
+
int adjustedY = calculateAdjustedY(view, y, shouldIgnoreStatusBar);
|
|
169
|
+
ViewAction action = DetoxAction.longPress(x, adjustedY, duration);
|
|
170
|
+
action.perform(uiController, view);
|
|
171
|
+
uiController.loopMainThreadUntilIdle();
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
124
175
|
}
|
|
125
176
|
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
package com.wix.detox.espresso;
|
|
2
2
|
|
|
3
|
+
import android.annotation.SuppressLint;
|
|
4
|
+
import android.content.Context;
|
|
3
5
|
import android.os.Handler;
|
|
6
|
+
import android.util.DisplayMetrics;
|
|
4
7
|
import android.util.Log;
|
|
8
|
+
import android.util.TypedValue;
|
|
5
9
|
import android.view.Choreographer;
|
|
10
|
+
import android.view.View;
|
|
6
11
|
|
|
7
12
|
import com.wix.detox.common.UIThread;
|
|
8
13
|
import com.wix.detox.espresso.action.common.utils.UiControllerUtils;
|
|
@@ -111,4 +116,10 @@ public class UiAutomatorHelper {
|
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
118
|
|
|
119
|
+
@SuppressLint({"DiscouragedApi", "InternalInsetResource"})
|
|
120
|
+
public static int getStatusBarHeightDps(View view) {
|
|
121
|
+
Context context = view.getContext();
|
|
122
|
+
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
|
|
123
|
+
return (int) (context.getResources().getDimensionPixelSize(resourceId) / ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT));
|
|
124
|
+
}
|
|
114
125
|
}
|
|
@@ -120,7 +120,7 @@ class IsDisplayingAtLeastDetoxMatcher(private val areaPercentage: Int) : TypeSaf
|
|
|
120
120
|
.defaultDisplay
|
|
121
121
|
.getMetrics(m)
|
|
122
122
|
|
|
123
|
-
val statusBarHeight =
|
|
123
|
+
val statusBarHeight = getStatusBarHeightPixels(view)
|
|
124
124
|
val actionBarHeight = getActionBarHeight(view)
|
|
125
125
|
return Rect(0, 0, m.widthPixels, m.heightPixels - (statusBarHeight + actionBarHeight))
|
|
126
126
|
}
|
|
@@ -138,7 +138,7 @@ class IsDisplayingAtLeastDetoxMatcher(private val areaPercentage: Int) : TypeSaf
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
@SuppressLint("InternalInsetResource", "DiscouragedApi")
|
|
141
|
-
private fun
|
|
141
|
+
private fun getStatusBarHeightPixels(view: View): Int {
|
|
142
142
|
val resourceId = view.context.resources.getIdentifier("status_bar_height", "dimen", "android")
|
|
143
143
|
return if (resourceId > 0) view.context.resources.getDimensionPixelSize(resourceId) else 0
|
|
144
144
|
}
|
package/detox.d.ts
CHANGED
|
@@ -784,6 +784,45 @@ declare global {
|
|
|
784
784
|
*/
|
|
785
785
|
setOrientation(orientation: Orientation): Promise<void>;
|
|
786
786
|
|
|
787
|
+
/**
|
|
788
|
+
* Perform a tap at arbitrary coordinates on the device's screen.
|
|
789
|
+
* @param point Coordinates in the element's coordinate space. Optional. defaults: x: 100, y: 100
|
|
790
|
+
* @param shouldIgnoreStatusBar Coordinates will be measured starting from under the status bar. this param will affect only in Android tests. Optional. default: true
|
|
791
|
+
* @example await device.tap();
|
|
792
|
+
* @example await device.tap({ x: 100, y: 150 }, false);
|
|
793
|
+
* @example await device.tap({ x: 100, y: 150 });
|
|
794
|
+
* @example await device.tap(false);
|
|
795
|
+
*/
|
|
796
|
+
tap(): Promise<void>;
|
|
797
|
+
tap(point: Point2D): Promise<void>;
|
|
798
|
+
tap(point: Point2D, shouldIgnoreStatusBar: boolean): Promise<void>;
|
|
799
|
+
tap(shouldIgnoreStatusBar: boolean): Promise<void>;
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Perform a long press at arbitrary coordinates on the device's screen. Custom press duration if needed.
|
|
803
|
+
* @param point Coordinates in the device's coordinate space. Optional. defaults: x: 100, y: 100
|
|
804
|
+
* @param duration Custom press duration time, in milliseconds. Optional (defaults to the standard long-press duration for Android and 1000 milliseconds for ios).
|
|
805
|
+
* Custom durations should be used cautiously, as they can affect test consistency and user experience expectations.
|
|
806
|
+
* They are typically necessary when testing components that behave differently from the platform's defaults or when simulating unique user interactions.
|
|
807
|
+
* @param shouldIgnoreStatusBar Coordinates will be measured starting from under the status bar. this param will affect only in Android tests. Optional. default: true
|
|
808
|
+
* @example await device.longPress();
|
|
809
|
+
* @example await device.longPress({ x: 100, y: 150 }, 2000, false);
|
|
810
|
+
* @example await device.longPress({ x: 100, y: 150 }, 2000);
|
|
811
|
+
* @example await device.longPress(2000, false);
|
|
812
|
+
* @example await device.longPress({ x: 100, y: 150 }, false);
|
|
813
|
+
* @example await device.longPress({ x: 100, y: 150 });
|
|
814
|
+
* @example await device.longPress(2000);
|
|
815
|
+
* @example await device.longPress(false);
|
|
816
|
+
*/
|
|
817
|
+
longPress(): Promise<void>;
|
|
818
|
+
longPress(point: Point2D, duration: number, shouldIgnoreStatusBar: boolean): Promise<void>;
|
|
819
|
+
longPress(point: Point2D, duration: number): Promise<void>;
|
|
820
|
+
longPress(duration: number, shouldIgnoreStatusBar: boolean): Promise<void>;
|
|
821
|
+
longPress(point: Point2D, shouldIgnoreStatusBar: boolean): Promise<void>;
|
|
822
|
+
longPress(point: Point2D): Promise<void>;
|
|
823
|
+
longPress(duration: number): Promise<void>;
|
|
824
|
+
longPress(shouldIgnoreStatusBar: boolean): Promise<void>;
|
|
825
|
+
|
|
787
826
|
/**
|
|
788
827
|
* Sets the simulator/emulator location to the given latitude and longitude.
|
|
789
828
|
*
|
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.
|
|
4
|
+
"version": "20.28.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"detox": "local-cli/cli.js"
|
|
7
7
|
},
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"caf": "^15.0.1",
|
|
72
72
|
"chalk": "^4.0.0",
|
|
73
73
|
"child-process-promise": "^2.2.0",
|
|
74
|
-
"detox-copilot": "^0.0.
|
|
74
|
+
"detox-copilot": "^0.0.24",
|
|
75
75
|
"execa": "^5.1.1",
|
|
76
76
|
"find-up": "^5.0.0",
|
|
77
77
|
"fs-extra": "^11.0.0",
|
|
@@ -116,5 +116,5 @@
|
|
|
116
116
|
"browserslist": [
|
|
117
117
|
"node 14"
|
|
118
118
|
],
|
|
119
|
-
"gitHead": "
|
|
119
|
+
"gitHead": "c97e583d7e33ad9472d5b3e68dda724f1300c83c"
|
|
120
120
|
}
|
|
@@ -73,6 +73,89 @@ class EspressoDetox {
|
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
static tap(x, y, shouldIgnoreStatusBar) {
|
|
77
|
+
if (typeof x !== "number") throw new Error("x should be a number, but got " + (x + (" (" + (typeof x + ")"))));
|
|
78
|
+
if (typeof y !== "number") throw new Error("y should be a number, but got " + (y + (" (" + (typeof y + ")"))));
|
|
79
|
+
if (typeof shouldIgnoreStatusBar !== "boolean") throw new Error("shouldIgnoreStatusBar should be a boolean, but got " + (shouldIgnoreStatusBar + (" (" + (typeof shouldIgnoreStatusBar + ")"))));
|
|
80
|
+
return {
|
|
81
|
+
target: {
|
|
82
|
+
type: "Class",
|
|
83
|
+
value: "com.wix.detox.espresso.EspressoDetox"
|
|
84
|
+
},
|
|
85
|
+
method: "tap",
|
|
86
|
+
args: [{
|
|
87
|
+
type: "Integer",
|
|
88
|
+
value: x
|
|
89
|
+
}, {
|
|
90
|
+
type: "Integer",
|
|
91
|
+
value: y
|
|
92
|
+
}, {
|
|
93
|
+
type: "boolean",
|
|
94
|
+
value: shouldIgnoreStatusBar
|
|
95
|
+
}]
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static longPress(x, y, shouldIgnoreStatusBar) {
|
|
100
|
+
function longPress3(x, y, shouldIgnoreStatusBar) {
|
|
101
|
+
if (typeof x !== "number") throw new Error("x should be a number, but got " + (x + (" (" + (typeof x + ")"))));
|
|
102
|
+
if (typeof y !== "number") throw new Error("y should be a number, but got " + (y + (" (" + (typeof y + ")"))));
|
|
103
|
+
if (typeof shouldIgnoreStatusBar !== "boolean") throw new Error("shouldIgnoreStatusBar should be a boolean, but got " + (shouldIgnoreStatusBar + (" (" + (typeof shouldIgnoreStatusBar + ")"))));
|
|
104
|
+
return {
|
|
105
|
+
target: {
|
|
106
|
+
type: "Class",
|
|
107
|
+
value: "com.wix.detox.espresso.EspressoDetox"
|
|
108
|
+
},
|
|
109
|
+
method: "longPress",
|
|
110
|
+
args: [{
|
|
111
|
+
type: "Integer",
|
|
112
|
+
value: x
|
|
113
|
+
}, {
|
|
114
|
+
type: "Integer",
|
|
115
|
+
value: y
|
|
116
|
+
}, {
|
|
117
|
+
type: "boolean",
|
|
118
|
+
value: shouldIgnoreStatusBar
|
|
119
|
+
}]
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function longPress4(x, y, duration, shouldIgnoreStatusBar) {
|
|
124
|
+
if (typeof x !== "number") throw new Error("x should be a number, but got " + (x + (" (" + (typeof x + ")"))));
|
|
125
|
+
if (typeof y !== "number") throw new Error("y should be a number, but got " + (y + (" (" + (typeof y + ")"))));
|
|
126
|
+
if (typeof duration !== "number") throw new Error("duration should be a number, but got " + (duration + (" (" + (typeof duration + ")"))));
|
|
127
|
+
if (typeof shouldIgnoreStatusBar !== "boolean") throw new Error("shouldIgnoreStatusBar should be a boolean, but got " + (shouldIgnoreStatusBar + (" (" + (typeof shouldIgnoreStatusBar + ")"))));
|
|
128
|
+
return {
|
|
129
|
+
target: {
|
|
130
|
+
type: "Class",
|
|
131
|
+
value: "com.wix.detox.espresso.EspressoDetox"
|
|
132
|
+
},
|
|
133
|
+
method: "longPress",
|
|
134
|
+
args: [{
|
|
135
|
+
type: "Integer",
|
|
136
|
+
value: x
|
|
137
|
+
}, {
|
|
138
|
+
type: "Integer",
|
|
139
|
+
value: y
|
|
140
|
+
}, {
|
|
141
|
+
type: "Integer",
|
|
142
|
+
value: duration
|
|
143
|
+
}, {
|
|
144
|
+
type: "boolean",
|
|
145
|
+
value: shouldIgnoreStatusBar
|
|
146
|
+
}]
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (arguments.length === 3) {
|
|
151
|
+
return longPress3.apply(null, arguments);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (arguments.length === 4) {
|
|
155
|
+
return longPress4.apply(null, arguments);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
76
159
|
}
|
|
77
160
|
|
|
78
161
|
module.exports = EspressoDetox;
|
|
@@ -18,7 +18,7 @@ const detoxCopilotFrameworkDriver = {
|
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
signature: 'by.text(text: string)',
|
|
21
|
-
description: 'Matches elements by their text.',
|
|
21
|
+
description: 'Matches elements by their text (value).',
|
|
22
22
|
example: "element(by.text('Login'))",
|
|
23
23
|
guidelines: ['Prefer test IDs over text matchers when possible.'],
|
|
24
24
|
},
|
|
@@ -34,6 +34,12 @@ const detoxCopilotFrameworkDriver = {
|
|
|
34
34
|
example: "element(by.id('listItem')).atIndex(2)",
|
|
35
35
|
guidelines: ['Use when multiple elements match the same matcher.'],
|
|
36
36
|
},
|
|
37
|
+
{
|
|
38
|
+
signature: 'by.label(label: string)',
|
|
39
|
+
description: 'Match elements with the specified label.',
|
|
40
|
+
example: "element(by.label('Tuesday, 1 October'));",
|
|
41
|
+
guidelines: ['Use when there are no other identifiers, such as for date pickers to select specific days.'],
|
|
42
|
+
},
|
|
37
43
|
],
|
|
38
44
|
},
|
|
39
45
|
{
|
|
@@ -106,7 +112,7 @@ const detoxCopilotFrameworkDriver = {
|
|
|
106
112
|
],
|
|
107
113
|
},
|
|
108
114
|
{
|
|
109
|
-
signature: 'waitFor(element: Matcher)
|
|
115
|
+
signature: 'waitFor(element: Matcher).toBeVisible(percent?: number).whileElement(element: Matcher).scroll(offset: number, direction: string)',
|
|
110
116
|
description: 'Continuously performs an action while waiting for an expectation to be fulfilled.',
|
|
111
117
|
example: `
|
|
112
118
|
await waitFor(element(by.text('Load More')))
|
|
@@ -185,9 +191,13 @@ jestExpect(attributes.text).toBe('Tap Me');`,
|
|
|
185
191
|
title: 'Assertions',
|
|
186
192
|
items: [
|
|
187
193
|
{
|
|
188
|
-
signature: 'toBeVisible()',
|
|
189
|
-
description: 'Asserts that the element is visible.',
|
|
190
|
-
example: "await expect(element(by.id('loginButton'))).toBeVisible();",
|
|
194
|
+
signature: 'toBeVisible(percent?: number)',
|
|
195
|
+
description: 'Asserts that the element is visible with at-least the specified percentage. Default percent is 75%.',
|
|
196
|
+
example: "await expect(element(by.id('loginButton'))).toBeVisible(38);",
|
|
197
|
+
guidelines: [
|
|
198
|
+
'Use the default visibility percent unless a different percentage is required.',
|
|
199
|
+
'If a percentage value is provided, use the exact percentage required for the test.',
|
|
200
|
+
],
|
|
191
201
|
},
|
|
192
202
|
{
|
|
193
203
|
signature: 'toExist()',
|
|
@@ -377,8 +387,8 @@ await device.launchApp({ launchArgs: { someLaunchArg: 1234 } });`,
|
|
|
377
387
|
items: [
|
|
378
388
|
{
|
|
379
389
|
signature: 'web.element(matcher: Matcher)',
|
|
380
|
-
description: 'Selects an element within a web view. Use when there is only one web view on the screen.',
|
|
381
|
-
example: `
|
|
390
|
+
description: 'Selects an element within a web view (`WKWebView` or `RNCWebView`). Use when there is only one web view on the screen.',
|
|
391
|
+
example: `
|
|
382
392
|
await web.element(by.web.id('email')).typeText('test@example.com');
|
|
383
393
|
await web.element(by.web.id('password')).typeText('password123');
|
|
384
394
|
await web.element(by.web.id('login-button')).tap();
|
|
@@ -387,19 +397,20 @@ await web.element(by.web.id('login-button')).tap();
|
|
|
387
397
|
'The web view may take time to load; add a delay using `await new Promise(resolve => setTimeout(resolve, milliseconds));` before the first interaction. This wait should happen only once.',
|
|
388
398
|
'After the initial wait, you can interact with web elements without additional delays.',
|
|
389
399
|
'Use `by.web.id` matcher when possible for matching web elements, as it is the most reliable.',
|
|
390
|
-
'Web APIs can only be used with web elements (within web views).
|
|
400
|
+
'Web APIs can only be used with web elements (within web views). Do not use web APIs for native elements or native APIs for web elements!',
|
|
401
|
+
'Confirm that you are targeting a web view before using this method.'
|
|
391
402
|
],
|
|
392
403
|
},
|
|
393
404
|
{
|
|
394
405
|
signature: 'web(nativeMatcher: NativeMatcher).element(matcher: Matcher)',
|
|
395
|
-
description: 'Selects an element within a specific web view matched by a native matcher. Use when there are multiple web views on the screen.',
|
|
406
|
+
description: 'Selects an element within a specific web view (`WKWebView` or `RNCWebView`) matched by a native matcher. Use when there are multiple web views on the screen.',
|
|
396
407
|
example: `
|
|
397
408
|
// Wait for the specific web view to appear and load (only once before interacting)
|
|
398
409
|
await expect(element(by.id('checkout-webview'))).toBeVisible();
|
|
399
410
|
|
|
400
411
|
// Interact with elements within a specific web view
|
|
401
412
|
const specificWebView = web(by.id('checkout-webview'));
|
|
402
|
-
|
|
413
|
+
|
|
403
414
|
await specificWebView.element(by.web.id('credit-card-number')).typeText('4111111111111111');
|
|
404
415
|
await specificWebView.element(by.web.id('expiration-date')).typeText('12/25');
|
|
405
416
|
await specificWebView.element(by.web.id('cvv')).typeText('123');
|
|
@@ -411,6 +422,7 @@ await specificWebView.element(by.web.id('pay-button')).tap();
|
|
|
411
422
|
'After the initial wait, you can interact with elements within the web view without additional delays.',
|
|
412
423
|
'Webview must be matched with `web(nativeMatcher)` (e.g., `web(by.id(..))` instead of `element(by.id(..))`).',
|
|
413
424
|
'Prefer the basic `web.element()` if only one web view is present on the screen.',
|
|
425
|
+
'Confirm that you are targeting a web view before using this method.'
|
|
414
426
|
],
|
|
415
427
|
},
|
|
416
428
|
{
|
|
@@ -485,8 +497,8 @@ await secondWebView.element(by.web.id('search-button')).tap();
|
|
|
485
497
|
example: `await web.element(by.web.label('Next')).tap();`,
|
|
486
498
|
guidelines: [
|
|
487
499
|
'Available on iOS only.',
|
|
488
|
-
'Use when the element has a unique label or aria-label.',
|
|
489
|
-
'Can be used to match buttons and input elements by their inner text content.',
|
|
500
|
+
'Use when the inner web element has a unique label or aria-label.',
|
|
501
|
+
'Can be used to match buttons and input elements inside a web view, by their inner text content.',
|
|
490
502
|
],
|
|
491
503
|
},
|
|
492
504
|
{
|
|
@@ -567,13 +579,13 @@ await web.element(by.web.id('email-input')).focus();
|
|
|
567
579
|
await web.element(by.web.id('email-input')).typeText('user@example.com');
|
|
568
580
|
`,
|
|
569
581
|
guidelines: [
|
|
570
|
-
'Useful for input fields that require focus before typing.',
|
|
582
|
+
'Useful for input fields in a web view that require focus before typing.',
|
|
571
583
|
'No need for secured interactions on iOS.',
|
|
572
584
|
]
|
|
573
585
|
},
|
|
574
586
|
{
|
|
575
587
|
signature: 'moveCursorToEnd()',
|
|
576
|
-
description: 'Moves the input cursor to the end of the element\'s content.',
|
|
588
|
+
description: 'Moves the input cursor in a web view to the end of the element\'s content.',
|
|
577
589
|
example: `
|
|
578
590
|
await web.element(by.web.id('message-box')).moveCursorToEnd();
|
|
579
591
|
await web.element(by.web.id('message-box')).typeText(' Adding more text.');
|
|
@@ -581,7 +593,7 @@ await web.element(by.web.id('message-box')).typeText(' Adding more text.');
|
|
|
581
593
|
},
|
|
582
594
|
{
|
|
583
595
|
signature: 'runScript(script: string, args?: any[])',
|
|
584
|
-
description: 'Runs a JavaScript function on the element.',
|
|
596
|
+
description: 'Runs a JavaScript function on the web view element.',
|
|
585
597
|
example: `
|
|
586
598
|
// Click an element using a custom script
|
|
587
599
|
await web.element(by.web.id('hidden-button')).runScript('el => el.click()');
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const DetoxRuntimeError = require('../../errors/DetoxRuntimeError');
|
|
2
2
|
const debug = require('../../utils/debug'); // debug utils, leave here even if unused
|
|
3
3
|
const log = require('../../utils/logger').child({ cat: 'device' });
|
|
4
|
+
const mapDeviceLongPressArguments = require('../../utils/mapDeviceLongPressArguments');
|
|
4
5
|
const traceMethods = require('../../utils/traceMethods');
|
|
5
6
|
const wrapWithStackTraceCutter = require('../../utils/wrapWithStackTraceCutter');
|
|
6
7
|
|
|
@@ -283,6 +284,16 @@ class RuntimeDevice {
|
|
|
283
284
|
await this.deviceDriver.setOrientation(orientation);
|
|
284
285
|
}
|
|
285
286
|
|
|
287
|
+
async tap(point, shouldIgnoreStatusBar) {
|
|
288
|
+
await this.deviceDriver.tap(point, shouldIgnoreStatusBar, this._bundleId);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async longPress(arg1, arg2, arg3) {
|
|
292
|
+
let { point, duration, shouldIgnoreStatusBar } = mapDeviceLongPressArguments(arg1, arg2, arg3);
|
|
293
|
+
|
|
294
|
+
await this.deviceDriver.longPress(point, duration, shouldIgnoreStatusBar, this._bundleId);
|
|
295
|
+
}
|
|
296
|
+
|
|
286
297
|
async setLocation(lat, lon) {
|
|
287
298
|
lat = String(lat);
|
|
288
299
|
lon = String(lon);
|
|
@@ -245,6 +245,22 @@ class AndroidDriver extends DeviceDriverBase {
|
|
|
245
245
|
await this.invocationManager.execute(call);
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
async tap(point, shouldIgnoreStatusBar) {
|
|
249
|
+
let x = point?.x ?? 100;
|
|
250
|
+
let y = point?.y ?? 100;
|
|
251
|
+
let _shouldIgnoreStatusBar = shouldIgnoreStatusBar ?? true;
|
|
252
|
+
const call = EspressoDetoxApi.tap(x, y, _shouldIgnoreStatusBar);
|
|
253
|
+
await this.invocationManager.execute(call);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async longPress(point, duration, shouldIgnoreStatusBar) {
|
|
257
|
+
let x = point?.x ?? 100;
|
|
258
|
+
let y = point?.y ?? 100;
|
|
259
|
+
let _shouldIgnoreStatusBar = shouldIgnoreStatusBar ?? true;
|
|
260
|
+
const call = duration ? EspressoDetoxApi.longPress(x, y, duration, _shouldIgnoreStatusBar): EspressoDetoxApi.longPress(x, y, _shouldIgnoreStatusBar);
|
|
261
|
+
await this.invocationManager.execute(call);
|
|
262
|
+
}
|
|
263
|
+
|
|
248
264
|
async generateViewHierarchyXml(shouldInjectTestIds) {
|
|
249
265
|
const hierarchy = await this.invocationManager.execute(DetoxApi.generateViewHierarchyXml(shouldInjectTestIds));
|
|
250
266
|
return hierarchy.result;
|
|
@@ -6,12 +6,17 @@ const _ = require('lodash');
|
|
|
6
6
|
|
|
7
7
|
const temporaryPath = require('../../../../artifacts/utils/temporaryPath');
|
|
8
8
|
const DetoxRuntimeError = require('../../../../errors/DetoxRuntimeError');
|
|
9
|
+
const XCUITestRunner = require('../../../../ios/XCUITestRunner');
|
|
10
|
+
const { assertTraceDescription } = require('../../../../utils/assertArgument');
|
|
9
11
|
const getAbsoluteBinaryPath = require('../../../../utils/getAbsoluteBinaryPath');
|
|
12
|
+
const { actionDescription } = require('../../../../utils/invocationTraceDescriptions');
|
|
10
13
|
const log = require('../../../../utils/logger').child({ cat: 'device' });
|
|
11
14
|
const pressAnyKey = require('../../../../utils/pressAnyKey');
|
|
15
|
+
const traceInvocationCall = require('../../../../utils/traceInvocationCall').bind(null, log);
|
|
12
16
|
|
|
13
17
|
const IosDriver = require('./IosDriver');
|
|
14
18
|
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
21
|
* @typedef SimulatorDriverDeps { DeviceDriverDeps }
|
|
17
22
|
* @property applesimutils { AppleSimUtils }
|
|
@@ -40,6 +45,19 @@ class SimulatorDriver extends IosDriver {
|
|
|
40
45
|
this._applesimutils = deps.applesimutils;
|
|
41
46
|
}
|
|
42
47
|
|
|
48
|
+
withAction(xcuitestRunner, action, traceDescription, ...params) {
|
|
49
|
+
assertTraceDescription(traceDescription);
|
|
50
|
+
|
|
51
|
+
const invocation = {
|
|
52
|
+
...(params.length !== 0 && { params }),
|
|
53
|
+
type: 'systemAction',
|
|
54
|
+
...(this.index !== undefined && { systemAtIndex: this.index }),
|
|
55
|
+
systemAction: action
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return traceInvocationCall(traceDescription, invocation, xcuitestRunner.execute(invocation));
|
|
59
|
+
}
|
|
60
|
+
|
|
43
61
|
getExternalId() {
|
|
44
62
|
return this.udid;
|
|
45
63
|
}
|
|
@@ -111,6 +129,23 @@ class SimulatorDriver extends IosDriver {
|
|
|
111
129
|
await this.emitter.emit('terminateApp', { deviceId: udid, bundleId });
|
|
112
130
|
}
|
|
113
131
|
|
|
132
|
+
async tap(point, shouldIgnoreStatusBar, _bundleId) {
|
|
133
|
+
const xcuitestRunner = new XCUITestRunner({ runtimeDevice: { id: this.getExternalId(), _bundleId } });
|
|
134
|
+
let x = point?.x ?? 100;
|
|
135
|
+
let y = point?.y ?? 100;
|
|
136
|
+
const traceDescription = actionDescription.tap({ x, y });
|
|
137
|
+
return this.withAction(xcuitestRunner, 'coordinateTap', traceDescription, x.toString(), y.toString());
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async longPress(point, pressDuration, shouldIgnoreStatusBar, _bundleId) {
|
|
141
|
+
const xcuitestRunner = new XCUITestRunner({ runtimeDevice: { id: this.getExternalId(), _bundleId } });
|
|
142
|
+
let x = point?.x ?? 100;
|
|
143
|
+
let y = point?.y ?? 100;
|
|
144
|
+
let _pressDuration = pressDuration ? (pressDuration / 1000) : 1;
|
|
145
|
+
const traceDescription = actionDescription.longPress({ x, y }, _pressDuration);
|
|
146
|
+
return this.withAction(xcuitestRunner, 'coordinateLongPress', traceDescription, x.toString(), y.toString(), _pressDuration.toString());
|
|
147
|
+
}
|
|
148
|
+
|
|
114
149
|
async setBiometricEnrollment(yesOrNo) {
|
|
115
150
|
await this._applesimutils.setBiometricEnrollment(this.udid, yesOrNo);
|
|
116
151
|
}
|
|
@@ -52,6 +52,14 @@ function assertPoint(point) {
|
|
|
52
52
|
throw new DetoxRuntimeError(`point should be an object with x and y properties, but got ${JSON.stringify(point)}`);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function assertShouldIgnoreStatusBar(shouldIgnoreStatusBar) {
|
|
56
|
+
if (typeof shouldIgnoreStatusBar === 'boolean') {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
throw new DetoxRuntimeError('shouldIgnoreStatusBar should be a boolean, but got ' + (shouldIgnoreStatusBar + (' (' + (typeof shouldIgnoreStatusBar + ')'))));
|
|
61
|
+
}
|
|
62
|
+
|
|
55
63
|
function assertUndefined(arg) {
|
|
56
64
|
if (arg === undefined) {
|
|
57
65
|
return true;
|
|
@@ -76,6 +84,7 @@ module.exports = {
|
|
|
76
84
|
assertString,
|
|
77
85
|
assertDuration,
|
|
78
86
|
assertPoint,
|
|
87
|
+
assertShouldIgnoreStatusBar,
|
|
79
88
|
assertUndefined,
|
|
80
89
|
assertTraceDescription
|
|
81
90
|
};
|
|
@@ -3,6 +3,7 @@ module.exports = {
|
|
|
3
3
|
adjustSliderToPosition: (newPosition) => `adjust slider to position ${newPosition}`,
|
|
4
4
|
clearText: () => 'clear input text',
|
|
5
5
|
getAttributes: () => 'get element attributes',
|
|
6
|
+
tap: (point) => `tap at ${JSON.stringify(point)}`,
|
|
6
7
|
longPress: (point, duration) => `long press${duration !== null ? ` for ${duration}ms` : ''}${point !== null ? ` at ${JSON.stringify(point)}` : ''}`,
|
|
7
8
|
longPressAndDrag: (duration, startX, startY, targetElement, endX, endY, speed, holdDuration) =>
|
|
8
9
|
`long press and drag from ${startX}, ${startY} to ${endX}, ${endY} with speed ${speed} and hold duration ${holdDuration}`,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const { DetoxRuntimeError } = require('../errors');
|
|
2
|
+
|
|
3
|
+
const { assertPoint, assertDuration, assertUndefined, assertShouldIgnoreStatusBar } = require('./assertArgument');
|
|
4
|
+
|
|
5
|
+
function mapDeviceLongPressArguments(optionalAllParams, optionalDurationOrIgnoreStatusBar, optionalIgnoreStatusBar) {
|
|
6
|
+
let point = null;
|
|
7
|
+
let duration = null;
|
|
8
|
+
let shouldIgnoreStatusBar = null;
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
if (optionalAllParams === undefined) {
|
|
12
|
+
// Do nothing.
|
|
13
|
+
} else if (typeof optionalAllParams === 'number') {
|
|
14
|
+
duration = optionalAllParams;
|
|
15
|
+
if (typeof optionalDurationOrIgnoreStatusBar === 'boolean') {
|
|
16
|
+
shouldIgnoreStatusBar = optionalDurationOrIgnoreStatusBar;
|
|
17
|
+
} else {
|
|
18
|
+
assertUndefined(optionalDurationOrIgnoreStatusBar);
|
|
19
|
+
}
|
|
20
|
+
assertUndefined(optionalIgnoreStatusBar);
|
|
21
|
+
} else if (typeof optionalAllParams === 'boolean') {
|
|
22
|
+
shouldIgnoreStatusBar = optionalAllParams;
|
|
23
|
+
assertUndefined(optionalDurationOrIgnoreStatusBar);
|
|
24
|
+
assertUndefined(optionalIgnoreStatusBar);
|
|
25
|
+
} else {
|
|
26
|
+
assertPoint(optionalAllParams);
|
|
27
|
+
point = optionalAllParams;
|
|
28
|
+
|
|
29
|
+
if (typeof optionalDurationOrIgnoreStatusBar === 'number') {
|
|
30
|
+
assertDuration(optionalDurationOrIgnoreStatusBar);
|
|
31
|
+
duration = optionalDurationOrIgnoreStatusBar;
|
|
32
|
+
} else if (typeof optionalDurationOrIgnoreStatusBar === 'boolean') {
|
|
33
|
+
assertShouldIgnoreStatusBar(optionalDurationOrIgnoreStatusBar);
|
|
34
|
+
shouldIgnoreStatusBar = optionalDurationOrIgnoreStatusBar;
|
|
35
|
+
assertUndefined(optionalIgnoreStatusBar);
|
|
36
|
+
} else if (optionalDurationOrIgnoreStatusBar !== undefined) {
|
|
37
|
+
assertDuration(optionalDurationOrIgnoreStatusBar);
|
|
38
|
+
} else {
|
|
39
|
+
assertUndefined(optionalDurationOrIgnoreStatusBar);
|
|
40
|
+
assertUndefined(optionalIgnoreStatusBar);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (optionalIgnoreStatusBar !== undefined) {
|
|
44
|
+
assertShouldIgnoreStatusBar(optionalIgnoreStatusBar);
|
|
45
|
+
shouldIgnoreStatusBar = optionalIgnoreStatusBar;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} catch (e) {
|
|
49
|
+
throw new DetoxRuntimeError(`longPress accepts either a duration (number) or a point ({x: number, y: number}) as ` +
|
|
50
|
+
`its first argument, optionally a duration (number) as its second argument, and optionally a ignoreStatusBar (boolean) as its third argument. Error: ${e.message}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { point, duration, shouldIgnoreStatusBar };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = mapDeviceLongPressArguments;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
c517cf62a949c264ce5aa29dbe7eb0b3
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0fd30160288562361e27f4dae15e88dd8c063321
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
43af12e399f647d3c726f03f03c6d5e9953ab473bc565018672c1dd6f9c23fa6
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
7eb38a3cfc446ee433478167eefe863d2faacfa5b2102685702d83d0b944236d3e4be9d89ad653134402872945bb122db884470d327bf99f7ecd769dacf437bc
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
cf8dd3a0b09c20f6536c73bb9b643c19
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
087bb87ec12653d348901f95966ed16f46f870b2
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
77ee8830fdf301ef8a7895575a4f0f3b76db9614b61942979b73d3b5001fac57
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3eb1ffc33ecae7c14b6ff36a525abacb4aa05f6d05083d2dc79bad26b63badecc5d22d45bf7dc97cc90804e4245db826e14c86d86fa49540be209708a6f854d1
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
b1fa5d53cea5a7597921246ec09f437f
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
d28ba67e5c3648d25e9c592b78803d33a1805ed8
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
9563aa36da9534de48b62cdfc7ff3b14782e260a29fbbff7189a220e1d74eaf7
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
ff322f7d952260e532f7678a490b6672136925f9d77420203d6038cde5a38a2fa2af2db6fbe5ba8005fe3668074228efdca4ea8f2f3e8077fc9386ded37e1642
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
367dd3d7936f459a3d2b84a90a4d4277
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
bc18b82a4beeee1569c3cfae9ccbbd11ab133d21
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
31152c35e9eac0a236cb5d016c3200ecd5181f7639c376f02d04289a06c76930
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
7a911e94257c299c2d560fe6d84ddc2d5264bec7953e39ae64bcafbe5041e43ffbb5bffcfd6c04bcbf62e62b4b764e609e35d37d5e4903266ace32fa214a0726
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
768c2187e364924f92a9b6ccf3c7d0a2
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
7e17fc60050feb2ea5e7dc9a93bf8b2fba13486d
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
a64ce40bbbf3b827d598206f3829f34072b1d6bcd0115d930e7aabadf7477534
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
60d8f60ededcfdd91034b452971b511a40a99a5a4afb42ae6fa88261cf5d29b8493b824eb2e8f453653bf723668b417943177f02fb0dcc9d58501563e7c10b54
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
acec75d4c28b08969fcdf94aff6253d3
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
c9739dec1a97e33efb975a87083b618963b98ed7
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
652c04a47a8ffb644d3e6085070a0b7401f2cb5ab5bc09d5a7a73f21d4a9fb9d
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
913e879197e5d3622aee6c3db2444ce453912f3ab383bebae3b0fd09e518b8c301e377666a13a3359f841307e895094b5a82e6eafda83e69abd43c3f10eec484
|