@testchimp/cli 0.1.4 → 0.1.6
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/dist/cli/program.js +33 -0
- package/dist/core/schemas.d.ts +25 -0
- package/dist/core/schemas.js +15 -0
- package/dist/core/tools.js +54 -5
- package/package.json +1 -1
package/dist/cli/program.js
CHANGED
|
@@ -46,6 +46,7 @@ export function buildCliProgram() {
|
|
|
46
46
|
.option("--release <s>")
|
|
47
47
|
.option("--environment <s>")
|
|
48
48
|
.option("--branch-name <s>")
|
|
49
|
+
.option("--platform <web|ios|android>")
|
|
49
50
|
.option("--file-paths <csv>", "comma-separated paths under platform tests root")
|
|
50
51
|
.option("--folder-path <path>", "folder under tests root, slash-separated")
|
|
51
52
|
.action(async (opts) => {
|
|
@@ -56,6 +57,8 @@ export function buildCliProgram() {
|
|
|
56
57
|
body.environment = opts.environment;
|
|
57
58
|
if (opts.branchName)
|
|
58
59
|
body.branchName = opts.branchName;
|
|
60
|
+
if (opts.platform)
|
|
61
|
+
body.platform = opts.platform;
|
|
59
62
|
const scope = {};
|
|
60
63
|
if (opts.filePaths)
|
|
61
64
|
scope.filePaths = String(opts.filePaths).split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -74,6 +77,8 @@ export function buildCliProgram() {
|
|
|
74
77
|
.option("--release <s>")
|
|
75
78
|
.option("--environment <s>")
|
|
76
79
|
.option("--branch-name <s>")
|
|
80
|
+
.option("--scenario-id <id>")
|
|
81
|
+
.option("--platform <web|ios|android>")
|
|
77
82
|
.option("--file-paths <csv>")
|
|
78
83
|
.option("--folder-path <path>")
|
|
79
84
|
.action(async (opts) => {
|
|
@@ -84,6 +89,10 @@ export function buildCliProgram() {
|
|
|
84
89
|
body.environment = opts.environment;
|
|
85
90
|
if (opts.branchName)
|
|
86
91
|
body.branchName = opts.branchName;
|
|
92
|
+
if (opts.scenarioId)
|
|
93
|
+
body.scenarioId = opts.scenarioId;
|
|
94
|
+
if (opts.platform)
|
|
95
|
+
body.platform = opts.platform;
|
|
87
96
|
const scope = {};
|
|
88
97
|
if (opts.filePaths)
|
|
89
98
|
scope.filePaths = String(opts.filePaths).split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -157,6 +166,30 @@ export function buildCliProgram() {
|
|
|
157
166
|
const out = await runTool("update-user-story", merged, { postMcp });
|
|
158
167
|
console.log(out);
|
|
159
168
|
});
|
|
169
|
+
program
|
|
170
|
+
.command("mark-plan-items-implementation-done")
|
|
171
|
+
.description(TOOL_DEFINITIONS.find((t) => t.kebab === "mark-plan-items-implementation-done").description)
|
|
172
|
+
.addOption(jsonInputOption())
|
|
173
|
+
.option("--scenario-ordinal-ids <csv>", "comma-separated TS-<n> numeric ids")
|
|
174
|
+
.option("--user-story-ordinal-ids <csv>", "comma-separated US-<n> numeric ids")
|
|
175
|
+
.action(async (opts) => {
|
|
176
|
+
const body = {};
|
|
177
|
+
if (opts.scenarioOrdinalIds) {
|
|
178
|
+
body.scenarioOrdinalIds = String(opts.scenarioOrdinalIds)
|
|
179
|
+
.split(",")
|
|
180
|
+
.map((s) => Number(s.trim()))
|
|
181
|
+
.filter((n) => Number.isFinite(n) && n > 0);
|
|
182
|
+
}
|
|
183
|
+
if (opts.userStoryOrdinalIds) {
|
|
184
|
+
body.userStoryOrdinalIds = String(opts.userStoryOrdinalIds)
|
|
185
|
+
.split(",")
|
|
186
|
+
.map((s) => Number(s.trim()))
|
|
187
|
+
.filter((n) => Number.isFinite(n) && n > 0);
|
|
188
|
+
}
|
|
189
|
+
const merged = mergeBodies(body, opts.jsonInput);
|
|
190
|
+
const out = await runTool("mark-plan-items-implementation-done", merged, { postMcp });
|
|
191
|
+
console.log(out);
|
|
192
|
+
});
|
|
160
193
|
program
|
|
161
194
|
.command("update-test-scenario")
|
|
162
195
|
.description(TOOL_DEFINITIONS.find((t) => t.kebab === "update-test-scenario").description)
|
package/dist/core/schemas.d.ts
CHANGED
|
@@ -3,6 +3,10 @@ export declare const scopeSchema: z.ZodOptional<z.ZodObject<{
|
|
|
3
3
|
filePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
4
4
|
folderPath: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodString]>>;
|
|
5
5
|
}, z.core.$strip>>;
|
|
6
|
+
export declare const executionJobDimensionFilterSchema: z.ZodObject<{
|
|
7
|
+
dimension: z.ZodString;
|
|
8
|
+
values: z.ZodArray<z.ZodString>;
|
|
9
|
+
}, z.core.$strip>;
|
|
6
10
|
export declare const listCoverageInput: z.ZodObject<{
|
|
7
11
|
release: z.ZodOptional<z.ZodString>;
|
|
8
12
|
environment: z.ZodOptional<z.ZodString>;
|
|
@@ -13,6 +17,11 @@ export declare const listCoverageInput: z.ZodObject<{
|
|
|
13
17
|
includeNonCoveredUserStories: z.ZodOptional<z.ZodBoolean>;
|
|
14
18
|
includeNonCoveredTestScenarios: z.ZodOptional<z.ZodBoolean>;
|
|
15
19
|
branchName: z.ZodOptional<z.ZodString>;
|
|
20
|
+
platform: z.ZodOptional<z.ZodEnum<{
|
|
21
|
+
web: "web";
|
|
22
|
+
ios: "ios";
|
|
23
|
+
android: "android";
|
|
24
|
+
}>>;
|
|
16
25
|
}, z.core.$strip>;
|
|
17
26
|
export declare const listExecutionInput: z.ZodObject<{
|
|
18
27
|
release: z.ZodOptional<z.ZodString>;
|
|
@@ -22,6 +31,18 @@ export declare const listExecutionInput: z.ZodObject<{
|
|
|
22
31
|
folderPath: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodString]>>;
|
|
23
32
|
}, z.core.$strip>>;
|
|
24
33
|
branchName: z.ZodOptional<z.ZodString>;
|
|
34
|
+
scenarioId: z.ZodOptional<z.ZodString>;
|
|
35
|
+
platform: z.ZodOptional<z.ZodEnum<{
|
|
36
|
+
web: "web";
|
|
37
|
+
ios: "ios";
|
|
38
|
+
android: "android";
|
|
39
|
+
}>>;
|
|
40
|
+
dimensionFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
41
|
+
dimension: z.ZodString;
|
|
42
|
+
values: z.ZodArray<z.ZodString>;
|
|
43
|
+
}, z.core.$strip>>>;
|
|
44
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
45
|
+
offset: z.ZodOptional<z.ZodNumber>;
|
|
25
46
|
}, z.core.$strip>;
|
|
26
47
|
export declare const fetchExecutionReportInput: z.ZodObject<{
|
|
27
48
|
batchInvocationId: z.ZodOptional<z.ZodString>;
|
|
@@ -39,6 +60,10 @@ export declare const createTestScenarioInput: z.ZodObject<{
|
|
|
39
60
|
export declare const updatePlanMarkdownInput: z.ZodObject<{
|
|
40
61
|
content: z.ZodString;
|
|
41
62
|
}, z.core.$strip>;
|
|
63
|
+
export declare const markPlanItemsImplementationDoneInput: z.ZodObject<{
|
|
64
|
+
scenarioOrdinalIds: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
|
|
65
|
+
userStoryOrdinalIds: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
|
|
66
|
+
}, z.core.$strip>;
|
|
42
67
|
export declare const emptyInput: z.ZodObject<{}, z.core.$strip>;
|
|
43
68
|
export declare const getBranchSpecificEndpointConfigInput: z.ZodObject<{
|
|
44
69
|
branchName: z.ZodOptional<z.ZodString>;
|
package/dist/core/schemas.js
CHANGED
|
@@ -5,6 +5,11 @@ export const scopeSchema = z
|
|
|
5
5
|
folderPath: z.union([z.array(z.string()), z.string()]).optional(),
|
|
6
6
|
})
|
|
7
7
|
.optional();
|
|
8
|
+
const executionPlatformSchema = z.enum(["web", "ios", "android"]);
|
|
9
|
+
export const executionJobDimensionFilterSchema = z.object({
|
|
10
|
+
dimension: z.string().min(1),
|
|
11
|
+
values: z.array(z.string()).min(1),
|
|
12
|
+
});
|
|
8
13
|
export const listCoverageInput = z.object({
|
|
9
14
|
release: z.string().optional(),
|
|
10
15
|
environment: z.string().optional(),
|
|
@@ -12,12 +17,18 @@ export const listCoverageInput = z.object({
|
|
|
12
17
|
includeNonCoveredUserStories: z.boolean().optional(),
|
|
13
18
|
includeNonCoveredTestScenarios: z.boolean().optional(),
|
|
14
19
|
branchName: z.string().optional(),
|
|
20
|
+
platform: executionPlatformSchema.optional(),
|
|
15
21
|
});
|
|
16
22
|
export const listExecutionInput = z.object({
|
|
17
23
|
release: z.string().optional(),
|
|
18
24
|
environment: z.string().optional(),
|
|
19
25
|
scope: scopeSchema,
|
|
20
26
|
branchName: z.string().optional(),
|
|
27
|
+
scenarioId: z.string().optional(),
|
|
28
|
+
platform: executionPlatformSchema.optional(),
|
|
29
|
+
dimensionFilters: z.array(executionJobDimensionFilterSchema).optional(),
|
|
30
|
+
limit: z.number().int().positive().max(500).optional(),
|
|
31
|
+
offset: z.number().int().nonnegative().optional(),
|
|
21
32
|
});
|
|
22
33
|
export const fetchExecutionReportInput = z
|
|
23
34
|
.object({
|
|
@@ -46,6 +57,10 @@ export const createTestScenarioInput = z.object({
|
|
|
46
57
|
export const updatePlanMarkdownInput = z.object({
|
|
47
58
|
content: z.string().min(1),
|
|
48
59
|
});
|
|
60
|
+
export const markPlanItemsImplementationDoneInput = z.object({
|
|
61
|
+
scenarioOrdinalIds: z.array(z.coerce.number().int().positive()).optional(),
|
|
62
|
+
userStoryOrdinalIds: z.array(z.coerce.number().int().positive()).optional(),
|
|
63
|
+
});
|
|
49
64
|
export const emptyInput = z.object({});
|
|
50
65
|
export const getBranchSpecificEndpointConfigInput = z.object({
|
|
51
66
|
branchName: z.string().optional(),
|
package/dist/core/tools.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { normalizeScope } from "./normalize.js";
|
|
2
2
|
import { runProvisionEphemeralEnvironmentAndWait } from "./ephemeralWait.js";
|
|
3
3
|
import * as S from "./schemas.js";
|
|
4
|
+
function platformToProtoEnum(platform) {
|
|
5
|
+
switch (platform) {
|
|
6
|
+
case "ios":
|
|
7
|
+
return "IOS_EXECUTION_PLATFORM";
|
|
8
|
+
case "android":
|
|
9
|
+
return "ANDROID_EXECUTION_PLATFORM";
|
|
10
|
+
default:
|
|
11
|
+
return "WEB_EXECUTION_PLATFORM";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
4
14
|
function listCoverageBody(args) {
|
|
5
15
|
const body = {};
|
|
6
16
|
if (args.release != null)
|
|
@@ -16,6 +26,8 @@ function listCoverageBody(args) {
|
|
|
16
26
|
}
|
|
17
27
|
if (args.branchName != null && args.branchName.trim() !== "")
|
|
18
28
|
body.branchName = args.branchName.trim();
|
|
29
|
+
if (args.platform != null)
|
|
30
|
+
body.platform = platformToProtoEnum(args.platform);
|
|
19
31
|
return body;
|
|
20
32
|
}
|
|
21
33
|
function listExecutionBody(args) {
|
|
@@ -28,13 +40,33 @@ function listExecutionBody(args) {
|
|
|
28
40
|
body.scope = normalizeScope(args.scope);
|
|
29
41
|
if (args.branchName != null && args.branchName.trim() !== "")
|
|
30
42
|
body.branchName = args.branchName.trim();
|
|
43
|
+
if (args.scenarioId != null && args.scenarioId.trim() !== "")
|
|
44
|
+
body.scenarioId = args.scenarioId.trim();
|
|
45
|
+
const dimensionFilters = [...(args.dimensionFilters ?? [])];
|
|
46
|
+
if (args.platform != null) {
|
|
47
|
+
const hasPlatformFilter = dimensionFilters.some((f) => f.dimension === "PLATFORM_EXECUTION_JOB_FILTER_DIMENSION");
|
|
48
|
+
if (!hasPlatformFilter) {
|
|
49
|
+
dimensionFilters.push({
|
|
50
|
+
dimension: "PLATFORM_EXECUTION_JOB_FILTER_DIMENSION",
|
|
51
|
+
values: [args.platform.toUpperCase()],
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (dimensionFilters.length > 0)
|
|
56
|
+
body.dimensionFilters = dimensionFilters;
|
|
57
|
+
if (args.limit != null)
|
|
58
|
+
body.limit = args.limit;
|
|
59
|
+
if (args.offset != null)
|
|
60
|
+
body.offset = args.offset;
|
|
31
61
|
return body;
|
|
32
62
|
}
|
|
33
63
|
export const TOOL_DEFINITIONS = [
|
|
34
64
|
{
|
|
35
65
|
kebab: "get-requirement-coverage",
|
|
36
66
|
description: "Fetch requirement (scenario) coverage under an optional platform-rooted folder scope (tests/... or plans/...). " +
|
|
37
|
-
"Use
|
|
67
|
+
"Use scope.filePaths or scope.folderPath (platform tests/plans roots). Omit branchName for cross-branch coverage " +
|
|
68
|
+
"(aggregates branch copies; execution jobs deduped by stable hash of tests-root-relative path + test name). " +
|
|
69
|
+
"Pass branchName only when results must be limited to one Git branch. Optional platform (web|ios|android) filters rollup.",
|
|
38
70
|
inputSchema: S.listCoverageInput,
|
|
39
71
|
execute: async (args, { postMcp }) => {
|
|
40
72
|
const json = await postMcp("/api/mcp/list_requirement_coverage", listCoverageBody(args));
|
|
@@ -43,8 +75,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
43
75
|
},
|
|
44
76
|
{
|
|
45
77
|
kebab: "get-execution-history",
|
|
46
|
-
description: "Fetch SmartTest execution history for an optional platform-rooted folder scope. " +
|
|
47
|
-
"Use branchName and scope.filePaths as for coverage.",
|
|
78
|
+
description: "Fetch SmartTest execution history for an optional platform-rooted folder scope, or for a scenario when scenarioId is set. " +
|
|
79
|
+
"Use branchName and scope.filePaths as for coverage. Optional platform (web|ios|android) and dimensionFilters narrow results.",
|
|
48
80
|
inputSchema: S.listExecutionInput,
|
|
49
81
|
execute: async (args, { postMcp }) => {
|
|
50
82
|
const json = await postMcp("/api/mcp/list_execution_history", listExecutionBody(args));
|
|
@@ -97,7 +129,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
97
129
|
{
|
|
98
130
|
kebab: "update-user-story",
|
|
99
131
|
description: "Sync a user story markdown file to the platform after local edits. " +
|
|
100
|
-
"Parses frontmatter (id: US-..., title, priority
|
|
132
|
+
"Parses frontmatter (id: US-..., title, priority) and updates the linked support file and entity. " +
|
|
133
|
+
"Implementation status is not stored in markdown; use mark-plan-items-implementation-done.",
|
|
101
134
|
inputSchema: S.updatePlanMarkdownInput,
|
|
102
135
|
execute: async (args, { postMcp }) => {
|
|
103
136
|
const a = args;
|
|
@@ -107,13 +140,29 @@ export const TOOL_DEFINITIONS = [
|
|
|
107
140
|
{
|
|
108
141
|
kebab: "update-test-scenario",
|
|
109
142
|
description: "Sync a test scenario markdown file to the platform after local edits. " +
|
|
110
|
-
"Parses frontmatter (id: TS-..., story: US-..., title, priority
|
|
143
|
+
"Parses frontmatter (id: TS-..., story: US-..., title, priority) and updates linking if story changes. " +
|
|
144
|
+
"Implementation status is not stored in markdown; use mark-plan-items-implementation-done.",
|
|
111
145
|
inputSchema: S.updatePlanMarkdownInput,
|
|
112
146
|
execute: async (args, { postMcp }) => {
|
|
113
147
|
const a = args;
|
|
114
148
|
return postMcp("/api/mcp/update_test_scenario", { content: a.content });
|
|
115
149
|
},
|
|
116
150
|
},
|
|
151
|
+
{
|
|
152
|
+
kebab: "mark-plan-items-implementation-done",
|
|
153
|
+
description: "Mark user stories and/or test scenarios implementation-complete in platform lifecycle (DB only; does not rewrite plan markdown). " +
|
|
154
|
+
"Use scenarioOrdinalIds / userStoryOrdinalIds (numeric parts of TS-<n> / US-<n>).",
|
|
155
|
+
inputSchema: S.markPlanItemsImplementationDoneInput,
|
|
156
|
+
execute: async (args, { postMcp }) => {
|
|
157
|
+
const a = args;
|
|
158
|
+
const body = {};
|
|
159
|
+
if (a.scenarioOrdinalIds?.length)
|
|
160
|
+
body.scenarioOrdinalIds = a.scenarioOrdinalIds;
|
|
161
|
+
if (a.userStoryOrdinalIds?.length)
|
|
162
|
+
body.userStoryOrdinalIds = a.userStoryOrdinalIds;
|
|
163
|
+
return postMcp("/api/mcp/mark_plan_items_implementation_done", body);
|
|
164
|
+
},
|
|
165
|
+
},
|
|
117
166
|
{
|
|
118
167
|
kebab: "get-eaas-config",
|
|
119
168
|
description: "Return the project's BunnyShell (Environment-as-a-Service) settings. Secrets are never returned.",
|