froth-webdriverio-framework 7.0.116 → 7.0.118-uat

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/CLAUDE.md ADDED
@@ -0,0 +1,215 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ **Version:** 9.0.5-ytlc
8
+
9
+ **Repository:** https://github.com/RoboticoDigitalProjects/froth-webdriverio.git
10
+
11
+ This is a WebdriverIO test automation framework (FROTH - "froth-webdriverio-framework") that supports web, Android, and iOS testing on both BrowserStack and local environments. The framework integrates with a Froth TestOps backend API for execution tracking, reporting, and test data management.
12
+
13
+ ## Commands
14
+
15
+ ### Running Tests
16
+
17
+ Tests are run using the WebdriverIO CLI with platform-specific configurations. Note: Configuration files (`android.conf.js`, `web.conf.js`, etc.) must be created at the root level to reference `froth_configs/wdio.common.conf.js`:
18
+
19
+ ```bash
20
+ # Android tests
21
+ npx wdio ./android.conf.js
22
+
23
+ # Web tests
24
+ npx wdio ./web.conf.js
25
+
26
+ # iOS tests
27
+ npx wdio ./ios.conf.js
28
+
29
+ # Lint code
30
+ npm run lint
31
+ ```
32
+
33
+ ### Creating Configuration Files
34
+
35
+ Platform-specific config files must be created at the root level. Example `android.conf.js`:
36
+
37
+ ```javascript
38
+ exports.config = require('./froth_configs/wdio.common.conf').config;
39
+ ```
40
+
41
+ ### Generating Allure Reports
42
+
43
+ Allure reports are generated manually using the Allure CLI:
44
+
45
+ ```bash
46
+ # Android reports
47
+ allure generate ./mobile/androidReports/allure-results --clean && allure open
48
+
49
+ # iOS reports
50
+ allure generate ./mobile/iosReports/allure-results --clean && allure open
51
+
52
+ # Web reports
53
+ allure generate ./webReports/allure-results --clean && allure open
54
+ ```
55
+
56
+ ### Running Tests
57
+
58
+ Tests are run by specifying environment variables and the config file:
59
+
60
+ ```bash
61
+ SUITE=./android_suites/samplesuite_x.js \
62
+ YML_NAME=./ymls/browserstack/android/android_pixel8.yml \
63
+ PLATFORM=browserstack \
64
+ EXECUTION_ID=123 \
65
+ API_TOKEN=your_token \
66
+ ORGANISATION_DOMAIN_URL=https://devapi.frothtestops.com \
67
+ npx wdio ./android.conf.js
68
+ ```
69
+
70
+ ## Architecture
71
+
72
+ ### Configuration Hierarchy
73
+
74
+ The framework uses a multi-layered configuration system:
75
+
76
+ 1. **Base Configuration** (`froth_configs/base.config.js`): Contains framework settings, specs path, timeouts, and Mocha options. Initializes a global `BUFFER` using `node-localstorage` for cross-module state sharing.
77
+
78
+ 2. **Common Configuration** (`froth_configs/wdio.common.conf.js`): Acts as the entry point. It:
79
+ - Loads capabilities from a YAML file specified by `YML_NAME`
80
+ - Decodes BrowserStack credentials from base64
81
+ - Determines platform (web vs mobile) based on `platformName` in capabilities
82
+ - Merges base config with platform-specific config
83
+
84
+ 3. **Platform-Specific Configs** (`froth_configs/browserstack/` and `froth_configs/local/`):
85
+ - `web.config.js`: BrowserStack web testing configuration
86
+ - `mobile.config.js`: BrowserStack mobile (Android/iOS) testing configuration
87
+ - Supports `BS_UPLOAD_MEDIA` for injecting media files into tests
88
+
89
+ ### Test Lifecycle and Hooks
90
+
91
+ All test lifecycle hooks are defined in `froth_configs/commonhook.js`:
92
+
93
+ - **onPrepare**: Initializes environment variables, execution details, suite details, and test data via `setallDatailinBuffer.js`. Registers global error handlers.
94
+ - **beforeSession**: Validates test syntax, configures BrowserStack capabilities, sets app path for mobile testing
95
+ - **beforeSuite**: Fetches BrowserStack session details, updates CICD run ID
96
+ - **beforeTest/afterTest**: Updates individual script execution status via API
97
+ - **afterSession**: Calculates total execution time, updates final execution status
98
+ - **onComplete**: Clears the BUFFER storage
99
+
100
+ ### Directory Structure
101
+
102
+ ```
103
+ froth_common_actions/ # Reusable test utilities and actions
104
+ ├── Utils.js # Central export point for all utilities
105
+ ├── scroll.js # Scrolling helpers (scrollToEnd, scrollDownToView, etc.)
106
+ ├── swipe.js # Gesture helpers (swipeUp, swipeDown, swipeWithCoordinates)
107
+ ├── click.js # Click helpers (clickIfVisible, doubleClick)
108
+ ├── assert.js # Assertion helpers (assertText, assertAttributeValue)
109
+ ├── random.js # Random data generators (RANDOMTEXT, RNDNUMBER, etc.)
110
+ ├── storeToBuffer.js # Buffer storage helpers for runtime data
111
+ ├── dbValidator.js # Database field validation
112
+ └── ... # Other common actions
113
+
114
+ froth_api_calls/ # API integration layer
115
+ ├── loginapi.js # Authentication token retrieval
116
+ ├── getexecutionDetails.js # Fetch/update execution details via TestOps API
117
+ ├── getsuiteDetails.js # Fetch test suite and script mappings
118
+ ├── readTestdata.js # Fetch test data by ID
119
+ └── browsersatckSessionInfo.js # BrowserStack session management
120
+
121
+ froth_configs/ # Configuration files
122
+ ├── base.config.js
123
+ ├── wdio.common.conf.js
124
+ ├── commonhook.js
125
+ ├── setallDatailinBuffer.js
126
+ ├── browserstack/ # BrowserStack-specific configs
127
+ └── local/ # Local execution configs
128
+
129
+ android/ # Android test scripts
130
+ android_suites/ # Android suite definitions (array of test files)
131
+ web/ # Web test scripts
132
+ web_suites/ # Web suite definitions
133
+ ymls/browserstack/ # Capability YAML files for different devices/browsers
134
+ ```
135
+
136
+ ### Test Data Flow
137
+
138
+ 1. Environment variables are loaded in `setallDatailinBuffer.js` during `onPrepare`
139
+ 2. Global state is stored in `BUFFER` (node-localstorage at `./buffer_storage`)
140
+ 3. Execution details are fetched from the Froth TestOps API
141
+ 4. Test scripts access data via `BUFFER.getItem()` / `BUFFER.setItem()`
142
+ 5. Results are posted back to the API via `updateScriptExecutionStatus` and `updateExecuitonDetails`
143
+
144
+ ### Test Suite Pattern
145
+
146
+ Suites are defined as simple modules exporting a `tests` array:
147
+
148
+ ```javascript
149
+ // Example: android_suites/samplesuite_x.js
150
+ module.exports = {
151
+ tests: [
152
+ '/absolute/path/to/android/test_script.js'
153
+ ]
154
+ };
155
+ ```
156
+
157
+ ### Common Actions Usage
158
+
159
+ Tests import utilities from the Utils export:
160
+
161
+ ```javascript
162
+ const Util = require('../froth_common_actions/Utils');
163
+
164
+ // Scrolling
165
+ await Util.scrollToEnd(2, 3);
166
+ await Util.scrollDownToView("Some Text");
167
+
168
+ // Swiping
169
+ await Util.swipeWithCoordinates(x1, y1, x2, y2);
170
+
171
+ // Random data
172
+ const randomText = Util.randomtext(10);
173
+ const randomNumber = Util.randomnumber(100, 999);
174
+
175
+ // Buffer storage
176
+ Util.storetext('key', 'value');
177
+ const value = BUFFER.getItem('key');
178
+ ```
179
+
180
+ ## Required Environment Variables
181
+
182
+ | Variable | Description | Example |
183
+ |----------|-------------|---------|
184
+ | `YML_NAME` | Path to capability YAML file | `./ymls/browserstack/android/android_pixel8.yml` |
185
+ | `SUITE` | Path to suite file | `./android_suites/samplesuite_x.js` |
186
+ | `PLATFORM` | `browserstack`, `browserstacklocal`, or omitted for local | `browserstack` |
187
+ | `EXECUTION_ID` | Test execution ID from TestOps | `123` |
188
+ | `API_TOKEN` | Authentication token for TestOps API | `eyJhbG...` |
189
+ | `ORGANISATION_DOMAIN_URL` | Froth TestOps API base URL | `https://devapi.frothtestops.com` |
190
+ | `CICD_RUN_ID` | CI/CD pipeline run ID (optional) | `build-456` |
191
+ | `BS_UPLOAD_MEDIA` | Comma-separated media URLs for BrowserStack (optional) | `url1,url2` |
192
+
193
+ ## Capability YAML Format
194
+
195
+ YAML files define device/browser capabilities. Example for Android:
196
+
197
+ ```yaml
198
+ userName: "bs_username"
199
+ accessKey: "base64_encoded_key"
200
+ platformName: "Android"
201
+ deviceName: "Samsung Galaxy S22 Ultra"
202
+ platformVersion: "12.0"
203
+ debug: true
204
+ networkLogs: true
205
+ interactiveDebugging: false
206
+ ```
207
+
208
+ ## Important Notes
209
+
210
+ - The framework uses `node-localstorage` to create a global `BUFFER` accessible across all modules
211
+ - All API responses from TestOps are AES-decrypted via `aesEncryptionDecryption.js`
212
+ - Test scripts map to TestOps via filename (script name must match `scriptName` in suite details)
213
+ - Mobile tests use Appium selectors like `id:com.example:id/elementId` and `-android uiautomator:...`
214
+ - Web tests use standard WebdriverIO selectors (`$`, `$$`)
215
+ - Framework supports both BrowserStack App Automate (mobile) and Automate (web) sessions
@@ -64,8 +64,10 @@ async function getBSSessionDetails(sessionType, bsUsername, bsPassword) {
64
64
 
65
65
  BUFFER.setItem("REPORT_URL", publicUrl);
66
66
  BUFFER.setItem("FROTH_TOTAL_DURATION", duration);
67
+ BUFFER.setItem("BS_BUILD_HASH_ID", hashId);
67
68
 
68
69
  console.log(`Public URL: ${publicUrl}`);
70
+ console.log(`Build Hash ID: ${hashId}`);
69
71
  } else if (response.status === 401) {
70
72
  console.error("Unauthorized: token expired to login Browserstack");
71
73
  } else {
@@ -160,11 +162,76 @@ async function escapeForBrowserStack(message) {
160
162
  .replace(/\r/g, "\\r");
161
163
  }
162
164
 
165
+ /* ===================== BROWSERSTACK REPORT API ===================== */
166
+
167
+ /**
168
+ * Updates BrowserStack report details in Froth TestOps database
169
+ * @param {string} frothUrl - Froth TestOps API base URL
170
+ * @param {string} token - Authentication token
171
+ * @param {string} bsBuildId - BrowserStack build ID (e.g., "frothtestops-coderepo_1286_20260211")
172
+ * @param {number} executionId - Test execution ID
173
+ * @param {string} status - BrowserStack status: "in_progress" (updates public URL only) or "completed"
174
+ */
175
+ async function updateBrowserStackReport(frothUrl, token, bsBuildId, executionId, status) {
176
+ if (!frothUrl || !token || !bsBuildId || !executionId || !status) {
177
+ console.error('❌ Missing required parameters for updateBrowserStackReport');
178
+ console.error(`frothUrl: ${frothUrl}, token: ${!!token}, bsBuildId: ${bsBuildId}, executionId: ${executionId}, status: ${status}`);
179
+ return null;
180
+ }
181
+
182
+ const validStatuses = ['in_progress', 'completed'];
183
+ if (!validStatuses.includes(status)) {
184
+ console.error(`❌ Invalid browserstack_status: ${status}. Must be one of: ${validStatuses.join(', ')}`);
185
+ return null;
186
+ }
187
+
188
+ const url = `${frothUrl}/api/download-browserstack-report/`;
189
+ console.log(`📤 Updating BrowserStack report [${status}]:`, url);
190
+
191
+ const payload = {
192
+ BS_BUILD_ID: bsBuildId,
193
+ execution_id: executionId,
194
+ browserstack_status: status
195
+ };
196
+
197
+ try {
198
+ const response = await fetch(url, {
199
+ method: 'POST',
200
+ headers: {
201
+ 'Authorization': `Bearer ${token}`,
202
+ 'Content-Type': 'application/json'
203
+ },
204
+ body: JSON.stringify(payload)
205
+ });
163
206
 
207
+ const contentType = response.headers.get('content-type') || '';
208
+ let data;
209
+ if (contentType.includes('application/json')) {
210
+ data = await response.json();
211
+ } else {
212
+ data = await response.text();
213
+ }
214
+
215
+ if (response.ok) {
216
+ console.log(`✅ BrowserStack report updated successfully [${status}]`);
217
+ return data;
218
+ } else if (response.status === 401) {
219
+ console.error('🔒 Unauthorized (401) - Token may be expired');
220
+ return null;
221
+ } else {
222
+ console.error(`❌ Failed to update BrowserStack report [${response.status}]:`, data);
223
+ return null;
224
+ }
164
225
 
226
+ } catch (error) {
227
+ console.error('❌ updateBrowserStackReport error:', error.message);
228
+ return null;
229
+ }
230
+ }
165
231
 
166
232
  module.exports = {
167
233
  getBSSessionDetails,
168
234
  getBSBuildDetails,
169
235
  amend2Browserstack,
236
+ updateBrowserStackReport,
170
237
  };
@@ -139,12 +139,12 @@ async function updateExecuitonDetails(frothUrl, token, id, resultdetails) {
139
139
  formData.append('excution_status', resultdetails.excution_status);
140
140
  }
141
141
 
142
- if (
143
- resultdetails.excution_time &&
144
- resultdetails.excution_time !== 'NaN:NaN:NaN'
145
- ) {
146
- formData.append('excution_time', resultdetails.excution_time);
147
- }
142
+ // if (
143
+ // resultdetails.excution_time &&
144
+ // resultdetails.excution_time !== 'NaN:NaN:NaN'
145
+ // ) {
146
+ // formData.append('excution_time', resultdetails.excution_time);
147
+ // }
148
148
 
149
149
  if (resultdetails.comments === null)
150
150
  console.log("Comments is null")
@@ -56,6 +56,7 @@ let amendBrowserStackLog = null;
56
56
  let logJsonDataToTable = null;
57
57
 
58
58
  let extractEmailLinks = null;
59
+ let comparator=null;
59
60
 
60
61
  if (process.env.LOCATION == null || process.env.LOCATION == 'local') {
61
62
  basepath = ".";
@@ -116,7 +117,7 @@ captureLoadNavigation = require(basepath + '/captureNavigationTime').captureLoad
116
117
 
117
118
  amendBrowserStackLog = require(basepath + '/../froth_api_calls/browsersatckSessionInfo').amend2Browserstack;
118
119
  logJsonDataToTable = require(basepath + '/logData').logJsonData2Table;
119
-
120
+ comparator=require(basepath +'/dbValidator').validateDbFields
120
121
  //extractEmailLinks = require(basepath + '/emailParsing');
121
122
  //export the variabels
122
123
  module.exports = {
@@ -163,5 +164,6 @@ module.exports = {
163
164
  captureLoadNavigation,
164
165
  amendBrowserStackLog,
165
166
  logJsonDataToTable,
166
- extractEmailLinks
167
+ extractEmailLinks,
168
+ comparator
167
169
  };
@@ -0,0 +1,219 @@
1
+ const amendBrowserStackLog = require('../froth_api_calls/browsersatckSessionInfo').amend2Browserstack;
2
+
3
+ /**
4
+ * Convert any timestamp string/number to milliseconds.
5
+ * Auto-detects multiple formats.
6
+ */
7
+ async function parseTimestamp(input) {
8
+ if (!input) return NaN;
9
+
10
+ // Case 1: Numeric → Unix timestamp (ms or sec)
11
+ if (!isNaN(input)) {
12
+ const num = Number(input);
13
+
14
+ // If it's in seconds (10 digits), convert to ms
15
+ if (num.toString().length === 10) return num * 1000;
16
+
17
+ // Already ms (13 digits)
18
+ return num;
19
+ }
20
+
21
+ // Case 2: Direct Date() parseable ISO formats
22
+ let parsed = Date.parse(input);
23
+ if (!isNaN(parsed)) return parsed;
24
+
25
+ // Case 3: Common non-ISO formats → manual normalization
26
+ const replacements = [
27
+ { pattern: /\//g, replace: '-' }, // 2025/01/10 -> 2025-01-10
28
+ { pattern: / /g, replace: 'T' } // 2025-01-10 12:30 -> 2025-01-10T12:30
29
+ ];
30
+
31
+ let normalized = input;
32
+ replacements.forEach(r => {
33
+ normalized = normalized.replace(r.pattern, r.replace);
34
+ });
35
+
36
+ parsed = Date.parse(normalized);
37
+ if (!isNaN(parsed)) return parsed;
38
+
39
+ // Case 4: Try reversing DD-MM-YYYY → YYYY-MM-DD
40
+ const dmY = /^(\d{2})-(\d{2})-(\d{4})(.*)$/;
41
+ if (dmY.test(input)) {
42
+ const parts = input.match(dmY);
43
+ normalized = `${parts[3]}-${parts[2]}-${parts[1]}${parts[4]}`;
44
+ parsed = Date.parse(normalized);
45
+ if (!isNaN(parsed)) return parsed;
46
+ }
47
+
48
+ return NaN;
49
+ }
50
+
51
+ /**
52
+ * Main DB validation utility
53
+ */
54
+ async function validateDbFields(actualData, expectedData, context = 'DB Validation') {
55
+ await amendBrowserStackLog(`🔍 START DB VALIDATION: ${context}`, 'info');
56
+
57
+ const results = [];
58
+ let allPassed = true;
59
+
60
+ for (const key of Object.keys(expectedData)) {
61
+
62
+ const expectedConfig = expectedData[key];
63
+ const actual = actualData[key];
64
+ let expected = expectedConfig;
65
+ let status = "PASS";
66
+
67
+ // -----------------------------
68
+ // SKIP DYNAMIC FIELDS
69
+ // -----------------------------
70
+ if (expectedConfig === "*dynamic*") {
71
+ results.push({
72
+ key, actual, expected: "(SKIPPED - dynamic)", status: "SKIPPED"
73
+ });
74
+ continue;
75
+ }
76
+
77
+ // -----------------------------
78
+ // NULLABLE VALIDATION (allow null/empty)
79
+ // -----------------------------
80
+ if (typeof expectedConfig === "object" && expectedConfig.type === "nullable") {
81
+ const isNullOrEmpty = actual === null || actual === undefined || actual === "" || actual === "null";
82
+ status = isNullOrEmpty ? "PASS" : "PASS"; // Always pass - just validates field exists
83
+ results.push({
84
+ key,
85
+ actual,
86
+ expected: "(nullable - any value allowed)",
87
+ status
88
+ });
89
+ continue;
90
+ }
91
+
92
+ // -----------------------------
93
+ // NUMBER WITH TOLERANCE
94
+ // -----------------------------
95
+ if (typeof expectedConfig === "object" && expectedConfig.type === "number") {
96
+ expected = expectedConfig.value;
97
+ const tolerance = expectedConfig.tolerance || 0;
98
+
99
+ const actualNum = parseFloat(actual);
100
+ const expectedNum = parseFloat(expected);
101
+
102
+ if (isNaN(actualNum) || isNaN(expectedNum)) {
103
+ status = "FAIL";
104
+ } else {
105
+ const diff = Math.abs(actualNum - expectedNum);
106
+ status = diff <= tolerance ? "PASS" : "FAIL";
107
+ }
108
+
109
+ results.push({
110
+ key,
111
+ actual,
112
+ expected: `${expected} (±${tolerance})`,
113
+ status
114
+ });
115
+
116
+ if (status === "FAIL") allPassed = false;
117
+ continue;
118
+ }
119
+
120
+ // -----------------------------
121
+ // PARTIAL STRING MATCH
122
+ // -----------------------------
123
+ if (typeof expectedConfig === "object" && expectedConfig.type === "partial") {
124
+ const substring = expectedConfig.contains;
125
+ const actualStr = String(actual || "");
126
+
127
+ status = actualStr.includes(substring) ? "PASS" : "FAIL";
128
+
129
+ results.push({
130
+ key,
131
+ actual,
132
+ expected: `(contains "${substring}")`,
133
+ status
134
+ });
135
+
136
+ if (status === "FAIL") allPassed = false;
137
+ continue;
138
+ }
139
+
140
+ // -----------------------------
141
+ // TIMESTAMP VALIDATION
142
+ // -----------------------------
143
+ if (typeof expectedConfig === "object" && expectedConfig.type === "timestamp") {
144
+ expected = expectedConfig.value;
145
+ const tolerance = expectedConfig.toleranceMs || 0;
146
+
147
+ let actualMs = await parseTimestamp(actual);
148
+ let expectedMs = await parseTimestamp(expected);
149
+
150
+ if (isNaN(actualMs) || isNaN(expectedMs)) {
151
+ status = "FAIL";
152
+ results.push({
153
+ key,
154
+ actual,
155
+ expected,
156
+ status: "FAIL - Unrecognized Timestamp Format"
157
+ });
158
+ allPassed = false;
159
+ continue;
160
+ }
161
+
162
+ const diff = Math.abs(actualMs - expectedMs);
163
+ if (diff > tolerance) status = "FAIL";
164
+
165
+ results.push({
166
+ key,
167
+ actual,
168
+ expected: `${expected} (±${tolerance}ms)`,
169
+ status
170
+ });
171
+
172
+ if (status === "FAIL") allPassed = false;
173
+ continue;
174
+ }
175
+
176
+ // -----------------------------
177
+ // NORMAL STRING/NUMBER COMPARISON
178
+ // -----------------------------
179
+ if (String(actual) !== String(expected)) {
180
+ status = "FAIL";
181
+ allPassed = false;
182
+ }
183
+
184
+ results.push({ key, actual, expected, status });
185
+ }
186
+
187
+ // -----------------------------
188
+ // BROWSERSTACK LOGGING
189
+ // -----------------------------
190
+ // const chunkSize = 800;
191
+ const combined = results
192
+ .map(r => `${r.key} => Expected: ${r.expected}, Actual: ${r.actual}, Status: ${r.status}`)
193
+ .join(" | ");
194
+
195
+ // const chunks = combined.match(new RegExp(`.{1,${chunkSize}}`, "g")) || [];
196
+ // for (const c of chunks) await amendBrowserStackLog(c, "info");
197
+
198
+ await amendBrowserStackLog(combined, "info");
199
+ // -----------------------------
200
+ // FINAL RESULT
201
+ // -----------------------------
202
+ await amendBrowserStackLog(
203
+ allPassed ? "✅ DB VALIDATION PASSED" : "❌ DB VALIDATION FAILED",
204
+ allPassed ? "info" : "error"
205
+ );
206
+
207
+ return allPassed;
208
+ }
209
+
210
+ module.exports = {
211
+ parseTimestamp,
212
+ validateDbFields
213
+ };
214
+
215
+ // async function main(){
216
+
217
+ // }
218
+
219
+ // main()
@@ -1,7 +1,7 @@
1
1
 
2
2
  const setAllDetails = require('./setallDatailinBuffer');
3
3
  const exeDetails = require('../froth_api_calls/getexecutionDetails');
4
- const { getBSSessionDetails } = require('../froth_api_calls/browsersatckSessionInfo');
4
+ const { getBSSessionDetails, updateBrowserStackReport } = require('../froth_api_calls/browsersatckSessionInfo');
5
5
  let globalErrorHandled = false;
6
6
  let suiteStartTime = 0;
7
7
  let totalTestDuration = 0;
@@ -187,11 +187,23 @@ const commonHooks = {
187
187
  suiteStartTime = Date.now();
188
188
 
189
189
  if (process.env.PLATFORM === 'browserstack' || process.env.PLATFORM === 'browserstacklocal') {
190
- await getBSSessionDetails(
191
- process.env.BS_SESSION_TYPE,
192
- process.env.BROWSERSTACK_USERNAME,
193
- process.env.BROWSERSTACK_ACCESS_KEY
194
- );
190
+ // await getBSSessionDetails(
191
+ // process.env.BS_SESSION_TYPE,
192
+ // process.env.BROWSERSTACK_USERNAME,
193
+ // process.env.BROWSERSTACK_ACCESS_KEY
194
+ // );
195
+
196
+ // Update BrowserStack report with in_progress status
197
+ const buildName = process.env.FROTH_TESTOPS_BUILD_NAME;
198
+ if (buildName) {
199
+ await updateBrowserStackReport(
200
+ BUFFER.getItem('ORGANISATION_DOMAIN_URL'),
201
+ BUFFER.getItem('FROTH_LOGIN_TOKEN'),
202
+ buildName,
203
+ BUFFER.getItem('FROTH_EXECUTION_ID'),
204
+ 'in_progress'
205
+ );
206
+ }
195
207
  }
196
208
 
197
209
  await exeDetails.update_CICDRUNID_ReportUrl(
@@ -287,6 +299,20 @@ const commonHooks = {
287
299
  console.log('⏱ Final execution time (hh:mm:ss):', resultdetails.excution_time);
288
300
  await safeUpdateExecution();
289
301
 
302
+ // Update BrowserStack report with completed status
303
+ if (process.env.PLATFORM === 'browserstack' || process.env.PLATFORM === 'browserstacklocal') {
304
+ const bsBuildHashId = BUFFER.getItem('BS_BUILD_HASH_ID');
305
+ if (bsBuildHashId) {
306
+ await updateBrowserStackReport(
307
+ BUFFER.getItem('ORGANISATION_DOMAIN_URL'),
308
+ BUFFER.getItem('FROTH_LOGIN_TOKEN'),
309
+ bsBuildHashId,
310
+ BUFFER.getItem('FROTH_EXECUTION_ID'),
311
+ 'completed'
312
+ );
313
+ }
314
+ }
315
+
290
316
 
291
317
  },
292
318
 
File without changes
package/package.json CHANGED
@@ -1,21 +1,11 @@
1
1
  {
2
2
  "name": "froth-webdriverio-framework",
3
- "version": "7.0.116",
3
+ "version": "7.0.118-uat",
4
4
  "readme": "WebdriverIO Integration",
5
5
  "description": "WebdriverIO and BrowserStack App Automate",
6
6
  "license": "MIT",
7
- "engines": {
8
- "node": "18"
9
- },
7
+
10
8
  "scripts": {
11
- "wdio:ios": "npx wdio ./ios.conf.js",
12
- "wdio:android": "npx wdio ./android.conf.js",
13
- "wdio:web": "npx wdio ./web.conf.js",
14
- "wdio:webbs": "npx wdio ./web.conf.bs.js",
15
- "wdio:ios:local": "npx wdio ./ios.local.conf.js",
16
- "android:generate-allure-report": "allure generate ./mobile/androidReports/allure-results --clean && allure open",
17
- "ios:generate-allure-report": "allure generate ./mobile/iosReports/allure-results --clean && allure open",
18
- "web:generate-allure-report": "allure generate ./webReports/allure-results --clean && allure open",
19
9
  "lint": "eslint ."
20
10
  },
21
11
  "repository": {