qa360 1.4.5 → 2.0.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/README.md +1 -1
- package/dist/commands/ai.d.ts +41 -0
- package/dist/commands/ai.js +499 -0
- package/dist/commands/ask.js +12 -12
- package/dist/commands/coverage.d.ts +8 -0
- package/dist/commands/coverage.js +252 -0
- package/dist/commands/explain.d.ts +27 -0
- package/dist/commands/explain.js +630 -0
- package/dist/commands/flakiness.d.ts +73 -0
- package/dist/commands/flakiness.js +435 -0
- package/dist/commands/generate.d.ts +66 -0
- package/dist/commands/generate.js +438 -0
- package/dist/commands/init.d.ts +56 -9
- package/dist/commands/init.js +217 -10
- package/dist/commands/monitor.d.ts +27 -0
- package/dist/commands/monitor.js +225 -0
- package/dist/commands/ollama.d.ts +40 -0
- package/dist/commands/ollama.js +301 -0
- package/dist/commands/pack.d.ts +37 -9
- package/dist/commands/pack.js +240 -141
- package/dist/commands/regression.d.ts +8 -0
- package/dist/commands/regression.js +340 -0
- package/dist/commands/repair.d.ts +26 -0
- package/dist/commands/repair.js +307 -0
- package/dist/commands/retry.d.ts +43 -0
- package/dist/commands/retry.js +275 -0
- package/dist/commands/run.d.ts +8 -3
- package/dist/commands/run.js +45 -31
- package/dist/commands/slo.d.ts +8 -0
- package/dist/commands/slo.js +327 -0
- package/dist/core/adapters/playwright-native-api.d.ts +183 -0
- package/dist/core/adapters/playwright-native-api.js +461 -0
- package/dist/core/adapters/playwright-ui.d.ts +7 -0
- package/dist/core/adapters/playwright-ui.js +29 -1
- package/dist/core/ai/anthropic-provider.d.ts +50 -0
- package/dist/core/ai/anthropic-provider.js +211 -0
- package/dist/core/ai/deepseek-provider.d.ts +81 -0
- package/dist/core/ai/deepseek-provider.js +254 -0
- package/dist/core/ai/index.d.ts +60 -0
- package/dist/core/ai/index.js +18 -0
- package/dist/core/ai/llm-client.d.ts +45 -0
- package/dist/core/ai/llm-client.js +7 -0
- package/dist/core/ai/mock-provider.d.ts +49 -0
- package/dist/core/ai/mock-provider.js +121 -0
- package/dist/core/ai/ollama-provider.d.ts +78 -0
- package/dist/core/ai/ollama-provider.js +192 -0
- package/dist/core/ai/openai-provider.d.ts +48 -0
- package/dist/core/ai/openai-provider.js +188 -0
- package/dist/core/ai/provider-factory.d.ts +160 -0
- package/dist/core/ai/provider-factory.js +269 -0
- package/dist/core/auth/api-key-provider.d.ts +16 -0
- package/dist/core/auth/api-key-provider.js +63 -0
- package/dist/core/auth/aws-iam-provider.d.ts +35 -0
- package/dist/core/auth/aws-iam-provider.js +177 -0
- package/dist/core/auth/azure-ad-provider.d.ts +15 -0
- package/dist/core/auth/azure-ad-provider.js +99 -0
- package/dist/core/auth/basic-auth-provider.d.ts +26 -0
- package/dist/core/auth/basic-auth-provider.js +111 -0
- package/dist/core/auth/gcp-adc-provider.d.ts +27 -0
- package/dist/core/auth/gcp-adc-provider.js +126 -0
- package/dist/core/auth/index.d.ts +238 -0
- package/dist/core/auth/index.js +82 -0
- package/dist/core/auth/jwt-provider.d.ts +19 -0
- package/dist/core/auth/jwt-provider.js +160 -0
- package/dist/core/auth/manager.d.ts +84 -0
- package/dist/core/auth/manager.js +230 -0
- package/dist/core/auth/oauth2-provider.d.ts +17 -0
- package/dist/core/auth/oauth2-provider.js +114 -0
- package/dist/core/auth/totp-provider.d.ts +31 -0
- package/dist/core/auth/totp-provider.js +134 -0
- package/dist/core/auth/ui-login-provider.d.ts +26 -0
- package/dist/core/auth/ui-login-provider.js +198 -0
- package/dist/core/cache/index.d.ts +7 -0
- package/dist/core/cache/index.js +6 -0
- package/dist/core/cache/lru-cache.d.ts +203 -0
- package/dist/core/cache/lru-cache.js +397 -0
- package/dist/core/coverage/analyzer.d.ts +101 -0
- package/dist/core/coverage/analyzer.js +415 -0
- package/dist/core/coverage/collector.d.ts +74 -0
- package/dist/core/coverage/collector.js +459 -0
- package/dist/core/coverage/config.d.ts +37 -0
- package/dist/core/coverage/config.js +156 -0
- package/dist/core/coverage/index.d.ts +11 -0
- package/dist/core/coverage/index.js +15 -0
- package/dist/core/coverage/types.d.ts +267 -0
- package/dist/core/coverage/types.js +6 -0
- package/dist/core/coverage/vault.d.ts +95 -0
- package/dist/core/coverage/vault.js +405 -0
- package/dist/core/dashboard/assets.d.ts +6 -0
- package/dist/core/dashboard/assets.js +690 -0
- package/dist/core/dashboard/index.d.ts +6 -0
- package/dist/core/dashboard/index.js +5 -0
- package/dist/core/dashboard/server.d.ts +72 -0
- package/dist/core/dashboard/server.js +354 -0
- package/dist/core/dashboard/types.d.ts +70 -0
- package/dist/core/dashboard/types.js +5 -0
- package/dist/core/discoverer/index.d.ts +115 -0
- package/dist/core/discoverer/index.js +250 -0
- package/dist/core/flakiness/index.d.ts +228 -0
- package/dist/core/flakiness/index.js +384 -0
- package/dist/core/generation/code-formatter.d.ts +111 -0
- package/dist/core/generation/code-formatter.js +307 -0
- package/dist/core/generation/code-generator.d.ts +144 -0
- package/dist/core/generation/code-generator.js +293 -0
- package/dist/core/generation/generator.d.ts +40 -0
- package/dist/core/generation/generator.js +76 -0
- package/dist/core/generation/index.d.ts +30 -0
- package/dist/core/generation/index.js +28 -0
- package/dist/core/generation/pack-generator.d.ts +107 -0
- package/dist/core/generation/pack-generator.js +416 -0
- package/dist/core/generation/prompt-builder.d.ts +132 -0
- package/dist/core/generation/prompt-builder.js +672 -0
- package/dist/core/generation/source-analyzer.d.ts +213 -0
- package/dist/core/generation/source-analyzer.js +657 -0
- package/dist/core/generation/test-optimizer.d.ts +117 -0
- package/dist/core/generation/test-optimizer.js +328 -0
- package/dist/core/generation/types.d.ts +214 -0
- package/dist/core/generation/types.js +4 -0
- package/dist/core/index.d.ts +23 -1
- package/dist/core/index.js +39 -0
- package/dist/core/pack/validator.js +31 -1
- package/dist/core/pack-v2/index.d.ts +9 -0
- package/dist/core/pack-v2/index.js +8 -0
- package/dist/core/pack-v2/loader.d.ts +62 -0
- package/dist/core/pack-v2/loader.js +231 -0
- package/dist/core/pack-v2/migrator.d.ts +56 -0
- package/dist/core/pack-v2/migrator.js +455 -0
- package/dist/core/pack-v2/validator.d.ts +61 -0
- package/dist/core/pack-v2/validator.js +577 -0
- package/dist/core/regression/detector.d.ts +107 -0
- package/dist/core/regression/detector.js +497 -0
- package/dist/core/regression/index.d.ts +9 -0
- package/dist/core/regression/index.js +11 -0
- package/dist/core/regression/trend-analyzer.d.ts +102 -0
- package/dist/core/regression/trend-analyzer.js +345 -0
- package/dist/core/regression/types.d.ts +222 -0
- package/dist/core/regression/types.js +7 -0
- package/dist/core/regression/vault.d.ts +87 -0
- package/dist/core/regression/vault.js +289 -0
- package/dist/core/repair/engine/fixer.d.ts +24 -0
- package/dist/core/repair/engine/fixer.js +226 -0
- package/dist/core/repair/engine/suggestion-engine.d.ts +18 -0
- package/dist/core/repair/engine/suggestion-engine.js +187 -0
- package/dist/core/repair/index.d.ts +10 -0
- package/dist/core/repair/index.js +13 -0
- package/dist/core/repair/repairer.d.ts +90 -0
- package/dist/core/repair/repairer.js +284 -0
- package/dist/core/repair/types.d.ts +91 -0
- package/dist/core/repair/types.js +6 -0
- package/dist/core/repair/utils/error-analyzer.d.ts +28 -0
- package/dist/core/repair/utils/error-analyzer.js +264 -0
- package/dist/core/retry/flakiness-integration.d.ts +60 -0
- package/dist/core/retry/flakiness-integration.js +228 -0
- package/dist/core/retry/index.d.ts +14 -0
- package/dist/core/retry/index.js +16 -0
- package/dist/core/retry/retry-engine.d.ts +80 -0
- package/dist/core/retry/retry-engine.js +296 -0
- package/dist/core/retry/types.d.ts +178 -0
- package/dist/core/retry/types.js +52 -0
- package/dist/core/retry/vault.d.ts +77 -0
- package/dist/core/retry/vault.js +304 -0
- package/dist/core/runner/e2e-helpers.d.ts +102 -0
- package/dist/core/runner/e2e-helpers.js +153 -0
- package/dist/core/runner/phase3-runner.d.ts +101 -2
- package/dist/core/runner/phase3-runner.js +559 -24
- package/dist/core/self-healing/assertion-healer.d.ts +97 -0
- package/dist/core/self-healing/assertion-healer.js +371 -0
- package/dist/core/self-healing/engine.d.ts +122 -0
- package/dist/core/self-healing/engine.js +538 -0
- package/dist/core/self-healing/index.d.ts +10 -0
- package/dist/core/self-healing/index.js +11 -0
- package/dist/core/self-healing/selector-healer.d.ts +103 -0
- package/dist/core/self-healing/selector-healer.js +372 -0
- package/dist/core/self-healing/types.d.ts +152 -0
- package/dist/core/self-healing/types.js +6 -0
- package/dist/core/slo/config.d.ts +107 -0
- package/dist/core/slo/config.js +360 -0
- package/dist/core/slo/index.d.ts +11 -0
- package/dist/core/slo/index.js +15 -0
- package/dist/core/slo/sli-calculator.d.ts +92 -0
- package/dist/core/slo/sli-calculator.js +364 -0
- package/dist/core/slo/slo-tracker.d.ts +148 -0
- package/dist/core/slo/slo-tracker.js +379 -0
- package/dist/core/slo/types.d.ts +281 -0
- package/dist/core/slo/types.js +7 -0
- package/dist/core/slo/vault.d.ts +102 -0
- package/dist/core/slo/vault.js +427 -0
- package/dist/core/tui/index.d.ts +7 -0
- package/dist/core/tui/index.js +6 -0
- package/dist/core/tui/monitor.d.ts +92 -0
- package/dist/core/tui/monitor.js +271 -0
- package/dist/core/tui/renderer.d.ts +33 -0
- package/dist/core/tui/renderer.js +218 -0
- package/dist/core/tui/types.d.ts +63 -0
- package/dist/core/tui/types.js +5 -0
- package/dist/core/types/pack-v2.d.ts +425 -0
- package/dist/core/types/pack-v2.js +8 -0
- package/dist/core/vault/index.d.ts +116 -0
- package/dist/core/vault/index.js +400 -5
- package/dist/core/watch/index.d.ts +7 -0
- package/dist/core/watch/index.js +6 -0
- package/dist/core/watch/watch-mode.d.ts +213 -0
- package/dist/core/watch/watch-mode.js +389 -0
- package/dist/index.js +68 -68
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.js +136 -0
- package/package.json +5 -1
- package/dist/core/adapters/playwright-api.d.ts +0 -82
- package/dist/core/adapters/playwright-api.js +0 -264
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Code Formatter
|
|
3
|
+
*
|
|
4
|
+
* Formats and validates generated test code.
|
|
5
|
+
* Supports TypeScript, JavaScript, Go, and Python.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Code Formatter class
|
|
9
|
+
*/
|
|
10
|
+
export class CodeFormatter {
|
|
11
|
+
config;
|
|
12
|
+
defaultConfig = {
|
|
13
|
+
indentStyle: 'spaces',
|
|
14
|
+
indentSize: 2,
|
|
15
|
+
lineWidth: 120,
|
|
16
|
+
semicolons: true,
|
|
17
|
+
quotes: 'single',
|
|
18
|
+
trailingCommas: true,
|
|
19
|
+
sortImports: true,
|
|
20
|
+
};
|
|
21
|
+
constructor(config = {}) {
|
|
22
|
+
this.config = { ...this.defaultConfig, ...config };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Format code based on language
|
|
26
|
+
*/
|
|
27
|
+
format(code, language) {
|
|
28
|
+
switch (language.toLowerCase()) {
|
|
29
|
+
case 'typescript':
|
|
30
|
+
case 'ts':
|
|
31
|
+
return this.formatTypeScript(code);
|
|
32
|
+
case 'javascript':
|
|
33
|
+
case 'js':
|
|
34
|
+
return this.formatJavaScript(code);
|
|
35
|
+
case 'go':
|
|
36
|
+
return this.formatGo(code);
|
|
37
|
+
case 'python':
|
|
38
|
+
case 'py':
|
|
39
|
+
return this.formatPython(code);
|
|
40
|
+
default:
|
|
41
|
+
return this.formatGeneric(code);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Format TypeScript code
|
|
46
|
+
*/
|
|
47
|
+
formatTypeScript(code) {
|
|
48
|
+
let formatted = code;
|
|
49
|
+
// Sort imports if enabled
|
|
50
|
+
if (this.config.sortImports) {
|
|
51
|
+
formatted = this.sortImports(formatted);
|
|
52
|
+
}
|
|
53
|
+
// Normalize quotes
|
|
54
|
+
formatted = this.normalizeQuotes(formatted);
|
|
55
|
+
// Normalize spacing around braces
|
|
56
|
+
formatted = this.normalizeBraces(formatted);
|
|
57
|
+
// Fix common formatting issues
|
|
58
|
+
formatted = this.fixCommonIssues(formatted);
|
|
59
|
+
return formatted;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Format JavaScript code
|
|
63
|
+
*/
|
|
64
|
+
formatJavaScript(code) {
|
|
65
|
+
// Same as TypeScript for now
|
|
66
|
+
return this.formatTypeScript(code);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Format Go code
|
|
70
|
+
*/
|
|
71
|
+
formatGo(code) {
|
|
72
|
+
let formatted = code;
|
|
73
|
+
// Go uses tabs, normalize indentation
|
|
74
|
+
if (this.config.indentStyle === 'spaces') {
|
|
75
|
+
// Convert spaces to tabs for Go (Go convention)
|
|
76
|
+
formatted = formatted.replace(/^( {2,})/gm, (match) => {
|
|
77
|
+
return '\t'.repeat(match.length / 2);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// Remove trailing whitespace
|
|
81
|
+
formatted = formatted.replace(/[ \t]+$/gm, '');
|
|
82
|
+
// Ensure file ends with newline
|
|
83
|
+
if (!formatted.endsWith('\n')) {
|
|
84
|
+
formatted += '\n';
|
|
85
|
+
}
|
|
86
|
+
return formatted;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Format Python code
|
|
90
|
+
*/
|
|
91
|
+
formatPython(code) {
|
|
92
|
+
let formatted = code;
|
|
93
|
+
// Python uses 4 spaces by convention
|
|
94
|
+
// Normalize indentation to 4 spaces
|
|
95
|
+
formatted = this.normalizePythonIndentation(formatted);
|
|
96
|
+
// Remove trailing whitespace
|
|
97
|
+
formatted = formatted.replace(/[ \t]+$/gm, '');
|
|
98
|
+
// Ensure file ends with newline
|
|
99
|
+
if (!formatted.endsWith('\n')) {
|
|
100
|
+
formatted += '\n';
|
|
101
|
+
}
|
|
102
|
+
return formatted;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Generic formatting
|
|
106
|
+
*/
|
|
107
|
+
formatGeneric(code) {
|
|
108
|
+
let formatted = code;
|
|
109
|
+
// Remove trailing whitespace
|
|
110
|
+
formatted = formatted.replace(/[ \t]+$/gm, '');
|
|
111
|
+
// Ensure file ends with newline
|
|
112
|
+
if (!formatted.endsWith('\n')) {
|
|
113
|
+
formatted += '\n';
|
|
114
|
+
}
|
|
115
|
+
return formatted;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Validate code syntax
|
|
119
|
+
*/
|
|
120
|
+
validate(code, language) {
|
|
121
|
+
const errors = [];
|
|
122
|
+
const warnings = [];
|
|
123
|
+
// Check for common issues
|
|
124
|
+
if (!code.trim()) {
|
|
125
|
+
errors.push('Code is empty');
|
|
126
|
+
}
|
|
127
|
+
// Check for balanced braces
|
|
128
|
+
const openBraces = (code.match(/\{/g) || []).length;
|
|
129
|
+
const closeBraces = (code.match(/\}/g) || []).length;
|
|
130
|
+
if (openBraces !== closeBraces) {
|
|
131
|
+
errors.push(`Unbalanced braces: ${openBraces} open, ${closeBraces} close`);
|
|
132
|
+
}
|
|
133
|
+
// Check for balanced parentheses
|
|
134
|
+
const openParens = (code.match(/\(/g) || []).length;
|
|
135
|
+
const closeParens = (code.match(/\)/g) || []).length;
|
|
136
|
+
if (openParens !== closeParens) {
|
|
137
|
+
errors.push(`Unbalanced parentheses: ${openParens} open, ${closeParens} close`);
|
|
138
|
+
}
|
|
139
|
+
// Check for balanced brackets
|
|
140
|
+
const openBrackets = (code.match(/\[/g) || []).length;
|
|
141
|
+
const closeBrackets = (code.match(/\]/g) || []).length;
|
|
142
|
+
if (openBrackets !== closeBrackets) {
|
|
143
|
+
errors.push(`Unbalanced brackets: ${openBrackets} open, ${closeBrackets} close`);
|
|
144
|
+
}
|
|
145
|
+
// Language-specific checks
|
|
146
|
+
if (language === 'typescript' || language === 'javascript') {
|
|
147
|
+
// Check for TODO comments
|
|
148
|
+
const todos = code.match(/TODO|FIXME|XXX/gi);
|
|
149
|
+
if (todos) {
|
|
150
|
+
warnings.push(`Found ${todos.length} TODO/FIXME comments`);
|
|
151
|
+
}
|
|
152
|
+
// Check for console.log
|
|
153
|
+
const consoleLogs = code.match(/console\.log/g);
|
|
154
|
+
if (consoleLogs && consoleLogs.length > 2) {
|
|
155
|
+
warnings.push(`Found ${consoleLogs.length} console.log statements`);
|
|
156
|
+
}
|
|
157
|
+
// Check for any declarations
|
|
158
|
+
if (code.includes(' any')) {
|
|
159
|
+
warnings.push('Using "any" type - consider specific types');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
formatted: this.format(code, language),
|
|
164
|
+
errors,
|
|
165
|
+
warnings,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Sort imports
|
|
170
|
+
*/
|
|
171
|
+
sortImports(code) {
|
|
172
|
+
const lines = code.split('\n');
|
|
173
|
+
const importSection = [];
|
|
174
|
+
const rest = [];
|
|
175
|
+
let inImports = false;
|
|
176
|
+
let afterImports = false;
|
|
177
|
+
for (const line of lines) {
|
|
178
|
+
const trimmed = line.trim();
|
|
179
|
+
if (trimmed.startsWith('import ')) {
|
|
180
|
+
inImports = true;
|
|
181
|
+
importSection.push(line);
|
|
182
|
+
}
|
|
183
|
+
else if (inImports && (trimmed.startsWith('//') || trimmed === '')) {
|
|
184
|
+
importSection.push(line);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
if (inImports) {
|
|
188
|
+
afterImports = true;
|
|
189
|
+
}
|
|
190
|
+
inImports = false;
|
|
191
|
+
rest.push(line);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Sort imports alphabetically
|
|
195
|
+
const sortedImports = this.sortImportLines(importSection);
|
|
196
|
+
return [...sortedImports, ...rest].join('\n');
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Sort import lines
|
|
200
|
+
*/
|
|
201
|
+
sortImportLines(importLines) {
|
|
202
|
+
// Separate by type: npm imports, relative imports, blank lines/comments
|
|
203
|
+
const npmImports = [];
|
|
204
|
+
const relativeImports = [];
|
|
205
|
+
const others = [];
|
|
206
|
+
for (const line of importLines) {
|
|
207
|
+
const trimmed = line.trim();
|
|
208
|
+
if (trimmed.startsWith('//') || trimmed === '') {
|
|
209
|
+
others.push(line);
|
|
210
|
+
}
|
|
211
|
+
else if (trimmed.includes("from '")) {
|
|
212
|
+
const fromMatch = trimmed.match(/from\s+['"]([^'"]+)['"]/);
|
|
213
|
+
if (fromMatch) {
|
|
214
|
+
const from = fromMatch[1];
|
|
215
|
+
if (from.startsWith('.') || from.startsWith('/')) {
|
|
216
|
+
relativeImports.push(line);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
npmImports.push(line);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
others.push(line);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Sort each group
|
|
228
|
+
npmImports.sort();
|
|
229
|
+
relativeImports.sort();
|
|
230
|
+
return [...others, ...npmImports, ...relativeImports];
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Normalize quotes
|
|
234
|
+
*/
|
|
235
|
+
normalizeQuotes(code) {
|
|
236
|
+
if (this.config.quotes === 'single') {
|
|
237
|
+
// Replace double quotes with single (except for escaped quotes)
|
|
238
|
+
return code.replace(/"([^"\\]*(\\.[^"\\]*)*)"/g, (_, content) => {
|
|
239
|
+
// Check if there are escaped single quotes inside
|
|
240
|
+
if (content.includes("\\'")) {
|
|
241
|
+
return `"${content}"`;
|
|
242
|
+
}
|
|
243
|
+
return `'${content.replace(/"/g, '\\"')}'`;
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// Replace single quotes with double
|
|
248
|
+
return code.replace(/'([^'\\]*(\\.[^'\\]*)*)'/g, (_, content) => {
|
|
249
|
+
if (content.includes('\\"')) {
|
|
250
|
+
return `'${content}'`;
|
|
251
|
+
}
|
|
252
|
+
return `"${content.replace(/'/g, "\\'")}"`;
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Normalize braces spacing
|
|
258
|
+
*/
|
|
259
|
+
normalizeBraces(code) {
|
|
260
|
+
// Ensure consistent spacing around braces
|
|
261
|
+
// Function declarations: function name() {
|
|
262
|
+
code = code.replace(/\s*\{\s*/g, ' {\n');
|
|
263
|
+
code = code.replace(/\s*\}\s*/g, '\n}\n');
|
|
264
|
+
return code;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Fix common formatting issues
|
|
268
|
+
*/
|
|
269
|
+
fixCommonIssues(code) {
|
|
270
|
+
let fixed = code;
|
|
271
|
+
// Remove multiple consecutive blank lines
|
|
272
|
+
fixed = fixed.replace(/\n{3,}/g, '\n\n');
|
|
273
|
+
// Remove trailing whitespace
|
|
274
|
+
fixed = fixed.replace(/[ \t]+$/gm, '');
|
|
275
|
+
// Ensure file ends with newline
|
|
276
|
+
if (!fixed.endsWith('\n')) {
|
|
277
|
+
fixed += '\n';
|
|
278
|
+
}
|
|
279
|
+
return fixed;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Normalize Python indentation
|
|
283
|
+
*/
|
|
284
|
+
normalizePythonIndentation(code) {
|
|
285
|
+
const lines = code.split('\n');
|
|
286
|
+
const result = [];
|
|
287
|
+
for (const line of lines) {
|
|
288
|
+
const trimmed = line.trim();
|
|
289
|
+
if (trimmed === '') {
|
|
290
|
+
result.push('');
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
// Count leading spaces
|
|
294
|
+
const leadingSpaces = line.match(/^(\s*)/)?.[0]?.length || 0;
|
|
295
|
+
const indentLevel = Math.ceil(leadingSpaces / 2);
|
|
296
|
+
// Python uses 4 spaces per indent level
|
|
297
|
+
result.push(' '.repeat(indentLevel * 4) + trimmed);
|
|
298
|
+
}
|
|
299
|
+
return result.join('\n');
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Create a formatter with default config
|
|
304
|
+
*/
|
|
305
|
+
export function createFormatter(config) {
|
|
306
|
+
return new CodeFormatter(config);
|
|
307
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Code Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates test code using LLM providers.
|
|
5
|
+
* Orchestrates prompt building and LLM calls.
|
|
6
|
+
*/
|
|
7
|
+
import { type OllamaConfig } from '../ai/index.js';
|
|
8
|
+
import type { TestSpec, GenerationSource, GeneratedTest, GenerationContext } from './types.js';
|
|
9
|
+
export type { GenerationSource, GeneratedTest };
|
|
10
|
+
export interface GenerationResult {
|
|
11
|
+
success: boolean;
|
|
12
|
+
tests: GeneratedTest[];
|
|
13
|
+
errors?: string[];
|
|
14
|
+
warnings?: string[];
|
|
15
|
+
metadata: {
|
|
16
|
+
duration: number;
|
|
17
|
+
model: string;
|
|
18
|
+
tokensUsed: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Code generator options
|
|
23
|
+
*/
|
|
24
|
+
export interface CodeGeneratorOptions {
|
|
25
|
+
/**
|
|
26
|
+
* LLM provider configuration
|
|
27
|
+
*/
|
|
28
|
+
ollama?: OllamaConfig;
|
|
29
|
+
/**
|
|
30
|
+
* Model to use
|
|
31
|
+
*/
|
|
32
|
+
model?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Temperature for generation (0-1)
|
|
35
|
+
*/
|
|
36
|
+
temperature?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Maximum tokens to generate
|
|
39
|
+
*/
|
|
40
|
+
maxTokens?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Timeout for LLM generation in milliseconds (default: 30000)
|
|
43
|
+
*/
|
|
44
|
+
timeout?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Format the generated code
|
|
47
|
+
*/
|
|
48
|
+
formatCode?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Optimize generated tests
|
|
51
|
+
*/
|
|
52
|
+
optimize?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Output directory for generated files
|
|
55
|
+
*/
|
|
56
|
+
outputDir?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Prompt builder options
|
|
59
|
+
*/
|
|
60
|
+
promptOptions?: {
|
|
61
|
+
includeComments?: boolean;
|
|
62
|
+
includeDetailedAssertions?: boolean;
|
|
63
|
+
includeRetryLogic?: boolean;
|
|
64
|
+
codeStyle?: 'clean' | 'verbose' | 'concise';
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generated test (internal)
|
|
69
|
+
*/
|
|
70
|
+
export interface GeneratedTestInternal extends GeneratedTest {
|
|
71
|
+
rawCode?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Code Generator class
|
|
75
|
+
*/
|
|
76
|
+
export declare class CodeGenerator {
|
|
77
|
+
private readonly provider;
|
|
78
|
+
private readonly formatter;
|
|
79
|
+
private readonly options;
|
|
80
|
+
private readonly defaultOptions;
|
|
81
|
+
constructor(options?: CodeGeneratorOptions);
|
|
82
|
+
/**
|
|
83
|
+
* Generate tests from a source
|
|
84
|
+
*/
|
|
85
|
+
generateFromSource(source: GenerationSource, context: Partial<GenerationContext>): Promise<GenerationResult>;
|
|
86
|
+
/**
|
|
87
|
+
* Wrap a promise with timeout
|
|
88
|
+
* Throws QA360TimeoutError on timeout
|
|
89
|
+
*/
|
|
90
|
+
private withTimeout;
|
|
91
|
+
/**
|
|
92
|
+
* Generate tests from OpenAPI spec
|
|
93
|
+
*/
|
|
94
|
+
generateFromOpenAPI(specPath: string, context?: Partial<GenerationContext>): Promise<GenerationResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Generate tests from HAR file
|
|
97
|
+
*/
|
|
98
|
+
generateFromHAR(filePath: string, context?: Partial<GenerationContext>): Promise<GenerationResult>;
|
|
99
|
+
/**
|
|
100
|
+
* Generate tests from URL
|
|
101
|
+
*/
|
|
102
|
+
generateFromUrl(url: string, context?: Partial<GenerationContext>): Promise<GenerationResult>;
|
|
103
|
+
/**
|
|
104
|
+
* Generate tests from spec
|
|
105
|
+
*/
|
|
106
|
+
generateFromSpec(spec: TestSpec, context?: Partial<GenerationContext>): Promise<GenerationResult>;
|
|
107
|
+
/**
|
|
108
|
+
* Generate tests from description
|
|
109
|
+
*/
|
|
110
|
+
generateFromDescription(description: string, context?: Partial<GenerationContext>): Promise<GenerationResult>;
|
|
111
|
+
/**
|
|
112
|
+
* Generate tests from code
|
|
113
|
+
*/
|
|
114
|
+
generateFromCode(code: string, language: string, context?: Partial<GenerationContext>): Promise<GenerationResult>;
|
|
115
|
+
/**
|
|
116
|
+
* Extract code from LLM response
|
|
117
|
+
* Handles markdown code blocks and plain code
|
|
118
|
+
*/
|
|
119
|
+
private extractCode;
|
|
120
|
+
/**
|
|
121
|
+
* Parse generated tests into separate files
|
|
122
|
+
*/
|
|
123
|
+
private parseGeneratedTests;
|
|
124
|
+
/**
|
|
125
|
+
* Summarize code
|
|
126
|
+
*/
|
|
127
|
+
private summarizeCode;
|
|
128
|
+
/**
|
|
129
|
+
* Get file path for generated test
|
|
130
|
+
*/
|
|
131
|
+
private getFilePath;
|
|
132
|
+
/**
|
|
133
|
+
* Check if generator is available
|
|
134
|
+
*/
|
|
135
|
+
isAvailable(): Promise<boolean>;
|
|
136
|
+
/**
|
|
137
|
+
* List available models
|
|
138
|
+
*/
|
|
139
|
+
listModels(): Promise<string[]>;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Convenience function to generate tests
|
|
143
|
+
*/
|
|
144
|
+
export declare function generateTests(source: GenerationSource, options?: CodeGeneratorOptions): Promise<GenerationResult>;
|