Repository: nightwatchjs/nightwatch (js)

Issue:
parallel_process_delay test setting not working with Nightwatchjs upgraded version 3.2.1
### Description of the bug/issue

We have been using the test setting 'parallel_process_delay' in our nightwatchjs automation framework with version 2.6.19 and it has been working fine.

When we upgraded the nightwatch js version to 3.2.1, we found that the test setting 'parallel_process_delay' is not working with parallel execution. 
All the test suite execution starts at once without any delay irrespective of how many milliseconds we pass as a value for this test setting in nightwatch.json. We tried with 10000 and 20000 ms but none helped.

Could you please also provide some inputs on how we can achieve parallel process delay, it will be of great help. We need to access our application through okta which has some restrictions on how many requests we can send once due to which we need to introduce some delay between the requests.

Our test automation framework has around 450 test suites and 5000+ test cases automated which we execute on Selenium Grid daily basis using parallel execution. 
This is a blocker for us as due to okta restriction we need to introduce some delay in the requests we are sending and currently we are not able to introduce any delay which is leading to test suite failures on large scale.

Thanks in advance.

### Steps to reproduce

1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error


### Sample test

_No response_

### Command to run

_No response_

### Verbose Output

_No response_

### Nightwatch Configuration

_No response_

### Nightwatch.js Version

3.2.1

### Node Version

16

### Browser

_No response_

### Operating System

_No response_

### Additional Information

_No response_

Pull request:
add parallel_process_delay in worker threads
## Changes
- add `parallel_process_delay` to worker_threads
- tests

## Impact
- fixes #3947 

Patch:
diff --git a/lib/api/_loaders/page-object.js b/lib/api/_loaders/page-object.js
index 65f20711..c4cf398e 100644
--- a/lib/api/_loaders/page-object.js
+++ b/lib/api/_loaders/page-object.js
@@ -1,4 +1,4 @@
-const lodashMerge = require('lodash.merge');
+const lodashMerge = require('lodash/merge');
 const BaseLoader = require('./_base-loader.js');
 const Page = require('../../page-object');
 
diff --git a/lib/api/_loaders/within-context.js b/lib/api/_loaders/within-context.js
index 152f7ba9..ff1d99c2 100644
--- a/lib/api/_loaders/within-context.js
+++ b/lib/api/_loaders/within-context.js
@@ -1,9 +1,8 @@
-const lodashMerge = require('lodash.merge');
 const BaseLoader = require('./_base-loader.js');
 const CommandWrapper = require('../../page-object/command-wrapper.js');
 const Element = require('../../element');
 
-let __commands_cache = {
+const __commands_cache = {
   __commands_cache: true
 };
 
diff --git a/lib/core/client.js b/lib/core/client.js
index ee1aa3f9..bd5d1554 100644
--- a/lib/core/client.js
+++ b/lib/core/client.js
@@ -1,7 +1,7 @@
 const EventEmitter = require('events');
 const {Key, Capabilities, Browser} = require('selenium-webdriver');
 
-const lodashMerge = require('lodash.merge');
+const lodashMerge = require('lodash/merge');
 const HttpRequest = require('../http/request.js');
 const Utils = require('../utils');
 const Settings = require('../settings/settings.js');
diff --git a/lib/index.js b/lib/index.js
index c1cc4740..ce32f930 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,5 +1,5 @@
 const {By, Key, Capabilities} = require('selenium-webdriver');
-const lodashMerge = require('lodash.merge');
+const lodashMerge = require('lodash/merge');
 const Utils = require('./utils');
 const Settings = require('./settings/settings.js');
 const ElementGlobal = require('./api/_loaders/element-global.js');
diff --git a/lib/reporter/reporters/html.js b/lib/reporter/reporters/html.js
index d168ed3a..1c101afb 100644
--- a/lib/reporter/reporters/html.js
+++ b/lib/reporter/reporters/html.js
@@ -1,6 +1,6 @@
 const open = require('open');
 const path = require('path');
-const lodashPick = require('lodash.pick');
+const lodashPick = require('lodash/pick');
 const HtmlReact = require('@nightwatch/html-reporter-template');
 const AnsiConverter = require('ansi-to-html');
 
diff --git a/lib/reporter/reporters/minimalJson.js b/lib/reporter/reporters/minimalJson.js
index a2da7091..09fb27cb 100644
--- a/lib/reporter/reporters/minimalJson.js
+++ b/lib/reporter/reporters/minimalJson.js
@@ -1,4 +1,4 @@
-const lodashPick = require('lodash.pick');
+const lodashPick = require('lodash/pick');
 const path = require('path');
 const Utils = require('../../utils');
 const {Logger} = Utils;
diff --git a/lib/reporter/results.js b/lib/reporter/results.js
index c7ccd982..1b7d948a 100644
--- a/lib/reporter/results.js
+++ b/lib/reporter/results.js
@@ -1,4 +1,4 @@
-const lodashMerge = require('lodash.merge');
+const lodashMerge = require('lodash/merge');
 const Utils = require('../utils');
 const {Logger} = Utils;
 
diff --git a/lib/runner/cli/cli.js b/lib/runner/cli/cli.js
index 2c3aed08..5d14ef2c 100644
--- a/lib/runner/cli/cli.js
+++ b/lib/runner/cli/cli.js
@@ -1,5 +1,5 @@
 const path = require('path');
-const lodashClone = require('lodash.clone');
+const lodashCloneDeep = require('lodash/cloneDeep');
 const ArgvSetup = require('./argv-setup.js');
 const Settings = require('../../settings/settings.js');
 const Globals = require('../../testsuite/globals.js');
@@ -383,7 +383,7 @@ class CliRunner {
    * @returns {CliRunner|Promise}
    */
   parseTestSettings(settings = {}, asyncLoading = true) {
-    this.userSettings = lodashClone(settings, true);
+    this.userSettings = lodashCloneDeep(settings);
     const result = this.initTestSettings(settings, this.baseSettings, this.argv, this.testEnv, asyncLoading);
 
     if (result instanceof Promise) {
diff --git a/lib/runner/concurrency/index.js b/lib/runner/concurrency/index.js
index f74b2ccb..b470eeb0 100644
--- a/lib/runner/concurrency/index.js
+++ b/lib/runner/concurrency/index.js
@@ -1,7 +1,7 @@
 const EventEmitter = require('events');
 const Utils = require('../../utils');
 const {isObject, isNumber} = Utils;
-const lodashClone = require('lodash.clone');
+const lodashCloneDeep = require('lodash/cloneDeep');
 const ChildProcess = require('./child-process.js');
 const WorkerPool = require('./worker-process.js');
 const {Logger} = Utils;
@@ -11,7 +11,7 @@ class Concurrency extends EventEmitter {
     super();
 
     this.argv = argv;
-    this.settings = lodashClone(settings, true);
+    this.settings = lodashCloneDeep(settings);
     this.useChildProcess = settings.use_child_process;
     this.childProcessOutput = {};
     this.globalExitCode = 0;
diff --git a/lib/runner/concurrency/worker-task.js b/lib/runner/concurrency/worker-task.js
index 696598ba..4bd36bfd 100644
--- a/lib/runner/concurrency/worker-task.js
+++ b/lib/runner/concurrency/worker-task.js
@@ -20,6 +20,7 @@ class WorkerTask extends EventEmitter {
     super();
 
     this.task_output = task_ouput || [];
+    this.startDelay = settings.parallel_process_delay;
     this.piscina = piscina;
     this.label = label;
     this.settings = settings;
@@ -93,22 +94,27 @@ class WorkerTask extends EventEmitter {
     };
     port2.unref();
 
-    return this.piscina.run({argv: this.argv, port1}, {transferList: [port1]})
-      .catch(err => err)
-      .then(failures => {
-        if (this.settings.disable_output_boxes){
-          // eslint-disable-next-line no-console
-          console.log(`${failures ? symbols.fail : symbols.ok} ${this.task_label}\n`, this.task_output.join('\n'), '\n');
-        } else {
-          // eslint-disable-next-line no-console
-          console.log(boxen(this.task_output.join('\n'), {title: `────────────────── ${failures ? symbols.fail : symbols.ok} ${this.task_label}`, padding: 1, borderColor: 'cyan'}));
-        }
-
-        //throw error to mark exit-code of the process
-        if (failures) {
-          throw new Error();
-        }
-      });
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        this.piscina.run({argv: this.argv, port1}, {transferList: [port1]})
+          .catch(err => err)
+          .then(failures => {
+            if (this.settings.disable_output_boxes){
+            // eslint-disable-next-line no-console
+              console.log(`${failures ? symbols.fail : symbols.ok} ${this.task_label}\n`, this.task_output.join('\n'), '\n');
+            } else {
+            // eslint-disable-next-line no-console
+              console.log(boxen(this.task_output.join('\n'), {title: `────────────────── ${failures ? symbols.fail : symbols.ok} ${this.task_label}`, padding: 1, borderColor: 'cyan'}));
+            }
+
+            //throw error to mark exit-code of the process
+            if (failures) {
+              return reject(new Error());
+            }
+            resolve();
+          });
+      }, this.index * this.startDelay);
+    });
   }
 }
 
diff --git a/lib/runner/folder-walk.js b/lib/runner/folder-walk.js
index d55d046d..312bd1e2 100644
--- a/lib/runner/folder-walk.js
+++ b/lib/runner/folder-walk.js
@@ -1,6 +1,6 @@
 const path = require('path');
 const minimatch = require('minimatch');
-const lodashClone = require('lodash.clone');
+const lodashCloneDeep = require('lodash/cloneDeep');
 const Utils = require('../utils');
 const FilenameMatcher = require('./matchers/filename.js');
 const TagsMatcher = require('./matchers/tags.js');
@@ -36,7 +36,7 @@ class Walker {
     }
 
     this.testSource = testSource;
-    this.settings = lodashClone(settings, true);
+    this.settings = lodashCloneDeep(settings);
     this.argv = argv;
     this.usingMocha = this.settings.test_runner && this.settings.test_runner.type === 'mocha';
     this.usingCucumber = this.settings.test_runner?.type === 'cucumber';
diff --git a/lib/settings/settings.js b/lib/settings/settings.js
index b1513814..2afa3c38 100644
--- a/lib/settings/settings.js
+++ b/lib/settings/settings.js
@@ -1,8 +1,8 @@
 const dotenv = require('dotenv');
 const path = require('path');
-const defaultsDeep = require('lodash.defaultsdeep');
-const lodashClone = require('lodash.clone');
-const lodashMerge = require('lodash.merge');
+const defaultsDeep = require('lodash/defaultsDeep');
+const lodashCloneDeep = require('lodash/cloneDeep');
+const lodashMerge = require('lodash/merge');
 const CI_Info = require('ci-info');
 const Defaults = require('./defaults.js');
 const Utils = require('../utils');
@@ -41,7 +41,7 @@ class Settings {
   }
 
   static getDefaults() {
-    return lodashClone(Settings.DEFAULTS, true);
+    return lodashCloneDeep(Settings.DEFAULTS);
   }
 
   /**
@@ -160,7 +160,7 @@ class Settings {
         return;
       }
 
-      const copyVal = lodashClone(this.baseSettings[key], true);
+      const copyVal = lodashCloneDeep(this.baseSettings[key]);
 
       if (isObject(this.settings[key])) {
         Object.assign(this.settings[key], copyVal);
diff --git a/lib/transport/selenium-webdriver/browserstack/browserstack.js b/lib/transport/selenium-webdriver/browserstack/browserstack.js
index aab48404..16a6bba5 100644
--- a/lib/transport/selenium-webdriver/browserstack/browserstack.js
+++ b/lib/transport/selenium-webdriver/browserstack/browserstack.js
@@ -1,7 +1,7 @@
 const stripAnsi = require('strip-ansi');
 const {Logger} = require('../../../utils');
 const AppiumBaseServer = require('../appiumBase.js');
-const defaultsDeep = require('lodash.defaultsdeep');
+const defaultsDeep = require('lodash/defaultsDeep');
 
 
 class Browserstack extends AppiumBaseServer {
diff --git a/lib/utils/index.js b/lib/utils/index.js
index 811d1069..91317447 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -1,7 +1,7 @@
 const path = require('path');
 const fs = require('fs');
 const glob = require('glob');
-const lodashMerge = require('lodash.merge');
+const lodashMerge = require('lodash/merge');
 const {By, Capabilities} = require('selenium-webdriver');
 const {inspect} = require('util');
 
diff --git a/lib/utils/logger/index.js b/lib/utils/logger/index.js
index 2c6d77cc..11d8478a 100644
--- a/lib/utils/logger/index.js
+++ b/lib/utils/logger/index.js
@@ -2,7 +2,7 @@ const util = require('util');
 const boxen = require('boxen');
 const didYouMean = require('didyoumean');
 const AssertionError = require('assertion-error');
-const lodashEscape = require('lodash.escape');
+const lodashEscape = require('lodash/escape');
 
 const LogSettings = require('./log_settings.js');
 const chalkColors = require('../chalkColors.js');
diff --git a/package-lock.json b/package-lock.json
index 8a54e1f3..8871ae04 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -29,11 +29,7 @@
         "envinfo": "7.11.0",
         "glob": "7.2.3",
         "jsdom": "^23.1.0",
-        "lodash.clone": "3.0.3",
-        "lodash.defaultsdeep": "4.6.1",
-        "lodash.escape": "4.0.1",
-        "lodash.merge": "4.6.2",
-        "lodash.pick": "4.4.0",
+        "lodash": "^4.17.21",
         "minimatch": "3.1.2",
         "minimist": "1.2.6",
         "mocha": "10.2.0",
@@ -64,7 +60,6 @@
         "is-ci": "^3.0.1",
         "js-yaml": "^3.13.1",
         "lint-staged": "^13.2.2",
-        "lodash": "^4.17.21",
         "mocha-junit-reporter": "^2.0.2",
         "mochawesome": "^7.1.3",
         "mochawesome-merge": "^4.2.1",
@@ -5412,8 +5407,7 @@
     "node_modules/lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
-      "dev": true
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
     },
     "node_modules/lodash-es": {
       "version": "4.17.21",
@@ -5421,93 +5415,16 @@
       "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
       "dev": true
     },
-    "node_modules/lodash._arraycopy": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz",
-      "integrity": "sha512-RHShTDnPKP7aWxlvXKiDT6IX2jCs6YZLCtNhOru/OX2Q/tzX295vVBK5oX1ECtN+2r86S0Ogy8ykP1sgCZAN0A=="
-    },
-    "node_modules/lodash._arrayeach": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz",
-      "integrity": "sha512-Mn7HidOVcl3mkQtbPsuKR0Fj0N6Q6DQB77CtYncZcJc0bx5qv2q4Gl6a0LC1AN+GSxpnBDNnK3CKEm9XNA4zqQ=="
-    },
-    "node_modules/lodash._baseassign": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz",
-      "integrity": "sha512-t3N26QR2IdSN+gqSy9Ds9pBu/J1EAFEshKlUHpJG3rvyJOYgcELIxcIeKKfZk7sjOz11cFfzJRsyFry/JyabJQ==",
-      "dependencies": {
-        "lodash._basecopy": "^3.0.0",
-        "lodash.keys": "^3.0.0"
-      }
-    },
-    "node_modules/lodash._baseclone": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz",
-      "integrity": "sha512-1K0dntf2dFQ5my0WoGKkduewR6+pTNaqX03kvs45y7G5bzl4B3kTR4hDfJIc2aCQDeLyQHhS280tc814m1QC1Q==",
-      "dependencies": {
-        "lodash._arraycopy": "^3.0.0",
-        "lodash._arrayeach": "^3.0.0",
-        "lodash._baseassign": "^3.0.0",
-        "lodash._basefor": "^3.0.0",
-        "lodash.isarray": "^3.0.0",
-        "lodash.keys": "^3.0.0"
-      }
-    },
-    "node_modules/lodash._basecopy": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz",
-      "integrity": "sha512-rFR6Vpm4HeCK1WPGvjZSJ+7yik8d8PVUdCJx5rT2pogG4Ve/2ZS7kfmO5l5T2o5V2mqlNIfSF5MZlr1+xOoYQQ=="
-    },
-    "node_modules/lodash._basefor": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz",
-      "integrity": "sha512-6bc3b8grkpMgDcVJv9JYZAk/mHgcqMljzm7OsbmcE2FGUMmmLQTPHlh/dFqR8LA0GQ7z4K67JSotVKu5058v1A=="
-    },
-    "node_modules/lodash._bindcallback": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
-      "integrity": "sha512-2wlI0JRAGX8WEf4Gm1p/mv/SZ+jLijpj0jyaE/AXeuQphzCgD8ZQW4oSpoN8JAopujOFGU3KMuq7qfHBWlGpjQ=="
-    },
-    "node_modules/lodash._getnative": {
-      "version": "3.9.1",
-      "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
-      "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA=="
-    },
-    "node_modules/lodash._isiterateecall": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz",
-      "integrity": "sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ=="
-    },
-    "node_modules/lodash.clone": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-3.0.3.tgz",
-      "integrity": "sha512-yVYPpFTdZDCLG2p07gVRTvcwN5X04oj2hu4gG6r0fer58JA08wAVxXzWM+CmmxO2bzOH8u8BkZTZqgX6juVF7A==",
-      "dependencies": {
-        "lodash._baseclone": "^3.0.0",
-        "lodash._bindcallback": "^3.0.0",
-        "lodash._isiterateecall": "^3.0.0"
-      }
-    },
     "node_modules/lodash.defaults": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
       "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
     },
-    "node_modules/lodash.defaultsdeep": {
-      "version": "4.6.1",
-      "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz",
-      "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA=="
-    },
     "node_modules/lodash.difference": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
       "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA=="
     },
-    "node_modules/lodash.escape": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
-      "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw=="
-    },
     "node_modules/lodash.flatten": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
@@ -5519,16 +5436,6 @@
       "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==",
       "dev": true
     },
-    "node_modules/lodash.isarguments": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
-      "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="
-    },
-    "node_modules/lodash.isarray": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
-      "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ=="
-    },
     "node_modules/lodash.isempty": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz",
@@ -5558,20 +5465,11 @@
       "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
       "dev": true
     },
-    "node_modules/lodash.keys": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
-      "integrity": "sha512-CuBsapFjcubOGMn3VD+24HOAPxM79tH+V6ivJL3CHYjtrawauDJHUk//Yew9Hvc6e9rbCrURGk8z6PC+8WJBfQ==",
-      "dependencies": {
-        "lodash._getnative": "^3.0.0",
-        "lodash.isarguments": "^3.0.0",
-        "lodash.isarray": "^3.0.0"
-      }
-    },
     "node_modules/lodash.merge": {
       "version": "4.6.2",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
     },
     "node_modules/lodash.mergewith": {
       "version": "4.6.2",
@@ -5579,11 +5477,6 @@
       "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
       "dev": true
     },
-    "node_modules/lodash.pick": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
-      "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q=="
-    },
     "node_modules/lodash.union": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
@@ -13179,8 +13072,7 @@
     "lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
-      "dev": true
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
     },
     "lodash-es": {
       "version": "4.17.21",
@@ -13188,93 +13080,16 @@
       "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
       "dev": true
     },
-    "lodash._arraycopy": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz",
-      "integrity": "sha512-RHShTDnPKP7aWxlvXKiDT6IX2jCs6YZLCtNhOru/OX2Q/tzX295vVBK5oX1ECtN+2r86S0Ogy8ykP1sgCZAN0A=="
-    },
-    "lodash._arrayeach": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz",
-      "integrity": "sha512-Mn7HidOVcl3mkQtbPsuKR0Fj0N6Q6DQB77CtYncZcJc0bx5qv2q4Gl6a0LC1AN+GSxpnBDNnK3CKEm9XNA4zqQ=="
-    },
-    "lodash._baseassign": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz",
-      "integrity": "sha512-t3N26QR2IdSN+gqSy9Ds9pBu/J1EAFEshKlUHpJG3rvyJOYgcELIxcIeKKfZk7sjOz11cFfzJRsyFry/JyabJQ==",
-      "requires": {
-        "lodash._basecopy": "^3.0.0",
-        "lodash.keys": "^3.0.0"
-      }
-    },
-    "lodash._baseclone": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz",
-      "integrity": "sha512-1K0dntf2dFQ5my0WoGKkduewR6+pTNaqX03kvs45y7G5bzl4B3kTR4hDfJIc2aCQDeLyQHhS280tc814m1QC1Q==",
-      "requires": {
-        "lodash._arraycopy": "^3.0.0",
-        "lodash._arrayeach": "^3.0.0",
-        "lodash._baseassign": "^3.0.0",
-        "lodash._basefor": "^3.0.0",
-        "lodash.isarray": "^3.0.0",
-        "lodash.keys": "^3.0.0"
-      }
-    },
-    "lodash._basecopy": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz",
-      "integrity": "sha512-rFR6Vpm4HeCK1WPGvjZSJ+7yik8d8PVUdCJx5rT2pogG4Ve/2ZS7kfmO5l5T2o5V2mqlNIfSF5MZlr1+xOoYQQ=="
-    },
-    "lodash._basefor": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz",
-      "integrity": "sha512-6bc3b8grkpMgDcVJv9JYZAk/mHgcqMljzm7OsbmcE2FGUMmmLQTPHlh/dFqR8LA0GQ7z4K67JSotVKu5058v1A=="
-    },
-    "lodash._bindcallback": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
-      "integrity": "sha512-2wlI0JRAGX8WEf4Gm1p/mv/SZ+jLijpj0jyaE/AXeuQphzCgD8ZQW4oSpoN8JAopujOFGU3KMuq7qfHBWlGpjQ=="
-    },
-    "lodash._getnative": {
-      "version": "3.9.1",
-      "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
-      "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA=="
-    },
-    "lodash._isiterateecall": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz",
-      "integrity": "sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ=="
-    },
-    "lodash.clone": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-3.0.3.tgz",
-      "integrity": "sha512-yVYPpFTdZDCLG2p07gVRTvcwN5X04oj2hu4gG6r0fer58JA08wAVxXzWM+CmmxO2bzOH8u8BkZTZqgX6juVF7A==",
-      "requires": {
-        "lodash._baseclone": "^3.0.0",
-        "lodash._bindcallback": "^3.0.0",
-        "lodash._isiterateecall": "^3.0.0"
-      }
-    },
     "lodash.defaults": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
       "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
     },
-    "lodash.defaultsdeep": {
-      "version": "4.6.1",
-      "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz",
-      "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA=="
-    },
     "lodash.difference": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
       "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA=="
     },
-    "lodash.escape": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
-      "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw=="
-    },
     "lodash.flatten": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
@@ -13286,16 +13101,6 @@
       "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==",
       "dev": true
     },
-    "lodash.isarguments": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
-      "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="
-    },
-    "lodash.isarray": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
-      "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ=="
-    },
     "lodash.isempty": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz",
@@ -13325,20 +13130,11 @@
       "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
       "dev": true
     },
-    "lodash.keys": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
-      "integrity": "sha512-CuBsapFjcubOGMn3VD+24HOAPxM79tH+V6ivJL3CHYjtrawauDJHUk//Yew9Hvc6e9rbCrURGk8z6PC+8WJBfQ==",
-      "requires": {
-        "lodash._getnative": "^3.0.0",
-        "lodash.isarguments": "^3.0.0",
-        "lodash.isarray": "^3.0.0"
-      }
-    },
     "lodash.merge": {
       "version": "4.6.2",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
     },
     "lodash.mergewith": {
       "version": "4.6.2",
@@ -13346,11 +13142,6 @@
       "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
       "dev": true
     },
-    "lodash.pick": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
-      "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q=="
-    },
     "lodash.union": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
diff --git a/package.json b/package.json
index d614f7ba..e303e7a8 100644
--- a/package.json
+++ b/package.json
@@ -35,11 +35,7 @@
     "envinfo": "7.11.0",
     "glob": "7.2.3",
     "jsdom": "^23.1.0",
-    "lodash.clone": "3.0.3",
-    "lodash.defaultsdeep": "4.6.1",
-    "lodash.escape": "4.0.1",
-    "lodash.merge": "4.6.2",
-    "lodash.pick": "4.4.0",
+    "lodash": "^4.17.21",
     "minimatch": "3.1.2",
     "minimist": "1.2.6",
     "mocha": "10.2.0",
@@ -67,7 +63,6 @@
     "is-ci": "^3.0.1",
     "js-yaml": "^3.13.1",
     "lint-staged": "^13.2.2",
-    "lodash": "^4.17.21",
     "mocha-junit-reporter": "^2.0.2",
     "mochawesome": "^7.1.3",
     "mochawesome-merge": "^4.2.1",


Test patch:
diff --git a/lib/testsuite/globals.js b/lib/testsuite/globals.js
index fb60ecc4..ffb6f507 100644
--- a/lib/testsuite/globals.js
+++ b/lib/testsuite/globals.js
@@ -1,5 +1,5 @@
 const path = require('path');
-const lodashClone = require('lodash.clone');
+const lodashCloneDeep = require('lodash/cloneDeep');
 const TestHooks = require('./hooks.js');
 const Context = require('./context.js');
 const Utils = require('../utils');
@@ -123,7 +123,7 @@ class GlobalsContext extends Context {
       externalGlobals = this.externalGlobals;
     } else {
       // if we already have globals, make a copy of them
-      externalGlobals = lodashClone(this.externalGlobals, true);
+      externalGlobals = lodashCloneDeep(this.externalGlobals);
     }
 
     Object.assign(this.__module, externalGlobals);
@@ -160,8 +160,8 @@ class GlobalsContext extends Context {
     }
 
 
-    let fnAsync = Utils.makeFnAsync(expectedArgs, this.module[fnName], this.module);
-    let args = this.getHookFnArgs(done, api, expectedArgs);
+    const fnAsync = Utils.makeFnAsync(expectedArgs, this.module[fnName], this.module);
+    const args = this.getHookFnArgs(done, api, expectedArgs);
 
     return fnAsync.apply(this.module, args);
   }
@@ -201,7 +201,7 @@ class GlobalsContext extends Context {
   }
 
   setCurrentEnv() {
-    this.moduleCopy = lodashClone(this.module, true);
+    this.moduleCopy = lodashCloneDeep(this.module);
 
     // select globals from the current environment
     if (this.currentEnv) {
@@ -252,7 +252,7 @@ class Globals {
    * Shallow merge with existing globals on the settings object
    */
   mergeWithExisting() {
-    const settingsCopy = lodashClone(this.settings, true);
+    const settingsCopy = lodashCloneDeep(this.settings);
     delete settingsCopy.globals;
 
     try {
diff --git a/test/extra/cucumber-config-noautostart.js b/test/extra/cucumber-config-noautostart.js
index 91d02fe0..f7d041e2 100644
--- a/test/extra/cucumber-config-noautostart.js
+++ b/test/extra/cucumber-config-noautostart.js
@@ -1,9 +1,9 @@
 const path = require('path');
-const lodashMerge = require('lodash.merge');
-const clone = require('lodash.clone');
+const lodashMerge = require('lodash/merge');
+const lodashClone = require('lodash/clone');
 
 const config = require('./cucumber-config.js');
-const configCopy = clone(config);
+const configCopy = lodashClone(config);
 const settings = lodashMerge(configCopy, {
   selenium: {
     port: 10193
diff --git a/test/lib/globals.js b/test/lib/globals.js
index 73e05eab..d001a263 100644
--- a/test/lib/globals.js
+++ b/test/lib/globals.js
@@ -1,7 +1,7 @@
 const fs = require('fs');
 const path = require('path');
 const assert = require('assert');
-const lodashMerge = require('lodash.merge');
+const lodashMerge = require('lodash/merge');
 const common = require('../common.js');
 const Settings = common.require('settings/settings.js');
 const Runner = common.require('runner/runner.js');
@@ -175,8 +175,8 @@ class Globals {
   }
 
   startTestRunner(testsPath, suppliedSettings) {
-    let settings = Settings.parse(suppliedSettings);
-    let runner = Runner.create(settings, {
+    const settings = Settings.parse(suppliedSettings);
+    const runner = Runner.create(settings, {
       reporter: 'junit'
     });
 
diff --git a/test/lib/mockserver.js b/test/lib/mockserver.js
index caa213a8..c47642e6 100644
--- a/test/lib/mockserver.js
+++ b/test/lib/mockserver.js
@@ -2,7 +2,7 @@ const http = require('http');
 const jsYaml = require('js-yaml');
 const fs   = require('fs');
 const path   = require('path');
-const defaultsDeep = require('lodash.defaultsdeep');
+const defaultsDeep = require('lodash/defaultsDeep');
 
 class MockServer {
 
@@ -50,7 +50,7 @@ class MockServer {
       });
 
       req.on('end', () => {
-        let item = this.nextInLine(req, postdata);
+        const item = this.nextInLine(req, postdata);
         let responsedata = '';
 
         if (item) {
@@ -139,7 +139,7 @@ class MockServer {
     const mockMethod = mock.method || 'get';
 
     for (let i = 0; i < this.mocks.length; i++) {
-      let item = this.mocks[i];
+      const item = this.mocks[i];
 
       if (
         item.url === mock.url && item.method.toLowerCase() === mockMethod.toLowerCase() &&
@@ -155,7 +155,7 @@ class MockServer {
     const data = postdata ? normalizeJSONString(postdata) : null;
 
     for (let i = 0; i < this.mocks.length; i++) {
-      let item = this.mocks[i];
+      const item = this.mocks[i];
 
       item.postdata = item.postdata || '';
       item.method = item.method || 'POST';
diff --git a/test/lib/nightwatch.js b/test/lib/nightwatch.js
index 85d8e218..5e385636 100644
--- a/test/lib/nightwatch.js
+++ b/test/lib/nightwatch.js
@@ -1,4 +1,4 @@
-const lodashMerge = require('lodash.merge');
+const lodashMerge = require('lodash/merge');
 const MockServer  = require('./mockserver.js');
 const common = require('../common.js');
 const Settings = common.require('settings/settings.js');
diff --git a/test/src/runner/cli/testCliRunnerParallel.js b/test/src/runner/cli/testCliRunnerParallel.js
index 709725e3..313ce13e 100644
--- a/test/src/runner/cli/testCliRunnerParallel.js
+++ b/test/src/runner/cli/testCliRunnerParallel.js
@@ -8,10 +8,11 @@ const {settings} = common;
 describe('Test CLI Runner in Parallel', function () {
   const ChildProcess = common.require('runner/concurrency/child-process.js');
   const WorkerProcess = common.require('runner/concurrency/worker-process.js');
+  const WorkerTask = common.require('runner/concurrency/worker-task.js');
   const RunnerBase = common.require('runner/runner.js');
 
   const filtered = Object.keys(require.cache).filter(item => (
-    item.endsWith('runner/runner.js') || item.endsWith('runner/concurrency/child-process.js') || item.endsWith('runner/concurrency/worker-process.js')
+    item.endsWith('runner/runner.js') || item.endsWith('runner/concurrency/child-process.js') || item.endsWith('runner/concurrency/worker-process.js') || item.endsWith('runner/concurrency/worker-task.js')
   ));
 
   if (filtered && filtered.length > 0) {
@@ -326,5 +327,52 @@ describe('Test CLI Runner in Parallel', function () {
     assert.strictEqual(runner.isTestWorkersEnabled(), false);
     assert.strictEqual(runner.parallelMode(), true);
   });
+  
+
+  it('run worker threads with parallel_process_delay - worker threads', function() {
+    let startDelay = 0;
+    class RunnerBaseMock extends RunnerBase {
+      static readTestSource(settings, argv) {
+        assert.strictEqual(settings.testWorkersEnabled, true);
+
+        return [
+          'test_file_1.js',
+          'test_file_2.js'
+        ];
+      }
+    }
+
+    class WorkerTaskMock extends WorkerTask {
+      async runWorkerTask(colors, type) {
+        startDelay = this.startDelay;
+       
+        return new Promise((resolve, reject) => {
+          try {
+            assert.strictEqual(this.startDelay, 10);
+            resolve();
+          } catch (err) {
+            reject(err);
+          }
+        }, 10);
+      }
+    }
+    mockery.registerMock('./worker-task.js', WorkerTaskMock);
+    mockery.registerMock('../runner.js', RunnerBaseMock);
 
+    return NightwatchClient.runTests({
+      env: 'default',
+      config: path.join(__dirname, '../../../extra/withgeckodriver-concurrent.json')
+    }, settings({
+      parallel_process_delay: 100,
+      globals: {
+        reporter() {
+          assert.strictEqual(startDelay, 100);
+        }
+      },
+      use_child_process: false,
+      silent: false,
+      output: true,
+      output_folder: false
+    }));
+  });
 });