froth-webdriverio-framework 5.0.16 → 6.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.
- package/froth_api_calls/browsersatckSessionInfo.js +1 -1
- package/froth_api_calls/getexecutionDetails.js +27 -21
- package/froth_common_actions/Utils.js +6 -1
- package/froth_common_actions/assert.js +1 -3
- package/froth_common_actions/emailParsing.js +161 -0
- package/froth_common_actions/logData.js +0 -1
- package/froth_configs/commonconfig.js +33 -60
- package/froth_configs/wdio.common.conf.js +74 -31
- package/package.json +4 -6
- package/froth_configs/wdio-require-patch.js +0 -47
|
@@ -108,7 +108,7 @@ async function amend2Browserstack(annotationMessage, level) {
|
|
|
108
108
|
console.log("Annotation message inside amend2Browserstack:" + annotationMessage)
|
|
109
109
|
if (process.env.PLATFORM === 'browserstack')
|
|
110
110
|
await driver.execute('browserstack_executor: {"action": "annotate", "arguments": {"data":"' + annotationMessage + '","level": "' + level + '"}}');
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
} catch (error) {
|
|
113
113
|
console.error('Error occurred while annoting to BS :', error);
|
|
114
114
|
throw error;
|
|
@@ -34,7 +34,7 @@ async function getExecuitonDetails(frothUrl, token, id) {
|
|
|
34
34
|
|
|
35
35
|
return jsondata;
|
|
36
36
|
} else if (response.status === 401) { // Unauthorized, token expired
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
console.error("Unauthorized, token expired" + response.status);
|
|
39
39
|
|
|
40
40
|
} else {
|
|
@@ -107,10 +107,7 @@ async function update_CICDRUNID_ReportUrl(frothUrl, token, id) {
|
|
|
107
107
|
|
|
108
108
|
// formData.append('id', BUFFER.getItem("FROTH_EXECUTION_ID"))
|
|
109
109
|
formData.append('updated_through_bot', true)
|
|
110
|
-
|
|
111
|
-
console.log("CICD_RUN_ID is null")
|
|
112
|
-
else
|
|
113
|
-
formData.append('run_id', process.env.CICD_RUN_ID)
|
|
110
|
+
formData.append('run_id', process.env.CICD_RUN_ID)
|
|
114
111
|
|
|
115
112
|
|
|
116
113
|
if (BUFFER.getItem("REPORT_URL") === null)
|
|
@@ -170,6 +167,7 @@ async function updateExecuitonDetails(frothUrl, token, id, resultdetails) {
|
|
|
170
167
|
} else
|
|
171
168
|
formData.append('excution_time', resultdetails.excution_time);
|
|
172
169
|
|
|
170
|
+
|
|
173
171
|
if (resultdetails.comments === null)
|
|
174
172
|
console.log("Comments is null")
|
|
175
173
|
else if (resultdetails.comments.length === 0)
|
|
@@ -177,7 +175,6 @@ async function updateExecuitonDetails(frothUrl, token, id, resultdetails) {
|
|
|
177
175
|
else
|
|
178
176
|
formData.append('comments', resultdetails.comments.join('<br>'))
|
|
179
177
|
|
|
180
|
-
|
|
181
178
|
// formData.append('comments', JSON.stringify(resultdetails.comments))
|
|
182
179
|
|
|
183
180
|
const response = await fetch(url, {
|
|
@@ -255,20 +252,20 @@ async function updateScriptExecutionStatus(frothUrl, token, scriptid, script_pla
|
|
|
255
252
|
}
|
|
256
253
|
|
|
257
254
|
}
|
|
258
|
-
async function
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
console.log("
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const url = `${frothUrl}/api/
|
|
255
|
+
async function updateScriptExecutionStatus(frothUrl, token, scriptid, script_platform, status) {
|
|
256
|
+
|
|
257
|
+
if (scriptid != 0) {
|
|
258
|
+
try {
|
|
259
|
+
console.log("script platform is " + script_platform)
|
|
260
|
+
const id = await getExecuitonScriptDetails(frothUrl, token, BUFFER.getItem("FROTH_EXECUTION_ID"), scriptid, script_platform)
|
|
261
|
+
console.log("ID is :" + id)
|
|
262
|
+
const url = `${frothUrl}/api/script-status-percentage/${id}/`;
|
|
266
263
|
const formData = new FormData();
|
|
267
264
|
|
|
268
265
|
console.log("URL" + url)
|
|
266
|
+
// formData.append('execution_id', BUFFER.getItem("EXECUTION_ID"))
|
|
267
|
+
formData.append('script_status', status)
|
|
269
268
|
formData.append('updated_through_bot', true)
|
|
270
|
-
formData.append('report_url', `reports/${process.env.EXECUTION_ID}/test-report.html`)
|
|
271
|
-
|
|
272
269
|
|
|
273
270
|
const response = await fetch(url, {
|
|
274
271
|
method: 'PUT',
|
|
@@ -281,9 +278,15 @@ async function updateReportUrl(frothUrl, token, id) {
|
|
|
281
278
|
|
|
282
279
|
if (response.ok) {
|
|
283
280
|
const data = await response.json();
|
|
284
|
-
|
|
281
|
+
// console.log("data is :"+data)
|
|
282
|
+
|
|
285
283
|
} else if (response.status === 401) { // Unauthorized, token expired
|
|
284
|
+
// Call login function to obtain a new token
|
|
285
|
+
// const newToken = await getLoginToken(BUFFER.getItem("SERVICE_USER"), BUFFER.getItem("SERVICE_PASSWORD")); // You need to implement the login function
|
|
286
|
+
// // Retry the request with the new token
|
|
287
|
+
// return updateScriptExecutionStatus(frothUrl, newToken, scriptid, script_platform, status);
|
|
286
288
|
console.log("Unauthorized, token expired" + response.status)
|
|
289
|
+
|
|
287
290
|
} else {
|
|
288
291
|
const errorText = await response.text();
|
|
289
292
|
console.error(`error in updating the status into DB ${response.status}`);
|
|
@@ -291,10 +294,13 @@ async function updateReportUrl(frothUrl, token, id) {
|
|
|
291
294
|
}
|
|
292
295
|
|
|
293
296
|
|
|
294
|
-
}
|
|
295
|
-
} catch (error) {
|
|
296
|
-
console.error('Error in update report url :', error);
|
|
297
297
|
|
|
298
|
+
} catch (error) {
|
|
299
|
+
console.error('Error updating data for script status:', error);
|
|
300
|
+
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
console.error('Else: Error updating data for script status: Invalid ID');
|
|
298
304
|
}
|
|
299
305
|
|
|
300
306
|
}
|
|
@@ -328,5 +334,5 @@ async function updateReportUrl(frothUrl, token, id) {
|
|
|
328
334
|
// }
|
|
329
335
|
|
|
330
336
|
// main();
|
|
331
|
-
module.exports = { getExecuitonDetails, updateExecuitonDetails, updateScriptExecutionStatus, update_CICDRUNID_ReportUrl
|
|
337
|
+
module.exports = { getExecuitonDetails, updateExecuitonDetails, updateScriptExecutionStatus, update_CICDRUNID_ReportUrl };
|
|
332
338
|
|
|
@@ -55,6 +55,8 @@ let captureLoadNavigation = null;
|
|
|
55
55
|
let amendBrowserStackLog = null;
|
|
56
56
|
let logJsonDataToTable = null;
|
|
57
57
|
|
|
58
|
+
let extractEmailLinks = null;
|
|
59
|
+
|
|
58
60
|
if (process.env.LOCATION == null || process.env.LOCATION == 'local') {
|
|
59
61
|
basepath = ".";
|
|
60
62
|
} else {
|
|
@@ -114,6 +116,8 @@ captureLoadNavigation= require(basepath + '/captureNavigationTime').captureLoadN
|
|
|
114
116
|
|
|
115
117
|
amendBrowserStackLog= require(basepath + '/../froth_api_calls/browsersatckSessionInfo').amend2Browserstack;
|
|
116
118
|
logJsonDataToTable= require(basepath + '/logData').logJsonData2Table;
|
|
119
|
+
|
|
120
|
+
extractEmailLinks = require(basepath + '/emailParsing');
|
|
117
121
|
//export the variabels
|
|
118
122
|
module.exports = {
|
|
119
123
|
scrollToEnd,
|
|
@@ -158,5 +162,6 @@ module.exports = {
|
|
|
158
162
|
switchToWindowByIndex,
|
|
159
163
|
captureLoadNavigation,
|
|
160
164
|
amendBrowserStackLog,
|
|
161
|
-
logJsonDataToTable
|
|
165
|
+
logJsonDataToTable,
|
|
166
|
+
extractEmailLinks
|
|
162
167
|
};
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
// Function to verify text in Android app
|
|
2
|
-
|
|
3
|
-
const assert=require("assert");
|
|
4
|
-
|
|
2
|
+
import assert from 'assert';
|
|
5
3
|
const amendToBrowserstack = require("../froth_api_calls/browsersatckSessionInfo").amend2Browserstack;
|
|
6
4
|
|
|
7
5
|
async function assertText(elementSelector, expectedText) {
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
2
|
+
|
|
3
|
+
const Imap = require('imap');
|
|
4
|
+
const { simpleParser } = require('mailparser');
|
|
5
|
+
const cheerio = require('cheerio');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Connects to an IMAP inbox, parses emails, and extracts all unique links.
|
|
9
|
+
* @param {Object} config - IMAP config { user, password, host, port, tls }
|
|
10
|
+
* @param {Object} [filter] - Optional filter { subject, from }
|
|
11
|
+
* @returns {Promise<Array<{link: string, text: string}>>}
|
|
12
|
+
*/
|
|
13
|
+
async function getEmailLinks(username,app_password, subjectdetails,fromemail) {
|
|
14
|
+
const config = {
|
|
15
|
+
user: username,
|
|
16
|
+
password: app_password,
|
|
17
|
+
host: 'imap.gmail.com',
|
|
18
|
+
port: 993,
|
|
19
|
+
tls: true
|
|
20
|
+
};
|
|
21
|
+
const filter = {
|
|
22
|
+
subject: subjectdetails,
|
|
23
|
+
from: fromemail
|
|
24
|
+
};
|
|
25
|
+
const imap = new Imap(config);
|
|
26
|
+
|
|
27
|
+
function openInbox(cb) {
|
|
28
|
+
imap.openBox('INBOX', true, cb);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
let results = new Map();
|
|
33
|
+
let allLinks = [];
|
|
34
|
+
let parseDone;
|
|
35
|
+
const parsePromise = new Promise(res => { parseDone = res; });
|
|
36
|
+
|
|
37
|
+
imap.once('ready', function () {
|
|
38
|
+
openInbox(function (err, box) {
|
|
39
|
+
if (err) return cleanup(err);
|
|
40
|
+
|
|
41
|
+
// Build search criteria
|
|
42
|
+
let searchCriteria = ['ALL'];
|
|
43
|
+
if (filter.subject) searchCriteria.push(['HEADER', 'SUBJECT', filter.subject]);
|
|
44
|
+
if (filter.from) searchCriteria.push(['FROM', filter.from]);
|
|
45
|
+
|
|
46
|
+
imap.search(searchCriteria, function (err, uids) {
|
|
47
|
+
if (err) return cleanup(err);
|
|
48
|
+
if (!uids.length) return cleanup(null, []);
|
|
49
|
+
|
|
50
|
+
// Sort UIDs descending to get the latest email first
|
|
51
|
+
uids = uids.sort((a, b) => b - a);
|
|
52
|
+
// Only fetch the latest email
|
|
53
|
+
const latestUid = uids[0];
|
|
54
|
+
const fetch = imap.fetch([latestUid], { bodies: '' });
|
|
55
|
+
fetch.on('message', function (msg) {
|
|
56
|
+
let buffer = '';
|
|
57
|
+
msg.on('body', function (stream) {
|
|
58
|
+
stream.on('data', function (chunk) {
|
|
59
|
+
buffer += chunk.toString('utf8');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
msg.once('end', async function () {
|
|
63
|
+
try {
|
|
64
|
+
const parsed = await simpleParser(buffer);
|
|
65
|
+
let links = [];
|
|
66
|
+
|
|
67
|
+
// Extract from HTML with element context
|
|
68
|
+
if (parsed.html) {
|
|
69
|
+
const $ = cheerio.load(parsed.html);
|
|
70
|
+
$('a[href], button[href], [role="button"][href], img[src], area[href]').each((_, el) => {
|
|
71
|
+
let link = null;
|
|
72
|
+
let text = '';
|
|
73
|
+
let elementType = el.tagName;
|
|
74
|
+
if (elementType === 'a' || elementType === 'area') {
|
|
75
|
+
link = $(el).attr('href');
|
|
76
|
+
text = $(el).text().trim() || link;
|
|
77
|
+
} else if (elementType === 'img') {
|
|
78
|
+
link = $(el).attr('src');
|
|
79
|
+
text = $(el).attr('alt') || link;
|
|
80
|
+
} else if (elementType === 'button' || $(el).attr('role') === 'button') {
|
|
81
|
+
link = $(el).attr('href');
|
|
82
|
+
text = $(el).text().trim() || link;
|
|
83
|
+
}
|
|
84
|
+
if (link) {
|
|
85
|
+
links.push({ link, text, element: elementType });
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Extract from plaintext
|
|
91
|
+
if (parsed.text) {
|
|
92
|
+
const urlRegex = /(https?:\/\/[^\s]+)/g;
|
|
93
|
+
let match;
|
|
94
|
+
while ((match = urlRegex.exec(parsed.text))) {
|
|
95
|
+
links.push({ link: match[1], text: match[1], element: 'text' });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Deduplicate by link and element context
|
|
100
|
+
for (const l of links) {
|
|
101
|
+
const key = l.link + '|' + l.element + '|' + l.text;
|
|
102
|
+
if (!results.has(key)) {
|
|
103
|
+
results.set(key, { link: l.link, text: l.text, element: l.element });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
allLinks = Array.from(results.values());
|
|
107
|
+
parseDone();
|
|
108
|
+
} catch (e) {
|
|
109
|
+
// Log and continue
|
|
110
|
+
console.error('Parse error:', e);
|
|
111
|
+
parseDone();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
fetch.once('error', cleanup);
|
|
116
|
+
fetch.once('end', async () => {
|
|
117
|
+
await parsePromise;
|
|
118
|
+
cleanup(null, allLinks);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
imap.once('error', cleanup);
|
|
125
|
+
|
|
126
|
+
function cleanup(err, data) {
|
|
127
|
+
imap.end();
|
|
128
|
+
if (err) reject(err);
|
|
129
|
+
else resolve(data);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
imap.connect();
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function findLinks(links,searchText) {
|
|
137
|
+
const findLinkByText = (links, searchText) => {
|
|
138
|
+
return links.find(l => l.text && l.text.toLowerCase().includes(searchText.toLowerCase()));
|
|
139
|
+
};
|
|
140
|
+
// Example usage:
|
|
141
|
+
const found = findLinkByText(links, searchText);
|
|
142
|
+
return found ? found.link : null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
// Example main function for direct execution
|
|
147
|
+
// if (require.main === module) {
|
|
148
|
+
// (async () => {
|
|
149
|
+
|
|
150
|
+
// const links = await extractEmailLinks('gfntesting44@gmail.com','wtrd qfee dicr hpba', "GFN Testing, review your Google Account settings","no-reply@google.com");
|
|
151
|
+
// console.log('Extracted links in main:', links);
|
|
152
|
+
// const foundLink = await findLinks(links,"Privacy Policy");
|
|
153
|
+
// console.log('Found Link: ', foundLink);
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
// })();
|
|
157
|
+
|
|
158
|
+
// }
|
|
159
|
+
|
|
160
|
+
module.exports = { getEmailLinks ,findLinks}
|
|
161
|
+
|
|
@@ -5,12 +5,10 @@ const { LocalStorage } = require('node-localstorage');
|
|
|
5
5
|
global.BUFFER = new LocalStorage('./storage');
|
|
6
6
|
const setAllDetails = require("./setallDatailinBuffer")
|
|
7
7
|
const exeDetails = require("../froth_api_calls/getexecutionDetails")
|
|
8
|
-
|
|
8
|
+
const getBSSessionDetails = require("../froth_api_calls/browsersatckSessionInfo").getBSSessionDetails;
|
|
9
9
|
const { fail } = require("assert");
|
|
10
10
|
const { error } = require('console');
|
|
11
|
-
const
|
|
12
|
-
global.Util=require('../froth_common_actions/Utils');
|
|
13
|
-
|
|
11
|
+
const browserstack = require("browserstack-local");
|
|
14
12
|
//const isBrowserStackEnabled = process.env.BROWSERSTACK;
|
|
15
13
|
let starttime;
|
|
16
14
|
let endtime;
|
|
@@ -21,6 +19,7 @@ const resultdetails = {
|
|
|
21
19
|
excution_status: null, // Pass/Fail
|
|
22
20
|
excution_time: null, // Execution time in milliseconds
|
|
23
21
|
};
|
|
22
|
+
let bsLocal;
|
|
24
23
|
// Description: This file contains the common configuration for the webdriverio framework.
|
|
25
24
|
const commonconfig = {
|
|
26
25
|
|
|
@@ -45,8 +44,6 @@ const commonconfig = {
|
|
|
45
44
|
|
|
46
45
|
beforeSession: async function (config, capabilities, specs) {
|
|
47
46
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
50
47
|
let syntaxFailed = false;
|
|
51
48
|
|
|
52
49
|
console.log('==== BEFORE SESSION HOOK ====');
|
|
@@ -83,7 +80,26 @@ const commonconfig = {
|
|
|
83
80
|
// set the capability like media and app and bs local
|
|
84
81
|
if (process.env.PLATFORM === 'browserstack') {
|
|
85
82
|
/// console.log("capabilities:", capabilities);
|
|
86
|
-
|
|
83
|
+
if (process.env.BROWSERSTACK_LOCAL === true) {
|
|
84
|
+
bsLocal = new browserstack.Local();
|
|
85
|
+
const localArgs = {
|
|
86
|
+
key: process.env.BROWSERSTACK_ACCESS_KEY,
|
|
87
|
+
forceLocal: true,
|
|
88
|
+
localIdentifier: capabilities.localIdentifier || 'wdioLocal'
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
await new Promise((resolve, reject) => {
|
|
92
|
+
bsLocal.start(localArgs, function (error) {
|
|
93
|
+
if (error) return reject(error);
|
|
94
|
+
console.log("====> BrowserStack Local started successfully.");
|
|
95
|
+
resolve();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Add Local settings into capabilities dynamically
|
|
100
|
+
capabilities['browserstack.local'] = true;
|
|
101
|
+
capabilities['browserstack.localIdentifier'] = localArgs.localIdentifier || 'ExecuteLocal';
|
|
102
|
+
}
|
|
87
103
|
// capabilities.accessKey = Buffer.from(capabilities.accessKey, 'base64').toString('utf-8');
|
|
88
104
|
if (capabilities.platformName === 'android' || capabilities.platformName === 'ios') {
|
|
89
105
|
capabilities.app = process.env.BROWSERSTACK_APP_PATH;
|
|
@@ -140,13 +156,7 @@ const commonconfig = {
|
|
|
140
156
|
|
|
141
157
|
if (process.env.PLATFORM === 'browserstack')
|
|
142
158
|
await getBSSessionDetails(process.env.BS_SESSION_TYPE, process.env.BROWSERSTACK_USERNAME, process.env.BROWSERSTACK_ACCESS_KEY);
|
|
143
|
-
else {
|
|
144
|
-
console.log("Local execution - no BrowserStack session details to fetch.");
|
|
145
|
-
// BUFFER.setItem("SESSION_ID", "local-session");
|
|
146
|
-
BUFFER.setItem("REPORT_URL", `./reports/${process.env.EXECUTION_ID}/test-report.html`);
|
|
147
|
-
//BUFFER.setItem("FROTH_TOTAL_DURATION", 0);
|
|
148
159
|
|
|
149
|
-
}
|
|
150
160
|
|
|
151
161
|
// const resultdetails = {};
|
|
152
162
|
await exeDetails.update_CICDRUNID_ReportUrl(BUFFER.getItem("ORGANISATION_DOMAIN_URL"), BUFFER.getItem("FROTH_LOGIN_TOKEN"), BUFFER.getItem("FROTH_EXECUTION_ID"))
|
|
@@ -166,7 +176,6 @@ const commonconfig = {
|
|
|
166
176
|
console.log('==== BEFORE HOOK FOR MOBILE ====');
|
|
167
177
|
}
|
|
168
178
|
|
|
169
|
-
|
|
170
179
|
},
|
|
171
180
|
|
|
172
181
|
/**
|
|
@@ -337,6 +346,15 @@ const commonconfig = {
|
|
|
337
346
|
|
|
338
347
|
await exeDetails.updateExecuitonDetails(BUFFER.getItem("ORGANISATION_DOMAIN_URL"), BUFFER.getItem("FROTH_LOGIN_TOKEN"), BUFFER.getItem("FROTH_EXECUTION_ID"), resultdetails)
|
|
339
348
|
|
|
349
|
+
if (bsLocal && bsLocal.isRunning()) {
|
|
350
|
+
console.log("====> Stopping BrowserStack Local...");
|
|
351
|
+
await new Promise((resolve) => {
|
|
352
|
+
bsLocal.stop(() => {
|
|
353
|
+
console.log("====> BrowserStack Local stopped.");
|
|
354
|
+
resolve();
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
}
|
|
340
358
|
// Perform any cleanup or post-test actions here
|
|
341
359
|
BUFFER.clear();
|
|
342
360
|
},
|
|
@@ -351,52 +369,7 @@ const commonconfig = {
|
|
|
351
369
|
console.log('Exit Code:', exitCode);
|
|
352
370
|
|
|
353
371
|
console.log('==== ALL TESTS ARE COMPLETED ====');
|
|
354
|
-
|
|
355
|
-
const outputFilePath = `./reports/${process.env.EXECUTION_ID}/test-report.html`;
|
|
356
|
-
const jsonFolder = `./reports/${process.env.EXECUTION_ID}/`; // Directory where JSON reports are saved
|
|
357
|
-
// Find latest JSON file (if multiple exist)
|
|
358
|
-
// const jsonFiles = fs
|
|
359
|
-
// .readdirSync(jsonFolder)
|
|
360
|
-
// .filter(file => file.endsWith('.json'))
|
|
361
|
-
// .map(file => ({
|
|
362
|
-
// name: file,
|
|
363
|
-
// time: fs.statSync(path.join(jsonFolder, file)).mtime.getTime(),
|
|
364
|
-
// }))
|
|
365
|
-
// .sort((a, b) => b.time - a.time);
|
|
366
|
-
|
|
367
|
-
// if (jsonFiles.length === 0) {
|
|
368
|
-
// console.warn('⚠️ No JSON file found, HTML not generated.');
|
|
369
|
-
// return;
|
|
370
|
-
// }
|
|
371
|
-
|
|
372
|
-
// const latestJson = path.join(jsonFolder, jsonFiles[0].name);
|
|
373
|
-
//console.log(`🧩 Using JSON report: ${latestJson}`);
|
|
374
|
-
|
|
375
|
-
// const reportGenerator = new HTMLReportGenerator(outputFilePath, jsonFolder);
|
|
376
|
-
|
|
377
|
-
// Call your method (you can rename "generate" to whatever method your class exposes)
|
|
378
|
-
// reportGenerator.generate({
|
|
379
|
-
// jsonFile: latestJson,
|
|
380
|
-
// reportPath: jsonFolder,
|
|
381
|
-
// reportName: `Execution Report - ${process.env.EXECUTION_ID}`,
|
|
382
|
-
// outputFile: outputFilePath,
|
|
383
|
-
// });
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
console.log(`✅ HTML report generated: ${outputFilePath}`);
|
|
387
|
-
// If you want to include historical data, specify the history JSON file path here.
|
|
388
|
-
const historyFile = `./reports/${process.env.EXECUTION_ID}/history.json`; // Optional
|
|
389
|
-
|
|
390
|
-
// Optionally, generate aggregated history data before generating the HTML report.
|
|
391
|
-
// JSONReporter.generateAggregateHistory({ reportPaths: jsonFolder, historyPath: historyFile });
|
|
392
|
-
|
|
393
|
-
const reportGenerator = new HTMLReportGenerator(outputFilePath, historyFile);
|
|
394
|
-
await reportGenerator.convertJSONFolderToHTML(jsonFolder);
|
|
395
|
-
|
|
396
|
-
await exeDetails.updateReportUrl(BUFFER.getItem("ORGANISATION_DOMAIN_URL"), BUFFER.getItem("FROTH_LOGIN_TOKEN"), BUFFER.getItem("FROTH_EXECUTION_ID"))
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
// return exitCode;
|
|
372
|
+
return exitCode;
|
|
400
373
|
}
|
|
401
374
|
|
|
402
375
|
};
|
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
const deepmerge = require('deepmerge')
|
|
2
|
-
//global.Util=require('./patch_require'); // Ensure this is imported first
|
|
3
2
|
const fs = require('fs');
|
|
4
3
|
const yaml = require('js-yaml');
|
|
5
4
|
const path = require('path');
|
|
6
5
|
const commonconfig = require('./commonconfig');
|
|
7
6
|
console.log('=====wdios common config===== ');
|
|
8
|
-
const { JSONReporter, HTMLReportGenerator } = require('wdio-json-html-reporter');
|
|
9
|
-
//require("./patch_require"); // Ensure this is imported first
|
|
10
7
|
|
|
11
8
|
// Select platform at runtime
|
|
12
9
|
const PLATFORM = process.env.PLATFORM || 'browserstack';
|
|
13
10
|
console.log('====>PLATFORM:', PLATFORM);
|
|
14
11
|
|
|
15
12
|
const configFile = process.env.YML_NAME;
|
|
16
|
-
|
|
13
|
+
|
|
17
14
|
const resultdetails = {
|
|
18
15
|
comments: [],
|
|
19
16
|
excution_status: null, // Pass/Fail
|
|
20
17
|
excution_time: null, // Execution time in milliseconds
|
|
21
18
|
};
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
25
20
|
// Load YAML file
|
|
26
21
|
//const capabilities = yaml.load(fs.readFileSync(path.join(path.resolve(process.cwd(), configFile)), 'utf8'));
|
|
27
22
|
|
|
@@ -92,46 +87,98 @@ function setupPrerequisites() {
|
|
|
92
87
|
process.env.BROWSERSTACK_USERNAME = capabilities.userName;
|
|
93
88
|
capabilities.accessKey = Buffer.from(capabilities.accessKey, 'base64').toString('utf-8');
|
|
94
89
|
process.env.BROWSERSTACK_ACCESS_KEY = capabilities.accessKey
|
|
95
|
-
|
|
96
90
|
console.log('capabilities.platformName:', capabilities.platformName ?? 'web');
|
|
97
|
-
process.env.BS_SESSION_TYPE = capabilities.platformName === 'android' || capabilities.platformName === 'ios' ? 'app-automate' : 'automate';
|
|
98
|
-
capabilities.buildName = process.env.FROTH_TESTOPS_BUILD_NAME;
|
|
99
91
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
// Determine session type automatically
|
|
93
|
+
const isMobile =
|
|
94
|
+
capabilities.platformName &&
|
|
95
|
+
(capabilities.platformName.toLowerCase() === "android" ||
|
|
96
|
+
capabilities.platformName.toLowerCase() === "ios");
|
|
97
|
+
|
|
98
|
+
if (isMobile) {
|
|
99
|
+
process.env.BS_SESSION_TYPE = "app-automate";
|
|
100
|
+
} else {
|
|
101
|
+
process.env.BS_SESSION_TYPE = "automate";
|
|
102
|
+
}
|
|
103
|
+
// process.env.BS_SESSION_TYPE = capabilities.platformName === 'android' || capabilities.platformName === 'ios' ? 'app-automate' : 'automate';
|
|
103
104
|
capabilities.buildName = process.env.FROTH_TESTOPS_BUILD_NAME;
|
|
104
105
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
setupPrerequisites();
|
|
111
|
+
|
|
112
|
+
// =====================================================
|
|
113
|
+
// IMPORTANT: NORMALIZE CAPABILITY FUNCTION
|
|
114
|
+
// =====================================================
|
|
115
|
+
function normalizeCapabilities(rawCaps, sessionType, platform) {
|
|
116
|
+
if (platform === "browserstack") {
|
|
117
|
+
// ----------------------------------
|
|
118
|
+
// BrowserStack Automate (Web)
|
|
119
|
+
// ----------------------------------
|
|
120
|
+
if (sessionType === "automate") {
|
|
121
|
+
return {
|
|
122
|
+
browserName: rawCaps.browserName,
|
|
123
|
+
browserVersion: rawCaps.browserVersion,
|
|
124
|
+
'bstack:options': {
|
|
125
|
+
os: rawCaps.os,
|
|
126
|
+
osVersion: rawCaps.osVersion,
|
|
127
|
+
consoleLogs: rawCaps.consoleLogs,
|
|
128
|
+
networkLogs: rawCaps.networkLogs,
|
|
129
|
+
debug: rawCaps.debug,
|
|
130
|
+
buildIdentifier: rawCaps.buildIdentifier,
|
|
131
|
+
// userName: rawCaps.userName,
|
|
132
|
+
// accessKey: process.env.BROWSERSTACK_ACCESS_KEY
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ----------------------------------
|
|
138
|
+
// BrowserStack APP-Automate (Mobile)
|
|
139
|
+
// ----------------------------------
|
|
140
|
+
return {
|
|
141
|
+
platformName: rawCaps.platformName,
|
|
142
|
+
'appium:platformVersion': rawCaps.platformVersion,
|
|
143
|
+
'appium:deviceName': rawCaps.deviceName,
|
|
144
|
+
'appium:deviceOrientation': rawCaps.deviceOrientation,
|
|
145
|
+
'appium:app': rawCaps.app || "",
|
|
146
|
+
'bstack:options': {
|
|
147
|
+
appProfiling: rawCaps.appProfiling,
|
|
148
|
+
networkLogs: rawCaps.networkLogs,
|
|
149
|
+
debug: rawCaps.debug,
|
|
150
|
+
interactiveDebugging: rawCaps.interactiveDebugging,
|
|
151
|
+
buildIdentifier: rawCaps.buildIdentifier,
|
|
152
|
+
|
|
153
|
+
// userName: rawCaps.userName,
|
|
154
|
+
// accessKey: process.env.BROWSERSTACK_ACCESS_KEY
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return rawCaps;
|
|
110
159
|
|
|
111
|
-
// Create per-execution report folder
|
|
112
|
-
const reportDir = path.resolve(process.cwd(), `./reports/${process.env.EXECUTION_ID}`);
|
|
113
|
-
if (!fs.existsSync(reportDir)) {
|
|
114
|
-
fs.mkdirSync(reportDir, { recursive: true });
|
|
115
160
|
}
|
|
161
|
+
const normalizedCaps = normalizeCapabilities(capabilities, process.env.BS_SESSION_TYPE, PLATFORM);
|
|
162
|
+
console.log("====> Final WDIO Normalised Capabilities:", normalizedCaps);
|
|
116
163
|
|
|
117
164
|
exports.config = deepmerge(commonconfig,
|
|
118
165
|
|
|
119
166
|
{
|
|
120
|
-
|
|
121
|
-
|
|
167
|
+
user: process.env.BROWSERSTACK_USERNAME,
|
|
168
|
+
key: process.env.BROWSERSTACK_ACCESS_KEY,
|
|
122
169
|
// debug: true,
|
|
123
170
|
// execArgv: ['--inspect-brk'],
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
171
|
+
services: PLATFORM === 'browserstack'
|
|
172
|
+
? ['browserstack']
|
|
173
|
+
: PLATFORM === 'saucelabs'
|
|
174
|
+
? ['sauce']
|
|
175
|
+
: [],
|
|
129
176
|
|
|
130
|
-
runner: 'local',
|
|
177
|
+
//runner: 'local',
|
|
131
178
|
specs: require(SUITE_FILE).tests,
|
|
132
179
|
|
|
133
180
|
maxInstances: 1,
|
|
134
|
-
capabilities: [
|
|
181
|
+
capabilities: [normalizedCaps],
|
|
135
182
|
|
|
136
183
|
logLevel: 'info',
|
|
137
184
|
coloredLogs: true,
|
|
@@ -143,15 +190,11 @@ exports.config = deepmerge(commonconfig,
|
|
|
143
190
|
connectionRetryCount: 3,
|
|
144
191
|
|
|
145
192
|
framework: 'mocha',
|
|
146
|
-
reporters: ['spec',
|
|
147
|
-
[JSONReporter, { outputDir: reportDir, outputFile: `./reports/${process.env.EXECUTION_ID}/test-results-${timestamp}.json`, screenshotOption: 'OnFailure' }],
|
|
148
|
-
],
|
|
149
|
-
|
|
193
|
+
reporters: ['spec'],
|
|
150
194
|
maxInstances: 10,
|
|
151
195
|
updateJob: false,
|
|
152
196
|
mochaOpts: {
|
|
153
197
|
ui: 'bdd',
|
|
154
198
|
timeout: 300000
|
|
155
199
|
}
|
|
156
|
-
|
|
157
200
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "froth-webdriverio-framework",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"readme": "WebdriverIO Integration",
|
|
5
5
|
"description": "WebdriverIO and BrowserStack App Automate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,14 +25,12 @@
|
|
|
25
25
|
"appium"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@wdio/allure-reporter": "^9.20.0",
|
|
29
28
|
"@wdio/appium-service": "^9.0.9",
|
|
30
29
|
"@wdio/browserstack-service": "^9.0.9",
|
|
31
30
|
"@wdio/cli": "^9.0.9",
|
|
32
31
|
"@wdio/local-runner": "^9.0.9",
|
|
33
32
|
"@wdio/mocha-framework": "^8.36.1",
|
|
34
33
|
"@wdio/spec-reporter": "^8.36.1",
|
|
35
|
-
"allure-commandline": "^2.34.1",
|
|
36
34
|
"appium": "^2.15.0",
|
|
37
35
|
"appium-uiautomator2-driver": "^4.0.1",
|
|
38
36
|
"assert": "^2.1.0",
|
|
@@ -43,15 +41,15 @@
|
|
|
43
41
|
"deepmerge": "^4.3.1",
|
|
44
42
|
"express": "^5.1.0",
|
|
45
43
|
"form-data": "^4.0.0",
|
|
46
|
-
"froth-webdriverio-framework": "^5.0.10",
|
|
47
44
|
"fs": "^0.0.1-security",
|
|
45
|
+
"imap-simple": "^5.1.0",
|
|
48
46
|
"js-yaml": "^4.1.0",
|
|
47
|
+
"mailparser": "^3.9.0",
|
|
49
48
|
"mysql2": "^3.10.2",
|
|
50
49
|
"node-fetch": "^3.3.2",
|
|
51
50
|
"node-localstorage": "^3.0.5",
|
|
52
51
|
"randexp": "^0.5.3",
|
|
53
52
|
"ts-node": "^10.9.2",
|
|
54
|
-
"typescript": "^5.4.5"
|
|
55
|
-
"wdio-json-html-reporter": "^1.5.14"
|
|
53
|
+
"typescript": "^5.4.5"
|
|
56
54
|
}
|
|
57
55
|
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* wdio-require-patch.js
|
|
3
|
-
* This script patches Node's require() so that any test file requiring
|
|
4
|
-
* '../B/froth_common_actions/Utils' automatically gets the correct global Util object.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const Module = require('module');
|
|
10
|
-
console.log('🔧 Applying wdio-require-patch.js...');
|
|
11
|
-
// --- 1️⃣ Locate the correct Utils.js dynamically ---
|
|
12
|
-
const possiblePaths = [
|
|
13
|
-
// Local project structure
|
|
14
|
-
path.resolve(__dirname, '../B/froth_common_actions/Utils.js'),
|
|
15
|
-
path.resolve(__dirname, '../node_modules/froth-webdriverio-framework/froth_common_actions/Utils.js'),
|
|
16
|
-
|
|
17
|
-
// Cloud structure (when framework is inside node_modules)
|
|
18
|
-
path.resolve(__dirname, './node_modules/froth-webdriverio-framework/froth_common_actions/Utils.js'),
|
|
19
|
-
|
|
20
|
-
// Fallback (when running inside another workspace)
|
|
21
|
-
path.resolve(process.cwd(), 'node_modules/froth-webdriverio-framework/froth_common_actions/Utils.js'),
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
const utilsPath = possiblePaths.find(p => fs.existsSync(p));
|
|
25
|
-
|
|
26
|
-
if (!utilsPath) {
|
|
27
|
-
console.warn('⚠️ Could not find Utils.js in expected paths. Global.Util not set.');
|
|
28
|
-
} else {
|
|
29
|
-
global.Util = require(utilsPath);
|
|
30
|
-
console.log(`✅ Global Util loaded from: ${utilsPath}`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// --- 2️⃣ Patch Node's require() so that any require of Utils returns global.Util ---
|
|
34
|
-
const originalRequire = Module.prototype.require;
|
|
35
|
-
|
|
36
|
-
Module.prototype.require = function (request) {
|
|
37
|
-
// Catch any variation of the Utils import
|
|
38
|
-
if (
|
|
39
|
-
request.includes('froth_common_actions/Utils') ||
|
|
40
|
-
request.endsWith('/Utils') ||
|
|
41
|
-
request.endsWith('/Utils.js')
|
|
42
|
-
) {
|
|
43
|
-
console.log(`⚡ Redirecting require('${request}') → global.Util`);
|
|
44
|
-
return global.Util;
|
|
45
|
-
}
|
|
46
|
-
return originalRequire.apply(this, arguments);
|
|
47
|
-
};
|