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('axios');
1
+ const axios = require("axios");
2
+ const FormData = require("form-data");
2
3
 
3
- const amendToBrowserstack = require("../froth_api_calls/browsersatckSessionInfo").amend2Browserstack;
4
+ const { mend2Browserstack: amendToBrowserstack } = require("../froth_api_calls/browsersatckSessionInfo");
4
5
 
5
- async function callapi(methodtype, api_url, queryParams, payloaddetails, body_type, authentication, headersdetails, attachments) {
6
- let response;
7
- // if (queryParams && Object.keys(queryParams).length > 0) {
8
- // const queryString = new URLSearchParams(queryParams).toString();
9
- // api_url += `?${queryString}`;
10
- // }
11
- console.log("Final API URL:", api_url);
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 method = methodtype.toUpperCase();
17
- let headers = await formheaders(authentication, headersdetails, body_type);
18
- let body = null;
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
- console.warn("Warning: GET request with a body is non-standard and may not work with all APIs.");
39
- const config = {
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
- console.log("Response Data:", response.data);
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
- console.error('Error during API call in call api menthod:', error);
69
- let annotationMessage = 'Error during API call:' + error.response ? error.response.data : error.message;
70
- await amendToBrowserstack(annotationMessage, "error");
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
- async function jsonToFormData(json) {
106
+
107
+ /**
108
+ * ===============================
109
+ * JSON → FORM DATA
110
+ * ===============================
111
+ */
112
+ function jsonToFormData(json) {
76
113
  const formData = new FormData();
77
114
 
78
- function appendFormData(data, parentKey = '') {
79
- if (data && typeof data === 'object' && !Array.isArray(data)) {
80
- for (const key in data) {
81
- if (data.hasOwnProperty(key)) {
82
- appendFormData(data[key], parentKey ? `${parentKey}[${key}]` : key);
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
- appendFormData(json);
91
- console.log("FormData in json to formdata:", formData);
125
+ append(json);
92
126
  return formData;
93
127
  }
94
128
 
95
- // Function to form headers
96
- async function formheaders(authentication, headers, body_type) {
97
- try {
98
- if (!headers || typeof headers !== "object") {
99
- headers = {};
100
- }
101
- if (body_type === "raw") {
102
- headers['Content-Type'] = 'application/json';
103
- }
104
- // Add authentication headers based on the type
105
- if (authentication && authentication.auth_type) {
106
- if (authentication.auth_type?.toLowerCase() === 'bearer') {
107
- console.log('Bearer token:', authentication.token);
108
- headers.Authorization = `Bearer ` + authentication.token;
109
- } else if (authentication.auth_type?.toLowerCase() === 'basic') {
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
- console.error('Error in formheaders:', e);
117
- let annotationMessage = `Error during API call: ${error.response?.data || error.message}`;
118
- await amendToBrowserstack(annotationMessage, "error");
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(attribute_name, attribute, buffer, buffername_value, datatype) {
145
- let assertionStatus = false; // Initialize status
146
- let valueToVerify;
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
- //if buffer is true, get the value from buffer
150
- buffer ? valueToVerify = BUFFER.getItem(buffername_value) : valueToVerify = buffername_value;
151
-
152
- if (datatype.toLowerCase() == "integer") {
153
- valueToVerify = Number(valueToVerify);
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
- console.error('Error in validateAttributeData:', e);
171
- console.log(`Attribute name : ${attribute_name} verification failed. Expected text: ${valueToVerify}. error: ${e.toString()}`);
172
- let annotationMessage = `Attribute name : ${attribute_name} - verification failed. Expected text: ${valueToVerify}. error: ${e.toString()}`;
173
- await amendToBrowserstack(annotationMessage, "error");
174
-
229
+ await amendToBrowserstack(
230
+ `${attributeName} verification failed`,
231
+ "error"
232
+ );
175
233
  throw e;
176
234
  }
177
235
  }
178
236
 
179
- module.exports = { callapi, validate };
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.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": {