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/dist/commands/init.js
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
|
import { existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
10
11
|
import { join, resolve } from 'path';
|
|
@@ -19,41 +20,243 @@ export const TEMPLATES = {
|
|
|
19
20
|
name: 'API Basic',
|
|
20
21
|
description: 'Simple API smoke tests (REST/GraphQL)',
|
|
21
22
|
gates: ['api_smoke'],
|
|
23
|
+
icon: '🔌',
|
|
22
24
|
},
|
|
23
25
|
'ui-basic': {
|
|
24
26
|
name: 'UI Basic',
|
|
25
27
|
description: 'Basic UI/E2E browser tests',
|
|
26
28
|
gates: ['ui'],
|
|
29
|
+
icon: '🌐',
|
|
27
30
|
},
|
|
28
31
|
'fullstack': {
|
|
29
32
|
name: 'Full Stack',
|
|
30
33
|
description: 'API + UI + Performance tests',
|
|
31
34
|
gates: ['api_smoke', 'ui', 'perf'],
|
|
35
|
+
icon: '🎯',
|
|
32
36
|
},
|
|
33
37
|
'security': {
|
|
34
38
|
name: 'Security',
|
|
35
39
|
description: 'SAST, DAST, secrets scanning, dependency checks',
|
|
36
40
|
gates: ['sast', 'dast', 'secrets', 'deps'],
|
|
41
|
+
icon: '🛡️',
|
|
37
42
|
},
|
|
38
43
|
'complete': {
|
|
39
44
|
name: 'Complete',
|
|
40
45
|
description: 'All quality gates (API, UI, Performance, Security, Accessibility)',
|
|
41
46
|
gates: ['api_smoke', 'ui', 'a11y', 'perf', 'sast', 'dast', 'secrets', 'deps'],
|
|
47
|
+
icon: '✨',
|
|
42
48
|
},
|
|
43
49
|
};
|
|
44
50
|
/**
|
|
45
|
-
* Gate descriptions
|
|
51
|
+
* Gate descriptions with beginner-friendly explanations
|
|
46
52
|
*/
|
|
47
53
|
export const GATE_DESCRIPTIONS = {
|
|
48
|
-
api_smoke:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
api_smoke: {
|
|
55
|
+
short: 'API smoke tests (REST/GraphQL health checks)',
|
|
56
|
+
beginner: 'Tests if your API is alive and responds correctly. Like checking if the server is up.',
|
|
57
|
+
example: 'Checks if GET /api/health returns 200 OK',
|
|
58
|
+
icon: '🔌',
|
|
59
|
+
},
|
|
60
|
+
ui: {
|
|
61
|
+
short: 'UI/E2E tests (browser automation)',
|
|
62
|
+
beginner: 'Tests your website in a real browser. Checks if pages load and buttons work.',
|
|
63
|
+
example: 'Opens your site and verifies the login form appears',
|
|
64
|
+
icon: '🌐',
|
|
65
|
+
},
|
|
66
|
+
a11y: {
|
|
67
|
+
short: 'Accessibility tests (WCAG compliance)',
|
|
68
|
+
beginner: 'Tests if your site is usable by people with disabilities (screen readers, color blindness).',
|
|
69
|
+
example: 'Checks if images have alt text for blind users',
|
|
70
|
+
icon: '♿',
|
|
71
|
+
},
|
|
72
|
+
perf: {
|
|
73
|
+
short: 'Performance tests (load/stress testing)',
|
|
74
|
+
beginner: 'Tests if your site stays fast when many people visit at once.',
|
|
75
|
+
example: 'Simulates 100 users visiting simultaneously',
|
|
76
|
+
icon: '⚡',
|
|
77
|
+
},
|
|
78
|
+
sast: {
|
|
79
|
+
short: 'Static security analysis (code scanning)',
|
|
80
|
+
beginner: 'Analyzes your source code for security vulnerabilities without running it.',
|
|
81
|
+
example: 'Finds hardcoded passwords or unsafe functions',
|
|
82
|
+
icon: '🔍',
|
|
83
|
+
},
|
|
84
|
+
dast: {
|
|
85
|
+
short: 'Dynamic security testing (runtime scanning)',
|
|
86
|
+
beginner: 'Attacks your running application to find security weaknesses.',
|
|
87
|
+
example: 'Tries common attacks like SQL injection',
|
|
88
|
+
icon: '⚔️',
|
|
89
|
+
},
|
|
90
|
+
secrets: {
|
|
91
|
+
short: 'Secrets detection (credential scanning)',
|
|
92
|
+
beginner: 'Scans for accidentally committed passwords, API keys, or tokens.',
|
|
93
|
+
example: 'Finds AWS keys in your code',
|
|
94
|
+
icon: '🔑',
|
|
95
|
+
},
|
|
96
|
+
deps: {
|
|
97
|
+
short: 'Dependency vulnerability checks',
|
|
98
|
+
beginner: 'Checks if your libraries have known security issues.',
|
|
99
|
+
example: 'Checks if you use an old version of a package with bugs',
|
|
100
|
+
icon: '📦',
|
|
101
|
+
},
|
|
56
102
|
};
|
|
103
|
+
/**
|
|
104
|
+
* Beginner mode - Step by step guided experience
|
|
105
|
+
*/
|
|
106
|
+
async function beginnerMode() {
|
|
107
|
+
console.log(chalk.bold.cyan('\n╔════════════════════════════════════════════════════════════╗'));
|
|
108
|
+
console.log(chalk.bold.cyan('║ 👋 Welcome to QA360 - Your Quality Assistant! ║'));
|
|
109
|
+
console.log(chalk.bold.cyan('║ ║'));
|
|
110
|
+
console.log(chalk.bold.cyan('║ Let\'s create your first test pack together, step by step. ║'));
|
|
111
|
+
console.log(chalk.bold.cyan('╚════════════════════════════════════════════════════════════╝\n'));
|
|
112
|
+
console.log(chalk.gray('📦 What is a "test pack"?'));
|
|
113
|
+
console.log(chalk.white(' A test pack defines WHAT to test (your API, website) and HOW (smoke tests, security scans).\n'));
|
|
114
|
+
// Step 1: Project name
|
|
115
|
+
console.log(chalk.bold('Step 1️⃣ - Project Name'));
|
|
116
|
+
console.log(chalk.gray(' Give your test pack a memorable name.\n'));
|
|
117
|
+
const { name } = await inquirer.prompt([
|
|
118
|
+
{
|
|
119
|
+
type: 'input',
|
|
120
|
+
name: 'name',
|
|
121
|
+
message: 'What should we call your test pack?',
|
|
122
|
+
default: 'my-first-tests',
|
|
123
|
+
validate: (input) => input.length > 0 || 'Name is required',
|
|
124
|
+
},
|
|
125
|
+
]);
|
|
126
|
+
console.log(chalk.green(` ✓ Great! Your test pack is called "${name}"\n`));
|
|
127
|
+
// Step 2: What to test?
|
|
128
|
+
console.log(chalk.bold('Step 2️⃣ - What do you want to test?'));
|
|
129
|
+
console.log(chalk.gray(' Select the type of application you want to test.\n'));
|
|
130
|
+
const { appType } = await inquirer.prompt([
|
|
131
|
+
{
|
|
132
|
+
type: 'list',
|
|
133
|
+
name: 'appType',
|
|
134
|
+
message: 'What type of application are you testing?',
|
|
135
|
+
choices: [
|
|
136
|
+
{ name: '🔌 API / Backend - REST or GraphQL API', value: 'api' },
|
|
137
|
+
{ name: '🌐 Website / Frontend - Web application', value: 'web' },
|
|
138
|
+
{ name: '🎯 Full Stack - Both API and Website', value: 'fullstack' },
|
|
139
|
+
{ name: '🛡️ Security Scan - Check for vulnerabilities', value: 'security' },
|
|
140
|
+
{ name: '✨ Everything - All tests enabled', value: 'complete' },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
]);
|
|
144
|
+
console.log(chalk.green(` ✓ You chose: ${appType}\n`));
|
|
145
|
+
// Step 3: Gates explanation and selection
|
|
146
|
+
const suggestedGates = TEMPLATES[appType === 'complete' ? 'complete' : appType === 'fullstack' ? 'fullstack' : appType === 'api' ? 'api-basic' : appType === 'web' ? 'ui-basic' : 'security']?.gates || [];
|
|
147
|
+
console.log(chalk.bold('Step 3️⃣ - Quality Gates'));
|
|
148
|
+
console.log(chalk.gray(' Gates are different types of tests. We\'ll suggest some based on your choice.\n'));
|
|
149
|
+
console.log(chalk.cyan(' Suggested gates for you:\n'));
|
|
150
|
+
for (const gate of suggestedGates) {
|
|
151
|
+
const info = GATE_DESCRIPTIONS[gate];
|
|
152
|
+
console.log(chalk.cyan(` ${info?.icon} ${gate}`));
|
|
153
|
+
console.log(chalk.gray(` ${info?.beginner}`));
|
|
154
|
+
console.log(chalk.gray(` Example: ${info?.example}\n`));
|
|
155
|
+
}
|
|
156
|
+
const { useSuggested } = await inquirer.prompt([
|
|
157
|
+
{
|
|
158
|
+
type: 'confirm',
|
|
159
|
+
name: 'useSuggested',
|
|
160
|
+
message: 'Use the suggested gates?',
|
|
161
|
+
default: true,
|
|
162
|
+
},
|
|
163
|
+
]);
|
|
164
|
+
let gates = suggestedGates;
|
|
165
|
+
if (!useSuggested) {
|
|
166
|
+
const { customGates } = await inquirer.prompt([
|
|
167
|
+
{
|
|
168
|
+
type: 'checkbox',
|
|
169
|
+
name: 'customGates',
|
|
170
|
+
message: 'Select the gates you want:',
|
|
171
|
+
choices: Object.entries(GATE_DESCRIPTIONS).map(([key, info]) => ({
|
|
172
|
+
name: `${info?.icon} ${key} - ${info?.short}`,
|
|
173
|
+
value: key,
|
|
174
|
+
})),
|
|
175
|
+
validate: (input) => input.length > 0 || 'Select at least one gate',
|
|
176
|
+
},
|
|
177
|
+
]);
|
|
178
|
+
gates = customGates;
|
|
179
|
+
}
|
|
180
|
+
console.log(chalk.green(` ✓ Selected gates: ${gates.join(', ')}\n`));
|
|
181
|
+
// Step 4: URLs
|
|
182
|
+
console.log(chalk.bold('Step 4️⃣ - Application URLs'));
|
|
183
|
+
console.log(chalk.gray(' Where is your application running?\n'));
|
|
184
|
+
const targets = {};
|
|
185
|
+
if (gates.includes('api_smoke')) {
|
|
186
|
+
console.log(chalk.cyan(' 📡 API Configuration'));
|
|
187
|
+
const apiAnswers = await inquirer.prompt([
|
|
188
|
+
{
|
|
189
|
+
type: 'input',
|
|
190
|
+
name: 'apiUrl',
|
|
191
|
+
message: 'What is your API base URL?',
|
|
192
|
+
default: 'https://api.example.com',
|
|
193
|
+
validate: (input) => input.startsWith('http') || 'Must start with http:// or https://',
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
type: 'input',
|
|
197
|
+
name: 'smokeTests',
|
|
198
|
+
message: 'Which endpoints should we test? (format: "METHOD /path -> status")',
|
|
199
|
+
default: 'GET /health -> 200',
|
|
200
|
+
},
|
|
201
|
+
]);
|
|
202
|
+
targets.api = {
|
|
203
|
+
baseUrl: apiAnswers.apiUrl,
|
|
204
|
+
smoke: [apiAnswers.smokeTests],
|
|
205
|
+
};
|
|
206
|
+
console.log(chalk.green(` ✓ API: ${apiAnswers.apiUrl}\n`));
|
|
207
|
+
}
|
|
208
|
+
if (gates.includes('ui') || gates.includes('a11y')) {
|
|
209
|
+
console.log(chalk.cyan(' 🌐 Website Configuration'));
|
|
210
|
+
const uiAnswers = await inquirer.prompt([
|
|
211
|
+
{
|
|
212
|
+
type: 'input',
|
|
213
|
+
name: 'webUrl',
|
|
214
|
+
message: 'What is your website URL?',
|
|
215
|
+
default: 'https://example.com',
|
|
216
|
+
validate: (input) => input.startsWith('http') || 'Must start with http:// or https://',
|
|
217
|
+
},
|
|
218
|
+
]);
|
|
219
|
+
targets.web = {
|
|
220
|
+
baseUrl: uiAnswers.webUrl,
|
|
221
|
+
pages: [uiAnswers.webUrl],
|
|
222
|
+
};
|
|
223
|
+
console.log(chalk.green(` ✓ Website: ${uiAnswers.webUrl}\n`));
|
|
224
|
+
}
|
|
225
|
+
// Build pack
|
|
226
|
+
const pack = {
|
|
227
|
+
version: 1,
|
|
228
|
+
name,
|
|
229
|
+
gates: gates,
|
|
230
|
+
targets,
|
|
231
|
+
};
|
|
232
|
+
// Add budgets
|
|
233
|
+
if (gates.includes('perf') || gates.includes('a11y')) {
|
|
234
|
+
pack.budgets = {};
|
|
235
|
+
if (gates.includes('perf')) {
|
|
236
|
+
pack.budgets.perf_p95_ms = 2000;
|
|
237
|
+
}
|
|
238
|
+
if (gates.includes('a11y')) {
|
|
239
|
+
pack.budgets.a11y_min = 90;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// Add security config
|
|
243
|
+
if (gates.some((g) => ['sast', 'dast', 'secrets', 'deps'].includes(g))) {
|
|
244
|
+
pack.security = {};
|
|
245
|
+
if (gates.includes('sast')) {
|
|
246
|
+
pack.security.sast = { max_critical: 0, max_high: 3 };
|
|
247
|
+
}
|
|
248
|
+
if (gates.includes('dast')) {
|
|
249
|
+
pack.security.dast = { max_high: 5 };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Final summary
|
|
253
|
+
console.log(chalk.bold.cyan('\n✨ Almost done! Here\'s your test pack summary:\n'));
|
|
254
|
+
console.log(chalk.white(` Name: ${chalk.bold(name)}`));
|
|
255
|
+
console.log(chalk.white(` Gates: ${gates.map(g => `${GATE_DESCRIPTIONS[g]?.icon} ${g}`).join(', ')}`));
|
|
256
|
+
console.log(chalk.white(` API: ${targets.api?.baseUrl || 'N/A'}`));
|
|
257
|
+
console.log(chalk.white(` Website: ${targets.web?.baseUrl || 'N/A'}`));
|
|
258
|
+
return pack;
|
|
259
|
+
}
|
|
57
260
|
/**
|
|
58
261
|
* Interactive prompts
|
|
59
262
|
*/
|
|
@@ -263,7 +466,11 @@ export async function initCommand(options = {}) {
|
|
|
263
466
|
}
|
|
264
467
|
// Generate pack config
|
|
265
468
|
let pack;
|
|
266
|
-
if (options.
|
|
469
|
+
if (options.beginner) {
|
|
470
|
+
// Beginner guided mode
|
|
471
|
+
pack = await beginnerMode();
|
|
472
|
+
}
|
|
473
|
+
else if (options.template) {
|
|
267
474
|
// Use template directly
|
|
268
475
|
pack = generateFromTemplate(options.template);
|
|
269
476
|
console.log(chalk.green(`✅ Using template: ${TEMPLATES[options.template]?.name || options.template}`));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Commands for TUI and Dashboard monitoring
|
|
3
|
+
* Phase 8: Real-time test execution visualization
|
|
4
|
+
*/
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
/**
|
|
7
|
+
* Monitor command - Run tests with TUI
|
|
8
|
+
*/
|
|
9
|
+
export declare const monitorCommand: (pack: string | undefined, options: {
|
|
10
|
+
mode?: string;
|
|
11
|
+
retries?: string;
|
|
12
|
+
timeout?: string;
|
|
13
|
+
docker?: boolean;
|
|
14
|
+
}) => Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Dashboard command - Run tests with web dashboard
|
|
17
|
+
*/
|
|
18
|
+
export declare const dashboardCommand: (pack: string | undefined, options: {
|
|
19
|
+
port?: string;
|
|
20
|
+
host?: string;
|
|
21
|
+
autoOpen?: boolean;
|
|
22
|
+
}) => Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Create monitor and dashboard commands
|
|
25
|
+
*/
|
|
26
|
+
export declare function createMonitorCommands(): Command;
|
|
27
|
+
export declare function createDashboardCommand(): Command;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Commands for TUI and Dashboard monitoring
|
|
3
|
+
* Phase 8: Real-time test execution visualization
|
|
4
|
+
*/
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { TUIMonitor } from '../core/index.js';
|
|
8
|
+
import { DashboardServer, EvidenceVault } from '../core/index.js';
|
|
9
|
+
import { Phase3Runner } from '../core/index.js';
|
|
10
|
+
import { existsSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import { loadPackConfig } from '../utils/config.js';
|
|
13
|
+
/**
|
|
14
|
+
* Monitor command - Run tests with TUI
|
|
15
|
+
*/
|
|
16
|
+
export const monitorCommand = async (pack, options) => {
|
|
17
|
+
const packPath = pack || findDefaultPack();
|
|
18
|
+
if (!packPath) {
|
|
19
|
+
console.error(chalk.red('Error: No pack file found. Specify a pack file or run qa360 init.'));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
// Load pack config
|
|
23
|
+
const packConfig = await loadPackConfig(packPath);
|
|
24
|
+
const workingDir = process.cwd();
|
|
25
|
+
// Get gate names
|
|
26
|
+
const gateNames = getGateNames(packConfig);
|
|
27
|
+
// Create TUI monitor
|
|
28
|
+
const monitor = new TUIMonitor({
|
|
29
|
+
refreshRate: 500,
|
|
30
|
+
colors: true,
|
|
31
|
+
compact: false
|
|
32
|
+
});
|
|
33
|
+
monitor.start(gateNames.length);
|
|
34
|
+
try {
|
|
35
|
+
// Create runner with event emission
|
|
36
|
+
const runner = new Phase3Runner({
|
|
37
|
+
workingDir,
|
|
38
|
+
pack: packConfig
|
|
39
|
+
});
|
|
40
|
+
// Emit events to monitor
|
|
41
|
+
monitor.emit({
|
|
42
|
+
type: 'phase_start',
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
data: { phase: 'Initializing' }
|
|
45
|
+
});
|
|
46
|
+
// Run tests (simplified - in real implementation, Phase3Runner would emit events)
|
|
47
|
+
const result = await runner.run();
|
|
48
|
+
// Update monitor with results
|
|
49
|
+
for (const gate of result.gates) {
|
|
50
|
+
monitor.emit({
|
|
51
|
+
type: 'gate_start',
|
|
52
|
+
timestamp: Date.now(),
|
|
53
|
+
data: { gate: gate.gate }
|
|
54
|
+
});
|
|
55
|
+
monitor.emit({
|
|
56
|
+
type: 'gate_end',
|
|
57
|
+
timestamp: Date.now(),
|
|
58
|
+
data: {
|
|
59
|
+
gate: gate.gate,
|
|
60
|
+
success: gate.success,
|
|
61
|
+
results: {
|
|
62
|
+
total: gate.results?.summary?.total || 0,
|
|
63
|
+
passed: gate.results?.summary?.passed || 0,
|
|
64
|
+
failed: gate.results?.summary?.failed || 0
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
monitor.complete();
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
monitor.emit({
|
|
73
|
+
type: 'error',
|
|
74
|
+
timestamp: Date.now(),
|
|
75
|
+
data: { error: error instanceof Error ? error.message : String(error) }
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Dashboard command - Run tests with web dashboard
|
|
81
|
+
*/
|
|
82
|
+
export const dashboardCommand = async (pack, options) => {
|
|
83
|
+
const packPath = pack || findDefaultPack();
|
|
84
|
+
if (!packPath) {
|
|
85
|
+
console.error(chalk.red('Error: No pack file found. Specify a pack file or run qa360 init.'));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
const port = options.port ? parseInt(options.port) : 8080;
|
|
89
|
+
const host = options.host || '127.0.0.1';
|
|
90
|
+
const workingDir = process.cwd();
|
|
91
|
+
// Load pack config
|
|
92
|
+
const packConfig = await loadPackConfig(packPath);
|
|
93
|
+
const gateNames = getGateNames(packConfig);
|
|
94
|
+
// Open vault for flakiness data
|
|
95
|
+
const vaultPath = join(workingDir, '.qa360');
|
|
96
|
+
const vault = await EvidenceVault.open(vaultPath);
|
|
97
|
+
// Create dashboard server
|
|
98
|
+
const dashboard = new DashboardServer({
|
|
99
|
+
port,
|
|
100
|
+
host,
|
|
101
|
+
autoOpen: options.autoOpen ?? true,
|
|
102
|
+
refreshRate: 1000
|
|
103
|
+
});
|
|
104
|
+
// Connect vault to dashboard for flakiness data
|
|
105
|
+
dashboard.setVault(vault);
|
|
106
|
+
await dashboard.start();
|
|
107
|
+
dashboard.setGates(gateNames);
|
|
108
|
+
console.log(chalk.green('✓ Dashboard started'));
|
|
109
|
+
console.log(chalk.gray(` Open http://${host}:${port} in your browser`));
|
|
110
|
+
// Run tests with dashboard updates
|
|
111
|
+
try {
|
|
112
|
+
const runner = new Phase3Runner({
|
|
113
|
+
workingDir,
|
|
114
|
+
pack: packConfig
|
|
115
|
+
});
|
|
116
|
+
dashboard.emit({
|
|
117
|
+
type: 'start',
|
|
118
|
+
timestamp: Date.now(),
|
|
119
|
+
data: { phase: 'Running tests' }
|
|
120
|
+
});
|
|
121
|
+
const result = await runner.run();
|
|
122
|
+
// Set run ID for flakiness tracking
|
|
123
|
+
if (result.runId) {
|
|
124
|
+
dashboard.setRunId(result.runId);
|
|
125
|
+
}
|
|
126
|
+
// Update dashboard with gate results
|
|
127
|
+
for (const gate of result.gates) {
|
|
128
|
+
dashboard.emit({
|
|
129
|
+
type: 'gate_start',
|
|
130
|
+
timestamp: Date.now(),
|
|
131
|
+
data: { gate: gate.gate }
|
|
132
|
+
});
|
|
133
|
+
dashboard.emit({
|
|
134
|
+
type: 'gate_end',
|
|
135
|
+
timestamp: Date.now(),
|
|
136
|
+
data: {
|
|
137
|
+
gate: gate.gate,
|
|
138
|
+
success: gate.success,
|
|
139
|
+
duration: gate.duration,
|
|
140
|
+
results: {
|
|
141
|
+
total: gate.results?.summary?.total || 0,
|
|
142
|
+
passed: gate.results?.summary?.passed || 0,
|
|
143
|
+
failed: gate.results?.summary?.failed || 0
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
await dashboard.emit({
|
|
149
|
+
type: 'complete',
|
|
150
|
+
timestamp: Date.now(),
|
|
151
|
+
data: {}
|
|
152
|
+
});
|
|
153
|
+
console.log(chalk.green('✓ Tests completed. Dashboard will remain open.'));
|
|
154
|
+
console.log(chalk.gray('Press Ctrl+C to exit.'));
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
await dashboard.emit({
|
|
158
|
+
type: 'error',
|
|
159
|
+
timestamp: Date.now(),
|
|
160
|
+
data: { error: error instanceof Error ? error.message : String(error) }
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
// Keep dashboard open until user exits
|
|
164
|
+
// Vault stays open for flakiness data access
|
|
165
|
+
try {
|
|
166
|
+
await keepAlive();
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
// Close vault when user exits
|
|
170
|
+
await vault.close();
|
|
171
|
+
await dashboard.stop();
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Create monitor and dashboard commands
|
|
176
|
+
*/
|
|
177
|
+
export function createMonitorCommands() {
|
|
178
|
+
const cmd = new Command('monitor');
|
|
179
|
+
cmd
|
|
180
|
+
.description('Run tests with terminal UI monitoring')
|
|
181
|
+
.argument('[pack]', 'Path to pack file')
|
|
182
|
+
.option('--mode <mode>', 'Execution mode: soft or strict', 'soft')
|
|
183
|
+
.option('--retries <number>', 'Number of retries for failed tests', '2')
|
|
184
|
+
.option('--timeout <ms>', 'Global timeout in milliseconds', '30000')
|
|
185
|
+
.option('--docker', 'Use Docker fallback for missing tools')
|
|
186
|
+
.action(monitorCommand);
|
|
187
|
+
return cmd;
|
|
188
|
+
}
|
|
189
|
+
export function createDashboardCommand() {
|
|
190
|
+
const cmd = new Command('dashboard');
|
|
191
|
+
cmd
|
|
192
|
+
.description('Run tests with web dashboard')
|
|
193
|
+
.argument('[pack]', 'Path to pack file')
|
|
194
|
+
.option('--port <number>', 'Dashboard port', '8080')
|
|
195
|
+
.option('--host <address>', 'Dashboard host', '127.0.0.1')
|
|
196
|
+
.option('--no-auto-open', 'Do not auto-open browser')
|
|
197
|
+
.action(dashboardCommand);
|
|
198
|
+
return cmd;
|
|
199
|
+
}
|
|
200
|
+
// Helper functions
|
|
201
|
+
function findDefaultPack() {
|
|
202
|
+
const candidates = ['qa360.yml', 'qa360.yaml', 'pack.yml', 'pack.yaml', '.qa360/pack.yml'];
|
|
203
|
+
for (const candidate of candidates) {
|
|
204
|
+
if (existsSync(join(process.cwd(), candidate))) {
|
|
205
|
+
return candidate;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
function getGateNames(packConfig) {
|
|
211
|
+
if (packConfig.version === 2 && packConfig.gates) {
|
|
212
|
+
// v2: gates is an object
|
|
213
|
+
return Object.keys(packConfig.gates);
|
|
214
|
+
}
|
|
215
|
+
else if (packConfig.gates && Array.isArray(packConfig.gates)) {
|
|
216
|
+
// v1: gates is an array
|
|
217
|
+
return packConfig.gates;
|
|
218
|
+
}
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
async function keepAlive() {
|
|
222
|
+
return new Promise(() => {
|
|
223
|
+
// Keep process alive
|
|
224
|
+
});
|
|
225
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Ollama Command
|
|
3
|
+
*
|
|
4
|
+
* Manage Ollama integration for AI-powered test generation.
|
|
5
|
+
* This command handles connection testing, model management, and configuration.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
/**
|
|
9
|
+
* Test Ollama connection
|
|
10
|
+
*/
|
|
11
|
+
export declare function ollamaTestCommand(options?: {
|
|
12
|
+
model?: string;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* List available models
|
|
16
|
+
*/
|
|
17
|
+
export declare function ollamaListCommand(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Pull a model
|
|
20
|
+
*/
|
|
21
|
+
export declare function ollamaPullCommand(model: string): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Generate tests using Ollama
|
|
24
|
+
*/
|
|
25
|
+
export declare function ollamaGenerateCommand(prompt: string, options?: {
|
|
26
|
+
model?: string;
|
|
27
|
+
type?: string;
|
|
28
|
+
json?: boolean;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Show Ollama configuration
|
|
32
|
+
*/
|
|
33
|
+
export declare function ollamaConfigCommand(options?: {
|
|
34
|
+
setUrl?: string;
|
|
35
|
+
setModel?: string;
|
|
36
|
+
}): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Create Ollama commands
|
|
39
|
+
*/
|
|
40
|
+
export declare function createOllamaCommands(): Command;
|