detox 20.0.6-breaking.new-global-lifecycle.0 → 20.0.9-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.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0-javadoc.jar → 20.0.9-prerelease.0/detox-20.0.9-prerelease.0-javadoc.jar} +0 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0-javadoc.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0-javadoc.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0-javadoc.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0-javadoc.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0-sources.jar → 20.0.9-prerelease.0/detox-20.0.9-prerelease.0-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0.aar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0.aar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0.aar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0.aar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0.pom → 20.0.9-prerelease.0/detox-20.0.9-prerelease.0.pom} +1 -7
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-prerelease.0.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.0.9-prerelease.0/detox-20.0.9-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/main/java/com/wix/detox/common/TextFileReader.kt +1 -1
- 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/internals.d.ts +11 -0
- package/local-cli/test.js +9 -4
- package/local-cli/testCommand/TestRunnerCommand.js +2 -3
- package/package.json +3 -3
- package/runners/jest/testEnvironment/index.js +3 -2
- 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 +5 -1
- package/src/{utils → logger/utils}/streamUtils.js +28 -1
- package/src/logger/utils/tracerLegacy.js +24 -1
- package/src/realms/DetoxContext.js +8 -0
- package/src/realms/DetoxInternalsFacade.js +1 -1
- package/src/realms/DetoxPrimaryContext.js +9 -10
- package/src/symbols.js +2 -0
- package/src/utils/errorUtils.js +4 -3
- package/src/utils/invocationTraceDescriptions.js +43 -0
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0-javadoc.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0-javadoc.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0-javadoc.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0-javadoc.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0.aar +0 -0
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0.aar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0.aar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0.aar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0.aar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0.pom.md5 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-breaking.new-global-lifecycle.0.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.0.6-breaking.new-global-lifecycle.0/detox-20.0.6-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
@@ -2,13 +2,16 @@ const { PassThrough, Transform } = require('stream');
|
|
2
2
|
|
3
3
|
const bunyanDebugStream = require('bunyan-debug-stream');
|
4
4
|
const duplexify = require('duplexify');
|
5
|
+
const fs = require('fs-extra');
|
6
|
+
const glob = require('glob');
|
5
7
|
const multiSort = require('multi-sort-stream');
|
6
8
|
const pipe = require('multipipe');
|
7
9
|
const JsonlParser = require('stream-json/jsonl/Parser');
|
8
10
|
const stripAnsi = require('strip-ansi');
|
9
11
|
const { AbstractEventBuilder } = require('trace-event-lib');
|
10
12
|
|
11
|
-
const
|
13
|
+
const temporary = require('../../artifacts/utils/temporaryPath');
|
14
|
+
const log = require('../../utils/logger').child({ cat: 'logger' });
|
12
15
|
|
13
16
|
function compareTimestamps(a, b) {
|
14
17
|
return +(a.value.time > b.value.time) - +(a.value.time < b.value.time);
|
@@ -203,6 +206,29 @@ function readJSONL() {
|
|
203
206
|
return duplexify.obj(preventErrorSubscriptions(writable), writable.pipe(readable));
|
204
207
|
}
|
205
208
|
|
209
|
+
/**
|
210
|
+
* @param {string} sessionId
|
211
|
+
* @returns {NodeJS.ReadableStream}
|
212
|
+
*/
|
213
|
+
function uniteSessionLogs(sessionId) {
|
214
|
+
const readable = through();
|
215
|
+
|
216
|
+
glob(temporary.for.jsonl(`${sessionId}.*`), function (err, logs) {
|
217
|
+
if (err) {
|
218
|
+
return readable.emit('error', err);
|
219
|
+
}
|
220
|
+
|
221
|
+
if (logs.length === 0) {
|
222
|
+
return;
|
223
|
+
}
|
224
|
+
|
225
|
+
const jsonlStreams = logs.map(filePath => fs.createReadStream(filePath).pipe(readJSONL()));
|
226
|
+
mergeSortedJSONL(jsonlStreams).pipe(readable);
|
227
|
+
});
|
228
|
+
|
229
|
+
return readable;
|
230
|
+
}
|
231
|
+
|
206
232
|
module.exports = {
|
207
233
|
readJSONL,
|
208
234
|
writeJSON,
|
@@ -210,4 +236,5 @@ module.exports = {
|
|
210
236
|
mergeSortedJSONL,
|
211
237
|
debugStream,
|
212
238
|
chromeTraceStream,
|
239
|
+
uniteSessionLogs,
|
213
240
|
};
|
@@ -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;
|
@@ -94,6 +94,14 @@ class DetoxContext {
|
|
94
94
|
[symbols.onRunFinish] = (...args) => this[symbols.worker].onRunFinish(...args);
|
95
95
|
[symbols.config] = funpermaproxy(() => this[symbols.session].detoxConfig);
|
96
96
|
[symbols.session] = funpermaproxy(() => this[$sessionState]);
|
97
|
+
[symbols.tracing] = Object.freeze({
|
98
|
+
createEventStream: () => {
|
99
|
+
const streamUtils = require('../logger/utils/streamUtils');
|
100
|
+
return streamUtils
|
101
|
+
.uniteSessionLogs(this[$sessionState].id)
|
102
|
+
.pipe(streamUtils.chromeTraceStream());
|
103
|
+
},
|
104
|
+
});
|
97
105
|
/** @abstract */
|
98
106
|
[symbols.reportFailedTests](_testFilePaths, _permanent) {}
|
99
107
|
/**
|
@@ -29,7 +29,7 @@ class DetoxInternalsFacade {
|
|
29
29
|
this.reportFailedTests = context[symbols.reportFailedTests];
|
30
30
|
this.resolveConfig = context[symbols.resolveConfig];
|
31
31
|
this.session = context[symbols.session];
|
32
|
-
this.
|
32
|
+
this.tracing = context[symbols.tracing];
|
33
33
|
this.worker = funpermaproxy(() => context[symbols.worker]);
|
34
34
|
}
|
35
35
|
}
|
@@ -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
|
}
|
@@ -230,17 +232,14 @@ class DetoxPrimaryContext extends DetoxContext {
|
|
230
232
|
}
|
231
233
|
|
232
234
|
if (this[_areLogsEnabled]()) {
|
233
|
-
const streamUtils = require('../utils/streamUtils');
|
235
|
+
const streamUtils = require('../logger/utils/streamUtils');
|
234
236
|
const { rootDir } = this[symbols.config].artifacts;
|
235
237
|
|
236
238
|
await fs.mkdirp(rootDir);
|
237
239
|
const [out1Stream, out2Stream, out3Stream] = ['detox.log.jsonl', 'detox.log', 'detox.trace.json']
|
238
240
|
.map((filename) => fs.createWriteStream(path.join(rootDir, filename)));
|
239
241
|
|
240
|
-
const mergedStream = streamUtils
|
241
|
-
.mergeSortedJSONL(
|
242
|
-
logs.map(filePath => fs.createReadStream(filePath).pipe(streamUtils.readJSONL()))
|
243
|
-
);
|
242
|
+
const mergedStream = streamUtils.uniteSessionLogs(sessionId);
|
244
243
|
|
245
244
|
await Promise.all([
|
246
245
|
pipe(mergedStream, streamUtils.writeJSONL(), out1Stream),
|
package/src/symbols.js
CHANGED
@@ -21,6 +21,7 @@
|
|
21
21
|
* readonly reportFailedTests: unique symbol;
|
22
22
|
* readonly resolveConfig: unique symbol;
|
23
23
|
* readonly session: unique symbol;
|
24
|
+
* readonly tracing: unique symbol;
|
24
25
|
* readonly uninstallWorker: unique symbol;
|
25
26
|
* readonly worker: unique symbol;
|
26
27
|
* }}
|
@@ -54,6 +55,7 @@ module.exports = {
|
|
54
55
|
logger: Symbol('logger'),
|
55
56
|
resolveConfig: Symbol('resolveConfig'),
|
56
57
|
session: Symbol('session'),
|
58
|
+
tracing: Symbol('tracing'),
|
57
59
|
uninstallWorker: Symbol('uninstallWorker'),
|
58
60
|
worker: Symbol('worker'),
|
59
61
|
//#endregion
|
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
|
-
91686ed0f19733f7c115a7139ed4186b
|
@@ -1 +0,0 @@
|
|
1
|
-
b1948b37f2825ae6901ecf5b0f0e77974d383e90
|
@@ -1 +0,0 @@
|
|
1
|
-
c78417c3025b0c430f0270d630bd8cc3d680cbd413481ce57f63f44f682fa734
|
@@ -1 +0,0 @@
|
|
1
|
-
2ed2683d4799f88018b813696666c11fceba5be25b261a780fba9b4f85fa52518577315762760f2c5898bb75dfd8b9f7141a4fe276f17dbf431f4db2f8972b62
|
@@ -1 +0,0 @@
|
|
1
|
-
4a1f392222691dde5ff23796ed1f9be2
|
@@ -1 +0,0 @@
|
|
1
|
-
a26bb973c995a237be6da201ee7ce88c73c1633f
|
@@ -1 +0,0 @@
|
|
1
|
-
88cf8c98c0342ba8052e6483f32c0b21c55e8b49e3e821a8ab7f4d1241f3ac95
|
@@ -1 +0,0 @@
|
|
1
|
-
086ec58c930ddd33b055906bc7baeab54bf0c61e824168e714542b3c12435765ea79207f73084cf9f865d5afd4d0d277454bad7df260d91904fc839068ba3345
|
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
|
-
7ed72c62d3647105b8ba45872b8ea606
|
@@ -1 +0,0 @@
|
|
1
|
-
57c6d248535a325fb3b5f8ff5ae28fe452723d3c
|
@@ -1 +0,0 @@
|
|
1
|
-
33f28069a49462150368c6c44ea5644083c155771aefdd51f0e41858dc43e61d
|
@@ -1 +0,0 @@
|
|
1
|
-
6d57cb6061f616f09785d65238df8c1bb4751a95e65f1dd038b4c4b7d7b72a5d6572fc49ee4085c26a44407219a8c952e46263457fee030a17b3767654557da6
|
@@ -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
|
-
})
|