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/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
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
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
|
|
25
|
+
const serverAddress = process.env[envVars.SUITEST_BE_SERVER] || 'the.suite.st';
|
|
25
26
|
|
|
26
27
|
const main = Object.freeze({
|
|
27
|
-
apiUrl:
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
298
|
-
|
|
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
|
-
|
|
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:
|
|
330
|
-
setContext(context:
|
|
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
|
-
|
|
364
|
-
psVideo?: true,
|
|
316
|
+
active?: true,
|
|
365
317
|
}
|
|
366
318
|
|
|
367
319
|
type ScreenOrientationValues =
|
package/lib/api/endpoints.js
CHANGED
|
@@ -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);
|
package/lib/api/webSockets.js
CHANGED
|
@@ -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/
|
|
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');
|
|
@@ -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 =
|
|
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;
|
|
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
|
-
|
|
80
|
-
|
|
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 (
|
|
276
|
-
|
|
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
|
|
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:
|
|
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) =>
|
|
36
|
-
|
|
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
|
|
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
|
|
11
|
-
const {authNotAllowed, sessionClosed, sessionClosing} = require('../texts');
|
|
12
|
-
const envVars = require('../constants/enviroment');
|
|
8
|
+
const {sessionClosed, sessionClosing} = require('../texts');
|
|
13
9
|
|
|
14
10
|
/**
|
|
15
|
-
*
|
|
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
|
-
//
|
|
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();
|