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 +215 -0
- package/froth_api_calls/browsersatckSessionInfo.js +67 -0
- package/froth_api_calls/getexecutionDetails.js +6 -6
- package/froth_common_actions/Utils.js +4 -2
- package/froth_common_actions/dbValidator.js +219 -0
- package/froth_configs/commonhook.js +32 -6
- package/log/key-metrics.json.lock +0 -0
- package/package.json +2 -12
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
|
-
|
|
144
|
-
|
|
145
|
-
) {
|
|
146
|
-
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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.
|
|
3
|
+
"version": "7.0.118-uat",
|
|
4
4
|
"readme": "WebdriverIO Integration",
|
|
5
5
|
"description": "WebdriverIO and BrowserStack App Automate",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
|
|
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": {
|