detox 20.27.2 → 20.27.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|