suitest-js-api 2.5.2 → 3.0.3
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/config/index.js +7 -7
- package/index.d.ts +23 -71
- package/lib/api/endpoints.js +0 -7
- package/lib/api/webSockets.js +1 -1
- package/lib/api/wsContentTypes.js +0 -2
- package/lib/chains/elementChain.js +90 -19
- package/lib/chains/saveScreenshotChain.js +60 -5
- package/lib/commands/closeSession.js +4 -29
- package/lib/commands/openSession.js +13 -52
- package/lib/commands/pairDevice.js +3 -3
- package/lib/commands/releaseDevice.js +1 -1
- package/lib/commands/setAppConfig.js +0 -2
- package/lib/commands/{interactive.js → startREPL.js} +10 -8
- package/lib/composers/attributesComposer.js +18 -0
- package/lib/composers/cssPropsComposer.js +18 -0
- package/lib/composers/handleComposer.js +23 -0
- package/lib/composers/index.js +6 -0
- package/lib/composers/thenComposer.js +1 -2
- package/lib/constants/composer.js +3 -0
- package/lib/constants/element.js +2 -0
- package/lib/constants/enviroment.js +6 -1
- package/lib/constants/index.js +1 -0
- package/lib/constants/ipcMessageId.js +1 -1
- package/lib/constants/modes.js +1 -2
- package/lib/constants/session.js +1 -3
- package/lib/constants/timestamp.js +1 -1
- package/lib/constants/validationKeys.js +5 -5
- package/lib/constants/vrc.js +2 -0
- package/lib/testLauncher/SuitestLauncher.js +65 -77
- package/lib/testLauncher/buildArgs.js +3 -6
- package/lib/testLauncher/commands/run.js +111 -0
- package/lib/testLauncher/composeConfig.js +154 -15
- package/lib/testLauncher/launcherLogger.js +3 -23
- package/lib/testLauncher/processArgs.js +1 -1
- package/lib/testLauncher/repl.js +3 -3
- package/lib/texts.js +18 -39
- package/lib/utils/AuthContext.js +17 -76
- package/lib/utils/getDeviceInfo.js +16 -10
- package/lib/utils/logger.js +4 -3
- package/lib/utils/{interactiveProgressHandler.js → progressHandler.js} +1 -1
- package/lib/utils/sentry/Raven.js +1 -1
- package/lib/utils/sessionStarter.js +43 -38
- package/lib/utils/socketChainHelper.js +17 -8
- package/lib/utils/stackTraceParser.js +25 -1
- package/lib/utils/testHelpers/mockHelpers.js +0 -2
- package/lib/utils/testHelpers/mockWebSocket.js +1 -1
- package/lib/utils/testHelpers/testLauncherTest.js +0 -43
- package/lib/utils/testLauncherHelper.js +38 -105
- package/lib/validation/elementPropTypes.js +2 -0
- package/lib/validation/jsonSchemas.js +97 -97
- package/lib/validation/validators.js +25 -3
- package/lib/validation/validatorsMap.js +13 -13
- package/package.json +7 -5
- package/suitest.js +7 -15
- package/typeDefinition/ElementChain.d.ts +29 -1
- package/typeDefinition/constants/Element.d.ts +2 -0
- package/typeDefinition/constants/Vrc.d.ts +2 -0
- package/typeDefinition/modifiers.d.ts +13 -0
- package/lib/commands/endTest.js +0 -28
- package/lib/commands/startTest.js +0 -55
- package/lib/commands/startTestPack.js +0 -93
- package/lib/testLauncher/commands/automated.js +0 -70
- package/lib/testLauncher/commands/common.js +0 -43
- package/lib/testLauncher/commands/interactive.js +0 -70
package/lib/utils/AuthContext.js
CHANGED
|
@@ -15,89 +15,40 @@ const texts = require('../texts');
|
|
|
15
15
|
* List of request keys and methods allowed by context
|
|
16
16
|
*/
|
|
17
17
|
const guestAllowed = {
|
|
18
|
-
http: {
|
|
18
|
+
http: {},
|
|
19
19
|
ws: [],
|
|
20
20
|
};
|
|
21
|
-
|
|
21
|
+
|
|
22
|
+
const tokenAllowed = {
|
|
22
23
|
http: {
|
|
23
|
-
[endpoints.session]: ['POST'],
|
|
24
|
-
[endpoints.testPackGenTokens]: ['POST'],
|
|
25
24
|
[endpoints.apps]: ['GET'],
|
|
26
25
|
[endpoints.appById]: ['GET'],
|
|
27
26
|
[endpoints.appTags]: ['GET'],
|
|
28
27
|
[endpoints.appConfigs]: ['GET'],
|
|
29
28
|
[endpoints.appConfigById]: ['GET'],
|
|
30
|
-
[endpoints.appTestPacks]: ['GET'],
|
|
31
|
-
[endpoints.appTestPackById]: ['GET'],
|
|
32
29
|
[endpoints.appTestDefinitions]: ['GET'],
|
|
33
30
|
[endpoints.appTestDefinitionById]: ['GET'],
|
|
34
31
|
[endpoints.devices]: ['GET'],
|
|
35
32
|
[endpoints.testRun]: ['POST'],
|
|
36
33
|
[endpoints.testRunById]: ['GET', 'DELETE'],
|
|
37
34
|
[endpoints.testRunOnDevice]: ['DELETE'],
|
|
38
|
-
[endpoints.testPackRun]: ['POST'],
|
|
39
|
-
[endpoints.testPackRunById]: ['GET', 'DELETE'],
|
|
40
|
-
},
|
|
41
|
-
ws: [],
|
|
42
|
-
};
|
|
43
|
-
const interactiveSessionTokenAllowed = {
|
|
44
|
-
http: {
|
|
45
|
-
[endpoints.session]: ['POST'],
|
|
46
|
-
[endpoints.sessionClose]: ['POST'],
|
|
47
|
-
[endpoints.apps]: ['GET'],
|
|
48
|
-
[endpoints.appById]: ['GET'],
|
|
49
|
-
[endpoints.appTags]: ['GET'],
|
|
50
|
-
[endpoints.appConfigs]: ['GET'],
|
|
51
|
-
[endpoints.appConfigById]: ['GET'],
|
|
52
|
-
[endpoints.appTestPacks]: ['GET'],
|
|
53
|
-
[endpoints.appTestPackById]: ['GET'],
|
|
54
|
-
[endpoints.appTestDefinitions]: ['GET'],
|
|
55
|
-
[endpoints.appTestDefinitionById]: ['GET'],
|
|
56
|
-
[endpoints.devices]: ['GET'],
|
|
57
35
|
},
|
|
58
36
|
ws: [
|
|
59
|
-
wsContentTypes.startTest, wsContentTypes.endTest,
|
|
60
37
|
wsContentTypes.pairDevice, wsContentTypes.releaseDevice,
|
|
61
38
|
wsContentTypes.selectConfiguration, wsContentTypes.query,
|
|
62
39
|
wsContentTypes.eval, wsContentTypes.testLine,
|
|
63
40
|
wsContentTypes.takeScreenshot,
|
|
64
41
|
],
|
|
65
42
|
};
|
|
66
|
-
|
|
67
|
-
http: {
|
|
68
|
-
[endpoints.session]: ['POST'],
|
|
69
|
-
[endpoints.sessionClose]: ['POST'],
|
|
70
|
-
[endpoints.apps]: ['GET'],
|
|
71
|
-
[endpoints.appById]: ['GET'],
|
|
72
|
-
[endpoints.appTags]: ['GET'],
|
|
73
|
-
[endpoints.appConfigs]: ['GET'],
|
|
74
|
-
[endpoints.appConfigById]: ['GET'],
|
|
75
|
-
[endpoints.appTestPacks]: ['GET'],
|
|
76
|
-
[endpoints.appTestPackById]: ['GET'],
|
|
77
|
-
[endpoints.appTestDefinitions]: ['GET'],
|
|
78
|
-
[endpoints.appTestDefinitionById]: ['GET'],
|
|
79
|
-
[endpoints.devices]: ['GET'],
|
|
80
|
-
},
|
|
81
|
-
ws: [
|
|
82
|
-
wsContentTypes.startTest, wsContentTypes.endTest,
|
|
83
|
-
wsContentTypes.pairDevice, wsContentTypes.releaseDevice,
|
|
84
|
-
wsContentTypes.selectConfiguration, wsContentTypes.query,
|
|
85
|
-
wsContentTypes.eval, wsContentTypes.testLine,
|
|
86
|
-
wsContentTypes.takeScreenshot
|
|
87
|
-
],
|
|
88
|
-
};
|
|
43
|
+
|
|
89
44
|
const allowedRequests = {
|
|
90
45
|
http: {
|
|
91
|
-
[sessionConstants.ACCESS_TOKEN]: accessTokenAllowed.http,
|
|
92
|
-
[sessionConstants.AUTOMATED]: automatedSessionTokenAllowed.http,
|
|
93
|
-
[sessionConstants.INTERACTIVE]: interactiveSessionTokenAllowed.http,
|
|
94
46
|
[sessionConstants.GUEST]: guestAllowed.http,
|
|
47
|
+
[sessionConstants.TOKEN]: tokenAllowed.http,
|
|
95
48
|
},
|
|
96
49
|
ws: {
|
|
97
|
-
[sessionConstants.ACCESS_TOKEN]: accessTokenAllowed.ws,
|
|
98
|
-
[sessionConstants.AUTOMATED]: automatedSessionTokenAllowed.ws,
|
|
99
|
-
[sessionConstants.INTERACTIVE]: interactiveSessionTokenAllowed.ws,
|
|
100
50
|
[sessionConstants.GUEST]: guestAllowed.ws,
|
|
51
|
+
[sessionConstants.TOKEN]: tokenAllowed.ws,
|
|
101
52
|
},
|
|
102
53
|
};
|
|
103
54
|
|
|
@@ -134,7 +85,8 @@ class AuthContext extends Context {
|
|
|
134
85
|
super();
|
|
135
86
|
|
|
136
87
|
this.headers = {};
|
|
137
|
-
this.
|
|
88
|
+
this.tokenId = '';
|
|
89
|
+
this.tokenPassword = '';
|
|
138
90
|
super.setContext(sessionConstants.GUEST);
|
|
139
91
|
}
|
|
140
92
|
|
|
@@ -142,27 +94,20 @@ class AuthContext extends Context {
|
|
|
142
94
|
* Set context. Update headers based on new context.
|
|
143
95
|
*
|
|
144
96
|
* @param {Symbol} newContext context symbol constant
|
|
145
|
-
* @param {string|undefined} tokenId
|
|
146
|
-
* @param {string|undefined} tokenPassword
|
|
97
|
+
* @param {string|undefined} [tokenId]
|
|
98
|
+
* @param {string|undefined} [tokenPassword]
|
|
147
99
|
*/
|
|
148
100
|
setContext(newContext, tokenId, tokenPassword) {
|
|
149
101
|
switch (newContext) {
|
|
150
|
-
case sessionConstants.
|
|
102
|
+
case sessionConstants.TOKEN:
|
|
151
103
|
super.setContext(newContext);
|
|
152
|
-
this.
|
|
104
|
+
this.tokenId = tokenId;
|
|
105
|
+
this.tokenPassword = tokenPassword;
|
|
153
106
|
this.headers = {
|
|
154
|
-
'
|
|
155
|
-
'X-TokenPassword': tokenPassword,
|
|
107
|
+
'Authorization': `Basic ${Buffer.from(`${tokenId}:${tokenPassword}`).toString('base64')}`,
|
|
156
108
|
};
|
|
157
109
|
break;
|
|
158
110
|
|
|
159
|
-
case sessionConstants.AUTOMATED:
|
|
160
|
-
case sessionConstants.INTERACTIVE:
|
|
161
|
-
super.setContext(newContext);
|
|
162
|
-
this.tokenKey = tokenId;
|
|
163
|
-
this.headers = {'deviceAccessToken': tokenId};
|
|
164
|
-
break;
|
|
165
|
-
|
|
166
111
|
default:
|
|
167
112
|
this.clear();
|
|
168
113
|
break;
|
|
@@ -170,12 +115,12 @@ class AuthContext extends Context {
|
|
|
170
115
|
}
|
|
171
116
|
|
|
172
117
|
/**
|
|
173
|
-
*
|
|
118
|
+
* Reset context to default
|
|
174
119
|
*/
|
|
175
120
|
clear() {
|
|
176
121
|
super.setContext(sessionConstants.GUEST);
|
|
177
122
|
this.headers = {};
|
|
178
|
-
this.
|
|
123
|
+
this.tokenId = '';
|
|
179
124
|
}
|
|
180
125
|
|
|
181
126
|
/**
|
|
@@ -226,11 +171,7 @@ class AuthContext extends Context {
|
|
|
226
171
|
}
|
|
227
172
|
|
|
228
173
|
getToken() {
|
|
229
|
-
return this.
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
isInteractiveSession() {
|
|
233
|
-
return this.context === sessionConstants.INTERACTIVE;
|
|
174
|
+
return `${this.tokenId}:${this.tokenPassword}`;
|
|
234
175
|
}
|
|
235
176
|
}
|
|
236
177
|
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
const request = require('../api/request');
|
|
2
2
|
const endpoints = require('../api/endpoints');
|
|
3
3
|
const fetch = require('node-fetch');
|
|
4
|
-
const
|
|
4
|
+
const R = require('ramda');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Return devices details
|
|
8
8
|
* @param {Object} instance main class instance
|
|
9
|
-
* @param {array}
|
|
9
|
+
* @param {array} devicesList array of devices id's
|
|
10
10
|
* @returns {object}
|
|
11
11
|
*/
|
|
12
|
-
async function getDevicesDetails({authContext},
|
|
12
|
+
async function getDevicesDetails({authContext}, devicesList) {
|
|
13
13
|
const authorizeHttp = await authContext.authorizeHttp(endpoints.devices, {
|
|
14
14
|
method: 'GET',
|
|
15
15
|
});
|
|
16
16
|
let response = await request(
|
|
17
17
|
[endpoints.devices, null, {limit: 100}],
|
|
18
|
-
authorizeHttp
|
|
18
|
+
authorizeHttp,
|
|
19
19
|
);
|
|
20
20
|
let devices = [...response.values];
|
|
21
21
|
|
|
@@ -25,14 +25,20 @@ async function getDevicesDetails({authContext}, devicesIds) {
|
|
|
25
25
|
devices = [...devices, ...response.values];
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
return devicesList.reduce((result, d) => {
|
|
29
|
+
const device = devices.find(respDev => respDev.deviceId === d.device);
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
if (device) {
|
|
32
|
+
result.push({
|
|
33
|
+
...device,
|
|
34
|
+
...R.pick(['config', 'presetName'], d),
|
|
35
|
+
displayName: getDeviceName(device),
|
|
36
|
+
shortDisplayName: getDeviceName(device, true),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
return result;
|
|
41
|
+
}, []);
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
/**
|
package/lib/utils/logger.js
CHANGED
|
@@ -198,8 +198,7 @@ const createLogger = (config, pairedDeviceContext) => {
|
|
|
198
198
|
|
|
199
199
|
const fontColor = method.color || stColors.suit;
|
|
200
200
|
const consoleMethod = console[method.consoleMethod];
|
|
201
|
-
|
|
202
|
-
let {opType, logMessages} = extractOpType(messages);
|
|
201
|
+
const {opType, logMessages} = extractOpType(messages);
|
|
203
202
|
|
|
204
203
|
const logSource = devicePrefix();
|
|
205
204
|
|
|
@@ -283,7 +282,9 @@ const createLogger = (config, pairedDeviceContext) => {
|
|
|
283
282
|
},
|
|
284
283
|
|
|
285
284
|
intro: (message, ...params) => {
|
|
286
|
-
const paintedParams = params.map(param =>
|
|
285
|
+
const paintedParams = params.map(param => {
|
|
286
|
+
return param === null || param === undefined ? param : stColors.bold(param);
|
|
287
|
+
});
|
|
287
288
|
|
|
288
289
|
logger.log(stColors.suit(message(...paintedParams)));
|
|
289
290
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* During
|
|
2
|
+
* During a test execution there are often periods of time
|
|
3
3
|
* where longer waits occur and it is not immediately obvious why it is so.
|
|
4
4
|
* In this case server sends WS events with content type 'progress'.
|
|
5
5
|
* Description is printed to stdout.
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
const
|
|
2
|
-
const {AUTOMATED} = require('../constants/modes');
|
|
1
|
+
const {TOKEN} = require('../constants/modes');
|
|
3
2
|
const chainPromise = require('./chainPromise');
|
|
4
3
|
const {validate, validators} = require('../validation');
|
|
5
4
|
const {captureException} = require('./sentry/Raven');
|
|
6
|
-
const wsContentTypes = require('../api/wsContentTypes');
|
|
7
5
|
const envVars = require('../constants/enviroment');
|
|
8
6
|
const messageId = require('../constants/ipcMessageId');
|
|
9
7
|
const ipcClient = require('../testLauncher/ipc/client');
|
|
10
|
-
const {
|
|
11
|
-
const
|
|
8
|
+
const {replWarn} = require('../texts');
|
|
9
|
+
const wsContentTypes = require('../api/wsContentTypes');
|
|
12
10
|
|
|
13
11
|
// Unchained commands
|
|
14
12
|
const {openSessionUnchained: openSession} = require('../commands/openSession');
|
|
15
13
|
const {pairDeviceUnchained: pairDevice} = require('../commands/pairDevice');
|
|
16
14
|
const {setAppConfigUnchained: setAppConfig} = require('../commands/setAppConfig');
|
|
15
|
+
const R = require('ramda');
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Connect child process to launcher IPC server
|
|
@@ -33,47 +32,45 @@ const connectToMasterIpc = (port) => {
|
|
|
33
32
|
};
|
|
34
33
|
|
|
35
34
|
/**
|
|
36
|
-
* Start
|
|
35
|
+
* Start suitest session based on config
|
|
37
36
|
* @param {Object} suitest instance
|
|
38
37
|
* @param {string} deviceId
|
|
39
|
-
* @param {
|
|
38
|
+
* @param {string} configId
|
|
39
|
+
* @param {string} [presetName]
|
|
40
40
|
*/
|
|
41
|
-
const bootstrapSession = async(suitest, deviceId,
|
|
41
|
+
const bootstrapSession = async(suitest, {deviceId, configId, presetName}) => {
|
|
42
42
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
validate(validators.SESSION_BOOTSTRAP_AUTOMATED, config, 'suitest automated config');
|
|
43
|
+
if (suitest.config.sessionType === TOKEN) {
|
|
44
|
+
validate(validators.SESSION_BOOTSTRAP_TOKEN, suitest.config, 'suitest token configuration');
|
|
46
45
|
await openSession(
|
|
47
46
|
suitest,
|
|
48
|
-
{
|
|
49
|
-
|
|
47
|
+
{
|
|
48
|
+
tokenId: suitest.config.tokenId,
|
|
49
|
+
tokenPassword: suitest.config.tokenPassword,
|
|
50
|
+
},
|
|
50
51
|
);
|
|
51
|
-
const pairDeviceResult = await pairDevice(suitest, deviceId);
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
const configIdOrOverride = R.path([presetName, 'config'], suitest.config.presets);
|
|
54
|
+
const configOverride = R.is(Object, configIdOrOverride)
|
|
55
|
+
? R.omit(['configId'], configIdOrOverride)
|
|
56
|
+
: {};
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
await setAppConfig(suitest, configId, configOverride);
|
|
59
|
+
await pairDevice(suitest, deviceId);
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
if (suitest.config.isDebugMode) {
|
|
62
|
+
await suitest.webSockets.send({type: wsContentTypes.enableDebugMode});
|
|
63
|
+
}
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
await suitest.webSockets.send({type: wsContentTypes.enableDebugMode});
|
|
65
|
+
// add startREPL command only to child process instance and if running single device
|
|
66
|
+
suitest.startREPL = suitest.config.runsOnSingleDevice
|
|
67
|
+
? require('../../lib/commands/startREPL')
|
|
68
|
+
: () => {
|
|
69
|
+
suitest.logger.warn(replWarn());
|
|
70
|
+
|
|
71
|
+
return Promise.resolve();
|
|
72
|
+
};
|
|
72
73
|
}
|
|
73
|
-
// add interactive command only to child process instance and if mode is interactive
|
|
74
|
-
suitest.interactive = require('../../lib/commands/interactive');
|
|
75
|
-
await pairDevice(suitest, deviceId);
|
|
76
|
-
await setAppConfig(suitest, config.appConfigId);
|
|
77
74
|
} catch (error) {
|
|
78
75
|
await captureException(error);
|
|
79
76
|
// copy of handleLauncherError not imported to avoid cycle dependencies
|
|
@@ -94,12 +91,20 @@ const bootstrapSession = async(suitest, deviceId, config) => {
|
|
|
94
91
|
/* istanbul ignore next */
|
|
95
92
|
const connectToIpcAndBootstrapSession = async(suitest) => {
|
|
96
93
|
if (process.env[envVars.SUITEST_CHILD_PROCESS]) {
|
|
97
|
-
const [deviceId, ipcPort] = process.env[envVars.SUITEST_CHILD_PROCESS].split('|');
|
|
98
|
-
|
|
94
|
+
const [deviceId, configId, ipcPort] = process.env[envVars.SUITEST_CHILD_PROCESS].split('|');
|
|
95
|
+
|
|
96
|
+
const suitestCtx = await connectToMasterIpc(ipcPort);
|
|
99
97
|
|
|
100
|
-
suitest.configuration.extend(
|
|
98
|
+
suitest.configuration.extend(suitestCtx);
|
|
101
99
|
|
|
102
|
-
await bootstrapSession(
|
|
100
|
+
await bootstrapSession(
|
|
101
|
+
suitest,
|
|
102
|
+
{
|
|
103
|
+
deviceId,
|
|
104
|
+
configId,
|
|
105
|
+
presetName: process.env[envVars.SUITEST_PRESET_NAME],
|
|
106
|
+
},
|
|
107
|
+
);
|
|
103
108
|
}
|
|
104
109
|
};
|
|
105
110
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
2
3
|
const {isNil} = require('ramda');
|
|
3
4
|
const assert = require('assert');
|
|
4
5
|
const SuitestError = require('./SuitestError');
|
|
@@ -9,8 +10,8 @@ const {
|
|
|
9
10
|
responseMessageCode,
|
|
10
11
|
} = require('./socketErrorMessages');
|
|
11
12
|
const t = require('../texts');
|
|
12
|
-
const {mapLogLevelsToTranslationModule} = require(
|
|
13
|
-
const {getSnippetLogs} = require(
|
|
13
|
+
const {mapLogLevelsToTranslationModule} = require('./chainUtils');
|
|
14
|
+
const {getSnippetLogs} = require('./socketErrorMessages');
|
|
14
15
|
|
|
15
16
|
const assertionErrorTypes = ['queryFailed', 'appRunning', 'appNotRunning', 'queryTimeout'];
|
|
16
17
|
|
|
@@ -63,7 +64,11 @@ const processServerResponse = (logger, verbosity) =>
|
|
|
63
64
|
'cookieValue',
|
|
64
65
|
'cookieExists',
|
|
65
66
|
'elementProps',
|
|
67
|
+
'elementCssProps',
|
|
66
68
|
'elementExists',
|
|
69
|
+
'elementHandles',
|
|
70
|
+
'elementHandle',
|
|
71
|
+
'attributes',
|
|
67
72
|
'execute',
|
|
68
73
|
'location',
|
|
69
74
|
].find(key => key in res);
|
|
@@ -121,12 +126,16 @@ const processServerResponse = (logger, verbosity) =>
|
|
|
121
126
|
} else if (data.dataFormat === 'base64') {
|
|
122
127
|
return res.buffer.toString('base64');
|
|
123
128
|
} else if (data.fileName) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
const dir = path.dirname(data.fileName);
|
|
130
|
+
|
|
131
|
+
return fs.promises
|
|
132
|
+
.access(dir)
|
|
133
|
+
.catch(() => fs.promises.mkdir(dir))
|
|
134
|
+
.then(() => fs.promises.writeFile(data.fileName, res.buffer))
|
|
135
|
+
.catch(e => {
|
|
136
|
+
logger.error(e.message);
|
|
137
|
+
throwErr(e);
|
|
138
|
+
});
|
|
130
139
|
}
|
|
131
140
|
|
|
132
141
|
return;
|
|
@@ -22,10 +22,18 @@ function fetchSource(error, contextSize = 5) {
|
|
|
22
22
|
return sourceLines.slice(start, end);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {Object} StackTraceItem
|
|
27
|
+
* @property {number} line
|
|
28
|
+
* @property {number} column
|
|
29
|
+
* @property {string} function
|
|
30
|
+
* @property {string} file
|
|
31
|
+
*/
|
|
32
|
+
|
|
25
33
|
/**
|
|
26
34
|
* Parse error stack trace
|
|
27
35
|
* @param {Error|undefined} [error]
|
|
28
|
-
* @returns {string|
|
|
36
|
+
* @returns {string|Array<StackTraceItem>} parsed or stringified stack trace
|
|
29
37
|
* @example [{
|
|
30
38
|
* "line": 4,
|
|
31
39
|
* "column": 11,
|
|
@@ -118,6 +126,21 @@ function getFirstStackLine(stack) {
|
|
|
118
126
|
return stripAbsolutePaths(stripSuitestCodeFromStack(stack)).split('\n').find(isStackLine) || '';
|
|
119
127
|
}
|
|
120
128
|
|
|
129
|
+
/**
|
|
130
|
+
* @description get first non suitest stack item.
|
|
131
|
+
* @returns {void | StackTraceItem}
|
|
132
|
+
*/
|
|
133
|
+
function getFirstNotSuitestStackItem(error = new Error()) {
|
|
134
|
+
/** @type {StackTraceItem[]} */
|
|
135
|
+
const parsedStack = stackTraceParser(error);
|
|
136
|
+
|
|
137
|
+
if (!Array.isArray(parsedStack)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return parsedStack.find(x => !isSuitestMethod(x.file));
|
|
142
|
+
}
|
|
143
|
+
|
|
121
144
|
/**
|
|
122
145
|
* Remember call stack before function execution
|
|
123
146
|
* If error is thrown, prepend error stack with cached stack
|
|
@@ -146,6 +169,7 @@ module.exports = {
|
|
|
146
169
|
prependStack,
|
|
147
170
|
stackTraceWrapper,
|
|
148
171
|
getFirstStackLine,
|
|
172
|
+
getFirstNotSuitestStackItem,
|
|
149
173
|
isStackLine,
|
|
150
174
|
stripAbsolutePaths,
|
|
151
175
|
};
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/* istanbul ignore file */
|
|
2
2
|
|
|
3
|
-
const sinon = require('sinon');
|
|
4
3
|
const mock = require('mock-require');
|
|
5
4
|
const testLauncherHelper = require('../testLauncherHelper');
|
|
6
5
|
|
|
7
6
|
mock('../testLauncherHelper', {
|
|
8
7
|
...testLauncherHelper,
|
|
9
|
-
promptPassword: sinon.stub().resolves('password'),
|
|
10
8
|
});
|
|
@@ -7,7 +7,7 @@ const {v1: uuid} = require('uuid');
|
|
|
7
7
|
const {config} = require('../../../index');
|
|
8
8
|
const SuitestError = require('../SuitestError');
|
|
9
9
|
const texts = require('../../texts');
|
|
10
|
-
const {handleProgress} = require('../
|
|
10
|
+
const {handleProgress} = require('../progressHandler');
|
|
11
11
|
const mock = require('mock-require');
|
|
12
12
|
|
|
13
13
|
let ws = null,
|
|
@@ -4,9 +4,6 @@ require('../../../config/setTestConfig');
|
|
|
4
4
|
require('../../utils/testHelpers/mockWebSocket');
|
|
5
5
|
const testServer = require('../../utils/testServer');
|
|
6
6
|
const nock = require('nock');
|
|
7
|
-
const makeUrlFromArray = require('../makeUrlFromArray');
|
|
8
|
-
const endpoints = require('../../api/endpoints');
|
|
9
|
-
const config = require('../../../config');
|
|
10
7
|
const stubDeviceInfoFeed = require('./mockDeviceInfo');
|
|
11
8
|
|
|
12
9
|
process.on('exit', () => {
|
|
@@ -17,48 +14,8 @@ process.on('exit', () => {
|
|
|
17
14
|
async function setUp() {
|
|
18
15
|
await testServer.restart();
|
|
19
16
|
|
|
20
|
-
// automated
|
|
21
|
-
nock(config.apiUrl)
|
|
22
|
-
.post(makeUrlFromArray([endpoints.testPackGenTokens, {id: 10}]))
|
|
23
|
-
.reply(
|
|
24
|
-
200,
|
|
25
|
-
{
|
|
26
|
-
deviceAccessToken: 'deviceAccessToken',
|
|
27
|
-
testPack: {
|
|
28
|
-
devices: [{deviceId: '1'}],
|
|
29
|
-
},
|
|
30
|
-
}
|
|
31
|
-
);
|
|
32
|
-
|
|
33
17
|
stubDeviceInfoFeed();
|
|
34
18
|
|
|
35
|
-
// automated no devices
|
|
36
|
-
nock(config.apiUrl)
|
|
37
|
-
.post(makeUrlFromArray([endpoints.testPackGenTokens, {id: 20}]))
|
|
38
|
-
.reply(
|
|
39
|
-
200,
|
|
40
|
-
{
|
|
41
|
-
deviceAccessToken: 'deviceAccessToken',
|
|
42
|
-
testPack: {
|
|
43
|
-
devices: [],
|
|
44
|
-
},
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
// interactive
|
|
49
|
-
nock(config.apiUrl)
|
|
50
|
-
.post(makeUrlFromArray([endpoints.session, {
|
|
51
|
-
username: 'userEmail',
|
|
52
|
-
password: 'userPass',
|
|
53
|
-
orgId: 'orgId',
|
|
54
|
-
}]))
|
|
55
|
-
.reply(200, {deviceAccessToken: 'deviceAccessToken'});
|
|
56
|
-
|
|
57
|
-
// close session
|
|
58
|
-
nock(config.apiUrl)
|
|
59
|
-
.post(endpoints.sessionClose)
|
|
60
|
-
.reply(200, {});
|
|
61
|
-
|
|
62
19
|
const suitest = require('../../../index');
|
|
63
20
|
|
|
64
21
|
suitest.logger.success = () => null;
|