detox 20.0.2-breaking.new-global-lifecycle.0 → 20.0.5-breaking.new-global-lifecycle.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. package/Detox-android/com/wix/detox/{20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0-javadoc.jar → 20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-javadoc.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0-sources.jar → 20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/{20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0.aar → 20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.aar} +0 -0
  12. package/Detox-android/com/wix/detox/{20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0.aar.md5 → 20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.aar.md5} +0 -0
  13. package/Detox-android/com/wix/detox/{20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0.aar.sha1 → 20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.aar.sha1} +0 -0
  14. package/Detox-android/com/wix/detox/{20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0.aar.sha256 → 20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.aar.sha256} +0 -0
  15. package/Detox-android/com/wix/detox/{20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0.aar.sha512 → 20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.aar.sha512} +0 -0
  16. package/Detox-android/com/wix/detox/{20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0.pom → 20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.pom} +1 -1
  17. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.pom.md5 +1 -0
  18. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.pom.sha1 +1 -0
  19. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.pom.sha256 +1 -0
  20. package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-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/index.d.ts +49 -40
  29. package/internals.d.ts +75 -22
  30. package/local-cli/build.js +1 -1
  31. package/local-cli/build.test.js +14 -14
  32. package/local-cli/cli.js +1 -1
  33. package/local-cli/rebuild-framework-cache.js +1 -1
  34. package/local-cli/test.js +6 -5
  35. package/local-cli/test.test.js +2 -2
  36. package/local-cli/testCommand/TestRunnerCommand.js +4 -3
  37. package/package.json +5 -4
  38. package/runners/jest/globalSetup.js +1 -1
  39. package/runners/jest/globalTeardown.js +1 -1
  40. package/runners/jest/testEnvironment/index.js +34 -17
  41. package/runners/jest/testEnvironment/listeners/DetoxCoreListener.js +78 -43
  42. package/runners/jest/testEnvironment/listeners/SpecReporter.js +2 -2
  43. package/runners/jest/testEnvironment/listeners/WorkerAssignReporter.js +2 -2
  44. package/src/DetoxWorker.js +11 -2
  45. package/src/artifacts/ArtifactsManager.js +8 -23
  46. package/src/artifacts/instruments/ios/SimulatorInstrumentsRecording.js +3 -3
  47. package/src/artifacts/log/ios/SimulatorLogRecording.js +1 -1
  48. package/src/artifacts/screenshot/SimulatorScreenshotPlugin.js +1 -1
  49. package/src/artifacts/templates/artifact/Artifact.js +1 -1
  50. package/src/artifacts/templates/plugin/ArtifactPlugin.js +1 -1
  51. package/src/artifacts/utils/temporaryPath.js +18 -7
  52. package/src/artifacts/video/SimulatorRecordVideoPlugin.js +1 -1
  53. package/src/client/AsyncWebSocket.js +8 -17
  54. package/src/client/Client.js +1 -1
  55. package/src/configuration/collectCliConfig.js +1 -1
  56. package/src/configuration/composeDeviceConfig.js +1 -1
  57. package/src/configuration/composeLoggerConfig.js +17 -8
  58. package/src/configuration/composeRunnerConfig.js +49 -2
  59. package/src/configuration/index.js +14 -9
  60. package/src/configuration/loadExternalConfig.js +1 -1
  61. package/src/devices/allocation/DeviceAllocator.js +3 -2
  62. package/src/devices/allocation/drivers/android/emulator/AVDValidator.js +1 -1
  63. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +3 -2
  64. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocationHelper.js +1 -1
  65. package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +3 -2
  66. package/src/devices/allocation/drivers/android/emulator/EmulatorVersionResolver.js +4 -6
  67. package/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js +1 -1
  68. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceAllocationHelper.js +1 -1
  69. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceNaming.js +3 -3
  70. package/src/devices/common/drivers/android/genycloud/services/GenyRecipesService.js +1 -1
  71. package/src/devices/common/drivers/android/tools/EmulatorTelnet.js +1 -1
  72. package/src/devices/common/drivers/android/tools/FreeDeviceFinder.js +1 -1
  73. package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +1 -1
  74. package/src/devices/lifecycle/GenyGlobalLifecycleHandler.js +9 -13
  75. package/src/devices/runtime/RuntimeDevice.js +7 -8
  76. package/src/devices/runtime/drivers/DeviceDriverBase.js +1 -1
  77. package/src/devices/runtime/drivers/android/AndroidDriver.js +1 -1
  78. package/src/devices/runtime/drivers/ios/SimulatorDriver.js +1 -1
  79. package/src/errors/DetoxConfigErrorComposer.js +4 -2
  80. package/src/ipc/IPCClient.js +3 -8
  81. package/src/ipc/IPCServer.js +11 -11
  82. package/src/ipc/{state.js → SessionState.js} +23 -50
  83. package/src/logger/DetoxLogger.js +264 -155
  84. package/src/logger/index.js +4 -0
  85. package/src/logger/utils/BunyanLogger.js +72 -0
  86. package/src/logger/utils/CategoryThreadDispatcher.js +58 -0
  87. package/src/logger/utils/MessageStack.js +24 -0
  88. package/src/logger/{TraceThreadDispatcher.js → utils/ThreadDispatcher.js} +34 -5
  89. package/src/logger/{customConsoleLogger.js → utils/customConsoleLogger.js} +4 -4
  90. package/src/logger/utils/sanitizeBunyanContext.js +28 -0
  91. package/src/logger/utils/tracerLegacy.js +25 -0
  92. package/src/realms/DetoxContext.js +65 -57
  93. package/src/realms/DetoxInternalsFacade.js +7 -5
  94. package/src/realms/DetoxPrimaryContext.js +118 -41
  95. package/src/realms/DetoxSecondaryContext.js +31 -30
  96. package/src/server/DetoxConnection.js +18 -23
  97. package/src/server/DetoxServer.js +7 -10
  98. package/src/server/DetoxSession.js +6 -6
  99. package/src/server/DetoxSessionManager.js +1 -1
  100. package/src/server/handlers/RegisteredConnectionHandler.js +1 -2
  101. package/src/symbols.js +12 -8
  102. package/src/utils/childProcess/exec.js +1 -1
  103. package/src/utils/childProcess/spawn.js +1 -1
  104. package/src/utils/streamUtils.js +10 -11
  105. package/src/utils/trace.js +2 -18
  106. package/src/utils/traceMethods.js +15 -0
  107. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0-javadoc.jar.md5 +0 -1
  108. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0-javadoc.jar.sha1 +0 -1
  109. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0-javadoc.jar.sha256 +0 -1
  110. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0-javadoc.jar.sha512 +0 -1
  111. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0-sources.jar.md5 +0 -1
  112. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0-sources.jar.sha1 +0 -1
  113. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0-sources.jar.sha256 +0 -1
  114. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0-sources.jar.sha512 +0 -1
  115. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0.pom.md5 +0 -1
  116. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0.pom.sha1 +0 -1
  117. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0.pom.sha256 +0 -1
  118. package/Detox-android/com/wix/detox/20.0.2-breaking.new-global-lifecycle.0/detox-20.0.2-breaking.new-global-lifecycle.0.pom.sha512 +0 -1
  119. package/src/logger/DetoxTraceEventBuilder.js +0 -21
  120. package/src/logger/DetoxTracer.js +0 -133
  121. package/src/utils/ChromeTracingExporter.js +0 -54
@@ -1,6 +1,6 @@
1
1
  const isUndefined = (x) => x === undefined;
2
2
 
3
- class TraceThreadDispatcher {
3
+ class ThreadDispatcher {
4
4
  /**
5
5
  * @param {object} options
6
6
  * @param {Detox.Logger} options.logger
@@ -17,14 +17,31 @@ class TraceThreadDispatcher {
17
17
  this.threads = [];
18
18
  }
19
19
 
20
- begin(id = 0) {
20
+ /**
21
+ * @param {string | number} [id]
22
+ * @returns {number}
23
+ */
24
+ begin(id) {
21
25
  const tid = this._findTID(id);
22
26
  this.threads[tid] = id;
23
27
  this.stacks[tid] = (this.stacks[tid] || 0) + 1;
24
28
  return this._transpose(tid);
25
29
  }
26
30
 
27
- end(id = 0) {
31
+ /**
32
+ * @param {string | number} [id]
33
+ * @returns {number}
34
+ */
35
+ resolve(id) {
36
+ const tid = this._findTID(id);
37
+ return this._transpose(tid);
38
+ }
39
+
40
+ /**
41
+ * @param {string | number} [id]
42
+ * @returns {number}
43
+ */
44
+ end(id) {
28
45
  const tid = this._findTID(id);
29
46
  if (this.stacks[tid] && --this.stacks[tid] === 0) {
30
47
  delete this.threads[tid];
@@ -32,9 +49,17 @@ class TraceThreadDispatcher {
32
49
  return this._transpose(tid);
33
50
  }
34
51
 
52
+ /**
53
+ * @param {string | number | undefined} id
54
+ * @returns {number}
55
+ * @private
56
+ *
57
+ * Memory-efficient finder of a free item index in the threads array.
58
+ */
35
59
  _findTID(id) {
36
60
  let tid = this.threads.indexOf(id);
37
61
  if (tid === -1) {
62
+ // Try to find a recently released slot in the array:
38
63
  tid = this.threads.findIndex(isUndefined);
39
64
  }
40
65
  return tid === -1 ? this.threads.length : tid;
@@ -43,10 +68,14 @@ class TraceThreadDispatcher {
43
68
  _transpose(id) {
44
69
  const result = this.min + id;
45
70
  if (result > this.max) {
46
- this.logger.warn({ event: 'THREAD_DISPATCHER' }, `${this.name} trace thread dispatcher has run out of available thread IDs: ${this.min}..${this.max}`);
71
+ this.logger.warn(
72
+ { cat: ['logger', 'thread-dispatcher'] },
73
+ `${this.name} trace thread dispatcher has run out of available thread IDs: ${this.min}..${this.max}`
74
+ );
47
75
  }
76
+
48
77
  return Math.min(result, this.max);
49
78
  }
50
79
  }
51
80
 
52
- module.exports = TraceThreadDispatcher;
81
+ module.exports = ThreadDispatcher;
@@ -1,6 +1,6 @@
1
1
  const util = require('util');
2
2
 
3
- const callsites = require('../utils/callsites');
3
+ const callsites = require('../../utils/callsites');
4
4
 
5
5
  const USER_STACK_FRAME_INDEX = 2;
6
6
 
@@ -25,20 +25,20 @@ function getStackDump() {
25
25
 
26
26
  function proxyLog(bunyanLoggerFn) {
27
27
  return (...args) => {
28
- bunyanLoggerFn({ event: 'USER_LOG' }, getOrigin(), '\n', util.format(...args));
28
+ bunyanLoggerFn({ origin: getOrigin() }, util.format(...args));
29
29
  };
30
30
  }
31
31
 
32
32
  function proxyTracing(bunyanLoggerFn) {
33
33
  return (...args) => {
34
- bunyanLoggerFn({ event: 'USER_LOG' }, getOrigin(), '\n Trace:', util.format(...args), '\n\r' + getStackDump());
34
+ bunyanLoggerFn({ origin: getOrigin(), stack: getStackDump() }, util.format(...args));
35
35
  };
36
36
  }
37
37
 
38
38
  function proxyAssert(bunyanLoggerFn) {
39
39
  return (condition, ...args) => {
40
40
  if (!condition) {
41
- bunyanLoggerFn({ event: 'USER_LOG' }, getOrigin(), '\n AssertionError:', util.format(...args));
41
+ bunyanLoggerFn({ origin: getOrigin() }, 'AssertionError:', util.format(...args));
42
42
  }
43
43
  };
44
44
  }
@@ -0,0 +1,28 @@
1
+ const _ = require('lodash');
2
+
3
+ const RESERVED_PROPERTIES = [
4
+ 'hostname',
5
+ 'level',
6
+ 'msg',
7
+ 'name',
8
+ 'pid',
9
+ 'time',
10
+ ];
11
+
12
+ function hasProperty(p) {
13
+ return _.has(this, p);
14
+ }
15
+
16
+ function hasReservedProperties(o) {
17
+ return RESERVED_PROPERTIES.some(hasProperty, o); // eslint-disable-line unicorn/no-array-method-this-argument
18
+ }
19
+
20
+ function escapeCallback(value, key) {
21
+ return RESERVED_PROPERTIES.includes(key) ? `${key}$` : key;
22
+ }
23
+
24
+ function sanitizeBunyanContext(context) {
25
+ return hasReservedProperties(context) ? _.mapKeys(context, escapeCallback) : context;
26
+ }
27
+
28
+ module.exports = sanitizeBunyanContext;
@@ -0,0 +1,25 @@
1
+ const methods = {
2
+ startSection(logger) {
3
+ return (msg, args) => logger.trace.begin(args, msg);
4
+ },
5
+
6
+ endSection(logger) {
7
+ return (msg, args) => logger.trace.end(args);
8
+ },
9
+
10
+ traceCall(logger) {
11
+ return (name, action) => logger.trace.complete(name, action);
12
+ },
13
+ };
14
+
15
+ function installLegacyTracerInterface(logger, target) {
16
+ target.traceCall = methods.traceCall(logger);
17
+ target.trace = Object.freeze({
18
+ startSection: methods.startSection(logger),
19
+ endSection: methods.endSection(logger),
20
+ });
21
+
22
+ return this;
23
+ }
24
+
25
+ module.exports = { install: installLegacyTracerInterface };
@@ -1,48 +1,63 @@
1
1
  const funpermaproxy = require('funpermaproxy');
2
2
 
3
+ const temporary = require('../artifacts/utils/temporaryPath');
3
4
  const { DetoxRuntimeError } = require('../errors');
4
- const DetoxLogger = require('../logger/DetoxLogger');
5
- const DetoxTracer = require('../logger/DetoxTracer');
5
+ const { DetoxLogger, installLegacyTracerInterface } = require('../logger');
6
6
  const symbols = require('../symbols');
7
7
 
8
8
  const DetoxConstants = require('./DetoxConstants');
9
9
 
10
10
  const $cleanup = Symbol('cleanup');
11
- const $logger = Symbol('logger');
12
11
  const $restoreSessionState = Symbol('restoreSessionState');
13
12
  const $sessionState = Symbol('restoreSessionState');
14
- const $tracer = Symbol('tracer');
15
-
16
- const _worker = Symbol('worker');
13
+ const $status = Symbol('status');
14
+ const $worker = Symbol('worker');
17
15
 
18
16
  class DetoxContext {
19
17
  constructor() {
20
- this[symbols.globalSetup] = this[symbols.globalSetup].bind(this);
21
- this[symbols.globalTeardown] = this[symbols.globalTeardown].bind(this);
18
+ /** @type {DetoxInternals.DetoxStatus} */
19
+ this[$status] = 'inactive';
20
+
21
+ const _init = this[symbols.init].bind(this);
22
+ this[symbols.init] = async (opts) => {
23
+ this[$status] = 'init';
24
+ await _init(opts);
25
+ this[$status] = 'active';
26
+ };
27
+
28
+ const _cleanup = this[symbols.cleanup].bind(this);
29
+ this[symbols.cleanup] = async () => {
30
+ this[$status] = 'cleanup';
31
+ try {
32
+ await _cleanup();
33
+ } finally {
34
+ this[$status] = 'inactive';
35
+ }
36
+ };
37
+
38
+ this[symbols.getStatus] = this[symbols.getStatus].bind(this);
22
39
  this[symbols.reportFailedTests] = this[symbols.reportFailedTests].bind(this);
23
40
  this[symbols.resolveConfig] = this[symbols.resolveConfig].bind(this);
24
- this[symbols.setup] = this[symbols.setup].bind(this);
25
- this[symbols.teardown] = this[symbols.teardown].bind(this);
41
+ this[symbols.installWorker] = this[symbols.installWorker].bind(this);
42
+ this[symbols.uninstallWorker] = this[symbols.uninstallWorker].bind(this);
26
43
 
27
44
  this[$sessionState] = this[$restoreSessionState]();
28
45
 
29
- const loggerConfig = this[$sessionState].detoxConfig
30
- ? this[$sessionState].detoxConfig.loggerConfig
31
- : undefined;
32
-
33
46
  /**
34
- * @protected
35
47
  * @type {DetoxLogger & Detox.Logger}
36
48
  */
37
- this[$logger] = new DetoxLogger(loggerConfig);
38
- /** @protected */
39
- this[$tracer] = DetoxTracer.default({
40
- logger: this[$logger],
49
+ this[symbols.logger] = new DetoxLogger({
50
+ file: temporary.for.jsonl(`${this[$sessionState].id}.${process.pid}`),
51
+ userConfig: this[$sessionState].detoxConfig
52
+ ? this[$sessionState].detoxConfig.logger
53
+ : null,
41
54
  });
42
- /** @deprecated */
43
- this.traceCall = this[$tracer].bind(this[$tracer]);
55
+
56
+ this.log = this[symbols.logger].child({ cat: 'user' });
57
+ installLegacyTracerInterface(this.log, this);
58
+
44
59
  /** @type {import('../DetoxWorker') | null} */
45
- this[_worker] = null;
60
+ this[$worker] = null;
46
61
  }
47
62
 
48
63
  //#region Public members
@@ -62,19 +77,6 @@ class DetoxContext {
62
77
  return DetoxConstants;
63
78
  }
64
79
 
65
- /**
66
- * @returns {Detox.Logger}
67
- */
68
- get log() {
69
- return this[$logger];
70
- }
71
-
72
- /**
73
- * @returns {Detox.Tracer}
74
- */
75
- get trace() {
76
- return this[$tracer];
77
- }
78
80
  //#endregion
79
81
 
80
82
  //#region Internal members
@@ -96,57 +98,63 @@ class DetoxContext {
96
98
  [symbols.reportFailedTests](_testFilePaths, _permanent) {}
97
99
  /**
98
100
  * @abstract
99
- * @param {Partial<DetoxInternals.DetoxGlobalSetupOptions>} _opts
101
+ * @param {Partial<DetoxInternals.DetoxInitOptions>} _opts
100
102
  * @returns {Promise<DetoxInternals.RuntimeConfig>}
101
103
  */
102
104
  async [symbols.resolveConfig](_opts) { return null; }
103
105
 
106
+ [symbols.getStatus]() {
107
+ return this[$status];
108
+ }
109
+
104
110
  get [symbols.worker]() {
105
- if (!this[_worker]) {
111
+ if (!this[$worker]) {
106
112
  throw new DetoxRuntimeError({
107
- message: `Detox worker instance has not been initialized in this context (${this.constructor.name}).`,
108
- hint: DetoxRuntimeError.reportIssueIfJest + '\n' + 'Otherwise, make sure you call detox.setup() beforehand.',
113
+ message: `Detox worker instance has not been installed in this context (${this.constructor.name}).`,
114
+ hint: DetoxRuntimeError.reportIssueIfJest + '\n' + 'Otherwise, make sure you call detox.installWorker() beforehand.',
109
115
  });
110
116
  }
111
117
 
112
- return this[_worker];
118
+ return this[$worker];
113
119
  }
114
120
 
115
121
  /**
116
122
  * @abstract
117
- * @param {Partial<DetoxInternals.DetoxGlobalSetupOptions>} [_opts]
123
+ * @param {Partial<DetoxInternals.DetoxInitOptions>} [_opts]
118
124
  * @returns {Promise<void>}
119
125
  */
120
- async [symbols.globalSetup](_opts = {}) {}
126
+ async [symbols.init](_opts = {}) {}
121
127
 
122
128
  /**
123
- * @param {Partial<DetoxInternals.DetoxConfigurationSetupOptions>} [opts]
129
+ * @param {Partial<DetoxInternals.DetoxInstallWorkerOptions>} [opts]
124
130
  */
125
- async [symbols.setup](opts) {
126
- const theGlobal = opts.global || global;
127
- theGlobal['__detox__'] = this;
128
- this[$logger].overrideConsole(theGlobal);
131
+ async [symbols.installWorker](opts) {
132
+ if (opts.global) {
133
+ opts.global['__detox__'] = this;
134
+ this.log.overrideConsole(opts.global);
135
+ }
129
136
 
130
137
  const DetoxWorker = require('../DetoxWorker');
131
- DetoxWorker.global = theGlobal;
132
- this[_worker] = new DetoxWorker(this);
133
- await this[_worker].init();
138
+ DetoxWorker.global = opts.global || global;
139
+ this[$worker] = new DetoxWorker(this);
140
+ this[$worker].id = opts.workerId;
141
+ await this[$worker].init();
134
142
  }
135
143
 
136
- async [symbols.teardown]() {
144
+ async [symbols.uninstallWorker]() {
137
145
  try {
138
- if (this[_worker]) {
139
- await this[_worker].cleanup();
146
+ if (this[$worker]) {
147
+ await this[$worker].cleanup();
140
148
  }
141
149
  } finally {
142
- this[_worker] = null;
150
+ this[$worker] = null;
143
151
  }
144
152
  }
145
153
 
146
154
  /**
147
155
  * @abstract
148
156
  */
149
- async [symbols.globalTeardown]() {}
157
+ async [symbols.cleanup]() {}
150
158
  //#endregion
151
159
 
152
160
  //#region Protected members
@@ -163,8 +171,8 @@ class DetoxContext {
163
171
  module.exports = DetoxContext;
164
172
  module.exports.protected = {
165
173
  $cleanup,
166
- $logger,
167
174
  $restoreSessionState,
175
+ $status,
168
176
  $sessionState,
169
- $tracer,
177
+ $worker,
170
178
  };
@@ -8,11 +8,12 @@ class DetoxInternalsFacade {
8
8
  */
9
9
  constructor(context) {
10
10
  this.config = context[symbols.config];
11
- this.globalSetup = context[symbols.globalSetup];
12
- this.globalTeardown = context[symbols.globalTeardown];
13
- this.log = context.log;
14
- this.setup = context[symbols.setup];
15
- this.teardown = context[symbols.teardown];
11
+ this.getStatus = context[symbols.getStatus];
12
+ this.init = context[symbols.init];
13
+ this.cleanup = context[symbols.cleanup];
14
+ this.log = context[symbols.logger];
15
+ this.installWorker = context[symbols.installWorker];
16
+ this.uninstallWorker = context[symbols.uninstallWorker];
16
17
  this.onHookFailure = context[symbols.onHookFailure];
17
18
  this.onHookStart = context[symbols.onHookStart];
18
19
  this.onHookSuccess = context[symbols.onHookSuccess];
@@ -28,6 +29,7 @@ class DetoxInternalsFacade {
28
29
  this.reportFailedTests = context[symbols.reportFailedTests];
29
30
  this.resolveConfig = context[symbols.resolveConfig];
30
31
  this.session = context[symbols.session];
32
+ this.trace = context.trace;
31
33
  this.worker = funpermaproxy(() => context[symbols.worker]);
32
34
  }
33
35
  }
@@ -1,24 +1,34 @@
1
1
  const path = require('path');
2
2
  const { URL } = require('url');
3
+ const { promisify } = require('util');
3
4
 
4
5
  const fs = require('fs-extra');
6
+ const glob = require('glob');
5
7
  const pipe = require('multipipe');
8
+ const onSignalExit = require('signal-exit');
6
9
 
7
10
  const temporary = require('../artifacts/utils/temporaryPath');
8
11
  const { DetoxRuntimeError } = require('../errors');
9
- const { PrimarySessionState } = require('../ipc/state');
12
+ const SessionState = require('../ipc/SessionState');
10
13
  const symbols = require('../symbols');
11
14
 
15
+ const globAsync = promisify(glob);
16
+ const globSync = glob.sync;
17
+
12
18
  const DetoxContext = require('./DetoxContext');
13
19
 
14
- const { $logger, $restoreSessionState, $sessionState } = DetoxContext.protected;
20
+ const { $restoreSessionState, $sessionState, $worker } = DetoxContext.protected;
15
21
 
16
22
  const _finalizeLogs = Symbol('finalizeLogs');
23
+ const _finalizeLogsSync = Symbol('finalizeLogsSync');
17
24
  const _globalLifecycleHandler = Symbol('globalLifecycleHandler');
18
25
  const _ipcServer = Symbol('ipcServer');
19
26
  const _resetLockFile = Symbol('resetLockFile');
20
27
  const _wss = Symbol('wss');
21
28
  const _dirty = Symbol('dirty');
29
+ const _emergencyTeardown = Symbol('emergencyTeardown');
30
+ const _areLogsEnabled = Symbol('areLogsEnabled');
31
+ const _lifecycleLogger = Symbol('lifecycleLogger');
22
32
 
23
33
  class DetoxPrimaryContext extends DetoxContext {
24
34
  constructor() {
@@ -31,6 +41,8 @@ class DetoxPrimaryContext extends DetoxContext {
31
41
  * @type {import('../ipc/IPCServer') | null}
32
42
  */
33
43
  this[_ipcServer] = null;
44
+ /** @type {Detox.Logger} */
45
+ this[_lifecycleLogger] = this[symbols.logger].child({ cat: 'lifecycle' });
34
46
  }
35
47
 
36
48
  //#region Internal members
@@ -52,9 +64,9 @@ class DetoxPrimaryContext extends DetoxContext {
52
64
 
53
65
  /**
54
66
  * @override
55
- * @param {Partial<DetoxInternals.DetoxGlobalSetupOptions>} [opts]
67
+ * @param {Partial<DetoxInternals.DetoxInitOptions>} [opts]
56
68
  */
57
- async [symbols.globalSetup](opts) {
69
+ async [symbols.init](opts = {}) {
58
70
  if (this[_dirty]) {
59
71
  throw new DetoxRuntimeError({
60
72
  message: 'Cannot initialize primary Detox context more than once.',
@@ -63,21 +75,26 @@ class DetoxPrimaryContext extends DetoxContext {
63
75
  }
64
76
 
65
77
  this[_dirty] = true;
78
+ onSignalExit(this[_emergencyTeardown]);
79
+
66
80
  const detoxConfig = await this[symbols.resolveConfig](opts);
67
81
 
68
- const { behaviorConfig, deviceConfig, loggerConfig, sessionConfig } = detoxConfig;
69
- await this[$logger].setConfig(loggerConfig);
82
+ const {
83
+ behavior: behaviorConfig,
84
+ device: deviceConfig,
85
+ logger: loggerConfig,
86
+ session: sessionConfig
87
+ } = detoxConfig;
88
+ await this[symbols.logger].setConfig(loggerConfig);
70
89
 
71
- this.trace.begin({
72
- cat: 'lifecycle',
73
- args: this[$sessionState],
74
- name: process.argv.slice(1).join(' '),
75
- });
90
+ this[_lifecycleLogger].trace.begin({
91
+ data: this[$sessionState],
92
+ }, process.argv.slice(1).join(' '));
76
93
 
77
94
  const IPCServer = require('../ipc/IPCServer');
78
95
  this[_ipcServer] = new IPCServer({
79
96
  sessionState: this[$sessionState],
80
- logger: this[$logger],
97
+ logger: this[symbols.logger],
81
98
  });
82
99
 
83
100
  await this[_ipcServer].init();
@@ -106,63 +123,98 @@ class DetoxPrimaryContext extends DetoxContext {
106
123
  }
107
124
 
108
125
  if (!sessionConfig.server && this[_wss]) {
126
+ // @ts-ignore
109
127
  sessionConfig.server = `ws://localhost:${this[_wss].port}`;
110
128
  }
111
129
 
112
130
  await fs.writeFile(this[$sessionState].detoxConfigSnapshotPath, this[$sessionState].stringify());
113
131
  process.env.DETOX_CONFIG_SNAPSHOT_PATH = this[$sessionState].detoxConfigSnapshotPath;
114
132
 
115
- // TODO: think about signal-exit and cleaning up the logs
133
+ if (opts.workerId !== null) {
134
+ await this[symbols.installWorker](opts);
135
+ }
116
136
  }
117
137
 
118
138
  /**
119
139
  * @override
120
- * @param {Partial<DetoxInternals.DetoxConfigurationSetupOptions>} [opts]
140
+ * @param {Partial<DetoxInternals.DetoxInstallWorkerOptions>} [opts]
121
141
  */
122
- async [symbols.setup](opts) {
123
- const workerId = opts.workerId || 1;
124
- this[$sessionState].workerId = workerId;
142
+ async [symbols.installWorker](opts = {}) {
143
+ const workerId = this[$sessionState].workerId = opts.workerId = opts.workerId || 'worker';
125
144
  this[_ipcServer].onRegisterWorker({ workerId });
126
- await super[symbols.setup](opts);
145
+
146
+ await super[symbols.installWorker](opts);
127
147
  }
128
148
 
129
149
  /** @override */
130
- async [symbols.globalTeardown]() {
150
+ async [symbols.cleanup]() {
151
+ try {
152
+ if (this[$worker]) {
153
+ await this[symbols.uninstallWorker]();
154
+ }
155
+ } finally {
156
+ if (this[_globalLifecycleHandler]) {
157
+ await this[_globalLifecycleHandler].globalCleanup();
158
+ this[_globalLifecycleHandler] = null;
159
+ }
160
+
161
+ if (this[_wss]) {
162
+ await this[_wss].close();
163
+ this[_wss] = null;
164
+ }
165
+
166
+ if (this[_ipcServer]) {
167
+ await this[_ipcServer].dispose();
168
+ this[_ipcServer] = null;
169
+ }
170
+
171
+ await fs.remove(this[$sessionState].detoxConfigSnapshotPath);
172
+
173
+ try {
174
+ this[_lifecycleLogger].trace.end();
175
+ await this[_finalizeLogs]();
176
+ } catch (err) {
177
+ this[_lifecycleLogger].error({ err }, 'Encountered an error while merging the process logs:');
178
+ }
179
+ }
180
+ }
181
+
182
+ [_emergencyTeardown] = (_code, signal) => {
183
+ if (!signal) {
184
+ return;
185
+ }
186
+
131
187
  if (this[_globalLifecycleHandler]) {
132
- await this[_globalLifecycleHandler].globalCleanup();
188
+ this[_globalLifecycleHandler].emergencyCleanup();
133
189
  this[_globalLifecycleHandler] = null;
134
190
  }
135
191
 
136
192
  if (this[_wss]) {
137
- await this[_wss].close();
138
- this[_wss] = null;
193
+ this[_wss].close();
139
194
  }
140
195
 
141
196
  if (this[_ipcServer]) {
142
- await this[_ipcServer].dispose();
143
- this[_ipcServer] = null;
197
+ this[_ipcServer].dispose();
144
198
  }
145
199
 
146
- await fs.remove(this[$sessionState].detoxConfigSnapshotPath);
147
- delete process.env.DETOX_CONFIG_SNAPSHOT_PATH;
148
-
149
200
  try {
150
- this.trace.end({ cat: 'lifecycle' });
151
- await this[_finalizeLogs]();
201
+ this[_lifecycleLogger].trace.end({ abortSignal: signal });
202
+ this[_finalizeLogsSync]();
152
203
  } catch (err) {
153
- this[$logger].error({ err }, 'Encountered an error while merging the process logs:');
204
+ this[symbols.logger].error({ err }, 'Encountered an error while merging the process logs:');
154
205
  }
155
- }
206
+ };
207
+
156
208
  //#endregion
157
209
 
158
210
  //#region Protected members
159
211
  /**
160
212
  * @protected
161
213
  * @override
162
- * @return {PrimarySessionState}
214
+ * @return {SessionState}
163
215
  */
164
216
  [$restoreSessionState]() {
165
- return new PrimarySessionState({
217
+ return new SessionState({
166
218
  detoxConfigSnapshotPath: temporary.for.json(),
167
219
  detoxIPCServer: `primary-${process.pid}`,
168
220
  });
@@ -171,17 +223,16 @@ class DetoxPrimaryContext extends DetoxContext {
171
223
 
172
224
  //#region Private members
173
225
  async[_finalizeLogs]() {
174
- const logs = [this[$logger].file, ...this[$sessionState].logFiles].filter(f => f && fs.existsSync(f));
226
+ const sessionId = this[$sessionState].id;
227
+ const logs = await globAsync(temporary.for.jsonl(`${sessionId}.*`));
175
228
  if (logs.length === 0) {
176
229
  return;
177
230
  }
178
231
 
179
- const streamUtils = require('../utils/streamUtils');
180
- const { rootDir, plugins } = this[symbols.config].artifactsConfig || {};
181
- const logConfig = plugins && plugins.log || 'none';
182
- const enabled = rootDir && (typeof logConfig === 'string' ? logConfig !== 'none' : logConfig.enabled);
232
+ if (this[_areLogsEnabled]) {
233
+ const streamUtils = require('../utils/streamUtils');
234
+ const { rootDir } = this[symbols.config].artifacts;
183
235
 
184
- if (enabled) {
185
236
  await fs.mkdirp(rootDir);
186
237
  const [out1Stream, out2Stream, out3Stream] = ['detox.log.jsonl', 'detox.log', 'detox.trace.json']
187
238
  .map((filename) => fs.createWriteStream(path.join(rootDir, filename)));
@@ -193,7 +244,7 @@ class DetoxPrimaryContext extends DetoxContext {
193
244
 
194
245
  await Promise.all([
195
246
  pipe(mergedStream, streamUtils.writeJSONL(), out1Stream),
196
- pipe(mergedStream, streamUtils.debugStream(this[$logger].config.options), out2Stream),
247
+ pipe(mergedStream, streamUtils.debugStream(this[symbols.logger].config.options), out2Stream),
197
248
  pipe(mergedStream, streamUtils.chromeTraceStream(), streamUtils.writeJSON(), out3Stream),
198
249
  ]);
199
250
  }
@@ -201,10 +252,36 @@ class DetoxPrimaryContext extends DetoxContext {
201
252
  await Promise.all(logs.map(filepath => fs.remove(filepath)));
202
253
  }
203
254
 
255
+ async[_finalizeLogsSync]() {
256
+ const logsEnabled = this[_areLogsEnabled]();
257
+
258
+ const { rootDir } = this[symbols.config].artifacts;
259
+ fs.mkdirpSync(rootDir);
260
+
261
+ const sessionId = this[$sessionState].id;
262
+ const logs = globSync(temporary.for.jsonl(`${sessionId}.*`));
263
+
264
+ for (const log of logs) {
265
+ if (logsEnabled) {
266
+ fs.moveSync(log, path.join(rootDir, path.basename(log)));
267
+ } else {
268
+ fs.removeSync(log);
269
+ }
270
+ }
271
+ }
272
+
273
+ [_areLogsEnabled]() {
274
+ const { rootDir, plugins } = this[symbols.config].artifacts || {};
275
+ const logConfig = plugins && plugins.log || 'none';
276
+ const enabled = rootDir && (typeof logConfig === 'string' ? logConfig !== 'none' : logConfig.enabled);
277
+
278
+ return enabled;
279
+ }
280
+
204
281
  async[_resetLockFile]() {
205
282
  const DeviceRegistry = require('../devices/DeviceRegistry');
206
283
 
207
- const deviceType = this[symbols.config].deviceConfig.type;
284
+ const deviceType = this[symbols.config].device.type;
208
285
 
209
286
  switch (deviceType) {
210
287
  case 'ios.none':