@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
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
|
|
@@ -7,17 +7,18 @@
|
|
|
7
7
|
* npx xqt <command> [options]
|
|
8
8
|
*
|
|
9
9
|
* Commands:
|
|
10
|
-
* init
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
10
|
+
* init Scaffold project structure (tests/, playwright.config.ts, tests.json, .xrayrc)
|
|
11
|
+
* edit Launch browser-based editor for tests.json
|
|
12
|
+
* push Push/update tests in Xray Cloud + sync Test Plan + sync folders
|
|
13
|
+
* pull Pull test definitions from Xray Cloud into tests.json
|
|
14
|
+
* exec Create a new Test Execution with a specific set of tests
|
|
15
|
+
* import Import test results (JUnit XML or Playwright JSON) — new execution per run
|
|
16
|
+
* plan Create a new Xray Test Plan in JIRA
|
|
17
|
+
* sync Sync Xray repository folder structure from tests.json folder fields
|
|
18
|
+
* status Show local + remote project status summary
|
|
19
|
+
* validate Validate tests.json against its schema
|
|
20
|
+
* pipeline Generate Azure Pipelines YAML
|
|
21
|
+
* mcp Start the MCP server for GitHub Copilot agent-mode integration
|
|
21
22
|
*/
|
|
22
23
|
|
|
23
24
|
import { Command } from "commander";
|
|
@@ -32,8 +33,8 @@ const pkg = JSON.parse(readFileSync(path.join(__dirname, "..", "package.json"),
|
|
|
32
33
|
const program = new Command();
|
|
33
34
|
|
|
34
35
|
program
|
|
35
|
-
.name("
|
|
36
|
-
.description("QE workflow toolkit for Xray Cloud — test management,
|
|
36
|
+
.name("xqt")
|
|
37
|
+
.description("QE workflow toolkit for Xray Cloud — test management, CI integration, and test planning")
|
|
37
38
|
.version(pkg.version)
|
|
38
39
|
.option("--verbose", "Enable debug output")
|
|
39
40
|
.option("--env <path>", "Custom path to .env file");
|
|
@@ -42,121 +43,154 @@ program
|
|
|
42
43
|
|
|
43
44
|
program
|
|
44
45
|
.command("init")
|
|
45
|
-
.description(
|
|
46
|
+
.description(
|
|
47
|
+
"Scaffold a QA project: resources/, tests/, playwright.config.ts, tests.json, .xrayrc, XQT-GUIDE.md"
|
|
48
|
+
)
|
|
46
49
|
.action(async (opts, cmd) => {
|
|
47
50
|
const mod = await import("../commands/init.js");
|
|
48
51
|
await mod.default(cmd.optsWithGlobals());
|
|
49
52
|
});
|
|
50
53
|
|
|
51
54
|
program
|
|
52
|
-
.command("
|
|
53
|
-
.description("
|
|
54
|
-
.option("--
|
|
55
|
-
.option("--spec <path>", "OpenAPI/Swagger spec file to analyze")
|
|
56
|
-
.option("--knowledge <path>", "Custom knowledge folder path")
|
|
57
|
-
.option("--ticket <key>", "JIRA ticket key to fetch and analyze")
|
|
58
|
-
.option("--prompt <text>", "Natural language prompt for test generation")
|
|
55
|
+
.command("edit")
|
|
56
|
+
.description("Launch browser-based editor for tests.json")
|
|
57
|
+
.option("--port <number>", "Port for local editor server", "0")
|
|
59
58
|
.action(async (opts, cmd) => {
|
|
60
|
-
const mod = await import("../commands/
|
|
59
|
+
const mod = await import("../commands/editJson.js");
|
|
61
60
|
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
62
61
|
});
|
|
63
62
|
|
|
64
63
|
program
|
|
65
|
-
.command("
|
|
66
|
-
.
|
|
67
|
-
.
|
|
64
|
+
.command("push")
|
|
65
|
+
.alias("push-tests")
|
|
66
|
+
.description(
|
|
67
|
+
"Create/update tests in Xray Cloud, sync to Test Plan, sync folder structure"
|
|
68
|
+
)
|
|
69
|
+
.option("--plan <key>", "Test Plan key (overrides .xrayrc testPlanKey)")
|
|
70
|
+
.option("--create-execution", "Create a Test Execution linked to the Test Plan after pushing")
|
|
71
|
+
.option("--execution-env <label>", "Environment label for created execution (e.g. IOP-QA)")
|
|
72
|
+
.option("--execution-summary <text>", "Custom summary for created execution")
|
|
68
73
|
.action(async (opts, cmd) => {
|
|
69
|
-
const mod = await import("../commands/
|
|
74
|
+
const mod = await import("../commands/pushTests.js");
|
|
70
75
|
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
71
76
|
});
|
|
72
77
|
|
|
73
78
|
program
|
|
74
|
-
.command("
|
|
75
|
-
.
|
|
76
|
-
.
|
|
77
|
-
.option("--
|
|
79
|
+
.command("pull")
|
|
80
|
+
.alias("pull-tests")
|
|
81
|
+
.description("Pull test definitions from Xray Cloud into tests.json")
|
|
82
|
+
.option("--plan <key>", "Fetch tests from a specific Test Plan")
|
|
83
|
+
.option("--project <key>", "Fetch all tests in a JIRA project")
|
|
84
|
+
.option("--limit <n>", "Maximum number of tests to fetch (default: 100)")
|
|
78
85
|
.action(async (opts, cmd) => {
|
|
79
|
-
const mod = await import("../commands/
|
|
86
|
+
const mod = await import("../commands/pullTests.js");
|
|
80
87
|
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
81
88
|
});
|
|
82
89
|
|
|
83
90
|
program
|
|
84
|
-
.command("
|
|
85
|
-
.
|
|
86
|
-
.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
.command("exec")
|
|
92
|
+
.alias("create-execution")
|
|
93
|
+
.description(
|
|
94
|
+
"Create a new Test Execution with a specific set of tests. " +
|
|
95
|
+
"Use --quiet to print only the execution key for CI variable capture: " +
|
|
96
|
+
"EXEC_KEY=$(xqt exec --env IOP-QA --quiet)"
|
|
97
|
+
)
|
|
98
|
+
.option("--env <environment>", "Environment label (e.g. IOP-QA, IOP-PROD)")
|
|
99
|
+
.option("--plan <key>", "Test Plan key to link the execution to (overrides .xrayrc)")
|
|
100
|
+
.option("--tests <ids>", "Comma-separated testIds or JIRA keys to include (default: all mapped tests)")
|
|
101
|
+
.option("--summary <text>", "Custom Test Execution summary")
|
|
102
|
+
.option("--description <text>", "Custom description")
|
|
103
|
+
.option("--quiet", "Print ONLY the execution key (for CI variable capture)")
|
|
90
104
|
.action(async (opts, cmd) => {
|
|
91
|
-
const mod = await import("../commands/
|
|
105
|
+
const mod = await import("../commands/createExecution.js");
|
|
92
106
|
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
93
107
|
});
|
|
94
108
|
|
|
95
109
|
program
|
|
96
|
-
.command("
|
|
97
|
-
.
|
|
98
|
-
.
|
|
99
|
-
|
|
100
|
-
|
|
110
|
+
.command("import")
|
|
111
|
+
.alias("import-results")
|
|
112
|
+
.description(
|
|
113
|
+
"Import test results into Xray Cloud — creates a NEW Test Execution per run (JUnit XML or Playwright JSON)"
|
|
114
|
+
)
|
|
115
|
+
.requiredOption("--file <path>", "Path to results file (.json for Playwright, .xml for JUnit)")
|
|
116
|
+
.option("--env <environment>", "Test environment label (e.g. IOP-DEV, IOP-QA, IOP-PROD)")
|
|
117
|
+
.option("--plan <key>", "Test Plan key to associate execution with (overrides .xrayrc)")
|
|
118
|
+
.option("--exec <key>", "Import INTO an existing Test Execution key (from xqt exec)")
|
|
119
|
+
.option("--version <version>", "Fix version / release version")
|
|
120
|
+
.option("--revision <revision>", "Revision / build number")
|
|
121
|
+
.option("--summary <text>", "Custom Test Execution summary")
|
|
122
|
+
.option("--description <text>", "Custom Test Execution description")
|
|
101
123
|
.action(async (opts, cmd) => {
|
|
102
|
-
const mod = await import("../commands/
|
|
124
|
+
const mod = await import("../commands/importResults.js");
|
|
103
125
|
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
104
126
|
});
|
|
105
127
|
|
|
106
128
|
program
|
|
107
|
-
.command("
|
|
108
|
-
.
|
|
109
|
-
.
|
|
110
|
-
.
|
|
111
|
-
.option("--
|
|
112
|
-
.option("--
|
|
129
|
+
.command("plan")
|
|
130
|
+
.alias("create-plan")
|
|
131
|
+
.description("Create a new Test Plan in JIRA and save its key to .xrayrc and tests.json")
|
|
132
|
+
.requiredOption("--summary <text>", "Test Plan summary / title")
|
|
133
|
+
.option("--version <version>", "Fix version to associate with the plan")
|
|
134
|
+
.option("--label <labels>", "Comma-separated labels to apply")
|
|
113
135
|
.action(async (opts, cmd) => {
|
|
114
|
-
const mod = await import("../commands/
|
|
136
|
+
const mod = await import("../commands/createPlan.js");
|
|
115
137
|
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
116
138
|
});
|
|
117
139
|
|
|
118
140
|
program
|
|
119
|
-
.command("
|
|
120
|
-
.
|
|
121
|
-
.
|
|
141
|
+
.command("sync")
|
|
142
|
+
.alias("sync-folders")
|
|
143
|
+
.description("Sync Xray repository folder structure from the folder field in tests.json")
|
|
122
144
|
.action(async (opts, cmd) => {
|
|
123
|
-
const mod = await import("../commands/
|
|
145
|
+
const mod = await import("../commands/syncFolders.js");
|
|
124
146
|
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
125
147
|
});
|
|
126
148
|
|
|
127
149
|
program
|
|
128
|
-
.command("
|
|
129
|
-
.description("
|
|
130
|
-
.option("--
|
|
150
|
+
.command("status")
|
|
151
|
+
.description("Show local and remote project status (tests.json, mapping, Test Plan)")
|
|
152
|
+
.option("--plan <key>", "Test Plan key to check (overrides .xrayrc)")
|
|
131
153
|
.action(async (opts, cmd) => {
|
|
132
|
-
const mod = await import("../
|
|
154
|
+
const mod = await import("../commands/status.js");
|
|
155
|
+
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
program
|
|
159
|
+
.command("validate")
|
|
160
|
+
.description("Validate tests.json and business-rules.yaml against their schemas (exits 1 on errors)")
|
|
161
|
+
.option("--file <path>", "Override path to tests.json")
|
|
162
|
+
.option("--schema <name>", "Validate only one schema: tests | business-rules | all (default: all)")
|
|
163
|
+
.action(async (opts, cmd) => {
|
|
164
|
+
const mod = await import("../commands/validate.js");
|
|
133
165
|
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
134
166
|
});
|
|
135
167
|
|
|
136
168
|
program
|
|
137
|
-
.command("
|
|
138
|
-
.
|
|
139
|
-
.
|
|
140
|
-
.
|
|
141
|
-
.option("--report <path>", "Write a JSON diff report to this path (useful as a pipeline artifact)")
|
|
169
|
+
.command("pipeline")
|
|
170
|
+
.alias("gen-pipeline")
|
|
171
|
+
.description("Generate an Azure Pipelines YAML template")
|
|
172
|
+
.option("--output <path>", "Output file path", "azure-pipelines.yml")
|
|
142
173
|
.action(async (opts, cmd) => {
|
|
143
|
-
const mod = await import("../commands/
|
|
174
|
+
const mod = await import("../commands/genPipeline.js");
|
|
144
175
|
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
145
176
|
});
|
|
146
177
|
|
|
147
178
|
program
|
|
148
|
-
.command("
|
|
149
|
-
.
|
|
150
|
-
.
|
|
151
|
-
|
|
179
|
+
.command("mcp")
|
|
180
|
+
.alias("mcp-server")
|
|
181
|
+
.description(
|
|
182
|
+
"Start the MCP server for GitHub Copilot agent-mode integration. " +
|
|
183
|
+
"Runs in stdio mode by default (for VS Code), or HTTP mode with --port."
|
|
184
|
+
)
|
|
185
|
+
.option("--port <number>", "Run in HTTP / StreamableHTTP mode on the given port")
|
|
152
186
|
.action(async (opts, cmd) => {
|
|
153
|
-
const mod = await import("../
|
|
187
|
+
const mod = await import("../mcp/server.js");
|
|
154
188
|
await mod.default({ ...opts, ...cmd.optsWithGlobals() });
|
|
155
189
|
});
|
|
156
190
|
|
|
157
191
|
// ─── Parse & run ───────────────────────────────────────────────────────────────
|
|
158
192
|
|
|
159
193
|
program.parseAsync(process.argv).catch((err) => {
|
|
160
|
-
console.error(`\n
|
|
194
|
+
console.error(`\n${err.message}\n`);
|
|
161
195
|
process.exit(1);
|
|
162
|
-
});
|
|
196
|
+
});
|
|
@@ -1,46 +1,135 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Command:
|
|
2
|
+
* Command: xqt create-execution
|
|
3
3
|
*
|
|
4
|
-
* Creates a
|
|
5
|
-
*
|
|
4
|
+
* Creates a new Xray Test Execution in JIRA with a defined set of tests.
|
|
5
|
+
*
|
|
6
|
+
* Use this when you want to PRE-CREATE an execution (to control which tests
|
|
7
|
+
* are included, set fields up-front, or know the execution key before running).
|
|
8
|
+
* Then run your tests and import results with:
|
|
9
|
+
* xqt import-results --file results.json --exec <key>
|
|
10
|
+
*
|
|
11
|
+
* Alternatively, skip this command entirely — `xqt import-results` without
|
|
12
|
+
* --exec will auto-create a fresh execution from the results automatically.
|
|
13
|
+
*
|
|
14
|
+
* Usage examples:
|
|
15
|
+
* # Create from all tests in the Test Plan (default):
|
|
16
|
+
* xqt create-execution --env IOP-QA
|
|
17
|
+
*
|
|
18
|
+
* # Create from a specific plan with a custom summary:
|
|
19
|
+
* xqt create-execution --env IOP-PROD --plan APIEE-1234 --summary "Release 2.5 smoke"
|
|
20
|
+
*
|
|
21
|
+
* # Create and only include specific tests (comma-separated testIds):
|
|
22
|
+
* xqt create-execution --env IOP-QA --tests TC-API-001,TC-API-002,TC-API-003
|
|
23
|
+
*
|
|
24
|
+
* # Print only the execution key (for CI variable capture):
|
|
25
|
+
* EXEC_KEY=$(xqt create-execution --env IOP-QA --quiet)
|
|
6
26
|
*/
|
|
7
27
|
|
|
28
|
+
import fs from "node:fs";
|
|
8
29
|
import logger, { setVerbose } from "../lib/logger.js";
|
|
9
30
|
import { loadConfig, validateConfig } from "../lib/config.js";
|
|
10
|
-
import {
|
|
31
|
+
import { authenticate, createTestExecution, getIssue } from "../lib/xrayClient.js";
|
|
32
|
+
import { loadMapping } from "../lib/testCaseBuilder.js";
|
|
11
33
|
|
|
12
34
|
export default async function createExecution(opts = {}) {
|
|
13
35
|
if (opts.verbose) setVerbose(true);
|
|
14
36
|
|
|
15
|
-
|
|
37
|
+
const quiet = opts.quiet || false;
|
|
38
|
+
|
|
39
|
+
if (!quiet) logger.rocket("@msalaam/xray-qe-toolkit — Create Test Execution\n");
|
|
16
40
|
|
|
17
41
|
const cfg = loadConfig({ envPath: opts.env });
|
|
18
42
|
validateConfig(cfg);
|
|
19
43
|
|
|
20
|
-
|
|
44
|
+
// ── Resolve environment ────────────────────────────────────────────────────
|
|
45
|
+
const environment = opts.environment || cfg.defaultEnvironment || "IOP-DEV";
|
|
46
|
+
const environments = [environment];
|
|
21
47
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
48
|
+
// ── Resolve plan key ───────────────────────────────────────────────────────
|
|
49
|
+
let planKey = opts.plan || cfg.testPlanKey;
|
|
50
|
+
if (!opts.plan) {
|
|
51
|
+
// Also check tests.json for testPlan.key
|
|
52
|
+
if (fs.existsSync(cfg.testsPath)) {
|
|
53
|
+
const testsConfig = JSON.parse(fs.readFileSync(cfg.testsPath, "utf8"));
|
|
54
|
+
planKey = planKey || testsConfig.testPlan?.key;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
29
57
|
|
|
30
|
-
|
|
31
|
-
|
|
58
|
+
if (!quiet) {
|
|
59
|
+
logger.info(`Environment: ${environment}`);
|
|
60
|
+
if (planKey) logger.info(`Test Plan: ${planKey}`);
|
|
32
61
|
logger.blank();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ── Authenticate ───────────────────────────────────────────────────────────
|
|
65
|
+
const xrayToken = await authenticate(cfg);
|
|
66
|
+
|
|
67
|
+
// ── Resolve which tests to include ────────────────────────────────────────
|
|
68
|
+
let testIssueIds = [];
|
|
69
|
+
|
|
70
|
+
if (opts.tests) {
|
|
71
|
+
// User specified specific test IDs (local testId values or JIRA keys)
|
|
72
|
+
const mapping = loadMapping(cfg.mappingPath);
|
|
73
|
+
const requestedIds = opts.tests.split(",").map((s) => s.trim());
|
|
74
|
+
|
|
75
|
+
for (const id of requestedIds) {
|
|
76
|
+
const entry = mapping[id];
|
|
77
|
+
if (entry?.id) {
|
|
78
|
+
testIssueIds.push(String(entry.id));
|
|
79
|
+
} else {
|
|
80
|
+
// Might be a JIRA key — look it up
|
|
81
|
+
try {
|
|
82
|
+
const issue = await getIssue(cfg, id);
|
|
83
|
+
if (issue?.id) testIssueIds.push(String(issue.id));
|
|
84
|
+
else if (!quiet) logger.warn(`Could not resolve test ID: ${id}`);
|
|
85
|
+
} catch {
|
|
86
|
+
if (!quiet) logger.warn(`Skipping unresolvable test ID: ${id}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
39
89
|
}
|
|
90
|
+
|
|
91
|
+
if (!quiet) logger.info(`Using ${testIssueIds.length}/${requestedIds.length} specified test(s)`);
|
|
92
|
+
} else if (planKey) {
|
|
93
|
+
// Default: pull all test IDs from the plan via mapping
|
|
94
|
+
const mapping = loadMapping(cfg.mappingPath);
|
|
95
|
+
testIssueIds = Object.values(mapping)
|
|
96
|
+
.filter((v) => v.id && !String(v.id).startsWith("_"))
|
|
97
|
+
.map((v) => String(v.id));
|
|
98
|
+
|
|
99
|
+
if (!quiet) logger.info(`Using all ${testIssueIds.length} mapped test(s) from plan`);
|
|
40
100
|
}
|
|
41
101
|
|
|
102
|
+
// ── Build summary ──────────────────────────────────────────────────────────
|
|
103
|
+
const summary =
|
|
104
|
+
opts.summary ||
|
|
105
|
+
`${environment} Test Run — ${new Date().toLocaleString()}`;
|
|
106
|
+
|
|
107
|
+
// ── Create execution ───────────────────────────────────────────────────────
|
|
108
|
+
if (!quiet) logger.send("Creating Test Execution...");
|
|
109
|
+
|
|
110
|
+
const { key, id } = await createTestExecution(cfg, xrayToken, {
|
|
111
|
+
summary,
|
|
112
|
+
description: opts.description,
|
|
113
|
+
environments,
|
|
114
|
+
testIssueIds,
|
|
115
|
+
testPlanKey: planKey || undefined,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (quiet) {
|
|
119
|
+
// Output only the key — suitable for CI variable capture:
|
|
120
|
+
// EXEC_KEY=$(xqt create-execution --env IOP-QA --quiet)
|
|
121
|
+
process.stdout.write(key + "\n");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
logger.blank();
|
|
126
|
+
logger.success(`Test Execution created: ${key}`);
|
|
127
|
+
logger.link(`${cfg.jiraUrl}/browse/${key}`);
|
|
42
128
|
logger.blank();
|
|
43
|
-
|
|
44
|
-
console.log(
|
|
129
|
+
|
|
130
|
+
console.log("Next steps:");
|
|
131
|
+
console.log(` 1. Run your tests (e.g. npx playwright test)`);
|
|
132
|
+
console.log(` 2. Import results into this execution:`);
|
|
133
|
+
console.log(` xqt import-results --file test-results/results.json --exec ${key}`);
|
|
45
134
|
logger.blank();
|
|
46
135
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command: xqt create-plan --summary <text> [--version <ver>]
|
|
3
|
+
*
|
|
4
|
+
* Creates a new Xray Test Plan in JIRA, then writes its key to .xrayrc
|
|
5
|
+
* and to the testPlan.key field in tests.json.
|
|
6
|
+
*
|
|
7
|
+
* Usage examples:
|
|
8
|
+
* xqt create-plan --summary "Payments v2.0 Regression"
|
|
9
|
+
* xqt create-plan --summary "Sprint 12 Smoke" --version "2024.12"
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import logger, { setVerbose } from "../lib/logger.js";
|
|
14
|
+
import { loadConfig, validateConfig } from "../lib/config.js";
|
|
15
|
+
import { authenticate, createIssue, createTestPlan, getIssue } from "../lib/xrayClient.js";
|
|
16
|
+
|
|
17
|
+
export default async function createPlan(opts = {}) {
|
|
18
|
+
if (opts.verbose) setVerbose(true);
|
|
19
|
+
|
|
20
|
+
logger.rocket("@msalaam/xray-qe-toolkit — Create Test Plan\n");
|
|
21
|
+
|
|
22
|
+
const cfg = loadConfig({ envPath: opts.env });
|
|
23
|
+
validateConfig(cfg);
|
|
24
|
+
|
|
25
|
+
const summary = opts.summary;
|
|
26
|
+
if (!summary) {
|
|
27
|
+
logger.error("--summary is required");
|
|
28
|
+
logger.info("Usage: xqt create-plan --summary 'My Test Plan'");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
logger.info(`Summary: ${summary}`);
|
|
33
|
+
if (opts.version) logger.info(`Version: ${opts.version}`);
|
|
34
|
+
if (opts.label) logger.info(`Labels: ${opts.label}`);
|
|
35
|
+
logger.blank();
|
|
36
|
+
|
|
37
|
+
// ── Authenticate ───────────────────────────────────────────────────────────
|
|
38
|
+
const xrayToken = await authenticate(cfg);
|
|
39
|
+
|
|
40
|
+
// ── Create the Test Plan via JIRA REST ────────────────────────────────────
|
|
41
|
+
logger.send("Creating Test Plan in JIRA...");
|
|
42
|
+
|
|
43
|
+
let planKey;
|
|
44
|
+
try {
|
|
45
|
+
const fields = {
|
|
46
|
+
summary,
|
|
47
|
+
description: summary,
|
|
48
|
+
...(opts.label && {
|
|
49
|
+
labels: opts.label.split(",").map((l) => l.trim()),
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const issue = await createIssue(cfg, "Test Plan", fields);
|
|
54
|
+
planKey = issue.key;
|
|
55
|
+
} catch (err) {
|
|
56
|
+
// Fallback: try via Xray GraphQL createTestPlan mutation
|
|
57
|
+
logger.debug(`JIRA REST failed, trying Xray GraphQL: ${err.message}`);
|
|
58
|
+
try {
|
|
59
|
+
const result = await createTestPlan(cfg, xrayToken, {
|
|
60
|
+
projectId: cfg.jiraProjectKey,
|
|
61
|
+
summary,
|
|
62
|
+
description: summary,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// GraphQL returns { testPlan: { issueId, projectId } } — resolve key via JIRA
|
|
66
|
+
const issueId = result?.testPlan?.issueId;
|
|
67
|
+
if (issueId) {
|
|
68
|
+
try {
|
|
69
|
+
const issue = await getIssue(cfg, issueId);
|
|
70
|
+
planKey = issue?.key;
|
|
71
|
+
} catch {
|
|
72
|
+
logger.debug(`Could not resolve JIRA key for issueId ${issueId}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch (gqlErr) {
|
|
76
|
+
logger.error(`Could not create Test Plan: ${gqlErr.message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!planKey) {
|
|
82
|
+
logger.error("Test Plan created but no key was returned. Check JIRA.");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
logger.success(`Test Plan created: ${planKey}`);
|
|
87
|
+
logger.link(`${cfg.jiraUrl}/browse/${planKey}`);
|
|
88
|
+
logger.blank();
|
|
89
|
+
|
|
90
|
+
// ── Update .xrayrc ─────────────────────────────────────────────────────────
|
|
91
|
+
const xrayrcPath = ".xrayrc";
|
|
92
|
+
if (fs.existsSync(xrayrcPath)) {
|
|
93
|
+
const xrayrc = JSON.parse(fs.readFileSync(xrayrcPath, "utf8"));
|
|
94
|
+
xrayrc.testPlanKey = planKey;
|
|
95
|
+
fs.writeFileSync(xrayrcPath, JSON.stringify(xrayrc, null, 2));
|
|
96
|
+
logger.save(`.xrayrc updated with testPlanKey: ${planKey}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── Update tests.json ──────────────────────────────────────────────────────
|
|
100
|
+
if (fs.existsSync(cfg.testsPath)) {
|
|
101
|
+
const testsConfig = JSON.parse(fs.readFileSync(cfg.testsPath, "utf8"));
|
|
102
|
+
testsConfig.testPlan = { key: planKey, summary };
|
|
103
|
+
fs.writeFileSync(cfg.testsPath, JSON.stringify(testsConfig, null, 2));
|
|
104
|
+
logger.save(`tests.json updated with testPlan.key: ${planKey}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
logger.blank();
|
|
108
|
+
logger.success("Done — run 'xqt push-tests' to sync your tests to this plan");
|
|
109
|
+
logger.blank();
|
|
110
|
+
}
|
package/commands/editJson.js
CHANGED
package/commands/genPipeline.js
CHANGED
|
@@ -40,6 +40,6 @@ export default async function genPipeline(opts = {}) {
|
|
|
40
40
|
console.log(" 5. Playwright tests must have xray annotations to map results:");
|
|
41
41
|
console.log(" test.info().annotations.push({ type: 'xray', description: 'PROJ-123' })");
|
|
42
42
|
console.log("");
|
|
43
|
-
console.log("⚠️ Remember: edit
|
|
43
|
+
console.log("⚠️ Remember: xqt edit and QE review gates are NOT part of CI.");
|
|
44
44
|
console.log(" Run those steps locally before committing.\n");
|
|
45
45
|
}
|