froth-webdriverio-framework 6.0.0 → 6.0.2

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.
@@ -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
- if (process.env.CICD_RUN_ID === null)
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 updateReportUrl(frothUrl, token, id) {
259
- try {
260
- console.log("in update execution details UPDATE_EXECUTION: " + BUFFER.getItem("UPDATE_EXECUTION"))
261
- if (BUFFER.getItem("FROTH_UPDATE_EXECUTION") === 'TRUE') {
262
- console.log("Execution already updated.")
263
- }
264
- else {
265
- const url = `${frothUrl}/api/test-execution-update/${id}/`;
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
- BUFFER.setItem("FROTH_UPDATE_EXECUTION", 'TRUE')
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, updateReportUrl};
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
- //import assert from 'assert';
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
+
@@ -1,6 +1,5 @@
1
1
  const amendToBrowserstack = require("../froth_api_calls/browsersatckSessionInfo").amend2Browserstack;
2
2
 
3
-
4
3
  async function logJsonData2Table(jsonData) {
5
4
  try {
6
5
  console.log("Logging JSON data to table format in BrowserStack:");
@@ -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
- //const getBSSessionDetails = require("../froth_api_calls/browsersatckSessionInfo").getBSSessionDetails;
8
+ const getBSSessionDetails = require("../froth_api_calls/browsersatckSessionInfo").getBSSessionDetails;
9
9
  const { fail } = require("assert");
10
10
  const { error } = require('console');
11
- const HTMLReportGenerator = require('wdio-json-html-reporter').HTMLReportGenerator;
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,10 +80,36 @@ 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
- capabilities.browserstackLocal = process.env.BROWSERSTACK_LOCAL;
83
+ console.log("====> BROWSERSTACK_LOCAL : "+process.env.BROWSERSTACK_LOCAL);
84
+
85
+ if (process.env.BROWSERSTACK_LOCAL) {
86
+ bsLocal = new browserstack.Local();
87
+ const localArgs = {
88
+ key: process.env.BROWSERSTACK_ACCESS_KEY,
89
+ forceLocal: true,
90
+ localIdentifier: capabilities.localIdentifier || 'wdioLocal'
91
+ };
92
+
93
+ await new Promise((resolve, reject) => {
94
+ bsLocal.start(localArgs, function (error) {
95
+ if (error) return reject(error);
96
+ console.log("====> BrowserStack Local started successfully.");
97
+ resolve();
98
+ });
99
+ });
100
+
101
+ // Add Local settings into capabilities dynamically
102
+ capabilities['browserstack.local'] = true;
103
+ capabilities['browserstack.localIdentifier'] = localArgs.localIdentifier || 'ExecuteLocal';
104
+ }
105
+ else
106
+ console.log("====> BROWSERSTACK_LOCAL not set. Skipping BrowserStack Local startup."+process.env.BROWSERSTACK_LOCAL);
107
+
87
108
  // capabilities.accessKey = Buffer.from(capabilities.accessKey, 'base64').toString('utf-8');
88
109
  if (capabilities.platformName === 'android' || capabilities.platformName === 'ios') {
89
- capabilities.app = process.env.BROWSERSTACK_APP_PATH;
110
+ capabilities['appium:app'] = process.env.BROWSERSTACK_APP_PATH;
111
+
112
+ // capabilities.app = process.env.BROWSERSTACK_APP_PATH;
90
113
  if (capabilities.app === undefined || capabilities.app === null || capabilities.app === '') {
91
114
  console.error("🚨 Error: BROWSERSTACK_APP_PATH is not defined or empty.");
92
115
  resultdetails.excution_status = 'FAILED';
@@ -140,13 +163,7 @@ const commonconfig = {
140
163
 
141
164
  if (process.env.PLATFORM === 'browserstack')
142
165
  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
166
 
149
- }
150
167
 
151
168
  // const resultdetails = {};
152
169
  await exeDetails.update_CICDRUNID_ReportUrl(BUFFER.getItem("ORGANISATION_DOMAIN_URL"), BUFFER.getItem("FROTH_LOGIN_TOKEN"), BUFFER.getItem("FROTH_EXECUTION_ID"))
@@ -166,7 +183,6 @@ const commonconfig = {
166
183
  console.log('==== BEFORE HOOK FOR MOBILE ====');
167
184
  }
168
185
 
169
-
170
186
  },
171
187
 
172
188
  /**
@@ -337,6 +353,15 @@ const commonconfig = {
337
353
 
338
354
  await exeDetails.updateExecuitonDetails(BUFFER.getItem("ORGANISATION_DOMAIN_URL"), BUFFER.getItem("FROTH_LOGIN_TOKEN"), BUFFER.getItem("FROTH_EXECUTION_ID"), resultdetails)
339
355
 
356
+ if (bsLocal && bsLocal.isRunning()) {
357
+ console.log("====> Stopping BrowserStack Local...");
358
+ await new Promise((resolve) => {
359
+ bsLocal.stop(() => {
360
+ console.log("====> BrowserStack Local stopped.");
361
+ resolve();
362
+ });
363
+ });
364
+ }
340
365
  // Perform any cleanup or post-test actions here
341
366
  BUFFER.clear();
342
367
  },
@@ -351,52 +376,7 @@ const commonconfig = {
351
376
  console.log('Exit Code:', exitCode);
352
377
 
353
378
  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;
379
+ return exitCode;
400
380
  }
401
381
 
402
382
  };
@@ -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
- const timestamp = new Date().toISOString().replace(/[-:.TZ]/g, '').slice(0, 14); // e.g., 20251014_121532
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
- } else {
101
- console.log("inside this fuction for local setup ");
102
- console.log('capabilities.platformName:', capabilities.platformName ?? 'web');
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
- const specificConfig = setupPrerequisites();
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
- // user: process.env.BROWSERSTACK_USERNAME,
121
- // key: process.env.BROWSERSTACK_ACCESS_KEY,
167
+ user: process.env.BROWSERSTACK_USERNAME,
168
+ key: process.env.BROWSERSTACK_ACCESS_KEY,
122
169
  // debug: true,
123
170
  // execArgv: ['--inspect-brk'],
124
- // services: PLATFORM === 'browserstack'
125
- // ? ['browserstack']
126
- // : PLATFORM === 'saucelabs'
127
- // ? ['sauce']
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: [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": "6.0.0",
3
+ "version": "6.0.2",
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
- };