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
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/qa360)
|
|
6
6
|
[](https://github.com/xyqotech/qa360/blob/main/LICENSE)
|
|
7
7
|
[](https://github.com/xyqotech/qa360)
|
|
8
|
-
[](https://github.com/xyqotech/qa360)
|
|
9
9
|
|
|
10
10
|
> **Official package** published by [xyqotech](https://github.com/xyqotech) with npm provenance signatures
|
|
11
11
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 AI Command
|
|
3
|
+
*
|
|
4
|
+
* Manage AI provider integration for test generation.
|
|
5
|
+
* Supports DeepSeek, Ollama, OpenAI, Anthropic with automatic selection.
|
|
6
|
+
*
|
|
7
|
+
* DeepSeek is recommended for best value (GPT-4 level at 95% lower cost).
|
|
8
|
+
*/
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
/**
|
|
11
|
+
* List all available AI providers
|
|
12
|
+
*/
|
|
13
|
+
export declare function aiListCommand(): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Use a specific AI provider
|
|
16
|
+
*/
|
|
17
|
+
export declare function aiUseCommand(provider: string, options?: {
|
|
18
|
+
verify?: boolean;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Benchmark all available providers
|
|
22
|
+
*/
|
|
23
|
+
export declare function aiBenchmarkCommand(options?: {
|
|
24
|
+
prompt?: string;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Generate tests using the best available provider
|
|
28
|
+
*/
|
|
29
|
+
export declare function aiGenerateCommand(prompt: string, options?: {
|
|
30
|
+
provider?: string;
|
|
31
|
+
type?: string;
|
|
32
|
+
json?: boolean;
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Show AI configuration
|
|
36
|
+
*/
|
|
37
|
+
export declare function aiConfigCommand(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Create AI commands
|
|
40
|
+
*/
|
|
41
|
+
export declare function createAICommands(): Command;
|
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 AI Command
|
|
3
|
+
*
|
|
4
|
+
* Manage AI provider integration for test generation.
|
|
5
|
+
* Supports DeepSeek, Ollama, OpenAI, Anthropic with automatic selection.
|
|
6
|
+
*
|
|
7
|
+
* DeepSeek is recommended for best value (GPT-4 level at 95% lower cost).
|
|
8
|
+
*/
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import ora from 'ora';
|
|
12
|
+
import Table from 'cli-table3';
|
|
13
|
+
import { createBest, getProviderInfo, getProviderStatus, createLLMProvider, DeepSeekProvider, DeepSeekError, OllamaProvider, OllamaError, OpenAIProvider, OpenAIError, AnthropicProvider, AnthropicError } from '../core/index.js';
|
|
14
|
+
/**
|
|
15
|
+
* List all available AI providers
|
|
16
|
+
*/
|
|
17
|
+
export async function aiListCommand() {
|
|
18
|
+
const spinner = ora('Checking AI providers...').start();
|
|
19
|
+
try {
|
|
20
|
+
const providers = await getProviderInfo();
|
|
21
|
+
spinner.succeed('AI Providers Status:\n');
|
|
22
|
+
// Create table for display
|
|
23
|
+
const table = new Table({
|
|
24
|
+
head: [chalk.bold('Provider'), chalk.bold('Status'), chalk.bold('Cost'), chalk.bold('Description')],
|
|
25
|
+
colWidths: [15, 10, 10, 40],
|
|
26
|
+
style: {
|
|
27
|
+
head: ['cyan', 'bold']
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
for (const provider of providers) {
|
|
31
|
+
const status = provider.available
|
|
32
|
+
? chalk.green('✓ Available')
|
|
33
|
+
: chalk.gray('✗ Not set up');
|
|
34
|
+
const costLevel = {
|
|
35
|
+
free: chalk.green('FREE'),
|
|
36
|
+
low: chalk.yellow('LOW'),
|
|
37
|
+
medium: chalk.yellow('MED'),
|
|
38
|
+
high: chalk.red('HIGH')
|
|
39
|
+
}[provider.costLevel];
|
|
40
|
+
table.push([
|
|
41
|
+
chalk.bold(provider.name),
|
|
42
|
+
status,
|
|
43
|
+
costLevel,
|
|
44
|
+
provider.description
|
|
45
|
+
]);
|
|
46
|
+
}
|
|
47
|
+
console.log(table.toString());
|
|
48
|
+
// Show setup instructions for unavailable providers
|
|
49
|
+
const unavailable = providers.filter(p => !p.available && p.type !== 'mock');
|
|
50
|
+
if (unavailable.length > 0) {
|
|
51
|
+
console.log(chalk.yellow('\n[INFO] Setup available providers:\n'));
|
|
52
|
+
if (!providers.find(p => p.type === 'deepseek')?.available) {
|
|
53
|
+
console.log(chalk.cyan('DeepSeek (Recommended - Best Value):'));
|
|
54
|
+
console.log(chalk.gray(' 1. Get API key: https://platform.deepseek.com/api_keys'));
|
|
55
|
+
console.log(chalk.gray(' 2. Run: qa360 secrets add DEEPSEEK_API_KEY\n'));
|
|
56
|
+
}
|
|
57
|
+
if (!providers.find(p => p.type === 'ollama')?.available) {
|
|
58
|
+
console.log(chalk.cyan('Ollama (Free - Local):'));
|
|
59
|
+
console.log(chalk.gray(' 1. Install: brew install ollama'));
|
|
60
|
+
console.log(chalk.gray(' 2. Run: ollama serve'));
|
|
61
|
+
console.log(chalk.gray(' 3. Pull model: ollama pull deepseek-coder\n'));
|
|
62
|
+
}
|
|
63
|
+
if (!providers.find(p => p.type === 'openai')?.available) {
|
|
64
|
+
console.log(chalk.cyan('OpenAI:'));
|
|
65
|
+
console.log(chalk.gray(' 1. Get API key: https://platform.openai.com/api-keys'));
|
|
66
|
+
console.log(chalk.gray(' 2. Run: qa360 secrets add OPENAI_API_KEY\n'));
|
|
67
|
+
}
|
|
68
|
+
if (!providers.find(p => p.type === 'anthropic')?.available) {
|
|
69
|
+
console.log(chalk.cyan('Anthropic:'));
|
|
70
|
+
console.log(chalk.gray(' 1. Get API key: https://console.anthropic.com/'));
|
|
71
|
+
console.log(chalk.gray(' 2. Run: qa360 secrets add ANTHROPIC_API_KEY\n'));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Show best provider
|
|
75
|
+
const bestProvider = providers.find(p => p.available && p.type !== 'mock');
|
|
76
|
+
if (bestProvider) {
|
|
77
|
+
console.log(chalk.green(`\n[STAR] Best available: ${chalk.bold(bestProvider.name)}`));
|
|
78
|
+
console.log(chalk.gray(` ${bestProvider.description}`));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.log(chalk.red('\n[!] No AI providers available. Set up one above to use AI features.\n'));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
spinner.fail('Failed to check providers');
|
|
86
|
+
console.error(chalk.red('Error:'), error.message);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Use a specific AI provider
|
|
91
|
+
*/
|
|
92
|
+
export async function aiUseCommand(provider, options = {}) {
|
|
93
|
+
const spinner = ora(`Testing ${provider}...`).start();
|
|
94
|
+
try {
|
|
95
|
+
let available = false;
|
|
96
|
+
let error = '';
|
|
97
|
+
switch (provider.toLowerCase()) {
|
|
98
|
+
case 'deepseek': {
|
|
99
|
+
const p = new DeepSeekProvider();
|
|
100
|
+
available = await p.isAvailable();
|
|
101
|
+
if (!available)
|
|
102
|
+
error = 'DEEPSEEK_API_KEY not set or invalid';
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case 'ollama': {
|
|
106
|
+
const p = new OllamaProvider();
|
|
107
|
+
available = await p.isAvailable();
|
|
108
|
+
if (!available)
|
|
109
|
+
error = 'Ollama not running or not installed';
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case 'openai': {
|
|
113
|
+
const p = new OpenAIProvider();
|
|
114
|
+
available = await p.isAvailable();
|
|
115
|
+
if (!available)
|
|
116
|
+
error = 'OPENAI_API_KEY not set or invalid';
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case 'anthropic': {
|
|
120
|
+
const p = new AnthropicProvider();
|
|
121
|
+
available = await p.isAvailable();
|
|
122
|
+
if (!available)
|
|
123
|
+
error = 'ANTHROPIC_API_KEY not set or invalid';
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
default:
|
|
127
|
+
spinner.fail(`Unknown provider: ${provider}`);
|
|
128
|
+
console.log(chalk.yellow('\nAvailable providers: deepseek, ollama, openai, anthropic'));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (available) {
|
|
132
|
+
spinner.succeed(`${provider} is available!`);
|
|
133
|
+
if (options.verify) {
|
|
134
|
+
await runVerificationTest(provider);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.log(chalk.green(`\n[OK] ${provider} is ready to use!\n`));
|
|
138
|
+
console.log(chalk.gray('Use --verify to run a test generation.\n'));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
spinner.fail(`${provider} is not available`);
|
|
143
|
+
console.log(chalk.red(`\n[X] ${error}\n`));
|
|
144
|
+
showSetupInstructions(provider);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
spinner.fail('Provider check failed');
|
|
149
|
+
console.error(chalk.red('Error:'), error.message);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Benchmark all available providers
|
|
154
|
+
*/
|
|
155
|
+
export async function aiBenchmarkCommand(options = {}) {
|
|
156
|
+
const spinner = ora('Checking providers...').start();
|
|
157
|
+
const testPrompt = options.prompt || 'Generate a simple API smoke test for https://api.example.com/health';
|
|
158
|
+
try {
|
|
159
|
+
const status = await getProviderStatus();
|
|
160
|
+
spinner.info('Starting benchmark...\n');
|
|
161
|
+
const results = [];
|
|
162
|
+
// Benchmark each provider
|
|
163
|
+
const providers = [
|
|
164
|
+
{ key: 'deepseek', name: 'DeepSeek', factory: () => new DeepSeekProvider() },
|
|
165
|
+
{ key: 'ollama', name: 'Ollama', factory: () => new OllamaProvider() },
|
|
166
|
+
{ key: 'openai', name: 'OpenAI', factory: () => new OpenAIProvider() },
|
|
167
|
+
{ key: 'anthropic', name: 'Anthropic', factory: () => new AnthropicProvider() },
|
|
168
|
+
];
|
|
169
|
+
console.log(chalk.bold('Benchmark Results:\n'));
|
|
170
|
+
console.log(chalk.gray(`Prompt: "${testPrompt}"\n`));
|
|
171
|
+
const table = new Table({
|
|
172
|
+
head: [chalk.bold('Provider'), chalk.bold('Status'), chalk.bold('Time'), chalk.bold('Tokens')],
|
|
173
|
+
colWidths: [15, 15, 12, 15],
|
|
174
|
+
style: { head: ['cyan', 'bold'] }
|
|
175
|
+
});
|
|
176
|
+
for (const { key, name, factory } of providers) {
|
|
177
|
+
if (!status[key]) {
|
|
178
|
+
table.push([name, chalk.gray('Not available'), '-', '-']);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const pSpinner = ora(`Testing ${name}...`).start();
|
|
182
|
+
try {
|
|
183
|
+
const provider = factory();
|
|
184
|
+
const start = Date.now();
|
|
185
|
+
const response = await provider.generate({
|
|
186
|
+
prompt: testPrompt,
|
|
187
|
+
maxTokens: 500
|
|
188
|
+
});
|
|
189
|
+
const time = Date.now() - start;
|
|
190
|
+
pSpinner.succeed();
|
|
191
|
+
table.push([
|
|
192
|
+
name,
|
|
193
|
+
chalk.green('✓ Success'),
|
|
194
|
+
chalk.cyan(`${time}ms`),
|
|
195
|
+
chalk.gray(`${response.usage.totalTokens}`)
|
|
196
|
+
]);
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
pSpinner.fail();
|
|
200
|
+
table.push([
|
|
201
|
+
name,
|
|
202
|
+
chalk.red('✗ Failed'),
|
|
203
|
+
'-',
|
|
204
|
+
`-`
|
|
205
|
+
]);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
console.log(table.toString());
|
|
209
|
+
// Show cost comparison
|
|
210
|
+
console.log(chalk.yellow('\n[$] Cost Comparison (per 1M tokens):\n'));
|
|
211
|
+
console.log(chalk.gray(' DeepSeek: $0.14 input, $0.28 output (~99.5% savings vs GPT-4)'));
|
|
212
|
+
console.log(chalk.gray(' Ollama: FREE (local, privacy)'));
|
|
213
|
+
console.log(chalk.gray(' OpenAI: ~$30 input, ~$60 output'));
|
|
214
|
+
console.log(chalk.gray(' Anthropic: ~$30 input, ~$60 output\n'));
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
spinner.fail('Benchmark failed');
|
|
218
|
+
console.error(chalk.red('Error:'), error.message);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Generate tests using the best available provider
|
|
223
|
+
*/
|
|
224
|
+
export async function aiGenerateCommand(prompt, options = {}) {
|
|
225
|
+
let spinner;
|
|
226
|
+
try {
|
|
227
|
+
const systemPrompt = buildSystemPrompt(options.type || 'api');
|
|
228
|
+
const provider = options.provider
|
|
229
|
+
? await createLLMProvider({ preferred: options.provider })
|
|
230
|
+
: await createBest();
|
|
231
|
+
const providerType = provider.getProviderType();
|
|
232
|
+
spinner = ora(`Using ${chalk.cyan(providerType)}...`).start();
|
|
233
|
+
const response = await provider.generate({
|
|
234
|
+
prompt,
|
|
235
|
+
systemPrompt,
|
|
236
|
+
maxTokens: 4096,
|
|
237
|
+
temperature: 0.7,
|
|
238
|
+
});
|
|
239
|
+
spinner.succeed(`Generated using ${chalk.cyan(providerType)}!\n`);
|
|
240
|
+
if (options.json) {
|
|
241
|
+
console.log(JSON.stringify({
|
|
242
|
+
provider: providerType,
|
|
243
|
+
content: response.content,
|
|
244
|
+
usage: response.usage,
|
|
245
|
+
model: response.model
|
|
246
|
+
}, null, 2));
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
console.log(chalk.bold('Generated Content:\n'));
|
|
250
|
+
console.log(chalk.gray('─'.repeat(70)));
|
|
251
|
+
console.log(response.content);
|
|
252
|
+
console.log(chalk.gray('─'.repeat(70)));
|
|
253
|
+
console.log(chalk.gray(`\n[INFO] Tokens: ${response.usage.totalTokens} (${response.usage.promptTokens} in + ${response.usage.completionTokens} out)`));
|
|
254
|
+
// Show cost estimate for DeepSeek
|
|
255
|
+
if (providerType === 'deepseek') {
|
|
256
|
+
const deepSeek = new DeepSeekProvider();
|
|
257
|
+
const cost = deepSeek.estimateCost(response.usage.promptTokens, response.usage.completionTokens);
|
|
258
|
+
console.log(chalk.gray(`[$] Cost: $${cost.toFixed(6)} (vs ~$${(cost * 200).toFixed(4)} with GPT-4)`));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
spinner?.fail('Generation failed');
|
|
264
|
+
if (error instanceof DeepSeekError) {
|
|
265
|
+
console.error(chalk.red('\nDeepSeek Error:'), error.message);
|
|
266
|
+
if (error.details) {
|
|
267
|
+
console.error(chalk.gray(JSON.stringify(error.details, null, 2)));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else if (error instanceof OllamaError) {
|
|
271
|
+
console.error(chalk.red('\nOllama Error:'), error.message);
|
|
272
|
+
}
|
|
273
|
+
else if (error instanceof OpenAIError) {
|
|
274
|
+
console.error(chalk.red('\nOpenAI Error:'), error.message);
|
|
275
|
+
}
|
|
276
|
+
else if (error instanceof AnthropicError) {
|
|
277
|
+
console.error(chalk.red('\nAnthropic Error:'), error.message);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
console.error(chalk.red('Error:'), error.message);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Show AI configuration
|
|
286
|
+
*/
|
|
287
|
+
export async function aiConfigCommand() {
|
|
288
|
+
console.log(chalk.cyan('QA360 AI Configuration:\n'));
|
|
289
|
+
const status = await getProviderStatus();
|
|
290
|
+
const configs = [
|
|
291
|
+
{ provider: 'DeepSeek', envVar: 'DEEPSEEK_API_KEY', set: !!process.env.DEEPSEEK_API_KEY },
|
|
292
|
+
{ provider: 'Ollama', envVar: 'OLLAMA_BASE_URL', set: !!process.env.OLLAMA_BASE_URL },
|
|
293
|
+
{ provider: 'OpenAI', envVar: 'OPENAI_API_KEY', set: !!process.env.OPENAI_API_KEY },
|
|
294
|
+
{ provider: 'Anthropic', envVar: 'ANTHROPIC_API_KEY', set: !!process.env.ANTHROPIC_API_KEY },
|
|
295
|
+
];
|
|
296
|
+
const table = new Table({
|
|
297
|
+
head: [chalk.bold('Provider'), chalk.bold('Environment Variable'), chalk.bold('Status')],
|
|
298
|
+
colWidths: [15, 30, 15],
|
|
299
|
+
style: { head: ['cyan', 'bold'] }
|
|
300
|
+
});
|
|
301
|
+
for (const config of configs) {
|
|
302
|
+
const status = config.set
|
|
303
|
+
? chalk.green('✓ Set')
|
|
304
|
+
: chalk.gray('✗ Not set');
|
|
305
|
+
table.push([config.provider, chalk.gray(config.envVar), status]);
|
|
306
|
+
}
|
|
307
|
+
console.log(table.toString());
|
|
308
|
+
console.log(chalk.yellow('\n[INFO] Set API keys using:'));
|
|
309
|
+
console.log(chalk.gray(' export VARIABLE=value'));
|
|
310
|
+
console.log(chalk.gray(' # OR'));
|
|
311
|
+
console.log(chalk.gray(' qa360 secrets add VARIABLE\n'));
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Run verification test for a provider
|
|
315
|
+
*/
|
|
316
|
+
async function runVerificationTest(provider) {
|
|
317
|
+
const spinner = ora(`Running verification test with ${provider}...`).start();
|
|
318
|
+
try {
|
|
319
|
+
let testProvider;
|
|
320
|
+
switch (provider.toLowerCase()) {
|
|
321
|
+
case 'deepseek':
|
|
322
|
+
testProvider = new DeepSeekProvider();
|
|
323
|
+
break;
|
|
324
|
+
case 'ollama':
|
|
325
|
+
testProvider = new OllamaProvider();
|
|
326
|
+
break;
|
|
327
|
+
case 'openai':
|
|
328
|
+
testProvider = new OpenAIProvider();
|
|
329
|
+
break;
|
|
330
|
+
case 'anthropic':
|
|
331
|
+
testProvider = new AnthropicProvider();
|
|
332
|
+
break;
|
|
333
|
+
default:
|
|
334
|
+
throw new Error(`Unknown provider: ${provider}`);
|
|
335
|
+
}
|
|
336
|
+
const response = await testProvider.generate({
|
|
337
|
+
prompt: 'Say "OK" if you can read this.',
|
|
338
|
+
maxTokens: 10
|
|
339
|
+
});
|
|
340
|
+
spinner.succeed('Verification test passed!\n');
|
|
341
|
+
console.log(chalk.green('[OK] Provider is working correctly!'));
|
|
342
|
+
console.log(chalk.gray(`Response: ${response.content.trim()}\n`));
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
spinner.fail('Verification test failed');
|
|
346
|
+
console.error(chalk.red('Error:'), error.message);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Show setup instructions for a provider
|
|
351
|
+
*/
|
|
352
|
+
function showSetupInstructions(provider) {
|
|
353
|
+
switch (provider.toLowerCase()) {
|
|
354
|
+
case 'deepseek':
|
|
355
|
+
console.log(chalk.yellow('[NOTE] To set up DeepSeek:\n'));
|
|
356
|
+
console.log(chalk.gray(' 1. Get API key: https://platform.deepseek.com/api_keys'));
|
|
357
|
+
console.log(chalk.gray(' 2. Run: export DEEPSEEK_API_KEY=your_key'));
|
|
358
|
+
console.log(chalk.gray(' 3. Or: qa360 secrets add DEEPSEEK_API_KEY\n'));
|
|
359
|
+
break;
|
|
360
|
+
case 'ollama':
|
|
361
|
+
console.log(chalk.yellow('[NOTE] To set up Ollama:\n'));
|
|
362
|
+
console.log(chalk.gray(' 1. Install: brew install ollama'));
|
|
363
|
+
console.log(chalk.gray(' 2. Start: ollama serve'));
|
|
364
|
+
console.log(chalk.gray(' 3. Pull: ollama pull deepseek-coder\n'));
|
|
365
|
+
break;
|
|
366
|
+
case 'openai':
|
|
367
|
+
console.log(chalk.yellow('[NOTE] To set up OpenAI:\n'));
|
|
368
|
+
console.log(chalk.gray(' 1. Get API key: https://platform.openai.com/api-keys'));
|
|
369
|
+
console.log(chalk.gray(' 2. Run: export OPENAI_API_KEY=your_key'));
|
|
370
|
+
console.log(chalk.gray(' 3. Or: qa360 secrets add OPENAI_API_KEY\n'));
|
|
371
|
+
break;
|
|
372
|
+
case 'anthropic':
|
|
373
|
+
console.log(chalk.yellow('[NOTE] To set up Anthropic:\n'));
|
|
374
|
+
console.log(chalk.gray(' 1. Get API key: https://console.anthropic.com/'));
|
|
375
|
+
console.log(chalk.gray(' 2. Run: export ANTHROPIC_API_KEY=your_key'));
|
|
376
|
+
console.log(chalk.gray(' 3. Or: qa360 secrets add ANTHROPIC_API_KEY\n'));
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Build system prompt for test generation
|
|
382
|
+
*/
|
|
383
|
+
function buildSystemPrompt(type) {
|
|
384
|
+
const basePrompt = `You are QA360, an expert QA test generator.
|
|
385
|
+
Generate production-ready, well-structured test code following best practices.
|
|
386
|
+
|
|
387
|
+
Rules:
|
|
388
|
+
- Generate COMPLETE, working code (no "...", no comments saying "to be implemented")
|
|
389
|
+
- Include proper imports and setup
|
|
390
|
+
- Handle errors gracefully
|
|
391
|
+
- Use modern async/await patterns
|
|
392
|
+
- Add helpful comments for complex logic
|
|
393
|
+
- Return ONLY the code, no explanations before or after`;
|
|
394
|
+
const typePrompts = {
|
|
395
|
+
api: `${basePrompt}
|
|
396
|
+
|
|
397
|
+
For API tests using Playwright:
|
|
398
|
+
- Use @playwright/test
|
|
399
|
+
- Use test() and expect() from Playwright
|
|
400
|
+
- Test REST API endpoints (GET, POST, PUT, DELETE)
|
|
401
|
+
- Include proper assertions for status codes, headers, and response body
|
|
402
|
+
- Handle both success and error cases`,
|
|
403
|
+
ui: `${basePrompt}
|
|
404
|
+
|
|
405
|
+
For UI/E2E tests using Playwright:
|
|
406
|
+
- Use @playwright/test
|
|
407
|
+
- Use page.goto(), page.click(), page.fill(), etc.
|
|
408
|
+
- Include explicit waits where needed (waitForLoadState, waitForSelector)
|
|
409
|
+
- Add assertions for page content
|
|
410
|
+
- Use data-testid attributes when possible`,
|
|
411
|
+
perf: `${basePrompt}
|
|
412
|
+
|
|
413
|
+
For performance tests using K6:
|
|
414
|
+
- Use k6/http module
|
|
415
|
+
- Define thresholds for response times and error rates
|
|
416
|
+
- Include realistic user scenarios
|
|
417
|
+
- Use options for VUs and duration`,
|
|
418
|
+
a11y: `${basePrompt}
|
|
419
|
+
|
|
420
|
+
For accessibility tests:
|
|
421
|
+
- Use @axe-core/playwright
|
|
422
|
+
- Test WCAG compliance
|
|
423
|
+
- Check for common a11y issues`,
|
|
424
|
+
};
|
|
425
|
+
return typePrompts[type] || typePrompts.api;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Create AI commands
|
|
429
|
+
*/
|
|
430
|
+
export function createAICommands() {
|
|
431
|
+
const aiCmd = new Command('ai')
|
|
432
|
+
.description('Manage AI providers (DeepSeek, Ollama, OpenAI, Anthropic)');
|
|
433
|
+
aiCmd
|
|
434
|
+
.command('list')
|
|
435
|
+
.description('List all available AI providers and their status')
|
|
436
|
+
.action(async () => {
|
|
437
|
+
try {
|
|
438
|
+
await aiListCommand();
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
console.error(chalk.red('Error listing providers:'), error.message);
|
|
442
|
+
process.exit(1);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
aiCmd
|
|
446
|
+
.command('use <provider>')
|
|
447
|
+
.description('Test and use a specific AI provider')
|
|
448
|
+
.option('--verify', 'Run a verification test')
|
|
449
|
+
.action(async (provider, options) => {
|
|
450
|
+
try {
|
|
451
|
+
await aiUseCommand(provider, options);
|
|
452
|
+
}
|
|
453
|
+
catch (error) {
|
|
454
|
+
console.error(chalk.red('Error testing provider:'), error.message);
|
|
455
|
+
process.exit(1);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
aiCmd
|
|
459
|
+
.command('benchmark')
|
|
460
|
+
.description('Benchmark all available AI providers')
|
|
461
|
+
.option('-p, --prompt <prompt>', 'Custom prompt for benchmark')
|
|
462
|
+
.action(async (options) => {
|
|
463
|
+
try {
|
|
464
|
+
await aiBenchmarkCommand(options);
|
|
465
|
+
}
|
|
466
|
+
catch (error) {
|
|
467
|
+
console.error(chalk.red('Error running benchmark:'), error.message);
|
|
468
|
+
process.exit(1);
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
aiCmd
|
|
472
|
+
.command('generate <prompt>')
|
|
473
|
+
.description('Generate tests using the best available AI provider')
|
|
474
|
+
.option('-P, --provider <provider>', 'Use specific provider (deepseek, ollama, openai, anthropic)')
|
|
475
|
+
.option('-t, --type <type>', 'Test type (api, ui, perf, a11y)', 'api')
|
|
476
|
+
.option('--json', 'Output as JSON')
|
|
477
|
+
.action(async (prompt, options) => {
|
|
478
|
+
try {
|
|
479
|
+
await aiGenerateCommand(prompt, options);
|
|
480
|
+
}
|
|
481
|
+
catch (error) {
|
|
482
|
+
console.error(chalk.red('Error generating:'), error.message);
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
aiCmd
|
|
487
|
+
.command('config')
|
|
488
|
+
.description('Show AI configuration and environment variables')
|
|
489
|
+
.action(async () => {
|
|
490
|
+
try {
|
|
491
|
+
await aiConfigCommand();
|
|
492
|
+
}
|
|
493
|
+
catch (error) {
|
|
494
|
+
console.error(chalk.red('Error showing config:'), error.message);
|
|
495
|
+
process.exit(1);
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
return aiCmd;
|
|
499
|
+
}
|
package/dist/commands/ask.js
CHANGED
|
@@ -9,7 +9,7 @@ import { dump } from 'js-yaml';
|
|
|
9
9
|
export class QA360Ask {
|
|
10
10
|
qa360Dir = join(process.cwd(), '.qa360');
|
|
11
11
|
async generatePack(query) {
|
|
12
|
-
console.log(chalk.blue('
|
|
12
|
+
console.log(chalk.blue('\n[QA360 Ask] Generating test pack from your description...\n'));
|
|
13
13
|
// Si pas de query, mode interactif
|
|
14
14
|
if (!query) {
|
|
15
15
|
return await this.interactiveMode();
|
|
@@ -36,12 +36,12 @@ export class QA360Ask {
|
|
|
36
36
|
name: 'type',
|
|
37
37
|
message: 'Type de tests principal:',
|
|
38
38
|
choices: [
|
|
39
|
-
{ name: '
|
|
40
|
-
{ name: '
|
|
41
|
-
{ name: '
|
|
42
|
-
{ name: '
|
|
43
|
-
{ name: '
|
|
44
|
-
{ name: '
|
|
39
|
+
{ name: '[WEB] API REST/GraphQL', value: 'api' },
|
|
40
|
+
{ name: '[UI] Interface Web (UI)', value: 'web' },
|
|
41
|
+
{ name: '[PERF] Performance', value: 'performance' },
|
|
42
|
+
{ name: '[SEC] Securite', value: 'security' },
|
|
43
|
+
{ name: '[A11Y] Accessibilite', value: 'accessibility' },
|
|
44
|
+
{ name: '[ALL] Complet (Multi-adapters)', value: 'complete' }
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
47
|
{
|
|
@@ -411,11 +411,11 @@ export class QA360Ask {
|
|
|
411
411
|
return packPath;
|
|
412
412
|
}
|
|
413
413
|
displayPack(pack) {
|
|
414
|
-
console.log(chalk.bold('\n
|
|
414
|
+
console.log(chalk.bold('\n[PACK] Pack genere:\n'));
|
|
415
415
|
console.log(chalk.blue(`Nom: ${pack.name}`));
|
|
416
416
|
console.log(chalk.gray(`Description: ${pack.description}`));
|
|
417
417
|
console.log(chalk.green(`Adapters: ${pack.adapters.join(', ')}`));
|
|
418
|
-
console.log(chalk.yellow(`Tests: ${pack.tests.length} test(s)
|
|
418
|
+
console.log(chalk.yellow(`Tests: ${pack.tests.length} test(s) configure(s)`));
|
|
419
419
|
if (pack.environment?.TARGET_URL) {
|
|
420
420
|
console.log(chalk.cyan(`Cible: ${pack.environment.TARGET_URL}`));
|
|
421
421
|
}
|
|
@@ -427,11 +427,11 @@ export async function askCommand(query) {
|
|
|
427
427
|
const pack = await ask.generatePack(query);
|
|
428
428
|
const packPath = await ask.savePack(pack);
|
|
429
429
|
ask.displayPack(pack);
|
|
430
|
-
console.log(chalk.bold(`\n
|
|
431
|
-
console.log(chalk.blue('
|
|
430
|
+
console.log(chalk.bold(`\n[OK] Pack sauvegarde: ${packPath}`));
|
|
431
|
+
console.log(chalk.blue('[INFO] Prochaine etape: qa360 run'));
|
|
432
432
|
}
|
|
433
433
|
catch (error) {
|
|
434
|
-
console.error(chalk.red('
|
|
434
|
+
console.error(chalk.red('[X] Erreur lors de la generation du pack:'), error);
|
|
435
435
|
process.exit(1);
|
|
436
436
|
}
|
|
437
437
|
}
|