@provartesting/provardx-cli 1.5.0 → 1.5.2
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/lib/mcp/docs/PROVAR_TOOL_GUIDE.md +18 -6
- package/lib/mcp/prompts/guidePrompts.js +21 -11
- package/lib/mcp/prompts/guidePrompts.js.map +1 -1
- package/lib/mcp/server.d.ts +1 -0
- package/lib/mcp/server.js +90 -21
- package/lib/mcp/server.js.map +1 -1
- package/lib/mcp/tools/antTools.d.ts +20 -0
- package/lib/mcp/tools/antTools.js +105 -41
- package/lib/mcp/tools/antTools.js.map +1 -1
- package/lib/mcp/tools/automationTools.js +65 -35
- package/lib/mcp/tools/automationTools.js.map +1 -1
- package/lib/mcp/tools/connectionTools.js +100 -30
- package/lib/mcp/tools/connectionTools.js.map +1 -1
- package/lib/mcp/tools/defectTools.js +12 -7
- package/lib/mcp/tools/defectTools.js.map +1 -1
- package/lib/mcp/tools/descHelper.d.ts +5 -0
- package/lib/mcp/tools/descHelper.js +14 -0
- package/lib/mcp/tools/descHelper.js.map +1 -0
- package/lib/mcp/tools/nitroXTools.js +72 -32
- package/lib/mcp/tools/nitroXTools.js.map +1 -1
- package/lib/mcp/tools/orgDescribeTools.d.ts +57 -0
- package/lib/mcp/tools/orgDescribeTools.js +329 -0
- package/lib/mcp/tools/orgDescribeTools.js.map +1 -0
- package/lib/mcp/tools/pageObjectGenerate.js +33 -15
- package/lib/mcp/tools/pageObjectGenerate.js.map +1 -1
- package/lib/mcp/tools/pageObjectValidate.js +11 -4
- package/lib/mcp/tools/pageObjectValidate.js.map +1 -1
- package/lib/mcp/tools/projectInspect.js +31 -5
- package/lib/mcp/tools/projectInspect.js.map +1 -1
- package/lib/mcp/tools/projectValidateFromPath.js +128 -22
- package/lib/mcp/tools/projectValidateFromPath.js.map +1 -1
- package/lib/mcp/tools/propertiesTools.d.ts +12 -0
- package/lib/mcp/tools/propertiesTools.js +155 -17
- package/lib/mcp/tools/propertiesTools.js.map +1 -1
- package/lib/mcp/tools/qualityHubApiTools.js +8 -7
- package/lib/mcp/tools/qualityHubApiTools.js.map +1 -1
- package/lib/mcp/tools/qualityHubTools.js +108 -37
- package/lib/mcp/tools/qualityHubTools.js.map +1 -1
- package/lib/mcp/tools/rcaTools.d.ts +17 -0
- package/lib/mcp/tools/rcaTools.js +69 -15
- package/lib/mcp/tools/rcaTools.js.map +1 -1
- package/lib/mcp/tools/testCaseGenerate.d.ts +1 -0
- package/lib/mcp/tools/testCaseGenerate.js +116 -13
- package/lib/mcp/tools/testCaseGenerate.js.map +1 -1
- package/lib/mcp/tools/testCaseStepTools.js +27 -9
- package/lib/mcp/tools/testCaseStepTools.js.map +1 -1
- package/lib/mcp/tools/testCaseValidate.d.ts +14 -1
- package/lib/mcp/tools/testCaseValidate.js +190 -69
- package/lib/mcp/tools/testCaseValidate.js.map +1 -1
- package/lib/mcp/tools/testPlanTools.js +43 -26
- package/lib/mcp/tools/testPlanTools.js.map +1 -1
- package/lib/mcp/tools/testPlanValidate.js +64 -11
- package/lib/mcp/tools/testPlanValidate.js.map +1 -1
- package/lib/mcp/tools/testSuiteValidate.js +99 -10
- package/lib/mcp/tools/testSuiteValidate.js.map +1 -1
- package/lib/mcp/utils/detailLevel.d.ts +9 -0
- package/lib/mcp/utils/detailLevel.js +20 -0
- package/lib/mcp/utils/detailLevel.js.map +1 -0
- package/lib/mcp/utils/fieldMask.d.ts +17 -0
- package/lib/mcp/utils/fieldMask.js +75 -0
- package/lib/mcp/utils/fieldMask.js.map +1 -0
- package/lib/mcp/utils/testCasePlanMode.d.ts +41 -0
- package/lib/mcp/utils/testCasePlanMode.js +208 -0
- package/lib/mcp/utils/testCasePlanMode.js.map +1 -0
- package/lib/mcp/utils/tokenMeta.d.ts +40 -0
- package/lib/mcp/utils/tokenMeta.js +90 -0
- package/lib/mcp/utils/tokenMeta.js.map +1 -0
- package/lib/mcp/utils/validationDiff.d.ts +57 -0
- package/lib/mcp/utils/validationDiff.js +191 -0
- package/lib/mcp/utils/validationDiff.js.map +1 -0
- package/lib/mcp/utils/validationScore.d.ts +15 -0
- package/lib/mcp/utils/validationScore.js +31 -0
- package/lib/mcp/utils/validationScore.js.map +1 -0
- package/lib/mcp/utils/warningCodes.d.ts +10 -0
- package/lib/mcp/utils/warningCodes.js +19 -0
- package/lib/mcp/utils/warningCodes.js.map +1 -0
- package/oclif.manifest.json +1 -1
- package/package.json +13 -1
|
@@ -77,15 +77,27 @@ provar_properties_set { file_path: "<output_path>", key: "connectionName", valu
|
|
|
77
77
|
|
|
78
78
|
## "I want to write a new test"
|
|
79
79
|
|
|
80
|
+
A Provar test case is a tree (scenarios → UI screens → asserts), not a flat list of steps. The agent that calls `provar_testcase_generate` is responsible for constructing the full tree in **one** call. Splitting authoring across many tool calls causes scenario numbering drift, flat asserts, and inconsistent step types — `provar_testcase_step_edit` is for **amending** an existing test case, not for **constructing** one.
|
|
81
|
+
|
|
82
|
+
Recommended sequence:
|
|
83
|
+
|
|
80
84
|
```
|
|
81
|
-
1. provar_project_inspect
|
|
82
|
-
2.
|
|
83
|
-
3.
|
|
84
|
-
4. provar_testcase_validate
|
|
85
|
-
5. provar_testplan_add-instance
|
|
86
|
-
6. provar_testplan_validate
|
|
85
|
+
1. provar_project_inspect { project_path } ← find coverage gaps first
|
|
86
|
+
2. provar_qualityhub_examples_retrieve { object_or_scenario } ← ground in corpus examples for the step types you need
|
|
87
|
+
3. provar_testcase_generate { test_case_name, steps: [<ALL steps>] } ← single call, full step tree in one payload
|
|
88
|
+
4. provar_testcase_validate { file_path } ← must pass before adding to plan
|
|
89
|
+
5. provar_testplan_add-instance { project_path, plan_name, test_case_path }
|
|
90
|
+
6. provar_testplan_validate { project_path, plan_name }
|
|
87
91
|
```
|
|
88
92
|
|
|
93
|
+
Use `provar_testcase_step_edit` only when:
|
|
94
|
+
|
|
95
|
+
- Adding a single step to an existing, already-validated test case
|
|
96
|
+
- Fixing a step's attributes after a validation finding
|
|
97
|
+
- Targeted edits during debugging
|
|
98
|
+
|
|
99
|
+
Do **not** use `provar_testcase_step_edit` to construct a test case step-by-step from an empty skeleton — the LLM loses scenario context between calls and the resulting structure is unreliable.
|
|
100
|
+
|
|
89
101
|
---
|
|
90
102
|
|
|
91
103
|
## "I want to work with Salesforce metadata"
|
|
@@ -225,16 +225,23 @@ Required sequence — do not skip steps:
|
|
|
225
225
|
6. provar_qualityhub_defect_create → optional, create defects for failures`,
|
|
226
226
|
'author-test': `## Author a New Test Case
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
228
|
+
Construct the full step tree in a single \`provar_testcase_generate\` call.
|
|
229
|
+
\`provar_testcase_step_edit\` is for amending an existing case, not for
|
|
230
|
+
building one step-by-step (that pattern drops scenarios and flattens nesting).
|
|
231
|
+
|
|
232
|
+
1. provar_project_inspect → find coverage gaps before writing
|
|
233
|
+
2. provar_qualityhub_examples_retrieve → ground in corpus examples for the step types you need
|
|
234
|
+
3. provar_automation_metadata_download → if SF metadata is stale (missing fields/objects)
|
|
235
|
+
4. provar_pageobject_generate → only if a new page object is needed
|
|
236
|
+
5. provar_pageobject_validate → validate before compile
|
|
237
|
+
6. provar_automation_compile → after any page object change
|
|
238
|
+
7. provar_testcase_generate → single call, pass ALL steps in one payload
|
|
239
|
+
8. provar_testcase_validate → MUST pass before adding to a plan
|
|
240
|
+
9. provar_testplan_add-instance → add to an existing plan
|
|
241
|
+
10. provar_testplan_validate → validate the plan
|
|
242
|
+
|
|
243
|
+
Use provar_testcase_step_edit only to amend an existing validated test case
|
|
244
|
+
(single-step add, attribute fix, debug edit) — never to construct one from scratch.`,
|
|
238
245
|
'debug-failures': `## Debug Failing Tests
|
|
239
246
|
|
|
240
247
|
1. provar_testrun_report_locate → find the report file
|
|
@@ -278,11 +285,14 @@ provar_pageobject_validate
|
|
|
278
285
|
provar_nitrox_generate OR provar_nitrox_patch
|
|
279
286
|
└── provar_nitrox_validate (always validate after)
|
|
280
287
|
|
|
281
|
-
provar_testcase_generate
|
|
288
|
+
provar_testcase_generate (construct full case — pass ALL steps in one call)
|
|
282
289
|
└── provar_testcase_validate
|
|
283
290
|
└── provar_testplan_add-instance
|
|
284
291
|
└── provar_testplan_validate
|
|
285
292
|
|
|
293
|
+
provar_testcase_step_edit (amend an existing validated case only — never construct)
|
|
294
|
+
└── provar_testcase_validate
|
|
295
|
+
|
|
286
296
|
### Safe to run in parallel (no dependency between them)
|
|
287
297
|
- provar_project_inspect + provar_connection_list
|
|
288
298
|
- provar_pageobject_validate on multiple files
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guidePrompts.js","sourceRoot":"","sources":["../../../src/mcp/prompts/guidePrompts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,gFAAgF;AAEhF,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,CAAC,MAAM,CACX,yBAAyB,EACzB,kOAAkO,EAClO;QACE,WAAW,EAAE,CAAC;aACX,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,gIAAgI,CACjI;QACH,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,gJAAgJ,CACjJ;KACJ,EACD,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC1B,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE;;;;EAKhB,WAAW;wBACT,CAAC,CAAC,oCAAoC,WAAW,EAAE;wBACnD,CAAC,CAAC,iQACN;;kBAEkB,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4B7F,IAAI,KAAK,aAAa;wBACpB,CAAC,CAAC;;;;;;;;;;;;;8EAawE;wBAC1E,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;8EAsBN;;;;;;;;;;;;;;;;;yEAiByE;iBAC9D;aACF;SACF;KACF,CAAC,CACH,CAAC;AACJ,CAAC;AAED,gFAAgF;AAEhF,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,MAAM,CACX,2BAA2B,EAC3B,2NAA2N,EAC3N;QACE,YAAY,EAAE,CAAC;aACZ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,iIAAiI,CAClI;QACH,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yDAAyD,CAAC;KACvG,EACD,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;QAClC,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE;;EAGhB,YAAY;wBACV,CAAC,CAAC,6BAA6B,YAAY,EAAE;wBAC7C,CAAC,CAAC,4FACN;EACE,WAAW,CAAC,CAAC,CAAC,mBAAmB,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAqDzB;iBACjB;aACF;SACF;KACF,CAAC,CACH,CAAC;AACJ,CAAC;AAED,gFAAgF;AAEhF,MAAM,UAAU,2BAA2B,CAAC,MAAiB;IAC3D,MAAM,CAAC,MAAM,CACX,4BAA4B,EAC5B,4NAA4N,EAC5N;QACE,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,0PAA0P,CAC3P;KACJ,EACD,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;QACX,MAAM,KAAK,GAA2B;YACpC,WAAW,EAAE;;;;;;;;;;2DAUsC;YAEnD,iBAAiB,EAAE;;;;;;;;4EAQiD;YAEpE,aAAa,EAAE
|
|
1
|
+
{"version":3,"file":"guidePrompts.js","sourceRoot":"","sources":["../../../src/mcp/prompts/guidePrompts.ts"],"names":[],"mappings":"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"}
|
package/lib/mcp/server.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export interface ServerConfig {
|
|
|
7
7
|
updateCommand: string | null;
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
|
+
export declare function parseActiveGroups(): Set<string> | null;
|
|
10
11
|
export declare function createProvarMcpServer(config: ServerConfig): McpServer;
|
|
11
12
|
/**
|
|
12
13
|
* Resolve the docs directory for bundled MCP Markdown resources.
|
package/lib/mcp/server.js
CHANGED
|
@@ -32,7 +32,72 @@ import { registerAllTestPlanTools } from './tools/testPlanTools.js';
|
|
|
32
32
|
import { registerAllNitroXTools } from './tools/nitroXTools.js';
|
|
33
33
|
import { registerAllTestCaseStepTools } from './tools/testCaseStepTools.js';
|
|
34
34
|
import { registerAllConnectionTools } from './tools/connectionTools.js';
|
|
35
|
+
import { registerAllOrgDescribeTools } from './tools/orgDescribeTools.js';
|
|
35
36
|
import { registerAllPrompts } from './prompts/index.js';
|
|
37
|
+
import { createDepthGuardState, wrapWithDepthGuard, } from './utils/tokenMeta.js';
|
|
38
|
+
import { desc } from './tools/descHelper.js';
|
|
39
|
+
// ── Tool group registry ───────────────────────────────────────────────────────
|
|
40
|
+
// Groups are keyed in lowercase so they match the lowercased env var values.
|
|
41
|
+
const TOOL_GROUPS = {
|
|
42
|
+
nitrox: [registerAllNitroXTools],
|
|
43
|
+
automation: [registerAllAutomationTools],
|
|
44
|
+
qualityhub: [registerAllQualityHubTools, registerAllQualityHubApiTools, registerAllDefectTools],
|
|
45
|
+
validation: [
|
|
46
|
+
registerProjectValidateFromPath,
|
|
47
|
+
registerAllAntTools,
|
|
48
|
+
registerAllPropertiesTools,
|
|
49
|
+
registerTestCaseValidate,
|
|
50
|
+
registerTestSuiteValidate,
|
|
51
|
+
registerTestPlanValidate,
|
|
52
|
+
registerPageObjectValidate,
|
|
53
|
+
],
|
|
54
|
+
authoring: [
|
|
55
|
+
registerTestCaseGenerate,
|
|
56
|
+
registerPageObjectGenerate,
|
|
57
|
+
registerAllTestCaseStepTools,
|
|
58
|
+
registerAllTestPlanTools,
|
|
59
|
+
],
|
|
60
|
+
inspect: [registerProjectInspect, registerAllOrgDescribeTools],
|
|
61
|
+
connection: [registerAllConnectionTools],
|
|
62
|
+
rca: [registerAllRcaTools],
|
|
63
|
+
};
|
|
64
|
+
export function parseActiveGroups() {
|
|
65
|
+
const env = process.env['PROVAR_MCP_TOOLS'];
|
|
66
|
+
if (!env?.trim())
|
|
67
|
+
return null;
|
|
68
|
+
const requested = new Set(env
|
|
69
|
+
.split(',')
|
|
70
|
+
.map((g) => g.trim().toLowerCase())
|
|
71
|
+
.filter(Boolean));
|
|
72
|
+
if (requested.size === 0) {
|
|
73
|
+
log('warn', 'PROVAR_MCP_TOOLS was set but contained no valid group names — activating all groups', { raw: env });
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const known = new Set(Object.keys(TOOL_GROUPS));
|
|
77
|
+
const matched = new Set();
|
|
78
|
+
const unknown = [];
|
|
79
|
+
for (const g of requested) {
|
|
80
|
+
if (known.has(g))
|
|
81
|
+
matched.add(g);
|
|
82
|
+
else
|
|
83
|
+
unknown.push(g);
|
|
84
|
+
}
|
|
85
|
+
if (unknown.length > 0) {
|
|
86
|
+
log('warn', 'PROVAR_MCP_TOOLS contains unknown group names — they will be ignored', {
|
|
87
|
+
raw: env,
|
|
88
|
+
unknown,
|
|
89
|
+
known: [...known],
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (matched.size === 0) {
|
|
93
|
+
log('warn', 'PROVAR_MCP_TOOLS matched no known group names — activating all groups', {
|
|
94
|
+
raw: env,
|
|
95
|
+
known: [...known],
|
|
96
|
+
});
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return matched;
|
|
100
|
+
}
|
|
36
101
|
export function createProvarMcpServer(config) {
|
|
37
102
|
log('info', 'Creating Provar MCP server', { allowedPaths: config.allowedPaths });
|
|
38
103
|
const server = new McpServer({
|
|
@@ -42,9 +107,13 @@ export function createProvarMcpServer(config) {
|
|
|
42
107
|
// ── Sanity-check tool ────────────────────────────────────────────────────────
|
|
43
108
|
server.registerTool('provardx_ping', {
|
|
44
109
|
title: 'Ping MCP Server',
|
|
45
|
-
description: 'Sanity-check tool. Echoes back a message with a timestamp. Use this to verify the MCP server is reachable before calling other tools.',
|
|
110
|
+
description: desc('Sanity-check tool. Echoes back a message with a timestamp. Use this to verify the MCP server is reachable before calling other tools.', 'Echo message back with timestamp; verify MCP server is reachable.'),
|
|
46
111
|
inputSchema: {
|
|
47
|
-
message: z
|
|
112
|
+
message: z
|
|
113
|
+
.string()
|
|
114
|
+
.optional()
|
|
115
|
+
.default('ping')
|
|
116
|
+
.describe(desc('Optional message to echo back', 'message to echo')),
|
|
48
117
|
},
|
|
49
118
|
}, ({ message }) => {
|
|
50
119
|
const result = {
|
|
@@ -60,26 +129,20 @@ export function createProvarMcpServer(config) {
|
|
|
60
129
|
structuredContent: result,
|
|
61
130
|
};
|
|
62
131
|
});
|
|
132
|
+
// ── Depth-guard middleware (PDX-474) ─────────────────────────────────────────
|
|
133
|
+
const rawLimit = parseInt(process.env['PROVAR_MCP_MAX_TOOL_DEPTH'] ?? '50', 10);
|
|
134
|
+
const depthLimit = Number.isNaN(rawLimit) || rawLimit <= 0 ? 50 : rawLimit;
|
|
135
|
+
const depthState = createDepthGuardState();
|
|
136
|
+
patchWithMiddleware(server, depthState, depthLimit);
|
|
63
137
|
// ── Provar tools ─────────────────────────────────────────────────────────────
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
registerAllPropertiesTools(server, config);
|
|
73
|
-
registerAllQualityHubTools(server);
|
|
74
|
-
registerAllQualityHubApiTools(server);
|
|
75
|
-
registerAllAutomationTools(server, config);
|
|
76
|
-
registerAllDefectTools(server);
|
|
77
|
-
registerAllAntTools(server, config);
|
|
78
|
-
registerAllRcaTools(server, config);
|
|
79
|
-
registerAllTestPlanTools(server, config);
|
|
80
|
-
registerAllNitroXTools(server, config);
|
|
81
|
-
registerAllTestCaseStepTools(server, config);
|
|
82
|
-
registerAllConnectionTools(server, config);
|
|
138
|
+
const activeGroups = parseActiveGroups();
|
|
139
|
+
for (const [group, registrars] of Object.entries(TOOL_GROUPS)) {
|
|
140
|
+
if (activeGroups === null || activeGroups.has(group)) {
|
|
141
|
+
for (const register of registrars) {
|
|
142
|
+
register(server, config);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
83
146
|
// ── Provar prompts ───────────────────────────────────────────────────────────
|
|
84
147
|
registerAllPrompts(server);
|
|
85
148
|
// ── Documentation resources ──────────────────────────────────────────────────
|
|
@@ -167,6 +230,12 @@ export function createProvarMcpServer(config) {
|
|
|
167
230
|
});
|
|
168
231
|
return server;
|
|
169
232
|
}
|
|
233
|
+
function patchWithMiddleware(server, state, limit) {
|
|
234
|
+
const orig = server.registerTool.bind(server);
|
|
235
|
+
// Cast through unknown to patch the overloaded method without triggering no-unsafe-any.
|
|
236
|
+
const patchable = server;
|
|
237
|
+
patchable.registerTool = (name, config, handler) => orig(name, config, wrapWithDepthGuard(name, handler, state, limit));
|
|
238
|
+
}
|
|
170
239
|
/**
|
|
171
240
|
* Resolve the docs directory for bundled MCP Markdown resources.
|
|
172
241
|
* In compiled output (lib/mcp/) the sibling docs/ dir exists; in dev/ts-node
|
package/lib/mcp/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAE1C,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnD,MAAM,cAAc,GAAY,WAAW,CAAC,oBAAoB,CAAyB,CAAC,OAAO,CAAC;AAClG,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,+BAA+B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,15 +18,35 @@ export interface AntValidationResult {
|
|
|
18
18
|
}
|
|
19
19
|
/** Pure function — exported for unit testing */
|
|
20
20
|
export declare function validateAntXml(xmlContent: string): AntValidationResult;
|
|
21
|
+
export type JUnitErrorCategory = 'INFRASTRUCTURE' | 'ASSERTION' | 'LOCATOR' | 'TIMEOUT' | 'OTHER';
|
|
21
22
|
export interface JUnitStepResult {
|
|
22
23
|
testItemId: string;
|
|
23
24
|
title: string;
|
|
24
25
|
status: 'pass' | 'fail' | 'skip';
|
|
25
26
|
errorMessage?: string;
|
|
27
|
+
error_category?: JUnitErrorCategory;
|
|
28
|
+
retryable?: boolean;
|
|
26
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Classify a failure message into a coarse-grained category used for retry decisions.
|
|
32
|
+
* Mirrors the classifier in rcaTools.ts (PDX-490) so a downstream consumer sees the
|
|
33
|
+
* same labelling whether they consume `provar_automation_testrun.steps[]` or
|
|
34
|
+
* `provar_testrun_rca.failures[]`.
|
|
35
|
+
*
|
|
36
|
+
* Returns `undefined` when no pattern matches.
|
|
37
|
+
*/
|
|
38
|
+
export declare function classifyStepErrorCategory(errorText: string): JUnitErrorCategory | undefined;
|
|
39
|
+
/** Only transient categories (INFRASTRUCTURE, TIMEOUT) are retryable. */
|
|
40
|
+
export declare function isStepRetryable(category: JUnitErrorCategory | undefined): boolean | undefined;
|
|
27
41
|
export interface JUnitParseResult {
|
|
28
42
|
steps: JUnitStepResult[];
|
|
29
43
|
warning?: string;
|
|
44
|
+
/**
|
|
45
|
+
* True iff at least one JUnit XML file was located AND parsed without throwing.
|
|
46
|
+
* Distinguishes "we have data and the test selector matched zero cases" (legit RUN-001 signal)
|
|
47
|
+
* from "we have no data because nothing parsed" (insufficient info — must stay silent).
|
|
48
|
+
*/
|
|
49
|
+
parsedAny: boolean;
|
|
30
50
|
}
|
|
31
51
|
/**
|
|
32
52
|
* Scan a Provar results directory for JUnit XML files and return structured step results.
|
|
@@ -12,6 +12,7 @@ import { XMLParser } from 'fast-xml-parser';
|
|
|
12
12
|
import { assertPathAllowed, PathPolicyError } from '../security/pathPolicy.js';
|
|
13
13
|
import { makeError, makeRequestId } from '../schemas/common.js';
|
|
14
14
|
import { log } from '../logging/logger.js';
|
|
15
|
+
import { desc } from './descHelper.js';
|
|
15
16
|
// ── Sub-schemas ───────────────────────────────────────────────────────────────
|
|
16
17
|
const FilesetSchema = z.object({
|
|
17
18
|
dir: z.string().describe('Directory path (relative or absolute) for the fileset'),
|
|
@@ -56,126 +57,141 @@ const AttachmentPropertiesSchema = z.object({
|
|
|
56
57
|
export function registerAntGenerate(server, config) {
|
|
57
58
|
server.registerTool('provar_ant_generate', {
|
|
58
59
|
title: 'Generate ANT Build File',
|
|
59
|
-
description: [
|
|
60
|
+
description: desc([
|
|
60
61
|
'Generate a Provar ANT build.xml file.',
|
|
61
62
|
'Produces the standard <project> skeleton with Provar-Compile and Run-Test-Case tasks.',
|
|
62
63
|
'Supports targeting tests by project folder, plan folder, or specific .testcase files via filesets.',
|
|
63
64
|
'Returns XML content. Writes to disk only when dry_run=false.',
|
|
64
|
-
].join(' '),
|
|
65
|
+
].join(' '), 'Generate a Provar ANT build.xml with Provar-Compile and Run-Test-Case tasks.'),
|
|
65
66
|
inputSchema: {
|
|
66
67
|
// ── Core paths ──────────────────────────────────────────────────────────
|
|
67
68
|
provar_home: z
|
|
68
69
|
.string()
|
|
69
|
-
.describe('Absolute path to the Provar installation directory (e.g. "C:/Program Files/Provar/"). Used for provar.home property and ant taskdef classpaths.'),
|
|
70
|
+
.describe(desc('Absolute path to the Provar installation directory (e.g. "C:/Program Files/Provar/"). Used for provar.home property and ant taskdef classpaths.', 'string, absolute path to Provar installation')),
|
|
70
71
|
project_path: z
|
|
71
72
|
.string()
|
|
72
73
|
.default('..')
|
|
73
|
-
.describe('Path to the Provar test project root. Defaults to ".." (parent of the ANT folder).'),
|
|
74
|
+
.describe(desc('Path to the Provar test project root. Defaults to ".." (parent of the ANT folder).', 'string, path to project root')),
|
|
74
75
|
results_path: z
|
|
75
76
|
.string()
|
|
76
77
|
.default('../ANT/Results')
|
|
77
|
-
.describe('Path where test results are written. Defaults to "../ANT/Results".'),
|
|
78
|
+
.describe(desc('Path where test results are written. Defaults to "../ANT/Results".', 'string, path for test results')),
|
|
78
79
|
project_cache_path: z
|
|
79
80
|
.string()
|
|
80
81
|
.optional()
|
|
81
|
-
.describe('Path to the .provarCaches directory. Defaults to "../../.provarCaches" relative to the ANT folder.'),
|
|
82
|
+
.describe(desc('Path to the .provarCaches directory. Defaults to "../../.provarCaches" relative to the ANT folder.', 'string, optional; path to .provarCaches')),
|
|
82
83
|
license_path: z
|
|
83
84
|
.string()
|
|
84
85
|
.optional()
|
|
85
|
-
.describe('Path to the Provar .licenses directory (e.g. "${env.PROVAR_HOME}/.licenses").'),
|
|
86
|
+
.describe(desc('Path to the Provar .licenses directory (e.g. "${env.PROVAR_HOME}/.licenses").', 'string, optional; path to .licenses dir')),
|
|
86
87
|
smtp_path: z
|
|
87
88
|
.string()
|
|
88
89
|
.optional()
|
|
89
|
-
.describe('Path to the Provar .smtp directory (e.g. "${env.PROVAR_HOME}/.smtp").'),
|
|
90
|
+
.describe(desc('Path to the Provar .smtp directory (e.g. "${env.PROVAR_HOME}/.smtp").', 'string, optional; path to .smtp dir')),
|
|
90
91
|
// ── Test selection ──────────────────────────────────────────────────────
|
|
91
92
|
filesets: z
|
|
92
93
|
.array(FilesetSchema)
|
|
93
94
|
.min(1)
|
|
94
|
-
.describe('One or more filesets defining which tests to run. ' +
|
|
95
|
+
.describe(desc('One or more filesets defining which tests to run. ' +
|
|
95
96
|
'To run all tests under a folder: { dir: "../tests" }. ' +
|
|
96
97
|
'To run a plan: { id: "testplan", dir: "../plans/MyPlan" }. ' +
|
|
97
|
-
'To run specific test cases: { dir: "../tests/Suite", includes: ["MyTest.testcase"] }.'),
|
|
98
|
+
'To run specific test cases: { dir: "../tests/Suite", includes: ["MyTest.testcase"] }.', 'array, min 1; filesets defining which tests to run')),
|
|
98
99
|
// ── Browser / environment ───────────────────────────────────────────────
|
|
99
100
|
web_browser: z
|
|
100
101
|
.enum(['Chrome', 'Chrome_Headless', 'Firefox', 'Edge', 'Edge_Legacy', 'Safari', 'IE'])
|
|
101
102
|
.default('Chrome')
|
|
102
|
-
.describe('Web browser to use for test execution.'),
|
|
103
|
+
.describe(desc('Web browser to use for test execution.', 'enum Chrome|Chrome_Headless|Firefox|Edge|Safari|IE')),
|
|
103
104
|
web_browser_configuration: z
|
|
104
105
|
.string()
|
|
105
106
|
.default('Full Screen')
|
|
106
|
-
.describe('Browser window configuration (e.g. "Full Screen").'),
|
|
107
|
-
web_browser_provider_name: z
|
|
107
|
+
.describe(desc('Browser window configuration (e.g. "Full Screen").', 'string, browser window config')),
|
|
108
|
+
web_browser_provider_name: z
|
|
109
|
+
.string()
|
|
110
|
+
.default('Desktop')
|
|
111
|
+
.describe(desc('Browser provider name (e.g. "Desktop").', 'string, browser provider name')),
|
|
108
112
|
web_browser_device_name: z
|
|
109
113
|
.string()
|
|
110
114
|
.default('Full Screen')
|
|
111
|
-
.describe('Browser device name (e.g. "Full Screen").'),
|
|
115
|
+
.describe(desc('Browser device name (e.g. "Full Screen").', 'string, browser device name')),
|
|
112
116
|
test_environment: z
|
|
113
117
|
.string()
|
|
114
118
|
.default('')
|
|
115
|
-
.describe('Named test environment to use (must match a connection in the project). Empty string uses default.'),
|
|
119
|
+
.describe(desc('Named test environment to use (must match a connection in the project). Empty string uses default.', 'string, optional; named test environment')),
|
|
116
120
|
// ── Cache / metadata ────────────────────────────────────────────────────
|
|
117
121
|
salesforce_metadata_cache: z
|
|
118
122
|
.enum(['Reuse', 'Refresh', 'Reload'])
|
|
119
123
|
.default('Reuse')
|
|
120
|
-
.describe('Salesforce metadata cache strategy: Reuse (fastest, uses cached), Refresh (re-downloads), Reload (clears and re-downloads).'),
|
|
124
|
+
.describe(desc('Salesforce metadata cache strategy: Reuse (fastest, uses cached), Refresh (re-downloads), Reload (clears and re-downloads).', 'enum Reuse|Refresh|Reload')),
|
|
121
125
|
// ── Output / logging ────────────────────────────────────────────────────
|
|
122
126
|
results_path_disposition: z
|
|
123
127
|
.enum(['Increment', 'Replace', 'Reuse'])
|
|
124
128
|
.default('Increment')
|
|
125
|
-
.describe('How to handle the results folder when it already exists: Increment (new subfolder), Replace (overwrite), Reuse (append).'),
|
|
129
|
+
.describe(desc('How to handle the results folder when it already exists: Increment (new subfolder), Replace (overwrite), Reuse (append).', 'enum Increment|Replace|Reuse')),
|
|
126
130
|
test_output_level: z
|
|
127
131
|
.enum(['BASIC', 'WARNING', 'DEBUG'])
|
|
128
132
|
.default('BASIC')
|
|
129
|
-
.describe('Verbosity level for test output logs.'),
|
|
133
|
+
.describe(desc('Verbosity level for test output logs.', 'enum BASIC|WARNING|DEBUG')),
|
|
130
134
|
plugin_output_level: z
|
|
131
135
|
.enum(['BASIC', 'WARNING', 'DEBUG'])
|
|
132
136
|
.default('WARNING')
|
|
133
|
-
.describe('Verbosity level for plugin output logs.'),
|
|
137
|
+
.describe(desc('Verbosity level for plugin output logs.', 'enum BASIC|WARNING|DEBUG')),
|
|
134
138
|
// ── Execution behaviour ─────────────────────────────────────────────────
|
|
135
139
|
stop_test_run_on_error: z
|
|
136
140
|
.boolean()
|
|
137
141
|
.default(false)
|
|
138
|
-
.describe('Abort the entire test run when any test case fails.'),
|
|
142
|
+
.describe(desc('Abort the entire test run when any test case fails.', 'bool, optional; abort on failure')),
|
|
139
143
|
exclude_callable_test_cases: z
|
|
140
144
|
.boolean()
|
|
141
145
|
.default(true)
|
|
142
|
-
.describe('Skip test cases marked as callable (library/helper) when true.'),
|
|
146
|
+
.describe(desc('Skip test cases marked as callable (library/helper) when true.', 'bool, optional; skip callable tests')),
|
|
143
147
|
dont_fail_build: z
|
|
144
148
|
.boolean()
|
|
145
149
|
.optional()
|
|
146
|
-
.describe('When true, the ANT build does not fail even if tests fail. Useful for CI pipelines that collect results separately.'),
|
|
147
|
-
invoke_test_run_monitor: z
|
|
150
|
+
.describe(desc('When true, the ANT build does not fail even if tests fail. Useful for CI pipelines that collect results separately.', 'bool, optional; skip build failure on test fail')),
|
|
151
|
+
invoke_test_run_monitor: z
|
|
152
|
+
.boolean()
|
|
153
|
+
.default(true)
|
|
154
|
+
.describe(desc('Enable the Provar test run monitor.', 'bool, optional; enable test run monitor')),
|
|
148
155
|
// ── Secrets / security ──────────────────────────────────────────────────
|
|
149
156
|
secrets_password: z
|
|
150
157
|
.string()
|
|
151
158
|
.default('${env.ProvarSecretsPassword}')
|
|
152
|
-
.describe('Encryption key used to decrypt the Provar .secrets file (the password string itself, not a file path). Defaults to reading from the ProvarSecretsPassword environment variable.'),
|
|
159
|
+
.describe(desc('Encryption key used to decrypt the Provar .secrets file (the password string itself, not a file path). Defaults to reading from the ProvarSecretsPassword environment variable.', 'string, NOT a file path; encryption key for .secrets')),
|
|
153
160
|
test_environment_secrets_password: z
|
|
154
161
|
.string()
|
|
155
162
|
.optional()
|
|
156
|
-
.describe('Per-environment secrets password. Defaults to reading from the ProvarSecretsPassword_EnvName environment variable.'),
|
|
163
|
+
.describe(desc('Per-environment secrets password. Defaults to reading from the ProvarSecretsPassword_EnvName environment variable.', 'string, optional; per-environment secrets key')),
|
|
157
164
|
// ── Test Cycle ──────────────────────────────────────────────────────────
|
|
158
|
-
test_cycle_path: z
|
|
165
|
+
test_cycle_path: z
|
|
166
|
+
.string()
|
|
167
|
+
.optional()
|
|
168
|
+
.describe(desc('Path to a TestCycle folder (used with test cycle reporting).', 'string, optional; path to TestCycle folder')),
|
|
159
169
|
test_cycle_run_type: z
|
|
160
170
|
.enum(['ALL', 'FAILED', 'NEW'])
|
|
161
171
|
.optional()
|
|
162
|
-
.describe('Which tests in the cycle to run (ALL, FAILED, NEW).'),
|
|
172
|
+
.describe(desc('Which tests in the cycle to run (ALL, FAILED, NEW).', 'enum ALL|FAILED|NEW, optional')),
|
|
163
173
|
// ── Plan features ───────────────────────────────────────────────────────
|
|
164
174
|
plan_features: z
|
|
165
175
|
.array(PlanFeatureSchema)
|
|
166
176
|
.optional()
|
|
167
|
-
.describe('Output and notification features to enable/disable (e.g. PDF, PIECHART, EMAIL). ' +
|
|
168
|
-
'Only meaningful when running by test plan.'),
|
|
177
|
+
.describe(desc('Output and notification features to enable/disable (e.g. PDF, PIECHART, EMAIL). ' +
|
|
178
|
+
'Only meaningful when running by test plan.', 'array, optional; plan output/notification features')),
|
|
169
179
|
// ── Email / attachment reporting ────────────────────────────────────────
|
|
170
|
-
email_properties: EmailPropertiesSchema.optional().describe('Email notification settings. Omit to exclude <emailProperties> from the XML.'),
|
|
171
|
-
attachment_properties: AttachmentPropertiesSchema.optional().describe('Attachment/report content settings. Omit to exclude <attachmentProperties> from the XML.'),
|
|
180
|
+
email_properties: EmailPropertiesSchema.optional().describe(desc('Email notification settings. Omit to exclude <emailProperties> from the XML.', 'object, optional; email notification settings')),
|
|
181
|
+
attachment_properties: AttachmentPropertiesSchema.optional().describe(desc('Attachment/report content settings. Omit to exclude <attachmentProperties> from the XML.', 'object, optional; attachment/report content settings')),
|
|
172
182
|
// ── File output ─────────────────────────────────────────────────────────
|
|
173
183
|
output_path: z
|
|
174
184
|
.string()
|
|
175
185
|
.optional()
|
|
176
|
-
.describe('Where to write the build.xml file (returned in response). Required when dry_run=false.'),
|
|
177
|
-
overwrite: z
|
|
178
|
-
|
|
186
|
+
.describe(desc('Where to write the build.xml file (returned in response). Required when dry_run=false.', 'string, optional; absolute path for build.xml output')),
|
|
187
|
+
overwrite: z
|
|
188
|
+
.boolean()
|
|
189
|
+
.default(false)
|
|
190
|
+
.describe(desc('Overwrite output_path if the file already exists.', 'bool, optional; overwrite if exists')),
|
|
191
|
+
dry_run: z
|
|
192
|
+
.boolean()
|
|
193
|
+
.default(true)
|
|
194
|
+
.describe(desc('true = return XML only (default); false = write to output_path.', 'bool, optional; true=return only, false=write')),
|
|
179
195
|
},
|
|
180
196
|
}, (input) => {
|
|
181
197
|
const requestId = makeRequestId();
|
|
@@ -234,15 +250,21 @@ export function registerAntGenerate(server, config) {
|
|
|
234
250
|
export function registerAntValidate(server, config) {
|
|
235
251
|
server.registerTool('provar_ant_validate', {
|
|
236
252
|
title: 'Validate ANT Build File',
|
|
237
|
-
description: [
|
|
253
|
+
description: desc([
|
|
238
254
|
'Validate a Provar ANT build.xml for structural correctness.',
|
|
239
255
|
'Checks XML well-formedness, required <taskdef> declarations, <Provar-Compile> step,',
|
|
240
256
|
'<Run-Test-Case> with required attributes (provarHome, projectPath, resultsPath),',
|
|
241
257
|
'and at least one <fileset> child. Returns is_valid, issues list, and a validity_score.',
|
|
242
|
-
].join(' '),
|
|
258
|
+
].join(' '), 'Validate a Provar ANT build.xml for structural correctness.'),
|
|
243
259
|
inputSchema: {
|
|
244
|
-
content: z
|
|
245
|
-
|
|
260
|
+
content: z
|
|
261
|
+
.string()
|
|
262
|
+
.optional()
|
|
263
|
+
.describe(desc('XML content to validate directly', 'string, optional; inline XML')),
|
|
264
|
+
file_path: z
|
|
265
|
+
.string()
|
|
266
|
+
.optional()
|
|
267
|
+
.describe(desc('Path to the build.xml file to validate', 'string, optional; absolute path to build.xml')),
|
|
246
268
|
},
|
|
247
269
|
}, ({ content, file_path }) => {
|
|
248
270
|
const requestId = makeRequestId();
|
|
@@ -642,6 +664,35 @@ function finalizeAnt(issues, provarHome, projectPath, resultsPath, webBrowser, t
|
|
|
642
664
|
issues,
|
|
643
665
|
};
|
|
644
666
|
}
|
|
667
|
+
/**
|
|
668
|
+
* Classify a failure message into a coarse-grained category used for retry decisions.
|
|
669
|
+
* Mirrors the classifier in rcaTools.ts (PDX-490) so a downstream consumer sees the
|
|
670
|
+
* same labelling whether they consume `provar_automation_testrun.steps[]` or
|
|
671
|
+
* `provar_testrun_rca.failures[]`.
|
|
672
|
+
*
|
|
673
|
+
* Returns `undefined` when no pattern matches.
|
|
674
|
+
*/
|
|
675
|
+
export function classifyStepErrorCategory(errorText) {
|
|
676
|
+
if (/Connection reset|Failed to read client socket message|socket hang up|ECONNRESET/i.test(errorText)) {
|
|
677
|
+
return 'INFRASTRUCTURE';
|
|
678
|
+
}
|
|
679
|
+
if (/NoSuchElementException/i.test(errorText))
|
|
680
|
+
return 'LOCATOR';
|
|
681
|
+
if (/TimeoutException/i.test(errorText))
|
|
682
|
+
return 'TIMEOUT';
|
|
683
|
+
if (/AssertionException/i.test(errorText))
|
|
684
|
+
return 'ASSERTION';
|
|
685
|
+
if (/SessionNotCreatedException|WebDriverException|ClassNotFoundException|LicenseException|InvalidPasswordException/i.test(errorText)) {
|
|
686
|
+
return 'OTHER';
|
|
687
|
+
}
|
|
688
|
+
return undefined;
|
|
689
|
+
}
|
|
690
|
+
/** Only transient categories (INFRASTRUCTURE, TIMEOUT) are retryable. */
|
|
691
|
+
export function isStepRetryable(category) {
|
|
692
|
+
if (category === undefined)
|
|
693
|
+
return undefined;
|
|
694
|
+
return category === 'INFRASTRUCTURE' || category === 'TIMEOUT';
|
|
695
|
+
}
|
|
645
696
|
function extractFailureText(el) {
|
|
646
697
|
if (!el)
|
|
647
698
|
return undefined;
|
|
@@ -695,8 +746,15 @@ function extractStepsFromJUnit(parsed) {
|
|
|
695
746
|
status = 'skip';
|
|
696
747
|
const errorMessage = extractFailureText(tc['failure'] ?? tc['error']);
|
|
697
748
|
const step = { testItemId: String(idx), title, status };
|
|
698
|
-
if (errorMessage)
|
|
749
|
+
if (errorMessage) {
|
|
699
750
|
step.errorMessage = errorMessage;
|
|
751
|
+
const error_category = classifyStepErrorCategory(errorMessage);
|
|
752
|
+
const retryable = isStepRetryable(error_category);
|
|
753
|
+
if (error_category !== undefined)
|
|
754
|
+
step.error_category = error_category;
|
|
755
|
+
if (retryable !== undefined)
|
|
756
|
+
step.retryable = retryable;
|
|
757
|
+
}
|
|
700
758
|
steps.push(step);
|
|
701
759
|
}
|
|
702
760
|
}
|
|
@@ -722,13 +780,14 @@ function findXmlFiles(dir) {
|
|
|
722
780
|
*/
|
|
723
781
|
export function parseJUnitResults(resultsDir) {
|
|
724
782
|
if (!fs.existsSync(resultsDir)) {
|
|
725
|
-
return { steps: [], warning: `Results directory not found: ${resultsDir}
|
|
783
|
+
return { steps: [], warning: `Results directory not found: ${resultsDir}`, parsedAny: false };
|
|
726
784
|
}
|
|
727
785
|
const xmlFiles = findXmlFiles(resultsDir);
|
|
728
786
|
if (xmlFiles.length === 0) {
|
|
729
787
|
return {
|
|
730
788
|
steps: [],
|
|
731
789
|
warning: 'No JUnit XML files found in results directory — structured step output unavailable.',
|
|
790
|
+
parsedAny: false,
|
|
732
791
|
};
|
|
733
792
|
}
|
|
734
793
|
const parser = new XMLParser({
|
|
@@ -758,18 +817,23 @@ export function parseJUnitResults(resultsDir) {
|
|
|
758
817
|
return {
|
|
759
818
|
steps: [],
|
|
760
819
|
warning: 'JUnit XML files found but could not be parsed — structured step output unavailable.',
|
|
820
|
+
parsedAny: false,
|
|
761
821
|
};
|
|
762
822
|
}
|
|
763
823
|
if (allSteps.length === 0) {
|
|
824
|
+
// We did parse at least one file; the file just had zero <testcase> entries (or none we could
|
|
825
|
+
// recognise as steps). This is the legitimate "selector matched nothing" signal that RUN-001
|
|
826
|
+
// is built to catch.
|
|
764
827
|
return {
|
|
765
828
|
steps: [],
|
|
766
829
|
warning: 'JUnit XML found but no test steps could be extracted — files may not be standard JUnit format.',
|
|
830
|
+
parsedAny: true,
|
|
767
831
|
};
|
|
768
832
|
}
|
|
769
833
|
const warning = parseFailures > 0
|
|
770
834
|
? `${parseFailures} JUnit XML file(s) could not be parsed — step data may be incomplete.`
|
|
771
835
|
: undefined;
|
|
772
|
-
return { steps: allSteps, warning };
|
|
836
|
+
return { steps: allSteps, warning, parsedAny: true };
|
|
773
837
|
}
|
|
774
838
|
// ── Registration ──────────────────────────────────────────────────────────────
|
|
775
839
|
export function registerAllAntTools(server, config) {
|