@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/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 Scaffold tests.json, xray-mapping.json, knowledge/ folder, .env.example
11
- * gen-tests Generate test cases from knowledge/ using AI
12
- * edit-json Launch browser-based QE review gate editor
13
- * push-tests Push / update tests in Xray Cloud
14
- * gen-postman Generate Postman collection from tests.json (with optional AI)
15
- * create-execution Create a new Test Execution in JIRA
16
- * import-results Import test results (JUnit XML or Playwright JSON) into Xray
17
- * gen-pipeline Generate an Azure Pipelines YAML template
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
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("xray-qe")
36
- .description("QE workflow toolkit for Xray Cloud — test management, Postman generation, and CI integration")
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("Initialize a new project with knowledge/ folder, tests.json, xray-mapping.json, and .env.example")
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("gen-tests")
53
- .description("Generate test cases from knowledge/ folder using AI")
54
- .option("--ai", "Enable AI-assisted generation (requires connected provider)")
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/genTests.js");
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("edit-json")
66
- .description("Launch browser-based QE review gate editor for tests.json")
67
- .option("--port <number>", "Port for the local editor server", "0")
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/editJson.js");
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("push-tests")
75
- .description("Push or update tests in Xray Cloud from tests.json")
76
- .option("--testExecKey <key>", "Link tests to an existing Test Execution key instead of creating a new one")
77
- .option("--skip-exec", "Push tests without creating or linking a Test Execution")
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/pushTests.js");
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("gen-postman")
85
- .description("Generate a Postman collection from tests.json (with optional AI enhancement)")
86
- .option("--base-url <url>", "Base URL for API requests", "{{baseUrl}}")
87
- .option("--ai", "Enable AI-assisted generation for better assertions")
88
- .option("--spec <path>", "OpenAPI/Swagger spec file for schema-driven generation")
89
- .option("--knowledge <path>", "Custom knowledge folder path")
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/genPostman.js");
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("create-execution")
97
- .description("Create a new Test Execution issue in JIRA")
98
- .requiredOption("--summary <text>", "Test Execution summary/title")
99
- .option("--description <text>", "Test Execution description")
100
- .option("--issue <key>", "Parent issue key to link the execution to")
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/createExecution.js");
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("import-results")
108
- .description("Import test results into Xray Cloud (JUnit XML or Playwright JSON)")
109
- .requiredOption("--file <path>", "Path to the results file (.xml for JUnit, .json for Playwright)")
110
- .option("--testExecKey <key>", "Test Execution key to associate results with (creates new if omitted)")
111
- .option("--summary <text>", "Test Execution summary (for new executions)")
112
- .option("--description <text>", "Test Execution description (for new executions)")
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/importResults.js");
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("gen-pipeline")
120
- .description("Generate an Azure Pipelines YAML template")
121
- .option("--output <path>", "Output file path", "azure-pipelines.yml")
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/genPipeline.js");
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("mcp-server")
129
- .description("Start Model Context Protocol server for Copilot agent integration")
130
- .option("--port <number>", "Server port (default: stdio mode)")
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("../mcp/server.js");
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("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)")
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/compareOpenapi.js");
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("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")
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("../commands/updateSnapshot.js");
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${err.message}\n`);
194
+ console.error(`\n${err.message}\n`);
161
195
  process.exit(1);
162
- });
196
+ });
@@ -1,46 +1,135 @@
1
1
  /**
2
- * Command: xray-qe create-execution --summary "..." [--description "..."] [--issue QE-123]
2
+ * Command: xqt create-execution
3
3
  *
4
- * Creates a standalone Test Execution issue in JIRA.
5
- * Optionally links it to a parent issue via --issue.
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 { createIssue, linkIssues } from "../lib/xrayClient.js";
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
- logger.rocket("@msalaam/xray-qe-toolkit Create Execution\n");
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
- logger.pin(`Creating Test Execution: "${opts.summary}"...`);
44
+ // ── Resolve environment ────────────────────────────────────────────────────
45
+ const environment = opts.environment || cfg.defaultEnvironment || "IOP-DEV";
46
+ const environments = [environment];
21
47
 
22
- const execution = await createIssue(cfg, "Test Execution", {
23
- summary: opts.summary,
24
- description: opts.description || opts.summary,
25
- });
26
-
27
- logger.success(`${execution.key} created`);
28
- logger.link(`View: ${cfg.jiraUrl}/browse/${execution.key}`);
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
- // Optionally link to parent issue
31
- if (opts.issue) {
58
+ if (!quiet) {
59
+ logger.info(`Environment: ${environment}`);
60
+ if (planKey) logger.info(`Test Plan: ${planKey}`);
32
61
  logger.blank();
33
- logger.link(`Linking to ${opts.issue}...`);
34
- try {
35
- await linkIssues(cfg, execution.key, opts.issue);
36
- logger.step(`Linked to ${opts.issue}`);
37
- } catch (err) {
38
- logger.warn(`Could not link: ${err.message}`);
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
- console.log(`Test Execution Key: ${execution.key}`);
44
- console.log(`Test Execution ID: ${execution.id}`);
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
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Command: xray-qe edit-json [--port <number>]
2
+ * Command: xqt edit [--port <number>]
3
3
  *
4
4
  * QE Review Gate — launches a local Express server with a browser-based
5
5
  * JSON editor that lets QEs:
@@ -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-json and QE review gates are NOT part of CI.");
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
  }