detox 20.14.0-smoke.0 → 20.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. package/.eslintignore +1 -0
  2. package/Detox-android/com/wix/detox/{20.14.0-smoke.0/detox-20.14.0-smoke.0-javadoc.jar → 20.14.0/detox-20.14.0-javadoc.jar} +0 -0
  3. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0-javadoc.jar.md5 +1 -0
  4. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0-javadoc.jar.sha1 +1 -0
  5. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0-javadoc.jar.sha256 +1 -0
  6. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0-javadoc.jar.sha512 +1 -0
  7. package/Detox-android/com/wix/detox/{20.14.0-smoke.0/detox-20.14.0-smoke.0-sources.jar → 20.14.0/detox-20.14.0-sources.jar} +0 -0
  8. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0-sources.jar.md5 +1 -0
  9. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0-sources.jar.sha1 +1 -0
  10. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0-sources.jar.sha256 +1 -0
  11. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0-sources.jar.sha512 +1 -0
  12. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0.aar +0 -0
  13. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0.aar.md5 +1 -0
  14. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0.aar.sha1 +1 -0
  15. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0.aar.sha256 +1 -0
  16. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0.aar.sha512 +1 -0
  17. package/Detox-android/com/wix/detox/{20.14.0-smoke.0/detox-20.14.0-smoke.0.pom → 20.14.0/detox-20.14.0.pom} +1 -1
  18. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0.pom.md5 +1 -0
  19. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0.pom.sha1 +1 -0
  20. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0.pom.sha256 +1 -0
  21. package/Detox-android/com/wix/detox/20.14.0/detox-20.14.0.pom.sha512 +1 -0
  22. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  23. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  24. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  25. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  26. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  27. package/Detox-ios-src.tbz +0 -0
  28. package/Detox-ios.tbz +0 -0
  29. package/android/detox/build.gradle +8 -4
  30. package/android/detox/src/full/java/com/wix/detox/Detox.java +1 -7
  31. package/android/detox/src/full/java/com/wix/detox/DetoxMain.kt +55 -72
  32. package/android/detox/src/full/java/com/wix/detox/adapters/server/DetoxActionHandlers.kt +0 -15
  33. package/android/detox/src/full/java/com/wix/detox/adapters/server/DetoxActionsDispatcher.kt +16 -6
  34. package/android/detox/src/full/java/com/wix/detox/adapters/server/DetoxServerAdapter.kt +0 -2
  35. package/android/detox/src/full/java/com/wix/detox/espresso/web/WebElement.java +4 -4
  36. package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeExtension.kt +0 -14
  37. package/android/detox/src/full/java/com/wix/invoke/types/Invocation.java +2 -2
  38. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/MotionEvents.kt +60 -4
  39. package/android/detox/src/testFull/java/com/wix/invoke/JsonParserTest.java +23 -7
  40. package/android/detox/src/testFull/resources/targetInvocationEspressoWebDetoxScript.json +47 -0
  41. package/detox.d.ts +1830 -0
  42. package/globals.d.ts +23 -0
  43. package/index.d.ts +2 -1838
  44. package/internals.d.ts +2 -1
  45. package/jest.config.js +108 -0
  46. package/local-cli/testCommand/TestRunnerCommand.js +26 -3
  47. package/local-cli/utils/interruptListeners.js +15 -0
  48. package/package.json +3 -99
  49. package/runners/jest/reporter.js +21 -1
  50. package/runners/jest/reporters/DetoxIPCReporter.js +34 -0
  51. package/runners/jest/reporters/DetoxReporterDispatcher.js +144 -0
  52. package/runners/jest/reporters/DetoxSummaryReporter.js +16 -0
  53. package/runners/jest/reporters/DetoxVerboseReporter.js +16 -0
  54. package/runners/jest/reporters/index.js +6 -0
  55. package/runners/jest/testEnvironment/index.js +7 -1
  56. package/src/android/core/WebElement.js +24 -6
  57. package/src/android/espressoapi/web/WebElement.js +1 -4
  58. package/src/configuration/composeRunnerConfig.js +3 -1
  59. package/src/devices/allocation/drivers/ios/SimulatorAllocDriver.js +3 -5
  60. package/src/devices/allocation/factories/ios.js +1 -4
  61. package/src/devices/common/drivers/android/tools/instrumentationArgs.js +7 -1
  62. package/src/devices/runtime/drivers/ios/SimulatorDriver.js +5 -4
  63. package/src/devices/runtime/factories/ios.js +0 -2
  64. package/src/ipc/IPCClient.js +2 -2
  65. package/src/ipc/IPCServer.js +5 -3
  66. package/src/realms/DetoxContext.js +1 -1
  67. package/src/realms/DetoxPrimaryContext.js +2 -2
  68. package/src/realms/DetoxSecondaryContext.js +2 -2
  69. package/src/utils/assertIsFunction.js +35 -0
  70. package/src/utils/isArrowFunction.js +24 -0
  71. package/tsconfig.json +3 -0
  72. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0-javadoc.jar.md5 +0 -1
  73. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0-javadoc.jar.sha1 +0 -1
  74. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0-javadoc.jar.sha256 +0 -1
  75. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0-javadoc.jar.sha512 +0 -1
  76. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0-sources.jar.md5 +0 -1
  77. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0-sources.jar.sha1 +0 -1
  78. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0-sources.jar.sha256 +0 -1
  79. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0-sources.jar.sha512 +0 -1
  80. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0.aar +0 -0
  81. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0.aar.md5 +0 -1
  82. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0.aar.sha1 +0 -1
  83. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0.aar.sha256 +0 -1
  84. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0.aar.sha512 +0 -1
  85. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0.pom.md5 +0 -1
  86. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0.pom.sha1 +0 -1
  87. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0.pom.sha256 +0 -1
  88. package/Detox-android/com/wix/detox/20.14.0-smoke.0/detox-20.14.0-smoke.0.pom.sha512 +0 -1
  89. package/runners/jest/reporters/DetoxReporter.js +0 -154
  90. package/src/devices/allocation/drivers/ios/SimulatorLauncher.js +0 -25
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
package/jest.config.js ADDED
@@ -0,0 +1,108 @@
1
+ const DEBUG = process.argv.includes('--reporters');
2
+
3
+ /** @type{import('jest-allure2-reporter').ReporterOptions} */
4
+ const jestAllure2ReporterOptions = {
5
+ testCase: {
6
+ labels: {
7
+ package: ({ filePath }) => filePath.slice(1).join('/'),
8
+ testMethod: ({ testCase }) => testCase.fullName,
9
+ tag: ['unit'],
10
+ },
11
+ },
12
+ };
13
+
14
+ /** @type {import('@jest/types').Config.InitialOptions} */
15
+ module.exports = {
16
+ setupFiles: [
17
+ '<rootDir>/__tests__/setupJest.js'
18
+ ],
19
+ testEnvironment: DEBUG ? 'node' : 'jest-allure2-reporter/environment-node',
20
+ testRunner: 'jest-circus/runner',
21
+ roots: [
22
+ 'node_modules',
23
+ 'local-cli',
24
+ 'src',
25
+ 'runners'
26
+ ],
27
+ testPathIgnorePatterns: [
28
+ '/node_modules/',
29
+ 'local-cli/test.js'
30
+ ],
31
+ coveragePathIgnorePatterns: [
32
+ '/node_modules/',
33
+ '__tests__',
34
+ '.test.js$',
35
+ '.mock.js$',
36
+ 'index.js',
37
+ 'internals.js',
38
+ 'local-cli/utils',
39
+ 'src/environmentFactory',
40
+ 'src/android/espressoapi',
41
+ 'src/artifacts/factories/index.js',
42
+ 'src/artifacts/providers/index.js',
43
+ 'src/artifacts/log',
44
+ 'src/artifacts/screenshot',
45
+ 'src/artifacts/video',
46
+ 'src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js',
47
+ 'src/devices/allocation/drivers/android/emulator/patchAvdSkinConfig.js',
48
+ 'src/devices/allocation/.*AllocDriver.js',
49
+ 'src/devices/allocation/drivers/ios',
50
+ 'src/devices/allocation/factories',
51
+ 'src/devices/allocation/factories/drivers',
52
+ 'src/devices/cookies',
53
+ 'src/devices/common/drivers/android/exec/ADB.js',
54
+ 'src/devices/common/drivers/android/emulator/exec/EmulatorExec.js',
55
+ 'src/devices/common/drivers/android/tools/EmulatorTelnet.js',
56
+ 'src/devices/common/drivers/ios/tools',
57
+ 'src/devices/runtime/drivers/android/AndroidDriver.js',
58
+ 'src/devices/runtime/drivers/android/emulator/EmulatorDriver.js',
59
+ 'src/devices/runtime/drivers/DeviceDriverBase.js',
60
+ 'src/devices/runtime/drivers/ios',
61
+ 'src/devices/runtime/factories',
62
+ 'src/devices/runtime/factories/drivers',
63
+ 'src/devices/validation/EnvironmentValidatorBase.js',
64
+ 'src/devices/validation/factories',
65
+ 'src/matchers/factories',
66
+ 'src/utils/appdatapath.js',
67
+ 'src/utils/debug.js',
68
+ 'src/utils/environment.js',
69
+ 'src/utils/logger.js',
70
+ 'src/utils/pipeCommands.js',
71
+ 'src/utils/pressAnyKey.js',
72
+ 'src/utils/shellUtils.js',
73
+ 'runners/jest/reporters',
74
+ 'runners/jest/testEnvironment',
75
+ 'src/DetoxWorker.js',
76
+ 'src/logger/utils/streamUtils.js',
77
+ 'src/realms'
78
+ ],
79
+ resetMocks: true,
80
+ resetModules: true,
81
+ reporters: [
82
+ 'default',
83
+ [
84
+ 'jest-allure2-reporter',
85
+ jestAllure2ReporterOptions,
86
+ ]
87
+ ],
88
+ coverageReporters: [
89
+ 'html',
90
+ 'json',
91
+ 'text',
92
+ 'clover',
93
+ [
94
+ 'lcov',
95
+ {
96
+ projectRoot: '..'
97
+ }
98
+ ]
99
+ ],
100
+ coverageThreshold: {
101
+ global: {
102
+ statements: 100,
103
+ branches: 100,
104
+ functions: 100,
105
+ lines: 100
106
+ }
107
+ }
108
+ };
@@ -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.14.0-smoke.0",
4
+ "version": "20.14.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": "^1.2.1",
54
+ "jest-allure2-reporter": "2.0.0-alpha.11",
55
55
  "mockdate": "^2.0.1",
56
56
  "prettier": "^2.4.1",
57
57
  "react-native": "0.71.10",
@@ -106,104 +106,8 @@
106
106
  "engines": {
107
107
  "node": ">=14.5.0"
108
108
  },
109
- "jest": {
110
- "setupFiles": [
111
- "<rootDir>/__tests__/setupJest.js"
112
- ],
113
- "testEnvironment": "node",
114
- "testRunner": "jest-circus/runner",
115
- "roots": [
116
- "node_modules",
117
- "local-cli",
118
- "src",
119
- "runners"
120
- ],
121
- "testPathIgnorePatterns": [
122
- "/node_modules/",
123
- "local-cli/test.js"
124
- ],
125
- "coveragePathIgnorePatterns": [
126
- "/node_modules/",
127
- "__tests__",
128
- ".test.js$",
129
- ".mock.js$",
130
- "index.js",
131
- "internals.js",
132
- "local-cli/utils",
133
- "src/environmentFactory",
134
- "src/android/espressoapi",
135
- "src/artifacts/factories/index.js",
136
- "src/artifacts/providers/index.js",
137
- "src/artifacts/log",
138
- "src/artifacts/screenshot",
139
- "src/artifacts/video",
140
- "src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js",
141
- "src/devices/allocation/drivers/android/emulator/patchAvdSkinConfig.js",
142
- "src/devices/allocation/.*AllocDriver.js",
143
- "src/devices/allocation/drivers/ios",
144
- "src/devices/allocation/factories",
145
- "src/devices/allocation/factories/drivers",
146
- "src/devices/cookies",
147
- "src/devices/common/drivers/android/exec/ADB.js",
148
- "src/devices/common/drivers/android/emulator/exec/EmulatorExec.js",
149
- "src/devices/common/drivers/android/tools/EmulatorTelnet.js",
150
- "src/devices/common/drivers/ios/tools",
151
- "src/devices/runtime/drivers/android/AndroidDriver.js",
152
- "src/devices/runtime/drivers/android/emulator/EmulatorDriver.js",
153
- "src/devices/runtime/drivers/DeviceDriverBase.js",
154
- "src/devices/runtime/drivers/ios",
155
- "src/devices/runtime/factories",
156
- "src/devices/runtime/factories/drivers",
157
- "src/devices/validation/EnvironmentValidatorBase.js",
158
- "src/devices/validation/factories",
159
- "src/matchers/factories",
160
- "src/utils/appdatapath.js",
161
- "src/utils/debug.js",
162
- "src/utils/environment.js",
163
- "src/utils/logger.js",
164
- "src/utils/pipeCommands.js",
165
- "src/utils/pressAnyKey.js",
166
- "src/utils/shellUtils.js",
167
- "runners/jest/reporters",
168
- "runners/jest/testEnvironment",
169
- "src/DetoxWorker.js",
170
- "src/logger/utils/streamUtils.js",
171
- "src/realms"
172
- ],
173
- "resetMocks": true,
174
- "resetModules": true,
175
- "reporters": [
176
- "default",
177
- [
178
- "jest-allure2-reporter",
179
- {
180
- "getEnvironmentInfo": false
181
- }
182
- ]
183
- ],
184
- "coverageReporters": [
185
- "html",
186
- "json",
187
- "text",
188
- "clover",
189
- [
190
- "lcov",
191
- {
192
- "projectRoot": ".."
193
- }
194
- ]
195
- ],
196
- "coverageThreshold": {
197
- "global": {
198
- "statements": 100,
199
- "branches": 100,
200
- "functions": 100,
201
- "lines": 100
202
- }
203
- }
204
- },
205
109
  "browserslist": [
206
110
  "node 14"
207
111
  ],
208
- "gitHead": "e9ae9173b3e36e9926a74454aa20709ec71d4da8"
112
+ "gitHead": "4400519a8fdf3adb546766aa869b35f4c407f8e8"
209
113
  }
@@ -1 +1,21 @@
1
- module.exports = require('./reporters/DetoxReporter');
1
+ /** @typedef {import('@jest/reporters').Reporter} Reporter */
2
+
3
+ const {
4
+ DetoxIPCReporter,
5
+ DetoxReporterDispatcher,
6
+ DetoxSummaryReporter,
7
+ DetoxVerboseReporter,
8
+ } = require('./reporters');
9
+
10
+ /** @implements {Reporter} */
11
+ class DetoxReporter extends DetoxReporterDispatcher {
12
+ constructor(globalConfig) {
13
+ super(globalConfig, {
14
+ DetoxSummaryReporter,
15
+ DetoxVerboseReporter,
16
+ DetoxIPCReporter,
17
+ });
18
+ }
19
+ }
20
+
21
+ module.exports = DetoxReporter;
@@ -0,0 +1,34 @@
1
+ const { config, reportTestResults } = require('../../../internals');
2
+
3
+ /** @typedef {import('@jest/reporters').Reporter} Reporter */
4
+
5
+ /** @implements {Partial<Reporter>} */
6
+ class DetoxIPCReporter {
7
+ /**
8
+ * @param {Set<import('@jest/reporters').TestContext>} testContexts
9
+ * @param {import('@jest/reporters').AggregatedResult} aggregatedResult
10
+ */
11
+ async onRunComplete(testContexts, aggregatedResult) {
12
+ const lostTests = aggregatedResult.numTotalTestSuites - aggregatedResult.testResults.length;
13
+
14
+ await reportTestResults(aggregatedResult.testResults.map(r => ({
15
+ success: !r.failureMessage,
16
+ testFilePath: r.testFilePath,
17
+ testExecError: r.testExecError,
18
+ isPermanentFailure: lostTests > 0 || this._isPermanentFailure(r),
19
+ })));
20
+ }
21
+
22
+ /**
23
+ * @param {import('@jest/test-result').TestResult} testResult
24
+ */
25
+ _isPermanentFailure(testResult) {
26
+ if (config.testRunner.jest.retryAfterCircusRetries) {
27
+ return false;
28
+ }
29
+
30
+ return testResult.testResults.some(r => r.status === 'failed' && r.invocations > 1);
31
+ }
32
+ }
33
+
34
+ module.exports = DetoxIPCReporter;
@@ -0,0 +1,144 @@
1
+ /** @typedef {import('@jest/reporters').Reporter} Reporter */
2
+
3
+ const { config, unsafe_conductEarlyTeardown } = require('../../../internals');
4
+ const Deferred = require('../../../src/utils/Deferred');
5
+
6
+ /** @implements {Reporter} */
7
+ class DetoxReporterDispatcher {
8
+ /**
9
+ * @param {import('@jest/types').Config.GlobalConfig} globalConfig
10
+ * @param {Record<string, new (globalConfig: import('@jest/types').Config.GlobalConfig) => Partial<Reporter>>} reporters
11
+ */
12
+ constructor(globalConfig, reporters) {
13
+ this._bail = globalConfig.bail;
14
+ /** @type {Deferred | null} */
15
+ this._lastRunComplete = null;
16
+ /** @type {Set<string>} */
17
+ this._pendingTestFiles = new Set();
18
+ /** @type {Promise<any> | null} */
19
+ this._onRunCompletePromise = null;
20
+ /** @type {Partial<Reporter>[]} */
21
+ this._reporters = Object.values(reporters).map((Reporter) => new Reporter(globalConfig));
22
+ }
23
+
24
+ getLastError() {
25
+ for (const reporter of this._reporters) {
26
+ let error = typeof reporter.getLastError === 'function'
27
+ ? reporter.getLastError()
28
+ : undefined;
29
+
30
+ if (error) {
31
+ return error;
32
+ }
33
+ }
34
+
35
+ return;
36
+ }
37
+
38
+ onRunStart(aggregatedResult, options) {
39
+ return this._dispatch('onRunStart', aggregatedResult, options);
40
+ }
41
+
42
+ onTestFileStart(test) {
43
+ this._pendingTestFiles.add(test.path);
44
+ return this._dispatch(['onTestFileStart', 'onTestStart'], test);
45
+ }
46
+
47
+ onTestStart(test) {
48
+ // Legacy method
49
+ return this.onTestFileStart(test);
50
+ }
51
+
52
+ // NEW! Supported only since Jest 29.6.0
53
+ onTestCaseStart(test, testCaseStartInfo) {
54
+ return this._dispatch('onTestCaseStart', test, testCaseStartInfo);
55
+ }
56
+
57
+ onTestCaseResult(test, testCaseResult) {
58
+ return this._dispatch('onTestCaseResult', test, testCaseResult);
59
+ }
60
+
61
+ async onTestFileResult(test, testResult, aggregatedResult) {
62
+ this._pendingTestFiles.delete(test.path);
63
+
64
+ await this._dispatch(['onTestFileResult', 'onTestResult'], test, testResult, aggregatedResult);
65
+
66
+ if (this._lastRunComplete && this._pendingTestFiles.size === 0) {
67
+ this._lastRunComplete.resolve(aggregatedResult);
68
+ }
69
+ }
70
+
71
+ onTestResult(test, testResult, aggregatedResult) {
72
+ // Legacy method
73
+ return this.onTestFileResult(test, testResult, aggregatedResult);
74
+ }
75
+
76
+ onRunComplete(testContexts, aggregatedResult) {
77
+ if (!this._lastRunComplete) {
78
+ // Both `_lastRunComplete` and `_onRunCompletePromise` are used to prevent
79
+ // a bug in Jest, where `onRunComplete` is called multiple times when
80
+ // Jest runs with `--bail` and multiple workers, and `onRunComplete`
81
+ // is implemented as an asynchronous long-running operation.
82
+ this._lastRunComplete = this._pendingTestFiles.size === 0
83
+ ? Deferred.resolved(aggregatedResult)
84
+ : new Deferred();
85
+ }
86
+
87
+ if (!this._onRunCompletePromise) {
88
+ this._onRunCompletePromise = this._doRunComplete(testContexts, aggregatedResult);
89
+ }
90
+
91
+ return this._onRunCompletePromise;
92
+ }
93
+
94
+ /**
95
+ * @private
96
+ * @param {Set<import('@jest/reporters').TestContext>} testContexts
97
+ * @param {import('@jest/test-result').AggregatedResult} aggregatedResult
98
+ * @returns {Promise<void>}
99
+ */
100
+ async _doRunComplete(testContexts, aggregatedResult) {
101
+ const earlyTeardown = this._bail > 0 && aggregatedResult.numFailedTests >= this._bail;
102
+ if (earlyTeardown) {
103
+ const lostTests = aggregatedResult.numTotalTestSuites - aggregatedResult.testResults.length;
104
+ if (lostTests > 0 && config.testRunner.retries > 0) {
105
+ console.warn(
106
+ 'Jest aborted the test execution before all scheduled test files have been reported.\n' +
107
+ 'If you want to retry the whole test run, please disable Jest\'s --bail option.'
108
+ );
109
+ }
110
+
111
+ await unsafe_conductEarlyTeardown();
112
+ }
113
+
114
+ await this._lastRunComplete.promise;
115
+ await this._dispatch('onRunComplete', testContexts, aggregatedResult);
116
+ }
117
+
118
+ /**
119
+ * @private
120
+ * @param {string | string[]} rawMethodNames
121
+ * @param {...any} args
122
+ * @returns {Promise<void>}
123
+ */
124
+ _dispatch(rawMethodNames, ...args) {
125
+ const methodNames = Array.isArray(rawMethodNames)
126
+ ? rawMethodNames
127
+ : [rawMethodNames];
128
+
129
+ const maybePromises = [];
130
+
131
+ for (const reporter of this._reporters) {
132
+ for (const methodName of methodNames) {
133
+ if (typeof reporter[methodName] === 'function') {
134
+ maybePromises.push(reporter[methodName](...args));
135
+ break;
136
+ }
137
+ }
138
+ }
139
+
140
+ return Promise.all(maybePromises).then(() => void 0);
141
+ }
142
+ }
143
+
144
+ module.exports = DetoxReporterDispatcher;
@@ -0,0 +1,16 @@
1
+ /** @typedef {import('@jest/reporters').Reporter} Reporter */
2
+
3
+ const resolveFrom = require('resolve-from');
4
+ /** @type {new (globalConfig: any) => import('@jest/reporters').SummaryReporter} */
5
+ const SummaryReporter = require(resolveFrom(process.cwd(), '@jest/reporters')).SummaryReporter;
6
+
7
+ /** @implements {Partial<Reporter>} */
8
+ class DetoxSummaryReporter {
9
+ constructor(globalConfig) {
10
+ if (globalConfig.reporters.every(([name]) => name !== 'default' && name !== 'summary')) {
11
+ return new SummaryReporter(globalConfig);
12
+ }
13
+ }
14
+ }
15
+
16
+ module.exports = DetoxSummaryReporter;
@@ -0,0 +1,16 @@
1
+ /** @typedef {import('@jest/reporters').Reporter} Reporter */
2
+
3
+ const resolveFrom = require('resolve-from');
4
+ /** @type {new (globalConfig: any) => import('@jest/reporters').VerboseReporter} */
5
+ const VerboseReporter = require(resolveFrom(process.cwd(), '@jest/reporters')).VerboseReporter;
6
+
7
+ /** @implements {Partial<Reporter>} */
8
+ class DetoxVerboseReporter {
9
+ constructor(globalConfig) {
10
+ if (globalConfig.reporters.every(([name]) => name !== 'default')) {
11
+ return new VerboseReporter(globalConfig);
12
+ }
13
+ }
14
+ }
15
+
16
+ module.exports = DetoxVerboseReporter;
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ DetoxIPCReporter: require('./DetoxIPCReporter'),
3
+ DetoxReporterDispatcher: require('./DetoxReporterDispatcher'),
4
+ DetoxSummaryReporter: require('./DetoxSummaryReporter'),
5
+ DetoxVerboseReporter: require('./DetoxVerboseReporter'),
6
+ };
@@ -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}`,
@@ -1,5 +1,7 @@
1
1
  const DetoxRuntimeError = require('../../errors/DetoxRuntimeError');
2
2
  const invoke = require('../../invoke');
3
+ const assertIsFunction = require('../../utils/assertIsFunction');
4
+ const isArrowFunction = require('../../utils/isArrowFunction');
3
5
  const actions = require('../actions/web');
4
6
  const EspressoWebDetoxApi = require('../espressoapi/web/EspressoWebDetox');
5
7
  const WebViewElementApi = require('../espressoapi/web/WebViewElement');
@@ -73,12 +75,14 @@ class WebElement {
73
75
  return await new ActionInteraction(this[_invocationManager], new actions.WebMoveCursorEnd(this)).execute();
74
76
  }
75
77
 
76
- async runScript(script) {
77
- return await new ActionInteraction(this[_invocationManager], new actions.WebRunScriptAction(this, script)).execute();
78
- }
78
+ async runScript(maybeFunction, args) {
79
+ const script = stringifyScript(maybeFunction);
79
80
 
80
- async runScriptWithArgs(script, args) {
81
- return await new ActionInteraction(this[_invocationManager], new actions.WebRunScriptWithArgsAction(this, script, args)).execute();
81
+ if (args) {
82
+ return await new ActionInteraction(this[_invocationManager], new actions.WebRunScriptWithArgsAction(this, script, args)).execute();
83
+ } else {
84
+ return await new ActionInteraction(this[_invocationManager], new actions.WebRunScriptAction(this, script)).execute();
85
+ }
82
86
  }
83
87
 
84
88
  async getCurrentUrl() {
@@ -116,10 +120,24 @@ class WebViewElement {
116
120
  });
117
121
  }
118
122
 
119
- throw new DetoxRuntimeError(`element() argument is invalid, expected a web matcher, but got ${typeof element}`);
123
+ throw new DetoxRuntimeError(`element() argument is invalid, expected a web matcher, but got ${typeof webMatcher}`);
120
124
  }
121
125
  }
122
126
 
127
+ function stringifyScript(maybeFunction) {
128
+ if (typeof maybeFunction !== 'string' && typeof maybeFunction !== 'function') {
129
+ return maybeFunction;
130
+ }
131
+
132
+ const script = (typeof maybeFunction === 'function' ? maybeFunction.toString() : assertIsFunction(maybeFunction)).trim();
133
+ // WebElement interactions don't support arrow functions for some reason.
134
+ if (isArrowFunction(script)) {
135
+ return `function arrowWorkaround() { return (${script}).apply(this, arguments); }`;
136
+ }
137
+
138
+ return script;
139
+ }
140
+
123
141
  module.exports = {
124
142
  WebElement,
125
143
  WebViewElement,
@@ -76,10 +76,7 @@ class WebElement {
76
76
  return {
77
77
  target: element,
78
78
  method: "runScriptWithArgs",
79
- args: [script, {
80
- type: "ArrayList<Object>",
81
- value: args
82
- }]
79
+ args: [script, args]
83
80
  };
84
81
  }
85
82