@msalaam/xray-qe-toolkit 1.3.3 → 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 +114 -2
- package/bin/cli.js +24 -1
- package/commands/compareOpenapi.js +78 -0
- package/commands/genPipeline.js +6 -3
- package/commands/init.js +3 -3
- package/commands/updateSnapshot.js +34 -0
- package/package.json +6 -3
- package/templates/README.template.md +17 -12
- package/templates/azure-pipelines.yml +31 -12
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
- [import-results](#xray-qe-import-results)
|
|
23
23
|
- [gen-pipeline](#xray-qe-gen-pipeline)
|
|
24
24
|
- [mcp-server](#xray-qe-mcp-server)
|
|
25
|
+
- [compare-openapi](#xray-qe-compare-openapi)
|
|
26
|
+
- [update-snapshot](#xray-qe-update-snapshot)
|
|
25
27
|
- [Configuration](#configuration)
|
|
26
28
|
- [Environment Variables (.env)](#environment-variables-env)
|
|
27
29
|
- [Project Config (.xrayrc)](#project-config-xrayrc)
|
|
@@ -49,8 +51,9 @@
|
|
|
49
51
|
4. **Postman collection generation** from test definitions
|
|
50
52
|
5. **CI pipeline template** for Azure DevOps (Newman + Xray import)
|
|
51
53
|
6. **Playwright integration** — import Playwright JSON results with automatic test mapping
|
|
52
|
-
7. **
|
|
53
|
-
8. **
|
|
54
|
+
7. **OpenAPI contract enforcement** — `compare-openapi` diffs a live spec against a QA snapshot and fails the pipeline on breaking changes; `update-snapshot` promotes a new baseline deliberately
|
|
55
|
+
8. **Modular architecture** — every function is importable for programmatic use
|
|
56
|
+
9. **AI-ready scaffolds** — optional AI assistance for test generation (manual workflow fully supported)
|
|
54
57
|
|
|
55
58
|
### QE Review Gate Philosophy
|
|
56
59
|
|
|
@@ -559,6 +562,100 @@ npx xqt mcp-server --port 3100
|
|
|
559
562
|
|
|
560
563
|
---
|
|
561
564
|
|
|
565
|
+
### `xqt compare-openapi`
|
|
566
|
+
|
|
567
|
+
Compare an OpenAPI spec against an approved QA snapshot and fail the pipeline if breaking changes are detected.
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
# Basic comparison — exits 1 on breaking changes
|
|
571
|
+
npx xqt compare-openapi \
|
|
572
|
+
--current ../api-repo/openapi.yaml \
|
|
573
|
+
--snapshot ./openapi.snapshot.yaml
|
|
574
|
+
|
|
575
|
+
# Save a JSON diff report as a pipeline artifact
|
|
576
|
+
npx xqt compare-openapi \
|
|
577
|
+
--current ../api-repo/openapi.yaml \
|
|
578
|
+
--snapshot ./openapi.snapshot.yaml \
|
|
579
|
+
--report openapi-diff-report.json
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
| Option | Description | Required |
|
|
583
|
+
|----------------------|------------------------------------------------------------------|----------|
|
|
584
|
+
| `--current <path>` | Path to the current (live) OpenAPI spec | Yes |
|
|
585
|
+
| `--snapshot <path>` | Path to the approved QA baseline snapshot | Yes |
|
|
586
|
+
| `--report <path>` | Write full diff results to a JSON file (useful as CI artifact) | No |
|
|
587
|
+
|
|
588
|
+
**Behaviour:**
|
|
589
|
+
- Snapshot = approved QA contract baseline
|
|
590
|
+
- Current = proposed/live spec
|
|
591
|
+
- Exits `0` if no breaking changes; logs a count of non-breaking differences
|
|
592
|
+
- Exits `1` on any breaking change and prints the full diff
|
|
593
|
+
- If `--report` is specified and breaking changes are found, a JSON report is written
|
|
594
|
+
|
|
595
|
+
**Example Azure Pipelines step (from your test repo pipeline):**
|
|
596
|
+
|
|
597
|
+
```yaml
|
|
598
|
+
steps:
|
|
599
|
+
- checkout: self
|
|
600
|
+
|
|
601
|
+
- checkout: git://Project/portfolio-api
|
|
602
|
+
path: api-repo
|
|
603
|
+
|
|
604
|
+
- script: |
|
|
605
|
+
npx xqt compare-openapi \
|
|
606
|
+
--current api-repo/openapi.yaml \
|
|
607
|
+
--snapshot openapi.snapshot.yaml \
|
|
608
|
+
--report openapi-diff-report.json
|
|
609
|
+
displayName: Compare OpenAPI Contracts
|
|
610
|
+
|
|
611
|
+
- publish: openapi-diff-report.json
|
|
612
|
+
artifact: openapi-diff
|
|
613
|
+
condition: failed()
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**Governance model:**
|
|
617
|
+
- Dev repo is never modified by QE
|
|
618
|
+
- QE test repo pipeline checks out the API repo and enforces the contract
|
|
619
|
+
- Breaking change → pipeline fails → dev must fix spec or raise a contract change review
|
|
620
|
+
- QE then runs `update-snapshot` to promote the new baseline
|
|
621
|
+
|
|
622
|
+
---
|
|
623
|
+
|
|
624
|
+
### `xqt update-snapshot`
|
|
625
|
+
|
|
626
|
+
Overwrite the QA snapshot baseline with the current spec. **Always a deliberate, manual action — never called automatically.**
|
|
627
|
+
|
|
628
|
+
```bash
|
|
629
|
+
npx xqt update-snapshot \
|
|
630
|
+
--current ../api-repo/openapi.yaml \
|
|
631
|
+
--snapshot ./openapi.snapshot.yaml
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
| Option | Description | Required |
|
|
635
|
+
|----------------------|-----------------------------------------------------|----------|
|
|
636
|
+
| `--current <path>` | Path to the current (live) OpenAPI spec to promote | Yes |
|
|
637
|
+
| `--snapshot <path>` | Path to the snapshot file to overwrite | Yes |
|
|
638
|
+
|
|
639
|
+
**Workflow:**
|
|
640
|
+
1. Dev raises a contract change review
|
|
641
|
+
2. QE approves the new contract
|
|
642
|
+
3. QE runs `update-snapshot` locally
|
|
643
|
+
4. QE raises a PR in the test repo with the updated snapshot
|
|
644
|
+
5. PR is reviewed and merged — new baseline is established
|
|
645
|
+
|
|
646
|
+
```bash
|
|
647
|
+
# After merging the approved contract change:
|
|
648
|
+
npx xqt update-snapshot \
|
|
649
|
+
--current ../api-repo/openapi.yaml \
|
|
650
|
+
--snapshot ./openapi.snapshot.yaml
|
|
651
|
+
|
|
652
|
+
git add openapi.snapshot.yaml
|
|
653
|
+
git commit -m "chore: promote OpenAPI snapshot to v2.5.0"
|
|
654
|
+
git push
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
562
659
|
## Configuration
|
|
563
660
|
|
|
564
661
|
### Environment Variables (.env)
|
|
@@ -754,6 +851,21 @@ Use this checklist to align a new team's board and Xray configuration with the t
|
|
|
754
851
|
│ 10. newman run collection.postman.json --reporters junit │
|
|
755
852
|
│ 11. npx xqt import-results --file results.xml --testExecKey QE-123│
|
|
756
853
|
│ │
|
|
854
|
+
├─────────────────────────────────────────────────────────────────────────┤
|
|
855
|
+
│ CONTRACT ENFORCEMENT (TEST REPO PIPELINE) │
|
|
856
|
+
│ │
|
|
857
|
+
│ 12. Checkout API repo │
|
|
858
|
+
│ 13. npx xqt compare-openapi │
|
|
859
|
+
│ --current api-repo/openapi.yaml │
|
|
860
|
+
│ --snapshot openapi.snapshot.yaml │
|
|
861
|
+
│ → Fails pipeline on breaking changes │
|
|
862
|
+
│ │
|
|
863
|
+
│ (When QE approves a contract change) │
|
|
864
|
+
│ 14. npx xqt update-snapshot │
|
|
865
|
+
│ --current api-repo/openapi.yaml │
|
|
866
|
+
│ --snapshot openapi.snapshot.yaml │
|
|
867
|
+
│ → Commit updated snapshot + raise PR │
|
|
868
|
+
│ │
|
|
757
869
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
758
870
|
```
|
|
759
871
|
|
package/bin/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @msalaam/xray-qe-toolkit — CLI Entry Point
|
|
@@ -16,6 +16,8 @@
|
|
|
16
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
|
+
* compare-openapi Compare OpenAPI specs and fail if breaking changes are detected
|
|
20
|
+
* update-snapshot Overwrite the QA snapshot baseline with the current spec
|
|
19
21
|
*/
|
|
20
22
|
|
|
21
23
|
import { Command } from "commander";
|
|
@@ -131,6 +133,27 @@ program
|
|
|
131
133
|
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
132
134
|
});
|
|
133
135
|
|
|
136
|
+
program
|
|
137
|
+
.command("compare-openapi")
|
|
138
|
+
.description("Compare OpenAPI specs against a QA snapshot and fail if breaking changes are detected")
|
|
139
|
+
.requiredOption("--current <path>", "Path to the current (live) OpenAPI spec")
|
|
140
|
+
.requiredOption("--snapshot <path>", "Path to the approved QA snapshot")
|
|
141
|
+
.option("--report <path>", "Write a JSON diff report to this path (useful as a pipeline artifact)")
|
|
142
|
+
.action(async (opts, cmd) => {
|
|
143
|
+
const mod = await import("../commands/compareOpenapi.js");
|
|
144
|
+
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
program
|
|
148
|
+
.command("update-snapshot")
|
|
149
|
+
.description("Overwrite the QA snapshot baseline with the current spec (always a deliberate, manual action)")
|
|
150
|
+
.requiredOption("--current <path>", "Path to the current (live) OpenAPI spec to promote")
|
|
151
|
+
.requiredOption("--snapshot <path>", "Path to the snapshot file to overwrite")
|
|
152
|
+
.action(async (opts, cmd) => {
|
|
153
|
+
const mod = await import("../commands/updateSnapshot.js");
|
|
154
|
+
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
155
|
+
});
|
|
156
|
+
|
|
134
157
|
// ─── Parse & run ───────────────────────────────────────────────────────────────
|
|
135
158
|
|
|
136
159
|
program.parseAsync(process.argv).catch((err) => {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { diff } from 'openapi-diff';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Compare two OpenAPI specs and detect breaking changes.
|
|
7
|
+
* @param {string} currentPath - Path to the current (live) OpenAPI spec.
|
|
8
|
+
* @param {string} snapshotPath - Path to the approved QA snapshot.
|
|
9
|
+
* @param {string|null} reportPath - Optional path to write a JSON diff report.
|
|
10
|
+
*/
|
|
11
|
+
export async function compareOpenApiSpecs(currentPath, snapshotPath, reportPath = null) {
|
|
12
|
+
const resolvedCurrent = path.resolve(currentPath);
|
|
13
|
+
const resolvedSnapshot = path.resolve(snapshotPath);
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(resolvedCurrent)) {
|
|
16
|
+
throw new Error(`Current OpenAPI spec not found: ${resolvedCurrent}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!fs.existsSync(resolvedSnapshot)) {
|
|
20
|
+
throw new Error(`Snapshot OpenAPI spec not found: ${resolvedSnapshot}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const currentSpec = fs.readFileSync(resolvedCurrent, 'utf-8');
|
|
24
|
+
const snapshotSpec = fs.readFileSync(resolvedSnapshot, 'utf-8');
|
|
25
|
+
|
|
26
|
+
const result = await diff({
|
|
27
|
+
sourceSpec: { content: snapshotSpec }, // approved baseline
|
|
28
|
+
destinationSpec: { content: currentSpec } // proposed change
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const summary = {
|
|
32
|
+
breaking: result.breakingDifferencesFound,
|
|
33
|
+
breakingDifferences: result.breakingDifferences,
|
|
34
|
+
nonBreakingDifferences: result.nonBreakingDifferences
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (reportPath && result.breakingDifferencesFound) {
|
|
38
|
+
const resolvedReport = path.resolve(reportPath);
|
|
39
|
+
fs.writeFileSync(resolvedReport, JSON.stringify(summary, null, 2), 'utf-8');
|
|
40
|
+
console.log(`📄 Diff report written to: ${resolvedReport}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return summary;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default async function compareOpenapi(opts) {
|
|
47
|
+
const { current, snapshot, report } = opts;
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const result = await compareOpenApiSpecs(current, snapshot, report || null);
|
|
51
|
+
|
|
52
|
+
if (result.breaking) {
|
|
53
|
+
console.error('\n❌ Breaking changes detected:\n');
|
|
54
|
+
console.error(JSON.stringify(result.breakingDifferences, null, 2));
|
|
55
|
+
|
|
56
|
+
const count = result.breakingDifferences?.length ?? 0;
|
|
57
|
+
console.error(`\n${count} breaking difference(s) found.`);
|
|
58
|
+
|
|
59
|
+
if (!report) {
|
|
60
|
+
console.error('\nTip: use --report <path> to save a full diff report as a pipeline artifact.');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const nonBreaking = result.nonBreakingDifferences?.length ?? 0;
|
|
67
|
+
console.log('\n✅ No breaking changes detected.');
|
|
68
|
+
|
|
69
|
+
if (nonBreaking > 0) {
|
|
70
|
+
console.log(`ℹ️ ${nonBreaking} non-breaking difference(s) found (safe to proceed).`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
process.exit(0);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
console.error(`\n❌ ${err.message}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
package/commands/genPipeline.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Command: xray-qe gen-pipeline [--output <path>]
|
|
3
3
|
*
|
|
4
4
|
* Copies the Azure Pipelines YAML template into the consuming project.
|
|
5
|
-
* The template runs
|
|
5
|
+
* The template runs Playwright and imports JSON results — no QE logic in CI.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import fs from "node:fs";
|
|
@@ -34,8 +34,11 @@ export default async function genPipeline(opts = {}) {
|
|
|
34
34
|
console.log(" 1. Set pipeline variables in Azure DevOps:");
|
|
35
35
|
console.log(" XRAY_ID, XRAY_SECRET, JIRA_PROJECT_KEY, JIRA_URL,");
|
|
36
36
|
console.log(" JIRA_API_TOKEN, JIRA_EMAIL, TEST_EXEC_KEY");
|
|
37
|
-
console.log(" 2.
|
|
38
|
-
console.log(" 3.
|
|
37
|
+
console.log(" 2. Optionally set API_BASE_URL for Playwright tests");
|
|
38
|
+
console.log(" 3. Commit the generated file to your repo");
|
|
39
|
+
console.log(" 4. Create a pipeline in Azure DevOps pointing to this YAML");
|
|
40
|
+
console.log(" 5. Playwright tests must have xray annotations to map results:");
|
|
41
|
+
console.log(" test.info().annotations.push({ type: 'xray', description: 'PROJ-123' })");
|
|
39
42
|
console.log("");
|
|
40
43
|
console.log("⚠️ Remember: edit-json and QE review gates are NOT part of CI.");
|
|
41
44
|
console.log(" Run those steps locally before committing.\n");
|
package/commands/init.js
CHANGED
|
@@ -114,7 +114,7 @@ export default async function init(opts = {}) {
|
|
|
114
114
|
const xrayrc = {
|
|
115
115
|
testsPath: "tests.json",
|
|
116
116
|
mappingPath: "xray-mapping.json",
|
|
117
|
-
|
|
117
|
+
playwrightResultsPath: "playwright-results.json",
|
|
118
118
|
knowledgePath: "knowledge",
|
|
119
119
|
playwrightDir: "playwright-tests",
|
|
120
120
|
};
|
|
@@ -136,7 +136,7 @@ export default async function init(opts = {}) {
|
|
|
136
136
|
console.log(" 4. Generate test cases: npx xray-qe gen-tests --ai (or edit tests.json manually)");
|
|
137
137
|
console.log(" 5. Review tests: npx xray-qe edit-json");
|
|
138
138
|
console.log(" 6. Push tests to Xray: npx xray-qe push-tests");
|
|
139
|
-
console.log(" 7. Generate
|
|
140
|
-
console.log(" 8.
|
|
139
|
+
console.log(" 7. Generate CI pipeline: npx xqt gen-pipeline");
|
|
140
|
+
console.log(" 8. Run Playwright tests and import results: npx xqt import-results --file playwright-results.json");
|
|
141
141
|
console.log("");
|
|
142
142
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fse from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Copy the current OpenAPI spec over the snapshot baseline.
|
|
6
|
+
* This is intentionally a manual, explicit action — never called automatically.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} currentPath - Path to the current (live) OpenAPI spec.
|
|
9
|
+
* @param {string} snapshotPath - Path to the snapshot file to overwrite.
|
|
10
|
+
*/
|
|
11
|
+
export async function updateSnapshot(currentPath, snapshotPath) {
|
|
12
|
+
const resolvedCurrent = path.resolve(currentPath);
|
|
13
|
+
const resolvedSnapshot = path.resolve(snapshotPath);
|
|
14
|
+
|
|
15
|
+
if (!(await fse.pathExists(resolvedCurrent))) {
|
|
16
|
+
throw new Error(`Current OpenAPI spec not found: ${resolvedCurrent}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
await fse.copy(resolvedCurrent, resolvedSnapshot, { overwrite: true });
|
|
20
|
+
console.log(`✅ Snapshot updated: ${resolvedSnapshot}`);
|
|
21
|
+
console.log(` Source: ${resolvedCurrent}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default async function updateSnapshotCmd(opts) {
|
|
25
|
+
const { current, snapshot } = opts;
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
await updateSnapshot(current, snapshot);
|
|
29
|
+
console.log('\nBaseline established. Commit the updated snapshot to your test repo to record the new contract.');
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.error(`\n❌ ${err.message}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@msalaam/xray-qe-toolkit",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Full QE workflow toolkit for Xray Cloud integration — test management,
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "Full QE workflow toolkit for Xray Cloud integration — test management, Playwright integration, CI pipeline scaffolding, and browser-based review gates for API regression projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"xqt": "./bin/cli.js"
|
|
@@ -35,7 +35,10 @@
|
|
|
35
35
|
"commander": "^13.1.0",
|
|
36
36
|
"dotenv": "^17.2.3",
|
|
37
37
|
"express": "^5.1.0",
|
|
38
|
-
"
|
|
38
|
+
"fs-extra": "^11.3.3",
|
|
39
|
+
"open": "^10.1.2",
|
|
40
|
+
"openapi-diff": "^0.24.1",
|
|
41
|
+
"yaml": "^2.8.2"
|
|
39
42
|
},
|
|
40
43
|
"devDependencies": {
|
|
41
44
|
"ajv": "^8.17.1"
|
|
@@ -63,13 +63,7 @@ npx xqt edit-json
|
|
|
63
63
|
npx xqt push-tests
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
### 5.
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
npx xqt gen-postman
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### 6. Set Up CI Pipeline
|
|
66
|
+
### 5. Set Up CI Pipeline
|
|
73
67
|
|
|
74
68
|
```bash
|
|
75
69
|
# Generate Azure Pipelines template
|
|
@@ -84,10 +78,11 @@ npx xqt gen-pipeline
|
|
|
84
78
|
| `npx xqt gen-tests` | Generate test cases from knowledge/ |
|
|
85
79
|
| `npx xqt edit-json` | Review/edit tests in browser |
|
|
86
80
|
| `npx xqt push-tests` | Push tests to Xray Cloud |
|
|
87
|
-
| `npx xqt gen-postman` | Generate Postman collection |
|
|
88
81
|
| `npx xqt gen-pipeline` | Generate CI pipeline template |
|
|
89
82
|
| `npx xqt create-execution` | Create Test Execution issue |
|
|
90
|
-
| `npx xqt import-results` | Import
|
|
83
|
+
| `npx xqt import-results` | Import Playwright JSON results (CI) |
|
|
84
|
+
| `npx xqt compare-openapi` | Diff live spec vs QA snapshot — fails on breaking changes |
|
|
85
|
+
| `npx xqt update-snapshot` | Promote current spec as new QA baseline (manual only) |
|
|
91
86
|
|
|
92
87
|
Run any command with `--help` for details:
|
|
93
88
|
```bash
|
|
@@ -101,8 +96,18 @@ npx xqt gen-tests --help
|
|
|
101
96
|
2. Generate tests (AI or manual)
|
|
102
97
|
3. Review in edit-json
|
|
103
98
|
4. Push to Xray
|
|
104
|
-
5. Generate
|
|
105
|
-
6. Run in CI
|
|
99
|
+
5. Generate CI pipeline: npx xqt gen-pipeline
|
|
100
|
+
6. Run Playwright tests in CI
|
|
101
|
+
7. Import results: npx xqt import-results --file playwright-results.json
|
|
102
|
+
|
|
103
|
+
Contract Enforcement (from your test repo pipeline):
|
|
104
|
+
8. Checkout API repo in pipeline
|
|
105
|
+
9. npx xqt compare-openapi --current api-repo/openapi.yaml --snapshot openapi.snapshot.yaml
|
|
106
|
+
→ Fail pipeline on breaking changes
|
|
107
|
+
|
|
108
|
+
When a contract change is approved:
|
|
109
|
+
10. npx xqt update-snapshot --current api-repo/openapi.yaml --snapshot openapi.snapshot.yaml
|
|
110
|
+
→ Commit updated snapshot + raise PR to establish new baseline
|
|
106
111
|
```
|
|
107
112
|
|
|
108
113
|
## Files
|
|
@@ -111,7 +116,7 @@ npx xqt gen-tests --help
|
|
|
111
116
|
|------|---------|
|
|
112
117
|
| `tests.json` | Test definitions (source of truth) |
|
|
113
118
|
| `xray-mapping.json` | Maps test IDs to JIRA keys |
|
|
114
|
-
| `
|
|
119
|
+
| `playwright-results.json` | Playwright JSON report (CI output) |
|
|
115
120
|
| `.env` | Credentials (DO NOT COMMIT) |
|
|
116
121
|
| `.xrayrc` | Project config |
|
|
117
122
|
| `knowledge/` | API specs and requirements |
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
# Generated by @msalaam/xray-qe-toolkit
|
|
4
4
|
# ──────────────────────────────────────────────────────────────
|
|
5
5
|
#
|
|
6
|
-
# This pipeline runs
|
|
7
|
-
#
|
|
6
|
+
# This pipeline runs Playwright tests and imports the JSON results
|
|
7
|
+
# into Xray Cloud via xqt import-results.
|
|
8
8
|
#
|
|
9
9
|
# IMPORTANT: edit-json and QE review gates are NOT part of CI.
|
|
10
10
|
# Those steps must be run locally before committing.
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
# Required pipeline variables (set in Azure DevOps UI → Variables):
|
|
13
13
|
# XRAY_ID, XRAY_SECRET, JIRA_PROJECT_KEY, JIRA_URL,
|
|
14
14
|
# JIRA_API_TOKEN, JIRA_EMAIL, TEST_EXEC_KEY
|
|
15
|
+
#
|
|
16
|
+
# Optional pipeline variables:
|
|
17
|
+
# API_BASE_URL — base URL passed to Playwright tests
|
|
15
18
|
# ──────────────────────────────────────────────────────────────
|
|
16
19
|
|
|
17
20
|
trigger:
|
|
@@ -24,7 +27,7 @@ pool:
|
|
|
24
27
|
vmImage: "ubuntu-latest"
|
|
25
28
|
|
|
26
29
|
variables:
|
|
27
|
-
NODE_VERSION: "
|
|
30
|
+
NODE_VERSION: "20.x"
|
|
28
31
|
|
|
29
32
|
steps:
|
|
30
33
|
- task: NodeTool@0
|
|
@@ -37,17 +40,24 @@ steps:
|
|
|
37
40
|
displayName: "Install dependencies"
|
|
38
41
|
|
|
39
42
|
- script: |
|
|
40
|
-
npx
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
npx playwright install --with-deps chromium
|
|
44
|
+
displayName: "Install Playwright browsers"
|
|
45
|
+
|
|
46
|
+
- script: |
|
|
47
|
+
npx playwright test \
|
|
48
|
+
--reporter=list,json \
|
|
49
|
+
--output=playwright-results
|
|
50
|
+
displayName: "Run Playwright tests"
|
|
51
|
+
continueOnError: true
|
|
52
|
+
env:
|
|
53
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME: playwright-results.json
|
|
54
|
+
API_BASE_URL: $(API_BASE_URL)
|
|
45
55
|
|
|
46
56
|
- script: |
|
|
47
57
|
npx xqt import-results \
|
|
48
|
-
--file results.
|
|
58
|
+
--file playwright-results.json \
|
|
49
59
|
--testExecKey $(TEST_EXEC_KEY)
|
|
50
|
-
displayName: "Import results to Xray"
|
|
60
|
+
displayName: "Import Playwright results to Xray"
|
|
51
61
|
env:
|
|
52
62
|
XRAY_ID: $(XRAY_ID)
|
|
53
63
|
XRAY_SECRET: $(XRAY_SECRET)
|
|
@@ -57,9 +67,18 @@ steps:
|
|
|
57
67
|
JIRA_EMAIL: $(JIRA_EMAIL)
|
|
58
68
|
|
|
59
69
|
- task: PublishTestResults@2
|
|
60
|
-
displayName: "Publish JUnit results"
|
|
70
|
+
displayName: "Publish Playwright JUnit results"
|
|
71
|
+
condition: always()
|
|
61
72
|
inputs:
|
|
62
73
|
testResultsFormat: "JUnit"
|
|
63
|
-
testResultsFiles: "results.xml"
|
|
74
|
+
testResultsFiles: "playwright-results/results.xml"
|
|
64
75
|
mergeTestResults: true
|
|
65
76
|
failTaskOnFailedTests: true
|
|
77
|
+
|
|
78
|
+
- task: PublishPipelineArtifact@1
|
|
79
|
+
displayName: "Upload Playwright HTML report"
|
|
80
|
+
condition: always()
|
|
81
|
+
inputs:
|
|
82
|
+
targetPath: "playwright-report"
|
|
83
|
+
artifact: "playwright-report"
|
|
84
|
+
publishLocation: "pipeline"
|