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.
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
- package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
- package/Detox-ios-src.tbz +0 -0
- package/Detox-ios.tbz +0 -0
- package/android/build.gradle +2 -2
- package/android/detox/build.gradle +2 -2
- package/android/detox/publish-pom.gradle +5 -1
- package/android/detox/publishing.gradle +9 -7
- package/android/detox/src/full/java/com/wix/detox/adapters/server/WebSocketClient.java +3 -1
- package/android/detox/src/full/java/com/wix/detox/espresso/DetoxAction.java +1 -3
- package/android/detox/src/full/java/com/wix/detox/espresso/UiAutomatorHelper.java +1 -1
- package/android/detox/src/full/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +22 -0
- package/android/detox/src/{main → full}/java/com/wix/detox/espresso/action/GetAttributesAction.kt +13 -1
- package/android/detox/src/full/java/com/wix/detox/espresso/common/SliderHelper.kt +75 -0
- package/android/detox/src/full/java/com/wix/detox/espresso/matcher/ViewMatchers.kt +16 -23
- package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeLoadingMonitor.kt +54 -8
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/RN66Workaround.kt +13 -4
- package/android/detox/src/main/java/com/wix/detox/common/DetoxErrors.java +4 -1
- package/android/detox/src/main/java/com/wix/detox/espresso/UiControllerSpy.kt +2 -1
- package/android/detox/src/main/java/com/wix/detox/espresso/action/common/ReflectUtils.kt +10 -0
- package/android/detox/src/main/java/com/wix/detox/espresso/action/common/utils/UiControllerUtils.kt +1 -1
- package/android/detox/src/testFull/java/com/wix/detox/espresso/action/GetAttributesActionTest.kt +15 -3
- package/android/detox/src/testFull/java/com/wix/detox/espresso/common/SliderHelperTest.kt +39 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +2 -1
- package/android/gradlew +181 -107
- package/index.d.ts +5 -0
- package/internals.d.ts +25 -18
- package/local-cli/build.js +1 -1
- package/local-cli/build.test.js +14 -14
- package/local-cli/test.js +3 -3
- package/local-cli/test.test.js +20 -14
- package/local-cli/testCommand/TestRunnerCommand.js +6 -6
- package/package.json +4 -4
- package/runners/jest/testEnvironment/index.js +1 -1
- package/runners/jest/testEnvironment/listeners/DetoxCoreListener.js +5 -9
- package/runners/jest/testEnvironment/listeners/SpecReporter.js +1 -1
- package/runners/jest/testEnvironment/listeners/WorkerAssignReporter.js +1 -1
- package/src/DetoxWorker.js +12 -1
- package/src/artifacts/log/android/ADBLogcatRecording.js +11 -28
- package/src/configuration/composeAppsConfig.js +1 -1
- package/src/configuration/composeRunnerConfig.js +49 -1
- package/src/configuration/index.js +9 -8
- package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +1 -0
- package/src/devices/common/drivers/android/exec/ADB.js +5 -0
- package/src/errors/DetoxConfigErrorComposer.js +5 -3
- package/src/ipc/IPCClient.js +3 -2
- package/src/ipc/IPCServer.js +16 -3
- package/src/ipc/state.js +24 -2
- package/src/realms/DetoxContext.js +3 -3
- package/src/realms/DetoxPrimaryContext.js +13 -7
- package/src/realms/DetoxSecondaryContext.js +4 -3
- package/src/utils/ChromeTracingExporter.js +6 -5
- package/src/utils/environment.js +30 -15
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/android/detox/src/main/java/com/wix/detox/espresso/action/AdjustSliderToPositionAction.kt +0 -36
- package/android/detox/src/testFull/java/com/wix/detox/espresso/action/AdjustSliderToPositionActionTest.kt +0 -59
package/local-cli/test.test.js
CHANGED
@@ -116,7 +116,7 @@ describe('CLI', () => {
|
|
116
116
|
});
|
117
117
|
});
|
118
118
|
|
119
|
-
test('should use
|
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.
|
144
|
-
context.session.
|
145
|
-
|
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
|
-
|
162
|
-
|
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.
|
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._ =
|
108
|
-
|
109
|
-
detox.session.
|
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.
|
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": "^
|
49
|
+
"jest": "^28.0.0",
|
50
50
|
"mockdate": "^2.0.1",
|
51
51
|
"prettier": "1.7.0",
|
52
|
-
"react-native": "0.
|
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": "
|
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.
|
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 {
|
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.
|
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
|
-
|
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.
|
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.
|
15
|
+
if (config.testRunner.jest.reportWorkerAssign) {
|
16
16
|
log.info({ event: 'WORKER_ASSIGN' }, `${this._formatTestName()} is assigned to ${this._formatDeviceName()}`);
|
17
17
|
}
|
18
18
|
}
|
package/src/DetoxWorker.js
CHANGED
@@ -56,7 +56,13 @@ class DetoxWorker {
|
|
56
56
|
async init() {
|
57
57
|
if (this._isCleaningUp) return;
|
58
58
|
|
59
|
-
const {
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
44
|
-
|
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
|
-
|
65
|
-
|
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
|
-
|
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() {
|
@@ -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
|
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
|
-
|
116
|
-
|
117
|
-
|
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
|
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
|
666
|
+
testRunner,
|
665
667
|
...this.contents,
|
666
668
|
} : {
|
667
669
|
...this._focusOnConfiguration(c => _.pick(c, ['testRunner'])),
|
package/src/ipc/IPCClient.js
CHANGED
@@ -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() {
|
package/src/ipc/IPCServer.js
CHANGED
@@ -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
|
-
|
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({
|
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({
|
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.
|
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.
|
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 {
|
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
|
-
|
124
|
-
this[
|
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].
|
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].
|
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(
|
64
|
+
await this[_ipcClient].registerWorker(workerId);
|
64
65
|
await super[symbols.setup](opts);
|
65
66
|
}
|
66
67
|
|