@msalaam/xray-qe-toolkit 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +219 -25
- package/bin/cli.js +1 -1
- package/commands/importResults.js +63 -2
- package/lib/playwrightConverter.js +6 -0
- package/lib/xrayClient.js +45 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
- [Installation](#installation)
|
|
12
12
|
- [AI Setup (Optional)](#ai-setup-optional)
|
|
13
13
|
- [Quick Start](#quick-start)
|
|
14
|
+
- [Playwright Quick Start](#playwright-quick-start)
|
|
14
15
|
- [CLI Commands](#cli-commands)
|
|
15
16
|
- [init](#xray-qe-init)
|
|
16
17
|
- [gen-tests](#xray-qe-gen-tests)
|
|
@@ -421,23 +422,51 @@ npx xqt create-execution --summary "Feature XYZ" --issue QE-123
|
|
|
421
422
|
|
|
422
423
|
Import test results into Xray Cloud. Supports JUnit XML and Playwright JSON formats. Designed for CI — no human interaction.
|
|
423
424
|
|
|
424
|
-
|
|
425
|
+
#### Format Comparison
|
|
426
|
+
|
|
427
|
+
| Feature | JUnit XML | Playwright JSON |
|
|
428
|
+
|---------|-----------|----------------|
|
|
429
|
+
| **Update existing tests** | ❌ No - always creates new tests | ✅ Yes - via annotations |
|
|
430
|
+
| **Test key mapping** | ❌ Not supported | ✅ `test.info().annotations` |
|
|
431
|
+
| **Attachments/Evidence** | ❌ Not supported | ✅ Supported |
|
|
432
|
+
| **Best for** | Newman, generic test runners | Playwright tests |
|
|
433
|
+
| **Recommendation** | ⚠️ Use only for tools without test keys | ✅ **Recommended for Playwright** |
|
|
434
|
+
|
|
435
|
+
#### JUnit XML (Newman, generic test runners)
|
|
436
|
+
|
|
437
|
+
⚠️ **Important:** JUnit XML **cannot update existing Xray tests** - it will always create new test cases. Only use this for Newman or test runners that don't support test keys.
|
|
438
|
+
|
|
425
439
|
```bash
|
|
426
440
|
npx xqt import-results --file results.xml --testExecKey QE-123
|
|
427
441
|
```
|
|
428
442
|
|
|
429
|
-
|
|
443
|
+
#### Playwright JSON (Recommended)
|
|
444
|
+
|
|
445
|
+
✅ **Updates existing tests** via annotations. Use this format to link results to your existing Xray test cases.
|
|
446
|
+
|
|
430
447
|
```bash
|
|
431
448
|
# Generate JSON report in Playwright
|
|
432
449
|
npx playwright test --reporter=json > playwright-results.json
|
|
433
450
|
|
|
434
|
-
# Import to Xray (
|
|
451
|
+
# Import to Xray (updates existing tests via annotations)
|
|
435
452
|
npx xqt import-results --file playwright-results.json --testExecKey QE-123
|
|
436
453
|
|
|
437
454
|
# Create new Test Execution automatically
|
|
438
455
|
npx xqt import-results --file playwright-results.json --summary "Regression Suite"
|
|
439
456
|
```
|
|
440
457
|
|
|
458
|
+
**Required test code:**
|
|
459
|
+
```typescript
|
|
460
|
+
import { test, expect } from '@playwright/test';
|
|
461
|
+
|
|
462
|
+
test('Verify API endpoint', async ({ request }) => {
|
|
463
|
+
// THIS LINE links to existing Xray test
|
|
464
|
+
test.info().annotations.push({ type: 'xray', description: 'PROJ-123' });
|
|
465
|
+
|
|
466
|
+
// Your test code...
|
|
467
|
+
});
|
|
468
|
+
```
|
|
469
|
+
|
|
441
470
|
| Option | Description | Required |
|
|
442
471
|
|-----------------------------|---------------------------------------------------|----------|
|
|
443
472
|
| `--file <path>` | Path to results file (.xml or .json) | Yes |
|
|
@@ -449,22 +478,28 @@ npx xqt import-results --file playwright-results.json --summary "Regression Suit
|
|
|
449
478
|
|
|
450
479
|
**Mapping Playwright Tests to Xray:**
|
|
451
480
|
|
|
452
|
-
To link Playwright test results to existing Xray test cases, use annotations
|
|
481
|
+
To link Playwright test results to existing Xray test cases, **you must use annotations**:
|
|
453
482
|
|
|
454
483
|
```typescript
|
|
455
|
-
//
|
|
484
|
+
// ✅ CORRECT - Updates existing test PROJ-123
|
|
456
485
|
test('User login flow', async ({ page }) => {
|
|
457
486
|
test.info().annotations.push({ type: 'xray', description: 'PROJ-123' });
|
|
458
487
|
// ... test code
|
|
459
488
|
});
|
|
460
489
|
|
|
461
|
-
//
|
|
490
|
+
// ✅ Alternative - Include test key in title
|
|
462
491
|
test('[PROJ-456] User registration validates email', async ({ page }) => {
|
|
463
492
|
// ... test code
|
|
464
493
|
});
|
|
494
|
+
|
|
495
|
+
// ❌ WRONG - Creates new test every time
|
|
496
|
+
test('User login flow', async ({ page }) => {
|
|
497
|
+
// Missing annotation - will create duplicate test!
|
|
498
|
+
// ... test code
|
|
499
|
+
});
|
|
465
500
|
```
|
|
466
501
|
|
|
467
|
-
|
|
502
|
+
**Without annotations:** A new test will be created in Xray with the test title as the summary (not recommended for existing tests).
|
|
468
503
|
|
|
469
504
|
---
|
|
470
505
|
|
|
@@ -720,33 +755,192 @@ Use this checklist to align a new team's board and Xray configuration with the t
|
|
|
720
755
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
721
756
|
```
|
|
722
757
|
|
|
723
|
-
|
|
758
|
+
## Playwright Quick Start
|
|
759
|
+
|
|
760
|
+
### Complete Setup for Updating Existing Xray Tests
|
|
761
|
+
|
|
762
|
+
This is the **recommended workflow** for teams using Playwright with existing Xray test cases.
|
|
724
763
|
|
|
725
|
-
|
|
764
|
+
#### Step 1: Install Playwright (in your test repo)
|
|
726
765
|
|
|
727
766
|
```bash
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
767
|
+
npm install --save-dev @playwright/test
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
#### Step 2: Configure Playwright
|
|
771
|
+
|
|
772
|
+
Create `playwright.config.ts` in your test repo:
|
|
773
|
+
|
|
774
|
+
```typescript
|
|
775
|
+
import { defineConfig } from '@playwright/test';
|
|
776
|
+
|
|
777
|
+
export default defineConfig({
|
|
778
|
+
reporter: [
|
|
779
|
+
['html'], // For local viewing
|
|
780
|
+
['json', { outputFile: 'playwright-results.json' }], // For Xray import
|
|
781
|
+
],
|
|
782
|
+
|
|
783
|
+
use: {
|
|
784
|
+
baseURL: process.env.API_BASE_URL || 'https://your-api.com',
|
|
785
|
+
extraHTTPHeaders: {
|
|
786
|
+
'Authorization': `Bearer ${process.env.API_TOKEN}`,
|
|
787
|
+
'X-API-Key': process.env.API_KEY || '',
|
|
788
|
+
},
|
|
789
|
+
},
|
|
790
|
+
});
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
#### Step 3: Write Tests with Xray Annotations
|
|
794
|
+
|
|
795
|
+
⚠️ **Critical:** Every test MUST have an annotation to update existing Xray tests.
|
|
796
|
+
|
|
797
|
+
```typescript
|
|
798
|
+
import { test, expect } from '@playwright/test';
|
|
799
|
+
|
|
800
|
+
test.describe('Regression Tests', () => {
|
|
801
|
+
|
|
802
|
+
test('Verify API returns 200 for valid request', async ({ request }) => {
|
|
803
|
+
// THIS LINE links to your existing Xray test
|
|
804
|
+
test.info().annotations.push({ type: 'xray', description: 'APIEE-7131' });
|
|
805
|
+
|
|
806
|
+
const response = await request.post('/api/verify', {
|
|
807
|
+
data: { userId: '123', action: 'validate' }
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
// Attach response as evidence for Xray
|
|
811
|
+
test.info().attach('response-evidence', {
|
|
812
|
+
body: JSON.stringify({
|
|
813
|
+
status: response.status(),
|
|
814
|
+
headers: response.headers(),
|
|
815
|
+
body: await response.json()
|
|
816
|
+
}, null, 2),
|
|
817
|
+
contentType: 'application/json'
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
expect(response.status()).toBe(200);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
test('Verify API returns 400 for invalid data', async ({ request }) => {
|
|
824
|
+
test.info().annotations.push({ type: 'xray', description: 'APIEE-7132' });
|
|
825
|
+
// ... test implementation
|
|
826
|
+
});
|
|
827
|
+
|
|
733
828
|
});
|
|
829
|
+
```
|
|
734
830
|
|
|
735
|
-
|
|
736
|
-
npx playwright test --reporter=json > playwright-results.json
|
|
831
|
+
#### Step 4: Map All Your Tests
|
|
737
832
|
|
|
738
|
-
|
|
739
|
-
npx xqt import-results --file playwright-results.json --testExecKey PROJ-456
|
|
833
|
+
Based on your `xray-mapping.json`, add annotations to each test:
|
|
740
834
|
|
|
741
|
-
|
|
742
|
-
|
|
835
|
+
```typescript
|
|
836
|
+
// If your xray-mapping.json shows:
|
|
837
|
+
// "TC-001": { "key": "APIEE-7131", "id": "1879092" }
|
|
838
|
+
// "TC-002": { "key": "APIEE-7132", "id": "1879095" }
|
|
839
|
+
|
|
840
|
+
test('Test Case 1', async ({ request }) => {
|
|
841
|
+
test.info().annotations.push({ type: 'xray', description: 'APIEE-7131' });
|
|
842
|
+
// ...
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
test('Test Case 2', async ({ request }) => {
|
|
846
|
+
test.info().annotations.push({ type: 'xray', description: 'APIEE-7132' });
|
|
847
|
+
// ...
|
|
848
|
+
});
|
|
743
849
|
```
|
|
744
850
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
851
|
+
#### Step 5: Run Locally
|
|
852
|
+
|
|
853
|
+
```bash
|
|
854
|
+
# Run tests
|
|
855
|
+
npx playwright test
|
|
856
|
+
|
|
857
|
+
# View HTML report
|
|
858
|
+
npx playwright show-report
|
|
859
|
+
|
|
860
|
+
# Upload to Xray (updates existing tests)
|
|
861
|
+
npx xqt import-results --file playwright-results.json --testExecKey APIEE-6811
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
**What happens:**
|
|
865
|
+
- ✅ Tests WITH annotations (`test.info().annotations.push(...)`) → Updates existing Xray tests
|
|
866
|
+
- ⏭️ Tests WITHOUT annotations → **Automatically skipped** (won't create duplicates)
|
|
867
|
+
- 📊 Summary shows: Passed, Failed, Skipped counts
|
|
868
|
+
- 🔗 Direct link to view results in Xray
|
|
869
|
+
|
|
870
|
+
**Verbose mode** (see exactly what's being uploaded):
|
|
871
|
+
```bash
|
|
872
|
+
npx xqt import-results \
|
|
873
|
+
--file playwright-results.json \
|
|
874
|
+
--testExecKey APIEE-6811 \
|
|
875
|
+
--verbose
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
This saves `playwright-results-xray-debug.json` for inspection.
|
|
879
|
+
|
|
880
|
+
#### Step 6: Configure CI/CD
|
|
881
|
+
|
|
882
|
+
**Azure Pipelines:**
|
|
883
|
+
|
|
884
|
+
```yaml
|
|
885
|
+
steps:
|
|
886
|
+
- task: NodeTool@0
|
|
887
|
+
inputs:
|
|
888
|
+
versionSpec: '18.x'
|
|
889
|
+
|
|
890
|
+
- script: npm ci
|
|
891
|
+
displayName: 'Install dependencies'
|
|
892
|
+
|
|
893
|
+
- script: npx playwright test
|
|
894
|
+
displayName: 'Run Playwright tests'
|
|
895
|
+
continueOnError: true
|
|
896
|
+
|
|
897
|
+
- script: |
|
|
898
|
+
npx xqt import-results \
|
|
899
|
+
--file playwright-results.json \
|
|
900
|
+
--testExecKey APIEE-6811
|
|
901
|
+
displayName: 'Upload results to Xray'
|
|
902
|
+
env:
|
|
903
|
+
XRAY_ID: $(XRAY_ID)
|
|
904
|
+
XRAY_SECRET: $(XRAY_SECRET)
|
|
905
|
+
JIRA_PROJECT_KEY: $(JIRA_PROJECT_KEY)
|
|
906
|
+
JIRA_URL: $(JIRA_URL)
|
|
907
|
+
JIRA_API_TOKEN: $(JIRA_API_TOKEN)
|
|
908
|
+
JIRA_EMAIL: $(JIRA_EMAIL)
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
**GitHub Actions:**
|
|
912
|
+
|
|
913
|
+
```yaml
|
|
914
|
+
steps:
|
|
915
|
+
- uses: actions/setup-node@v3
|
|
916
|
+
with:
|
|
917
|
+
node-version: '18'
|
|
918
|
+
|
|
919
|
+
- run: npm ci
|
|
920
|
+
|
|
921
|
+
- run: npx playwright test
|
|
922
|
+
|
|
923
|
+
- run: |
|
|
924
|
+
npx xqt import-results \
|
|
925
|
+
--file playwright-results.json \
|
|
926
|
+
--testExecKey APIEE-6811
|
|
927
|
+
env:
|
|
928
|
+
XRAY_ID: ${{ secrets.XRAY_ID }}
|
|
929
|
+
XRAY_SECRET: ${{ secrets.XRAY_SECRET }}
|
|
930
|
+
JIRA_PROJECT_KEY: ${{ secrets.JIRA_PROJECT_KEY }}
|
|
931
|
+
JIRA_URL: ${{ secrets.JIRA_URL }}
|
|
932
|
+
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
|
|
933
|
+
JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
### Benefits
|
|
937
|
+
|
|
938
|
+
✅ **Updates existing tests** - No duplicate test creation
|
|
939
|
+
✅ **Automatic mapping** - Via annotations in test code
|
|
940
|
+
✅ **Evidence attachments** - Screenshots, responses, traces
|
|
941
|
+
✅ **Detailed reporting** - Retries, worker info, error details
|
|
942
|
+
✅ **CI/CD ready** - Standard workflow for automation
|
|
943
|
+
✅ **Single source of truth** - Test code + Xray together
|
|
750
944
|
|
|
751
945
|
---
|
|
752
946
|
|
package/bin/cli.js
CHANGED
|
@@ -48,7 +48,7 @@ export default async function importResults(opts = {}) {
|
|
|
48
48
|
|
|
49
49
|
if (fileExt === '.json') {
|
|
50
50
|
// Playwright JSON format
|
|
51
|
-
logger.send(`
|
|
51
|
+
logger.send(`Converting Playwright results to Xray format...`);
|
|
52
52
|
|
|
53
53
|
const playwrightJson = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
54
54
|
|
|
@@ -58,11 +58,38 @@ export default async function importResults(opts = {}) {
|
|
|
58
58
|
projectKey: cfg.jiraProjectKey,
|
|
59
59
|
summary: opts.summary || `Playwright Test Execution - ${new Date().toLocaleString()}`,
|
|
60
60
|
description: opts.description,
|
|
61
|
+
skipWithoutAnnotations: !!opts.testExecKey, // Skip tests without keys when updating existing execution
|
|
61
62
|
});
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
// Check if we have tests to upload
|
|
65
|
+
if (xrayJson.tests.length === 0) {
|
|
66
|
+
logger.warn('⚠️ No tests with Xray annotations found.');
|
|
67
|
+
logger.info('\nAdd annotations to your tests:');
|
|
68
|
+
logger.info(` test.info().annotations.push({ type: 'xray', description: 'APIEE-XXXX' });\n`);
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const testsWithKeys = xrayJson.tests.filter(t => t.testKey).length;
|
|
73
|
+
const testsWithoutKeys = xrayJson.tests.filter(t => !t.testKey).length;
|
|
74
|
+
|
|
75
|
+
logger.step(`Found ${testsWithKeys} test(s) with Xray annotations`);
|
|
76
|
+
|
|
77
|
+
if (testsWithoutKeys > 0 && !opts.testExecKey) {
|
|
78
|
+
logger.info(` ${testsWithoutKeys} test(s) without annotations will create new tests`);
|
|
79
|
+
} else if (testsWithoutKeys > 0 && opts.testExecKey) {
|
|
80
|
+
logger.warn(` Skipped ${testsWithoutKeys} test(s) without annotations`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Save debug output if verbose
|
|
84
|
+
if (opts.verbose) {
|
|
85
|
+
const debugPath = filePath.replace('.json', '-xray-debug.json');
|
|
86
|
+
fs.writeFileSync(debugPath, JSON.stringify(xrayJson, null, 2));
|
|
87
|
+
logger.info(` Debug: Xray JSON saved to ${path.basename(debugPath)}`);
|
|
88
|
+
}
|
|
89
|
+
logger.blank();
|
|
64
90
|
|
|
65
91
|
// Import to Xray
|
|
92
|
+
logger.send(`Uploading ${xrayJson.tests.length} test result(s) to Xray...`);
|
|
66
93
|
result = await importResultsXrayJson(cfg, xrayToken, xrayJson);
|
|
67
94
|
} else {
|
|
68
95
|
// JUnit XML format (default)
|
|
@@ -73,12 +100,46 @@ export default async function importResults(opts = {}) {
|
|
|
73
100
|
}
|
|
74
101
|
|
|
75
102
|
logger.success("Results imported successfully");
|
|
103
|
+
logger.blank();
|
|
76
104
|
|
|
105
|
+
// Display summary for JSON results
|
|
106
|
+
if (fileExt === '.json' && xrayJson) {
|
|
107
|
+
const summaryStats = calculateTestSummary(xrayJson.tests);
|
|
108
|
+
|
|
109
|
+
logger.info('Summary:');
|
|
110
|
+
logger.success(` ✓ Passed: ${summaryStats.passed}`)
|
|
111
|
+
;
|
|
112
|
+
if (summaryStats.failed > 0) {
|
|
113
|
+
logger.error(` ✗ Failed: ${summaryStats.failed}`);
|
|
114
|
+
}
|
|
115
|
+
if (summaryStats.skipped > 0) {
|
|
116
|
+
logger.warn(` ⊘ Skipped: ${summaryStats.skipped}`);
|
|
117
|
+
}
|
|
118
|
+
logger.blank();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Show link to Test Execution
|
|
77
122
|
if (result?.key) {
|
|
78
123
|
logger.link(`View: ${cfg.jiraUrl}/browse/${result.key}`);
|
|
79
124
|
} else if (result?.testExecIssue?.key) {
|
|
80
125
|
logger.link(`View: ${cfg.jiraUrl}/browse/${result.testExecIssue.key}`);
|
|
126
|
+
} else if (opts.testExecKey) {
|
|
127
|
+
logger.link(`View: ${cfg.jiraUrl}/browse/${opts.testExecKey}`);
|
|
81
128
|
}
|
|
82
129
|
|
|
83
130
|
logger.blank();
|
|
84
131
|
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Calculate test result summary
|
|
135
|
+
* @param {array} tests - Xray test results
|
|
136
|
+
* @returns {object} Summary stats
|
|
137
|
+
*/
|
|
138
|
+
function calculateTestSummary(tests) {
|
|
139
|
+
return {
|
|
140
|
+
passed: tests.filter(t => t.status === 'PASSED').length,
|
|
141
|
+
failed: tests.filter(t => t.status === 'FAILED').length,
|
|
142
|
+
skipped: tests.filter(t => t.status === 'SKIPPED').length,
|
|
143
|
+
total: tests.length,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* @param {string} options.projectKey - JIRA project key for test creation
|
|
20
20
|
* @param {string} options.summary - Test Execution summary (optional)
|
|
21
21
|
* @param {string} options.description - Test Execution description (optional)
|
|
22
|
+
* @param {boolean} options.skipWithoutAnnotations - Skip tests without Xray annotations (default: false)
|
|
22
23
|
* @returns {object} Xray JSON format
|
|
23
24
|
*/
|
|
24
25
|
export function convertPlaywrightToXray(playwrightJson, options = {}) {
|
|
@@ -147,6 +148,11 @@ function convertTest(test, spec, suite, options) {
|
|
|
147
148
|
// Extract Xray test key from annotations or title
|
|
148
149
|
const testKey = extractTestKey(spec.title, test.annotations);
|
|
149
150
|
|
|
151
|
+
// Skip tests without annotations if requested
|
|
152
|
+
if (options.skipWithoutAnnotations && !testKey) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
150
156
|
// Map Playwright status to Xray status
|
|
151
157
|
const status = mapStatus(result.status);
|
|
152
158
|
|
package/lib/xrayClient.js
CHANGED
|
@@ -371,15 +371,51 @@ export async function importResults(cfg, xrayToken, xmlBuffer, testExecKey) {
|
|
|
371
371
|
export async function importResultsXrayJson(cfg, xrayToken, xrayJson) {
|
|
372
372
|
const url = "https://xray.cloud.getxray.app/api/v2/import/execution";
|
|
373
373
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
374
|
+
try {
|
|
375
|
+
const response = await axios.post(url, xrayJson, {
|
|
376
|
+
httpsAgent,
|
|
377
|
+
headers: {
|
|
378
|
+
Authorization: `Bearer ${xrayToken}`,
|
|
379
|
+
"Content-Type": "application/json",
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
return response.data;
|
|
384
|
+
} catch (error) {
|
|
385
|
+
// Enhanced error reporting for Xray API issues
|
|
386
|
+
if (error.response) {
|
|
387
|
+
const status = error.response.status;
|
|
388
|
+
const data = error.response.data;
|
|
389
|
+
|
|
390
|
+
let errorMsg = `Xray API returned ${status} error`;
|
|
391
|
+
|
|
392
|
+
if (typeof data === 'string') {
|
|
393
|
+
errorMsg += `: ${data}`;
|
|
394
|
+
} else if (data?.error) {
|
|
395
|
+
errorMsg += `: ${data.error}`;
|
|
396
|
+
} else if (data?.message) {
|
|
397
|
+
errorMsg += `: ${data.message}`;
|
|
398
|
+
} else if (data) {
|
|
399
|
+
errorMsg += `: ${JSON.stringify(data)}`;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
logger.error(errorMsg);
|
|
403
|
+
|
|
404
|
+
// Common issues help
|
|
405
|
+
if (status === 400) {
|
|
406
|
+
logger.warn('\n💡 Common causes of 400 errors:');
|
|
407
|
+
logger.warn(' • Missing test annotations in Playwright tests');
|
|
408
|
+
logger.warn(' • Invalid test key format (should be PROJ-123)');
|
|
409
|
+
logger.warn(' • Test execution key not found');
|
|
410
|
+
logger.warn(' • Malformed JSON structure');
|
|
411
|
+
logger.warn(' • Test keys reference non-existent tests\n');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
throw new Error(errorMsg);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
throw error;
|
|
418
|
+
}
|
|
383
419
|
}
|
|
384
420
|
|
|
385
421
|
// ─── Retry wrapper ─────────────────────────────────────────────────────────────
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@msalaam/xray-qe-toolkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Full QE workflow toolkit for Xray Cloud integration — test management, Postman generation, CI pipeline scaffolding, and browser-based review gates for API regression projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|