Repository: wix/Detox
Pull request #3778 (2022-12-23 09:26:13)

fix(generation): invalid isArray assertion
## Description

I noticed thanks to the linter that our assertion here was never correct:

```diff
-if (typeof X !== 'object' || !X instanceof Array) {
+if (typeof X !== 'object' || !Array.isArray(X)) {
```

That's because `!X` gets evaluated as a boolean first, and then it is never an instance of an array, for an obvious reason.


Patch:
diff --git a/detox/.eslintrc.js b/detox/.eslintrc.js
index 54d21ab4e..b5649767a 100644
--- a/detox/.eslintrc.js
+++ b/detox/.eslintrc.js
@@ -54,12 +54,7 @@ module.exports = {
       }
     ],
     'no-prototype-builtins': 'off',
-    'no-unused-vars': [
-      'error',
-      {
-        'argsIgnorePattern': '^_'
-      }
-    ],
+    'no-unused-vars': 'off',
     'node/no-unpublished-require': 'warn',
     'object-curly-spacing': [
       'error',
diff --git a/detox/local-cli/__mocks__/executable b/detox/local-cli/__mocks__/executable
index db749e27f..11f0cd997 100755
--- a/detox/local-cli/__mocks__/executable
+++ b/detox/local-cli/__mocks__/executable
@@ -4,22 +4,32 @@ const fs = require('fs');
 
 const DETOX_REGEXP = /^DETOX_(?!DISABLE_POD_INSTALL|DISABLE_POSTINSTALL)/;
 
-const env = Object.keys(process.env)
-  .filter(k => DETOX_REGEXP.test(k.toUpperCase()))
-  .reduce((acc, key) => {
-    acc[key] = process.env[key];
-    return acc;
-  }, {});
+function main() {
+    const [, ...argv] = process.argv;
+    const env = Object.keys(process.env)
+        .filter(k => DETOX_REGEXP.test(k.toUpperCase()))
+        .reduce((acc, key) => {
+            acc[key] = process.env[key];
+            return acc;
+        }, {});
 
-const [, ...argv] = process.argv;
+    const output = JSON.stringify({ env, argv }) + '\n';
 
-const output = JSON.stringify({ env, argv }) + '\n';
-if (process.env.CLI_TEST_STDOUT) {
-    fs.appendFileSync(process.env.CLI_TEST_STDOUT, output);
-} else {
-    process.stdout.write(output);
+    if (process.env.CLI_TEST_STDOUT) {
+        fs.appendFileSync(process.env.CLI_TEST_STDOUT, output);
+    } else {
+        process.stdout.write(output);
+    }
+
+    if (process.env.CLI_EXIT_CODE) {
+        process.exit(+process.env.CLI_EXIT_CODE);
+    } else {
+        process.exit(0);
+    }
 }
 
-if (process.env.CLI_EXIT_CODE) {
-    process.exit(+process.env.CLI_EXIT_CODE);
+if (process.env.CLI_SLEEP > 0) {
+    setTimeout(main, +process.env.CLI_SLEEP);
+} else {
+    main();
 }
diff --git a/detox/package.json b/detox/package.json
index 55e8217ad..037193971 100644
--- a/detox/package.json
+++ b/detox/package.json
@@ -41,6 +41,7 @@
     "@types/ws": "^7.4.0",
     "@typescript-eslint/eslint-plugin": "^5.4.0",
     "@typescript-eslint/parser": "^5.4.0",
+    "cross-env": "^7.0.3",
     "eslint": "^8.3.0",
     "eslint-plugin-import": "^2.23.3",
     "eslint-plugin-no-only-tests": "^2.6.0",
@@ -69,7 +70,6 @@
     "ini": "^1.3.4",
     "json-cycle": "^1.3.0",
     "lodash": "^4.17.5",
-    "minimist": "^1.2.0",
     "multi-sort-stream": "^1.0.3",
     "multipipe": "^4.0.0",
     "node-ipc": "^9.2.1",
diff --git a/detox/src/android/espressoapi/EspressoDetox.js b/detox/src/android/espressoapi/EspressoDetox.js
index 05ffa4791..bb9f72793 100644
--- a/detox/src/android/espressoapi/EspressoDetox.js
+++ b/detox/src/android/espressoapi/EspressoDetox.js
@@ -52,7 +52,7 @@ class EspressoDetox {
   }
 
   static setURLBlacklist(urls) {
-    if (typeof urls !== 'object' || !urls instanceof Array) {
+    if (typeof urls !== 'object' || !Array.isArray(urls)) {
       throw new Error('urls must be an array, got ' + typeof urls);
     }
 
diff --git a/detox/src/android/espressoapi/web/WebElement.js b/detox/src/android/espressoapi/web/WebElement.js
index 8af504915..1733db9d2 100644
--- a/detox/src/android/espressoapi/web/WebElement.js
+++ b/detox/src/android/espressoapi/web/WebElement.js
@@ -69,7 +69,7 @@ class WebElement {
   static runScriptWithArgs(element, script, args) {
     if (typeof script !== "string") throw new Error("script should be a string, but got " + (script + (" (" + (typeof script + ")"))));
 
-    if (typeof args !== 'object' || !args instanceof Array) {
+    if (typeof args !== 'object' || !Array.isArray(args)) {
       throw new Error('args must be an array, got ' + typeof args);
     }
 
diff --git a/detox/src/artifacts/templates/plugin/ArtifactPlugin.js b/detox/src/artifacts/templates/plugin/ArtifactPlugin.js
index 9c521fabe..6025c1848 100644
--- a/detox/src/artifacts/templates/plugin/ArtifactPlugin.js
+++ b/detox/src/artifacts/templates/plugin/ArtifactPlugin.js
@@ -1,5 +1,5 @@
+/* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none" }] */
 // @ts-nocheck
-/* eslint-disable no-unused-vars */
 
 const _ = require('lodash');
 
diff --git a/detox/src/client/AsyncWebSocket.js b/detox/src/client/AsyncWebSocket.js
index 39e1a7b32..d2d1a117b 100644
--- a/detox/src/client/AsyncWebSocket.js
+++ b/detox/src/client/AsyncWebSocket.js
@@ -1,3 +1,4 @@
+/* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none" }] */
 // @ts-nocheck
 const _ = require('lodash');
 const WebSocket = require('ws');
@@ -176,7 +177,7 @@ class AsyncWebSocket {
    * @param {WebSocket.OpenEvent} event
    * @private
    */
-  _onOpen(event) { // eslint-disable-line no-unused-vars
+  _onOpen(event) {
     log.trace(`opened web socket to: ${this._url}`);
     this._opening.resolve();
     this._opening = null;
@@ -268,7 +269,7 @@ class AsyncWebSocket {
    * @param {WebSocket.CloseEvent | null} event
    * @private
    */
-  _onClose(event) { // eslint-disable-line no-unused-vars
+  _onClose(event) {
     if (this._closing) {
       this._closing.resolve();
     }
diff --git a/detox/src/configuration/collectCliConfig.js b/detox/src/configuration/collectCliConfig.js
index 4c44eff64..b492850ff 100644
--- a/detox/src/configuration/collectCliConfig.js
+++ b/detox/src/configuration/collectCliConfig.js
@@ -23,7 +23,7 @@ const asNumber = (value) => {
 };
 
 function collectCliConfig({ argv }) {
-  const env = (key) => argparse.getArgValue(key);
+  const env = (key) => argparse.getEnvValue(key);
   const get = (key, fallback) => {
     const value = argv && Reflect.has(argv, key) ? argv[key] : env(key);
     return value === undefined ? fallback : value;
diff --git a/detox/src/configuration/composeLoggerConfig.js b/detox/src/configuration/composeLoggerConfig.js
index 554563ece..0c7c19314 100644
--- a/detox/src/configuration/composeLoggerConfig.js
+++ b/detox/src/configuration/composeLoggerConfig.js
@@ -1,3 +1,4 @@
+/* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none" }] */
 const _ = require('lodash');
 
 const { castLevel, defaultOptions } = require('../logger/DetoxLogger');
diff --git a/detox/src/devices/allocation/drivers/AllocationDriverBase.js b/detox/src/devices/allocation/drivers/AllocationDriverBase.js
index 29b984e1a..542eab8f9 100644
--- a/detox/src/devices/allocation/drivers/AllocationDriverBase.js
+++ b/detox/src/devices/allocation/drivers/AllocationDriverBase.js
@@ -1,4 +1,6 @@
+/* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none" }] */
 // @ts-nocheck
+
 /**
  * @typedef DeallocOptions
  * @property shutdown { Boolean }
@@ -9,14 +11,14 @@ class AllocationDriverBase {
    * @param deviceConfig { Object }
    * @return {Promise<DeviceCookie>}
    */
-  async allocate(deviceConfig) {} // eslint-disable-line no-unused-vars
+  async allocate(deviceConfig) {}
 
   /**
    * @param cookie { DeviceCookie }
    * @param options { DeallocOptions }
    * @return {Promise<void>}
    */
-  async free(cookie, options) {} // eslint-disable-line no-unused-vars
+  async free(cookie, options) {}
 }
 
 module.exports = AllocationDriverBase;
diff --git a/detox/src/devices/allocation/factories/base.js b/detox/src/devices/allocation/factories/base.js
index abfddc47c..7c45032e1 100644
--- a/detox/src/devices/allocation/factories/base.js
+++ b/detox/src/devices/allocation/factories/base.js
@@ -16,7 +16,7 @@ class DeviceAllocatorFactory {
    * @returns { AllocationDriverBase }
    * @private
    */
-  _createDriver(deps) {} // eslint-disable-line no-unused-vars
+  _createDriver(deps) {} // eslint-disable-line @typescript-eslint/no-unused-vars
 }
 
 module.exports = DeviceAllocatorFactory;
diff --git a/detox/src/devices/common/drivers/android/tools/AppInstallHelper.js b/detox/src/devices/common/drivers/android/tools/AppInstallHelper.js
index e2968fcbf..c21dfafa0 100644
--- a/detox/src/devices/common/drivers/android/tools/AppInstallHelper.js
+++ b/detox/src/devices/common/drivers/android/tools/AppInstallHelper.js
@@ -1,13 +1,13 @@
 // TODO Tweak such that if apk's already exist on the device (need to store uniquely), they will not be resent (would optimize cloud, for example)
 
 class AppInstallHelper {
-  constructor(adb, fileXfer) {
+  constructor(adb, fileTransfer) {
     this._adb = adb;
-    this._fileXfer = fileXfer;
+    this._fileTransfer = fileTransfer;
   }
 
   async install(deviceId, appBinaryPath, testBinaryPath) {
-    await this._fileXfer.prepareDestinationDir(deviceId);
+    await this._fileTransfer.prepareDestinationDir(deviceId);
     await this._pushAndInstallBinary(deviceId, appBinaryPath, 'Application.apk');
     if (testBinaryPath) {
       await this._pushAndInstallBinary(deviceId, testBinaryPath, 'Test.apk');
@@ -15,7 +15,7 @@ class AppInstallHelper {
   }
 
   async _pushAndInstallBinary(deviceId, binaryPath, binaryFilenameOnTarget) {
-    const binaryPathOnTarget = await this._fileXfer.send(deviceId, binaryPath, binaryFilenameOnTarget);
+    const binaryPathOnTarget = await this._fileTransfer.send(deviceId, binaryPath, binaryFilenameOnTarget);
     await this._adb.remoteInstall(deviceId, binaryPathOnTarget);
   }
 }
diff --git a/detox/src/devices/common/drivers/android/tools/FileXfer.js b/detox/src/devices/common/drivers/android/tools/FileTransfer.js
similarity index 91%
rename from detox/src/devices/common/drivers/android/tools/FileXfer.js
rename to detox/src/devices/common/drivers/android/tools/FileTransfer.js
index a14fde8bd..551b9eab1 100644
--- a/detox/src/devices/common/drivers/android/tools/FileXfer.js
+++ b/detox/src/devices/common/drivers/android/tools/FileTransfer.js
@@ -1,6 +1,6 @@
 const path = require('path');
 
-class FileXfer {
+class FileTransfer {
   constructor(adb, destinationDir) {
     this._adb = adb;
     this._dir = destinationDir;
@@ -18,4 +18,4 @@ class FileXfer {
   }
 }
 
-module.exports = FileXfer;
+module.exports = FileTransfer;
diff --git a/detox/src/devices/common/drivers/android/tools/TempFileTransfer.js b/detox/src/devices/common/drivers/android/tools/TempFileTransfer.js
new file mode 100644
index 000000000..42648fe75
--- /dev/null
+++ b/detox/src/devices/common/drivers/android/tools/TempFileTransfer.js
@@ -0,0 +1,14 @@
+const FileTransfer = require('./FileTransfer');
+
+const FILE_PATH = '/data/local/tmp/detox';
+
+class TempFileTransfer extends FileTransfer {
+  constructor(adb) {
+    super(adb, FILE_PATH);
+  }
+}
+
+module.exports = {
+  TempFileTransfer,
+  FILE_PATH
+};
diff --git a/detox/src/devices/common/drivers/android/tools/TempFileXfer.js b/detox/src/devices/common/drivers/android/tools/TempFileXfer.js
deleted file mode 100644
index 900c8e9b8..000000000
--- a/detox/src/devices/common/drivers/android/tools/TempFileXfer.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// @ts-nocheck
-const FileXfer = require('./FileXfer');
-
-const EMU_TEMP_PATH = '/data/local/tmp';
-const EMU_TEMP_INSTALL_PATH = `${EMU_TEMP_PATH}/detox`;
-
-class TempFileXfer extends FileXfer {
-  /**
-   * @param adb { ADB }
-   */
-  constructor(adb) {
-    super(adb, EMU_TEMP_INSTALL_PATH);
-  }
-}
-
-module.exports = TempFileXfer;
diff --git a/detox/src/devices/lifecycle/GenyGlobalLifecycleHandler.js b/detox/src/devices/lifecycle/GenyGlobalLifecycleHandler.js
index 0c609579d..61d0c4f23 100644
--- a/detox/src/devices/lifecycle/GenyGlobalLifecycleHandler.js
+++ b/detox/src/devices/lifecycle/GenyGlobalLifecycleHandler.js
@@ -12,8 +12,6 @@ class GenyGlobalLifecycleHandler {
     this._instanceLifecycleService = instanceLifecycleService;
   }
 
-  // TODO: remove this ignore as soon as DetoxPrimaryContext is covered with tests
-  /* istanbul ignore next */
   async globalInit() {}
 
   emergencyCleanup() {
diff --git a/detox/src/devices/runtime/drivers/android/AndroidDriver.js b/detox/src/devices/runtime/drivers/android/AndroidDriver.js
index 8f5e8a541..8ff4d523c 100644
--- a/detox/src/devices/runtime/drivers/android/AndroidDriver.js
+++ b/detox/src/devices/runtime/drivers/android/AndroidDriver.js
@@ -1,3 +1,4 @@
+/* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none" }] */
 // @ts-nocheck
 const path = require('path');
 const URL = require('url').URL;
@@ -31,7 +32,7 @@ const log = logger.child({ cat: 'device' });
  * @property adb { ADB }
  * @property aapt { AAPT }
  * @property apkValidator { ApkValidator }
- * @property fileXfer { FileXfer }
+ * @property fileTransfer { FileTransfer }
  * @property appInstallHelper { AppInstallHelper }
  * @property appUninstallHelper { AppUninstallHelper }
  * @property devicePathBuilder { AndroidDevicePathBuilder }
@@ -51,7 +52,7 @@ class AndroidDriver extends DeviceDriverBase {
     this.aapt = deps.aapt;
     this.apkValidator = deps.apkValidator;
     this.invocationManager = deps.invocationManager;
-    this.fileXfer = deps.fileXfer;
+    this.fileTransfer = deps.fileTransfer;
     this.appInstallHelper = deps.appInstallHelper;
     this.appUninstallHelper = deps.appUninstallHelper;
     this.devicePathBuilder = deps.devicePathBuilder;
@@ -162,11 +163,11 @@ class AndroidDriver extends DeviceDriverBase {
       }
   }
 
-  async pressBack() { // eslint-disable-line no-unused-vars
+  async pressBack() {
     await this.uiDevice.pressBack();
   }
 
-  async sendToHome(params) { // eslint-disable-line no-unused-vars
+  async sendToHome(params) {
     await this.uiDevice.pressHome();
   }
 
@@ -331,8 +332,8 @@ class AndroidDriver extends DeviceDriverBase {
   }
 
   async _sendNotificationDataToDevice(dataFileLocalPath, adbName) {
-    await this.fileXfer.prepareDestinationDir(adbName);
-    return await this.fileXfer.send(adbName, dataFileLocalPath, 'notification.json');
+    await this.fileTransfer.prepareDestinationDir(adbName);
+    return await this.fileTransfer.send(adbName, dataFileLocalPath, 'notification.json');
   }
 
   _startActivityWithUrl(url) {
diff --git a/detox/src/devices/runtime/factories/android.js b/detox/src/devices/runtime/factories/android.js
index e025ecf4c..053ebe3b6 100644
--- a/detox/src/devices/runtime/factories/android.js
+++ b/detox/src/devices/runtime/factories/android.js
@@ -1,3 +1,4 @@
+/* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none" }] */
 const RuntimeDeviceFactory = require('./base');
 
 class RuntimeDriverFactoryAndroid extends RuntimeDeviceFactory {
@@ -6,7 +7,7 @@ class RuntimeDriverFactoryAndroid extends RuntimeDeviceFactory {
     const adb = serviceLocator.adb;
     const aapt = serviceLocator.aapt;
     const apkValidator = serviceLocator.apkValidator;
-    const fileXfer = serviceLocator.fileXfer;
+    const fileTransfer = serviceLocator.fileTransfer;
     const devicePathBuilder = serviceLocator.devicePathBuilder;
 
     const AppInstallHelper = require('../../common/drivers/android/tools/AppInstallHelper');
@@ -18,9 +19,9 @@ class RuntimeDriverFactoryAndroid extends RuntimeDeviceFactory {
       adb,
       aapt,
       apkValidator,
-      fileXfer,
+      fileTransfer,
       devicePathBuilder,
-      appInstallHelper: new AppInstallHelper(adb, fileXfer),
+      appInstallHelper: new AppInstallHelper(adb, fileTransfer),
       appUninstallHelper: new AppUninstallHelper(adb),
       instrumentation: new MonitoredInstrumentation(adb),
     };
@@ -41,7 +42,7 @@ class AndroidEmulator extends RuntimeDriverFactoryAndroid {
 }
 
 class AndroidAttached extends RuntimeDriverFactoryAndroid {
-  _createDriver(deviceCookie, deps, configs) { // eslint-disable-line no-unused-vars
+  _createDriver(deviceCookie, deps, configs) {
     const props = {
       adbName: deviceCookie.adbName,
     };
@@ -52,7 +53,7 @@ class AndroidAttached extends RuntimeDriverFactoryAndroid {
 }
 
 class Genycloud extends RuntimeDriverFactoryAndroid {
-  _createDriver(deviceCookie, deps, configs) { // eslint-disable-line no-unused-vars
+  _createDriver(deviceCookie, deps, configs) {
     const props = {
       instance: deviceCookie.instance,
     };
diff --git a/detox/src/devices/runtime/factories/base.js b/detox/src/devices/runtime/factories/base.js
index 626b73605..1c277ebbe 100644
--- a/detox/src/devices/runtime/factories/base.js
+++ b/detox/src/devices/runtime/factories/base.js
@@ -1,3 +1,4 @@
+/* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none" }] */
 const RuntimeDevice = require('../RuntimeDevice');
 
 class RuntimeDeviceFactory {
@@ -7,8 +8,8 @@ class RuntimeDeviceFactory {
     return new RuntimeDevice({ ...commonDeps, ...configs }, runtimeDriver);
   }
 
-  _createDriverDependencies(commonDeps) { } // eslint-disable-line no-unused-vars
-  _createDriver(deviceCookie, deps, configs) {} // eslint-disable-line no-unused-vars
+  _createDriverDependencies(commonDeps) { }
+  _createDriver(deviceCookie, deps, configs) {}
 }
 
 module.exports = RuntimeDeviceFactory;
diff --git a/detox/src/logger/DetoxLogger.js b/detox/src/logger/DetoxLogger.js
index 70e6e385d..512a51bf9 100644
--- a/detox/src/logger/DetoxLogger.js
+++ b/detox/src/logger/DetoxLogger.js
@@ -310,19 +310,19 @@ class DetoxLogger {
   /** @internal */
   static defaultOptions({ level }) {
     const ph = level === 'trace' || level === 'debug'
-      ? value => require('chalk').grey(value) + ' '
-      : value => require('chalk').grey(value);
+      ? value => require('chalk').default.grey(value) + ' '
+      : value => require('chalk').default.grey(value);
 
     const id = level === 'trace'
-      ? value => require('chalk').yellow(`@${value}`)
+      ? value => require('chalk').default.yellow(`@${value}`)
       : undefined;
 
     const cat = level === 'trace' || level === 'debug'
-      ? (value) => require('chalk').yellow(`${value}`.split(',', 1)[0])
+      ? (value) => require('chalk').default.yellow(`${value}`.split(',', 1)[0])
       : undefined;
 
     const event = level === 'trace' || level === 'debug'
-      ? (value) => require('chalk').grey(`:${value}`)
+      ? (value) => require('chalk').default.grey(`:${value}`)
       : undefined;
 
     const identity = x => x;
diff --git a/detox/src/servicelocator/android/index.js b/detox/src/servicelocator/android/index.js
index c1b7a9599..0bf53c95d 100644
--- a/detox/src/servicelocator/android/index.js
+++ b/detox/src/servicelocator/android/index.js
@@ -3,7 +3,7 @@ const DeviceRegistry = require('../../devices/DeviceRegistry');
 const AAPT = require('../../devices/common/drivers/android/exec/AAPT');
 const ADB = require('../../devices/common/drivers/android/exec/ADB');
 const ApkValidator = require('../../devices/common/drivers/android/tools/ApkValidator');
-const TempFileXfer = require('../../devices/common/drivers/android/tools/TempFileXfer');
+const { TempFileTransfer } = require('../../devices/common/drivers/android/tools/TempFileTransfer');
 
 const AndroidServiceLocator = {
   get emulator() {
@@ -18,7 +18,7 @@ const AndroidServiceLocator = {
 AndroidServiceLocator.adb = new ADB();
 AndroidServiceLocator.aapt = new AAPT();
 AndroidServiceLocator.apkValidator = new ApkValidator(AndroidServiceLocator.aapt);
-AndroidServiceLocator.fileXfer = new TempFileXfer(AndroidServiceLocator.adb);
+AndroidServiceLocator.fileTransfer = new TempFileTransfer(AndroidServiceLocator.adb);
 AndroidServiceLocator.deviceRegistry = DeviceRegistry.forAndroid();
 AndroidServiceLocator.devicePathBuilder = new AndroidDevicePathBuilder();
 
diff --git a/detox/src/utils/ExclusiveLockfile.js b/detox/src/utils/ExclusiveLockfile.js
index e4e48b6c1..da29ae99c 100644
--- a/detox/src/utils/ExclusiveLockfile.js
+++ b/detox/src/utils/ExclusiveLockfile.js
@@ -86,6 +86,7 @@ class ExclusiveLockfile {
    */
   _doRead() {
     const contents = fs.readFileSync(this._lockFilePath, this._options.read);
+    // @ts-ignore Node.js can parse buffer as JSON
     return JSON.parse(contents);
   }
 
diff --git a/detox/src/utils/argparse.js b/detox/src/utils/argparse.js
index 302c94e17..020ead6fa 100644
--- a/detox/src/utils/argparse.js
+++ b/detox/src/utils/argparse.js
@@ -1,15 +1,9 @@
 const path = require('path');
 
 const _ = require('lodash');
-const argv = require('minimist')(process.argv.slice(2));
 
 const { escape } = require('./pipeCommands');
 
-function getArgValue(key, alias) {
-  const value = _getArgvValue(key, alias);
-  return value === undefined ? getEnvValue(key) : value;
-}
-
 function getEnvValue(key) {
   const envKey = _.findKey(process.env, matchesKey(
     `DETOX_${_.snakeCase(key)}`.toUpperCase()
@@ -23,31 +17,12 @@ function getEnvValue(key) {
   return value;
 }
 
-function _getArgvValue(...aliases) {
-  if (!argv) {
-    return;
-  }
-
-  for (const alias of aliases) {
-    if (alias && argv[alias]) {
-      return argv[alias];
-    }
-  }
-}
-
 function matchesKey(key) {
   return /* istanbul ignore next */ process.platform === 'win32'
     ? (value, envKey) => envKey.toUpperCase() === key
     : (value, envKey) => envKey === key;
 }
 
-function getFlag(key) {
-  if (argv && argv[key]) {
-    return true;
-  }
-  return false;
-}
-
 const DEFAULT_JOIN_ARGUMENTS_OPTIONS = {
   prefix: '--',
   joiner: ' ',
@@ -92,9 +67,7 @@ function getCurrentCommand() {
 }
 
 module.exports = {
-  getArgValue,
   getEnvValue,
-  getFlag,
   joinArgs,
   getCurrentCommand,
 };
diff --git a/generation/core/type-checks.js b/generation/core/type-checks.js
index 7dd095b69..657ddae3c 100644
--- a/generation/core/type-checks.js
+++ b/generation/core/type-checks.js
@@ -23,9 +23,9 @@ function isGreyMatcher({ name }) {
   return templateFromString(
     `
   if (
-    typeof ARG !== "object" || 
+    typeof ARG !== "object" ||
     ARG.type !== "Invocation" ||
-    typeof ARG.value !== "object" || 
+    typeof ARG.value !== "object" ||
     typeof ARG.value.target !== "object" ||
     ARG.value.target.value !== "GREYMatchers"
   ) {
@@ -40,9 +40,9 @@ function isGreyAction({ name }) {
   return templateFromString(
     `
   if (
-    typeof ARG !== "object" || 
+    typeof ARG !== "object" ||
     ARG.type !== "Invocation" ||
-    typeof ARG.value !== "object" || 
+    typeof ARG.value !== "object" ||
     typeof ARG.value.target !== "object" ||
     ARG.value.target.value !== "GREYActions"
   ) {
@@ -70,8 +70,8 @@ function isArray({ name }) {
   return templateFromString(
     `
 if (
-  (typeof ARG !== 'object') || 
-  (!ARG instanceof Array)
+  (typeof ARG !== 'object') ||
+  !Array.isArray(ARG)
 ) {
     throw new Error('${name} must be an array, got ' + typeof ARG);
   }


Test patch:
diff --git a/detox/__tests__/helpers.js b/detox/__tests__/helpers.js
index 020eb46f7..4669bb0d3 100644
--- a/detox/__tests__/helpers.js
+++ b/detox/__tests__/helpers.js
@@ -1,6 +1,8 @@
 const path = require('path');
 
+const fs = require('fs-extra');
 const _ = require('lodash');
+const tempfile = require('tempfile');
 const yargs = require('yargs');
 
 function callCli(modulePath, cmd) {
@@ -27,14 +29,58 @@ function callCli(modulePath, cmd) {
         'camel-case-expansion': false,
         'dot-notation': false,
         'duplicate-arguments-array': false,
+        'populate--': true,
       })
       .command(spiedModule)
+      .wrap(null)
       .exitProcess(false)
       .fail((msg, err) => reject(err || msg))
       .parse(cmd, (err) => err && reject(err));
   });
 }
 
+function buildMockCommand(options = {}) {
+  if (!options.stdout) {
+    options.stdout = tempfile('.txt');
+  }
+
+  return {
+    get cmd() {
+      const env = [
+        options.exitCode ? `CLI_EXIT_CODE=${options.exitCode}` : undefined,
+        options.sleep ? `CLI_SLEEP=${options.sleep}` : undefined,
+        `CLI_TEST_STDOUT=${options.stdout}`,
+      ].filter(Boolean).join(' ');
+
+      return `cross-env ${env} node ${path.join(__dirname, '../local-cli/__mocks__/executable')}`;
+    },
+
+    options,
+
+    _calls: undefined,
+
+    get calls() {
+      if (this._calls === undefined) {
+        if (fs.existsSync(options.stdout)) {
+          this._calls = fs.readFileSync(options.stdout, 'utf8')
+            .trim()
+            .split('\n')
+            .map(c => JSON.parse(c));
+        } else {
+          this._calls = [];
+        }
+      }
+
+      return this._calls;
+    },
+
+    async clean() {
+      await fs.remove(options.stdout);
+    },
+  };
+}
+
+exports.buildMockCommand = buildMockCommand;
 exports.callCli = callCli;
 exports.latestInstanceOf = (clazz) => _.last(clazz.mock.instances);
 exports.lastCallTo = (mocked) => _.last(mocked.mock.calls);
diff --git a/detox/local-cli/test.test.js b/detox/local-cli/test.test.js
index 5dbc81fe4..d21bf84ea 100644
--- a/detox/local-cli/test.test.js
+++ b/detox/local-cli/test.test.js
@@ -11,17 +11,16 @@ jest.mock('./utils/jestInternals');
 const cp = require('child_process');
 const cpSpawn = cp.spawn;
 const os = require('os');
-const path = require('path');
 const util = require('util');
 
 const fs = require('fs-extra');
 const _ = require('lodash');
-const yargs = require('yargs');
+
+const { buildMockCommand, callCli } = require('../__tests__/helpers');
 
 const { DEVICE_LAUNCH_ARGS_DEPRECATION } = require('./testCommand/warnings');
 
 describe('CLI', () => {
-  let _cliCallDump;
   let _env;
   let logger;
   let _temporaryFiles;
@@ -31,26 +30,25 @@ describe('CLI', () => {
   let GenyDeviceRegistryFactory;
   let jestInternals;
 
+  let mockExecutable;
+
   afterEach(() => {
     cp.spawn = cpSpawn;
   });
 
   beforeEach(() => {
-    _cliCallDump = undefined;
     _env = process.env;
     _temporaryFiles = [];
 
-    process.env = {
-      ..._env,
-      CLI_TEST_STDOUT: tempfile('.txt'),
-    };
+    process.env = { ..._env };
 
-    const executable = path.relative(process.cwd(), path.join(__dirname, '__mocks__/executable'));
+    mockExecutable = buildMockCommand();
+    _temporaryFiles.push(mockExecutable.options.stdout);
 
     detoxConfig = {
       testRunner: {
         args: {
-          $0: os.platform() === 'win32' ? `node ${executable}` : executable,
+          $0: mockExecutable.cmd,
           config: 'e2e/config.json'
         },
         forwardEnv: true,
@@ -98,7 +96,7 @@ describe('CLI', () => {
   describe('by default', () => {
     test('by default, should attempt to load config from package.json or .detoxrc', async () => {
       const expectedError = /^Cannot run Detox without a configuration/;
-      await expect(runRaw('test')).rejects.toThrowError(expectedError);
+      await expect(callCli('./test', 'test')).rejects.toThrowError(expectedError);
     });
   });
 
@@ -519,45 +517,6 @@ describe('CLI', () => {
     return tempFilePath;
   }
 
-  async function runRaw(...command) {
-    let memArgv;
-
-    try {
-      memArgv = process.argv.splice(2, Infinity, ...command);
-
-      return await new Promise((resolve, reject) => {
-        const testCommand = require('./test');
-        const originalHandler = testCommand.handler;
-
-        const parser = yargs()
-          .scriptName('detox')
-          .parserConfiguration({
-            'boolean-negation': false,
-            'camel-case-expansion': false,
-            'dot-notation': false,
-            'duplicate-arguments-array': false,
-            'populate--': true,
-          })
-          .command({
-            ...testCommand,
-            async handler(argv) {
-              try {
-                await originalHandler(argv);
-                resolve();
-              } catch (e) {
-                reject(e);
-              }
-            },
-          })
-          .wrap(null);
-
-        parser.parse(command, err => err && reject(err));
-      });
-    } finally {
-      memArgv && process.argv.splice(2, Infinity, ...memArgv);
-    }
-  }
-
   async function run(...args) {
     let contents = `module.exports = ${util.inspect(detoxConfig, { depth: Infinity })};`;
     if (detoxConfig.testRunner && detoxConfig.testRunner.inspectBrk) {
@@ -566,18 +525,11 @@ describe('CLI', () => {
 
     detoxConfigPath = tempfile('.js', contents);
     const __configPath = Math.random() > 0.5 ? '-C' : '--config-path';
-    return runRaw('test', __configPath, detoxConfigPath, ...args);
+    await callCli('./test', ['test', __configPath, detoxConfigPath, ...args]);
   }
 
   function cliCall(index = 0) {
-    if (!_cliCallDump) {
-      _cliCallDump = fs.readFileSync(process.env.CLI_TEST_STDOUT, 'utf8')
-        .split('\n')
-        .filter(Boolean)
-        .map(line => JSON.parse(line));
-    }
-
-    const mockCall = _cliCallDump[index];
+    const mockCall = mockExecutable.calls[index];
     if (!mockCall) {
       return null;
     }
@@ -597,6 +549,7 @@ describe('CLI', () => {
   }
 
   function mockExitCode(code) {
-    process.env.CLI_EXIT_CODE = code;
+    mockExecutable.options.exitCode = code;
+    detoxConfig.testRunner.args.$0 = mockExecutable.cmd;
   }
 });
diff --git a/detox/src/android/AndroidExpect.test.js b/detox/src/android/AndroidExpect.test.js
index 82c32591e..eb040687d 100644
--- a/detox/src/android/AndroidExpect.test.js
+++ b/detox/src/android/AndroidExpect.test.js
@@ -515,6 +515,15 @@ describe('AndroidExpect', () => {
         await e.web.element(e.by.web.tag('tag')).runScriptWithArgs(script, argsArr);
       });
 
+      it('runScriptWithArgs (unhappy paths)', async () => {
+        const script = 'function bar(a,b) {}';
+        const someElement = e.web.element(e.by.web.id('id'));
+
+        await expect(someElement.runScriptWithArgs(script, 42)).rejects.toThrowError(/args must be an array/);
+        await expect(someElement.runScriptWithArgs(script, null)).rejects.toThrowError(/args must be an array/);
+        await expect(someElement.runScriptWithArgs(script, {})).rejects.toThrowError(/args must be an array/);
+      });
+
       it('getCurrentUrl', async () => {
         await e.web.element(e.by.web.id('id')).getCurrentUrl();
         await e.web.element(e.by.web.className('className')).getCurrentUrl();
diff --git a/detox/src/artifacts/templates/plugin/TwoSnapshotsPerTestPlugin.js b/detox/src/artifacts/templates/plugin/TwoSnapshotsPerTestPlugin.js
index bc94473bf..ceb036f74 100644
--- a/detox/src/artifacts/templates/plugin/TwoSnapshotsPerTestPlugin.js
+++ b/detox/src/artifacts/templates/plugin/TwoSnapshotsPerTestPlugin.js
@@ -1,3 +1,4 @@
+/* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none" }] */
 // @ts-nocheck
 const DetoxRuntimeError = require('../../../errors/DetoxRuntimeError');
 
@@ -85,7 +86,7 @@ class TwoSnapshotsPerTestPlugin extends ArtifactPlugin {
    * @protected
    * @abstract
    */
-  async preparePathForSnapshot(testSummary, snapshotName) {} // eslint-disable-line no-unused-vars
+  async preparePathForSnapshot(testSummary, snapshotName) {}
 
   /***
    * Creates a handle for a test artifact (video recording, log, etc.)
diff --git a/detox/src/artifacts/templates/plugin/WholeTestRecorderPlugin.js b/detox/src/artifacts/templates/plugin/WholeTestRecorderPlugin.js
index 50efbcb69..d2311348f 100644
--- a/detox/src/artifacts/templates/plugin/WholeTestRecorderPlugin.js
+++ b/detox/src/artifacts/templates/plugin/WholeTestRecorderPlugin.js
@@ -1,4 +1,4 @@
-// @ts-nocheck
+/* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none" }] */
 const ArtifactPlugin = require('./ArtifactPlugin');
 
 /***
@@ -8,6 +8,7 @@ class WholeTestRecorderPlugin extends ArtifactPlugin {
   constructor({ api }) {
     super({ api });
 
+    /** @type {*} */
     this.testRecording = null;
   }
 
@@ -51,7 +52,7 @@ class WholeTestRecorderPlugin extends ArtifactPlugin {
    * @abstract
    * @protected
    */
-  createTestRecording() {}
+  createTestRecording(config) {}
 
   /***
    * @abstract
diff --git a/detox/src/configuration/index.test.js b/detox/src/configuration/index.test.js
index aeb97eb04..29d0a6544 100644
--- a/detox/src/configuration/index.test.js
+++ b/detox/src/configuration/index.test.js
@@ -18,7 +18,7 @@ describe('composeDetoxConfig', () => {
 
     args = {};
 
-    require('../utils/argparse').getArgValue.mockImplementation(key => args[key]);
+    require('../utils/argparse').getEnvValue.mockImplementation(key => args[key]);
     configuration = require('./index');
   });
 
diff --git a/detox/src/devices/common/drivers/android/tools/AppInstallHelper.test.js b/detox/src/devices/common/drivers/android/tools/AppInstallHelper.test.js
index 9a1acb003..aedb4586a 100644
--- a/detox/src/devices/common/drivers/android/tools/AppInstallHelper.test.js
+++ b/detox/src/devices/common/drivers/android/tools/AppInstallHelper.test.js
@@ -10,23 +10,23 @@ describe('Android app installation helper', () => {
     adb = new ADBMock();
   });
 
-  let fileXfer;
+  let fileTransfer;
   let uut;
   beforeEach(() => {
-    const TempFileXfer = jest.genMockFromModule('./TempFileXfer');
-    fileXfer = new TempFileXfer();
+    const FileTransfer = jest.genMockFromModule('./FileTransfer');
+    fileTransfer = new FileTransfer(adb, '/mock-destination-dir');
 
     const AppInstallHelper = require('./AppInstallHelper');
-    uut = new AppInstallHelper(adb, fileXfer);
+    uut = new AppInstallHelper(adb, fileTransfer);
   });
 
   it('should recreate the transient dir on the device', async () => {
     await uut.install(deviceId, appBinaryPath, testBinaryPath);
-    expect(fileXfer.prepareDestinationDir).toHaveBeenCalledWith(deviceId);
+    expect(fileTransfer.prepareDestinationDir).toHaveBeenCalledWith(deviceId);
   });
 
   it('should throw if transient dir prep fails', async () => {
-    fileXfer.prepareDestinationDir.mockRejectedValue(new Error('mocked error in adb-shell'));
+    fileTransfer.prepareDestinationDir.mockRejectedValue(new Error('mocked error in adb-shell'));
 
     try {
       await uut.install(deviceId, appBinaryPath, testBinaryPath);
@@ -36,16 +36,16 @@ describe('Android app installation helper', () => {
 
   it('should push app-binary file to the device', async () => {
     await uut.install(deviceId, appBinaryPath, testBinaryPath);
-    expect(fileXfer.send).toHaveBeenCalledWith(deviceId, appBinaryPath, 'Application.apk');
+    expect(fileTransfer.send).toHaveBeenCalledWith(deviceId, appBinaryPath, 'Application.apk');
   });
 
   it('should push test-binary file to the device', async () => {
     await uut.install(deviceId, appBinaryPath, testBinaryPath);
-    expect(fileXfer.send).toHaveBeenCalledWith(deviceId, testBinaryPath, 'Test.apk');
+    expect(fileTransfer.send).toHaveBeenCalledWith(deviceId, testBinaryPath, 'Test.apk');
   });
 
   it('should break if file push fails', async () => {
-    fileXfer.send.mockRejectedValue(new Error('mocked error in adb-push'));
+    fileTransfer.send.mockRejectedValue(new Error('mocked error in adb-push'));
 
     try {
       await uut.install(deviceId, appBinaryPath, testBinaryPath);
@@ -54,7 +54,7 @@ describe('Android app installation helper', () => {
   });
 
   it('should remote-install both binaries via shell', async () => {
-    fileXfer.send
+    fileTransfer.send
       .mockReturnValueOnce('/mocked-final-dir/first.apk')
       .mockReturnValueOnce('/mocked-final-dir/second.apk');
 
@@ -73,12 +73,12 @@ describe('Android app installation helper', () => {
   });
 
   it('should allow for an install with no test binary', async () => {
-    fileXfer.send
+    fileTransfer.send
       .mockReturnValueOnce('/mocked-final-dir/first.apk')
       .mockReturnValueOnce('/mocked-final-dir/second.apk');
 
     await uut.install(deviceId, appBinaryPath, undefined);
-    expect(fileXfer.send).toHaveBeenCalledTimes(1);
+    expect(fileTransfer.send).toHaveBeenCalledTimes(1);
     expect(adb.remoteInstall).toHaveBeenCalledWith(deviceId, '/mocked-final-dir/first.apk');
     expect(adb.remoteInstall).toHaveBeenCalledTimes(1);
   });
diff --git a/detox/src/devices/common/drivers/android/tools/FileXfer.test.js b/detox/src/devices/common/drivers/android/tools/FileTransfer.test.js
similarity index 91%
rename from detox/src/devices/common/drivers/android/tools/FileXfer.test.js
rename to detox/src/devices/common/drivers/android/tools/FileTransfer.test.js
index 287dec3ff..1a7186457 100644
--- a/detox/src/devices/common/drivers/android/tools/FileXfer.test.js
+++ b/detox/src/devices/common/drivers/android/tools/FileTransfer.test.js
@@ -9,8 +9,8 @@ describe('File-transfer util', () => {
     const ADBMock = jest.genMockFromModule('../exec/ADB');
     adb = new ADBMock();
 
-    const FileXfer = require('./FileXfer');
-    uut = new FileXfer(adb, deviceDestinationDir);
+    const FileTransfer = require('./FileTransfer');
+    uut = new FileTransfer(adb, deviceDestinationDir);
   });
 
   it('should create the destination directory on the device', async () => {
diff --git a/detox/src/devices/common/drivers/android/tools/TempFileXfer.test.js b/detox/src/devices/common/drivers/android/tools/TempFileTransfer.test.js
similarity index 78%
rename from detox/src/devices/common/drivers/android/tools/TempFileXfer.test.js
rename to detox/src/devices/common/drivers/android/tools/TempFileTransfer.test.js
index 0fa3fd709..c63df0817 100644
--- a/detox/src/devices/common/drivers/android/tools/TempFileXfer.test.js
+++ b/detox/src/devices/common/drivers/android/tools/TempFileTransfer.test.js
@@ -5,8 +5,8 @@ describe('Temp file transfer', () => {
     const ADB = jest.genMockFromModule('../exec/ADB');
     adb = new ADB();
 
-    const TempFileXfer = require('./TempFileXfer');
-    uut = new TempFileXfer(adb);
+    const { TempFileTransfer } = require('./TempFileTransfer');
+    uut = new TempFileTransfer(adb);
   });
 
   it('should use the default temp-dir', async () => {
diff --git a/detox/src/devices/lifecycle/GenyGlobalLifecycleHandler.test.js b/detox/src/devices/lifecycle/GenyGlobalLifecycleHandler.test.js
index 787912d11..52a2a1aa3 100644
--- a/detox/src/devices/lifecycle/GenyGlobalLifecycleHandler.test.js
+++ b/detox/src/devices/lifecycle/GenyGlobalLifecycleHandler.test.js
@@ -38,6 +38,12 @@ describe('Global-context lifecycle handler for Genymotion SaaS emulators', () =>
     data: { name },
   });
 
+  describe('global init', () => {
+    it('should do nothing', async () => {
+      await expect(lifecycleHandler.globalInit()).resolves.toBeUndefined();
+    });
+  });
+
   describe('global clean-up', () => {
     const givenDeletionPendingDevices = (rawDevices) => deviceCleanupRegistry.readRegisteredDevices.mockResolvedValue({ rawDevices });
     const givenNoDeletionPendingDevices = () => givenDeletionPendingDevices([]);
diff --git a/detox/src/devices/runtime/RuntimeDevice.test.js b/detox/src/devices/runtime/RuntimeDevice.test.js
index be18a9ffc..c6a743834 100644
--- a/detox/src/devices/runtime/RuntimeDevice.test.js
+++ b/detox/src/devices/runtime/RuntimeDevice.test.js
@@ -11,7 +11,6 @@ describe('Device', () => {
   let errorComposer;
   let emitter;
   let RuntimeDevice;
-  let argparse;
   let Client;
   let client;
   let driverMock;
@@ -19,9 +18,6 @@ describe('Device', () => {
   beforeEach(async () => {
     jest.mock('../../utils/logger');
 
-    jest.mock('../../utils/argparse');
-    argparse = require('../../utils/argparse');
-
     jest.mock('./drivers/DeviceDriverBase');
     DeviceDriverBase = require('./drivers/DeviceDriverBase');
 
@@ -362,7 +358,6 @@ describe('Device', () => {
     it(`(relaunch) with delete=false when reuse is enabled should not uninstall and install`, async () => {
       const expectedArgs = expectedDriverArgs;
       const device = await aValidDevice();
-      argparse.getArgValue.mockReturnValue(true);
 
       await device.relaunchApp();
 
diff --git a/detox/src/devices/runtime/drivers/android/AndroidDriver.test.js b/detox/src/devices/runtime/drivers/android/AndroidDriver.test.js
index a37dcf5cb..52334ad48 100644
--- a/detox/src/devices/runtime/drivers/android/AndroidDriver.test.js
+++ b/detox/src/devices/runtime/drivers/android/AndroidDriver.test.js
@@ -17,7 +17,7 @@ describe('Android driver', () => {
   let adb;
   let aapt;
   let apkValidator;
-  let fileXfer;
+  let fileTransfer;
   let appInstallHelper;
   let appUninstallHelper;
   let instrumentation;
@@ -36,7 +36,7 @@ describe('Android driver', () => {
       adb,
       aapt,
       apkValidator,
-      fileXfer,
+      fileTransfer,
       appInstallHelper,
       appUninstallHelper,
       instrumentation,
@@ -213,21 +213,21 @@ describe('Android driver', () => {
     describe('in app launch (with dedicated arg)', () => {
       it('should prepare the device for receiving notification data file', async () => {
         await uut.launchApp(bundleId, notificationArgs, '');
-        expect(fileXfer.prepareDestinationDir).toHaveBeenCalledWith(adbName);
+        expect(fileTransfer.prepareDestinationDir).toHaveBeenCalledWith(adbName);
       });
 
       it('should transfer the notification data file to the device', async () => {
         await uut.launchApp(bundleId, notificationArgs, '');
-        expect(fileXfer.send).toHaveBeenCalledWith(adbName, notificationArgs.detoxUserNotificationDataURL, 'notification.json');
+        expect(fileTransfer.send).toHaveBeenCalledWith(adbName, notificationArgs.detoxUserNotificationDataURL, 'notification.json');
       });
 
       it('should not send the data if device prep fails', async () => {
-        fileXfer.prepareDestinationDir.mockRejectedValue(new Error());
+        fileTransfer.prepareDestinationDir.mockRejectedValue(new Error());
         await expect(uut.launchApp(bundleId, notificationArgs, '')).rejects.toThrowError();
       });
 
       it('should launch instrumentation with a modified notification data URL arg', async () => {
-        fileXfer.send.mockReturnValue(mockNotificationDataTargetPath);
+        fileTransfer.send.mockReturnValue(mockNotificationDataTargetPath);
 
         await uut.launchApp(bundleId, notificationArgs, '');
 
@@ -252,8 +252,8 @@ describe('Android driver', () => {
         it('should pre-transfer notification data to device', async () => {
           await spec.applyFn();
 
-          expect(fileXfer.prepareDestinationDir).toHaveBeenCalledWith(adbName);
-          expect(fileXfer.send).toHaveBeenCalledWith(adbName, notificationArgs.detoxUserNotificationDataURL, 'notification.json');
+          expect(fileTransfer.prepareDestinationDir).toHaveBeenCalledWith(adbName);
+          expect(fileTransfer.send).toHaveBeenCalledWith(adbName, notificationArgs.detoxUserNotificationDataURL, 'notification.json');
         });
 
         it('should start the app with notification data using invocation-manager', async () => {
@@ -277,7 +277,7 @@ describe('Android driver', () => {
         await uut.launchApp(bundleId, {}, '');
         await uut.deliverPayload(notificationArgsDelayed);
 
-        expect(fileXfer.send).not.toHaveBeenCalled();
+        expect(fileTransfer.send).not.toHaveBeenCalled();
       });
 
       it('should not start the app using invocation-manager', async () => {
@@ -580,10 +580,10 @@ describe('Android driver', () => {
     const ApkValidator = require('../../../common/drivers/android/tools/ApkValidator');
     apkValidator = new ApkValidator();
 
-    jest.mock('../../../common/drivers/android/tools/TempFileXfer');
-    const FileXfer = jest.requireMock('../../../common/drivers/android/tools/TempFileXfer');
-    fileXfer = new FileXfer();
-    fileXfer.send.mockResolvedValue(mockNotificationDataTargetPath);
+    jest.mock('../../../common/drivers/android/tools/FileTransfer');
+    const FileTransfer = jest.requireMock('../../../common/drivers/android/tools/FileTransfer');
+    fileTransfer = new FileTransfer();
+    fileTransfer.send.mockResolvedValue(mockNotificationDataTargetPath);
 
     jest.mock('../../../common/drivers/android/tools/AppInstallHelper');
     const AppInstallHelper = require('../../../common/drivers/android/tools/AppInstallHelper');
diff --git a/detox/src/utils/argparse.test.js b/detox/src/utils/argparse.test.js
index 192627289..beccd8c54 100644
--- a/detox/src/utils/argparse.test.js
+++ b/detox/src/utils/argparse.test.js
@@ -1,92 +1,40 @@
 jest.unmock('process');
 
 describe('argparse', () => {
-  let minimist;
-
-  beforeEach(() => {
-    jest.mock('minimist');
-    minimist = require('minimist');
-  });
-
-  describe('getArgValue()', () => {
-    describe('using env variables', () => {
-      let _env;
-      let argparse;
-
-      beforeEach(() => {
-        _env = process.env;
-
-        process.env = {
-          ..._env,
-          DETOX_FOO_BAR: 'value',
-        };
-
-        minimist.mockReturnValue(undefined);
-        argparse = require('./argparse');
-      });
-
-      afterEach(() => {
-        process.env = _env;
-      });
-
-      it(`nonexistent key should return undefined`, () => {
-        expect(argparse.getArgValue('blah')).not.toBeDefined();
-      });
+  describe('getEnvValue()', () => {
+    let _env;
+    let argparse;
 
-      it(`existing DETOX_SNAKE_FORMAT key should return its value (kebab-case input)`, () => {
-        expect(argparse.getArgValue('foo-bar')).toBe('value');
-      });
+    beforeEach(() => {
+      _env = process.env;
 
-      it(`existing DETOX_SNAKE_FORMAT key should return its value (camelCase input)`, () => {
-        expect(argparse.getArgValue('fooBar')).toBe('value');
-      });
+      process.env = {
+        ..._env,
+        DETOX_FOO_BAR: 'value',
+      };
 
-      it('should return undefined if process.env contain something with a string of undefined' ,() => {
-        process.env.DETOX_FOO_BAR = 'undefined';
-        expect(argparse.getArgValue('fooBar')).toBe(undefined);
-      });
+      argparse = require('./argparse');
     });
 
-    describe('using arguments', () => {
-      let argparse;
-
-      beforeEach(() => {
-        minimist.mockReturnValue({ 'kebab-case-key': 'a value', 'b': 'shortened' });
-        argparse = require('./argparse');
-      });
-
-      it(`nonexistent key should return undefined result`, () => {
-        expect(argparse.getArgValue('blah')).not.toBeDefined();
-      });
-
-      it(`alias alternative should fiind the result`, () => {
-        expect(argparse.getArgValue('blah', 'b')).toBe('shortened');
-      });
-
-      it(`existing key should return a result`, () => {
-        expect(argparse.getArgValue('kebab-case-key')).toBe('a value');
-      });
+    afterEach(() => {
+      process.env = _env;
     });
-  });
 
-  describe('getFlag()', () => {
-    let argparse;
-
-    beforeEach(() => {
-      minimist.mockReturnValue({ 'flag-true': 1, 'flag-false': 0 });
-      argparse = require('./argparse');
+    it(`nonexistent key should return undefined`, () => {
+      expect(argparse.getEnvValue('blah')).not.toBeDefined();
     });
 
-    it('should return true if flag value is truthy', () => {
-      expect(argparse.getFlag('flag-true')).toBe(true);
+    it(`existing DETOX_SNAKE_FORMAT key should return its value (kebab-case input)`, () => {
+      expect(argparse.getEnvValue('foo-bar')).toBe('value');
     });
 
-    it('should return false if flag is not set', () => {
-      expect(argparse.getFlag('flag-false')).toBe(false);
+    it(`existing DETOX_SNAKE_FORMAT key should return its value (camelCase input)`, () => {
+      expect(argparse.getEnvValue('fooBar')).toBe('value');
     });
 
-    it('should return true if flag is not set', () => {
-      expect(argparse.getFlag('flag-missing')).toBe(false);
+    it('should return undefined if process.env contain something with a string of undefined' ,() => {
+      process.env.DETOX_FOO_BAR = 'undefined';
+      expect(argparse.getEnvValue('fooBar')).toBe(undefined);
     });
   });
 
@@ -130,5 +78,14 @@ describe('argparse', () => {
       expect(argparse.getCurrentCommand()).toMatch(/^[^\\/]\S*jest.*$/);
       expect(argparse.getCurrentCommand()).toContain(process.argv.slice(2).join(' '));
     });
+
+    it('should return the rest of the command as-is', () => {
+      process.argv.push(__dirname);
+      try {
+        expect(argparse.getCurrentCommand()).toContain(__dirname);
+      } finally {
+        process.argv.pop();
+      }
+    });
   });
 });