suitest-js-api 3.7.2 → 3.8.1
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/README.md +4 -2
- package/config/index.js +2 -2
- package/index.d.ts +7 -1
- package/lib/api/webSockets.js +15 -0
- package/lib/api/wsContentTypes.js +2 -0
- package/lib/commands/pairDevice.js +15 -1
- package/lib/commands/startRecording.js +33 -0
- package/lib/commands/stopRecording.js +31 -0
- package/lib/composers/acceptModalComposer.js +12 -9
- package/lib/composers/cloneComposer.js +1 -1
- package/lib/composers/matchBrightScriptComposer.js +1 -1
- package/lib/composers/matchRepoComposer.js +14 -11
- package/lib/composers/requestMatchesComposer.js +2 -2
- package/lib/composers/responseMatchesComposer.js +3 -3
- package/lib/composers/swipeComposer.js +16 -7
- package/lib/constants/{enviroment.js → environment.js} +2 -2
- package/lib/constants/recordingOptions.js +13 -0
- package/lib/constants/screenOrientation.js +2 -0
- package/lib/testLauncher/commands/run.js +11 -0
- package/lib/testLauncher/repl.js +1 -1
- package/lib/texts.js +3 -0
- package/lib/utils/AuthContext.js +2 -1
- package/lib/utils/sessionStarter.js +6 -2
- package/lib/utils/testLauncherHelper.js +2 -2
- package/lib/validation/jsonSchemas.js +22 -20
- package/lib/validation/validators.js +1 -3
- package/package.json +2 -1
- package/suitest.js +6 -0
package/README.md
CHANGED
|
@@ -45,6 +45,7 @@ etc.
|
|
|
45
45
|
- Locally installed Chrome, Firefox, Safari and Edge
|
|
46
46
|
- Apple tvOS, iOS and iPadOS devices, including simulators
|
|
47
47
|
- Roku
|
|
48
|
+
- Vizio
|
|
48
49
|
|
|
49
50
|
Suitest supports automating end-to-end testing of:
|
|
50
51
|
|
|
@@ -52,11 +53,12 @@ Suitest supports automating end-to-end testing of:
|
|
|
52
53
|
- Samsung Orsay and Tizen apps
|
|
53
54
|
- LG NetCast and webOS apps
|
|
54
55
|
- HTML apps for other TV's or set-top boxes
|
|
55
|
-
- Android TV apps (including FireTV)
|
|
56
|
-
- tvOS apps
|
|
56
|
+
- Android TV and mobile apps (including FireTV)
|
|
57
|
+
- Apple TV (tvOS), iOS and iPadOS apps
|
|
57
58
|
- Xbox One and Xbox Series S and X apps
|
|
58
59
|
- PlayStation 4 / 5 apps
|
|
59
60
|
- Roku apps
|
|
61
|
+
- Vizio SmartCast
|
|
60
62
|
- Traditional websites and web apps for desktop browsers.
|
|
61
63
|
|
|
62
64
|
## Contributing
|
package/config/index.js
CHANGED
|
@@ -11,7 +11,7 @@ const timestamp = require('../lib/constants/timestamp');
|
|
|
11
11
|
const {validate, validators} = require('../lib/validation');
|
|
12
12
|
const {invalidConfigObj} = require('../lib/texts');
|
|
13
13
|
const {pickNonNil} = require('../lib/utils/common');
|
|
14
|
-
const envVars = require('../lib/constants/
|
|
14
|
+
const envVars = require('../lib/constants/environment');
|
|
15
15
|
|
|
16
16
|
const sentryDsn = 'https://1f74b885d0c44549b57f307733d60351:dd736ff3ac994104ab6635da53d9be2e@sentry.io/288812';
|
|
17
17
|
const DEFAULT_TIMEOUT = 2000;
|
|
@@ -19,7 +19,7 @@ const DEFAULT_TIMEOUT = 2000;
|
|
|
19
19
|
const overridableFields = [
|
|
20
20
|
'tokenId', 'tokenPassword', 'concurrency', 'preset', 'presets', 'deviceId', 'appConfigId', 'inspect', 'inspectBrk',
|
|
21
21
|
'logLevel', 'logDir', 'timestamp', 'configFile', 'disallowCrashReports', 'defaultTimeout', 'screenshotDir',
|
|
22
|
-
'includeChangelist',
|
|
22
|
+
'includeChangelist', 'recordingOption', 'webhookUrl',
|
|
23
23
|
];
|
|
24
24
|
|
|
25
25
|
const serverAddress = process.env[envVars.SUITEST_BE_SERVER] || 'the.suite.st';
|
package/index.d.ts
CHANGED
|
@@ -53,10 +53,12 @@ declare namespace suitest {
|
|
|
53
53
|
openSession(options: OpenSessionOptions): Promise<OpenSessionResult|SuitestError>;
|
|
54
54
|
closeSession(): Promise<object|SuitestError>;
|
|
55
55
|
setAppConfig(configId: string, options?: ConfigOverride): Promise<void|SuitestError>;
|
|
56
|
-
pairDevice(deviceId: string): Promise<DeviceData|SuitestError>;
|
|
56
|
+
pairDevice(deviceId: string, {recording, webhookUrl}: {recording?: 'autostart' | 'manualstart' | 'none', webhookUrl?: string} | undefined): Promise<DeviceData|SuitestError>;
|
|
57
57
|
releaseDevice(): Promise<void|SuitestError>;
|
|
58
58
|
startREPL(options?: ReplOptions): Promise<void>;
|
|
59
59
|
getAppConfig(): Promise<AppConfiguration|SuitestError>;
|
|
60
|
+
startRecording({webhookUrl}?: {webhookUrl: string}): Promise<void|SuitestError>;
|
|
61
|
+
stopRecording({discard}?: {discard: boolean}): Promise<void|SuitestError>;
|
|
60
62
|
|
|
61
63
|
// config
|
|
62
64
|
getConfig(): ConfigureOptions;
|
|
@@ -64,6 +66,8 @@ declare namespace suitest {
|
|
|
64
66
|
setContinueOnFatalError(continueOnFatalError: ConfigureOptions['continueOnFatalError']): void;
|
|
65
67
|
setDisallowCrashReports(disallowCrashReports: ConfigureOptions['disallowCrashReports']): void;
|
|
66
68
|
setLogLevel(logLevel: ConfigureOptions['logLevel']): void;
|
|
69
|
+
setRecordingOption(recordingOption: ConfigureOptions['recordingOption']): void;
|
|
70
|
+
setWebhookUrl(webhookUrl: ConfigureOptions['webhookUrl']): void;
|
|
67
71
|
|
|
68
72
|
// subjects
|
|
69
73
|
location(): LocationChain;
|
|
@@ -277,6 +281,8 @@ declare namespace suitest {
|
|
|
277
281
|
|
|
278
282
|
interface ConfigureOptions {
|
|
279
283
|
logLevel: 'silent'|'normal'|'verbose'|'debug'|'silly';
|
|
284
|
+
recordingOption: 'autostart'|'manualstart'|'none';
|
|
285
|
+
webhookUrl: string;
|
|
280
286
|
disallowCrashReports: boolean;
|
|
281
287
|
continueOnFatalError: boolean;
|
|
282
288
|
defaultTimeout: number;
|
package/lib/api/webSockets.js
CHANGED
|
@@ -157,6 +157,21 @@ const webSocketsFactory = (self) => {
|
|
|
157
157
|
});
|
|
158
158
|
});
|
|
159
159
|
}
|
|
160
|
+
} else if (
|
|
161
|
+
path([message.messageId, 'contentType'])(requestPromises) === 'startRecording' &&
|
|
162
|
+
message.content.result === 'error'
|
|
163
|
+
) {
|
|
164
|
+
// Allowing result: 'error' for startRecording command and not interrupting test flow
|
|
165
|
+
|
|
166
|
+
const messageId = message.messageId;
|
|
167
|
+
const res = message.content.response || message.content;
|
|
168
|
+
const req = requestPromises[messageId];
|
|
169
|
+
const contentMessage = res.error ? res.error : texts.unknownError();
|
|
170
|
+
|
|
171
|
+
logger.error(getInfoErrorMessage(contentMessage, '', res, ''));
|
|
172
|
+
req.resolve(res);
|
|
173
|
+
|
|
174
|
+
delete requestPromises[messageId];
|
|
160
175
|
} else {
|
|
161
176
|
handleResponse(message);
|
|
162
177
|
}
|
|
@@ -15,12 +15,20 @@ const SuitestError = require('../utils/SuitestError');
|
|
|
15
15
|
* Pair with device
|
|
16
16
|
* @param {Object} instance - main class instance
|
|
17
17
|
* @param {string} deviceId - device to connect to
|
|
18
|
+
* @param {Object | undefined} recordingSettings - {recording, webhookUrl} object. Optional.
|
|
18
19
|
* @returns {ChainablePromise.<DeviceData>}
|
|
19
20
|
*/
|
|
20
|
-
async function pairDevice(
|
|
21
|
+
async function pairDevice(
|
|
22
|
+
{webSockets, authContext, logger, pairedDeviceContext, config},
|
|
23
|
+
deviceId,
|
|
24
|
+
recordingSettings
|
|
25
|
+
) {
|
|
21
26
|
// validate deviceId string to be in uuid format
|
|
22
27
|
validate(validators.UUID, deviceId, invalidInputMessage(pairDevice.name, 'Device id'));
|
|
23
28
|
|
|
29
|
+
const recordingDefault = recordingSettings ? recordingSettings.recording : 'none';
|
|
30
|
+
const webhookUrlDefault = recordingSettings ? recordingSettings.webhookUrl : undefined;
|
|
31
|
+
|
|
24
32
|
const [deviceDetails] = await getDevicesDetails({authContext}, [{device: deviceId}]);
|
|
25
33
|
|
|
26
34
|
if (!deviceDetails)
|
|
@@ -31,12 +39,18 @@ async function pairDevice({webSockets, authContext, logger, pairedDeviceContext}
|
|
|
31
39
|
|
|
32
40
|
const deviceName = deviceDetails.displayName;
|
|
33
41
|
|
|
42
|
+
const recordingOption = config.recordingOption || recordingDefault;
|
|
43
|
+
|
|
44
|
+
const webhookUrlOption = config.webhookUrl || webhookUrlDefault;
|
|
45
|
+
|
|
34
46
|
logger.delayed(connectingToDevice(deviceName, deviceId), 4e3);
|
|
35
47
|
|
|
36
48
|
// authorize
|
|
37
49
|
const authedContent = await authContext.authorizeWs({
|
|
38
50
|
type: wsContentTypes.pairDevice,
|
|
39
51
|
deviceId,
|
|
52
|
+
recording: recordingOption,
|
|
53
|
+
webhookUrl: webhookUrlOption,
|
|
40
54
|
}, pairDevice.name);
|
|
41
55
|
// make ws request
|
|
42
56
|
const res = await webSockets.send(authedContent);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start recording command.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const wsContentTypes = require('../api/wsContentTypes');
|
|
6
|
+
const chainPromise = require('../utils/chainPromise');
|
|
7
|
+
const {startRecordingMessage} = require('../texts');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Start recording
|
|
11
|
+
* @param {SUITEST_API} instance of main class
|
|
12
|
+
* @returns {ChainablePromise.<void>}
|
|
13
|
+
*/
|
|
14
|
+
async function startRecording({webSockets, authContext, logger}, recordingSettings) {
|
|
15
|
+
const webhookUrl = recordingSettings ? recordingSettings.webhookUrl : undefined;
|
|
16
|
+
|
|
17
|
+
// authorize
|
|
18
|
+
const authedContent = await authContext.authorizeWs(
|
|
19
|
+
{
|
|
20
|
+
type: wsContentTypes.startRecording,
|
|
21
|
+
webhookUrl,
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// make ws request
|
|
26
|
+
const response = await webSockets.send(authedContent);
|
|
27
|
+
|
|
28
|
+
if (response.result === 'success') {
|
|
29
|
+
logger.log(startRecordingMessage());
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = chainPromise(startRecording);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop recording command.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const wsContentTypes = require('../api/wsContentTypes');
|
|
6
|
+
const chainPromise = require('../utils/chainPromise');
|
|
7
|
+
const {stopRecordingMessage} = require('../texts');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Stop recording
|
|
11
|
+
* @param {SUITEST_API} instance of main class
|
|
12
|
+
* @returns {ChainablePromise.<void>}
|
|
13
|
+
*/
|
|
14
|
+
async function stopRecording({webSockets, authContext, logger}, recordingSettings) {
|
|
15
|
+
const discard = recordingSettings ? recordingSettings.discard : false;
|
|
16
|
+
|
|
17
|
+
// authorize
|
|
18
|
+
const authedContent = await authContext.authorizeWs(
|
|
19
|
+
{
|
|
20
|
+
type: wsContentTypes.stopRecording,
|
|
21
|
+
discard,
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// make ws request
|
|
26
|
+
await webSockets.send(authedContent);
|
|
27
|
+
|
|
28
|
+
logger.log(stopRecordingMessage());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = chainPromise(stopRecording);
|
|
@@ -8,14 +8,17 @@ const {invalidInputMessage} = require('../texts');
|
|
|
8
8
|
* Accept modal dialog, optionally sending message
|
|
9
9
|
* @param {String} [message] - text to be send to modal dialog
|
|
10
10
|
*/
|
|
11
|
-
const acceptModalComposer = makeModifierComposer(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
const acceptModalComposer = makeModifierComposer(
|
|
12
|
+
composers.ACCEPT_MODAL,
|
|
13
|
+
['acceptModal'],
|
|
14
|
+
(_, meta, message = null) => ({
|
|
15
|
+
...meta,
|
|
16
|
+
isAcceptModal: true,
|
|
17
|
+
acceptModalMessage: validate(
|
|
18
|
+
validators.NON_EMPTY_STRING_OR_NUll,
|
|
19
|
+
message,
|
|
20
|
+
invalidInputMessage('acceptModal', 'Message value'),
|
|
21
|
+
),
|
|
22
|
+
}));
|
|
20
23
|
|
|
21
24
|
module.exports = acceptModalComposer;
|
|
@@ -38,16 +38,19 @@ const processMatchRepoArgs = makeArgumentsProcessor(
|
|
|
38
38
|
}),
|
|
39
39
|
);
|
|
40
40
|
|
|
41
|
-
const matchRepoComposer = makeModifierComposer(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
const matchRepoComposer = makeModifierComposer(
|
|
42
|
+
composers.MATCH_REPO,
|
|
43
|
+
['matchRepo', 'matchesRepo'],
|
|
44
|
+
(_, data, ...args) => ({
|
|
45
|
+
...data,
|
|
46
|
+
comparator: {
|
|
47
|
+
type: SUBJ_COMPARATOR.MATCH,
|
|
48
|
+
props: validation.validate(
|
|
49
|
+
validation.validators.ELEMENT_REPO_PROPS,
|
|
50
|
+
processMatchRepoArgs(args),
|
|
51
|
+
invalidInputMessage('matchRepo', 'Property'),
|
|
52
|
+
),
|
|
53
|
+
},
|
|
54
|
+
}));
|
|
52
55
|
|
|
53
56
|
module.exports = matchRepoComposer;
|
|
@@ -31,7 +31,7 @@ const processRequestArgs = makeArgumentsProcessor(
|
|
|
31
31
|
*/
|
|
32
32
|
({name, val, type = PROP_COMPARATOR.EQUAL}) => getRequestMatchItem(name, val, type),
|
|
33
33
|
objArg => 'name' in objArg && 'val' in objArg,
|
|
34
|
-
getRequestMatchItem
|
|
34
|
+
getRequestMatchItem,
|
|
35
35
|
);
|
|
36
36
|
|
|
37
37
|
const requestMatchesComposer = makeModifierComposer(
|
|
@@ -47,7 +47,7 @@ const requestMatchesComposer = makeModifierComposer(
|
|
|
47
47
|
invalidInputMessage('requestMatch', 'Header'),
|
|
48
48
|
),
|
|
49
49
|
},
|
|
50
|
-
})
|
|
50
|
+
}),
|
|
51
51
|
);
|
|
52
52
|
|
|
53
53
|
module.exports = requestMatchesComposer;
|
|
@@ -32,7 +32,7 @@ const processResponseArgs = makeArgumentsProcessor(
|
|
|
32
32
|
*/
|
|
33
33
|
({name, val, type = PROP_COMPARATOR.EQUAL}) => getResponseMatchComposers(name, val, type),
|
|
34
34
|
objArg => 'name' in objArg && 'val' in objArg,
|
|
35
|
-
getResponseMatchComposers
|
|
35
|
+
getResponseMatchComposers,
|
|
36
36
|
);
|
|
37
37
|
|
|
38
38
|
const responseMatchesComposer = makeModifierComposer(
|
|
@@ -45,10 +45,10 @@ const responseMatchesComposer = makeModifierComposer(
|
|
|
45
45
|
props: validation.validate(
|
|
46
46
|
validation.validators.RESPONSE_MATCHES,
|
|
47
47
|
processResponseArgs(args),
|
|
48
|
-
invalidInputMessage('responseMatch', 'Header')
|
|
48
|
+
invalidInputMessage('responseMatch', 'Header'),
|
|
49
49
|
),
|
|
50
50
|
},
|
|
51
|
-
})
|
|
51
|
+
}),
|
|
52
52
|
);
|
|
53
53
|
|
|
54
54
|
module.exports = responseMatchesComposer;
|
|
@@ -6,12 +6,21 @@ const {invalidInputMessage} = require('../texts');
|
|
|
6
6
|
/**
|
|
7
7
|
* Defines click method
|
|
8
8
|
*/
|
|
9
|
-
const swipeComposer = makeModifierComposer(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
const swipeComposer = makeModifierComposer(
|
|
10
|
+
composers.TAP,
|
|
11
|
+
['swipe', 'flick'],
|
|
12
|
+
(_, meta, direction, distance, duration) => ({
|
|
13
|
+
...meta,
|
|
14
|
+
isSwipe: true,
|
|
15
|
+
direction: validate(validators.DIRECTIONS, direction, invalidInputMessage('swipe/flick', 'direction')),
|
|
16
|
+
distance: validate(
|
|
17
|
+
validators.ST_VAR_OR_POSITIVE_NUMBER, distance,
|
|
18
|
+
invalidInputMessage('swipe/flick', 'distance'),
|
|
19
|
+
),
|
|
20
|
+
duration: validate(
|
|
21
|
+
validators.ST_VAR_OR_POSITIVE_NUMBER, duration,
|
|
22
|
+
invalidInputMessage('swipe/flick', 'duration'),
|
|
23
|
+
),
|
|
24
|
+
}));
|
|
16
25
|
|
|
17
26
|
module.exports = swipeComposer;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const ENVIRONMENT_VARS = Object.freeze({
|
|
2
2
|
/**
|
|
3
3
|
* @description indicates that session is running as launcher child process, contains deviceId|configId|ipcPortNumber
|
|
4
4
|
*/
|
|
@@ -7,4 +7,4 @@ const ENVIROMENT_VARS = Object.freeze({
|
|
|
7
7
|
SUITEST_BE_SERVER: 'SUITEST_BE_SERVER',
|
|
8
8
|
});
|
|
9
9
|
|
|
10
|
-
module.exports =
|
|
10
|
+
module.exports = ENVIRONMENT_VARS;
|
|
@@ -2,6 +2,7 @@ const {once} = require('ramda');
|
|
|
2
2
|
const {composeConfig} = require('../composeConfig');
|
|
3
3
|
const texts = require('../../texts');
|
|
4
4
|
const logLevels = require('../../constants/logLevels');
|
|
5
|
+
const recordingOptions = require('../../constants/recordingOptions');
|
|
5
6
|
const SuitestLauncher = require('../SuitestLauncher');
|
|
6
7
|
const suitest = require('../../../index');
|
|
7
8
|
|
|
@@ -99,6 +100,16 @@ const builder = yargs => {
|
|
|
99
100
|
boolean: true,
|
|
100
101
|
default: false,
|
|
101
102
|
global: false,
|
|
103
|
+
})
|
|
104
|
+
.option('recording-option', {
|
|
105
|
+
describe: texts.cliRecordingOption(),
|
|
106
|
+
global: false,
|
|
107
|
+
choices: Object.values(recordingOptions),
|
|
108
|
+
})
|
|
109
|
+
.option('webhook-url', {
|
|
110
|
+
describe: 'webhook url for recording option',
|
|
111
|
+
global: false,
|
|
112
|
+
type: 'string',
|
|
102
113
|
});
|
|
103
114
|
};
|
|
104
115
|
|
package/lib/testLauncher/repl.js
CHANGED
|
@@ -6,7 +6,7 @@ const chokidar = require('chokidar');
|
|
|
6
6
|
|
|
7
7
|
const texts = require('../texts');
|
|
8
8
|
const ipcClient = require('./ipc/client');
|
|
9
|
-
const envVars = require('../constants/
|
|
9
|
+
const envVars = require('../constants/environment');
|
|
10
10
|
const messageId = require('../constants/ipcMessageId');
|
|
11
11
|
const errorListeners = require('../utils/errorListeners');
|
|
12
12
|
const {getShortFunctionBody} = require('../utils/stringUtils');
|
package/lib/texts.js
CHANGED
|
@@ -212,4 +212,7 @@ ${leaves}`,
|
|
|
212
212
|
defaultTimeout: () => 'Globally set timeout value in milliseconds. Equals to 2000 when left out.',
|
|
213
213
|
cliTimestamp: () => `Logger timestamp format. Use "${timestamp.none}" to disable. [default: "${timestamp.default}"]. This string is processed with momentJS.`,
|
|
214
214
|
cliConfig: () => 'Config file to override default config.',
|
|
215
|
+
cliRecordingOption: () => 'Enables recording of test session',
|
|
216
|
+
startRecordingMessage: () => 'Recording started',
|
|
217
|
+
stopRecordingMessage: () => 'Recording stopped',
|
|
215
218
|
};
|
package/lib/utils/AuthContext.js
CHANGED
|
@@ -38,6 +38,7 @@ const tokenAllowed = {
|
|
|
38
38
|
wsContentTypes.selectConfiguration, wsContentTypes.query,
|
|
39
39
|
wsContentTypes.eval, wsContentTypes.testLine,
|
|
40
40
|
wsContentTypes.takeScreenshot, wsContentTypes.getConfiguration,
|
|
41
|
+
wsContentTypes.startRecording, wsContentTypes.stopRecording,
|
|
41
42
|
],
|
|
42
43
|
};
|
|
43
44
|
|
|
@@ -56,7 +57,7 @@ const allowedRequests = {
|
|
|
56
57
|
* Decorate http requestObject with current context headers
|
|
57
58
|
* @param {*} requestObject
|
|
58
59
|
* @param {*} headers
|
|
59
|
-
* @returns requestObject
|
|
60
|
+
* @returns requestObject decorated with auth headers
|
|
60
61
|
*/
|
|
61
62
|
function decorateRequestObject(requestObject, headers) {
|
|
62
63
|
return Object.assign({}, requestObject, {headers});
|
|
@@ -2,7 +2,7 @@ const {TOKEN} = require('../constants/modes');
|
|
|
2
2
|
const chainPromise = require('./chainPromise');
|
|
3
3
|
const {validate, validators} = require('../validation');
|
|
4
4
|
const {captureException} = require('./sentry/Raven');
|
|
5
|
-
const envVars = require('../constants/
|
|
5
|
+
const envVars = require('../constants/environment');
|
|
6
6
|
const messageId = require('../constants/ipcMessageId');
|
|
7
7
|
const ipcClient = require('../testLauncher/ipc/client');
|
|
8
8
|
const {replWarn} = require('../texts');
|
|
@@ -56,7 +56,11 @@ const bootstrapSession = async(suitest, {deviceId, configId, presetName}) => {
|
|
|
56
56
|
: {};
|
|
57
57
|
|
|
58
58
|
await setAppConfig(suitest, configId, configOverride);
|
|
59
|
-
await pairDevice(
|
|
59
|
+
await pairDevice(
|
|
60
|
+
suitest,
|
|
61
|
+
deviceId,
|
|
62
|
+
{recording: suitest.config.recording, webhookUrl: suitest.config.webhookUrl},
|
|
63
|
+
);
|
|
60
64
|
|
|
61
65
|
if (suitest.config.isDebugMode) {
|
|
62
66
|
await suitest.webSockets.send({type: wsContentTypes.enableDebugMode});
|
|
@@ -11,7 +11,7 @@ const texts = require('../texts');
|
|
|
11
11
|
|
|
12
12
|
const Queue = require('./Queue');
|
|
13
13
|
const {registerProcess} = require('../testLauncher/processReaper');
|
|
14
|
-
const envVars = require('../constants/
|
|
14
|
+
const envVars = require('../constants/environment');
|
|
15
15
|
|
|
16
16
|
const {version} = require('../../package.json');
|
|
17
17
|
const {stripAnsiChars} = require('./stringUtils');
|
|
@@ -192,7 +192,7 @@ function logOutput(child, chunk, logStream) {
|
|
|
192
192
|
* needs to be called to enable sending terminal codes.
|
|
193
193
|
* When repl ends the node process for some reason does not want to take over
|
|
194
194
|
* the ownership of the terminal anymore (setRawMode(false) does not help).
|
|
195
|
-
* This function manually enables ctrl+c
|
|
195
|
+
* This function manually enables ctrl+c together with the keypress module
|
|
196
196
|
* @param {string} ch
|
|
197
197
|
* @param {Object} key
|
|
198
198
|
*/
|
|
@@ -147,7 +147,7 @@ const deviation = {
|
|
|
147
147
|
'then': {'type': ['number', 'string']},
|
|
148
148
|
'else': {'const': 'undefined'},
|
|
149
149
|
};
|
|
150
|
-
const
|
|
150
|
+
const propComparator = {
|
|
151
151
|
'type': 'string',
|
|
152
152
|
'enum': Object.values(PROP_COMPARATOR),
|
|
153
153
|
};
|
|
@@ -164,7 +164,7 @@ schemas[validationKeys.ELEMENT_PROPS] = {
|
|
|
164
164
|
'type': 'string',
|
|
165
165
|
'enum': Object.values(ELEMENT_PROP),
|
|
166
166
|
},
|
|
167
|
-
'type':
|
|
167
|
+
'type': propComparator,
|
|
168
168
|
'val': {'const': VALUE.REPO},
|
|
169
169
|
deviation,
|
|
170
170
|
}},
|
|
@@ -173,7 +173,7 @@ schemas[validationKeys.ELEMENT_PROPS] = {
|
|
|
173
173
|
'type': 'string',
|
|
174
174
|
'enum': ELEMENT_PROP_BY_TYPE.integer,
|
|
175
175
|
},
|
|
176
|
-
'type':
|
|
176
|
+
'type': propComparator,
|
|
177
177
|
'val': {'type': ['integer', 'string']},
|
|
178
178
|
deviation,
|
|
179
179
|
}},
|
|
@@ -182,55 +182,55 @@ schemas[validationKeys.ELEMENT_PROPS] = {
|
|
|
182
182
|
'type': 'string',
|
|
183
183
|
'enum': ELEMENT_PROP_BY_TYPE.string,
|
|
184
184
|
},
|
|
185
|
-
'type':
|
|
185
|
+
'type': propComparator,
|
|
186
186
|
'val': {'type': 'string'},
|
|
187
187
|
deviation,
|
|
188
188
|
}},
|
|
189
189
|
{'properties': {
|
|
190
190
|
'name': {'enum': ELEMENT_PROP_BY_TYPE.number},
|
|
191
|
-
'type':
|
|
191
|
+
'type': propComparator,
|
|
192
192
|
'val': {'type': ['number', 'string']},
|
|
193
193
|
deviation,
|
|
194
194
|
}},
|
|
195
195
|
{'properties': {
|
|
196
196
|
'name': {'enum': ELEMENT_PROP_BY_TYPE.boolean},
|
|
197
|
-
'type':
|
|
197
|
+
'type': propComparator,
|
|
198
198
|
'val': {'type': 'boolean'},
|
|
199
199
|
deviation,
|
|
200
200
|
}},
|
|
201
201
|
{'properties': {
|
|
202
202
|
'name': {'const': ELEMENT_PROP.BORDER_STYLE},
|
|
203
|
-
'type':
|
|
203
|
+
'type': propComparator,
|
|
204
204
|
'val': {'enum': Object.values(BORDER_STYLE)},
|
|
205
205
|
}},
|
|
206
206
|
{'properties': {
|
|
207
207
|
'name': {'const': ELEMENT_PROP.VIDEO_STATE},
|
|
208
|
-
'type':
|
|
208
|
+
'type': propComparator,
|
|
209
209
|
'val': {'enum': Object.values(VIDEO_STATE)},
|
|
210
210
|
}},
|
|
211
211
|
{'properties': {
|
|
212
212
|
'name': {'const': ELEMENT_PROP.VISIBILITY},
|
|
213
|
-
'type':
|
|
213
|
+
'type': propComparator,
|
|
214
214
|
'val': {'enum': [VISIBILITY_STATE.COLLAPSED, VISIBILITY_STATE.INVISIBLE, VISIBILITY_STATE.VISIBLE]},
|
|
215
215
|
}},
|
|
216
216
|
{'properties': {
|
|
217
217
|
'name': {'const': ELEMENT_PROP.CONTENT_MODE},
|
|
218
|
-
'type':
|
|
218
|
+
'type': propComparator,
|
|
219
219
|
'val': {'enum': Object.values(CONTENT_MODE)},
|
|
220
220
|
}},
|
|
221
221
|
{'properties': {
|
|
222
222
|
'name': {'const': ELEMENT_PROP.STATE},
|
|
223
|
-
'type':
|
|
223
|
+
'type': propComparator,
|
|
224
224
|
'val': {'enum': Object.values(ELEMENT_STATE)},
|
|
225
225
|
}},
|
|
226
226
|
{'properties': {
|
|
227
227
|
'name': {'const': ELEMENT_PROP.TEXT_ALIGNMENT},
|
|
228
|
-
'type':
|
|
228
|
+
'type': propComparator,
|
|
229
229
|
'val': {'enum': Object.values(TEXT_ALIGNMENT)},
|
|
230
230
|
}},
|
|
231
231
|
{'properties': {
|
|
232
232
|
'name': {'const': ELEMENT_PROP.IMAGE_LOAD_STATE},
|
|
233
|
-
'type':
|
|
233
|
+
'type': propComparator,
|
|
234
234
|
'val': {'enum': Object.values(IMAGE_LOAD_STATE)},
|
|
235
235
|
}},
|
|
236
236
|
],
|
|
@@ -245,7 +245,7 @@ schemas[validationKeys.ELEMENT_REPO_PROPS] = {
|
|
|
245
245
|
'additionalProperties': false,
|
|
246
246
|
'properties': {
|
|
247
247
|
'name': {'enum': Object.values(ELEMENT_PROP)},
|
|
248
|
-
'type':
|
|
248
|
+
'type': propComparator,
|
|
249
249
|
'val': {'const': VALUE.REPO},
|
|
250
250
|
deviation,
|
|
251
251
|
},
|
|
@@ -261,17 +261,17 @@ schemas[validationKeys.REQUEST_MATCHES] = {
|
|
|
261
261
|
{'properties': {
|
|
262
262
|
'name': {'type': 'string'},
|
|
263
263
|
'val': {'type': 'string'},
|
|
264
|
-
'compare':
|
|
264
|
+
'compare': propComparator,
|
|
265
265
|
}},
|
|
266
266
|
{'properties': {
|
|
267
267
|
'name': {'const': NETWORK_PROP.METHOD},
|
|
268
268
|
'val': {'enum': Object.values(NETWORK_METHOD)},
|
|
269
|
-
'compare':
|
|
269
|
+
'compare': propComparator,
|
|
270
270
|
}},
|
|
271
271
|
{'properties': {
|
|
272
272
|
'name': {'const': NETWORK_PROP.BODY},
|
|
273
273
|
'val': {'type': 'string'},
|
|
274
|
-
'compare':
|
|
274
|
+
'compare': propComparator,
|
|
275
275
|
}},
|
|
276
276
|
],
|
|
277
277
|
},
|
|
@@ -286,17 +286,17 @@ schemas[validationKeys.RESPONSE_MATCHES] = {
|
|
|
286
286
|
{'properties': {
|
|
287
287
|
'name': {'type': 'string'},
|
|
288
288
|
'val': {'type': 'string'},
|
|
289
|
-
'compare':
|
|
289
|
+
'compare': propComparator,
|
|
290
290
|
}},
|
|
291
291
|
{'properties': {
|
|
292
292
|
'name': {'const': NETWORK_PROP.STATUS},
|
|
293
293
|
'val': {'type': ['number', 'string']},
|
|
294
|
-
'compare':
|
|
294
|
+
'compare': propComparator,
|
|
295
295
|
}},
|
|
296
296
|
{'properties': {
|
|
297
297
|
'name': {'const': NETWORK_PROP.BODY},
|
|
298
298
|
'val': {'type': 'string'},
|
|
299
|
-
'compare':
|
|
299
|
+
'compare': propComparator,
|
|
300
300
|
}},
|
|
301
301
|
],
|
|
302
302
|
},
|
|
@@ -383,6 +383,8 @@ schemas[validationKeys.CONFIGURE] = {
|
|
|
383
383
|
'presets': {'type': 'object'},
|
|
384
384
|
'screenshotDir': {'type': 'string'},
|
|
385
385
|
'includeChangelist': {'type': 'boolean'},
|
|
386
|
+
'recordingOption': {'type': 'string'},
|
|
387
|
+
'webhookUrl': {'type': 'string'},
|
|
386
388
|
},
|
|
387
389
|
};
|
|
388
390
|
|
|
@@ -28,8 +28,6 @@ function throwError(text) {
|
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const getElementPropName = (mapOfNames, name) => typeof mapOfNames[name] === 'string' ? mapOfNames[name] : name;
|
|
32
|
-
|
|
33
31
|
/**
|
|
34
32
|
* @description returns prettified errors messages for element properties errors
|
|
35
33
|
* @param {Object} validate
|
|
@@ -130,7 +128,7 @@ function prettifyJsonSchemaErrors(validate) {
|
|
|
130
128
|
* @param {*|Symbol} schemaKey json schema of schemas map key
|
|
131
129
|
* @param {*} data
|
|
132
130
|
* @param {String} errorMessage
|
|
133
|
-
* @returns {*} input data or
|
|
131
|
+
* @returns {*} input data or throws error
|
|
134
132
|
* @throws SuitestError
|
|
135
133
|
*/
|
|
136
134
|
function validateJsonSchema(schemaKey, data, errorMessage) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "suitest-js-api",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.1",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"repository": "git@github.com:SuitestAutomation/suitest-js-api.git",
|
|
6
6
|
"author": "Suitest <hello@suite.st>",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"Android TV testing",
|
|
19
19
|
"Apple TV testing",
|
|
20
20
|
"Roku testing",
|
|
21
|
+
"Vizio SmartCast testing",
|
|
21
22
|
"Smart TV test automation",
|
|
22
23
|
"Fire TV test automation"
|
|
23
24
|
],
|
package/suitest.js
CHANGED
|
@@ -8,6 +8,8 @@ const {pairDevice} = require('./lib/commands/pairDevice');
|
|
|
8
8
|
const releaseDevice = require('./lib/commands/releaseDevice');
|
|
9
9
|
const {setAppConfig} = require('./lib/commands/setAppConfig');
|
|
10
10
|
const {getAppConfig} = require('./lib/commands/getAppConfig');
|
|
11
|
+
const startRecording = require('./lib/commands/startRecording');
|
|
12
|
+
const stopRecording = require('./lib/commands/stopRecording');
|
|
11
13
|
|
|
12
14
|
// Chains
|
|
13
15
|
const openAppFactory = require('./lib/chains/openAppChain');
|
|
@@ -89,6 +91,8 @@ class SUITEST_API extends EventEmitter {
|
|
|
89
91
|
this.setContinueOnFatalError = (continueOnFatalError) => this.configuration.override({continueOnFatalError});
|
|
90
92
|
this.setDisallowCrashReports = (disallowCrashReports) => this.configuration.override({disallowCrashReports});
|
|
91
93
|
this.setLogLevel = (logLevel) => this.configuration.override({logLevel});
|
|
94
|
+
this.setRecordingOption = (recordingOption) => this.configuration.override({recordingOption});
|
|
95
|
+
this.setWebhookUrl = (webhookUrl) => this.configuration.override({webhookUrl});
|
|
92
96
|
|
|
93
97
|
// creating methods based on instance dependencies
|
|
94
98
|
this.logger = createLogger(this.configuration.config, this.pairedDeviceContext);
|
|
@@ -102,6 +106,8 @@ class SUITEST_API extends EventEmitter {
|
|
|
102
106
|
this.closeSession = (...args) => closeSession(this, ...args);
|
|
103
107
|
this.releaseDevice = (...args) => releaseDevice(this, ...args);
|
|
104
108
|
this.getAppConfig = (...args) => getAppConfig(this, ...args);
|
|
109
|
+
this.startRecording = (...args) => startRecording(this, ...args);
|
|
110
|
+
this.stopRecording = (...args) => stopRecording(this, ...args);
|
|
105
111
|
|
|
106
112
|
const {openApp, openAppAssert} = openAppFactory(this);
|
|
107
113
|
const {closeApp, closeAppAssert} = closeAppFactory(this);
|