@devassure/tpp-engine 0.0.0-stage → 1.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/DEV_README.md +19 -0
- package/README.md +6 -3
- package/index.js +121 -0
- package/package.json +30 -4
- package/src/accessibility/combined-a11y-report.js +208 -0
- package/src/accessibility/tpp-a11y.js +81 -0
- package/src/actions.js +3201 -0
- package/src/api/api-auth.js +18 -0
- package/src/api/api-service.js +289 -0
- package/src/app-config.json +7 -0
- package/src/autoheal-actions.js +309 -0
- package/src/console-log/console-log-manager.js +39 -0
- package/src/console-log/console-log-utils.js +201 -0
- package/src/database/mysql/mysql-service.js +23 -0
- package/src/database/neo4j/neo4j-service.js +32 -0
- package/src/database/oracle/oracle-service.js +48 -0
- package/src/database/pg/pg-service.js +29 -0
- package/src/downloads/downloads-manager.js +86 -0
- package/src/downloads/downloads-utils.js +113 -0
- package/src/globalsetup.js +68 -0
- package/src/globalteardown.js +57 -0
- package/src/hooks/each-hook.js +45 -0
- package/src/hooks/hook-utils.js +21 -0
- package/src/library/authenticator/authenticator-utils.js +43 -0
- package/src/library/csv/csv-utils.js +297 -0
- package/src/library/date/date-utils.js +168 -0
- package/src/library/emailTesting/TokenConfig.js +46 -0
- package/src/library/emailTesting/emailTesting-utils.js +527 -0
- package/src/library/excel/excel-utils.js +922 -0
- package/src/library/file/file-action-utils.js +129 -0
- package/src/library/file/file-utils.js +127 -0
- package/src/library/json/json-utils.js +104 -0
- package/src/library/math/math-utils.js +283 -0
- package/src/library/softAssert/softAssert-utils.js +154 -0
- package/src/library/table/table-utils.js +92 -0
- package/src/library/text/text-utils.js +45 -0
- package/src/library/tpp-library.js +81 -0
- package/src/library/web/slider-utils.js +66 -0
- package/src/mobile/appium/appium-manager.js +158 -0
- package/src/mobile/device-cloud/browserstack-service.js +44 -0
- package/src/mobile/device-cloud/commands/browserstack-commands.js +20 -0
- package/src/mobile/device-cloud/commands/device-cloud-commands.js +17 -0
- package/src/mobile/device-cloud/device-cloud-service.js +29 -0
- package/src/mobile/device-cloud/pcloudy-service.js +65 -0
- package/src/mobile/mobile-actions.js +1568 -0
- package/src/mobile/recorder-utils/css-selector-generator.js +2 -0
- package/src/mobile/recorder-utils/finder.js +752 -0
- package/src/mobile/recorder-utils/mobile-recorder.js +266 -0
- package/src/mobile/web-driver-utils.js +110 -0
- package/src/models/tpp-element.js +58 -0
- package/src/models/tpp-mobile-element.js +50 -0
- package/src/network-log/network-log-manager.js +96 -0
- package/src/network-log/network-log-utils.js +361 -0
- package/src/types/elements.types.js +24 -0
- package/src/utils/ai-utils.js +48 -0
- package/src/utils/assert-utils.js +50 -0
- package/src/utils/autoheal-utils.js +9 -0
- package/src/utils/common-utils.js +120 -0
- package/src/utils/cookie-utils.js +70 -0
- package/src/utils/json-utils.js +10 -0
- package/src/utils/log-utils.js +10 -0
- package/src/utils/logger.js +76 -0
- package/src/utils/screenshot-utils.js +42 -0
- package/src/utils/token-utils.js +18 -0
- package/src/utils/vectordb-utils.js +125 -0
- package/src/visual-test/tpp-visual-test.js +122 -0
- package/src/web/web-executor-agent.js +149 -0
- package/src/web/web-executor-data-fetch.js +353 -0
- package/test-excel-utils.js +570 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
const { Logger, LOG_PREFIX } = require("../../utils/logger");
|
|
2
|
+
const { compare } = require("../../utils/assert-utils");
|
|
3
|
+
|
|
4
|
+
class SoftAssertUtils{
|
|
5
|
+
constructor(caller, blacklistedLogs, takeScreenShot) {
|
|
6
|
+
this.caller = caller;
|
|
7
|
+
this.blacklistedLogs = blacklistedLogs;
|
|
8
|
+
this.takeScreenShot = takeScreenShot;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
setCaller(caller) {
|
|
12
|
+
this.caller = caller;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
setBlacklistedLogs(blacklistedLogs) {
|
|
16
|
+
this.blacklistedLogs = blacklistedLogs;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
create_soft_assert() {
|
|
20
|
+
const logLabel = `Create Soft Assert`;
|
|
21
|
+
const logger = new Logger(this.caller, this.blacklistedLogs);
|
|
22
|
+
logger.startLog(logLabel);
|
|
23
|
+
const softAssert = {
|
|
24
|
+
assertions: [],
|
|
25
|
+
};
|
|
26
|
+
logger.endLog(logLabel);
|
|
27
|
+
return softAssert;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async soft_assert_text(softAssert, text, comparator, expected, customMessage) {
|
|
31
|
+
const logLabel = customMessage?.trim()
|
|
32
|
+
? `Soft Assert text, message: ${customMessage}, text: ${text?.toString()?.replaceAll("\n", " ")}, ${comparator}, expected: ${expected}`
|
|
33
|
+
: `Soft Assert text, text: ${text?.toString()?.replaceAll("\n", " ")}, ${comparator}, expected: ${expected}`;
|
|
34
|
+
const logger = new Logger(this.caller, this.blacklistedLogs);
|
|
35
|
+
logger.startLog(logLabel);
|
|
36
|
+
const result = compare(text, comparator, expected);
|
|
37
|
+
if (!result) {
|
|
38
|
+
logger.log(logLabel, "Soft assertion failure.");
|
|
39
|
+
await this.takeScreenShot(`softAssertFailure ${softAssert.assertions.length+1}`);
|
|
40
|
+
let message = `Soft Assert Text is not as expected. actual: ${text} ---- comparator: ${comparator} ---- expected: ${expected}`;
|
|
41
|
+
if(customMessage) {
|
|
42
|
+
message = `${customMessage} -- ${message}`;
|
|
43
|
+
}
|
|
44
|
+
softAssert.assertions.push({ result, message });
|
|
45
|
+
}
|
|
46
|
+
logger.endLog(logLabel);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async soft_assert_number(softAssert, number, comparator, expected, customMessage) {
|
|
50
|
+
const logLabel = customMessage?.trim()
|
|
51
|
+
? `Soft Assert number, message: ${customMessage}, number: ${number}, ${comparator}, expected: ${expected}`
|
|
52
|
+
: `Soft Assert number, number: ${number}, ${comparator}, expected: ${expected}`;
|
|
53
|
+
const logger = new Logger(this.caller, this.blacklistedLogs);
|
|
54
|
+
logger.startLog(logLabel);
|
|
55
|
+
const result = compare(number, comparator, expected);
|
|
56
|
+
if (!result) {
|
|
57
|
+
logger.log(logLabel, "Soft assertion failure.");
|
|
58
|
+
await this.takeScreenShot(`softAssertFailure ${softAssert.assertions.length+1}`);
|
|
59
|
+
let message = `Soft Assert Number is not as expected. actual: ${number} ---- comparator: ${comparator} ---- expected: ${expected}`;
|
|
60
|
+
if(customMessage) {
|
|
61
|
+
message = `${customMessage} -- ${message}`;
|
|
62
|
+
}
|
|
63
|
+
softAssert.assertions.push({ result, message });
|
|
64
|
+
}
|
|
65
|
+
logger.endLog(logLabel);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async soft_assert_boolean(softAssert, value, expected, customMessage) {
|
|
69
|
+
const logLabel = customMessage?.trim()
|
|
70
|
+
? `Soft Assert boolean, message: ${customMessage}, value: ${value}, expected: ${expected}`
|
|
71
|
+
: `Soft Assert boolean, value: ${value}, expected: ${expected}`;
|
|
72
|
+
const logger = new Logger(this.caller, this.blacklistedLogs);
|
|
73
|
+
logger.startLog(logLabel);
|
|
74
|
+
const result = !!value === !!expected;
|
|
75
|
+
if (!result) {
|
|
76
|
+
logger.log(logLabel, "Soft assertion failure.");
|
|
77
|
+
await this.takeScreenShot(`softAssertFailure ${softAssert.assertions.length+1}`);
|
|
78
|
+
let message = `Soft Assert Boolean value is not as expected. actual: ${value} ---- expected: ${expected}`;
|
|
79
|
+
if(customMessage) {
|
|
80
|
+
message = `${customMessage} -- ${message}`;
|
|
81
|
+
}
|
|
82
|
+
softAssert.assertions.push({ result, message });
|
|
83
|
+
}
|
|
84
|
+
logger.endLog(logLabel);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async soft_assert_text_with_message(softAssert, text, comparator, expected, message) {
|
|
88
|
+
this.soft_assert_text(softAssert, text, comparator, expected, message);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async soft_assert_number_with_message(softAssert ,number, comparator, expected, message) {
|
|
92
|
+
this.soft_assert_number(softAssert ,number, comparator, expected, message)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async soft_assert_boolean_with_message(softAssert ,value, expected, message) {
|
|
96
|
+
this.soft_assert_boolean(softAssert ,value, expected, message)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async soft_assert_is_not_null_with_message(softAssert, value, customMessage) {
|
|
100
|
+
const logLabel = customMessage?.trim()
|
|
101
|
+
? `Soft Assert is NOT NULL, message: ${customMessage}, variable: ${value}`
|
|
102
|
+
: `Soft Assert is NOT NULL, variable: ${value}`;
|
|
103
|
+
const logger = new Logger(this.caller, this.blacklistedLogs);
|
|
104
|
+
logger.startLog(logLabel);
|
|
105
|
+
const result = value != null;
|
|
106
|
+
if (!result) {
|
|
107
|
+
logger.log(logLabel, "Soft assertion failure.");
|
|
108
|
+
await this.takeScreenShot(`softAssertFailure ${softAssert.assertions.length+1}`);
|
|
109
|
+
let message = `Soft Assert - input variable is null. value: ${value}`;
|
|
110
|
+
if(customMessage) {
|
|
111
|
+
message = `${customMessage} -- ${message}`;
|
|
112
|
+
}
|
|
113
|
+
softAssert.assertions.push({ result, message });
|
|
114
|
+
}
|
|
115
|
+
logger.endLog(logLabel);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async soft_assert_is_null_with_message(softAssert, value, customMessage) {
|
|
119
|
+
const logLabel = customMessage?.trim()
|
|
120
|
+
? `Soft Assert is NULL, message: ${customMessage}, variable: ${value}`
|
|
121
|
+
: `Soft Assert is NULL, variable: ${value}`;
|
|
122
|
+
const logger = new Logger(this.caller, this.blacklistedLogs);
|
|
123
|
+
logger.startLog(logLabel);
|
|
124
|
+
const result = value == null;
|
|
125
|
+
if (!result) {
|
|
126
|
+
logger.log(logLabel, "Soft assertion failure.");
|
|
127
|
+
await this.takeScreenShot(`softAssertFailure ${softAssert.assertions.length+1}`);
|
|
128
|
+
let message = `Soft Assert - input variable is not null. value: ${value}`;
|
|
129
|
+
if(customMessage) {
|
|
130
|
+
message = `${customMessage} -- ${message}`;
|
|
131
|
+
}
|
|
132
|
+
softAssert.assertions.push({ result, message });
|
|
133
|
+
}
|
|
134
|
+
logger.endLog(logLabel);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
soft_assert_all(softAssert){
|
|
138
|
+
const logLabel = `Soft Assert All`;
|
|
139
|
+
const logger = new Logger(this.caller, this.blacklistedLogs);
|
|
140
|
+
logger.startLog(logLabel);
|
|
141
|
+
if (softAssert.assertions?.length > 0) {
|
|
142
|
+
const errors = softAssert.assertions.map(a => a.message).join('\n');
|
|
143
|
+
logger.failureLog(logLabel);
|
|
144
|
+
logger.action_log(`${LOG_PREFIX}${this.caller} :: [EXCEPTION] Soft assertion failures:`);
|
|
145
|
+
softAssert.assertions.forEach(error =>
|
|
146
|
+
logger.action_log(`${LOG_PREFIX}${this.caller} :: [EXCEPTION] ${error.message}`)
|
|
147
|
+
);
|
|
148
|
+
throw {message: `Soft assert - ${softAssert.assertions.length} validations failed!\n${errors}`};
|
|
149
|
+
}
|
|
150
|
+
logger.endLog(logLabel);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
exports.SoftAssertUtils = SoftAssertUtils;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const { isNumber, getLastElements, getFirstElements, getElementsInRange, getElementsAtIndices } = require("../../utils/common-utils");
|
|
2
|
+
const { Logger } = require("../../utils/logger");
|
|
3
|
+
|
|
4
|
+
class TableUtils {
|
|
5
|
+
|
|
6
|
+
constructor(caller, blacklistedLogs, logLabel) {
|
|
7
|
+
this.caller = caller;
|
|
8
|
+
this.blacklistedLogs = blacklistedLogs;
|
|
9
|
+
this.logLabel = logLabel;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
filterColumns(params, fields, ignoreMissingData, firstRowHeader) {
|
|
13
|
+
let logLabel = this.logLabel;
|
|
14
|
+
let logger = new Logger(this.caller, this.blacklistedLogs);
|
|
15
|
+
|
|
16
|
+
//Filter columns
|
|
17
|
+
if(!!params.columns && params.columns !== 'all') {
|
|
18
|
+
if(params.columns.startsWith('last')) {
|
|
19
|
+
const count = params.columns.replaceAll('last', '').trim();
|
|
20
|
+
if(!isNumber(count)) {
|
|
21
|
+
logger.failureLog(logLabel);
|
|
22
|
+
throw {message: `${params.columns} is not a valid columns filter!`}
|
|
23
|
+
}
|
|
24
|
+
fields = getLastElements(fields, parseInt(count), ignoreMissingData)
|
|
25
|
+
} if(params.columns.startsWith('first')) {
|
|
26
|
+
const count = params.columns.replaceAll('first', '').trim();
|
|
27
|
+
if(!isNumber(count)) {
|
|
28
|
+
logger.failureLog(logLabel);
|
|
29
|
+
throw {message: `${params.columns} is not a valid columns filter!`}
|
|
30
|
+
}
|
|
31
|
+
fields = getFirstElements(fields, parseInt(count), ignoreMissingData)
|
|
32
|
+
} else if(params.columns.includes('-')) {
|
|
33
|
+
const data = getElementsInRange(fields, params.columns, ignoreMissingData);
|
|
34
|
+
if(!data.success) {
|
|
35
|
+
logger.failureLog(logLabel);
|
|
36
|
+
throw {message: data.error};
|
|
37
|
+
}
|
|
38
|
+
fields = data.data;
|
|
39
|
+
} else if(params.columns.includes(',')) {
|
|
40
|
+
if(isNumber(params.columns.split(',')[0].trim())) {
|
|
41
|
+
const data = getElementsAtIndices(fields, params.columns, ignoreMissingData);
|
|
42
|
+
if(!data.success) {
|
|
43
|
+
logger.failureLog(logLabel);
|
|
44
|
+
throw {message: data.error};
|
|
45
|
+
}
|
|
46
|
+
fields = data.data;
|
|
47
|
+
} else {
|
|
48
|
+
if(!firstRowHeader) {
|
|
49
|
+
logger.failureLog(logLabel);
|
|
50
|
+
throw {message: `First row is not a set as header, column names can't be used to filter data!`}
|
|
51
|
+
}
|
|
52
|
+
let columns = params.columns.split(',');
|
|
53
|
+
const missing = ignoreMissingData
|
|
54
|
+
? []
|
|
55
|
+
: columns.filter(c => !fields.find(f => f.trim() === c.trim()));
|
|
56
|
+
if(missing?.length) {
|
|
57
|
+
logger.failureLog(logLabel);
|
|
58
|
+
throw {message: `Some columns are missing! ${missing.join(',')}`}
|
|
59
|
+
}
|
|
60
|
+
if(ignoreMissingData) columns = columns.filter(c => fields.find(f => f.trim() === c.trim()))
|
|
61
|
+
fields = columns;
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
if(isNumber(params.columns)) {
|
|
65
|
+
const index = parseInt(params.columns.trim());
|
|
66
|
+
if(index < 0 || (index >= fields.length && !ignoreMissingData)) {
|
|
67
|
+
logger.failureLog(logLabel);
|
|
68
|
+
throw {message: `${params.columns} - index is invalid. Columns = ${fields.length}`};
|
|
69
|
+
}
|
|
70
|
+
fields = ignoreMissingData && index >= fields.length ? [] : [fields[index]];
|
|
71
|
+
} else {
|
|
72
|
+
if(!firstRowHeader) {
|
|
73
|
+
logger.failureLog(logLabel);
|
|
74
|
+
throw {message: `First row is not a set as header, column names can't be used to filter data!`}
|
|
75
|
+
}
|
|
76
|
+
const col = params.columns.trim();
|
|
77
|
+
const missing = !fields.find(f => f.trim() === col);
|
|
78
|
+
if(!ignoreMissingData && missing) {
|
|
79
|
+
logger.failureLog(logLabel);
|
|
80
|
+
throw {message: `Column ${params.columns} is not found!`};
|
|
81
|
+
}
|
|
82
|
+
fields = ignoreMissingData && missing ? [] : [params.columns.trim()];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return fields;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
exports.TableUtils = TableUtils;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const { Logger } = require("../../utils/logger");
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
|
|
4
|
+
class TextUtils {
|
|
5
|
+
constructor(caller, blacklistedLogs) {
|
|
6
|
+
this.caller = caller;
|
|
7
|
+
this.blacklistedLogs = blacklistedLogs;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
setCaller(caller) {
|
|
11
|
+
this.caller = caller;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
setBlacklistedLogs(blacklistedLogs) {
|
|
15
|
+
this.blacklistedLogs = blacklistedLogs;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
random_string(length) {
|
|
19
|
+
const logLabel = `Text.Random string of length ${length}`;
|
|
20
|
+
const logger = new Logger(this.caller, this.blacklistedLogs);
|
|
21
|
+
logger.startLog(logLabel);
|
|
22
|
+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
23
|
+
let result = '';
|
|
24
|
+
for (let i = 0; i < length; i++) {
|
|
25
|
+
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
26
|
+
}
|
|
27
|
+
logger.endLog(logLabel);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
random_alphanumeric_string(length) {
|
|
32
|
+
const logLabel = `Text.Random alphanumeric string of length ${length}`;
|
|
33
|
+
const logger = new Logger(this.caller, this.blacklistedLogs);
|
|
34
|
+
logger.startLog(logLabel);
|
|
35
|
+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
36
|
+
let result = '';
|
|
37
|
+
for (let i = 0; i < length; i++) {
|
|
38
|
+
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
39
|
+
}
|
|
40
|
+
logger.endLog(logLabel);
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
exports.TextUtils = TextUtils;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const { ConsoleLogUtils } = require("../console-log/console-log-utils");
|
|
2
|
+
const { DownloadsUtils } = require("../downloads/downloads-utils");
|
|
3
|
+
const { NetworkLogUtils } = require("../network-log/network-log-utils");
|
|
4
|
+
const { CsvUtils } = require("./csv/csv-utils");
|
|
5
|
+
const { FileActionUtils } = require("./file/file-action-utils");
|
|
6
|
+
const { MathUtils } = require("./math/math-utils");
|
|
7
|
+
const { TextUtils } = require("./text/text-utils");
|
|
8
|
+
const { SoftAssertUtils } = require("./softAssert/softAssert-utils");
|
|
9
|
+
const { EmailTestingUtils } = require("./emailTesting/emailTesting-utils");
|
|
10
|
+
const { ExcelUtils } = require("./excel/excel-utils");
|
|
11
|
+
const { AuthenticatorUtils } = require("./authenticator/authenticator-utils");
|
|
12
|
+
const { JsonUtils } = require("./json/json-utils");
|
|
13
|
+
const { DateUtils } = require("./date/date-utils");
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TppLibrary {
|
|
17
|
+
|
|
18
|
+
constructor(caller, blackListedLogs, webComponents, customConfig, takeScreenShot, page) {
|
|
19
|
+
this.file = new FileActionUtils(caller, blackListedLogs);
|
|
20
|
+
this.csv = new CsvUtils(caller, blackListedLogs);
|
|
21
|
+
this.excel = new ExcelUtils(caller, blackListedLogs, customConfig);
|
|
22
|
+
this.hasWebComponents = !!webComponents;
|
|
23
|
+
if(!!webComponents) {
|
|
24
|
+
this.networkLog = new NetworkLogUtils(caller, blackListedLogs, webComponents.networkLogManager);
|
|
25
|
+
this.consoleLog = new ConsoleLogUtils(caller, blackListedLogs, webComponents.consoleLogManager);
|
|
26
|
+
this.downloads = new DownloadsUtils(caller, blackListedLogs, webComponents.downloadsManager);
|
|
27
|
+
}
|
|
28
|
+
this.math = new MathUtils(caller, blackListedLogs);
|
|
29
|
+
this.text = new TextUtils(caller, blackListedLogs);
|
|
30
|
+
this.date = new DateUtils(caller, blackListedLogs);
|
|
31
|
+
this.json = new JsonUtils(caller, blackListedLogs);
|
|
32
|
+
this.softAssert = new SoftAssertUtils(caller, blackListedLogs, takeScreenShot);
|
|
33
|
+
this.emailTesting = new EmailTestingUtils(caller, blackListedLogs, takeScreenShot, page);
|
|
34
|
+
this.authenticator = new AuthenticatorUtils(caller, blackListedLogs);
|
|
35
|
+
this.customConfig = customConfig;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setCaller(caller) {
|
|
39
|
+
this.file.setCaller(caller);
|
|
40
|
+
this.csv.setCaller(caller);
|
|
41
|
+
this.excel.setCaller(caller);
|
|
42
|
+
this.math.setCaller(caller);
|
|
43
|
+
this.text.setCaller(caller);
|
|
44
|
+
this.date.setCaller(caller);
|
|
45
|
+
this.json.setCaller(caller);
|
|
46
|
+
if(this.hasWebComponents) {
|
|
47
|
+
this.networkLog.setCaller(caller);
|
|
48
|
+
this.consoleLog.setCaller(caller);
|
|
49
|
+
this.downloads.setCaller(caller);
|
|
50
|
+
this.downloads.downloadsManager?.setCaller(caller);
|
|
51
|
+
}
|
|
52
|
+
this.softAssert.setCaller(caller);
|
|
53
|
+
this.emailTesting.setCaller(caller);
|
|
54
|
+
this.authenticator.setCaller(caller);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setBlacklistedLogs(blacklistedLogs) {
|
|
58
|
+
this.file.setBlacklistedLogs(blacklistedLogs);
|
|
59
|
+
this.csv.setBlacklistedLogs(blacklistedLogs);
|
|
60
|
+
this.excel.setBlacklistedLogs(blacklistedLogs);
|
|
61
|
+
this.math.setBlacklistedLogs(blacklistedLogs);
|
|
62
|
+
this.text.setBlacklistedLogs(blacklistedLogs);
|
|
63
|
+
this.date.setBlacklistedLogs(blacklistedLogs);
|
|
64
|
+
this.json.setBlacklistedLogs(blacklistedLogs);
|
|
65
|
+
if(this.hasWebComponents) {
|
|
66
|
+
this.networkLog.setBlacklistedLogs(blacklistedLogs);
|
|
67
|
+
this.consoleLog.setBlacklistedLogs(blacklistedLogs);
|
|
68
|
+
this.downloads.setBlacklistedLogs(blacklistedLogs);
|
|
69
|
+
}
|
|
70
|
+
this.softAssert.setBlacklistedLogs(blacklistedLogs);
|
|
71
|
+
this.emailTesting.setBlacklistedLogs(blacklistedLogs);
|
|
72
|
+
this.authenticator.setBlacklistedLogs(blacklistedLogs);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
setPage(page) {
|
|
76
|
+
this.emailTesting.setPage(page);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
exports.TppLibrary = TppLibrary;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
class SliderUtils {
|
|
2
|
+
|
|
3
|
+
constructor(page) {
|
|
4
|
+
this.page = page;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
validateSliderRange(targetValue, startValue, endValue) {
|
|
8
|
+
if(startValue === endValue) {
|
|
9
|
+
throw {message: `Start and end can't be same! (${startValue}, ${endValue})`, source: 'tpp'};
|
|
10
|
+
}
|
|
11
|
+
const isIncreasing = startValue < endValue;
|
|
12
|
+
if((isIncreasing && (targetValue < startValue || targetValue > endValue)) ||
|
|
13
|
+
(!isIncreasing && (targetValue > startValue || targetValue < endValue))) {
|
|
14
|
+
throw {message: `Value ${targetValue} is not in range of ${startValue} and ${endValue}`, source: 'tpp'};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async pickValueInSlider(tppElement, value, start, end, stepValue = null) {
|
|
19
|
+
this.validateSliderRange(value, start, end);
|
|
20
|
+
|
|
21
|
+
let ratio;
|
|
22
|
+
|
|
23
|
+
if (stepValue) {
|
|
24
|
+
const numberOfSteps = Math.floor(Math.abs(end - start) / stepValue);
|
|
25
|
+
const stepIndex = (value - start) / stepValue;
|
|
26
|
+
ratio = stepIndex / numberOfSteps;
|
|
27
|
+
} else {
|
|
28
|
+
ratio = Math.abs(value - start) / Math.abs(end - start);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await this.clickSliderAtRatio(tppElement, ratio, start > end);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async clickSliderAtRatio(tppElement, ratio, isDecreasing = false) {
|
|
35
|
+
if (ratio < 0 || ratio > 1) {
|
|
36
|
+
throw {message: `Ratio ${ratio} is not in range 0 to 1`, source: 'tpp'};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const slider = tppElement.locator();
|
|
40
|
+
const boundingBox = await slider.boundingBox();
|
|
41
|
+
if (!boundingBox) {
|
|
42
|
+
throw {message: 'Unable to retrieve bounding box for slider!', source: 'tpp'};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const sliderWidth = boundingBox.width;
|
|
46
|
+
|
|
47
|
+
let clickX = !isDecreasing
|
|
48
|
+
? boundingBox.x + sliderWidth * ratio
|
|
49
|
+
: boundingBox.x + sliderWidth * (1 - ratio);
|
|
50
|
+
|
|
51
|
+
// To avoid clicking outside the slider
|
|
52
|
+
if(ratio == 1) {
|
|
53
|
+
clickX = isDecreasing ? clickX + 1 : clickX - 1;
|
|
54
|
+
} else if (ratio === 0) {
|
|
55
|
+
clickX = isDecreasing ? clickX - 1 : clickX + 1;
|
|
56
|
+
}
|
|
57
|
+
const clickY = boundingBox.y + boundingBox.height / 2;
|
|
58
|
+
|
|
59
|
+
let page = this.page.page && this.page.page() ? this.page.page() : this.page;
|
|
60
|
+
await page.mouse.click(clickX, clickY);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
exports.SliderUtils = SliderUtils;
|
|
66
|
+
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const { spawn, exec } = require('child_process');
|
|
2
|
+
const net = require('net');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { FileUtils } = require('../../library/file/file-utils');
|
|
5
|
+
const { FileActionUtils } = require('../../library/file/file-action-utils');
|
|
6
|
+
const treeKill = require('tree-kill');
|
|
7
|
+
|
|
8
|
+
function isPortAvailable(port) {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
const server = net.createServer();
|
|
11
|
+
server.once('error', (err) => {
|
|
12
|
+
if (err.code === 'EADDRINUSE') {
|
|
13
|
+
resolve(false);
|
|
14
|
+
} else {
|
|
15
|
+
reject(err);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
server.once('listening', () => {
|
|
19
|
+
try {
|
|
20
|
+
server.close(() => resolve(true));
|
|
21
|
+
} catch (_e) {
|
|
22
|
+
resolve(false);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
server.listen(port, '0.0.0.0');
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isPortAvailableCommand(port) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
let command;
|
|
32
|
+
|
|
33
|
+
if (os.platform() === 'win32') {
|
|
34
|
+
command = `netstat -ano | findstr :${port}`;
|
|
35
|
+
} else {
|
|
36
|
+
command = `lsof -i :${port}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exec(command, (error, stdout, stderr) => {
|
|
40
|
+
if (error) {
|
|
41
|
+
if (stderr.includes('not recognized') || error.message.includes('not recognized')) {
|
|
42
|
+
reject(new Error(`netstat not found`));
|
|
43
|
+
} else {
|
|
44
|
+
resolve(true);
|
|
45
|
+
}
|
|
46
|
+
} else if (stdout) {
|
|
47
|
+
resolve(false);
|
|
48
|
+
} else {
|
|
49
|
+
reject(stderr);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function startAppiumServers(appiumStartScript, count = 1) {
|
|
56
|
+
let startPort = 4723
|
|
57
|
+
const pids = [];
|
|
58
|
+
const ports = [];
|
|
59
|
+
for (let i = 0; i < count; i++) {
|
|
60
|
+
const {pid, port} = await startAppiumServer(appiumStartScript, startPort);
|
|
61
|
+
pids.push(pid);
|
|
62
|
+
ports.push(port);
|
|
63
|
+
startPort = port + 1;
|
|
64
|
+
}
|
|
65
|
+
const data = {pids: pids, ports: ports};
|
|
66
|
+
new FileUtils().writeTextContent("mobile-status/appium.json", JSON.stringify(data, null, 4));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function startAppiumServer(appiumStartScript, startPort = 4723, maxRetries = 10) {
|
|
70
|
+
let currentPort = startPort;
|
|
71
|
+
let retries = 0;
|
|
72
|
+
|
|
73
|
+
while (retries < maxRetries) {
|
|
74
|
+
let portAvailable;
|
|
75
|
+
try{
|
|
76
|
+
portAvailable = await isPortAvailable(currentPort);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.log("Error checking port status", err);
|
|
79
|
+
try {
|
|
80
|
+
portAvailable = await isPortAvailableCommand(currentPort);
|
|
81
|
+
} catch (err2) {
|
|
82
|
+
console.log("Error checking port status via command", err2);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!!portAvailable) {
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
let resolved = false;
|
|
88
|
+
const appiumProcess = spawn(appiumStartScript, [currentPort.toString()], { shell: true });
|
|
89
|
+
|
|
90
|
+
appiumProcess.stdout.on('data', (data) => {
|
|
91
|
+
const output = data.toString();
|
|
92
|
+
console.log(`Appium: ${output}`);
|
|
93
|
+
if (output.includes("Appium REST http interface listener started on")) {
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
resolved = true;
|
|
96
|
+
resolve({ pid: appiumProcess.pid, port: currentPort });
|
|
97
|
+
}, 1000);
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
appiumProcess.stderr.on('data', (data) => {
|
|
103
|
+
console.error(`Appium error: ${data}`);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
appiumProcess.on('close', (code) => {
|
|
107
|
+
console.log(`Appium server stopped with code ${code}`);
|
|
108
|
+
if(!resolved) resolve({});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
});
|
|
112
|
+
} else {
|
|
113
|
+
console.log(`Port ${currentPort} is in use. Trying port ${currentPort + 1}...`);
|
|
114
|
+
currentPort++;
|
|
115
|
+
retries++;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const errorMessage = `Could not find an available port after ${maxRetries} retries.`;
|
|
120
|
+
console.log(`[[TPP]] Could not find an available port after ${maxRetries} retries. :: [ERROR]`);
|
|
121
|
+
throw {message: errorMessage};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function stopAppiumServers() {
|
|
125
|
+
const {pids} = getAppiumConfig();
|
|
126
|
+
for (let i = 0; i < (pids?.length || 0); i++) {
|
|
127
|
+
await stopAppiumServer(pids?.at(i));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function stopAppiumServer(pid) {
|
|
132
|
+
return new Promise((resolve, _reject) => {
|
|
133
|
+
treeKill(pid, 'SIGKILL', (err) => {
|
|
134
|
+
if (err) {
|
|
135
|
+
console.error(`Failed to kill Appium process ${pid}:`, err);
|
|
136
|
+
} else {
|
|
137
|
+
console.log(`Appium process and all child processes killed. ${pid}:`);
|
|
138
|
+
}
|
|
139
|
+
resolve(`Appium process ${pid} killed successfully.`);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function getAppiumConfig() {
|
|
145
|
+
const content = new FileActionUtils().readContentIgnoreMissing("mobile-status/appium.json", "{}");
|
|
146
|
+
return JSON.parse(content);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function resetAppiumConfig() {
|
|
150
|
+
new FileUtils().writeTextContent("mobile-status/appium.json", "{}");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
exports.startAppiumServers = startAppiumServers;
|
|
154
|
+
exports.stopAppiumServers = stopAppiumServers;
|
|
155
|
+
exports.getAppiumConfig = getAppiumConfig;
|
|
156
|
+
exports.resetAppiumConfig = resetAppiumConfig;
|
|
157
|
+
exports.startAppiumServer = startAppiumServer;
|
|
158
|
+
exports.stopAppiumServer = stopAppiumServer;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const { generateRandomString } = require("../../utils/common-utils");
|
|
2
|
+
|
|
3
|
+
function getBrowserStackCapabilities(config, app, testInfo) {
|
|
4
|
+
const cloudConfig = config.deviceCloud;
|
|
5
|
+
let platformVersion = config?.os === 'ios'
|
|
6
|
+
? config.cloudDevice.iosVersion
|
|
7
|
+
: config.cloudDevice.androidVersion;
|
|
8
|
+
return {
|
|
9
|
+
'bstack:options': {
|
|
10
|
+
'userName': cloudConfig.config.userName,
|
|
11
|
+
'accessKey': cloudConfig.config.accessKey,
|
|
12
|
+
"sessionName" : testInfo?.label || "devassure-desktop-app-editor",
|
|
13
|
+
"projectName" : testInfo?.projectInfo?.projectId || "devassure-default",
|
|
14
|
+
"buildName" : `${testInfo?.projectInfo?.jobName || "devassure-desktop-app"} ${testInfo?.projectInfo?.jobId || generateRandomString()}`,
|
|
15
|
+
},
|
|
16
|
+
'appium:app': app.cloudData?.app_url,
|
|
17
|
+
'appium:deviceName': config.cloudDevice.name,
|
|
18
|
+
'appium:platformVersion': platformVersion,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getBrowserStackConnectionProps(_config) {
|
|
23
|
+
return {
|
|
24
|
+
hostname: 'hub-cloud.browserstack.com',
|
|
25
|
+
port: 443,
|
|
26
|
+
baseUrl: 'https://hub-cloud.browserstack.com',
|
|
27
|
+
path: '/wd/hub',
|
|
28
|
+
protocol: 'https',
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function setBrowserStackTestStatus(driver, pass, message) {
|
|
33
|
+
if(pass == null) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
let status = pass ? 'passed' : 'failed';
|
|
37
|
+
try {
|
|
38
|
+
await driver.execute(`browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"${status}","reason": "${message || ""}"}}`);
|
|
39
|
+
} catch(ex) {}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
exports.getBrowserStackCapabilities = getBrowserStackCapabilities;
|
|
43
|
+
exports.getBrowserStackConnectionProps = getBrowserStackConnectionProps;
|
|
44
|
+
exports.setBrowserStackTestStatus = setBrowserStackTestStatus;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class BrowserStackCommands {
|
|
2
|
+
|
|
3
|
+
constructor(driver) {
|
|
4
|
+
this.driver = driver;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
async openApp() {
|
|
8
|
+
await this.driver.launchApp();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async closeApp() {
|
|
12
|
+
await this.driver.closeApp();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async minimiseApp(timeInSeconds) {
|
|
16
|
+
await this.driver.background(timeInSeconds);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
exports.BrowserStackCommands = BrowserStackCommands;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const { BrowserStackCommands } = require("./browserstack-commands");
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function getCloudCommand(config, driver) {
|
|
5
|
+
|
|
6
|
+
if(!config.deviceCloud) {
|
|
7
|
+
return undefined
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
switch(config.deviceCloud?.type) {
|
|
11
|
+
case 'BrowserStack': return new BrowserStackCommands(driver);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
exports.getCloudCommand=getCloudCommand;
|