detox 19.9.0 → 21.0.0-breaking.new-global-lifecycle.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (219) hide show
  1. package/Detox-android/com/wix/detox/{19.9.0/detox-19.9.0-javadoc.jar → 21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0-javadoc.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{19.9.0/detox-19.9.0-sources.jar → 21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0.aar +0 -0
  12. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0.aar.md5 +1 -0
  13. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0.aar.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0.aar.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0.aar.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/{19.9.0/detox-19.9.0.pom → 21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0.pom} +2 -2
  17. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0.pom.md5 +1 -0
  18. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0.pom.sha1 +1 -0
  19. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0.pom.sha256 +1 -0
  20. package/Detox-android/com/wix/detox/21.0.0-breaking.new-global-lifecycle.0/detox-21.0.0-breaking.new-global-lifecycle.0.pom.sha512 +1 -0
  21. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  22. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  23. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  24. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  25. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  26. package/Detox-ios-src.tbz +0 -0
  27. package/Detox-ios.tbz +0 -0
  28. package/android/build.gradle +2 -2
  29. package/android/detox/build.gradle +2 -2
  30. package/android/detox/publish-pom.gradle +1 -5
  31. package/android/detox/publishing.gradle +7 -9
  32. package/android/detox/src/full/java/com/wix/detox/Detox.java +2 -59
  33. package/android/detox/src/full/java/com/wix/detox/NotificationDataParser.kt +3 -3
  34. package/android/detox/src/full/java/com/wix/detox/adapters/server/WebSocketClient.java +1 -3
  35. package/android/detox/src/full/java/com/wix/detox/espresso/DetoxAction.java +3 -1
  36. package/android/detox/src/full/java/com/wix/detox/espresso/UiAutomatorHelper.java +1 -1
  37. package/android/detox/src/full/java/com/wix/detox/espresso/matcher/ViewMatchers.kt +23 -16
  38. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeLoadingMonitor.kt +8 -54
  39. package/android/detox/src/main/java/com/wix/detox/common/DetoxErrors.java +1 -4
  40. package/android/detox/src/main/java/com/wix/detox/espresso/UiControllerSpy.kt +1 -2
  41. package/android/detox/src/main/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +36 -0
  42. package/android/detox/src/{full → main}/java/com/wix/detox/espresso/action/GetAttributesAction.kt +1 -13
  43. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/utils/UiControllerUtils.kt +1 -1
  44. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/AdjustSliderToPositionActionTest.kt +59 -0
  45. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/GetAttributesActionTest.kt +3 -15
  46. package/android/gradle/wrapper/gradle-wrapper.properties +1 -2
  47. package/android/gradlew +107 -181
  48. package/index.d.ts +208 -186
  49. package/index.js +1 -0
  50. package/internals.d.ts +124 -0
  51. package/internals.js +4 -0
  52. package/local-cli/build-framework-cache.js +2 -2
  53. package/local-cli/build.js +7 -8
  54. package/local-cli/build.test.js +46 -40
  55. package/local-cli/clean-framework-cache.js +3 -3
  56. package/local-cli/cli.js +0 -1
  57. package/local-cli/init.js +20 -55
  58. package/local-cli/templates/jest.js +9 -34
  59. package/local-cli/test.js +18 -282
  60. package/local-cli/test.test.js +332 -673
  61. package/local-cli/testCommand/TestRunnerCommand.js +157 -0
  62. package/local-cli/{utils/testCommandArgs.js → testCommand/builder.js} +10 -35
  63. package/local-cli/testCommand/middlewares.js +75 -0
  64. package/local-cli/{utils → testCommand}/warnings.js +0 -10
  65. package/local-cli/utils/jestInternals.js +13 -2
  66. package/local-cli/utils/yargsUtils.js +67 -0
  67. package/package.json +28 -22
  68. package/runners/deprecation.js +47 -0
  69. package/runners/jest/JestCircusEnvironment.js +3 -38
  70. package/runners/jest/adapter.d.ts +4 -10
  71. package/runners/jest/adapter.js +3 -6
  72. package/runners/jest/assignReporter.d.ts +4 -1
  73. package/runners/jest/assignReporter.js +3 -6
  74. package/runners/jest/deprecation.js +25 -0
  75. package/runners/jest/globalSetup.js +1 -0
  76. package/runners/jest/globalTeardown.js +1 -0
  77. package/runners/jest/index.js +21 -0
  78. package/runners/jest/reporter.js +1 -0
  79. package/runners/jest/reporters/DetoxReporter.js +5 -0
  80. package/runners/jest/specReporter.d.ts +4 -9
  81. package/runners/jest/specReporter.js +3 -10
  82. package/runners/jest/streamlineReporter.js +3 -22
  83. package/runners/jest/testEnvironment/index.js +155 -0
  84. package/runners/{jest-circus → jest/testEnvironment}/listeners/DetoxCoreListener.js +35 -17
  85. package/runners/{jest-circus → jest/testEnvironment}/listeners/DetoxInitErrorListener.js +1 -1
  86. package/runners/jest/testEnvironment/listeners/DetoxPlatformFilterListener.js +27 -0
  87. package/runners/jest/{SpecReporterImpl.js → testEnvironment/listeners/SpecReporter.js} +61 -7
  88. package/runners/jest/testEnvironment/listeners/WorkerAssignReporter.js +34 -0
  89. package/runners/jest/testEnvironment/listeners/index.js +13 -0
  90. package/runners/{jest-circus → jest/testEnvironment}/utils/assertExistingContext.js +2 -2
  91. package/runners/jest/testEnvironment/utils/assertJestCircus27.js +56 -0
  92. package/runners/jest/testEnvironment/utils/assertJestCircus27.test.js +23 -0
  93. package/runners/jest/{utils → testEnvironment/utils}/getFullTestName.js +0 -0
  94. package/runners/jest/{utils → testEnvironment/utils}/hasTimedOut.js +0 -0
  95. package/runners/jest/{utils → testEnvironment/utils}/index.js +0 -0
  96. package/runners/jest/{utils → testEnvironment/utils}/stdout.js +0 -0
  97. package/runners/jest-circus/environment/index.js +6 -0
  98. package/runners/jest-circus/index.js +1 -10
  99. package/runners/jest-circus/reporter.js +1 -0
  100. package/runners/migration.js +37 -0
  101. package/runners/mocha/DetoxMochaAdapter.js +3 -35
  102. package/runners/mocha/adapter.d.ts +4 -7
  103. package/runners/mocha/adapter.js +3 -5
  104. package/src/{Detox.js → DetoxWorker.js} +131 -164
  105. package/src/artifacts/ArtifactsManager.js +51 -2
  106. package/src/artifacts/log/android/ADBLogcatRecording.js +28 -11
  107. package/src/artifacts/providers/index.js +0 -4
  108. package/src/artifacts/utils/buildDefaultArtifactsRootDirpath.js +2 -4
  109. package/src/artifacts/utils/temporaryPath.js +2 -0
  110. package/src/client/AsyncWebSocket.js +1 -1
  111. package/src/client/actions/actions.js +2 -2
  112. package/src/configuration/collectCliConfig.js +2 -4
  113. package/src/configuration/composeAppsConfig.js +9 -67
  114. package/src/configuration/composeArtifactsConfig.js +6 -32
  115. package/src/configuration/composeBehaviorConfig.js +3 -13
  116. package/src/configuration/composeDeviceConfig.js +37 -62
  117. package/src/configuration/composeLoggerConfig.js +50 -0
  118. package/src/configuration/composeRunnerConfig.js +74 -14
  119. package/src/configuration/composeSessionConfig.js +1 -3
  120. package/src/configuration/index.js +24 -23
  121. package/src/configuration/utils/deviceAppTypes.js +0 -1
  122. package/src/devices/allocation/DeviceAllocator.js +3 -3
  123. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +4 -3
  124. package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +2 -6
  125. package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +0 -1
  126. package/src/devices/allocation/drivers/ios/SimulatorAllocDriver.js +1 -1
  127. package/src/devices/allocation/drivers/ios/SimulatorLauncher.js +3 -3
  128. package/src/devices/allocation/factories/index.js +0 -1
  129. package/src/devices/common/drivers/DeviceLauncher.js +2 -2
  130. package/src/devices/common/drivers/android/emulator/exec/EmulatorExec.js +1 -1
  131. package/src/devices/common/drivers/android/exec/ADB.js +0 -5
  132. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceNaming.js +4 -9
  133. package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +34 -9
  134. package/src/devices/lifecycle/GenyGlobalLifecycleHandler.js +1 -1
  135. package/src/devices/runtime/RuntimeDevice.js +69 -81
  136. package/src/devices/runtime/drivers/index.js +0 -1
  137. package/src/devices/runtime/drivers/ios/SimulatorDriver.js +3 -2
  138. package/src/devices/runtime/factories/ios.js +1 -8
  139. package/src/devices/runtime/utils/LaunchArgsEditor.js +4 -59
  140. package/src/devices/runtime/utils/Storage.js +4 -0
  141. package/src/environmentFactory.js +0 -8
  142. package/src/errors/DetoxConfigErrorComposer.js +76 -29
  143. package/src/errors/DetoxError.js +4 -0
  144. package/src/errors/DetoxRuntimeError.js +5 -5
  145. package/src/errors/index.js +2 -0
  146. package/src/ipc/IPCClient.js +117 -0
  147. package/src/ipc/IPCServer.js +87 -0
  148. package/src/ipc/state.js +76 -0
  149. package/src/logger/DetoxLogger.js +244 -0
  150. package/src/logger/DetoxTraceEventBuilder.js +21 -0
  151. package/src/logger/DetoxTracer.js +133 -0
  152. package/src/logger/TraceThreadDispatcher.js +52 -0
  153. package/src/{utils → logger}/customConsoleLogger.js +1 -1
  154. package/src/realms/DetoxConstants.js +13 -0
  155. package/src/realms/DetoxContext.js +170 -0
  156. package/src/realms/DetoxInternalsFacade.js +35 -0
  157. package/src/realms/DetoxPrimaryContext.js +229 -0
  158. package/src/realms/DetoxSecondaryContext.js +94 -0
  159. package/src/realms/index.js +10 -0
  160. package/src/realms/primary.js +3 -0
  161. package/src/realms/secondary.js +3 -0
  162. package/src/server/DetoxConnection.js +1 -1
  163. package/src/symbols.js +56 -0
  164. package/src/utils/ChromeTracingExporter.js +5 -6
  165. package/src/utils/Timer.js +14 -6
  166. package/{local-cli/utils/misc.js → src/utils/envUtils.js} +0 -9
  167. package/src/utils/logger.js +2 -162
  168. package/src/utils/shellUtils.js +18 -0
  169. package/src/utils/streamUtils.js +214 -0
  170. package/src/utils/trace.js +9 -44
  171. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0-javadoc.jar.md5 +0 -1
  172. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0-javadoc.jar.sha1 +0 -1
  173. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0-javadoc.jar.sha256 +0 -1
  174. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0-javadoc.jar.sha512 +0 -1
  175. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0-sources.jar.md5 +0 -1
  176. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0-sources.jar.sha1 +0 -1
  177. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0-sources.jar.sha256 +0 -1
  178. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0-sources.jar.sha512 +0 -1
  179. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0.aar +0 -0
  180. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0.aar.md5 +0 -1
  181. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0.aar.sha1 +0 -1
  182. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0.aar.sha256 +0 -1
  183. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0.aar.sha512 +0 -1
  184. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0.pom.md5 +0 -1
  185. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0.pom.sha1 +0 -1
  186. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0.pom.sha256 +0 -1
  187. package/Detox-android/com/wix/detox/19.9.0/detox-19.9.0.pom.sha512 +0 -1
  188. package/android/detox/src/full/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +0 -22
  189. package/android/detox/src/full/java/com/wix/detox/espresso/common/SliderHelper.kt +0 -75
  190. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/ReflectUtils.kt +0 -10
  191. package/android/detox/src/testFull/java/com/wix/detox/espresso/common/SliderHelperTest.kt +0 -39
  192. package/local-cli/templates/mocha.js +0 -32
  193. package/local-cli/utils/splitArgv.js +0 -107
  194. package/runners/integration.js +0 -16
  195. package/runners/jest/DetoxAdapterCircus.js +0 -60
  196. package/runners/jest/DetoxAdapterImpl.js +0 -81
  197. package/runners/jest/DetoxAdapterJasmine.js +0 -67
  198. package/runners/jest/DetoxStreamlineJestReporter.js +0 -98
  199. package/runners/jest/FailingTestsReporter.js +0 -16
  200. package/runners/jest/SpecReporterCircus.js +0 -51
  201. package/runners/jest/SpecReporterJasmine.js +0 -39
  202. package/runners/jest/WorkerAssignReporterCircus.js +0 -17
  203. package/runners/jest/WorkerAssignReporterImpl.js +0 -21
  204. package/runners/jest/WorkerAssignReporterJasmine.js +0 -15
  205. package/runners/jest/runnerInfo.js +0 -9
  206. package/runners/jest-circus/environment.js +0 -206
  207. package/runners/jest-circus/utils/assertJestCircus26.js +0 -39
  208. package/runners/jest-circus/utils/wrapErrorWithNoopLifecycle.js +0 -14
  209. package/src/DetoxConstants.js +0 -13
  210. package/src/DetoxExportWrapper.js +0 -140
  211. package/src/artifacts/timeline/TimelineArtifactPlugin.js +0 -95
  212. package/src/devices/allocation/drivers/NoneAllocDriver.js +0 -10
  213. package/src/devices/allocation/factories/none.js +0 -11
  214. package/src/index.js +0 -6
  215. package/src/utils/MissingDetox.js +0 -78
  216. package/src/utils/fakeTimestampsProvider.js +0 -9
  217. package/src/utils/getWorkerId.js +0 -5
  218. package/src/utils/lastFailedTests.js +0 -38
  219. package/src/utils/sh.js +0 -18
@@ -3,45 +3,77 @@ if (process.platform === 'win32') {
3
3
  jest.retryTimes(1); // TODO: investigate why it gets stuck for the 1st time on Windows
4
4
  }
5
5
 
6
- jest.mock('child_process');
7
- jest.mock('../src/utils/logger');
6
+ jest.mock('../src/logger/DetoxLogger');
8
7
  jest.mock('../src/devices/DeviceRegistry');
9
8
  jest.mock('../src/devices/allocation/drivers/android/genycloud/GenyDeviceRegistryFactory');
10
- jest.mock('../src/utils/lastFailedTests');
9
+ jest.mock('./utils/jestInternals');
10
+
11
+ const os = require('os');
12
+ const path = require('path');
11
13
 
12
14
  const fs = require('fs-extra');
13
15
  const _ = require('lodash');
14
16
  const yargs = require('yargs');
15
17
 
16
- const { DEVICE_LAUNCH_ARGS_DEPRECATION } = require('./utils/warnings');
18
+ const { DEVICE_LAUNCH_ARGS_DEPRECATION } = require('./testCommand/warnings');
17
19
 
18
20
  describe('CLI', () => {
19
- let cp;
21
+ let _cliCallDump;
22
+ let _env;
20
23
  let logger;
21
- let temporaryFiles;
24
+ let _temporaryFiles;
22
25
  let detoxConfig;
23
26
  let detoxConfigPath;
24
27
  let DeviceRegistry;
25
28
  let GenyDeviceRegistryFactory;
26
- let _env;
29
+ let jestInternals;
27
30
 
28
31
  beforeEach(() => {
32
+ _cliCallDump = undefined;
29
33
  _env = process.env;
30
- process.env = { ..._env };
34
+ _temporaryFiles = [];
35
+
36
+ process.env = {
37
+ ..._env,
38
+ CLI_TEST_STDOUT: tempfile('.txt'),
39
+ };
40
+
41
+ const executable = path.relative(process.cwd(), path.join(__dirname, '__mocks__/executable'));
31
42
 
32
43
  detoxConfig = {
44
+ testRunner: {
45
+ args: {
46
+ $0: os.platform() === 'win32' ? `node ${executable}` : executable,
47
+ config: 'e2e/config.json'
48
+ },
49
+ },
33
50
  configurations: {
34
51
  single: {
35
- type: 'ios.simulator',
36
- device: 'iPhone X',
37
- binaryPath: 'path/to/app',
52
+ device: {
53
+ type: 'ios.simulator',
54
+ device: 'iPhone X'
55
+ },
56
+ apps: [],
38
57
  },
39
58
  },
40
59
  };
41
60
 
42
- cp = require('child_process');
43
- logger = require('../src/utils/logger');
44
- temporaryFiles = [];
61
+ const realJestInternals = jest.requireActual('./utils/jestInternals');
62
+ jestInternals = require('./utils/jestInternals');
63
+ Object.assign(jestInternals, realJestInternals);
64
+ jestInternals.readJestConfig = jest.fn(async (argv) => {
65
+ const runnerConfigTemplate = _.omit(
66
+ JSON.parse(require('./templates/jest').runnerConfig),
67
+ ['reporters', 'testEnvironment']
68
+ );
69
+
70
+ return realJestInternals.readJestConfig({
71
+ ...argv,
72
+ config: tempfile('.json', JSON.stringify(runnerConfigTemplate)),
73
+ });
74
+ });
75
+
76
+ logger = () => require('../src/logger/DetoxLogger').instances[0];
45
77
  DeviceRegistry = require('../src/devices/DeviceRegistry');
46
78
  DeviceRegistry.forAndroid.mockImplementation(() => new DeviceRegistry());
47
79
  DeviceRegistry.forIOS.mockImplementation(() => new DeviceRegistry());
@@ -52,7 +84,7 @@ describe('CLI', () => {
52
84
  afterEach(async () => {
53
85
  process.env = _env;
54
86
 
55
- await Promise.all(temporaryFiles.map(name => fs.remove(name)));
87
+ await Promise.all(_temporaryFiles.map(name => fs.remove(name)));
56
88
  });
57
89
 
58
90
  describe('by default', () => {
@@ -62,701 +94,333 @@ describe('CLI', () => {
62
94
  });
63
95
  });
64
96
 
65
- describe('(mocha)', () => {
66
- describe('given no extra args', () => {
67
- beforeEach(async () => run());
68
-
69
- test('should default to mocha', () => expect(cliCall().command).toMatch(/^mocha/));
70
- test('should default to --opts e2e/mocha.opts', () => expect(cliCall().command).toMatch(/--opts e2e\/mocha.opts/));
71
- test('should default to "e2e" test folder', () => expect(cliCall().command).toMatch(/ e2e$/));
72
- test('should pass --use-custom-logger true', () => expect(cliCall().command).toMatch(/--use-custom-logger true/));
73
- test('should not override process.env', () => expect(cliCall().env).toStrictEqual({}));
74
- test('should produce a default command (integration test)', () => {
75
- const quoteChar = detoxConfigPath.indexOf('\\') === -1 ? '' : undefined;
76
- const args = [
77
- `--opts`, `e2e/mocha.opts`,
78
- `--grep`, `:android:`, `--invert`,
79
- `--config-path`, quote(detoxConfigPath, quoteChar),
80
- `--use-custom-logger`, `true`
81
- ].join(' ');
82
-
83
- expect(cliCall().command).toBe(`mocha ${args} e2e`);
84
- });
85
- });
86
-
87
- test.each([['-o'], ['--runner-config']])('%s <path> should be aware of mocha.opts extension', async (__runnerConfig) => {
88
- await run(`${__runnerConfig} e2e/custom.opts`);
89
- expect(cliCall().command).toContain(`--opts e2e/custom.opts`);
90
- });
91
-
92
- test.each([['-o'], ['--runner-config']])('%s <path> should be aware of .mocharc extension', async (__runnerConfig) => {
93
- await run(`${__runnerConfig} e2e/.mocharc`);
94
- expect(cliCall().command).toContain(`--config e2e/.mocharc`);
95
- });
96
-
97
- test.each([['-l'], ['--loglevel']])('%s <value> should be passed as CLI argument', async (__loglevel) => {
98
- await run(`${__loglevel} verbose`);
99
- expect(cliCall().command).toContain(`--loglevel verbose`);
100
- });
101
-
102
- test('--no-color should be passed as CLI argument', async () => {
103
- await run(`--no-color`);
104
- expect(cliCall().command).toContain(' --no-colors ');
105
- });
106
-
107
- test.each([['-R'], ['--retries']])('%s <value> should print warning', async (__retries) => {
108
- await run(`${__retries} 1`);
109
- expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('Cannot use -R, --retries.'));
110
- });
111
-
112
- test.each([['-R'], ['--retries']])('%s <value> should be ignored', async (__retries) => {
113
- cp.execSync.mockImplementation(() => { throw new Error; });
114
- await run(`${__retries} 1`).catch(_.noop);
115
-
116
- expect(cliCall(0)).toBeDefined();
117
- expect(cliCall(1)).toBe(null);
118
- });
119
-
120
- test.each([['-r'], ['--reuse']])('%s <value> should be passed as CLI argument', async (__reuse) => {
121
- await run(`${__reuse}`);
122
- expect(cliCall().command).toContain('--reuse');
123
- });
124
-
125
- test.each([['-u'], ['--cleanup']])('%s <value> should be passed as CLI argument', async (__cleanup) => {
126
- await run(`${__cleanup}`);
127
- expect(cliCall().command).toContain('--cleanup');
128
- });
129
-
130
- test.each([['-d'], ['--debug-synchronization']])('%s <value> should have default value = 3000', async (__debug_synchronization) => {
131
- await run(`${__debug_synchronization}`);
132
- expect(cliCall().command).toContain('--debug-synchronization 3000');
133
- });
134
-
135
- test.each([['-d'], ['--debug-synchronization']])('%s <value> should be passed as 0 when given false', async (__debug_synchronization) => {
136
- await run(`${__debug_synchronization} false`);
137
- expect(cliCall().command).toContain('--debug-synchronization 0');
138
- });
139
-
140
- test.each([['-a'], ['--artifacts-location']])('%s <value> should be passed as CLI argument', async (__artifacts_location) => {
141
- await run(`${__artifacts_location} someLocation`);
142
- expect(cliCall().command).toContain('--artifacts-location someLocation');
143
- });
144
-
145
- test('--record-logs <value> should be passed as CLI argument', async () => {
146
- await run(`--record-logs all`);
147
- expect(cliCall().command).toContain('--record-logs all');
148
- });
149
-
150
- test('--take-screenshots <value> should be passed as CLI argument', async () => {
151
- await run(`--take-screenshots failing`);
152
- expect(cliCall().command).toContain('--take-screenshots failing');
153
- });
154
-
155
- test('--record-videos <value> should be passed as CLI argument', async () => {
156
- await run(`--record-videos failing`);
157
- expect(cliCall().command).toContain('--record-videos failing');
158
- });
159
-
160
- test('--capture-view-hierarchy <value> should be passed as CLI argument', async () => {
161
- await run(`--capture-view-hierarchy enabled`);
162
- expect(cliCall().command).toContain('--capture-view-hierarchy enabled');
163
- });
164
-
165
- test('--record-performance <value> should be passed as CLI argument', async () => {
166
- await run(`--record-performance all`);
167
- expect(cliCall().command).toContain('--record-performance all');
168
- });
169
-
170
- test('--record-timeline <value> should print "unsupported" warning', async () => {
171
- await run(`--record-timeline all`);
172
- expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('Cannot use --record-timeline.'));
173
- });
174
-
175
- test.each([['-w'], ['--workers']])('%s <value> should print "unsupported" warning', async (__workers) => {
176
- await run(`${__workers} 2`);
177
- expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('Cannot use -w, --workers.'));
178
- });
179
-
180
- test.each([['-H'], ['--headless']])('%s <value> should be passed as CLI argument', async (__headless) => {
181
- await run(`${__headless}`);
182
- expect(cliCall().command).toContain('--headless');
183
- });
184
-
185
- test('--gpu <value> should be passed as CLI argument', async () => {
186
- await run(`--gpu angle_indirect`);
187
- expect(cliCall().command).toContain('--gpu angle_indirect');
188
- });
189
-
190
- test('--device-boot-args should be passed as an environment variable (without deprecation warnings)', async () => {
191
- await run(`--device-boot-args "--verbose"`);
192
- expect(cliCall().env).toEqual({
193
- DETOX_DEVICE_BOOT_ARGS: '--verbose',
194
- });
195
- expect(logger.warn).not.toHaveBeenCalledWith(DEVICE_LAUNCH_ARGS_DEPRECATION);
196
- });
197
-
198
- test('--device-launch-args should serve as a deprecated alias to --device-boot-args', async () => {
199
- await run(`--device-launch-args "--verbose"`);
200
- expect(cliCall().env).toEqual({
201
- DETOX_DEVICE_BOOT_ARGS: '--verbose',
202
- });
203
- expect(logger.warn).toHaveBeenCalledWith(DEVICE_LAUNCH_ARGS_DEPRECATION);
204
- });
205
-
206
- test('--app-launch-args should be passed as an environment variable', async () => {
207
- await run(`--app-launch-args "--debug yes"`);
208
- expect(cliCall().env).toEqual({
209
- DETOX_APP_LAUNCH_ARGS: '--debug yes',
210
- });
211
- });
212
-
213
- test('--use-custom-logger false should be prevent passing CLI argument', async () => {
214
- await run(`--use-custom-logger false`);
215
- expect(cliCall().command).not.toContain('--use-custom-logger');
216
- });
217
-
218
- test('--force-adb-install should be ignored for iOS', async () => {
219
- singleConfig().type = 'ios.simulator';
220
- await run(`--force-adb-install`);
221
-
222
- expect(cliCall().command).not.toContain('--force-adb-install');
223
- });
224
-
225
- test('--force-adb-install should be passed as CLI argument for Android', async () => {
226
- singleConfig().type = 'android.emulator';
227
- await run(`--force-adb-install`);
228
-
229
- expect(cliCall().command).toContain('--force-adb-install');
230
- });
231
-
232
- test.each([['-n'], ['--device-name']])('%s <value> should be passed as CLI argument', async (__device_name) => {
233
- await run(`${__device_name} TheDevice`);
234
- expect(cliCall().command).toContain('--device-name TheDevice');
235
- });
236
-
237
- test('should omit --grep --invert for custom platforms', async () => {
238
- singleConfig().type = tempfile('.js', aCustomDriverModule());
239
-
97
+ describe.each([
98
+ ['iOS', 'ios.simulator'],
99
+ ['Android', 'android.emulator'],
100
+ ])('given no extra args (%s)', (_platform, deviceType) => {
101
+ beforeEach(async () => {
102
+ singleConfig().device.type = deviceType;
240
103
  await run();
241
- expect(cliCall().command).not.toContain(' --invert ');
242
- expect(cliCall().command).not.toContain(' --grep ');
243
- });
244
-
245
- test('specifying direct test paths', async () => {
246
- await run(`e2e/01.sanity.test.js e2e/02.sanity.test.js`);
247
- expect(cliCall().command).not.toMatch(/ e2e /);
248
- expect(cliCall().command).not.toMatch(/ e2e$/);
249
- expect(cliCall().command).toMatch(/ e2e\/01.sanity.test.js e2e\/02.sanity.test.js$/);
250
104
  });
251
105
 
252
- test('e.g., --bail should be passed through', async () => {
253
- await run(`--bail`);
254
- expect(cliCall().command).toContain('--bail');
106
+ test('should produce a default command', () => {
107
+ expect(cliCall().argv).toEqual([expect.stringContaining('executable'), '--config', 'e2e/config.json']);
255
108
  });
256
109
 
257
- test('e.g., --reporter spec should be passed through', async () => {
258
- await run(`--reporter spec`);
259
- expect(cliCall().command).toContain('--reporter spec');
110
+ test('should not override environment variables', () => {
111
+ expect(cliCall().env).toEqual({ DETOX_CONFIG_SNAPSHOT_PATH: expect.any(String) });
260
112
  });
261
113
 
262
- test('e.g., --bail e2e/Login.test.js should be split to --bail and e2e/Login.test.js', async () => {
263
- await run(`--bail e2e/Login.test.js --reporter spec`);
264
- expect(cliCall().command).toContain('--bail --reporter spec e2e/Login.test.js');
265
- });
266
-
267
- test.each([
268
- [`--runner-config "mocha configs/.mocharc"`, `--config ${quote('mocha configs/.mocharc')}`],
269
- [`--artifacts-location "artifacts dir/"`, `--artifacts-location ${quote('artifacts dir/')}`],
270
- [`--device-name "iPhone X"`, `--device-name ${quote('iPhone X')}`],
271
- [`"e2e tests/first test.spec.js"`, `"e2e tests/first test.spec.js"`],
272
- ])('should escape %s when forwarding it as a CLI argument', async (cmd, expected) => {
273
- await run(cmd);
274
- expect(cliCall().command).toContain(` ${expected}`);
114
+ test('should hint essential environment variables', () => {
115
+ expect(cliCall().fullCommand).toMatch(/^DETOX_CONFIG_PATH=.*\bexecutable/);
275
116
  });
276
117
  });
277
118
 
278
- describe('(jest)', () => {
279
- beforeEach(() => {
280
- detoxConfig.testRunner = 'jest';
281
- });
282
-
283
- describe('given no extra args (iOS)', () => {
284
- beforeEach(async () => {
285
- singleConfig().type = 'ios.simulator';
286
- await run();
287
- });
288
-
289
- test('should produce a default command (integration test, ios)', () => {
290
- const args = `--config e2e/config.json --testNamePattern ${quote('^((?!:android:).)*$')} --maxWorkers 1`;
291
- expect(cliCall().command).toBe(`jest ${args} e2e`);
292
- });
293
-
294
- test('should put default environment variables (integration test, ios)', () => {
295
- expect(cliCall().env).toEqual({
296
- DETOX_START_TIMESTAMP: expect.any(Number),
297
- DETOX_CONFIG_PATH: expect.any(String),
298
- DETOX_REPORT_SPECS: true,
299
- DETOX_USE_CUSTOM_LOGGER: true,
300
- });
301
- });
302
- });
303
-
304
- describe('given no extra args (Android)', () => {
305
- beforeEach(async () => {
306
- singleConfig().type = 'android.emulator';
307
- await run();
308
- });
309
-
310
- test('should produce a default command (integration test)', () => {
311
- const args = `--config e2e/config.json --testNamePattern ${quote('^((?!:ios:).)*$')} --maxWorkers 1`;
312
- expect(cliCall().command).toBe(`jest ${args} e2e`);
313
- });
314
-
315
- test('should put default environment variables (integration test)', () => {
316
- expect(cliCall().env).toEqual({
317
- DETOX_START_TIMESTAMP: expect.any(Number),
318
- DETOX_CONFIG_PATH: expect.any(String),
319
- DETOX_REPORT_SPECS: true,
320
- DETOX_USE_CUSTOM_LOGGER: true,
321
- });
322
- });
323
- });
324
-
325
- describe('given skipLegacyWorkersInjection: true', () => {
326
- describe.each([
327
- ['ios.simulator'],
328
- ['android.emulator'],
329
- ])('for %s', (configurationType) => {
330
- it('should omit --maxWorkers CLI arg', async () => {
331
- singleConfig().type = configurationType;
332
- detoxConfig.skipLegacyWorkersInjection = true;
333
- detoxConfig.runnerConfig = 'package.json';
334
-
335
- await run();
336
-
337
- expect(cliCall().command).not.toMatch(/--maxWorkers/);
338
- });
339
- });
340
- });
341
-
342
- test.each([['-c'], ['--configuration']])(
343
- '%s <configuration> should provide inverted --testNamePattern that configuration (jest)',
344
- async (__configuration) => {
345
- detoxConfig.configurations.iosTest = { ...detoxConfig.configurations.single };
346
- detoxConfig.configurations.iosTest.type = 'ios.simulator';
347
- detoxConfig.configurations.androidTest = { ...detoxConfig.configurations.single };
348
- detoxConfig.configurations.androidTest.type = 'android.emulator';
349
-
350
- await run(`${__configuration} androidTest`);
351
- expect(cliCall(0).command).toContain(`--testNamePattern ${quote('^((?!:ios:).)*$')}`);
352
- expect(cliCall(0).env.DETOX_CONFIGURATION).toBe('androidTest');
353
-
354
- await run(`${__configuration} iosTest`);
355
- expect(cliCall(1).command).toContain(`--testNamePattern ${quote('^((?!:android:).)*$')}`);
356
- expect(cliCall(1).env.DETOX_CONFIGURATION).toBe('iosTest');
357
- }
358
- );
359
-
360
- test.each([['-o'], ['--runner-config']])('%s <path> should point to the specified Jest config', async (__runnerConfig) => {
361
- await run(`${__runnerConfig} e2e/custom.config.js`);
362
- expect(cliCall().command).toContain(`--config e2e/custom.config.js`);
363
- });
364
-
365
- test.each([['-l'], ['--loglevel']])('%s <value> should be passed as environment variable', async (__loglevel) => {
366
- await run(`${__loglevel} trace`);
367
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_LOGLEVEL: 'trace' }));
368
- });
369
-
370
- test('--no-color should be passed as CLI argument', async () => {
371
- await run(`--no-color`);
372
- expect(cliCall().command).toContain(' --no-color ');
373
- });
374
-
375
- test.each([['-R'], ['--retries']])('%s <value> should execute successful run once', async (__retries) => {
376
- await run(`-R 1`);
377
- expect(cliCall(1)).toBe(null);
378
- });
379
-
380
- test.each([['-R'], ['--retries']])('%s <value> should clear failed tests file', async (__retries) => {
381
- await run(`-R 1`);
382
- const { resetLastFailedTests } = require('../src/utils/lastFailedTests');
383
- expect(resetLastFailedTests).toHaveBeenCalled();
384
- });
385
-
386
- test.each([['-R'], ['--retries']])('%s <value> should execute unsuccessful run N extra times', async (__retries) => {
387
- const { loadLastFailedTests } = require('../src/utils/lastFailedTests');
388
- loadLastFailedTests.mockReturnValueOnce(['e2e/failing1.test.js', 'e2e/failing2.test.js']);
389
- loadLastFailedTests.mockReturnValueOnce(['e2e/failing2.test.js']);
390
- cp.execSync.mockImplementation(() => { throw new Error; });
391
-
392
- await run(`-R 2`).catch(_.noop);
393
- expect(cliCall(0).command).toMatch(/ e2e$/);
394
- expect(cliCall(0).env).not.toHaveProperty('DETOX_RERUN_INDEX');
395
-
396
- expect(cliCall(1).command).toMatch(/ e2e\/failing1.test.js e2e\/failing2.test.js$/);
397
- expect(cliCall(1).env.DETOX_RERUN_INDEX).toBe(1);
398
-
399
- expect(cliCall(2).command).toMatch(/ e2e\/failing2.test.js$/);
400
- expect(cliCall(2).env.DETOX_RERUN_INDEX).toBe(2);
401
- });
402
-
403
- test.each([['-R'], ['--retries']])('%s <value> should not restart test runner if there are no failing tests paths', async (__retries) => {
404
- const { loadLastFailedTests } = require('../src/utils/lastFailedTests');
405
- loadLastFailedTests.mockReturnValueOnce([]);
406
- cp.execSync.mockImplementation(() => { throw new Error; });
407
-
408
- await run(`-R 1`).catch(_.noop);
409
- expect(cliCall(0)).not.toBe(null);
410
- expect(cliCall(1)).toBe(null);
411
- });
412
-
413
- test.each([['-R'], ['--retries']])('%s <value> should retain -- <...explicitPassthroughArgs>', async (__retries) => {
414
- const { loadLastFailedTests } = require('../src/utils/lastFailedTests');
415
- loadLastFailedTests.mockReturnValue(['tests/failing.test.js']);
416
- cp.execSync.mockImplementation(() => { throw new Error; });
417
-
418
- await run(`-R 1 tests -- --debug`).catch(_.noop);
419
- expect(cliCall(0).command).toMatch(/ --debug .* tests$/);
420
- expect(cliCall(1).command).toMatch(/ --debug .* tests\/failing.test.js$/);
421
- });
422
-
423
- test.each([['-r'], ['--reuse']])('%s <value> should be passed as environment variable', async (__reuse) => {
424
- await run(`${__reuse}`);
425
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_REUSE: true }));
426
- });
427
-
428
- test.each([['-u'], ['--cleanup']])('%s <value> should be passed as environment variable', async (__cleanup) => {
429
- await run(`${__cleanup}`);
430
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_CLEANUP: true }));
431
- });
432
-
433
- test.each([['-d'], ['--debug-synchronization']])('%s <value> should be passed as environment variable', async (__debug_synchronization) => {
434
- await run(`${__debug_synchronization} 5000`);
435
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_DEBUG_SYNCHRONIZATION: 5000 }));
436
- });
437
-
438
- test.each([['-d'], ['--debug-synchronization']])('%s <value> should be passed as 0 when given false', async (__debug_synchronization) => {
439
- await run(`${__debug_synchronization} false`);
440
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_DEBUG_SYNCHRONIZATION: 0 }));
441
- });
442
-
443
- test.each([['-d'], ['--debug-synchronization']])('%s <value> should have default value = 3000', async (__debug_synchronization) => {
444
- await run(`${__debug_synchronization}`);
445
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_DEBUG_SYNCHRONIZATION: 3000 }));
446
- });
447
-
448
- test.each([['-a'], ['--artifacts-location']])('%s <value> should be passed as environment variable', async (__artifacts_location) => {
449
- await run(`${__artifacts_location} /tmp`);
450
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_ARTIFACTS_LOCATION: '/tmp' }));
451
- });
452
-
453
- test('--record-logs <value> should be passed as environment variable', async () => {
454
- await run(`--record-logs all`);
455
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_RECORD_LOGS: 'all' }));
456
- });
119
+ test('should use runnerConfig.specs as default specs', async () => {
120
+ detoxConfig.testRunner.args._ = ['e2e/sanity'];
121
+ await run();
122
+ expect(_.last(cliCall().argv)).toEqual('e2e/sanity');
123
+ });
457
124
 
458
- test('--take-screenshots <value> should be passed as environment variable', async () => {
459
- await run(`--take-screenshots failing`);
460
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_TAKE_SCREENSHOTS: 'failing' }));
461
- });
125
+ test.each([['--config']])('%s <path> should point to the specified Jest config', async (__runnerConfig) => {
126
+ await run(__runnerConfig, 'e2e/custom.config.js');
127
+ expect(cliCall().argv).toEqual([expect.stringContaining('executable'), '--config', 'e2e/custom.config.js']);
128
+ });
462
129
 
463
- test('--record-videos <value> should be passed as environment variable', async () => {
464
- await run(`--record-videos failing`);
465
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_RECORD_VIDEOS: 'failing' }));
466
- });
130
+ test.each([['-l'], ['--loglevel']])('%s <value> should be passed as environment variable', async (__loglevel) => {
131
+ await run(__loglevel, 'trace');
132
+ expect(cliCall().env).not.toHaveProperty('DETOX_LOGLEVEL');
133
+ expect(cliCall().fullCommand).toMatch(/ DETOX_LOGLEVEL="trace" /);
134
+ });
467
135
 
468
- test('--record-performance <value> should be passed as environment variable', async () => {
469
- await run(`--record-performance all`);
470
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_RECORD_PERFORMANCE: 'all' }));
471
- });
136
+ test.each([['-R'], ['--retries']])('%s <value> should execute successful run once', async (__retries) => {
137
+ await run(__retries, 1);
138
+ expect(cliCall(1)).toBe(null);
139
+ });
472
140
 
473
- test('--record-timeline <value> should be passed as environment variable', async () => {
474
- await run(`--record-timeline all`);
475
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_RECORD_TIMELINE: 'all' }));
141
+ test.each([['-R'], ['--retries']])('%s <value> should execute unsuccessful run N extra times', async (__retries) => {
142
+ const context = require('../internals');
143
+ context.session.failedTestFiles = ['e2e/failing1.test.js', 'e2e/failing2.test.js'];
144
+ context.session.failedTestFiles.splice = jest.fn(() => {
145
+ context.session.failedTestFiles = ['e2e/failing2.test.js'];
476
146
  });
477
147
 
478
- test('--capture-view-hierarchy <value> should be passed as environment variable', async () => {
479
- await run(`--capture-view-hierarchy enabled`);
480
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_CAPTURE_VIEW_HIERARCHY: 'enabled' }));
481
- });
148
+ mockExitCode(1);
482
149
 
483
- describe.each([
484
- [false],
485
- [true],
486
- ])('when skipLegacyWorkersInjection is %j', (skipLegacyWorkersInjection) => {
487
- beforeEach(() => {
488
- Object.assign(detoxConfig, {
489
- skipLegacyWorkersInjection,
490
- runnerConfig: tempfile('.json', JSON.stringify({
491
- maxWorkers: 1,
492
- })),
493
- });
494
- });
150
+ await run(__retries, 2).catch(_.noop);
495
151
 
496
- test.each([['-w'], ['--workers']])('%s <value> should be passed as CLI argument', async (__workers) => {
497
- await run(`${__workers} 2`);
498
- expect(cliCall().command).toContain('--maxWorkers 2');
499
- });
152
+ expect(cliCall(0).env).not.toHaveProperty('DETOX_RERUN_INDEX');
153
+ expect(cliCall(0).argv).toEqual([expect.stringMatching(/executable$/), '--config', 'e2e/config.json']);
154
+ expect(cliCall(0).fullCommand).not.toMatch(/DETOX_RERUN_INDEX/);
500
155
 
501
- test.each([['-w'], ['--workers']])('%s <value> should be replaced with --maxWorkers <value>', async (__workers) => {
502
- await run(`${__workers} 2 --maxWorkers 3`);
156
+ expect(cliCall(1).env.DETOX_RERUN_INDEX).toBe('1');
157
+ expect(cliCall(1).argv).toEqual([expect.stringMatching(/executable$/), '--config', 'e2e/config.json', 'e2e/failing1.test.js', 'e2e/failing2.test.js']);
158
+ expect(cliCall(1).fullCommand).not.toMatch(/DETOX_RERUN_INDEX/);
503
159
 
504
- const { command } = cliCall();
505
- expect(command).toContain('--maxWorkers 3');
506
- expect(command).not.toContain('--maxWorkers 2');
507
- });
160
+ expect(cliCall(2).argv).toEqual([expect.stringMatching(/executable$/), '--config', 'e2e/config.json', 'e2e/failing2.test.js']);
161
+ expect(cliCall(2).env.DETOX_RERUN_INDEX).toBe('2');
162
+ expect(cliCall(2).fullCommand).not.toMatch(/DETOX_RERUN_INDEX/);
163
+ });
508
164
 
509
- test.each([['-w'], ['--workers']])('%s <value> can be overriden by a later value', async (__workers) => {
510
- await run(`${__workers} 2 ${__workers} 3`);
165
+ test.each([['-R'], ['--retries']])('%s <value> should not restart test runner if there are no failing tests paths', async (__retries) => {
166
+ mockExitCode(1);
511
167
 
512
- const { command } = cliCall();
513
- expect(command).toContain('--maxWorkers 3');
514
- expect(command).not.toContain('--maxWorkers 2');
515
- });
168
+ await run(__retries, 1).catch(_.noop);
169
+ expect(cliCall(0)).not.toBe(null);
170
+ expect(cliCall(1)).toBe(null);
171
+ });
516
172
 
517
- test.each([['-w'], ['--workers']])('%s <value> should not warn anything for iOS', async (__workers) => {
518
- singleConfig().type = 'ios.simulator';
519
- await run(`${__workers} 2`);
520
- expect(logger.warn).not.toHaveBeenCalled();
521
- });
173
+ test.each([['-R'], ['--retries']])('%s <value> should retain -- <...explicitPassthroughArgs>', async (__retries) => {
174
+ const context = require('../internals');
175
+ context.session.failedTestFiles = ['tests/failing.test.js'];
522
176
 
523
- test.each([['-w'], ['--workers']])('%s <value> should not put readOnlyEmu environment variable for iOS', async (__workers) => {
524
- singleConfig().type = 'ios.simulator';
525
- await run(`${__workers} 2`);
526
- expect(cliCall().env).not.toHaveProperty('DETOX_READ_ONLY_EMU');
527
- });
177
+ mockExitCode(1);
528
178
 
529
- test.each([['-w'], ['--workers']])('%s <value> should not put readOnlyEmu environment variable for android.attached', async (__workers) => {
530
- singleConfig().type = 'android.attached';
531
- await run(`${__workers} 2`);
532
- expect(cliCall().env).not.toHaveProperty('DETOX_READ_ONLY_EMU');
533
- });
179
+ await run(__retries, 1, 'tests', '--', '--debug').catch(_.noop);
534
180
 
535
- test.each([['-w'], ['--workers']])('%s <value> should not put readOnlyEmu environment variable for android.emulator if there is a single worker', async (__workers) => {
536
- singleConfig().type = 'android.emulator';
537
- await run(`${__workers} 1`);
538
- expect(cliCall().env).not.toHaveProperty('DETOX_READ_ONLY_EMU');
539
- });
181
+ expect(cliCall(0).argv).toEqual([expect.stringMatching(/executable$/), '--config', 'e2e/config.json', '--debug', 'tests']);
182
+ expect(cliCall(1).argv).toEqual([expect.stringMatching(/executable$/), '--config', 'e2e/config.json', '--debug', 'tests/failing.test.js']);
183
+ });
540
184
 
541
- test.each([['-w'], ['--workers']])('%s <value> should put readOnlyEmu environment variable for Android if there are multiple workers', async (__workers) => {
542
- singleConfig().type = 'android.emulator';
543
- await run(`${__workers} 2`);
544
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_READ_ONLY_EMU: true }));
545
- });
546
- });
185
+ test.each([['-r'], ['--reuse']])('%s <value> should be passed as environment variable', async (__reuse) => {
186
+ await run(__reuse);
187
+ expect(cliCall().env).not.toHaveProperty('DETOX_REUSE');
188
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_REUSE=true /);
189
+ });
547
190
 
548
- test('should omit --testNamePattern for custom platforms', async () => {
549
- singleConfig().type = tempfile('.js', aCustomDriverModule());
191
+ test.each([['-u'], ['--cleanup']])('%s <value> should be passed as environment variable', async (__cleanup) => {
192
+ await run(__cleanup);
193
+ expect(cliCall().env).not.toHaveProperty('DETOX_CLEANUP');
194
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_CLEANUP=true /);
195
+ });
550
196
 
551
- await run();
552
- expect(cliCall().command).not.toContain('--testNamePattern');
553
- });
197
+ test.each([['-d'], ['--debug-synchronization']])('%s <value> should be passed as environment variable', async (__debug_synchronization) => {
198
+ await run(__debug_synchronization, 5000);
199
+ expect(cliCall().env).not.toHaveProperty('DETOX_DEBUG_SYNCHRONIZATION');
200
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_DEBUG_SYNCHRONIZATION=5000 /);
201
+ });
554
202
 
555
- test.each([['-t'], ['--testNamePattern']])('should override --testNamePattern if a custom %s value is passed', async (__testNamePattern) => {
556
- await run(`${__testNamePattern} customPattern`);
557
- const { command } = cliCall();
203
+ test.each([['-d'], ['--debug-synchronization']])('%s <value> should be passed as 0 when given false', async (__debug_synchronization) => {
204
+ await run(__debug_synchronization, false);
205
+ expect(cliCall().env).not.toHaveProperty('DETOX_DEBUG_SYNCHRONIZATION');
206
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_DEBUG_SYNCHRONIZATION=0 /);
207
+ });
558
208
 
559
- expect(command).not.toMatch(/--testNamePattern .*(ios|android)/);
560
- expect(command).toMatch(/--testNamePattern customPattern($| )/);
561
- });
209
+ test.each([['-d'], ['--debug-synchronization']])('%s <value> should have default value = 3000', async (__debug_synchronization) => {
210
+ await run(`${__debug_synchronization}`);
211
+ expect(cliCall().env).not.toHaveProperty('DETOX_DEBUG_SYNCHRONIZATION');
212
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_DEBUG_SYNCHRONIZATION=3000 /);
213
+ });
562
214
 
563
- test('--jest-report-specs, by default, should be true, as environment variable', async () => {
564
- await run();
565
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_REPORT_SPECS: true }));
566
- });
215
+ test.each([['-a'], ['--artifacts-location']])('%s <value> should be passed as environment variable', async (__artifacts_location) => {
216
+ await run(__artifacts_location, '/tmp');
217
+ expect(cliCall().env).not.toHaveProperty('DETOX_ARTIFACTS_LOCATION');
218
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_ARTIFACTS_LOCATION="\/tmp" /);
219
+ });
567
220
 
568
- test('--jest-report-specs, by default, should be false, if multiple workers are enabled', async () => {
569
- await run('--workers 2');
570
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_REPORT_SPECS: false }));
571
- });
221
+ test('--record-logs <value> should be passed as environment variable', async () => {
222
+ await run('--record-logs', 'all');
223
+ expect(cliCall().env).not.toHaveProperty('DETOX_RECORD_LOGS');
224
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_RECORD_LOGS="all" /);
225
+ });
572
226
 
573
- test('--jest-report-specs, set explicitly, should override single worker defaults', async () => {
574
- await run('--jest-report-specs false');
575
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_REPORT_SPECS: false }));
576
- });
227
+ test('--take-screenshots <value> should be passed as environment variable', async () => {
228
+ await run('--take-screenshots', 'failing');
229
+ expect(cliCall().env).not.toHaveProperty('DETOX_TAKE_SCREENSHOTS');
230
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_TAKE_SCREENSHOTS="failing" /);
231
+ });
577
232
 
578
- test('--jest-report-specs, set explicitly, should override multiple workers defaults', async () => {
579
- await run('--workers 2 --jest-report-specs');
580
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_REPORT_SPECS: true }));
581
- });
233
+ test('--record-videos <value> should be passed as environment variable', async () => {
234
+ await run('--record-videos', 'failing');
235
+ expect(cliCall().env).not.toHaveProperty('DETOX_RECORD_VIDEOS');
236
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_RECORD_VIDEOS="failing" /);
237
+ });
582
238
 
583
- test.each([['-H'], ['--headless']])('%s <value> should be passed as environment variable', async (__headless) => {
584
- await run(`${__headless}`);
585
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_HEADLESS: true }));
586
- });
239
+ test('--record-performance <value> should be passed as environment variable', async () => {
240
+ await run('--record-performance', 'all');
241
+ expect(cliCall().env).not.toHaveProperty('DETOX_RECORD_PERFORMANCE');
242
+ expect(cliCall().fullCommand).toMatch(/\DETOX_RECORD_PERFORMANCE="all" /);
243
+ });
587
244
 
588
- test('--gpu <value> should be passed as environment variable', async () => {
589
- await run(`--gpu angle_indirect`);
590
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_GPU: 'angle_indirect' }));
591
- });
245
+ test('--capture-view-hierarchy <value> should be passed as environment variable', async () => {
246
+ await run('--capture-view-hierarchy', 'enabled');
247
+ expect(cliCall().env).not.toHaveProperty('DETOX_CAPTURE_VIEW_HIERARCHY');
248
+ expect(cliCall().fullCommand).toMatch(/\DETOX_CAPTURE_VIEW_HIERARCHY="enabled" /);
249
+ });
592
250
 
593
- test('--device-boot-args should be passed as an environment variable (without deprecation warnings)', async () => {
594
- await run(`--device-boot-args "--verbose"`);
595
- expect(cliCall().env).toEqual(expect.objectContaining({
596
- DETOX_DEVICE_BOOT_ARGS: '--verbose'
597
- }));
598
- expect(logger.warn).not.toHaveBeenCalledWith(DEVICE_LAUNCH_ARGS_DEPRECATION);
599
- });
251
+ test('--jest-report-specs, set explicitly, should be passed as an environment variable', async () => {
252
+ await run('--jest-report-specs');
253
+ expect(cliCall().env).not.toHaveProperty('DETOX_REPORT_SPECS');
254
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_REPORT_SPECS=true /);
255
+ });
600
256
 
601
- test('--device-launch-args should serve as a deprecated alias to --device-boot-args', async () => {
602
- await run(`--device-launch-args "--verbose"`);
603
- expect(cliCall().env.DETOX_DEVICE_BOOT_ARGS).toBe('--verbose');
604
- expect(logger.warn).toHaveBeenCalledWith(DEVICE_LAUNCH_ARGS_DEPRECATION);
605
- });
257
+ test.each([['-H'], ['--headless']])('%s <value> should be passed as environment variable', async (__headless) => {
258
+ await run(__headless);
259
+ expect(cliCall().env).not.toHaveProperty('DETOX_HEADLESS');
260
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_HEADLESS=true /);
261
+ });
606
262
 
607
- test('--app-launch-args should be passed as an environment variable', async () => {
608
- await run(`--app-launch-args "--debug yes"`);
609
- expect(cliCall().env).toEqual(expect.objectContaining({
610
- DETOX_APP_LAUNCH_ARGS: '--debug yes',
611
- }));
612
- });
263
+ test('--gpu <value> should be passed as environment variable', async () => {
264
+ await run('--gpu', 'angle_indirect');
265
+ expect(cliCall().env).not.toHaveProperty('DETOX_GPU');
266
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_GPU="angle_indirect" /);
267
+ });
613
268
 
614
- test('--use-custom-logger false should be prevent passing environment variable', async () => {
615
- await run(`--use-custom-logger false`);
616
- expect(cliCall().env).toEqual(expect.objectContaining({
617
- DETOX_USE_CUSTOM_LOGGER: false
618
- }));
619
- });
269
+ test('--device-boot-args should be passed as an environment variable (without deprecation warnings)', async () => {
270
+ await run('--device-boot-args="--verbose"');
271
+ expect(cliCall().env).not.toHaveProperty('DETOX_DEVICE_BOOT_ARGS');
272
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_DEVICE_BOOT_ARGS="--verbose" /);
273
+ expect(logger().warn).not.toHaveBeenCalledWith(DEVICE_LAUNCH_ARGS_DEPRECATION);
274
+ });
620
275
 
621
- test('--force-adb-install should be ignored for iOS', async () => {
622
- singleConfig().type = 'ios.simulator';
623
- await run(`--force-adb-install`);
624
- expect(cliCall().env).not.toHaveProperty('DETOX_FORCE_ADB_INSTALL');
625
- });
276
+ test('--device-launch-args should serve as a deprecated alias to --device-boot-args', async () => {
277
+ await run(`--device-launch-args="--verbose"`);
278
+ expect(cliCall().env).not.toHaveProperty('DETOX_DEVICE_BOOT_ARGS');
279
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_DEVICE_BOOT_ARGS="--verbose" /);
280
+ expect(logger().warn).toHaveBeenCalledWith(DEVICE_LAUNCH_ARGS_DEPRECATION);
281
+ });
626
282
 
627
- test('--force-adb-install should be passed as environment variable', async () => {
628
- singleConfig().type = 'android.emulator';
629
- await run(`--force-adb-install`);
630
- expect(cliCall().env).toEqual(expect.objectContaining({
631
- DETOX_FORCE_ADB_INSTALL: true,
632
- }));
633
- });
283
+ test('--app-launch-args should be passed as an environment variable', async () => {
284
+ await run(`--app-launch-args="--debug yes"`);
285
+ expect(cliCall().env).not.toHaveProperty('DETOX_APP_LAUNCH_ARGS');
286
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_APP_LAUNCH_ARGS="--debug yes" /);
287
+ });
634
288
 
635
- test.each([['-n'], ['--device-name']])('%s <value> should be passed as environment variable', async (__device_name) => {
636
- await run(`${__device_name} TheDevice`);
637
- expect(cliCall().env).toEqual(expect.objectContaining({
638
- DETOX_DEVICE_NAME: 'TheDevice',
639
- }));
640
- });
289
+ test('--use-custom-logger false should be prevent passing environment variable', async () => {
290
+ await run(`--use-custom-logger=false`);
291
+ expect(cliCall().env).not.toHaveProperty('DETOX_USE_CUSTOM_LOGGER');
292
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_USE_CUSTOM_LOGGER=false /);
293
+ });
641
294
 
642
- test('specifying direct test paths', async () => {
643
- await run(`e2e/01.sanity.test.js e2e/02.sanity.test.js`);
644
- expect(cliCall().command).not.toMatch(/ e2e /);
645
- expect(cliCall().command).not.toMatch(/ e2e$/);
646
- expect(cliCall().command).toMatch(/ e2e\/01.sanity.test.js e2e\/02.sanity.test.js$/);
647
- });
295
+ test('--force-adb-install should be ignored for iOS', async () => {
296
+ singleConfig().device.type = 'ios.simulator';
297
+ await run(`--force-adb-install`);
298
+ expect(cliCall().env).not.toHaveProperty('DETOX_FORCE_ADB_INSTALL');
299
+ expect(cliCall().fullCommand).not.toMatch(/DETOX_FORCE_ADB_INSTALL/);
300
+ });
648
301
 
649
- // TODO: fix --inspect-brk behavior on Windows, and replace (cmd|js) with js here
650
- test.each([
651
- ['--inspect-brk e2eFolder', /^node --inspect-brk .*jest\.(?:cmd|js) .* e2eFolder$/, {}],
652
- ['-d e2eFolder', / e2eFolder$/, { DETOX_DEBUG_SYNCHRONIZATION: 3000 }],
653
- ['--debug-synchronization e2eFolder', / e2eFolder$/, { DETOX_DEBUG_SYNCHRONIZATION: 3000 }],
654
- ['-r e2eFolder', / e2eFolder$/, { DETOX_REUSE: true }],
655
- ['--reuse e2eFolder', / e2eFolder$/, { DETOX_REUSE: true }],
656
- ['-u e2eFolder', / e2eFolder$/, { DETOX_CLEANUP: true }],
657
- ['--cleanup e2eFolder', / e2eFolder$/, { DETOX_CLEANUP: true }],
658
- ['--jest-report-specs e2eFolder', / e2eFolder$/, { DETOX_REPORT_SPECS: true }],
659
- ['-H e2eFolder', / e2eFolder$/, { DETOX_HEADLESS: true }],
660
- ['--headless e2eFolder', / e2eFolder$/, { DETOX_HEADLESS: true }],
661
- ['--keepLockFile e2eFolder', / e2eFolder$/, {}],
662
- ['--use-custom-logger e2eFolder', / e2eFolder$/, { DETOX_USE_CUSTOM_LOGGER: true }],
663
- ['--force-adb-install e2eFolder', / e2eFolder$/, { DETOX_FORCE_ADB_INSTALL: true }],
664
- ])('"%s" should be disambigued correctly', async (command, commandMatcher, envMatcher) => {
665
- singleConfig().type = 'android.emulator';
666
- await run(command);
667
-
668
- expect(cliCall().command).toMatch(commandMatcher);
669
- expect(cliCall().env).toEqual(expect.objectContaining(envMatcher));
670
- });
302
+ test('--force-adb-install should be passed as environment variable', async () => {
303
+ singleConfig().device.type = 'android.emulator';
304
+ await run(`--force-adb-install`);
305
+ expect(cliCall().env).not.toHaveProperty('DETOX_FORCE_ADB_INSTALL');
306
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_FORCE_ADB_INSTALL=true /);
307
+ });
671
308
 
672
- test('e.g., --debug should be passed through', async () => {
673
- await run(`--debug`);
674
- expect(cliCall().command).toContain('--debug');
675
- });
309
+ test.each([['-n'], ['--device-name']])('%s <value> should be passed as environment variable', async (__device_name) => {
310
+ await run(__device_name, 'TheDevice');
311
+ expect(cliCall().env).not.toHaveProperty('DETOX_DEVICE_NAME');
312
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_DEVICE_NAME="TheDevice" /);
313
+ });
676
314
 
677
- test('e.g., --coverageProvider v8 should be passed through', async () => {
678
- await run(`--coverageProvider v8`);
679
- expect(cliCall().command).toContain('--coverageProvider v8');
680
- });
315
+ test('specifying direct test paths instead of default args._', async () => {
316
+ detoxConfig.testRunner.args._ = ['e2e/'];
317
+ await run('e2e/01.sanity.test.js', 'e2e/02.sanity.test.js');
681
318
 
682
- test('e.g., --debug e2e/Login.test.js should be split to --debug and e2e/Login.test.js', async () => {
683
- await run(`--debug e2e/Login.test.js --coverageProvider v8`);
684
- expect(cliCall().command).toMatch(/--debug --coverageProvider v8 e2e\/Login.test.js$/);
685
- });
319
+ expect(cliCall().argv).not.toContain('e2e');
320
+ expect(cliCall().argv.slice(-2)).toEqual(['e2e/01.sanity.test.js', 'e2e/02.sanity.test.js']);
321
+ });
686
322
 
687
- test.each([
688
- [`--testNamePattern "should tap"`, `--testNamePattern ${quote('should tap')}`],
689
- [`"e2e tests/first test.spec.js"`, `"e2e tests/first test.spec.js"`],
690
- ])('should escape %s when forwarding it as a CLI argument', async (cmd, expected) => {
691
- await run(cmd);
692
- expect(cliCall().command).toContain(` ${expected}`);
693
- });
323
+ test.todo('--inspect-brk should work');
324
+
325
+ test.each([
326
+ ['-d e2eFolder', / e2eFolder$/, /\bDETOX_DEBUG_SYNCHRONIZATION=3000/],
327
+ ['--debug-synchronization e2eFolder', / e2eFolder$/, /\bDETOX_DEBUG_SYNCHRONIZATION=3000/],
328
+ ['-r e2eFolder', / e2eFolder$/, /\bDETOX_REUSE=true/],
329
+ ['--reuse e2eFolder', / e2eFolder$/, /\bDETOX_REUSE=true/],
330
+ ['-u e2eFolder', / e2eFolder$/, /\bDETOX_CLEANUP=true/],
331
+ ['--cleanup e2eFolder', / e2eFolder$/, /\bDETOX_CLEANUP=true/],
332
+ ['--jest-report-specs e2eFolder', / e2eFolder$/, /\bDETOX_REPORT_SPECS=true/],
333
+ ['-H e2eFolder', / e2eFolder$/, /\bDETOX_HEADLESS=true/],
334
+ ['--headless e2eFolder', / e2eFolder$/, /\bDETOX_HEADLESS=true/],
335
+ ['--keepLockFile e2eFolder', / e2eFolder$/, /\bDETOX_KEEP_LOCKFILE=true/],
336
+ ['--use-custom-logger e2eFolder', / e2eFolder$/, /\bDETOX_USE_CUSTOM_LOGGER=true/],
337
+ ['--force-adb-install e2eFolder', / e2eFolder$/, /\bDETOX_FORCE_ADB_INSTALL=true/],
338
+ ])('"%s" should be disambigued correctly', async (command, commandMatcher, envMatcher) => {
339
+ singleConfig().device.type = 'android.emulator';
340
+ await run(...command.split(' '));
341
+
342
+ expect(cliCall().argv.join(' ')).toMatch(commandMatcher);
343
+ expect(cliCall().fullCommand).toEqual(expect.objectContaining(envMatcher));
694
344
  });
695
345
 
696
- describe.each([['mocha'], ['jest']])('(%s)', (testRunner) => {
697
- beforeEach(() => {
698
- detoxConfig.testRunner = testRunner;
699
- });
346
+ test('e.g., --debug should be passed through', async () => {
347
+ await run(`--debug`);
348
+ expect(cliCall().argv).toContain('--debug');
349
+ });
700
350
 
701
- test(`should deduce wrapped ${testRunner} CLI`, async () => {
702
- detoxConfig.testRunner = `nyc ${testRunner}`;
703
- await run();
704
- expect(cliCall().command).toMatch(RegExp(`nyc ${testRunner} .* e2e$`));
705
- });
351
+ test('e.g., --coverageProvider v8 should be passed through', async () => {
352
+ await run('--coverageProvider', 'v8');
353
+ expect(cliCall().argv.slice(-2)).toEqual(['--coverageProvider', 'v8']);
354
+ });
706
355
 
707
- describe.each([['ios.simulator'], ['android.emulator']])('for %s', (deviceType) => {
708
- beforeEach(() => {
709
- Object.values(detoxConfig.configurations)[0].type = deviceType;
710
- });
356
+ test('e.g., --debug e2e/Login.test.js should be split to --debug and e2e/Login.test.js', async () => {
357
+ await run('--debug', 'e2e/Login.test.js', '--coverageProvider', 'v8');
711
358
 
712
- test('--keepLockFile should be suppress clearing the device lock file', async () => {
713
- await run('--keepLockFile');
714
- expect(DeviceRegistry).not.toHaveBeenCalled();
715
- });
359
+ expect(cliCall().argv).toEqual([
360
+ expect.stringMatching(/executable$/),
361
+ '--config', 'e2e/config.json',
362
+ '--debug',
363
+ '--coverageProvider', 'v8',
364
+ 'e2e/Login.test.js'
365
+ ]);
366
+ });
716
367
 
717
- test('--keepLockFile omission means clearing the device lock file', async () => {
718
- await run();
719
- expect(DeviceRegistry.mock.instances[0].reset).toHaveBeenCalled();
720
- });
721
- });
368
+ test('should escape whitespaces when forwarding a CLI argument', async () => {
369
+ await run(`e2e tests/first test.spec.js`);
370
+ expect(_.last(cliCall().argv)).toEqual(`e2e tests/first test.spec.js`);
371
+ });
722
372
 
723
- test('-- <...explicitPassthroughArgs> should be forwarded to the test runner CLI as-is', async () => {
724
- await run('--device-boot-args detoxArgs e2eFolder -- a -a --a --device-boot-args runnerArgs');
725
- expect(cliCall().command).toMatch(/a -a --a --device-boot-args runnerArgs .* e2eFolder$/);
726
- expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_DEVICE_BOOT_ARGS: 'detoxArgs' }));
727
- });
373
+ test(`should be able to use custom test runner commands`, async () => {
374
+ detoxConfig.testRunner.args.$0 += ' --hello';
375
+ await run();
376
+ expect(cliCall().argv).toContain('--hello');
377
+ });
728
378
 
729
- test('-- <...explicitPassthroughArgs> should omit double-dash "--" itself, when forwarding args', async () => {
730
- await run('./detox -- --forceExit');
379
+ test('-- <...explicitPassthroughArgs> should be forwarded to the test runner CLI as-is', async () => {
380
+ await run('--device-boot-args', 'detoxArgs', 'e2eFolder', '--', 'a', '-a', '--a', '--device-boot-args', 'runnerArgs');
381
+ expect(cliCall().argv).toEqual([
382
+ expect.stringMatching(/executable$/),
383
+ '--config', 'e2e/config.json',
384
+ 'a',
385
+ '-a',
386
+ '--a',
387
+ '--device-boot-args',
388
+ 'runnerArgs',
389
+ 'e2eFolder',
390
+ ]);
391
+
392
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_DEVICE_BOOT_ARGS="detoxArgs" /);
393
+ });
731
394
 
732
- expect(cliCall().command).toMatch(/ --forceExit .* \.\/detox$/);
733
- expect(cliCall().command).not.toMatch(/ -- --forceExit .* \.\/detox$/);
734
- });
395
+ test('-- <...explicitPassthroughArgs> should omit double-dash "--" only once when forwarding args', async () => {
396
+ await run('--', '--', '--deepParameter');
735
397
 
736
- test('--inspect-brk should prepend "node --inspect-brk" to the command', async () => {
737
- // TODO: fix --inspect-brk behavior on Windows
738
- if (process.platform === 'win32') return;
398
+ expect(cliCall().argv).toContain('--');
399
+ expect(cliCall().argv).toContain('--deepParameter');
400
+ });
739
401
 
740
- await run('--inspect-brk');
741
- const absolutePathToTestRunnerJs = require.resolve(`.bin/${testRunner}`);
742
- expect(cliCall().command).toMatch(RegExp(`^node --inspect-brk ${absolutePathToTestRunnerJs}`));
743
- });
402
+ // TODO: revive this test
403
+ test.skip('--inspect-brk should prepend "node --inspect-brk" to the command', async () => {
404
+ await run('--inspect-brk');
744
405
 
745
- test('should append $DETOX_ARGV_OVERRIDE to detox test ... command and print a warning', async () => {
746
- process.env.PLATFORM = 'ios';
747
- process.env.DETOX_ARGV_OVERRIDE = '--inspect-brk --testNamePattern "[$PLATFORM] tap" e2e/sanity/*.test.js';
748
- await run();
406
+ if (process.platform === 'win32') {
407
+ expect(cliCall().argv).toMatch(/^node --inspect-brk \.\/node_modules\/jest\/bin\/jest\.js/);
408
+ } else {
409
+ expect(cliCall().argv).toMatch(/^node --inspect-brk \.\/node_modules\/\.bin\/jest/);
410
+ }
411
+ });
749
412
 
750
- const pattern = new RegExp(`^node --inspect-brk.* --testNamePattern ${quote('\\[ios\\] tap')}.* e2e/sanity/\\*\\.test.js$`);
413
+ test('should append $DETOX_ARGV_OVERRIDE to detox test ... command and print a warning', async () => {
414
+ process.env.PLATFORM = 'ios';
415
+ process.env.DETOX_ARGV_OVERRIDE = os.platform() === 'win32'
416
+ ? '--testNamePattern="[%PLATFORM%] tap" -l trace e2e/sanity/*.test.js'
417
+ : '--testNamePattern="[$PLATFORM] tap" -l trace e2e/sanity/*.test.js';
751
418
 
752
- expect(cliCall().command).toMatch(pattern);
753
- expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('$DETOX_ARGV_OVERRIDE is detected'));
754
- });
755
- });
419
+ await run();
756
420
 
757
- test('should fail for unrecognized test runner', async () => {
758
- detoxConfig.testRunner = 'ava';
759
- await expect(run('--inspect-brk')).rejects.toThrowError(/ava.*is not supported/);
421
+ expect(cliCall().fullCommand).toMatch(/\bDETOX_LOGLEVEL="trace" /);
422
+ expect(cliCall().argv.slice(-3)).toEqual(['--testNamePattern', '[ios] tap', 'e2e/sanity/*.test.js']);
423
+ expect(logger().warn).toHaveBeenCalledWith(expect.stringContaining('$DETOX_ARGV_OVERRIDE is detected'));
760
424
  });
761
425
 
762
426
  // Helpers
@@ -769,15 +433,15 @@ describe('CLI', () => {
769
433
  fs.writeFileSync(tempFilePath, content);
770
434
  }
771
435
 
772
- temporaryFiles.push(tempFilePath);
436
+ _temporaryFiles.push(tempFilePath);
773
437
  return tempFilePath;
774
438
  }
775
439
 
776
- async function runRaw(command = '') {
777
- let argv;
440
+ async function runRaw(...command) {
441
+ let memArgv;
778
442
 
779
443
  try {
780
- argv = process.argv.splice(2, Infinity, ...command.trim().split(' '));
444
+ memArgv = process.argv.splice(2, Infinity, ...command);
781
445
 
782
446
  return await new Promise((resolve, reject) => {
783
447
  const testCommand = require('./test');
@@ -808,27 +472,36 @@ describe('CLI', () => {
808
472
  parser.parse(command, err => err && reject(err));
809
473
  });
810
474
  } finally {
811
- argv && process.argv.splice(2, Infinity, ...argv);
475
+ memArgv && process.argv.splice(2, Infinity, ...memArgv);
812
476
  }
813
477
  }
814
478
 
815
- async function run(command = '') {
479
+ async function run(...args) {
816
480
  detoxConfigPath = tempfile('.json', JSON.stringify(detoxConfig));
817
481
  const __configPath = Math.random() > 0.5 ? '-C' : '--config-path';
818
- return runRaw(`test ${__configPath} ${detoxConfigPath} ${command}`);
482
+ return runRaw('test', __configPath, detoxConfigPath, ...args);
819
483
  }
820
484
 
821
485
  function cliCall(index = 0) {
822
- const mockCall = cp.execSync.mock.calls[index];
486
+ if (!_cliCallDump) {
487
+ _cliCallDump = fs.readFileSync(process.env.CLI_TEST_STDOUT, 'utf8')
488
+ .split('\n')
489
+ .filter(Boolean)
490
+ .map(line => JSON.parse(line));
491
+ }
492
+
493
+ const mockCall = _cliCallDump[index];
823
494
  if (!mockCall) {
824
495
  return null;
825
496
  }
826
497
 
827
- const [command, opts] = mockCall;
828
-
829
498
  return {
830
- command,
831
- env: _.omitBy(opts.env, (_value, key) => key in process.env),
499
+ ...mockCall,
500
+ fullCommand: _.chain(logger().log.mock.calls)
501
+ .filter(([_level, _childMeta, meta]) => meta && meta.event === 'RUN_START')
502
+ .get(index)
503
+ .get(3)
504
+ .value(),
832
505
  };
833
506
  }
834
507
 
@@ -836,21 +509,7 @@ describe('CLI', () => {
836
509
  return Object.values(detoxConfig.configurations)[0];
837
510
  }
838
511
 
839
- function isInCMD() {
840
- return process.platform === 'win32' && !process.env.SHELL;
841
- }
842
-
843
- function quote(s, q = isInCMD() ? `"` : `'`) {
844
- return q + s + q;
845
- }
846
-
847
- function aCustomDriverModule() {
848
- return `
849
- class RuntimeDriverClass {};
850
- class DeviceAllocationDriverClass {};
851
- class DeviceDeallocationDriverClass {};
852
- class ExpectClass {};
853
- module.exports = { RuntimeDriverClass, DeviceAllocationDriverClass, DeviceDeallocationDriverClass, ExpectClass }
854
- `;
512
+ function mockExitCode(code) {
513
+ process.env.CLI_EXIT_CODE = code;
855
514
  }
856
515
  });