froth-webdriverio-framework 7.0.10 → 7.0.12
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/froth_api_calls/aesEncryptionDecryption.js +125 -0
- package/froth_api_calls/browsersatckSessionInfo.js +1 -4
- package/froth_api_calls/getexecutionDetails.js +22 -0
- package/froth_api_calls/loginapi.js +1 -1
- package/froth_common_actions/apicall.js +33 -17
- package/froth_configs/browserstack/mobile.config.js +1 -1
- package/package.json +1 -1
- package/froth_api_calls/aesEncryption.js +0 -33
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
|
|
3
|
+
//const SECRET_KEY = "froth-testops@rd";
|
|
4
|
+
|
|
5
|
+
async function encryptData(data, secretKey) {
|
|
6
|
+
try {
|
|
7
|
+
if (!data) return null
|
|
8
|
+
|
|
9
|
+
// Generate 12-byte nonce (GCM standard)
|
|
10
|
+
const nonce = crypto.getRandomValues(new Uint8Array(12))
|
|
11
|
+
|
|
12
|
+
// Import key (must be exactly 32 bytes)
|
|
13
|
+
const key = await crypto.subtle.importKey(
|
|
14
|
+
"raw",
|
|
15
|
+
new TextEncoder().encode(secretKey),
|
|
16
|
+
{ name: "AES-GCM" },
|
|
17
|
+
false,
|
|
18
|
+
["encrypt"]
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
// Encrypt
|
|
22
|
+
const encryptedBuffer = await crypto.subtle.encrypt(
|
|
23
|
+
{
|
|
24
|
+
name: "AES-GCM",
|
|
25
|
+
iv: nonce,
|
|
26
|
+
tagLength: 128, // 16 bytes
|
|
27
|
+
},
|
|
28
|
+
key,
|
|
29
|
+
new TextEncoder().encode(data)
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
// WebCrypto output = ciphertext + tag
|
|
33
|
+
const encryptedBytes = new Uint8Array(encryptedBuffer)
|
|
34
|
+
|
|
35
|
+
const ciphertext = encryptedBytes.slice(0, encryptedBytes.length - 16)
|
|
36
|
+
const tag = encryptedBytes.slice(encryptedBytes.length - 16)
|
|
37
|
+
|
|
38
|
+
// nonce + tag + ciphertext (same as backend)
|
|
39
|
+
const payload = new Uint8Array(
|
|
40
|
+
nonce.length + tag.length + ciphertext.length
|
|
41
|
+
)
|
|
42
|
+
payload.set(nonce)
|
|
43
|
+
payload.set(tag, nonce.length)
|
|
44
|
+
payload.set(ciphertext, nonce.length + tag.length)
|
|
45
|
+
|
|
46
|
+
// Base64 encode
|
|
47
|
+
return btoa(String.fromCharCode(...payload))
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error("Encryption failed:", err)
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async function decrpytData(encryptedData) {
|
|
58
|
+
try {
|
|
59
|
+
if (!encryptedData) return null
|
|
60
|
+
|
|
61
|
+
// Base64 → Uint8Array
|
|
62
|
+
const encryptedBytes = Uint8Array.from(atob(encryptedData), c =>
|
|
63
|
+
c.charCodeAt(0)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
// Extract nonce, tag, ciphertext
|
|
67
|
+
const nonce = encryptedBytes.slice(0, 12) // 12 bytes
|
|
68
|
+
const tag = encryptedBytes.slice(12, 28) // 16 bytes
|
|
69
|
+
const ciphertext = encryptedBytes.slice(28)
|
|
70
|
+
|
|
71
|
+
// WebCrypto expects: ciphertext + tag
|
|
72
|
+
const encryptedPayload = new Uint8Array(ciphertext.length + tag.length)
|
|
73
|
+
encryptedPayload.set(ciphertext)
|
|
74
|
+
encryptedPayload.set(tag, ciphertext.length)
|
|
75
|
+
|
|
76
|
+
// Import key
|
|
77
|
+
const key = await crypto.subtle.importKey(
|
|
78
|
+
"raw",
|
|
79
|
+
new TextEncoder().encode(config.encryptKeyValue),
|
|
80
|
+
{ name: "AES-GCM" },
|
|
81
|
+
false,
|
|
82
|
+
["decrypt"]
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
// Decrypt
|
|
86
|
+
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
87
|
+
{
|
|
88
|
+
name: "AES-GCM",
|
|
89
|
+
iv: nonce,
|
|
90
|
+
tagLength: 128, // bits (16 bytes)
|
|
91
|
+
},
|
|
92
|
+
key,
|
|
93
|
+
encryptedPayload
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
return new TextDecoder().decode(decryptedBuffer)
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error("Error decrypting:", error)
|
|
99
|
+
return null
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
encryptData,
|
|
106
|
+
decrpytData
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
await encryptData(
|
|
110
|
+
data,
|
|
111
|
+
secret_key
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const decrypted_data = await decrpytData(response)
|
|
115
|
+
const data = JSON.parse(decrypted_data)
|
|
116
|
+
console.log("decrpted data" + data)
|
|
117
|
+
return data
|
|
118
|
+
|
|
119
|
+
// Encrypt a password
|
|
120
|
+
//const encryptedPassword = encryptData("admin@1234", SECRET_KEY);
|
|
121
|
+
//console.log("Encrypted Password:", encryptedPassword);
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
@@ -126,10 +126,7 @@ async function amend2Browserstack(annotationMessage, level) {
|
|
|
126
126
|
: chunks[i];
|
|
127
127
|
|
|
128
128
|
// 🔍 LOCAL LOG
|
|
129
|
-
console.log(
|
|
130
|
-
console.log(`BrowserStack Chunk ${i + 1}/${chunks.length}`);
|
|
131
|
-
console.log(chunkMessage);
|
|
132
|
-
console.log("=======================================");
|
|
129
|
+
console.log(`=======> BrowserStack Chunk ${i + 1}/${chunks.length}`, JSON.stringify(chunkMessage, null, 2));
|
|
133
130
|
|
|
134
131
|
// 🚀 SEND TO BROWSERSTACK (WDIO-safe)
|
|
135
132
|
if (process.env.PLATFORM === "browserstack") {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const aes=require('./aesEncryptionDecryption')
|
|
1
2
|
/* =========================================================
|
|
2
3
|
Execution / Script API Helpers (Normalized)
|
|
3
4
|
========================================================= */
|
|
@@ -261,3 +262,24 @@ module.exports = {
|
|
|
261
262
|
updateScriptExecutionStatus,
|
|
262
263
|
update_CICDRUNID_ReportUrl
|
|
263
264
|
};
|
|
265
|
+
|
|
266
|
+
async function main() {
|
|
267
|
+
try {
|
|
268
|
+
const frothUrl = "devapi.frothtestops.com";
|
|
269
|
+
// const username = "subhra.subudhi@roboticodigital.com";
|
|
270
|
+
// const password = "V2VsY29tZUAxMjM=";
|
|
271
|
+
|
|
272
|
+
// const token = await getLoginToken(frothUrl, username, password);
|
|
273
|
+
// if (!token) {
|
|
274
|
+
// throw new Error('Login failed, no token obtained');
|
|
275
|
+
// }
|
|
276
|
+
const token='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzcyNTgyMzk5LCJpYXQiOjE3NjQ3NjU3OTQsImp0aSI6IjRhOWUyNjlkNWZhMzRlYzQ4NjZhZjk5MTc3Y2MwNzZiIiwidXNlcl9pZCI6NH0.1a_KwJvW3LOoBgzWMZBCoyGnA3eNm_XYeiah6Ql_EoA'
|
|
277
|
+
const id = 147;
|
|
278
|
+
const data = await getExecuitonDetails(frothUrl, token, id);
|
|
279
|
+
console.log("Retrieved JSON Data:", data);
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.error('Error in main function:', error);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
main();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//const fetch = require('node-fetch'); // Import the fetch function
|
|
2
2
|
//const FormData = require('form-data'); // Import the FormData constructor
|
|
3
3
|
|
|
4
|
-
const enc = require('./
|
|
4
|
+
const enc = require('./aesEncryptionDecryption.js')
|
|
5
5
|
async function getLoginToken(frothUrl, email, password) {
|
|
6
6
|
try {
|
|
7
7
|
const url = `${frothUrl}/api/login/`;
|
|
@@ -8,7 +8,7 @@ const amendToBrowserstack = require("../froth_api_calls/browsersatckSessionInfo"
|
|
|
8
8
|
* UTILS
|
|
9
9
|
* ===============================
|
|
10
10
|
*/
|
|
11
|
-
function safeStringify(data) {
|
|
11
|
+
async function safeStringify(data) {
|
|
12
12
|
try {
|
|
13
13
|
return JSON.stringify(data, null, 2);
|
|
14
14
|
} catch {
|
|
@@ -32,6 +32,7 @@ async function callapi(
|
|
|
32
32
|
) {
|
|
33
33
|
let response;
|
|
34
34
|
const startTime = Date.now();
|
|
35
|
+
console.log("Final API URL:", api_url);
|
|
35
36
|
|
|
36
37
|
try {
|
|
37
38
|
const method = methodType.toUpperCase();
|
|
@@ -39,11 +40,18 @@ async function callapi(
|
|
|
39
40
|
|
|
40
41
|
let body;
|
|
41
42
|
if (bodyType === "raw" && payloadDetails) {
|
|
43
|
+
console.log("Payload (JSON):", JSON.stringify(payloaddetails));
|
|
42
44
|
body = payloadDetails;
|
|
45
|
+
console.log("Payload (raw):", body);
|
|
46
|
+
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
if (bodyType === "form-data" && payloadDetails) {
|
|
46
|
-
body = jsonToFormData(payloadDetails);
|
|
50
|
+
body = await jsonToFormData(payloadDetails);
|
|
51
|
+
console.log("Payload (FormData):", body);
|
|
52
|
+
for (let [key, value] of body.entries()) {
|
|
53
|
+
console.log(`FormData Key: ${key}, Value: ${value}`);
|
|
54
|
+
}
|
|
47
55
|
Object.assign(headers, body.getHeaders());
|
|
48
56
|
}
|
|
49
57
|
|
|
@@ -52,7 +60,7 @@ async function callapi(
|
|
|
52
60
|
* Full request annotated with chunking handled in amend2Browserstack
|
|
53
61
|
*/
|
|
54
62
|
await amendToBrowserstack.amend2Browserstack(
|
|
55
|
-
safeStringify({
|
|
63
|
+
await safeStringify({
|
|
56
64
|
method,
|
|
57
65
|
url: apiUrl,
|
|
58
66
|
bodyType,
|
|
@@ -73,14 +81,15 @@ async function callapi(
|
|
|
73
81
|
const duration = Date.now() - startTime;
|
|
74
82
|
|
|
75
83
|
// Always store full response
|
|
76
|
-
|
|
84
|
+
// BUFFER.setItem("LAST_API_RESPONSE", response.data);
|
|
85
|
+
console.log("Response Data:", response.data);
|
|
77
86
|
|
|
78
87
|
/**
|
|
79
88
|
* -------- ANNOTATE FULL RESPONSE --------
|
|
80
89
|
* Now chunking is handled in amend2Browserstack
|
|
81
90
|
*/
|
|
82
91
|
await amendToBrowserstack.amend2Browserstack(
|
|
83
|
-
safeStringify({
|
|
92
|
+
await safeStringify({
|
|
84
93
|
status: response.status,
|
|
85
94
|
durationMs: duration,
|
|
86
95
|
data: response.data
|
|
@@ -89,8 +98,9 @@ async function callapi(
|
|
|
89
98
|
);
|
|
90
99
|
|
|
91
100
|
} catch (error) {
|
|
101
|
+
console.error('Error during API call in call api menthod:', error);
|
|
92
102
|
await amendToBrowserstack.amend2Browserstack(
|
|
93
|
-
safeStringify({
|
|
103
|
+
await safeStringify({
|
|
94
104
|
message: error.message,
|
|
95
105
|
status: error.response?.status,
|
|
96
106
|
data: error.response?.data
|
|
@@ -109,20 +119,21 @@ async function callapi(
|
|
|
109
119
|
* JSON → FORM DATA
|
|
110
120
|
* ===============================
|
|
111
121
|
*/
|
|
112
|
-
function jsonToFormData(json) {
|
|
122
|
+
async function jsonToFormData(json) {
|
|
113
123
|
const formData = new FormData();
|
|
114
124
|
|
|
115
|
-
function append(data, parentKey) {
|
|
125
|
+
async function append(data, parentKey) {
|
|
116
126
|
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
117
|
-
Object.keys(data)
|
|
118
|
-
append(data[key], parentKey ? `${parentKey}[${key}]` : key)
|
|
119
|
-
|
|
127
|
+
for (const key of Object.keys(data)) {
|
|
128
|
+
await append(data[key], parentKey ? `${parentKey}[${key}]` : key);
|
|
129
|
+
}
|
|
120
130
|
} else {
|
|
121
131
|
formData.append(parentKey, data);
|
|
122
132
|
}
|
|
123
133
|
}
|
|
124
134
|
|
|
125
|
-
append(json);
|
|
135
|
+
await append(json);
|
|
136
|
+
console.log("FormData in json to formdata:", formData);
|
|
126
137
|
return formData;
|
|
127
138
|
}
|
|
128
139
|
|
|
@@ -149,6 +160,8 @@ async function formHeaders(authentication, headers = {}, bodyType) {
|
|
|
149
160
|
).toString("base64")}`;
|
|
150
161
|
}
|
|
151
162
|
}
|
|
163
|
+
console.log('Headers:', headers);
|
|
164
|
+
|
|
152
165
|
|
|
153
166
|
return headers;
|
|
154
167
|
}
|
|
@@ -168,7 +181,7 @@ async function validate(
|
|
|
168
181
|
) {
|
|
169
182
|
switch (actionName.toLowerCase()) {
|
|
170
183
|
case "verify":
|
|
171
|
-
return validateAttributeData(
|
|
184
|
+
return await validateAttributeData(
|
|
172
185
|
attributeName,
|
|
173
186
|
actualValue,
|
|
174
187
|
useBuffer,
|
|
@@ -189,7 +202,7 @@ async function validate(
|
|
|
189
202
|
`Retrieved value from BUFFER: ${bufferKey}`,
|
|
190
203
|
"info"
|
|
191
204
|
);
|
|
192
|
-
return BUFFER.getItem(bufferKey);
|
|
205
|
+
return await BUFFER.getItem(bufferKey);
|
|
193
206
|
}
|
|
194
207
|
}
|
|
195
208
|
|
|
@@ -200,6 +213,7 @@ async function validateAttributeData(
|
|
|
200
213
|
bufferKey,
|
|
201
214
|
datatype
|
|
202
215
|
) {
|
|
216
|
+
let assertionStatus = false; // Initialize status
|
|
203
217
|
let expectedValue = useBuffer
|
|
204
218
|
? BUFFER.getItem(bufferKey)
|
|
205
219
|
: bufferKey;
|
|
@@ -220,14 +234,16 @@ async function validateAttributeData(
|
|
|
220
234
|
}
|
|
221
235
|
|
|
222
236
|
try {
|
|
223
|
-
expect(
|
|
237
|
+
expect(attribute).toBe(expectedValue, `${attributeName} --> Expected value: ${expectedValue}, but got: ${attribute}`);
|
|
238
|
+
let annotationMessage = `Attribute name : ${attributeName} - verification passed. Actual text: ${attribute}, Expected text: ${expectedValue}.`;
|
|
224
239
|
await amendToBrowserstack.amend2Browserstack(
|
|
225
|
-
|
|
240
|
+
annotationMessage,
|
|
226
241
|
"info"
|
|
227
242
|
);
|
|
228
243
|
} catch (e) {
|
|
244
|
+
let annotationMessage = `Attribute name : ${attributeName} - verification failed. Expected text: ${expectedValue}. error: ${e.toString()}`;
|
|
229
245
|
await amendToBrowserstack.amend2Browserstack(
|
|
230
|
-
|
|
246
|
+
annotationMessage,
|
|
231
247
|
"error"
|
|
232
248
|
);
|
|
233
249
|
throw e;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "froth-webdriverio-framework",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.12",
|
|
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": {
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
const crypto = require('crypto');
|
|
2
|
-
|
|
3
|
-
//const SECRET_KEY = "froth-testops@rd";
|
|
4
|
-
|
|
5
|
-
async function encryptData(data, secretKey) {
|
|
6
|
-
// Generate a random 16-byte IV (Initialization Vector)
|
|
7
|
-
const iv = crypto.randomBytes(16);
|
|
8
|
-
|
|
9
|
-
// Ensure the key is 16 bytes long
|
|
10
|
-
const key = Buffer.from(secretKey, 'utf-8');
|
|
11
|
-
|
|
12
|
-
// Initialize cipher in CBC mode with the key and IV
|
|
13
|
-
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
|
|
14
|
-
|
|
15
|
-
// Encrypt the data
|
|
16
|
-
let encrypted = cipher.update(data, 'utf-8', 'base64');
|
|
17
|
-
encrypted += cipher.final('base64');
|
|
18
|
-
|
|
19
|
-
// Combine the IV and encrypted data, encode as base64
|
|
20
|
-
const encryptedData = Buffer.concat([iv, Buffer.from(encrypted, 'base64')]).toString('base64');
|
|
21
|
-
|
|
22
|
-
return encryptedData;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// Encrypt a password
|
|
28
|
-
//const encryptedPassword = encryptData("admin@1234", SECRET_KEY);
|
|
29
|
-
//console.log("Encrypted Password:", encryptedPassword);
|
|
30
|
-
|
|
31
|
-
module.exports = encryptData;
|
|
32
|
-
|
|
33
|
-
|