edsger 0.2.0 → 0.2.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/dist/api/features/feature-utils.d.ts +13 -0
- package/dist/api/features/feature-utils.js +46 -0
- package/dist/api/features/get-feature.d.ts +5 -0
- package/dist/api/features/get-feature.js +19 -0
- package/dist/api/features/index.d.ts +7 -0
- package/dist/api/features/index.js +9 -0
- package/dist/api/features/status-updater.d.ts +27 -0
- package/dist/api/features/status-updater.js +64 -0
- package/dist/api/features/test-cases.d.ts +21 -0
- package/dist/api/features/test-cases.js +63 -0
- package/dist/api/features/update-feature.d.ts +13 -0
- package/dist/api/features/update-feature.js +31 -0
- package/dist/api/features/user-stories.d.ts +21 -0
- package/dist/api/features/user-stories.js +63 -0
- package/dist/api/features.d.ts +100 -0
- package/dist/api/features.js +219 -0
- package/dist/api/mcp-client.d.ts +18 -0
- package/dist/api/mcp-client.js +58 -0
- package/dist/api/products.d.ts +10 -0
- package/dist/api/products.js +22 -0
- package/dist/api/test-reports.d.ts +9 -0
- package/dist/api/test-reports.js +25 -0
- package/dist/cli/commands/code-implementation-command.d.ts +2 -0
- package/dist/cli/commands/code-implementation-command.js +36 -0
- package/dist/cli/commands/code-review-command.d.ts +2 -0
- package/dist/cli/commands/code-review-command.js +39 -0
- package/dist/cli/commands/feature-analysis-command.d.ts +2 -0
- package/dist/cli/commands/feature-analysis-command.js +36 -0
- package/dist/cli/commands/functional-testing-command.d.ts +2 -0
- package/dist/cli/commands/functional-testing-command.js +36 -0
- package/dist/cli/commands/technical-design-command.d.ts +2 -0
- package/dist/cli/commands/technical-design-command.js +36 -0
- package/dist/cli/commands/workflow-command.d.ts +2 -0
- package/dist/cli/commands/workflow-command.js +34 -0
- package/dist/cli/formatters/code-implementation-formatter.d.ts +9 -0
- package/dist/cli/formatters/code-implementation-formatter.js +27 -0
- package/dist/cli/formatters/feature-analysis-formatter.d.ts +2 -0
- package/dist/cli/formatters/feature-analysis-formatter.js +27 -0
- package/dist/cli/formatters/functional-testing-formatter.d.ts +15 -0
- package/dist/cli/formatters/functional-testing-formatter.js +37 -0
- package/dist/cli/formatters/technical-design-formatter.d.ts +7 -0
- package/dist/cli/formatters/technical-design-formatter.js +30 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +99 -0
- package/dist/cli/utils/validation.d.ts +25 -0
- package/dist/cli/utils/validation.js +58 -0
- package/dist/cli/utils/workflow-utils.d.ts +21 -0
- package/dist/cli/utils/workflow-utils.js +47 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +11 -466
- package/dist/config.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/{bug-fixing → phases/bug-fixing}/analyzer.d.ts +1 -1
- package/dist/{bug-fixing → phases/bug-fixing}/analyzer.js +1 -1
- package/dist/{bug-fixing → phases/bug-fixing}/context-fetcher.d.ts +4 -22
- package/dist/{bug-fixing → phases/bug-fixing}/context-fetcher.js +14 -58
- package/dist/{bug-fixing → phases/bug-fixing}/mcp-server.js +1 -30
- package/dist/phases/code-implementation/analyzer.d.ts +33 -0
- package/dist/{code-implementation → phases/code-implementation}/analyzer.js +174 -15
- package/dist/phases/code-implementation/context-fetcher.d.ts +17 -0
- package/dist/phases/code-implementation/context-fetcher.js +86 -0
- package/dist/{code-implementation → phases/code-implementation}/mcp-server.js +1 -30
- package/dist/{code-review → phases/code-review}/reviewer.d.ts +1 -1
- package/dist/{feature-analysis → phases/feature-analysis}/analyzer.d.ts +3 -2
- package/dist/{feature-analysis → phases/feature-analysis}/analyzer.js +29 -127
- package/dist/phases/feature-analysis/context-fetcher.d.ts +18 -0
- package/dist/phases/feature-analysis/context-fetcher.js +86 -0
- package/dist/{feature-analysis → phases/feature-analysis}/http-fallback.js +1 -1
- package/dist/{feature-analysis → phases/feature-analysis}/mcp-server.js +1 -24
- package/dist/{functional-testing → phases/functional-testing}/analyzer.d.ts +17 -2
- package/dist/{functional-testing → phases/functional-testing}/analyzer.js +225 -31
- package/dist/phases/functional-testing/context-fetcher.d.ts +16 -0
- package/dist/phases/functional-testing/context-fetcher.js +81 -0
- package/dist/{functional-testing → phases/functional-testing}/http-fallback.js +1 -1
- package/dist/{functional-testing → phases/functional-testing}/index.d.ts +1 -1
- package/dist/{functional-testing → phases/functional-testing}/index.js +1 -1
- package/dist/{functional-testing → phases/functional-testing}/mcp-server.js +1 -30
- package/dist/{functional-testing → phases/functional-testing}/test-report-creator.d.ts +26 -0
- package/dist/{functional-testing → phases/functional-testing}/test-report-creator.js +87 -5
- package/dist/phases/functional-testing/test-retry-handler.d.ts +16 -0
- package/dist/phases/functional-testing/test-retry-handler.js +75 -0
- package/dist/{pull-request → phases/pull-request}/creator.js +47 -6
- package/dist/phases/pull-request/handler.d.ts +16 -0
- package/dist/phases/pull-request/handler.js +60 -0
- package/dist/{technical-design → phases/technical-design}/analyzer.d.ts +7 -2
- package/dist/phases/technical-design/analyzer.js +418 -0
- package/dist/phases/technical-design/context-fetcher.d.ts +12 -0
- package/dist/phases/technical-design/context-fetcher.js +39 -0
- package/dist/{technical-design → phases/technical-design}/http-fallback.js +1 -1
- package/dist/{technical-design → phases/technical-design}/mcp-server.js +1 -30
- package/dist/prompts/bug-fixing.d.ts +2 -0
- package/dist/prompts/bug-fixing.js +63 -0
- package/dist/prompts/code-implementation.d.ts +3 -0
- package/dist/prompts/code-implementation.js +132 -0
- package/dist/prompts/feature-analysis.d.ts +3 -0
- package/dist/prompts/feature-analysis.js +149 -0
- package/dist/prompts/formatters.d.ts +29 -0
- package/dist/prompts/formatters.js +139 -0
- package/dist/prompts/functional-testing.d.ts +3 -0
- package/dist/prompts/functional-testing.js +126 -0
- package/dist/prompts/index.d.ts +6 -0
- package/dist/prompts/index.js +7 -0
- package/dist/prompts/technical-design.d.ts +3 -0
- package/dist/prompts/technical-design.js +130 -0
- package/dist/services/checklist.d.ts +99 -0
- package/dist/services/checklist.js +337 -0
- package/dist/types/features.d.ts +29 -0
- package/dist/types/features.js +1 -0
- package/dist/types/index.d.ts +112 -0
- package/dist/types/index.js +1 -0
- package/dist/types/pipeline.d.ts +25 -0
- package/dist/types/pipeline.js +4 -0
- package/dist/utils/logger.d.ts +19 -0
- package/dist/utils/logger.js +52 -0
- package/dist/utils/pipeline-logger.d.ts +8 -0
- package/dist/utils/pipeline-logger.js +35 -0
- package/dist/workflow-runner/config/phase-configs.d.ts +5 -0
- package/dist/workflow-runner/config/phase-configs.js +34 -0
- package/dist/workflow-runner/config/stage-configs.d.ts +5 -0
- package/dist/workflow-runner/config/stage-configs.js +34 -0
- package/dist/workflow-runner/core/feature-filter.d.ts +16 -0
- package/dist/workflow-runner/core/feature-filter.js +46 -0
- package/dist/workflow-runner/core/feature-filter.test.d.ts +4 -0
- package/dist/workflow-runner/core/feature-filter.test.js +127 -0
- package/dist/workflow-runner/core/index.d.ts +8 -0
- package/dist/workflow-runner/core/index.js +12 -0
- package/dist/workflow-runner/core/pipeline-evaluator.d.ts +24 -0
- package/dist/workflow-runner/core/pipeline-evaluator.js +32 -0
- package/dist/workflow-runner/core/state-manager.d.ts +24 -0
- package/dist/workflow-runner/core/state-manager.js +42 -0
- package/dist/workflow-runner/core/workflow-logger.d.ts +20 -0
- package/dist/workflow-runner/core/workflow-logger.js +65 -0
- package/dist/workflow-runner/executors/phase-executor.d.ts +8 -0
- package/dist/workflow-runner/executors/phase-executor.js +183 -0
- package/dist/workflow-runner/executors/stage-executor.d.ts +8 -0
- package/dist/workflow-runner/executors/stage-executor.js +49 -0
- package/dist/workflow-runner/feature-service.d.ts +17 -0
- package/dist/workflow-runner/feature-service.js +60 -0
- package/dist/workflow-runner/feature-workflow-runner.d.ts +26 -0
- package/dist/workflow-runner/feature-workflow-runner.js +113 -0
- package/dist/workflow-runner/index.d.ts +0 -1
- package/dist/workflow-runner/index.js +0 -1
- package/dist/workflow-runner/pipeline-runner.d.ts +9 -19
- package/dist/workflow-runner/pipeline-runner.js +247 -256
- package/dist/workflow-runner/pipeline.d.ts +18 -0
- package/dist/workflow-runner/pipeline.js +197 -0
- package/dist/workflow-runner/processor.d.ts +40 -0
- package/dist/workflow-runner/processor.js +191 -0
- package/dist/workflow-runner/types.d.ts +48 -0
- package/dist/workflow-runner/types.js +4 -0
- package/dist/workflow-runner/workflow-processor.d.ts +6 -23
- package/dist/workflow-runner/workflow-processor.js +38 -100
- package/package.json +2 -2
- package/dist/code-implementation/analyzer.d.ts +0 -19
- package/dist/code-implementation/context-fetcher.d.ts +0 -38
- package/dist/code-implementation/context-fetcher.js +0 -147
- package/dist/feature-analysis/context-fetcher.d.ts +0 -54
- package/dist/feature-analysis/context-fetcher.js +0 -193
- package/dist/functional-testing/context-fetcher.d.ts +0 -47
- package/dist/functional-testing/context-fetcher.js +0 -192
- package/dist/technical-design/analyzer.js +0 -338
- package/dist/technical-design/context-fetcher.d.ts +0 -42
- package/dist/technical-design/context-fetcher.js +0 -170
- /package/dist/{bug-fixing → phases/bug-fixing}/index.d.ts +0 -0
- /package/dist/{bug-fixing → phases/bug-fixing}/index.js +0 -0
- /package/dist/{bug-fixing → phases/bug-fixing}/mcp-server.d.ts +0 -0
- /package/dist/{code-implementation → phases/code-implementation}/mcp-server.d.ts +0 -0
- /package/dist/{code-review → phases/code-review}/reviewer.js +0 -0
- /package/dist/{feature-analysis → phases/feature-analysis}/http-fallback.d.ts +0 -0
- /package/dist/{feature-analysis → phases/feature-analysis}/index.d.ts +0 -0
- /package/dist/{feature-analysis → phases/feature-analysis}/index.js +0 -0
- /package/dist/{feature-analysis → phases/feature-analysis}/mcp-server.d.ts +0 -0
- /package/dist/{functional-testing → phases/functional-testing}/http-fallback.d.ts +0 -0
- /package/dist/{functional-testing → phases/functional-testing}/mcp-server.d.ts +0 -0
- /package/dist/{pull-request → phases/pull-request}/creator.d.ts +0 -0
- /package/dist/{technical-design → phases/technical-design}/http-fallback.d.ts +0 -0
- /package/dist/{technical-design → phases/technical-design}/mcp-server.d.ts +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { query } from '@anthropic-ai/claude-code';
|
|
2
|
-
import { logInfo, logError } from '
|
|
2
|
+
import { logInfo, logError } from '../../utils/logger.js';
|
|
3
|
+
import { formatChecklistsForContext, } from '../../services/checklist.js';
|
|
3
4
|
import { saveFunctionalTestResultsWithRetry } from './http-fallback.js';
|
|
4
|
-
import {
|
|
5
|
+
import { fetchFunctionalTestingContext, formatContextForPrompt, } from './context-fetcher.js';
|
|
6
|
+
import { updateFeatureStatus } from '../../api/features/index.js';
|
|
5
7
|
import { createTestReport, } from './test-report-creator.js';
|
|
6
8
|
function userMessage(content) {
|
|
7
9
|
return {
|
|
@@ -13,7 +15,7 @@ async function* prompt(testingPrompt) {
|
|
|
13
15
|
yield userMessage(testingPrompt);
|
|
14
16
|
await new Promise((res) => setTimeout(res, 10000));
|
|
15
17
|
}
|
|
16
|
-
export const runFunctionalTesting = async (options, config) => {
|
|
18
|
+
export const runFunctionalTesting = async (options, config, checklistContext) => {
|
|
17
19
|
const { featureId, mcpServerUrl, mcpToken, verbose } = options;
|
|
18
20
|
if (verbose) {
|
|
19
21
|
logInfo(`Starting functional testing for feature ID: ${featureId}`);
|
|
@@ -24,9 +26,9 @@ export const runFunctionalTesting = async (options, config) => {
|
|
|
24
26
|
if (verbose) {
|
|
25
27
|
logInfo('Fetching feature testing context via MCP endpoints...');
|
|
26
28
|
}
|
|
27
|
-
const context = await
|
|
28
|
-
const systemPrompt = createSystemPrompt(config);
|
|
29
|
-
const testingPrompt = createTestingPromptWithContext(featureId, context);
|
|
29
|
+
const context = await fetchFunctionalTestingContext(mcpServerUrl, mcpToken, featureId, verbose);
|
|
30
|
+
const systemPrompt = createSystemPrompt(config, mcpServerUrl, mcpToken, featureId);
|
|
31
|
+
const testingPrompt = createTestingPromptWithContext(featureId, context, checklistContext, verbose);
|
|
30
32
|
let lastAssistantResponse = '';
|
|
31
33
|
let structuredTestResult = null;
|
|
32
34
|
let testStatus = 'testing_failed';
|
|
@@ -75,10 +77,89 @@ export const runFunctionalTesting = async (options, config) => {
|
|
|
75
77
|
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
76
78
|
}
|
|
77
79
|
else {
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
+
// Search for JSON objects in the complete response
|
|
81
|
+
const lines = responseText.split('\n');
|
|
82
|
+
let foundJson = false;
|
|
83
|
+
for (let i = 0; i < lines.length; i++) {
|
|
84
|
+
const line = lines[i].trim();
|
|
85
|
+
if (line.includes('{') &&
|
|
86
|
+
(line.includes('"test_result"') ||
|
|
87
|
+
line.includes('"status"') ||
|
|
88
|
+
line.includes('"checklist_item_results"'))) {
|
|
89
|
+
// Found a potential JSON line, extract complete JSON object
|
|
90
|
+
let braceCount = 0;
|
|
91
|
+
let jsonStr = '';
|
|
92
|
+
let inString = false;
|
|
93
|
+
let escapeNext = false;
|
|
94
|
+
for (let j = i; j < lines.length; j++) {
|
|
95
|
+
const currentLine = lines[j];
|
|
96
|
+
for (let k = 0; k < currentLine.length; k++) {
|
|
97
|
+
const char = currentLine[k];
|
|
98
|
+
if (escapeNext) {
|
|
99
|
+
escapeNext = false;
|
|
100
|
+
jsonStr += char;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (char === '\\') {
|
|
104
|
+
escapeNext = true;
|
|
105
|
+
jsonStr += char;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (char === '"' && !escapeNext) {
|
|
109
|
+
inString = !inString;
|
|
110
|
+
}
|
|
111
|
+
if (!inString) {
|
|
112
|
+
if (char === '{')
|
|
113
|
+
braceCount++;
|
|
114
|
+
if (char === '}')
|
|
115
|
+
braceCount--;
|
|
116
|
+
}
|
|
117
|
+
jsonStr += char;
|
|
118
|
+
if (braceCount === 0 && jsonStr.includes('{')) {
|
|
119
|
+
try {
|
|
120
|
+
jsonResult = JSON.parse(jsonStr);
|
|
121
|
+
foundJson = true;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Continue searching
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (foundJson)
|
|
130
|
+
break;
|
|
131
|
+
if (j > i)
|
|
132
|
+
jsonStr += '\n';
|
|
133
|
+
}
|
|
134
|
+
if (foundJson)
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Final fallback: Try to parse the entire response as JSON
|
|
139
|
+
if (!foundJson) {
|
|
140
|
+
jsonResult = JSON.parse(responseText);
|
|
141
|
+
}
|
|
80
142
|
}
|
|
81
|
-
if (jsonResult &&
|
|
143
|
+
if (jsonResult && jsonResult.test_result) {
|
|
144
|
+
const testData = jsonResult.test_result;
|
|
145
|
+
structuredTestResult = {
|
|
146
|
+
status: testData.status || 'testing_failed',
|
|
147
|
+
title: testData.title || 'Functional Test Report',
|
|
148
|
+
summary: testData.summary || 'Test execution completed',
|
|
149
|
+
total_test_cases: testData.total_test_cases || 0,
|
|
150
|
+
passed_test_cases: testData.passed_test_cases || 0,
|
|
151
|
+
failed_test_cases: testData.failed_test_cases || 0,
|
|
152
|
+
test_cases: testData.test_cases || [],
|
|
153
|
+
execution_details: testData.execution_details || responseText,
|
|
154
|
+
timestamp: testData.timestamp || new Date().toISOString(),
|
|
155
|
+
checklist_item_results: testData.checklist_item_results || [],
|
|
156
|
+
};
|
|
157
|
+
testStatus = structuredTestResult.status;
|
|
158
|
+
}
|
|
159
|
+
else if (jsonResult &&
|
|
160
|
+
typeof jsonResult === 'object' &&
|
|
161
|
+
jsonResult.status) {
|
|
162
|
+
// Fallback: handle flat structure for backward compatibility
|
|
82
163
|
structuredTestResult = {
|
|
83
164
|
status: jsonResult.status || 'testing_failed',
|
|
84
165
|
title: jsonResult.title || 'Functional Test Report',
|
|
@@ -89,6 +170,7 @@ export const runFunctionalTesting = async (options, config) => {
|
|
|
89
170
|
test_cases: jsonResult.test_cases || [],
|
|
90
171
|
execution_details: jsonResult.execution_details || responseText,
|
|
91
172
|
timestamp: jsonResult.timestamp || new Date().toISOString(),
|
|
173
|
+
checklist_item_results: jsonResult.checklist_item_results || [],
|
|
92
174
|
};
|
|
93
175
|
testStatus = structuredTestResult.status;
|
|
94
176
|
}
|
|
@@ -108,6 +190,7 @@ export const runFunctionalTesting = async (options, config) => {
|
|
|
108
190
|
failed_test_cases: 1,
|
|
109
191
|
execution_details: message.result || lastAssistantResponse,
|
|
110
192
|
timestamp: new Date().toISOString(),
|
|
193
|
+
checklist_item_results: [],
|
|
111
194
|
};
|
|
112
195
|
testStatus = 'testing_failed';
|
|
113
196
|
}
|
|
@@ -124,7 +207,13 @@ export const runFunctionalTesting = async (options, config) => {
|
|
|
124
207
|
if (verbose) {
|
|
125
208
|
logInfo('Saving test results...');
|
|
126
209
|
}
|
|
127
|
-
const statusSaved = await
|
|
210
|
+
const statusSaved = await updateFeatureStatus({
|
|
211
|
+
mcpServerUrl,
|
|
212
|
+
mcpToken,
|
|
213
|
+
featureId,
|
|
214
|
+
status: testStatus,
|
|
215
|
+
verbose,
|
|
216
|
+
});
|
|
128
217
|
// Try HTTP fallback if direct update failed
|
|
129
218
|
if (!statusSaved) {
|
|
130
219
|
if (verbose) {
|
|
@@ -155,11 +244,68 @@ export const runFunctionalTesting = async (options, config) => {
|
|
|
155
244
|
testResults: structuredTestResult.execution_details || lastAssistantResponse,
|
|
156
245
|
status: testStatus,
|
|
157
246
|
};
|
|
247
|
+
// Convert test cases to test results format for MCP
|
|
248
|
+
let testResults;
|
|
249
|
+
if (structuredTestResult.test_cases &&
|
|
250
|
+
structuredTestResult.test_cases.length > 0) {
|
|
251
|
+
// Fetch test cases for this feature to get proper test_case_ids
|
|
252
|
+
try {
|
|
253
|
+
const testCasesResponse = await fetch(`${mcpServerUrl}/mcp`, {
|
|
254
|
+
method: 'POST',
|
|
255
|
+
headers: {
|
|
256
|
+
'Content-Type': 'application/json',
|
|
257
|
+
Authorization: `Bearer ${mcpToken}`,
|
|
258
|
+
},
|
|
259
|
+
body: JSON.stringify({
|
|
260
|
+
jsonrpc: '2.0',
|
|
261
|
+
method: 'test_cases/list',
|
|
262
|
+
params: {
|
|
263
|
+
feature_id: featureId,
|
|
264
|
+
},
|
|
265
|
+
id: Math.random().toString(36).substring(7),
|
|
266
|
+
}),
|
|
267
|
+
});
|
|
268
|
+
if (testCasesResponse.ok) {
|
|
269
|
+
const testCasesData = await testCasesResponse.json();
|
|
270
|
+
if (!testCasesData.error && testCasesData.result) {
|
|
271
|
+
const existingTestCases = testCasesData.result.test_cases || [];
|
|
272
|
+
// Map structured test results to existing test cases by name
|
|
273
|
+
testResults = structuredTestResult.test_cases
|
|
274
|
+
.map((testCase) => {
|
|
275
|
+
const matchingTestCase = existingTestCases.find((existing) => existing.name
|
|
276
|
+
.toLowerCase()
|
|
277
|
+
.includes(testCase.name.toLowerCase()) ||
|
|
278
|
+
testCase.name
|
|
279
|
+
.toLowerCase()
|
|
280
|
+
.includes(existing.name.toLowerCase()));
|
|
281
|
+
if (matchingTestCase) {
|
|
282
|
+
return {
|
|
283
|
+
test_case_id: matchingTestCase.id,
|
|
284
|
+
result: testCase.status,
|
|
285
|
+
notes: testCase.error_message || testCase.description || null,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
})
|
|
290
|
+
.filter(Boolean);
|
|
291
|
+
if (verbose && testResults.length > 0) {
|
|
292
|
+
logInfo(`Mapped ${testResults.length} test results to existing test cases`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
if (verbose) {
|
|
299
|
+
logError(`Failed to fetch test cases for mapping: ${error}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
158
303
|
testReportResult = await createTestReport({
|
|
159
304
|
mcpServerUrl,
|
|
160
305
|
mcpToken,
|
|
161
306
|
featureId,
|
|
162
307
|
testReportData,
|
|
308
|
+
testResults,
|
|
163
309
|
verbose,
|
|
164
310
|
});
|
|
165
311
|
if (testReportResult.success && verbose) {
|
|
@@ -177,6 +323,9 @@ export const runFunctionalTesting = async (options, config) => {
|
|
|
177
323
|
testReport: testReportResult,
|
|
178
324
|
status: 'success',
|
|
179
325
|
message: `Functional testing completed with status: ${testStatus}`,
|
|
326
|
+
data: {
|
|
327
|
+
checklist_item_results: structuredTestResult.checklist_item_results,
|
|
328
|
+
},
|
|
180
329
|
};
|
|
181
330
|
}
|
|
182
331
|
else {
|
|
@@ -194,7 +343,13 @@ export const runFunctionalTesting = async (options, config) => {
|
|
|
194
343
|
logError(`Functional testing failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
195
344
|
// Try to save error status
|
|
196
345
|
try {
|
|
197
|
-
const errorSaved = await
|
|
346
|
+
const errorSaved = await updateFeatureStatus({
|
|
347
|
+
mcpServerUrl,
|
|
348
|
+
mcpToken,
|
|
349
|
+
featureId,
|
|
350
|
+
status: 'testing_failed',
|
|
351
|
+
verbose,
|
|
352
|
+
});
|
|
198
353
|
if (!errorSaved) {
|
|
199
354
|
// Try HTTP fallback
|
|
200
355
|
await saveFunctionalTestResultsWithRetry({
|
|
@@ -221,7 +376,23 @@ export const runFunctionalTesting = async (options, config) => {
|
|
|
221
376
|
};
|
|
222
377
|
}
|
|
223
378
|
};
|
|
224
|
-
const createSystemPrompt = (_config) => {
|
|
379
|
+
const createSystemPrompt = (_config, mcpServerUrl, mcpToken, featureId) => {
|
|
380
|
+
let mcpInstructions = '';
|
|
381
|
+
if (mcpServerUrl && mcpToken && featureId) {
|
|
382
|
+
mcpInstructions = `
|
|
383
|
+
|
|
384
|
+
**MANDATORY Checklist Compliance**:
|
|
385
|
+
If you are provided with checklists in the context, you MUST satisfy ALL of them during your testing work. Checklists are mandatory requirements that define quality standards and cannot be ignored or skipped.
|
|
386
|
+
|
|
387
|
+
- Review each checklist carefully and ensure your testing addresses every requirement
|
|
388
|
+
- You MUST include ALL provided checklist item IDs in the "checklist_item_results" field of your JSON response (use the exact UUID from "ID:" field in items list)
|
|
389
|
+
- Set "is_passed": true for each checklist item you have completed successfully
|
|
390
|
+
- Provide appropriate "value" based on the item type (boolean: true/false, text: descriptive text, number: numeric value)
|
|
391
|
+
- If you cannot satisfy a checklist item requirement, set "is_passed": false and explain why in the "notes" field
|
|
392
|
+
- The system will validate that all checklist items have been addressed - missing items will cause the pipeline to fail
|
|
393
|
+
|
|
394
|
+
CRITICAL: Checklists are not optional suggestions - they are mandatory quality gates that must be satisfied.`;
|
|
395
|
+
}
|
|
225
396
|
return `You are a professional QA automation engineer performing comprehensive functional testing using headless Playwright MCP.
|
|
226
397
|
|
|
227
398
|
**Your Role**: Execute end-to-end functional tests for implemented features using headless browser automation.
|
|
@@ -269,32 +440,55 @@ You MUST end your response with a JSON object containing the test results in thi
|
|
|
269
440
|
|
|
270
441
|
\`\`\`json
|
|
271
442
|
{
|
|
272
|
-
"
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
443
|
+
"test_result": {
|
|
444
|
+
"status": "testing_passed" | "testing_failed",
|
|
445
|
+
"title": "Descriptive test report title",
|
|
446
|
+
"summary": "Brief summary of what was tested and results",
|
|
447
|
+
"total_test_cases": number,
|
|
448
|
+
"passed_test_cases": number,
|
|
449
|
+
"failed_test_cases": number,
|
|
450
|
+
"test_cases": [
|
|
451
|
+
{
|
|
452
|
+
"name": "Test case name",
|
|
453
|
+
"status": "passed" | "failed" | "skipped",
|
|
454
|
+
"description": "What this test validates",
|
|
455
|
+
"error_message": "Error details if failed (optional)"
|
|
456
|
+
}
|
|
457
|
+
],
|
|
458
|
+
"checklist_item_results": [
|
|
459
|
+
{
|
|
460
|
+
"checklist_item_id": "EXACT_CHECKLIST_ITEM_UUID_FROM_ID_FIELD",
|
|
461
|
+
"is_passed": true,
|
|
462
|
+
"value": "Result value based on item type (boolean, text, number, etc.)",
|
|
463
|
+
"notes": "Optional notes about this specific checklist item"
|
|
464
|
+
}
|
|
465
|
+
],
|
|
466
|
+
"execution_details": "Detailed execution log and findings",
|
|
467
|
+
"timestamp": "ISO timestamp"
|
|
468
|
+
}
|
|
288
469
|
}
|
|
289
470
|
\`\`\`
|
|
290
471
|
|
|
291
|
-
|
|
472
|
+
MANDATORY: If checklists are provided in the context, you MUST include checklist_item_results for ALL checklist items. Every checklist item ID must be addressed - either passed or with explanation in notes why it cannot be satisfied. Missing any checklist item will cause the pipeline to fail.
|
|
473
|
+
|
|
474
|
+
IMPORTANT: In the checklist context, look for lines that say "ID: [UUID]" in the items list - use these exact UUIDs as checklist_item_id values. Do NOT use the item title or description as the checklist_item_id.
|
|
475
|
+
|
|
476
|
+
Focus on systematic testing based on the provided context information.${mcpInstructions}`;
|
|
292
477
|
};
|
|
293
|
-
const createTestingPromptWithContext = (featureId, context) => {
|
|
478
|
+
const createTestingPromptWithContext = (featureId, context, checklistContext, verbose) => {
|
|
294
479
|
const contextInfo = formatContextForPrompt(context);
|
|
480
|
+
// Add checklist context to the testing prompt
|
|
481
|
+
let finalContextInfo = contextInfo;
|
|
482
|
+
if (checklistContext && checklistContext.checklists.length > 0) {
|
|
483
|
+
const checklistInfo = formatChecklistsForContext(checklistContext);
|
|
484
|
+
finalContextInfo = contextInfo + '\n\n' + checklistInfo;
|
|
485
|
+
if (verbose) {
|
|
486
|
+
logInfo(`Added ${checklistContext.checklists.length} checklists to testing context`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
295
489
|
return `Please perform comprehensive functional testing for feature ID: ${featureId}
|
|
296
490
|
|
|
297
|
-
${
|
|
491
|
+
${finalContextInfo}
|
|
298
492
|
|
|
299
493
|
## Testing Instructions
|
|
300
494
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FeatureInfo, UserStory, TestCase } from '../../types/features.js';
|
|
2
|
+
import { type ProductInfo } from '../../api/products.js';
|
|
3
|
+
export interface FunctionalTestingContext {
|
|
4
|
+
feature: FeatureInfo;
|
|
5
|
+
product: ProductInfo;
|
|
6
|
+
user_stories: UserStory[];
|
|
7
|
+
test_cases: TestCase[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Fetch all functional testing context information via MCP endpoints
|
|
11
|
+
*/
|
|
12
|
+
export declare function fetchFunctionalTestingContext(mcpServerUrl: string, mcpToken: string, featureId: string, verbose?: boolean): Promise<FunctionalTestingContext>;
|
|
13
|
+
/**
|
|
14
|
+
* Format the context into a readable string for Claude Code
|
|
15
|
+
*/
|
|
16
|
+
export declare function formatContextForPrompt(context: FunctionalTestingContext): string;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { logInfo, logError } from '../../utils/logger.js';
|
|
2
|
+
import { getFeature, getUserStories, getTestCases, } from '../../api/features/index.js';
|
|
3
|
+
import { getProduct } from '../../api/products.js';
|
|
4
|
+
/**
|
|
5
|
+
* Fetch all functional testing context information via MCP endpoints
|
|
6
|
+
*/
|
|
7
|
+
export async function fetchFunctionalTestingContext(mcpServerUrl, mcpToken, featureId, verbose) {
|
|
8
|
+
try {
|
|
9
|
+
if (verbose) {
|
|
10
|
+
logInfo(`Fetching complete functional testing context for feature: ${featureId}`);
|
|
11
|
+
}
|
|
12
|
+
// Fetch all required data in parallel for better performance
|
|
13
|
+
const [feature, user_stories, test_cases] = await Promise.all([
|
|
14
|
+
getFeature(mcpServerUrl, mcpToken, featureId, verbose),
|
|
15
|
+
getUserStories(mcpServerUrl, mcpToken, featureId, verbose),
|
|
16
|
+
getTestCases(mcpServerUrl, mcpToken, featureId, verbose),
|
|
17
|
+
]);
|
|
18
|
+
const product = await getProduct(mcpServerUrl, mcpToken, feature.product_id, verbose);
|
|
19
|
+
if (verbose) {
|
|
20
|
+
logInfo(`✅ Functional testing context fetched successfully:`);
|
|
21
|
+
logInfo(` Feature: ${feature.name}`);
|
|
22
|
+
logInfo(` Product: ${product.name}`);
|
|
23
|
+
logInfo(` User Stories: ${user_stories.length}`);
|
|
24
|
+
logInfo(` Test Cases: ${test_cases.length}`);
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
feature,
|
|
28
|
+
product,
|
|
29
|
+
user_stories,
|
|
30
|
+
test_cases,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
35
|
+
logError(`Failed to fetch functional testing context: ${errorMessage}`);
|
|
36
|
+
throw new Error(`Context fetch failed: ${errorMessage}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Format the context into a readable string for Claude Code
|
|
41
|
+
*/
|
|
42
|
+
export function formatContextForPrompt(context) {
|
|
43
|
+
const formatUserStories = (stories) => {
|
|
44
|
+
if (stories.length === 0)
|
|
45
|
+
return 'No user stories defined.';
|
|
46
|
+
return stories
|
|
47
|
+
.map((story, index) => `${index + 1}. **${story.title}** (Status: ${story.status})
|
|
48
|
+
${story.description}`)
|
|
49
|
+
.join('\n\n');
|
|
50
|
+
};
|
|
51
|
+
const formatTestCases = (cases) => {
|
|
52
|
+
if (cases.length === 0)
|
|
53
|
+
return 'No test cases defined.';
|
|
54
|
+
return cases
|
|
55
|
+
.map((testCase, index) => `${index + 1}. **${testCase.name}** ${testCase.is_critical ? '[CRITICAL]' : '[OPTIONAL]'}
|
|
56
|
+
${testCase.description}`)
|
|
57
|
+
.join('\n\n');
|
|
58
|
+
};
|
|
59
|
+
return `# Functional Testing Context
|
|
60
|
+
|
|
61
|
+
## Feature Information
|
|
62
|
+
- **ID**: ${context.feature.id}
|
|
63
|
+
- **Name**: ${context.feature.name}
|
|
64
|
+
- **Description**: ${context.feature.description || 'No description provided'}
|
|
65
|
+
- **Current Status**: ${context.feature.status}
|
|
66
|
+
|
|
67
|
+
## Product Information
|
|
68
|
+
- **Product**: ${context.product.name}
|
|
69
|
+
- **Product ID**: ${context.product.id}
|
|
70
|
+
- **Description**: ${context.product.description || 'No product description'}
|
|
71
|
+
|
|
72
|
+
## User Stories to Test (${context.user_stories.length})
|
|
73
|
+
${formatUserStories(context.user_stories)}
|
|
74
|
+
|
|
75
|
+
## Test Cases to Execute (${context.test_cases.length})
|
|
76
|
+
${formatTestCases(context.test_cases)}
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
**Testing Instructions**: The feature has been implemented. Execute comprehensive functional testing using headless Playwright to verify all user stories work correctly and all test cases pass. Test both positive and negative scenarios.`;
|
|
81
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { logInfo, logError } from '
|
|
1
|
+
import { logInfo, logError } from '../../utils/logger.js';
|
|
2
2
|
export async function saveFunctionalTestResultsViaHttp(options) {
|
|
3
3
|
const { mcpServerUrl, mcpToken, featureId, testStatus, testResults: _testResults, verbose, } = options;
|
|
4
4
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { runFunctionalTesting, checkFunctionalTestingRequirements, type FunctionalTestingOptions, type FunctionalTestingResult, } from './analyzer.js';
|
|
2
2
|
export { createFunctionalTestingMcpServer } from './mcp-server.js';
|
|
3
3
|
export { saveFunctionalTestResultsViaHttp, verifyTestStatusSaved, saveFunctionalTestResultsWithRetry, } from './http-fallback.js';
|
|
4
|
-
export {
|
|
4
|
+
export { fetchFunctionalTestingContext, formatContextForPrompt, type FunctionalTestingContext, } from './context-fetcher.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { runFunctionalTesting, checkFunctionalTestingRequirements, } from './analyzer.js';
|
|
2
2
|
export { createFunctionalTestingMcpServer } from './mcp-server.js';
|
|
3
3
|
export { saveFunctionalTestResultsViaHttp, verifyTestStatusSaved, saveFunctionalTestResultsWithRetry, } from './http-fallback.js';
|
|
4
|
-
export {
|
|
4
|
+
export { fetchFunctionalTestingContext, formatContextForPrompt, } from './context-fetcher.js';
|
|
@@ -1,35 +1,6 @@
|
|
|
1
1
|
import { createSdkMcpServer, tool } from '@anthropic-ai/claude-code';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
async function callMcpEndpoint(mcpServerUrl, mcpToken, method, params) {
|
|
5
|
-
try {
|
|
6
|
-
const response = await fetch(`${mcpServerUrl}/mcp`, {
|
|
7
|
-
method: 'POST',
|
|
8
|
-
headers: {
|
|
9
|
-
'Content-Type': 'application/json',
|
|
10
|
-
Authorization: `Bearer ${mcpToken}`,
|
|
11
|
-
},
|
|
12
|
-
body: JSON.stringify({
|
|
13
|
-
jsonrpc: '2.0',
|
|
14
|
-
method,
|
|
15
|
-
params,
|
|
16
|
-
id: Math.random().toString(36).substring(7),
|
|
17
|
-
}),
|
|
18
|
-
});
|
|
19
|
-
if (!response.ok) {
|
|
20
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
21
|
-
}
|
|
22
|
-
const data = await response.json();
|
|
23
|
-
if (data.error) {
|
|
24
|
-
throw new Error(data.error.message || 'MCP call failed');
|
|
25
|
-
}
|
|
26
|
-
return data.result;
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
console.error(`MCP call failed for ${method}:`, error instanceof Error ? error.message : String(error));
|
|
30
|
-
throw error;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
3
|
+
import { callMcpEndpoint } from '../../api/mcp-client.js';
|
|
33
4
|
// Create an SDK MCP server with custom tools for functional testing
|
|
34
5
|
export const createFunctionalTestingMcpServer = (mcpServerUrl, mcpToken) => {
|
|
35
6
|
return createSdkMcpServer({
|
|
@@ -31,16 +31,42 @@ export interface TestReportCreateOptions {
|
|
|
31
31
|
mcpToken: string;
|
|
32
32
|
featureId: string;
|
|
33
33
|
testReportData: TestReportData;
|
|
34
|
+
testResults?: TestResultInput[];
|
|
34
35
|
verbose?: boolean;
|
|
35
36
|
}
|
|
37
|
+
export interface TestResultInput {
|
|
38
|
+
test_case_id: string;
|
|
39
|
+
result: 'passed' | 'failed' | 'skipped';
|
|
40
|
+
notes?: string;
|
|
41
|
+
}
|
|
36
42
|
export interface TestReportResult {
|
|
37
43
|
success: boolean;
|
|
38
44
|
testReport?: TestReport;
|
|
39
45
|
testReportUrl?: string;
|
|
40
46
|
error?: string;
|
|
41
47
|
}
|
|
48
|
+
export interface TestReportResultsOptions {
|
|
49
|
+
mcpServerUrl: string;
|
|
50
|
+
mcpToken: string;
|
|
51
|
+
testReportId: string;
|
|
52
|
+
testResults: TestResultInput[];
|
|
53
|
+
verbose?: boolean;
|
|
54
|
+
}
|
|
55
|
+
export interface TestReportResultsResponse {
|
|
56
|
+
success: boolean;
|
|
57
|
+
resultsCreated?: number;
|
|
58
|
+
error?: string;
|
|
59
|
+
}
|
|
42
60
|
/**
|
|
43
61
|
* Create a test report via MCP endpoint
|
|
44
62
|
* Uses structured data generated by Claude Code
|
|
45
63
|
*/
|
|
46
64
|
export declare function createTestReport(options: TestReportCreateOptions): Promise<TestReportResult>;
|
|
65
|
+
/**
|
|
66
|
+
* Manual fallback: Call MCP endpoint with structured data
|
|
67
|
+
* This is used when Claude Code fails to call MCP directly
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* Create test report results via MCP endpoint
|
|
71
|
+
*/
|
|
72
|
+
export declare function createTestReportResults(options: TestReportResultsOptions): Promise<TestReportResultsResponse>;
|