detox 20.36.5 → 20.38.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 (57) hide show
  1. package/Detox-android/com/wix/detox/{20.36.5/detox-20.36.5-sources.jar → 20.38.0/detox-20.38.0-sources.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0-sources.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0-sources.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0-sources.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0-sources.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0.aar +0 -0
  7. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0.aar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0.aar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0.aar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0.aar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/{20.36.5/detox-20.36.5.pom → 20.38.0/detox-20.38.0.pom} +3 -3
  12. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0.pom.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0.pom.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0.pom.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.38.0/detox-20.38.0.pom.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  17. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  18. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  19. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  20. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  21. package/Detox-ios-framework.tbz +0 -0
  22. package/Detox-ios-src.tbz +0 -0
  23. package/Detox-ios-xcuitest.tbz +0 -0
  24. package/android/build.gradle +1 -1
  25. package/detox.d.ts +18 -0
  26. package/internals.d.ts +1 -0
  27. package/jest.config.js +1 -0
  28. package/local-cli/run-server.js +4 -2
  29. package/local-cli/test.js +5 -0
  30. package/local-cli/testCommand/TestRunnerCommand.js +7 -1
  31. package/local-cli/testCommand/builder.js +9 -0
  32. package/local-cli/testCommand/middlewares.js +4 -0
  33. package/local-cli/utils/patchJestUtil.js +33 -0
  34. package/package.json +8 -8
  35. package/runners/jest/testEnvironment/index.js +2 -0
  36. package/runners/jest/testEnvironment/listeners/REPLListener.js +41 -0
  37. package/runners/jest/testEnvironment/listeners/index.js +2 -0
  38. package/src/DetoxWorker.js +3 -0
  39. package/src/configuration/collectCliConfig.js +24 -2
  40. package/src/configuration/composeRunnerConfig.js +1 -0
  41. package/src/configuration/index.js +1 -1
  42. package/src/errors/DetoxConfigErrorComposer.js +11 -0
  43. package/src/realms/DetoxContext.js +10 -0
  44. package/src/utils/repl.js +65 -0
  45. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5-sources.jar.md5 +0 -1
  46. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5-sources.jar.sha1 +0 -1
  47. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5-sources.jar.sha256 +0 -1
  48. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5-sources.jar.sha512 +0 -1
  49. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5.aar +0 -0
  50. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5.aar.md5 +0 -1
  51. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5.aar.sha1 +0 -1
  52. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5.aar.sha256 +0 -1
  53. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5.aar.sha512 +0 -1
  54. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5.pom.md5 +0 -1
  55. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5.pom.sha1 +0 -1
  56. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5.pom.sha256 +0 -1
  57. package/Detox-android/com/wix/detox/20.36.5/detox-20.36.5.pom.sha512 +0 -1
@@ -0,0 +1 @@
1
+ cd6b81476bd6e88a8fe1369381325120
@@ -0,0 +1 @@
1
+ f82675f6dc5e03fd170ed3cc30e99dbab6648473
@@ -0,0 +1 @@
1
+ 561be9a913ab5930e9b466d131fb7021fc20de55412d66a9d5694276614b212b
@@ -0,0 +1 @@
1
+ 770c669973689ca6faee1fddf22f2adaf23d0cffc2fac1fa7ad41267f527749a30fe69cfd64255ae4a6ee4a62445585c1fbdf7f865eff4c54176b2aa03d0622d
@@ -0,0 +1 @@
1
+ 7bc475de4a5116524e5b69c97a6f56eb
@@ -0,0 +1 @@
1
+ a57cf4c25d9aa215f0139b050cb11abd366482e9
@@ -0,0 +1 @@
1
+ 01da2b899e2cc1fee3dcbe37d43ac4d07c4f1ef75f6b67dcb78364c8b1193b9a
@@ -0,0 +1 @@
1
+ 6a66d135b7417c2d53741331c59d14ec3c07fe51a28411e53e71f11f5ab93edddb84a6e3ee6bb76923fed74f8c4db6370503f8123563b0cab1939f4c6e224d1d
@@ -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.36.5</version>
6
+ <version>20.38.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>
@@ -33,7 +33,7 @@
33
33
  <dependency>
34
34
  <groupId>org.jetbrains.kotlin</groupId>
35
35
  <artifactId>kotlin-stdlib-jdk8</artifactId>
36
- <version>1.9.24</version>
36
+ <version>2.0.21</version>
37
37
  <scope>compile</scope>
38
38
  </dependency>
39
39
  <dependency>
@@ -93,7 +93,7 @@
93
93
  <dependency>
94
94
  <groupId>org.jetbrains.kotlin</groupId>
95
95
  <artifactId>kotlin-reflect</artifactId>
96
- <version>1.9.24</version>
96
+ <version>2.0.21</version>
97
97
  <scope>runtime</scope>
98
98
  </dependency>
99
99
  <dependency>
@@ -0,0 +1 @@
1
+ 9ff2c2b4cc6fcc3b011162f7e59172b5
@@ -0,0 +1 @@
1
+ 0003d7fbaeb6f6077b332efb5a89f54d57c7cd09
@@ -0,0 +1 @@
1
+ 62b1ab529ff7b45f7719300853a914260dd4dcaa35a70aa2925d85b4a37ac8cf
@@ -0,0 +1 @@
1
+ ba0b2a012ec49770e1bc2793b527e38811b84157a8a00b743c4a2468392c93a6a318fb01f40afa1863c42baa49308895c9073b5efe7644aa9e84bc28d8d6cae3
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>20.36.5</latest>
7
- <release>20.36.5</release>
6
+ <latest>20.38.0</latest>
7
+ <release>20.38.0</release>
8
8
  <versions>
9
- <version>20.36.5</version>
9
+ <version>20.38.0</version>
10
10
  </versions>
11
- <lastUpdated>20250417062907</lastUpdated>
11
+ <lastUpdated>20250514094123</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 1d72db54f719ac35b8cb178351580da0
1
+ ebc7abe24dbe6e11ba93075f93e50cc1
@@ -1 +1 @@
1
- 3574c8586f5c35975a2c54e17fd7b6250b7e0223
1
+ fb7550fce775797ec43f0b85f272529513b80f89
@@ -1 +1 @@
1
- 3ca2c75d772bcb1bf1445ed6b7debfbf5f4c068115069869451262e5289adfe0
1
+ 21b16c971588effc92a2602e4cff5357b552c0a40355518db176b1dec3fdf1c5
@@ -1 +1 @@
1
- bc626eac565f93fbddbe4cb56fb50df2466abbe453ba9237b85e20d0fd31767cb218bd6f8ae0f01fd1200f064b067c6f65254dd854ce95e9b4821b0e8c9d8012
1
+ 8e83cb8dcda5e989d74faeef7b658d004d4f8ccbc53ca7ef02ad73949321269c008f1a0f33afc0fde1b0431e92c4ff26a4db60ea1f33cecef474d8f1fbb52775
Binary file
package/Detox-ios-src.tbz CHANGED
Binary file
Binary file
@@ -3,7 +3,7 @@ buildscript {
3
3
 
4
4
  ext {
5
5
  isOfficialDetoxLib = true
6
- kotlinVersion = '1.9.24'
6
+ kotlinVersion = '2.0.21'
7
7
  dokkaVersion = '1.9.10'
8
8
  buildToolsVersion = '35.0.0'
9
9
  compileSdkVersion = 35
package/detox.d.ts CHANGED
@@ -230,6 +230,11 @@ declare global {
230
230
  * Retries count. Zero means a single attempt to run tests.
231
231
  */
232
232
  retries?: number;
233
+ /**
234
+ * Arguments that should not be used during retries.
235
+ * @default ['shard']
236
+ */
237
+ noRetryArgs?: string[];
233
238
  /**
234
239
  * When true, tells Detox CLI to cancel next retrying if it gets
235
240
  * at least one report about a permanent test suite failure.
@@ -497,6 +502,19 @@ declare global {
497
502
  * @see https://wix.github.io/Detox/docs/19.x/api/detox-object-api/#detoxtracecall
498
503
  */
499
504
  readonly traceCall: <T>(event: string, action: () => Promise<T>, args?: Record<string, unknown>) => Promise<T>;
505
+
506
+ /**
507
+ * Enter the REPL mode.
508
+ * Works only with `--repl` CLI flag or DETOX_REPL environment variable.
509
+ * @param context Optional context to be passed to the REPL.
510
+ * @example
511
+ * await detox.REPL();
512
+ * @example
513
+ * await detox.REPL({ myScreenDriver, usefulConstants });
514
+ *
515
+ * @see https://wix.github.io/Detox/docs/guide/detox-repl
516
+ */
517
+ REPL(context?: object): Promise<void>;
500
518
  }
501
519
 
502
520
  interface Logger {
package/internals.d.ts CHANGED
@@ -306,6 +306,7 @@ declare global {
306
306
  reuse: string;
307
307
  takeScreenshots: string;
308
308
  useCustomLogger: string;
309
+ repl: boolean | 'auto';
309
310
  }>>;
310
311
  }
311
312
  }
package/jest.config.js CHANGED
@@ -69,6 +69,7 @@ module.exports = {
69
69
  'src/utils/logger.js',
70
70
  'src/utils/pipeCommands.js',
71
71
  'src/utils/pressAnyKey.js',
72
+ 'src/utils/repl.js',
72
73
  'src/utils/shellUtils.js',
73
74
  'runners/jest/reporters',
74
75
  'runners/jest/testEnvironment',
@@ -1,6 +1,6 @@
1
1
  const collectCliConfig = require('../src/configuration/collectCliConfig');
2
2
  const composeLoggerConfig = require('../src/configuration/composeLoggerConfig');
3
- const { DetoxRuntimeError } = require('../src/errors');
3
+ const { DetoxConfigErrorComposer, DetoxRuntimeError } = require('../src/errors');
4
4
  const DetoxServer = require('../src/server/DetoxServer');
5
5
  const logger = require('../src/utils/logger');
6
6
 
@@ -31,12 +31,14 @@ module.exports.handler = async function runServer(argv) {
31
31
  throw new DetoxRuntimeError(`The port should be between 1 and 65535, got ${argv.port}`);
32
32
  }
33
33
 
34
+ const errorComposer = new DetoxConfigErrorComposer();
35
+
34
36
  await logger.setConfig(composeLoggerConfig({
35
37
  // @ts-ignore
36
38
  globalConfig: {},
37
39
  // @ts-ignore
38
40
  localConfig: {},
39
- cliConfig: collectCliConfig({ argv }),
41
+ cliConfig: collectCliConfig({ argv, errorComposer }),
40
42
  }));
41
43
 
42
44
  await new DetoxServer({
package/local-cli/test.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const detox = require('../internals');
2
2
 
3
3
  const TestRunnerCommand = require('./testCommand/TestRunnerCommand');
4
+ const patchJestUtil = require('./utils/patchJestUtil');
4
5
 
5
6
  module.exports.command = 'test';
6
7
  module.exports.desc = 'Run your test suites with the test runner specified in the project\'s Detox config';
@@ -20,6 +21,10 @@ module.exports.handler = async function test({ detoxArgs, runnerArgs }) {
20
21
  await detox.init(opts);
21
22
  }
22
23
 
24
+ if (config.cli.repl) {
25
+ patchJestUtil();
26
+ }
27
+
23
28
  const runnerCommand = new TestRunnerCommand({
24
29
  config,
25
30
  env: process.env,
@@ -31,6 +31,7 @@ class TestRunnerCommand {
31
31
  this._argv = runnerConfig.args;
32
32
  this._detached = runnerConfig.detached;
33
33
  this._retries = runnerConfig.retries;
34
+ this._noRetryArgs = runnerConfig.noRetryArgs;
34
35
  this._envHint = this._buildEnvHint(opts.env);
35
36
  this._startCommands = this._prepareStartCommands(commands, cliConfig);
36
37
  this._envFwd = {};
@@ -40,6 +41,11 @@ class TestRunnerCommand {
40
41
  this._envFwd = this._buildEnvOverride(cliConfig, deviceConfig);
41
42
  Object.assign(this._envHint, this._envFwd);
42
43
  }
44
+
45
+ if (cliConfig.repl) {
46
+ this._envFwd.DETOX_REPL = cliConfig.repl;
47
+ this._envHint.DETOX_REPL = cliConfig.repl;
48
+ }
43
49
  }
44
50
 
45
51
  async execute() {
@@ -81,7 +87,7 @@ class TestRunnerCommand {
81
87
  if (--runsLeft > 0) {
82
88
  // @ts-ignore
83
89
  detox.session.testSessionIndex++; // it is always the primary context, so we can update it
84
-
90
+ this._noRetryArgs.forEach(arg => delete this._argv[arg]);
85
91
  this._argv._ = testFilesToRetry.map(useForwardSlashes);
86
92
  this._logRelaunchError(testFilesToRetry);
87
93
  }
@@ -135,5 +135,14 @@ module.exports = {
135
135
  group: 'Debugging:',
136
136
  describe: '[Jest Only] Allows debugging of the underlying test runner',
137
137
  boolean: true,
138
+ },
139
+ 'repl': {
140
+ group: 'Debugging:',
141
+ describe: 'Launch REPL mode. Use --repl=auto to enter REPL on test failures.',
142
+ coerce(value) {
143
+ if (value === false || value === 'false') return false;
144
+ if (value === true || value === 'true') return true;
145
+ return value;
146
+ },
138
147
  }
139
148
  };
@@ -49,6 +49,10 @@ function splitArgv(argv) {
49
49
  detoxArgs['debug-synchronization'] = 3000;
50
50
  runnerArgv._.unshift(erroneousPassthrough);
51
51
  }
52
+ if (typeof detoxArgs.repl === 'string' && detoxArgs.repl !== 'auto') {
53
+ runnerArgv._.unshift(detoxArgs.repl);
54
+ detoxArgs.repl = true;
55
+ }
52
56
 
53
57
  const runnerArgs = disengageBooleanArgs(runnerArgv, getJestBooleanArgs());
54
58
  return { detoxArgs, runnerArgs };
@@ -0,0 +1,33 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const resolveFrom = require('resolve-from');
5
+
6
+ const log = require('../../src/utils/logger').child({ cat: 'jest-patch' });
7
+
8
+ function patchJestUtil() {
9
+ try {
10
+ const jestUtilPath = resolveFrom(process.cwd(), 'jest-util/package.json');
11
+ const isInteractivePath = path.join(path.dirname(jestUtilPath), 'build/isInteractive.js');
12
+
13
+ if (!fs.existsSync(isInteractivePath)) {
14
+ log.warn('Could not find node_modules/jest-util/build/isInteractive.js to patch!');
15
+ return;
16
+ }
17
+
18
+ const content = fs.readFileSync(isInteractivePath, 'utf8');
19
+ if (!content.includes('DETOX_REPL')) {
20
+ const patchedContent = content.replace(
21
+ "process.env.TERM !== 'dumb'",
22
+ "process.env.TERM !== 'dumb' && /* patched by Detox */ !process.env.DETOX_REPL"
23
+ );
24
+ fs.writeFileSync(`${isInteractivePath}.bak`, content);
25
+ fs.writeFileSync(isInteractivePath, patchedContent);
26
+ log.info('Successfully patched jest-util for REPL support');
27
+ }
28
+ } catch (error) {
29
+ log.warn({ err: error }, 'Failed to patch jest-util for REPL support');
30
+ }
31
+ }
32
+
33
+ module.exports = patchJestUtil;
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.36.5",
4
+ "version": "20.38.0",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -37,15 +37,15 @@
37
37
  "@react-native-community/cli": "15.0.1",
38
38
  "@react-native-community/cli-platform-android": "15.0.1",
39
39
  "@react-native-community/cli-platform-ios": "15.0.1",
40
- "@react-native/babel-preset": "0.76.3",
41
- "@react-native/eslint-config": "0.76.3",
42
- "@react-native/metro-config": "0.76.3",
43
- "@react-native/typescript-config": "0.76.3",
40
+ "@react-native/babel-preset": "0.77.2",
41
+ "@react-native/eslint-config": "0.77.2",
42
+ "@react-native/metro-config": "0.77.2",
43
+ "@react-native/typescript-config": "0.77.2",
44
44
  "@tsconfig/react-native": "^3.0.0",
45
45
  "@types/bunyan": "^1.8.8",
46
46
  "@types/child-process-promise": "^2.2.1",
47
47
  "@types/fs-extra": "^11.0.4",
48
- "@types/jest": "^29.0.0",
48
+ "@types/jest": "^29.5.13",
49
49
  "@types/node": "^14.18.33",
50
50
  "@types/node-ipc": "^9.2.0",
51
51
  "@types/ws": "^7.4.0",
@@ -62,7 +62,7 @@
62
62
  "jest-allure2-reporter": "^2.0.0-beta.18",
63
63
  "metro-react-native-babel-preset": "0.76.8",
64
64
  "prettier": "^3.1.1",
65
- "react-native": "0.76.3",
65
+ "react-native": "0.77.2",
66
66
  "react-native-codegen": "^0.0.8",
67
67
  "typescript": "~5.3.3",
68
68
  "wtfnode": "^0.9.1"
@@ -120,5 +120,5 @@
120
120
  "browserslist": [
121
121
  "node 14"
122
122
  ],
123
- "gitHead": "735ad4bf567419f8ec9be22c8a256fadd0393652"
123
+ "gitHead": "43a5d55c841100c08f138be67c788ba1c9014206"
124
124
  }
@@ -13,6 +13,7 @@ const {
13
13
  DetoxCoreListener,
14
14
  DetoxInitErrorListener,
15
15
  DetoxPlatformFilterListener,
16
+ REPLListener,
16
17
  SpecReporter,
17
18
  WorkerAssignReporter
18
19
  } = require('./listeners');
@@ -62,6 +63,7 @@ class DetoxCircusEnvironment extends WithEmitter(NodeEnvironment) {
62
63
  DetoxCoreListener,
63
64
  SpecReporter,
64
65
  WorkerAssignReporter,
66
+ REPLListener,
65
67
  });
66
68
 
67
69
  // Artifacts flushing should be delayed to avoid conflicts with third-party reporters
@@ -0,0 +1,41 @@
1
+ const internals = require('../../../../internals');
2
+ const { enterREPL } = require('../../../../src/utils/repl');
3
+
4
+ const log = internals.log.child({ cat: 'lifecycle,jest-environment' });
5
+ const noop = () => {};
6
+
7
+ class REPLListener {
8
+ constructor({ env }) {
9
+ this._env = env;
10
+ }
11
+
12
+ async setup(_events, state) {
13
+ const repl = internals.config.cli.repl;
14
+
15
+ if (repl) {
16
+ state.testTimeout = 2 * 60 * 60 * 1000; // 2 hours
17
+ }
18
+
19
+ if (repl !== 'auto') {
20
+ Object.assign(this, {
21
+ hook_failure: noop,
22
+ test_fn_failure: noop,
23
+ });
24
+ }
25
+ }
26
+
27
+ async hook_failure({ error }) {
28
+ await this._enterREPL(error);
29
+ }
30
+
31
+ async test_fn_failure({ error }) {
32
+ await this._enterREPL(error);
33
+ }
34
+
35
+ async _enterREPL(error) {
36
+ log.error(error);
37
+ await enterREPL();
38
+ }
39
+ }
40
+
41
+ module.exports = REPLListener;
@@ -1,6 +1,7 @@
1
1
  const DetoxCoreListener = require('./DetoxCoreListener');
2
2
  const DetoxInitErrorListener = require('./DetoxInitErrorListener');
3
3
  const DetoxPlatformFilterListener = require('./DetoxPlatformFilterListener');
4
+ const REPLListener = require('./REPLListener');
4
5
  const SpecReporter = require('./SpecReporter');
5
6
  const WorkerAssignReporter = require('./WorkerAssignReporter');
6
7
 
@@ -8,6 +9,7 @@ module.exports = {
8
9
  DetoxCoreListener,
9
10
  DetoxInitErrorListener,
10
11
  DetoxPlatformFilterListener,
12
+ REPLListener,
11
13
  SpecReporter,
12
14
  WorkerAssignReporter,
13
15
  };
@@ -65,6 +65,8 @@ class DetoxWorker {
65
65
  this.pilot = null;
66
66
  /** @type {Detox.PilotFacade} */
67
67
  this.copilot = null;
68
+ /** @type {Function} */
69
+ this.REPL = context.REPL;
68
70
 
69
71
  this._deviceCookie = null;
70
72
 
@@ -172,6 +174,7 @@ class DetoxWorker {
172
174
  ...matchers,
173
175
  device: this.device,
174
176
  pilot: this.pilot,
177
+ REPL: this.REPL,
175
178
  detox: this,
176
179
  };
177
180
 
@@ -1,5 +1,6 @@
1
1
  const _ = require('lodash');
2
2
 
3
+ const { DetoxConfigErrorComposer } = require('../errors');
3
4
  const argparse = require('../utils/argparse');
4
5
 
5
6
  const asBoolean = (value) => {
@@ -22,13 +23,33 @@ const asNumber = (value) => {
22
23
  : undefined;
23
24
  };
24
25
 
25
- function collectCliConfig({ argv }) {
26
+ const asBooleanEnum = (value) => {
27
+ if (value == null || value === '') {
28
+ return undefined;
29
+ }
30
+ if (value === 'true') return true;
31
+ if (value === 'false') return false;
32
+ return value;
33
+ };
34
+
35
+ /**
36
+ * @param {object} opts
37
+ * @param {Record<string, any>} [opts.argv]
38
+ * @param {DetoxConfigErrorComposer} [opts.errorComposer]
39
+ */
40
+ function collectCliConfig({ argv, errorComposer }) {
26
41
  const env = (key) => argparse.getEnvValue(key);
27
42
  const get = (key, fallback) => {
28
43
  const value = argv && Reflect.has(argv, key) ? argv[key] : env(key);
29
44
  return value === undefined ? fallback : value;
30
45
  };
31
46
 
47
+ const inspectBrk = asBoolean(get('inspect-brk'));
48
+ const repl = asBooleanEnum(get('repl'));
49
+ if (inspectBrk && repl) {
50
+ throw errorComposer.mutuallyExclusiveCliOptions('--inspect-brk', '--repl');
51
+ }
52
+
32
53
  return _.omitBy({
33
54
  artifactsLocation: get('artifacts-location'),
34
55
  captureViewHierarchy: get('capture-view-hierarchy'),
@@ -54,8 +75,9 @@ function collectCliConfig({ argv }) {
54
75
  reuse: asBoolean(get('reuse')),
55
76
  useCustomLogger: asBoolean(get('use-custom-logger')),
56
77
  retries: asNumber(get('retries')),
57
- inspectBrk: asBoolean(get('inspect-brk')),
58
78
  start: get('start'),
79
+ repl,
80
+ inspectBrk,
59
81
  }, _.isUndefined);
60
82
  }
61
83
 
@@ -36,6 +36,7 @@ function composeRunnerConfig(opts) {
36
36
  forwardEnv: false,
37
37
  detached: false,
38
38
  bail: false,
39
+ noRetryArgs: ['shard'],
39
40
  jest: {
40
41
  setupTimeout: 300000,
41
42
  teardownTimeout: 30000,
@@ -22,7 +22,7 @@ async function composeDetoxConfig({
22
22
  errorComposer = new DetoxConfigErrorComposer(),
23
23
  override = undefined,
24
24
  }) {
25
- const cliConfig = collectCliConfig({ argv });
25
+ const cliConfig = collectCliConfig({ argv, errorComposer });
26
26
  const findupResult = await loadExternalConfig({
27
27
  errorComposer,
28
28
  configPath: cliConfig.configPath,
@@ -117,6 +117,17 @@ class DetoxConfigErrorComposer {
117
117
  }
118
118
  // endregion
119
119
 
120
+ // region CLI options validation
121
+
122
+ mutuallyExclusiveCliOptions(option1, option2) {
123
+ return new DetoxConfigError({
124
+ message: `The ${J(option1)} and ${J(option2)} options cannot be used together`,
125
+ hint: `These options are mutually exclusive. Please use either ${option1} or ${option2}, but not both.`
126
+ });
127
+ }
128
+
129
+ // endregion
130
+
120
131
  // region configuration/index
121
132
 
122
133
  noConfigurationSpecified() {
@@ -87,6 +87,16 @@ class DetoxContext {
87
87
 
88
88
  copilot = funpermaproxy.callable(() => this[symbols.worker].copilot);
89
89
 
90
+ REPL = (context) => {
91
+ const config = this[symbols.config];
92
+ const repl = require('../utils/repl');
93
+ if (config && config.cli && config.cli.repl) {
94
+ return repl.enterREPL(context);
95
+ }
96
+
97
+ this.log.warn('To use the Detox REPL, you must enable it with either the --repl CLI flag or DETOX_REPL environment variable');
98
+ };
99
+
90
100
  get DetoxConstants() {
91
101
  return DetoxConstants;
92
102
  }
@@ -0,0 +1,65 @@
1
+ const repl = require('node:repl');
2
+
3
+ const log = require('../utils/logger').child({ cat: 'repl' });
4
+
5
+ async function enterREPL(context = {}) {
6
+ log.info('Entering Detox REPL...\nType .help to see available commands');
7
+
8
+ const replServer = repl.start({
9
+ prompt: 'detox> ',
10
+ useColors: true,
11
+ useGlobal: true,
12
+ preview: true,
13
+ breakEvalOnSigint: true,
14
+ });
15
+
16
+ // Add Detox globals
17
+ const detox = require('../../index');
18
+ Object.assign(replServer.context, detox);
19
+
20
+ // Add user-provided context
21
+ if (typeof context === 'object') {
22
+ Object.assign(replServer.context, context);
23
+ }
24
+
25
+ // Define .dumpxml command
26
+ replServer.defineCommand('dumpxml', {
27
+ help: 'Print view hierarchy XML',
28
+ async action() {
29
+ this.clearBufferedCommand();
30
+ try {
31
+ const xml = await detox.device.generateViewHierarchyXml();
32
+ if (xml) {
33
+ log.info(xml);
34
+ }
35
+ } catch (error) {
36
+ log.error('Failed to generate view hierarchy.\n%s', error);
37
+ }
38
+ this.displayPrompt();
39
+ }
40
+ });
41
+
42
+ // Define .ai command for natural language interaction
43
+ replServer.defineCommand('pilot', {
44
+ help: 'Execute natural language command (e.g. .pilot Tap on login button)',
45
+ async action(input) {
46
+ this.clearBufferedCommand();
47
+ try {
48
+ if (!input || !input.trim()) {
49
+ log.warn('Please provide a valid command. Example: .pilot Tap on login button');
50
+ } else {
51
+ await detox.pilot.perform(input);
52
+ }
53
+ } catch (error) {
54
+ log.error('Failed to execute Detox Pilot command.\n%s', error);
55
+ }
56
+ this.displayPrompt();
57
+ }
58
+ });
59
+
60
+ return new Promise((resolve) => replServer.on('exit', resolve));
61
+ }
62
+
63
+ module.exports = {
64
+ enterREPL,
65
+ };
@@ -1 +0,0 @@
1
- a35252c84ebc037351bc0e2287d0b141
@@ -1 +0,0 @@
1
- 372d48c91c6d31ad27f5c4a7215e6552d16b0ad2
@@ -1 +0,0 @@
1
- 9b2dc100e4afad8659c35ba9700fc5bc912bf01eb14134cad19bd46ae9545802
@@ -1 +0,0 @@
1
- a7aa65d3ca4aaf007e304a26868ebb970f33ff6d59f4cd066f6020ba8a69bd81815f299411df19ea101d8b7576902a853e8c795eb858562ba98f8f54fa6c00fb
@@ -1 +0,0 @@
1
- 4ab2c960e9f1d12a9daa555f3db6554c
@@ -1 +0,0 @@
1
- 4068d06887f8949b32a0cf36fed6f3e70bb4f922
@@ -1 +0,0 @@
1
- 189187fc397bdb111df5485b64ff7154c84ce232240594646896f11c6f433ef6
@@ -1 +0,0 @@
1
- 3906c9b7a0dc913a2697a735819c907f62c4eb516f40e492bba0df74e54ee99927b36d5771f942e29054a94dd03c573bfed9a372756c5fcb42f0d3f0b53f85df
@@ -1 +0,0 @@
1
- 91e5fc41935796ca8642c0717146b73b
@@ -1 +0,0 @@
1
- fa243db146231e0109760c4c202718b3b37ccc39
@@ -1 +0,0 @@
1
- 6992cd1bdfca7007e6e8c74c5c5da84e10ed0120e5df97c2b081d86b78bf3eb7
@@ -1 +0,0 @@
1
- 3e1224295a34e293d511d4acd33983a1acdba7bcdc2a3fc1b33faee76138e1d8bace31b456d5ee216bf5d40159491b0968c8c694504526c0a273a8bdc6ec6f5c