detox 20.0.14-is-hittable-over-master.0 → 20.0.14-prerelease.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. package/Detox-android/com/wix/detox/{20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0-javadoc.jar → 20.0.14-prerelease.0/detox-20.0.14-prerelease.0-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.0-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.0-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.0-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.0-javadoc.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0-sources.jar → 20.0.14-prerelease.0/detox-20.0.14-prerelease.0-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.0-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.0-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.0-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.0-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/{20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0.aar → 20.0.14-prerelease.0/detox-20.0.14-prerelease.0.aar} +0 -0
  12. package/Detox-android/com/wix/detox/{20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0.aar.md5 → 20.0.14-prerelease.0/detox-20.0.14-prerelease.0.aar.md5} +0 -0
  13. package/Detox-android/com/wix/detox/{20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0.aar.sha1 → 20.0.14-prerelease.0/detox-20.0.14-prerelease.0.aar.sha1} +0 -0
  14. package/Detox-android/com/wix/detox/{20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0.aar.sha256 → 20.0.14-prerelease.0/detox-20.0.14-prerelease.0.aar.sha256} +0 -0
  15. package/Detox-android/com/wix/detox/{20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0.aar.sha512 → 20.0.14-prerelease.0/detox-20.0.14-prerelease.0.aar.sha512} +0 -0
  16. package/Detox-android/com/wix/detox/{20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0.pom → 20.0.14-prerelease.0/detox-20.0.14-prerelease.0.pom} +1 -1
  17. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.0.pom.md5 +1 -0
  18. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.0.pom.sha1 +1 -0
  19. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.0.pom.sha256 +1 -0
  20. package/Detox-android/com/wix/detox/20.0.14-prerelease.0/detox-20.0.14-prerelease.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 +0 -1
  29. package/android/detox/proguard-rules-app.pro +4 -2
  30. package/index.d.ts +105 -19
  31. package/internals.d.ts +103 -22
  32. package/local-cli/cli.js +1 -1
  33. package/local-cli/init.js +1 -1
  34. package/local-cli/test.js +6 -6
  35. package/local-cli/test.test.js +41 -41
  36. package/local-cli/testCommand/TestRunnerCommand.js +75 -78
  37. package/local-cli/testCommand/builder.js +0 -1
  38. package/local-cli/testCommand/middlewares.js +1 -11
  39. package/local-cli/testCommand/warnings.js +0 -3
  40. package/local-cli/utils/jestInternals.js +4 -1
  41. package/package.json +27 -13
  42. package/runners/deprecation.js +42 -44
  43. package/runners/jest/index.d.ts +60 -0
  44. package/runners/jest/index.js +3 -8
  45. package/runners/jest/reporters/DetoxReporter.js +21 -10
  46. package/runners/jest/testEnvironment/index.js +57 -41
  47. package/runners/jest/testEnvironment/listeners/DetoxCoreListener.js +7 -3
  48. package/runners/jest/testEnvironment/listeners/SpecReporter.js +12 -14
  49. package/runners/jest/testEnvironment/listeners/WorkerAssignReporter.js +1 -5
  50. package/runners/jest/testEnvironment/utils/assertJestCircus27.js +17 -3
  51. package/runners/jest/testEnvironment/utils/assertJestCircus27.test.js +0 -1
  52. package/src/DetoxWorker.js +95 -55
  53. package/src/artifacts/ArtifactsManager.js +6 -24
  54. package/src/configuration/collectCliConfig.js +1 -12
  55. package/src/configuration/composeLoggerConfig.js +2 -2
  56. package/src/configuration/composeRunnerConfig.js +8 -6
  57. package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +1 -1
  58. package/src/errors/DetoxConfigErrorComposer.js +6 -1
  59. package/src/ipc/SessionState.js +8 -6
  60. package/src/logger/DetoxLogger.js +5 -25
  61. package/src/logger/index.js +1 -0
  62. package/src/logger/utils/CategoryThreadDispatcher.js +7 -29
  63. package/src/logger/utils/DetoxLogFinalizer.js +134 -0
  64. package/src/logger/utils/ThreadDispatcher.js +5 -25
  65. package/src/logger/utils/customConsoleLogger.js +4 -3
  66. package/src/logger/utils/streamUtils.js +34 -26
  67. package/src/realms/DetoxContext.js +10 -13
  68. package/src/realms/DetoxInternalsFacade.js +0 -6
  69. package/src/realms/DetoxPrimaryContext.js +29 -84
  70. package/src/realms/DetoxSecondaryContext.js +6 -10
  71. package/src/symbols.js +0 -12
  72. package/src/utils/argparse.js +11 -0
  73. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0-javadoc.jar.md5 +0 -1
  74. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0-javadoc.jar.sha1 +0 -1
  75. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0-javadoc.jar.sha256 +0 -1
  76. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0-javadoc.jar.sha512 +0 -1
  77. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0-sources.jar.md5 +0 -1
  78. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0-sources.jar.sha1 +0 -1
  79. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0-sources.jar.sha256 +0 -1
  80. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0-sources.jar.sha512 +0 -1
  81. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0.pom.md5 +0 -1
  82. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0.pom.sha1 +0 -1
  83. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0.pom.sha256 +0 -1
  84. package/Detox-android/com/wix/detox/20.0.14-is-hittable-over-master.0/detox-20.0.14-is-hittable-over-master.0.pom.sha512 +0 -1
  85. package/runners/jest/deprecation.js +0 -25
  86. package/src/configuration/utils/warnings.js +0 -12
@@ -0,0 +1,134 @@
1
+ const path = require('path');
2
+ const { promisify } = require('util');
3
+
4
+ const fs = require('fs-extra');
5
+ const glob = require('glob');
6
+ const pipe = require('multipipe');
7
+
8
+ const temporary = require('../../artifacts/utils/temporaryPath');
9
+
10
+ const globAsync = promisify(glob);
11
+ const globSync = glob.sync;
12
+ const streamUtils = () => require('../../logger/utils/streamUtils');
13
+
14
+ /**
15
+ * @typedef DetoxLogFinalizerConfig
16
+ * @property {import('../../ipc/SessionState')} session
17
+ */
18
+
19
+ class DetoxLogFinalizer {
20
+ /** @param {DetoxLogFinalizerConfig} config */
21
+ constructor(config) {
22
+ this._session = config.session;
23
+ }
24
+
25
+ createEventStream() {
26
+ const sessionId = this._session.id;
27
+ const logs = globSync(temporary.for.jsonl(`${sessionId}.*`));
28
+
29
+ return streamUtils()
30
+ .uniteSessionLogs(logs)
31
+ .pipe(streamUtils().chromeTraceStream());
32
+ }
33
+
34
+ async finalize() {
35
+ const sessionId = this._session.id;
36
+ const logs = await globAsync(temporary.for.jsonl(`${sessionId}.*`));
37
+ if (logs.length === 0) {
38
+ return;
39
+ }
40
+
41
+ if (this._areLogsEnabled()) {
42
+ const rootDir = this._config.artifacts.rootDir;
43
+
44
+ await fs.mkdirp(rootDir);
45
+ const [out1Stream, out2Stream] = ['detox.log', 'detox.trace.json']
46
+ .map((filename) => fs.createWriteStream(path.join(rootDir, filename)));
47
+
48
+ const tidHashMap = await this._scanForThreadIds(logs);
49
+ const mergedStream = streamUtils().uniteSessionLogs(logs);
50
+
51
+ await Promise.all([
52
+ pipe(mergedStream, streamUtils().debugStream(this._config.logger.options), out1Stream),
53
+ pipe(mergedStream, streamUtils().chromeTraceStream(tidHashMap), streamUtils().writeJSON(), out2Stream),
54
+ ]);
55
+ }
56
+
57
+ await Promise.all(logs.map(filepath => fs.remove(filepath)));
58
+ }
59
+
60
+ finalizeSync() {
61
+ const sessionId = this._session.id;
62
+ const rootDir = this._config.artifacts.rootDir;
63
+ const logsEnabled = this._areLogsEnabled();
64
+
65
+ if (logsEnabled) {
66
+ fs.mkdirpSync(rootDir);
67
+ }
68
+
69
+ const logs = globSync(temporary.for.jsonl(`${sessionId}.*`));
70
+
71
+ for (const log of logs) {
72
+ if (logsEnabled) {
73
+ fs.moveSync(log, path.join(rootDir, path.basename(log)));
74
+ } else {
75
+ fs.removeSync(log);
76
+ }
77
+ }
78
+ }
79
+
80
+ /** @private */
81
+ get _config() {
82
+ // The config appears later in the lifecycle, so we need to access it lazily
83
+ return this._session.detoxConfig;
84
+ }
85
+
86
+ /** @private */
87
+ _areLogsEnabled() {
88
+ const { rootDir, plugins } = this._config.artifacts;
89
+ if (!rootDir || !plugins) {
90
+ return false;
91
+ }
92
+
93
+ if (!plugins.log.enabled) {
94
+ return false;
95
+ }
96
+
97
+ if (!plugins.log.keepOnlyFailedTestsArtifacts) {
98
+ return true;
99
+ }
100
+
101
+ return this._session.testResults.some(r => !r.success);
102
+ }
103
+
104
+ /** @private */
105
+ async _scanForThreadIds(logs) {
106
+ const processes = await new Promise((resolve, reject) => {
107
+ const result = {};
108
+ streamUtils().uniteSessionLogs(logs)
109
+ .on('end', () => resolve(result))
110
+ .on('error', (err) => reject(err))
111
+ .on('data', (event) => {
112
+ const { ph, pid, tid, cat } = event;
113
+ if (ph === 'B' || ph === 'i') {
114
+ const categories = (result[pid] = result[pid] || {});
115
+ const mainCategory = String(cat).split(',')[0];
116
+ const tids = (categories[mainCategory] = categories[mainCategory] || []);
117
+ if (!tids.includes(tid)) {
118
+ tids.push(tid);
119
+ }
120
+ }
121
+ });
122
+ });
123
+
124
+ const tidArray = Object.entries(processes).flatMap(([pid, categories]) => {
125
+ return Object.entries(categories).flatMap(([category, tids]) => {
126
+ return tids.map(tid => `${pid}:${category}:${tid}`);
127
+ });
128
+ });
129
+
130
+ return new Map(tidArray.map((hash, index) => [hash, index]));
131
+ }
132
+ }
133
+
134
+ module.exports = DetoxLogFinalizer;
@@ -2,17 +2,10 @@ const isUndefined = (x) => x === undefined;
2
2
 
3
3
  class ThreadDispatcher {
4
4
  /**
5
- * @param {object} options
6
- * @param {Detox.Logger} options.logger
7
- * @param {string} options.name
8
- * @param {number} options.min
9
- * @param {number} [options.max]
5
+ * @param {string} name
10
6
  */
11
- constructor({ logger, name, min, max = Infinity }) {
12
- this.logger = logger;
7
+ constructor(name) {
13
8
  this.name = name;
14
- this.min = min;
15
- this.max = max;
16
9
  this.stacks = [];
17
10
  this.threads = [];
18
11
  }
@@ -25,7 +18,7 @@ class ThreadDispatcher {
25
18
  const tid = this._findTID(id);
26
19
  this.threads[tid] = id;
27
20
  this.stacks[tid] = (this.stacks[tid] || 0) + 1;
28
- return this._transpose(tid);
21
+ return tid;
29
22
  }
30
23
 
31
24
  /**
@@ -33,8 +26,7 @@ class ThreadDispatcher {
33
26
  * @returns {number}
34
27
  */
35
28
  resolve(id) {
36
- const tid = this._findTID(id);
37
- return this._transpose(tid);
29
+ return this._findTID(id);
38
30
  }
39
31
 
40
32
  /**
@@ -46,7 +38,7 @@ class ThreadDispatcher {
46
38
  if (this.stacks[tid] && --this.stacks[tid] === 0) {
47
39
  delete this.threads[tid];
48
40
  }
49
- return this._transpose(tid);
41
+ return tid;
50
42
  }
51
43
 
52
44
  /**
@@ -64,18 +56,6 @@ class ThreadDispatcher {
64
56
  }
65
57
  return tid === -1 ? this.threads.length : tid;
66
58
  }
67
-
68
- _transpose(id) {
69
- const result = this.min + id;
70
- if (result > 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
- );
75
- }
76
-
77
- return Math.min(result, this.max);
78
- }
79
59
  }
80
60
 
81
61
  module.exports = ThreadDispatcher;
@@ -25,20 +25,20 @@ function getStackDump() {
25
25
 
26
26
  function proxyLog(bunyanLoggerFn) {
27
27
  return (...args) => {
28
- bunyanLoggerFn({ origin: getOrigin() }, util.format(...args));
28
+ bunyanLoggerFn({ cat: 'user', origin: getOrigin() }, util.format(...args));
29
29
  };
30
30
  }
31
31
 
32
32
  function proxyTracing(bunyanLoggerFn) {
33
33
  return (...args) => {
34
- bunyanLoggerFn({ origin: getOrigin(), stack: getStackDump() }, util.format(...args));
34
+ bunyanLoggerFn({ cat: 'user', 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({ origin: getOrigin() }, 'AssertionError:', util.format(...args));
41
+ bunyanLoggerFn({ cat: 'user', origin: getOrigin() }, 'AssertionError:', util.format(...args));
42
42
  }
43
43
  };
44
44
  }
@@ -48,6 +48,7 @@ function overrideConsoleMethods(console, bunyanLogger) {
48
48
  const log = bunyanLogger;
49
49
 
50
50
  override(console, 'log', log.info.bind(log));
51
+ override(console, 'info', log.info.bind(log));
51
52
  override(console, 'warn', log.warn.bind(log));
52
53
  override(console, 'trace', log.info.bind(log));
53
54
  override(console, 'error', log.error.bind(log));
@@ -3,14 +3,12 @@ const { PassThrough, Transform } = require('stream');
3
3
  const bunyanDebugStream = require('bunyan-debug-stream');
4
4
  const duplexify = require('duplexify');
5
5
  const fs = require('fs-extra');
6
- const glob = require('glob');
7
6
  const multiSort = require('multi-sort-stream');
8
7
  const pipe = require('multipipe');
9
8
  const JsonlParser = require('stream-json/jsonl/Parser');
10
9
  const stripAnsi = require('strip-ansi');
11
10
  const { AbstractEventBuilder } = require('trace-event-lib');
12
11
 
13
- const temporary = require('../../artifacts/utils/temporaryPath');
14
12
  const log = require('../../utils/logger').child({ cat: 'logger' });
15
13
 
16
14
  function compareTimestamps(a, b) {
@@ -143,27 +141,49 @@ class SimpleEventBuilder extends AbstractEventBuilder {
143
141
  }
144
142
  }
145
143
 
146
- function chromeTraceStream() {
144
+ const ERROR_TID = 37707;
145
+
146
+ function getTidHash({ pid, tid, cat }) {
147
+ const mainCategory = String(cat).split(',')[0];
148
+ return `${pid}:${mainCategory}:${tid}`;
149
+ }
150
+
151
+ function getFixedTID(tidHashMap, event) {
152
+ let tid = tidHashMap.get(getTidHash(event));
153
+ if (tid === undefined) {
154
+ tid = ERROR_TID;
155
+ }
156
+
157
+ return tid;
158
+ }
159
+
160
+ /**
161
+ * @param {Map<string, number>} [tidHashMap]
162
+ */
163
+ function chromeTraceStream(tidHashMap) {
147
164
  const knownPids = new Set();
148
165
  const knownTids = new Set();
166
+ const fixTID = tidHashMap ? getFixedTID.bind(null, tidHashMap) : (x) => x.tid;
149
167
 
150
168
  return flatMapTransform((data) => {
151
169
  // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
152
- const { cat = 'default', msg: name, ph = 'i', pid, tid, time, name: _name, hostname: _hostname, ...args } = data;
170
+ const { cat: rawCat, msg: name, ph = 'i', pid, tid: _tid, time, name: _name, hostname: _hostname, ...args } = data;
153
171
  const ts = new Date(time).getTime() * 1E3;
172
+ const cat = rawCat || 'undefined';
173
+ const tid = fixTID(data);
154
174
 
155
175
  const builder = new SimpleEventBuilder();
156
176
  if (!knownPids.has(pid)) {
157
- builder.process_name(pid === process.pid ? 'primary' : 'secondary', pid);
177
+ builder.metadata({ pid, ts, name: 'process_name', args: { name: pid === process.pid ? 'primary' : 'secondary' } });
178
+ builder.metadata({ pid, ts, name: 'process_sort_index', args: { sort_index: knownPids.size } });
158
179
  knownPids.add(pid);
159
180
  }
160
181
 
161
- const tidHash = `${pid}:${tid}`;
162
- if (!knownTids.has(tidHash)) {
182
+ if (!knownTids.has(tid)) {
163
183
  const primaryCategory = cat.split(',', 1)[0];
164
- builder.thread_name(primaryCategory, tid, pid);
165
- builder.thread_sort_index(tid, tid, pid);
166
- knownTids.add(tidHash);
184
+ builder.metadata({ pid, tid, ts, name: 'thread_name', args: { name: primaryCategory } });
185
+ builder.metadata({ pid, tid, ts, name: 'thread_sort_index', args: { sort_index: tid } });
186
+ knownTids.add(tid);
167
187
  }
168
188
 
169
189
  const event = { ph, name, pid, tid, cat, ts, args };
@@ -207,25 +227,13 @@ function readJSONL() {
207
227
  }
208
228
 
209
229
  /**
210
- * @param {string} sessionId
230
+ * @param {string[]} logs
211
231
  * @returns {NodeJS.ReadableStream}
212
232
  */
213
- function uniteSessionLogs(sessionId) {
233
+ function uniteSessionLogs(logs) {
214
234
  const readable = through();
215
-
216
- glob(temporary.for.jsonl(`${sessionId}.*`), function (err, logs) {
217
- if (err) {
218
- return readable.emit('error', err);
219
- }
220
-
221
- if (logs.length === 0) {
222
- return;
223
- }
224
-
225
- const jsonlStreams = logs.map(filePath => fs.createReadStream(filePath).pipe(readJSONL()));
226
- mergeSortedJSONL(jsonlStreams).pipe(readable);
227
- });
228
-
235
+ const jsonlStreams = logs.map(filePath => fs.createReadStream(filePath).pipe(readJSONL()));
236
+ mergeSortedJSONL(jsonlStreams).pipe(readable);
229
237
  return readable;
230
238
  }
231
239
 
@@ -2,16 +2,19 @@ const funpermaproxy = require('funpermaproxy');
2
2
 
3
3
  const temporary = require('../artifacts/utils/temporaryPath');
4
4
  const { DetoxRuntimeError } = require('../errors');
5
- const { DetoxLogger, installLegacyTracerInterface } = require('../logger');
5
+ const { DetoxLogger, DetoxLogFinalizer, installLegacyTracerInterface } = require('../logger');
6
6
  const symbols = require('../symbols');
7
7
 
8
8
  const DetoxConstants = require('./DetoxConstants');
9
9
 
10
+ //#region Protected symbols
10
11
  const $cleanup = Symbol('cleanup');
12
+ const $logFinalizer = Symbol('logFinalizer');
11
13
  const $restoreSessionState = Symbol('restoreSessionState');
12
14
  const $sessionState = Symbol('restoreSessionState');
13
15
  const $status = Symbol('status');
14
16
  const $worker = Symbol('worker');
17
+ //#endregion
15
18
 
16
19
  class DetoxContext {
17
20
  constructor() {
@@ -56,6 +59,10 @@ class DetoxContext {
56
59
  this.log = this[symbols.logger].child({ cat: 'user' });
57
60
  installLegacyTracerInterface(this.log, this);
58
61
 
62
+ this[$logFinalizer] = new DetoxLogFinalizer({
63
+ session: this[$sessionState]
64
+ });
65
+
59
66
  /** @type {import('../DetoxWorker') | null} */
60
67
  this[$worker] = null;
61
68
  }
@@ -80,27 +87,16 @@ class DetoxContext {
80
87
  //#endregion
81
88
 
82
89
  //#region Internal members
83
- [symbols.onRunStart] = (...args) => this[symbols.worker].onRunStart(...args);
84
90
  [symbols.onRunDescribeStart] = (...args) => this[symbols.worker].onRunDescribeStart(...args);
85
91
  [symbols.onTestStart] = (...args) => this[symbols.worker].onTestStart(...args);
86
- [symbols.onHookStart] = (...args) => this[symbols.worker].onHookStart(...args);
87
92
  [symbols.onHookFailure] = (...args) => this[symbols.worker].onHookFailure(...args);
88
- [symbols.onHookSuccess] = (...args) => this[symbols.worker].onHookSuccess(...args);
89
- [symbols.onTestFnStart] = (...args) => this[symbols.worker].onTestFnStart(...args);
90
93
  [symbols.onTestFnFailure] = (...args) => this[symbols.worker].onTestFnFailure(...args);
91
- [symbols.onTestFnSuccess] = (...args) => this[symbols.worker].onTestFnSuccess(...args);
92
94
  [symbols.onTestDone] = (...args) => this[symbols.worker].onTestDone(...args);
93
95
  [symbols.onRunDescribeFinish] = (...args) => this[symbols.worker].onRunDescribeFinish(...args);
94
- [symbols.onRunFinish] = (...args) => this[symbols.worker].onRunFinish(...args);
95
96
  [symbols.config] = funpermaproxy(() => this[symbols.session].detoxConfig);
96
97
  [symbols.session] = funpermaproxy(() => this[$sessionState]);
97
98
  [symbols.tracing] = Object.freeze({
98
- createEventStream: () => {
99
- const streamUtils = require('../logger/utils/streamUtils');
100
- return streamUtils
101
- .uniteSessionLogs(this[$sessionState].id)
102
- .pipe(streamUtils.chromeTraceStream());
103
- },
99
+ createEventStream: () => this[$logFinalizer].createEventStream(),
104
100
  });
105
101
  /** @abstract */
106
102
  [symbols.reportTestResults](_testResults) {}
@@ -179,6 +175,7 @@ class DetoxContext {
179
175
  module.exports = DetoxContext;
180
176
  module.exports.protected = {
181
177
  $cleanup,
178
+ $logFinalizer,
182
179
  $restoreSessionState,
183
180
  $status,
184
181
  $sessionState,
@@ -15,16 +15,10 @@ class DetoxInternalsFacade {
15
15
  this.installWorker = context[symbols.installWorker];
16
16
  this.uninstallWorker = context[symbols.uninstallWorker];
17
17
  this.onHookFailure = context[symbols.onHookFailure];
18
- this.onHookStart = context[symbols.onHookStart];
19
- this.onHookSuccess = context[symbols.onHookSuccess];
20
18
  this.onRunDescribeFinish = context[symbols.onRunDescribeFinish];
21
19
  this.onRunDescribeStart = context[symbols.onRunDescribeStart];
22
- this.onRunFinish = context[symbols.onRunFinish];
23
- this.onRunStart = context[symbols.onRunStart];
24
20
  this.onTestDone = context[symbols.onTestDone];
25
21
  this.onTestFnFailure = context[symbols.onTestFnFailure];
26
- this.onTestFnStart = context[symbols.onTestFnStart];
27
- this.onTestFnSuccess = context[symbols.onTestFnSuccess];
28
22
  this.onTestStart = context[symbols.onTestStart];
29
23
  this.reportTestResults = context[symbols.reportTestResults];
30
24
  this.resolveConfig = context[symbols.resolveConfig];
@@ -1,34 +1,30 @@
1
- const path = require('path');
2
1
  const { URL } = require('url');
3
- const { promisify } = require('util');
4
2
 
5
3
  const fs = require('fs-extra');
6
- const glob = require('glob');
7
- const pipe = require('multipipe');
8
4
  const onSignalExit = require('signal-exit');
9
5
 
10
6
  const temporary = require('../artifacts/utils/temporaryPath');
11
7
  const { DetoxRuntimeError } = require('../errors');
12
8
  const SessionState = require('../ipc/SessionState');
13
9
  const symbols = require('../symbols');
14
-
15
- const globAsync = promisify(glob);
16
- const globSync = glob.sync;
10
+ const { getCurrentCommand } = require('../utils/argparse');
11
+ const uuid = require('../utils/uuid');
17
12
 
18
13
  const DetoxContext = require('./DetoxContext');
19
14
 
20
- const { $restoreSessionState, $sessionState, $worker } = DetoxContext.protected;
15
+ // Protected symbols
16
+ const { $logFinalizer, $restoreSessionState, $sessionState, $worker } = DetoxContext.protected;
21
17
 
22
- const _finalizeLogs = Symbol('finalizeLogs');
23
- const _finalizeLogsSync = Symbol('finalizeLogsSync');
18
+ //#region Private symbols
24
19
  const _globalLifecycleHandler = Symbol('globalLifecycleHandler');
25
20
  const _ipcServer = Symbol('ipcServer');
26
21
  const _resetLockFile = Symbol('resetLockFile');
27
22
  const _wss = Symbol('wss');
28
23
  const _dirty = Symbol('dirty');
29
24
  const _emergencyTeardown = Symbol('emergencyTeardown');
30
- const _areLogsEnabled = Symbol('areLogsEnabled');
31
25
  const _lifecycleLogger = Symbol('lifecycleLogger');
26
+ const _sessionFile = Symbol('sessionFile');
27
+ //#endregion
32
28
 
33
29
  class DetoxPrimaryContext extends DetoxContext {
34
30
  constructor() {
@@ -37,6 +33,8 @@ class DetoxPrimaryContext extends DetoxContext {
37
33
  this[_dirty] = false;
38
34
  this[_wss] = null;
39
35
  this[_globalLifecycleHandler] = null;
36
+ /** Path to file where the initial session object is serialized */
37
+ this[_sessionFile] = '';
40
38
  /**
41
39
  * @type {import('../ipc/IPCServer') | null}
42
40
  */
@@ -88,8 +86,9 @@ class DetoxPrimaryContext extends DetoxContext {
88
86
  await this[symbols.logger].setConfig(loggerConfig);
89
87
 
90
88
  this[_lifecycleLogger].trace.begin({
89
+ cwd: process.cwd(),
91
90
  data: this[$sessionState],
92
- }, process.argv.slice(1).join(' '));
91
+ }, getCurrentCommand());
93
92
 
94
93
  const IPCServer = require('../ipc/IPCServer');
95
94
  this[_ipcServer] = new IPCServer({
@@ -127,8 +126,10 @@ class DetoxPrimaryContext extends DetoxContext {
127
126
  sessionConfig.server = `ws://localhost:${this[_wss].port}`;
128
127
  }
129
128
 
130
- await fs.writeFile(this[$sessionState].detoxConfigSnapshotPath, this[$sessionState].stringify());
131
- process.env.DETOX_CONFIG_SNAPSHOT_PATH = this[$sessionState].detoxConfigSnapshotPath;
129
+ this[_sessionFile] = temporary.for.json(this[$sessionState].id);
130
+ await fs.writeFile(this[_sessionFile], this[$sessionState].stringify());
131
+ process.env.DETOX_CONFIG_SNAPSHOT_PATH = this[_sessionFile];
132
+ this[_lifecycleLogger].trace(`Serialized the session state at: ${this[_sessionFile]}`);
132
133
 
133
134
  if (opts.workerId !== null) {
134
135
  await this[symbols.installWorker](opts);
@@ -140,10 +141,12 @@ class DetoxPrimaryContext extends DetoxContext {
140
141
  * @param {Partial<DetoxInternals.DetoxInstallWorkerOptions>} [opts]
141
142
  */
142
143
  async [symbols.installWorker](opts = {}) {
143
- const workerId = this[$sessionState].workerId = opts.workerId = opts.workerId || 'worker';
144
+ const workerId = opts.workerId || 'worker';
145
+
146
+ this[$sessionState].workerId = workerId;
144
147
  this[_ipcServer].onRegisterWorker({ workerId });
145
148
 
146
- await super[symbols.installWorker](opts);
149
+ await super[symbols.installWorker]({ ...opts, workerId });
147
150
  }
148
151
 
149
152
  /** @override */
@@ -168,12 +171,14 @@ class DetoxPrimaryContext extends DetoxContext {
168
171
  this[_ipcServer] = null;
169
172
  }
170
173
 
171
- await fs.remove(this[$sessionState].detoxConfigSnapshotPath);
174
+ if (this[_sessionFile]) {
175
+ await fs.remove(this[_sessionFile]);
176
+ }
172
177
 
173
178
  if (this[_dirty]) {
174
179
  try {
175
180
  this[_lifecycleLogger].trace.end();
176
- await this[_finalizeLogs]();
181
+ await this[$logFinalizer].finalize();
177
182
  } catch (err) {
178
183
  this[_lifecycleLogger].error({ err }, 'Encountered an error while merging the process logs:');
179
184
  }
@@ -199,9 +204,13 @@ class DetoxPrimaryContext extends DetoxContext {
199
204
  this[_ipcServer].dispose();
200
205
  }
201
206
 
207
+ if (this[_sessionFile]) {
208
+ fs.removeSync(this[_sessionFile]);
209
+ }
210
+
202
211
  try {
203
212
  this[_lifecycleLogger].trace.end({ abortSignal: signal });
204
- this[_finalizeLogsSync]();
213
+ this[$logFinalizer].finalizeSync();
205
214
  } catch (err) {
206
215
  this[symbols.logger].error({ err }, 'Encountered an error while merging the process logs:');
207
216
  }
@@ -217,77 +226,13 @@ class DetoxPrimaryContext extends DetoxContext {
217
226
  */
218
227
  [$restoreSessionState]() {
219
228
  return new SessionState({
220
- detoxConfigSnapshotPath: temporary.for.json(),
229
+ id: uuid.UUID(),
221
230
  detoxIPCServer: `primary-${process.pid}`,
222
231
  });
223
232
  }
224
233
  //#endregion
225
234
 
226
235
  //#region Private members
227
- async[_finalizeLogs]() {
228
- const sessionId = this[$sessionState].id;
229
- const logs = await globAsync(temporary.for.jsonl(`${sessionId}.*`));
230
- if (logs.length === 0) {
231
- return;
232
- }
233
-
234
- if (this[_areLogsEnabled]()) {
235
- const streamUtils = require('../logger/utils/streamUtils');
236
- const { rootDir } = this[symbols.config].artifacts;
237
-
238
- await fs.mkdirp(rootDir);
239
- const [out1Stream, out2Stream] = ['detox.log', 'detox.trace.json']
240
- .map((filename) => fs.createWriteStream(path.join(rootDir, filename)));
241
-
242
- const mergedStream = streamUtils.uniteSessionLogs(sessionId);
243
-
244
- await Promise.all([
245
- pipe(mergedStream, streamUtils.debugStream(this[symbols.logger].config.options), out1Stream),
246
- pipe(mergedStream, streamUtils.chromeTraceStream(), streamUtils.writeJSON(), out2Stream),
247
- ]);
248
- }
249
-
250
- await Promise.all(logs.map(filepath => fs.remove(filepath)));
251
- }
252
-
253
- async[_finalizeLogsSync]() {
254
- const logsEnabled = this[_areLogsEnabled]();
255
-
256
- const { rootDir } = this[symbols.config].artifacts;
257
-
258
- if (logsEnabled) {
259
- fs.mkdirpSync(rootDir);
260
- }
261
-
262
- const sessionId = this[$sessionState].id;
263
- const logs = globSync(temporary.for.jsonl(`${sessionId}.*`));
264
-
265
- for (const log of logs) {
266
- if (logsEnabled) {
267
- fs.moveSync(log, path.join(rootDir, path.basename(log)));
268
- } else {
269
- fs.removeSync(log);
270
- }
271
- }
272
- }
273
-
274
- [_areLogsEnabled]() {
275
- const { rootDir, plugins } = this[symbols.config].artifacts || {};
276
- if (!rootDir || !plugins) {
277
- return false;
278
- }
279
-
280
- if (!plugins.log.enabled) {
281
- return false;
282
- }
283
-
284
- if (!plugins.log.keepOnlyFailedTestsArtifacts) {
285
- return true;
286
- }
287
-
288
- return this[$sessionState].testResults.some(r => !r.success);
289
- }
290
-
291
236
  async[_resetLockFile]() {
292
237
  const DeviceRegistry = require('../devices/DeviceRegistry');
293
238
 
@@ -6,9 +6,12 @@ const symbols = require('../symbols');
6
6
 
7
7
  const DetoxContext = require('./DetoxContext');
8
8
 
9
+ // Protected symbols
9
10
  const { $restoreSessionState, $sessionState, $worker } = DetoxContext.protected;
11
+
12
+ //#region Private symbols
10
13
  const _ipcClient = Symbol('ipcClient');
11
- const _shortLifecycle = Symbol('shortLifecycle');
14
+ //#endregion
12
15
 
13
16
  class DetoxSecondaryContext extends DetoxContext {
14
17
  constructor() {
@@ -19,13 +22,6 @@ class DetoxSecondaryContext extends DetoxContext {
19
22
  * @type {import('../ipc/IPCClient')}
20
23
  */
21
24
  this[_ipcClient] = null;
22
- /**
23
- * @private
24
- * @type {undefined | boolean}
25
- *
26
- * TODO: explain what is short lifecycle and why we need it
27
- */
28
- this[_shortLifecycle] = false;
29
25
  }
30
26
 
31
27
  //#region Internal members
@@ -74,9 +70,9 @@ class DetoxSecondaryContext extends DetoxContext {
74
70
 
75
71
  /** @override */
76
72
  async [symbols.installWorker](opts = {}) {
77
- const workerId = opts.workerId = opts.workerId || 'worker';
73
+ const workerId = opts.workerId || 'worker';
78
74
  await this[_ipcClient].registerWorker(workerId);
79
- await super[symbols.installWorker](opts);
75
+ await super[symbols.installWorker]({ ...opts, workerId });
80
76
  }
81
77
  //#endregion
82
78