@msalaam/xray-qe-toolkit 1.4.0 → 1.5.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/.env.example +23 -9
- package/README.md +768 -1434
- package/bin/cli.js +107 -73
- package/commands/createExecution.js +112 -23
- package/commands/createPlan.js +110 -0
- package/commands/editJson.js +1 -1
- package/commands/genPipeline.js +1 -1
- package/commands/importResults.js +107 -74
- package/commands/init.js +258 -70
- package/commands/pullTests.js +128 -0
- package/commands/pushTests.js +146 -43
- package/commands/status.js +108 -0
- package/commands/syncFolders.js +62 -0
- package/commands/validate.js +136 -0
- package/lib/config.js +50 -13
- package/lib/index.js +43 -4
- package/lib/playwrightConverter.js +91 -173
- package/lib/testCaseBuilder.js +198 -54
- package/lib/xrayClient.js +853 -202
- package/package.json +6 -8
- package/schema/business-rules.schema.json +110 -0
- package/schema/tests.schema.json +145 -19
- package/templates/README.template.md +570 -169
- package/templates/SPEC-DRIVEN-APPROACH.md +372 -0
- package/templates/azure-pipelines.yml +129 -77
- package/templates/business-rules.yaml +83 -0
- package/templates/resources-README.md +112 -0
- package/templates/tests.json +208 -37
- package/commands/compareOpenapi.js +0 -78
- package/commands/genPostman.js +0 -70
- package/commands/genTests.js +0 -138
- package/commands/updateSnapshot.js +0 -34
- package/lib/postmanGenerator.js +0 -304
- package/templates/knowledge-README.md +0 -121
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Resources — AI Test Generation Sources
|
|
2
|
+
|
|
3
|
+
This folder contains documentation and specifications used by the AI agent to generate test cases for Xray.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Folder Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
resources/
|
|
11
|
+
├── api-specs/ OpenAPI/Swagger specifications (YAML/JSON)
|
|
12
|
+
├── requirements/ Business requirements, acceptance criteria, logic docs
|
|
13
|
+
└── tickets/ Exported JIRA tickets, Confluence pages, user stories
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Supported File Types
|
|
19
|
+
|
|
20
|
+
### `api-specs/`
|
|
21
|
+
- **OpenAPI 3.x / Swagger 2.0** — `.yaml`, `.yml`, `.json`
|
|
22
|
+
- **GraphQL schemas** — `.graphql`, `.gql`
|
|
23
|
+
|
|
24
|
+
Files in this folder drive model, service, assertion, and spec generation via the spec-driven workflow.
|
|
25
|
+
|
|
26
|
+
### `requirements/`
|
|
27
|
+
- **Markdown** — `.md`
|
|
28
|
+
- **Plain text** — `.txt`
|
|
29
|
+
- **Word documents** — `.docx`
|
|
30
|
+
- **PDFs** — `.pdf`
|
|
31
|
+
|
|
32
|
+
Business logic, acceptance criteria, workflow descriptions, and domain rules. Used to generate test scenarios, edge cases, and validation logic.
|
|
33
|
+
|
|
34
|
+
### `tickets/`
|
|
35
|
+
- **JIRA exports** — `.json`, `.xml`
|
|
36
|
+
- **Confluence HTML exports** — `.html`
|
|
37
|
+
- **User stories** — `.md`, `.txt`
|
|
38
|
+
|
|
39
|
+
Ticket acceptance criteria and linked documentation. Used to ensure test coverage aligns with stories and epics.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## How the AI Agent Uses These Files
|
|
44
|
+
|
|
45
|
+
1. **Test Case Generation** (AI agent reads these files and populates `tests.json`)
|
|
46
|
+
- Reads `openapi.yaml` (API shape) and `business-rules.yaml` (domain rules)
|
|
47
|
+
- Produces `tests.json` entries with stable `test_id` values for Xray traceability
|
|
48
|
+
- Each `business-rules.yaml` rule maps 1:1 to a `tests.json` entry
|
|
49
|
+
- See **[SPEC-DRIVEN-APPROACH.md](../SPEC-DRIVEN-APPROACH.md)** for the full workflow
|
|
50
|
+
|
|
51
|
+
2. **Xray Sync** (after `tests.json` is generated)
|
|
52
|
+
- `npx xqt validate` then `npx xqt push-tests`
|
|
53
|
+
- Creates Xray Test issues + Test Sets in Jira
|
|
54
|
+
- Populates `xray-mapping.json` with issue keys
|
|
55
|
+
|
|
56
|
+
3. **Playwright Test Creation** (after Xray sync)
|
|
57
|
+
- Copilot skill reads `tests.json` + `xray-mapping.json`
|
|
58
|
+
- Creates one Playwright `test()` call per `tests.json` entry
|
|
59
|
+
- Real Jira keys in test titles from day one
|
|
60
|
+
|
|
61
|
+
4. **Continuous Refinement**
|
|
62
|
+
- As you add/update specs and business rules, re-run the generation workflow
|
|
63
|
+
- The toolkit is **idempotent** — existing tests are updated, retired rules get `skip: true`
|
|
64
|
+
- Always validate before pushing: `npx xqt validate` then `npx xqt push-tests`
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Best Practices
|
|
69
|
+
|
|
70
|
+
✅ **DO:**
|
|
71
|
+
- Keep specs up to date with your API implementation
|
|
72
|
+
- Organize files by feature/domain for easier tracking
|
|
73
|
+
- Use descriptive filenames (e.g., `auth-api-v2.yaml`, `payment-flows.md`)
|
|
74
|
+
- Commit this folder to source control (Git)
|
|
75
|
+
|
|
76
|
+
⚠️ **DON'T:**
|
|
77
|
+
- Store credentials or secrets in these files
|
|
78
|
+
- Include massive binary files (>10MB) — extract relevant sections
|
|
79
|
+
- Mix unrelated specs in the same file
|
|
80
|
+
- Forget to update specs when the API changes
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Example Workflow
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# 1. Add your OpenAPI spec
|
|
88
|
+
cp ~/Downloads/my-api.yaml resources/openapi.yaml
|
|
89
|
+
|
|
90
|
+
# 2. Populate business rules (or generate a draft from the spec)
|
|
91
|
+
# Use Copilot + SPEC-DRIVEN-APPROACH.md to generate resources/business-rules.yaml
|
|
92
|
+
|
|
93
|
+
# 3. Validate knowledge artifacts
|
|
94
|
+
npx xqt validate --schema business-rules
|
|
95
|
+
|
|
96
|
+
# 4. Use Copilot (spec-driven workflow) to generate models, services, assertions, and specs
|
|
97
|
+
# See SPEC-DRIVEN-APPROACH.md for the full step-by-step
|
|
98
|
+
|
|
99
|
+
# 5. Validate tests.json
|
|
100
|
+
npx xqt validate
|
|
101
|
+
|
|
102
|
+
# 6. Push tests to Xray
|
|
103
|
+
npx xqt push-tests
|
|
104
|
+
|
|
105
|
+
# 7. Run tests and import results
|
|
106
|
+
npx playwright test
|
|
107
|
+
npx xqt import-results --file test-results/results.json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
**Note:** This folder is the knowledge input for the spec-driven workflow. See **[SPEC-DRIVEN-APPROACH.md](../SPEC-DRIVEN-APPROACH.md)** for the complete greenfield/brownfield generation process.
|
package/templates/tests.json
CHANGED
|
@@ -1,74 +1,245 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"summary": "
|
|
4
|
-
|
|
2
|
+
"testPlan": {
|
|
3
|
+
"summary": "Death_Claims_Portfolio_API — Test Plan"
|
|
4
|
+
},
|
|
5
|
+
"planWorkflow": {
|
|
6
|
+
"mode": "dual-track",
|
|
7
|
+
"createPlan": {
|
|
8
|
+
"tool": "xqt",
|
|
9
|
+
"enabled": true
|
|
10
|
+
},
|
|
11
|
+
"linkTestSets": {
|
|
12
|
+
"mode": "auto"
|
|
13
|
+
},
|
|
14
|
+
"execution": {
|
|
15
|
+
"contractStage": "separate"
|
|
16
|
+
}
|
|
5
17
|
},
|
|
6
18
|
"tests": [
|
|
7
19
|
{
|
|
8
|
-
"test_id": "TC-
|
|
20
|
+
"test_id": "TC-DEATHCLA-BR-001",
|
|
21
|
+
"testKind": "business-rule",
|
|
9
22
|
"type": "api",
|
|
10
23
|
"skip": false,
|
|
11
|
-
"tags": ["
|
|
24
|
+
"tags": ["smoke", "regression"],
|
|
25
|
+
"folder": "/Death_Claims_Portfolio_API/HealthCheck/Validation",
|
|
26
|
+
"testSet": "Health Check",
|
|
27
|
+
"requirementKeys": [],
|
|
12
28
|
"xray": {
|
|
13
|
-
"summary": "
|
|
14
|
-
"
|
|
29
|
+
"summary": "Service returns 200 OK when healthy",
|
|
30
|
+
"testType": "Manual",
|
|
15
31
|
"priority": "High",
|
|
16
|
-
"labels": ["
|
|
32
|
+
"labels": ["smoke", "regression"],
|
|
17
33
|
"steps": [
|
|
18
34
|
{
|
|
19
|
-
"action": "Send GET
|
|
20
|
-
"data": "
|
|
21
|
-
"expected_result": "
|
|
22
|
-
}
|
|
35
|
+
"action": "Send GET /health with no request body",
|
|
36
|
+
"data": "",
|
|
37
|
+
"expected_result": "HTTP 200, Content-Type: application/json, body: { \"status\": \"ok\" }"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"spec": {
|
|
42
|
+
"operation": {
|
|
43
|
+
"method": "GET",
|
|
44
|
+
"path": "/health",
|
|
45
|
+
"operationId": ""
|
|
46
|
+
},
|
|
47
|
+
"expectations": {
|
|
48
|
+
"statusCodes": [200],
|
|
49
|
+
"contentType": "application/json",
|
|
50
|
+
"responseSchemaHints": []
|
|
51
|
+
},
|
|
52
|
+
"dataHints": {
|
|
53
|
+
"pathParams": [],
|
|
54
|
+
"queryParams": [],
|
|
55
|
+
"body": "",
|
|
56
|
+
"env": []
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"test_id": "TC-DEATHCLA-BR-002",
|
|
62
|
+
"testKind": "business-rule",
|
|
63
|
+
"type": "api",
|
|
64
|
+
"skip": false,
|
|
65
|
+
"tags": ["smoke", "regression"],
|
|
66
|
+
"folder": "/Death_Claims_Portfolio_API/ClientLookup/BusinessLogic",
|
|
67
|
+
"testSet": "Client Lookup",
|
|
68
|
+
"requirementKeys": [],
|
|
69
|
+
"xray": {
|
|
70
|
+
"summary": "Valid client number returns portfolio details",
|
|
71
|
+
"testType": "Manual",
|
|
72
|
+
"priority": "High",
|
|
73
|
+
"labels": ["smoke", "regression"],
|
|
74
|
+
"steps": [
|
|
23
75
|
{
|
|
24
|
-
"action": "
|
|
25
|
-
"data": "
|
|
26
|
-
"expected_result": "
|
|
76
|
+
"action": "Send GET /v1/death-claims/portfolio/{clientNo} with a valid client number",
|
|
77
|
+
"data": "clientNo: env.TEST_CLIENT_NO",
|
|
78
|
+
"expected_result": "HTTP 200, Content-Type: application/json, body matches ED43Response schema"
|
|
27
79
|
}
|
|
28
80
|
]
|
|
81
|
+
},
|
|
82
|
+
"spec": {
|
|
83
|
+
"operation": {
|
|
84
|
+
"method": "GET",
|
|
85
|
+
"path": "/v1/death-claims/portfolio/{clientNo}",
|
|
86
|
+
"operationId": "getPortfolioByClientNo"
|
|
87
|
+
},
|
|
88
|
+
"expectations": {
|
|
89
|
+
"statusCodes": [200],
|
|
90
|
+
"contentType": "application/json",
|
|
91
|
+
"responseSchemaHints": ["#/components/schemas/ED43Response"]
|
|
92
|
+
},
|
|
93
|
+
"dataHints": {
|
|
94
|
+
"pathParams": ["clientNo"],
|
|
95
|
+
"queryParams": [],
|
|
96
|
+
"body": "",
|
|
97
|
+
"env": ["TEST_CLIENT_NO"]
|
|
98
|
+
}
|
|
29
99
|
}
|
|
30
100
|
},
|
|
31
101
|
{
|
|
32
|
-
"test_id": "TC-
|
|
102
|
+
"test_id": "TC-DEATHCLA-CT-GET-V1_DEATH_CLAIMS_PORTFOLIO_CLIENTNO-200",
|
|
103
|
+
"testKind": "contract",
|
|
33
104
|
"type": "api",
|
|
34
105
|
"skip": false,
|
|
35
|
-
"tags": ["regression", "
|
|
106
|
+
"tags": ["regression", "integration"],
|
|
107
|
+
"folder": "/Death_Claims_Portfolio_API/Contract/ClientLookup",
|
|
108
|
+
"testSet": "Contract - Client Lookup",
|
|
109
|
+
"requirementKeys": [],
|
|
36
110
|
"xray": {
|
|
37
|
-
"summary": "
|
|
38
|
-
"
|
|
111
|
+
"summary": "Contract: GET /v1/death-claims/portfolio/{clientNo} → 200",
|
|
112
|
+
"testType": "Manual",
|
|
39
113
|
"priority": "Medium",
|
|
40
|
-
"labels": ["
|
|
114
|
+
"labels": ["regression", "integration"],
|
|
41
115
|
"steps": [
|
|
42
116
|
{
|
|
43
|
-
"action": "Send
|
|
44
|
-
"data": "
|
|
45
|
-
"expected_result": "
|
|
46
|
-
}
|
|
117
|
+
"action": "Send GET /v1/death-claims/portfolio/{clientNo} with a valid client number",
|
|
118
|
+
"data": "clientNo: env.TEST_CLIENT_NO",
|
|
119
|
+
"expected_result": "HTTP 200, Content-Type: application/json, response validates against ED43Response schema (status-code, content-type, required-fields, type-checks)"
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
"spec": {
|
|
124
|
+
"operation": {
|
|
125
|
+
"method": "GET",
|
|
126
|
+
"path": "/v1/death-claims/portfolio/{clientNo}",
|
|
127
|
+
"operationId": "getPortfolioByClientNo"
|
|
128
|
+
},
|
|
129
|
+
"expectations": {
|
|
130
|
+
"statusCodes": [200],
|
|
131
|
+
"contentType": "application/json",
|
|
132
|
+
"responseSchemaHints": ["#/components/schemas/ED43Response"]
|
|
133
|
+
},
|
|
134
|
+
"dataHints": {
|
|
135
|
+
"pathParams": ["clientNo"],
|
|
136
|
+
"queryParams": [],
|
|
137
|
+
"body": "",
|
|
138
|
+
"env": ["TEST_CLIENT_NO"]
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"contract": {
|
|
142
|
+
"operationId": "getPortfolioByClientNo",
|
|
143
|
+
"expectedStatus": 200,
|
|
144
|
+
"expectedContentType": "application/json",
|
|
145
|
+
"responseSchemaRef": "#/components/schemas/ED43Response",
|
|
146
|
+
"validationScope": ["status-code", "content-type", "required-fields", "type-checks"]
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"test_id": "TC-DEATHCLA-CT-GET-V1_DEATH_CLAIMS_PORTFOLIO_CLIENTNO-400",
|
|
151
|
+
"testKind": "contract",
|
|
152
|
+
"type": "api",
|
|
153
|
+
"skip": false,
|
|
154
|
+
"tags": ["regression", "integration"],
|
|
155
|
+
"folder": "/Death_Claims_Portfolio_API/Contract/ClientLookup",
|
|
156
|
+
"testSet": "Contract - Client Lookup",
|
|
157
|
+
"requirementKeys": [],
|
|
158
|
+
"xray": {
|
|
159
|
+
"summary": "Contract: GET /v1/death-claims/portfolio/{clientNo} → 400",
|
|
160
|
+
"testType": "Manual",
|
|
161
|
+
"priority": "High",
|
|
162
|
+
"labels": ["regression", "integration"],
|
|
163
|
+
"steps": [
|
|
47
164
|
{
|
|
48
|
-
"action": "
|
|
49
|
-
"data": "
|
|
50
|
-
"expected_result": "
|
|
165
|
+
"action": "Send GET /v1/death-claims/portfolio/{clientNo} with an invalid client number format",
|
|
166
|
+
"data": "clientNo: env.TEST_INVALID_CLIENT_NO",
|
|
167
|
+
"expected_result": "HTTP 400, Content-Type: application/json, response validates against ErrorResponse schema (status-code, content-type, required-fields, type-checks)"
|
|
51
168
|
}
|
|
52
169
|
]
|
|
170
|
+
},
|
|
171
|
+
"spec": {
|
|
172
|
+
"operation": {
|
|
173
|
+
"method": "GET",
|
|
174
|
+
"path": "/v1/death-claims/portfolio/{clientNo}",
|
|
175
|
+
"operationId": "getPortfolioByClientNo"
|
|
176
|
+
},
|
|
177
|
+
"expectations": {
|
|
178
|
+
"statusCodes": [400],
|
|
179
|
+
"contentType": "application/json",
|
|
180
|
+
"responseSchemaHints": ["#/components/schemas/ErrorResponse"]
|
|
181
|
+
},
|
|
182
|
+
"dataHints": {
|
|
183
|
+
"pathParams": ["clientNo"],
|
|
184
|
+
"queryParams": [],
|
|
185
|
+
"body": "",
|
|
186
|
+
"env": ["TEST_INVALID_CLIENT_NO"]
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"contract": {
|
|
190
|
+
"operationId": "getPortfolioByClientNo",
|
|
191
|
+
"expectedStatus": 400,
|
|
192
|
+
"expectedContentType": "application/json",
|
|
193
|
+
"responseSchemaRef": "#/components/schemas/ErrorResponse",
|
|
194
|
+
"validationScope": ["status-code", "content-type", "required-fields", "type-checks"]
|
|
53
195
|
}
|
|
54
196
|
},
|
|
55
197
|
{
|
|
56
|
-
"test_id": "TC-
|
|
198
|
+
"test_id": "TC-DEATHCLA-CT-GET-V1_DEATH_CLAIMS_PORTFOLIO_CLIENTNO-404",
|
|
199
|
+
"testKind": "contract",
|
|
57
200
|
"type": "api",
|
|
58
|
-
"skip":
|
|
59
|
-
"tags": ["
|
|
201
|
+
"skip": false,
|
|
202
|
+
"tags": ["regression", "integration"],
|
|
203
|
+
"folder": "/Death_Claims_Portfolio_API/Contract/ClientLookup",
|
|
204
|
+
"testSet": "Contract - Client Lookup",
|
|
205
|
+
"requirementKeys": [],
|
|
60
206
|
"xray": {
|
|
61
|
-
"summary": "
|
|
62
|
-
"
|
|
63
|
-
"priority": "
|
|
64
|
-
"labels": ["
|
|
207
|
+
"summary": "Contract: GET /v1/death-claims/portfolio/{clientNo} → 404",
|
|
208
|
+
"testType": "Manual",
|
|
209
|
+
"priority": "High",
|
|
210
|
+
"labels": ["regression", "integration"],
|
|
65
211
|
"steps": [
|
|
66
212
|
{
|
|
67
|
-
"action": "Send
|
|
68
|
-
"data": "
|
|
69
|
-
"expected_result": "
|
|
213
|
+
"action": "Send GET /v1/death-claims/portfolio/{clientNo} with an unknown client number",
|
|
214
|
+
"data": "clientNo: env.TEST_UNKNOWN_CLIENT_NO",
|
|
215
|
+
"expected_result": "HTTP 404, Content-Type: application/json, response validates against ErrorResponse schema (status-code, content-type, required-fields, type-checks)"
|
|
70
216
|
}
|
|
71
217
|
]
|
|
218
|
+
},
|
|
219
|
+
"spec": {
|
|
220
|
+
"operation": {
|
|
221
|
+
"method": "GET",
|
|
222
|
+
"path": "/v1/death-claims/portfolio/{clientNo}",
|
|
223
|
+
"operationId": "getPortfolioByClientNo"
|
|
224
|
+
},
|
|
225
|
+
"expectations": {
|
|
226
|
+
"statusCodes": [404],
|
|
227
|
+
"contentType": "application/json",
|
|
228
|
+
"responseSchemaHints": ["#/components/schemas/ErrorResponse"]
|
|
229
|
+
},
|
|
230
|
+
"dataHints": {
|
|
231
|
+
"pathParams": ["clientNo"],
|
|
232
|
+
"queryParams": [],
|
|
233
|
+
"body": "",
|
|
234
|
+
"env": ["TEST_UNKNOWN_CLIENT_NO"]
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
"contract": {
|
|
238
|
+
"operationId": "getPortfolioByClientNo",
|
|
239
|
+
"expectedStatus": 404,
|
|
240
|
+
"expectedContentType": "application/json",
|
|
241
|
+
"responseSchemaRef": "#/components/schemas/ErrorResponse",
|
|
242
|
+
"validationScope": ["status-code", "content-type", "required-fields", "type-checks"]
|
|
72
243
|
}
|
|
73
244
|
}
|
|
74
245
|
]
|
|
@@ -1,78 +0,0 @@
|
|
|
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/genPostman.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Command: xray-qe gen-postman [--base-url <url>] [--ai] [--spec <path>] [--knowledge <path>]
|
|
3
|
-
*
|
|
4
|
-
* Generates a Postman Collection v2.1 JSON file from tests.json.
|
|
5
|
-
* Optionally uses xray-mapping.json to embed JIRA keys for better traceability.
|
|
6
|
-
* With --ai flag, enables AI-assisted generation (requires connected provider).
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import fs from "node:fs";
|
|
10
|
-
import logger, { setVerbose } from "../lib/logger.js";
|
|
11
|
-
import { loadConfig } from "../lib/config.js";
|
|
12
|
-
import { generate } from "../lib/postmanGenerator.js";
|
|
13
|
-
import { loadMapping } from "../lib/testCaseBuilder.js";
|
|
14
|
-
|
|
15
|
-
export default async function genPostman(opts = {}) {
|
|
16
|
-
if (opts.verbose) setVerbose(true);
|
|
17
|
-
|
|
18
|
-
logger.rocket("@msalaam/xray-qe-toolkit — Generate Postman Collection\n");
|
|
19
|
-
|
|
20
|
-
const cfg = loadConfig({ envPath: opts.env });
|
|
21
|
-
|
|
22
|
-
if (!fs.existsSync(cfg.testsPath)) {
|
|
23
|
-
logger.error(`tests.json not found at ${cfg.testsPath}`);
|
|
24
|
-
logger.info("Run 'npx xray-qe init' to create a starter tests.json");
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const testsConfig = JSON.parse(fs.readFileSync(cfg.testsPath, "utf8"));
|
|
29
|
-
|
|
30
|
-
// Load mapping to embed JIRA keys when available
|
|
31
|
-
const mapping = loadMapping(cfg.mappingPath);
|
|
32
|
-
|
|
33
|
-
// Filter out skipped tests
|
|
34
|
-
const filteredConfig = {
|
|
35
|
-
...testsConfig,
|
|
36
|
-
tests: (testsConfig.tests || []).filter((t) => !t.skip),
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// AI flag handling
|
|
40
|
-
if (opts.ai) {
|
|
41
|
-
logger.info("🤖 AI-assisted generation enabled");
|
|
42
|
-
logger.warn("AI provider not yet connected — using schema-driven generation as fallback");
|
|
43
|
-
logger.blank();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Knowledge path override
|
|
47
|
-
const knowledgePath = opts.knowledge || cfg.knowledgePath;
|
|
48
|
-
if (opts.knowledge) {
|
|
49
|
-
logger.info(`Using custom knowledge path: ${knowledgePath}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Spec file handling (schema-driven generation without AI)
|
|
53
|
-
if (opts.spec && fs.existsSync(opts.spec)) {
|
|
54
|
-
logger.info(`Using OpenAPI spec: ${opts.spec}`);
|
|
55
|
-
logger.info("📝 Schema-driven endpoint inference enabled");
|
|
56
|
-
// TODO: Parse OpenAPI spec and enhance endpoint/method/body inference
|
|
57
|
-
logger.blank();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
generate(filteredConfig, cfg.collectionPath, {
|
|
61
|
-
baseUrl: opts.baseUrl || "{{baseUrl}}",
|
|
62
|
-
mapping: mapping,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
logger.blank();
|
|
66
|
-
logger.success(`Collection saved to ${cfg.collectionPath}`);
|
|
67
|
-
logger.info("Import into Postman or run with Newman:");
|
|
68
|
-
console.log(` npx newman run ${cfg.collectionPath}`);
|
|
69
|
-
logger.blank();
|
|
70
|
-
}
|