detox 20.12.1 → 20.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. package/Detox-android/com/wix/detox/{20.12.1/detox-20.12.1-javadoc.jar → 20.13.0/detox-20.13.0-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0-javadoc.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.12.1/detox-20.12.1-sources.jar → 20.13.0/detox-20.13.0-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0.aar +0 -0
  12. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0.aar.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0.aar.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0.aar.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0.aar.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/{20.12.1/detox-20.12.1.pom → 20.13.0/detox-20.13.0.pom} +1 -7
  17. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0.pom.md5 +1 -0
  18. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0.pom.sha1 +1 -0
  19. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0.pom.sha256 +1 -0
  20. package/Detox-android/com/wix/detox/20.13.0/detox-20.13.0.pom.sha512 +1 -0
  21. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  22. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  23. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  24. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  25. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  26. package/Detox-ios-src.tbz +0 -0
  27. package/Detox-ios.tbz +0 -0
  28. package/android/detox/build.gradle +4 -3
  29. package/android/detox/src/full/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +2 -2
  30. package/android/detox/src/full/java/com/wix/detox/espresso/action/GetAttributesAction.kt +30 -31
  31. package/android/detox/src/full/java/com/wix/detox/espresso/common/MaterialSliderHelper.kt +21 -0
  32. package/android/detox/src/full/java/com/wix/detox/espresso/common/{SliderHelper.kt → ReactSliderHelper.kt} +6 -5
  33. package/android/detox/src/full/java/com/wix/detox/espresso/matcher/ViewMatchers.kt +2 -2
  34. package/android/detox/src/testFull/java/com/wix/detox/espresso/common/MaterialSliderHelperTest.kt +33 -0
  35. package/android/detox/src/testFull/java/com/wix/detox/espresso/common/{SliderHelperTest.kt → ReactSliderHelperTest.kt} +3 -3
  36. package/internals.d.ts +10 -1
  37. package/local-cli/reset-lock-file.js +5 -9
  38. package/package.json +5 -6
  39. package/runners/jest/reporters/DetoxReporter.js +127 -9
  40. package/runners/jest/testEnvironment/index.js +5 -0
  41. package/src/DetoxWorker.js +5 -11
  42. package/src/artifacts/providers/index.js +3 -3
  43. package/src/artifacts/screenshot/SimulatorScreenshotPlugin.js +0 -17
  44. package/src/configuration/composeLoggerConfig.js +1 -0
  45. package/src/devices/allocation/DeviceAllocator.js +66 -20
  46. package/src/devices/allocation/DeviceList.js +44 -0
  47. package/src/devices/allocation/DeviceRegistry.js +189 -0
  48. package/src/devices/allocation/drivers/AllocationDriverBase.d.ts +15 -0
  49. package/src/devices/{common/drivers/android/tools → allocation/drivers/android}/FreeDeviceFinder.js +11 -10
  50. package/src/devices/allocation/drivers/android/attached/AttachedAndroidAllocDriver.js +22 -17
  51. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +97 -38
  52. package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +32 -45
  53. package/src/devices/allocation/drivers/android/emulator/FreeEmulatorFinder.js +1 -1
  54. package/src/devices/allocation/drivers/android/emulator/FreePortFinder.js +37 -0
  55. package/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js +3 -3
  56. package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +104 -32
  57. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceLauncher.js +40 -31
  58. package/src/devices/allocation/drivers/android/genycloud/GenyRegistry.js +121 -0
  59. package/src/devices/allocation/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +24 -0
  60. package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyRecipesService.js +1 -1
  61. package/src/devices/allocation/drivers/android/genycloud/services/dto/GenyInstance.js +83 -0
  62. package/src/devices/allocation/drivers/android/genycloud/services/dto/GenyRecipe.js +25 -0
  63. package/src/devices/allocation/drivers/ios/SimulatorAllocDriver.js +94 -51
  64. package/src/devices/allocation/drivers/ios/SimulatorLauncher.js +11 -7
  65. package/src/devices/allocation/drivers/ios/SimulatorQuery.js +24 -0
  66. package/src/devices/allocation/factories/android.js +29 -35
  67. package/src/devices/allocation/factories/ios.js +7 -5
  68. package/src/devices/common/drivers/DeviceCookie.d.ts +12 -0
  69. package/src/devices/common/drivers/android/cookies.d.ts +11 -0
  70. package/src/devices/common/drivers/android/emulator/exec/EmulatorExec.js +17 -5
  71. package/src/devices/common/drivers/android/exec/ADB.js +1 -0
  72. package/src/devices/common/drivers/ios/cookies.d.ts +9 -0
  73. package/src/devices/cookies/index.js +0 -6
  74. package/src/devices/runtime/drivers/android/genycloud/GenyCloudDriver.js +7 -6
  75. package/src/devices/runtime/factories/android.js +3 -11
  76. package/src/devices/runtime/factories/ios.js +3 -2
  77. package/src/{servicelocator → devices/servicelocator}/android/emulatorServiceLocator.js +1 -1
  78. package/src/devices/servicelocator/android/genycloudServiceLocator.js +17 -0
  79. package/src/devices/servicelocator/android/index.js +23 -0
  80. package/src/{validation → devices/validation}/EnvironmentValidatorBase.js +1 -0
  81. package/src/{validation → devices/validation}/android/GenycloudEnvValidator.js +2 -2
  82. package/src/{validation → devices/validation}/factories/index.js +1 -1
  83. package/src/{validation → devices/validation}/ios/IosSimulatorEnvValidator.js +2 -2
  84. package/src/environmentFactory.js +1 -11
  85. package/src/ipc/IPCClient.js +22 -1
  86. package/src/ipc/IPCServer.js +40 -1
  87. package/src/ipc/SessionState.js +1 -0
  88. package/src/logger/DetoxLogger.js +2 -2
  89. package/src/realms/DetoxContext.js +8 -0
  90. package/src/realms/DetoxInternalsFacade.js +1 -0
  91. package/src/realms/DetoxPrimaryContext.js +48 -42
  92. package/src/realms/DetoxSecondaryContext.js +27 -0
  93. package/src/realms/symbols.js +6 -0
  94. package/src/utils/PIDService.js +27 -0
  95. package/src/utils/environment.js +8 -15
  96. package/src/utils/errorUtils.js +3 -3
  97. package/tsconfig.json +5 -3
  98. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-javadoc.jar.md5 +0 -1
  99. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-javadoc.jar.sha1 +0 -1
  100. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-javadoc.jar.sha256 +0 -1
  101. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-javadoc.jar.sha512 +0 -1
  102. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-sources.jar.md5 +0 -1
  103. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-sources.jar.sha1 +0 -1
  104. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-sources.jar.sha256 +0 -1
  105. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-sources.jar.sha512 +0 -1
  106. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.aar +0 -0
  107. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.aar.md5 +0 -1
  108. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.aar.sha1 +0 -1
  109. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.aar.sha256 +0 -1
  110. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.aar.sha512 +0 -1
  111. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.pom.md5 +0 -1
  112. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.pom.sha1 +0 -1
  113. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.pom.sha256 +0 -1
  114. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.pom.sha512 +0 -1
  115. package/src/devices/DeviceRegistry.js +0 -176
  116. package/src/devices/allocation/drivers/AllocationDriverBase.js +0 -30
  117. package/src/devices/allocation/drivers/android/attached/AttachedAndroidLauncher.js +0 -13
  118. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocationHelper.js +0 -72
  119. package/src/devices/allocation/drivers/android/genycloud/GenyDeviceRegistryFactory.js +0 -16
  120. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceAllocationHelper.js +0 -65
  121. package/src/devices/common/drivers/DeviceAllocationHelper.js +0 -20
  122. package/src/devices/common/drivers/DeviceLauncher.js +0 -19
  123. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +0 -25
  124. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLookupService.js +0 -38
  125. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceNaming.js +0 -14
  126. package/src/devices/common/drivers/android/genycloud/services/dto/GenyInstance.js +0 -66
  127. package/src/devices/common/drivers/android/genycloud/services/dto/GenyRecipe.js +0 -13
  128. package/src/devices/cookies/AndroidDeviceCookie.js +0 -13
  129. package/src/devices/cookies/AndroidEmulatorCookie.js +0 -6
  130. package/src/devices/cookies/AttachedAndroidDeviceCookie.js +0 -12
  131. package/src/devices/cookies/DeviceCookie.js +0 -4
  132. package/src/devices/cookies/GenycloudEmulatorCookie.js +0 -20
  133. package/src/devices/cookies/IosCookie.js +0 -6
  134. package/src/devices/cookies/IosSimulatorCookie.js +0 -10
  135. package/src/devices/lifecycle/GenyGlobalLifecycleHandler.js +0 -71
  136. package/src/devices/lifecycle/factories/GenyGlobalLifecycleHandlerFactory.js +0 -18
  137. package/src/servicelocator/android/genycloudServiceLocator.js +0 -21
  138. package/src/servicelocator/android/index.js +0 -25
  139. package/src/servicelocator/ios.js +0 -7
  140. /package/src/devices/{common → allocation}/drivers/android/genycloud/exec/GenyCloudExec.js +0 -0
  141. /package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyAuthService.js +0 -0
@@ -1,24 +1,142 @@
1
1
  const resolveFrom = require('resolve-from');
2
- /** @type {typeof import('@jest/reporters').VerboseReporter} */
2
+ /** @type {new (globalConfig: any) => import('@jest/reporters').VerboseReporter} */
3
3
  const JestVerboseReporter = require(resolveFrom(process.cwd(), '@jest/reporters')).VerboseReporter;
4
+ /** @type {new (globalConfig: any) => import('@jest/reporters').SummaryReporter} */
5
+ const SummaryReporter = require(resolveFrom(process.cwd(), '@jest/reporters')).SummaryReporter;
4
6
 
5
- const { config, reportTestResults } = require('../../../internals');
7
+ const { config, reportTestResults, unsafe_conductEarlyTeardown, cleanup } = require('../../../internals');
8
+ const Deferred = require('../../../src/utils/Deferred');
6
9
 
7
10
  class DetoxReporter extends JestVerboseReporter {
11
+ constructor(globalConfig) {
12
+ super(globalConfig);
13
+
14
+ /** @type {Deferred | null} */
15
+ this._lastRunComplete = null;
16
+ /** @type {Set<string>} */
17
+ this._pendingTestFiles = new Set();
18
+ /** @type {Promise<any> | null} */
19
+ this._runCompletePromise = null;
20
+ /** @type {import('@jest/reporters').SummaryReporter | null} */
21
+ this._summaryReporter = this._initSummaryReporter();
22
+ }
23
+
24
+ onRunStart(aggregatedResults, options) {
25
+ super.onRunStart(aggregatedResults, options);
26
+
27
+ if (this._summaryReporter) {
28
+ this._summaryReporter.onRunStart(aggregatedResults, options);
29
+ }
30
+ }
31
+
32
+ onTestFileStart(test) {
33
+ this._pendingTestFiles.add(test.path);
34
+
35
+ // @ts-ignore Precaution in case Jest migrates to the new signature
36
+ if (typeof super.onTestFileStart === 'function') {
37
+ // @ts-ignore
38
+ super.onTestFileStart(test);
39
+ } else {
40
+ super.onTestStart(test);
41
+ }
42
+ }
43
+
44
+ onTestFileResult(test, testResult, aggregatedResult) {
45
+ this._pendingTestFiles.delete(test.path);
46
+
47
+ // @ts-ignore Precaution in case Jest migrates to the new signature
48
+ if (typeof super.onTestFileResult === 'function') {
49
+ // @ts-ignore
50
+ super.onTestFileResult(test, testResult, aggregatedResult);
51
+ } else {
52
+ super.onTestResult(test, testResult, aggregatedResult);
53
+ }
54
+
55
+ if (this._lastRunComplete && this._pendingTestFiles.size === 0) {
56
+ this._lastRunComplete.resolve(aggregatedResult);
57
+ }
58
+ }
59
+
8
60
  /**
9
- * @param {import('@jest/test-result').AggregatedResult} results
61
+ * @param {Set<import('@jest/reporters').TestContext>} testContexts
62
+ * @param {import('@jest/reporters').AggregatedResult} aggregatedResult
63
+ * @returns {Promise<any> | null}
10
64
  */
11
- // @ts-ignore
12
- async onRunComplete(_contexts, results) {
13
- // @ts-ignore
14
- await super.onRunComplete(_contexts, results);
65
+ // @ts-ignore We need to use the complete signature, not the one from the base class
66
+ onRunComplete(testContexts, aggregatedResult) {
67
+ if (!this._runCompletePromise) {
68
+ // Both `_lastRunComplete` and `_runCompletePromise` are used to prevent
69
+ // a bug in Jest, where `onRunComplete` is called multiple times when
70
+ // Jest runs with `--bail` and multiple workers, and `onRunComplete`
71
+ // is implemented as an asynchronous long-running operation.
72
+ this._lastRunComplete = this._pendingTestFiles.size === 0
73
+ ? Deferred.resolved(aggregatedResult)
74
+ : new Deferred();
15
75
 
16
- await reportTestResults(results.testResults.map(r => ({
76
+ this._runCompletePromise = this._onRunComplete(testContexts, aggregatedResult).catch(err => {
77
+ console.error(err);
78
+ throw err;
79
+ });
80
+ }
81
+
82
+ return this._runCompletePromise;
83
+ }
84
+
85
+ /**
86
+ * @param {Set<import('@jest/reporters').TestContext>} testContexts
87
+ * @param {import('@jest/reporters').AggregatedResult} aggregatedResult
88
+ * @returns {Promise<any> | null}
89
+ */
90
+ async _onRunComplete(testContexts, { numFailedTests }) {
91
+ const bail = this._globalConfig.bail;
92
+ const earlyTeardown = bail > 0 && numFailedTests >= bail;
93
+ if (earlyTeardown) {
94
+ await unsafe_conductEarlyTeardown();
95
+ }
96
+
97
+ const aggregatedResult = await this._lastRunComplete.promise;
98
+ const lostTests = aggregatedResult.numTotalTestSuites - aggregatedResult.testResults.length;
99
+
100
+ // @ts-expect-error TS2554
101
+ super.onRunComplete(testContexts, aggregatedResult);
102
+
103
+ if (earlyTeardown && lostTests > 0 && config.testRunner.retries > 0) {
104
+ console.warn(
105
+ 'Jest aborted the test execution before all scheduled test files have been reported.\n' +
106
+ 'This brings us to a dilemma: retry the whole test run, or retry only known failed test files.\n' +
107
+ 'Both options are bad in their own way, this is why we have decided to not support this edge case.\n' +
108
+ 'If you want to retry the whole test run, please disable Jest\'s --bail option.'
109
+ );
110
+ }
111
+
112
+ await reportTestResults(aggregatedResult.testResults.map(r => ({
17
113
  success: !r.failureMessage,
18
114
  testFilePath: r.testFilePath,
19
115
  testExecError: r.testExecError,
20
- isPermanentFailure: this._isPermanentFailure(r),
116
+ isPermanentFailure: lostTests > 0 || this._isPermanentFailure(r),
21
117
  })));
118
+
119
+ if (this._summaryReporter) {
120
+ this._summaryReporter.onRunComplete(testContexts, aggregatedResult);
121
+ }
122
+
123
+ if (earlyTeardown) {
124
+ await cleanup();
125
+ }
126
+ }
127
+
128
+ /**
129
+ * @returns {import('@jest/reporters').SummaryReporter | null}
130
+ * @private
131
+ */
132
+ _initSummaryReporter() {
133
+ /** @type {(config: import('@jest/types').Config.ReporterConfig) => boolean} */
134
+ const isSummaryReporter = (config) => config[0] === 'summary';
135
+ if (this._globalConfig.reporters.some(isSummaryReporter)) {
136
+ return null;
137
+ }
138
+
139
+ return new SummaryReporter(this._globalConfig);
22
140
  }
23
141
 
24
142
  /**
@@ -70,7 +70,12 @@ class DetoxCircusEnvironment extends NodeEnvironment {
70
70
  await this.initDetox();
71
71
  }
72
72
 
73
+ // @ts-expect-error TS2425
73
74
  async handleTestEvent(event, state) {
75
+ if (detox.session.unsafe_earlyTeardown) {
76
+ throw new Error('Detox halted test execution due to an early teardown request');
77
+ }
78
+
74
79
  this._timer.schedule(state.testTimeout != null ? state.testTimeout : this.setupTimeout);
75
80
 
76
81
  if (SYNC_CIRCUS_EVENTS.has(event.name)) {
@@ -33,7 +33,6 @@ class DetoxWorker {
33
33
  onError: this._onEmitError.bind(this),
34
34
  });
35
35
 
36
-
37
36
  /** @type {DetoxInternals.RuntimeConfig['apps']} */
38
37
  this._appsConfig = null;
39
38
  /** @type {DetoxInternals.RuntimeConfig['artifacts']} */
@@ -60,7 +59,6 @@ class DetoxWorker {
60
59
  /** @type {Detox.WebFacade} */
61
60
  this.web = null;
62
61
 
63
- this._deviceAllocator = null;
64
62
  this._deviceCookie = null;
65
63
 
66
64
  this.trace = this._context.trace;
@@ -115,14 +113,13 @@ class DetoxWorker {
115
113
  const {
116
114
  // @ts-ignore
117
115
  envValidatorFactory,
118
- deviceAllocatorFactory,
119
116
  // @ts-ignore
120
117
  artifactsManagerFactory,
121
118
  // @ts-ignore
122
119
  matchersFactory,
123
120
  // @ts-ignore
124
121
  runtimeDeviceFactory,
125
- } = environmentFactory.createFactories(this._deviceConfig);
122
+ } = environmentFactory.createFactories(deviceConfig);
126
123
 
127
124
  const envValidator = envValidatorFactory.createValidator();
128
125
  yield envValidator.validate();
@@ -135,10 +132,7 @@ class DetoxWorker {
135
132
  };
136
133
 
137
134
  this._artifactsManager = artifactsManagerFactory.createArtifactsManager(this._artifactsConfig, commonDeps);
138
- this._deviceAllocator = deviceAllocatorFactory.createDeviceAllocator(commonDeps);
139
- this._deviceCookie = yield this._deviceAllocator.allocate(this._deviceConfig);
140
-
141
- yield this._deviceAllocator.postAllocate(this._deviceCookie);
135
+ this._deviceCookie = yield this._context[symbols.allocateDevice]();
142
136
 
143
137
  this.device = runtimeDeviceFactory.createRuntimeDevice(
144
138
  this._deviceCookie,
@@ -157,6 +151,8 @@ class DetoxWorker {
157
151
  });
158
152
  Object.assign(this, matchers);
159
153
 
154
+ yield this._eventEmitter.emit('bootDevice', { deviceId: this.device.id });
155
+
160
156
  if (behaviorConfig.init.exposeGlobals) {
161
157
  const injectedGlobals = {
162
158
  ...matchers,
@@ -207,11 +203,9 @@ class DetoxWorker {
207
203
  }
208
204
 
209
205
  if (this._deviceCookie) {
210
- const shutdown = this._behaviorConfig ? this._behaviorConfig.cleanup.shutdownDevice : false;
211
- await this._deviceAllocator.free(this._deviceCookie, { shutdown });
206
+ await this._context[symbols.deallocateDevice](this._deviceCookie);
212
207
  }
213
208
 
214
- this._deviceAllocator = null;
215
209
  this._deviceCookie = null;
216
210
  this.device = null;
217
211
  }
@@ -4,7 +4,7 @@ class ArtifactPluginsProvider {
4
4
 
5
5
  class AndroidArtifactPluginsProvider extends ArtifactPluginsProvider {
6
6
  declareArtifactPlugins({ client }) {
7
- const serviceLocator = require('../../servicelocator/android');
7
+ const serviceLocator = require('../../devices/servicelocator/android');
8
8
  const adb = serviceLocator.adb;
9
9
  const devicePathBuilder = serviceLocator.devicePathBuilder;
10
10
 
@@ -34,8 +34,8 @@ class IosArtifactPluginsProvider extends ArtifactPluginsProvider {
34
34
 
35
35
  class IosSimulatorArtifactPluginsProvider extends IosArtifactPluginsProvider {
36
36
  declareArtifactPlugins({ client }) {
37
- const serviceLocator = require('../../servicelocator/ios');
38
- const appleSimUtils = serviceLocator.appleSimUtils;
37
+ const AppleSimUtils = require('../../devices/common/drivers/ios/tools/AppleSimUtils');
38
+ const appleSimUtils = new AppleSimUtils();
39
39
 
40
40
  const SimulatorInstrumentsPlugin = require('../instruments/ios/SimulatorInstrumentsPlugin');
41
41
  const SimulatorLogPlugin = require('../log/ios/SimulatorLogPlugin');
@@ -22,23 +22,6 @@ class SimulatorScreenshotPlugin extends ScreenshotArtifactPlugin {
22
22
  }
23
23
  }
24
24
 
25
- async onBootDevice(event) {
26
- await super.onBootDevice(event);
27
-
28
- if (this.enabled && event.coldBoot) {
29
- await this.appleSimUtils.takeScreenshot(event.deviceId, '/dev/null').catch(() => {
30
- log.debug({}, `
31
- NOTE: For an unknown yet reason, taking the first screenshot is apt
32
- to fail when booting iOS Simulator in a hidden window mode (or on CI).
33
- Detox applies a workaround by taking a dummy screenshot to ensure
34
- that the future ones are going to work fine. This screenshot is not
35
- saved anywhere, and the error above is suppressed for all log levels
36
- except for "debug" and "trace."
37
- `.trim());
38
- });
39
- }
40
- }
41
-
42
25
  async onBeforeUninstallApp(event) {
43
26
  await this.api.requestIdleCallback(async () => {
44
27
  const snapshots = [
@@ -32,6 +32,7 @@ function composeLoggerConfig(opts) {
32
32
  if (!config) return acc;
33
33
  const { options } = config;
34
34
  return _.merge(acc, {
35
+ // @ts-ignore-line
35
36
  options: typeof options === 'function' ? options(acc) : options
36
37
  });
37
38
  },
@@ -1,43 +1,89 @@
1
- // @ts-nocheck
2
- const log = require('../../utils/logger').child({ cat: 'device' });
1
+ /**
2
+ * @typedef {import('./drivers/AllocationDriverBase').AllocationDriverBase} AllocationDriverBase
3
+ * @typedef {import('./drivers/AllocationDriverBase').DeallocOptions} DeallocOptions
4
+ * @typedef {import('../common/drivers/DeviceCookie').DeviceCookie} DeviceCookie
5
+ */
6
+
7
+ const log = require('../../utils/logger').child({ cat: 'device,device-allocation' });
3
8
  const traceMethods = require('../../utils/traceMethods');
4
9
 
5
10
  class DeviceAllocator {
6
11
  /**
7
- * @param allocationDriver { AllocationDriverBase }
12
+ * @param {AllocationDriverBase} allocationDriver
8
13
  */
9
14
  constructor(allocationDriver) {
10
15
  this._driver = allocationDriver;
11
- traceMethods(log, this, ['allocate', 'postAllocate', 'free']);
16
+ this._counter = 0;
17
+ this._ids = new Map();
18
+ traceMethods(log, this, ['init', 'cleanup', 'emergencyCleanup']);
12
19
  }
13
20
 
14
21
  /**
15
- * @param deviceConfig { Object }
16
- * @return {Promise<DeviceCookie>}
22
+ * @returns {Promise<void>}
17
23
  */
18
- allocate(deviceConfig) {
19
- return this._driver.allocate(deviceConfig);
24
+ async init() {
25
+ if (typeof this._driver.init === 'function') {
26
+ await this._driver.init();
27
+ }
20
28
  }
21
29
 
22
30
  /**
23
- * @param {DeviceCookie} deviceCookie
24
- * @return {Promise<unknown>}
31
+ * @param {Detox.DetoxDeviceConfig} deviceConfig
32
+ * @returns {Promise<DeviceCookie>}
25
33
  */
26
- postAllocate(deviceCookie) {
27
- if (typeof this._driver.postAllocate !== 'function') {
28
- return Promise.resolve();
29
- }
34
+ async allocate(deviceConfig) {
35
+ const tid = this._counter++;
36
+ return await log.trace.complete({ data: deviceConfig, id: tid }, 'allocate', async () => {
37
+ const cookie = await this._driver.allocate(deviceConfig);
38
+ log.debug({ data: cookie }, `settled on ${cookie.name || cookie.id}`);
39
+ this._ids.set(cookie.id, tid);
40
+ return cookie;
41
+ });
42
+ }
30
43
 
31
- return this._driver.postAllocate(deviceCookie);
44
+ /**
45
+ * @param {DeviceCookie} cookie
46
+ * @returns {Promise<DeviceCookie>}
47
+ */
48
+ async postAllocate(cookie) {
49
+ const tid = this._ids.get(cookie.id);
50
+ return await log.trace.complete({ data: cookie, id: tid }, `post-allocate: ${cookie.id}`, async () => {
51
+ const updatedCookie = typeof this._driver.postAllocate === 'function'
52
+ ? await this._driver.postAllocate(cookie)
53
+ : undefined;
54
+
55
+ return updatedCookie || cookie;
56
+ });
32
57
  }
33
58
 
34
59
  /**
35
- * @param cookie { DeviceCookie }
36
- * @param options { DeallocOptions }
37
- * @return {Promise<void>}
60
+ * @param {DeviceCookie} cookie
61
+ * @param {DeallocOptions} options
62
+ * @returns {Promise<void>}
38
63
  */
39
- free(cookie, options) {
40
- return this._driver.free(cookie, options);
64
+ async free(cookie, options = {}) {
65
+ const tid = this._ids.get(cookie.id);
66
+ await log.trace.complete({ data: options, id: tid }, `free: ${cookie.id}`, async () => {
67
+ await this._driver.free(cookie, options);
68
+ });
69
+ }
70
+
71
+ /**
72
+ * @returns {Promise<void>}
73
+ */
74
+ async cleanup() {
75
+ if (typeof this._driver.cleanup === 'function') {
76
+ await this._driver.cleanup();
77
+ }
78
+ }
79
+
80
+ /**
81
+ * @returns {void}
82
+ */
83
+ emergencyCleanup() {
84
+ if (typeof this._driver.emergencyCleanup === 'function') {
85
+ this._driver.emergencyCleanup();
86
+ }
41
87
  }
42
88
  }
43
89
 
@@ -0,0 +1,44 @@
1
+ class DeviceListReadonly {
2
+ constructor(devices = []) {
3
+ this._devices = new Map(devices.map(device => [device.id, device]));
4
+ }
5
+
6
+ concat(other) {
7
+ return new DeviceListReadonly([...this, ...other]);
8
+ }
9
+
10
+ getIds() {
11
+ return [...this._devices.keys()];
12
+ }
13
+
14
+ [Symbol.iterator]() {
15
+ return this._devices.values();
16
+ }
17
+
18
+ /**
19
+ * @param {string} deviceId
20
+ * @returns {boolean}
21
+ */
22
+ includes(deviceId) {
23
+ return this._devices.has(deviceId);
24
+ }
25
+ }
26
+
27
+ class DeviceList extends DeviceListReadonly {
28
+ filter(predicate) {
29
+ return new DeviceListReadonly([...this].filter(predicate));
30
+ }
31
+
32
+ add(deviceId, data) {
33
+ this._devices.set(deviceId, {
34
+ id: deviceId,
35
+ ...data,
36
+ });
37
+ }
38
+
39
+ delete(deviceId) {
40
+ this._devices.delete(deviceId);
41
+ }
42
+ }
43
+
44
+ module.exports = DeviceList;
@@ -0,0 +1,189 @@
1
+ const ExclusiveLockfile = require('../../utils/ExclusiveLockfile');
2
+ const PIDService = require('../../utils/PIDService');
3
+ const { getDeviceRegistryPath } = require('../../utils/environment');
4
+ const safeAsync = require('../../utils/safeAsync');
5
+
6
+ const DeviceList = require('./DeviceList');
7
+
8
+ const readOptions = {
9
+ encoding: 'utf8',
10
+ };
11
+
12
+ class DeviceRegistry {
13
+ constructor({
14
+ lockfilePath = getDeviceRegistryPath(),
15
+ sessionId = '',
16
+ pidService = new PIDService(),
17
+ } = {}) {
18
+ /***
19
+ * @private
20
+ * @type {string}
21
+ */
22
+ this._lockfilePath = lockfilePath;
23
+ /***
24
+ * @private
25
+ * @type {string}
26
+ */
27
+ this._sessionId = sessionId;
28
+ /***
29
+ * @private
30
+ */
31
+ this._pidService = pidService;
32
+ /***
33
+ * @protected
34
+ * @type {ExclusiveLockfile}
35
+ */
36
+ this._lockfile = new ExclusiveLockfile(this._lockfilePath, {
37
+ getInitialState: this._getInitialLockFileState.bind(this),
38
+ readOptions,
39
+ });
40
+ }
41
+
42
+ get lockFilePath() {
43
+ return this._lockfilePath;
44
+ }
45
+
46
+ /**
47
+ * Safety method to ensure that there are no remains of previously crashed Detox sessions.
48
+ */
49
+ async reset() {
50
+ await this._lockfile.exclusively(() => {
51
+ const empty = this._getInitialLockFileState();
52
+ this._lockfile.write(empty);
53
+ });
54
+ }
55
+
56
+ /***
57
+ * @param {string|Function} getDeviceId
58
+ * @returns {Promise<string>}
59
+ */
60
+ async registerDevice(getDeviceId) {
61
+ return this._lockfile.exclusively(async () => {
62
+ const deviceId = await safeAsync(getDeviceId);
63
+ if (deviceId) {
64
+ this._upsertDevice(deviceId, {
65
+ busy: true,
66
+ sessionId: this._sessionId,
67
+ pid: this._pidService.getPid(),
68
+ });
69
+ }
70
+ return deviceId;
71
+ });
72
+ }
73
+
74
+ /***
75
+ * @param {string|Function} getDeviceId
76
+ * @returns {Promise<string>}
77
+ */
78
+ async releaseDevice(getDeviceId) {
79
+ return this._lockfile.exclusively(async () => {
80
+ const deviceId = await safeAsync(getDeviceId);
81
+ if (deviceId) {
82
+ this._upsertDevice(deviceId, {
83
+ busy: false,
84
+ sessionId: this._sessionId,
85
+ pid: this._pidService.getPid(),
86
+ });
87
+ }
88
+ return deviceId;
89
+ });
90
+ }
91
+
92
+ /***
93
+ * @param {string|Function} getDeviceId
94
+ * @returns {Promise<void>}
95
+ */
96
+ async unregisterDevice(getDeviceId) {
97
+ await this._lockfile.exclusively(async () => {
98
+ const deviceId = await safeAsync(getDeviceId);
99
+ if (deviceId) {
100
+ this._deleteDevice(deviceId);
101
+ }
102
+ });
103
+ }
104
+
105
+ async unregisterSessionDevices() {
106
+ await this._lockfile.exclusively(async () => {
107
+ const allDevices = this._getRegisteredDevices();
108
+ const sessionDevices = allDevices.filter(device => device.sessionId === this._sessionId);
109
+ for (const id of sessionDevices.getIds()) {
110
+ allDevices.delete(id);
111
+ }
112
+ this._lockfile.write([...allDevices]);
113
+ });
114
+ }
115
+
116
+ async unregisterZombieDevices() {
117
+ await this._lockfile.exclusively(async () => {
118
+ const allDevices = this._getRegisteredDevices();
119
+ const zombieDevices = allDevices.filter(device => {
120
+ return device.sessionId !== this._sessionId && !this._pidService.isAlive(device.pid);
121
+ });
122
+
123
+ for (const id of zombieDevices.getIds()) {
124
+ allDevices.delete(id);
125
+ }
126
+ this._lockfile.write([...allDevices]);
127
+ });
128
+ }
129
+
130
+ /**
131
+ * @returns {Promise<DeviceList>}
132
+ */
133
+ async readSessionDevices() {
134
+ let devices;
135
+ await this._lockfile.exclusively(() => {
136
+ devices = this._getSessionDevicesSync();
137
+ });
138
+ return devices;
139
+ }
140
+
141
+ getTakenDevicesSync() {
142
+ const allDevices = this._getRegisteredDevices();
143
+ const busyDevices = allDevices.filter(device => device.busy);
144
+ const externalDevices = allDevices.filter(device => device.sessionId !== this._sessionId);
145
+ return busyDevices.concat(externalDevices);
146
+ }
147
+
148
+ /***
149
+ * @private
150
+ */
151
+ _getInitialLockFileState() {
152
+ return [];
153
+ }
154
+
155
+ /**
156
+ * @private
157
+ */
158
+ _upsertDevice(deviceId, data) {
159
+ const devices = this._getRegisteredDevices();
160
+ devices.add(deviceId, data);
161
+ this._lockfile.write([...devices]);
162
+ }
163
+
164
+ /**
165
+ * @private
166
+ */
167
+ _deleteDevice(deviceId) {
168
+ const devices = this._getRegisteredDevices();
169
+ devices.delete(deviceId);
170
+ this._lockfile.write([...devices]);
171
+ }
172
+
173
+ /***
174
+ * @private
175
+ * @returns {DeviceList}
176
+ */
177
+ _getRegisteredDevices() {
178
+ const devices = this._lockfile.read();
179
+ return new DeviceList(devices);
180
+ }
181
+
182
+ _getSessionDevicesSync() {
183
+ const devices = this._getRegisteredDevices();
184
+ const sessionDevices = devices.filter(device => device.sessionId === this._sessionId);
185
+ return sessionDevices;
186
+ }
187
+ }
188
+
189
+ module.exports = DeviceRegistry;
@@ -0,0 +1,15 @@
1
+ /* eslint-disable import/no-unresolved,node/no-missing-import,node/no-unsupported-features/es-syntax */
2
+ import { DeviceCookie } from '../../common/drivers/DeviceCookie';
3
+
4
+ export interface DeallocOptions {
5
+ shutdown?: boolean;
6
+ }
7
+
8
+ export interface AllocationDriverBase {
9
+ init?(): Promise<void>;
10
+ allocate(deviceConfig: any): Promise<DeviceCookie>;
11
+ postAllocate?(deviceCookie: DeviceCookie): Promise<DeviceCookie | void>;
12
+ free(cookie: DeviceCookie, options: DeallocOptions): Promise<void>;
13
+ cleanup?(): Promise<void>;
14
+ emergencyCleanup?(): void;
15
+ }