detox 20.13.5-smoke.0 → 20.13.6-smoke.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. package/Detox-android/com/wix/detox/{20.13.5-smoke.0/detox-20.13.5-smoke.0-javadoc.jar → 20.13.6-smoke.0/detox-20.13.6-smoke.0-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0-javadoc.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.13.5-smoke.0/detox-20.13.5-smoke.0-sources.jar → 20.13.6-smoke.0/detox-20.13.6-smoke.0-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/{20.13.5-smoke.0/detox-20.13.5-smoke.0.pom → 20.13.6-smoke.0/detox-20.13.6-smoke.0.pom} +1 -1
  12. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0.pom.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0.pom.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0.pom.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.13.6-smoke.0/detox-20.13.6-smoke.0.pom.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  17. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  18. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  19. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  20. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  21. package/Detox-ios-src.tbz +0 -0
  22. package/Detox-ios.tbz +0 -0
  23. package/detox.d.ts +7 -0
  24. package/internals.d.ts +2 -1
  25. package/local-cli/testCommand/TestRunnerCommand.js +26 -3
  26. package/local-cli/utils/interruptListeners.js +15 -0
  27. package/package.json +3 -3
  28. package/runners/jest/testEnvironment/index.js +7 -1
  29. package/src/DetoxWorker.js +1 -1
  30. package/src/configuration/composeRunnerConfig.js +3 -1
  31. package/src/ipc/IPCClient.js +4 -4
  32. package/src/ipc/IPCServer.js +8 -6
  33. package/src/realms/DetoxContext.js +3 -3
  34. package/src/realms/DetoxPrimaryContext.js +62 -27
  35. package/src/realms/DetoxSecondaryContext.js +4 -4
  36. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0-javadoc.jar.md5 +0 -1
  37. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0-javadoc.jar.sha1 +0 -1
  38. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0-javadoc.jar.sha256 +0 -1
  39. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0-javadoc.jar.sha512 +0 -1
  40. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0-sources.jar.md5 +0 -1
  41. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0-sources.jar.sha1 +0 -1
  42. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0-sources.jar.sha256 +0 -1
  43. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0-sources.jar.sha512 +0 -1
  44. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0.pom.md5 +0 -1
  45. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0.pom.sha1 +0 -1
  46. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0.pom.sha256 +0 -1
  47. package/Detox-android/com/wix/detox/20.13.5-smoke.0/detox-20.13.5-smoke.0.pom.sha512 +0 -1
  48. /package/Detox-android/com/wix/detox/{20.13.5-smoke.0/detox-20.13.5-smoke.0.aar → 20.13.6-smoke.0/detox-20.13.6-smoke.0.aar} +0 -0
  49. /package/Detox-android/com/wix/detox/{20.13.5-smoke.0/detox-20.13.5-smoke.0.aar.md5 → 20.13.6-smoke.0/detox-20.13.6-smoke.0.aar.md5} +0 -0
  50. /package/Detox-android/com/wix/detox/{20.13.5-smoke.0/detox-20.13.5-smoke.0.aar.sha1 → 20.13.6-smoke.0/detox-20.13.6-smoke.0.aar.sha1} +0 -0
  51. /package/Detox-android/com/wix/detox/{20.13.5-smoke.0/detox-20.13.5-smoke.0.aar.sha256 → 20.13.6-smoke.0/detox-20.13.6-smoke.0.aar.sha256} +0 -0
  52. /package/Detox-android/com/wix/detox/{20.13.5-smoke.0/detox-20.13.5-smoke.0.aar.sha512 → 20.13.6-smoke.0/detox-20.13.6-smoke.0.aar.sha512} +0 -0
@@ -0,0 +1 @@
1
+ a8c32841cfe8699cd49f4ef7f591472e
@@ -0,0 +1 @@
1
+ a0afb25b95e6c2a82390c1f9968d94e5b8436162
@@ -0,0 +1 @@
1
+ e92828fd53c05706a15d49e26f77a6f7b9642e8e81fd28b029205d8998c8649a
@@ -0,0 +1 @@
1
+ 57fcea330953f4e84acdf023b0dd523c68a014103380ac0d6a0e0cf8fe1472cc26fd34c39b85513cc6ba785829231966f731d0fc75ac44935c4e5d15928c924c
@@ -0,0 +1 @@
1
+ e0c894040964164934f0406c7d63a027
@@ -0,0 +1 @@
1
+ b56ad3333cc41de17016b17978bc2177f2f0d119
@@ -0,0 +1 @@
1
+ e3b942f00eb4eae4821caac3fe3949572311f537ecc7fa2064c6f33d77de72be
@@ -0,0 +1 @@
1
+ 28563ea2ea0ffc7e31608cdce526c117b5b1686c2e7632d092a684615d87bef65a797d7f26c4ddb1608c6c903cf71ef300719d8a42d4493584dcdafaadbfdb49
@@ -3,7 +3,7 @@
3
3
  <modelVersion>4.0.0</modelVersion>
4
4
  <groupId>com.wix</groupId>
5
5
  <artifactId>detox</artifactId>
6
- <version>20.13.5-smoke.0</version>
6
+ <version>20.13.6-smoke.0</version>
7
7
  <packaging>aar</packaging>
8
8
  <name>Detox</name>
9
9
  <description>Gray box end-to-end testing and automation library for mobile apps</description>
@@ -0,0 +1 @@
1
+ ab0225332f8d4e6124f512e5edfe31e4
@@ -0,0 +1 @@
1
+ b82bc2b62fe1b75833b74ca68d6b7b3820d55fa0
@@ -0,0 +1 @@
1
+ 0cf6289f1b9631dcfb44d1a44d1bd87676b5e51f725715680387cc20adeb6d31
@@ -0,0 +1 @@
1
+ 45a470926557bc7af525f23de0a297ada47eca664e541a5cb5d64d44fcab82276becb74f865788fa9311d8b11dc014ea182bff18f0608c463146445b83b264db
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>20.13.5-smoke.0</latest>
7
- <release>20.13.5-smoke.0</release>
6
+ <latest>20.13.6-smoke.0</latest>
7
+ <release>20.13.6-smoke.0</release>
8
8
  <versions>
9
- <version>20.13.5-smoke.0</version>
9
+ <version>20.13.6-smoke.0</version>
10
10
  </versions>
11
- <lastUpdated>20231031113340</lastUpdated>
11
+ <lastUpdated>20231124065119</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 7f4907fe31213c5ffdb0d44e71e84a22
1
+ fa4690e95e1b8d3ee728867b00f62ff1
@@ -1 +1 @@
1
- 4b7b6b7c13fd32b7e19f58e75a86baf492950c2d
1
+ 0d426cba4d5333f29442498e7d8b7e3f928b3ff7
@@ -1 +1 @@
1
- a202bfb3cb9386d9caa65646400585c302cff4065473929c3bd13a1842cd2577
1
+ f090842a373cd08b1a6ed99e84151f02202793f87c0bdbd5d31ec133f492f84a
@@ -1 +1 @@
1
- 5814a51706f6d119195a272c959ad6c8c5d1112ac3ef6fe69a387b426ad34cea2e21bbb852a243c3c314d464ccb80d58c2b62e6e9e522db84bb1bbce35388305
1
+ 5b503c0628b3f251d893b32a1da11aa000f9d411dc5da36782e22ce77c5474d97aaf68bea30d8983c673426b358da8b53c6a20ecc8194fac408df1eda2ed9912
package/Detox-ios-src.tbz CHANGED
Binary file
package/Detox-ios.tbz CHANGED
Binary file
package/detox.d.ts CHANGED
@@ -235,6 +235,13 @@ declare global {
235
235
  * @see {DetoxInternals.DetoxTestFileReport#isPermanentFailure}
236
236
  */
237
237
  bail?: boolean;
238
+ /**
239
+ * When true, tells `detox test` to spawn the test runner in a detached mode.
240
+ * This is useful in CI environments, where you want to intercept SIGINT and SIGTERM signals to gracefully shut down the test runner and the device.
241
+ * Instead of passing the kill signal to the child process (the test runner), Detox will send an emergency shutdown request to all the workers, and then it will wait for them to finish.
242
+ * @default false
243
+ */
244
+ detached?: boolean;
238
245
  /**
239
246
  * Custom handler to process --inspect-brk CLI flag.
240
247
  * Use it when you rely on another test runner than Jest to mutate the config.
package/internals.d.ts CHANGED
@@ -116,8 +116,9 @@ declare global {
116
116
  /**
117
117
  * Workaround for Jest exiting abruptly in --bail mode.
118
118
  * Makes sure that all workers and their test environments are properly torn down.
119
+ * @param [permanent] - forbids further retries
119
120
  */
120
- unsafe_conductEarlyTeardown(): Promise<void>;
121
+ unsafe_conductEarlyTeardown(permanent?: boolean): Promise<void>;
121
122
  /**
122
123
  * Reports to Detox CLI about passed and failed test files.
123
124
  * The failed test files might be re-run again if
@@ -12,6 +12,7 @@ const { escapeSpaces, useForwardSlashes } = require('../../src/utils/shellUtils'
12
12
  const sleep = require('../../src/utils/sleep');
13
13
  const AppStartCommand = require('../startCommand/AppStartCommand');
14
14
  const { markErrorAsLogged } = require('../utils/cliErrorHandling');
15
+ const interruptListeners = require('../utils/interruptListeners');
15
16
 
16
17
  const TestRunnerError = require('./TestRunnerError');
17
18
 
@@ -28,10 +29,12 @@ class TestRunnerCommand {
28
29
  const appsConfig = opts.config.apps;
29
30
 
30
31
  this._argv = runnerConfig.args;
32
+ this._detached = runnerConfig.detached;
31
33
  this._retries = runnerConfig.retries;
32
34
  this._envHint = this._buildEnvHint(opts.env);
33
35
  this._startCommands = this._prepareStartCommands(appsConfig, cliConfig);
34
36
  this._envFwd = {};
37
+ this._terminating = false;
35
38
 
36
39
  if (runnerConfig.forwardEnv) {
37
40
  this._envFwd = this._buildEnvOverride(cliConfig, deviceConfig);
@@ -59,16 +62,20 @@ class TestRunnerCommand {
59
62
  } catch (e) {
60
63
  launchError = e;
61
64
 
65
+ if (this._terminating) {
66
+ runsLeft = 0;
67
+ }
68
+
62
69
  const failedTestFiles = detox.session.testResults.filter(r => !r.success);
63
70
 
64
71
  const { bail } = detox.config.testRunner;
65
72
  if (bail && failedTestFiles.some(r => r.isPermanentFailure)) {
66
- throw e;
73
+ runsLeft = 0;
67
74
  }
68
75
 
69
76
  const testFilesToRetry = failedTestFiles.filter(r => !r.isPermanentFailure).map(r => r.testFilePath);
70
- if (_.isEmpty(testFilesToRetry)) {
71
- throw e;
77
+ if (testFilesToRetry.length === 0) {
78
+ runsLeft = 0;
72
79
  }
73
80
 
74
81
  if (--runsLeft > 0) {
@@ -143,6 +150,15 @@ class TestRunnerCommand {
143
150
  }, _.isUndefined);
144
151
  }
145
152
 
153
+ _onTerminate = () => {
154
+ if (this._terminating) {
155
+ return;
156
+ }
157
+
158
+ this._terminating = true;
159
+ return detox.unsafe_conductEarlyTeardown(true);
160
+ };
161
+
146
162
  async _spawnTestRunner() {
147
163
  const fullCommand = this._buildSpawnArguments().map(escapeSpaces);
148
164
  const fullCommandWithHint = printEnvironmentVariables(this._envHint) + fullCommand.join(' ');
@@ -153,6 +169,7 @@ class TestRunnerCommand {
153
169
  cp.spawn(fullCommand[0], fullCommand.slice(1), {
154
170
  shell: true,
155
171
  stdio: 'inherit',
172
+ detached: this._detached,
156
173
  env: _({})
157
174
  .assign(process.env)
158
175
  .assign(this._envFwd)
@@ -162,6 +179,8 @@ class TestRunnerCommand {
162
179
  })
163
180
  .on('error', /* istanbul ignore next */ (err) => reject(err))
164
181
  .on('exit', (code, signal) => {
182
+ interruptListeners.unsubscribe(this._onTerminate);
183
+
165
184
  if (code === 0) {
166
185
  log.trace.end({ success: true });
167
186
  resolve();
@@ -175,6 +194,10 @@ class TestRunnerCommand {
175
194
  reject(markErrorAsLogged(error));
176
195
  }
177
196
  });
197
+
198
+ if (this._detached) {
199
+ interruptListeners.subscribe(this._onTerminate);
200
+ }
178
201
  });
179
202
  }
180
203
 
@@ -0,0 +1,15 @@
1
+ function subscribe(listener) {
2
+ process.on('SIGINT', listener);
3
+ process.on('SIGTERM', listener);
4
+ }
5
+
6
+ function unsubscribe(listener) {
7
+ process.removeListener('SIGINT', listener);
8
+ process.removeListener('SIGTERM', listener);
9
+ }
10
+
11
+ module.exports = {
12
+ subscribe,
13
+ unsubscribe,
14
+ };
15
+
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.13.5-smoke.0",
4
+ "version": "20.13.6-smoke.0",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -51,7 +51,7 @@
51
51
  "eslint-plugin-node": "^11.1.0",
52
52
  "eslint-plugin-unicorn": "^47.0.0",
53
53
  "jest": "^28.1.3",
54
- "jest-allure2-reporter": "2.0.0-alpha.6",
54
+ "jest-allure2-reporter": "2.0.0-alpha.10",
55
55
  "mockdate": "^2.0.1",
56
56
  "prettier": "^2.4.1",
57
57
  "react-native": "0.71.10",
@@ -109,5 +109,5 @@
109
109
  "browserslist": [
110
110
  "node 14"
111
111
  ],
112
- "gitHead": "dbd293634bf7159cdf0ebd23dcde982225783a4c"
112
+ "gitHead": "b34effb080a931111f44eb2c1bbd6bdc67253fc1"
113
113
  }
@@ -73,7 +73,9 @@ class DetoxCircusEnvironment extends NodeEnvironment {
73
73
  // @ts-expect-error TS2425
74
74
  async handleTestEvent(event, state) {
75
75
  if (detox.session.unsafe_earlyTeardown) {
76
- throw new Error('Detox halted test execution due to an early teardown request');
76
+ if (event.name === 'test_fn_start' || event.name === 'hook_start') {
77
+ throw new Error('Detox halted test execution due to an early teardown request');
78
+ }
77
79
  }
78
80
 
79
81
  this._timer.schedule(state.testTimeout != null ? state.testTimeout : this.setupTimeout);
@@ -107,6 +109,10 @@ class DetoxCircusEnvironment extends NodeEnvironment {
107
109
  * @protected
108
110
  */
109
111
  async initDetox() {
112
+ if (detox.session.unsafe_earlyTeardown) {
113
+ throw new Error('Detox halted test execution due to an early teardown request');
114
+ }
115
+
110
116
  const opts = {
111
117
  global: this.global,
112
118
  workerId: `w${process.env.JEST_WORKER_ID}`,
@@ -132,7 +132,7 @@ class DetoxWorker {
132
132
  };
133
133
 
134
134
  this._artifactsManager = artifactsManagerFactory.createArtifactsManager(this._artifactsConfig, commonDeps);
135
- this._deviceCookie = yield this._context[symbols.allocateDevice]();
135
+ this._deviceCookie = yield this._context[symbols.allocateDevice](this._deviceConfig);
136
136
 
137
137
  this.device = runtimeDeviceFactory.createRuntimeDevice(
138
138
  this._deviceCookie,
@@ -32,6 +32,7 @@ function composeRunnerConfig(opts) {
32
32
  retries: 0,
33
33
  inspectBrk: inspectBrkHookDefault,
34
34
  forwardEnv: false,
35
+ detached: false,
35
36
  bail: false,
36
37
  jest: {
37
38
  setupTimeout: 300000,
@@ -56,8 +57,9 @@ function composeRunnerConfig(opts) {
56
57
 
57
58
  if (typeof merged.inspectBrk === 'function') {
58
59
  if (cliConfig.inspectBrk) {
59
- merged.retries = 0;
60
+ merged.detached = false;
60
61
  merged.forwardEnv = true;
62
+ merged.retries = 0;
61
63
  merged.inspectBrk(merged);
62
64
  }
63
65
 
@@ -60,8 +60,8 @@ class IPCClient {
60
60
  this._sessionState.patch(sessionState);
61
61
  }
62
62
 
63
- async allocateDevice() {
64
- const { deviceCookie, error } = deserializeObjectWithError(await this._emit('allocateDevice', {}));
63
+ async allocateDevice(deviceConfig) {
64
+ const { deviceCookie, error } = deserializeObjectWithError(await this._emit('allocateDevice', { deviceConfig }));
65
65
  if (error) {
66
66
  throw error;
67
67
  }
@@ -86,8 +86,8 @@ class IPCClient {
86
86
  this._sessionState.patch(sessionState);
87
87
  }
88
88
 
89
- async conductEarlyTeardown() {
90
- const sessionState = await this._emit('conductEarlyTeardown', {});
89
+ async conductEarlyTeardown({ permanent }) {
90
+ const sessionState = await this._emit('conductEarlyTeardown', { permanent });
91
91
  this._sessionState.patch(sessionState);
92
92
  }
93
93
 
@@ -9,7 +9,7 @@ class IPCServer {
9
9
  * @param {import('./SessionState')} options.sessionState
10
10
  * @param {Detox.Logger} options.logger
11
11
  * @param {object} options.callbacks
12
- * @param {() => Promise<any>} options.callbacks.onAllocateDevice
12
+ * @param {(deviceConfig: DetoxInternals.RuntimeConfig['device']) => Promise<any>} options.callbacks.onAllocateDevice
13
13
  * @param {(cookie: any) => Promise<void>} options.callbacks.onDeallocateDevice
14
14
  */
15
15
  constructor({ sessionState, logger, callbacks }) {
@@ -73,6 +73,7 @@ class IPCServer {
73
73
  this._ipc.server.emit(socket, 'registerContextDone', {
74
74
  testResults: this._sessionState.testResults,
75
75
  testSessionIndex: this._sessionState.testSessionIndex,
76
+ unsafe_earlyTeardown: this._sessionState.unsafe_earlyTeardown,
76
77
  });
77
78
  }
78
79
 
@@ -90,10 +91,11 @@ class IPCServer {
90
91
  }
91
92
  }
92
93
 
93
- onConductEarlyTeardown(_data = null, socket = null) {
94
- // Note that we don't save `unsafe_earlyTeardown` in the primary session state
95
- // because it's transient and needed only to make the workers quit early.
94
+ onConductEarlyTeardown({ permanent }, socket = null) {
96
95
  const newState = { unsafe_earlyTeardown: true };
96
+ if (permanent) {
97
+ Object.assign(this._sessionState, newState);
98
+ }
97
99
 
98
100
  if (socket) {
99
101
  this._ipc.server.emit(socket, 'conductEarlyTeardownDone', newState);
@@ -102,11 +104,11 @@ class IPCServer {
102
104
  this._ipc.server.broadcast('sessionStateUpdate', newState);
103
105
  }
104
106
 
105
- async onAllocateDevice(_payload, socket) {
107
+ async onAllocateDevice({ deviceConfig }, socket) {
106
108
  let deviceCookie;
107
109
 
108
110
  try {
109
- deviceCookie = await this._callbacks.onAllocateDevice();
111
+ deviceCookie = await this._callbacks.onAllocateDevice(deviceConfig);
110
112
  this._ipc.server.emit(socket, 'allocateDeviceDone', { deviceCookie });
111
113
  } catch (error) {
112
114
  this._ipc.server.emit(socket, 'allocateDeviceDone', serializeObjectWithError({ error }));
@@ -102,7 +102,7 @@ class DetoxContext {
102
102
  /** @abstract */
103
103
  [symbols.reportTestResults](_testResults) {}
104
104
  /** @abstract */
105
- [symbols.conductEarlyTeardown]() {}
105
+ [symbols.conductEarlyTeardown](_permanent) {}
106
106
  /**
107
107
  * @abstract
108
108
  * @param {Partial<DetoxInternals.DetoxInitOptions>} _opts
@@ -152,10 +152,10 @@ class DetoxContext {
152
152
  }
153
153
 
154
154
  /** @abstract */
155
- async [symbols.allocateDevice]() {}
155
+ async [symbols.allocateDevice](_deviceConfig) {}
156
156
 
157
157
  /** @abstract */
158
- async [symbols.deallocateDevice]() {}
158
+ async [symbols.deallocateDevice](_deviceCookie) {}
159
159
 
160
160
  async [symbols.uninstallWorker]() {
161
161
  try {
@@ -23,7 +23,9 @@ const _emergencyTeardown = Symbol('emergencyTeardown');
23
23
  const _lifecycleLogger = Symbol('lifecycleLogger');
24
24
  const _sessionFile = Symbol('sessionFile');
25
25
  const _logFinalError = Symbol('logFinalError');
26
- const _deviceAllocator = Symbol('deviceAllocator');
26
+ const _cookieAllocators = Symbol('cookieAllocators');
27
+ const _deviceAllocators = Symbol('deviceAllocators');
28
+ const _initDeviceAllocator = Symbol('initDeviceAllocator');
27
29
  //#endregion
28
30
 
29
31
  class DetoxPrimaryContext extends DetoxContext {
@@ -32,7 +34,8 @@ class DetoxPrimaryContext extends DetoxContext {
32
34
 
33
35
  this[_dirty] = false;
34
36
  this[_wss] = null;
35
- this[_deviceAllocator] = null;
37
+ this[_cookieAllocators] = {};
38
+ this[_deviceAllocators] = {};
36
39
 
37
40
  /** Path to file where the initial session object is serialized */
38
41
  this[_sessionFile] = '';
@@ -51,9 +54,9 @@ class DetoxPrimaryContext extends DetoxContext {
51
54
  }
52
55
  }
53
56
 
54
- [symbols.conductEarlyTeardown] = async () => {
57
+ [symbols.conductEarlyTeardown] = async (permanent = false) => {
55
58
  if (this[_ipcServer]) {
56
- await this[_ipcServer].onConductEarlyTeardown();
59
+ await this[_ipcServer].onConductEarlyTeardown({ permanent });
57
60
  }
58
61
  };
59
62
 
@@ -85,7 +88,6 @@ class DetoxPrimaryContext extends DetoxContext {
85
88
  const detoxConfig = await this[symbols.resolveConfig](opts);
86
89
 
87
90
  const {
88
- device: deviceConfig,
89
91
  logger: loggerConfig,
90
92
  session: sessionConfig
91
93
  } = detoxConfig;
@@ -109,16 +111,6 @@ class DetoxPrimaryContext extends DetoxContext {
109
111
 
110
112
  await this[_ipcServer].init();
111
113
 
112
- const environmentFactory = require('../environmentFactory');
113
-
114
- const { deviceAllocatorFactory } = environmentFactory.createFactories(deviceConfig);
115
- this[_deviceAllocator] = deviceAllocatorFactory.createDeviceAllocator({
116
- detoxConfig,
117
- detoxSession: this[$sessionState],
118
- });
119
-
120
- await this[_deviceAllocator].init();
121
-
122
114
  // TODO: Detox-server creation ought to be delegated to a generator/factory.
123
115
  const DetoxServer = require('../server/DetoxServer');
124
116
  if (sessionConfig.autoStart) {
@@ -162,15 +154,16 @@ class DetoxPrimaryContext extends DetoxContext {
162
154
  }
163
155
 
164
156
  /** @override */
165
- async [symbols.allocateDevice]() {
166
- const { device } = this[$sessionState].detoxConfig;
167
- const deviceCookie = await this[_deviceAllocator].allocate(device);
157
+ async [symbols.allocateDevice](deviceConfig) {
158
+ const deviceAllocator = await this[_initDeviceAllocator](deviceConfig);
159
+ const deviceCookie = await deviceAllocator.allocate(deviceConfig);
160
+ this[_cookieAllocators][deviceCookie.id] = deviceAllocator;
168
161
 
169
162
  try {
170
- return await this[_deviceAllocator].postAllocate(deviceCookie);
163
+ return await deviceAllocator.postAllocate(deviceCookie);
171
164
  } catch (e) {
172
165
  try {
173
- await this[_deviceAllocator].free(deviceCookie, { shutdown: true });
166
+ await deviceAllocator.free(deviceCookie, { shutdown: true });
174
167
  } catch (e2) {
175
168
  this[symbols.logger].error({ cat: 'device', err: e2 }, `Failed to free ${deviceCookie.name || deviceCookie.id} after a failed allocation`);
176
169
  }
@@ -181,7 +174,17 @@ class DetoxPrimaryContext extends DetoxContext {
181
174
 
182
175
  /** @override */
183
176
  async [symbols.deallocateDevice](cookie) {
184
- await this[_deviceAllocator].free(cookie);
177
+ const deviceAllocator = this[_cookieAllocators][cookie.id];
178
+ if (!deviceAllocator) {
179
+ throw new DetoxRuntimeError({
180
+ message: `Cannot deallocate device ${cookie.id} because it was not allocated by this context.`,
181
+ hint: `See the actually known allocated devices below:`,
182
+ debugInfo: Object.keys(this[_cookieAllocators]).map(id => ` - ${id}`).join('\n'),
183
+ });
184
+ }
185
+
186
+ await deviceAllocator.free(cookie);
187
+ delete this[_cookieAllocators][cookie.id];
185
188
  }
186
189
 
187
190
  /** @override */
@@ -191,11 +194,18 @@ class DetoxPrimaryContext extends DetoxContext {
191
194
  await this[symbols.uninstallWorker]();
192
195
  }
193
196
  } finally {
194
- if (this[_deviceAllocator]) {
195
- await this[_deviceAllocator].cleanup();
196
- this[_deviceAllocator] = null;
197
+ for (const key of Object.keys(this[_deviceAllocators])) {
198
+ const deviceAllocator = this[_deviceAllocators][key];
199
+ delete this[_deviceAllocators][key];
200
+ try {
201
+ await deviceAllocator.cleanup();
202
+ } catch (err) {
203
+ this[symbols.logger].error({ cat: 'device', err }, `Failed to cleanup device allocator for ${key}`);
204
+ }
197
205
  }
198
206
 
207
+ this[_cookieAllocators] = {};
208
+
199
209
  if (this[_wss]) {
200
210
  await this[_wss].close();
201
211
  this[_wss] = null;
@@ -227,11 +237,18 @@ class DetoxPrimaryContext extends DetoxContext {
227
237
  return;
228
238
  }
229
239
 
230
- if (this[_deviceAllocator]) {
231
- this[_deviceAllocator].emergencyCleanup();
232
- this[_deviceAllocator] = null;
240
+ for (const key of Object.keys(this[_deviceAllocators])) {
241
+ const deviceAllocator = this[_deviceAllocators][key];
242
+ delete this[_deviceAllocators][key];
243
+ try {
244
+ deviceAllocator.emergencyCleanup();
245
+ } catch (err) {
246
+ this[symbols.logger].error({ cat: 'device', err }, `Failed to emergency cleanup device allocator for ${key}`);
247
+ }
233
248
  }
234
249
 
250
+ this[_cookieAllocators] = {};
251
+
235
252
  if (this[_wss]) {
236
253
  this[_wss].close();
237
254
  }
@@ -253,6 +270,24 @@ class DetoxPrimaryContext extends DetoxContext {
253
270
  }
254
271
  };
255
272
 
273
+ [_initDeviceAllocator] = async (deviceConfig) => {
274
+ const deviceType = deviceConfig.type;
275
+ if (!this[_deviceAllocators][deviceType]) {
276
+ const environmentFactory = require('../environmentFactory');
277
+ const { deviceAllocatorFactory } = environmentFactory.createFactories(deviceConfig);
278
+ const { detoxConfig } = this[$sessionState];
279
+ const deviceAllocator = deviceAllocatorFactory.createDeviceAllocator({
280
+ detoxConfig,
281
+ detoxSession: this[$sessionState],
282
+ });
283
+
284
+ await deviceAllocator.init();
285
+ this[_deviceAllocators][deviceType] = deviceAllocator;
286
+ }
287
+
288
+ return this[_deviceAllocators][deviceType];
289
+ };
290
+
256
291
  [_logFinalError] = (err) => {
257
292
  this[_lifecycleLogger].error(err, 'Encountered an error while merging the process logs:');
258
293
  };
@@ -33,9 +33,9 @@ class DetoxSecondaryContext extends DetoxContext {
33
33
  }
34
34
  }
35
35
 
36
- [symbols.conductEarlyTeardown] = async () => {
36
+ [symbols.conductEarlyTeardown] = async (permanent = false) => {
37
37
  if (this[_ipcClient]) {
38
- await this[_ipcClient].conductEarlyTeardown();
38
+ await this[_ipcClient].conductEarlyTeardown({ permanent });
39
39
  } else {
40
40
  throw new DetoxInternalError('Detected an attempt to report early teardown using a non-initialized context.');
41
41
  }
@@ -63,9 +63,9 @@ class DetoxSecondaryContext extends DetoxContext {
63
63
  }
64
64
 
65
65
  /** @override */
66
- async [symbols.allocateDevice]() {
66
+ async [symbols.allocateDevice](deviceConfig) {
67
67
  if (this[_ipcClient]) {
68
- const deviceCookie = await this[_ipcClient].allocateDevice();
68
+ const deviceCookie = await this[_ipcClient].allocateDevice(deviceConfig);
69
69
  return deviceCookie;
70
70
  } else {
71
71
  throw new DetoxInternalError('Detected an attempt to allocate a device using a non-initialized context.');
@@ -1 +0,0 @@
1
- 5255fde6aca81768a24ce63dcfa9f797
@@ -1 +0,0 @@
1
- 827a40f943fd6ce57d5419a0d697cd20f8c367f9
@@ -1 +0,0 @@
1
- 22c778066d0f509c58998b90e237dda1a537e65e51fd6fd381dbf00f6d84e4d5
@@ -1 +0,0 @@
1
- e8565e7a1045fab352bf6ece22ae06b2ddb89dbfae769f005079678f3c5dea9d383afd4aeeeb066286c1ab680b43aff0f455fd1db5e3de9d99ba10d1e304299f
@@ -1 +0,0 @@
1
- 1cf108e11200c36908872deac4795afb
@@ -1 +0,0 @@
1
- 70c5ff9fb21a820221c1109980e6996a727c3fb4
@@ -1 +0,0 @@
1
- cf2343de0df2893f2463a209e93e52fbfab347f9315a6bf1f4ac7754275841d7
@@ -1 +0,0 @@
1
- 04b323ce5c8fde4d8e6614b8aefb3e1597a3d6f2ed59f6939e30d0e1f5412e8d8e252f5a4b5051942e6d7e796106fa235da30cd88185f71ad76771bafdb09456
@@ -1 +0,0 @@
1
- 5e84a09aa092033ceb9e3bc661e1ea65
@@ -1 +0,0 @@
1
- ea2e0cbe952640d79220d3d52eea21ccc02eb421
@@ -1 +0,0 @@
1
- 7facef73570cc6adba4f6afec1af48fd594e054da7bdba7196514b91fc19e7a9
@@ -1 +0,0 @@
1
- 0263e3f324511df0a54b60510ad29051ce2efe1f4cb224390963cb0c57c9b7b027f4cfb136683f963ff0a9170bd580cdacba867a0f528bbeebc61eaae438baa9