detox 20.26.3-smoke.0 → 20.27.1
Sign up to get free protection for your applications and to get access to all the features.
- package/Detox-android/com/wix/detox/{20.26.3-smoke.0/detox-20.26.3-smoke.0-sources.jar → 20.27.1/detox-20.27.1-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.aar +0 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.aar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.26.3-smoke.0/detox-20.26.3-smoke.0.pom → 20.27.1/detox-20.27.1.pom} +1 -1
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.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.26.3-smoke.0/detox-legacy-20.26.3-smoke.0-sources.jar → 20.27.1/detox-legacy-20.27.1-sources.jar} +0 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.aar +0 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.aar.md5 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox-legacy/{20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.pom → 20.27.1/detox-legacy-20.27.1.pom} +1 -1
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.pom.md5 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.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/proguard-rules-app.pro +3 -0
- package/android/detox/src/full/java/com/wix/detox/espresso/hierarchy/ViewHierarchyGenerator.kt +86 -21
- package/detox.d.ts +15 -0
- package/globals.d.ts +2 -0
- package/jest.config.js +1 -0
- package/package.json +3 -3
- package/src/DetoxWorker.js +9 -0
- package/src/copilot/DetoxCopilot.js +33 -0
- package/src/copilot/detoxCopilotFrameworkDriver.js +320 -0
- package/src/realms/DetoxContext.js +2 -0
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0.aar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0.pom.md5 +0 -1
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0.pom.sha512 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.aar +0 -0
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.aar.md5 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.pom.md5 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.pom.sha512 +0 -1
- package/src/copilot/CopilotDriver.js +0 -140
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
6a7785c5beade116fb95f224126e3d94
|
@@ -0,0 +1 @@
|
|
1
|
+
282a3d71cfb16689e484ad5b8cbdf8b4ffcc2fd4
|
@@ -0,0 +1 @@
|
|
1
|
+
c3a3ba182f11c386c4ec93281f2cbf630a4315cad97289a0f002972c2fb234c9
|
@@ -0,0 +1 @@
|
|
1
|
+
0babdde000779bbe81fbf61964e4578b2a23ddd8ae8f65aaa48f71e6d3cd1306d473501c44ba95f48416a99a2e5cad8a38ebe85d272616522a0bfe4e09e1878f
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
14161501d9cf4606ee49296e14c047e4
|
@@ -0,0 +1 @@
|
|
1
|
+
38e956edb72453c00c00ab752443f394389737bf
|
@@ -0,0 +1 @@
|
|
1
|
+
0a76b1d5efeea5e83fa9e92e75bc29913b9e7db28b7b30652638966506299f90
|
@@ -0,0 +1 @@
|
|
1
|
+
f3b3510a065744da054200cbcfb62549d469aa27068939879f07c64421cfef223aa34ed6346b6e9c5839a1a42cdc76699b16ad4395545abfb01cd611381df408
|
@@ -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.27.1</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
|
+
4fbbcc9b1460bdaad46d32f629e3d589
|
@@ -0,0 +1 @@
|
|
1
|
+
961629dada06b32a080e9abe7909bb91032de53a
|
@@ -0,0 +1 @@
|
|
1
|
+
322e950b4d47c41096aa1525ae34d866e7955bd3d2d091f4b207158a6f622c9b
|
@@ -0,0 +1 @@
|
|
1
|
+
3117059ac5711d3e06fea0be3a0fcea07f077bc6b7be465a26b7591784da214fadf299c3794eba97fb60bbebced3256943dbb211ac7702132ae9635665d5d788
|
@@ -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.27.1</latest>
|
7
|
+
<release>20.27.1</release>
|
8
8
|
<versions>
|
9
|
-
<version>20.
|
9
|
+
<version>20.27.1</version>
|
10
10
|
</versions>
|
11
|
-
<lastUpdated>
|
11
|
+
<lastUpdated>20240923182824</lastUpdated>
|
12
12
|
</versioning>
|
13
13
|
</metadata>
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
b0663aafff90f42ece492a2bcaf7baec
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
1001cbc58bbfa8763defd8cef091b50046364d02
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
07d7f8da49dbf7ab284acb34967da94bb84c6cea2c8ee0898811a775ae7450fe
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2fb37d07f83c220fac552876664268022630f22a2b51cdb3392655832f3dd24bb712e0ebc2128dde7d72f3cd57599b80d0d021e86737ce69a28fab47bccb69a5
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
d8fa6581305d5677dc8fc505c979afa4
|
@@ -0,0 +1 @@
|
|
1
|
+
0bf9b98aede1b304cf57d489f1e043f6ac28ee84
|
@@ -0,0 +1 @@
|
|
1
|
+
216378cc3328e66e3f7a236cfcdb3672f963f1db10b18499c1ffd8476bcd4b6a
|
@@ -0,0 +1 @@
|
|
1
|
+
9756c8ae3a816b6285ee16774ea82237da03e24c3dd48f9e564c2251abf97afc6b36952df51de07795f78300d61f37c27b74c6c61477e029afb3068dc57ba8f4
|
@@ -0,0 +1 @@
|
|
1
|
+
02d974914d78e64b365486e3369ae1e1
|
@@ -0,0 +1 @@
|
|
1
|
+
173b898bfecd41c34b0ad24d7e1996c8d3b0621b
|
@@ -0,0 +1 @@
|
|
1
|
+
8a30f740e17ad80816f17ceb61b354d7da12f651764e040ddf85b1c3609992a3
|
@@ -0,0 +1 @@
|
|
1
|
+
90f61852b868773b3548daf4c04793cc0285df750cbfb8947375ec81f5882469cf6e6132295e3b115b27d61733c1bb5f4954a14e7246aa8da19e97bd28191c5f
|
@@ -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.27.1</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
|
+
1d711e19c5856f3987b3f2821d591b68
|
@@ -0,0 +1 @@
|
|
1
|
+
a522dde967fb79aed046806088ecb7457f0e938b
|
@@ -0,0 +1 @@
|
|
1
|
+
7b9667b2fa6ca43e92d90c1b0e2c1054c11458fd4d72730edf45c793672f726c
|
@@ -0,0 +1 @@
|
|
1
|
+
00d73fa19f105dc273bd44b52c02b73c8eeb70405f61b6bc3ad8a19c640f7b28c829ddbd34fd9c052ed9decec7d199855dcd2eb5f0a191fee2dfc789f1a8ae93
|
@@ -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.27.1</latest>
|
7
|
+
<release>20.27.1</release>
|
8
8
|
<versions>
|
9
|
-
<version>20.
|
9
|
+
<version>20.27.1</version>
|
10
10
|
</versions>
|
11
|
-
<lastUpdated>
|
11
|
+
<lastUpdated>20240923182941</lastUpdated>
|
12
12
|
</versioning>
|
13
13
|
</metadata>
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
fd1de26f13a6099dc0ea8653c61cd67d
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
ec4629f0d73e338d6ed23b0cad15f2e54b7ade64
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
84679287bb5c654b7ca9f5c860712d5d327f17670b89dfae4df2e686ee142899
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
1d231b1ce95ce6608a20498393cfc9c005598bcbe78da9c3595679e315b83a0c41bc93b54be0632a5183a8e233ebe758f1a1fa5d233c5e9b1f532b24e5ce9c7f
|
package/Detox-ios-framework.tbz
CHANGED
Binary file
|
package/Detox-ios-src.tbz
CHANGED
Binary file
|
package/Detox-ios-xcuitest.tbz
CHANGED
Binary file
|
@@ -16,6 +16,9 @@
|
|
16
16
|
|
17
17
|
-keep class kotlin.reflect.** { *; }
|
18
18
|
-keep class kotlin.coroutines.CoroutineDispatcher { *; }
|
19
|
+
-keep class kotlin.coroutines.CoroutineScope { *; }
|
20
|
+
-keep class kotlin.coroutines.CoroutineContext { *; }
|
21
|
+
-keep class kotlinx.coroutines.BuildersKt { *; }
|
19
22
|
-keep class kotlin.jvm.** { *; }
|
20
23
|
-keep class kotlin.collections.** { *; }
|
21
24
|
-keep class kotlin.text.** { *; }
|
package/android/detox/src/full/java/com/wix/detox/espresso/hierarchy/ViewHierarchyGenerator.kt
CHANGED
@@ -3,19 +3,53 @@ package com.wix.detox.espresso.hierarchy
|
|
3
3
|
import android.util.Xml
|
4
4
|
import android.view.View
|
5
5
|
import android.view.ViewGroup
|
6
|
+
import android.webkit.WebView
|
6
7
|
import android.widget.TextView
|
7
8
|
import com.wix.detox.reactnative.ui.getAccessibilityLabel
|
9
|
+
import kotlinx.coroutines.Dispatchers
|
10
|
+
import kotlinx.coroutines.runBlocking
|
11
|
+
import kotlinx.coroutines.suspendCancellableCoroutine
|
12
|
+
import kotlinx.coroutines.withContext
|
8
13
|
import org.xmlpull.v1.XmlSerializer
|
9
14
|
import java.io.StringWriter
|
15
|
+
import kotlin.coroutines.resume
|
16
|
+
|
17
|
+
|
18
|
+
private const val GET_HTML_SCRIPT = """
|
19
|
+
const blacklistedTags = ['script', 'style', 'head', 'meta'];
|
20
|
+
const blackListedTagsSelector = blacklistedTags.join(',');
|
21
|
+
|
22
|
+
(function() {
|
23
|
+
// Clone the entire document
|
24
|
+
var clonedDoc = document.documentElement.cloneNode(true);
|
25
|
+
|
26
|
+
// Remove all <script> and <style> tags from the cloned document
|
27
|
+
var scripts = clonedDoc.querySelectorAll(blackListedTagsSelector);
|
28
|
+
scripts.forEach(function(script) {
|
29
|
+
script.remove();
|
30
|
+
});
|
31
|
+
|
32
|
+
// Create an instance of XMLSerializer
|
33
|
+
var serializer = new XMLSerializer();
|
34
|
+
|
35
|
+
// Serialize the cloned DOM to a string
|
36
|
+
var serializedHtml = serializer.serializeToString(clonedDoc);
|
37
|
+
|
38
|
+
// Return the serialized HTML as a string
|
39
|
+
return serializedHtml;
|
40
|
+
})();
|
41
|
+
"""
|
10
42
|
|
11
43
|
object ViewHierarchyGenerator {
|
12
44
|
@JvmStatic
|
13
45
|
fun generateXml(shouldInjectTestIds: Boolean): String {
|
14
|
-
|
15
|
-
|
46
|
+
return runBlocking {
|
47
|
+
val rootViews = RootViewsHelper.getRootViews()
|
48
|
+
generateXmlFromViews(rootViews, shouldInjectTestIds)
|
49
|
+
}
|
16
50
|
}
|
17
51
|
|
18
|
-
private fun generateXmlFromViews(rootViews: List<View?>?, shouldInjectTestIds: Boolean): String {
|
52
|
+
private suspend fun generateXmlFromViews(rootViews: List<View?>?, shouldInjectTestIds: Boolean): String {
|
19
53
|
return StringWriter().use { writer ->
|
20
54
|
val serializer = Xml.newSerializer().apply {
|
21
55
|
setOutput(writer)
|
@@ -39,7 +73,7 @@ object ViewHierarchyGenerator {
|
|
39
73
|
}
|
40
74
|
}
|
41
75
|
|
42
|
-
private fun serializeViewHierarchy(
|
76
|
+
private suspend fun serializeViewHierarchy(
|
43
77
|
view: View,
|
44
78
|
serializer: XmlSerializer,
|
45
79
|
shouldInjectTestIds: Boolean,
|
@@ -48,20 +82,59 @@ object ViewHierarchyGenerator {
|
|
48
82
|
serializer.startTag("", view.javaClass.simpleName)
|
49
83
|
serializeViewAttributes(view, serializer, shouldInjectTestIds, indexPath)
|
50
84
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
view.getChildAt(i),
|
55
|
-
serializer,
|
56
|
-
shouldInjectTestIds,
|
57
|
-
indexPath + i
|
58
|
-
)
|
59
|
-
}
|
85
|
+
when (view) {
|
86
|
+
is WebView -> serializeWebView(view, serializer)
|
87
|
+
is ViewGroup -> serializeViewGroupChildren(view, serializer, shouldInjectTestIds, indexPath)
|
60
88
|
}
|
61
89
|
|
62
90
|
serializer.endTag("", view.javaClass.simpleName)
|
63
91
|
}
|
64
92
|
|
93
|
+
private suspend fun serializeWebView(
|
94
|
+
webView: WebView,
|
95
|
+
serializer: XmlSerializer,
|
96
|
+
) {
|
97
|
+
val html = getWebViewHtml(webView)
|
98
|
+
serializer.cdsect(html)
|
99
|
+
}
|
100
|
+
|
101
|
+
private suspend fun getWebViewHtml(webView: WebView): String = withContext(Dispatchers.Main) {
|
102
|
+
suspendCancellableCoroutine { cancellableContinuation ->
|
103
|
+
webView.evaluateJavascript(GET_HTML_SCRIPT) { html ->
|
104
|
+
cancellableContinuation.resume(html.unescapeUnicodeString())
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
private fun String.unescapeUnicodeString(): String {
|
110
|
+
// Replace all Unicode escape sequences (e.g., \u003C -> <)
|
111
|
+
return this
|
112
|
+
.replace("\\u003C", "<")
|
113
|
+
.replace("\\u003E", ">")
|
114
|
+
.replace("\\u0022", "\"")
|
115
|
+
.replace("\\u0027", "'")
|
116
|
+
.replace("\\u0026", "&")
|
117
|
+
.replace("\\u003D", "=")
|
118
|
+
.replace("\\u002F", "/")
|
119
|
+
.replace("\\n", "\n")
|
120
|
+
}
|
121
|
+
|
122
|
+
private suspend fun serializeViewGroupChildren(
|
123
|
+
view: ViewGroup,
|
124
|
+
serializer: XmlSerializer,
|
125
|
+
shouldInjectTestIds: Boolean,
|
126
|
+
indexPath: List<Int>
|
127
|
+
) {
|
128
|
+
for (i in 0 until view.childCount) {
|
129
|
+
serializeViewHierarchy(
|
130
|
+
view.getChildAt(i),
|
131
|
+
serializer,
|
132
|
+
shouldInjectTestIds,
|
133
|
+
indexPath + i
|
134
|
+
)
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
65
138
|
private fun serializeViewAttributes(
|
66
139
|
view: View,
|
67
140
|
serializer: XmlSerializer,
|
@@ -79,14 +152,6 @@ object ViewHierarchyGenerator {
|
|
79
152
|
"label" to (view.getAccessibilityLabel()?.toString() ?: "")
|
80
153
|
)
|
81
154
|
|
82
|
-
view.id.takeIf { it != View.NO_ID }?.let {
|
83
|
-
attributes["id"] = try {
|
84
|
-
view.resources.getResourceName(it)
|
85
|
-
} catch (e: Exception) {
|
86
|
-
it.toString()
|
87
|
-
}
|
88
|
-
}
|
89
|
-
|
90
155
|
val location = IntArray(2).apply { view.getLocationInWindow(this) }
|
91
156
|
attributes["x"] = location[0].toString()
|
92
157
|
attributes["y"] = location[1].toString()
|
package/detox.d.ts
CHANGED
@@ -9,6 +9,7 @@
|
|
9
9
|
// * Dor Ben Baruch <https://github.com/Dor256>
|
10
10
|
|
11
11
|
import { BunyanDebugStreamOptions } from 'bunyan-debug-stream';
|
12
|
+
import { CopilotFacade, PromptHandler } from "detox-copilot";
|
12
13
|
|
13
14
|
declare global {
|
14
15
|
namespace Detox {
|
@@ -445,6 +446,8 @@ declare global {
|
|
445
446
|
|
446
447
|
readonly system: SystemFacade;
|
447
448
|
|
449
|
+
readonly copilot: DetoxCopilotFacade;
|
450
|
+
|
448
451
|
readonly DetoxConstants: {
|
449
452
|
userNotificationTriggers: {
|
450
453
|
push: 'push';
|
@@ -1287,6 +1290,18 @@ declare global {
|
|
1287
1290
|
element(systemMatcher: SystemMatcher): IndexableSystemElement;
|
1288
1291
|
}
|
1289
1292
|
|
1293
|
+
interface DetoxCopilotFacade extends Pick<CopilotFacade, "perform"> {
|
1294
|
+
/**
|
1295
|
+
* Initializes the Copilot with the given prompt handler.
|
1296
|
+
* Must be called before any other Copilot methods.
|
1297
|
+
* @note Copilot APIs are still in experimental phase and are subject to changes in the near future.
|
1298
|
+
* @param promptHandler The prompt handler to use.
|
1299
|
+
*/
|
1300
|
+
init: (promptHandler: DetoxCopilotPromptHandler) => void;
|
1301
|
+
}
|
1302
|
+
|
1303
|
+
interface DetoxCopilotPromptHandler extends PromptHandler {}
|
1304
|
+
|
1290
1305
|
interface IndexableSystemElement extends SystemElement {
|
1291
1306
|
/**
|
1292
1307
|
* Choose from multiple elements matching the same matcher using index
|
package/globals.d.ts
CHANGED
@@ -9,6 +9,7 @@ declare global {
|
|
9
9
|
const by: Detox.DetoxExportWrapper['by'];
|
10
10
|
const web: Detox.DetoxExportWrapper['web'];
|
11
11
|
const system: Detox.DetoxExportWrapper['system'];
|
12
|
+
const copilot: Detox.DetoxExportWrapper['copilot'];
|
12
13
|
|
13
14
|
namespace NodeJS {
|
14
15
|
interface Global {
|
@@ -20,6 +21,7 @@ declare global {
|
|
20
21
|
by: Detox.DetoxExportWrapper['by'];
|
21
22
|
web: Detox.DetoxExportWrapper['web'];
|
22
23
|
system: Detox.DetoxExportWrapper['system'];
|
24
|
+
copilot: Detox.DetoxExportWrapper['copilot'];
|
23
25
|
}
|
24
26
|
}
|
25
27
|
}
|
package/jest.config.js
CHANGED
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.27.1",
|
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.8",
|
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": "a45478f98e0ad2d64327923ce191c314d8c27596"
|
120
120
|
}
|
package/src/DetoxWorker.js
CHANGED
@@ -2,6 +2,7 @@ const CAF = require('caf');
|
|
2
2
|
const _ = require('lodash');
|
3
3
|
|
4
4
|
const Client = require('./client/Client');
|
5
|
+
const DetoxCopilot = require('./copilot/DetoxCopilot');
|
5
6
|
const environmentFactory = require('./environmentFactory');
|
6
7
|
const { DetoxRuntimeErrorComposer } = require('./errors');
|
7
8
|
const { InvocationManager } = require('./invoke');
|
@@ -58,6 +59,8 @@ class DetoxWorker {
|
|
58
59
|
this.by = null;
|
59
60
|
/** @type {Detox.WebFacade} */
|
60
61
|
this.web = null;
|
62
|
+
/** @type {Detox.DetoxCopilotFacade} */
|
63
|
+
this.copilot = null;
|
61
64
|
|
62
65
|
this._deviceCookie = null;
|
63
66
|
|
@@ -121,6 +124,8 @@ class DetoxWorker {
|
|
121
124
|
runtimeDeviceFactory,
|
122
125
|
} = environmentFactory.createFactories(deviceConfig);
|
123
126
|
|
127
|
+
this.copilot = new DetoxCopilot();
|
128
|
+
|
124
129
|
const envValidator = envValidatorFactory.createValidator();
|
125
130
|
yield envValidator.validate();
|
126
131
|
|
@@ -157,6 +162,7 @@ class DetoxWorker {
|
|
157
162
|
const injectedGlobals = {
|
158
163
|
...matchers,
|
159
164
|
device: this.device,
|
165
|
+
copilot: this.copilot,
|
160
166
|
detox: this,
|
161
167
|
};
|
162
168
|
|
@@ -219,6 +225,9 @@ class DetoxWorker {
|
|
219
225
|
};
|
220
226
|
|
221
227
|
onTestStart = function* (_signal, testSummary) {
|
228
|
+
// Copilot is reset before each test to ensure a clean state
|
229
|
+
this.copilot.resetIfNeeded();
|
230
|
+
|
222
231
|
this._validateTestSummary('beforeEach', testSummary);
|
223
232
|
|
224
233
|
yield this._dumpUnhandledErrorsIfAny({
|
@@ -0,0 +1,33 @@
|
|
1
|
+
const copilot = require('detox-copilot').default;
|
2
|
+
|
3
|
+
const detoxCopilotFrameworkDriver = require('./detoxCopilotFrameworkDriver');
|
4
|
+
|
5
|
+
class DetoxCopilot {
|
6
|
+
constructor() {
|
7
|
+
this.isInitialized = false;
|
8
|
+
}
|
9
|
+
|
10
|
+
init(promptHandler) {
|
11
|
+
copilot.init({
|
12
|
+
frameworkDriver: detoxCopilotFrameworkDriver,
|
13
|
+
promptHandler: promptHandler
|
14
|
+
});
|
15
|
+
|
16
|
+
this.isInitialized = true;
|
17
|
+
}
|
18
|
+
|
19
|
+
resetIfNeeded() {
|
20
|
+
if (!this.isInitialized) {
|
21
|
+
// Copilot is not initialized, nothing to reset
|
22
|
+
return;
|
23
|
+
}
|
24
|
+
|
25
|
+
copilot.reset();
|
26
|
+
}
|
27
|
+
|
28
|
+
perform(intent) {
|
29
|
+
return copilot.perform(intent);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
module.exports = DetoxCopilot;
|
@@ -0,0 +1,320 @@
|
|
1
|
+
// eslint-disable-next-line node/no-extraneous-require
|
2
|
+
const jestExpect = require('expect').default;
|
3
|
+
|
4
|
+
const detox = require('../..');
|
5
|
+
|
6
|
+
const detoxCopilotFrameworkDriver = {
|
7
|
+
apiCatalog: {
|
8
|
+
context: { ...detox, jestExpect },
|
9
|
+
categories: [
|
10
|
+
{
|
11
|
+
title: 'Matchers',
|
12
|
+
items: [
|
13
|
+
{
|
14
|
+
signature: 'by.id(id: string)',
|
15
|
+
description: 'Matches elements by their test ID.',
|
16
|
+
example: "element(by.id('loginButton'))",
|
17
|
+
guidelines: ['Always use test-ids (accessibility identifiers) from the UI hierarchy to identify elements.'],
|
18
|
+
},
|
19
|
+
{
|
20
|
+
signature: 'by.text(text: string)',
|
21
|
+
description: 'Matches elements by their text.',
|
22
|
+
example: "element(by.text('Login'))",
|
23
|
+
guidelines: ['Avoid using text matchers when possible; prefer test-ids.'],
|
24
|
+
},
|
25
|
+
{
|
26
|
+
signature: 'by.type(type: string)',
|
27
|
+
description: 'Matches elements by their type.',
|
28
|
+
example: "element(by.type('RCTTextInput'))",
|
29
|
+
guidelines: ['Use type matchers as a last resort; prefer test-ids.'],
|
30
|
+
},
|
31
|
+
{
|
32
|
+
signature: 'atIndex(index: number)',
|
33
|
+
description: 'Selects the element at the specified index from a set of matched elements.',
|
34
|
+
example: "element(by.id('listItem')).atIndex(2)",
|
35
|
+
guidelines: ['Use `atIndex` when multiple elements match the same matcher to select a specific one by index.'],
|
36
|
+
},
|
37
|
+
],
|
38
|
+
},
|
39
|
+
{
|
40
|
+
title: 'Actions',
|
41
|
+
items: [
|
42
|
+
{
|
43
|
+
signature: 'tap(point?: Point2D)',
|
44
|
+
description: 'Simulates tap on an element.',
|
45
|
+
example: "await element(by.id('loginButton')).tap();",
|
46
|
+
guidelines: ["Use `element(by.id('testID'))` to locate elements."],
|
47
|
+
},
|
48
|
+
{
|
49
|
+
signature: 'longPress(point?: Point2D, duration?: number)',
|
50
|
+
description: 'Simulates long press on an element.',
|
51
|
+
example: "await element(by.id('menuItem')).longPress();",
|
52
|
+
guidelines: [
|
53
|
+
'If the target element is not accessible, interact with its container or the most relevant parent element.',
|
54
|
+
'Long-press should be called with the relevant params only, e.g. `longPress(2000)`, `longPress({ x: 100, y: 200 })` or `longPress({ x: 100, y: 200 }, 2000)`.',
|
55
|
+
],
|
56
|
+
},
|
57
|
+
{
|
58
|
+
signature: 'multiTap(times: number)',
|
59
|
+
description: 'Simulates multiple taps on an element.',
|
60
|
+
example: "await element(by.id('tappable')).multiTap(3);",
|
61
|
+
guidelines: ['All taps are applied as part of the same gesture.'],
|
62
|
+
},
|
63
|
+
{
|
64
|
+
signature: 'typeText(text: string)',
|
65
|
+
description: 'Types text into a text field.',
|
66
|
+
example: "await element(by.id('usernameInput')).typeText('myusername');",
|
67
|
+
guidelines: ['Typing can only be done on text field elements.'],
|
68
|
+
},
|
69
|
+
{
|
70
|
+
signature: 'replaceText(text: string)',
|
71
|
+
description: 'Replaces text in a text field.',
|
72
|
+
example: "await element(by.id('textField')).replaceText('new text');",
|
73
|
+
guidelines: ['Faster than `typeText()`, but may not trigger all text input callbacks.'],
|
74
|
+
},
|
75
|
+
{
|
76
|
+
signature: 'clearText()',
|
77
|
+
description: 'Clears text from a text field.',
|
78
|
+
example: "await element(by.id('textField')).clearText();",
|
79
|
+
guidelines: ['Use this to clear text from input fields.'],
|
80
|
+
},
|
81
|
+
{
|
82
|
+
signature: 'tapReturnKey()',
|
83
|
+
description: 'Simulates tapping the return key on the keyboard while the element is focused.',
|
84
|
+
example: "await element(by.id('textField')).tapReturnKey();",
|
85
|
+
guidelines: ['Use this to simulate pressing the return key while typing into a text input field.'],
|
86
|
+
},
|
87
|
+
{
|
88
|
+
signature: 'tapBackspaceKey()',
|
89
|
+
description: 'Simulates tapping the backspace key on the keyboard while the element is focused.',
|
90
|
+
example: "await element(by.id('textField')).tapBackspaceKey();",
|
91
|
+
guidelines: ['Use this to simulate deleting text by pressing the backspace key in a text input field.'],
|
92
|
+
},
|
93
|
+
{
|
94
|
+
signature: 'adjustSliderToPosition(normalizedPosition: number)',
|
95
|
+
description: 'Adjusts the slider to a specified position between its minimum and maximum values.',
|
96
|
+
example: "await element(by.id('slider')).adjustSliderToPosition(0.75);",
|
97
|
+
guidelines: ['The position is a normalized value between 0 and 1, where 0 is minimum and 1 is maximum.'],
|
98
|
+
},
|
99
|
+
{
|
100
|
+
signature: 'scroll(offset: number, direction: string, startPositionX?: number, startPositionY?: number)',
|
101
|
+
description: 'Scrolls an element.',
|
102
|
+
example: "await element(by.id('scrollView')).scroll(100, 'down');",
|
103
|
+
guidelines: ['Specify direction as "up", "down", "left", or "right".'],
|
104
|
+
},
|
105
|
+
{
|
106
|
+
signature: 'scrollTo(edge: string)',
|
107
|
+
description: 'Scrolls to an edge of the element.',
|
108
|
+
example: "await element(by.id('scrollView')).scrollTo('bottom');",
|
109
|
+
guidelines: ['Specify edge as "top", "bottom", "left", or "right".'],
|
110
|
+
},
|
111
|
+
{
|
112
|
+
signature: 'scrollToIndex(index: number)',
|
113
|
+
description: 'Scrolls the element to the specified index. (Android only)',
|
114
|
+
example: "await element(by.id('scrollView')).scrollToIndex(10);",
|
115
|
+
guidelines: ['Use this to scroll to a specific item in a list. Only available on Android.'],
|
116
|
+
},
|
117
|
+
{
|
118
|
+
signature: 'swipe(direction: string, speed?: string, normalizedOffset?: number)',
|
119
|
+
description: 'Simulates a swipe on the element.',
|
120
|
+
example: "await element(by.id('scrollView')).swipe('up', 'slow', 0.5);",
|
121
|
+
guidelines: [
|
122
|
+
'Specify direction as "up", "down", "left", or "right".',
|
123
|
+
'Speed can be "fast", "slow". default is "fast".',
|
124
|
+
],
|
125
|
+
},
|
126
|
+
{
|
127
|
+
signature: 'setColumnToValue(column: number, value: string)',
|
128
|
+
description: 'Sets a picker column to a specific value (iOS only).',
|
129
|
+
example: "await element(by.id('pickerView')).setColumnToValue(1, '6');",
|
130
|
+
guidelines: ['Use this for picker views on iOS.'],
|
131
|
+
},
|
132
|
+
{
|
133
|
+
signature: 'setDatePickerDate(dateString: string, dateFormat: string)',
|
134
|
+
description: 'Sets a date picker to a specific date.',
|
135
|
+
example: "await element(by.id('datePicker')).setDatePickerDate('2023-05-25', 'yyyy-MM-dd');",
|
136
|
+
guidelines: ['Use ISO8601 format when possible.'],
|
137
|
+
},
|
138
|
+
{
|
139
|
+
signature: 'performAccessibilityAction(actionName: string)',
|
140
|
+
description: 'Triggers an accessibility action.',
|
141
|
+
example: "await element(by.id('scrollView')).performAccessibilityAction('activate');",
|
142
|
+
guidelines: ['Use this to trigger specific accessibility actions.'],
|
143
|
+
},
|
144
|
+
{
|
145
|
+
signature: 'pinch(scale: number, speed?: string, angle?: number)',
|
146
|
+
description: 'Simulates a pinch gesture.',
|
147
|
+
example: "await element(by.id('PinchableScrollView')).pinch(1.1);",
|
148
|
+
guidelines: ['Use scale < 1 to zoom out, > 1 to zoom in.'],
|
149
|
+
},
|
150
|
+
|
151
|
+
{
|
152
|
+
signature: 'getAttributes()',
|
153
|
+
description: `
|
154
|
+
Retrieves various attributes of the element.
|
155
|
+
|
156
|
+
**Attributes include:**
|
157
|
+
- **Common**: text (string), label (string), placeholder (string), enabled (boolean), identifier (string), visible (boolean), value (string | number | boolean), frame (object: x (number), y (number), width (number), height (number))
|
158
|
+
- **iOS-only**: activationPoint (object: x (number), y (number)), normalizedActivationPoint (object: x (number), y (number)), hittable (boolean), elementFrame (object: x (number), y (number), width (number), height (number)), elementBounds (object: x (number), y (number), width (number), height (number)), safeAreaInsets (object: top (number), bottom (number), left (number), right (number)), elementSafeBounds (object: x (number), y (number), width (number), height (number)), date (Date), normalizedSliderPosition (number), contentOffset (object: x (number), y (number)), contentInset (object: top (number), bottom (number), left (number), right (number)), adjustedContentInset (object: top (number), bottom (number), left (number), right (number)))
|
159
|
+
- **Android-only**: visibility (string: 'visible', 'invisible', 'gone'), width (number) *(deprecated)*, height (number) *(deprecated)*, elevation (number), alpha (number), focused (boolean), textSize (number), length (number)
|
160
|
+
|
161
|
+
*Note:* Attributes may vary based on the platform and element type. If an attribute's value is null or cannot be computed, the key might be absent or contain an empty string.
|
162
|
+
`,
|
163
|
+
example: `
|
164
|
+
// Retrieve attributes of an element
|
165
|
+
const attributes = await element(by.text('Tap Me')).getAttributes();
|
166
|
+
jestExpect(attributes.text).toBe('Tap Me');
|
167
|
+
|
168
|
+
// Numerical assertions with allowed error range
|
169
|
+
jestExpect(attributes.frame.x).toBeCloseTo(100, 1);
|
170
|
+
jestExpect(attributes.frame.y).toBeCloseTo(200, 1);
|
171
|
+
|
172
|
+
// Platform-specific attribute check
|
173
|
+
if (device.getPlatform() === 'ios') {
|
174
|
+
jestExpect(attributes.hittable).toBe(true);
|
175
|
+
} else if (device.getPlatform() === 'android') {
|
176
|
+
jestExpect(attributes.visibility).toBe('visible');
|
177
|
+
}
|
178
|
+
`,
|
179
|
+
guidelines: [
|
180
|
+
'Use this to get properties like text, value, visibility, etc., for assertions or debugging. But only if the regular matchers or assertions are not sufficient.',
|
181
|
+
'Note that numerical values like position or size may not be very accurate; consider allowing a small error range in assertions.',
|
182
|
+
'Check the platform using `device.getPlatform()` before using platform-specific attributes.',
|
183
|
+
'Attributes include text, label, placeholder, enabled, identifier, visible, value, frame (with x, y, width, height), and platform-specific attributes.',
|
184
|
+
],
|
185
|
+
},
|
186
|
+
{
|
187
|
+
signature: 'takeScreenshot(name: string)',
|
188
|
+
description: 'Captures a screenshot of the element.',
|
189
|
+
example: "const imagePath = await element(by.id('menuRoot')).takeScreenshot('menu_screenshot');",
|
190
|
+
guidelines: ['Use this to capture screenshots of elements for documentation or debugging purposes.'],
|
191
|
+
},
|
192
|
+
{
|
193
|
+
signature: 'longPressAndDrag(duration: number, normalizedPositionX: number, normalizedPositionY: number, targetElement: NativeElement, normalizedTargetPositionX?: number, normalizedTargetPositionY?: number, speed?: string, holdDuration?: number)',
|
194
|
+
description: 'Simulates a long press on the element and then drags it to a target element.',
|
195
|
+
example: "await element(by.id('draggable')).longPressAndDrag(2000, NaN, NaN, element(by.id('target')), NaN, NaN, 'fast', 0);",
|
196
|
+
guidelines: ['Use this to simulate drag-and-drop interactions between elements.'],
|
197
|
+
},
|
198
|
+
{
|
199
|
+
signature: 'launchApp(params: object)',
|
200
|
+
description: 'Launches the app with specified parameters.',
|
201
|
+
example: 'await device.launchApp({newInstance: true});',
|
202
|
+
guidelines: ['Use this to launch the app with specific configurations.'],
|
203
|
+
},
|
204
|
+
{
|
205
|
+
signature: 'reloadReactNative()',
|
206
|
+
description: 'Reloads the React Native JS bundle.',
|
207
|
+
example: 'await device.reloadReactNative();',
|
208
|
+
guidelines: ['Faster than `launchApp()`, use when you just need to reset React Native state/logic.'],
|
209
|
+
},
|
210
|
+
],
|
211
|
+
},
|
212
|
+
{
|
213
|
+
title: 'Assertions',
|
214
|
+
items: [
|
215
|
+
{
|
216
|
+
signature: 'toBeVisible()',
|
217
|
+
description: 'Asserts that the element is visible.',
|
218
|
+
example: "await expect(element(by.id('loginButton'))).toBeVisible();",
|
219
|
+
guidelines: ['Use this to check if an element is visible on the screen.'],
|
220
|
+
},
|
221
|
+
{
|
222
|
+
signature: 'toExist()',
|
223
|
+
description: 'Asserts that the element exists.',
|
224
|
+
example: "await expect(element(by.id('username'))).toExist();",
|
225
|
+
guidelines: ['Use this to check if an element exists in the hierarchy, even if not visible.'],
|
226
|
+
},
|
227
|
+
{
|
228
|
+
signature: 'toHaveText(text: string)',
|
229
|
+
description: 'Asserts that the element has the specified text.',
|
230
|
+
example: "await expect(element(by.id('label'))).toHaveText('Hello, World!');",
|
231
|
+
guidelines: ['Use this to check the text content of an element.'],
|
232
|
+
},
|
233
|
+
{
|
234
|
+
signature: 'toHaveValue(value: string)',
|
235
|
+
description: 'Asserts that the element has the specified value.',
|
236
|
+
example: "await expect(element(by.id('slider'))).toHaveValue('0.5');",
|
237
|
+
guidelines: ['Use this to check the value of an element.'],
|
238
|
+
},
|
239
|
+
{
|
240
|
+
signature: 'toBeFocused()',
|
241
|
+
description: 'Asserts that the element is focused.',
|
242
|
+
example: "await expect(element(by.id('emailInput'))).toBeFocused();",
|
243
|
+
guidelines: ['Use this to check if an element is currently focused.'],
|
244
|
+
},
|
245
|
+
{
|
246
|
+
signature: 'toHaveLabel(label: string)',
|
247
|
+
description: 'Asserts that the element has the specified accessibility label.',
|
248
|
+
example: "await expect(element(by.id('submitButton'))).toHaveLabel('Submit');",
|
249
|
+
guidelines: [
|
250
|
+
'Use this to check the accessibility label of an element. Note that in React Native, the `accessibilityLabel` prop may behave differently on iOS and Android.',
|
251
|
+
],
|
252
|
+
},
|
253
|
+
{
|
254
|
+
signature: 'toHaveId(id: string)',
|
255
|
+
description: 'Asserts that the element has the specified accessibility identifier.',
|
256
|
+
example: "await expect(element(by.text('Submit'))).toHaveId('submitButton');",
|
257
|
+
guidelines: ['Use this to check the testID/accessibility identifier of an element.'],
|
258
|
+
},
|
259
|
+
{
|
260
|
+
signature: 'toHaveSliderPosition(normalizedPosition: number, tolerance?: number)',
|
261
|
+
description:
|
262
|
+
'Asserts that the slider element has the specified normalized position [0, 1], within an optional tolerance.',
|
263
|
+
example:
|
264
|
+
"await expect(element(by.id('slider'))).toHaveSliderPosition(0.75);\nawait expect(element(by.id('slider'))).toHaveSliderPosition(0.3113, 0.00001);",
|
265
|
+
guidelines: ['Use this to verify the slider\'s position. Normalized position is between 0 and 1.'],
|
266
|
+
},
|
267
|
+
{
|
268
|
+
signature: 'toHaveToggleValue(value: boolean)',
|
269
|
+
description: 'Asserts that a toggle-able element is on/checked or off/unchecked.',
|
270
|
+
example:
|
271
|
+
"await expect(element(by.id('switch'))).toHaveToggleValue(true);\nawait expect(element(by.id('checkbox'))).toHaveToggleValue(false);",
|
272
|
+
guidelines: ['Use this to check the state of toggleable elements.'],
|
273
|
+
},
|
274
|
+
{
|
275
|
+
signature: 'withTimeout(timeout: number)',
|
276
|
+
description:
|
277
|
+
'Waits until the expectation is resolved for the specified amount of time.',
|
278
|
+
example:
|
279
|
+
"await waitFor(element(by.id('bigButton'))).toBeVisible().withTimeout(2000);",
|
280
|
+
guidelines: ['Use this to set a custom timeout for an expectation.'],
|
281
|
+
},
|
282
|
+
{
|
283
|
+
signature: 'not',
|
284
|
+
description: 'Negates the expectation.',
|
285
|
+
example:
|
286
|
+
"await expect(element(by.id('tinyButton'))).not.toBeVisible();\nawait expect(element(by.id('tinyButton'))).not.toExist();",
|
287
|
+
guidelines: ["Use 'not' to negate an expectation."],
|
288
|
+
},
|
289
|
+
],
|
290
|
+
},
|
291
|
+
{
|
292
|
+
title: 'Utilities',
|
293
|
+
items: [
|
294
|
+
{
|
295
|
+
signature: 'jestExpect',
|
296
|
+
description: 'Jest expect utility for jest-assisted assertions. It is already imported in the environment.',
|
297
|
+
example: `
|
298
|
+
// Use jestExpect for assertions
|
299
|
+
jestExpect(2 + 2).toBe(4);
|
300
|
+
jestExpect('hello').toBe('hello');
|
301
|
+
jestExpect(true).toBeTruthy();
|
302
|
+
`,
|
303
|
+
guidelines: ['Use jestExpect for assertions in tests, only when the default expect is not helpful for the specific case.'],
|
304
|
+
},
|
305
|
+
],
|
306
|
+
}
|
307
|
+
],
|
308
|
+
},
|
309
|
+
|
310
|
+
captureSnapshotImage: async function () {
|
311
|
+
const fileName = `snapshot_${Date.now()}.png`;
|
312
|
+
return await detox.device.takeScreenshot(fileName);
|
313
|
+
},
|
314
|
+
|
315
|
+
captureViewHierarchyString: async function () {
|
316
|
+
return detox.device.generateViewHierarchyXml();
|
317
|
+
},
|
318
|
+
};
|
319
|
+
|
320
|
+
module.exports = detoxCopilotFrameworkDriver;
|
@@ -1 +0,0 @@
|
|
1
|
-
940ec659b88e4b038b31f9525577b142
|
@@ -1 +0,0 @@
|
|
1
|
-
f2c62acc0bf957056471cbeeda8ef61a2bda1d16
|
package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0-sources.jar.sha256
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
87a3c3959cbfdfe97752011c3d875be0cc8617a382d7413c44cbf54cfb8a9633
|
package/Detox-android/com/wix/detox/20.26.3-smoke.0/detox-20.26.3-smoke.0-sources.jar.sha512
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ed9fb83b4eb9c70641132da18c4c238263666f18dfe3d0dc67f7e3236b60637aec516aeebc7196af797f829c093dbf25167d5701d2a345422345bca7ec1bc0a9
|
Binary file
|
@@ -1 +0,0 @@
|
|
1
|
-
19245568a5b1fc7e18eae4c34019b2d0
|
@@ -1 +0,0 @@
|
|
1
|
-
dfb1a4d4b6a06bbe8952fe821ab3278364fc3cf2
|
@@ -1 +0,0 @@
|
|
1
|
-
294b99bfcfc4acf89c10c76c4cfb07b61dec92d8f45a2ed137092aa766d7e215
|
@@ -1 +0,0 @@
|
|
1
|
-
546fa7e195d2c2077d9e41f2b003ead174c820ce45027752e86a366e673023cc41d09b5b5ccd3bdb693f403e6c8b65567af12208ece66e65d85937e9a65f677d
|
@@ -1 +0,0 @@
|
|
1
|
-
cbd939ff8e445e0c1caa7a3d061e38d3
|
@@ -1 +0,0 @@
|
|
1
|
-
02aaa6639fc2adf1b17bdb38fc6dd3892ea16169
|
@@ -1 +0,0 @@
|
|
1
|
-
f7c5d3017f74d9864b679a1625710762cff073ac5c983af7f55473ddeec7c8f6
|
@@ -1 +0,0 @@
|
|
1
|
-
95cfa1ae0feb18779349b53698e3678ae84319e68594b72ff582711fe936541ea8bb516ccc6636ebae7112b3987a8f85c915de81e802997c1ae3eaa0628798a6
|
@@ -1 +0,0 @@
|
|
1
|
-
86db18d1e8a5094c69622facab32eef9
|
@@ -1 +0,0 @@
|
|
1
|
-
0dfb34c3b85a1e010a80b793cedce11e51ab8573
|
@@ -1 +0,0 @@
|
|
1
|
-
9c9e4ac76f9cdcbd566c81f83d58ec1d69f69b84312e31c807c7282d9fa275d0
|
@@ -1 +0,0 @@
|
|
1
|
-
be4c786c5eb41d54a0fcdad6f95390fa5bb20e9afff3bc48f351a03ad50ec745a1b0272d13b92893ece863906db0d548b5bfe5f352b86f0218b9d36bbce1f1ef
|
Binary file
|
package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.aar.md5
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0dd6f951f83bfb1c258c09e57291b5f3
|
package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.aar.sha1
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
54212838677b6cf72c2eb964dc911a9a80cf54a7
|
package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.aar.sha256
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
c2452ceba0cb3a2a6517086d81a0e990d82ab263395a0e1166b8178769cd8a51
|
package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.aar.sha512
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
3d71f12171376fcf35b46ebc6a6cf942e0a7151a8f26287b41b6e2807f4bd8d37704aeaa89b7b0231c2286a301ebbfa3ff77a0a9cee92c02463c0085a4190085
|
package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.pom.md5
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
9ccc32612fad441bf3f312e701818c11
|
package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.pom.sha1
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ba4c524ab26d96b548bb32a6bfe64ecb85fd07dd
|
package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.pom.sha256
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
f97b8cda1d0ea446e149e8981eb8d2be57e5bc342f73363dfeeca01ab1e1a354
|
package/Detox-android/com/wix/detox-legacy/20.26.3-smoke.0/detox-legacy-20.26.3-smoke.0.pom.sha512
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
e9ae21b7bb4ea61356dd029c74e87a27513114889cd937bad8ae374211f3850425a821994c68b7db345c9de81bc955f50c6b2d245bacd007e1ae40a29ca2f816
|
@@ -1,140 +0,0 @@
|
|
1
|
-
const { device } = require('../..');
|
2
|
-
|
3
|
-
class DetoxDriver {
|
4
|
-
constructor() {
|
5
|
-
this.availableAPI = {
|
6
|
-
matchers: [
|
7
|
-
{
|
8
|
-
signature: 'by.id(id: string)',
|
9
|
-
description: 'Matches elements by their test ID.',
|
10
|
-
example: "element(by.id('loginButton'))",
|
11
|
-
guidelines: ['Always use test-ids (accessibility identifiers) from the UI hierarchy to identify elements.']
|
12
|
-
},
|
13
|
-
{
|
14
|
-
signature: 'by.text(text: string)',
|
15
|
-
description: 'Matches elements by their text.',
|
16
|
-
example: "element(by.text('Login'))",
|
17
|
-
guidelines: ['Avoid using text matchers when possible, prefer test-ids.']
|
18
|
-
},
|
19
|
-
{
|
20
|
-
signature: 'by.type(type: string)',
|
21
|
-
description: 'Matches elements by their type.',
|
22
|
-
example: "element(by.type('RCTTextInput'))",
|
23
|
-
guidelines: ['Use type matchers as a last resort, prefer test-ids.']
|
24
|
-
}
|
25
|
-
],
|
26
|
-
actions: [
|
27
|
-
{
|
28
|
-
signature: 'tap(point?: Point2D)',
|
29
|
-
description: 'Simulates tap on an element',
|
30
|
-
example: "await element(by.id('loginButton')).tap();",
|
31
|
-
guidelines: ['Use element(by.id(\'testID\')) to locate elements.']
|
32
|
-
},
|
33
|
-
{
|
34
|
-
signature: 'longPress(point?: Point2D, duration?: number)',
|
35
|
-
description: 'Simulates long press on an element',
|
36
|
-
example: "await element(by.id('menuItem')).longPress();",
|
37
|
-
guidelines: ['If the target element is not accessible, interact with its container or the most relevant parent element.']
|
38
|
-
},
|
39
|
-
{
|
40
|
-
signature: 'typeText(text: string)',
|
41
|
-
description: 'Types text into a text field',
|
42
|
-
example: "await element(by.id('usernameInput')).typeText('myusername');",
|
43
|
-
guidelines: ['Typing can only be done on text field elements.']
|
44
|
-
},
|
45
|
-
{
|
46
|
-
signature: 'replaceText(text: string)',
|
47
|
-
description: 'Replaces text in a text field',
|
48
|
-
example: "await element(by.id('usernameInput')).replaceText('newusername');",
|
49
|
-
guidelines: ['Use this to replace existing text in a field.']
|
50
|
-
},
|
51
|
-
{
|
52
|
-
signature: 'clearText()',
|
53
|
-
description: 'Clears text from a text field',
|
54
|
-
example: "await element(by.id('usernameInput')).clearText();",
|
55
|
-
guidelines: ['Use this to clear existing text from a field.']
|
56
|
-
},
|
57
|
-
{
|
58
|
-
signature: 'scrollTo(edge: Direction, startPositionX?: number, startPositionY?: number)',
|
59
|
-
description: 'Scrolls to an edge',
|
60
|
-
example: "await element(by.id('scrollView')).scrollTo('bottom');",
|
61
|
-
guidelines: ['Scrolling must be done only on scroll-view elements.']
|
62
|
-
},
|
63
|
-
{
|
64
|
-
signature: 'scrollToIndex(index: Number)',
|
65
|
-
description: 'Scrolls to a specific index',
|
66
|
-
example: "await element(by.id('flatList')).scrollToIndex(5);",
|
67
|
-
guidelines: ['Use this for scrolling to a specific item in a list.']
|
68
|
-
},
|
69
|
-
{
|
70
|
-
signature: 'adjustSliderToPosition(newPosition: number)',
|
71
|
-
description: 'Adjusts slider to a position',
|
72
|
-
example: "await element(by.id('slider')).adjustSliderToPosition(0.75);",
|
73
|
-
guidelines: ['The position should be a number between 0 and 1.']
|
74
|
-
},
|
75
|
-
{
|
76
|
-
signature: 'setColumnToValue(column: number, value: string)',
|
77
|
-
description: 'Sets picker view column to a value (iOS only)',
|
78
|
-
example: "await element(by.id('datePicker')).setColumnToValue(1, '2023');",
|
79
|
-
guidelines: ['This is only available on iOS.']
|
80
|
-
},
|
81
|
-
{
|
82
|
-
signature: 'performAccessibilityAction(actionName: string)',
|
83
|
-
description: 'Triggers an accessibility action',
|
84
|
-
example: "await element(by.id('button')).performAccessibilityAction('longpress');",
|
85
|
-
guidelines: ['Use the provided value from the intent and do not change it.']
|
86
|
-
},
|
87
|
-
{
|
88
|
-
signature: 'swipe(direction: Direction, speed?: Speed, percentage?: number)',
|
89
|
-
description: 'Swipes in the specified direction',
|
90
|
-
example: "await element(by.id('card')).swipe('left', 'fast');",
|
91
|
-
guidelines: ['Use this for swiping gestures on elements.']
|
92
|
-
},
|
93
|
-
{
|
94
|
-
signature: 'pinch(scale: number, speed?: Speed, angle?: number)',
|
95
|
-
description: 'Performs a pinch gesture (iOS only)',
|
96
|
-
example: "await element(by.id('image')).pinch(0.5);",
|
97
|
-
guidelines: ['This is only available on iOS. Scale < 1 zooms out, scale > 1 zooms in.']
|
98
|
-
}
|
99
|
-
],
|
100
|
-
assertions: [
|
101
|
-
{
|
102
|
-
signature: 'toBeVisible()',
|
103
|
-
description: 'Asserts that the element is visible',
|
104
|
-
example: "await expect(element(by.id('loginButton'))).toBeVisible();",
|
105
|
-
guidelines: ['Use this to check if an element is visible on the screen.']
|
106
|
-
},
|
107
|
-
{
|
108
|
-
signature: 'toExist()',
|
109
|
-
description: 'Asserts that the element exists',
|
110
|
-
example: "await expect(element(by.id('username'))).toExist();",
|
111
|
-
guidelines: ['Use this to check if an element exists in the hierarchy, even if not visible.']
|
112
|
-
},
|
113
|
-
{
|
114
|
-
signature: 'toHaveText(text: string)',
|
115
|
-
description: 'Asserts that the element has the specified text',
|
116
|
-
example: "await expect(element(by.id('label'))).toHaveText('Hello, World!');",
|
117
|
-
guidelines: ['Use this to check the text content of an element.']
|
118
|
-
},
|
119
|
-
{
|
120
|
-
signature: 'toHaveValue(value: string)',
|
121
|
-
description: 'Asserts that the element has the specified value',
|
122
|
-
example: "await expect(element(by.id('slider'))).toHaveValue('0.5');",
|
123
|
-
guidelines: ['Use this to check the value of an element.']
|
124
|
-
}
|
125
|
-
]
|
126
|
-
};
|
127
|
-
}
|
128
|
-
|
129
|
-
async captureSnapshotImage() {
|
130
|
-
const fileName = `snapshot_${Date.now()}.png`;
|
131
|
-
await device.takeScreenshot(fileName);
|
132
|
-
return fileName;
|
133
|
-
}
|
134
|
-
|
135
|
-
async captureViewHierarchyString() {
|
136
|
-
return device.generateViewHierarchyXml();
|
137
|
-
}
|
138
|
-
}
|
139
|
-
|
140
|
-
module.exports = DetoxDriver;
|