detox 20.0.0-breaking.new-global-lifecycle.0 → 20.0.3-breaking.new-global-lifecycle.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. package/Detox-android/com/wix/detox/{20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-javadoc.jar → 20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-javadoc.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-sources.jar → 20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.aar +0 -0
  12. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.aar.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.aar.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.aar.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.aar.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/{20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.pom → 20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.pom} +2 -2
  17. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.pom.md5 +1 -0
  18. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.pom.sha1 +1 -0
  19. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-breaking.new-global-lifecycle.0.pom.sha256 +1 -0
  20. package/Detox-android/com/wix/detox/20.0.3-breaking.new-global-lifecycle.0/detox-20.0.3-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/android/build.gradle +2 -2
  29. package/android/detox/build.gradle +2 -2
  30. package/android/detox/publish-pom.gradle +5 -1
  31. package/android/detox/publishing.gradle +9 -7
  32. package/android/detox/src/full/java/com/wix/detox/adapters/server/WebSocketClient.java +3 -1
  33. package/android/detox/src/full/java/com/wix/detox/espresso/DetoxAction.java +1 -3
  34. package/android/detox/src/full/java/com/wix/detox/espresso/UiAutomatorHelper.java +1 -1
  35. package/android/detox/src/full/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +22 -0
  36. package/android/detox/src/{main → full}/java/com/wix/detox/espresso/action/GetAttributesAction.kt +13 -1
  37. package/android/detox/src/full/java/com/wix/detox/espresso/common/SliderHelper.kt +75 -0
  38. package/android/detox/src/full/java/com/wix/detox/espresso/matcher/ViewMatchers.kt +16 -23
  39. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeLoadingMonitor.kt +54 -8
  40. package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/RN66Workaround.kt +13 -4
  41. package/android/detox/src/main/java/com/wix/detox/common/DetoxErrors.java +4 -1
  42. package/android/detox/src/main/java/com/wix/detox/espresso/UiControllerSpy.kt +2 -1
  43. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/ReflectUtils.kt +10 -0
  44. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/utils/UiControllerUtils.kt +1 -1
  45. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/GetAttributesActionTest.kt +15 -3
  46. package/android/detox/src/testFull/java/com/wix/detox/espresso/common/SliderHelperTest.kt +39 -0
  47. package/android/gradle/wrapper/gradle-wrapper.properties +2 -1
  48. package/android/gradlew +181 -107
  49. package/index.d.ts +5 -0
  50. package/internals.d.ts +25 -18
  51. package/local-cli/build.js +1 -1
  52. package/local-cli/build.test.js +14 -14
  53. package/local-cli/test.js +3 -3
  54. package/local-cli/test.test.js +20 -14
  55. package/local-cli/testCommand/TestRunnerCommand.js +6 -6
  56. package/package.json +4 -4
  57. package/runners/jest/testEnvironment/index.js +1 -1
  58. package/runners/jest/testEnvironment/listeners/DetoxCoreListener.js +5 -9
  59. package/runners/jest/testEnvironment/listeners/SpecReporter.js +1 -1
  60. package/runners/jest/testEnvironment/listeners/WorkerAssignReporter.js +1 -1
  61. package/src/DetoxWorker.js +12 -1
  62. package/src/artifacts/log/android/ADBLogcatRecording.js +11 -28
  63. package/src/configuration/composeAppsConfig.js +1 -1
  64. package/src/configuration/composeRunnerConfig.js +49 -1
  65. package/src/configuration/index.js +9 -8
  66. package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +1 -0
  67. package/src/devices/common/drivers/android/exec/ADB.js +5 -0
  68. package/src/errors/DetoxConfigErrorComposer.js +5 -3
  69. package/src/ipc/IPCClient.js +3 -2
  70. package/src/ipc/IPCServer.js +16 -3
  71. package/src/ipc/state.js +24 -2
  72. package/src/realms/DetoxContext.js +3 -3
  73. package/src/realms/DetoxPrimaryContext.js +13 -7
  74. package/src/realms/DetoxSecondaryContext.js +4 -3
  75. package/src/utils/ChromeTracingExporter.js +6 -5
  76. package/src/utils/environment.js +30 -15
  77. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-javadoc.jar.md5 +0 -1
  78. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-javadoc.jar.sha1 +0 -1
  79. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-javadoc.jar.sha256 +0 -1
  80. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-javadoc.jar.sha512 +0 -1
  81. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-sources.jar.md5 +0 -1
  82. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-sources.jar.sha1 +0 -1
  83. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-sources.jar.sha256 +0 -1
  84. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0-sources.jar.sha512 +0 -1
  85. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.aar +0 -0
  86. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.aar.md5 +0 -1
  87. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.aar.sha1 +0 -1
  88. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.aar.sha256 +0 -1
  89. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.aar.sha512 +0 -1
  90. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.pom.md5 +0 -1
  91. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.pom.sha1 +0 -1
  92. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.pom.sha256 +0 -1
  93. package/Detox-android/com/wix/detox/20.0.0-breaking.new-global-lifecycle.0/detox-20.0.0-breaking.new-global-lifecycle.0.pom.sha512 +0 -1
  94. package/android/detox/src/main/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +0 -36
  95. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/AdjustSliderToPositionActionTest.kt +0 -59
@@ -116,7 +116,7 @@ describe('CLI', () => {
116
116
  });
117
117
  });
118
118
 
119
- test('should use runnerConfig.specs as default specs', async () => {
119
+ test('should use testRunner.args._ as default specs', async () => {
120
120
  detoxConfig.testRunner.args._ = ['e2e/sanity'];
121
121
  await run();
122
122
  expect(_.last(cliCall().argv)).toEqual('e2e/sanity');
@@ -140,26 +140,32 @@ describe('CLI', () => {
140
140
 
141
141
  test.each([['-R'], ['--retries']])('%s <value> should execute unsuccessful run N extra times', async (__retries) => {
142
142
  const context = require('../internals');
143
- context.session.failedTestFiles = ['e2e/failing1.test.js', 'e2e/failing2.test.js'];
144
- context.session.failedTestFiles.splice = jest.fn(() => {
145
- context.session.failedTestFiles = ['e2e/failing2.test.js'];
146
- });
143
+ context.session.testFilesToRetry = ['e2e/failing1.test.js', 'e2e/failing2.test.js'];
144
+ context.session.testFilesToRetry.splice = jest.fn()
145
+ .mockImplementationOnce(() => ['e2e/failing1.test.js', 'e2e/failing2.test.js'])
146
+ .mockImplementationOnce(() => ['e2e/failing2.test.js']);
147
147
 
148
148
  mockExitCode(1);
149
149
 
150
150
  await run(__retries, 2).catch(_.noop);
151
151
 
152
- expect(cliCall(0).env).not.toHaveProperty('DETOX_RERUN_INDEX');
153
152
  expect(cliCall(0).argv).toEqual([expect.stringMatching(/executable$/), '--config', 'e2e/config.json']);
154
- expect(cliCall(0).fullCommand).not.toMatch(/DETOX_RERUN_INDEX/);
155
-
156
- expect(cliCall(1).env.DETOX_RERUN_INDEX).toBe('1');
157
153
  expect(cliCall(1).argv).toEqual([expect.stringMatching(/executable$/), '--config', 'e2e/config.json', 'e2e/failing1.test.js', 'e2e/failing2.test.js']);
158
- expect(cliCall(1).fullCommand).not.toMatch(/DETOX_RERUN_INDEX/);
159
-
160
154
  expect(cliCall(2).argv).toEqual([expect.stringMatching(/executable$/), '--config', 'e2e/config.json', 'e2e/failing2.test.js']);
161
- expect(cliCall(2).env.DETOX_RERUN_INDEX).toBe('2');
162
- expect(cliCall(2).fullCommand).not.toMatch(/DETOX_RERUN_INDEX/);
155
+ });
156
+
157
+ test.each([['-R'], ['--retries']])('%s <value> should bail if has permanently failed tests', async (__retries) => {
158
+ const context = require('../internals');
159
+ context.session.failedTestFiles = ['e2e/failing1.test.js'];
160
+ context.session.testFilesToRetry = ['e2e/failing2.test.js'];
161
+
162
+ mockExitCode(1);
163
+
164
+ await run(__retries, 2).catch(_.noop);
165
+
166
+ expect(cliCall(0).env).not.toHaveProperty('DETOX_RERUN_INDEX');
167
+ expect(cliCall(0).argv).toEqual([expect.stringMatching(/executable$/), '--config', 'e2e/config.json']);
168
+ expect(cliCall(1)).toBe(null);
163
169
  });
164
170
 
165
171
  test.each([['-R'], ['--retries']])('%s <value> should not restart test runner if there are no failing tests paths', async (__retries) => {
@@ -172,7 +178,7 @@ describe('CLI', () => {
172
178
 
173
179
  test.each([['-R'], ['--retries']])('%s <value> should retain -- <...explicitPassthroughArgs>', async (__retries) => {
174
180
  const context = require('../internals');
175
- context.session.failedTestFiles = ['tests/failing.test.js'];
181
+ context.session.testFilesToRetry = ['tests/failing.test.js'];
176
182
 
177
183
  mockExitCode(1);
178
184
 
@@ -88,7 +88,7 @@ class TestRunnerCommand {
88
88
  try {
89
89
  if (launchError) {
90
90
  const list = this._argv._.map((file, index) => ` ${index + 1}. ${file}`).join('\n');
91
- detox.log.error(
91
+ detox.log.error({ event: 'RETRY_RUN' },
92
92
  `There were failing tests in the following files:\n${list}\n\n` +
93
93
  'Detox CLI is going to restart the test runner with those files...\n'
94
94
  );
@@ -99,14 +99,14 @@ class TestRunnerCommand {
99
99
  } catch (e) {
100
100
  launchError = e;
101
101
 
102
- const { failedTestFiles } = detox.session;
103
- if (_.isEmpty(failedTestFiles)) {
102
+ const { failedTestFiles, testFilesToRetry } = detox.session;
103
+ if (!_.isEmpty(failedTestFiles) || _.isEmpty(testFilesToRetry)) {
104
104
  throw e;
105
105
  }
106
106
 
107
- this._argv._ = failedTestFiles.slice();
108
- this._env.DETOX_RERUN_INDEX = 1 + (this._env.DETOX_RERUN_INDEX || 0);
109
- detox.session.failedTestFiles.splice(0, Infinity);
107
+ this._argv._ = testFilesToRetry.splice(0, Infinity);
108
+ // @ts-ignore
109
+ detox.session.testSessionIndex++; // it is always a primary context, so we can update it
110
110
  }
111
111
  } while (launchError && --runsLeft > 0);
112
112
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "detox",
3
3
  "description": "E2E tests and automation for mobile",
4
- "version": "20.0.0-breaking.new-global-lifecycle.0",
4
+ "version": "20.0.3-breaking.new-global-lifecycle.0",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -46,10 +46,10 @@
46
46
  "eslint-plugin-no-only-tests": "^2.6.0",
47
47
  "eslint-plugin-node": "^11.1.0",
48
48
  "eslint-plugin-unicorn": "^39.0.0",
49
- "jest": "^27.5.1",
49
+ "jest": "^28.0.0",
50
50
  "mockdate": "^2.0.1",
51
51
  "prettier": "1.7.0",
52
- "react-native": "0.67.2",
52
+ "react-native": "0.68.2",
53
53
  "react-native-codegen": "^0.0.8",
54
54
  "typescript": "^4.5.2",
55
55
  "wtfnode": "^0.9.1"
@@ -183,5 +183,5 @@
183
183
  }
184
184
  }
185
185
  },
186
- "gitHead": "e89c826fdbd09989dcb1cc82eb3cc7d821ffbf3e"
186
+ "gitHead": "1e9a8a54aa4c8d73cf106770522723d0f6f1f10e"
187
187
  }
@@ -46,7 +46,7 @@ class DetoxCircusEnvironment extends NodeEnvironment {
46
46
  /** @protected */
47
47
  this.testEventListeners = [];
48
48
  /** @protected */
49
- this.initTimeout = detox.config.runnerConfig.jest.initTimeout;
49
+ this.initTimeout = detox.config.testRunner.jest.initTimeout;
50
50
  }
51
51
 
52
52
  /** @override */
@@ -16,18 +16,13 @@ class DetoxCoreListener {
16
16
  }
17
17
 
18
18
  _getTestInvocations(test) {
19
- const { DETOX_RERUN_INDEX } = process.env;
20
-
21
- if (!isNaN(DETOX_RERUN_INDEX)) {
22
- return Number(DETOX_RERUN_INDEX) * this._testRunTimes + test.invocations;
23
- } else {
24
- return test.invocations;
25
- }
19
+ const { testSessionIndex } = detoxInternals.session;
20
+ return testSessionIndex * this._testRunTimes + test.invocations;
26
21
  }
27
22
 
28
23
  async setup() {
29
24
  // Workaround to override Jest's expect
30
- if (detoxInternals.config.behaviorConfig.init.exposeGlobals) {
25
+ if (detoxInternals.config.behavior.init.exposeGlobals) {
31
26
  this._env.global.expect = detox.expect;
32
27
  }
33
28
  }
@@ -103,7 +98,8 @@ class DetoxCoreListener {
103
98
 
104
99
  async run_finish(_event, state) {
105
100
  if (this._hasFailedTests(state.rootDescribeBlock)) {
106
- await detoxInternals.reportFailedTests([this._env.testPath]);
101
+ const handledByJestCircus = this._testRunTimes > 1 && !detoxInternals.config.testRunner.jest.retryAfterCircusRetries;
102
+ await detoxInternals.reportFailedTests([this._env.testPath], handledByJestCircus);
107
103
  }
108
104
  }
109
105
 
@@ -16,7 +16,7 @@ class SpecReporter {
16
16
  }
17
17
 
18
18
  get enabled() {
19
- const jestSection = config.runnerConfig.jest;
19
+ const jestSection = config.testRunner.jest;
20
20
  const reportSpecs = jestSection && jestSection.reportSpecs;
21
21
 
22
22
  return reportSpecs !== undefined ? reportSpecs : session.workersCount === 1;
@@ -12,7 +12,7 @@ class WorkerAssignReporter {
12
12
  }
13
13
 
14
14
  run_start() {
15
- if (config.runnerConfig.jest.reportWorkerAssign) {
15
+ if (config.testRunner.jest.reportWorkerAssign) {
16
16
  log.info({ event: 'WORKER_ASSIGN' }, `${this._formatTestName()} is assigned to ${this._formatDeviceName()}`);
17
17
  }
18
18
  }
@@ -56,7 +56,13 @@ class DetoxWorker {
56
56
  async init() {
57
57
  if (this._isCleaningUp) return;
58
58
 
59
- const { appsConfig, artifactsConfig, behaviorConfig, deviceConfig, sessionConfig } = this._config;
59
+ const {
60
+ apps: appsConfig,
61
+ artifacts: artifactsConfig,
62
+ behavior: behaviorConfig,
63
+ device: deviceConfig,
64
+ session: sessionConfig
65
+ } = this._config;
60
66
  this._appsConfig = appsConfig;
61
67
  this._artifactsConfig = artifactsConfig;
62
68
  this._behaviorConfig = behaviorConfig;
@@ -232,6 +238,11 @@ class DetoxWorker {
232
238
  if (this._isCleaningUp) return;
233
239
  await this.device.uninstallApp();
234
240
  if (this._isCleaningUp) return;
241
+ }
242
+
243
+ for (const appName of appNames) {
244
+ await this.device.selectApp(appName);
245
+ if (this._isCleaningUp) return;
235
246
  await this.device.installApp();
236
247
  if (this._isCleaningUp) return;
237
248
  }
@@ -1,6 +1,5 @@
1
1
  const DetoxRuntimeError = require('../../../errors/DetoxRuntimeError');
2
2
  const { interruptProcess } = require('../../../utils/childProcess');
3
- const log = require('../../../utils/logger').child({ __filename });
4
3
  const retry = require('../../../utils/retry');
5
4
  const sleep = require('../../../utils/sleep');
6
5
  const Artifact = require('../../templates/artifact/Artifact');
@@ -26,26 +25,18 @@ class ADBLogcatRecording extends Artifact {
26
25
  this._waitUntilLogFileIsCreated = null;
27
26
  }
28
27
 
29
- get hasRecordedFile() {
30
- return !!this._waitUntilLogFileIsCreated;
31
- }
32
-
33
28
  async doStart() {
34
29
  const pid = this.pid.get();
35
30
 
36
- if (pid > 0) {
37
- this.processPromise = this.adb.logcat(this.deviceId, {
38
- file: this.pathToLogOnDevice,
39
- time: this.since.get(),
40
- pid,
41
- });
31
+ this.processPromise = this.adb.logcat(this.deviceId, {
32
+ file: this.pathToLogOnDevice,
33
+ time: this.since.get(),
34
+ pid: pid > 0 ? pid : 0,
35
+ });
42
36
 
43
- this._waitUntilLogFileIsCreated = sleep(300).then(() => {
44
- return retry(() => this._assertLogIsCreated());
45
- });
46
- } else {
47
- log.debug('Ignoring a command to start recording, because PID of the app is missing');
48
- }
37
+ this._waitUntilLogFileIsCreated = sleep(300).then(() => {
38
+ return retry(() => this._assertLogIsCreated());
39
+ });
49
40
  }
50
41
 
51
42
  async doStop() {
@@ -61,20 +52,12 @@ class ADBLogcatRecording extends Artifact {
61
52
  }
62
53
 
63
54
  async doSave(artifactPath) {
64
- if (this.hasRecordedFile) {
65
- await this.adb.pull(this.deviceId, this.pathToLogOnDevice, artifactPath);
66
- await this.adb.rm(this.deviceId, this.pathToLogOnDevice);
67
- } else {
68
- log.debug(`Skipping saving artifact because the recording has not started: ${artifactPath}`);
69
- }
55
+ await this.adb.pull(this.deviceId, this.pathToLogOnDevice, artifactPath);
56
+ await this.adb.rm(this.deviceId, this.pathToLogOnDevice);
70
57
  }
71
58
 
72
59
  async doDiscard() {
73
- if (this.hasRecordedFile) {
74
- await this.adb.rm(this.deviceId, this.pathToLogOnDevice);
75
- } else {
76
- log.debug(`Skipping discarding artifact due to a not started recording: ${this.pathToLogOnDevice}`);
77
- }
60
+ await this.adb.rm(this.deviceId, this.pathToLogOnDevice);
78
61
  }
79
62
 
80
63
  async _assertLogIsCreated() {
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  const _ = require('lodash');
3
- const parse = require('yargs/yargs').Parser;
3
+ const parse = require('yargs-parser');
4
4
 
5
5
  const deviceAppTypes = require('./utils/deviceAppTypes');
6
6
 
@@ -2,6 +2,8 @@ const os = require('os');
2
2
 
3
3
  const _ = require('lodash');
4
4
 
5
+ const log = require('../utils/logger');
6
+
5
7
  /**
6
8
  * @param {object} opts
7
9
  * @param {Detox.DetoxConfig} opts.globalConfig
@@ -12,7 +14,7 @@ const _ = require('lodash');
12
14
  * @returns {Detox.DetoxTestRunnerConfig} opts.testRunnerArgv
13
15
  */
14
16
  function composeRunnerConfig(opts) {
15
- const globalConfig = opts.globalConfig.testRunner;
17
+ const globalConfig = adaptLegacyRunnerConfig(opts.globalConfig);
16
18
  if (globalConfig != null && typeof globalConfig !== 'object') {
17
19
  throw opts.errorComposer.invalidTestRunnerProperty(true);
18
20
  }
@@ -31,6 +33,7 @@ function composeRunnerConfig(opts) {
31
33
  inspectBrk: inspectBrkHookDefault,
32
34
  jest: {
33
35
  initTimeout: 300000,
36
+ retryAfterCircusRetries: false,
34
37
  reportSpecs: undefined,
35
38
  reportWorkerAssign: true,
36
39
  },
@@ -60,6 +63,51 @@ function composeRunnerConfig(opts) {
60
63
  return merged;
61
64
  }
62
65
 
66
+ function adaptLegacyRunnerConfig(globalConfig) {
67
+ let isLegacy = false;
68
+
69
+ const runnerConfigKey = 'runnerConfig' in globalConfig ? 'runnerConfig' : 'runner-config';
70
+ if (_.isString(globalConfig[runnerConfigKey])) {
71
+ isLegacy = true;
72
+ log.warn(`Detected a deprecated "${runnerConfigKey}" property (string).`);
73
+ }
74
+
75
+ const testRunnerKey = 'testRunner' in globalConfig ? 'testRunner' : 'test-runner';
76
+ if (_.isString(globalConfig[testRunnerKey])) {
77
+ isLegacy = true;
78
+ log.warn(`Detected a deprecated "${testRunnerKey}" property (string).`);
79
+ }
80
+
81
+ if (globalConfig.specs != null) {
82
+ isLegacy = true;
83
+ log.warn(`Detected a deprecated "specs" property.`);
84
+ }
85
+
86
+ if (!isLegacy) {
87
+ return globalConfig.testRunner;
88
+ }
89
+
90
+ log.warn(`Please migrate your Detox config according to the guide: [TODO: insert the migration guilde link]`);
91
+ const testRunner = globalConfig[testRunnerKey];
92
+ const runnerConfig = globalConfig[runnerConfigKey];
93
+ const specs = globalConfig.specs != null ? String(globalConfig.specs) : undefined;
94
+
95
+ const args = {};
96
+ if (_.isString(testRunner)) {
97
+ args.$0 = testRunner;
98
+ }
99
+
100
+ if (_.isString(runnerConfig)) {
101
+ args.config = runnerConfig;
102
+ }
103
+
104
+ if (specs) {
105
+ args._ = [specs];
106
+ }
107
+
108
+ return { args };
109
+ }
110
+
63
111
  function hasEmptyPositionalArgs(value, key) {
64
112
  return key === '_' ? _.isEmpty(value) : false;
65
113
  }
@@ -105,16 +105,17 @@ async function composeDetoxConfig({
105
105
  });
106
106
 
107
107
  const result = {
108
- appsConfig,
109
- artifactsConfig,
110
- behaviorConfig,
111
- cliConfig,
112
108
  configurationName,
113
- deviceConfig,
114
109
  errorComposer,
115
- loggerConfig,
116
- runnerConfig,
117
- sessionConfig,
110
+
111
+ apps: appsConfig,
112
+ artifacts: artifactsConfig,
113
+ behavior: behaviorConfig,
114
+ cli: cliConfig,
115
+ device: deviceConfig,
116
+ logger: loggerConfig,
117
+ testRunner: runnerConfig,
118
+ session: sessionConfig,
118
119
  };
119
120
 
120
121
  return result;
@@ -40,6 +40,7 @@ class GenyAllocDriver extends AllocationDriverBase {
40
40
  const { adbName } = instance;
41
41
 
42
42
  await this._adb.disableAndroidAnimations(adbName);
43
+ await this._adb.setWiFiToggle(adbName, true);
43
44
  await this._adb.apiLevel(adbName);
44
45
  return new GenycloudEmulatorCookie(instance);
45
46
  }
@@ -206,6 +206,11 @@ class ADB {
206
206
  await this.shell(deviceId, `settings put global transition_animation_scale 0`);
207
207
  }
208
208
 
209
+ async setWiFiToggle(deviceId, state) {
210
+ const value = (state === true ? 'enable' : 'disable');
211
+ await this.shell(deviceId, `svc wifi ${value}`);
212
+ }
213
+
209
214
  async screencap(deviceId, path) {
210
215
  await this.shell(deviceId, `screencap ${path}`);
211
216
  }
@@ -409,7 +409,7 @@ Please check your Detox config${this._atPath()}`,
409
409
  case 'gpuMode':
410
410
  return this._unsupportedPropertyByDeviceType('gpuMode', ['android.emulator'], deviceAlias);
411
411
  case 'headless':
412
- return this._unsupportedPropertyByDeviceType('headless', ['android.emulator'], deviceAlias);
412
+ return this._unsupportedPropertyByDeviceType('headless', ['ios.simulator', 'android.emulator'], deviceAlias);
413
413
  case 'readonly':
414
414
  return this._unsupportedPropertyByDeviceType('readonly', ['android.emulator'], deviceAlias);
415
415
  case 'utilBinaryPaths':
@@ -656,12 +656,14 @@ Examine your Detox config${this._atPath()}`,
656
656
  }
657
657
 
658
658
  invalidTestRunnerProperty(isGlobal) {
659
+ const testRunner = _.get(this.contents, ['testRunner']);
660
+
659
661
  return new DetoxConfigError({
660
- message: `testRunner should be an object, not a string`,
662
+ message: `testRunner should be an object, not a ${typeof testRunner}`,
661
663
  hint: `Check that in your Detox config${this._atPath()}`,
662
664
  inspectOptions: { depth: isGlobal ? 0 : 3 },
663
665
  debugInfo: isGlobal ? {
664
- testRunner: _.get(this.contents, ['testRunner']),
666
+ testRunner,
665
667
  ...this.contents,
666
668
  } : {
667
669
  ...this._focusOnConfiguration(c => _.pick(c, ['testRunner'])),
@@ -53,9 +53,10 @@ class IPCClient {
53
53
 
54
54
  /**
55
55
  * @param {string[]} testFilePaths
56
+ * @param {Boolean} permanent
56
57
  */
57
- async reportFailedTests(testFilePaths) {
58
- await this._emit('failedTests', { testFilePaths });
58
+ async reportFailedTests(testFilePaths, permanent) {
59
+ await this._emit('failedTests', { testFilePaths, permanent });
59
60
  }
60
61
 
61
62
  async _connectToServer() {
@@ -55,7 +55,11 @@ class IPCServer {
55
55
  this._sessionState.logFiles.push(logFile);
56
56
  }
57
57
 
58
- this._ipc.server.emit(socket, 'registerContextDone', {});
58
+ this._ipc.server.emit(socket, 'registerContextDone', {
59
+ failedTestFiles: this._sessionState.failedTestFiles,
60
+ testFilesToRetry: this._sessionState.testFilesToRetry,
61
+ testSessionIndex: this._sessionState.testSessionIndex,
62
+ });
59
63
  }
60
64
 
61
65
  onRegisterWorker({ workerId }, socket) {
@@ -69,12 +73,21 @@ class IPCServer {
69
73
  }
70
74
  }
71
75
 
72
- onFailedTests({ testFilePaths }, socket) {
73
- this._sessionState.failedTestFiles.push(...testFilePaths);
76
+ onFailedTests({ testFilePaths, permanent }, socket = null) {
77
+ if (permanent) {
78
+ this._sessionState.failedTestFiles.push(...testFilePaths);
79
+ } else {
80
+ this._sessionState.testFilesToRetry.push(...testFilePaths);
81
+ }
74
82
 
75
83
  if (socket) {
76
84
  this._ipc.server.emit(socket, 'failedTestsDone', {});
77
85
  }
86
+
87
+ this._ipc.server.broadcast('sessionStateUpdate', {
88
+ failedTestFiles: this._sessionState.failedTestFiles,
89
+ testFilesToRetry: this._sessionState.testFilesToRetry,
90
+ });
78
91
  }
79
92
  }
80
93
 
package/src/ipc/state.js CHANGED
@@ -38,25 +38,47 @@ class SessionState {
38
38
  }
39
39
 
40
40
  class SecondarySessionState extends SessionState {
41
- constructor({ id = uuid.UUID(), detoxConfigSnapshotPath = '', detoxConfig = null, detoxIPCServer = '', workerId = undefined, workersCount = 0 }) {
41
+ constructor({
42
+ id = uuid.UUID(),
43
+ detoxConfigSnapshotPath = '',
44
+ detoxConfig = null,
45
+ detoxIPCServer = '',
46
+ failedTestFiles = [],
47
+ testFilesToRetry = [],
48
+ testSessionIndex = 0,
49
+ workerId = undefined,
50
+ workersCount = 0
51
+ }) {
42
52
  super();
43
53
 
44
54
  this.id = id;
45
55
  this.detoxConfigSnapshotPath = detoxConfigSnapshotPath;
46
56
  this.detoxConfig = detoxConfig;
47
57
  this.detoxIPCServer = detoxIPCServer;
58
+ this.failedTestFiles = failedTestFiles;
59
+ this.testFilesToRetry = testFilesToRetry;
60
+ this.testSessionIndex = testSessionIndex;
48
61
  this.workerId = workerId;
49
62
  this.workersCount = workersCount;
50
63
  }
51
64
  }
52
65
 
53
66
  class PrimarySessionState extends SecondarySessionState {
54
- constructor({ contexts = [], failedTestFiles = [], logFiles = [], ...baseOpts }) {
67
+ constructor({
68
+ contexts = [],
69
+ logFiles = [],
70
+ failedTestFiles = [],
71
+ testFilesToRetry = [],
72
+ testSessionIndex = 0,
73
+ ...baseOpts
74
+ }) {
55
75
  super(baseOpts);
56
76
 
57
77
  this.contexts = contexts;
58
78
  this.failedTestFiles = failedTestFiles;
59
79
  this.logFiles = logFiles;
80
+ this.testSessionIndex = testSessionIndex;
81
+ this.testFilesToRetry = testFilesToRetry;
60
82
  }
61
83
  }
62
84
 
@@ -19,7 +19,7 @@ class DetoxContext {
19
19
  constructor() {
20
20
  this[symbols.globalSetup] = this[symbols.globalSetup].bind(this);
21
21
  this[symbols.globalTeardown] = this[symbols.globalTeardown].bind(this);
22
- this[symbols.reportFailedTests] = this[symbols.globalTeardown].bind(this);
22
+ this[symbols.reportFailedTests] = this[symbols.reportFailedTests].bind(this);
23
23
  this[symbols.resolveConfig] = this[symbols.resolveConfig].bind(this);
24
24
  this[symbols.setup] = this[symbols.setup].bind(this);
25
25
  this[symbols.teardown] = this[symbols.teardown].bind(this);
@@ -27,7 +27,7 @@ class DetoxContext {
27
27
  this[$sessionState] = this[$restoreSessionState]();
28
28
 
29
29
  const loggerConfig = this[$sessionState].detoxConfig
30
- ? this[$sessionState].detoxConfig.loggerConfig
30
+ ? this[$sessionState].detoxConfig.logger
31
31
  : undefined;
32
32
 
33
33
  /**
@@ -93,7 +93,7 @@ class DetoxContext {
93
93
  [symbols.config] = funpermaproxy(() => this[symbols.session].detoxConfig);
94
94
  [symbols.session] = funpermaproxy(() => this[$sessionState]);
95
95
  /** @abstract */
96
- [symbols.reportFailedTests](_testFilePaths) {}
96
+ [symbols.reportFailedTests](_testFilePaths, _permanent) {}
97
97
  /**
98
98
  * @abstract
99
99
  * @param {Partial<DetoxInternals.DetoxGlobalSetupOptions>} _opts
@@ -34,9 +34,9 @@ class DetoxPrimaryContext extends DetoxContext {
34
34
  }
35
35
 
36
36
  //#region Internal members
37
- async [symbols.reportFailedTests](testFilePaths) {
37
+ async [symbols.reportFailedTests](testFilePaths, permanent = false) {
38
38
  if (this[_ipcServer]) {
39
- this[_ipcServer].onFailedTests({ testFilePaths });
39
+ this[_ipcServer].onFailedTests({ testFilePaths, permanent });
40
40
  }
41
41
  }
42
42
 
@@ -65,7 +65,12 @@ class DetoxPrimaryContext extends DetoxContext {
65
65
  this[_dirty] = true;
66
66
  const detoxConfig = await this[symbols.resolveConfig](opts);
67
67
 
68
- const { behaviorConfig, deviceConfig, loggerConfig, sessionConfig } = detoxConfig;
68
+ const {
69
+ behavior: behaviorConfig,
70
+ device: deviceConfig,
71
+ logger: loggerConfig,
72
+ session: sessionConfig
73
+ } = detoxConfig;
69
74
  await this[$logger].setConfig(loggerConfig);
70
75
 
71
76
  this.trace.begin({
@@ -120,8 +125,9 @@ class DetoxPrimaryContext extends DetoxContext {
120
125
  * @param {Partial<DetoxInternals.DetoxConfigurationSetupOptions>} [opts]
121
126
  */
122
127
  async [symbols.setup](opts) {
123
- this[$sessionState].workerId = opts.workerId;
124
- this[_ipcServer].onRegisterWorker({ workerId: opts.workerId });
128
+ const workerId = opts.workerId || 1;
129
+ this[$sessionState].workerId = workerId;
130
+ this[_ipcServer].onRegisterWorker({ workerId });
125
131
  await super[symbols.setup](opts);
126
132
  }
127
133
 
@@ -176,7 +182,7 @@ class DetoxPrimaryContext extends DetoxContext {
176
182
  }
177
183
 
178
184
  const streamUtils = require('../utils/streamUtils');
179
- const { rootDir, plugins } = this[symbols.config].artifactsConfig || {};
185
+ const { rootDir, plugins } = this[symbols.config].artifacts || {};
180
186
  const logConfig = plugins && plugins.log || 'none';
181
187
  const enabled = rootDir && (typeof logConfig === 'string' ? logConfig !== 'none' : logConfig.enabled);
182
188
 
@@ -203,7 +209,7 @@ class DetoxPrimaryContext extends DetoxContext {
203
209
  async[_resetLockFile]() {
204
210
  const DeviceRegistry = require('../devices/DeviceRegistry');
205
211
 
206
- const deviceType = this[symbols.config].deviceConfig.type;
212
+ const deviceType = this[symbols.config].device.type;
207
213
 
208
214
  switch (deviceType) {
209
215
  case 'ios.none':
@@ -21,9 +21,9 @@ class DetoxSecondaryContext extends DetoxContext {
21
21
  }
22
22
 
23
23
  //#region Internal members
24
- async [symbols.reportFailedTests](testFilePaths) {
24
+ async [symbols.reportFailedTests](testFilePaths, permanent = false) {
25
25
  if (this[_ipcClient]) {
26
- await this[_ipcClient].reportFailedTests(testFilePaths);
26
+ await this[_ipcClient].reportFailedTests(testFilePaths, permanent);
27
27
  } else {
28
28
  throw new DetoxInternalError('Detected an attempt to report failed tests using a non-initialized context.');
29
29
  }
@@ -59,8 +59,9 @@ class DetoxSecondaryContext extends DetoxContext {
59
59
  logger: this[$logger],
60
60
  });
61
61
 
62
+ const workerId = opts.workerId || 1;
62
63
  await this[_ipcClient].init();
63
- await this[_ipcClient].registerWorker(opts.workerId);
64
+ await this[_ipcClient].registerWorker(workerId);
64
65
  await super[symbols.setup](opts);
65
66
  }
66
67