detox 20.34.4 → 20.35.0

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 (51) hide show
  1. package/Detox-android/com/wix/detox/{20.34.4/detox-20.34.4-sources.jar → 20.35.0/detox-20.35.0-sources.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.35.0/detox-20.35.0-sources.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.35.0/detox-20.35.0-sources.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.35.0/detox-20.35.0-sources.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.35.0/detox-20.35.0-sources.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.34.4/detox-20.34.4.pom → 20.35.0/detox-20.35.0.pom} +1 -1
  7. package/Detox-android/com/wix/detox/20.35.0/detox-20.35.0.pom.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.35.0/detox-20.35.0.pom.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.35.0/detox-20.35.0.pom.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.35.0/detox-20.35.0.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/local-cli/utils/frameworkUtils.js +2 -3
  23. package/package.json +6 -5
  24. package/src/DetoxWorker.js +16 -10
  25. package/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js +28 -14
  26. package/src/devices/common/drivers/android/exec/ADB.js +14 -4
  27. package/src/devices/common/drivers/android/exec/BinaryExec.js +8 -7
  28. package/src/devices/runtime/drivers/android/genycloud/GenyCloudDriver.js +6 -0
  29. package/src/devices/runtime/drivers/ios/SimulatorDriver.js +6 -7
  30. package/src/ios/XCUITestRunner.js +2 -3
  31. package/src/pilot/DetoxPilot.js +49 -0
  32. package/src/realms/DetoxContext.js +2 -0
  33. package/src/utils/assertArgument.js +3 -1
  34. package/src/utils/childProcess/exec.js +9 -2
  35. package/src/utils/childProcess/spawn.js +33 -15
  36. package/src/utils/environment.js +10 -11
  37. package/Detox-android/com/wix/detox/20.34.4/detox-20.34.4-sources.jar.md5 +0 -1
  38. package/Detox-android/com/wix/detox/20.34.4/detox-20.34.4-sources.jar.sha1 +0 -1
  39. package/Detox-android/com/wix/detox/20.34.4/detox-20.34.4-sources.jar.sha256 +0 -1
  40. package/Detox-android/com/wix/detox/20.34.4/detox-20.34.4-sources.jar.sha512 +0 -1
  41. package/Detox-android/com/wix/detox/20.34.4/detox-20.34.4.pom.md5 +0 -1
  42. package/Detox-android/com/wix/detox/20.34.4/detox-20.34.4.pom.sha1 +0 -1
  43. package/Detox-android/com/wix/detox/20.34.4/detox-20.34.4.pom.sha256 +0 -1
  44. package/Detox-android/com/wix/detox/20.34.4/detox-20.34.4.pom.sha512 +0 -1
  45. package/src/copilot/DetoxCopilot.js +0 -21
  46. package/src/copilot/detoxCopilotFrameworkDriver.js +0 -703
  47. /package/Detox-android/com/wix/detox/{20.34.4/detox-20.34.4.aar → 20.35.0/detox-20.35.0.aar} +0 -0
  48. /package/Detox-android/com/wix/detox/{20.34.4/detox-20.34.4.aar.md5 → 20.35.0/detox-20.35.0.aar.md5} +0 -0
  49. /package/Detox-android/com/wix/detox/{20.34.4/detox-20.34.4.aar.sha1 → 20.35.0/detox-20.35.0.aar.sha1} +0 -0
  50. /package/Detox-android/com/wix/detox/{20.34.4/detox-20.34.4.aar.sha256 → 20.35.0/detox-20.35.0.aar.sha256} +0 -0
  51. /package/Detox-android/com/wix/detox/{20.34.4/detox-20.34.4.aar.sha512 → 20.35.0/detox-20.35.0.aar.sha512} +0 -0
@@ -0,0 +1 @@
1
+ e10fbe02ae741953ba7fcaf4e49c46b2
@@ -0,0 +1 @@
1
+ 54cd5b84590cf69c3fbe857a45195d431fcc0d03
@@ -0,0 +1 @@
1
+ 9ebc41643bf86a13cc5360a51de676d69cee5d050ba69ff860e0fa3f67f36329
@@ -0,0 +1 @@
1
+ c498fd81f0f898366a6c8916fbe40a60838abd79055b198613ae44b31bd902db16ae32b955924d2b2fe380002589d1830214565409ec4c5e12aae27efad3b38c
@@ -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.4</version>
6
+ <version>20.35.0</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
+ 143eb7c5c6f86392b48530a0e3797140
@@ -0,0 +1 @@
1
+ c30db2dbe5e3d3378ad63879153f4c4c56a6a3c3
@@ -0,0 +1 @@
1
+ 07e8ba7093940f31c77fc95027d7125a8888fb4c2973843d1abef8318e1135e2
@@ -0,0 +1 @@
1
+ c831c274c24a3d1d22682108e90ae976880d96abf02a03cc18e70fdd89a13d487d5e4df6fe725fb7b7bd61d0dada06dda2dd0155fcf25106812efaacf6acbe4d
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>20.34.4</latest>
7
- <release>20.34.4</release>
6
+ <latest>20.35.0</latest>
7
+ <release>20.35.0</release>
8
8
  <versions>
9
- <version>20.34.4</version>
9
+ <version>20.35.0</version>
10
10
  </versions>
11
- <lastUpdated>20250228153149</lastUpdated>
11
+ <lastUpdated>20250315183637</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 06c32f673954b1d53e564d3718cd4944
1
+ 2b2da1caee0ef6508eb79eba3c6ea2b1
@@ -1 +1 @@
1
- 422cf2392817b37bfbffbfcf9a6053133404bb08
1
+ b34995356a8d0d1334d7f0eaf6dbe077a68cbded
@@ -1 +1 @@
1
- 396411db488a866223fb2a3008e903ad29270fc3899fb0bccb6502063a9824fd
1
+ fe9f9de98f86637d4ba74fd60601adf63a52c994b022d94f9d6d7406e6477462
@@ -1 +1 @@
1
- 5247250ffabcf87485dca59afa70c250325b70763c968d7380b5d280cd4fac8115c7dc650ec0d3f6762317cd5fb298c2f2e22b1d0887798fa5363d9ab0a98e53
1
+ 9f5fe00e8e14a49bc02a5f0d643b8ac49c2182acf22739f27e2b9cacb42aff2f014ce7c1e32768154ce030376da6337502bf2b5402464e9f7c76f18ef90e79bf
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,
@@ -1,13 +1,12 @@
1
1
  const os = require('os');
2
2
  const path = require('path');
3
3
 
4
- const { spawn } = require('child-process-promise');
5
4
  const fs = require('fs-extra');
6
5
 
7
6
  const detox = require('../../internals');
7
+ const { spawnAndLog } = require('../../src/utils/childProcess');
8
8
  const { getFrameworkDirPath, getXCUITestRunnerDirPath } = require('../../src/utils/environment');
9
9
 
10
-
11
10
  const frameworkBuildScript = '../../scripts/build_local_framework.ios.sh';
12
11
  const xcuitestBuildScript = '../../scripts/build_local_xcuitest.ios.sh';
13
12
 
@@ -26,7 +25,7 @@ async function execBuildScript(targetPath, scriptPath, descriptor) {
26
25
  const scriptFullPath = path.join(__dirname, scriptPath);
27
26
 
28
27
  try {
29
- await spawn(scriptFullPath, [], { stdio: 'inherit' });
28
+ await spawnAndLog(scriptFullPath, [], { stdio: 'inherit' });
30
29
  } catch (error) {
31
30
  detox.log.error(`Error while building ${descriptor}:\n${error}`);
32
31
  throw error;
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.4",
4
+ "version": "20.35.0",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -64,17 +64,17 @@
64
64
  "prettier": "^3.1.1",
65
65
  "react-native": "0.76.3",
66
66
  "react-native-codegen": "^0.0.8",
67
- "typescript": "^5.3.3",
67
+ "typescript": "~5.3.3",
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
- "child-process-promise": "^2.2.0",
77
- "detox-copilot": "^0.0.27",
78
78
  "execa": "^5.1.1",
79
79
  "find-up": "^5.0.0",
80
80
  "fs-extra": "^11.0.0",
@@ -87,6 +87,7 @@
87
87
  "multi-sort-stream": "^1.0.3",
88
88
  "multipipe": "^4.0.0",
89
89
  "node-ipc": "9.2.1",
90
+ "promisify-child-process": "^4.1.2",
90
91
  "proper-lockfile": "^3.0.2",
91
92
  "resolve-from": "^5.0.0",
92
93
  "sanitize-filename": "^1.6.1",
@@ -119,5 +120,5 @@
119
120
  "browserslist": [
120
121
  "node 14"
121
122
  ],
122
- "gitHead": "ce6e754c352f1670a17ef7560e9e2c1ebb373a20"
123
+ "gitHead": "76d581f3f67875f245eca865e6f3a449332c63bd"
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
 
@@ -31,23 +31,37 @@ function launchEmulatorProcess(emulatorExec, adb, emulatorLaunchCommand) {
31
31
 
32
32
  log = log.child({ child_pid: childProcessPromise.childProcess.pid });
33
33
 
34
- adb.waitForDevice(emulatorLaunchCommand.adbName).then(() => childProcessPromise._cpResolve());
34
+ // Create a deferred promise that resolves when the device is ready
35
+ let resolveEmulatorReady;
36
+ const emulatorReadyPromise = new Promise(resolve => {
37
+ resolveEmulatorReady = resolve;
38
+ });
35
39
 
36
- return childProcessPromise.then(() => true).catch((err) => {
37
- detach();
40
+ // Wait for the device to be ready
41
+ adb.waitForDevice(emulatorLaunchCommand.adbName).then(() => {
42
+ resolveEmulatorReady();
43
+ });
38
44
 
39
- if (childProcessOutput.includes(`There's another emulator instance running with the current AVD`)) {
40
- return false;
41
- }
45
+ // Use Promise.race to resolve with the first one to complete - either the emulator process exits
46
+ // or the emulator device is ready
47
+ return Promise.race([childProcessPromise, emulatorReadyPromise])
48
+ .then(() => true)
49
+ .catch((err) => {
50
+ detach();
42
51
 
43
- log.error({ event: 'SPAWN_FAIL', error: true, err }, err.message);
44
- log.error({ event: 'SPAWN_FAIL', stderr: true }, childProcessOutput);
45
- throw err;
46
- }).then((coldBoot) => {
47
- detach();
48
- log.debug({ event: 'SPAWN_SUCCESS', stdout: true }, childProcessOutput);
49
- return coldBoot;
50
- });
52
+ if (childProcessOutput && childProcessOutput.includes(`There's another emulator instance running with the current AVD`)) {
53
+ return false;
54
+ }
55
+
56
+ log.error({ event: 'SPAWN_FAIL', error: true, err }, err.message);
57
+ log.error({ event: 'SPAWN_FAIL', stderr: true }, childProcessOutput);
58
+ throw err;
59
+ })
60
+ .then((coldBoot) => {
61
+ detach();
62
+ log.debug({ event: 'SPAWN_SUCCESS', stdout: true }, childProcessOutput);
63
+ return coldBoot;
64
+ });
51
65
  }
52
66
 
53
67
  module.exports = { launchEmulatorProcess };
@@ -8,11 +8,19 @@ const { escape } = require('../../../../../utils/pipeCommands');
8
8
  const DeviceHandle = require('../tools/DeviceHandle');
9
9
  const EmulatorHandle = require('../tools/EmulatorHandle');
10
10
 
11
- const INSTALL_TIMEOUT = 45000;
11
+ const DEFAULT_EXEC_OPTIONS = {
12
+ retries: 1,
13
+ };
14
+ const DEFAULT_INSTALL_OPTIONS = {
15
+ timeout: 60000,
16
+ retries: 3,
17
+ };
12
18
 
13
19
  class ADB {
14
20
  constructor() {
15
21
  this._cachedApiLevels = new Map();
22
+ this.defaultExecOptions = DEFAULT_EXEC_OPTIONS;
23
+ this.installOptions = DEFAULT_INSTALL_OPTIONS;
16
24
  this.adbBin = getAdbPath();
17
25
  }
18
26
 
@@ -113,7 +121,7 @@ class ADB {
113
121
  const command = (apiLvl >= 23)
114
122
  ? `install -r -g -t ${apkPath}`
115
123
  : `install -rg ${apkPath}`;
116
- const result = await this.adbCmdSpawned(deviceId, command, { timeout: INSTALL_TIMEOUT, retries: 3 });
124
+ const result = await this.adbCmdSpawned(deviceId, command, this.installOptions);
117
125
 
118
126
  const [failure] = (result.stdout || '').match(/^Failure \[.*\]$/m) || [];
119
127
  if (failure) {
@@ -129,7 +137,7 @@ class ADB {
129
137
  const command = (apiLvl >= 23)
130
138
  ? `pm install -r -g -t ${path}`
131
139
  : `pm install -rg ${path}`;
132
- return this.shellSpawned(deviceId, command, { timeout: INSTALL_TIMEOUT, retries: 3 });
140
+ return this.shellSpawned(deviceId, command, this.installOptions);
133
141
  }
134
142
 
135
143
  async uninstall(deviceId, appId) {
@@ -353,7 +361,7 @@ class ADB {
353
361
  const serial = `${deviceId ? `-s ${deviceId}` : ''}`;
354
362
  const cmd = `"${this.adbBin}" ${serial} ${params}`;
355
363
  const _options = {
356
- retries: 1,
364
+ ...this.defaultExecOptions,
357
365
  ...options,
358
366
  };
359
367
  return execWithRetriesAndLogs(cmd, _options);
@@ -364,8 +372,10 @@ class ADB {
364
372
  const serial = deviceId ? ['-s', deviceId] : [];
365
373
  const _flags = [...serial, ...flags];
366
374
  const _spawnOptions = {
375
+ ...this.defaultExecOptions,
367
376
  ...spawnOptions,
368
377
  capture: ['stdout'],
378
+ encoding: 'utf8',
369
379
  };
370
380
  return spawnWithRetriesAndLogs(this.adbBin, _flags, _spawnOptions);
371
381
  }
@@ -1,7 +1,4 @@
1
- // @ts-nocheck
2
- const spawn = require('child-process-promise').spawn;
3
-
4
- const exec = require('../../../../../utils/childProcess').execWithRetriesAndLogs;
1
+ const { execAsync, spawnAndLog } = require('../../../../../utils/childProcess');
5
2
 
6
3
  class ExecCommand {
7
4
  toString() {
@@ -27,15 +24,19 @@ class BinaryExec {
27
24
  }
28
25
 
29
26
  async exec(command) {
30
- return (await exec(`"${this.binary}" ${command._getArgsString()}`)).stdout;
27
+ return await execAsync(`"${this.binary}" ${command._getArgsString()}`);
31
28
  }
32
29
 
33
30
  spawn(command, stdout, stderr) {
34
- return spawn(this.binary, command._getArgs(), { detached: true, stdio: ['ignore', stdout, stderr] });
31
+ return spawnAndLog(this.binary, command._getArgs(), {
32
+ detached: true,
33
+ encoding: 'utf8',
34
+ stdio: ['ignore', stdout, stderr]
35
+ });
35
36
  }
36
37
  }
37
38
 
38
39
  module.exports = {
39
40
  ExecCommand,
40
- BinaryExec,
41
+ BinaryExec
41
42
  };
@@ -19,6 +19,12 @@ class GenyCloudDriver extends AndroidDriver {
19
19
  constructor(deps, { adbName, name }) {
20
20
  super(deps, { adbName });
21
21
 
22
+ // Make ADB more resilient to network latency and connection hiccups
23
+ this.adb.defaultExecOptions = {
24
+ retries: 5,
25
+ interval: 3000,
26
+ backoff: 'linear',
27
+ };
22
28
  this._instanceName = name;
23
29
  }
24
30
 
@@ -1,13 +1,14 @@
1
1
  // @ts-nocheck
2
2
  const path = require('path');
3
3
 
4
- const exec = require('child-process-promise').exec;
5
4
  const _ = require('lodash');
6
5
 
6
+
7
7
  const temporaryPath = require('../../../../artifacts/utils/temporaryPath');
8
8
  const DetoxRuntimeError = require('../../../../errors/DetoxRuntimeError');
9
9
  const XCUITestRunner = require('../../../../ios/XCUITestRunner');
10
10
  const { assertTraceDescription } = require('../../../../utils/assertArgument');
11
+ const { execAsync } = require('../../../../utils/childProcess');
11
12
  const getAbsoluteBinaryPath = require('../../../../utils/getAbsoluteBinaryPath');
12
13
  const { actionDescription } = require('../../../../utils/invocationTraceDescriptions');
13
14
  const log = require('../../../../utils/logger').child({ cat: 'device' });
@@ -16,7 +17,6 @@ const traceInvocationCall = require('../../../../utils/traceInvocationCall').bin
16
17
 
17
18
  const IosDriver = require('./IosDriver');
18
19
 
19
-
20
20
  /**
21
21
  * @typedef SimulatorDriverDeps { DeviceDriverDeps }
22
22
  * @property applesimutils { AppleSimUtils }
@@ -69,8 +69,7 @@ class SimulatorDriver extends IosDriver {
69
69
  async getBundleIdFromBinary(appPath) {
70
70
  appPath = getAbsoluteBinaryPath(appPath);
71
71
  try {
72
- const result = await exec(`/usr/libexec/PlistBuddy -c "Print CFBundleIdentifier" "${path.join(appPath, 'Info.plist')}"`);
73
- const bundleId = _.trim(result.stdout);
72
+ const bundleId = await execAsync(`/usr/libexec/PlistBuddy -c "Print CFBundleIdentifier" "${path.join(appPath, 'Info.plist')}"`);
74
73
  if (_.isEmpty(bundleId)) {
75
74
  throw new Error();
76
75
  }
@@ -141,7 +140,7 @@ class SimulatorDriver extends IosDriver {
141
140
  const xcuitestRunner = new XCUITestRunner({ runtimeDevice: { id: this.getExternalId(), _bundleId } });
142
141
  let x = point?.x ?? 100;
143
142
  let y = point?.y ?? 100;
144
- let _pressDuration = pressDuration ? (pressDuration / 1000) : 1;
143
+ let _pressDuration = pressDuration ? pressDuration / 1000 : 1;
145
144
  const traceDescription = actionDescription.longPress({ x, y }, _pressDuration);
146
145
  return this.withAction(xcuitestRunner, 'coordinateLongPress', traceDescription, x.toString(), y.toString(), _pressDuration.toString());
147
146
  }
@@ -210,7 +209,7 @@ class SimulatorDriver extends IosDriver {
210
209
  await this.emitter.emit('createExternalArtifact', {
211
210
  pluginId: 'screenshot',
212
211
  artifactName: screenshotName || path.basename(tempPath, '.png'),
213
- artifactPath: tempPath,
212
+ artifactPath: tempPath
214
213
  });
215
214
 
216
215
  return tempPath;
@@ -223,7 +222,7 @@ class SimulatorDriver extends IosDriver {
223
222
  await this.emitter.emit('createExternalArtifact', {
224
223
  pluginId: 'uiHierarchy',
225
224
  artifactName: artifactName,
226
- artifactPath: viewHierarchyURL,
225
+ artifactPath: viewHierarchyURL
227
226
  });
228
227
 
229
228
  return viewHierarchyURL;
@@ -1,6 +1,5 @@
1
- const { exec } = require('child-process-promise');
2
-
3
1
  const DetoxRuntimeError = require('../errors/DetoxRuntimeError');
2
+ const { execAsync } = require('../utils/childProcess');
4
3
  const environment = require('../utils/environment');
5
4
  const log = require('../utils/logger').child({ cat: 'xcuitest-runner' });
6
5
 
@@ -37,7 +36,7 @@ class XCUITestRunner {
37
36
  `--predicate 'process == "DetoxXCUITestRunner-Runner" && subsystem == "com.wix.DetoxXCUITestRunner.xctrunner"'`);
38
37
 
39
38
  try {
40
- return await exec(`TEST_RUNNER_PARAMS="${base64InvocationParams}" TEST_RUNNER_BUNDLE_ID="${this.runtimeDevice._bundleId}" xcodebuild ${flags.join(' ')}`);
39
+ return await execAsync(`TEST_RUNNER_PARAMS="${base64InvocationParams}" TEST_RUNNER_BUNDLE_ID="${this.runtimeDevice._bundleId}" xcodebuild ${flags.join(' ')}`);
41
40
  } catch (e) {
42
41
  const stdout = e.stdout.toString();
43
42
  const innerError = this.findInnerError(stdout);
@@ -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,7 +1,9 @@
1
+ const _ = require('lodash');
2
+
1
3
  const { DetoxInternalError, DetoxRuntimeError } = require('../errors');
2
4
 
3
5
  function firstEntry(obj) {
4
- return Object.entries(obj)[0];
6
+ return _.entries(obj)[0] || ['value', obj];
5
7
  }
6
8
 
7
9
  function assertType(expectedType) {
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
- const { exec } = require('child-process-promise');
3
2
  const _ = require('lodash');
3
+ const { exec } = require('promisify-child-process');
4
4
 
5
5
  const DetoxRuntimeError = require('../../errors/DetoxRuntimeError');
6
6
  const rootLogger = require('../logger').child({ cat: ['child-process', 'child-process-exec'] });
@@ -8,10 +8,17 @@ const retry = require('../retry');
8
8
 
9
9
  const execsCounter = require('./opsCounter');
10
10
 
11
+ /**
12
+ * Executes a command with retries and logs
13
+ * @param {*} bin - command to execute
14
+ * @param {*} options - options
15
+ * @returns {Promise<import('promisify-child-process').Output>}
16
+ */
11
17
  async function execWithRetriesAndLogs(bin, options = {}) {
12
18
  const {
13
19
  retries = 9,
14
20
  interval = 1000,
21
+ backoff,
15
22
  prefix = null,
16
23
  args = null,
17
24
  timeout,
@@ -30,7 +37,7 @@ async function execWithRetriesAndLogs(bin, options = {}) {
30
37
  try {
31
38
  logger.debug({ event: 'EXEC_CMD' }, `${cmd}`);
32
39
 
33
- await retry({ retries, interval }, async (tryNumber, lastError) => {
40
+ await retry({ retries, interval, backoff }, async (tryNumber, lastError) => {
34
41
  if (statusLogs.trying) {
35
42
  _logExecTrying(logger, statusLogs.trying, tryNumber, lastError);
36
43
  } else if (statusLogs.retrying) {