detox 20.27.2 → 20.27.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. 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
  2. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4-sources.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4-sources.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4-sources.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4-sources.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.aar +0 -0
  7. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.aar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.aar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.aar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.aar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/{20.27.2/detox-20.27.2.pom → 20.27.4/detox-20.27.4.pom} +1 -1
  12. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.pom.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.pom.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.pom.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.27.4/detox-20.27.4.pom.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  17. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  18. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  19. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  20. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  21. 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
  22. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4-sources.jar.md5 +1 -0
  23. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4-sources.jar.sha1 +1 -0
  24. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4-sources.jar.sha256 +1 -0
  25. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4-sources.jar.sha512 +1 -0
  26. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.aar +0 -0
  27. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.aar.md5 +1 -0
  28. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.aar.sha1 +1 -0
  29. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.aar.sha256 +1 -0
  30. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.aar.sha512 +1 -0
  31. 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
  32. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.pom.md5 +1 -0
  33. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.pom.sha1 +1 -0
  34. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.pom.sha256 +1 -0
  35. package/Detox-android/com/wix/detox-legacy/20.27.4/detox-legacy-20.27.4.pom.sha512 +1 -0
  36. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml +4 -4
  37. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.md5 +1 -1
  38. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha1 +1 -1
  39. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha256 +1 -1
  40. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha512 +1 -1
  41. package/Detox-ios-framework.tbz +0 -0
  42. package/Detox-ios-src.tbz +0 -0
  43. package/Detox-ios-xcuitest.tbz +0 -0
  44. package/android/detox/src/full/java/com/wix/detox/espresso/hierarchy/ViewHierarchyGenerator.kt +3 -3
  45. package/package.json +3 -3
  46. package/src/DetoxWorker.js +2 -0
  47. package/src/copilot/DetoxCopilot.js +2 -2
  48. package/src/copilot/detoxCopilotFrameworkDriver.js +481 -120
  49. package/src/realms/DetoxContext.js +2 -0
  50. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2-sources.jar.md5 +0 -1
  51. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2-sources.jar.sha1 +0 -1
  52. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2-sources.jar.sha256 +0 -1
  53. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2-sources.jar.sha512 +0 -1
  54. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.aar +0 -0
  55. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.aar.md5 +0 -1
  56. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.aar.sha1 +0 -1
  57. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.aar.sha256 +0 -1
  58. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.aar.sha512 +0 -1
  59. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.pom.md5 +0 -1
  60. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.pom.sha1 +0 -1
  61. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.pom.sha256 +0 -1
  62. package/Detox-android/com/wix/detox/20.27.2/detox-20.27.2.pom.sha512 +0 -1
  63. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2-sources.jar.md5 +0 -1
  64. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2-sources.jar.sha1 +0 -1
  65. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2-sources.jar.sha256 +0 -1
  66. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2-sources.jar.sha512 +0 -1
  67. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.aar +0 -0
  68. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.aar.md5 +0 -1
  69. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.aar.sha1 +0 -1
  70. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.aar.sha256 +0 -1
  71. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.aar.sha512 +0 -1
  72. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.pom.md5 +0 -1
  73. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.pom.sha1 +0 -1
  74. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.pom.sha256 +0 -1
  75. package/Detox-android/com/wix/detox-legacy/20.27.2/detox-legacy-20.27.2.pom.sha512 +0 -1
@@ -0,0 +1 @@
1
+ 9ace4a96235511950308086f1d57d806
@@ -0,0 +1 @@
1
+ 22cc9e5934c1c0ba48217d81dc0a0403d6383378
@@ -0,0 +1 @@
1
+ c4f49a1754ae4e50e6a32cd8f22a51cb920e2a58b8e9da6a5d443aa6715a59f5
@@ -0,0 +1 @@
1
+ 503797c9ab31332033756727a60af64c7797d51b731d88e6e841cba4c477f68ed1df2872602097587c91c5b357377007e7c9cb7e4f2b838172d1f99af52b5334
@@ -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.2</version>
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.2</latest>
7
- <release>20.27.2</release>
6
+ <latest>20.27.4</latest>
7
+ <release>20.27.4</release>
8
8
  <versions>
9
- <version>20.27.2</version>
9
+ <version>20.27.4</version>
10
10
  </versions>
11
- <lastUpdated>20240923232335</lastUpdated>
11
+ <lastUpdated>20241019191347</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 1c332385e258d9b6009780296daaa3fc
1
+ 4e15390d4670f66357943485fa05acea
@@ -1 +1 @@
1
- de539bd41334825c7b6a2b4a0ced6ac398565f01
1
+ 3475202451a2cb5e3534b59b14612e32df033626
@@ -1 +1 @@
1
- 3b985b7f4a71f8c5ef033963bca89765aa0cfbfc2ce3ad18cb29a0b5dbfb1b35
1
+ d1b63410f852b05d5e00c1a0358fd61afe51474be017e2e78fef15e30cb09d44
@@ -1 +1 @@
1
- 0a2022cf985f776334488f905721bc5763777358d92cde155e09a52fd2ef9e305c6dfdaa31e5dc803f63379d9593810de9548e22d5ac2980af12ab0bf5f45950
1
+ 8b1cc9ffb3d076da45ed87bbaa606ccd89be6fc53dd5e57e8f3d3be5ed09eece128d7cbe4f398606441c1f837cf07268e0a35071c90894c2bd0f5f259b8865c7
@@ -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.2</version>
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.2</latest>
7
- <release>20.27.2</release>
6
+ <latest>20.27.4</latest>
7
+ <release>20.27.4</release>
8
8
  <versions>
9
- <version>20.27.2</version>
9
+ <version>20.27.4</version>
10
10
  </versions>
11
- <lastUpdated>20240923232353</lastUpdated>
11
+ <lastUpdated>20241019191405</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 2ccbddedc654d1a7d86d074273584904
1
+ 06da87cac17d41b217b1204e3baac71c
@@ -1 +1 @@
1
- f78d3b392d696605fbec781b65fbdab3bfc414d0
1
+ 3260642fbd27ef17b3f677a27b9060f29ecddd68
@@ -1 +1 @@
1
- 17e844eb29d972ddf79ab194058a250a800d8c6fc37fcda0279765bde8b0997a
1
+ 7aaa8a6d2ea4aed57f4c789a4d2229070c7619b8466e959fe73d930c6d2f09c5
@@ -1 +1 @@
1
- 036197e1f91512738655e81e0ca8053ab9a2c43a83d8705cfaf6300b9b22f9c5bf6c820430de3f9adc5b2fad0b875cbe6a5d9c7f445697ea78eb8d31d3ed6b19
1
+ 437e4f341f9a2a317ff3fc9c8ad606d36974f884172e105b36c2eb256d3245791fc0dabad87c8c23ea76fc9995528af1b6d6b9a5a28f389afe158b0d8318e6da
Binary file
package/Detox-ios-src.tbz CHANGED
Binary file
Binary file
@@ -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["testID"] = newTestId
173
+ attributes["id"] = newTestId
174
174
  } else {
175
- attributes["testID"] = currentTestId
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.2",
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.9",
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": "fe1b57662ad6534772d7d552e85c92d64d484ba1"
119
+ "gitHead": "74a2c1426616e4e65fa830b9b98689e18a0677e3"
120
120
  }
@@ -59,6 +59,8 @@ class DetoxWorker {
59
59
  this.by = null;
60
60
  /** @type {Detox.WebFacade} */
61
61
  this.web = null;
62
+ /** @type {Detox.SystemFacade} */
63
+ this.system = null;
62
64
  /** @type {Detox.DetoxCopilotFacade} */
63
65
  this.copilot = null;
64
66
 
@@ -25,8 +25,8 @@ class DetoxCopilot {
25
25
  copilot.reset();
26
26
  }
27
27
 
28
- perform(intent) {
29
- return copilot.perform(intent);
28
+ perform(...steps) {
29
+ return copilot.perform(...steps);
30
30
  }
31
31
  }
32
32
 
@@ -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: ['Always use test-ids (accessibility identifiers) from the UI hierarchy to identify elements.'],
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: ['Avoid using text matchers when possible; prefer test-ids.'],
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; prefer test-ids.'],
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 a set of matched elements.',
33
+ description: 'Selects the element at the specified index from matched elements.',
34
34
  example: "element(by.id('listItem')).atIndex(2)",
35
- guidelines: ['Use `atIndex` when multiple elements match the same matcher to select a specific one by index.'],
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: ['Typing can only be done on text field elements.'],
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 the slider to a specified position between its minimum and maximum values.',
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: ['Specify direction as "up", "down", "left", or "right".'],
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: ['Specify edge as "top", "bottom", "left", or "right".'],
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 the element to the specified index. (Android only)',
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 on the element.',
120
- example: "await element(by.id('scrollView')).swipe('up', 'slow', 0.5);",
128
+ description: 'Simulates a swipe gesture.',
129
+ example: "await element(by.id('scrollView')).swipe('up');",
121
130
  guidelines: [
122
- 'Specify direction as "up", "down", "left", or "right".',
123
- 'Speed can be "fast", "slow". default is "fast".',
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 a picker column to a specific value (iOS only).',
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 a date picker to a specific date.',
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: 'Triggers an accessibility action.',
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('PinchableScrollView')).pinch(1.1);",
148
- guidelines: ['Use scale < 1 to zoom out, > 1 to zoom in.'],
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
- // Retrieve attributes of an element
165
- const attributes = await element(by.text('Tap Me')).getAttributes();
166
- jestExpect(attributes.text).toBe('Tap Me');
167
-
168
- // Numerical assertions with allowed error range
169
- jestExpect(attributes.frame.x).toBeCloseTo(100, 1);
170
- jestExpect(attributes.frame.y).toBeCloseTo(200, 1);
171
-
172
- // Platform-specific attribute check
173
- if (device.getPlatform() === 'ios') {
174
- jestExpect(attributes.hittable).toBe(true);
175
- } else if (device.getPlatform() === 'android') {
176
- jestExpect(attributes.visibility).toBe('visible');
177
- }
178
- `,
179
- guidelines: [
180
- 'Use this to get properties like text, value, visibility, etc., for assertions or debugging. But only if the regular matchers or assertions are not sufficient.',
181
- 'Note that numerical values like position or size may not be very accurate; consider allowing a small error range in assertions.',
182
- 'Check the platform using `device.getPlatform()` before using platform-specific attributes.',
183
- 'Attributes include text, label, placeholder, enabled, identifier, visible, value, frame (with x, y, width, height), and platform-specific attributes.',
184
- ],
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: 'launchApp(params: object)',
200
- description: 'Launches the app with specified parameters.',
201
- example: 'await device.launchApp({newInstance: true});',
202
- guidelines: ['Use this to launch the app with specific configurations.'],
203
- },
204
- {
205
- signature: 'reloadReactNative()',
206
- description: 'Reloads the React Native JS bundle.',
207
- example: 'await device.reloadReactNative();',
208
- guidelines: ['Faster than `launchApp()`, use when you just need to reset React Native state/logic.'],
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
- 'Asserts that the slider element has the specified normalized position [0, 1], within an optional tolerance.',
263
- example:
264
- "await expect(element(by.id('slider'))).toHaveSliderPosition(0.75);\nawait expect(element(by.id('slider'))).toHaveSliderPosition(0.3113, 0.00001);",
265
- guidelines: ['Use this to verify the slider\'s position. Normalized position is between 0 and 1.'],
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-able element is on/checked or off/unchecked.',
270
- example:
271
- "await expect(element(by.id('switch'))).toHaveToggleValue(true);\nawait expect(element(by.id('checkbox'))).toHaveToggleValue(false);",
272
- guidelines: ['Use this to check the state of toggleable elements.'],
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
- 'Waits until the expectation is resolved for the specified amount of time.',
278
- example:
279
- "await waitFor(element(by.id('bigButton'))).toBeVisible().withTimeout(2000);",
280
- guidelines: ['Use this to set a custom timeout for an expectation.'],
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 jest-assisted assertions. It is already imported in the environment.',
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
- // Use jestExpect for assertions
299
- jestExpect(2 + 2).toBe(4);
300
- jestExpect('hello').toBe('hello');
301
- jestExpect(true).toBeTruthy();
302
- `,
303
- guidelines: ['Use jestExpect for assertions in tests, only when the default expect is not helpful for the specific case.'],
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
- const fileName = `snapshot_${Date.now()}.png`;
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
@@ -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
@@ -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