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
package/config/index.js CHANGED
@@ -11,26 +11,27 @@ 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/enviroment');
14
15
 
15
16
  const sentryDsn = 'https://1f74b885d0c44549b57f307733d60351:dd736ff3ac994104ab6635da53d9be2e@sentry.io/288812';
16
17
  const DEFAULT_TIMEOUT = 2000;
17
18
 
18
19
  const overridableFields = [
19
- 'tokenKey', 'tokenId', 'tokenPassword', 'testPackId', 'concurrency', // launcher automated
20
- 'username', 'password', 'orgId', 'deviceId', 'appConfigId', 'inspect', 'inspectBrk', // launcher intaractive
21
- 'logLevel', 'logDir', 'timestamp', 'configFile', 'disallowCrashReports', 'defaultTimeout', // launcher common
20
+ 'tokenId', 'tokenPassword', 'concurrency', 'preset', 'presets', 'deviceId', 'appConfigId', 'inspect', 'inspectBrk',
21
+ 'logLevel', 'logDir', 'timestamp', 'configFile', 'disallowCrashReports', 'defaultTimeout', 'screenshotDir',
22
+ 'includeChangelist',
22
23
  ];
23
24
 
24
- const configurableFields = ['logLevel', 'disallowCrashReports', 'defaultTimeout'];
25
+ const serverAddress = process.env[envVars.SUITEST_BE_SERVER] || 'the.suite.st';
25
26
 
26
27
  const main = Object.freeze({
27
- apiUrl: 'https://the.suite.st/api/public/v3',
28
+ apiUrl: `https://${serverAddress}/api/public/v4`,
28
29
  disallowCrashReports: false,
29
30
  logLevel: logLevels.normal,
30
31
  sentryDsn,
31
32
  timestamp: timestamp.default,
32
33
  defaultTimeout: DEFAULT_TIMEOUT,
33
- wsUrl: 'wss://the.suite.st/api/public/v3/socket',
34
+ wsUrl: `wss://${serverAddress}/api/public/v4/socket`,
34
35
  });
35
36
 
36
37
  const test = Object.freeze({
@@ -69,7 +70,6 @@ const configFactory = () => {
69
70
  return {
70
71
  config,
71
72
  overridableFields,
72
- configurableFields,
73
73
  extend,
74
74
  override,
75
75
  };
package/index.d.ts CHANGED
@@ -47,25 +47,15 @@ export = suitest;
47
47
 
48
48
  declare namespace suitest {
49
49
  export interface ISuitestBase extends NodeJS.EventEmitter {
50
- startTestPack(options: StartTestPackOptions): Promise<StartTestPackResult|SuitestError>;
51
50
  openSession(options: OpenSessionOptions): Promise<OpenSessionResult|SuitestError>;
52
51
  closeSession(): Promise<object|SuitestError>;
53
52
  setAppConfig(configId: string, options?: ConfigOverride): Promise<void|SuitestError>;
54
53
  pairDevice(deviceId: string): Promise<DeviceData|SuitestError>;
55
54
  releaseDevice(): Promise<void|SuitestError>;
56
- // @deprecated use startTest without arguments
57
- startTest(clientTestId: string, options?: StartTestOptions): Promise<void|SuitestError>;
58
- startTest(): Promise<void|SuitestError>;
59
- endTest(): Promise<void|SuitestError>;
60
- interactive(options: ReplOptions): Promise<void>;
55
+ startREPL(options?: ReplOptions): Promise<void>;
61
56
 
62
57
  // config
63
58
  getConfig(): ConfigureOptions;
64
-
65
- /**
66
- * @deprecated use separate methods for changing configuration properties
67
- */
68
- configure(config: Partial<ConfigureOptions>): void;
69
59
  setDefaultTimeout(timeout: ConfigureOptions['defaultTimeout']): void;
70
60
  setContinueOnFatalError(continueOnFatalError: ConfigureOptions['continueOnFatalError']): void;
71
61
  setDisallowCrashReports(disallowCrashReports: ConfigureOptions['disallowCrashReports']): void;
@@ -76,7 +66,7 @@ declare namespace suitest {
76
66
  application(): ApplicationChain;
77
67
  clearAppData(): ClearAppDataChain;
78
68
  cookie(cookieName: string): CookieChain;
79
- element(elementSelector: ElementSelector | string): ElementChain;
69
+ element(elementSelector: ElementSelector[] | ElementSelector | string): ElementChain;
80
70
  video(): VideoChain;
81
71
  psVideo(): PlayStationVideoChain;
82
72
  executeCommand(jsCode: string): ExecuteCommandChain;
@@ -110,10 +100,18 @@ declare namespace suitest {
110
100
 
111
101
  /**
112
102
  * @description the complete path to the file name where the screenshot should be saved.
103
+ * Can be defined as string with placeholders, for example default path
104
+ * to screenshots folder looks like {screenshotDir}/{dateTime}-{currentFile}-l{currentLine}.png.
105
+ * Available placeholders:
106
+ * - screenshotDir - default value is "screenshots"
107
+ * - dateTime - time when saving screenshot happens in YYYY-MM-DD-HH-mm-ss-SSS format
108
+ * - currentFile - file where saving screenshot requested
109
+ * - currentLine - line of code where saving screenshot requested
113
110
  * @example
114
111
  * suitest.saveScreenshot('/path/to/file.png');
112
+ * suitest.saveScreenshot('{screenshotDir}/{dateTime}-{currentFile}-l{currentLine}.png');
115
113
  */
116
- saveScreenshot(fileName: string): TakeScreenshotChain<void>;
114
+ saveScreenshot(fileName?: string): TakeScreenshotChain<void>;
117
115
 
118
116
  getPairedDevice(): null | {
119
117
  deviceId: string,
@@ -128,7 +126,7 @@ declare namespace suitest {
128
126
  inactivityTimeout?: number,
129
127
  status: string,
130
128
  displayName?: string,
131
- shortDisplayName?: string
129
+ shortDisplayName?: string,
132
130
  }
133
131
 
134
132
  // constants
@@ -154,7 +152,6 @@ declare namespace suitest {
154
152
  authContext: AuthContext;
155
153
  appContext: Context;
156
154
  pairedDeviceContext: Context;
157
- testContext: Context;
158
155
 
159
156
  on(eventName: 'consoleLog', listener: (consoleLog: ConsoleLogEvent) => void): this;
160
157
  on(eventName: 'networkLog', listener: (networkLog: NetworkLogEvent) => void): this;
@@ -173,7 +170,7 @@ declare namespace suitest {
173
170
  application(): ApplicationChain;
174
171
  clearAppData(): ClearAppDataChain;
175
172
  cookie(cookieName: string): CookieChain;
176
- element(elementSelector: ElementSelector | string): ElementChain;
173
+ element(elementSelector: ElementSelector[] | ElementSelector | string): Omit<ElementChain, 'getCssProperties' | 'handle' | 'getAttributes'>;
177
174
  video(): VideoChain;
178
175
  psVideo(): PlayStationVideoChain;
179
176
  executeCommand(jsCode: string): ExecuteCommandChain;
@@ -247,7 +244,7 @@ declare namespace suitest {
247
244
  url?: string;
248
245
  suitestify?: boolean;
249
246
  domainList?: string[];
250
- freezeRules?: Array<{
247
+ mapRules?: Array<{
251
248
  methods: string[];
252
249
  url: string;
253
250
  type: string;
@@ -262,56 +259,13 @@ declare namespace suitest {
262
259
  [key: string]: any; // user should have ability to pass any property to config object
263
260
  }
264
261
 
265
- interface StartTestPackOptions {
266
- testPackId: number;
267
- accessTokenKey: string;
268
- accessTokenPassword: string;
269
- config?: ConfigOverride;
270
- metadata?: {
271
- version?: string;
272
- hash?: string;
273
- link?: string;
274
- },
275
- commitHash?: string;
276
- appVersion?: string;
277
- vcsBranch?: string;
278
- allowServiceCalls?: boolean;
279
- includeChangelist?: boolean;
280
- }
281
-
282
- interface StartTestPackResult {
283
- deviceAccessToken: string;
284
- tokenValidUntil: string;
285
- testPackRunId: string;
286
- testPack: {
287
- name: string;
288
- models: Array<{
289
- modelId: string;
290
- firmware: string;
291
- }>,
292
- devices: Array<{deviceId: string}>;
293
- }
294
- }
295
-
296
262
  type OpenSessionOptions = {
297
- username: string;
298
- password: string;
299
- orgId: string;
300
- } | {
301
- sessionToken: string;
302
- } | {
303
- accessTokenKey: string;
304
- accessTokenPassword: string;
263
+ tokenId: string;
264
+ tokenPassword: string;
305
265
  }
306
266
 
307
267
  interface OpenSessionResult {
308
- deviceAccessToken: string;
309
- tokenValidUntil: string;
310
- }
311
-
312
- interface StartTestOptions {
313
- name?: string;
314
- description?: string;
268
+ accessToken: string;
315
269
  }
316
270
 
317
271
  interface ConfigureOptions {
@@ -321,13 +275,9 @@ declare namespace suitest {
321
275
  defaultTimeout: number;
322
276
  }
323
277
 
324
- interface ResponseError {
325
- errorType: string;
326
- }
327
-
328
278
  interface Context {
329
- context: any;
330
- setContext(context: symbol): void;
279
+ context: unknown;
280
+ setContext(context: unknown): void;
331
281
  clear(): void;
332
282
  }
333
283
 
@@ -354,14 +304,16 @@ declare namespace suitest {
354
304
  apiId?: string;
355
305
  css?: string,
356
306
  xpath?: string,
307
+ handle?: string,
357
308
  attributes?: string,
358
309
  text?: string,
310
+ linkText?: string,
311
+ partialLinkText?: string,
359
312
  position?: string,
360
313
  size?: string,
361
314
  color?: string,
362
315
  index?: number,
363
- video?: true,
364
- psVideo?: true,
316
+ active?: true,
365
317
  }
366
318
 
367
319
  type ScreenOrientationValues =
@@ -5,24 +5,17 @@
5
5
  */
6
6
 
7
7
  const endpoints = {
8
- session: '/get-interactive-device-access-token',
9
- sessionClose: '/invalidate-token',
10
- testPackGenTokens: '/test-packs/:id/get-device-access-token',
11
8
  apps: '/apps',
12
9
  appById: '/apps/:appId',
13
10
  appTags: '/apps/:appId/tags',
14
11
  appConfigs: '/apps/:appId/configs',
15
12
  appConfigById: '/apps/:appId/configs/:configId',
16
- appTestPacks: '/apps/:appId/test-packs',
17
- appTestPackById: '/apps/:appId/test-packs/:testPackId',
18
13
  appTestDefinitions: '/apps/:appId/test-definitions',
19
14
  appTestDefinitionById: '/apps/:appId/versions/:versionId/tests/:testId',
20
15
  devices: '/devices',
21
16
  testRun: '/test-run',
22
17
  testRunById: '/test-run/:testRunId',
23
18
  testRunOnDevice: '/test-run/:testRunId/device/:deviceId',
24
- testPackRun: '/test-pack-run',
25
- testPackRunById: '/test-pack-run/:testPackRunId',
26
19
  };
27
20
 
28
21
  Object.freeze(endpoints);
@@ -11,7 +11,7 @@ const fetch = require('node-fetch');
11
11
 
12
12
  const SuitestError = require('../utils/SuitestError');
13
13
  const texts = require('../texts');
14
- const {handleProgress} = require('../utils/interactiveProgressHandler');
14
+ const {handleProgress} = require('../utils/progressHandler');
15
15
  const {getInfoErrorMessage} = require('../utils/socketErrorMessages');
16
16
  const {translateNotStartedReason} = require('../utils/translateResults');
17
17
  const logLevels = require('../../lib/constants/logLevels');
@@ -3,10 +3,8 @@
3
3
  */
4
4
 
5
5
  const contentTypes = {
6
- endTest: 'endTest',
7
6
  pairDevice: 'connectDevice',
8
7
  releaseDevice: 'releaseDevice',
9
- startTest: 'startTest',
10
8
  selectConfiguration: 'selectConfiguration',
11
9
  enableDebugMode: 'enableDebugMode',
12
10
  query: 'query',
@@ -32,10 +32,12 @@ const {
32
32
  makeToJSONComposer,
33
33
  untilComposer,
34
34
  visibleComposer,
35
+ handleComposer,
36
+ attributesComposer,
37
+ cssPropsComposer,
35
38
  } = require('../composers');
36
39
  const {
37
40
  invalidInputMessage,
38
- warnVideoAsElementDeprecation,
39
41
  assertElementMalformed,
40
42
  } = require('../texts');
41
43
  const {validate, validators} = require('../validation');
@@ -52,6 +54,29 @@ const {
52
54
  applyUntilCondition,
53
55
  } = require('../utils/chainUtils');
54
56
 
57
+ const replaceIndexByIfMultipleFoundReturn = (selector) => {
58
+ if (Reflect.has(selector, 'index')) {
59
+ const result = {...selector};
60
+
61
+ result.ifMultipleFoundReturn = result.index;
62
+ delete result.index;
63
+
64
+ return result;
65
+ }
66
+
67
+ return selector;
68
+ };
69
+
70
+ const processElementSelector = (selector) => {
71
+ const processObjectSelector = (s) => replaceIndexByIfMultipleFoundReturn(omit(['apiId'], s));
72
+
73
+ if (Array.isArray(selector)) {
74
+ return selector.map(processObjectSelector);
75
+ }
76
+
77
+ return processObjectSelector(selector);
78
+ };
79
+
55
80
  const elementFactory = (classInstance, video) => {
56
81
  const toJSON = data => {
57
82
  const type = getRequestType(data);
@@ -60,15 +85,10 @@ const elementFactory = (classInstance, video) => {
60
85
  type: 'element',
61
86
  };
62
87
  const apiId = data.selector.apiId;
63
- const selectors = omit(['apiId'], data.selector);
64
-
65
- if (Reflect.has(selectors, 'index')) {
66
- selectors.ifMultipleFoundReturn = selectors.index;
67
- delete selectors.index;
68
- }
88
+ const selectors = processElementSelector(data.selector);
69
89
 
70
90
  if (apiId) {
71
- subject.apiId = apiId; // not yet supported by network api
91
+ subject.apiId = apiId;
72
92
  } else {
73
93
  subject.val = selectors;
74
94
  }
@@ -76,9 +96,22 @@ const elementFactory = (classInstance, video) => {
76
96
  // query
77
97
  if (type === 'query') {
78
98
  socketMessage.subject = {
79
- type: 'elementProps',
80
- selector: apiId ? data.selector : selectors,
99
+ selector: apiId
100
+ ? data.selector
101
+ : selectors,
81
102
  };
103
+ if (data.handle) {
104
+ socketMessage.subject.type = 'elementHandle';
105
+ socketMessage.subject.multiple = data.handle.multiple;
106
+ } else if (data.attributes) {
107
+ socketMessage.subject.type = 'elementAttributes';
108
+ socketMessage.subject.attributes = data.attributes;
109
+ } else if (data.cssProps) {
110
+ socketMessage.subject.type = 'elementCssProps';
111
+ socketMessage.subject.elementCssProps = data.cssProps;
112
+ } else {
113
+ socketMessage.subject.type = 'elementProps';
114
+ }
82
115
  }
83
116
 
84
117
  // click and tap
@@ -178,6 +211,25 @@ const elementFactory = (classInstance, video) => {
178
211
  output.push(assertComposer);
179
212
  }
180
213
 
214
+ // should check that no any composer was applied to current chain (should be empty).
215
+ if (!data.isNegated
216
+ && !data.isAssert
217
+ && !data.isClick
218
+ && !data.tap
219
+ && !data.isSwipe
220
+ && !data.isScroll
221
+ && !data.isMoveTo
222
+ && isNil(data.sendText)
223
+ && isNil(data.setText)
224
+ && !data.comparator
225
+ && !data.timeout
226
+ && !data.handle
227
+ && !data.attributes
228
+ && !data.cssProps
229
+ ) {
230
+ output.push(cssPropsComposer, handleComposer, attributesComposer);
231
+ }
232
+
181
233
  if (!data.isNegated
182
234
  && !data.isClick
183
235
  && !data.tap
@@ -188,6 +240,9 @@ const elementFactory = (classInstance, video) => {
188
240
  && isNil(data.setText)
189
241
  && !isMatchesComparator
190
242
  && !isVisibleComparator
243
+ && !data.handle
244
+ && !data.cssProps
245
+ && !data.attributes
191
246
  ) {
192
247
  output.push(notComposer);
193
248
  }
@@ -205,6 +260,9 @@ const elementFactory = (classInstance, video) => {
205
260
  && !data.tap
206
261
  && isNil(data.sendText)
207
262
  && isNil(data.setText)
263
+ && !data.handle
264
+ && !data.cssProps
265
+ && !data.attributes
208
266
  ) {
209
267
  output.push(timeoutComposer);
210
268
  }
@@ -218,6 +276,9 @@ const elementFactory = (classInstance, video) => {
218
276
  && !data.isScroll
219
277
  && isNil(data.sendText)
220
278
  && isNil(data.setText)
279
+ && !data.handle
280
+ && !data.cssProps
281
+ && !data.attributes
221
282
  ) {
222
283
  output.push(existComposer, visibleComposer);
223
284
  if (!data.isNegated) {
@@ -238,6 +299,9 @@ const elementFactory = (classInstance, video) => {
238
299
  && isNil(data.sendText)
239
300
  && isNil(data.setText)
240
301
  && !data.isNegated
302
+ && !data.handle
303
+ && !data.cssProps
304
+ && !data.attributes
241
305
  ) {
242
306
  output.push(
243
307
  clickComposer,
@@ -267,27 +331,34 @@ const elementFactory = (classInstance, video) => {
267
331
  return output;
268
332
  };
269
333
 
270
- const deprecatedVideo = util.deprecate(video, warnVideoAsElementDeprecation());
271
-
272
334
  const elementChain = elementSelector => {
273
335
  const selector = typeof elementSelector === 'string' ? {apiId: elementSelector} : elementSelector;
274
336
 
275
- if (selector && selector.video && Object.keys(selector).length === 1) {
276
- return deprecatedVideo();
337
+ if (Array.isArray(selector)) {
338
+ selector.forEach(selectorItem => {
339
+ validate(
340
+ validators.ELEMENT_SELECTOR,
341
+ selectorItem,
342
+ invalidInputMessage('element', 'Element selector'),
343
+ );
344
+ });
345
+ } else {
346
+ validate(
347
+ validators.ELEMENT_SELECTOR,
348
+ selector,
349
+ invalidInputMessage('element', 'Element selector'),
350
+ );
277
351
  }
278
352
 
279
353
  return makeChain(classInstance, getComposers, {
280
354
  type: 'element',
281
- selector: validate(
282
- validators.ELEMENT_SELECTOR,
283
- selector,
284
- invalidInputMessage('element', 'Element selector')),
355
+ selector,
285
356
  });
286
357
  };
287
358
 
288
359
  return {
289
360
  element: elementChain,
290
- elementAssert: elementSelector => elementChain(elementSelector).toAssert(),
361
+ elementAssert: (selectors) => elementChain(selectors).toAssert(),
291
362
 
292
363
  // For testing
293
364
  getComposers,
@@ -1,3 +1,5 @@
1
+ const path = require('path');
2
+ const moment = require('moment');
1
3
  const makeChain = require('../utils/makeChain');
2
4
  const {
3
5
  makeToStringComposer,
@@ -7,8 +9,55 @@ const {
7
9
  } = require('../composers');
8
10
  const t = require('../texts');
9
11
  const {validate, validators} = require('../validation');
12
+ const {getFirstNotSuitestStackItem} = require('../utils/stackTraceParser');
13
+ const {DEFAULT_SCREENSHOT_DIR} = require('../constants');
14
+
15
+ const PATH_PLACEHOLDERS = ['screenshotDir', 'dateTime', 'currentFile', 'currentLine'];
16
+ // /{(screenshotDir|dateTime|currentFile|currentLine)}/
17
+ const CONTAINS_PLACEHOLDER_REGEX = new RegExp(
18
+ '{(' + PATH_PLACEHOLDERS.join('|') + ')}', 'g',
19
+ );
20
+ // '{screenshotDir}/{dateTime}-{currentFile}-l{currentLine}.png'
21
+ const DEFAULT_PATH = path.join('{screenshotDir}', '{dateTime}-{currentFile}-l{currentLine}.png');
22
+
23
+ /**
24
+ * @description checks that path contains any of placeholders
25
+ * @param {string} path
26
+ * @returns {boolean}
27
+ */
28
+ function pathContainsPlaceholders(path) {
29
+ return !!path.match(CONTAINS_PLACEHOLDER_REGEX);
30
+ }
10
31
 
11
32
  const saveScreenshotFactory = (classInstance) => {
33
+ function getPlaceholdersValues(date = new Date()) {
34
+ const userStackItem = getFirstNotSuitestStackItem();
35
+
36
+ return {
37
+ screenshotDir: path.resolve(
38
+ process.cwd(),
39
+ classInstance.config.screenshotDir || DEFAULT_SCREENSHOT_DIR,
40
+ ),
41
+ // example: 2021-09-06-13-40-29-954
42
+ dateTime: moment(date).format('YYYY-MM-DD-HH-mm-ss-SSS'),
43
+ currentFile: path.basename(userStackItem.file),
44
+ currentLine: userStackItem.line,
45
+ };
46
+ }
47
+
48
+ function processFilePath(filePath) {
49
+ if (pathContainsPlaceholders(filePath)) {
50
+ const placeholdersValues = getPlaceholdersValues();
51
+
52
+ return filePath.replace(
53
+ CONTAINS_PLACEHOLDER_REGEX,
54
+ (_matching, value) => placeholdersValues[value],
55
+ );
56
+ }
57
+
58
+ return filePath;
59
+ }
60
+
12
61
  const toJSON = () => ({type: 'takeScreenshot'});
13
62
 
14
63
  const toStringComposer = makeToStringComposer(toJSON);
@@ -32,20 +81,26 @@ const saveScreenshotFactory = (classInstance) => {
32
81
  /**
33
82
  * @param {string} fileName
34
83
  */
35
- const saveScreenshotChain = (fileName) => makeChain(classInstance, getComposers, {
36
- type: 'takeScreenshot',
37
- fileName: validate(
84
+ const saveScreenshotChain = (fileName = DEFAULT_PATH) => {
85
+ validate(
38
86
  validators.NON_EMPTY_STRING,
39
87
  fileName,
40
88
  t.invalidInputMessage('saveScreenshot', 'File name'),
41
- ),
42
- });
89
+ );
90
+
91
+ return makeChain(classInstance, getComposers, {
92
+ type: 'takeScreenshot',
93
+ fileName: processFilePath(fileName),
94
+ });
95
+ };
43
96
 
44
97
  return {
45
98
  saveScreenshot: saveScreenshotChain,
46
99
  // For Unit Testing
47
100
  getComposers,
48
101
  toJSON,
102
+ getPlaceholdersValues,
103
+ processFilePath,
49
104
  };
50
105
  };
51
106
 
@@ -1,46 +1,21 @@
1
1
  /**
2
- * Close session suitest method allows user to invalidate session token and clear current auth context.
2
+ * Close session suitest method that clear current auth context.
3
3
  */
4
4
 
5
- const request = require('../api/request');
6
- const endpoints = require('../api/endpoints');
7
5
  const ipcClient = require('../testLauncher/ipc/client');
8
6
  const ipcServer = require('../testLauncher/ipc/server');
9
7
  const chainPromise = require('../utils/chainPromise');
10
- const SuitestError = require('../utils/SuitestError');
11
- const {authNotAllowed, sessionClosed, sessionClosing} = require('../texts');
12
- const envVars = require('../constants/enviroment');
8
+ const {sessionClosed, sessionClosing} = require('../texts');
13
9
 
14
10
  /**
15
- * Invalidate session token on server side.
16
- * Clear current auth context.
11
+ * @description Clear current auth context.
17
12
  * @param {Object} instance of main class
18
13
  * @returns {Promise} response object
19
14
  */
20
15
  async function closeSession({webSockets, authContext, appContext, logger}) {
21
16
  logger.delayed(sessionClosing());
22
17
 
23
- // don't invalidate tokens in test launcher child processes
24
- if (!process.env[envVars.SUITEST_CHILD_PROCESS]) {
25
- // authorize and make api request
26
- const authedRequestObject = await authContext.authorizeHttp(endpoints.sessionClose, {
27
- method: 'POST',
28
- body: {
29
- token: authContext.getToken(),
30
- },
31
- }, {commandName: closeSession.name});
32
-
33
- await request(
34
- endpoints.sessionClose,
35
- authedRequestObject,
36
- () => new SuitestError(
37
- authNotAllowed(closeSession.name),
38
- SuitestError.AUTH_NOT_ALLOWED
39
- )
40
- );
41
- }
42
-
43
- // if success, clear session context, disconnect ws
18
+ // if success, clear auth context, disconnect ws
44
19
  webSockets.disconnect();
45
20
  ipcClient.disconnect();
46
21
  ipcServer.close();