detox 20.0.5-breaking.new-global-lifecycle.0 → 20.0.8-prerelease.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.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-javadoc.jar → 20.0.8-prerelease.0/detox-20.0.8-prerelease.0-javadoc.jar} +0 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0-javadoc.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0-javadoc.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0-javadoc.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0-javadoc.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-sources.jar → 20.0.8-prerelease.0/detox-20.0.8-prerelease.0-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0.aar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.pom → 20.0.8-prerelease.0/detox-20.0.8-prerelease.0.pom} +1 -7
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.0.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.8-prerelease.0/detox-20.0.8-prerelease.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 +12 -6
- package/android/detox/build.gradle +13 -9
- package/android/detox/proguard-rules-app.pro +4 -0
- package/android/detox/publishing.gradle +27 -27
- package/android/detox/src/full/java/com/wix/detox/DetoxCrashHandler.kt +1 -1
- package/android/detox/src/full/java/com/wix/detox/LaunchArgs.java +9 -0
- package/android/detox/src/full/java/com/wix/detox/TestEngineFacade.kt +1 -1
- package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeExtension.kt +15 -2
- package/android/detox/src/full/java/com/wix/detox/reactnative/ReactNativeIdlingResources.kt +43 -38
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/DelegatedIdleInterrogationStrategy.kt +7 -27
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/IdleInterrogationStrategy.kt +1 -11
- package/android/detox/src/testFull/java/com/wix/detox/espresso/action/DetoxMultiTapSpec.kt +4 -3
- package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/timers/DelegatedIdleInterrogationStrategySpec.kt +3 -11
- package/index.d.ts +21 -1
- package/local-cli/test.js +9 -4
- package/local-cli/testCommand/TestRunnerCommand.js +8 -7
- package/package.json +3 -3
- package/runners/jest/testEnvironment/index.js +4 -3
- package/src/DetoxWorker.js +0 -6
- package/src/android/core/NativeElement.js +56 -20
- package/src/android/core/NativeExpect.js +28 -9
- package/src/android/interactions/native.js +24 -18
- package/src/artifacts/timeline/TimelineContextTypes.js +7 -0
- package/src/client/Client.js +18 -1
- package/src/configuration/composeRunnerConfig.js +1 -0
- package/src/devices/common/drivers/android/tools/MonitoredInstrumentation.js +1 -1
- package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +3 -1
- package/src/devices/runtime/RuntimeDevice.js +2 -4
- package/src/devices/runtime/drivers/android/AndroidDriver.js +9 -1
- package/src/ios/expectTwo.js +152 -67
- package/src/logger/DetoxLogger.js +21 -17
- package/src/logger/utils/tracerLegacy.js +24 -1
- package/src/realms/DetoxPrimaryContext.js +26 -11
- package/src/utils/errorUtils.js +4 -3
- package/src/utils/invocationTraceDescriptions.js +43 -0
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-javadoc.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-javadoc.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-javadoc.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-javadoc.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.aar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.pom.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.5-breaking.new-global-lifecycle.0/detox-20.0.5-breaking.new-global-lifecycle.0.pom.sha512 +0 -1
- package/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/DefaultIdleInterrogationStrategy.kt +0 -84
- package/android/detox/src/testFull/java/com/wix/detox/reactnative/idlingresources/timers/DefaultIdleInterrogationStrategySpec.kt +0 -115
@@ -34,6 +34,22 @@ class DetoxLogger {
|
|
34
34
|
*/
|
35
35
|
this._sharedConfig = sharedConfig;
|
36
36
|
|
37
|
+
/** @type {object | undefined} */
|
38
|
+
this._context = context;
|
39
|
+
|
40
|
+
/** @public */
|
41
|
+
this.fatal = this._setupLogMethod('fatal');
|
42
|
+
/** @public */
|
43
|
+
this.error = this._setupLogMethod('error');
|
44
|
+
/** @public */
|
45
|
+
this.warn = this._setupLogMethod('warn');
|
46
|
+
/** @public */
|
47
|
+
this.info = this._setupLogMethod('info');
|
48
|
+
/** @public */
|
49
|
+
this.debug = this._setupLogMethod('debug');
|
50
|
+
/** @public */
|
51
|
+
this.trace = this._setupLogMethod('trace');
|
52
|
+
|
37
53
|
if (!context) {
|
38
54
|
// In this branch, `this` refers to the first (root) logger instance.
|
39
55
|
|
@@ -76,22 +92,6 @@ class DetoxLogger {
|
|
76
92
|
|
77
93
|
this.overrideConsole();
|
78
94
|
}
|
79
|
-
|
80
|
-
/** @type {object | undefined} */
|
81
|
-
this._context = context;
|
82
|
-
|
83
|
-
/** @public */
|
84
|
-
this.fatal = this._setupLogMethod('fatal');
|
85
|
-
/** @public */
|
86
|
-
this.error = this._setupLogMethod('error');
|
87
|
-
/** @public */
|
88
|
-
this.warn = this._setupLogMethod('warn');
|
89
|
-
/** @public */
|
90
|
-
this.info = this._setupLogMethod('info');
|
91
|
-
/** @public */
|
92
|
-
this.debug = this._setupLogMethod('debug');
|
93
|
-
/** @public */
|
94
|
-
this.trace = this._setupLogMethod('trace');
|
95
95
|
}
|
96
96
|
|
97
97
|
/**
|
@@ -232,7 +232,11 @@ class DetoxLogger {
|
|
232
232
|
const action = typeof maybeContext !== 'string' ? maybeAction : maybeMessage;
|
233
233
|
const args = maybeAction === action ? [maybeContext, maybeMessage] : [maybeContext];
|
234
234
|
const { context, msg } = this._parseArgs(null, args);
|
235
|
-
const end = (ctx) => this[level].end(
|
235
|
+
const end = (ctx) => this[level].end({
|
236
|
+
id: context.id,
|
237
|
+
cat: context.cat,
|
238
|
+
...ctx,
|
239
|
+
});
|
236
240
|
|
237
241
|
let result;
|
238
242
|
this[level].begin(context, ...msg);
|
@@ -8,15 +8,38 @@ const methods = {
|
|
8
8
|
},
|
9
9
|
|
10
10
|
traceCall(logger) {
|
11
|
-
return (name, action) => logger.trace.complete(name, action);
|
11
|
+
return (name, action, args = {}) => logger.trace.complete(args, name, action);
|
12
|
+
},
|
13
|
+
|
14
|
+
invocationCall(logger) {
|
15
|
+
return (sectionName, invocation, action) => {
|
16
|
+
return logger.trace.complete({
|
17
|
+
cat: 'ws-client,ws-client-invocation',
|
18
|
+
data: invocation,
|
19
|
+
stack: _getCallStackTrace(),
|
20
|
+
}, sectionName, action);
|
21
|
+
};
|
12
22
|
},
|
13
23
|
};
|
14
24
|
|
25
|
+
function _getCallStackTrace() {
|
26
|
+
return new Error().stack
|
27
|
+
.split('\n')
|
28
|
+
.slice(1) // Ignore Error message
|
29
|
+
.map(line => line
|
30
|
+
.replace(/^\s*at\s+/, '')
|
31
|
+
.replace(process.cwd(), '')
|
32
|
+
)
|
33
|
+
.filter(line => !line.includes('/detox/src')) // Ignore detox internal calls
|
34
|
+
.join('\n');
|
35
|
+
}
|
36
|
+
|
15
37
|
function installLegacyTracerInterface(logger, target) {
|
16
38
|
target.traceCall = methods.traceCall(logger);
|
17
39
|
target.trace = Object.freeze({
|
18
40
|
startSection: methods.startSection(logger),
|
19
41
|
endSection: methods.endSection(logger),
|
42
|
+
invocationCall: methods.invocationCall(logger),
|
20
43
|
});
|
21
44
|
|
22
45
|
return this;
|
@@ -53,7 +53,7 @@ class DetoxPrimaryContext extends DetoxContext {
|
|
53
53
|
}
|
54
54
|
|
55
55
|
async [symbols.resolveConfig](opts = {}) {
|
56
|
-
const session = this[
|
56
|
+
const session = this[$sessionState];
|
57
57
|
if (!session.detoxConfig) {
|
58
58
|
const configuration = require('../configuration');
|
59
59
|
session.detoxConfig = await configuration.composeDetoxConfig(opts);
|
@@ -170,11 +170,13 @@ class DetoxPrimaryContext extends DetoxContext {
|
|
170
170
|
|
171
171
|
await fs.remove(this[$sessionState].detoxConfigSnapshotPath);
|
172
172
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
173
|
+
if (this[_dirty]) {
|
174
|
+
try {
|
175
|
+
this[_lifecycleLogger].trace.end();
|
176
|
+
await this[_finalizeLogs]();
|
177
|
+
} catch (err) {
|
178
|
+
this[_lifecycleLogger].error({ err }, 'Encountered an error while merging the process logs:');
|
179
|
+
}
|
178
180
|
}
|
179
181
|
}
|
180
182
|
}
|
@@ -229,7 +231,7 @@ class DetoxPrimaryContext extends DetoxContext {
|
|
229
231
|
return;
|
230
232
|
}
|
231
233
|
|
232
|
-
if (this[_areLogsEnabled]) {
|
234
|
+
if (this[_areLogsEnabled]()) {
|
233
235
|
const streamUtils = require('../utils/streamUtils');
|
234
236
|
const { rootDir } = this[symbols.config].artifacts;
|
235
237
|
|
@@ -256,7 +258,10 @@ class DetoxPrimaryContext extends DetoxContext {
|
|
256
258
|
const logsEnabled = this[_areLogsEnabled]();
|
257
259
|
|
258
260
|
const { rootDir } = this[symbols.config].artifacts;
|
259
|
-
|
261
|
+
|
262
|
+
if (logsEnabled) {
|
263
|
+
fs.mkdirpSync(rootDir);
|
264
|
+
}
|
260
265
|
|
261
266
|
const sessionId = this[$sessionState].id;
|
262
267
|
const logs = globSync(temporary.for.jsonl(`${sessionId}.*`));
|
@@ -272,10 +277,20 @@ class DetoxPrimaryContext extends DetoxContext {
|
|
272
277
|
|
273
278
|
[_areLogsEnabled]() {
|
274
279
|
const { rootDir, plugins } = this[symbols.config].artifacts || {};
|
275
|
-
|
276
|
-
|
280
|
+
if (!rootDir || !plugins) {
|
281
|
+
return false;
|
282
|
+
}
|
283
|
+
|
284
|
+
if (!plugins.log.enabled) {
|
285
|
+
return false;
|
286
|
+
}
|
287
|
+
|
288
|
+
if (!plugins.log.keepOnlyFailedTestsArtifacts) {
|
289
|
+
return true;
|
290
|
+
}
|
277
291
|
|
278
|
-
|
292
|
+
const { failedTestFiles, testFilesToRetry } = this[$sessionState];
|
293
|
+
return failedTestFiles.length + testFilesToRetry.length > 0;
|
279
294
|
}
|
280
295
|
|
281
296
|
async[_resetLockFile]() {
|
package/src/utils/errorUtils.js
CHANGED
@@ -16,11 +16,12 @@ function filterErrorStack(error, predicate) {
|
|
16
16
|
}
|
17
17
|
|
18
18
|
function replaceErrorStack(source, target) {
|
19
|
-
const sourceStack = source.stack ||
|
20
|
-
const targetStack = target.stack || target.message;
|
19
|
+
const sourceStack = (source.stack || '');
|
21
20
|
const sourceMessage = sourceStack.replace(CLEAN_AT, '');
|
22
|
-
const targetMessage = targetStack.replace(CLEAN_AT, '');
|
23
21
|
const actualSourceStack = sourceStack.slice(sourceMessage.length);
|
22
|
+
|
23
|
+
const targetMessage = target.message || target.stack.replace(CLEAN_AT, '');
|
24
|
+
|
24
25
|
target.stack = targetMessage + actualSourceStack;
|
25
26
|
return target;
|
26
27
|
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module.exports = {
|
2
|
+
actionDescription: {
|
3
|
+
adjustSliderToPosition: (newPosition) => `adjust slider to position ${newPosition}`,
|
4
|
+
clearText: () => 'clear input text',
|
5
|
+
getAttributes: () => 'get element attributes',
|
6
|
+
longPress: (duration) => `long press${duration !== undefined ? ` for ${duration}ms` : ''}`,
|
7
|
+
longPressAndDrag: (duration, startX, startY, targetElement, endX, endY, speed, holdDuration) =>
|
8
|
+
`long press and drag from ${startX}, ${startY} to ${endX}, ${endY} with speed ${speed} and hold duration ${holdDuration}`,
|
9
|
+
multiTap: (times) => `tap ${times} times`,
|
10
|
+
pinch: (scale, speed, angle) => `pinch with scale ${scale}, speed ${speed}, and angle ${angle}`,
|
11
|
+
pinchWithAngle: (direction, speed, angle) => `pinch with direction ${direction}, speed ${speed}, and angle ${angle}`,
|
12
|
+
replaceText: (value) => `replace input text: "${value}"`,
|
13
|
+
scroll: (amount, direction, startPositionX, startPositionY) =>
|
14
|
+
`scroll ${amount} pixels ${direction}${startPositionX !== undefined && startPositionY !== undefined ? ` from normalized position (${startPositionX}, ${startPositionY})` : ''}`,
|
15
|
+
scrollTo: (edge) => `scroll to ${edge}`,
|
16
|
+
scrollToIndex: (index) => `scroll to index #${index}`,
|
17
|
+
setColumnToValue: (column, value) => `set column ${column} to value ${value}`,
|
18
|
+
setDatePickerDate: (dateString, dateFormat) => `set date picker date to ${dateString} using format ${dateFormat}`,
|
19
|
+
swipe: (direction, speed, normalizedSwipeOffset, normalizedStartingPointX, normalizedStartingPointY) =>
|
20
|
+
`swipe ${direction} ${speed} with offset ${normalizedSwipeOffset}
|
21
|
+
${!isNaN(normalizedStartingPointX) && !isNaN(normalizedStartingPointY) ? ` from normalized position (${normalizedStartingPointX}, ${normalizedStartingPointY})` : ''}`,
|
22
|
+
takeScreenshot: (screenshotName) => `take screenshot${screenshotName !== undefined ? ` with name "${screenshotName}"` : ''}`,
|
23
|
+
tapAtPoint: (value) => `tap${value !== undefined ? ` at ${JSON.stringify(value)}` : ''}`,
|
24
|
+
tapBackspaceKey: () => 'tap on backspace key',
|
25
|
+
tapReturnKey: () => 'tap on return key',
|
26
|
+
typeText: (value) => `type input text: "${value}"`,
|
27
|
+
},
|
28
|
+
expectDescription: {
|
29
|
+
waitFor: (actionDescription) => `wait for expectation while ${actionDescription}`,
|
30
|
+
waitForWithTimeout: (expectDescription, timeout) => `${expectDescription} with timeout (${timeout} ms)`,
|
31
|
+
withTimeout: (timeout) => `wait until timeout (${timeout} ms)`,
|
32
|
+
toBeFocused: () => 'to be focused',
|
33
|
+
toBeVisible: (percent) => `to be visible${percent !== undefined ? ` ${percent}%` : ''}`,
|
34
|
+
toExist: () => 'to exist',
|
35
|
+
toHaveText: (text) => `to have text: "${text}"`,
|
36
|
+
toHaveLabel: (label) => `to have label: "${label}"`,
|
37
|
+
toHaveId: (id) => `to have id: "${id}"`,
|
38
|
+
toHaveValue: (value) => `to have value: "${value}"`,
|
39
|
+
toHaveSliderPosition: (position, tolerance) => `to have slider position: ${position}${tolerance > 0 ? ` with tolerance ${tolerance}` : ''}`,
|
40
|
+
toHaveToggleValue: (value) => `to have toggle value: ${value}`,
|
41
|
+
full: (expectDescription, notCondition) => `expect element ${notCondition ? `not ${expectDescription}` : expectDescription}`
|
42
|
+
}
|
43
|
+
};
|
@@ -1 +0,0 @@
|
|
1
|
-
1c28e1a121a1eb49cfae610647edf0ad
|
@@ -1 +0,0 @@
|
|
1
|
-
e7d9c6c15b4467c257ee92ae349d3465446b4a40
|
@@ -1 +0,0 @@
|
|
1
|
-
274e47fbbdbe85abdf2ebfd4c76e00ef33ad353309da375460f71f9f6fe04c2a
|
@@ -1 +0,0 @@
|
|
1
|
-
063f4a5549133b4d661a7677d68a91473877b26ed3e29ef9d7502a83c179810d68f15dd9e35399b5bab23f3c97f96ee875a80abcb07d1dfa57a6177399dcc347
|
@@ -1 +0,0 @@
|
|
1
|
-
20a7fdefedfc40238042f083ca693c54
|
@@ -1 +0,0 @@
|
|
1
|
-
471d2b163fac9e08859166f2aacec3a1609348c8
|
@@ -1 +0,0 @@
|
|
1
|
-
4a344c3b3b5597488fece53c9cc7358705e6d40116b0779122428094bd021db7
|
@@ -1 +0,0 @@
|
|
1
|
-
93443f0d56c9f84ec24523b954e6e9782652c4bd5d8c94bf558812fade6750beb49f4678dcbaef53e1948edcf5fb5e35c47efb49c7acc79cd2ffa0fea2b287bd
|
Binary file
|
@@ -1 +0,0 @@
|
|
1
|
-
e7043eb4fd0dfa3c309fd8cd35ee68e5
|
@@ -1 +0,0 @@
|
|
1
|
-
80bef0c22a710dabb0e72b8d15c59125db1bd58b
|
@@ -1 +0,0 @@
|
|
1
|
-
7828ffdff493f8a856efad9b7db540e4e6b13b91260292efe98791013ba2db04
|
@@ -1 +0,0 @@
|
|
1
|
-
a57448b5eec5edfb2b45b99b9c8a6714b3384f71ade0f25da7b453d14c29597e818690b293cefa9be9362be29efb2c420664283cb1fc994883cd143d46506a2f
|
@@ -1 +0,0 @@
|
|
1
|
-
f947833decee666021174627c8909b4b
|
@@ -1 +0,0 @@
|
|
1
|
-
ae94fe4f6480eee73e5f4cc2617dc9b64c5602e0
|
@@ -1 +0,0 @@
|
|
1
|
-
b9ea5a9228c8248ce58fcfc5eb12983b642c2240ae61fd0929cfdafb664cc9a6
|
@@ -1 +0,0 @@
|
|
1
|
-
7d5986795398d9c6021a9d4085a4e1b2c3e794e51fb873ddfefc596d2f8b3a04a58c553fdb4202f6bd5fa4bbbf36373b318a7497a66e345b6f8f23bfee8b4145
|
@@ -1,84 +0,0 @@
|
|
1
|
-
@file:RNDropSupportTodo(62, "Remove all of this; Use DelegatedIdleInterrogationStrategy, instead.")
|
2
|
-
|
3
|
-
package com.wix.detox.reactnative.idlingresources.timers
|
4
|
-
|
5
|
-
import com.facebook.react.bridge.ReactContext
|
6
|
-
import com.wix.detox.common.RNDropSupportTodo
|
7
|
-
import com.wix.detox.reactnative.helpers.RNHelpers
|
8
|
-
import org.joor.Reflect
|
9
|
-
import java.util.*
|
10
|
-
|
11
|
-
private const val BUSY_WINDOW_THRESHOLD = 1500
|
12
|
-
|
13
|
-
private class TimerReflected(timer: Any) {
|
14
|
-
private var reflected = Reflect.on(timer)
|
15
|
-
|
16
|
-
val isRepeating: Boolean
|
17
|
-
get() = reflected.field("mRepeat").get()
|
18
|
-
val interval: Int
|
19
|
-
get() = reflected.field("mInterval").get()
|
20
|
-
val targetTime: Long
|
21
|
-
get() = reflected.field("mTargetTime").get()
|
22
|
-
}
|
23
|
-
|
24
|
-
private class TimingModuleReflected(private val timingModule: Any) {
|
25
|
-
val timersQueue: PriorityQueue<Any>
|
26
|
-
get() = Reflect.on(timingModule).field("mTimers").get()
|
27
|
-
val timersLock: Any
|
28
|
-
get() = Reflect.on(timingModule).field("mTimerGuard").get()
|
29
|
-
|
30
|
-
operator fun component1() = timersQueue
|
31
|
-
operator fun component2() = timersLock
|
32
|
-
}
|
33
|
-
|
34
|
-
class DefaultIdleInterrogationStrategy
|
35
|
-
internal constructor(private val timersModule: Any)
|
36
|
-
: IdleInterrogationStrategy {
|
37
|
-
|
38
|
-
override fun isIdleNow(): Boolean {
|
39
|
-
val (timersQueue, timersLock) = TimingModuleReflected(timersModule)
|
40
|
-
synchronized(timersLock) {
|
41
|
-
val nextTimer = timersQueue.peek()
|
42
|
-
nextTimer?.let {
|
43
|
-
return !isTimerInBusyWindow(it) && !hasBusyTimers(timersQueue)
|
44
|
-
}
|
45
|
-
return true
|
46
|
-
}
|
47
|
-
}
|
48
|
-
|
49
|
-
private fun isTimerInBusyWindow(timer: Any): Boolean {
|
50
|
-
val timerReflected = TimerReflected(timer)
|
51
|
-
return when {
|
52
|
-
timerReflected.isRepeating -> false
|
53
|
-
timerReflected.interval > BUSY_WINDOW_THRESHOLD -> false
|
54
|
-
else -> true
|
55
|
-
}
|
56
|
-
}
|
57
|
-
|
58
|
-
private fun hasBusyTimers(timersQueue: PriorityQueue<Any>): Boolean {
|
59
|
-
timersQueue.forEach {
|
60
|
-
if (isTimerInBusyWindow(it)) {
|
61
|
-
return true
|
62
|
-
}
|
63
|
-
}
|
64
|
-
return false
|
65
|
-
}
|
66
|
-
|
67
|
-
companion object {
|
68
|
-
fun createIfSupported(reactContext: ReactContext): DefaultIdleInterrogationStrategy? {
|
69
|
-
// RN = 0.62.0:
|
70
|
-
// Should have been handled by DelegatedIdleInterrogationStrategy.createIfSupported() but seems the new TimingModule class
|
71
|
-
// was released without the awaited-for "hasActiveTimersInRange()" method.
|
72
|
-
try {
|
73
|
-
val timingModule = RNHelpers.getNativeModule(reactContext, "com.facebook.react.modules.core.TimingModule")
|
74
|
-
val timersManager = Reflect.on(timingModule).get<Any>("mJavaTimerManager")
|
75
|
-
return DefaultIdleInterrogationStrategy(timersManager)
|
76
|
-
} catch (ex: Exception) {
|
77
|
-
}
|
78
|
-
|
79
|
-
// RN < 0.62
|
80
|
-
val timingModule = RNHelpers.getNativeModule(reactContext, "com.facebook.react.modules.core.Timing") ?: return null
|
81
|
-
return DefaultIdleInterrogationStrategy(timingModule)
|
82
|
-
}
|
83
|
-
}
|
84
|
-
}
|
@@ -1,115 +0,0 @@
|
|
1
|
-
package com.wix.detox.reactnative.idlingresources.timers
|
2
|
-
|
3
|
-
import com.facebook.react.bridge.NativeModule
|
4
|
-
import com.wix.detox.UTHelpers
|
5
|
-
import org.assertj.core.api.Assertions
|
6
|
-
import org.spekframework.spek2.Spek
|
7
|
-
import org.spekframework.spek2.style.specification.describe
|
8
|
-
import java.util.*
|
9
|
-
import java.util.concurrent.Executors
|
10
|
-
|
11
|
-
private const val BUSY_INTERVAL_MS = 1500
|
12
|
-
private const val MEANINGFUL_TIMER_INTERVAL = BUSY_INTERVAL_MS
|
13
|
-
|
14
|
-
data class TimerStub(
|
15
|
-
private var mCallbackID: Int,
|
16
|
-
private var mTargetTime: Long,
|
17
|
-
private var mInterval: Int,
|
18
|
-
private var mRepeat: Boolean)
|
19
|
-
|
20
|
-
class TimersNativeModuleStub : NativeModule {
|
21
|
-
val mTimers: PriorityQueue<Any> = PriorityQueue(2) { _, _ -> 0}
|
22
|
-
val mTimerGuard = "Lock-Mock"
|
23
|
-
|
24
|
-
override fun onCatalystInstanceDestroy() {}
|
25
|
-
override fun invalidate() {}
|
26
|
-
|
27
|
-
override fun getName(): String = "TimersNativeModuleStub"
|
28
|
-
override fun canOverrideExistingModule() = false
|
29
|
-
override fun initialize() {}
|
30
|
-
}
|
31
|
-
|
32
|
-
private fun now() = System.nanoTime() / 1000000L
|
33
|
-
private fun aTimer(targetTime: Long, interval: Int, isRepeating: Boolean): TimerStub {
|
34
|
-
return TimerStub(-1, targetTime, interval, isRepeating)
|
35
|
-
}
|
36
|
-
private fun aTimer(interval: Int, isRepeating: Boolean) = aTimer(now() + interval + 10, interval, isRepeating)
|
37
|
-
private fun aOneShotTimer(interval: Int) = aTimer(interval, false)
|
38
|
-
private fun aRepeatingTimer(interval: Int) = aTimer(interval, true)
|
39
|
-
private fun anOverdueTimer() = aTimer(now() - 100, 123, false)
|
40
|
-
|
41
|
-
object DefaultIdleInterrogationStrategySpec: Spek({
|
42
|
-
describe("Default timers idle-interrogation strategy") {
|
43
|
-
|
44
|
-
lateinit var timersNativeModule: TimersNativeModuleStub
|
45
|
-
|
46
|
-
beforeEachTest {
|
47
|
-
timersNativeModule = TimersNativeModuleStub()
|
48
|
-
}
|
49
|
-
|
50
|
-
fun givenTimer(timer: Any) {
|
51
|
-
timersNativeModule.mTimers.add(timer)
|
52
|
-
}
|
53
|
-
|
54
|
-
fun uut() = DefaultIdleInterrogationStrategy(timersNativeModule)
|
55
|
-
|
56
|
-
it("should be idle if there are no timers in queue") {
|
57
|
-
Assertions.assertThat(uut().isIdleNow()).isTrue()
|
58
|
-
}
|
59
|
-
|
60
|
-
it("should be busy if there's a meaningful pending timer") {
|
61
|
-
givenTimer(aOneShotTimer(MEANINGFUL_TIMER_INTERVAL))
|
62
|
-
Assertions.assertThat(uut().isIdleNow()).isFalse()
|
63
|
-
}
|
64
|
-
|
65
|
-
it("should be idle if pending timer is too far away (ie not meaningful)") {
|
66
|
-
givenTimer(aOneShotTimer(BUSY_INTERVAL_MS + 1))
|
67
|
-
Assertions.assertThat(uut().isIdleNow()).isTrue()
|
68
|
-
}
|
69
|
-
|
70
|
-
it("should be idle if the only timer is a repeating one") {
|
71
|
-
givenTimer(aRepeatingTimer(MEANINGFUL_TIMER_INTERVAL))
|
72
|
-
Assertions.assertThat(uut().isIdleNow()).isTrue()
|
73
|
-
}
|
74
|
-
|
75
|
-
it("should be busy if a meaningful pending timer lies beyond a repeating one") {
|
76
|
-
givenTimer(aRepeatingTimer(BUSY_INTERVAL_MS / 10))
|
77
|
-
givenTimer(aOneShotTimer(BUSY_INTERVAL_MS))
|
78
|
-
Assertions.assertThat(uut().isIdleNow()).isFalse()
|
79
|
-
}
|
80
|
-
|
81
|
-
/**
|
82
|
-
* Note: Reversed logic due to this issue: https://github.com/wix/Detox/issues/1171 !!!
|
83
|
-
*
|
84
|
-
* Apparently at times (rare) this caused Espresso to think we're idle too soon, rendering
|
85
|
-
* it never to query any idling resource again even after the timer effectively expires...
|
86
|
-
*/
|
87
|
-
it("should be *busy* even if all timers are overdue") {
|
88
|
-
givenTimer(anOverdueTimer())
|
89
|
-
givenTimer(anOverdueTimer())
|
90
|
-
Assertions.assertThat(uut().isIdleNow()).isFalse()
|
91
|
-
}
|
92
|
-
|
93
|
-
it("should be busy if has a meaningful pending timer set beyond an overdue timer") {
|
94
|
-
givenTimer(anOverdueTimer())
|
95
|
-
givenTimer(aOneShotTimer(MEANINGFUL_TIMER_INTERVAL))
|
96
|
-
Assertions.assertThat(uut().isIdleNow()).isFalse()
|
97
|
-
}
|
98
|
-
|
99
|
-
it("should yield to other threads using the timers module") {
|
100
|
-
val executor = Executors.newSingleThreadExecutor()
|
101
|
-
var isIdle: Boolean? = null
|
102
|
-
|
103
|
-
synchronized(timersNativeModule.mTimerGuard) {
|
104
|
-
executor.submit {
|
105
|
-
isIdle = uut().isIdleNow()
|
106
|
-
}
|
107
|
-
UTHelpers.yieldToOtherThreads(executor)
|
108
|
-
Assertions.assertThat(isIdle).isNull()
|
109
|
-
}
|
110
|
-
UTHelpers.yieldToOtherThreads(executor)
|
111
|
-
Assertions.assertThat(isIdle).isNotNull()
|
112
|
-
executor.shutdownNow()
|
113
|
-
}
|
114
|
-
}
|
115
|
-
})
|