@ash-mallick/browserstack-sync 1.3.1 → 1.4.0
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/README.md +55 -12
- package/bin/sync-runs.js +119 -54
- package/lib/sync-results.js +145 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,29 +60,70 @@ Creates folders and test cases for all spec files in your project.
|
|
|
60
60
|
|
|
61
61
|
### 4. Sync Test Run Results to BrowserStack
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
Specify your report file location:
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
|
-
npx
|
|
66
|
+
npx am-browserstack-sync-runs --report=test-results.json
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
**Supported formats (auto-detected):**
|
|
70
|
+
- Playwright JSON
|
|
71
|
+
- Cypress/Mochawesome JSON
|
|
72
|
+
- JUnit XML
|
|
73
|
+
|
|
74
|
+
**Configure report path** (so you don't need `--report` every time):
|
|
70
75
|
|
|
71
76
|
```bash
|
|
72
|
-
|
|
77
|
+
# Via environment variable
|
|
78
|
+
export BROWSERSTACK_REPORT_PATH=test-results.json
|
|
79
|
+
|
|
80
|
+
# Or in .am-browserstack-sync.json
|
|
81
|
+
{ "reportPath": "test-results.json" }
|
|
73
82
|
```
|
|
74
83
|
|
|
75
|
-
|
|
84
|
+
**Generate reports** (add to your test command):
|
|
76
85
|
|
|
77
86
|
```bash
|
|
78
|
-
|
|
87
|
+
# Playwright
|
|
88
|
+
npx playwright test --reporter=json --output-file=test-results.json
|
|
89
|
+
|
|
90
|
+
# Cypress
|
|
91
|
+
npx cypress run --reporter mochawesome
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**With custom run name:**
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npx am-browserstack-sync-runs --report=test-results.json --run-name="Nightly Run"
|
|
79
98
|
```
|
|
80
99
|
|
|
81
100
|
---
|
|
82
101
|
|
|
83
|
-
## GitLab CI
|
|
102
|
+
## GitLab CI Integration
|
|
84
103
|
|
|
85
|
-
|
|
104
|
+
### Option A: Your pipeline already generates reports
|
|
105
|
+
|
|
106
|
+
If your existing test job generates JSON/JUnit reports, just add sync after:
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
# Your existing test job
|
|
110
|
+
test:
|
|
111
|
+
script:
|
|
112
|
+
- npm ci
|
|
113
|
+
- CENV=prod npx playwright test --reporter=allure-playwright,json --output-file=test-results.json
|
|
114
|
+
artifacts:
|
|
115
|
+
paths:
|
|
116
|
+
- test-results.json
|
|
117
|
+
|
|
118
|
+
# Add this job to sync results to BrowserStack
|
|
119
|
+
sync_browserstack:
|
|
120
|
+
needs: [test]
|
|
121
|
+
script:
|
|
122
|
+
- npm install @ash-mallick/browserstack-sync
|
|
123
|
+
- npx am-browserstack-sync-runs --report=test-results.json --run-name="Pipeline #$CI_PIPELINE_ID"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Option B: Scheduled sync job (runs tests + syncs)
|
|
86
127
|
|
|
87
128
|
```yaml
|
|
88
129
|
scheduled_browserstack_sync:
|
|
@@ -90,12 +131,14 @@ scheduled_browserstack_sync:
|
|
|
90
131
|
- if: $CI_PIPELINE_SOURCE == "schedule"
|
|
91
132
|
script:
|
|
92
133
|
- npm ci
|
|
93
|
-
- npx playwright test --reporter=
|
|
134
|
+
- CENV=prod npx playwright test --reporter=allure-playwright,json --output-file=test-results.json || true
|
|
94
135
|
- npm install @ash-mallick/browserstack-sync
|
|
95
|
-
- npx am-browserstack-sync-runs
|
|
136
|
+
- npx am-browserstack-sync-runs --report=test-results.json
|
|
96
137
|
```
|
|
97
138
|
|
|
98
|
-
|
|
139
|
+
### Required CI/CD Variables
|
|
140
|
+
|
|
141
|
+
Add in **Settings → CI/CD → Variables**:
|
|
99
142
|
|
|
100
143
|
| Key | Value |
|
|
101
144
|
|-----|-------|
|
|
@@ -103,7 +146,7 @@ Add CI/CD Variables in GitLab (**Settings → CI/CD → Variables**):
|
|
|
103
146
|
| `BROWSERSTACK_ACCESS_KEY` | your_access_key |
|
|
104
147
|
| `BROWSERSTACK_PROJECT_ID` | PR-XXXX |
|
|
105
148
|
|
|
106
|
-
|
|
149
|
+
For scheduled jobs: **CI/CD → Schedules → New Schedule**.
|
|
107
150
|
|
|
108
151
|
---
|
|
109
152
|
|
package/bin/sync-runs.js
CHANGED
|
@@ -34,79 +34,108 @@ if (argv.includes('--help') || argv.includes('-h')) {
|
|
|
34
34
|
📊 BrowserStack Test Run Sync
|
|
35
35
|
|
|
36
36
|
Sync your Playwright/Cypress test results to BrowserStack Test Management.
|
|
37
|
-
|
|
37
|
+
Specify your report file location - supports JSON and XML formats.
|
|
38
38
|
|
|
39
39
|
USAGE:
|
|
40
|
-
npx am-browserstack-sync-runs [options]
|
|
40
|
+
npx am-browserstack-sync-runs --report=<path> [options]
|
|
41
41
|
|
|
42
42
|
OPTIONS:
|
|
43
|
-
--report=<path> Path to report file (
|
|
43
|
+
--report=<path> Path to your test report file (required unless configured)
|
|
44
44
|
--run-name=<name> Name for the test run (default: "CI Run - <timestamp>")
|
|
45
|
-
--format=<fmt> Report format: playwright-json, junit (
|
|
45
|
+
--format=<fmt> Report format: playwright-json, mochawesome, cypress, junit (auto-detected)
|
|
46
46
|
--help, -h Show this help
|
|
47
47
|
|
|
48
|
+
CONFIGURE REPORT PATH (optional - so you don't need --report every time):
|
|
49
|
+
Environment variable: BROWSERSTACK_REPORT_PATH=test-results.json
|
|
50
|
+
Config file: .am-browserstack-sync.json { "reportPath": "test-results.json" }
|
|
51
|
+
package.json: "amBrowserstackSync": { "reportPath": "test-results.json" }
|
|
52
|
+
|
|
48
53
|
ENVIRONMENT VARIABLES (required):
|
|
49
54
|
BROWSERSTACK_USERNAME Your BrowserStack username
|
|
50
55
|
BROWSERSTACK_ACCESS_KEY Your BrowserStack access key
|
|
51
56
|
BROWSERSTACK_PROJECT_ID Your project ID (e.g., PR-1234)
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
npx
|
|
58
|
+
SUPPORTED REPORT FORMATS:
|
|
59
|
+
- Playwright JSON: npx playwright test --reporter=json --output-file=test-results.json
|
|
60
|
+
- Cypress Mochawesome: npx cypress run --reporter mochawesome
|
|
61
|
+
- JUnit XML: npx playwright test --reporter=junit --output-file=junit.xml
|
|
56
62
|
|
|
63
|
+
EXAMPLES:
|
|
57
64
|
# Specify report file
|
|
58
65
|
npx am-browserstack-sync-runs --report=test-results.json
|
|
59
66
|
|
|
60
|
-
#
|
|
61
|
-
npx am-browserstack-sync-runs --run-name="Build
|
|
67
|
+
# With custom run name (great for CI)
|
|
68
|
+
npx am-browserstack-sync-runs --report=test-results.json --run-name="Build #123"
|
|
62
69
|
|
|
63
70
|
# Sync JUnit XML results
|
|
64
|
-
npx am-browserstack-sync-runs --report=junit.xml
|
|
71
|
+
npx am-browserstack-sync-runs --report=junit.xml
|
|
65
72
|
|
|
66
|
-
|
|
73
|
+
CI PIPELINE EXAMPLE:
|
|
67
74
|
# GitLab CI
|
|
68
75
|
script:
|
|
69
|
-
- npm
|
|
70
|
-
- npx
|
|
71
|
-
|
|
72
|
-
# GitHub Actions
|
|
73
|
-
- run: npm test
|
|
74
|
-
- run: npx am-browserstack-sync-runs --run-name="Build #\${{ github.run_number }}"
|
|
75
|
-
|
|
76
|
-
PREREQUISITES:
|
|
77
|
-
1. First, onboard your tests to BrowserStack:
|
|
78
|
-
npx am-browserstack-sync --all
|
|
79
|
-
|
|
80
|
-
2. Then add run sync to your pipeline:
|
|
81
|
-
npx am-browserstack-sync-runs
|
|
76
|
+
- npm ci
|
|
77
|
+
- npx playwright test --reporter=json --output-file=test-results.json || true
|
|
78
|
+
- npx am-browserstack-sync-runs --report=test-results.json --run-name="Pipeline #\$CI_PIPELINE_ID"
|
|
82
79
|
`);
|
|
83
80
|
process.exit(0);
|
|
84
81
|
}
|
|
85
82
|
|
|
86
|
-
//
|
|
87
|
-
function
|
|
83
|
+
// Check if a report file exists at the given path
|
|
84
|
+
function checkReportExists(reportPath) {
|
|
85
|
+
if (!reportPath) return null;
|
|
86
|
+
|
|
87
|
+
const cwd = process.cwd();
|
|
88
|
+
const fullPath = path.isAbsolute(reportPath) ? reportPath : path.join(cwd, reportPath);
|
|
89
|
+
|
|
90
|
+
if (fs.existsSync(fullPath)) {
|
|
91
|
+
const stat = fs.statSync(fullPath);
|
|
92
|
+
const ageMinutes = Math.round((Date.now() - stat.mtime.getTime()) / 60000);
|
|
93
|
+
console.log(`📄 Using report: ${reportPath}`);
|
|
94
|
+
console.log(` Modified: ${ageMinutes < 60 ? ageMinutes + ' minutes ago' : Math.round(ageMinutes / 60) + ' hours ago'}`);
|
|
95
|
+
return fullPath;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Get report path from config or environment
|
|
102
|
+
function getConfiguredReportPath() {
|
|
103
|
+
// Check environment variable first
|
|
104
|
+
if (process.env.BROWSERSTACK_REPORT_PATH) {
|
|
105
|
+
return process.env.BROWSERSTACK_REPORT_PATH;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check config file
|
|
88
109
|
const cwd = process.cwd();
|
|
89
|
-
const
|
|
90
|
-
'
|
|
91
|
-
'
|
|
92
|
-
'playwright-report/results.json',
|
|
93
|
-
'playwright-report.json',
|
|
94
|
-
'report.json',
|
|
95
|
-
'junit.xml',
|
|
96
|
-
'test-results.xml',
|
|
110
|
+
const configPaths = [
|
|
111
|
+
path.join(cwd, '.am-browserstack-sync.json'),
|
|
112
|
+
path.join(cwd, 'browserstack-sync.json'),
|
|
97
113
|
];
|
|
98
114
|
|
|
99
|
-
for (const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
115
|
+
for (const configPath of configPaths) {
|
|
116
|
+
if (fs.existsSync(configPath)) {
|
|
117
|
+
try {
|
|
118
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
119
|
+
if (config.reportPath) {
|
|
120
|
+
return config.reportPath;
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
// Ignore parse errors
|
|
124
|
+
}
|
|
103
125
|
}
|
|
104
126
|
}
|
|
105
127
|
|
|
106
|
-
// Check
|
|
107
|
-
const
|
|
108
|
-
if (fs.existsSync(
|
|
109
|
-
|
|
128
|
+
// Check package.json
|
|
129
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
130
|
+
if (fs.existsSync(pkgPath)) {
|
|
131
|
+
try {
|
|
132
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
133
|
+
if (pkg.amBrowserstackSync?.reportPath) {
|
|
134
|
+
return pkg.amBrowserstackSync.reportPath;
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
// Ignore parse errors
|
|
138
|
+
}
|
|
110
139
|
}
|
|
111
140
|
|
|
112
141
|
return null;
|
|
@@ -131,24 +160,60 @@ const runName = runNameArg
|
|
|
131
160
|
? runNameArg.slice('--run-name='.length).trim()
|
|
132
161
|
: process.env.BROWSERSTACK_RUN_NAME || `CI Run - ${new Date().toISOString().slice(0, 16).replace('T', ' ')}`;
|
|
133
162
|
|
|
134
|
-
//
|
|
163
|
+
// Determine report path: CLI arg > env var > config file
|
|
135
164
|
if (!reportPath) {
|
|
136
|
-
reportPath =
|
|
137
|
-
|
|
138
|
-
console.error(`
|
|
139
|
-
❌ No test report found!
|
|
140
|
-
|
|
141
|
-
Make sure your test framework generates a report file:
|
|
165
|
+
reportPath = getConfiguredReportPath();
|
|
166
|
+
}
|
|
142
167
|
|
|
143
|
-
|
|
144
|
-
|
|
168
|
+
// Check if report exists
|
|
169
|
+
if (reportPath) {
|
|
170
|
+
const resolvedPath = checkReportExists(reportPath);
|
|
171
|
+
if (!resolvedPath) {
|
|
172
|
+
console.error(`
|
|
173
|
+
❌ Report file not found: ${reportPath}
|
|
145
174
|
|
|
146
|
-
|
|
147
|
-
npx am-browserstack-sync-runs --report=path/to/results.json
|
|
175
|
+
Make sure your test run generates this report before syncing.
|
|
148
176
|
`);
|
|
149
177
|
process.exit(1);
|
|
150
178
|
}
|
|
151
|
-
|
|
179
|
+
reportPath = resolvedPath;
|
|
180
|
+
} else {
|
|
181
|
+
console.error(`
|
|
182
|
+
❌ No report path configured!
|
|
183
|
+
|
|
184
|
+
Please specify your test report location using one of these methods:
|
|
185
|
+
|
|
186
|
+
OPTION 1: Command line argument
|
|
187
|
+
npx am-browserstack-sync-runs --report=test-results.json
|
|
188
|
+
|
|
189
|
+
OPTION 2: Environment variable
|
|
190
|
+
export BROWSERSTACK_REPORT_PATH=test-results.json
|
|
191
|
+
npx am-browserstack-sync-runs
|
|
192
|
+
|
|
193
|
+
OPTION 3: Config file (.am-browserstack-sync.json)
|
|
194
|
+
{
|
|
195
|
+
"reportPath": "test-results.json"
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
OPTION 4: package.json
|
|
199
|
+
{
|
|
200
|
+
"amBrowserstackSync": {
|
|
201
|
+
"reportPath": "test-results.json"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
GENERATING REPORTS:
|
|
206
|
+
|
|
207
|
+
Playwright:
|
|
208
|
+
npx playwright test --reporter=json --output-file=test-results.json
|
|
209
|
+
|
|
210
|
+
Cypress (mochawesome):
|
|
211
|
+
npx cypress run --reporter mochawesome
|
|
212
|
+
|
|
213
|
+
JUnit XML (both):
|
|
214
|
+
npx playwright test --reporter=junit --output-file=junit.xml
|
|
215
|
+
`);
|
|
216
|
+
process.exit(1);
|
|
152
217
|
}
|
|
153
218
|
|
|
154
219
|
// Auto-detect format if not specified
|
package/lib/sync-results.js
CHANGED
|
@@ -108,6 +108,115 @@ function parseJunitXml(reportPath) {
|
|
|
108
108
|
return results;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Parse Mochawesome JSON report format (commonly used with Cypress).
|
|
113
|
+
* @param {string} reportPath - Path to the mochawesome JSON file
|
|
114
|
+
* @returns {Array<{title: string, status: string, duration: number, error?: string}>}
|
|
115
|
+
*/
|
|
116
|
+
function parseMochawesome(reportPath) {
|
|
117
|
+
const report = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));
|
|
118
|
+
const results = [];
|
|
119
|
+
|
|
120
|
+
// Mochawesome structure: { results: [{ suites: [{ tests: [...] }] }] }
|
|
121
|
+
function extractTests(suites) {
|
|
122
|
+
for (const suite of suites || []) {
|
|
123
|
+
for (const test of suite.tests || []) {
|
|
124
|
+
results.push({
|
|
125
|
+
title: test.title,
|
|
126
|
+
fullTitle: test.fullTitle || `${suite.title} > ${test.title}`,
|
|
127
|
+
status: mapMochawesomeStatus(test.state || test.pass),
|
|
128
|
+
duration: test.duration || 0,
|
|
129
|
+
error: test.err?.message || test.err?.estack || '',
|
|
130
|
+
file: suite.file,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Recursively process nested suites
|
|
135
|
+
if (suite.suites) {
|
|
136
|
+
extractTests(suite.suites);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Handle both mochawesome and mochawesome-merge formats
|
|
142
|
+
if (report.results) {
|
|
143
|
+
for (const result of report.results) {
|
|
144
|
+
extractTests(result.suites);
|
|
145
|
+
}
|
|
146
|
+
} else if (report.suites) {
|
|
147
|
+
extractTests(report.suites);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return results;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Parse Cypress JSON report (spec-based results).
|
|
155
|
+
* @param {string} reportPath - Path to the Cypress JSON report
|
|
156
|
+
* @returns {Array<{title: string, status: string, duration: number, error?: string}>}
|
|
157
|
+
*/
|
|
158
|
+
function parseCypressJson(reportPath) {
|
|
159
|
+
const report = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));
|
|
160
|
+
const results = [];
|
|
161
|
+
|
|
162
|
+
// Cypress native JSON structure
|
|
163
|
+
// { runs: [{ tests: [...], spec: {...} }] }
|
|
164
|
+
if (report.runs) {
|
|
165
|
+
for (const run of report.runs) {
|
|
166
|
+
for (const test of run.tests || []) {
|
|
167
|
+
const attempts = test.attempts || [];
|
|
168
|
+
const lastAttempt = attempts[attempts.length - 1] || {};
|
|
169
|
+
|
|
170
|
+
results.push({
|
|
171
|
+
title: test.title?.join(' > ') || test.title,
|
|
172
|
+
status: mapCypressStatus(test.state || lastAttempt.state),
|
|
173
|
+
duration: test.duration || lastAttempt.duration || 0,
|
|
174
|
+
error: lastAttempt.error?.message || test.displayError || '',
|
|
175
|
+
file: run.spec?.relative || run.spec?.name,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Cypress Dashboard JSON structure
|
|
182
|
+
// { tests: [...] }
|
|
183
|
+
if (report.tests && !report.runs) {
|
|
184
|
+
for (const test of report.tests) {
|
|
185
|
+
results.push({
|
|
186
|
+
title: test.title || test.name,
|
|
187
|
+
status: mapCypressStatus(test.state || test.status),
|
|
188
|
+
duration: test.duration || 0,
|
|
189
|
+
error: test.error?.message || '',
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return results;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Map Mochawesome status to BrowserStack status.
|
|
199
|
+
*/
|
|
200
|
+
function mapMochawesomeStatus(status) {
|
|
201
|
+
if (status === true || status === 'passed') return 'passed';
|
|
202
|
+
if (status === false || status === 'failed') return 'failed';
|
|
203
|
+
if (status === 'pending' || status === 'skipped') return 'skipped';
|
|
204
|
+
return status || 'passed';
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Map Cypress status to BrowserStack status.
|
|
209
|
+
*/
|
|
210
|
+
function mapCypressStatus(status) {
|
|
211
|
+
const map = {
|
|
212
|
+
passed: 'passed',
|
|
213
|
+
failed: 'failed',
|
|
214
|
+
pending: 'skipped',
|
|
215
|
+
skipped: 'skipped',
|
|
216
|
+
};
|
|
217
|
+
return map[status] || status;
|
|
218
|
+
}
|
|
219
|
+
|
|
111
220
|
/**
|
|
112
221
|
* Parse Playwright HTML report's index.html or results.json
|
|
113
222
|
*/
|
|
@@ -211,17 +320,52 @@ export async function syncResultsFromReport(options = {}) {
|
|
|
211
320
|
// Parse the report based on format
|
|
212
321
|
console.log('\n Parsing test results...');
|
|
213
322
|
let testResults;
|
|
323
|
+
let detectedFormat = format;
|
|
214
324
|
|
|
215
325
|
const stat = fs.statSync(reportPath);
|
|
216
326
|
if (stat.isDirectory()) {
|
|
217
327
|
// Assume it's a Playwright HTML report directory
|
|
218
328
|
testResults = parsePlaywrightHtmlReport(reportPath);
|
|
329
|
+
detectedFormat = 'playwright-html';
|
|
219
330
|
} else if (format === 'junit' || reportPath.endsWith('.xml')) {
|
|
220
331
|
testResults = parseJunitXml(reportPath);
|
|
332
|
+
detectedFormat = 'junit-xml';
|
|
221
333
|
} else {
|
|
222
|
-
|
|
334
|
+
// Try to auto-detect JSON format by reading the file
|
|
335
|
+
const content = fs.readFileSync(reportPath, 'utf-8');
|
|
336
|
+
const json = JSON.parse(content);
|
|
337
|
+
|
|
338
|
+
if (format === 'mochawesome' || json.stats?.testsRegistered !== undefined || (json.results && json.results[0]?.suites)) {
|
|
339
|
+
// Mochawesome format (Cypress)
|
|
340
|
+
testResults = parseMochawesome(reportPath);
|
|
341
|
+
detectedFormat = 'mochawesome';
|
|
342
|
+
} else if (format === 'cypress' || json.runs) {
|
|
343
|
+
// Cypress native JSON format
|
|
344
|
+
testResults = parseCypressJson(reportPath);
|
|
345
|
+
detectedFormat = 'cypress-json';
|
|
346
|
+
} else if (json.suites || json.config?.projects) {
|
|
347
|
+
// Playwright JSON format
|
|
348
|
+
testResults = parsePlaywrightJson(reportPath);
|
|
349
|
+
detectedFormat = 'playwright-json';
|
|
350
|
+
} else {
|
|
351
|
+
// Fallback: try Playwright first, then Mochawesome
|
|
352
|
+
try {
|
|
353
|
+
testResults = parsePlaywrightJson(reportPath);
|
|
354
|
+
detectedFormat = 'playwright-json';
|
|
355
|
+
} catch {
|
|
356
|
+
try {
|
|
357
|
+
testResults = parseMochawesome(reportPath);
|
|
358
|
+
detectedFormat = 'mochawesome';
|
|
359
|
+
} catch {
|
|
360
|
+
testResults = parseCypressJson(reportPath);
|
|
361
|
+
detectedFormat = 'cypress-json';
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
223
365
|
}
|
|
224
366
|
|
|
367
|
+
console.log(` Detected format: ${detectedFormat}`)
|
|
368
|
+
|
|
225
369
|
console.log(` Found ${testResults.length} test result(s)`);
|
|
226
370
|
|
|
227
371
|
if (testResults.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ash-mallick/browserstack-sync",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Sync Playwright & Cypress e2e specs to CSV and BrowserStack Test Management with FREE AI-powered test step extraction using Ollama (local).",
|
|
5
5
|
"author": "Ashutosh Mallick",
|
|
6
6
|
"type": "module",
|