@msalaam/xray-qe-toolkit 1.1.0 → 1.3.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 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)
@@ -31,6 +32,7 @@
31
32
  - [Jira/Xray Project Setup](#jiraxray-project-setup)
32
33
  - [QE Workflow](#qe-workflow)
33
34
  - [Building Regression Packs](#building-regression-packs)
35
+ - [Working with Existing Xray Tests](#working-with-existing-xray-tests)
34
36
  - [Idempotent Push](#idempotent-push)
35
37
  - [Programmatic API](#programmatic-api)
36
38
  - [Troubleshooting](#troubleshooting)
@@ -46,8 +48,9 @@
46
48
  3. **Idempotent push** — updates existing tests, creates new ones, skips duplicates
47
49
  4. **Postman collection generation** from test definitions
48
50
  5. **CI pipeline template** for Azure DevOps (Newman + Xray import)
49
- 6. **Modular architecture** — every function is importable for programmatic use
50
- 7. **AI-ready scaffolds** — optional AI assistance for test generation (manual workflow fully supported)
51
+ 6. **Playwright integration** — import Playwright JSON results with automatic test mapping
52
+ 7. **Modular architecture** — every function is importable for programmatic use
53
+ 8. **AI-ready scaffolds** — optional AI assistance for test generation (manual workflow fully supported)
51
54
 
52
55
  ### QE Review Gate Philosophy
53
56
 
@@ -242,7 +245,7 @@ All commands support these global options:
242
245
 
243
246
  ---
244
247
 
245
- ### `xray-qe init`
248
+ ### `xqt init`
246
249
 
247
250
  Scaffold a new project with starter templates.
248
251
 
@@ -261,7 +264,7 @@ Existing files are **never overwritten** — the command skips them with a warni
261
264
 
262
265
  ---
263
266
 
264
- ### `xray-qe gen-tests`
267
+ ### `xqt gen-tests`
265
268
 
266
269
  Generate test cases from `knowledge/` folder documentation (AI-assisted or manual guidance).
267
270
 
@@ -300,7 +303,7 @@ Existing files are **never overwritten** — the command skips them with a warni
300
303
 
301
304
  ---
302
305
 
303
- ### `xray-qe edit-json`
306
+ ### `xqt edit-json`
304
307
 
305
308
  Launch the browser-based QE review gate editor.
306
309
 
@@ -325,7 +328,7 @@ npx xqt edit-json --port 3000
325
328
 
326
329
  ---
327
330
 
328
- ### `xray-qe push-tests`
331
+ ### `xqt push-tests`
329
332
 
330
333
  Push or update tests in Xray Cloud from `tests.json`.
331
334
 
@@ -354,7 +357,7 @@ npx xqt push-tests --skip-exec
354
357
 
355
358
  ---
356
359
 
357
- ### `xray-qe gen-postman`
360
+ ### `xqt gen-postman`
358
361
 
359
362
  Generate a Postman Collection v2.1 JSON from `tests.json` (works with or without AI).
360
363
 
@@ -398,7 +401,7 @@ npx xqt gen-postman --knowledge ./docs
398
401
 
399
402
  ---
400
403
 
401
- ### `xray-qe create-execution`
404
+ ### `xqt create-execution`
402
405
 
403
406
  Create a standalone Test Execution issue in JIRA.
404
407
 
@@ -415,22 +418,92 @@ npx xqt create-execution --summary "Feature XYZ" --issue QE-123
415
418
 
416
419
  ---
417
420
 
418
- ### `xray-qe import-results`
421
+ ### `xqt import-results`
419
422
 
420
- Import JUnit/Newman XML results into Xray Cloud. Designed for CI — no human interaction.
423
+ Import test results into Xray Cloud. Supports JUnit XML and Playwright JSON formats. Designed for CI — no human interaction.
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.
421
438
 
422
439
  ```bash
423
440
  npx xqt import-results --file results.xml --testExecKey QE-123
424
441
  ```
425
442
 
426
- | Option | Description | Required |
427
- |---------------------|--------------------------------------------|----------|
428
- | `--file <path>` | Path to the JUnit/Newman results XML file | Yes |
429
- | `--testExecKey <key>` | Test Execution to associate results with | Yes |
443
+ #### Playwright JSON (Recommended)
444
+
445
+ **Updates existing tests** via annotations. Use this format to link results to your existing Xray test cases.
446
+
447
+ ```bash
448
+ # Generate JSON report in Playwright
449
+ npx playwright test --reporter=json > playwright-results.json
450
+
451
+ # Import to Xray (updates existing tests via annotations)
452
+ npx xqt import-results --file playwright-results.json --testExecKey QE-123
453
+
454
+ # Create new Test Execution automatically
455
+ npx xqt import-results --file playwright-results.json --summary "Regression Suite"
456
+ ```
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
+
470
+ | Option | Description | Required |
471
+ |-----------------------------|---------------------------------------------------|----------|
472
+ | `--file <path>` | Path to results file (.xml or .json) | Yes |
473
+ | `--testExecKey <key>` | Test Execution to link results to | No* |
474
+ | `--summary <text>` | Summary for new Test Execution (if no testExecKey) | No |
475
+ | `--description <text>` | Description for new Test Execution | No |
476
+
477
+ \* If `--testExecKey` is omitted, a new Test Execution will be created automatically.
478
+
479
+ **Mapping Playwright Tests to Xray:**
480
+
481
+ To link Playwright test results to existing Xray test cases, **you must use annotations**:
482
+
483
+ ```typescript
484
+ // ✅ CORRECT - Updates existing test PROJ-123
485
+ test('User login flow', async ({ page }) => {
486
+ test.info().annotations.push({ type: 'xray', description: 'PROJ-123' });
487
+ // ... test code
488
+ });
489
+
490
+ // ✅ Alternative - Include test key in title
491
+ test('[PROJ-456] User registration validates email', async ({ page }) => {
492
+ // ... test code
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
+ });
500
+ ```
501
+
502
+ **Without annotations:** A new test will be created in Xray with the test title as the summary (not recommended for existing tests).
430
503
 
431
504
  ---
432
505
 
433
- ### `xray-qe gen-pipeline`
506
+ ### `xqt gen-pipeline`
434
507
 
435
508
  Generate an Azure Pipelines YAML template.
436
509
 
@@ -453,7 +526,7 @@ The generated pipeline:
453
526
 
454
527
  ---
455
528
 
456
- ### `xray-qe mcp-server`
529
+ ### `xqt mcp-server`
457
530
 
458
531
  Start a Model Context Protocol server for GitHub Copilot agent-mode integration.
459
532
 
@@ -682,6 +755,177 @@ Use this checklist to align a new team's board and Xray configuration with the t
682
755
  └─────────────────────────────────────────────────────────────────────────┘
683
756
  ```
684
757
 
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.
763
+
764
+ #### Step 1: Install Playwright (in your test repo)
765
+
766
+ ```bash
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
+
828
+ });
829
+ ```
830
+
831
+ #### Step 4: Map All Your Tests
832
+
833
+ Based on your `xray-mapping.json`, add annotations to each test:
834
+
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
+ });
849
+ ```
850
+
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
+ #### Step 6: Configure CI/CD
865
+
866
+ **Azure Pipelines:**
867
+
868
+ ```yaml
869
+ steps:
870
+ - task: NodeTool@0
871
+ inputs:
872
+ versionSpec: '18.x'
873
+
874
+ - script: npm ci
875
+ displayName: 'Install dependencies'
876
+
877
+ - script: npx playwright test
878
+ displayName: 'Run Playwright tests'
879
+ continueOnError: true
880
+
881
+ - script: |
882
+ npx xqt import-results \
883
+ --file playwright-results.json \
884
+ --testExecKey APIEE-6811
885
+ displayName: 'Upload results to Xray'
886
+ env:
887
+ XRAY_ID: $(XRAY_ID)
888
+ XRAY_SECRET: $(XRAY_SECRET)
889
+ JIRA_PROJECT_KEY: $(JIRA_PROJECT_KEY)
890
+ JIRA_URL: $(JIRA_URL)
891
+ JIRA_API_TOKEN: $(JIRA_API_TOKEN)
892
+ JIRA_EMAIL: $(JIRA_EMAIL)
893
+ ```
894
+
895
+ **GitHub Actions:**
896
+
897
+ ```yaml
898
+ steps:
899
+ - uses: actions/setup-node@v3
900
+ with:
901
+ node-version: '18'
902
+
903
+ - run: npm ci
904
+
905
+ - run: npx playwright test
906
+
907
+ - run: |
908
+ npx xqt import-results \
909
+ --file playwright-results.json \
910
+ --testExecKey APIEE-6811
911
+ env:
912
+ XRAY_ID: ${{ secrets.XRAY_ID }}
913
+ XRAY_SECRET: ${{ secrets.XRAY_SECRET }}
914
+ JIRA_PROJECT_KEY: ${{ secrets.JIRA_PROJECT_KEY }}
915
+ JIRA_URL: ${{ secrets.JIRA_URL }}
916
+ JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
917
+ JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
918
+ ```
919
+
920
+ ### Benefits
921
+
922
+ ✅ **Updates existing tests** - No duplicate test creation
923
+ ✅ **Automatic mapping** - Via annotations in test code
924
+ ✅ **Evidence attachments** - Screenshots, responses, traces
925
+ ✅ **Detailed reporting** - Retries, worker info, error details
926
+ ✅ **CI/CD ready** - Standard workflow for automation
927
+ ✅ **Single source of truth** - Test code + Xray together
928
+
685
929
  ---
686
930
 
687
931
  ## Building Regression Packs
@@ -823,6 +1067,173 @@ newman run collection.postman.json # Full regression weekly
823
1067
 
824
1068
  ---
825
1069
 
1070
+ ## Working with Existing Xray Tests
1071
+
1072
+ If your team already has test cases in Xray Cloud that were created manually or by another tool, you can set up this toolkit to manage and update those existing tests.
1073
+
1074
+ ### Step 1: Query Existing Tests from Xray
1075
+
1076
+ Use the Xray GraphQL API to fetch your existing tests. Here's a script to generate the mapping file:
1077
+
1078
+ **fetch-existing-tests.js:**
1079
+ ```javascript
1080
+ import { authenticate, loadConfig } from "@msalaam/xray-qe-toolkit";
1081
+ import fs from "fs";
1082
+
1083
+ const cfg = loadConfig();
1084
+ const token = await authenticate(cfg);
1085
+
1086
+ // GraphQL query to fetch all tests in your project
1087
+ const query = `
1088
+ query {
1089
+ getTests(jql: "project = ${cfg.jiraProjectKey} AND issuetype = Test", limit: 1000) {
1090
+ total
1091
+ results {
1092
+ issueId
1093
+ jira(fields: ["key", "summary", "description", "priority", "labels"])
1094
+ }
1095
+ }
1096
+ }
1097
+ `;
1098
+
1099
+ const response = await fetch(cfg.xrayGraphQLUrl || "https://us.xray.cloud.getxray.app/api/v2/graphql", {
1100
+ method: "POST",
1101
+ headers: {
1102
+ "Content-Type": "application/json",
1103
+ Authorization: `Bearer ${token}`,
1104
+ },
1105
+ body: JSON.stringify({ query }),
1106
+ });
1107
+
1108
+ const data = await response.json();
1109
+ const tests = data.data.getTests.results;
1110
+
1111
+ // Generate xray-mapping.json
1112
+ const mapping = {};
1113
+ tests.forEach((test) => {
1114
+ const testId = test.jira.key; // Use JIRA key as test_id initially
1115
+ mapping[testId] = {
1116
+ key: test.jira.key,
1117
+ id: test.issueId,
1118
+ };
1119
+ });
1120
+
1121
+ fs.writeFileSync("xray-mapping.json", JSON.stringify(mapping, null, 2));
1122
+ console.log(`✓ Created xray-mapping.json with ${tests.length} tests`);
1123
+
1124
+ // Generate tests.json scaffold
1125
+ const testsJson = {
1126
+ testExecution: {
1127
+ summary: "Existing Tests - Managed by Toolkit",
1128
+ description: "Imported from existing Xray tests",
1129
+ },
1130
+ tests: tests.map((test) => ({
1131
+ test_id: test.jira.key,
1132
+ skip: false,
1133
+ tags: [],
1134
+ xray: {
1135
+ summary: test.jira.summary,
1136
+ description: test.jira.description || "",
1137
+ priority: test.jira.priority?.name || "Medium",
1138
+ labels: test.jira.labels || [],
1139
+ steps: [
1140
+ // You'll need to fetch test steps separately via another API call
1141
+ {
1142
+ action: "PLACEHOLDER - Edit this in npx xqt edit-json",
1143
+ data: "",
1144
+ expected_result: "PLACEHOLDER",
1145
+ },
1146
+ ],
1147
+ },
1148
+ })),
1149
+ };
1150
+
1151
+ fs.writeFileSync("tests.json", JSON.stringify(testsJson, null, 2));
1152
+ console.log(`✓ Created tests.json with ${tests.length} test scaffolds`);
1153
+ console.log("\nNext steps:");
1154
+ console.log("1. Run 'npx xqt edit-json' to review and complete test steps");
1155
+ console.log("2. Run 'npx xqt push-tests' to update tests in Xray");
1156
+ ```
1157
+
1158
+ ### Step 2: Run the Script
1159
+
1160
+ ```bash
1161
+ # Save the script above as fetch-existing-tests.js
1162
+ node fetch-existing-tests.js
1163
+ ```
1164
+
1165
+ This generates:
1166
+ - `xray-mapping.json` — maps your existing JIRA test keys to their IDs
1167
+ - `tests.json` — scaffold with summaries, descriptions, priorities, labels
1168
+
1169
+ ### Step 3: Complete Test Steps
1170
+
1171
+ The script can't fetch test steps automatically (requires additional API calls). Complete them in the editor:
1172
+
1173
+ ```bash
1174
+ npx xqt edit-json
1175
+ # Edit each test to add proper steps
1176
+ # Save when done
1177
+ ```
1178
+
1179
+ ### Step 4: Update Existing Tests
1180
+
1181
+ Now you can update your existing Xray tests:
1182
+
1183
+ ```bash
1184
+ npx xqt push-tests
1185
+ ```
1186
+
1187
+ Because the tests are in `xray-mapping.json`, they'll be **updated** (not duplicated).
1188
+
1189
+ ### Alternative: Manual Mapping Creation
1190
+
1191
+ If you have a small number of tests, create the mapping manually:
1192
+
1193
+ **xray-mapping.json:**
1194
+ ```json
1195
+ {
1196
+ "APIEE-6933": { "key": "APIEE-6933", "id": "1865623" },
1197
+ "APIEE-6934": { "key": "APIEE-6934", "id": "1865627" },
1198
+ "APIEE-6935": { "key": "APIEE-6935", "id": "1865628" }
1199
+ }
1200
+ ```
1201
+
1202
+ **tests.json:**
1203
+ ```json
1204
+ {
1205
+ "tests": [
1206
+ {
1207
+ "test_id": "APIEE-6933",
1208
+ "xray": {
1209
+ "summary": "Test User Login",
1210
+ "steps": [
1211
+ { "action": "...", "expected_result": "..." }
1212
+ ]
1213
+ }
1214
+ }
1215
+ ]
1216
+ }
1217
+ ```
1218
+
1219
+ Then run `npx xqt push-tests` to update.
1220
+
1221
+ ### Future Enhancement: Pull Command
1222
+
1223
+ A `pull-tests` command to automatically fetch and sync existing tests is planned:
1224
+
1225
+ ```bash
1226
+ # Future feature (not yet implemented)
1227
+ npx xqt pull-tests --project APIEE
1228
+ npx xqt pull-tests --jql "project = APIEE AND labels = regression"
1229
+ ```
1230
+
1231
+ This would automatically generate both `tests.json` and `xray-mapping.json` from Xray.
1232
+
1233
+ **Want this feature?** [Open an issue on GitHub](https://github.com/Muhammad-Salaam_omit/xray-qe-toolkit/issues) or contribute via PR.
1234
+
1235
+ ---
1236
+
826
1237
  ## Idempotent Push
827
1238
 
828
1239
  `push-tests` checks `xray-mapping.json` before each operation:
package/bin/cli.js CHANGED
@@ -13,7 +13,7 @@
13
13
  * push-tests Push / update tests in Xray Cloud
14
14
  * gen-postman Generate Postman collection from tests.json (with optional AI)
15
15
  * create-execution Create a new Test Execution in JIRA
16
- * import-results Import JUnit/Newman results into Xray
16
+ * import-results Import test results (JUnit XML or Playwright JSON) into Xray
17
17
  * gen-pipeline Generate an Azure Pipelines YAML template
18
18
  * mcp-server Start Model Context Protocol server for agent integration
19
19
  */
@@ -103,9 +103,11 @@ program
103
103
 
104
104
  program
105
105
  .command("import-results")
106
- .description("Import JUnit/Newman XML results into Xray Cloud")
107
- .requiredOption("--file <path>", "Path to the results XML file")
108
- .requiredOption("--testExecKey <key>", "Test Execution key to associate results with")
106
+ .description("Import test results into Xray Cloud (JUnit XML or Playwright JSON)")
107
+ .requiredOption("--file <path>", "Path to the results file (.xml for JUnit, .json for Playwright)")
108
+ .option("--testExecKey <key>", "Test Execution key to associate results with (creates new if omitted)")
109
+ .option("--summary <text>", "Test Execution summary (for new executions)")
110
+ .option("--description <text>", "Test Execution description (for new executions)")
109
111
  .action(async (opts, cmd) => {
110
112
  const mod = await import("../commands/importResults.js");
111
113
  await mod.default({ ...opts, ...cmd.optsWithGlobals() });
@@ -1,8 +1,11 @@
1
1
  /**
2
- * Command: xray-qe import-results --file <path> --testExecKey <key>
2
+ * Command: xqt import-results --file <path> [--testExecKey <key>]
3
3
  *
4
- * Imports JUnit/Newman XML results into Xray Cloud and associates
5
- * them with the specified Test Execution.
4
+ * Imports test results into Xray Cloud and associates them with a Test Execution.
5
+ *
6
+ * Supported formats:
7
+ * - JUnit XML (.xml) - Standard JUnit or XUnit format
8
+ * - Playwright JSON (.json) - Playwright JSON reporter output
6
9
  *
7
10
  * Designed to run in CI — no human interaction required.
8
11
  */
@@ -11,7 +14,8 @@ import fs from "node:fs";
11
14
  import path from "node:path";
12
15
  import logger, { setVerbose } from "../lib/logger.js";
13
16
  import { loadConfig, validateConfig } from "../lib/config.js";
14
- import { authenticate, importResults as importResultsApi } from "../lib/xrayClient.js";
17
+ import { authenticate, importResults as importResultsXml, importResultsXrayJson } from "../lib/xrayClient.js";
18
+ import { convertPlaywrightToXray } from "../lib/playwrightConverter.js";
15
19
 
16
20
  export default async function importResults(opts = {}) {
17
21
  if (opts.verbose) setVerbose(true);
@@ -27,23 +31,53 @@ export default async function importResults(opts = {}) {
27
31
  process.exit(1);
28
32
  }
29
33
 
30
- logger.info(`File: ${path.basename(filePath)}`);
31
- logger.info(`Test Execution: ${opts.testExecKey}`);
34
+ const fileName = path.basename(filePath);
35
+ const fileExt = path.extname(filePath).toLowerCase();
36
+
37
+ logger.info(`File: ${fileName}`);
38
+ if (opts.testExecKey) {
39
+ logger.info(`Test Execution: ${opts.testExecKey}`);
40
+ }
32
41
  logger.blank();
33
42
 
34
43
  // Authenticate
35
44
  const xrayToken = await authenticate(cfg);
36
45
 
37
- // Read XML file
38
- const xmlBuffer = fs.readFileSync(filePath);
39
- logger.send(`Importing ${xmlBuffer.length} bytes to Xray...`);
46
+ // Detect format and import
47
+ let result;
48
+
49
+ if (fileExt === '.json') {
50
+ // Playwright JSON format
51
+ logger.send(`Importing Playwright JSON results...`);
40
52
 
41
- const result = await importResultsApi(cfg, xrayToken, xmlBuffer, opts.testExecKey);
53
+ const playwrightJson = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
54
+
55
+ // Convert to Xray format
56
+ const xrayJson = convertPlaywrightToXray(playwrightJson, {
57
+ testExecutionKey: opts.testExecKey,
58
+ projectKey: cfg.jiraProjectKey,
59
+ summary: opts.summary || `Playwright Test Execution - ${new Date().toLocaleString()}`,
60
+ description: opts.description,
61
+ });
62
+
63
+ logger.step(`Converted ${xrayJson.tests.length} test results to Xray format`);
64
+
65
+ // Import to Xray
66
+ result = await importResultsXrayJson(cfg, xrayToken, xrayJson);
67
+ } else {
68
+ // JUnit XML format (default)
69
+ logger.send(`Importing JUnit XML results...`);
70
+
71
+ const xmlBuffer = fs.readFileSync(filePath);
72
+ result = await importResultsXml(cfg, xrayToken, xmlBuffer, opts.testExecKey);
73
+ }
42
74
 
43
75
  logger.success("Results imported successfully");
44
76
 
45
77
  if (result?.key) {
46
78
  logger.link(`View: ${cfg.jiraUrl}/browse/${result.key}`);
79
+ } else if (result?.testExecIssue?.key) {
80
+ logger.link(`View: ${cfg.jiraUrl}/browse/${result.testExecIssue.key}`);
47
81
  }
48
82
 
49
83
  logger.blank();