detox 20.34.5 → 20.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/Detox-android/com/wix/detox/{20.34.5/detox-20.34.5-sources.jar → 20.35.1/detox-20.35.1-sources.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.35.1/detox-20.35.1-sources.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.35.1/detox-20.35.1-sources.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.35.1/detox-20.35.1-sources.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.35.1/detox-20.35.1-sources.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.34.5/detox-20.34.5.pom → 20.35.1/detox-20.35.1.pom} +1 -1
  7. package/Detox-android/com/wix/detox/20.35.1/detox-20.35.1.pom.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.35.1/detox-20.35.1.pom.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.35.1/detox-20.35.1.pom.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.35.1/detox-20.35.1.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-ios-framework.tbz +0 -0
  17. package/Detox-ios-src.tbz +0 -0
  18. package/Detox-ios-xcuitest.tbz +0 -0
  19. package/detox.d.ts +14 -8
  20. package/globals.d.ts +2 -0
  21. package/jest.config.js +1 -1
  22. package/package.json +4 -3
  23. package/src/DetoxWorker.js +16 -10
  24. package/src/pilot/DetoxPilot.js +49 -0
  25. package/src/realms/DetoxContext.js +2 -0
  26. package/Detox-android/com/wix/detox/20.34.5/detox-20.34.5-sources.jar.md5 +0 -1
  27. package/Detox-android/com/wix/detox/20.34.5/detox-20.34.5-sources.jar.sha1 +0 -1
  28. package/Detox-android/com/wix/detox/20.34.5/detox-20.34.5-sources.jar.sha256 +0 -1
  29. package/Detox-android/com/wix/detox/20.34.5/detox-20.34.5-sources.jar.sha512 +0 -1
  30. package/Detox-android/com/wix/detox/20.34.5/detox-20.34.5.pom.md5 +0 -1
  31. package/Detox-android/com/wix/detox/20.34.5/detox-20.34.5.pom.sha1 +0 -1
  32. package/Detox-android/com/wix/detox/20.34.5/detox-20.34.5.pom.sha256 +0 -1
  33. package/Detox-android/com/wix/detox/20.34.5/detox-20.34.5.pom.sha512 +0 -1
  34. package/src/copilot/DetoxCopilot.js +0 -21
  35. package/src/copilot/detoxCopilotFrameworkDriver.js +0 -703
  36. /package/Detox-android/com/wix/detox/{20.34.5/detox-20.34.5.aar → 20.35.1/detox-20.35.1.aar} +0 -0
  37. /package/Detox-android/com/wix/detox/{20.34.5/detox-20.34.5.aar.md5 → 20.35.1/detox-20.35.1.aar.md5} +0 -0
  38. /package/Detox-android/com/wix/detox/{20.34.5/detox-20.34.5.aar.sha1 → 20.35.1/detox-20.35.1.aar.sha1} +0 -0
  39. /package/Detox-android/com/wix/detox/{20.34.5/detox-20.34.5.aar.sha256 → 20.35.1/detox-20.35.1.aar.sha256} +0 -0
  40. /package/Detox-android/com/wix/detox/{20.34.5/detox-20.34.5.aar.sha512 → 20.35.1/detox-20.35.1.aar.sha512} +0 -0
@@ -0,0 +1 @@
1
+ e3226500b003cc27b7f4bc0bc8243bc5
@@ -0,0 +1 @@
1
+ d642fd124d246aba966e01eeede0913ad73c12ca
@@ -0,0 +1 @@
1
+ 9068b93dce4da1d43316cf688a92538c478f29662bdd25da202c924bc77ccc30
@@ -0,0 +1 @@
1
+ d83a8916edbb21d0e1a979fbdc2b46c6099b4810b73ccf3384c8c7a0f9cd41670695657a52d21114d6d66209b793c769d57bf0640f45df0ca2524c3e499791a2
@@ -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.34.5</version>
6
+ <version>20.35.1</version>
7
7
  <packaging>aar</packaging>
8
8
  <name>Detox</name>
9
9
  <description>Gray box end-to-end testing and automation library for mobile apps</description>
@@ -0,0 +1 @@
1
+ 8dd88226651075c2d0e5060455f8ceeb
@@ -0,0 +1 @@
1
+ da5df4bc14940dd8abecc10b5ed55995d7363ecb
@@ -0,0 +1 @@
1
+ 954cd12e5219568664424f1bff533636278ba644370c850f8b9fa19a418d26cd
@@ -0,0 +1 @@
1
+ 704632d25f72b81388b3606ba5b98b75fe107ec4096f8f1a1f0ca94fb982a45545d206a330b51013182d7a1c7ed5c3cc185b00cc16caadf28abc0677f8a72d4d
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>20.34.5</latest>
7
- <release>20.34.5</release>
6
+ <latest>20.35.1</latest>
7
+ <release>20.35.1</release>
8
8
  <versions>
9
- <version>20.34.5</version>
9
+ <version>20.35.1</version>
10
10
  </versions>
11
- <lastUpdated>20250311134145</lastUpdated>
11
+ <lastUpdated>20250323102355</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 124aa88e915e9b18fd8a733a6b941d15
1
+ 2410bf1198dd0b83c799471e210f0734
@@ -1 +1 @@
1
- 9049aefa5e582d75a4f797598a69c570698aa159
1
+ 8f5b95d484a8bfc4160e619dc92463ac18de6ba9
@@ -1 +1 @@
1
- ac5eced7982a7c3f738b02c2431dadc151c7c34ca8bc5ad543fb8ab44133501a
1
+ ddcad5d44a4f583e715d4888d2262567d269ac93f060d5f07362852f412ea8bf
@@ -1 +1 @@
1
- a11b2a21800e4ea7748f5cb337929eba7c3e1070057306ea31821d6f18c256522c46262370f85f07d5e098d6bd4eb4f2c5ac23c0752c06f53a891f0cd6e9cdc4
1
+ 81f718f2022c141466e5794af41292d33156eab610bcc68d16fc12a5b80a29f97090f5519b3113be79f6ed8d32d7c2fdb24db71b5404aa904dffd102495181da
Binary file
package/Detox-ios-src.tbz CHANGED
Binary file
Binary file
package/detox.d.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  // * Dor Ben Baruch <https://github.com/Dor256>
10
10
 
11
11
  import { BunyanDebugStreamOptions } from 'bunyan-debug-stream';
12
- import { CopilotFacade, PromptHandler } from "detox-copilot";
12
+ import type { Pilot, PromptHandler as _PromptHandler } from '@wix-pilot/core'
13
13
 
14
14
  declare global {
15
15
  namespace Detox {
@@ -446,7 +446,13 @@ declare global {
446
446
 
447
447
  readonly system: SystemFacade;
448
448
 
449
- readonly copilot: DetoxCopilotFacade;
449
+ readonly pilot: PilotFacade;
450
+
451
+ /**
452
+ * @deprecated This API is deprecated and will be removed in the next major version.
453
+ * Please use `pilot` instead of `pilot`.
454
+ */
455
+ readonly copilot: PilotFacade;
450
456
 
451
457
  readonly DetoxConstants: {
452
458
  userNotificationTriggers: {
@@ -1329,17 +1335,17 @@ declare global {
1329
1335
  element(systemMatcher: SystemMatcher): IndexableSystemElement;
1330
1336
  }
1331
1337
 
1332
- interface DetoxCopilotFacade extends Pick<CopilotFacade, "perform"> {
1338
+ interface PilotFacade extends Pick<Pilot, "perform" | "autopilot" | "extendAPICatalog"> {
1333
1339
  /**
1334
- * Initializes the Copilot with the given prompt handler.
1335
- * Must be called before any other Copilot methods.
1336
- * @note Copilot APIs are still in experimental phase and are subject to changes in the near future.
1340
+ * Initializes the Pilot with the given prompt handler.
1341
+ * Must be called before any other Pilot methods.
1342
+ * @note Wix-Pilot APIs are still in experimental phase and are subject to changes in the near future.
1337
1343
  * @param promptHandler The prompt handler to use.
1338
1344
  */
1339
- init: (promptHandler: DetoxCopilotPromptHandler) => void;
1345
+ init: (promptHandler: PromptHandler) => void;
1340
1346
  }
1341
1347
 
1342
- interface DetoxCopilotPromptHandler extends PromptHandler {}
1348
+ type PromptHandler = _PromptHandler;
1343
1349
 
1344
1350
  interface IndexableSystemElement extends SystemElement {
1345
1351
  /**
package/globals.d.ts CHANGED
@@ -10,6 +10,7 @@ declare global {
10
10
  const web: Detox.DetoxExportWrapper['web'];
11
11
  const system: Detox.DetoxExportWrapper['system'];
12
12
  const copilot: Detox.DetoxExportWrapper['copilot'];
13
+ const pilot: Detox.DetoxExportWrapper['pilot'];
13
14
 
14
15
  namespace NodeJS {
15
16
  interface Global {
@@ -22,6 +23,7 @@ declare global {
22
23
  web: Detox.DetoxExportWrapper['web'];
23
24
  system: Detox.DetoxExportWrapper['system'];
24
25
  copilot: Detox.DetoxExportWrapper['copilot'];
26
+ pilot: Detox.DetoxExportWrapper['pilot'];
25
27
  }
26
28
  }
27
29
  }
package/jest.config.js CHANGED
@@ -75,7 +75,7 @@ module.exports = {
75
75
  'src/DetoxWorker.js',
76
76
  'src/logger/utils/streamUtils.js',
77
77
  'src/realms',
78
- 'src/copilot',
78
+ 'src/pilot',
79
79
  ],
80
80
  resetMocks: true,
81
81
  resetModules: true,
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.34.5",
4
+ "version": "20.35.1",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -68,12 +68,13 @@
68
68
  "wtfnode": "^0.9.1"
69
69
  },
70
70
  "dependencies": {
71
+ "@wix-pilot/core": "^3.1.4",
72
+ "@wix-pilot/detox": "^1.0.8",
71
73
  "ajv": "^8.6.3",
72
74
  "bunyan": "^1.8.12",
73
75
  "bunyan-debug-stream": "^3.1.0",
74
76
  "caf": "^15.0.1",
75
77
  "chalk": "^4.0.0",
76
- "detox-copilot": "^0.0.27",
77
78
  "execa": "^5.1.1",
78
79
  "find-up": "^5.0.0",
79
80
  "fs-extra": "^11.0.0",
@@ -119,5 +120,5 @@
119
120
  "browserslist": [
120
121
  "node 14"
121
122
  ],
122
- "gitHead": "3ad6fc5da870368e43d5b09b72c4d844b8342ace"
123
+ "gitHead": "ac0a00f69943dff669c1fb67b8b740292d7a4a7b"
123
124
  }
@@ -1,12 +1,11 @@
1
1
  const CAF = require('caf');
2
- const copilot = require('detox-copilot').default;
3
2
  const _ = require('lodash');
4
3
 
5
4
  const Client = require('./client/Client');
6
- const DetoxCopilot = require('./copilot/DetoxCopilot');
7
5
  const environmentFactory = require('./environmentFactory');
8
6
  const { DetoxRuntimeErrorComposer } = require('./errors');
9
7
  const { InvocationManager } = require('./invoke');
8
+ const DetoxPilot = require('./pilot/DetoxPilot');
10
9
  const symbols = require('./realms/symbols');
11
10
  const AsyncEmitter = require('./utils/AsyncEmitter');
12
11
  const uuid = require('./utils/uuid');
@@ -62,8 +61,8 @@ class DetoxWorker {
62
61
  this.web = null;
63
62
  /** @type {Detox.SystemFacade} */
64
63
  this.system = null;
65
- /** @type {Detox.DetoxCopilotFacade} */
66
- this.copilot = new DetoxCopilot();
64
+ /** @type {Detox.PilotFacade} */
65
+ this.pilot = null;
67
66
 
68
67
  this._deviceCookie = null;
69
68
 
@@ -103,7 +102,6 @@ class DetoxWorker {
103
102
  // @ts-ignore
104
103
  this._sessionConfig.sessionId = sessionConfig.sessionId || uuid.UUID();
105
104
  this._runtimeErrorComposer.appsConfig = this._appsConfig;
106
-
107
105
  this._client = new Client(sessionConfig);
108
106
  this._client.terminateApp = async () => {
109
107
  // @ts-ignore
@@ -111,6 +109,7 @@ class DetoxWorker {
111
109
  await this.device.terminateApp();
112
110
  }
113
111
  };
112
+ this.pilot = new DetoxPilot();
114
113
 
115
114
  yield this._client.connect();
116
115
 
@@ -163,12 +162,19 @@ class DetoxWorker {
163
162
  const injectedGlobals = {
164
163
  ...matchers,
165
164
  device: this.device,
166
- copilot: this.copilot,
165
+ pilot: this.pilot,
167
166
  detox: this,
168
167
  };
169
168
 
170
169
  this._injectedGlobalProperties = Object.keys(injectedGlobals);
171
170
  Object.assign(DetoxWorker.global, injectedGlobals);
171
+ Object.defineProperty(DetoxWorker.global, 'copilot', {
172
+ get: () => {
173
+ console.warn('Warning: "copilot" is deprecated. Please use "pilot" instead.');
174
+ return this.pilot;
175
+ },
176
+ configurable: true,
177
+ });
172
178
  }
173
179
 
174
180
  // @ts-ignore
@@ -226,8 +232,8 @@ class DetoxWorker {
226
232
  };
227
233
 
228
234
  onTestStart = function* (_signal, testSummary){
229
- if (copilot.isInitialized()) {
230
- copilot.start();
235
+ if (this.pilot.isInitialized()) {
236
+ this.pilot.start();
231
237
  }
232
238
 
233
239
  this._validateTestSummary('beforeEach', testSummary);
@@ -258,8 +264,8 @@ class DetoxWorker {
258
264
  testName: testSummary.fullName,
259
265
  });
260
266
 
261
- if (copilot.isInitialized()) {
262
- copilot.end(testSummary.status !== 'passed');
267
+ if (this.pilot.isInitialized()) {
268
+ this.pilot.end(testSummary.status === 'passed');
263
269
  }
264
270
  };
265
271
 
@@ -0,0 +1,49 @@
1
+ const { Pilot } = require('@wix-pilot/core');
2
+ const { DetoxFrameworkDriver } = require('@wix-pilot/detox');
3
+
4
+ const detox = require('../..');
5
+
6
+ /** @type {Detox.PilotFacade} */
7
+ class DetoxPilot {
8
+ init(promptHandler) {
9
+ this.pilot = new Pilot({
10
+ frameworkDriver: new DetoxFrameworkDriver(detox),
11
+ promptHandler: promptHandler
12
+ });
13
+ }
14
+
15
+ start(){
16
+ if (!this.isInitialized()) {
17
+ throw new Error('DetoxPilot is not initialized');
18
+ }
19
+ return this.pilot.start();
20
+ }
21
+
22
+ perform(...steps){
23
+ if (!this.isInitialized()) {
24
+ throw new Error('DetoxPilot is not initialized');
25
+ }
26
+ return this.pilot.perform(...steps);
27
+ }
28
+
29
+ autopilot(goal) {
30
+ return this.pilot.autopilot(goal);
31
+ }
32
+
33
+ extendAPICatalog(categories, context) {
34
+ return this.pilot.extendAPICatalog(categories, context);
35
+ }
36
+
37
+ end(shouldSaveInCache){
38
+ if(!this.isInitialized()){
39
+ throw new Error('DetoxPilot is not initialized');
40
+ }
41
+ return this.pilot.end(shouldSaveInCache);
42
+ }
43
+
44
+ isInitialized(){
45
+ return !!this.pilot;
46
+ }
47
+ }
48
+
49
+ module.exports = DetoxPilot;
@@ -83,6 +83,8 @@ class DetoxContext {
83
83
 
84
84
  system = funpermaproxy.callable(() => this[symbols.worker].system);
85
85
 
86
+ pilot = funpermaproxy.callable(() => this[symbols.worker].pilot);
87
+
86
88
  copilot = funpermaproxy.callable(() => this[symbols.worker].copilot);
87
89
 
88
90
  get DetoxConstants() {
@@ -1 +0,0 @@
1
- bd14839975954a26f5a5de90ea5db58d
@@ -1 +0,0 @@
1
- 4a95b5deea127463d33a0082724efa15c0219b7b
@@ -1 +0,0 @@
1
- 334fcb0435039628b166d5d24a9bc16d548458acdbace630b63a64f8e8392cf5
@@ -1 +0,0 @@
1
- 7e465d127ecc42d3dd2f3965cccecbf9925daaca5f1aef7e91004d14fa3b6ab0e7853fa7343b75123c75b6a803734f8ca5ac9d7441e1c69e310aa76637d3f3fd
@@ -1 +0,0 @@
1
- a21b9bbeda17018156da809bbd4ad378
@@ -1 +0,0 @@
1
- e6fd98f4bd1aa47112a571668f6848d2c3c242bd
@@ -1 +0,0 @@
1
- 6e9772775917f0c4dfb8b7da2399efd534c54bbfd83c356bcf8b888936b73bed
@@ -1 +0,0 @@
1
- b850844b3cacf4524dba60a84d81dbc2630bc028b29aa3a22822ae79986ec47879e9f32ad3492b13a5a86943a388854f0b3f437985d33a730f2fc5418fe021bc
@@ -1,21 +0,0 @@
1
- const copilot = require('detox-copilot').default;
2
-
3
- const detoxCopilotFrameworkDriver = require('./detoxCopilotFrameworkDriver');
4
-
5
- /**
6
- * @typedef {Object} Detox.DetoxCopilotFacade
7
- */
8
- class DetoxCopilot {
9
- init(promptHandler) {
10
- copilot.init({
11
- frameworkDriver: detoxCopilotFrameworkDriver,
12
- promptHandler: promptHandler
13
- });
14
- }
15
-
16
- perform(...steps) {
17
- return copilot.perform(...steps);
18
- }
19
- }
20
-
21
- module.exports = DetoxCopilot;
@@ -1,703 +0,0 @@
1
- // eslint-disable-next-line node/no-extraneous-require
2
- const jestExpect = require('expect').default;
3
-
4
- const detox = require('../..');
5
-
6
- const detoxCopilotFrameworkDriver = {
7
- apiCatalog: {
8
- context: { ...detox, jestExpect },
9
- categories: [
10
- {
11
- title: 'Matchers',
12
- items: [
13
- {
14
- signature: 'by.id(id: string)',
15
- description: 'Matches elements by their test ID.',
16
- example: "element(by.id('loginButton'))",
17
- guidelines: ['Use test IDs (accessibility identifiers) to uniquely identify elements. This is the best-practice matcher.'],
18
- },
19
- {
20
- signature: 'by.text(text: string)',
21
- description: 'Matches elements by their text (value).',
22
- example: "element(by.text('Login'))",
23
- guidelines: ['Prefer test IDs over text matchers when possible.'],
24
- },
25
- {
26
- signature: 'by.type(type: string)',
27
- description: 'Matches elements by their type.',
28
- example: "element(by.type('RCTTextInput'))",
29
- guidelines: ['Use type matchers as a last resort.'],
30
- },
31
- {
32
- signature: 'atIndex(index: number)',
33
- description: 'Selects the element at the specified index from matched elements.',
34
- example: "element(by.id('listItem')).atIndex(2)",
35
- guidelines: ['Use when multiple elements match the same matcher.'],
36
- },
37
- {
38
- signature: 'by.label(label: string)',
39
- description: 'Match elements with the specified label.',
40
- example: "element(by.label('Tuesday, 1 October'));",
41
- guidelines: ['Use when there are no other identifiers, such as for date pickers to select specific days.'],
42
- },
43
- ],
44
- },
45
- {
46
- title: 'Actions',
47
- items: [
48
- {
49
- signature: 'tap(point?: Point2D)',
50
- description: 'Simulates tap on an element.',
51
- example: "await element(by.id('loginButton')).tap();",
52
- },
53
- {
54
- signature: 'longPress(point?: Point2D, duration?: number)',
55
- description: 'Simulates long press on an element.',
56
- example: "await element(by.id('menuItem')).longPress();",
57
- guidelines: ['Tapping on edges of elements might work better when adding a small offset to the point.'],
58
- },
59
- {
60
- signature: 'multiTap(times: number)',
61
- description: 'Simulates multiple taps on an element.',
62
- example: "await element(by.id('tappable')).multiTap(3);",
63
- },
64
- {
65
- signature: 'typeText(text: string)',
66
- description: 'Types text into a text field.',
67
- example: "await element(by.id('usernameInput')).typeText('myusername');",
68
- guidelines: ['Element must be a text input field.'],
69
- },
70
- {
71
- signature: 'replaceText(text: string)',
72
- description: 'Replaces text in a text field.',
73
- example: "await element(by.id('textField')).replaceText('new text');",
74
- },
75
- {
76
- signature: 'clearText()',
77
- description: 'Clears text from a text field.',
78
- example: "await element(by.id('textField')).clearText();",
79
- },
80
- {
81
- signature: 'tapReturnKey()',
82
- description: 'Simulates tapping the return key on the keyboard while the element is focused.',
83
- example: "await element(by.id('textField')).tapReturnKey();",
84
- },
85
- {
86
- signature: 'tapBackspaceKey()',
87
- description: 'Simulates tapping the backspace key on the keyboard while the element is focused.',
88
- example: "await element(by.id('textField')).tapBackspaceKey();",
89
- },
90
- {
91
- signature: 'adjustSliderToPosition(normalizedPosition: number)',
92
- description: 'Adjusts slider to a normalized position between 0 and 1.',
93
- example: "await element(by.id('slider')).adjustSliderToPosition(0.75);",
94
- },
95
- {
96
- signature: 'scroll(offset: number, direction: string, startPositionX?: number, startPositionY?: number)',
97
- description: 'Scrolls an element by an offset in a direction.',
98
- example: "await element(by.id('scrollView')).scroll(100, 'down');",
99
- guidelines: [
100
- 'Direction can be "up", "down", "left", or "right".',
101
- 'Use `startPositionX` and `startPositionY` to specify the starting point of the scroll gesture.',
102
- '`startPositionX` and `startPositionY` are relative to the element\'s width and height, respectively. with values between 0 and 1.',
103
- 'If multiple scroll actions are needed while waiting for an element, use `whileElement()` in conjunction with `waitFor()`.',
104
- ],
105
- },
106
- {
107
- signature: 'scrollTo(edge: string, startPositionX?: number, startPositionY?: number)',
108
- description: 'Scrolls to an edge of the element.',
109
- example: "await element(by.id('scrollView')).scrollTo('bottom');",
110
- guidelines: [
111
- 'Edge can be "top", "bottom", "left", or "right".',
112
- 'Use `startPositionX` and `startPositionY` to specify the starting point of the scroll gesture.',
113
- '`startPositionX` and `startPositionY` are relative to the element\'s width and height, respectively. with values between 0 and 1.',
114
- ],
115
- },
116
- {
117
- signature: 'waitFor(element: Matcher).toBeVisible(percent?: number).whileElement(element: Matcher).scroll(offset: number, direction: string)',
118
- description: 'Continuously performs an action while waiting for an expectation to be fulfilled.',
119
- example: `
120
- await waitFor(element(by.text('Load More')))
121
- .toBeVisible()
122
- .whileElement(by.id('scrollView'))
123
- .scroll(50, 'down');`,
124
- guidelines: [
125
- 'Used in conjunction with `waitFor()` to perform actions like scrolling while waiting for an element to meet the expectation.',
126
- 'The action (e.g., `scroll`) is performed on the element specified in `whileElement()`.',
127
- ],
128
- },
129
- {
130
- signature: 'scrollToIndex(index: number)',
131
- description: 'Scrolls to the specified index (Android only).',
132
- example: "await element(by.id('scrollView')).scrollToIndex(10);",
133
- },
134
- {
135
- signature: 'swipe(direction: string, speed?: string, normalizedOffset?: number)',
136
- description: 'Simulates a swipe gesture.',
137
- example: "await element(by.id('scrollView')).swipe('up');",
138
- guidelines: [
139
- 'Speed can be "fast" or "slow"; default is "fast".',
140
- 'Direction can be "up", "down", "left", or "right".',
141
- 'Up swipe scrolls down, left swipe scrolls right.',
142
- ],
143
- },
144
- {
145
- signature: 'setColumnToValue(column: number, value: string)',
146
- description: 'Sets picker column to a value (iOS only).',
147
- example: "await element(by.id('pickerView')).setColumnToValue(1, '6');",
148
- },
149
- {
150
- signature: 'setDatePickerDate(dateString: string, dateFormat: string)',
151
- description: 'Sets date picker to a specific date.',
152
- example: "await element(by.id('datePicker')).setDatePickerDate('2023-05-25', 'yyyy-MM-dd');",
153
- },
154
- {
155
- signature: 'performAccessibilityAction(actionName: string)',
156
- description: 'Performs an accessibility action.',
157
- example: "await element(by.id('scrollView')).performAccessibilityAction('activate');",
158
- },
159
- {
160
- signature: 'pinch(scale: number, speed?: string, angle?: number)',
161
- description: 'Simulates a pinch gesture.',
162
- example: "await element(by.id('pinchableView')).pinch(1.1);",
163
- guidelines: ['Scale >1 to zoom in, <1 to zoom out.'],
164
- },
165
- {
166
- signature: 'getAttributes()',
167
- description: 'Retrieves attributes of the element.',
168
- example: `
169
- const attributes = await element(by.text('Tap Me')).getAttributes();
170
- jestExpect(attributes.text).toBe('Tap Me');`,
171
- },
172
- {
173
- signature: 'takeScreenshot(name: string)',
174
- description: 'Captures a screenshot of the element.',
175
- example: "const imagePath = await element(by.id('menuRoot')).takeScreenshot('menu_screenshot');",
176
- },
177
- {
178
- signature: 'longPressAndDrag(duration, normalizedPositionX, normalizedPositionY, targetElement, normalizedTargetPositionX, normalizedTargetPositionY, speed, holdDuration)',
179
- description: `Performs a long press and drags to a target element.
180
- - \`duration\` — the duration to press for, in ms (required)
181
- - \`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)
182
- - \`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)
183
- - \`targetElement\` — the target element to drag to (required)
184
- - \`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)
185
- - \`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)
186
- - \`speed\` — the speed of the drag (optional, valid input: "fast"/"slow" , default is "fast")
187
- - \`holdDuration\` — the duration before releasing at the end, in ms (optional, default is 1000)`,
188
- example: "await element(by.id('cellId_1')).longPressAndDrag(2000, 0.9, NaN, element(by.id('cellId_6')), 0.9, NaN, 'slow', 0);",
189
- },
190
- ],
191
- },
192
- {
193
- title: 'Assertions',
194
- items: [
195
- {
196
- signature: 'toBeVisible(percent?: number)',
197
- description: 'Asserts that the element is visible with at-least the specified percentage. Default percent is 75%.',
198
- example: "await expect(element(by.id('loginButton'))).toBeVisible(38);",
199
- guidelines: [
200
- 'Use the default visibility percent unless a different percentage is required.',
201
- 'If a percentage value is provided, use the exact percentage required for the test.',
202
- ],
203
- },
204
- {
205
- signature: 'toExist()',
206
- description: 'Asserts that the element exists.',
207
- example: "await expect(element(by.id('username'))).toExist();",
208
- },
209
- {
210
- signature: 'toHaveText(text: string)',
211
- description: 'Asserts that the element has the specified text.',
212
- example: "await expect(element(by.id('label'))).toHaveText('Hello, World!');",
213
- },
214
- {
215
- signature: 'toHaveValue(value: string)',
216
- description: 'Asserts that the element has the specified value.',
217
- example: "await expect(element(by.id('slider'))).toHaveValue('0.5');",
218
- },
219
- {
220
- signature: 'toBeFocused()',
221
- description: 'Asserts that the element is focused.',
222
- example: "await expect(element(by.id('emailInput'))).toBeFocused();",
223
- },
224
- {
225
- signature: 'toHaveLabel(label: string)',
226
- description: 'Asserts that the element has the specified accessibility label.',
227
- example: "await expect(element(by.id('submitButton'))).toHaveLabel('Submit');",
228
- },
229
- {
230
- signature: 'toHaveId(id: string)',
231
- description: 'Asserts that the element has the specified accessibility identifier.',
232
- example: "await expect(element(by.text('Submit'))).toHaveId('submitButton');",
233
- },
234
- {
235
- signature: 'toHaveSliderPosition(normalizedPosition: number, tolerance?: number)',
236
- description: 'Asserts that the slider is at a normalized position.',
237
- example: "await expect(element(by.id('slider'))).toHaveSliderPosition(0.75);",
238
- },
239
- {
240
- signature: 'toHaveToggleValue(value: boolean)',
241
- description: 'Asserts that a toggle element is on or off.',
242
- example: "await expect(element(by.id('switch'))).toHaveToggleValue(true);",
243
- },
244
- {
245
- signature: 'withTimeout(timeout: number)',
246
- description: 'Waits until the expectation is resolved or timeout occurs.',
247
- example: "await waitFor(element(by.id('bigButton'))).toBeVisible().withTimeout(2000);",
248
- },
249
- {
250
- signature: 'not',
251
- description: 'Negates the expectation.',
252
- example: "await expect(element(by.id('tinyButton'))).not.toBeVisible();",
253
- },
254
- ],
255
- },
256
- {
257
- title: 'Utilities',
258
- items: [
259
- {
260
- signature: 'jestExpect',
261
- description: 'Jest expect utility for additional assertions.',
262
- example: `
263
- jestExpect(2 + 2).toBe(4);
264
- jestExpect('hello').toBe('hello');`,
265
- },
266
- ],
267
- },
268
- {
269
- title: 'Device APIs',
270
- items: [
271
- {
272
- signature: 'device.launchApp(params?: object)',
273
- description: `
274
- Launches the app with specified parameters.
275
-
276
- **Parameters:**
277
- - \`newInstance\` (boolean): If \`true\`, terminates the app and launches a new instance.
278
- - \`delete\` (boolean): If \`true\`, deletes the app data before launching.
279
- - \`launchArgs\` (object): Additional launch arguments as key-value pairs.
280
- - \`url\` (string): URL to open in the app.
281
- - \`permissions\` (object): Permissions to grant the app. Supported permissions are:
282
- | Permission | Values |
283
- |-----------------|----------------------------|
284
- | **location** | always / inuse / never / unset |
285
- | **contacts** | YES / NO / unset / limited |
286
- | **photos** | YES / NO / unset / limited |
287
- | **calendar** | YES / NO / unset |
288
- | **camera** | YES / NO / unset |
289
- | **medialibrary**| YES / NO / unset |
290
- | **microphone** | YES / NO / unset |
291
- | **motion** | YES / NO / unset |
292
- | **reminders** | YES / NO / unset |
293
- | **siri** | YES / NO / unset |
294
- | **notifications**| YES / NO / unset |
295
- | **health** | YES / NO / unset |
296
- | **homekit** | YES / NO / unset |
297
- | **speech** | YES / NO / unset |
298
- | **faceid** | YES / NO / unset |
299
- | **userTracking**| YES / NO / unset |
300
- `,
301
- example: `
302
- await device.launchApp({ newInstance: true });
303
- await device.launchApp({ newInstance: true, permissions: { notifications: 'YES' } });
304
- await device.launchApp({ launchArgs: { someLaunchArg: 1234 } });`,
305
- guidelines: ['Use minimal parameters necessary for your launch scenario.'],
306
- },
307
- {
308
- signature: 'device.reloadReactNative()',
309
- description: 'Reloads the React Native JS bundle.',
310
- example: 'await device.reloadReactNative();',
311
- },
312
- {
313
- signature: 'device.setOrientation(orientation: string)',
314
- description: 'Rotates the device to the specified orientation.',
315
- example: 'await device.setOrientation("landscape");',
316
- guidelines: ['Orientation can be "portrait" or "landscape".'],
317
- },
318
- {
319
- signature: 'device.setLocation(lat: number, lon: number)',
320
- description: 'Sets the device location.',
321
- example: 'await device.setLocation(37.7749, -122.4194);',
322
- },
323
- {
324
- signature: 'device.takeScreenshot(name?: string)',
325
- description: 'Captures a screenshot of the device.',
326
- example: 'const path = await device.takeScreenshot("home_screen");',
327
- },
328
- {
329
- signature: 'device.getPlatform()',
330
- description: 'Returns the current device platform ("ios" or "android").',
331
- example: 'const platform = device.getPlatform();',
332
- guidelines: ['Use to conditionally execute platform-specific code.'],
333
- },
334
- {
335
- signature: 'device.openURL(url: string)',
336
- description: 'Opens a deeplink within the app, or a URL in the browser.',
337
- example: 'await device.openURL("app://home");',
338
- }
339
- ],
340
- },
341
- {
342
- title: 'System APIs (iOS)',
343
- items: [
344
- {
345
- signature: 'system.element(matcher: Matcher)',
346
- description: 'Selects an element within the system UI.',
347
- example: "system.element(by.system.label('Allow')).tap();",
348
- guidelines: [
349
- 'Can be used for iOS system alerts and permissions dialogs',
350
- 'Always use `system.element()` to interact with system dialog elements (alerts, permission requests).',
351
- 'Check the platform with `device.getPlatform()` before using, as it is iOS-specific',
352
- 'Permission alerts are part of the system UI, not the app UI. Therefore should be matched with `system.element()`.',
353
- '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.',
354
- ]
355
- },
356
- {
357
- signature: 'by.system.label(label: string)',
358
- description: 'Matches system elements by their label (text).',
359
- example: "system.element(by.system.label('Dismiss'));",
360
- guidelines: [
361
- 'System elements will not be found in the app\'s view hierarchy. Read the text from the snapshot.',
362
- 'If no system dialog can be found, throw an error with the relevant message.',
363
- ],
364
- },
365
- {
366
- signature: 'by.system.type(type: string)',
367
- description: 'Matches system elements by type.',
368
- example: "system.element(by.system.type('button'));",
369
- },
370
- {
371
- signature: 'tap()',
372
- description: 'Taps on a system element.',
373
- example: "system.element(by.system.label('Allow')).tap();",
374
- },
375
- {
376
- signature: 'toExist()',
377
- description: 'Asserts that the system element exists.',
378
- example: "await expect(system.element(by.system.label('Allow'))).toExist();",
379
- },
380
- {
381
- signature: 'not',
382
- description: 'Negates the expectation for system elements.',
383
- example: "await expect(system.element(by.system.label('Allow'))).not.toExist();",
384
- },
385
- ],
386
- },
387
- {
388
- title: 'Web APIs',
389
- items: [
390
- {
391
- signature: 'web.element(matcher: Matcher)',
392
- description: 'Selects an element within a web view (`WKWebView` or `RNCWebView`). Use when there is only one web view on the screen.',
393
- example: `
394
- await web.element(by.web.id('email')).typeText('test@example.com');
395
- await web.element(by.web.id('password')).typeText('password123');
396
- await web.element(by.web.id('login-button')).tap();
397
- `,
398
- guidelines: [
399
- '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.',
400
- 'After the initial wait, you can interact with web elements without additional delays.',
401
- 'Use `by.web.id` matcher when possible for matching web elements, as it is the most reliable.',
402
- 'Web APIs can only be used with web elements (within web views). Do not use web APIs for native elements or native APIs for web elements!',
403
- 'Confirm that you are targeting a web view before using this method.'
404
- ],
405
- },
406
- {
407
- signature: 'web(nativeMatcher: NativeMatcher).element(matcher: Matcher)',
408
- description: 'Selects an element within a specific web view (`WKWebView` or `RNCWebView`) matched by a native matcher. Use when there are multiple web views on the screen.',
409
- example: `
410
- // Wait for the specific web view to appear and load (only once before interacting)
411
- await expect(element(by.id('checkout-webview'))).toBeVisible();
412
-
413
- // Interact with elements within a specific web view
414
- const specificWebView = web(by.id('checkout-webview'));
415
-
416
- await specificWebView.element(by.web.id('credit-card-number')).typeText('4111111111111111');
417
- await specificWebView.element(by.web.id('expiration-date')).typeText('12/25');
418
- await specificWebView.element(by.web.id('cvv')).typeText('123');
419
- await specificWebView.element(by.web.id('pay-button')).tap();
420
- `,
421
- guidelines: [
422
- 'Use this method when multiple web views are present.',
423
- 'Ensure the web view is visible and its content is loaded before interacting. This wait should happen only once.',
424
- 'After the initial wait, you can interact with elements within the web view without additional delays.',
425
- 'Webview must be matched with `web(nativeMatcher)` (e.g., `web(by.id(..))` instead of `element(by.id(..))`).',
426
- 'Prefer the basic `web.element()` if only one web view is present on the screen.',
427
- 'Confirm that you are targeting a web view before using this method.'
428
- ],
429
- },
430
- {
431
- signature: 'web(nativeMatcher: NativeMatcher).atIndex(index: number).element(matcher: Matcher)',
432
- description: 'Selects an element within a specific web view at a given index (iOS only).',
433
- example: `
434
- // Interact with an element within the second web view (index starts from 0)
435
- const secondWebView = web(by.id('webview')).atIndex(1);
436
- await secondWebView.element(by.web.id('search-input')).typeText('Detox Testing');
437
- await secondWebView.element(by.web.id('search-button')).tap();
438
- `,
439
- guidelines: [
440
- 'Use when multiple web views with the same identifier are present on iOS.',
441
- 'This method is available for iOS only.',
442
- 'Check the platform with `device.getPlatform()` before using.',
443
- ],
444
- },
445
- {
446
- signature: 'by.web.id(id: string)',
447
- description: 'Matches web elements by their "id" attribute.',
448
- example: `await web.element(by.web.id('search')).tap();`,
449
- guidelines: [
450
- 'Prefer `by.web.id` over any other matchers when available.',
451
- ],
452
- },
453
- {
454
- signature: 'by.web.className(className: string)',
455
- description: 'Matches web elements by their CSS class name.',
456
- example: `await web.element(by.web.className('btn-login')).tap();`,
457
- },
458
- {
459
- signature: 'by.web.cssSelector(cssSelector: string)',
460
- description: 'Matches web elements using a CSS selector.',
461
- example: `await web.element(by.web.cssSelector('#product-list .product-item[data-id="123"]')).scrollToView();`,
462
- },
463
- {
464
- signature: 'by.web.name(name: string)',
465
- description: 'Matches web elements by their name attribute.',
466
- example: `await web.element(by.web.name('firstName')).typeText('John');`,
467
- },
468
- {
469
- signature: 'by.web.xpath(xpath: string)',
470
- description: 'Matches web elements using an XPath expression.',
471
- example: `await web.element(by.web.xpath('//button[text()="Continue"]')).tap();`,
472
- },
473
- {
474
- signature: 'by.web.href(href: string)',
475
- description: 'Matches web elements by their href attribute.',
476
- example: `await web.element(by.web.href('https://www.example.com/contact')).tap();`,
477
- },
478
- {
479
- signature: 'by.web.hrefContains(href: string)',
480
- description: 'Matches web elements whose href attribute contains the specified string.',
481
- example: `await web.element(by.web.hrefContains('/about')).tap();`,
482
- },
483
- {
484
- signature: 'by.web.tag(tag: string)',
485
- description: 'Matches web elements by their tag name.',
486
- example: `await expect(web.element(by.web.tag('h1'))).toHaveText('Welcome to Our Site');`,
487
- },
488
- {
489
- signature: 'by.web.value(value: string)',
490
- description: 'Matches web elements by their value attribute (iOS only).',
491
- example: `await web.element(by.web.value('Sign In')).tap();`,
492
- guidelines: [
493
- 'Available on iOS only.',
494
- ],
495
- },
496
- {
497
- signature: 'by.web.label(label: string)',
498
- description: 'Matches web elements by their label (iOS only, supports `asSecured()`).',
499
- example: `await web.element(by.web.label('Next')).tap();`,
500
- guidelines: [
501
- 'Available on iOS only.',
502
- 'Use when the inner web element has a unique label or aria-label.',
503
- 'Can be used to match buttons and input elements inside a web view, by their inner text content.',
504
- ],
505
- },
506
- {
507
- signature: 'by.web.type(accessibilityType: string)',
508
- description: 'Matches web elements by accessibility type (iOS only, `asSecured()` only).',
509
- example: `await web.element(by.web.type('textField')).asSecured().typeText('Sample Text');`,
510
- guidelines: [
511
- 'Available on iOS only and used with `asSecured()`.',
512
- 'Type can be any XCUIElement.ElementType, e.g., "button", "textField".',
513
- ],
514
- },
515
- {
516
- signature: 'atIndex(index: number)',
517
- description: 'Selects the web element at the specified index from matched elements.',
518
- example: `await expect(web.element(by.web.tag('h2')).atIndex(1)).toHaveText('Features');`,
519
- guidelines: [
520
- 'Use when multiple web elements match the same matcher.',
521
- ],
522
- },
523
- {
524
- signature: 'tap()',
525
- description: 'Taps on a web element.',
526
- example: `await web.element(by.web.label('Submit')).tap();`,
527
- guidelines: [
528
- 'Supports `asSecured()` on iOS.',
529
- ],
530
- },
531
- {
532
- signature: 'typeText(text: string)',
533
- description: 'Types text into a web element.',
534
- example: `await web.element(by.web.id('rich-text-editor')).typeText('This is a test message');`,
535
- guidelines: [
536
- 'Supports `asSecured()` on iOS.',
537
- ],
538
- },
539
- {
540
- signature: 'replaceText(text: string)',
541
- description: 'Replaces text in a web element.',
542
- example: `await web.element(by.web.id('username')).replaceText('new_user');`,
543
- guidelines: [
544
- 'Supports `asSecured()` on iOS.',
545
- ],
546
- },
547
- {
548
- signature: 'clearText()',
549
- description: 'Clears text from a web element.',
550
- example: `await web.element(by.web.id('comments')).clearText();`,
551
- guidelines: [
552
- 'Supports `asSecured()` on iOS.',
553
- ],
554
- },
555
- {
556
- signature: 'selectAllText()',
557
- description: 'Selects all text in a web element.',
558
- example: `await web.element(by.web.id('notes')).selectAllText();`,
559
- },
560
- {
561
- signature: 'getText()',
562
- description: 'Retrieves the text content of a web element.',
563
- example: `
564
- const description = await web.element(by.web.id('product-description')).getText();
565
- jestExpect(description).toContain('This product is made from the finest materials.');
566
- `,
567
- guidelines: [
568
- 'Use for assertions on the element\'s text content.',
569
- ],
570
- },
571
- {
572
- signature: 'scrollToView()',
573
- description: 'Scrolls the web view to bring the element into view.',
574
- example: `await web.element(by.web.id('contact-section')).scrollToView();`,
575
- },
576
- {
577
- signature: 'focus()',
578
- description: 'Focuses on a web element.',
579
- example: `
580
- await web.element(by.web.id('email-input')).focus();
581
- await web.element(by.web.id('email-input')).typeText('user@example.com');
582
- `,
583
- guidelines: [
584
- 'Useful for input fields in a web view that require focus before typing.',
585
- 'No need for secured interactions on iOS.',
586
- ]
587
- },
588
- {
589
- signature: 'moveCursorToEnd()',
590
- description: 'Moves the input cursor in a web view to the end of the element\'s content.',
591
- example: `
592
- await web.element(by.web.id('message-box')).moveCursorToEnd();
593
- await web.element(by.web.id('message-box')).typeText(' Adding more text.');
594
- `,
595
- },
596
- {
597
- signature: 'runScript(script: string, args?: any[])',
598
- description: 'Runs a JavaScript function on the web view element.',
599
- example: `
600
- // Click an element using a custom script
601
- await web.element(by.web.id('hidden-button')).runScript('el => el.click()');
602
-
603
- // Set the value of an input field using a custom script
604
- await web.element(by.web.id('username')).runScript('(el, args) => el.value = args[0]', ['new_user']);
605
-
606
- // Get the color of an element
607
- const color = await web.element(by.web.id('header')).runScript(function(el) {
608
- return window.getComputedStyle(el).color;
609
- });
610
- jestExpect(color).toBe('rgb(0, 0, 0)');
611
-
612
- // Scroll an element into view if not visible
613
- await web.element(by.web.id('lazy-image')).runScript('el => el.scrollIntoView()');
614
- `,
615
- guidelines: [
616
- 'The script can accept additional arguments and return a value.',
617
- 'Ensure that arguments and return values are serializable.',
618
- 'Useful for custom interactions or retrieving properties.',
619
- 'Avoid using this method for simple interactions like tapping or typing.',
620
- ],
621
- },
622
- {
623
- signature: 'getCurrentUrl()',
624
- description: 'Retrieves the current URL of the web view.',
625
- example: `
626
- await web.element(by.web.id('link-to-page')).tap();
627
- const currentUrl = await web.element(by.web.tag('body')).getCurrentUrl();
628
- jestExpect(currentUrl).toBe('https://www.example.com/target-page');
629
- `,
630
- guidelines: [
631
- 'Must be called from an inner element, not the root web view.',
632
- ],
633
- },
634
- {
635
- signature: 'getTitle()',
636
- description: 'Retrieves the title of the web view.',
637
- example: `
638
- const pageTitle = await web.element(by.web.tag('body')).getTitle();
639
- jestExpect(pageTitle).toBe('Dashboard - MyApp');
640
- `,
641
- guidelines: [
642
- 'Must be called from an inner element, not the root web view.',
643
- ],
644
- },
645
- {
646
- signature: 'toHaveText(text: string)',
647
- description: 'Asserts that the web element has the specified text. Used with `expect()`.',
648
- example: `await expect(web.element(by.web.name('submit-button'))).toHaveText('Submit');`,
649
- guidelines: [
650
- 'For web views, the text is the inner text of the element, e.g., `<h1>Welcome to the webpage!</h1>`.',
651
- ],
652
- },
653
- {
654
- signature: 'toExist()',
655
- description: 'Asserts that the web element exists. Used with `expect()`.',
656
- example: `await expect(web.element(by.web.id('error-message'))).toExist();`,
657
- guidelines: [
658
- 'Verifies the presence of a web element in the DOM.',
659
- 'Supports `asSecured()` on iOS.',
660
- ],
661
- },
662
- {
663
- signature: 'not',
664
- description: 'Negates the expectation for web elements.',
665
- example: `await expect(web.element(by.web.id('loading-spinner'))).not.toExist();`,
666
- guidelines: [
667
- 'Use `not` to assert that an element should not be present.',
668
- 'Supports `asSecured()` on iOS.',
669
- ],
670
- },
671
- {
672
- signature: 'asSecured()',
673
- description: 'Interacts with secured web views (iOS only).',
674
- example: `await web.element(by.web.label('Confirm')).asSecured().tap();`,
675
- guidelines: [
676
- 'Use for web pages with secured protocols when regular interactions fail.',
677
- 'Applicable on iOS only with specific APIs.',
678
- ],
679
- },
680
- ],
681
- },
682
- ],
683
- },
684
-
685
- captureSnapshotImage: async function () {
686
- const fileName = `snapshot_${Date.now()}.png`;
687
- try {
688
- return await detox.device.takeScreenshot(fileName);
689
- } catch (_error) {
690
- return null;
691
- }
692
- },
693
-
694
- captureViewHierarchyString: async function () {
695
- try {
696
- return detox.device.generateViewHierarchyXml();
697
- } catch (_error) {
698
- return 'Unavailable, app is probably not launched yet';
699
- }
700
- },
701
- };
702
-
703
- module.exports = detoxCopilotFrameworkDriver;