detox 20.27.2 → 20.27.4
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.2/detox-20.27.2-sources.jar → 20.27.4/detox-20.27.4-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.aar +0 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.aar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.27.2/detox-20.27.2.pom → 20.27.4/detox-20.27.4.pom} +1 -1
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.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.2/detox-legacy-20.27.2-sources.jar → 20.27.4/detox-legacy-20.27.4-sources.jar} +0 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.aar +0 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.aar.md5 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox-legacy/{20.27.2/detox-legacy-20.27.2.pom → 20.27.4/detox-legacy-20.27.4.pom} +1 -1
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.pom.md5 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.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/hierarchy/ViewHierarchyGenerator.kt +3 -3
- package/package.json +3 -3
- package/src/DetoxWorker.js +2 -0
- package/src/copilot/DetoxCopilot.js +2 -2
- package/src/copilot/detoxCopilotFrameworkDriver.js +481 -120
- package/src/realms/DetoxContext.js +2 -0
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.aar +0 -0
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.aar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.pom.md5 +0 -1
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.pom.sha512 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.aar +0 -0
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.aar.md5 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.pom.md5 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.pom.sha512 +0 -1
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
9ace4a96235511950308086f1d57d806
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
22cc9e5934c1c0ba48217d81dc0a0403d6383378
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
c4f49a1754ae4e50e6a32cd8f22a51cb920e2a58b8e9da6a5d443aa6715a59f5
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
503797c9ab31332033756727a60af64c7797d51b731d88e6e841cba4c477f68ed1df2872602097587c91c5b357377007e7c9cb7e4f2b838172d1f99af52b5334
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
05c87fc838b557d8c9537d4f432a9033
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
acc5c69d438baaadcfe97fa1f168e2799c3ce03a
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
9b9bb763d1adae07410c1057e4212277e5d1e3342afd286fbccd623511f86ce9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
507cda9204d0726835db6d44cb80d23dd7fe4765c9b6387e635c1ad9cf8849e5d3d799100796106e7fcdd7fe4b4adb6aeb582aaccbf1db85639ae6b81e8b552e
|
|
@@ -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.27.
|
|
6
|
+
<version>20.27.4</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
|
+
69801cb935f0a63f247f867c28709f8d
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
9605245c61fd0ea528dd0cd2eda73ac0866ba25f
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
69e7b3363b16f075838fe56272dd13fd089b4d9b344ab4d4456a91d9444e0bbb
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
9f3223915c0899fed72f86c5781ee7c1f6f9737f0a016224582372e7b5f29ec6672fd943163717465549159f3f75a11a2ee6eca488589b97bc29775c3cbc3fe5
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<groupId>com.wix</groupId>
|
|
4
4
|
<artifactId>detox</artifactId>
|
|
5
5
|
<versioning>
|
|
6
|
-
<latest>20.27.
|
|
7
|
-
<release>20.27.
|
|
6
|
+
<latest>20.27.4</latest>
|
|
7
|
+
<release>20.27.4</release>
|
|
8
8
|
<versions>
|
|
9
|
-
<version>20.27.
|
|
9
|
+
<version>20.27.4</version>
|
|
10
10
|
</versions>
|
|
11
|
-
<lastUpdated>
|
|
11
|
+
<lastUpdated>20241019191347</lastUpdated>
|
|
12
12
|
</versioning>
|
|
13
13
|
</metadata>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4e15390d4670f66357943485fa05acea
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
3475202451a2cb5e3534b59b14612e32df033626
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
d1b63410f852b05d5e00c1a0358fd61afe51474be017e2e78fef15e30cb09d44
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
8b1cc9ffb3d076da45ed87bbaa606ccd89be6fc53dd5e57e8f3d3be5ed09eece128d7cbe4f398606441c1f837cf07268e0a35071c90894c2bd0f5f259b8865c7
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
11e61e70a680b35bc8433c99dbe93fa7
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
461391ec015ccc3107d8d1d1f4f90fdef1e89d4d
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
37d6621cb43970a45655c142a2d3f87202228010f48c9359b1608f968fc29965
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
f21f72c93ca515baebf73681a7406d25682550f537943a6f7b3933ba0bc318a3c83a243e7c7ac45372deb363a7dce7396132e101545a586c7b23ecaa6d3606e4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
af13b71ab208d17b33c1fc7a1ef4e529
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
47dca63caffe4427b6fec3ab51e13ade0e234e6d
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
22561e652905451c48ffb378b015ae046634e2efea239e6f08875ccbd179db3e
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
46aa7c047bc8cbe7ad61270652a5bcc88955545e33c6d8338a9cc610e157d5cf6b2ecea224bacdb13cffc027d92617abecf0d1faa7eb89fb5b8c10cd7c410eb1
|
|
@@ -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.27.
|
|
6
|
+
<version>20.27.4</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
|
+
07b3035704b21925bce075aa8ceaa610
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5369c9600c539c3142ab367ea81df17920762e86
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
675cf33b02458dfa9d2c997a5d51f70a138d6e05cc2a1794c530ded4be481f48
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
8f6bf9c98b0d80bec51af79d562411dc85ac6fb11ee7cb952f1875abd43ac73df3ffb90dc426485fe8e21dc0200b27a0ad646fbc8edc89576502690eaa817ca3
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<groupId>com.wix</groupId>
|
|
4
4
|
<artifactId>detox-legacy</artifactId>
|
|
5
5
|
<versioning>
|
|
6
|
-
<latest>20.27.
|
|
7
|
-
<release>20.27.
|
|
6
|
+
<latest>20.27.4</latest>
|
|
7
|
+
<release>20.27.4</release>
|
|
8
8
|
<versions>
|
|
9
|
-
<version>20.27.
|
|
9
|
+
<version>20.27.4</version>
|
|
10
10
|
</versions>
|
|
11
|
-
<lastUpdated>
|
|
11
|
+
<lastUpdated>20241019191405</lastUpdated>
|
|
12
12
|
</versioning>
|
|
13
13
|
</metadata>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
06da87cac17d41b217b1204e3baac71c
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
3260642fbd27ef17b3f677a27b9060f29ecddd68
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
7aaa8a6d2ea4aed57f4c789a4d2229070c7619b8466e959fe73d930c6d2f09c5
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
437e4f341f9a2a317ff3fc9c8ad606d36974f884172e105b36c2eb256d3245791fc0dabad87c8c23ea76fc9995528af1b6d6b9a5a28f389afe158b0d8318e6da
|
package/Detox-ios-framework.tbz
CHANGED
|
Binary file
|
package/Detox-ios-src.tbz
CHANGED
|
Binary file
|
package/Detox-ios-xcuitest.tbz
CHANGED
|
Binary file
|
package/android/detox/src/full/java/com/wix/detox/espresso/hierarchy/ViewHierarchyGenerator.kt
CHANGED
|
@@ -16,10 +16,10 @@ import kotlin.coroutines.resume
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
private const val GET_HTML_SCRIPT = """
|
|
19
|
+
(function() {
|
|
19
20
|
const blacklistedTags = ['script', 'style', 'head', 'meta'];
|
|
20
21
|
const blackListedTagsSelector = blacklistedTags.join(',');
|
|
21
22
|
|
|
22
|
-
(function() {
|
|
23
23
|
// Clone the entire document
|
|
24
24
|
var clonedDoc = document.documentElement.cloneNode(true);
|
|
25
25
|
|
|
@@ -170,9 +170,9 @@ object ViewHierarchyGenerator {
|
|
|
170
170
|
if (shouldInjectNewTestId) {
|
|
171
171
|
val newTestId = "${injectedPrefix}${indexPath.joinToString("_")}"
|
|
172
172
|
view.tag = newTestId
|
|
173
|
-
attributes["
|
|
173
|
+
attributes["id"] = newTestId
|
|
174
174
|
} else {
|
|
175
|
-
attributes["
|
|
175
|
+
attributes["id"] = currentTestId
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
attributes
|
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.27.
|
|
4
|
+
"version": "20.27.4",
|
|
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.23",
|
|
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": "74a2c1426616e4e65fa830b9b98689e18a0677e3"
|
|
120
120
|
}
|
package/src/DetoxWorker.js
CHANGED
|
@@ -14,25 +14,25 @@ const detoxCopilotFrameworkDriver = {
|
|
|
14
14
|
signature: 'by.id(id: string)',
|
|
15
15
|
description: 'Matches elements by their test ID.',
|
|
16
16
|
example: "element(by.id('loginButton'))",
|
|
17
|
-
guidelines: ['
|
|
17
|
+
guidelines: ['Use test IDs (accessibility identifiers) to uniquely identify elements. This is the best-practice matcher.'],
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
signature: 'by.text(text: string)',
|
|
21
21
|
description: 'Matches elements by their text.',
|
|
22
22
|
example: "element(by.text('Login'))",
|
|
23
|
-
guidelines: ['
|
|
23
|
+
guidelines: ['Prefer test IDs over text matchers when possible.'],
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
signature: 'by.type(type: string)',
|
|
27
27
|
description: 'Matches elements by their type.',
|
|
28
28
|
example: "element(by.type('RCTTextInput'))",
|
|
29
|
-
guidelines: ['Use type matchers as a last resort
|
|
29
|
+
guidelines: ['Use type matchers as a last resort.'],
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
signature: 'atIndex(index: number)',
|
|
33
|
-
description: 'Selects the element at the specified index from
|
|
33
|
+
description: 'Selects the element at the specified index from matched elements.',
|
|
34
34
|
example: "element(by.id('listItem')).atIndex(2)",
|
|
35
|
-
guidelines: ['Use
|
|
35
|
+
guidelines: ['Use when multiple elements match the same matcher.'],
|
|
36
36
|
},
|
|
37
37
|
],
|
|
38
38
|
},
|
|
@@ -43,169 +43,141 @@ const detoxCopilotFrameworkDriver = {
|
|
|
43
43
|
signature: 'tap(point?: Point2D)',
|
|
44
44
|
description: 'Simulates tap on an element.',
|
|
45
45
|
example: "await element(by.id('loginButton')).tap();",
|
|
46
|
-
guidelines: ["Use `element(by.id('testID'))` to locate elements."],
|
|
47
46
|
},
|
|
48
47
|
{
|
|
49
48
|
signature: 'longPress(point?: Point2D, duration?: number)',
|
|
50
49
|
description: 'Simulates long press on an element.',
|
|
51
50
|
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
|
-
],
|
|
51
|
+
guidelines: ['Tapping on edges of elements might work better when adding a small offset to the point.'],
|
|
56
52
|
},
|
|
57
53
|
{
|
|
58
54
|
signature: 'multiTap(times: number)',
|
|
59
55
|
description: 'Simulates multiple taps on an element.',
|
|
60
56
|
example: "await element(by.id('tappable')).multiTap(3);",
|
|
61
|
-
guidelines: ['All taps are applied as part of the same gesture.'],
|
|
62
57
|
},
|
|
63
58
|
{
|
|
64
59
|
signature: 'typeText(text: string)',
|
|
65
60
|
description: 'Types text into a text field.',
|
|
66
61
|
example: "await element(by.id('usernameInput')).typeText('myusername');",
|
|
67
|
-
guidelines: ['
|
|
62
|
+
guidelines: ['Element must be a text input field.'],
|
|
68
63
|
},
|
|
69
64
|
{
|
|
70
65
|
signature: 'replaceText(text: string)',
|
|
71
66
|
description: 'Replaces text in a text field.',
|
|
72
67
|
example: "await element(by.id('textField')).replaceText('new text');",
|
|
73
|
-
guidelines: ['Faster than `typeText()`, but may not trigger all text input callbacks.'],
|
|
74
68
|
},
|
|
75
69
|
{
|
|
76
70
|
signature: 'clearText()',
|
|
77
71
|
description: 'Clears text from a text field.',
|
|
78
72
|
example: "await element(by.id('textField')).clearText();",
|
|
79
|
-
guidelines: ['Use this to clear text from input fields.'],
|
|
80
73
|
},
|
|
81
74
|
{
|
|
82
75
|
signature: 'tapReturnKey()',
|
|
83
76
|
description: 'Simulates tapping the return key on the keyboard while the element is focused.',
|
|
84
77
|
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
78
|
},
|
|
87
79
|
{
|
|
88
80
|
signature: 'tapBackspaceKey()',
|
|
89
81
|
description: 'Simulates tapping the backspace key on the keyboard while the element is focused.',
|
|
90
82
|
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
83
|
},
|
|
93
84
|
{
|
|
94
85
|
signature: 'adjustSliderToPosition(normalizedPosition: number)',
|
|
95
|
-
description: 'Adjusts
|
|
86
|
+
description: 'Adjusts slider to a normalized position between 0 and 1.',
|
|
96
87
|
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
88
|
},
|
|
99
89
|
{
|
|
100
90
|
signature: 'scroll(offset: number, direction: string, startPositionX?: number, startPositionY?: number)',
|
|
101
|
-
description: 'Scrolls an element.',
|
|
91
|
+
description: 'Scrolls an element by an offset in a direction.',
|
|
102
92
|
example: "await element(by.id('scrollView')).scroll(100, 'down');",
|
|
103
|
-
guidelines: [
|
|
93
|
+
guidelines: [
|
|
94
|
+
'Direction can be "up", "down", "left", or "right".',
|
|
95
|
+
'Use `startPositionX` and `startPositionY` to specify the starting point of the scroll gesture.',
|
|
96
|
+
'If multiple scroll actions are needed while waiting for an element, use `whileElement()` in conjunction with `waitFor()`.',
|
|
97
|
+
],
|
|
104
98
|
},
|
|
105
99
|
{
|
|
106
|
-
signature: 'scrollTo(edge: string)',
|
|
100
|
+
signature: 'scrollTo(edge: string, startPositionX?: number, startPositionY?: number)',
|
|
107
101
|
description: 'Scrolls to an edge of the element.',
|
|
108
102
|
example: "await element(by.id('scrollView')).scrollTo('bottom');",
|
|
109
|
-
guidelines: [
|
|
103
|
+
guidelines: [
|
|
104
|
+
'Edge can be "top", "bottom", "left", or "right".',
|
|
105
|
+
'Use `startPositionX` and `startPositionY` to specify the starting point of the scroll gesture.',
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
signature: 'waitFor(element: Matcher)..toBeVisible().whileElement(element: Matcher).scroll(offset: number, direction: string)',
|
|
110
|
+
description: 'Continuously performs an action while waiting for an expectation to be fulfilled.',
|
|
111
|
+
example: `
|
|
112
|
+
await waitFor(element(by.text('Load More')))
|
|
113
|
+
.toBeVisible()
|
|
114
|
+
.whileElement(by.id('scrollView'))
|
|
115
|
+
.scroll(50, 'down');`,
|
|
116
|
+
guidelines: [
|
|
117
|
+
'Used in conjunction with `waitFor()` to perform actions like scrolling while waiting for an element to meet the expectation.',
|
|
118
|
+
'The action (e.g., `scroll`) is performed on the element specified in `whileElement()`.',
|
|
119
|
+
],
|
|
110
120
|
},
|
|
111
121
|
{
|
|
112
122
|
signature: 'scrollToIndex(index: number)',
|
|
113
|
-
description: 'Scrolls
|
|
123
|
+
description: 'Scrolls to the specified index (Android only).',
|
|
114
124
|
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
125
|
},
|
|
117
126
|
{
|
|
118
127
|
signature: 'swipe(direction: string, speed?: string, normalizedOffset?: number)',
|
|
119
|
-
description: 'Simulates a swipe
|
|
120
|
-
example: "await element(by.id('scrollView')).swipe('up'
|
|
128
|
+
description: 'Simulates a swipe gesture.',
|
|
129
|
+
example: "await element(by.id('scrollView')).swipe('up');",
|
|
121
130
|
guidelines: [
|
|
122
|
-
'
|
|
123
|
-
'
|
|
131
|
+
'Speed can be "fast" or "slow"; default is "fast".',
|
|
132
|
+
'Direction can be "up", "down", "left", or "right".',
|
|
133
|
+
'Up swipe scrolls down, left swipe scrolls right.',
|
|
124
134
|
],
|
|
125
135
|
},
|
|
126
136
|
{
|
|
127
137
|
signature: 'setColumnToValue(column: number, value: string)',
|
|
128
|
-
description: 'Sets
|
|
138
|
+
description: 'Sets picker column to a value (iOS only).',
|
|
129
139
|
example: "await element(by.id('pickerView')).setColumnToValue(1, '6');",
|
|
130
|
-
guidelines: ['Use this for picker views on iOS.'],
|
|
131
140
|
},
|
|
132
141
|
{
|
|
133
142
|
signature: 'setDatePickerDate(dateString: string, dateFormat: string)',
|
|
134
|
-
description: 'Sets
|
|
143
|
+
description: 'Sets date picker to a specific date.',
|
|
135
144
|
example: "await element(by.id('datePicker')).setDatePickerDate('2023-05-25', 'yyyy-MM-dd');",
|
|
136
|
-
guidelines: ['Use ISO8601 format when possible.'],
|
|
137
145
|
},
|
|
138
146
|
{
|
|
139
147
|
signature: 'performAccessibilityAction(actionName: string)',
|
|
140
|
-
description: '
|
|
148
|
+
description: 'Performs an accessibility action.',
|
|
141
149
|
example: "await element(by.id('scrollView')).performAccessibilityAction('activate');",
|
|
142
|
-
guidelines: ['Use this to trigger specific accessibility actions.'],
|
|
143
150
|
},
|
|
144
151
|
{
|
|
145
152
|
signature: 'pinch(scale: number, speed?: string, angle?: number)',
|
|
146
153
|
description: 'Simulates a pinch gesture.',
|
|
147
|
-
example: "await element(by.id('
|
|
148
|
-
guidelines: ['
|
|
154
|
+
example: "await element(by.id('pinchableView')).pinch(1.1);",
|
|
155
|
+
guidelines: ['Scale >1 to zoom in, <1 to zoom out.'],
|
|
149
156
|
},
|
|
150
|
-
|
|
151
157
|
{
|
|
152
158
|
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
|
-
`,
|
|
159
|
+
description: 'Retrieves attributes of the element.',
|
|
163
160
|
example: `
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
],
|
|
161
|
+
const attributes = await element(by.text('Tap Me')).getAttributes();
|
|
162
|
+
jestExpect(attributes.text).toBe('Tap Me');`,
|
|
185
163
|
},
|
|
186
164
|
{
|
|
187
165
|
signature: 'takeScreenshot(name: string)',
|
|
188
166
|
description: 'Captures a screenshot of the element.',
|
|
189
167
|
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
168
|
},
|
|
198
169
|
{
|
|
199
|
-
signature: '
|
|
200
|
-
description:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
170
|
+
signature: 'longPressAndDrag(duration, normalizedPositionX, normalizedPositionY, targetElement, normalizedTargetPositionX, normalizedTargetPositionY, speed, holdDuration)',
|
|
171
|
+
description: `Performs a long press and drags to a target element.
|
|
172
|
+
- \`duration\` — the duration to press for, in ms (required)
|
|
173
|
+
- \`normalizedPositionX\` — X coordinate of the starting point, relative to the element width (required, a number between 0.0 and 1.0, NaN — choose an optimal value automatically)
|
|
174
|
+
- \`normalizedPositionY\` — Y coordinate of the starting point, relative to the element height (required, a number between 0.0 and 1.0, NaN — choose an optimal value automatically)
|
|
175
|
+
- \`targetElement\` — the target element to drag to (required)
|
|
176
|
+
- \`normalizedTargetPositionX\` — X coordinate of the ending point, relative to the target element width (optional, a number between 0.0 and 1.0, NaN — choose an optimal value automatically)
|
|
177
|
+
- \`normalizedTargetPositionY\` — Y coordinate of the ending point, relative to the target element height (optional, a number between 0.0 and 1.0, NaN — choose an optimal value automatically)
|
|
178
|
+
- \`speed\` — the speed of the drag (optional, valid input: "fast"/"slow" , default is "fast")
|
|
179
|
+
- \`holdDuration\` — the duration before releasing at the end, in ms (optional, default is 1000)`,
|
|
180
|
+
example: "await element(by.id('cellId_1')).longPressAndDrag(2000, 0.9, NaN, element(by.id('cellId_6')), 0.9, NaN, 'slow', 0);",
|
|
209
181
|
},
|
|
210
182
|
],
|
|
211
183
|
},
|
|
@@ -216,75 +188,56 @@ const detoxCopilotFrameworkDriver = {
|
|
|
216
188
|
signature: 'toBeVisible()',
|
|
217
189
|
description: 'Asserts that the element is visible.',
|
|
218
190
|
example: "await expect(element(by.id('loginButton'))).toBeVisible();",
|
|
219
|
-
guidelines: ['Use this to check if an element is visible on the screen.'],
|
|
220
191
|
},
|
|
221
192
|
{
|
|
222
193
|
signature: 'toExist()',
|
|
223
194
|
description: 'Asserts that the element exists.',
|
|
224
195
|
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
196
|
},
|
|
227
197
|
{
|
|
228
198
|
signature: 'toHaveText(text: string)',
|
|
229
199
|
description: 'Asserts that the element has the specified text.',
|
|
230
200
|
example: "await expect(element(by.id('label'))).toHaveText('Hello, World!');",
|
|
231
|
-
guidelines: ['Use this to check the text content of an element.'],
|
|
232
201
|
},
|
|
233
202
|
{
|
|
234
203
|
signature: 'toHaveValue(value: string)',
|
|
235
204
|
description: 'Asserts that the element has the specified value.',
|
|
236
205
|
example: "await expect(element(by.id('slider'))).toHaveValue('0.5');",
|
|
237
|
-
guidelines: ['Use this to check the value of an element.'],
|
|
238
206
|
},
|
|
239
207
|
{
|
|
240
208
|
signature: 'toBeFocused()',
|
|
241
209
|
description: 'Asserts that the element is focused.',
|
|
242
210
|
example: "await expect(element(by.id('emailInput'))).toBeFocused();",
|
|
243
|
-
guidelines: ['Use this to check if an element is currently focused.'],
|
|
244
211
|
},
|
|
245
212
|
{
|
|
246
213
|
signature: 'toHaveLabel(label: string)',
|
|
247
214
|
description: 'Asserts that the element has the specified accessibility label.',
|
|
248
215
|
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
216
|
},
|
|
253
217
|
{
|
|
254
218
|
signature: 'toHaveId(id: string)',
|
|
255
219
|
description: 'Asserts that the element has the specified accessibility identifier.',
|
|
256
220
|
example: "await expect(element(by.text('Submit'))).toHaveId('submitButton');",
|
|
257
|
-
guidelines: ['Use this to check the testID/accessibility identifier of an element.'],
|
|
258
221
|
},
|
|
259
222
|
{
|
|
260
223
|
signature: 'toHaveSliderPosition(normalizedPosition: number, tolerance?: number)',
|
|
261
|
-
description:
|
|
262
|
-
|
|
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.'],
|
|
224
|
+
description: 'Asserts that the slider is at a normalized position.',
|
|
225
|
+
example: "await expect(element(by.id('slider'))).toHaveSliderPosition(0.75);",
|
|
266
226
|
},
|
|
267
227
|
{
|
|
268
228
|
signature: 'toHaveToggleValue(value: boolean)',
|
|
269
|
-
description: 'Asserts that a toggle
|
|
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.'],
|
|
229
|
+
description: 'Asserts that a toggle element is on or off.',
|
|
230
|
+
example: "await expect(element(by.id('switch'))).toHaveToggleValue(true);",
|
|
273
231
|
},
|
|
274
232
|
{
|
|
275
233
|
signature: 'withTimeout(timeout: number)',
|
|
276
|
-
description:
|
|
277
|
-
|
|
278
|
-
example:
|
|
279
|
-
"await waitFor(element(by.id('bigButton'))).toBeVisible().withTimeout(2000);",
|
|
280
|
-
guidelines: ['Use this to set a custom timeout for an expectation.'],
|
|
234
|
+
description: 'Waits until the expectation is resolved or timeout occurs.',
|
|
235
|
+
example: "await waitFor(element(by.id('bigButton'))).toBeVisible().withTimeout(2000);",
|
|
281
236
|
},
|
|
282
237
|
{
|
|
283
238
|
signature: 'not',
|
|
284
239
|
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."],
|
|
240
|
+
example: "await expect(element(by.id('tinyButton'))).not.toBeVisible();",
|
|
288
241
|
},
|
|
289
242
|
],
|
|
290
243
|
},
|
|
@@ -293,22 +246,430 @@ const detoxCopilotFrameworkDriver = {
|
|
|
293
246
|
items: [
|
|
294
247
|
{
|
|
295
248
|
signature: 'jestExpect',
|
|
296
|
-
description: 'Jest expect utility for
|
|
249
|
+
description: 'Jest expect utility for additional assertions.',
|
|
250
|
+
example: `
|
|
251
|
+
jestExpect(2 + 2).toBe(4);
|
|
252
|
+
jestExpect('hello').toBe('hello');`,
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
title: 'Device APIs',
|
|
258
|
+
items: [
|
|
259
|
+
{
|
|
260
|
+
signature: 'device.launchApp(params?: object)',
|
|
261
|
+
description: `
|
|
262
|
+
Launches the app with specified parameters.
|
|
263
|
+
|
|
264
|
+
**Parameters:**
|
|
265
|
+
- \`newInstance\` (boolean): If \`true\`, terminates the app and launches a new instance.
|
|
266
|
+
- \`delete\` (boolean): If \`true\`, deletes the app data before launching.
|
|
267
|
+
- \`launchArgs\` (object): Additional launch arguments as key-value pairs.
|
|
268
|
+
- \`url\` (string): URL to open in the app.
|
|
269
|
+
- \`permissions\` (object): Permissions to grant the app. Supported permissions are:
|
|
270
|
+
| Permission | Values |
|
|
271
|
+
|-----------------|----------------------------|
|
|
272
|
+
| **location** | always / inuse / never / unset |
|
|
273
|
+
| **contacts** | YES / NO / unset / limited |
|
|
274
|
+
| **photos** | YES / NO / unset / limited |
|
|
275
|
+
| **calendar** | YES / NO / unset |
|
|
276
|
+
| **camera** | YES / NO / unset |
|
|
277
|
+
| **medialibrary**| YES / NO / unset |
|
|
278
|
+
| **microphone** | YES / NO / unset |
|
|
279
|
+
| **motion** | YES / NO / unset |
|
|
280
|
+
| **reminders** | YES / NO / unset |
|
|
281
|
+
| **siri** | YES / NO / unset |
|
|
282
|
+
| **notifications**| YES / NO / unset |
|
|
283
|
+
| **health** | YES / NO / unset |
|
|
284
|
+
| **homekit** | YES / NO / unset |
|
|
285
|
+
| **speech** | YES / NO / unset |
|
|
286
|
+
| **faceid** | YES / NO / unset |
|
|
287
|
+
| **userTracking**| YES / NO / unset |
|
|
288
|
+
`,
|
|
289
|
+
example: `
|
|
290
|
+
await device.launchApp({ newInstance: true });
|
|
291
|
+
await device.launchApp({ newInstance: true, permissions: { notifications: 'YES' } });
|
|
292
|
+
await device.launchApp({ launchArgs: { someLaunchArg: 1234 } });`,
|
|
293
|
+
guidelines: ['Use minimal parameters necessary for your launch scenario.'],
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
signature: 'device.reloadReactNative()',
|
|
297
|
+
description: 'Reloads the React Native JS bundle.',
|
|
298
|
+
example: 'await device.reloadReactNative();',
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
signature: 'device.setOrientation(orientation: string)',
|
|
302
|
+
description: 'Rotates the device to the specified orientation.',
|
|
303
|
+
example: 'await device.setOrientation("landscape");',
|
|
304
|
+
guidelines: ['Orientation can be "portrait" or "landscape".'],
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
signature: 'device.setLocation(lat: number, lon: number)',
|
|
308
|
+
description: 'Sets the device location.',
|
|
309
|
+
example: 'await device.setLocation(37.7749, -122.4194);',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
signature: 'device.takeScreenshot(name?: string)',
|
|
313
|
+
description: 'Captures a screenshot of the device.',
|
|
314
|
+
example: 'const path = await device.takeScreenshot("home_screen");',
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
signature: 'device.getPlatform()',
|
|
318
|
+
description: 'Returns the current device platform ("ios" or "android").',
|
|
319
|
+
example: 'const platform = device.getPlatform();',
|
|
320
|
+
guidelines: ['Use to conditionally execute platform-specific code.'],
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
signature: 'device.openURL(url: string)',
|
|
324
|
+
description: 'Opens a deeplink within the app, or a URL in the browser.',
|
|
325
|
+
example: 'await device.openURL("app://home");',
|
|
326
|
+
}
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
title: 'System APIs (iOS)',
|
|
331
|
+
items: [
|
|
332
|
+
{
|
|
333
|
+
signature: 'system.element(matcher: Matcher)',
|
|
334
|
+
description: 'Selects an element within the system UI.',
|
|
335
|
+
example: "system.element(by.system.label('Allow')).tap();",
|
|
336
|
+
guidelines: [
|
|
337
|
+
'Can be used for iOS system alerts and permissions dialogs',
|
|
338
|
+
'Always use `system.element()` to interact with system dialog elements (alerts, permission requests).',
|
|
339
|
+
'Check the platform with `device.getPlatform()` before using, as it is iOS-specific',
|
|
340
|
+
'Permission alerts are part of the system UI, not the app UI. Therefore should be matched with `system.element()`.',
|
|
341
|
+
'System dialogs are not part of the app, so they won\'t be found in the app\'s view hierarchy. Identify the relevant system element from the snapshot.',
|
|
342
|
+
]
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
signature: 'by.system.label(label: string)',
|
|
346
|
+
description: 'Matches system elements by their label (text).',
|
|
347
|
+
example: "system.element(by.system.label('Dismiss'));",
|
|
348
|
+
guidelines: [
|
|
349
|
+
'System elements will not be found in the app\'s view hierarchy. Read the text from the snapshot.',
|
|
350
|
+
'If no system dialog can be found, throw an error with the relevant message.',
|
|
351
|
+
],
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
signature: 'by.system.type(type: string)',
|
|
355
|
+
description: 'Matches system elements by type.',
|
|
356
|
+
example: "system.element(by.system.type('button'));",
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
signature: 'tap()',
|
|
360
|
+
description: 'Taps on a system element.',
|
|
361
|
+
example: "system.element(by.system.label('Allow')).tap();",
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
signature: 'toExist()',
|
|
365
|
+
description: 'Asserts that the system element exists.',
|
|
366
|
+
example: "await expect(system.element(by.system.label('Allow'))).toExist();",
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
signature: 'not',
|
|
370
|
+
description: 'Negates the expectation for system elements.',
|
|
371
|
+
example: "await expect(system.element(by.system.label('Allow'))).not.toExist();",
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
title: 'Web APIs',
|
|
377
|
+
items: [
|
|
378
|
+
{
|
|
379
|
+
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: `
|
|
382
|
+
await web.element(by.web.id('email')).typeText('test@example.com');
|
|
383
|
+
await web.element(by.web.id('password')).typeText('password123');
|
|
384
|
+
await web.element(by.web.id('login-button')).tap();
|
|
385
|
+
`,
|
|
386
|
+
guidelines: [
|
|
387
|
+
'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
|
+
'After the initial wait, you can interact with web elements without additional delays.',
|
|
389
|
+
'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). Avoid using web APIs for native elements or native APIs for web elements.',
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
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.',
|
|
396
|
+
example: `
|
|
397
|
+
// Wait for the specific web view to appear and load (only once before interacting)
|
|
398
|
+
await expect(element(by.id('checkout-webview'))).toBeVisible();
|
|
399
|
+
|
|
400
|
+
// Interact with elements within a specific web view
|
|
401
|
+
const specificWebView = web(by.id('checkout-webview'));
|
|
402
|
+
|
|
403
|
+
await specificWebView.element(by.web.id('credit-card-number')).typeText('4111111111111111');
|
|
404
|
+
await specificWebView.element(by.web.id('expiration-date')).typeText('12/25');
|
|
405
|
+
await specificWebView.element(by.web.id('cvv')).typeText('123');
|
|
406
|
+
await specificWebView.element(by.web.id('pay-button')).tap();
|
|
407
|
+
`,
|
|
408
|
+
guidelines: [
|
|
409
|
+
'Use this method when multiple web views are present.',
|
|
410
|
+
'Ensure the web view is visible and its content is loaded before interacting. This wait should happen only once.',
|
|
411
|
+
'After the initial wait, you can interact with elements within the web view without additional delays.',
|
|
412
|
+
'Webview must be matched with `web(nativeMatcher)` (e.g., `web(by.id(..))` instead of `element(by.id(..))`).',
|
|
413
|
+
'Prefer the basic `web.element()` if only one web view is present on the screen.',
|
|
414
|
+
],
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
signature: 'web(nativeMatcher: NativeMatcher).atIndex(index: number).element(matcher: Matcher)',
|
|
418
|
+
description: 'Selects an element within a specific web view at a given index (iOS only).',
|
|
419
|
+
example: `
|
|
420
|
+
// Interact with an element within the second web view (index starts from 0)
|
|
421
|
+
const secondWebView = web(by.id('webview')).atIndex(1);
|
|
422
|
+
await secondWebView.element(by.web.id('search-input')).typeText('Detox Testing');
|
|
423
|
+
await secondWebView.element(by.web.id('search-button')).tap();
|
|
424
|
+
`,
|
|
425
|
+
guidelines: [
|
|
426
|
+
'Use when multiple web views with the same identifier are present on iOS.',
|
|
427
|
+
'This method is available for iOS only.',
|
|
428
|
+
'Check the platform with `device.getPlatform()` before using.',
|
|
429
|
+
],
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
signature: 'by.web.id(id: string)',
|
|
433
|
+
description: 'Matches web elements by their "id" attribute.',
|
|
434
|
+
example: `await web.element(by.web.id('search')).tap();`,
|
|
435
|
+
guidelines: [
|
|
436
|
+
'Prefer `by.web.id` over any other matchers when available.',
|
|
437
|
+
],
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
signature: 'by.web.className(className: string)',
|
|
441
|
+
description: 'Matches web elements by their CSS class name.',
|
|
442
|
+
example: `await web.element(by.web.className('btn-login')).tap();`,
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
signature: 'by.web.cssSelector(cssSelector: string)',
|
|
446
|
+
description: 'Matches web elements using a CSS selector.',
|
|
447
|
+
example: `await web.element(by.web.cssSelector('#product-list .product-item[data-id="123"]')).scrollToView();`,
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
signature: 'by.web.name(name: string)',
|
|
451
|
+
description: 'Matches web elements by their name attribute.',
|
|
452
|
+
example: `await web.element(by.web.name('firstName')).typeText('John');`,
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
signature: 'by.web.xpath(xpath: string)',
|
|
456
|
+
description: 'Matches web elements using an XPath expression.',
|
|
457
|
+
example: `await web.element(by.web.xpath('//button[text()="Continue"]')).tap();`,
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
signature: 'by.web.href(href: string)',
|
|
461
|
+
description: 'Matches web elements by their href attribute.',
|
|
462
|
+
example: `await web.element(by.web.href('https://www.example.com/contact')).tap();`,
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
signature: 'by.web.hrefContains(href: string)',
|
|
466
|
+
description: 'Matches web elements whose href attribute contains the specified string.',
|
|
467
|
+
example: `await web.element(by.web.hrefContains('/about')).tap();`,
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
signature: 'by.web.tag(tag: string)',
|
|
471
|
+
description: 'Matches web elements by their tag name.',
|
|
472
|
+
example: `await expect(web.element(by.web.tag('h1'))).toHaveText('Welcome to Our Site');`,
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
signature: 'by.web.value(value: string)',
|
|
476
|
+
description: 'Matches web elements by their value attribute (iOS only).',
|
|
477
|
+
example: `await web.element(by.web.value('Sign In')).tap();`,
|
|
478
|
+
guidelines: [
|
|
479
|
+
'Available on iOS only.',
|
|
480
|
+
],
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
signature: 'by.web.label(label: string)',
|
|
484
|
+
description: 'Matches web elements by their label (iOS only, supports `asSecured()`).',
|
|
485
|
+
example: `await web.element(by.web.label('Next')).tap();`,
|
|
486
|
+
guidelines: [
|
|
487
|
+
'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.',
|
|
490
|
+
],
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
signature: 'by.web.type(accessibilityType: string)',
|
|
494
|
+
description: 'Matches web elements by accessibility type (iOS only, `asSecured()` only).',
|
|
495
|
+
example: `await web.element(by.web.type('textField')).asSecured().typeText('Sample Text');`,
|
|
496
|
+
guidelines: [
|
|
497
|
+
'Available on iOS only and used with `asSecured()`.',
|
|
498
|
+
'Type can be any XCUIElement.ElementType, e.g., "button", "textField".',
|
|
499
|
+
],
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
signature: 'atIndex(index: number)',
|
|
503
|
+
description: 'Selects the web element at the specified index from matched elements.',
|
|
504
|
+
example: `await expect(web.element(by.web.tag('h2')).atIndex(1)).toHaveText('Features');`,
|
|
505
|
+
guidelines: [
|
|
506
|
+
'Use when multiple web elements match the same matcher.',
|
|
507
|
+
],
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
signature: 'tap()',
|
|
511
|
+
description: 'Taps on a web element.',
|
|
512
|
+
example: `await web.element(by.web.label('Submit')).tap();`,
|
|
513
|
+
guidelines: [
|
|
514
|
+
'Supports `asSecured()` on iOS.',
|
|
515
|
+
],
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
signature: 'typeText(text: string)',
|
|
519
|
+
description: 'Types text into a web element.',
|
|
520
|
+
example: `await web.element(by.web.id('rich-text-editor')).typeText('This is a test message');`,
|
|
521
|
+
guidelines: [
|
|
522
|
+
'Supports `asSecured()` on iOS.',
|
|
523
|
+
],
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
signature: 'replaceText(text: string)',
|
|
527
|
+
description: 'Replaces text in a web element.',
|
|
528
|
+
example: `await web.element(by.web.id('username')).replaceText('new_user');`,
|
|
529
|
+
guidelines: [
|
|
530
|
+
'Supports `asSecured()` on iOS.',
|
|
531
|
+
],
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
signature: 'clearText()',
|
|
535
|
+
description: 'Clears text from a web element.',
|
|
536
|
+
example: `await web.element(by.web.id('comments')).clearText();`,
|
|
537
|
+
guidelines: [
|
|
538
|
+
'Supports `asSecured()` on iOS.',
|
|
539
|
+
],
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
signature: 'selectAllText()',
|
|
543
|
+
description: 'Selects all text in a web element.',
|
|
544
|
+
example: `await web.element(by.web.id('notes')).selectAllText();`,
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
signature: 'getText()',
|
|
548
|
+
description: 'Retrieves the text content of a web element.',
|
|
297
549
|
example: `
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
550
|
+
const description = await web.element(by.web.id('product-description')).getText();
|
|
551
|
+
jestExpect(description).toContain('This product is made from the finest materials.');
|
|
552
|
+
`,
|
|
553
|
+
guidelines: [
|
|
554
|
+
'Use for assertions on the element\'s text content.',
|
|
555
|
+
],
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
signature: 'scrollToView()',
|
|
559
|
+
description: 'Scrolls the web view to bring the element into view.',
|
|
560
|
+
example: `await web.element(by.web.id('contact-section')).scrollToView();`,
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
signature: 'focus()',
|
|
564
|
+
description: 'Focuses on a web element.',
|
|
565
|
+
example: `
|
|
566
|
+
await web.element(by.web.id('email-input')).focus();
|
|
567
|
+
await web.element(by.web.id('email-input')).typeText('user@example.com');
|
|
568
|
+
`,
|
|
569
|
+
guidelines: [
|
|
570
|
+
'Useful for input fields that require focus before typing.',
|
|
571
|
+
'No need for secured interactions on iOS.',
|
|
572
|
+
]
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
signature: 'moveCursorToEnd()',
|
|
576
|
+
description: 'Moves the input cursor to the end of the element\'s content.',
|
|
577
|
+
example: `
|
|
578
|
+
await web.element(by.web.id('message-box')).moveCursorToEnd();
|
|
579
|
+
await web.element(by.web.id('message-box')).typeText(' Adding more text.');
|
|
580
|
+
`,
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
signature: 'runScript(script: string, args?: any[])',
|
|
584
|
+
description: 'Runs a JavaScript function on the element.',
|
|
585
|
+
example: `
|
|
586
|
+
// Click an element using a custom script
|
|
587
|
+
await web.element(by.web.id('hidden-button')).runScript('el => el.click()');
|
|
588
|
+
|
|
589
|
+
// Set the value of an input field using a custom script
|
|
590
|
+
await web.element(by.web.id('username')).runScript('(el, args) => el.value = args[0]', ['new_user']);
|
|
591
|
+
|
|
592
|
+
// Get the color of an element
|
|
593
|
+
const color = await web.element(by.web.id('header')).runScript(function(el) {
|
|
594
|
+
return window.getComputedStyle(el).color;
|
|
595
|
+
});
|
|
596
|
+
jestExpect(color).toBe('rgb(0, 0, 0)');
|
|
597
|
+
|
|
598
|
+
// Scroll an element into view if not visible
|
|
599
|
+
await web.element(by.web.id('lazy-image')).runScript('el => el.scrollIntoView()');
|
|
600
|
+
`,
|
|
601
|
+
guidelines: [
|
|
602
|
+
'The script can accept additional arguments and return a value.',
|
|
603
|
+
'Ensure that arguments and return values are serializable.',
|
|
604
|
+
'Useful for custom interactions or retrieving properties.',
|
|
605
|
+
'Avoid using this method for simple interactions like tapping or typing.',
|
|
606
|
+
],
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
signature: 'getCurrentUrl()',
|
|
610
|
+
description: 'Retrieves the current URL of the web view.',
|
|
611
|
+
example: `
|
|
612
|
+
await web.element(by.web.id('link-to-page')).tap();
|
|
613
|
+
const currentUrl = await web.element(by.web.tag('body')).getCurrentUrl();
|
|
614
|
+
jestExpect(currentUrl).toBe('https://www.example.com/target-page');
|
|
615
|
+
`,
|
|
616
|
+
guidelines: [
|
|
617
|
+
'Must be called from an inner element, not the root web view.',
|
|
618
|
+
],
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
signature: 'getTitle()',
|
|
622
|
+
description: 'Retrieves the title of the web view.',
|
|
623
|
+
example: `
|
|
624
|
+
const pageTitle = await web.element(by.web.tag('body')).getTitle();
|
|
625
|
+
jestExpect(pageTitle).toBe('Dashboard - MyApp');
|
|
626
|
+
`,
|
|
627
|
+
guidelines: [
|
|
628
|
+
'Must be called from an inner element, not the root web view.',
|
|
629
|
+
],
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
signature: 'toHaveText(text: string)',
|
|
633
|
+
description: 'Asserts that the web element has the specified text. Used with `expect()`.',
|
|
634
|
+
example: `await expect(web.element(by.web.name('submit-button'))).toHaveText('Submit');`,
|
|
635
|
+
guidelines: [
|
|
636
|
+
'For web views, the text is the inner text of the element, e.g., `<h1>Welcome to the webpage!</h1>`.',
|
|
637
|
+
],
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
signature: 'toExist()',
|
|
641
|
+
description: 'Asserts that the web element exists. Used with `expect()`.',
|
|
642
|
+
example: `await expect(web.element(by.web.id('error-message'))).toExist();`,
|
|
643
|
+
guidelines: [
|
|
644
|
+
'Verifies the presence of a web element in the DOM.',
|
|
645
|
+
'Supports `asSecured()` on iOS.',
|
|
646
|
+
],
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
signature: 'not',
|
|
650
|
+
description: 'Negates the expectation for web elements.',
|
|
651
|
+
example: `await expect(web.element(by.web.id('loading-spinner'))).not.toExist();`,
|
|
652
|
+
guidelines: [
|
|
653
|
+
'Use `not` to assert that an element should not be present.',
|
|
654
|
+
'Supports `asSecured()` on iOS.',
|
|
655
|
+
],
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
signature: 'asSecured()',
|
|
659
|
+
description: 'Interacts with secured web views (iOS only).',
|
|
660
|
+
example: `await web.element(by.web.label('Confirm')).asSecured().tap();`,
|
|
661
|
+
guidelines: [
|
|
662
|
+
'Use for web pages with secured protocols when regular interactions fail.',
|
|
663
|
+
'Applicable on iOS only with specific APIs.',
|
|
664
|
+
],
|
|
304
665
|
},
|
|
305
666
|
],
|
|
306
|
-
}
|
|
667
|
+
},
|
|
307
668
|
],
|
|
308
669
|
},
|
|
309
670
|
|
|
310
671
|
captureSnapshotImage: async function () {
|
|
311
|
-
|
|
672
|
+
const fileName = `snapshot_${Date.now()}.png`;
|
|
312
673
|
try {
|
|
313
674
|
return await detox.device.takeScreenshot(fileName);
|
|
314
675
|
} catch (_error) {
|
|
@@ -81,6 +81,8 @@ class DetoxContext {
|
|
|
81
81
|
|
|
82
82
|
web = funpermaproxy.callable(() => this[symbols.worker].web);
|
|
83
83
|
|
|
84
|
+
system = funpermaproxy.callable(() => this[symbols.worker].system);
|
|
85
|
+
|
|
84
86
|
copilot = funpermaproxy.callable(() => this[symbols.worker].copilot);
|
|
85
87
|
|
|
86
88
|
get DetoxConstants() {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
00e771ad7d4bca70272e33279c46e466
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
a6159ac03cb71d378a64cc5ce2edd1785dbda1f9
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
34a26f4fb8f6602196e9ea8cc9e437b30a6f7969ae25aed3b38194b15a342a07
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0d2eb7a4c0e4704bcc18cece68a788f14a4b5daea69b2b629acb3cefe7952e38e605f7ed827338ac375ee840802b93c9a10e4fe8155cb9be7b2af9a499d69941
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
14161501d9cf4606ee49296e14c047e4
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
38e956edb72453c00c00ab752443f394389737bf
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0a76b1d5efeea5e83fa9e92e75bc29913b9e7db28b7b30652638966506299f90
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
f3b3510a065744da054200cbcfb62549d469aa27068939879f07c64421cfef223aa34ed6346b6e9c5839a1a42cdc76699b16ad4395545abfb01cd611381df408
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
6e2b31eb45632080d05012110384ed2c
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
63a763fcbfa75f8bb710cb574dfe26abf733752c
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
12142ae0599d9ea97ec70c8ab1aea4b0743f1e7c1ca83aacffbabebfeecf92ec
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
fad2af646bb65acc13528d220795c690668eabe5697c1d94495367e6cb73c55b9fc235b06e5baf2cd1d56a5e8a22df8adc6bde726050316168a8f9ce31be6dd6
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
e1481e091b0d7a1c7da0da248773c085
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
72b5129454784fbbd8bf5e5661c6534bd1d9dd63
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
288dd5edc91a6eee6d96834f5357e946fcb45d7e0302473959199bc37e46ad3a
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
d04dfa63bf863d39e689ba99cf45d2e27204d97cbe71d1cc67ed243e3e2b6637bc5c99654e5bd185dc93e89e716817a6247ea6bf965d9473e018596c8d31bb71
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
02d974914d78e64b365486e3369ae1e1
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
173b898bfecd41c34b0ad24d7e1996c8d3b0621b
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
8a30f740e17ad80816f17ceb61b354d7da12f651764e040ddf85b1c3609992a3
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
90f61852b868773b3548daf4c04793cc0285df750cbfb8947375ec81f5882469cf6e6132295e3b115b27d61733c1bb5f4954a14e7246aa8da19e97bd28191c5f
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
31eb9663feea512165f1608e0f4c9660
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
121dadd5b1d1c8db0fe0f7e72da53746e9bd88e8
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0016623b8e8a15087ccfb15a291b3b8f38d784b97f905034b07a9e172354d670
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
44a518be9a5e8c027288c81948beed0484709f201dee662765b1f4761f0073792748394aaae7537ad0e66837caf74ef2d90286da2b65a0dd8850d862d446736d
|