@testim/testim-cli 3.241.0 → 3.242.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/npm-shrinkwrap.json +865 -737
- package/package.json +3 -3
- package/player/services/playbackTimeoutCalculator.js +26 -25
- package/processHandler.js +12 -9
- package/processHandler.test.js +56 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testim/testim-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.242.0",
|
|
4
4
|
"description": "Command line interface for running Testing on your CI",
|
|
5
5
|
"author": "Oren Rubin",
|
|
6
6
|
"contributors": [{
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"multer": "1.4.4"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@applitools/eyes-sdk-core": "13.
|
|
51
|
-
"@applitools/visual-grid-client": "15.
|
|
50
|
+
"@applitools/eyes-sdk-core": "13.6.23",
|
|
51
|
+
"@applitools/visual-grid-client": "15.12.34",
|
|
52
52
|
"@testim/coralogix-logger": "1.1.27-beta.1",
|
|
53
53
|
"@testim/webdriverio": "0.0.5",
|
|
54
54
|
"abort-controller": "3.0.0",
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
3
|
const _ = require('lodash');
|
|
4
|
-
const logger = require('../../commons/logger').getLogger('playback-timeout-calculator');
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
const COMMUNICATION_BUFFER_TIME = 1000;
|
|
6
|
+
const UI_VERIFICATION_STEPS = ['simple-ui-verification', 'wait-for-simple-ui-verification'];
|
|
7
7
|
|
|
8
8
|
class PlaybackTimeoutCalculator {
|
|
9
|
-
|
|
10
9
|
constructor(isDebuggerConnected) {
|
|
11
10
|
this.resetStepVariables();
|
|
12
11
|
this.resetRetryVariables();
|
|
@@ -18,13 +17,13 @@ class PlaybackTimeoutCalculator {
|
|
|
18
17
|
this.totalStepTime = totalStepTime || 0;
|
|
19
18
|
this.totalStepTimesReport = [];
|
|
20
19
|
this.currentRetryTimesReport = {};
|
|
21
|
-
|
|
20
|
+
const now = Date.now();
|
|
22
21
|
this.currentRetryStart = now;
|
|
23
22
|
this.lastUpdateTime = now;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
resetRetryVariables() {
|
|
27
|
-
|
|
26
|
+
const now = Date.now();
|
|
28
27
|
this.currentRetryStart = now;
|
|
29
28
|
this.lastUpdateTime = now;
|
|
30
29
|
this.totalStepTimesReport.push(this.currentRetryTimesReport);
|
|
@@ -32,7 +31,7 @@ class PlaybackTimeoutCalculator {
|
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
initStepRun(stepPlayback) {
|
|
35
|
-
|
|
34
|
+
const getRetryTimeoutSuggestions = (totalStepTime) => {
|
|
36
35
|
const timeToPlayStep = this.getTotalStepTimeLeftToPlay(stepPlayback, totalStepTime);
|
|
37
36
|
const MINIMAL_RETRY_TIME = 5000;
|
|
38
37
|
if (timeToPlayStep <= MINIMAL_RETRY_TIME) {
|
|
@@ -42,7 +41,7 @@ class PlaybackTimeoutCalculator {
|
|
|
42
41
|
};
|
|
43
42
|
stepPlayback.setStartTimestamp();
|
|
44
43
|
const totalStepTime = this.getTotalStepRunTime(stepPlayback);
|
|
45
|
-
|
|
44
|
+
const currentRetryTimes = [...UI_VERIFICATION_STEPS, 'custom-validation'].includes(stepPlayback.stepType) ? [totalStepTime] : getRetryTimeoutSuggestions(totalStepTime);
|
|
46
45
|
this.resetStepVariables(totalStepTime, currentRetryTimes);
|
|
47
46
|
stepPlayback.context.data.maxTotalStepTime = totalStepTime;
|
|
48
47
|
}
|
|
@@ -57,9 +56,12 @@ class PlaybackTimeoutCalculator {
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
getTotalStepRunTime(stepPlayback) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
const HALF_HOUR_IN_MS = 30 * 60 * 1000;
|
|
60
|
+
let fallbackTimeout = stepPlayback.context.config.stepTimeout;
|
|
61
|
+
if (UI_VERIFICATION_STEPS.includes(stepPlayback.stepType)) {
|
|
62
|
+
fallbackTimeout = stepPlayback.context.config.applitoolsStepTimeout || HALF_HOUR_IN_MS;
|
|
63
|
+
}
|
|
64
|
+
return (stepPlayback.step.useStepTimeout && stepPlayback.step.stepTimeout) ? stepPlayback.step.stepTimeout : fallbackTimeout;
|
|
63
65
|
}
|
|
64
66
|
getTotalStepTimeLeftToPlay(stepPlayback, totalStepTime = this.totalStepTime) {
|
|
65
67
|
const playTimeSoFar = Date.now() - stepPlayback.startTimestamp;
|
|
@@ -71,7 +73,7 @@ class PlaybackTimeoutCalculator {
|
|
|
71
73
|
this.getTotalStepTimeLeftToPlay(stepPlayback);
|
|
72
74
|
}
|
|
73
75
|
getTotalCurrentRetryTimeLeft(stepPlayback) {
|
|
74
|
-
|
|
76
|
+
const totalRetryTime = Date.now() - this.currentRetryStart;
|
|
75
77
|
return this.getCurrentRetryTime(stepPlayback) - totalRetryTime + COMMUNICATION_BUFFER_TIME;
|
|
76
78
|
}
|
|
77
79
|
getTabTimeout(stepPlayback) {
|
|
@@ -97,7 +99,6 @@ class PlaybackTimeoutCalculator {
|
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
return (stepPlayback.step.events.length * timePerEvent) + buffer;
|
|
100
|
-
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
getActionTimeout(stepPlayback) {
|
|
@@ -108,10 +109,10 @@ class PlaybackTimeoutCalculator {
|
|
|
108
109
|
const actionType = stepPlayback.step.type;
|
|
109
110
|
const MIN_ACTION_PLAYBACK_TIME = 30000;
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
if (actionType ===
|
|
112
|
+
let actionTime;
|
|
113
|
+
if (actionType === 'sleep') {
|
|
113
114
|
actionTime = stepPlayback.step.durationMS + SLEEP_ERROR_MARGIN_MS;
|
|
114
|
-
} else if (actionType ===
|
|
115
|
+
} else if (actionType === 'android-scroll') {
|
|
115
116
|
actionTime = Math.max(this.calcAndroidScrollTimeout(stepPlayback), MIN_ACTION_PLAYBACK_TIME);
|
|
116
117
|
} else {
|
|
117
118
|
actionTime = Math.max(this.getTotalStepTimeLeftToPlay(stepPlayback), MIN_ACTION_PLAYBACK_TIME);
|
|
@@ -120,34 +121,34 @@ class PlaybackTimeoutCalculator {
|
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
setStepPhaseTime(phase) {
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
const now = Date.now();
|
|
125
|
+
const totalTime = now - this.lastUpdateTime;
|
|
125
126
|
this.lastUpdateTime = now;
|
|
126
127
|
this.currentRetryTimesReport[phase] = totalTime;
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
reportGetTabTime() {
|
|
130
|
-
this.setStepPhaseTime(
|
|
131
|
+
this.setStepPhaseTime('tab');
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
reportGetFrameTime() {
|
|
134
|
-
this.setStepPhaseTime(
|
|
135
|
+
this.setStepPhaseTime('frame');
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
reportCalcConditionTime() {
|
|
138
|
-
this.setStepPhaseTime(
|
|
139
|
+
this.setStepPhaseTime('condition');
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
reportPreLocateActionsTime() {
|
|
142
|
-
this.setStepPhaseTime(
|
|
143
|
+
this.setStepPhaseTime('pre-locate');
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
reportFindElementsTime() {
|
|
146
|
-
this.setStepPhaseTime(
|
|
147
|
+
this.setStepPhaseTime('locate');
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
reportStepActionTime() {
|
|
150
|
-
this.setStepPhaseTime(
|
|
151
|
+
this.setStepPhaseTime('action');
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
|
package/processHandler.js
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
+
const Promise = require('bluebird');
|
|
5
6
|
const logger = require('./commons/logger').getLogger('process-handler');
|
|
6
7
|
|
|
7
8
|
const exitHooks = [];
|
|
8
|
-
const Promise = require('bluebird');
|
|
9
9
|
|
|
10
|
-
module.exports = function (onExit) {
|
|
10
|
+
module.exports = function (onExit, _process = process) {
|
|
11
11
|
async function cleanup(err) {
|
|
12
12
|
// give cleanup and socket reports a chance to run
|
|
13
13
|
await Promise.all(exitHooks.map(x => x())).timeout(10000).catch(() => {});
|
|
14
14
|
onExit(err);
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
_process.on('uncaughtException', async (err) => {
|
|
17
17
|
logger.error('Caught exception', { err });
|
|
18
18
|
console.log('Uncaught exception');
|
|
19
19
|
if (err.message) {
|
|
@@ -25,7 +25,7 @@ module.exports = function (onExit) {
|
|
|
25
25
|
await cleanup(err);
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
_process.on('unhandledRejection', (reason) => {
|
|
29
29
|
// rollout manages promises incorrectly and generates unhandled rejections from within their code
|
|
30
30
|
logger.fatal('Caught unhandled promise rejection', reason);
|
|
31
31
|
//TODO(benji) - this is a pretty shitty way to detect this error since rollout can change their API endpoint
|
|
@@ -37,11 +37,11 @@ module.exports = function (onExit) {
|
|
|
37
37
|
throw reason;
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
_process.on('rejectionHandled', () => {
|
|
41
41
|
logger.error('Caught rejection handled');
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
_process.once('SIGTERM', () => {
|
|
45
45
|
const msg = 'Runner aborted - SIGTERM event';
|
|
46
46
|
const err = new Error(msg);
|
|
47
47
|
logger.error(msg);
|
|
@@ -49,7 +49,7 @@ module.exports = function (onExit) {
|
|
|
49
49
|
throw err;
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
_process.once('SIGINT', () => {
|
|
53
53
|
const msg = 'Runner aborted - SIGINT event';
|
|
54
54
|
const err = new Error(msg);
|
|
55
55
|
logger.error(msg);
|
|
@@ -58,12 +58,15 @@ module.exports = function (onExit) {
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
// One time self-call is expected :(
|
|
61
|
-
|
|
61
|
+
_process.once('exit', (e) => {
|
|
62
62
|
onExit(e);
|
|
63
63
|
});
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
-
|
|
67
66
|
module.exports.registerExitHook = function (hook) {
|
|
68
67
|
exitHooks.push(hook);
|
|
69
68
|
};
|
|
69
|
+
|
|
70
|
+
module.exports.reset = function () {
|
|
71
|
+
exitHooks.splice(0, exitHooks.length);
|
|
72
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
const { delay } = require('bluebird');
|
|
3
|
+
const EventEmitter = require('events');
|
|
4
|
+
const { expect, sinon } = require('../test/utils/testUtils');
|
|
5
|
+
const processHandler = require('./processHandler');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Process extends EventEmitter {
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
this.stdout = new EventEmitter();
|
|
12
|
+
this.stderr = new EventEmitter();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('testimTunnel', () => {
|
|
17
|
+
let process;
|
|
18
|
+
let onExitMock;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
process = new Process();
|
|
22
|
+
onExitMock = sinon.spy();
|
|
23
|
+
processHandler(onExitMock, process);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(async () => {
|
|
27
|
+
await delay(10);
|
|
28
|
+
expect(onExitMock).to.have.been.calledOnce;
|
|
29
|
+
processHandler.reset();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
it('should register a SIGTERM handler', (done) => {
|
|
34
|
+
processHandler.registerExitHook(done);
|
|
35
|
+
expect(() => process.emit('SIGTERM')).to.throw('Runner aborted - SIGTERM event');
|
|
36
|
+
});
|
|
37
|
+
it('should register a SIGINT handler', (done) => {
|
|
38
|
+
processHandler.registerExitHook(done);
|
|
39
|
+
expect(() => process.emit('SIGINT')).to.throw('Runner aborted - SIGINT event');
|
|
40
|
+
});
|
|
41
|
+
it('should register a unhandledRejection handler', () => {
|
|
42
|
+
expect(() => process.emit('unhandledRejection', new Error('reason'))).to.throw('reason');
|
|
43
|
+
onExitMock();
|
|
44
|
+
});
|
|
45
|
+
it('should register a uncaughtException handler', (done) => {
|
|
46
|
+
processHandler.registerExitHook(done);
|
|
47
|
+
expect(() => process.emit('uncaughtException', new Error())).not.to.throw();
|
|
48
|
+
});
|
|
49
|
+
it('should do nothing on rejectionHandled', () => {
|
|
50
|
+
expect(() => process.emit('rejectionHandled')).not.to.throw();
|
|
51
|
+
onExitMock();
|
|
52
|
+
});
|
|
53
|
+
it('should register a exit handler', () => {
|
|
54
|
+
expect(() => process.emit('exit')).not.to.throw();
|
|
55
|
+
});
|
|
56
|
+
});
|