@alternative-path/x-mcp 1.0.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/LICENSE +23 -0
- package/README.md +364 -0
- package/dist/agents/test-planner-context.d.ts +7 -0
- package/dist/agents/test-planner-context.d.ts.map +1 -0
- package/dist/agents/test-planner-context.js +283 -0
- package/dist/agents/test-planner-context.js.map +1 -0
- package/dist/agents/test-planner-prompt.d.ts +34 -0
- package/dist/agents/test-planner-prompt.d.ts.map +1 -0
- package/dist/agents/test-planner-prompt.js +82 -0
- package/dist/agents/test-planner-prompt.js.map +1 -0
- package/dist/api-client.d.ts +52 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +240 -0
- package/dist/api-client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +159 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/auth-tools.d.ts +17 -0
- package/dist/tools/auth-tools.d.ts.map +1 -0
- package/dist/tools/auth-tools.js +154 -0
- package/dist/tools/auth-tools.js.map +1 -0
- package/dist/tools/automation-tools.d.ts +25 -0
- package/dist/tools/automation-tools.d.ts.map +1 -0
- package/dist/tools/automation-tools.js +399 -0
- package/dist/tools/automation-tools.js.map +1 -0
- package/dist/tools/export-import-tools.d.ts +16 -0
- package/dist/tools/export-import-tools.d.ts.map +1 -0
- package/dist/tools/export-import-tools.js +62 -0
- package/dist/tools/export-import-tools.js.map +1 -0
- package/dist/tools/module-tools.d.ts +42 -0
- package/dist/tools/module-tools.d.ts.map +1 -0
- package/dist/tools/module-tools.js +302 -0
- package/dist/tools/module-tools.js.map +1 -0
- package/dist/tools/project-tools.d.ts +44 -0
- package/dist/tools/project-tools.d.ts.map +1 -0
- package/dist/tools/project-tools.js +67 -0
- package/dist/tools/project-tools.js.map +1 -0
- package/dist/tools/testcase-tools.d.ts +129 -0
- package/dist/tools/testcase-tools.d.ts.map +1 -0
- package/dist/tools/testcase-tools.js +762 -0
- package/dist/tools/testcase-tools.js.map +1 -0
- package/dist/tools/testgroup-launch-tools.d.ts +28 -0
- package/dist/tools/testgroup-launch-tools.d.ts.map +1 -0
- package/dist/tools/testgroup-launch-tools.js +332 -0
- package/dist/tools/testgroup-launch-tools.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,762 @@
|
|
|
1
|
+
export class TestCaseTools {
|
|
2
|
+
apiClient;
|
|
3
|
+
constructor(apiClient) {
|
|
4
|
+
this.apiClient = apiClient;
|
|
5
|
+
}
|
|
6
|
+
getTools() {
|
|
7
|
+
return [
|
|
8
|
+
{
|
|
9
|
+
name: "list_test_cases",
|
|
10
|
+
description: "List test cases. Can filter by module, search by title, or get all test cases in the project.",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
type: "object",
|
|
13
|
+
properties: {
|
|
14
|
+
moduleId: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Optional module ID to filter test cases by module, or 'all' for all modules (default: 'all')",
|
|
17
|
+
},
|
|
18
|
+
search: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "Optional search term to filter by test case title",
|
|
21
|
+
},
|
|
22
|
+
page: {
|
|
23
|
+
type: "number",
|
|
24
|
+
description: "Page number for pagination (default: 1)",
|
|
25
|
+
},
|
|
26
|
+
pageSize: {
|
|
27
|
+
type: "number",
|
|
28
|
+
description: "Number of results per page (default: 25)",
|
|
29
|
+
},
|
|
30
|
+
projectId: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Optional project ID. If not provided, uses the configured project ID.",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "get_test_case",
|
|
39
|
+
description: "Get detailed information about a specific test case by ID",
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: "object",
|
|
42
|
+
properties: {
|
|
43
|
+
testCaseId: {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "ID of the test case to retrieve",
|
|
46
|
+
},
|
|
47
|
+
projectId: {
|
|
48
|
+
type: "string",
|
|
49
|
+
description: "Optional project ID. If not provided, uses the configured project ID.",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
required: ["testCaseId"],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "get_execution_history",
|
|
57
|
+
description: "Get execution history for a test case (runs, results, dates).",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: "object",
|
|
60
|
+
properties: {
|
|
61
|
+
testCaseId: {
|
|
62
|
+
type: "string",
|
|
63
|
+
description: "ID (UUID) of the test case to get execution history for",
|
|
64
|
+
},
|
|
65
|
+
projectId: {
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "Optional project ID. If not provided, uses the configured project ID.",
|
|
68
|
+
},
|
|
69
|
+
page: {
|
|
70
|
+
type: "number",
|
|
71
|
+
description: "Page number for pagination (default: 1)",
|
|
72
|
+
},
|
|
73
|
+
pageSize: {
|
|
74
|
+
type: "number",
|
|
75
|
+
description: "Number of results per page (default: 25)",
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
required: ["testCaseId"],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "get_execution_plan",
|
|
83
|
+
description: "Get the exact details of the automated test steps (execution plan) for a test case. Returns the saved plan including steps, tool calls, assertions, and metadata. Use when you need the full automation script/plan for a test case (e.g. NLP-automated test).",
|
|
84
|
+
inputSchema: {
|
|
85
|
+
type: "object",
|
|
86
|
+
properties: {
|
|
87
|
+
testCaseId: {
|
|
88
|
+
type: "string",
|
|
89
|
+
description: "ID (UUID) of the test case to get the execution plan for",
|
|
90
|
+
},
|
|
91
|
+
projectId: {
|
|
92
|
+
type: "string",
|
|
93
|
+
description: "Optional project ID. If not provided, uses the configured project ID.",
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
required: ["testCaseId"],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "create_test_case",
|
|
101
|
+
description: "Create a new test case in a specified module, component, or subcomponent",
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: "object",
|
|
104
|
+
properties: {
|
|
105
|
+
title: {
|
|
106
|
+
type: "string",
|
|
107
|
+
description: "Title/summary of the test case (required)",
|
|
108
|
+
},
|
|
109
|
+
description: {
|
|
110
|
+
type: "string",
|
|
111
|
+
description: "Detailed description of the test case",
|
|
112
|
+
},
|
|
113
|
+
moduleId: {
|
|
114
|
+
type: "string",
|
|
115
|
+
description: "ID of the module/component/subcomponent where the test case will be created (required)",
|
|
116
|
+
},
|
|
117
|
+
type: {
|
|
118
|
+
type: "string",
|
|
119
|
+
description: "Type of test case (e.g., 'Functional Test', 'Regression Test', 'Smoke Test')",
|
|
120
|
+
},
|
|
121
|
+
status: {
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "Status of the test case (e.g., 'New', 'In Progress', 'Completed')",
|
|
124
|
+
},
|
|
125
|
+
priority: {
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "Priority of the test case (e.g., 'Low', 'Normal', 'High', 'Critical')",
|
|
128
|
+
},
|
|
129
|
+
estimatedDuration: {
|
|
130
|
+
type: "number",
|
|
131
|
+
description: "Estimated duration in minutes",
|
|
132
|
+
},
|
|
133
|
+
testData: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: "Test data as JSON string",
|
|
136
|
+
},
|
|
137
|
+
labels: {
|
|
138
|
+
type: "array",
|
|
139
|
+
items: { type: "string" },
|
|
140
|
+
description: "Array of label names to attach to the test case",
|
|
141
|
+
},
|
|
142
|
+
projectId: {
|
|
143
|
+
type: "string",
|
|
144
|
+
description: "Optional project ID. If not provided, uses the configured project ID.",
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
required: ["title", "moduleId"],
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: "update_test_case",
|
|
152
|
+
description: "Update an existing test case. You can update any field of the test case.",
|
|
153
|
+
inputSchema: {
|
|
154
|
+
type: "object",
|
|
155
|
+
properties: {
|
|
156
|
+
testCaseId: {
|
|
157
|
+
type: "string",
|
|
158
|
+
description: "ID of the test case to update (required)",
|
|
159
|
+
},
|
|
160
|
+
title: {
|
|
161
|
+
type: "string",
|
|
162
|
+
description: "New title for the test case",
|
|
163
|
+
},
|
|
164
|
+
description: {
|
|
165
|
+
type: "string",
|
|
166
|
+
description: "New description for the test case",
|
|
167
|
+
},
|
|
168
|
+
type: {
|
|
169
|
+
type: "string",
|
|
170
|
+
description: "New type for the test case",
|
|
171
|
+
},
|
|
172
|
+
status: {
|
|
173
|
+
type: "string",
|
|
174
|
+
description: "New status for the test case",
|
|
175
|
+
},
|
|
176
|
+
priority: {
|
|
177
|
+
type: "string",
|
|
178
|
+
description: "New priority for the test case",
|
|
179
|
+
},
|
|
180
|
+
estimatedDuration: {
|
|
181
|
+
type: "number",
|
|
182
|
+
description: "New estimated duration in minutes",
|
|
183
|
+
},
|
|
184
|
+
testData: {
|
|
185
|
+
type: "string",
|
|
186
|
+
description: "New test data as JSON string",
|
|
187
|
+
},
|
|
188
|
+
labels: {
|
|
189
|
+
type: "array",
|
|
190
|
+
items: { type: "string" },
|
|
191
|
+
description: "New array of label names",
|
|
192
|
+
},
|
|
193
|
+
projectId: {
|
|
194
|
+
type: "string",
|
|
195
|
+
description: "Optional project ID. If not provided, uses the configured project ID.",
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
required: ["testCaseId"],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "delete_test_case",
|
|
203
|
+
description: "Delete a test case by ID. This action cannot be undone.",
|
|
204
|
+
inputSchema: {
|
|
205
|
+
type: "object",
|
|
206
|
+
properties: {
|
|
207
|
+
testCaseId: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "ID of the test case to delete (required)",
|
|
210
|
+
},
|
|
211
|
+
projectId: {
|
|
212
|
+
type: "string",
|
|
213
|
+
description: "Optional project ID. If not provided, uses the configured project ID.",
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
required: ["testCaseId"],
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: "clone_test_case",
|
|
221
|
+
description: "Clone one or more test cases to a target module. Can clone a single test case or multiple test cases at once.",
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: {
|
|
225
|
+
testCaseId: {
|
|
226
|
+
type: "string",
|
|
227
|
+
description: "ID of the test case to clone (use this for single clone)",
|
|
228
|
+
},
|
|
229
|
+
testCaseIds: {
|
|
230
|
+
type: "array",
|
|
231
|
+
items: { type: "string" },
|
|
232
|
+
description: "Array of test case IDs to clone (use this for bulk clone)",
|
|
233
|
+
},
|
|
234
|
+
targetModuleId: {
|
|
235
|
+
type: "string",
|
|
236
|
+
description: "ID of the target module/component/subcomponent where the cloned test case(s) will be created (required)",
|
|
237
|
+
},
|
|
238
|
+
projectId: {
|
|
239
|
+
type: "string",
|
|
240
|
+
description: "Optional project ID. If not provided, uses the configured project ID.",
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
required: ["targetModuleId"],
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: "move_test_case",
|
|
248
|
+
description: "Move a test case from one module to another module, component, or subcomponent",
|
|
249
|
+
inputSchema: {
|
|
250
|
+
type: "object",
|
|
251
|
+
properties: {
|
|
252
|
+
testCaseId: {
|
|
253
|
+
type: "string",
|
|
254
|
+
description: "ID of the test case to move (required)",
|
|
255
|
+
},
|
|
256
|
+
targetModuleId: {
|
|
257
|
+
type: "string",
|
|
258
|
+
description: "ID of the target module/component/subcomponent where the test case will be moved (required)",
|
|
259
|
+
},
|
|
260
|
+
projectId: {
|
|
261
|
+
type: "string",
|
|
262
|
+
description: "Optional project ID. If not provided, uses the configured project ID.",
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
required: ["testCaseId", "targetModuleId"],
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
name: "bulk_create_test_cases",
|
|
270
|
+
description: "Create multiple test cases at once. Useful for importing or creating many test cases efficiently.",
|
|
271
|
+
inputSchema: {
|
|
272
|
+
type: "object",
|
|
273
|
+
properties: {
|
|
274
|
+
testCases: {
|
|
275
|
+
type: "array",
|
|
276
|
+
items: {
|
|
277
|
+
type: "object",
|
|
278
|
+
properties: {
|
|
279
|
+
title: { type: "string" },
|
|
280
|
+
description: { type: "string" },
|
|
281
|
+
moduleId: { type: "string" },
|
|
282
|
+
type: { type: "string" },
|
|
283
|
+
status: { type: "string" },
|
|
284
|
+
priority: { type: "string" },
|
|
285
|
+
estimatedDuration: { type: "number" },
|
|
286
|
+
testData: { type: "string" },
|
|
287
|
+
},
|
|
288
|
+
required: ["title", "moduleId"],
|
|
289
|
+
},
|
|
290
|
+
description: "Array of test case objects to create",
|
|
291
|
+
},
|
|
292
|
+
projectId: {
|
|
293
|
+
type: "string",
|
|
294
|
+
description: "Optional project ID. If not provided, uses the configured project ID.",
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
required: ["testCases"],
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
handles(name) {
|
|
303
|
+
return [
|
|
304
|
+
"list_test_cases",
|
|
305
|
+
"get_test_case",
|
|
306
|
+
"get_execution_history",
|
|
307
|
+
"get_execution_plan",
|
|
308
|
+
"create_test_case",
|
|
309
|
+
"update_test_case",
|
|
310
|
+
"delete_test_case",
|
|
311
|
+
"clone_test_case",
|
|
312
|
+
"move_test_case",
|
|
313
|
+
"bulk_create_test_cases",
|
|
314
|
+
].includes(name);
|
|
315
|
+
}
|
|
316
|
+
async handle(name, args) {
|
|
317
|
+
switch (name) {
|
|
318
|
+
case "list_test_cases":
|
|
319
|
+
return await this.listTestCases(args);
|
|
320
|
+
case "get_test_case":
|
|
321
|
+
return await this.getTestCase(args);
|
|
322
|
+
case "get_execution_history":
|
|
323
|
+
return await this.getExecutionHistory(args);
|
|
324
|
+
case "get_execution_plan":
|
|
325
|
+
return await this.getExecutionPlan(args);
|
|
326
|
+
case "create_test_case":
|
|
327
|
+
return await this.createTestCase(args);
|
|
328
|
+
case "update_test_case":
|
|
329
|
+
return await this.updateTestCase(args);
|
|
330
|
+
case "delete_test_case":
|
|
331
|
+
return await this.deleteTestCase(args);
|
|
332
|
+
case "clone_test_case":
|
|
333
|
+
return await this.cloneTestCase(args);
|
|
334
|
+
case "move_test_case":
|
|
335
|
+
return await this.moveTestCase(args);
|
|
336
|
+
case "bulk_create_test_cases":
|
|
337
|
+
return await this.bulkCreateTestCases(args);
|
|
338
|
+
default:
|
|
339
|
+
throw new Error(`Unknown test case tool: ${name}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
async listTestCases(args) {
|
|
343
|
+
const { moduleId, search, page = 1, pageSize = 25, projectId: argProjectId, } = args;
|
|
344
|
+
const projectId = argProjectId || this.apiClient["projectId"];
|
|
345
|
+
if (!projectId) {
|
|
346
|
+
throw new Error("Project ID is required. Provide it as an argument or set X_MCP_PROJECT_ID.");
|
|
347
|
+
}
|
|
348
|
+
const params = {
|
|
349
|
+
page,
|
|
350
|
+
pageSize,
|
|
351
|
+
};
|
|
352
|
+
// moduleId can be "all" or a UUID, default to "all" if not provided
|
|
353
|
+
params.moduleId = moduleId || "all";
|
|
354
|
+
if (search)
|
|
355
|
+
params.search = search;
|
|
356
|
+
const response = await this.apiClient.get(`${projectId}/testcase/testcaselist`, { params });
|
|
357
|
+
return {
|
|
358
|
+
content: [
|
|
359
|
+
{
|
|
360
|
+
type: "text",
|
|
361
|
+
text: JSON.stringify({
|
|
362
|
+
success: response.success,
|
|
363
|
+
message: response.message,
|
|
364
|
+
data: response.data,
|
|
365
|
+
}, null, 2),
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
async getTestCase(args) {
|
|
371
|
+
const { testCaseId, projectId: argProjectId } = args;
|
|
372
|
+
const projectId = argProjectId || this.apiClient["projectId"];
|
|
373
|
+
if (!testCaseId) {
|
|
374
|
+
throw new Error("Test case ID is required");
|
|
375
|
+
}
|
|
376
|
+
if (!projectId) {
|
|
377
|
+
throw new Error("Project ID is required. Provide it as an argument or set X_MCP_PROJECT_ID.");
|
|
378
|
+
}
|
|
379
|
+
const response = await this.apiClient.get(`${projectId}/testcase/${testCaseId}`);
|
|
380
|
+
return {
|
|
381
|
+
content: [
|
|
382
|
+
{
|
|
383
|
+
type: "text",
|
|
384
|
+
text: JSON.stringify({
|
|
385
|
+
success: response.success,
|
|
386
|
+
message: response.message,
|
|
387
|
+
data: response.data,
|
|
388
|
+
}, null, 2),
|
|
389
|
+
},
|
|
390
|
+
],
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
async getExecutionHistory(args) {
|
|
394
|
+
const { testCaseId, projectId: argProjectId, page = 1, pageSize = 25, } = args;
|
|
395
|
+
const projectId = argProjectId || this.apiClient["projectId"];
|
|
396
|
+
if (!testCaseId) {
|
|
397
|
+
throw new Error("Test case ID is required");
|
|
398
|
+
}
|
|
399
|
+
if (!projectId) {
|
|
400
|
+
throw new Error("Project ID is required. Provide it as an argument or set X_MCP_PROJECT_ID.");
|
|
401
|
+
}
|
|
402
|
+
const response = await this.apiClient.get(`${projectId}/testcase/fetchExecutionHistory/${testCaseId}`, {
|
|
403
|
+
params: { page, pageSize },
|
|
404
|
+
});
|
|
405
|
+
return {
|
|
406
|
+
content: [
|
|
407
|
+
{
|
|
408
|
+
type: "text",
|
|
409
|
+
text: JSON.stringify({
|
|
410
|
+
success: response.success,
|
|
411
|
+
message: response.message,
|
|
412
|
+
data: response.data,
|
|
413
|
+
}, null, 2),
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
async getExecutionPlan(args) {
|
|
419
|
+
const { testCaseId, projectId: argProjectId } = args;
|
|
420
|
+
const projectId = argProjectId || this.apiClient["projectId"];
|
|
421
|
+
if (!testCaseId) {
|
|
422
|
+
throw new Error("Test case ID is required");
|
|
423
|
+
}
|
|
424
|
+
if (!projectId) {
|
|
425
|
+
throw new Error("Project ID is required. Provide it as an argument or set X_MCP_PROJECT_ID.");
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
428
|
+
const response = await this.apiClient.get(`${projectId}/auto-execution/execution-records/${testCaseId}`);
|
|
429
|
+
const record = response?.data ?? null;
|
|
430
|
+
const plan = record?.plan ?? null;
|
|
431
|
+
const stepsCount = Array.isArray(plan?.steps) ? plan.steps.length : 0;
|
|
432
|
+
return {
|
|
433
|
+
content: [
|
|
434
|
+
{
|
|
435
|
+
type: "text",
|
|
436
|
+
text: JSON.stringify({
|
|
437
|
+
success: response?.success ?? true,
|
|
438
|
+
message: record
|
|
439
|
+
? stepsCount > 0
|
|
440
|
+
? "Execution plan fetched successfully."
|
|
441
|
+
: "Execution record exists but has no steps."
|
|
442
|
+
: "No automation plan saved for this test case.",
|
|
443
|
+
data: {
|
|
444
|
+
executionRecordId: record?.id,
|
|
445
|
+
testCaseId: record?.testcase_id ?? testCaseId,
|
|
446
|
+
plan: plan ?? null,
|
|
447
|
+
stepsCount,
|
|
448
|
+
created_at: record?.created_at,
|
|
449
|
+
updated_at: record?.updated_at,
|
|
450
|
+
created_by: record?.created_by,
|
|
451
|
+
updated_by: record?.updated_by,
|
|
452
|
+
},
|
|
453
|
+
}, null, 2),
|
|
454
|
+
},
|
|
455
|
+
],
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
catch (err) {
|
|
459
|
+
const notFound = err?.message?.includes("not found") ||
|
|
460
|
+
err?.response?.status === 404;
|
|
461
|
+
return {
|
|
462
|
+
content: [
|
|
463
|
+
{
|
|
464
|
+
type: "text",
|
|
465
|
+
text: JSON.stringify({
|
|
466
|
+
success: false,
|
|
467
|
+
message: notFound
|
|
468
|
+
? "No automation plan saved for this test case."
|
|
469
|
+
: err?.message ?? "Failed to fetch execution plan.",
|
|
470
|
+
data: {
|
|
471
|
+
testCaseId,
|
|
472
|
+
plan: null,
|
|
473
|
+
stepsCount: 0,
|
|
474
|
+
},
|
|
475
|
+
}, null, 2),
|
|
476
|
+
},
|
|
477
|
+
],
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
async createTestCase(args) {
|
|
482
|
+
const { title, description, moduleId, type, status, priority, estimatedDuration, testData, labels, projectId: argProjectId, } = args;
|
|
483
|
+
const projectId = argProjectId || this.apiClient["projectId"];
|
|
484
|
+
if (!title) {
|
|
485
|
+
throw new Error("Test case title is required");
|
|
486
|
+
}
|
|
487
|
+
if (!moduleId) {
|
|
488
|
+
throw new Error("Module ID is required");
|
|
489
|
+
}
|
|
490
|
+
if (!projectId) {
|
|
491
|
+
throw new Error("Project ID is required. Provide it as an argument or set X_MCP_PROJECT_ID.");
|
|
492
|
+
}
|
|
493
|
+
// Get user ID for generated_by and updated_by (required by backend)
|
|
494
|
+
const userId = this.apiClient.getUserId();
|
|
495
|
+
if (!userId) {
|
|
496
|
+
throw new Error("User ID not available. Please login first.");
|
|
497
|
+
}
|
|
498
|
+
const testCaseData = {
|
|
499
|
+
title: title.trim(),
|
|
500
|
+
module_id: moduleId,
|
|
501
|
+
description: description || "", // Default to empty string
|
|
502
|
+
type: type || "Functional Test", // Default type
|
|
503
|
+
status: status || "New", // Default status per OpenAPI spec
|
|
504
|
+
priority: priority || "Normal", // Default priority
|
|
505
|
+
generated_by: userId, // Required by database model
|
|
506
|
+
updated_by: userId, // Required by database model
|
|
507
|
+
};
|
|
508
|
+
if (estimatedDuration !== undefined) {
|
|
509
|
+
testCaseData.estimated_duration = estimatedDuration;
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
testCaseData.estimated_duration = 15; // Default duration
|
|
513
|
+
}
|
|
514
|
+
if (testData) {
|
|
515
|
+
try {
|
|
516
|
+
testCaseData.testdata = typeof testData === "string" ? JSON.parse(testData) : testData;
|
|
517
|
+
}
|
|
518
|
+
catch {
|
|
519
|
+
testCaseData.testdata = testData;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (labels && Array.isArray(labels)) {
|
|
523
|
+
// Transform label names to label objects
|
|
524
|
+
testCaseData.labels = labels.map((label) => {
|
|
525
|
+
if (typeof label === "string") {
|
|
526
|
+
return { name: label };
|
|
527
|
+
}
|
|
528
|
+
return label;
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
try {
|
|
532
|
+
const response = await this.apiClient.post(`${projectId}/testcase/create`, testCaseData);
|
|
533
|
+
return {
|
|
534
|
+
content: [
|
|
535
|
+
{
|
|
536
|
+
type: "text",
|
|
537
|
+
text: JSON.stringify({
|
|
538
|
+
success: response.success,
|
|
539
|
+
message: response.message,
|
|
540
|
+
data: response.data,
|
|
541
|
+
}, null, 2),
|
|
542
|
+
},
|
|
543
|
+
],
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
catch (error) {
|
|
547
|
+
// Provide more detailed error information
|
|
548
|
+
const errorMessage = error.message || "Error creating test case";
|
|
549
|
+
throw new Error(`Failed to create test case: ${errorMessage}`);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
async updateTestCase(args) {
|
|
553
|
+
const { testCaseId, title, description, type, status, priority, estimatedDuration, testData, labels, projectId: argProjectId, } = args;
|
|
554
|
+
const projectId = argProjectId || this.apiClient["projectId"];
|
|
555
|
+
if (!testCaseId) {
|
|
556
|
+
throw new Error("Test case ID is required");
|
|
557
|
+
}
|
|
558
|
+
if (!projectId) {
|
|
559
|
+
throw new Error("Project ID is required. Provide it as an argument or set X_MCP_PROJECT_ID.");
|
|
560
|
+
}
|
|
561
|
+
// Fetch current test case to preserve existing values for fields not being updated
|
|
562
|
+
// The backend assigns all fields directly, so we need to include existing values
|
|
563
|
+
let currentTestCase = null;
|
|
564
|
+
try {
|
|
565
|
+
const response = await this.apiClient.get(`${projectId}/testcase/${testCaseId}`);
|
|
566
|
+
currentTestCase = response.data;
|
|
567
|
+
}
|
|
568
|
+
catch (error) {
|
|
569
|
+
// If we can't fetch the current test case, continue without it
|
|
570
|
+
// The backend will handle missing fields
|
|
571
|
+
}
|
|
572
|
+
const updateData = {};
|
|
573
|
+
// Only include fields that are being updated
|
|
574
|
+
if (title !== undefined)
|
|
575
|
+
updateData.title = title;
|
|
576
|
+
if (description !== undefined)
|
|
577
|
+
updateData.description = description || "";
|
|
578
|
+
if (type !== undefined)
|
|
579
|
+
updateData.type = type;
|
|
580
|
+
if (status !== undefined)
|
|
581
|
+
updateData.status = status;
|
|
582
|
+
if (priority !== undefined)
|
|
583
|
+
updateData.priority = priority;
|
|
584
|
+
if (estimatedDuration !== undefined)
|
|
585
|
+
updateData.estimated_duration = estimatedDuration;
|
|
586
|
+
if (testData !== undefined) {
|
|
587
|
+
try {
|
|
588
|
+
updateData.testdata =
|
|
589
|
+
typeof testData === "string" ? JSON.parse(testData) : testData;
|
|
590
|
+
}
|
|
591
|
+
catch {
|
|
592
|
+
updateData.testdata = testData;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (labels !== undefined) {
|
|
596
|
+
// Transform label names to label objects
|
|
597
|
+
updateData.labels = labels.map((label) => {
|
|
598
|
+
if (typeof label === "string") {
|
|
599
|
+
return { name: label };
|
|
600
|
+
}
|
|
601
|
+
return label;
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
if (Object.keys(updateData).length === 0) {
|
|
605
|
+
throw new Error("At least one field must be provided to update");
|
|
606
|
+
}
|
|
607
|
+
// Add updated_by field from the logged-in user (required by backend)
|
|
608
|
+
const userId = this.apiClient.getUserId();
|
|
609
|
+
if (!userId) {
|
|
610
|
+
throw new Error("User ID not available. Please login first.");
|
|
611
|
+
}
|
|
612
|
+
updateData.updated_by = userId;
|
|
613
|
+
const response = await this.apiClient.put(`${projectId}/testcase/${testCaseId}`, updateData);
|
|
614
|
+
return {
|
|
615
|
+
content: [
|
|
616
|
+
{
|
|
617
|
+
type: "text",
|
|
618
|
+
text: JSON.stringify({
|
|
619
|
+
success: response.success,
|
|
620
|
+
message: response.message,
|
|
621
|
+
data: response.data,
|
|
622
|
+
}, null, 2),
|
|
623
|
+
},
|
|
624
|
+
],
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
async deleteTestCase(args) {
|
|
628
|
+
const { testCaseId, projectId: argProjectId } = args;
|
|
629
|
+
const projectId = argProjectId || this.apiClient["projectId"];
|
|
630
|
+
if (!testCaseId) {
|
|
631
|
+
throw new Error("Test case ID is required");
|
|
632
|
+
}
|
|
633
|
+
if (!projectId) {
|
|
634
|
+
throw new Error("Project ID is required. Provide it as an argument or set X_MCP_PROJECT_ID.");
|
|
635
|
+
}
|
|
636
|
+
// Backend expects ids as an array in the request body
|
|
637
|
+
const response = await this.apiClient.delete(`${projectId}/testcase/delete`, {
|
|
638
|
+
data: { ids: [testCaseId] },
|
|
639
|
+
headers: {
|
|
640
|
+
"Content-Type": "application/json",
|
|
641
|
+
},
|
|
642
|
+
});
|
|
643
|
+
return {
|
|
644
|
+
content: [
|
|
645
|
+
{
|
|
646
|
+
type: "text",
|
|
647
|
+
text: JSON.stringify({
|
|
648
|
+
success: response.success,
|
|
649
|
+
message: response.message,
|
|
650
|
+
data: response.data,
|
|
651
|
+
}, null, 2),
|
|
652
|
+
},
|
|
653
|
+
],
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
async cloneTestCase(args) {
|
|
657
|
+
const { testCaseId, testCaseIds, targetModuleId, projectId: argProjectId } = args;
|
|
658
|
+
const projectId = argProjectId || this.apiClient["projectId"];
|
|
659
|
+
if (!targetModuleId) {
|
|
660
|
+
throw new Error("Target module ID is required");
|
|
661
|
+
}
|
|
662
|
+
if (!testCaseId && (!testCaseIds || testCaseIds.length === 0)) {
|
|
663
|
+
throw new Error("Either testCaseId or testCaseIds array is required");
|
|
664
|
+
}
|
|
665
|
+
if (!projectId) {
|
|
666
|
+
throw new Error("Project ID is required. Provide it as an argument or set X_MCP_PROJECT_ID.");
|
|
667
|
+
}
|
|
668
|
+
const cloneData = {
|
|
669
|
+
targetModuleId,
|
|
670
|
+
};
|
|
671
|
+
if (testCaseId) {
|
|
672
|
+
cloneData.testCaseId = testCaseId;
|
|
673
|
+
}
|
|
674
|
+
if (testCaseIds) {
|
|
675
|
+
cloneData.testCaseIds = testCaseIds;
|
|
676
|
+
}
|
|
677
|
+
const response = await this.apiClient.post(`${projectId}/testcase/clone`, cloneData);
|
|
678
|
+
return {
|
|
679
|
+
content: [
|
|
680
|
+
{
|
|
681
|
+
type: "text",
|
|
682
|
+
text: JSON.stringify({
|
|
683
|
+
success: response.success,
|
|
684
|
+
message: response.message,
|
|
685
|
+
data: response.data,
|
|
686
|
+
}, null, 2),
|
|
687
|
+
},
|
|
688
|
+
],
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
async moveTestCase(args) {
|
|
692
|
+
const { testCaseId, targetModuleId, projectId: argProjectId } = args;
|
|
693
|
+
const projectId = argProjectId || this.apiClient["projectId"];
|
|
694
|
+
if (!testCaseId) {
|
|
695
|
+
throw new Error("Test case ID is required");
|
|
696
|
+
}
|
|
697
|
+
if (!targetModuleId) {
|
|
698
|
+
throw new Error("Target module ID is required");
|
|
699
|
+
}
|
|
700
|
+
if (!projectId) {
|
|
701
|
+
throw new Error("Project ID is required. Provide it as an argument or set X_MCP_PROJECT_ID.");
|
|
702
|
+
}
|
|
703
|
+
const response = await this.apiClient.post(`${projectId}/testcase/move`, {
|
|
704
|
+
testCaseId,
|
|
705
|
+
targetModuleId,
|
|
706
|
+
});
|
|
707
|
+
return {
|
|
708
|
+
content: [
|
|
709
|
+
{
|
|
710
|
+
type: "text",
|
|
711
|
+
text: JSON.stringify({
|
|
712
|
+
success: response.success,
|
|
713
|
+
message: response.message,
|
|
714
|
+
data: response.data,
|
|
715
|
+
}, null, 2),
|
|
716
|
+
},
|
|
717
|
+
],
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
async bulkCreateTestCases(args) {
|
|
721
|
+
const { testCases, projectId: argProjectId } = args;
|
|
722
|
+
const projectId = argProjectId || this.apiClient["projectId"];
|
|
723
|
+
if (!testCases || !Array.isArray(testCases) || testCases.length === 0) {
|
|
724
|
+
throw new Error("testCases array is required and must not be empty");
|
|
725
|
+
}
|
|
726
|
+
if (!projectId) {
|
|
727
|
+
throw new Error("Project ID is required. Provide it as an argument or set X_MCP_PROJECT_ID.");
|
|
728
|
+
}
|
|
729
|
+
// Transform the test cases to match the API format
|
|
730
|
+
// Backend expects "Module" (capital M) for bulk create validation
|
|
731
|
+
const transformedTestCases = testCases.map((tc) => ({
|
|
732
|
+
title: tc.title,
|
|
733
|
+
description: tc.description || "",
|
|
734
|
+
Module: tc.moduleId, // Backend validation expects "Module" (capital M)
|
|
735
|
+
type: tc.type || "Functional Test",
|
|
736
|
+
status: tc.status || "New",
|
|
737
|
+
priority: tc.priority || "Normal",
|
|
738
|
+
estimated_duration: tc.estimatedDuration || 15,
|
|
739
|
+
testdata: tc.testData
|
|
740
|
+
? typeof tc.testData === "string"
|
|
741
|
+
? JSON.parse(tc.testData)
|
|
742
|
+
: tc.testData
|
|
743
|
+
: null,
|
|
744
|
+
}));
|
|
745
|
+
const response = await this.apiClient.post(`${projectId}/testcase/bulkcreate`, {
|
|
746
|
+
testCases: transformedTestCases,
|
|
747
|
+
});
|
|
748
|
+
return {
|
|
749
|
+
content: [
|
|
750
|
+
{
|
|
751
|
+
type: "text",
|
|
752
|
+
text: JSON.stringify({
|
|
753
|
+
success: response.success,
|
|
754
|
+
message: response.message,
|
|
755
|
+
data: response.data,
|
|
756
|
+
}, null, 2),
|
|
757
|
+
},
|
|
758
|
+
],
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
//# sourceMappingURL=testcase-tools.js.map
|