@democratize-quality/mcp-server 1.2.0 → 1.2.1
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/cli.js +248 -0
- package/package.json +7 -5
- package/src/chatmodes//360/237/214/220 api-generator.chatmode.md" +409 -0
- package/src/chatmodes//360/237/214/220 api-healer.chatmode.md" +494 -0
- package/src/chatmodes//360/237/214/220 api-planner.chatmode.md" +954 -0
- package/src/config/environments/api-only.js +72 -0
- package/src/config/environments/development.js +73 -0
- package/src/config/environments/production.js +88 -0
- package/src/config/index.js +360 -0
- package/src/config/server.js +60 -0
- package/src/config/tools/api.js +86 -0
- package/src/config/tools/browser.js +109 -0
- package/src/config/tools/default.js +51 -0
- package/src/docs/Agent_README.md +310 -0
- package/src/docs/QUICK_REFERENCE.md +111 -0
- package/src/server.ts +234 -0
- package/src/services/browserService.js +344 -0
- package/src/skills/api-planning/SKILL.md +224 -0
- package/src/skills/test-execution/SKILL.md +777 -0
- package/src/skills/test-generation/SKILL.md +309 -0
- package/src/skills/test-healing/SKILL.md +405 -0
- package/src/tools/api/api-generator.js +1884 -0
- package/src/tools/api/api-healer.js +636 -0
- package/src/tools/api/api-planner.js +2617 -0
- package/src/tools/api/api-project-setup.js +332 -0
- package/src/tools/api/api-request.js +660 -0
- package/src/tools/api/api-session-report.js +1297 -0
- package/src/tools/api/api-session-status.js +414 -0
- package/src/tools/api/prompts/README.md +293 -0
- package/src/tools/api/prompts/generation-prompts.js +722 -0
- package/src/tools/api/prompts/healing-prompts.js +214 -0
- package/src/tools/api/prompts/index.js +44 -0
- package/src/tools/api/prompts/orchestrator.js +353 -0
- package/src/tools/api/prompts/validation-rules.js +358 -0
- package/src/tools/base/ToolBase.js +249 -0
- package/src/tools/base/ToolRegistry.js +288 -0
- package/src/tools/browser/advanced/browser-console.js +403 -0
- package/src/tools/browser/advanced/browser-dialog.js +338 -0
- package/src/tools/browser/advanced/browser-evaluate.js +356 -0
- package/src/tools/browser/advanced/browser-file.js +499 -0
- package/src/tools/browser/advanced/browser-keyboard.js +362 -0
- package/src/tools/browser/advanced/browser-mouse.js +351 -0
- package/src/tools/browser/advanced/browser-network.js +440 -0
- package/src/tools/browser/advanced/browser-pdf.js +426 -0
- package/src/tools/browser/advanced/browser-tabs.js +516 -0
- package/src/tools/browser/advanced/browser-wait.js +397 -0
- package/src/tools/browser/click.js +187 -0
- package/src/tools/browser/close.js +79 -0
- package/src/tools/browser/dom.js +89 -0
- package/src/tools/browser/launch.js +86 -0
- package/src/tools/browser/navigate.js +289 -0
- package/src/tools/browser/screenshot.js +370 -0
- package/src/tools/browser/type.js +193 -0
- package/src/tools/index.js +114 -0
- package/src/utils/agentInstaller.js +437 -0
- package/src/utils/browserHelpers.js +102 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 Democratize Quality
|
|
3
|
+
*
|
|
4
|
+
* This file is part of Democratize Quality MCP Server.
|
|
5
|
+
*
|
|
6
|
+
* Democratize Quality MCP Server is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* Democratize Quality MCP Server is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU Affero General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
* along with Democratize Quality MCP Server. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Healing Prompts - Fix issues in generated code
|
|
22
|
+
*
|
|
23
|
+
* These prompts are used to fix validation errors in generated code.
|
|
24
|
+
* Applied when validation fails, with specific feedback about issues.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
/**
|
|
29
|
+
* Heal Playwright test code issues
|
|
30
|
+
*/
|
|
31
|
+
playwrightHealing: {
|
|
32
|
+
system: `You are a code fixing expert for REST and GraphQL API tests. Fix the issues in the generated Playwright test code while maintaining functionality.
|
|
33
|
+
|
|
34
|
+
Requirements:
|
|
35
|
+
- Support both REST and GraphQL API testing patterns
|
|
36
|
+
- For GraphQL: POST method with { query: "...", variables: {...} } body
|
|
37
|
+
- For GraphQL: Validate response has 'data' or 'errors' property
|
|
38
|
+
- Fix ONLY the reported issues
|
|
39
|
+
- Maintain all existing functionality
|
|
40
|
+
- Keep the same code structure
|
|
41
|
+
- Use proper indentation (6 spaces for test body)
|
|
42
|
+
- Return ONLY the fixed code, no explanations
|
|
43
|
+
- DO NOT create any additional files
|
|
44
|
+
- DO NOT generate README, SUMMARY, or documentation`,
|
|
45
|
+
|
|
46
|
+
user: (context) => {
|
|
47
|
+
const { originalCode, issues, scenario } = context;
|
|
48
|
+
|
|
49
|
+
let prompt = `Fix the following issues in this Playwright test code:
|
|
50
|
+
|
|
51
|
+
**Original Code:**
|
|
52
|
+
\`\`\`typescript
|
|
53
|
+
${originalCode}
|
|
54
|
+
\`\`\`
|
|
55
|
+
|
|
56
|
+
**Issues Found:**
|
|
57
|
+
${issues.map((issue, i) => `${i + 1}. [${issue.severity.toUpperCase()}] ${issue.message}
|
|
58
|
+
Fix: ${issue.fix}`).join('\n')}
|
|
59
|
+
|
|
60
|
+
**Original Scenario Context:**
|
|
61
|
+
- API Type: ${scenario.isGraphQL ? 'GraphQL' : 'REST'}
|
|
62
|
+
- Method: ${scenario.method}
|
|
63
|
+
- Endpoint: ${scenario.endpoint}
|
|
64
|
+
- Expected Status: ${scenario.expect?.status || 200}
|
|
65
|
+
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
// NEW: GraphQL-specific healing guidance
|
|
69
|
+
if (scenario.isGraphQL && scenario.graphql) {
|
|
70
|
+
prompt += `**GraphQL-Specific Requirements:**
|
|
71
|
+
This is a GraphQL test. Ensure:
|
|
72
|
+
1. Uses POST method (not ${scenario.method})
|
|
73
|
+
2. Endpoint is: ${scenario.endpoint || '/graphql'}
|
|
74
|
+
3. Request body structure:
|
|
75
|
+
\`\`\`javascript
|
|
76
|
+
data: {
|
|
77
|
+
query: \`${scenario.graphql.query.replace(/`/g, '\\`')}\`,
|
|
78
|
+
variables: ${JSON.stringify(scenario.graphql.variables, null, 6)}
|
|
79
|
+
}
|
|
80
|
+
\`\`\`
|
|
81
|
+
4. Response validation:
|
|
82
|
+
- For success: expect(responseData).toHaveProperty('data')
|
|
83
|
+
- For errors: expect(responseData).toHaveProperty('errors')
|
|
84
|
+
|
|
85
|
+
`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
prompt += `**Instructions:**
|
|
89
|
+
1. Fix ALL reported issues
|
|
90
|
+
2. Maintain the same functionality
|
|
91
|
+
3. Keep proper indentation (6 spaces)
|
|
92
|
+
4. For URL construction, use template literals: \`\${baseUrl}/endpoint\`
|
|
93
|
+
5. Ensure await is used for all async calls
|
|
94
|
+
6. Return ONLY the fixed code, starting at line 1 of the test body
|
|
95
|
+
|
|
96
|
+
DO NOT:
|
|
97
|
+
- Add explanations or comments about fixes
|
|
98
|
+
- Change the test logic
|
|
99
|
+
- Add or remove functionality
|
|
100
|
+
- Include test() wrapper or imports
|
|
101
|
+
- Create any additional files (README, SUMMARY, GUIDE, etc.)
|
|
102
|
+
- Suggest file creation
|
|
103
|
+
|
|
104
|
+
Return the complete fixed test body code.`;
|
|
105
|
+
|
|
106
|
+
return prompt;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Heal Jest test code issues
|
|
112
|
+
*/
|
|
113
|
+
jestHealing: {
|
|
114
|
+
system: `You are a code fixing expert for REST and GraphQL API tests. Fix the issues in the generated Jest test code while maintaining functionality.
|
|
115
|
+
|
|
116
|
+
Requirements:
|
|
117
|
+
- Support both REST and GraphQL API testing patterns
|
|
118
|
+
- For GraphQL: POST method with { query: "...", variables: {...} } in data field
|
|
119
|
+
- For GraphQL: Validate response.data has 'data' or 'errors' property
|
|
120
|
+
- Fix ONLY the reported issues
|
|
121
|
+
- Maintain all existing functionality
|
|
122
|
+
- Keep the same code structure
|
|
123
|
+
- Use proper indentation (6 spaces for test body)
|
|
124
|
+
- Return ONLY the fixed code, no explanations
|
|
125
|
+
- DO NOT create any additional files
|
|
126
|
+
- DO NOT generate README, SUMMARY, or documentation`,
|
|
127
|
+
|
|
128
|
+
user: (context) => {
|
|
129
|
+
const { originalCode, issues, scenario } = context;
|
|
130
|
+
|
|
131
|
+
let prompt = `Fix the following issues in this Jest test code:
|
|
132
|
+
|
|
133
|
+
**Original Code:**
|
|
134
|
+
\`\`\`typescript
|
|
135
|
+
${originalCode}
|
|
136
|
+
\`\`\`
|
|
137
|
+
|
|
138
|
+
**Issues Found:**
|
|
139
|
+
${issues.map((issue, i) => `${i + 1}. [${issue.severity.toUpperCase()}] ${issue.message}
|
|
140
|
+
Fix: ${issue.fix}`).join('\n')}
|
|
141
|
+
|
|
142
|
+
**Original Scenario Context:**
|
|
143
|
+
- API Type: ${scenario.isGraphQL ? 'GraphQL' : 'REST'}
|
|
144
|
+
- Method: ${scenario.method}
|
|
145
|
+
- Endpoint: ${scenario.endpoint}
|
|
146
|
+
- Expected Status: ${scenario.expect?.status || 200}
|
|
147
|
+
|
|
148
|
+
`;
|
|
149
|
+
|
|
150
|
+
// NEW: GraphQL-specific healing guidance for Jest
|
|
151
|
+
if (scenario.isGraphQL && scenario.graphql) {
|
|
152
|
+
prompt += `**GraphQL-Specific Requirements:**
|
|
153
|
+
This is a GraphQL test. Ensure:
|
|
154
|
+
1. Uses POST method in makeRequest: method: 'POST'
|
|
155
|
+
2. URL is: '${scenario.endpoint || '/graphql'}'
|
|
156
|
+
3. Request data structure:
|
|
157
|
+
\`\`\`javascript
|
|
158
|
+
data: {
|
|
159
|
+
query: \`${scenario.graphql.query.replace(/`/g, '\\`')}\`,
|
|
160
|
+
variables: ${JSON.stringify(scenario.graphql.variables, null, 6)}
|
|
161
|
+
}
|
|
162
|
+
\`\`\`
|
|
163
|
+
4. Response validation:
|
|
164
|
+
- For success: expect(response.data).toHaveProperty('data')
|
|
165
|
+
- For errors: expect(response.data).toHaveProperty('errors')
|
|
166
|
+
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
prompt += `**Instructions:**
|
|
171
|
+
1. Fix ALL reported issues
|
|
172
|
+
2. Maintain the same functionality
|
|
173
|
+
3. Keep proper indentation (6 spaces)
|
|
174
|
+
4. Use apiUtils.makeRequest() correctly
|
|
175
|
+
5. Ensure await is used for all async calls
|
|
176
|
+
6. Return ONLY the fixed code, starting at line 1 of the test body
|
|
177
|
+
|
|
178
|
+
DO NOT:
|
|
179
|
+
- Add explanations or comments about fixes
|
|
180
|
+
- Change the test logic
|
|
181
|
+
- Add or remove functionality
|
|
182
|
+
- Include test() wrapper or imports
|
|
183
|
+
- Create any additional files (README, SUMMARY, GUIDE, etc.)
|
|
184
|
+
- Suggest file creation
|
|
185
|
+
|
|
186
|
+
Return the complete fixed test body code.`;
|
|
187
|
+
|
|
188
|
+
return prompt;
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Generic healing prompt for any format
|
|
194
|
+
*/
|
|
195
|
+
genericHealing: {
|
|
196
|
+
system: `You are a code fixing expert. Fix the reported issues in the code while maintaining all functionality.`,
|
|
197
|
+
|
|
198
|
+
user: (context) => {
|
|
199
|
+
const { originalCode, issues, format, language } = context;
|
|
200
|
+
|
|
201
|
+
return `Fix these issues in ${format} ${language} code:
|
|
202
|
+
|
|
203
|
+
**Issues:**
|
|
204
|
+
${issues.map((issue, i) => `${i + 1}. ${issue.message} - Fix: ${issue.fix}`).join('\n')}
|
|
205
|
+
|
|
206
|
+
**Code:**
|
|
207
|
+
\`\`\`${language}
|
|
208
|
+
${originalCode}
|
|
209
|
+
\`\`\`
|
|
210
|
+
|
|
211
|
+
Return the fixed code only, no explanations.`;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 Democratize Quality
|
|
3
|
+
*
|
|
4
|
+
* This file is part of Democratize Quality MCP Server.
|
|
5
|
+
*
|
|
6
|
+
* Democratize Quality MCP Server is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* Democratize Quality MCP Server is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU Affero General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
* along with Democratize Quality MCP Server. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Prompt Orchestration System
|
|
22
|
+
*
|
|
23
|
+
* Exports all components of the prompt orchestration system for AI-based test generation.
|
|
24
|
+
*
|
|
25
|
+
* @module prompts
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const generationPrompts = require('./generation-prompts');
|
|
29
|
+
const validationRules = require('./validation-rules');
|
|
30
|
+
const healingPrompts = require('./healing-prompts');
|
|
31
|
+
const PromptOrchestrator = require('./orchestrator');
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
// Main orchestrator class
|
|
35
|
+
PromptOrchestrator,
|
|
36
|
+
|
|
37
|
+
// Individual components
|
|
38
|
+
generationPrompts,
|
|
39
|
+
validationRules,
|
|
40
|
+
healingPrompts,
|
|
41
|
+
|
|
42
|
+
// Convenience factory
|
|
43
|
+
createOrchestrator: (options) => new PromptOrchestrator(options)
|
|
44
|
+
};
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 Democratize Quality
|
|
3
|
+
*
|
|
4
|
+
* This file is part of Democratize Quality MCP Server.
|
|
5
|
+
*
|
|
6
|
+
* Democratize Quality MCP Server is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* Democratize Quality MCP Server is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU Affero General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
* along with Democratize Quality MCP Server. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Prompt Orchestrator
|
|
22
|
+
*
|
|
23
|
+
* Coordinates the Planning → Generation → Validation → Healing workflow
|
|
24
|
+
* for AI-based test code generation.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const generationPrompts = require('./generation-prompts');
|
|
28
|
+
const validationRules = require('./validation-rules');
|
|
29
|
+
const healingPrompts = require('./healing-prompts');
|
|
30
|
+
|
|
31
|
+
class PromptOrchestrator {
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
this.maxHealingAttempts = options.maxHealingAttempts || 3;
|
|
34
|
+
this.language = options.language || 'typescript';
|
|
35
|
+
this.outputFormat = options.outputFormat || 'playwright';
|
|
36
|
+
this.verbose = options.verbose || false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generate test code with validation and healing
|
|
41
|
+
*
|
|
42
|
+
* Workflow:
|
|
43
|
+
* 1. Generate code using AI
|
|
44
|
+
* 2. Validate generated code
|
|
45
|
+
* 3. If invalid, heal and retry (up to maxHealingAttempts)
|
|
46
|
+
* 4. Return validated code or fallback
|
|
47
|
+
*/
|
|
48
|
+
async generateTestCode(context, aiGenerator) {
|
|
49
|
+
const { scenario, baseUrl } = context;
|
|
50
|
+
|
|
51
|
+
this.log(`📝 Generating ${this.outputFormat} test for: ${scenario.title}`);
|
|
52
|
+
|
|
53
|
+
// Step 1: Generate initial code
|
|
54
|
+
let generatedCode = await this._generateCode(context, aiGenerator);
|
|
55
|
+
|
|
56
|
+
// Step 1.5: Validate single file output (SAFETY NET)
|
|
57
|
+
// Some helpful AI models try to create README, SUMMARY, etc.
|
|
58
|
+
generatedCode = this._validateSingleFileOutput(generatedCode, context.targetFile);
|
|
59
|
+
|
|
60
|
+
// Step 2: Validate
|
|
61
|
+
let validation = this._validateCode(generatedCode, context);
|
|
62
|
+
|
|
63
|
+
// Step 3: Heal if needed (up to maxHealingAttempts)
|
|
64
|
+
let healingAttempt = 0;
|
|
65
|
+
while (!validation.valid && healingAttempt < this.maxHealingAttempts) {
|
|
66
|
+
healingAttempt++;
|
|
67
|
+
this.log(`🔧 Healing attempt ${healingAttempt}/${this.maxHealingAttempts}...`);
|
|
68
|
+
this.log(` Issues: ${validation.issues.map(i => i.message).join(', ')}`);
|
|
69
|
+
|
|
70
|
+
generatedCode = await this._healCode(generatedCode, validation.issues, context, aiGenerator);
|
|
71
|
+
validation = this._validateCode(generatedCode, context);
|
|
72
|
+
|
|
73
|
+
if (validation.valid) {
|
|
74
|
+
this.log(`✅ Code healed successfully!`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Step 4: Return result
|
|
79
|
+
if (!validation.valid) {
|
|
80
|
+
this.log(`⚠️ Warning: Code still has issues after ${healingAttempt} healing attempts`);
|
|
81
|
+
this.log(` Remaining issues: ${validation.issues.map(i => i.message).join(', ')}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
code: generatedCode,
|
|
86
|
+
valid: validation.valid,
|
|
87
|
+
issues: validation.issues,
|
|
88
|
+
healingAttempts: healingAttempt,
|
|
89
|
+
metadata: {
|
|
90
|
+
scenario: scenario.title,
|
|
91
|
+
format: this.outputFormat,
|
|
92
|
+
language: this.language
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Generate code using AI
|
|
99
|
+
*/
|
|
100
|
+
async _generateCode(context, aiGenerator) {
|
|
101
|
+
const promptConfig = this._getGenerationPrompt();
|
|
102
|
+
|
|
103
|
+
const systemPrompt = promptConfig.system;
|
|
104
|
+
const userPrompt = promptConfig.user({
|
|
105
|
+
...context,
|
|
106
|
+
language: this.language,
|
|
107
|
+
options: { format: this.outputFormat }
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
this.log(`💭 Sending generation prompt to AI...`);
|
|
111
|
+
|
|
112
|
+
// Call AI generator (this would be GitHub Copilot API, OpenAI, etc.)
|
|
113
|
+
const code = await aiGenerator.generate(systemPrompt, userPrompt);
|
|
114
|
+
|
|
115
|
+
return code.trim();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Validate generated code
|
|
120
|
+
*/
|
|
121
|
+
_validateCode(code, context) {
|
|
122
|
+
this.log(`🔍 Validating generated code...`);
|
|
123
|
+
|
|
124
|
+
const isGraphQL = context?.scenario?.isGraphQL || false;
|
|
125
|
+
const validator = validationRules.getValidator(this.outputFormat);
|
|
126
|
+
const validation = validator.validateAll(code, this.language, isGraphQL);
|
|
127
|
+
|
|
128
|
+
if (validation.valid) {
|
|
129
|
+
this.log(`✅ Code validation passed!`);
|
|
130
|
+
} else {
|
|
131
|
+
this.log(`❌ Code validation failed with ${validation.issues.length} issues`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return validation;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Heal code issues using AI
|
|
139
|
+
*/
|
|
140
|
+
async _healCode(originalCode, issues, context, aiGenerator) {
|
|
141
|
+
const healingConfig = this._getHealingPrompt();
|
|
142
|
+
|
|
143
|
+
const systemPrompt = healingConfig.system;
|
|
144
|
+
const userPrompt = healingConfig.user({
|
|
145
|
+
originalCode,
|
|
146
|
+
issues,
|
|
147
|
+
scenario: context.scenario,
|
|
148
|
+
format: this.outputFormat,
|
|
149
|
+
language: this.language
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
this.log(`🏥 Sending healing prompt to AI...`);
|
|
153
|
+
|
|
154
|
+
// Call AI generator for healing
|
|
155
|
+
const healedCode = await aiGenerator.generate(systemPrompt, userPrompt);
|
|
156
|
+
|
|
157
|
+
return healedCode.trim();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get generation prompt for current format
|
|
162
|
+
*/
|
|
163
|
+
_getGenerationPrompt() {
|
|
164
|
+
switch(this.outputFormat) {
|
|
165
|
+
case 'playwright':
|
|
166
|
+
return generationPrompts.playwrightScenario;
|
|
167
|
+
case 'jest':
|
|
168
|
+
return generationPrompts.jestScenario;
|
|
169
|
+
case 'postman':
|
|
170
|
+
return generationPrompts.postmanTestScript;
|
|
171
|
+
default:
|
|
172
|
+
throw new Error(`Unknown output format: ${this.outputFormat}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get healing prompt for current format
|
|
178
|
+
*/
|
|
179
|
+
_getHealingPrompt() {
|
|
180
|
+
switch(this.outputFormat) {
|
|
181
|
+
case 'playwright':
|
|
182
|
+
return healingPrompts.playwrightHealing;
|
|
183
|
+
case 'jest':
|
|
184
|
+
return healingPrompts.jestHealing;
|
|
185
|
+
default:
|
|
186
|
+
return healingPrompts.genericHealing;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Log helper
|
|
192
|
+
*/
|
|
193
|
+
log(message) {
|
|
194
|
+
if (this.verbose) {
|
|
195
|
+
console.log(message);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Generate full test file (with imports and structure)
|
|
201
|
+
*/
|
|
202
|
+
generateTestFile(testBodies, testPlan, options = {}) {
|
|
203
|
+
const { format, language, sessionId } = options;
|
|
204
|
+
|
|
205
|
+
switch(format) {
|
|
206
|
+
case 'playwright':
|
|
207
|
+
return this._generatePlaywrightFile(testBodies, testPlan, language, sessionId);
|
|
208
|
+
case 'jest':
|
|
209
|
+
return this._generateJestFile(testBodies, testPlan, language, sessionId);
|
|
210
|
+
default:
|
|
211
|
+
throw new Error(`Unknown format: ${format}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Generate complete Playwright test file
|
|
217
|
+
*/
|
|
218
|
+
_generatePlaywrightFile(testBodies, testPlan, language, sessionId) {
|
|
219
|
+
const isTS = language === 'typescript';
|
|
220
|
+
const imports = isTS
|
|
221
|
+
? `import { test, expect } from '@playwright/test';`
|
|
222
|
+
: `const { test, expect } = require('@playwright/test');`;
|
|
223
|
+
|
|
224
|
+
const testCases = testPlan.sections.flatMap((section, sectionIndex) =>
|
|
225
|
+
section.scenarios.map((scenario, scenarioIndex) => {
|
|
226
|
+
const testBody = testBodies[`${sectionIndex}-${scenarioIndex}`];
|
|
227
|
+
return `
|
|
228
|
+
test('${scenario.title}', async ({ request }) => {
|
|
229
|
+
${testBody}
|
|
230
|
+
});`;
|
|
231
|
+
}).join('\n')
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
return `// Generated API Tests for: ${testPlan.title}
|
|
235
|
+
// Generated by Democratize Quality MCP Server (AI-Generated)
|
|
236
|
+
// Session ID: ${sessionId}
|
|
237
|
+
// Language: ${language}
|
|
238
|
+
|
|
239
|
+
${imports}
|
|
240
|
+
|
|
241
|
+
test.describe('${testPlan.title} - API Tests', () => {
|
|
242
|
+
const baseUrl = '${testPlan.baseUrl}';
|
|
243
|
+
${testCases}
|
|
244
|
+
});`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Generate complete Jest test file
|
|
249
|
+
*/
|
|
250
|
+
_generateJestFile(testBodies, testPlan, language, sessionId) {
|
|
251
|
+
const isTS = language === 'typescript';
|
|
252
|
+
const imports = isTS
|
|
253
|
+
? `import axios from 'axios';\nimport { ApiTestUtils } from './test-utils';`
|
|
254
|
+
: `const axios = require('axios');\nconst { ApiTestUtils } = require('./test-utils');`;
|
|
255
|
+
|
|
256
|
+
const typeAnnotation = isTS ? ': ApiTestUtils' : '';
|
|
257
|
+
|
|
258
|
+
const testCases = testPlan.sections.flatMap((section, sectionIndex) =>
|
|
259
|
+
section.scenarios.map((scenario, scenarioIndex) => {
|
|
260
|
+
const testBody = testBodies[`${sectionIndex}-${scenarioIndex}`];
|
|
261
|
+
return `
|
|
262
|
+
test('${scenario.title}', async () => {
|
|
263
|
+
${testBody}
|
|
264
|
+
});`;
|
|
265
|
+
}).join('\n')
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
return `// Generated Jest API Tests for: ${testPlan.title}
|
|
269
|
+
// Generated by Democratize Quality MCP Server (AI-Generated)
|
|
270
|
+
// Session ID: ${sessionId}
|
|
271
|
+
// Language: ${language}
|
|
272
|
+
|
|
273
|
+
${imports}
|
|
274
|
+
|
|
275
|
+
describe('${testPlan.title} API Tests', () => {
|
|
276
|
+
let apiUtils${typeAnnotation};
|
|
277
|
+
const baseUrl = '${testPlan.baseUrl}';
|
|
278
|
+
|
|
279
|
+
beforeAll(async () => {
|
|
280
|
+
apiUtils = new ApiTestUtils(baseUrl);
|
|
281
|
+
console.log('Test suite initialized');
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
afterAll(async () => {
|
|
285
|
+
await apiUtils.cleanup();
|
|
286
|
+
console.log('Test suite completed');
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
describe('Test Scenarios', () => {${testCases}
|
|
290
|
+
});
|
|
291
|
+
});`;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Validate that AI generated ONLY the requested file
|
|
296
|
+
* Some helpful models try to create READMEs, summaries, etc.
|
|
297
|
+
* This is a safety net in case prompts are ignored.
|
|
298
|
+
*
|
|
299
|
+
* @param {string} generatedCode - The generated code from AI
|
|
300
|
+
* @param {string} targetFileName - The expected file name
|
|
301
|
+
* @returns {string} - Filtered code with only the test file
|
|
302
|
+
*/
|
|
303
|
+
_validateSingleFileOutput(generatedCode, targetFileName) {
|
|
304
|
+
// Check for multiple file indicators
|
|
305
|
+
const multiFilePatterns = [
|
|
306
|
+
/(?:create|generate|add)\s+(?:a\s+)?(?:README|SUMMARY|GUIDE|NOTES)/i,
|
|
307
|
+
/(?:also|additionally),?\s+(?:create|generate|write)/i,
|
|
308
|
+
/^#+\s+(?:README|SUMMARY|Quick Reference|Guide)/m,
|
|
309
|
+
/File:\s+[A-Z_-]+\.md/i,
|
|
310
|
+
/```[a-z]*\s*\n#+\s+(?:README|Summary)/i,
|
|
311
|
+
/\*\*File:\*\*\s+(?:README|SUMMARY|GUIDE)/i
|
|
312
|
+
];
|
|
313
|
+
|
|
314
|
+
for (const pattern of multiFilePatterns) {
|
|
315
|
+
if (pattern.test(generatedCode)) {
|
|
316
|
+
this.log(`⚠️ Warning: AI attempted to create additional files - filtering...`);
|
|
317
|
+
// Extract just the test code, ignore suggestions
|
|
318
|
+
return this._extractTestCodeOnly(generatedCode);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return generatedCode;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Extract only the actual test code from AI output
|
|
327
|
+
* Remove any file creation suggestions or documentation
|
|
328
|
+
*
|
|
329
|
+
* @param {string} output - The full AI output
|
|
330
|
+
* @returns {string} - Extracted test code only
|
|
331
|
+
*/
|
|
332
|
+
_extractTestCodeOnly(output) {
|
|
333
|
+
// Look for code fence or actual code
|
|
334
|
+
const codeFenceMatch = output.match(/```(?:typescript|javascript|ts|js)?\n([\s\S]+?)```/);
|
|
335
|
+
if (codeFenceMatch) {
|
|
336
|
+
this.log(`✅ Extracted test code from code fence`);
|
|
337
|
+
return codeFenceMatch[1];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Try to find the test code by looking for test.describe or describe
|
|
341
|
+
const testDescribeMatch = output.match(/((?:test\.)?describe\(['"`][\s\S]+?\n\}\);)/);
|
|
342
|
+
if (testDescribeMatch) {
|
|
343
|
+
this.log(`✅ Extracted test code by pattern matching`);
|
|
344
|
+
return testDescribeMatch[1];
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// If no extraction possible, return as-is but warn
|
|
348
|
+
this.log(`⚠️ Could not extract test code - returning full output`);
|
|
349
|
+
return output;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
module.exports = PromptOrchestrator;
|