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,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Generate Command
|
|
3
|
+
*
|
|
4
|
+
* AI-powered test generation using Ollama.
|
|
5
|
+
* Generates tests from OpenAPI specs, HAR files, URLs, or code.
|
|
6
|
+
*
|
|
7
|
+
* Phase 4 - AI Test Generation
|
|
8
|
+
*/
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import ora from 'ora';
|
|
12
|
+
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
13
|
+
import { dirname, resolve } from 'path';
|
|
14
|
+
import { generateTests, generateApiTestsFromOpenAPI, generateUiTestsFromUrl, generatePerfTests, generateUnitTests, checkGenerationAvailability, } from '../core/index.js';
|
|
15
|
+
/**
|
|
16
|
+
* Create generate commands group
|
|
17
|
+
*/
|
|
18
|
+
export function createGenerateCommands() {
|
|
19
|
+
const generateCmd = new Command('generate')
|
|
20
|
+
.description('AI-powered test generation (Phase 4)');
|
|
21
|
+
// Main generate command
|
|
22
|
+
generateCmd
|
|
23
|
+
.argument('<source>', 'Source: OpenAPI spec, HAR file, URL, or description')
|
|
24
|
+
.option('--type <type>', 'Source type: openapi, har, url, code')
|
|
25
|
+
.option('-o, --output <dir>', 'Output directory', './tests/generated')
|
|
26
|
+
.option('--framework <name>', 'Target framework: playwright, k6, vitest, jest')
|
|
27
|
+
.option('--model <name>', 'Ollama model to use')
|
|
28
|
+
.option('--optimize', 'Optimize generated tests')
|
|
29
|
+
.option('--no-format', 'Skip code formatting')
|
|
30
|
+
.action(async (source, options) => {
|
|
31
|
+
await generateCommand(source, options);
|
|
32
|
+
});
|
|
33
|
+
// API tests subcommand
|
|
34
|
+
generateCmd
|
|
35
|
+
.command('api <spec>')
|
|
36
|
+
.description('Generate API tests from OpenAPI spec')
|
|
37
|
+
.option('-o, --output <dir>', 'Output directory', './tests/api')
|
|
38
|
+
.option('--model <name>', 'Ollama model to use')
|
|
39
|
+
.option('--optimize', 'Optimize generated tests')
|
|
40
|
+
.action(async (spec, options) => {
|
|
41
|
+
await generateApiCommand(spec, options);
|
|
42
|
+
});
|
|
43
|
+
// UI tests subcommand
|
|
44
|
+
generateCmd
|
|
45
|
+
.command('ui <url>')
|
|
46
|
+
.description('Generate UI tests from URL')
|
|
47
|
+
.option('-o, --output <dir>', 'Output directory', './tests/ui')
|
|
48
|
+
.option('--model <name>', 'Ollama model to use')
|
|
49
|
+
.option('--optimize', 'Optimize generated tests')
|
|
50
|
+
.action(async (url, options) => {
|
|
51
|
+
await generateUiCommand(url, options);
|
|
52
|
+
});
|
|
53
|
+
// Performance tests subcommand
|
|
54
|
+
generateCmd
|
|
55
|
+
.command('perf <spec>')
|
|
56
|
+
.description('Generate performance tests from OpenAPI spec')
|
|
57
|
+
.option('-o, --output <dir>', 'Output directory', './tests/performance')
|
|
58
|
+
.option('--model <name>', 'Ollama model to use')
|
|
59
|
+
.option('--duration <time>', 'Test duration', '30s')
|
|
60
|
+
.option('--vus <number>', 'Virtual users', '10')
|
|
61
|
+
.action(async (spec, options) => {
|
|
62
|
+
await generatePerfCommand(spec, options);
|
|
63
|
+
});
|
|
64
|
+
// Unit tests subcommand
|
|
65
|
+
generateCmd
|
|
66
|
+
.command('unit <file>')
|
|
67
|
+
.description('Generate unit tests from source code')
|
|
68
|
+
.option('-l, --language <lang>', 'Source language (typescript, javascript, go, python)')
|
|
69
|
+
.option('-o, --output <dir>', 'Output directory', './tests/unit')
|
|
70
|
+
.option('--model <name>', 'Ollama model to use')
|
|
71
|
+
.option('--optimize', 'Optimize generated tests')
|
|
72
|
+
.action(async (file, options) => {
|
|
73
|
+
await generateUnitCommand(file, options);
|
|
74
|
+
});
|
|
75
|
+
// Check availability subcommand
|
|
76
|
+
generateCmd
|
|
77
|
+
.command('check')
|
|
78
|
+
.description('Check if Ollama is available for generation')
|
|
79
|
+
.action(async () => {
|
|
80
|
+
await checkCommand();
|
|
81
|
+
});
|
|
82
|
+
return generateCmd;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Generate tests command handler
|
|
86
|
+
*/
|
|
87
|
+
export async function generateCommand(source, options = {}) {
|
|
88
|
+
const spinner = ora('Initializing QA360 AI Test Generator...').start();
|
|
89
|
+
try {
|
|
90
|
+
// Check Ollama availability
|
|
91
|
+
const { available, models, recommended } = await checkGenerationAvailability();
|
|
92
|
+
if (!available) {
|
|
93
|
+
spinner.fail('Ollama not available');
|
|
94
|
+
console.log(chalk.red('\nOllama is not running'));
|
|
95
|
+
console.log(chalk.yellow('\nStart Ollama:'));
|
|
96
|
+
console.log(chalk.gray(' ollama serve'));
|
|
97
|
+
console.log(chalk.yellow('\nInstall Ollama:'));
|
|
98
|
+
console.log(chalk.gray(' brew install ollama # macOS'));
|
|
99
|
+
console.log(chalk.gray(' # Or visit: https://ollama.com\n'));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
spinner.succeed('Connected to Ollama');
|
|
103
|
+
// Show using model
|
|
104
|
+
console.log(chalk.gray(`Using: ${options.model || recommended}\n`));
|
|
105
|
+
// Determine source type
|
|
106
|
+
const sourceType = options.type || detectSourceType(source);
|
|
107
|
+
// Build generation source
|
|
108
|
+
const generationSource = buildGenerationSource(source, sourceType, options);
|
|
109
|
+
// Generate tests
|
|
110
|
+
const genSpinner = ora(`Generating ${sourceType} tests...`).start();
|
|
111
|
+
const result = await generateTests(generationSource, {
|
|
112
|
+
model: options.model,
|
|
113
|
+
outputDir: options.output || './tests/generated',
|
|
114
|
+
formatCode: options.format !== false,
|
|
115
|
+
optimize: options.optimize,
|
|
116
|
+
promptOptions: {
|
|
117
|
+
includeComments: true,
|
|
118
|
+
includeDetailedAssertions: true,
|
|
119
|
+
includeRetryLogic: true,
|
|
120
|
+
codeStyle: 'clean',
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
if (!result.success) {
|
|
124
|
+
genSpinner.fail('Generation failed');
|
|
125
|
+
console.log(chalk.red('\nErrors:'));
|
|
126
|
+
for (const error of result.errors || []) {
|
|
127
|
+
console.log(chalk.red(` - ${error}`));
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
genSpinner.succeed(`Generated ${result.tests.length} test files\n`);
|
|
132
|
+
// Save tests
|
|
133
|
+
const savedFiles = await saveTests(result.tests, options.output);
|
|
134
|
+
// Show summary
|
|
135
|
+
showGenerationSummary(result, savedFiles);
|
|
136
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
137
|
+
console.log(chalk.yellow('\nWarnings:'));
|
|
138
|
+
for (const warning of result.warnings) {
|
|
139
|
+
console.log(chalk.yellow(` - ${warning}`));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
console.log(chalk.green('\nTest generation complete!'));
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
spinner.fail('Generation error');
|
|
146
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Generate API tests from OpenAPI spec
|
|
151
|
+
*/
|
|
152
|
+
export async function generateApiCommand(specPath, options = {}) {
|
|
153
|
+
const spinner = ora('Generating API tests from OpenAPI spec...').start();
|
|
154
|
+
try {
|
|
155
|
+
const result = await generateApiTestsFromOpenAPI(specPath, {
|
|
156
|
+
model: options.model,
|
|
157
|
+
outputDir: options.output || './tests/api',
|
|
158
|
+
formatCode: true,
|
|
159
|
+
optimize: options.optimize,
|
|
160
|
+
});
|
|
161
|
+
if (!result.success) {
|
|
162
|
+
spinner.fail('Generation failed');
|
|
163
|
+
for (const error of result.errors || []) {
|
|
164
|
+
console.log(chalk.red(` - ${error}`));
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
spinner.succeed(`Generated ${result.tests.length} API test files\n`);
|
|
169
|
+
const savedFiles = await saveTests(result.tests, options.output || './tests/api');
|
|
170
|
+
showGenerationSummary(result, savedFiles);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
spinner.fail('Generation error');
|
|
174
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Generate UI tests from URL
|
|
179
|
+
*/
|
|
180
|
+
export async function generateUiCommand(url, options = {}) {
|
|
181
|
+
const spinner = ora('Generating UI tests from URL...').start();
|
|
182
|
+
try {
|
|
183
|
+
const result = await generateUiTestsFromUrl(url, {
|
|
184
|
+
model: options.model,
|
|
185
|
+
outputDir: options.output || './tests/ui',
|
|
186
|
+
formatCode: true,
|
|
187
|
+
optimize: options.optimize,
|
|
188
|
+
});
|
|
189
|
+
if (!result.success) {
|
|
190
|
+
spinner.fail('Generation failed');
|
|
191
|
+
for (const error of result.errors || []) {
|
|
192
|
+
console.log(chalk.red(` - ${error}`));
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
spinner.succeed(`Generated ${result.tests.length} UI test files\n`);
|
|
197
|
+
const savedFiles = await saveTests(result.tests, options.output || './tests/ui');
|
|
198
|
+
showGenerationSummary(result, savedFiles);
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
spinner.fail('Generation error');
|
|
202
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Generate performance tests
|
|
207
|
+
*/
|
|
208
|
+
export async function generatePerfCommand(specPath, options = {}) {
|
|
209
|
+
const spinner = ora('Generating performance tests...').start();
|
|
210
|
+
try {
|
|
211
|
+
const result = await generatePerfTests(specPath, {
|
|
212
|
+
model: options.model,
|
|
213
|
+
outputDir: options.output || './tests/performance',
|
|
214
|
+
formatCode: true,
|
|
215
|
+
optimize: false, // Don't optimize perf tests
|
|
216
|
+
});
|
|
217
|
+
if (!result.success) {
|
|
218
|
+
spinner.fail('Generation failed');
|
|
219
|
+
for (const error of result.errors || []) {
|
|
220
|
+
console.log(chalk.red(` - ${error}`));
|
|
221
|
+
}
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
spinner.succeed(`Generated ${result.tests.length} performance test files\n`);
|
|
225
|
+
const savedFiles = await saveTests(result.tests, options.output || './tests/performance');
|
|
226
|
+
showGenerationSummary(result, savedFiles);
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
spinner.fail('Generation error');
|
|
230
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Generate unit tests from code
|
|
235
|
+
*/
|
|
236
|
+
export async function generateUnitCommand(filePath, options = {}) {
|
|
237
|
+
const spinner = ora('Generating unit tests...').start();
|
|
238
|
+
try {
|
|
239
|
+
// Read source code
|
|
240
|
+
const { readFileSync } = await import('fs');
|
|
241
|
+
const code = readFileSync(filePath, 'utf-8');
|
|
242
|
+
const language = options.language || detectLanguage(filePath);
|
|
243
|
+
const result = await generateUnitTests(code, language, {
|
|
244
|
+
model: options.model,
|
|
245
|
+
outputDir: options.output || './tests/unit',
|
|
246
|
+
formatCode: true,
|
|
247
|
+
optimize: options.optimize,
|
|
248
|
+
});
|
|
249
|
+
if (!result.success) {
|
|
250
|
+
spinner.fail('Generation failed');
|
|
251
|
+
for (const error of result.errors || []) {
|
|
252
|
+
console.log(chalk.red(` - ${error}`));
|
|
253
|
+
}
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
spinner.succeed(`Generated ${result.tests.length} unit test files\n`);
|
|
257
|
+
const savedFiles = await saveTests(result.tests, options.output || './tests/unit');
|
|
258
|
+
showGenerationSummary(result, savedFiles);
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
spinner.fail('Generation error');
|
|
262
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Detect source type from string
|
|
267
|
+
*/
|
|
268
|
+
function detectSourceType(source) {
|
|
269
|
+
if (source.endsWith('.json') || source.endsWith('.yaml') || source.endsWith('.yml')) {
|
|
270
|
+
return 'openapi';
|
|
271
|
+
}
|
|
272
|
+
if (source.endsWith('.har')) {
|
|
273
|
+
return 'har';
|
|
274
|
+
}
|
|
275
|
+
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
276
|
+
return 'url';
|
|
277
|
+
}
|
|
278
|
+
return 'code';
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Detect language from file path
|
|
282
|
+
*/
|
|
283
|
+
function detectLanguage(filePath) {
|
|
284
|
+
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
285
|
+
const languageMap = {
|
|
286
|
+
'ts': 'typescript',
|
|
287
|
+
'tsx': 'typescript',
|
|
288
|
+
'js': 'javascript',
|
|
289
|
+
'jsx': 'javascript',
|
|
290
|
+
'go': 'go',
|
|
291
|
+
'py': 'python',
|
|
292
|
+
'rs': 'rust',
|
|
293
|
+
};
|
|
294
|
+
return languageMap[ext || ''] || 'typescript';
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Build generation source from input
|
|
298
|
+
*/
|
|
299
|
+
function buildGenerationSource(source, type, options) {
|
|
300
|
+
switch (type) {
|
|
301
|
+
case 'openapi':
|
|
302
|
+
return { type: 'openapi', urlOrPath: source };
|
|
303
|
+
case 'har':
|
|
304
|
+
return { type: 'har', filePath: source };
|
|
305
|
+
case 'url':
|
|
306
|
+
return { type: 'url', url: source };
|
|
307
|
+
case 'code':
|
|
308
|
+
return { type: 'description', description: source };
|
|
309
|
+
default:
|
|
310
|
+
return { type: 'description', description: source };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Save generated tests to files
|
|
315
|
+
*/
|
|
316
|
+
async function saveTests(tests, outputDir) {
|
|
317
|
+
const savedFiles = [];
|
|
318
|
+
for (const test of tests) {
|
|
319
|
+
const filePath = resolve(test.filePath);
|
|
320
|
+
// Create directory if needed
|
|
321
|
+
const dir = dirname(filePath);
|
|
322
|
+
if (!existsSync(dir)) {
|
|
323
|
+
mkdirSync(dir, { recursive: true });
|
|
324
|
+
}
|
|
325
|
+
// Write file
|
|
326
|
+
writeFileSync(filePath, test.code, 'utf-8');
|
|
327
|
+
savedFiles.push(filePath);
|
|
328
|
+
}
|
|
329
|
+
return savedFiles;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Show generation summary
|
|
333
|
+
*/
|
|
334
|
+
function showGenerationSummary(result, savedFiles) {
|
|
335
|
+
console.log(chalk.bold('\nGeneration Summary\n'));
|
|
336
|
+
// Metadata
|
|
337
|
+
console.log(chalk.gray(` Duration: ${result.metadata.duration}ms`));
|
|
338
|
+
console.log(chalk.gray(` Tokens: ${result.metadata.tokensUsed}\n`));
|
|
339
|
+
// Tests
|
|
340
|
+
console.log(chalk.bold(' Generated Tests:'));
|
|
341
|
+
let totalTests = 0;
|
|
342
|
+
let totalAssertions = 0;
|
|
343
|
+
for (const test of result.tests) {
|
|
344
|
+
console.log(chalk.cyan(` - ${test.name}`));
|
|
345
|
+
console.log(chalk.gray(` Framework: ${test.framework}`));
|
|
346
|
+
console.log(chalk.gray(` Language: ${test.language}`));
|
|
347
|
+
console.log(chalk.gray(` Tests: ${test.summary.testCount}`));
|
|
348
|
+
if (test.summary.assertionCount) {
|
|
349
|
+
console.log(chalk.gray(` Assertions: ${test.summary.assertionCount}`));
|
|
350
|
+
}
|
|
351
|
+
console.log(chalk.gray(` Lines: ${test.summary.lines}`));
|
|
352
|
+
console.log(chalk.gray(` File: ${test.filePath}\n`));
|
|
353
|
+
totalTests += test.summary.testCount || 0;
|
|
354
|
+
totalAssertions += test.summary.assertionCount || 0;
|
|
355
|
+
}
|
|
356
|
+
console.log(chalk.bold(' Totals:'));
|
|
357
|
+
console.log(chalk.green(` Files: ${result.tests.length}`));
|
|
358
|
+
console.log(chalk.green(` Test Cases: ${totalTests}`));
|
|
359
|
+
console.log(chalk.green(` Assertions: ${totalAssertions}\n`));
|
|
360
|
+
// Saved files
|
|
361
|
+
console.log(chalk.bold(' Saved Files:'));
|
|
362
|
+
for (const file of savedFiles) {
|
|
363
|
+
console.log(chalk.gray(` - ${file}`));
|
|
364
|
+
}
|
|
365
|
+
console.log('');
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Build system prompt for test type
|
|
369
|
+
*/
|
|
370
|
+
export function buildSystemPrompt(type) {
|
|
371
|
+
const prompts = {
|
|
372
|
+
api: `You are an expert API test generator. Generate comprehensive Playwright API tests in TypeScript.
|
|
373
|
+
|
|
374
|
+
Requirements:
|
|
375
|
+
- Use test.describe() for grouping
|
|
376
|
+
- Include happy path and error cases
|
|
377
|
+
- Add proper assertions for status codes and response schemas
|
|
378
|
+
- Handle authentication properly
|
|
379
|
+
- Use fetch() or axios for HTTP requests`,
|
|
380
|
+
ui: `You are an expert UI/E2E test generator. Generate comprehensive Playwright UI tests in TypeScript.
|
|
381
|
+
|
|
382
|
+
Requirements:
|
|
383
|
+
- Use test.describe() for grouping
|
|
384
|
+
- Include page object pattern where appropriate
|
|
385
|
+
- Add explicit waits (waitForSelector, waitForURL)
|
|
386
|
+
- Test user interactions and assertions
|
|
387
|
+
- Handle dynamic content properly`,
|
|
388
|
+
perf: `You are an expert performance test generator. Generate K6 performance tests in JavaScript.
|
|
389
|
+
|
|
390
|
+
Requirements:
|
|
391
|
+
- Define realistic scenarios
|
|
392
|
+
- Add thresholds for response times
|
|
393
|
+
- Use proper load stages (ramp up, sustain, ramp down)
|
|
394
|
+
- Include error rate checks
|
|
395
|
+
- Model realistic user behavior`,
|
|
396
|
+
unit: `You are an expert unit test generator. Generate Vitest tests in TypeScript.
|
|
397
|
+
|
|
398
|
+
Requirements:
|
|
399
|
+
- Test all functions/classes
|
|
400
|
+
- Include edge cases and error conditions
|
|
401
|
+
- Mock external dependencies
|
|
402
|
+
- Use describe/it blocks
|
|
403
|
+
- Aim for high coverage`,
|
|
404
|
+
};
|
|
405
|
+
return prompts[type] || prompts.api;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Check if generation is available
|
|
409
|
+
*/
|
|
410
|
+
export async function checkCommand() {
|
|
411
|
+
const spinner = ora('Checking AI generation availability...').start();
|
|
412
|
+
try {
|
|
413
|
+
const { available, models, recommended } = await checkGenerationAvailability();
|
|
414
|
+
if (!available) {
|
|
415
|
+
spinner.fail('Ollama not available');
|
|
416
|
+
console.log(chalk.red('\nOllama is not running'));
|
|
417
|
+
console.log(chalk.yellow('\nStart Ollama:'));
|
|
418
|
+
console.log(chalk.gray(' ollama serve'));
|
|
419
|
+
console.log(chalk.yellow('\nInstall Ollama:'));
|
|
420
|
+
console.log(chalk.gray(' brew install ollama # macOS'));
|
|
421
|
+
console.log(chalk.gray(' # Or visit: https://ollama.com\n'));
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
spinner.succeed('AI generation is available!\n');
|
|
425
|
+
console.log(chalk.bold(' Available Models:'));
|
|
426
|
+
for (const model of models) {
|
|
427
|
+
const isRecommended = model === recommended || model.includes(recommended);
|
|
428
|
+
const icon = isRecommended ? '*' : ' ';
|
|
429
|
+
const color = isRecommended ? chalk.green : chalk.gray;
|
|
430
|
+
console.log(color(` ${icon} ${model}`));
|
|
431
|
+
}
|
|
432
|
+
console.log(chalk.green(`\nReady to generate tests using: ${recommended}\n`));
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
spinner.fail('Availability check failed');
|
|
436
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
437
|
+
}
|
|
438
|
+
}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* qa360 init
|
|
6
6
|
* qa360 init --template api-basic
|
|
7
7
|
* qa360 init --output custom-pack.yml
|
|
8
|
+
* qa360 init --beginner # Guided mode for first-time users
|
|
8
9
|
*/
|
|
9
10
|
/**
|
|
10
11
|
* CLI options for init command
|
|
@@ -14,6 +15,7 @@ export interface InitOptions {
|
|
|
14
15
|
output?: string;
|
|
15
16
|
force?: boolean;
|
|
16
17
|
yes?: boolean;
|
|
18
|
+
beginner?: boolean;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Available templates
|
|
@@ -23,40 +25,85 @@ export declare const TEMPLATES: {
|
|
|
23
25
|
name: string;
|
|
24
26
|
description: string;
|
|
25
27
|
gates: string[];
|
|
28
|
+
icon: string;
|
|
26
29
|
};
|
|
27
30
|
'ui-basic': {
|
|
28
31
|
name: string;
|
|
29
32
|
description: string;
|
|
30
33
|
gates: string[];
|
|
34
|
+
icon: string;
|
|
31
35
|
};
|
|
32
36
|
fullstack: {
|
|
33
37
|
name: string;
|
|
34
38
|
description: string;
|
|
35
39
|
gates: string[];
|
|
40
|
+
icon: string;
|
|
36
41
|
};
|
|
37
42
|
security: {
|
|
38
43
|
name: string;
|
|
39
44
|
description: string;
|
|
40
45
|
gates: string[];
|
|
46
|
+
icon: string;
|
|
41
47
|
};
|
|
42
48
|
complete: {
|
|
43
49
|
name: string;
|
|
44
50
|
description: string;
|
|
45
51
|
gates: string[];
|
|
52
|
+
icon: string;
|
|
46
53
|
};
|
|
47
54
|
};
|
|
48
55
|
/**
|
|
49
|
-
* Gate descriptions
|
|
56
|
+
* Gate descriptions with beginner-friendly explanations
|
|
50
57
|
*/
|
|
51
58
|
export declare const GATE_DESCRIPTIONS: {
|
|
52
|
-
api_smoke:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
api_smoke: {
|
|
60
|
+
short: string;
|
|
61
|
+
beginner: string;
|
|
62
|
+
example: string;
|
|
63
|
+
icon: string;
|
|
64
|
+
};
|
|
65
|
+
ui: {
|
|
66
|
+
short: string;
|
|
67
|
+
beginner: string;
|
|
68
|
+
example: string;
|
|
69
|
+
icon: string;
|
|
70
|
+
};
|
|
71
|
+
a11y: {
|
|
72
|
+
short: string;
|
|
73
|
+
beginner: string;
|
|
74
|
+
example: string;
|
|
75
|
+
icon: string;
|
|
76
|
+
};
|
|
77
|
+
perf: {
|
|
78
|
+
short: string;
|
|
79
|
+
beginner: string;
|
|
80
|
+
example: string;
|
|
81
|
+
icon: string;
|
|
82
|
+
};
|
|
83
|
+
sast: {
|
|
84
|
+
short: string;
|
|
85
|
+
beginner: string;
|
|
86
|
+
example: string;
|
|
87
|
+
icon: string;
|
|
88
|
+
};
|
|
89
|
+
dast: {
|
|
90
|
+
short: string;
|
|
91
|
+
beginner: string;
|
|
92
|
+
example: string;
|
|
93
|
+
icon: string;
|
|
94
|
+
};
|
|
95
|
+
secrets: {
|
|
96
|
+
short: string;
|
|
97
|
+
beginner: string;
|
|
98
|
+
example: string;
|
|
99
|
+
icon: string;
|
|
100
|
+
};
|
|
101
|
+
deps: {
|
|
102
|
+
short: string;
|
|
103
|
+
beginner: string;
|
|
104
|
+
example: string;
|
|
105
|
+
icon: string;
|
|
106
|
+
};
|
|
60
107
|
};
|
|
61
108
|
/**
|
|
62
109
|
* Main init command
|