detox 20.0.15-prerelease.0 → 20.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. package/Detox-android/com/wix/detox/{20.0.15-prerelease.0/detox-20.0.15-prerelease.0-javadoc.jar → 20.1.0/detox-20.1.0-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0-javadoc.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.0.15-prerelease.0/detox-20.0.15-prerelease.0-sources.jar → 20.1.0/detox-20.1.0-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0.aar +0 -0
  12. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0.aar.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0.aar.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0.aar.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0.aar.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/{20.0.15-prerelease.0/detox-20.0.15-prerelease.0.pom → 20.1.0/detox-20.1.0.pom} +1 -1
  17. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0.pom.md5 +1 -0
  18. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0.pom.sha1 +1 -0
  19. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.0.pom.sha256 +1 -0
  20. package/Detox-android/com/wix/detox/20.1.0/detox-20.1.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/detox/src/full/java/com/wix/detox/TestEngineFacade.kt +3 -3
  29. package/android/detox/src/full/java/com/wix/detox/adapters/server/QueryStatusActionHandler.kt +12 -80
  30. package/android/detox/src/full/java/com/wix/detox/espresso/common/UiControllerImplReflected.kt +16 -0
  31. package/android/detox/src/full/java/com/wix/detox/espresso/idlingresources/DescriptiveIdlingResource.kt +8 -0
  32. package/android/detox/src/full/java/com/wix/detox/espresso/registry/BusyResourcesInquirer.kt +48 -0
  33. package/android/detox/src/full/java/com/wix/detox/inquiry/DetoxBusyResource.kt +92 -0
  34. package/android/detox/src/full/java/com/wix/detox/{reactnative/idlingresources/IdlingResourceDescription.kt → inquiry/DetoxBusyResourceDescription.kt} +6 -6
  35. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/AnimatedModuleIdlingResource.java +18 -7
  36. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/AsyncStorageIdlingResource.kt +2 -4
  37. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/BridgeIdlingResource.java +12 -6
  38. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/DetoxBaseIdlingResource.java +2 -0
  39. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/NetworkIdlingResource.java +14 -8
  40. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/TimersIdlingResource.kt +1 -6
  41. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/UIManagerModuleReflected.kt +1 -1
  42. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/UIModuleIdlingResource.kt +4 -6
  43. package/android/detox/src/testFull/java/com/wix/detox/adapters/server/QueryStatusActionHandlerSpec.kt +35 -94
  44. package/android/detox/src/testFull/java/com/wix/detox/espresso/registry/{IRStatusInquirerTest.kt → BusyResourcesInquirerTest.kt} +46 -7
  45. package/android/detox/src/testFull/java/com/wix/detox/{reactnative/idlingresources/IdlingResourceDescriptionSpec.kt → inquiry/DetoxBusyResourceDescriptionSpec.kt} +6 -6
  46. package/android/detox/src/testFull/java/com/wix/detox/inquiry/DetoxBusyResourceSpec.kt +134 -0
  47. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/AsyncStorageIdlingResourceSpec.kt +4 -5
  48. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/NetworkIdlingResourcesTest.kt +5 -5
  49. package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/timers/TimersIdlingResourceSpec.kt +4 -6
  50. package/index.d.ts +1 -1
  51. package/internals.d.ts +2 -2
  52. package/local-cli/cli.js +6 -3
  53. package/local-cli/testCommand/TestRunnerCommand.js +25 -12
  54. package/local-cli/testCommand/TestRunnerError.js +17 -0
  55. package/package.json +3 -6
  56. package/runners/deprecation.js +2 -2
  57. package/src/artifacts/utils/temporaryPath.js +32 -2
  58. package/src/client/actions/SyncStatusSchema.json +21 -0
  59. package/src/client/actions/formatters/SyncStatusFormatter.js +2 -0
  60. package/src/client/actions/formatters/sync-resources/BgThreadFormatter.js +5 -0
  61. package/src/configuration/composeRunnerConfig.js +1 -1
  62. package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +3 -3
  63. package/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js +3 -16
  64. package/src/devices/common/drivers/android/exec/ADB.js +4 -0
  65. package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +1 -1
  66. package/src/devices/lifecycle/GenyGlobalLifecycleHandler.js +2 -0
  67. package/src/errors/DetoxError.js +5 -1
  68. package/src/ipc/IPCClient.js +26 -17
  69. package/src/ipc/IPCServer.js +11 -3
  70. package/src/ipc/SessionState.js +4 -6
  71. package/src/logger/DetoxLogger.js +34 -5
  72. package/src/logger/utils/BunyanLogger.js +39 -0
  73. package/src/logger/utils/CategoryThreadDispatcher.js +2 -1
  74. package/src/logger/utils/DetoxLogFinalizer.js +83 -57
  75. package/src/logger/utils/MessageStack.js +17 -6
  76. package/src/logger/utils/customConsoleLogger.js +18 -2
  77. package/src/logger/utils/getMainCategory.js +5 -0
  78. package/src/logger/utils/streams/BunyanTransformer.js +72 -0
  79. package/src/logger/utils/streams/ChromeTraceTransformer.js +132 -0
  80. package/src/logger/utils/streams/DetoxJSONLParser.js +31 -0
  81. package/src/logger/utils/streams/JSONLStringer.js +55 -0
  82. package/src/logger/utils/streams/index.js +7 -0
  83. package/src/logger/utils/streams/transformers.js +39 -0
  84. package/src/realms/DetoxContext.js +2 -1
  85. package/src/realms/DetoxPrimaryContext.js +13 -3
  86. package/src/realms/DetoxSecondaryContext.js +2 -2
  87. package/src/utils/pathUtils.js +11 -0
  88. package/src/utils/shellUtils.js +17 -0
  89. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0-javadoc.jar.md5 +0 -1
  90. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0-javadoc.jar.sha1 +0 -1
  91. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0-javadoc.jar.sha256 +0 -1
  92. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0-javadoc.jar.sha512 +0 -1
  93. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0-sources.jar.md5 +0 -1
  94. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0-sources.jar.sha1 +0 -1
  95. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0-sources.jar.sha256 +0 -1
  96. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0-sources.jar.sha512 +0 -1
  97. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0.aar +0 -0
  98. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0.aar.md5 +0 -1
  99. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0.aar.sha1 +0 -1
  100. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0.aar.sha256 +0 -1
  101. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0.aar.sha512 +0 -1
  102. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0.pom.md5 +0 -1
  103. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0.pom.sha1 +0 -1
  104. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0.pom.sha256 +0 -1
  105. package/Detox-android/com/wix/detox/20.0.15-prerelease.0/detox-20.0.15-prerelease.0.pom.sha512 +0 -1
  106. package/android/detox/src/full/java/com/wix/detox/espresso/registry/IRStatusInquirer.kt +0 -24
  107. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/DescriptiveIdlingResource.kt +0 -10
  108. package/local-cli/build.test.js +0 -104
  109. package/local-cli/run-server.test.js +0 -23
  110. package/local-cli/test.test.js +0 -602
  111. package/runners/jest/index.test.js +0 -13
  112. package/runners/jest/testEnvironment/utils/assertJestCircus27.test.js +0 -22
  113. package/src/logger/utils/streamUtils.js +0 -248
@@ -0,0 +1,17 @@
1
+ const { DetoxError } = require('../../src/errors');
2
+
3
+ class TestRunnerError extends DetoxError {
4
+ constructor({ command, code, signal }) {
5
+ super(`Command failed with exit code = ${code}:\n${command}`);
6
+
7
+ this.code = code;
8
+ this.signal = signal;
9
+ this.name = 'TestRunnerError';
10
+ }
11
+
12
+ format() {
13
+ return '';
14
+ }
15
+ }
16
+
17
+ module.exports = TestRunnerError;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "detox",
3
3
  "description": "E2E tests and automation for mobile",
4
- "version": "20.0.15-prerelease.0",
4
+ "version": "20.1.0",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -61,10 +61,9 @@
61
61
  "caf": "^15.0.1",
62
62
  "chalk": "^2.4.2",
63
63
  "child-process-promise": "^2.2.0",
64
- "duplexify": "^4.1.2",
65
64
  "find-up": "^4.1.0",
66
65
  "fs-extra": "^4.0.2",
67
- "funpermaproxy": "^1.0.1",
66
+ "funpermaproxy": "^1.1.0",
68
67
  "glob": "^8.0.3",
69
68
  "ini": "^1.3.4",
70
69
  "json-cycle": "^1.3.0",
@@ -82,7 +81,6 @@
82
81
  "signal-exit": "^3.0.3",
83
82
  "stream-json": "^1.7.4",
84
83
  "strip-ansi": "^6.0.1",
85
- "tail": "^2.0.0",
86
84
  "telnet-client": "1.2.8",
87
85
  "tempfile": "^2.0.0",
88
86
  "trace-event-lib": "^1.3.1",
@@ -164,7 +162,6 @@
164
162
  "runners/jest/testEnvironment",
165
163
  "src/DetoxWorker.js",
166
164
  "src/logger/utils/streamUtils.js",
167
- "src/ipc",
168
165
  "src/realms"
169
166
  ],
170
167
  "resetMocks": true,
@@ -190,5 +187,5 @@
190
187
  }
191
188
  }
192
189
  },
193
- "gitHead": "5bfd04ef867767c3eb9690c418bf80588d367d3e"
190
+ "gitHead": "57097c7301cae2460bd3b36c1749c4048d84dd39"
194
191
  }
@@ -26,7 +26,7 @@ console.error(centerText(`\
26
26
 
27
27
  ${bold(header)}
28
28
 
29
- https://wix.github.io/Detox/docs/next/guide/migration
29
+ https://wix.github.io/Detox/docs/guide/migration
30
30
 
31
31
  Sorry to say that Detox 20 comes without old adapters for Jest.
32
32
  You have to rearrange your init code before you can continue your journey.
@@ -40,6 +40,6 @@ ${bold(header)}
40
40
 
41
41
  `, header.length));
42
42
 
43
- const error = new Error('\nPlease follow the migration guide:\nhttps://wix.github.io/Detox/docs/next/guide/migration\n\n');
43
+ const error = new Error('\nPlease follow the migration guide:\nhttps://wix.github.io/Detox/docs/guide/migration\n\n');
44
44
  error.stack = '';
45
45
  throw error;
@@ -1,14 +1,42 @@
1
1
  const path = require('path');
2
+ const { promisify } = require('util');
2
3
 
4
+ const glob = require('glob');
3
5
  const tempfile = require('tempfile');
4
6
 
7
+ const { useForwardSlashes } = require('../../utils/shellUtils');
8
+
9
+ const globSync = glob.sync;
10
+ const globAsync = promisify(glob);
11
+
12
+ function getRoot() {
13
+ return path.dirname(tempfile());
14
+ }
15
+
16
+ function createGlobber(ext) {
17
+ const fullExt = `.detox.${ext}`;
18
+
19
+ return {
20
+ sync: (pattern) => {
21
+ const cwd = getRoot();
22
+ const files = globSync(useForwardSlashes(pattern + fullExt), { cwd });
23
+ return files.map(f => path.join(cwd, f));
24
+ },
25
+ async: async (pattern) => {
26
+ const cwd = getRoot();
27
+ const files = await globAsync(useForwardSlashes(pattern + fullExt), { cwd });
28
+ return files.map(f => path.join(cwd, f));
29
+ },
30
+ };
31
+ }
32
+
5
33
  function createTempFileBuilderFn(fileExtension) {
6
34
  /**
7
35
  * @param {string} [basename]
8
36
  */
9
37
  return (basename) => {
10
38
  return basename
11
- ? path.join(path.dirname(tempfile()), `${basename}.detox.${fileExtension}`)
39
+ ? path.join(getRoot(), `${basename}.detox.${fileExtension}`)
12
40
  : tempfile(`.detox.${fileExtension}`);
13
41
  };
14
42
  }
@@ -23,5 +51,7 @@ module.exports = {
23
51
  dtxrec: createTempFileBuilderFn('dtxrec'),
24
52
  viewhierarchy: createTempFileBuilderFn('viewhierarchy'),
25
53
  },
26
- mask: () => path.join(tempfile(), '..') + path.sep + '*.detox.*',
54
+ find: {
55
+ jsonl: createGlobber('jsonl'),
56
+ },
27
57
  };
@@ -232,6 +232,27 @@
232
232
  ],
233
233
  "additionalProperties":false
234
234
  },
235
+ {
236
+ "properties":{
237
+ "name":{
238
+ "const":"bg"
239
+ },
240
+ "description":{
241
+ "type":"object",
242
+ "properties":{
243
+ "reason":{
244
+ "type":"string"
245
+ }
246
+ },
247
+ "additionalProperties":false
248
+ }
249
+ },
250
+ "required":[
251
+ "name",
252
+ "description"
253
+ ],
254
+ "additionalProperties":false
255
+ },
235
256
  {
236
257
  "properties":{
237
258
  "name":{
@@ -4,6 +4,7 @@ const Ajv = require('ajv');
4
4
  const DetoxInternalError = require('../../../errors/DetoxInternalError');
5
5
  const statusSchema = require('../SyncStatusSchema.json');
6
6
 
7
+ const bgThreadFormatter = require('./sync-resources/BgThreadFormatter');
7
8
  const delayedPerformSelectorFormatter = require('./sync-resources/DelayedPerformSelectorFormatter');
8
9
  const dispatchQueueFormatter = require('./sync-resources/DispatchQueueFormatter');
9
10
  const jsTimersFormatter = require('./sync-resources/JavaScriptTimersFormatter');
@@ -51,6 +52,7 @@ function resourcesDescriptionsFromJSON(jsonDescriptions) {
51
52
  }
52
53
 
53
54
  const resourceFormatters = {
55
+ bg: bgThreadFormatter,
54
56
  delayed_perform_selector: delayedPerformSelectorFormatter,
55
57
  dispatch_queue: dispatchQueueFormatter,
56
58
  run_loop: runLoopFormatter,
@@ -0,0 +1,5 @@
1
+ const { makeResourceTitle } = require('./utils');
2
+
3
+ module.exports = function(properties) {
4
+ return `${makeResourceTitle(`Background work taking place in ${properties.reason}.`)}`;
5
+ };
@@ -91,7 +91,7 @@ function adaptLegacyRunnerConfig(globalConfig) {
91
91
  return globalConfig.testRunner;
92
92
  }
93
93
 
94
- log.warn(`Please migrate your Detox config according to the guide:\nhttps://wix.github.io/Detox/docs/next/guide/migration\n`);
94
+ log.warn(`Please migrate your Detox config according to the guide:\nhttps://wix.github.io/Detox/docs/guide/migration\n`);
95
95
  const testRunner = globalConfig[testRunnerKey];
96
96
  const runnerConfig = globalConfig[runnerConfigKey];
97
97
  const specs = globalConfig.specs != null ? String(globalConfig.specs) : undefined;
@@ -36,7 +36,7 @@ class EmulatorLauncher extends DeviceLauncher {
36
36
  retries: 2,
37
37
  interval: 100,
38
38
  conditionFn: isUnknownEmulatorError,
39
- }, () => this._launchEmulator(avdName, launchCommand));
39
+ }, () => this._launchEmulator(avdName, launchCommand, adbName));
40
40
  }
41
41
  await this._awaitEmulatorBoot(adbName);
42
42
  await this._notifyBootEvent(adbName, avdName, !isRunning);
@@ -60,8 +60,8 @@ class EmulatorLauncher extends DeviceLauncher {
60
60
  await this._notifyShutdownCompleted(adbName);
61
61
  }
62
62
 
63
- _launchEmulator(emulatorName, launchCommand) {
64
- return launchEmulatorProcess(emulatorName, this._emulatorExec, launchCommand);
63
+ _launchEmulator(emulatorName, launchCommand, adbName) {
64
+ return launchEmulatorProcess(emulatorName, this._emulatorExec, launchCommand, this._adb, adbName);
65
65
  }
66
66
 
67
67
  async _awaitEmulatorBoot(adbName) {
@@ -1,29 +1,15 @@
1
1
  const fs = require('fs');
2
2
 
3
3
  const _ = require('lodash');
4
- const { Tail } = require('tail');
5
4
 
6
5
  const unitLogger = require('../../../../../utils/logger').child({ cat: 'device' });
7
6
 
8
- function launchEmulatorProcess(emulatorName, emulatorExec, emulatorLaunchCommand) {
7
+ function launchEmulatorProcess(emulatorName, emulatorExec, emulatorLaunchCommand, adb, adbName) {
9
8
  let childProcessOutput;
10
-
11
9
  const portName = emulatorLaunchCommand.port ? `-${emulatorLaunchCommand.port}` : '';
12
10
  const tempLog = `./${emulatorName}${portName}.log`;
13
11
  const stdout = fs.openSync(tempLog, 'a');
14
12
  const stderr = fs.openSync(tempLog, 'a');
15
- const tailOptions = {
16
- useWatchFile: true,
17
- fsWatchOptions: {
18
- interval: 1500,
19
- },
20
- };
21
- const tail = new Tail(tempLog, tailOptions)
22
- .on('line', (line) => {
23
- if (line.includes('Adb connected, start proxing data')) {
24
- childProcessPromise._cpResolve();
25
- }
26
- });
27
13
 
28
14
  function detach() {
29
15
  if (childProcessOutput) {
@@ -32,7 +18,6 @@ function launchEmulatorProcess(emulatorName, emulatorExec, emulatorLaunchCommand
32
18
 
33
19
  childProcessOutput = fs.readFileSync(tempLog, 'utf8');
34
20
 
35
- tail.unwatch();
36
21
  fs.closeSync(stdout);
37
22
  fs.closeSync(stderr);
38
23
  fs.unlink(tempLog, _.noop);
@@ -46,6 +31,8 @@ function launchEmulatorProcess(emulatorName, emulatorExec, emulatorLaunchCommand
46
31
 
47
32
  log = log.child({ child_pid: childProcessPromise.childProcess.pid });
48
33
 
34
+ adb.waitForDevice(adbName).then(() => childProcessPromise._cpResolve());
35
+
49
36
  return childProcessPromise.then(() => true).catch((err) => {
50
37
  detach();
51
38
 
@@ -189,6 +189,10 @@ class ADB {
189
189
  }
190
190
  }
191
191
 
192
+ async waitForDevice(deviceId) {
193
+ return await this.adbCmd(deviceId, 'wait-for-device');
194
+ }
195
+
192
196
  async apiLevel(deviceId) {
193
197
  if (this._cachedApiLevels.has(deviceId)) {
194
198
  return this._cachedApiLevels.get(deviceId);
@@ -93,7 +93,7 @@ class AppleSimUtils {
93
93
  `(https://developer.apple.com/xcode/). In case you already have the latest Xcode version installed, ` +
94
94
  `try run the command: \`sudo xcode-select -s /Applications/Xcode.app\`. If you are running tests from CI, ` +
95
95
  `we recommend running them with "--headless" device configuration (see: ` +
96
- `https://wix.github.io/Detox/docs/next/cli/test/#options).`
96
+ `https://wix.github.io/Detox/docs/cli/test/#options).`
97
97
  );
98
98
  }
99
99
 
@@ -6,7 +6,9 @@ const cleanupLogData = {
6
6
 
7
7
  class GenyGlobalLifecycleHandler {
8
8
  constructor({ deviceCleanupRegistry, instanceLifecycleService }) {
9
+ /** @private */
9
10
  this._deviceCleanupRegistry = deviceCleanupRegistry;
11
+ /** @private */
10
12
  this._instanceLifecycleService = instanceLifecycleService;
11
13
  }
12
14
 
@@ -8,6 +8,10 @@ class DetoxError extends Error {
8
8
  this.name = 'DetoxError';
9
9
  }
10
10
 
11
+ format() {
12
+ return this.message;
13
+ }
14
+
11
15
  static get reportIssue() {
12
16
  return 'Please report this issue on our GitHub tracker:\nhttps://github.com/wix/Detox/issues';
13
17
  }
@@ -32,7 +36,7 @@ class DetoxError extends Error {
32
36
  */
33
37
  static format(err, inspectOptions = { depth: 1 }) {
34
38
  if (err instanceof DetoxError) {
35
- return err.message;
39
+ return err.format();
36
40
  }
37
41
 
38
42
  if (_.isError(err) && /^Command failed:/.test(err.message)) {
@@ -4,21 +4,21 @@ const { DetoxInternalError } = require('../errors');
4
4
  const { serializeObjectWithError } = require('../utils/errorUtils');
5
5
 
6
6
  class IPCClient {
7
- constructor({ id, logger, state }) {
7
+ constructor({ id, logger, sessionState }) {
8
8
  this._id = id;
9
9
  /** @type {import('../logger/DetoxLogger')} logger */
10
10
  this._logger = logger.child({ cat: 'ipc' });
11
11
  /** @type {import('./SessionState')} */
12
- this._state = state;
12
+ this._sessionState = sessionState;
13
13
 
14
- this._client = null;
14
+ this._ipc = null;
15
15
  this._serverConnection = null;
16
16
  }
17
17
 
18
18
  async init() {
19
- this._client = new IPC();
19
+ this._ipc = new IPC();
20
20
 
21
- Object.assign(this._client.config, {
21
+ Object.assign(this._ipc.config, {
22
22
  id: this._id,
23
23
  appspace: 'detox.',
24
24
  logger: (msg) => this._logger.trace(msg),
@@ -33,14 +33,22 @@ class IPCClient {
33
33
  async dispose() {
34
34
  this._serverConnection = null;
35
35
 
36
- if (this._client) {
37
- this._client.disconnect(this.serverId);
38
- this._client = null;
36
+ if (this._ipc) {
37
+ await new Promise((resolve, reject) => {
38
+ this._ipc.of[this.serverId]
39
+ // @ts-ignore
40
+ .once('disconnect', resolve)
41
+ .once('error', reject);
42
+
43
+ this._ipc.disconnect(this.serverId);
44
+ });
45
+
46
+ this._ipc = null;
39
47
  }
40
48
  }
41
49
 
42
50
  get sessionState() {
43
- return this._state;
51
+ return this._sessionState;
44
52
  }
45
53
 
46
54
  get serverId() {
@@ -49,7 +57,7 @@ class IPCClient {
49
57
 
50
58
  async registerWorker(workerId) {
51
59
  const sessionState = await this._emit('registerWorker', { workerId });
52
- this._state.patch(sessionState);
60
+ this._sessionState.patch(sessionState);
53
61
  }
54
62
 
55
63
  /**
@@ -59,18 +67,18 @@ class IPCClient {
59
67
  const sessionState = await this._emit('reportTestResults', {
60
68
  testResults: testResults.map(r => serializeObjectWithError(r, 'testExecError')),
61
69
  });
62
- this._state.patch(sessionState);
70
+ this._sessionState.patch(sessionState);
63
71
  }
64
72
 
65
73
  async _connectToServer() {
66
74
  const serverId = this.serverId;
67
75
 
68
76
  this._serverConnection = await new Promise((resolve, reject) => {
69
- this._client.connectTo(serverId, (client) => {
77
+ this._ipc.connectTo(serverId, (client) => {
70
78
  client.of[serverId]
71
- .on('error', reject)
72
- .on('disconnect', () => reject(new DetoxInternalError('IPC server has unexpectedly disconnected.')))
73
- .on('connect', () => resolve(client.of[serverId]));
79
+ .once('error', reject)
80
+ .once('disconnect', () => reject(new DetoxInternalError('IPC server has unexpectedly disconnected.')))
81
+ .once('connect', () => resolve(client.of[serverId]));
74
82
  });
75
83
  });
76
84
 
@@ -79,7 +87,7 @@ class IPCClient {
79
87
 
80
88
  async _registerContext() {
81
89
  const sessionState = await this._emit('registerContext', { id: this._id });
82
- this._state.patch(sessionState);
90
+ this._sessionState.patch(sessionState);
83
91
  }
84
92
 
85
93
  async _emit(event, payload) {
@@ -90,6 +98,7 @@ class IPCClient {
90
98
  return new Promise((resolve, reject) => {
91
99
  const server = this._serverConnection;
92
100
 
101
+ /* istanbul ignore next */
93
102
  function onError(err) {
94
103
  server.off('error', onError);
95
104
  server.off(`${event}Done`, onDone);
@@ -110,7 +119,7 @@ class IPCClient {
110
119
  }
111
120
 
112
121
  _onSessionStateUpdate = (payload) => {
113
- this._state.patch(payload);
122
+ this._sessionState.patch(payload);
114
123
  };
115
124
  }
116
125
 
@@ -14,12 +14,17 @@ class IPCServer {
14
14
  this._logger = logger.child({ cat: 'ipc,ipc-server' });
15
15
  this._ipc = null;
16
16
  this._workers = new Set();
17
+ this._contexts = new Set();
17
18
  }
18
19
 
19
20
  get id() {
20
21
  return this._sessionState.detoxIPCServer;
21
22
  }
22
23
 
24
+ get contexts() {
25
+ return [...this._contexts];
26
+ }
27
+
23
28
  get sessionState() {
24
29
  return this._sessionState;
25
30
  }
@@ -45,15 +50,18 @@ class IPCServer {
45
50
  return;
46
51
  }
47
52
 
48
- return new Promise((resolve, reject) =>{
53
+ await new Promise((resolve, reject) =>{
49
54
  // @ts-ignore
50
- this._ipc.server.server.close(e => e ? reject(e) : resolve());
55
+ this._ipc.server.server.close(e => /* istanbul ignore next */
56
+ e ? reject(e) : resolve());
51
57
  this._ipc.server.stop();
52
58
  });
59
+
60
+ this._ipc = null;
53
61
  }
54
62
 
55
63
  onRegisterContext({ id }, socket) {
56
- this._sessionState.contexts.push(id);
64
+ this._contexts.add(id);
57
65
 
58
66
  this._ipc.server.emit(socket, 'registerContextDone', {
59
67
  testResults: this._sessionState.testResults,
@@ -9,7 +9,6 @@ const context = vm.createContext({ require }, {
9
9
  class SessionState {
10
10
  constructor({
11
11
  id = '',
12
- contexts = [],
13
12
  detoxConfig = null,
14
13
  detoxIPCServer = '',
15
14
  testResults = [],
@@ -17,7 +16,6 @@ class SessionState {
17
16
  workersCount = 0
18
17
  }) {
19
18
  this.id = id;
20
- this.contexts = contexts;
21
19
  this.detoxConfig = detoxConfig;
22
20
  this.detoxIPCServer = detoxIPCServer;
23
21
  this.testResults = testResults;
@@ -30,7 +28,7 @@ class SessionState {
30
28
  }
31
29
 
32
30
  stringify() {
33
- return cycle.stringify(this, SessionState.stringifier);
31
+ return cycle.stringify(this, SessionState._stringifier);
34
32
  }
35
33
 
36
34
  /**
@@ -39,10 +37,10 @@ class SessionState {
39
37
  static parse(stringified) {
40
38
  const Class = this; // eslint-disable-line unicorn/no-this-assignment
41
39
  // @ts-ignore
42
- return new Class(cycle.parse(stringified, SessionState.reviver));
40
+ return new Class(cycle.parse(stringified, SessionState._reviver));
43
41
  }
44
42
 
45
- static reviver(key, val) {
43
+ static _reviver(key, val) {
46
44
  if (typeof val === 'object' && val !== null && typeof val.$fn == 'string') {
47
45
  return vm.runInContext(val.$fn, context);
48
46
  } else {
@@ -50,7 +48,7 @@ class SessionState {
50
48
  }
51
49
  }
52
50
 
53
- static stringifier(key, val) {
51
+ static _stringifier(key, val) {
54
52
  if (typeof val === 'function') {
55
53
  return { $fn: `(${val})` };
56
54
  } else {
@@ -19,11 +19,12 @@ const sanitizeBunyanContext = require('./utils/sanitizeBunyanContext');
19
19
  * @property {CategoryThreadDispatcher} [dispatcher]
20
20
  * @property {BunyanLogger} [bunyan]
21
21
  * @property {MessageStack} [messageStack]
22
+ * @property {boolean} [unsafeMode] Disables sanitization of user input, used for integration tests.
22
23
  */
23
24
 
24
25
  class DetoxLogger {
25
26
  /**
26
- * @param {Pick<SharedLoggerConfig, 'file' | 'userConfig'>} sharedConfig
27
+ * @param {SharedLoggerConfig} sharedConfig
27
28
  * @param {object} [context]
28
29
  */
29
30
  constructor(sharedConfig, context) {
@@ -91,7 +92,7 @@ class DetoxLogger {
91
92
  * @returns {DetoxLogger}
92
93
  */
93
94
  child(overrides) {
94
- const merged = this._mergeContexts(this._context, sanitizeBunyanContext(overrides));
95
+ const merged = this._mergeContexts(this._context, this._sanitizeContext(overrides));
95
96
  return new DetoxLogger(this._sharedConfig, merged);
96
97
  }
97
98
 
@@ -119,6 +120,25 @@ class DetoxLogger {
119
120
  this.overrideConsole();
120
121
  }
121
122
 
123
+ /**
124
+ * Closes the file descriptors to make sure that the temporary
125
+ * JSONL files are flushed and contain the last error messages.
126
+ * This safety measure is especially important for Windows OS.
127
+ *
128
+ * @async
129
+ * @internal
130
+ */
131
+ async close() {
132
+ if (this._context) {
133
+ throw new DetoxInternalError(
134
+ 'Trying to close file streams from a non-root logger.\n' +
135
+ 'If you are not fiddling with Detox internals on purpose, yet you see this error, then...'
136
+ );
137
+ }
138
+
139
+ await this._sharedConfig.bunyan.closeFileStreams();
140
+ }
141
+
122
142
  /**
123
143
  * @internal
124
144
  */
@@ -192,7 +212,7 @@ class DetoxLogger {
192
212
 
193
213
  /** @private */
194
214
  _beginInternal(level, context, msg) {
195
- this._sharedConfig.messageStack.push(context.tid, msg);
215
+ this._sharedConfig.messageStack.push(context, msg);
196
216
  this._sharedConfig.bunyan.logger[level](context, ...msg);
197
217
  }
198
218
 
@@ -204,7 +224,7 @@ class DetoxLogger {
204
224
 
205
225
  /** @private */
206
226
  _endInternal(level, context, msg) {
207
- const beginMsg = this._sharedConfig.messageStack.pop(context.tid);
227
+ const beginMsg = this._sharedConfig.messageStack.pop(context);
208
228
  if (msg.length === 0) {
209
229
  msg = beginMsg;
210
230
  }
@@ -272,12 +292,21 @@ class DetoxLogger {
272
292
  const context = this._mergeContexts(
273
293
  this._context,
274
294
  boundContext,
275
- sanitizeBunyanContext(userContext),
295
+ this._sanitizeContext(userContext),
276
296
  );
277
297
 
278
298
  return { context, msg };
279
299
  }
280
300
 
301
+ /** @private */
302
+ _sanitizeContext(context) {
303
+ if (this._sharedConfig.unsafeMode) {
304
+ return context;
305
+ }
306
+
307
+ return sanitizeBunyanContext(context);
308
+ }
309
+
281
310
  /** @internal */
282
311
  static defaultOptions({ level }) {
283
312
  const ph = level === 'trace' || level === 'debug'
@@ -71,6 +71,45 @@ class BunyanLogger {
71
71
 
72
72
  return this;
73
73
  }
74
+
75
+ async closeFileStreams() {
76
+ const { _closeStream } = BunyanLogger;
77
+ const internalBunyanStreams = this._bunyan['streams'];
78
+ const openFileStreams = _.filter(internalBunyanStreams, this._isOpenFileStream);
79
+ _.remove(internalBunyanStreams, openFileStreams);
80
+
81
+ await Promise.all(openFileStreams.map(_closeStream));
82
+ }
83
+
84
+ /** @private */
85
+ _isOpenFileStream = (bunyanStream) => {
86
+ switch (bunyanStream.type) {
87
+ case 'file':
88
+ return bunyanStream.path && !bunyanStream.stream.destroyed;
89
+ case 'raw':
90
+ /* istanbul ignore next */
91
+ const stream = bunyanStream.stream === this._debugStream.stream
92
+ ? bunyanStream.stream._out
93
+ : bunyanStream.stream;
94
+
95
+ /* istanbul ignore next */
96
+ return stream.fd !== undefined && !stream.closed;
97
+ }
98
+ };
99
+
100
+ /** @private */
101
+ static _closeStream(bunyanStream) {
102
+ return new Promise((resolve, reject) => {
103
+ bunyanStream.stream.end((err) => {
104
+ /* istanbul ignore next */
105
+ if (err) {
106
+ reject(err);
107
+ } else {
108
+ resolve();
109
+ }
110
+ });
111
+ });
112
+ }
74
113
  }
75
114
 
76
115
  module.exports = BunyanLogger;
@@ -1,4 +1,5 @@
1
1
  const ThreadDispatcher = require('./ThreadDispatcher');
2
+ const getMainCategory = require('./getMainCategory');
2
3
 
3
4
  class CategoryThreadDispatcher {
4
5
  constructor() {
@@ -24,7 +25,7 @@ class CategoryThreadDispatcher {
24
25
 
25
26
  /** @returns {ThreadDispatcher} */
26
27
  _resolveDispatcher(cat) {
27
- const mainCategory = cat ? cat.split(',', 1)[0] : 'undefined';
28
+ const mainCategory = getMainCategory(cat);
28
29
  if (!this._dispatchers[mainCategory]) {
29
30
  this._dispatchers[mainCategory] = new ThreadDispatcher(mainCategory);
30
31
  }