froth-webdriverio-framework 7.0.0 → 7.0.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.
|
@@ -88,6 +88,38 @@ async function amend2Browserstack(annotationMessage, level) {
|
|
|
88
88
|
throw error;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
+
async function amend2Browserstack(annotationMessage, level) {
|
|
92
|
+
try {
|
|
93
|
+
const MAX_CHUNK_SIZE = 800 * 1024; // 800 KB
|
|
94
|
+
|
|
95
|
+
// Convert message to string in case it's not
|
|
96
|
+
let messageStr = typeof annotationMessage === "string" ? annotationMessage : JSON.stringify(annotationMessage);
|
|
97
|
+
|
|
98
|
+
// Split into chunks if needed
|
|
99
|
+
const chunks = [];
|
|
100
|
+
for (let i = 0; i < messageStr.length; i += MAX_CHUNK_SIZE) {
|
|
101
|
+
chunks.push(messageStr.slice(i, i + MAX_CHUNK_SIZE));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Send each chunk to BrowserStack sequentially
|
|
105
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
106
|
+
const chunkMessage = chunks.length > 1
|
|
107
|
+
? `PART ${i + 1}/${chunks.length}\n${chunks[i]}`
|
|
108
|
+
: chunks[i];
|
|
109
|
+
|
|
110
|
+
console.log(`Annotation message: ${chunkMessage}`);
|
|
111
|
+
|
|
112
|
+
if (process.env.PLATFORM === 'browserstack' || process.env.PLATFORM === 'browserstacklocal') {
|
|
113
|
+
await driver.execute(`browserstack_executor: {"action": "annotate", "arguments": {"data":"${chunkMessage}","level": "${level}"}}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error('Error annotating BrowserStack session:', error);
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
91
123
|
|
|
92
124
|
module.exports = {
|
|
93
125
|
getBSSessionDetails,
|
|
@@ -1,179 +1,245 @@
|
|
|
1
|
-
const axios = require(
|
|
1
|
+
const axios = require("axios");
|
|
2
|
+
const FormData = require("form-data");
|
|
2
3
|
|
|
3
|
-
const amendToBrowserstack = require("../froth_api_calls/browsersatckSessionInfo")
|
|
4
|
+
const { mend2Browserstack: amendToBrowserstack } = require("../froth_api_calls/browsersatckSessionInfo");
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
/**
|
|
7
|
+
* ===============================
|
|
8
|
+
* UTILS
|
|
9
|
+
* ===============================
|
|
10
|
+
*/
|
|
11
|
+
function safeStringify(data) {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.stringify(data, null, 2);
|
|
14
|
+
} catch {
|
|
15
|
+
return String(data);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
12
18
|
|
|
19
|
+
/**
|
|
20
|
+
* ===============================
|
|
21
|
+
* MAIN API CALL
|
|
22
|
+
* ===============================
|
|
23
|
+
*/
|
|
24
|
+
async function callapi(
|
|
25
|
+
methodType,
|
|
26
|
+
apiUrl,
|
|
27
|
+
queryParams,
|
|
28
|
+
payloadDetails,
|
|
29
|
+
bodyType,
|
|
30
|
+
authentication,
|
|
31
|
+
headersDetails
|
|
32
|
+
) {
|
|
33
|
+
let response;
|
|
34
|
+
const startTime = Date.now();
|
|
13
35
|
|
|
14
36
|
try {
|
|
37
|
+
const method = methodType.toUpperCase();
|
|
38
|
+
const headers = await formHeaders(authentication, headersDetails, bodyType);
|
|
15
39
|
|
|
16
|
-
let
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
console.log("Body Type:", body_type);
|
|
20
|
-
console.log("method:", method);
|
|
21
|
-
console.log("headers:", headers);
|
|
22
|
-
|
|
23
|
-
if (body_type === "raw" && payloaddetails && Object.keys(payloaddetails).length > 0) {
|
|
24
|
-
console.log("Payload (JSON):", JSON.stringify(payloaddetails));
|
|
25
|
-
body = JSON.stringify(payloaddetails);
|
|
26
|
-
console.log("Payload (raw):", body);
|
|
27
|
-
} else if (body_type === "form-data" && payloaddetails) {
|
|
28
|
-
body = await jsonToFormData(payloaddetails);
|
|
29
|
-
console.log("Payload (FormData):", body);
|
|
30
|
-
for (let [key, value] of body.entries()) {
|
|
31
|
-
console.log(`FormData Key: ${key}, Value: ${value}`);
|
|
32
|
-
}
|
|
33
|
-
} else {
|
|
34
|
-
console.log("No body sent (body_type not provided or empty payload).");
|
|
40
|
+
let body;
|
|
41
|
+
if (bodyType === "raw" && payloadDetails) {
|
|
42
|
+
body = payloadDetails;
|
|
35
43
|
}
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
method: method,
|
|
41
|
-
url: api_url,
|
|
42
|
-
headers: headers
|
|
43
|
-
};
|
|
44
|
-
const isBodyEmpty =
|
|
45
|
-
body === null ||
|
|
46
|
-
body === undefined ||
|
|
47
|
-
body === ''
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (!isBodyEmpty) {
|
|
51
|
-
config.data = body;
|
|
45
|
+
if (bodyType === "form-data" && payloadDetails) {
|
|
46
|
+
body = jsonToFormData(payloadDetails);
|
|
47
|
+
Object.assign(headers, body.getHeaders());
|
|
52
48
|
}
|
|
53
|
-
console.log("body:", body);
|
|
54
|
-
console.log("Config for API call:", config);
|
|
55
|
-
response = await axios(config);
|
|
56
|
-
|
|
57
|
-
// response = await axios({
|
|
58
|
-
// method: method,
|
|
59
|
-
// url: api_url,
|
|
60
|
-
// headers: headers,
|
|
61
|
-
// data: body === null ? undefined : body // Axios allows body with GET in some cases
|
|
62
|
-
// });
|
|
63
49
|
|
|
64
|
-
|
|
65
|
-
|
|
50
|
+
/**
|
|
51
|
+
* -------- ANNOTATE REQUEST --------
|
|
52
|
+
* Full request annotated with chunking handled in amend2Browserstack
|
|
53
|
+
*/
|
|
54
|
+
await amendToBrowserstack(
|
|
55
|
+
safeStringify({
|
|
56
|
+
method,
|
|
57
|
+
url: apiUrl,
|
|
58
|
+
bodyType,
|
|
59
|
+
body: payloadDetails
|
|
60
|
+
}),
|
|
61
|
+
"info"
|
|
62
|
+
);
|
|
63
|
+
/**
|
|
64
|
+
* -------- API CALL --------
|
|
65
|
+
*/
|
|
66
|
+
response = await axios({
|
|
67
|
+
method,
|
|
68
|
+
url: apiUrl,
|
|
69
|
+
headers,
|
|
70
|
+
...(body && { data: body })
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const duration = Date.now() - startTime;
|
|
74
|
+
|
|
75
|
+
// Always store full response
|
|
76
|
+
BUFFER.setItem("LAST_API_RESPONSE", response.data);
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* -------- ANNOTATE FULL RESPONSE --------
|
|
80
|
+
* Now chunking is handled in amend2Browserstack
|
|
81
|
+
*/
|
|
82
|
+
await amendToBrowserstack(
|
|
83
|
+
safeStringify({
|
|
84
|
+
status: response.status,
|
|
85
|
+
durationMs: duration,
|
|
86
|
+
data: response.data
|
|
87
|
+
}),
|
|
88
|
+
"info"
|
|
89
|
+
);
|
|
66
90
|
|
|
67
91
|
} catch (error) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
92
|
+
await amendToBrowserstack(
|
|
93
|
+
safeStringify({
|
|
94
|
+
message: error.message,
|
|
95
|
+
status: error.response?.status,
|
|
96
|
+
data: error.response?.data
|
|
97
|
+
}),
|
|
98
|
+
"error"
|
|
99
|
+
);
|
|
100
|
+
|
|
71
101
|
throw error;
|
|
72
102
|
}
|
|
103
|
+
|
|
73
104
|
return response;
|
|
74
105
|
}
|
|
75
|
-
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* ===============================
|
|
109
|
+
* JSON → FORM DATA
|
|
110
|
+
* ===============================
|
|
111
|
+
*/
|
|
112
|
+
function jsonToFormData(json) {
|
|
76
113
|
const formData = new FormData();
|
|
77
114
|
|
|
78
|
-
function
|
|
79
|
-
if (data && typeof data ===
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
}
|
|
115
|
+
function append(data, parentKey) {
|
|
116
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
117
|
+
Object.keys(data).forEach(key =>
|
|
118
|
+
append(data[key], parentKey ? `${parentKey}[${key}]` : key)
|
|
119
|
+
);
|
|
85
120
|
} else {
|
|
86
121
|
formData.append(parentKey, data);
|
|
87
122
|
}
|
|
88
123
|
}
|
|
89
124
|
|
|
90
|
-
|
|
91
|
-
console.log("FormData in json to formdata:", formData);
|
|
125
|
+
append(json);
|
|
92
126
|
return formData;
|
|
93
127
|
}
|
|
94
128
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
headers.Authorization = `Basic ` + btoa(authentication.username + `:` + authentication.password);
|
|
111
|
-
}
|
|
129
|
+
/**
|
|
130
|
+
* ===============================
|
|
131
|
+
* HEADERS
|
|
132
|
+
* ===============================
|
|
133
|
+
*/
|
|
134
|
+
async function formHeaders(authentication, headers = {}, bodyType) {
|
|
135
|
+
if (bodyType === "raw") {
|
|
136
|
+
headers["Content-Type"] = "application/json";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (authentication?.auth_type) {
|
|
140
|
+
const type = authentication.auth_type.toLowerCase();
|
|
141
|
+
|
|
142
|
+
if (type === "bearer") {
|
|
143
|
+
headers.Authorization = `Bearer ${authentication.token}`;
|
|
112
144
|
}
|
|
113
|
-
console.log('Headers:', headers);
|
|
114
|
-
} catch (e) {
|
|
115
145
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
146
|
+
if (type === "basic") {
|
|
147
|
+
headers.Authorization = `Basic ${Buffer.from(
|
|
148
|
+
`${authentication.username}:${authentication.password}`
|
|
149
|
+
).toString("base64")}`;
|
|
150
|
+
}
|
|
119
151
|
}
|
|
120
|
-
return headers;
|
|
121
152
|
|
|
153
|
+
return headers;
|
|
122
154
|
}
|
|
123
|
-
async function validate(attribute_name, attribute, actionname, buffer, buffername_value, datatype) {
|
|
124
|
-
|
|
125
|
-
if (actionname.toLowerCase() == "verify") {
|
|
126
|
-
await validateAttributeData(attribute_name, attribute, buffer, buffername_value, datatype);
|
|
127
|
-
} else if (actionname.toLowerCase() == "setbuffer") {
|
|
128
|
-
BUFFER.setItem(buffername_value, attribute);
|
|
129
|
-
let annotationMessage = `The vlaue has been stored in the buffer : ${buffername_value}.`;
|
|
130
|
-
await amendToBrowserstack(annotationMessage, "info");
|
|
131
|
-
|
|
132
|
-
} else if (actionname.toLowerCase() == "getbuffer") {
|
|
133
|
-
let annotationMessage = `The vlaue has been retrived from the buffer : ${buffername_value}.`;
|
|
134
|
-
await amendToBrowserstack(annotationMessage, "info");
|
|
135
|
-
return await BUFFER.getItem(buffername_value);
|
|
136
155
|
|
|
156
|
+
/**
|
|
157
|
+
* ===============================
|
|
158
|
+
* VALIDATION
|
|
159
|
+
* ===============================
|
|
160
|
+
*/
|
|
161
|
+
async function validate(
|
|
162
|
+
attributeName,
|
|
163
|
+
actualValue,
|
|
164
|
+
actionName,
|
|
165
|
+
useBuffer,
|
|
166
|
+
bufferKey,
|
|
167
|
+
datatype
|
|
168
|
+
) {
|
|
169
|
+
switch (actionName.toLowerCase()) {
|
|
170
|
+
case "verify":
|
|
171
|
+
return validateAttributeData(
|
|
172
|
+
attributeName,
|
|
173
|
+
actualValue,
|
|
174
|
+
useBuffer,
|
|
175
|
+
bufferKey,
|
|
176
|
+
datatype
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
case "setbuffer":
|
|
180
|
+
BUFFER.setItem(bufferKey, actualValue);
|
|
181
|
+
await amendToBrowserstack(
|
|
182
|
+
`Stored value in BUFFER: ${bufferKey}`,
|
|
183
|
+
"info"
|
|
184
|
+
);
|
|
185
|
+
break;
|
|
186
|
+
|
|
187
|
+
case "getbuffer":
|
|
188
|
+
await amendToBrowserstack(
|
|
189
|
+
`Retrieved value from BUFFER: ${bufferKey}`,
|
|
190
|
+
"info"
|
|
191
|
+
);
|
|
192
|
+
return BUFFER.getItem(bufferKey);
|
|
137
193
|
}
|
|
138
|
-
// } catch (e) {
|
|
139
|
-
// console.error('Error in validate method:', e);
|
|
140
|
-
// throw e;
|
|
141
|
-
// }
|
|
142
194
|
}
|
|
143
195
|
|
|
144
|
-
async function validateAttributeData(
|
|
145
|
-
|
|
146
|
-
|
|
196
|
+
async function validateAttributeData(
|
|
197
|
+
attributeName,
|
|
198
|
+
actualValue,
|
|
199
|
+
useBuffer,
|
|
200
|
+
bufferKey,
|
|
201
|
+
datatype
|
|
202
|
+
) {
|
|
203
|
+
let expectedValue = useBuffer
|
|
204
|
+
? BUFFER.getItem(bufferKey)
|
|
205
|
+
: bufferKey;
|
|
206
|
+
|
|
207
|
+
switch (datatype.toLowerCase()) {
|
|
208
|
+
case "integer":
|
|
209
|
+
expectedValue = Number(expectedValue);
|
|
210
|
+
break;
|
|
211
|
+
case "boolean":
|
|
212
|
+
expectedValue = Boolean(expectedValue);
|
|
213
|
+
break;
|
|
214
|
+
case "decimal":
|
|
215
|
+
expectedValue = parseFloat(expectedValue);
|
|
216
|
+
break;
|
|
217
|
+
case "string":
|
|
218
|
+
expectedValue = String(expectedValue);
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
147
221
|
|
|
148
222
|
try {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
} else if (datatype.toLowerCase() == "boolean") {
|
|
155
|
-
valueToVerify = Boolean(valueToVerify);
|
|
156
|
-
} else if (datatype.toLowerCase() == "decimal") {
|
|
157
|
-
valueToVerify = parseFloat(valueToVerify);
|
|
158
|
-
} else if (datatype.toLowerCase() == "string") {
|
|
159
|
-
valueToVerify = valueToVerify.toString();
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
expect(attribute).toBe(valueToVerify, `${attribute_name} --> Expected value: ${valueToVerify}, but got: ${attribute}`);
|
|
163
|
-
assertionStatus = true; // If assertion passes, set status to true
|
|
164
|
-
|
|
165
|
-
console.log(`Attribute name : ${attribute_name} verification passed. Actual text: ${attribute}, Expected text: ${valueToVerify}.`);
|
|
166
|
-
let annotationMessage = `Attribute name : ${attribute_name} - verification passed. Actual text: ${attribute}, Expected text: ${valueToVerify}.`;
|
|
167
|
-
await amendToBrowserstack(annotationMessage, "info");
|
|
168
|
-
|
|
223
|
+
expect(actualValue).toBe(expectedValue);
|
|
224
|
+
await amendToBrowserstack(
|
|
225
|
+
`${attributeName} verification passed`,
|
|
226
|
+
"info"
|
|
227
|
+
);
|
|
169
228
|
} catch (e) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
229
|
+
await amendToBrowserstack(
|
|
230
|
+
`${attributeName} verification failed`,
|
|
231
|
+
"error"
|
|
232
|
+
);
|
|
175
233
|
throw e;
|
|
176
234
|
}
|
|
177
235
|
}
|
|
178
236
|
|
|
179
|
-
|
|
237
|
+
/**
|
|
238
|
+
* ===============================
|
|
239
|
+
* EXPORTS
|
|
240
|
+
* ===============================
|
|
241
|
+
*/
|
|
242
|
+
module.exports = {
|
|
243
|
+
callapi,
|
|
244
|
+
validate
|
|
245
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "froth-webdriverio-framework",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"readme": "WendriverIO Integration with [BrowserStack](https://www.browserstack.com)",
|
|
5
5
|
"description": "Selenium examples for WebdriverIO and BrowserStack App Automate",
|
|
6
6
|
"scripts": {
|