detox 20.27.1 → 20.27.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. package/Detox-android/com/wix/detox/{20.27.1/detox-20.27.1-sources.jar → 20.27.3/detox-20.27.3-sources.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.27.3/detox-20.27.3-sources.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.27.3/detox-20.27.3-sources.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.27.3/detox-20.27.3-sources.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.27.3/detox-20.27.3-sources.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.27.1/detox-20.27.1.pom → 20.27.3/detox-20.27.3.pom} +1 -1
  7. package/Detox-android/com/wix/detox/20.27.3/detox-20.27.3.pom.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.27.3/detox-20.27.3.pom.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.27.3/detox-20.27.3.pom.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.27.3/detox-20.27.3.pom.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  12. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  13. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  14. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  15. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  16. package/Detox-android/com/wix/detox-legacy/{20.27.1/detox-legacy-20.27.1-sources.jar → 20.27.3/detox-legacy-20.27.3-sources.jar} +0 -0
  17. package/Detox-android/com/wix/detox-legacy/20.27.3/detox-legacy-20.27.3-sources.jar.md5 +1 -0
  18. package/Detox-android/com/wix/detox-legacy/20.27.3/detox-legacy-20.27.3-sources.jar.sha1 +1 -0
  19. package/Detox-android/com/wix/detox-legacy/20.27.3/detox-legacy-20.27.3-sources.jar.sha256 +1 -0
  20. package/Detox-android/com/wix/detox-legacy/20.27.3/detox-legacy-20.27.3-sources.jar.sha512 +1 -0
  21. package/Detox-android/com/wix/detox-legacy/{20.27.1/detox-legacy-20.27.1.pom → 20.27.3/detox-legacy-20.27.3.pom} +1 -1
  22. package/Detox-android/com/wix/detox-legacy/20.27.3/detox-legacy-20.27.3.pom.md5 +1 -0
  23. package/Detox-android/com/wix/detox-legacy/20.27.3/detox-legacy-20.27.3.pom.sha1 +1 -0
  24. package/Detox-android/com/wix/detox-legacy/20.27.3/detox-legacy-20.27.3.pom.sha256 +1 -0
  25. package/Detox-android/com/wix/detox-legacy/20.27.3/detox-legacy-20.27.3.pom.sha512 +1 -0
  26. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml +4 -4
  27. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.md5 +1 -1
  28. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha1 +1 -1
  29. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha256 +1 -1
  30. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha512 +1 -1
  31. package/Detox-ios-framework.tbz +0 -0
  32. package/Detox-ios-src.tbz +0 -0
  33. package/Detox-ios-xcuitest.tbz +0 -0
  34. package/package.json +3 -3
  35. package/src/DetoxWorker.js +2 -0
  36. package/src/copilot/DetoxCopilot.js +2 -2
  37. package/src/copilot/detoxCopilotFrameworkDriver.js +459 -121
  38. package/src/realms/DetoxContext.js +2 -0
  39. package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1-sources.jar.md5 +0 -1
  40. package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1-sources.jar.sha1 +0 -1
  41. package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1-sources.jar.sha256 +0 -1
  42. package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1-sources.jar.sha512 +0 -1
  43. package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.pom.md5 +0 -1
  44. package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.pom.sha1 +0 -1
  45. package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.pom.sha256 +0 -1
  46. package/Detox-android/com/wix/detox/20.27.1/detox-20.27.1.pom.sha512 +0 -1
  47. package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1-sources.jar.md5 +0 -1
  48. package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1-sources.jar.sha1 +0 -1
  49. package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1-sources.jar.sha256 +0 -1
  50. package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1-sources.jar.sha512 +0 -1
  51. package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.pom.md5 +0 -1
  52. package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.pom.sha1 +0 -1
  53. package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.pom.sha256 +0 -1
  54. package/Detox-android/com/wix/detox-legacy/20.27.1/detox-legacy-20.27.1.pom.sha512 +0 -1
  55. /package/Detox-android/com/wix/detox/{20.27.1/detox-20.27.1.aar → 20.27.3/detox-20.27.3.aar} +0 -0
  56. /package/Detox-android/com/wix/detox/{20.27.1/detox-20.27.1.aar.md5 → 20.27.3/detox-20.27.3.aar.md5} +0 -0
  57. /package/Detox-android/com/wix/detox/{20.27.1/detox-20.27.1.aar.sha1 → 20.27.3/detox-20.27.3.aar.sha1} +0 -0
  58. /package/Detox-android/com/wix/detox/{20.27.1/detox-20.27.1.aar.sha256 → 20.27.3/detox-20.27.3.aar.sha256} +0 -0
  59. /package/Detox-android/com/wix/detox/{20.27.1/detox-20.27.1.aar.sha512 → 20.27.3/detox-20.27.3.aar.sha512} +0 -0
  60. /package/Detox-android/com/wix/detox-legacy/{20.27.1/detox-legacy-20.27.1.aar → 20.27.3/detox-legacy-20.27.3.aar} +0 -0
  61. /package/Detox-android/com/wix/detox-legacy/{20.27.1/detox-legacy-20.27.1.aar.md5 → 20.27.3/detox-legacy-20.27.3.aar.md5} +0 -0
  62. /package/Detox-android/com/wix/detox-legacy/{20.27.1/detox-legacy-20.27.1.aar.sha1 → 20.27.3/detox-legacy-20.27.3.aar.sha1} +0 -0
  63. /package/Detox-android/com/wix/detox-legacy/{20.27.1/detox-legacy-20.27.1.aar.sha256 → 20.27.3/detox-legacy-20.27.3.aar.sha256} +0 -0
  64. /package/Detox-android/com/wix/detox-legacy/{20.27.1/detox-legacy-20.27.1.aar.sha512 → 20.27.3/detox-legacy-20.27.3.aar.sha512} +0 -0
@@ -0,0 +1 @@
1
+ 8cd2f48786d004ff90fcc48db9b9e69a
@@ -0,0 +1 @@
1
+ 4afea56e335fc27ee2b92be48673dea8a05185aa
@@ -0,0 +1 @@
1
+ 0568be3312760e3a3a4259cc771a7253074502547e8eb3350213cde9f8db6165
@@ -0,0 +1 @@
1
+ 2287316f0b862561456bef92c561598fcff17e286f17bc262470ef25cc6f71e84b16a8058e37f7f1536fc3a0433df990ba20173dd132b4c561c71e62a07e2fe6
@@ -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.1</version>
6
+ <version>20.27.3</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
+ 2d7e0ca8745e2abb2087e946367c0538
@@ -0,0 +1 @@
1
+ 071d2ecd2ac499ecef2bff9c5b3f830c613709ca
@@ -0,0 +1 @@
1
+ 865a27d4ce918936ef0a629b2eb2c21dedc9e21629710732b2eb8ae9fd4374df
@@ -0,0 +1 @@
1
+ 58f3c7bc9e6bbc5885f03460545c4832bab2c29490afcbc003d5ee0acb3cf6975f0d890292d4ac587d73c986a42e1c67624617f6fee6da9163ccf76ee5d730ab
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>20.27.1</latest>
7
- <release>20.27.1</release>
6
+ <latest>20.27.3</latest>
7
+ <release>20.27.3</release>
8
8
  <versions>
9
- <version>20.27.1</version>
9
+ <version>20.27.3</version>
10
10
  </versions>
11
- <lastUpdated>20240923182824</lastUpdated>
11
+ <lastUpdated>20241009154552</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- b0663aafff90f42ece492a2bcaf7baec
1
+ 27e42826d06e4da1ec0844db0255a70c
@@ -1 +1 @@
1
- 1001cbc58bbfa8763defd8cef091b50046364d02
1
+ 8a719a4551a0fde056e3f53cd841131fe908fc4d
@@ -1 +1 @@
1
- 07d7f8da49dbf7ab284acb34967da94bb84c6cea2c8ee0898811a775ae7450fe
1
+ ad19b8a5540aabca7a9ea07890509a1e9161848db2682d1c82274168fcf710ef
@@ -1 +1 @@
1
- 2fb37d07f83c220fac552876664268022630f22a2b51cdb3392655832f3dd24bb712e0ebc2128dde7d72f3cd57599b80d0d021e86737ce69a28fab47bccb69a5
1
+ b4fcc3ee40ae9bb22b6c97fd768a3b52281ca32229fd3fc50d8512b81ccec8cbbc3c94f03be1e843016fe97de37a5042938ba549e512894d6c08686ebf2d43c2
@@ -0,0 +1 @@
1
+ b4efad81e134dacbb7d81b566aed6d89
@@ -0,0 +1 @@
1
+ e994e4dad3f2dce5f5fde279bc6cd917ee6b2f07
@@ -0,0 +1 @@
1
+ 7e9153002e13fa5ffeef31eeda43b00e38bacbcba46640ce59fb42220a51c22a
@@ -0,0 +1 @@
1
+ 818984bbddf7bf9561bc6bc9e646cb2f89d5cd83c863aef37c5bf1a3911d08c6a4dc04b8251a85208c7ae308b11e6970a94d33fb2293dbd8bf3c6df6cd55d3ab
@@ -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.1</version>
6
+ <version>20.27.3</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
+ 965972ea4b2739cb5d958a329b9bcc3d
@@ -0,0 +1 @@
1
+ b6d5cfb02046495791ca031d12b9f2310f799a0e
@@ -0,0 +1 @@
1
+ b27221155e7cfcfab8c62c949d47cd71967e19b243c001e53ac518e4cf900769
@@ -0,0 +1 @@
1
+ d9c28fba54e53d5546f30c9b43326175f6ac8cf3194a8d0faea4d97b487198eb994f06249994c579b0f82f7ab0c7685689fe605887668dc2eaa5e659892e388a
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox-legacy</artifactId>
5
5
  <versioning>
6
- <latest>20.27.1</latest>
7
- <release>20.27.1</release>
6
+ <latest>20.27.3</latest>
7
+ <release>20.27.3</release>
8
8
  <versions>
9
- <version>20.27.1</version>
9
+ <version>20.27.3</version>
10
10
  </versions>
11
- <lastUpdated>20240923182941</lastUpdated>
11
+ <lastUpdated>20241009154624</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- fd1de26f13a6099dc0ea8653c61cd67d
1
+ 6ddea16ac39ebcaa72590d9b50ca41a1
@@ -1 +1 @@
1
- ec4629f0d73e338d6ed23b0cad15f2e54b7ade64
1
+ 1ad379b7c3de738b8adebc19d859742fdbf6797a
@@ -1 +1 @@
1
- 84679287bb5c654b7ca9f5c860712d5d327f17670b89dfae4df2e686ee142899
1
+ 61f5bf7772111546173237486ddc5909ee6bc7053595adc3305c0ccfeb927736
@@ -1 +1 @@
1
- 1d231b1ce95ce6608a20498393cfc9c005598bcbe78da9c3595679e315b83a0c41bc93b54be0632a5183a8e233ebe758f1a1fa5d233c5e9b1f532b24e5ce9c7f
1
+ 03e8049bdb4784b4c54b25e9a14b30012ee6212ae6b7329d61ea357ed97f485d1e88a8f2e0d573046020c07fd7681ac9cd84189d195598b867401ef83e97b1e9
Binary file
package/Detox-ios-src.tbz CHANGED
Binary file
Binary file
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.1",
4
+ "version": "20.27.3",
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.8",
74
+ "detox-copilot": "^0.0.14",
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": "a45478f98e0ad2d64327923ce191c314d8c27596"
119
+ "gitHead": "4ccef46fa6cbc2526d02af45f3b2ca92f6e5d1df"
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: 'whileElement(element: Matcher)',
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
- },
198
- {
199
- signature: 'launchApp(params: object)',
200
- description: 'Launches the app with specified parameters.',
201
- example: 'await device.launchApp({newInstance: true});',
202
- guidelines: ['Use this to launch the app with specific configurations.'],
203
168
  },
204
169
  {
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,27 +246,412 @@ 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
+ `,
297
289
  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.'],
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
+ 'Check the platform with `device.getPlatform()` before using, as it is iOS-specific',
339
+ '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.',
340
+ ]
341
+ },
342
+ {
343
+ signature: 'by.system.label(label: string)',
344
+ description: 'Matches system elements by label.',
345
+ example: "system.element(by.system.label('Dismiss'));",
346
+ },
347
+ {
348
+ signature: 'by.system.type(type: string)',
349
+ description: 'Matches system elements by type.',
350
+ example: "system.element(by.system.type('button'));",
351
+ },
352
+ {
353
+ signature: 'tap()',
354
+ description: 'Taps on a system element.',
355
+ example: "system.element(by.system.label('Allow')).tap();",
356
+ },
357
+ {
358
+ signature: 'toExist()',
359
+ description: 'Asserts that the system element exists.',
360
+ example: "await system.element(by.system.label('Allow')).toExist();",
361
+ },
362
+ {
363
+ signature: 'not',
364
+ description: 'Negates the expectation for system elements.',
365
+ example: "await system.element(by.system.label('Allow')).not.toExist();",
304
366
  },
305
367
  ],
306
- }
368
+ },
369
+ {
370
+ title: 'Web APIs',
371
+ items: [
372
+ {
373
+ signature: 'web.element(matcher: Matcher)',
374
+ description: 'Selects an element within a web view. Use when there is only one web view on the screen.',
375
+ example: "const element = web.element(by.web.id('username'));",
376
+ guidelines: [
377
+ 'Web APIs can only be used with web elements (within web views).',
378
+ 'Avoid using web APIs for native elements or native APIs for web elements.',
379
+ 'Always prefer the `by.web.id` matcher when possible.',
380
+ ],
381
+ },
382
+ {
383
+ signature: 'web(nativeMatcher).element(matcher: Matcher)',
384
+ description: 'Selects an element within a specific web view matched by a native matcher. Use when there are multiple web views on the screen.',
385
+ example: "const element = web(by.id('webview')).element(by.web.id('password'));",
386
+ guidelines: [
387
+ 'Use this method when multiple web views are present.',
388
+ 'Prefer `web.element` if only one web view is present on the screen.',
389
+ ],
390
+ },
391
+ {
392
+ signature: 'web(nativeMatcher).atIndex(index: number).element(matcher: Matcher)',
393
+ description: 'Selects an element within a specific web view at a given index (iOS only).',
394
+ example: "const element = web(by.id('webview')).atIndex(1).element(by.web.id('password'));",
395
+ guidelines: [
396
+ 'Use when multiple web views with the same identifier are present on iOS.',
397
+ 'This matcher is available for iOS only.',
398
+ 'Check the platform with `device.getPlatform()` before using.',
399
+ ],
400
+ },
401
+ {
402
+ signature: 'by.web.id(id: string)',
403
+ description: 'Matches web elements by their ID attribute.',
404
+ example: "web.element(by.web.id('submit_button'));",
405
+ guidelines: [
406
+ 'Use for web elements with unique IDs.',
407
+ 'This is the best-practice matcher for web elements.',
408
+ ],
409
+ },
410
+ {
411
+ signature: 'by.web.className(className: string)',
412
+ description: 'Matches web elements by their CSS class name.',
413
+ example: "web.element(by.web.className('btn-primary'));",
414
+ },
415
+ {
416
+ signature: 'by.web.cssSelector(cssSelector: string)',
417
+ description: 'Matches web elements using a CSS selector.',
418
+ example: "web.element(by.web.cssSelector('.container > .item'));",
419
+ },
420
+ {
421
+ signature: 'by.web.name(name: string)',
422
+ description: 'Matches web elements by their name attribute.',
423
+ example: "web.element(by.web.name('email'));",
424
+ },
425
+ {
426
+ signature: 'by.web.xpath(xpath: string)',
427
+ description: 'Matches web elements using an XPath expression.',
428
+ example: "web.element(by.web.xpath('//*[@id=\"submit\"]'));",
429
+ guidelines: [
430
+ 'Use when `by.web.id` is not available.',
431
+ 'XPath matchers can be less performant.',
432
+ ],
433
+ },
434
+ {
435
+ signature: 'by.web.href(href: string)',
436
+ description: 'Matches web elements by their href attribute.',
437
+ example: "web.element(by.web.href('https://example.com'));",
438
+ },
439
+ {
440
+ signature: 'by.web.hrefContains(href: string)',
441
+ description: 'Matches web elements whose href attribute contains the specified string.',
442
+ example: "web.element(by.web.hrefContains('example.com'));",
443
+ },
444
+ {
445
+ signature: 'by.web.tag(tag: string)',
446
+ description: 'Matches web elements by their tag name.',
447
+ example: "web.element(by.web.tag('h1'));",
448
+ },
449
+ {
450
+ signature: 'by.web.value(value: string)',
451
+ description: 'Matches web elements by their value attribute (iOS only).',
452
+ example: "web.element(by.web.value('Submit'));",
453
+ guidelines: ['Available on iOS only.'],
454
+ },
455
+ {
456
+ signature: 'by.web.label(label: string)',
457
+ description: 'Matches web elements by their accessibility label (iOS only, supports `asSecured()`).',
458
+ example: "web.element(by.web.label('Submit')).asSecured();",
459
+ guidelines: [
460
+ 'Available on iOS only.',
461
+ 'Use when the element has a unique label.',
462
+ ],
463
+ },
464
+ {
465
+ signature: 'by.web.type(accessibilityType: string)',
466
+ description: 'Matches web elements by accessibility type (iOS only, with `asSecured()`).',
467
+ example: "web.element(by.web.type('textField')).asSecured();",
468
+ guidelines: [
469
+ 'Available on iOS only and used with `asSecured()`.',
470
+ 'Type can be any XCUIElement.ElementType, e.g., "button", "textField".',
471
+ ],
472
+ },
473
+ {
474
+ signature: 'atIndex(index: number)',
475
+ description: 'Selects the web element at the specified index from matched elements.',
476
+ example: "web.element(by.web.tag('h1')).atIndex(1);",
477
+ guidelines: ['Use when multiple web elements match the same matcher.'],
478
+ },
479
+ {
480
+ signature: 'tap()',
481
+ description: 'Taps on a web element.',
482
+ example: "await web.element(by.web.id('link')).tap();",
483
+ guidelines: [
484
+ 'Supports `asSecured()` on iOS.',
485
+ 'Use `asSecured()` when interacting with secured web views.',
486
+ ],
487
+ },
488
+ {
489
+ signature: 'typeText(text: string, isContentEditable?: boolean)',
490
+ description: 'Types text into a web element.',
491
+ example: "await web.element(by.web.name('search')).typeText('Detox');",
492
+ guidelines: [
493
+ 'Set `isContentEditable` to `true` for content-editable elements on Android.',
494
+ 'On iOS, content-editable elements are automatically detected.',
495
+ 'Supports `asSecured()` on iOS.',
496
+ ],
497
+ },
498
+ {
499
+ signature: 'replaceText(text: string)',
500
+ description: 'Replaces text in a web element.',
501
+ example: "await web.element(by.web.name('search')).replaceText('Detox');",
502
+ guidelines: [
503
+ 'Currently not supported for content-editable elements on Android.',
504
+ 'Supports `asSecured()` on iOS.',
505
+ ],
506
+ },
507
+ {
508
+ signature: 'clearText()',
509
+ description: 'Clears text from a web element.',
510
+ example: "await web.element(by.web.name('search')).clearText();",
511
+ guidelines: [
512
+ 'Currently not supported for content-editable elements on Android.',
513
+ 'Supports `asSecured()` on iOS.',
514
+ ],
515
+ },
516
+ {
517
+ signature: 'selectAllText()',
518
+ description: 'Selects all text in a web element.',
519
+ example: "await web.element(by.web.id('editor')).selectAllText();",
520
+ guidelines: [
521
+ 'Supported for content-editable elements only on Android.',
522
+ 'On iOS, Detox can select all text of any element that supports it.',
523
+ ],
524
+ },
525
+ {
526
+ signature: 'getText()',
527
+ description: 'Retrieves the text content of a web element.',
528
+ example: `
529
+ const text = await web.element(by.web.id('identifier')).getText();
530
+ jestExpect(text).toBe('Hello World!');
531
+ `,
532
+ guidelines: [
533
+ 'Use for assertions on the element\'s text content.',
534
+ 'Requires importing `jestExpect` for assertions.',
535
+ ],
536
+ },
537
+ {
538
+ signature: 'scrollToView()',
539
+ description: 'Scrolls the web view to bring the element into view.',
540
+ example: "await web.element(by.web.id('footer')).scrollToView();",
541
+ },
542
+ {
543
+ signature: 'focus()',
544
+ description: 'Focuses on a web element.',
545
+ example: "await web.element(by.web.id('search')).focus();",
546
+ },
547
+ {
548
+ signature: 'moveCursorToEnd()',
549
+ description: 'Moves the input cursor to the end of the element\'s content.',
550
+ example: "await web.element(by.web.id('editor')).moveCursorToEnd();",
551
+ guidelines: [
552
+ 'Supported for content-editable elements only on Android.',
553
+ 'On iOS, Detox can move the cursor to the end of any element that supports it.',
554
+ ],
555
+ },
556
+ {
557
+ signature: 'runScript(script: string, args?: any[])',
558
+ description: 'Runs a JavaScript function on the element.',
559
+ example: `
560
+ const webElement = web.element(by.web.id('identifier'));
561
+ await webElement.runScript('(el) => el.click()');
562
+
563
+ // With arguments
564
+ await webElement.runScript('(el, args) => el.setAttribute("value", args[0])', ['Detox']);
565
+
566
+ // Using function syntax
567
+ const fontSize = await webElement.runScript(function get(element) {
568
+ return element.style.fontSize;
569
+ });
570
+ jestExpect(fontSize).toBe('16px');
571
+
572
+ // Scrolling to the bottom of a scrollable web-element
573
+ await webElement.runScript('el => el.scrollTop = el.scrollHeight');
574
+ `,
575
+ guidelines: [
576
+ 'The script can accept additional arguments and return a value.',
577
+ 'Ensure that arguments and return values are serializable.',
578
+ 'Useful for custom interactions or retrieving properties.',
579
+ ],
580
+ },
581
+ {
582
+ signature: 'getCurrentUrl()',
583
+ description: 'Retrieves the current URL of the web view.',
584
+ example: `
585
+ const url = await web.element(by.web.id('identifier')).getCurrentUrl();
586
+ jestExpect(url).toBe('https://example.com');
587
+ `,
588
+ guidelines: [
589
+ 'Must be called from an inner element, not the root web view.',
590
+ 'May have issues on Android; check relevant GitHub issues.',
591
+ ],
592
+ },
593
+ {
594
+ signature: 'getTitle()',
595
+ description: 'Retrieves the title of the web view.',
596
+ example: `
597
+ const title = await web.element(by.web.id('identifier')).getTitle();
598
+ jestExpect(title).toBe('Welcome Page');
599
+ `,
600
+ guidelines: [
601
+ 'Must be called from an inner element, not the root web view.',
602
+ ],
603
+ },
604
+ {
605
+ signature: 'toHaveText(text: string)',
606
+ description: 'Asserts that the web element has the specified text.',
607
+ example: "await expect(web.element(by.web.tag('h1'))).toHaveText('Welcome');",
608
+ },
609
+ {
610
+ signature: 'toExist()',
611
+ description: 'Asserts that the web element exists.',
612
+ example: "await expect(web.element(by.web.xpath('//*[@id=\"main\"]'))).toExist();",
613
+ guidelines: [
614
+ 'Supports `asSecured()` on iOS.',
615
+ ],
616
+ },
617
+ {
618
+ signature: 'not',
619
+ description: 'Negates the expectation for web elements.',
620
+ example: "await expect(web.element(by.web.id('error'))).not.toExist();",
621
+ guidelines: [
622
+ 'Supports `asSecured()` on iOS.',
623
+ ],
624
+ },
625
+ {
626
+ signature: 'asSecured()',
627
+ description: 'Interacts with secured web views (iOS only).',
628
+ example: "await web.element(by.web.label('Submit')).asSecured().tap();",
629
+ guidelines: [
630
+ 'Use for web pages with secured protocols when regular interactions fail.',
631
+ 'Available on iOS only and currently experimental.',
632
+ 'Less performant and has fewer APIs.',
633
+ ],
634
+ },
635
+ ],
636
+ },
307
637
  ],
308
638
  },
309
639
 
310
640
  captureSnapshotImage: async function () {
311
641
  const fileName = `snapshot_${Date.now()}.png`;
312
- return await detox.device.takeScreenshot(fileName);
642
+ try {
643
+ return await detox.device.takeScreenshot(fileName);
644
+ } catch (_error) {
645
+ return null;
646
+ }
313
647
  },
314
648
 
315
649
  captureViewHierarchyString: async function () {
316
- return detox.device.generateViewHierarchyXml();
650
+ try {
651
+ return detox.device.generateViewHierarchyXml();
652
+ } catch (_error) {
653
+ return 'Unavailable, app is probably not launched yet';
654
+ }
317
655
  },
318
656
  };
319
657
 
@@ -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
- 6a7785c5beade116fb95f224126e3d94
@@ -1 +0,0 @@
1
- 282a3d71cfb16689e484ad5b8cbdf8b4ffcc2fd4
@@ -1 +0,0 @@
1
- c3a3ba182f11c386c4ec93281f2cbf630a4315cad97289a0f002972c2fb234c9
@@ -1 +0,0 @@
1
- 0babdde000779bbe81fbf61964e4578b2a23ddd8ae8f65aaa48f71e6d3cd1306d473501c44ba95f48416a99a2e5cad8a38ebe85d272616522a0bfe4e09e1878f
@@ -1 +0,0 @@
1
- 4fbbcc9b1460bdaad46d32f629e3d589
@@ -1 +0,0 @@
1
- 961629dada06b32a080e9abe7909bb91032de53a
@@ -1 +0,0 @@
1
- 322e950b4d47c41096aa1525ae34d866e7955bd3d2d091f4b207158a6f622c9b
@@ -1 +0,0 @@
1
- 3117059ac5711d3e06fea0be3a0fcea07f077bc6b7be465a26b7591784da214fadf299c3794eba97fb60bbebced3256943dbb211ac7702132ae9635665d5d788
@@ -1 +0,0 @@
1
- d8fa6581305d5677dc8fc505c979afa4
@@ -1 +0,0 @@
1
- 0bf9b98aede1b304cf57d489f1e043f6ac28ee84
@@ -1 +0,0 @@
1
- 216378cc3328e66e3f7a236cfcdb3672f963f1db10b18499c1ffd8476bcd4b6a
@@ -1 +0,0 @@
1
- 9756c8ae3a816b6285ee16774ea82237da03e24c3dd48f9e564c2251abf97afc6b36952df51de07795f78300d61f37c27b74c6c61477e029afb3068dc57ba8f4
@@ -1 +0,0 @@
1
- 1d711e19c5856f3987b3f2821d591b68
@@ -1 +0,0 @@
1
- a522dde967fb79aed046806088ecb7457f0e938b
@@ -1 +0,0 @@
1
- 7b9667b2fa6ca43e92d90c1b0e2c1054c11458fd4d72730edf45c793672f726c
@@ -1 +0,0 @@
1
- 00d73fa19f105dc273bd44b52c02b73c8eeb70405f61b6bc3ad8a19c640f7b28c829ddbd34fd9c052ed9decec7d199855dcd2eb5f0a191fee2dfc789f1a8ae93