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.
Files changed (64) hide show
  1. package/config/index.js +7 -7
  2. package/index.d.ts +23 -71
  3. package/lib/api/endpoints.js +0 -7
  4. package/lib/api/webSockets.js +1 -1
  5. package/lib/api/wsContentTypes.js +0 -2
  6. package/lib/chains/elementChain.js +90 -19
  7. package/lib/chains/saveScreenshotChain.js +60 -5
  8. package/lib/commands/closeSession.js +4 -29
  9. package/lib/commands/openSession.js +13 -52
  10. package/lib/commands/pairDevice.js +3 -3
  11. package/lib/commands/releaseDevice.js +1 -1
  12. package/lib/commands/setAppConfig.js +0 -2
  13. package/lib/commands/{interactive.js → startREPL.js} +10 -8
  14. package/lib/composers/attributesComposer.js +18 -0
  15. package/lib/composers/cssPropsComposer.js +18 -0
  16. package/lib/composers/handleComposer.js +23 -0
  17. package/lib/composers/index.js +6 -0
  18. package/lib/composers/thenComposer.js +1 -2
  19. package/lib/constants/composer.js +3 -0
  20. package/lib/constants/element.js +2 -0
  21. package/lib/constants/enviroment.js +6 -1
  22. package/lib/constants/index.js +1 -0
  23. package/lib/constants/ipcMessageId.js +1 -1
  24. package/lib/constants/modes.js +1 -2
  25. package/lib/constants/session.js +1 -3
  26. package/lib/constants/timestamp.js +1 -1
  27. package/lib/constants/validationKeys.js +5 -5
  28. package/lib/constants/vrc.js +2 -0
  29. package/lib/testLauncher/SuitestLauncher.js +65 -77
  30. package/lib/testLauncher/buildArgs.js +3 -6
  31. package/lib/testLauncher/commands/run.js +111 -0
  32. package/lib/testLauncher/composeConfig.js +154 -15
  33. package/lib/testLauncher/launcherLogger.js +3 -23
  34. package/lib/testLauncher/processArgs.js +1 -1
  35. package/lib/testLauncher/repl.js +3 -3
  36. package/lib/texts.js +18 -39
  37. package/lib/utils/AuthContext.js +17 -76
  38. package/lib/utils/getDeviceInfo.js +16 -10
  39. package/lib/utils/logger.js +4 -3
  40. package/lib/utils/{interactiveProgressHandler.js → progressHandler.js} +1 -1
  41. package/lib/utils/sentry/Raven.js +1 -1
  42. package/lib/utils/sessionStarter.js +43 -38
  43. package/lib/utils/socketChainHelper.js +17 -8
  44. package/lib/utils/stackTraceParser.js +25 -1
  45. package/lib/utils/testHelpers/mockHelpers.js +0 -2
  46. package/lib/utils/testHelpers/mockWebSocket.js +1 -1
  47. package/lib/utils/testHelpers/testLauncherTest.js +0 -43
  48. package/lib/utils/testLauncherHelper.js +38 -105
  49. package/lib/validation/elementPropTypes.js +2 -0
  50. package/lib/validation/jsonSchemas.js +97 -97
  51. package/lib/validation/validators.js +25 -3
  52. package/lib/validation/validatorsMap.js +13 -13
  53. package/package.json +7 -5
  54. package/suitest.js +7 -15
  55. package/typeDefinition/ElementChain.d.ts +29 -1
  56. package/typeDefinition/constants/Element.d.ts +2 -0
  57. package/typeDefinition/constants/Vrc.d.ts +2 -0
  58. package/typeDefinition/modifiers.d.ts +13 -0
  59. package/lib/commands/endTest.js +0 -28
  60. package/lib/commands/startTest.js +0 -55
  61. package/lib/commands/startTestPack.js +0 -93
  62. package/lib/testLauncher/commands/automated.js +0 -70
  63. package/lib/testLauncher/commands/common.js +0 -43
  64. package/lib/testLauncher/commands/interactive.js +0 -70
@@ -1,4 +1,3 @@
1
- const read = require('read');
2
1
  const ramda = require('ramda');
3
2
  const path = require('path');
4
3
  const moment = require('moment');
@@ -12,17 +11,14 @@ const texts = require('../texts');
12
11
 
13
12
  const Queue = require('./Queue');
14
13
  const {registerProcess} = require('../testLauncher/processReaper');
15
- const {startTestPackUnchained} = require('../commands/startTestPack');
16
14
  const envVars = require('../constants/enviroment');
17
15
 
18
16
  const {version} = require('../../package.json');
19
17
  const {stripAnsiChars} = require('./stringUtils');
20
18
 
21
- const {AUTOMATED, INTERACTIVE, TEST_COMMAND} = require('../constants/modes');
19
+ const {TEST_COMMAND} = require('../constants/modes');
22
20
 
23
21
  const {fetchLatestSuitestVersion, warnNewVersionAvailable} = require('./packageMetadataHelper');
24
- const SuitestError = require('./SuitestError');
25
- const t = require('../texts');
26
22
 
27
23
  const ipcServer = require('../testLauncher/ipc/server');
28
24
  const messageId = require('../constants/ipcMessageId');
@@ -56,31 +52,9 @@ function handleChildResult(finishedWithErrors) {
56
52
  }
57
53
  }
58
54
 
59
- /**
60
- * Prompt user password.
61
- * If nothing provided, ask again.
62
- */
63
- function promptPassword() {
64
- return new Promise((resolve) => read({
65
- prompt: texts['tl.promptPassword'](),
66
- silent: true,
67
- replace: '*',
68
- }, (err, pass) => {
69
- /* istanbul ignore if */
70
- if (err) {
71
- // prompt dismissed with CTRL+C or the like.
72
- process.stdout.write('\n');
73
- } else if (!pass) {
74
- resolve(promptPassword());
75
- } else {
76
- resolve(pass);
77
- }
78
- }));
79
- }
80
-
81
55
  /**
82
56
  * Validate test launcher's args based on session type
83
- * @param {String} type session type "AUTOMATED" or "INTERACTIVE", "TEST_COMMAND"
57
+ * @param {'TEST_COMMAND'|'TOKEN'} type session type
84
58
  * @param {Object} args test launcher arguments
85
59
  */
86
60
  function validateInput(type, args) {
@@ -91,7 +65,7 @@ function validateInput(type, args) {
91
65
 
92
66
  validation.validate(
93
67
  validation.validators[`TEST_LAUNCHER_${type}`],
94
- args, msg
68
+ args, msg,
95
69
  );
96
70
  } catch (error) {
97
71
  log.argsValidationError(error);
@@ -169,10 +143,10 @@ function isDebugMode({inspectBrk, inspect}) {
169
143
  }
170
144
 
171
145
  /**
172
- * Throw error and exit process when user wants to run debugging in automated session
146
+ * Throw error and exit process when user wants to run debugging with multiply devices
173
147
  */
174
- function throwDebugInAutomatedError() {
175
- logger.error(texts['tl.inspectOnlyForInteractiveMode']());
148
+ function throwDebugForManyDevicesError() {
149
+ logger.error(texts['tl.inspectOnlyForSingleDevice']());
176
150
  process.exit(1);
177
151
  }
178
152
 
@@ -181,10 +155,10 @@ function throwDebugInAutomatedError() {
181
155
  * @param {Array} cmdArgv - user test command and it's parameters
182
156
  * @param {Object} ownArgv - implicitly derived launcher parameters
183
157
  * @param {Object} options - child process environment options
184
- * @param {string} runMode - suitest execution mode (interactive|automated)
158
+ * @param {Boolean} singleDeviceOnly - overall execution will be ran for single device
185
159
  * @returns {ChildProcess} - spawned child process
186
160
  */
187
- async function launchChild(cmdArgv, ownArgv, options, runMode) {
161
+ async function launchChild(cmdArgv, ownArgv, options, singleDeviceOnly) {
188
162
  const [command, ...args] = cmdArgv;
189
163
  const debugOption = getDebugOption(ownArgv);
190
164
 
@@ -192,14 +166,14 @@ async function launchChild(cmdArgv, ownArgv, options, runMode) {
192
166
  args.unshift(debugOption);
193
167
  }
194
168
 
195
- if (runMode === INTERACTIVE) {
169
+ if (singleDeviceOnly) {
196
170
  options.stdio = 'pipe';
197
171
  }
198
172
 
199
173
  return cp.spawn(
200
174
  command,
201
175
  args,
202
- options
176
+ options,
203
177
  );
204
178
  }
205
179
 
@@ -251,16 +225,16 @@ function addLauncherIpcListeners() {
251
225
  * @param {ChildProcess} child - the child process instance running the test
252
226
  * @param {Function} onExit - callback function to call when the process finishes
253
227
  * @param {WritableStream} logStream - the output stream of the child process.
254
- * @param {String} runMode - suitest test execution mode
228
+ * @param {Boolean} singleDeviceOnly - overall execution will be ran for single device
255
229
  */
256
- function attachIO(child, onExit, logStream, runMode) {
230
+ function attachIO(child, onExit, logStream, singleDeviceOnly) {
257
231
  const [childIn, childOut, childErr] = child.stdio;
258
232
  const logIo = chunk => logOutput(child, chunk, logStream);
259
233
 
260
234
  registerProcess(child);
261
235
 
262
- // in interactive mode pipe stdin so that console is not confused by REPL
263
- if (runMode === INTERACTIVE) {
236
+ // for single device execution pipe stdin so that console is not confused by REPL
237
+ if (singleDeviceOnly) {
264
238
  keypress(process.stdin);
265
239
  process.stdin.pipe(childIn);
266
240
  childIn.on('data', logIo);
@@ -282,14 +256,14 @@ function attachIO(child, onExit, logStream, runMode) {
282
256
  * @param {Array} cmdArgv - user test command and it's parameters
283
257
  * @param {Object} ownArgv - implicitly derived launcher parameters
284
258
  * @param {Object} device - full device information
285
- * @param {String} runMode - Suitest execution mode (interactive|automated)
259
+ * @param {Boolean} singleDeviceOnly - overall execution will be ran for single device
286
260
  * @param {number} ipcPort
287
261
  * @returns {Promise<any>}
288
262
  */
289
- async function runTestOnDevice(cmdArgv, ownArgv, device, runMode, ipcPort) {
263
+ async function runTestOnDevice(cmdArgv, ownArgv, device, singleDeviceOnly, ipcPort) {
290
264
  const {logDir} = ownArgv;
291
- const options = getChildOptions(device.deviceId, ipcPort);
292
- const child = await launchChild(cmdArgv, ownArgv, options, runMode);
265
+ const options = getChildOptions(device, ipcPort);
266
+ const child = await launchChild(cmdArgv, ownArgv, options, singleDeviceOnly);
293
267
  const logStream = getDeviceLogStream(device, cmdArgv, new Date(), logDir);
294
268
 
295
269
  return new Promise(resolve => {
@@ -297,34 +271,34 @@ async function runTestOnDevice(cmdArgv, ownArgv, device, runMode, ipcPort) {
297
271
  cmdArgv.join(' '),
298
272
  ownArgv.orgId,
299
273
  ownArgv.appConfigId,
300
- runMode === AUTOMATED ? ownArgv.testPackId : '',
301
- new Date()
274
+ new Date(),
302
275
  ));
303
276
 
304
277
  const onExit = (exitCode, signal) => {
305
278
  logStream && logStream.write(texts.fileLogCompleted(
306
- exitCode, signal
279
+ exitCode, signal,
307
280
  ));
308
281
  resolve(exitCode, signal);
309
282
  };
310
283
 
311
- attachIO(child, onExit, logStream, runMode);
284
+ attachIO(child, onExit, logStream, singleDeviceOnly);
312
285
  });
313
286
  }
314
287
 
315
288
  /**
316
289
  * Prepares environment for the child process.
317
290
  *
318
- * @param {string} deviceId - id of the device
291
+ * @param {string} device - device object. deviceId and config should be provided.
319
292
  * @param {number} port - ipc port
320
293
  * @returns {{shell: true, env: Object}}
321
294
  */
322
- function getChildOptions(deviceId, port) {
295
+ function getChildOptions(device, port) {
323
296
  return {
324
297
  shell: true,
325
298
  env: {
326
299
  ...process.env,
327
- [envVars.SUITEST_CHILD_PROCESS]: `${deviceId}|${port}`,
300
+ [envVars.SUITEST_CHILD_PROCESS]: `${device.deviceId}|${device.config}|${port}`,
301
+ [envVars.SUITEST_PRESET_NAME]: device.presetName,
328
302
  FORCE_COLOR: true,
329
303
  NODE_NO_READLINE: 1, // enable repl in advanced consoles
330
304
  },
@@ -332,37 +306,7 @@ function getChildOptions(deviceId, port) {
332
306
  }
333
307
 
334
308
  /**
335
- * Prepares data for execution of the test pack
336
- *
337
- * @param {Object} ownArgv - implicitly derived launcher paratemers
338
- * @returns {Promise<{devices, deviceAccessToken: *}>}
339
- */
340
- async function prepareTestPackExecution(ownArgv) {
341
- const {tokenKey, tokenId, tokenPassword, testPackId} = ownArgv;
342
-
343
- const testPackInfo = await startTestPackUnchained(suitest, {
344
- accessTokenKey: tokenKey || tokenId,
345
- accessTokenPassword: tokenPassword,
346
- testPackId,
347
- });
348
-
349
- const {pluck, path} = ramda;
350
- const {deviceAccessToken} = testPackInfo;
351
- const devices = pluck('deviceId', path(['testPack', 'devices'], testPackInfo));
352
- const deviceCount = devices.length;
353
-
354
- if (!deviceCount) {
355
- throw new SuitestError(t['errorType.testPackNoDevices'](), SuitestError.INVALID_INPUT);
356
- }
357
-
358
- return {
359
- devices,
360
- deviceAccessToken,
361
- };
362
- }
363
-
364
- /**
365
- * Runs test pack on all devices in automated mode
309
+ * Runs all devices by spawning subprocesses
366
310
  *
367
311
  * @param {Array} cmdArgv - user command and it's arguments
368
312
  * @param {Object} ownArgv - implicitly derived parameters
@@ -372,17 +316,20 @@ async function prepareTestPackExecution(ownArgv) {
372
316
  */
373
317
  function runAllDevices(cmdArgv, ownArgv, devices, ipcPort) {
374
318
  const tests = devices.map(device => () => runTestOnDevice(
375
- cmdArgv, ownArgv, device, AUTOMATED, ipcPort
319
+ cmdArgv,
320
+ ownArgv,
321
+ device,
322
+ devices.length === 1,
323
+ ipcPort,
376
324
  ));
377
325
 
378
326
  const queue = new Queue(ownArgv.concurrency, tests);
379
327
 
380
328
  logger.intro(
381
- texts.launcherSummaryAutomated,
382
- ownArgv.testPackId,
329
+ texts.launcherSummary,
383
330
  cmdArgv.join(' '),
384
331
  ownArgv.logDir,
385
- ...devices.map(device => device.displayName)
332
+ ...devices.map(device => device.displayName),
386
333
  );
387
334
 
388
335
  return queue.start().then(result => {
@@ -391,26 +338,15 @@ function runAllDevices(cmdArgv, ownArgv, devices, ipcPort) {
391
338
  const code = res.result.code || res.result;
392
339
 
393
340
  return res.result.error || code !== 0;
394
- }
341
+ },
395
342
  );
396
343
 
397
- log.finalAutomated(failedDevices.length, result.length - failedDevices.length);
398
- warnNewVersionAvailable(version, suitestVersion);
344
+ log.final(failedDevices.length, result.length - failedDevices.length);
345
+ warnNewVersionAvailable(logger, version, suitestVersion);
399
346
  handleChildResult(failedDevices.length !== 0);
400
347
  });
401
348
  }
402
349
 
403
- /**
404
- * Completing steps for interactive run
405
- *
406
- * @param {Boolean} withError
407
- */
408
- function finishInteractive(withError) {
409
- log.finalInteractive(withError);
410
- warnNewVersionAvailable(version, suitestVersion);
411
- handleChildResult(withError);
412
- }
413
-
414
350
  /**
415
351
  * Get's the version of the latest published suitest-js-api package
416
352
  * @returns {Promise<String>}
@@ -428,7 +364,7 @@ async function getVersion() {
428
364
  * @param {number} concurrency
429
365
  * @modifies emitter
430
366
  */
431
- function increaseMaxListeners(emitter, deviceCount, concurrency) {
367
+ function increaseMaxListeners(emitter, deviceCount, concurrency= 0) {
432
368
  const threadCount = concurrency === 0 ? deviceCount : Math.min(concurrency, deviceCount);
433
369
 
434
370
  emitter.setMaxListeners(emitter.getMaxListeners() + threadCount * 2);
@@ -437,16 +373,13 @@ function increaseMaxListeners(emitter, deviceCount, concurrency) {
437
373
  module.exports = {
438
374
  handleLauncherError,
439
375
  handleChildResult,
440
- promptPassword,
441
376
  validateInput,
442
- prepareTestPackExecution,
443
377
  runTestOnDevice,
444
378
  runAllDevices,
445
- finishInteractive,
446
379
  getVersion,
447
380
  isDebugMode,
448
381
  getDebugOption,
449
382
  addLauncherIpcListeners,
450
- throwDebugInAutomatedError,
383
+ throwDebugForManyDevicesError,
451
384
  increaseMaxListeners,
452
385
  };
@@ -92,6 +92,8 @@ const ELEMENT_PROP_TYPES = {
92
92
  [ELEMENT_PROP.EXTENDS]: 'string',
93
93
  [ELEMENT_PROP.UI_ELEMENT_ID]: 'string',
94
94
  [ELEMENT_PROP.INDEX]: 'integer',
95
+ [ELEMENT_PROP.OFFSET_TOP]: 'number',
96
+ [ELEMENT_PROP.OFFSET_LEFT]: 'number',
95
97
  };
96
98
 
97
99
  Object.freeze(ELEMENT_PROP_TYPES);
@@ -28,7 +28,7 @@ schemas[validationKeys.CONFIG_OVERRIDE] = {
28
28
  'type': 'array',
29
29
  'items': {'type': 'string'},
30
30
  },
31
- 'freezeRules': {
31
+ 'mapRules': {
32
32
  'type': 'array',
33
33
  'items': {
34
34
  'type': 'object',
@@ -62,26 +62,6 @@ schemas[validationKeys.CONFIG_OVERRIDE] = {
62
62
  },
63
63
  };
64
64
 
65
- schemas[validationKeys.START_TEST_PACK] = {
66
- 'type': 'object',
67
- 'required': ['testPackId', 'includeChangelist'],
68
- 'properties': {
69
- 'testPackId': {'type': ['number', 'string']},
70
- 'accessTokenKey': {'type': 'string'},
71
- 'accessTokenPassword': {'type': 'string'},
72
- 'config': schemas[validationKeys.CONFIG_OVERRIDE],
73
- 'includeChangelist': {'type': 'boolean'},
74
- 'metadata': {
75
- 'type': 'object',
76
- 'properties': {
77
- 'version': {'type': 'string'},
78
- 'hash': {'type': 'string'},
79
- 'link': {'type': 'string'},
80
- },
81
- },
82
- },
83
- };
84
-
85
65
  schemas[validationKeys.LAUNCH_MODE] = {
86
66
  'type': 'string',
87
67
  'enum': Object.values(LAUNCH_MODE),
@@ -104,29 +84,11 @@ schemas[validationKeys.UUID] = {
104
84
 
105
85
  schemas[validationKeys.OPEN_SESSION] = {
106
86
  'type': 'object',
107
- 'oneOf': [
108
- {
109
- 'required': ['username', 'password', 'orgId'],
110
- 'properties': {
111
- 'username': {'type': 'string'},
112
- 'password': {'type': 'string'},
113
- 'orgId': {'type': 'string'},
114
- },
115
- },
116
- {
117
- 'required': ['sessionToken'],
118
- 'properties': {
119
- 'sessionToken': {'type': 'string'},
120
- },
121
- },
122
- {
123
- 'required': ['accessTokenKey', 'accessTokenPassword'],
124
- 'properties': {
125
- 'accessTokenKey': {'type': 'string'},
126
- 'accessTokenPassword': {'type': 'string'},
127
- },
128
- },
129
- ],
87
+ 'required': ['tokenId', 'tokenPassword'],
88
+ 'properties': {
89
+ 'tokenId': {'type': 'string'},
90
+ 'tokenPassword': {'type': 'string'},
91
+ },
130
92
  };
131
93
 
132
94
  schemas[validationKeys.STRING] = {
@@ -148,23 +110,31 @@ schemas[validationKeys.ELEMENT_SELECTOR] = {
148
110
  'xpath': {'type': 'string'},
149
111
  'attributes': {'type': 'string'},
150
112
  'text': {'type': 'string'},
113
+ 'linkText': {'type': 'string'},
114
+ 'partialLinkText': {'type': 'string'},
151
115
  'position': {'type': 'string'},
152
116
  'size': {'type': 'string'},
153
117
  'color': {'type': 'string'},
154
118
  'index': {
155
119
  'type': 'integer',
156
120
  'exclusiveMinimum': 0,
157
- }
121
+ },
122
+ 'handle': schemas[validationKeys.NON_EMPTY_STRING],
123
+ 'active': {'type': 'boolean'},
158
124
  },
159
125
  'anyOf': [
160
126
  {'required': ['css']},
161
127
  {'required': ['xpath']},
162
128
  {'required': ['attributes']},
163
129
  {'required': ['text']},
130
+ {'required': ['linkText']},
131
+ {'required': ['partialLinkText']},
164
132
  {'required': ['position']},
165
133
  {'required': ['size']},
166
134
  {'required': ['color']},
167
135
  {'required': ['apiId']},
136
+ {'required': ['handle']},
137
+ {'required': ['active']},
168
138
  ],
169
139
  };
170
140
 
@@ -328,63 +298,56 @@ schemas[validationKeys.RESPONSE_MATCHES] = {
328
298
  },
329
299
  };
330
300
 
331
- schemas[validationKeys.TEST_LAUNCHER_AUTOMATED] = {
301
+ schemas[validationKeys.TEST_LAUNCHER_TOKEN] = {
332
302
  'type': 'object',
333
- 'oneOf': [
334
- {
335
- 'required': ['tokenKey', 'tokenPassword', 'testPackId'],
336
- 'properties': {
337
- 'tokenKey': {'type': 'string'},
338
- 'tokenPassword': {'type': 'string'},
339
- 'testPackId': {'type': ['number', 'string']},
340
- },
341
- },
342
- {
343
- 'required': ['tokenId', 'tokenPassword', 'testPackId'],
344
- 'properties': {
345
- 'tokenId': {'type': 'string'},
346
- 'tokenPassword': {'type': 'string'},
347
- 'testPackId': {'type': ['number', 'string']},
303
+ 'required': ['tokenId', 'tokenPassword'],
304
+ 'properties': {
305
+ 'tokenId': {'type': 'string'},
306
+ 'tokenPassword': {'type': 'string'},
307
+ 'presets': {
308
+ 'type': 'object',
309
+ 'additionalProperties': {
310
+ 'type': 'object',
311
+ 'properties': {
312
+ 'device': {
313
+ 'oneOf': [
314
+ {'type': 'string'},
315
+ {
316
+ 'type': 'object',
317
+ 'properties': {
318
+ 'deviceId': {'type': 'string'},
319
+ },
320
+ 'additionalProperties': false,
321
+ },
322
+ ],
323
+ },
324
+ 'config': {
325
+ 'oneOf': [
326
+ {'type': 'string'},
327
+ {
328
+ 'type': 'object',
329
+ 'properties': {
330
+ 'configId': {'type': 'string'},
331
+ },
332
+ 'additionalProperties': false,
333
+ },
334
+ ],
335
+ },
336
+ },
348
337
  },
349
- },
350
- ],
351
- };
352
338
 
353
- schemas[validationKeys.TEST_LAUNCHER_INTERACTIVE] = {
354
- 'type': 'object',
355
- 'required': ['username', 'password', 'orgId', 'deviceId', 'appConfigId'],
356
- 'properties': {
357
- 'username': {'type': 'string'},
358
- 'password': {'type': 'string'},
359
- 'orgId': {'type': 'string'},
339
+ },
360
340
  'appConfigId': {'type': 'string'},
361
341
  'deviceId': {'type': 'string'},
362
342
  },
363
343
  };
364
344
 
365
- schemas[validationKeys.SESSION_BOOTSTRAP_AUTOMATED] = {
366
- 'type': 'object',
367
- 'required': ['sessionToken'],
368
- 'properties': {
369
- 'sessionToken': {
370
- 'type': 'string',
371
- 'minLength': 1,
372
- },
373
- },
374
- };
375
-
376
- schemas[validationKeys.SESSION_BOOTSTRAP_INTERACTIVE] = {
345
+ schemas[validationKeys.SESSION_BOOTSTRAP_TOKEN] = {
377
346
  'type': 'object',
378
- 'required': ['sessionToken', 'appConfigId'],
347
+ 'required': ['tokenId'],
379
348
  'properties': {
380
- 'sessionToken': {
381
- 'type': 'string',
382
- 'minLength': 1,
383
- },
384
- 'appConfigId': {
385
- 'type': 'string',
386
- 'minLength': 1,
387
- },
349
+ 'tokenId': {'type': 'string'},
350
+ 'tokenPassword': {'type': 'string'},
388
351
  },
389
352
  };
390
353
 
@@ -404,14 +367,17 @@ schemas[validationKeys.CONFIGURE] = {
404
367
  'defaultTimeout': {'type': 'number'},
405
368
  'logLevel': {'enum': ['silent', 'normal', 'verbose', 'debug', 'silly']},
406
369
  'orgId': {'type': 'string'},
407
- 'password': {'type': 'string'},
408
370
  'repl': {'type': 'boolean'},
409
371
  'timestamp': {'type': 'string'},
410
- 'testPackId': {'type': ['number', 'string']},
411
- 'tokenKey': {'type': 'string'},
412
- 'tokenId': {'type': 'string'},
413
372
  'tokenPassword': {'type': 'string'},
414
- 'username': {'type': 'string'},
373
+ 'tokenId': {'type': 'string'},
374
+ 'preset': {
375
+ 'type': 'array',
376
+ 'items': {'type': 'string'},
377
+ },
378
+ 'presets': {'type': 'object'},
379
+ 'screenshotDir': {'type': 'string'},
380
+ 'includeChangelist': {'type': 'boolean'},
415
381
  },
416
382
  };
417
383
 
@@ -440,6 +406,40 @@ schemas[validationKeys.DIRECTIONS] = {
440
406
  'enum': [DIRECTIONS.UP, DIRECTIONS.DOWN, DIRECTIONS.LEFT, DIRECTIONS.RIGHT],
441
407
  };
442
408
 
409
+ schemas[validationKeys.CSS_PROPS] = {
410
+ 'schemaId': validationKeys.CSS_PROPS,
411
+ 'type': 'array',
412
+ 'minItems': 1,
413
+ 'items': {
414
+ 'type': 'string',
415
+ 'minLength': 1,
416
+ },
417
+ };
418
+
419
+ schemas[validationKeys.ELEMENT_HANDLE] = {
420
+ 'schemaId': validationKeys.ELEMENT_HANDLE,
421
+ 'oneOf': [
422
+ {'type': 'boolean'},
423
+ {
424
+ 'type': 'object',
425
+ 'properties': {
426
+ 'multiple': {
427
+ 'type': 'boolean',
428
+ },
429
+ },
430
+ },
431
+ ],
432
+ };
433
+
434
+ schemas[validationKeys.ELEMENT_ATTRIBUTES] = {
435
+ 'schemaId': validationKeys.ELEMENT_ATTRIBUTES,
436
+ 'type': 'array',
437
+ 'items': {
438
+ 'type': 'string',
439
+ 'minLength': 1,
440
+ },
441
+ };
442
+
443
443
  Object.freeze(schemas);
444
444
 
445
445
  module.exports = schemas;
@@ -85,8 +85,27 @@ function prettifyJsonSchemaErrors(validate) {
85
85
  if (validate.schema.schemaId === validationKeys.ELEMENT_PROPS) {
86
86
  errors = prettifyElementPropsErrors(validate);
87
87
  } else if (validate.schema.schemaId === validationKeys.CONFIGURE) {
88
- // example: Invalid input provided for configuration object. 'disallowCrashReports' should be boolean.
89
- errors = validate.errors.map(i => `'${i.dataPath.slice(1)}' ${i.message}.`);
88
+ errors = validate.errors.map(i => {
89
+ if (i.keyword === 'additionalProperties') {
90
+ // example: Invalid input provided for configuration object. It should NOT have additional properties: 'screenshotDir'
91
+ return `It ${i.message}: '${i.params.additionalProperty}'`;
92
+ }
93
+
94
+ // example: Invalid input provided for configuration object. 'disallowCrashReports' should be boolean.
95
+ return `'${i.dataPath.slice(1)}' ${i.message}.`;
96
+ });
97
+ } else if (
98
+ [validationKeys.ELEMENT_ATTRIBUTES, validationKeys.CSS_PROPS].includes(validate.schema.schemaId)
99
+ ) {
100
+ errors = validate.errors.map(i => {
101
+ if (!i.dataPath) {
102
+ return i.message;
103
+ }
104
+
105
+ // Example:
106
+ // Element attributes item at [1] index should be string
107
+ return `item at ${i.dataPath} index ${i.message}`;
108
+ });
90
109
  } else if (validate.schema.schemaId === validationKeys.ELEMENT_SELECTOR) {
91
110
  errors = prettifyElementSelectorsErrors(validate);
92
111
  } else {
@@ -94,6 +113,9 @@ function prettifyJsonSchemaErrors(validate) {
94
113
  if (err.keyword === 'enum') {
95
114
  return `${err.message}: "${err.params.allowedValues.join('", "')}"`; // -> ...of the allowed values: "all", "currentUrl"
96
115
  }
116
+ if (err.dataPath) {
117
+ return err.dataPath + ' ' + err.message;
118
+ }
97
119
 
98
120
  return err.message;
99
121
  });
@@ -226,7 +248,7 @@ const validateRepoProps = ({props, data, text}) => {
226
248
 
227
249
  if (repoProps.length) {
228
250
  throwError(
229
- text + ' ' + (selector.video ? invalidVideoInheredProps : invalidInheredProps)(repoProps.join(', '))
251
+ text + ' ' + (selector.video ? invalidVideoInheredProps : invalidInheredProps)(repoProps.join(', ')),
230
252
  );
231
253
  }
232
254
 
@@ -35,9 +35,6 @@ const validatorsMap = {
35
35
  [validationKeys.ELEMENT_SELECTOR]: (value, text) => {
36
36
  return validators.validateJsonSchema(validationKeys.ELEMENT_SELECTOR, value, text);
37
37
  },
38
- [validationKeys.START_TEST_PACK]: (value, text) => {
39
- return validators.validateJsonSchema(validationKeys.START_TEST_PACK, value, text);
40
- },
41
38
  [validationKeys.OPEN_SESSION]: (value, text) => {
42
39
  return validators.validateJsonSchema(validationKeys.OPEN_SESSION, value, text);
43
40
  },
@@ -66,20 +63,14 @@ const validatorsMap = {
66
63
  [validationKeys.RESPONSE_MATCHES]: (value, text) => {
67
64
  return validators.validateJsonSchema(validationKeys.RESPONSE_MATCHES, value, text);
68
65
  },
69
- [validationKeys.TEST_LAUNCHER_AUTOMATED]: (value, text) => {
70
- return validators.validateJsonSchema(validationKeys.TEST_LAUNCHER_AUTOMATED, value, text);
71
- },
72
- [validationKeys.TEST_LAUNCHER_INTERACTIVE]: (value, text) => {
73
- return validators.validateJsonSchema(validationKeys.TEST_LAUNCHER_INTERACTIVE, value, text);
74
- },
75
66
  [validationKeys.TEST_LAUNCHER_TEST_COMMAND]: (value, text) => {
76
67
  return validators.validateNonEmptyArrayOfStrings(value, text);
77
68
  },
78
- [validationKeys.SESSION_BOOTSTRAP_AUTOMATED]: (value, text) => {
79
- return validators.validateJsonSchema(validationKeys.SESSION_BOOTSTRAP_AUTOMATED, value, text);
69
+ [validationKeys.TEST_LAUNCHER_TOKEN]: (value, text) => {
70
+ return validators.validateJsonSchema(validationKeys.TEST_LAUNCHER_TOKEN, value, text);
80
71
  },
81
- [validationKeys.SESSION_BOOTSTRAP_INTERACTIVE]: (value, text) => {
82
- return validators.validateJsonSchema(validationKeys.SESSION_BOOTSTRAP_INTERACTIVE, value, text);
72
+ [validationKeys.SESSION_BOOTSTRAP_TOKEN]: (value, text) => {
73
+ return validators.validateJsonSchema(validationKeys.SESSION_BOOTSTRAP_TOKEN, value, text);
83
74
  },
84
75
  [validationKeys.UNTIL_CONDITION_CHAIN]: value => {
85
76
  return validators.validateUntilConditionChain(value);
@@ -96,6 +87,15 @@ const validatorsMap = {
96
87
  [validationKeys.DIRECTIONS]: (value, text) => {
97
88
  return validators.validateJsonSchema(validationKeys.DIRECTIONS, value, text);
98
89
  },
90
+ [validationKeys.CSS_PROPS]: (value, text) => {
91
+ return validators.validateJsonSchema(validationKeys.CSS_PROPS, value, text);
92
+ },
93
+ [validationKeys.ELEMENT_HANDLE]: (value, text) => {
94
+ return validators.validateJsonSchema(validationKeys.ELEMENT_HANDLE, value, text);
95
+ },
96
+ [validationKeys.ELEMENT_ATTRIBUTES]: (value, text) => {
97
+ return validators.validateJsonSchema(validationKeys.ELEMENT_ATTRIBUTES, value, text);
98
+ },
99
99
  };
100
100
 
101
101
  Object.freeze(validatorsMap);