tachibot-mcp 2.0.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/.env.example +260 -0
- package/CHANGELOG.md +54 -0
- package/CODE_OF_CONDUCT.md +56 -0
- package/CONTRIBUTING.md +54 -0
- package/Dockerfile +36 -0
- package/LICENSE +644 -0
- package/README.md +201 -0
- package/SECURITY.md +95 -0
- package/dist/personality/komaai-expressions.js +12 -0
- package/dist/profiles/balanced.json +33 -0
- package/dist/profiles/code_focus.json +33 -0
- package/dist/profiles/full.json +33 -0
- package/dist/profiles/minimal.json +33 -0
- package/dist/profiles/research_power.json +33 -0
- package/dist/scripts/build-profiles.js +46 -0
- package/dist/src/application/services/focus/FocusModeRegistry.js +46 -0
- package/dist/src/application/services/focus/FocusTool.service.js +109 -0
- package/dist/src/application/services/focus/ModeRegistry.js +46 -0
- package/dist/src/application/services/focus/modes/focus-deep.mode.js +27 -0
- package/dist/src/application/services/focus/modes/status.mode.js +50 -0
- package/dist/src/application/services/focus/modes/tachibot-status.mode.js +50 -0
- package/dist/src/collaborative-orchestrator.js +391 -0
- package/dist/src/config/model-constants.js +188 -0
- package/dist/src/config/model-defaults.js +57 -0
- package/dist/src/config/model-preferences.js +382 -0
- package/dist/src/config/timeout-config.js +130 -0
- package/dist/src/config.js +173 -0
- package/dist/src/domain/interfaces/IFocusMode.js +5 -0
- package/dist/src/domain/interfaces/IProvider.js +6 -0
- package/dist/src/domain/interfaces/ITool.js +5 -0
- package/dist/src/focus-deep.js +245 -0
- package/dist/src/infrastructure/ascii/art/robots.ascii.js +16 -0
- package/dist/src/mcp-client.js +90 -0
- package/dist/src/memory/index.js +17 -0
- package/dist/src/memory/memory-config.js +135 -0
- package/dist/src/memory/memory-interface.js +174 -0
- package/dist/src/memory/memory-manager.js +383 -0
- package/dist/src/memory/providers/devlog-provider.js +385 -0
- package/dist/src/memory/providers/hybrid-provider.js +399 -0
- package/dist/src/memory/providers/local-provider.js +388 -0
- package/dist/src/memory/providers/mem0-provider.js +337 -0
- package/dist/src/modes/architect.js +477 -0
- package/dist/src/modes/auditor.js +362 -0
- package/dist/src/modes/challenger.js +841 -0
- package/dist/src/modes/code-reviewer.js +382 -0
- package/dist/src/modes/commit-guardian.js +424 -0
- package/dist/src/modes/documentation-writer.js +572 -0
- package/dist/src/modes/scout.js +587 -0
- package/dist/src/modes/shared/helpers/challenger-helpers.js +454 -0
- package/dist/src/modes/shared/helpers/index.js +17 -0
- package/dist/src/modes/shared/helpers/scout-helpers.js +270 -0
- package/dist/src/modes/shared/helpers/verifier-helpers.js +332 -0
- package/dist/src/modes/test-architect.js +767 -0
- package/dist/src/modes/verifier.js +378 -0
- package/dist/src/monitoring/performance-monitor.js +435 -0
- package/dist/src/optimization/batch-executor.js +121 -0
- package/dist/src/optimization/context-pruner.js +196 -0
- package/dist/src/optimization/cost-monitor.js +338 -0
- package/dist/src/optimization/index.js +65 -0
- package/dist/src/optimization/model-router.js +264 -0
- package/dist/src/optimization/result-cache.js +114 -0
- package/dist/src/optimization/token-optimizer.js +257 -0
- package/dist/src/optimization/token-tracker.js +118 -0
- package/dist/src/orchestrator-instructions.js +128 -0
- package/dist/src/orchestrator-lite.js +139 -0
- package/dist/src/orchestrator.js +191 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionEngine.js +1 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionStrategy.js +5 -0
- package/dist/src/orchestrators/collaborative/interfaces/IVisualizationRenderer.js +1 -0
- package/dist/src/orchestrators/collaborative/registries/ModelProviderRegistry.js +95 -0
- package/dist/src/orchestrators/collaborative/registries/ToolAdapterRegistry.js +64 -0
- package/dist/src/orchestrators/collaborative/services/tool-execution/ToolExecutionService.js +502 -0
- package/dist/src/orchestrators/collaborative/services/visualization/VisualizationService.js +206 -0
- package/dist/src/orchestrators/collaborative/types/session-types.js +5 -0
- package/dist/src/profiles/balanced.js +37 -0
- package/dist/src/profiles/code_focus.js +37 -0
- package/dist/src/profiles/debug_intensive.js +59 -0
- package/dist/src/profiles/full.js +37 -0
- package/dist/src/profiles/minimal.js +37 -0
- package/dist/src/profiles/research_code.js +59 -0
- package/dist/src/profiles/research_power.js +37 -0
- package/dist/src/profiles/types.js +5 -0
- package/dist/src/profiles/workflow_builder.js +53 -0
- package/dist/src/prompt-engineer-lite.js +78 -0
- package/dist/src/prompt-engineer.js +399 -0
- package/dist/src/reasoning-chain.js +508 -0
- package/dist/src/sequential-thinking.js +291 -0
- package/dist/src/server-diagnostic.js +74 -0
- package/dist/src/server-raw.js +158 -0
- package/dist/src/server-simple.js +58 -0
- package/dist/src/server.js +514 -0
- package/dist/src/session/session-logger.js +617 -0
- package/dist/src/session/session-manager.js +571 -0
- package/dist/src/session/session-tools.js +400 -0
- package/dist/src/tools/advanced-modes.js +200 -0
- package/dist/src/tools/claude-integration.js +356 -0
- package/dist/src/tools/consolidated/ai-router.js +174 -0
- package/dist/src/tools/consolidated/ai-tool.js +48 -0
- package/dist/src/tools/consolidated/brainstorm-tool.js +87 -0
- package/dist/src/tools/consolidated/environment-detector.js +80 -0
- package/dist/src/tools/consolidated/index.js +50 -0
- package/dist/src/tools/consolidated/search-tool.js +110 -0
- package/dist/src/tools/consolidated/workflow-tool.js +238 -0
- package/dist/src/tools/gemini-tools.js +329 -0
- package/dist/src/tools/grok-enhanced.js +376 -0
- package/dist/src/tools/grok-tools.js +299 -0
- package/dist/src/tools/lmstudio-tools.js +223 -0
- package/dist/src/tools/openai-tools.js +498 -0
- package/dist/src/tools/openrouter-tools.js +317 -0
- package/dist/src/tools/optimized-wrapper.js +204 -0
- package/dist/src/tools/perplexity-tools.js +294 -0
- package/dist/src/tools/pingpong-tool.js +343 -0
- package/dist/src/tools/qwen-wrapper.js +74 -0
- package/dist/src/tools/tool-router.js +444 -0
- package/dist/src/tools/unified-ai-provider.js +260 -0
- package/dist/src/tools/workflow-runner.js +425 -0
- package/dist/src/tools/workflow-validator-tool.js +107 -0
- package/dist/src/types.js +23 -0
- package/dist/src/utils/input-validator.js +130 -0
- package/dist/src/utils/model-router.js +91 -0
- package/dist/src/utils/progress-stream.js +255 -0
- package/dist/src/utils/provider-router.js +88 -0
- package/dist/src/utils/smart-api-client.js +146 -0
- package/dist/src/utils/table-builder.js +218 -0
- package/dist/src/utils/timestamp-formatter.js +134 -0
- package/dist/src/utils/tool-compressor.js +257 -0
- package/dist/src/utils/tool-config.js +201 -0
- package/dist/src/validators/dependency-graph-validator.js +147 -0
- package/dist/src/validators/interpolation-validator.js +222 -0
- package/dist/src/validators/output-usage-validator.js +151 -0
- package/dist/src/validators/syntax-validator.js +102 -0
- package/dist/src/validators/tool-registry-validator.js +123 -0
- package/dist/src/validators/tool-types.js +97 -0
- package/dist/src/validators/types.js +8 -0
- package/dist/src/validators/workflow-validator.js +134 -0
- package/dist/src/visualizer-lite.js +42 -0
- package/dist/src/visualizer.js +179 -0
- package/dist/src/workflows/circuit-breaker.js +199 -0
- package/dist/src/workflows/custom-workflows.js +451 -0
- package/dist/src/workflows/engine/AutoSynthesizer.js +97 -0
- package/dist/src/workflows/engine/StepParameterResolver.js +74 -0
- package/dist/src/workflows/engine/VariableInterpolator.js +123 -0
- package/dist/src/workflows/engine/WorkflowDiscovery.js +125 -0
- package/dist/src/workflows/engine/WorkflowExecutionEngine.js +485 -0
- package/dist/src/workflows/engine/WorkflowExecutor.js +113 -0
- package/dist/src/workflows/engine/WorkflowFileManager.js +244 -0
- package/dist/src/workflows/engine/WorkflowHelpers.js +114 -0
- package/dist/src/workflows/engine/WorkflowOutputFormatter.js +83 -0
- package/dist/src/workflows/engine/events/WorkflowEventBus.js +132 -0
- package/dist/src/workflows/engine/events/interfaces/IEventBus.js +5 -0
- package/dist/src/workflows/engine/handlers/ErrorRecoveryHandler.js +162 -0
- package/dist/src/workflows/engine/handlers/PromptEnhancementHandler.js +115 -0
- package/dist/src/workflows/engine/handlers/SessionPersistenceHandler.js +167 -0
- package/dist/src/workflows/engine/handlers/StepExecutionHandler.js +231 -0
- package/dist/src/workflows/engine/handlers/ToolInvocationHandler.js +46 -0
- package/dist/src/workflows/engine/interfaces/IAutoSynthesizer.js +5 -0
- package/dist/src/workflows/engine/interfaces/IStepParameterResolver.js +5 -0
- package/dist/src/workflows/engine/interfaces/IVariableInterpolator.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowDiscovery.js +4 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowFileManager.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowOutputFormatter.js +5 -0
- package/dist/src/workflows/engine/state/WorkflowStateMachine.js +194 -0
- package/dist/src/workflows/engine/state/interfaces/IStateMachine.js +17 -0
- package/dist/src/workflows/fallback-strategies.js +373 -0
- package/dist/src/workflows/message-queue.js +455 -0
- package/dist/src/workflows/model-router.js +189 -0
- package/dist/src/workflows/orchestrator-examples.js +174 -0
- package/dist/src/workflows/orchestrator-integration.js +200 -0
- package/dist/src/workflows/self-healing.js +524 -0
- package/dist/src/workflows/tool-mapper.js +407 -0
- package/dist/src/workflows/tool-orchestrator.js +796 -0
- package/dist/src/workflows/workflow-engine.js +573 -0
- package/dist/src/workflows/workflow-parser.js +283 -0
- package/dist/src/workflows/workflow-types.js +95 -0
- package/dist/src/workflows.js +568 -0
- package/dist/test-workflow-file-output.js +93 -0
- package/docs/API_KEYS.md +570 -0
- package/docs/CLAUDE_CODE_SETUP.md +181 -0
- package/docs/CLAUDE_DESKTOP_MANUAL.md +127 -0
- package/docs/CONFIGURATION.md +745 -0
- package/docs/FOCUS_MODES.md +240 -0
- package/docs/INSTALLATION_BOTH.md +145 -0
- package/docs/TERMS.md +352 -0
- package/docs/TOOLS_REFERENCE.md +1622 -0
- package/docs/TOOL_PARAMETERS.md +496 -0
- package/docs/TOOL_PROFILES.md +236 -0
- package/docs/WORKFLOWS.md +987 -0
- package/docs/WORKFLOW_OUTPUT.md +198 -0
- package/docs/WORKFLOW_PROGRESS_TRACKING.md +305 -0
- package/docs/workflows/design-brainstorm.md +335 -0
- package/package.json +97 -0
- package/profiles/balanced.json +37 -0
- package/profiles/code_focus.json +37 -0
- package/profiles/debug_intensive.json +34 -0
- package/profiles/full.json +37 -0
- package/profiles/minimal.json +37 -0
- package/profiles/research_power.json +37 -0
- package/profiles/workflow_builder.json +37 -0
- package/smithery.yaml +66 -0
- package/start.sh +8 -0
- package/tools.config.json +81 -0
- package/tsconfig.json +18 -0
- package/workflows/accessibility-code-audit.yaml +92 -0
- package/workflows/code-architecture-review.yaml +202 -0
- package/workflows/code-review.yaml +142 -0
- package/workflows/core/iterative-problem-solver.yaml +283 -0
- package/workflows/creative-brainstorm-yaml.yaml +215 -0
- package/workflows/pingpong.yaml +141 -0
- package/workflows/system/README.md +412 -0
- package/workflows/system/challenger.yaml +175 -0
- package/workflows/system/scout.yaml +164 -0
- package/workflows/system/verifier.yaml +133 -0
- package/workflows/ultra-creative-brainstorm.yaml +318 -0
- package/workflows/ux-research-flow.yaml +92 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
export class TestArchitect {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.defaultModel = 'qwen3-coder-480b'; // Best for code generation
|
|
4
|
+
this.defaultMaxTokens = 4000;
|
|
5
|
+
this.defaultFramework = 'jest';
|
|
6
|
+
this.defaultTestTypes = ['unit', 'integration', 'e2e'];
|
|
7
|
+
}
|
|
8
|
+
async architectTests(code, options = {}) {
|
|
9
|
+
const model = options.model || this.defaultModel;
|
|
10
|
+
const framework = options.testFramework || this.defaultFramework;
|
|
11
|
+
const testTypes = options.testTypes || this.defaultTestTypes;
|
|
12
|
+
const coverage = options.coverage || 'thorough';
|
|
13
|
+
const generateMocks = options.generateMocks !== false; // default true
|
|
14
|
+
// Type assertions to help TypeScript understand these won't be undefined
|
|
15
|
+
const safeFramework = framework;
|
|
16
|
+
const safeTestTypes = testTypes;
|
|
17
|
+
const safeCoverage = coverage;
|
|
18
|
+
// Analyze code structure
|
|
19
|
+
const codeStructure = await this.analyzeCodeForTesting(code);
|
|
20
|
+
// Design test suite architecture
|
|
21
|
+
const testSuite = await this.designTestSuite(codeStructure, safeFramework, safeCoverage);
|
|
22
|
+
// Generate test files
|
|
23
|
+
const testFiles = await this.generateTestFiles(codeStructure, safeFramework, safeTestTypes);
|
|
24
|
+
// Generate edge cases
|
|
25
|
+
const edgeCases = await this.generateEdgeCases(codeStructure);
|
|
26
|
+
// Generate mocks if requested
|
|
27
|
+
const mockFiles = generateMocks ? await this.generateMockFiles(codeStructure, safeFramework) : [];
|
|
28
|
+
// Generate test configuration
|
|
29
|
+
const testConfig = await this.generateTestConfiguration(safeFramework, testSuite, safeCoverage);
|
|
30
|
+
// Create synthesis
|
|
31
|
+
const synthesis = this.synthesizeTestArchitecture(testSuite, testFiles, mockFiles, testConfig, edgeCases);
|
|
32
|
+
return {
|
|
33
|
+
testSuite,
|
|
34
|
+
testFiles,
|
|
35
|
+
mockFiles,
|
|
36
|
+
testConfig,
|
|
37
|
+
edgeCases,
|
|
38
|
+
synthesis
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async analyzeCodeForTesting(code) {
|
|
42
|
+
const structure = {
|
|
43
|
+
functions: this.extractTestableFunction(code),
|
|
44
|
+
classes: this.extractTestableClasses(code),
|
|
45
|
+
exports: this.extractExports(code),
|
|
46
|
+
dependencies: this.extractDependencies(code),
|
|
47
|
+
asyncOperations: this.findAsyncOperations(code),
|
|
48
|
+
errorHandling: this.findErrorHandling(code),
|
|
49
|
+
complexity: this.assessTestingComplexity(code),
|
|
50
|
+
riskAreas: this.identifyRiskAreas(code)
|
|
51
|
+
};
|
|
52
|
+
return structure;
|
|
53
|
+
}
|
|
54
|
+
extractTestableFunction(code) {
|
|
55
|
+
const functions = [];
|
|
56
|
+
const functionPatterns = [
|
|
57
|
+
/(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)\s*(?::\s*([^{]+))?\s*\{/g,
|
|
58
|
+
/(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?\(([^)]*)\)\s*=>\s*/g
|
|
59
|
+
];
|
|
60
|
+
functionPatterns.forEach(pattern => {
|
|
61
|
+
let match;
|
|
62
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
63
|
+
const [, name, params] = match;
|
|
64
|
+
const funcBody = this.extractFunctionBody(code, match.index);
|
|
65
|
+
functions.push({
|
|
66
|
+
name,
|
|
67
|
+
parameters: this.parseParameters(params),
|
|
68
|
+
isAsync: match[0].includes('async'),
|
|
69
|
+
isExported: match[0].includes('export'),
|
|
70
|
+
complexity: this.calculateFunctionComplexity(funcBody),
|
|
71
|
+
hasErrorHandling: funcBody.includes('try') || funcBody.includes('catch'),
|
|
72
|
+
body: funcBody,
|
|
73
|
+
testPriority: this.calculateTestPriority(name, funcBody)
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return functions;
|
|
78
|
+
}
|
|
79
|
+
extractFunctionBody(code, startIndex) {
|
|
80
|
+
let braceCount = 0;
|
|
81
|
+
let startBrace = -1;
|
|
82
|
+
for (let i = startIndex; i < code.length; i++) {
|
|
83
|
+
if (code[i] === '{') {
|
|
84
|
+
if (startBrace === -1)
|
|
85
|
+
startBrace = i;
|
|
86
|
+
braceCount++;
|
|
87
|
+
}
|
|
88
|
+
else if (code[i] === '}') {
|
|
89
|
+
braceCount--;
|
|
90
|
+
if (braceCount === 0) {
|
|
91
|
+
return code.substring(startBrace, i + 1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return '';
|
|
96
|
+
}
|
|
97
|
+
calculateFunctionComplexity(body) {
|
|
98
|
+
const complexityIndicators = ['if', 'for', 'while', 'switch', 'try', '?'];
|
|
99
|
+
const count = complexityIndicators.reduce((sum, indicator) => sum + (body.split(indicator).length - 1), 0);
|
|
100
|
+
return count > 8 ? 'high' : count > 3 ? 'medium' : 'low';
|
|
101
|
+
}
|
|
102
|
+
calculateTestPriority(name, body) {
|
|
103
|
+
// Critical: Security, authentication, payment, data validation
|
|
104
|
+
const criticalKeywords = ['auth', 'login', 'password', 'validate', 'security', 'payment', 'encrypt'];
|
|
105
|
+
if (criticalKeywords.some(keyword => name.toLowerCase().includes(keyword))) {
|
|
106
|
+
return 'critical';
|
|
107
|
+
}
|
|
108
|
+
// High: Error handling, async operations, external APIs
|
|
109
|
+
if (body.includes('throw') || body.includes('await') || body.includes('fetch') || body.includes('axios')) {
|
|
110
|
+
return 'high';
|
|
111
|
+
}
|
|
112
|
+
// Medium: Business logic, calculations, transformations
|
|
113
|
+
if (name.includes('calculate') || name.includes('process') || name.includes('transform')) {
|
|
114
|
+
return 'medium';
|
|
115
|
+
}
|
|
116
|
+
return 'low';
|
|
117
|
+
}
|
|
118
|
+
extractTestableClasses(code) {
|
|
119
|
+
const classes = [];
|
|
120
|
+
const classPattern = /(?:export\s+)?class\s+(\w+)(?:\s+extends\s+(\w+))?\s*\{([^}]+)\}/g;
|
|
121
|
+
let match;
|
|
122
|
+
while ((match = classPattern.exec(code)) !== null) {
|
|
123
|
+
const [, name, extends_, body] = match;
|
|
124
|
+
const methods = this.extractClassMethods(body);
|
|
125
|
+
classes.push({
|
|
126
|
+
name,
|
|
127
|
+
extends: extends_,
|
|
128
|
+
methods,
|
|
129
|
+
isExported: match[0].includes('export'),
|
|
130
|
+
testPriority: methods.some((m) => m.testPriority === 'critical') ? 'critical' : 'high'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return classes;
|
|
134
|
+
}
|
|
135
|
+
extractClassMethods(body) {
|
|
136
|
+
const methods = [];
|
|
137
|
+
const methodPattern = /(async\s+)?(\w+)\s*\(([^)]*)\)\s*(?::\s*([^{]+))?\s*\{/g;
|
|
138
|
+
let match;
|
|
139
|
+
while ((match = methodPattern.exec(body)) !== null) {
|
|
140
|
+
const [, async, name, params, returnType] = match;
|
|
141
|
+
methods.push({
|
|
142
|
+
name,
|
|
143
|
+
isAsync: !!async,
|
|
144
|
+
parameters: this.parseParameters(params),
|
|
145
|
+
returnType,
|
|
146
|
+
testPriority: this.calculateTestPriority(name, body)
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return methods;
|
|
150
|
+
}
|
|
151
|
+
extractExports(code) {
|
|
152
|
+
const exports = [];
|
|
153
|
+
const patterns = [
|
|
154
|
+
/export\s+(?:default\s+)?(?:class|function|const)\s+(\w+)/g,
|
|
155
|
+
/export\s*\{\s*([^}]+)\s*\}/g
|
|
156
|
+
];
|
|
157
|
+
patterns.forEach(pattern => {
|
|
158
|
+
let match;
|
|
159
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
160
|
+
if (match[1].includes(',')) {
|
|
161
|
+
// Handle multiple exports: export { a, b, c }
|
|
162
|
+
match[1].split(',').forEach(name => exports.push(name.trim()));
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
exports.push(match[1]);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
return exports;
|
|
170
|
+
}
|
|
171
|
+
extractDependencies(code) {
|
|
172
|
+
const dependencies = [];
|
|
173
|
+
const importPattern = /import\s+(?:\{([^}]+)\}|\*\s+as\s+(\w+)|(\w+))?\s+from\s+['"]([^'"]+)['"]/g;
|
|
174
|
+
let match;
|
|
175
|
+
while ((match = importPattern.exec(code)) !== null) {
|
|
176
|
+
const [, named, namespace, default_, module] = match;
|
|
177
|
+
dependencies.push({
|
|
178
|
+
module,
|
|
179
|
+
imports: named ? named.split(',').map(s => s.trim()) : namespace ? [namespace] : [default_],
|
|
180
|
+
type: this.categorizeImport(module)
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return dependencies;
|
|
184
|
+
}
|
|
185
|
+
categorizeImport(module) {
|
|
186
|
+
if (module.startsWith('./') || module.startsWith('../'))
|
|
187
|
+
return 'internal';
|
|
188
|
+
if (['fs', 'path', 'http', 'crypto', 'util'].includes(module))
|
|
189
|
+
return 'builtin';
|
|
190
|
+
return 'external';
|
|
191
|
+
}
|
|
192
|
+
findAsyncOperations(code) {
|
|
193
|
+
const asyncOps = [];
|
|
194
|
+
const patterns = [
|
|
195
|
+
{ pattern: /await\s+([^;\n]+)/g, type: 'await' },
|
|
196
|
+
{ pattern: /\.then\s*\(/g, type: 'promise' },
|
|
197
|
+
{ pattern: /new\s+Promise/g, type: 'promise-constructor' },
|
|
198
|
+
{ pattern: /setTimeout|setInterval/g, type: 'timer' }
|
|
199
|
+
];
|
|
200
|
+
patterns.forEach(({ pattern, type }) => {
|
|
201
|
+
let match;
|
|
202
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
203
|
+
asyncOps.push({
|
|
204
|
+
type,
|
|
205
|
+
code: match[0],
|
|
206
|
+
line: this.getLineNumber(code, match.index)
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
return asyncOps;
|
|
211
|
+
}
|
|
212
|
+
findErrorHandling(code) {
|
|
213
|
+
const errorHandling = [];
|
|
214
|
+
const patterns = [
|
|
215
|
+
{ pattern: /try\s*\{[^}]*\}\s*catch/g, type: 'try-catch' },
|
|
216
|
+
{ pattern: /throw\s+new\s+Error/g, type: 'throw' },
|
|
217
|
+
{ pattern: /\.catch\s*\(/g, type: 'promise-catch' }
|
|
218
|
+
];
|
|
219
|
+
patterns.forEach(({ pattern, type }) => {
|
|
220
|
+
const matches = code.match(pattern) || [];
|
|
221
|
+
errorHandling.push({
|
|
222
|
+
type,
|
|
223
|
+
count: matches.length
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
return errorHandling;
|
|
227
|
+
}
|
|
228
|
+
identifyRiskAreas(code) {
|
|
229
|
+
const risks = [];
|
|
230
|
+
if (code.includes('eval('))
|
|
231
|
+
risks.push('Code injection risk (eval)');
|
|
232
|
+
if (code.includes('innerHTML'))
|
|
233
|
+
risks.push('XSS risk (innerHTML)');
|
|
234
|
+
if (code.includes('JSON.parse') && !code.includes('try'))
|
|
235
|
+
risks.push('JSON parsing without error handling');
|
|
236
|
+
if (code.includes('parseInt') && !code.includes('10'))
|
|
237
|
+
risks.push('parseInt without radix');
|
|
238
|
+
if (code.includes('== null') || code.includes('!= null'))
|
|
239
|
+
risks.push('Loose equality with null');
|
|
240
|
+
return risks;
|
|
241
|
+
}
|
|
242
|
+
getLineNumber(code, index) {
|
|
243
|
+
return code.substring(0, index).split('\n').length;
|
|
244
|
+
}
|
|
245
|
+
parseParameters(paramString) {
|
|
246
|
+
if (!paramString.trim())
|
|
247
|
+
return [];
|
|
248
|
+
return paramString.split(',').map(param => {
|
|
249
|
+
const trimmed = param.trim();
|
|
250
|
+
const [nameAndType] = trimmed.split('=');
|
|
251
|
+
const [name, type] = nameAndType.includes(':')
|
|
252
|
+
? nameAndType.split(':').map(s => s.trim())
|
|
253
|
+
: [nameAndType.trim(), 'any'];
|
|
254
|
+
return { name, type: type || 'any' };
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
assessTestingComplexity(code) {
|
|
258
|
+
const lines = code.split('\n').length;
|
|
259
|
+
const functions = (code.match(/function|=>/g) || []).length;
|
|
260
|
+
const asyncOps = (code.match(/await|\.then/g) || []).length;
|
|
261
|
+
const complexity = lines + functions * 5 + asyncOps * 3;
|
|
262
|
+
return complexity > 300 ? 'high' : complexity > 100 ? 'medium' : 'low';
|
|
263
|
+
}
|
|
264
|
+
async designTestSuite(structure, framework, coverage) {
|
|
265
|
+
const totalFunctions = structure.functions.length;
|
|
266
|
+
const totalClasses = structure.classes.length;
|
|
267
|
+
const criticalItems = [...structure.functions, ...structure.classes]
|
|
268
|
+
.filter(item => item.testPriority === 'critical').length;
|
|
269
|
+
const testMultiplier = coverage === 'basic' ? 2 : coverage === 'thorough' ? 4 : 6;
|
|
270
|
+
const totalTests = (totalFunctions + totalClasses * 3) * testMultiplier + structure.edgeCases?.length || 0;
|
|
271
|
+
const testCategories = [
|
|
272
|
+
{
|
|
273
|
+
name: 'Unit Tests',
|
|
274
|
+
testCount: totalFunctions * testMultiplier,
|
|
275
|
+
priority: 'high',
|
|
276
|
+
description: 'Individual function and method testing'
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: 'Integration Tests',
|
|
280
|
+
testCount: Math.ceil(totalTests * 0.3),
|
|
281
|
+
priority: 'medium',
|
|
282
|
+
description: 'Component interaction testing'
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: 'Edge Cases',
|
|
286
|
+
testCount: structure.riskAreas.length * 2,
|
|
287
|
+
priority: 'high',
|
|
288
|
+
description: 'Boundary and error condition testing'
|
|
289
|
+
}
|
|
290
|
+
];
|
|
291
|
+
if (criticalItems > 0) {
|
|
292
|
+
testCategories.push({
|
|
293
|
+
name: 'Security Tests',
|
|
294
|
+
testCount: criticalItems * 3,
|
|
295
|
+
priority: 'high',
|
|
296
|
+
description: 'Security and validation testing'
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
name: `${framework.charAt(0).toUpperCase() + framework.slice(1)} Test Suite`,
|
|
301
|
+
description: `Comprehensive test suite with ${coverage} coverage`,
|
|
302
|
+
framework,
|
|
303
|
+
totalTests,
|
|
304
|
+
testCategories,
|
|
305
|
+
setupRequirements: this.generateSetupRequirements(structure, framework),
|
|
306
|
+
estimatedCoverage: coverage === 'basic' ? 70 : coverage === 'thorough' ? 85 : 95
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
generateSetupRequirements(structure, framework) {
|
|
310
|
+
const requirements = [
|
|
311
|
+
`Install ${framework} and related dependencies`,
|
|
312
|
+
'Configure test scripts in package.json'
|
|
313
|
+
];
|
|
314
|
+
if (structure.asyncOperations.length > 0) {
|
|
315
|
+
requirements.push('Setup async testing utilities');
|
|
316
|
+
}
|
|
317
|
+
if (structure.dependencies.some((d) => d.type === 'external')) {
|
|
318
|
+
requirements.push('Configure mocking for external dependencies');
|
|
319
|
+
}
|
|
320
|
+
if (structure.riskAreas.length > 0) {
|
|
321
|
+
requirements.push('Setup security testing tools');
|
|
322
|
+
}
|
|
323
|
+
return requirements;
|
|
324
|
+
}
|
|
325
|
+
async generateTestFiles(structure, framework, testTypes) {
|
|
326
|
+
const testFiles = [];
|
|
327
|
+
// Generate unit test files
|
|
328
|
+
if (testTypes.includes('unit')) {
|
|
329
|
+
structure.functions.forEach((func) => {
|
|
330
|
+
testFiles.push({
|
|
331
|
+
fileName: `${func.name}.test.${framework === 'jest' ? 'js' : 'ts'}`,
|
|
332
|
+
testType: 'unit',
|
|
333
|
+
tests: this.generateUnitTests(func, framework),
|
|
334
|
+
imports: this.generateTestImports(func, framework),
|
|
335
|
+
setup: this.generateTestSetup(func, framework),
|
|
336
|
+
teardown: this.generateTestTeardown(func, framework),
|
|
337
|
+
content: this.generateTestFileContent(func, framework, 'unit')
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
// Generate integration test files
|
|
342
|
+
if (testTypes.includes('integration')) {
|
|
343
|
+
const integrationFile = {
|
|
344
|
+
fileName: `integration.test.${framework === 'jest' ? 'js' : 'ts'}`,
|
|
345
|
+
testType: 'integration',
|
|
346
|
+
tests: this.generateIntegrationTests(structure, framework),
|
|
347
|
+
imports: [`import { ${structure.exports.join(', ')} } from '../src/index'`],
|
|
348
|
+
setup: 'beforeAll(() => { /* Setup test environment */ });',
|
|
349
|
+
teardown: 'afterAll(() => { /* Cleanup test environment */ });',
|
|
350
|
+
content: this.generateIntegrationTestContent(structure, framework)
|
|
351
|
+
};
|
|
352
|
+
testFiles.push(integrationFile);
|
|
353
|
+
}
|
|
354
|
+
// Generate E2E test files
|
|
355
|
+
if (testTypes.includes('e2e')) {
|
|
356
|
+
testFiles.push({
|
|
357
|
+
fileName: `e2e.test.${framework === 'playwright' ? 'ts' : 'js'}`,
|
|
358
|
+
testType: 'e2e',
|
|
359
|
+
tests: this.generateE2ETests(structure, framework),
|
|
360
|
+
imports: this.generateE2EImports(framework),
|
|
361
|
+
setup: 'beforeEach(() => { /* Setup browser */ });',
|
|
362
|
+
teardown: 'afterEach(() => { /* Cleanup browser */ });',
|
|
363
|
+
content: this.generateE2ETestContent(structure, framework)
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
return testFiles;
|
|
367
|
+
}
|
|
368
|
+
generateUnitTests(func, framework) {
|
|
369
|
+
const tests = [];
|
|
370
|
+
// Happy path test
|
|
371
|
+
tests.push({
|
|
372
|
+
name: `should ${func.name} successfully with valid input`,
|
|
373
|
+
description: `Test ${func.name} with expected input parameters`,
|
|
374
|
+
type: 'positive',
|
|
375
|
+
priority: func.testPriority,
|
|
376
|
+
code: this.generateHappyPathTest(func, framework),
|
|
377
|
+
assertions: [`expect(result).toBeDefined()`, `expect(result).not.toBeNull()`],
|
|
378
|
+
mocks: func.isAsync ? ['mockExternalService'] : undefined
|
|
379
|
+
});
|
|
380
|
+
// Error handling test
|
|
381
|
+
if (func.hasErrorHandling) {
|
|
382
|
+
tests.push({
|
|
383
|
+
name: `should handle errors in ${func.name}`,
|
|
384
|
+
description: `Test error handling in ${func.name}`,
|
|
385
|
+
type: 'negative',
|
|
386
|
+
priority: 'high',
|
|
387
|
+
code: this.generateErrorTest(func, framework),
|
|
388
|
+
assertions: [`expect(() => ${func.name}(invalidInput)).toThrow()`],
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
// Edge cases
|
|
392
|
+
if (func.parameters.length > 0) {
|
|
393
|
+
tests.push({
|
|
394
|
+
name: `should handle edge cases in ${func.name}`,
|
|
395
|
+
description: `Test boundary conditions for ${func.name}`,
|
|
396
|
+
type: 'edge',
|
|
397
|
+
priority: 'medium',
|
|
398
|
+
code: this.generateEdgeCaseTest(func, framework),
|
|
399
|
+
assertions: [`expect(result).toBeValid()`],
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
return tests;
|
|
403
|
+
}
|
|
404
|
+
generateHappyPathTest(func, framework) {
|
|
405
|
+
const params = func.parameters.map((p) => this.generateTestValue(p.type)).join(', ');
|
|
406
|
+
const asyncPrefix = func.isAsync ? 'await ' : '';
|
|
407
|
+
return `
|
|
408
|
+
test('should ${func.name} successfully with valid input', ${func.isAsync ? 'async ' : ''}() => {
|
|
409
|
+
${func.isAsync ? '// Setup mocks if needed' : ''}
|
|
410
|
+
const result = ${asyncPrefix}${func.name}(${params});
|
|
411
|
+
expect(result).toBeDefined();
|
|
412
|
+
${this.generateAdditionalAssertions(func)}
|
|
413
|
+
});`;
|
|
414
|
+
}
|
|
415
|
+
generateErrorTest(func, framework) {
|
|
416
|
+
return `
|
|
417
|
+
test('should handle errors in ${func.name}', ${func.isAsync ? 'async ' : ''}() => {
|
|
418
|
+
${func.isAsync ? 'await expect(' : 'expect(() =>'}${func.name}(null)${func.isAsync ? ').rejects.toThrow()' : ').toThrow()'};
|
|
419
|
+
});`;
|
|
420
|
+
}
|
|
421
|
+
generateEdgeCaseTest(func, framework) {
|
|
422
|
+
const edgeParams = func.parameters.map((p) => this.generateEdgeValue(p.type)).join(', ');
|
|
423
|
+
return `
|
|
424
|
+
test('should handle edge cases in ${func.name}', ${func.isAsync ? 'async ' : ''}() => {
|
|
425
|
+
const result = ${func.isAsync ? 'await ' : ''}${func.name}(${edgeParams});
|
|
426
|
+
expect(result).toBeValid(); // Replace with appropriate assertion
|
|
427
|
+
});`;
|
|
428
|
+
}
|
|
429
|
+
generateTestValue(type) {
|
|
430
|
+
const typeMap = {
|
|
431
|
+
'string': '"test"',
|
|
432
|
+
'number': '42',
|
|
433
|
+
'boolean': 'true',
|
|
434
|
+
'object': '{}',
|
|
435
|
+
'array': '[]',
|
|
436
|
+
'undefined': 'undefined',
|
|
437
|
+
'null': 'null'
|
|
438
|
+
};
|
|
439
|
+
return typeMap[type.toLowerCase()] || '{}';
|
|
440
|
+
}
|
|
441
|
+
generateEdgeValue(type) {
|
|
442
|
+
const edgeMap = {
|
|
443
|
+
'string': '""', // empty string
|
|
444
|
+
'number': '0',
|
|
445
|
+
'boolean': 'false',
|
|
446
|
+
'object': 'null',
|
|
447
|
+
'array': '[]'
|
|
448
|
+
};
|
|
449
|
+
return edgeMap[type.toLowerCase()] || 'null';
|
|
450
|
+
}
|
|
451
|
+
generateAdditionalAssertions(func) {
|
|
452
|
+
if (func.name.toLowerCase().includes('calculate')) {
|
|
453
|
+
return 'expect(typeof result).toBe("number");';
|
|
454
|
+
}
|
|
455
|
+
if (func.name.toLowerCase().includes('validate')) {
|
|
456
|
+
return 'expect(typeof result).toBe("boolean");';
|
|
457
|
+
}
|
|
458
|
+
return '// Add specific assertions based on function behavior';
|
|
459
|
+
}
|
|
460
|
+
generateTestImports(func, framework) {
|
|
461
|
+
const imports = [`import { ${func.name} } from '../src/index'`];
|
|
462
|
+
if (framework === 'jest') {
|
|
463
|
+
imports.push("import { jest } from '@jest/globals'");
|
|
464
|
+
}
|
|
465
|
+
if (func.isAsync) {
|
|
466
|
+
imports.push("// Import mocking utilities if needed");
|
|
467
|
+
}
|
|
468
|
+
return imports;
|
|
469
|
+
}
|
|
470
|
+
generateTestSetup(func, framework) {
|
|
471
|
+
if (func.isAsync) {
|
|
472
|
+
return `beforeEach(() => {
|
|
473
|
+
// Setup mocks and test environment
|
|
474
|
+
jest.clearAllMocks();
|
|
475
|
+
});`;
|
|
476
|
+
}
|
|
477
|
+
return 'beforeEach(() => { /* Setup if needed */ });';
|
|
478
|
+
}
|
|
479
|
+
generateTestTeardown(func, framework) {
|
|
480
|
+
if (func.isAsync) {
|
|
481
|
+
return `afterEach(() => {
|
|
482
|
+
// Cleanup mocks and resources
|
|
483
|
+
jest.restoreAllMocks();
|
|
484
|
+
});`;
|
|
485
|
+
}
|
|
486
|
+
return 'afterEach(() => { /* Cleanup if needed */ });';
|
|
487
|
+
}
|
|
488
|
+
generateTestFileContent(func, framework, testType) {
|
|
489
|
+
const imports = this.generateTestImports(func, framework).join('\n');
|
|
490
|
+
const setup = this.generateTestSetup(func, framework);
|
|
491
|
+
const teardown = this.generateTestTeardown(func, framework);
|
|
492
|
+
const tests = this.generateUnitTests(func, framework);
|
|
493
|
+
return `${imports}
|
|
494
|
+
|
|
495
|
+
describe('${func.name}', () => {
|
|
496
|
+
${setup}
|
|
497
|
+
|
|
498
|
+
${teardown}
|
|
499
|
+
|
|
500
|
+
${tests.map(test => test.code).join('\n ')}
|
|
501
|
+
});`;
|
|
502
|
+
}
|
|
503
|
+
generateIntegrationTests(structure, framework) {
|
|
504
|
+
const tests = [];
|
|
505
|
+
// Test component interactions
|
|
506
|
+
tests.push({
|
|
507
|
+
name: 'should integrate components correctly',
|
|
508
|
+
description: 'Test integration between multiple components',
|
|
509
|
+
type: 'positive',
|
|
510
|
+
priority: 'high',
|
|
511
|
+
code: this.generateComponentIntegrationTest(structure, framework),
|
|
512
|
+
assertions: ['expect(result).toBeDefined()']
|
|
513
|
+
});
|
|
514
|
+
return tests;
|
|
515
|
+
}
|
|
516
|
+
generateComponentIntegrationTest(structure, framework) {
|
|
517
|
+
const mainFunctions = structure.functions.slice(0, 2);
|
|
518
|
+
return `
|
|
519
|
+
test('should integrate components correctly', async () => {
|
|
520
|
+
// Test integration between ${mainFunctions.map((f) => f.name).join(' and ')}
|
|
521
|
+
${mainFunctions.map((f) => `const ${f.name}Result = await ${f.name}(${this.generateTestValue('any')});`).join('\n ')}
|
|
522
|
+
|
|
523
|
+
// Assert integration works
|
|
524
|
+
${mainFunctions.map((f) => `expect(${f.name}Result).toBeDefined();`).join('\n ')}
|
|
525
|
+
});`;
|
|
526
|
+
}
|
|
527
|
+
generateIntegrationTestContent(structure, framework) {
|
|
528
|
+
const tests = this.generateIntegrationTests(structure, framework);
|
|
529
|
+
return `import { ${structure.exports.join(', ')} } from '../src/index';
|
|
530
|
+
|
|
531
|
+
describe('Integration Tests', () => {
|
|
532
|
+
beforeAll(() => {
|
|
533
|
+
// Setup integration test environment
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
afterAll(() => {
|
|
537
|
+
// Cleanup integration test environment
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
${tests.map(test => test.code).join('\n ')}
|
|
541
|
+
});`;
|
|
542
|
+
}
|
|
543
|
+
generateE2ETests(structure, framework) {
|
|
544
|
+
// Basic E2E test structure
|
|
545
|
+
return [{
|
|
546
|
+
name: 'should work end-to-end',
|
|
547
|
+
description: 'Complete user workflow test',
|
|
548
|
+
type: 'positive',
|
|
549
|
+
priority: 'medium',
|
|
550
|
+
code: this.generateE2ETestCode(structure, framework),
|
|
551
|
+
assertions: ['expect(page).toContainText("success")']
|
|
552
|
+
}];
|
|
553
|
+
}
|
|
554
|
+
generateE2ETestCode(structure, framework) {
|
|
555
|
+
if (framework === 'playwright') {
|
|
556
|
+
return `
|
|
557
|
+
test('should work end-to-end', async ({ page }) => {
|
|
558
|
+
await page.goto('/');
|
|
559
|
+
// Add E2E test steps here
|
|
560
|
+
await expect(page).toHaveTitle(/Expected Title/);
|
|
561
|
+
});`;
|
|
562
|
+
}
|
|
563
|
+
return `
|
|
564
|
+
test('should work end-to-end', async () => {
|
|
565
|
+
// E2E test implementation
|
|
566
|
+
expect(true).toBe(true); // Replace with actual test
|
|
567
|
+
});`;
|
|
568
|
+
}
|
|
569
|
+
generateE2EImports(framework) {
|
|
570
|
+
if (framework === 'playwright') {
|
|
571
|
+
return ["import { test, expect } from '@playwright/test'"];
|
|
572
|
+
}
|
|
573
|
+
return ["// Import E2E testing framework"];
|
|
574
|
+
}
|
|
575
|
+
generateE2ETestContent(structure, framework) {
|
|
576
|
+
const imports = this.generateE2EImports(framework).join('\n');
|
|
577
|
+
const tests = this.generateE2ETests(structure, framework);
|
|
578
|
+
return `${imports}
|
|
579
|
+
|
|
580
|
+
describe('E2E Tests', () => {
|
|
581
|
+
${tests.map(test => test.code).join('\n ')}
|
|
582
|
+
});`;
|
|
583
|
+
}
|
|
584
|
+
async generateEdgeCases(structure) {
|
|
585
|
+
const edgeCases = [];
|
|
586
|
+
structure.functions.forEach((func) => {
|
|
587
|
+
func.parameters.forEach((param) => {
|
|
588
|
+
edgeCases.push({
|
|
589
|
+
scenario: `${func.name} with ${param.type} boundary values`,
|
|
590
|
+
input: this.generateBoundaryInput(param.type),
|
|
591
|
+
expectedOutput: 'Should handle gracefully',
|
|
592
|
+
description: `Test ${func.name} with edge case input for ${param.name}`,
|
|
593
|
+
severity: param.type === 'string' ? 'medium' : 'high',
|
|
594
|
+
testCode: this.generateEdgeCaseTestCode(func, param)
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
return edgeCases.slice(0, 10); // Limit to prevent overflow
|
|
599
|
+
}
|
|
600
|
+
generateBoundaryInput(type) {
|
|
601
|
+
const boundaries = {
|
|
602
|
+
'string': ['', 'a'.repeat(1000), 'special chars: !@#$%^&*()'],
|
|
603
|
+
'number': [0, -1, Number.MAX_VALUE, Number.MIN_VALUE, NaN, Infinity],
|
|
604
|
+
'array': [[], new Array(1000).fill(0)],
|
|
605
|
+
'object': [{}, null, undefined]
|
|
606
|
+
};
|
|
607
|
+
return boundaries[type.toLowerCase()] || null;
|
|
608
|
+
}
|
|
609
|
+
generateEdgeCaseTestCode(func, param) {
|
|
610
|
+
const boundaryValues = this.generateBoundaryInput(param.type);
|
|
611
|
+
return `
|
|
612
|
+
test('should handle ${param.name} boundary values', () => {
|
|
613
|
+
const boundaryValues = ${JSON.stringify(boundaryValues)};
|
|
614
|
+
boundaryValues.forEach(value => {
|
|
615
|
+
expect(() => ${func.name}(value)).not.toThrow();
|
|
616
|
+
});
|
|
617
|
+
});`;
|
|
618
|
+
}
|
|
619
|
+
async generateMockFiles(structure, framework) {
|
|
620
|
+
const mocks = [];
|
|
621
|
+
// Generate service mocks
|
|
622
|
+
const externalDeps = structure.dependencies.filter((d) => d.type === 'external');
|
|
623
|
+
externalDeps.forEach((dep) => {
|
|
624
|
+
mocks.push({
|
|
625
|
+
fileName: `__mocks__/${dep.module}.js`,
|
|
626
|
+
mockType: 'service',
|
|
627
|
+
content: this.generateServiceMock(dep, framework),
|
|
628
|
+
description: `Mock for ${dep.module} service`
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
// Generate API mocks
|
|
632
|
+
const asyncOps = structure.asyncOperations.filter((op) => op.type === 'await');
|
|
633
|
+
if (asyncOps.length > 0) {
|
|
634
|
+
mocks.push({
|
|
635
|
+
fileName: '__mocks__/api.js',
|
|
636
|
+
mockType: 'api',
|
|
637
|
+
content: this.generateApiMock(framework),
|
|
638
|
+
description: 'Mock for API calls'
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
return mocks;
|
|
642
|
+
}
|
|
643
|
+
generateServiceMock(dependency, framework) {
|
|
644
|
+
return `// Mock for ${dependency.module}
|
|
645
|
+
export default {
|
|
646
|
+
${dependency.imports.map((imp) => `
|
|
647
|
+
${imp}: jest.fn(() => Promise.resolve('mocked-${imp}'))`).join(',\n')}
|
|
648
|
+
};`;
|
|
649
|
+
}
|
|
650
|
+
generateApiMock(framework) {
|
|
651
|
+
return `// API Mock
|
|
652
|
+
export const mockApi = {
|
|
653
|
+
get: jest.fn(() => Promise.resolve({ data: 'mocked-data' })),
|
|
654
|
+
post: jest.fn(() => Promise.resolve({ data: 'created' })),
|
|
655
|
+
put: jest.fn(() => Promise.resolve({ data: 'updated' })),
|
|
656
|
+
delete: jest.fn(() => Promise.resolve({ data: 'deleted' }))
|
|
657
|
+
};`;
|
|
658
|
+
}
|
|
659
|
+
async generateTestConfiguration(framework, testSuite, coverage) {
|
|
660
|
+
const configs = {
|
|
661
|
+
jest: {
|
|
662
|
+
configFile: `module.exports = {
|
|
663
|
+
preset: 'ts-jest',
|
|
664
|
+
testEnvironment: 'node',
|
|
665
|
+
collectCoverage: true,
|
|
666
|
+
collectCoverageFrom: [
|
|
667
|
+
'src/**/*.{ts,js}',
|
|
668
|
+
'!src/**/*.d.ts'
|
|
669
|
+
],
|
|
670
|
+
coverageDirectory: 'coverage',
|
|
671
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
|
672
|
+
testMatch: ['**/__tests__/**/*.(ts|js)', '**/*.(test|spec).(ts|js)'],
|
|
673
|
+
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']
|
|
674
|
+
};`,
|
|
675
|
+
scripts: {
|
|
676
|
+
'test': 'jest',
|
|
677
|
+
'test:watch': 'jest --watch',
|
|
678
|
+
'test:coverage': 'jest --coverage'
|
|
679
|
+
},
|
|
680
|
+
dependencies: ['jest', '@types/jest', 'ts-jest']
|
|
681
|
+
},
|
|
682
|
+
vitest: {
|
|
683
|
+
configFile: `import { defineConfig } from 'vitest/config';
|
|
684
|
+
|
|
685
|
+
export default defineConfig({
|
|
686
|
+
test: {
|
|
687
|
+
globals: true,
|
|
688
|
+
environment: 'node',
|
|
689
|
+
coverage: {
|
|
690
|
+
provider: 'c8',
|
|
691
|
+
reporter: ['text', 'json', 'html']
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
});`,
|
|
695
|
+
scripts: {
|
|
696
|
+
'test': 'vitest run',
|
|
697
|
+
'test:watch': 'vitest',
|
|
698
|
+
'test:coverage': 'vitest run --coverage'
|
|
699
|
+
},
|
|
700
|
+
dependencies: ['vitest', '@vitest/ui', 'c8']
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
const config = configs[framework] || configs.jest;
|
|
704
|
+
const coverageThreshold = coverage === 'basic' ? 70 : coverage === 'thorough' ? 85 : 95;
|
|
705
|
+
return {
|
|
706
|
+
framework,
|
|
707
|
+
configFile: config.configFile,
|
|
708
|
+
scripts: config.scripts,
|
|
709
|
+
dependencies: config.dependencies,
|
|
710
|
+
coverage: {
|
|
711
|
+
threshold: coverageThreshold,
|
|
712
|
+
reports: ['text', 'lcov', 'html'],
|
|
713
|
+
directories: ['src/'],
|
|
714
|
+
excludes: ['node_modules/', 'dist/', '*.d.ts']
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
synthesizeTestArchitecture(testSuite, testFiles, mockFiles, testConfig, edgeCases) {
|
|
719
|
+
let synthesis = `## Test Architecture Summary\n\n`;
|
|
720
|
+
// Test Suite Overview
|
|
721
|
+
synthesis += `**Test Suite: ${testSuite.name}**\n`;
|
|
722
|
+
synthesis += `- Framework: ${testSuite.framework}\n`;
|
|
723
|
+
synthesis += `- Total Tests: ${testSuite.totalTests}\n`;
|
|
724
|
+
synthesis += `- Estimated Coverage: ${testSuite.estimatedCoverage}%\n`;
|
|
725
|
+
synthesis += `- Test Categories: ${testSuite.testCategories.length}\n\n`;
|
|
726
|
+
// Test Categories
|
|
727
|
+
synthesis += `**Test Categories:**\n`;
|
|
728
|
+
testSuite.testCategories.forEach(category => {
|
|
729
|
+
synthesis += `- ${category.name}: ${category.testCount} tests (${category.priority} priority)\n`;
|
|
730
|
+
});
|
|
731
|
+
synthesis += '\n';
|
|
732
|
+
// Generated Files
|
|
733
|
+
synthesis += `**Generated Files:**\n`;
|
|
734
|
+
synthesis += `- Test Files: ${testFiles.length}\n`;
|
|
735
|
+
testFiles.forEach(file => {
|
|
736
|
+
synthesis += ` - ${file.fileName} (${file.testType}, ${file.tests.length} tests)\n`;
|
|
737
|
+
});
|
|
738
|
+
if (mockFiles.length > 0) {
|
|
739
|
+
synthesis += `- Mock Files: ${mockFiles.length}\n`;
|
|
740
|
+
mockFiles.forEach(mock => {
|
|
741
|
+
synthesis += ` - ${mock.fileName} (${mock.mockType})\n`;
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
synthesis += '\n';
|
|
745
|
+
// Edge Cases
|
|
746
|
+
const criticalEdgeCases = edgeCases.filter(e => e.severity === 'high').length;
|
|
747
|
+
if (criticalEdgeCases > 0) {
|
|
748
|
+
synthesis += `**Critical Edge Cases: ${criticalEdgeCases}**\n`;
|
|
749
|
+
edgeCases.filter(e => e.severity === 'high').slice(0, 3).forEach(edge => {
|
|
750
|
+
synthesis += `- ${edge.scenario}\n`;
|
|
751
|
+
});
|
|
752
|
+
synthesis += '\n';
|
|
753
|
+
}
|
|
754
|
+
// Setup Instructions
|
|
755
|
+
synthesis += `**Setup Instructions:**\n`;
|
|
756
|
+
testSuite.setupRequirements.forEach(req => {
|
|
757
|
+
synthesis += `1. ${req}\n`;
|
|
758
|
+
});
|
|
759
|
+
synthesis += `\n**Next Steps:**\n`;
|
|
760
|
+
synthesis += `1. Install dependencies: ${testConfig.dependencies.join(', ')}\n`;
|
|
761
|
+
synthesis += `2. Add test scripts to package.json\n`;
|
|
762
|
+
synthesis += `3. Create ${testConfig.framework} configuration file\n`;
|
|
763
|
+
synthesis += `4. Run tests to establish baseline\n`;
|
|
764
|
+
synthesis += `5. Integrate with CI/CD pipeline\n`;
|
|
765
|
+
return synthesis;
|
|
766
|
+
}
|
|
767
|
+
}
|