Repository: wix/Detox (js)

Issue:
Detox retry of failed tests is not working when a single test fail
### Did you test using the latest Detox?

- [x] I have tested this issue on the latest Detox release and it still reproduces.

### Did your test fail and you're not sure why?

- [x] I have read the troubleshooting guide and it didn't help me.

### What happened?

1.  Run detox with retry
2. Make only one test fail
Result => retry will not work

Looks like jest is not working well with shard option and single file.
Removing shard option from the command line works.

### In what environment did this happen?

Detox version: 20.37.0
React Native version: 
Has Fabric (React Native's new rendering system) enabled: (yes/no) 
Node version: 
Test-runner (select one): jest / other


Detox logs:


11:54:45.875 detox[29382] i There were failing tests in the following files:
--
  | 1. detox/admin/hiddenCollapsed.test.js
  |  
  | Detox CLI is going to restart the test runner with those files...
  |  
  | 11:54:45.876 detox[29382] B jest --forceExit --no-color --ci --shard 2/2 /Users/builder/uibuilder/work/detox/admin/hiddenCollapsed.test.js
  | No tests found, exiting with code 1
  | Run with `--passWithNoTests` to exit with code 0
  | In /Users/builder/uibuilder/work
  | 103 files checked.
  | testMatch: /Users/builder/uibuilder/work/detox/admin/backendFunctions.test.js, /Users/builder/uibuilder/work/detox/admin/hiddenCollapsed.test.js, /Users/builder/uibuilder/work/detox/admin/layout.test.js, /Users/builder/uibuilder/work/detox/admin/mobuiButton.test.js, /Users/builder/uibuilder/work/detox/admin/mobuiImage.test.js, /Users/builder/uibuilder/work/detox/admin/mobuiPicker.test.js, /Users/builder/uibuilder/work/detox/admin/mobuiSwitch.test.js, /Users/builder/uibuilder/work/detox/admin/mobuiText.test.js, /Users/builder/uibuilder/work/detox/admin/mobuiTextInput.test.js, /Users/builder/uibuilder/work/detox/admin/showInAppNotification.test.js, /Users/builder/uibuilder/work/detox/admin/showToastNotification.test.js, /Users/builder/uibuilder/work/detox/admin/storage.test.js, /Users/builder/uibuilder/work/detox/admin/showActionSheet.test.js, /Users/builder/uibuilder/work/detox/admin/widgetViewerScreen.test.js, /Users/builder/uibuilder/work/detox/admin/widgetViewerSectionScreen.test.js - 15 matches
  | testPathIgnorePatterns: /node_modules/ - 103 matches
  | testRegex:  - 0 matches
  | Pattern: /Users/builder/uibuilder/work/detox/admin/hiddenCollapsed.test.js - 0 matches





Pull request description:
feat(config): noRetryArgs option
## Description

Resolves #4764

This pull request introduces functionality to handle specific command-line arguments during test retries in Detox. The key changes include adding support for a `noRetryArgs` configuration to exclude certain arguments (like `shard`) during retries, updating the test runner logic to implement this behavior, and enhancing documentation to explain the new feature.

 - [x] I have added/updated the relevant references in the [documentation](https://github.com/wix/Detox/tree/master/docs) files.
 - [x] I have made the necessary changes in the [types index](https://github.com/wix/Detox/blob/master/detox/index.d.ts) file.

Patch:
diff --git a/detox/detox.d.ts b/detox/detox.d.ts
index 179dc138b..0fc824f91 100644
--- a/detox/detox.d.ts
+++ b/detox/detox.d.ts
@@ -230,6 +230,11 @@ declare global {
              * Retries count. Zero means a single attempt to run tests.
              */
             retries?: number;
+            /**
+             * Arguments that should not be used during retries.
+             * @default ['shard']
+             */
+            noRetryArgs?: string[];
             /**
              * When true, tells Detox CLI to cancel next retrying if it gets
              * at least one report about a permanent test suite failure.
diff --git a/detox/src/configuration/composeRunnerConfig.js b/detox/src/configuration/composeRunnerConfig.js
index e088dc8ff..091d6e398 100644
--- a/detox/src/configuration/composeRunnerConfig.js
+++ b/detox/src/configuration/composeRunnerConfig.js
@@ -36,6 +36,7 @@ function composeRunnerConfig(opts) {
       forwardEnv: false,
       detached: false,
       bail: false,
+      noRetryArgs: ['shard'],
       jest: {
         setupTimeout: 300000,
         teardownTimeout: 30000,


Test patch:
diff --git a/detox/local-cli/test.test.js b/detox/local-cli/test.test.js
index 5607e624b..511209848 100644
--- a/detox/local-cli/test.test.js
+++ b/detox/local-cli/test.test.js
@@ -635,6 +635,27 @@ describe('CLI', () => {
     expect(logger().warn).toHaveBeenCalledWith(expect.stringContaining('$DETOX_ARGV_OVERRIDE is detected'));
   });
 
+  test('should remove Jest sharding flags during retries', async () => {
+    mockExitCode(1);
+
+    const context = require('../internals');
+    context.session.testResults = [{
+      testFilePath: 'e2e/failing.test.js',
+      success: false,
+      isPermanentFailure: false,
+    }];
+
+    await run('--retries', 1, '--shard', '1/3').catch(_.noop);
+
+    // First call should include the shard flag
+    expect(cliCall(0).argv).toContain('--shard');
+    expect(cliCall(0).argv).toContain('1/3');
+
+    // Second call (retry) should NOT include the shard flag
+    expect(cliCall(1).argv).not.toContain('--shard');
+    expect(cliCall(1).argv).not.toContain('1/3');
+  });
+
   // Helpers
 
   function tempfile(extension, content) {
diff --git a/detox/local-cli/testCommand/TestRunnerCommand.js b/detox/local-cli/testCommand/TestRunnerCommand.js
index 9cba748da..65e6cd70a 100644
--- a/detox/local-cli/testCommand/TestRunnerCommand.js
+++ b/detox/local-cli/testCommand/TestRunnerCommand.js
@@ -31,6 +31,7 @@ class TestRunnerCommand {
     this._argv = runnerConfig.args;
     this._detached = runnerConfig.detached;
     this._retries = runnerConfig.retries;
+    this._noRetryArgs = runnerConfig.noRetryArgs;
     this._envHint = this._buildEnvHint(opts.env);
     this._startCommands = this._prepareStartCommands(commands, cliConfig);
     this._envFwd = {};
@@ -86,7 +87,7 @@ class TestRunnerCommand {
         if (--runsLeft > 0) {
           // @ts-ignore
           detox.session.testSessionIndex++; // it is always the primary context, so we can update it
-
+          this._noRetryArgs.forEach(arg => delete this._argv[arg]);
           this._argv._ = testFilesToRetry.map(useForwardSlashes);
           this._logRelaunchError(testFilesToRetry);
         }
diff --git a/detox/src/configuration/composeRunnerConfig.test.js b/detox/src/configuration/composeRunnerConfig.test.js
index 84bfc5450..9a61b6bd6 100644
--- a/detox/src/configuration/composeRunnerConfig.test.js
+++ b/detox/src/configuration/composeRunnerConfig.test.js
@@ -48,6 +48,7 @@ describe('composeRunnerConfig', () => {
       bail: false,
       detached: false,
       forwardEnv: false,
+      noRetryArgs: ['shard'],
     });
   });
 
@@ -63,6 +64,7 @@ describe('composeRunnerConfig', () => {
       retries: 1,
       detached: true,
       forwardEnv: true,
+      noRetryArgs: ['shard', 'customArg'],
     };
 
     expect(composeRunnerConfig()).toEqual({
@@ -81,6 +83,7 @@ describe('composeRunnerConfig', () => {
       retries: 1,
       detached: true,
       forwardEnv: true,
+      noRetryArgs: ['shard', 'customArg'],
     });
   });
 
@@ -97,6 +100,7 @@ describe('composeRunnerConfig', () => {
       retries: 1,
       detached: true,
       forwardEnv: true,
+      noRetryArgs: ['anotherArg', 'yetAnotherArg'],
     };
 
     expect(composeRunnerConfig()).toEqual({
@@ -115,6 +119,7 @@ describe('composeRunnerConfig', () => {
       retries: 1,
       detached: true,
       forwardEnv: true,
+      noRetryArgs: ['anotherArg', 'yetAnotherArg'],
     });
   });
 
@@ -229,6 +234,7 @@ describe('composeRunnerConfig', () => {
       bail: true,
       detached: true,
       retries: 1,
+      noRetryArgs: ['shard', 'customArg'],
     };
 
     localConfig.testRunner = {
@@ -244,6 +250,7 @@ describe('composeRunnerConfig', () => {
       bail: false,
       detached: false,
       retries: 3,
+      noRetryArgs: ['anotherArg', 'yetAnotherArg'],
     };
 
     expect(composeRunnerConfig()).toEqual({
@@ -266,6 +273,7 @@ describe('composeRunnerConfig', () => {
       detached: false,
       retries: 3,
       forwardEnv: false,
+      noRetryArgs: ['anotherArg', 'yetAnotherArg'],
     });
   });
 
diff --git a/docs/config/testRunner.mdx b/docs/config/testRunner.mdx
index 524f5d2c1..495b09158 100644
--- a/docs/config/testRunner.mdx
+++ b/docs/config/testRunner.mdx
@@ -142,6 +142,31 @@ Default: `false`.
 When true, tells `detox test` to cancel next retrying if it gets at least one report about a [permanent test suite failure](../api/internals.mdx#reporting-test-results).
 Has no effect, if [`testRunner.retries`] is undefined or set to zero.
 
+### `testRunner.noRetryArgs` \[string[]]
+
+Default: `['shard']`.
+
+Specifies an array of command-line argument names that should be removed when retrying failed tests. This is useful for arguments that don't make sense or might cause issues during retry runs.
+
+For example, with the default configuration, when tests fail and are retried, the `shard` argument will be removed from the test runner command:
+
+```bash
+# First run with sharding
+jest --shard=1/3 e2e/tests
+# Retry run without sharding, only running failed tests
+jest path/to/failed/test.js
+```
+
+You can customize this array to include any arguments that should be excluded during retries:
+
+```json
+{
+  "testRunner": {
+    "noRetryArgs": ["shard", "maxWorkers", "customArg"]
+  }
+}
+```
+
 ### `testRunner.detached` \[boolean]
 
 Default: `false`.
@@ -434,3 +459,4 @@ detox test -c ios.sim.debug -- --help
 [`testRunner.args.$0`]: #testrunnerargs0-string
 [`testRunner.inspectBrk`]: #testrunnerinspectbrk-function
 [`testRunner.retries`]: #testrunnerretries-number
+[`testRunner.noRetryArgs`]: #testrunnernoretryargs-string