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/pack.js
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* QA360 Pack Commands
|
|
3
|
+
*
|
|
3
4
|
* Implements pack validate, lint, and migrate commands
|
|
5
|
+
* Supports both v1 and v2 pack formats
|
|
4
6
|
*/
|
|
5
7
|
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
6
|
-
import {
|
|
8
|
+
import { resolve } from 'path';
|
|
7
9
|
import chalk from 'chalk';
|
|
8
10
|
import ora from 'ora';
|
|
9
11
|
import * as yaml from 'js-yaml';
|
|
10
|
-
import { PackValidator, PackMigrator } from '../core/index.js';
|
|
12
|
+
import { PackValidator, PackMigrator as PackMigratorV1 } from '../core/index.js';
|
|
13
|
+
import { PackValidatorV2, PackMigrator, PackLoaderV2 } from '../core/index.js';
|
|
11
14
|
export class QA360Pack {
|
|
12
|
-
qa360Dir =
|
|
13
|
-
packPath =
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
qa360Dir = resolve(process.cwd(), '.qa360');
|
|
16
|
+
packPath = resolve(this.qa360Dir, 'pack.yml');
|
|
17
|
+
validatorV1 = new PackValidator();
|
|
18
|
+
migratorV1 = new PackMigratorV1();
|
|
16
19
|
/**
|
|
17
|
-
* Validate pack.yml
|
|
20
|
+
* Validate pack.yml (auto-detects v1 or v2)
|
|
18
21
|
*/
|
|
19
|
-
async validate(packPath) {
|
|
20
|
-
const spinner = ora('🔍
|
|
22
|
+
async validate(packPath, options) {
|
|
23
|
+
const spinner = ora('🔍 Validating pack...').start();
|
|
21
24
|
try {
|
|
22
25
|
const targetPath = packPath || this.packPath;
|
|
23
26
|
if (!existsSync(targetPath)) {
|
|
24
|
-
spinner.fail(chalk.red('❌ Pack
|
|
25
|
-
console.log(chalk.yellow(`📁
|
|
26
|
-
console.log(chalk.blue('💡
|
|
27
|
+
spinner.fail(chalk.red('❌ Pack not found'));
|
|
28
|
+
console.log(chalk.yellow(`📁 Path: ${targetPath}`));
|
|
29
|
+
console.log(chalk.blue('💡 Use: qa360 init to create a pack'));
|
|
27
30
|
return { valid: false, exitCode: 1 };
|
|
28
31
|
}
|
|
29
32
|
// Load and parse pack
|
|
@@ -33,46 +36,94 @@ export class QA360Pack {
|
|
|
33
36
|
pack = yaml.load(packContent);
|
|
34
37
|
}
|
|
35
38
|
catch (parseError) {
|
|
36
|
-
spinner.fail(chalk.red('❌
|
|
37
|
-
console.log(chalk.red(`\n🔍
|
|
39
|
+
spinner.fail(chalk.red('❌ YAML parse error'));
|
|
40
|
+
console.log(chalk.red(`\n🔍 Error: ${parseError instanceof Error ? parseError.message : 'Parse error'}`));
|
|
38
41
|
return { valid: false, exitCode: 1 };
|
|
39
42
|
}
|
|
40
|
-
//
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
// Detect version
|
|
44
|
+
const loader = new PackLoaderV2();
|
|
45
|
+
const detectedVersion = this.detectPackVersion(pack);
|
|
46
|
+
if (detectedVersion === 2) {
|
|
47
|
+
return await this.validateV2(targetPath, pack, spinner, options?.checkFiles);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return await this.validateV1(pack, spinner);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
spinner.fail(chalk.red('❌ Validation error'));
|
|
55
|
+
console.log(chalk.red(`\n🔍 Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
56
|
+
return { valid: false, exitCode: 1 };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Validate v1 pack
|
|
61
|
+
*/
|
|
62
|
+
async validateV1(pack, spinner) {
|
|
63
|
+
const result = this.validatorV1.validate(pack);
|
|
64
|
+
if (result.valid) {
|
|
65
|
+
spinner.succeed(chalk.green('✅ Pack v1 is valid'));
|
|
66
|
+
if (result.warnings.length > 0) {
|
|
67
|
+
console.log(chalk.yellow('\n⚠️ Warnings:'));
|
|
68
|
+
for (const warning of result.warnings) {
|
|
69
|
+
console.log(chalk.yellow(` • ${warning.code}: ${warning.message}`));
|
|
70
|
+
if (warning.suggestion) {
|
|
71
|
+
console.log(chalk.blue(` 💡 ${warning.suggestion}`));
|
|
52
72
|
}
|
|
53
73
|
}
|
|
54
|
-
// Show pack summary
|
|
55
|
-
this.showPackSummary(pack);
|
|
56
|
-
return { valid: true, exitCode: 0 };
|
|
57
74
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
75
|
+
this.showPackSummary(pack);
|
|
76
|
+
return { valid: true, exitCode: 0 };
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
spinner.fail(chalk.red('❌ Pack v1 validation failed'));
|
|
80
|
+
console.log(chalk.red('\n🔍 Errors found:'));
|
|
81
|
+
for (const error of result.errors) {
|
|
82
|
+
console.log(chalk.red(` • ${error.code}: ${error.message}`));
|
|
83
|
+
console.log(chalk.gray(` 📍 Path: ${error.path}`));
|
|
84
|
+
if (error.suggestion) {
|
|
85
|
+
console.log(chalk.blue(` 💡 ${error.suggestion}`));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
console.log(chalk.blue('\n💡 Tip: Use qa360 pack migrate to upgrade to v2'));
|
|
89
|
+
return { valid: false, exitCode: 1 };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Validate v2 pack
|
|
94
|
+
*/
|
|
95
|
+
async validateV2(packPath, pack, spinner, checkFiles) {
|
|
96
|
+
const validator = new PackValidatorV2(packPath);
|
|
97
|
+
const result = await validator.validate(pack, { checkFilesExist: checkFiles });
|
|
98
|
+
if (result.valid) {
|
|
99
|
+
spinner.succeed(chalk.green('✅ Pack v2 is valid'));
|
|
100
|
+
if (result.warnings.length > 0) {
|
|
101
|
+
console.log(chalk.yellow('\n⚠️ Warnings:'));
|
|
102
|
+
for (const warning of result.warnings.slice(0, 10)) {
|
|
103
|
+
console.log(chalk.yellow(` • ${warning.code}: ${warning.message}`));
|
|
104
|
+
if (warning.path) {
|
|
105
|
+
console.log(chalk.gray(` 📍 ${warning.path}`));
|
|
66
106
|
}
|
|
67
107
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
108
|
+
if (result.warnings.length > 10) {
|
|
109
|
+
console.log(chalk.gray(` ... and ${result.warnings.length - 10} more warnings`));
|
|
110
|
+
}
|
|
71
111
|
}
|
|
112
|
+
this.showPackV2Summary(pack, result);
|
|
113
|
+
return { valid: true, exitCode: 0 };
|
|
72
114
|
}
|
|
73
|
-
|
|
74
|
-
spinner.fail(chalk.red('❌
|
|
75
|
-
console.log(chalk.red(
|
|
115
|
+
else {
|
|
116
|
+
spinner.fail(chalk.red('❌ Pack v2 validation failed'));
|
|
117
|
+
console.log(chalk.red('\n🔍 Errors found:'));
|
|
118
|
+
for (const error of result.errors.slice(0, 10)) {
|
|
119
|
+
console.log(chalk.red(` • ${error.code}: ${error.message}`));
|
|
120
|
+
if (error.path) {
|
|
121
|
+
console.log(chalk.gray(` 📍 ${error.path}`));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (result.errors.length > 10) {
|
|
125
|
+
console.log(chalk.gray(` ... and ${result.errors.length - 10} more errors`));
|
|
126
|
+
}
|
|
76
127
|
return { valid: false, exitCode: 1 };
|
|
77
128
|
}
|
|
78
129
|
}
|
|
@@ -80,12 +131,12 @@ export class QA360Pack {
|
|
|
80
131
|
* Lint and auto-fix pack.yml
|
|
81
132
|
*/
|
|
82
133
|
async lint(packPath) {
|
|
83
|
-
const spinner = ora('🔧
|
|
134
|
+
const spinner = ora('🔧 Auto-fixing pack...').start();
|
|
84
135
|
try {
|
|
85
136
|
const targetPath = packPath || this.packPath;
|
|
86
137
|
if (!existsSync(targetPath)) {
|
|
87
|
-
spinner.fail(chalk.red('❌ Pack
|
|
88
|
-
console.log(chalk.yellow(`📁
|
|
138
|
+
spinner.fail(chalk.red('❌ Pack not found'));
|
|
139
|
+
console.log(chalk.yellow(`📁 Path: ${targetPath}`));
|
|
89
140
|
return { success: false, exitCode: 1 };
|
|
90
141
|
}
|
|
91
142
|
// Load pack
|
|
@@ -95,19 +146,29 @@ export class QA360Pack {
|
|
|
95
146
|
pack = yaml.load(packContent);
|
|
96
147
|
}
|
|
97
148
|
catch (parseError) {
|
|
98
|
-
spinner.fail(chalk.red('❌
|
|
99
|
-
console.log(chalk.red(`\n🔍
|
|
149
|
+
spinner.fail(chalk.red('❌ YAML parse error'));
|
|
150
|
+
console.log(chalk.red(`\n🔍 Error: ${parseError instanceof Error ? parseError.message : 'Parse error'}`));
|
|
100
151
|
return { success: false, exitCode: 1 };
|
|
101
152
|
}
|
|
102
153
|
// Apply auto-fixes
|
|
103
154
|
const fixes = this.applyAutoFixes(pack);
|
|
104
155
|
if (fixes.length === 0) {
|
|
105
|
-
spinner.succeed(chalk.green('✅ Pack
|
|
156
|
+
spinner.succeed(chalk.green('✅ Pack already valid'));
|
|
106
157
|
return { success: true, exitCode: 0 };
|
|
107
158
|
}
|
|
108
159
|
// Validate after fixes
|
|
109
|
-
const
|
|
110
|
-
|
|
160
|
+
const detectedVersion = this.detectPackVersion(pack);
|
|
161
|
+
let valid = false;
|
|
162
|
+
if (detectedVersion === 2) {
|
|
163
|
+
const validator = new PackValidatorV2(targetPath);
|
|
164
|
+
const result = await validator.validate(pack);
|
|
165
|
+
valid = result.valid || result.errors.length < 5;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const result = this.validatorV1.validate(pack);
|
|
169
|
+
valid = result.valid || result.errors.length < 5;
|
|
170
|
+
}
|
|
171
|
+
if (valid) {
|
|
111
172
|
// Save fixed pack
|
|
112
173
|
const fixedYaml = yaml.dump(pack, {
|
|
113
174
|
indent: 2,
|
|
@@ -115,112 +176,132 @@ export class QA360Pack {
|
|
|
115
176
|
noRefs: true
|
|
116
177
|
});
|
|
117
178
|
writeFileSync(targetPath, fixedYaml, 'utf8');
|
|
118
|
-
spinner.succeed(chalk.green('✅ Pack
|
|
119
|
-
console.log(chalk.blue('\n🔧
|
|
179
|
+
spinner.succeed(chalk.green('✅ Pack fixed and saved'));
|
|
180
|
+
console.log(chalk.blue('\n🔧 Fixes applied:'));
|
|
120
181
|
for (const fix of fixes) {
|
|
121
182
|
console.log(chalk.green(` • ${fix}`));
|
|
122
183
|
}
|
|
123
|
-
// Show remaining issues
|
|
124
|
-
if (result.errors.length > 0) {
|
|
125
|
-
console.log(chalk.yellow('\n⚠️ Corrections manuelles requises:'));
|
|
126
|
-
for (const error of result.errors.slice(0, 3)) { // Show max 3
|
|
127
|
-
console.log(chalk.yellow(` • ${error.message}`));
|
|
128
|
-
if (error.suggestion) {
|
|
129
|
-
console.log(chalk.blue(` 💡 ${error.suggestion}`));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
184
|
return { success: true, exitCode: 0 };
|
|
134
185
|
}
|
|
135
186
|
else {
|
|
136
|
-
spinner.fail(chalk.red('❌
|
|
137
|
-
console.log(chalk.red('\n🔍
|
|
187
|
+
spinner.fail(chalk.red('❌ Auto-fix insufficient'));
|
|
188
|
+
console.log(chalk.red('\n🔍 Manual fixes required'));
|
|
138
189
|
return { success: false, exitCode: 1 };
|
|
139
190
|
}
|
|
140
191
|
}
|
|
141
192
|
catch (error) {
|
|
142
|
-
spinner.fail(chalk.red('❌
|
|
143
|
-
console.log(chalk.red(`\n🔍
|
|
193
|
+
spinner.fail(chalk.red('❌ Lint error'));
|
|
194
|
+
console.log(chalk.red(`\n🔍 Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
144
195
|
return { success: false, exitCode: 1 };
|
|
145
196
|
}
|
|
146
197
|
}
|
|
147
198
|
/**
|
|
148
|
-
* Migrate
|
|
199
|
+
* Migrate pack to v2
|
|
149
200
|
*/
|
|
150
|
-
async migrate(packPath) {
|
|
151
|
-
const spinner = ora('🔄
|
|
201
|
+
async migrate(packPath, options) {
|
|
202
|
+
const spinner = ora('🔄 Migrating pack to v2...').start();
|
|
152
203
|
try {
|
|
153
204
|
const targetPath = packPath || this.packPath;
|
|
154
205
|
if (!existsSync(targetPath)) {
|
|
155
|
-
spinner.fail(chalk.red('❌ Pack
|
|
156
|
-
console.log(chalk.yellow(`📁
|
|
206
|
+
spinner.fail(chalk.red('❌ Pack not found'));
|
|
207
|
+
console.log(chalk.yellow(`📁 Path: ${targetPath}`));
|
|
157
208
|
return { success: false, exitCode: 1 };
|
|
158
209
|
}
|
|
159
|
-
// Load
|
|
210
|
+
// Load pack
|
|
160
211
|
const packContent = readFileSync(targetPath, 'utf8');
|
|
161
|
-
let
|
|
212
|
+
let pack;
|
|
162
213
|
try {
|
|
163
|
-
|
|
214
|
+
pack = yaml.load(packContent);
|
|
164
215
|
}
|
|
165
216
|
catch (parseError) {
|
|
166
|
-
spinner.fail(chalk.red('❌
|
|
167
|
-
console.log(chalk.red(`\n🔍
|
|
217
|
+
spinner.fail(chalk.red('❌ YAML parse error'));
|
|
218
|
+
console.log(chalk.red(`\n🔍 Error: ${parseError instanceof Error ? parseError.message : 'Parse error'}`));
|
|
168
219
|
return { success: false, exitCode: 1 };
|
|
169
220
|
}
|
|
170
|
-
// Check
|
|
171
|
-
|
|
172
|
-
|
|
221
|
+
// Check current version
|
|
222
|
+
const detectedVersion = this.detectPackVersion(pack);
|
|
223
|
+
if (detectedVersion === 2) {
|
|
224
|
+
spinner.succeed(chalk.green('✅ Pack already in v2 format'));
|
|
173
225
|
return { success: true, exitCode: 0 };
|
|
174
226
|
}
|
|
175
|
-
|
|
176
|
-
|
|
227
|
+
if (detectedVersion !== 1) {
|
|
228
|
+
spinner.fail(chalk.yellow('⚠️ Unknown pack format'));
|
|
229
|
+
console.log(chalk.yellow(`Version detected: ${pack.version || 'unknown'}`));
|
|
230
|
+
return { success: false, exitCode: 1 };
|
|
231
|
+
}
|
|
232
|
+
// Migrate v1 to v2
|
|
233
|
+
const migrator = new PackMigrator();
|
|
234
|
+
const migrationResult = migrator.migrate(pack);
|
|
177
235
|
if (!migrationResult.success) {
|
|
178
|
-
spinner.fail(chalk.red('❌
|
|
179
|
-
console.log(chalk.red('\n🔍
|
|
236
|
+
spinner.fail(chalk.red('❌ Migration failed'));
|
|
237
|
+
console.log(chalk.red('\n🔍 Errors:'));
|
|
180
238
|
for (const warning of migrationResult.warnings) {
|
|
181
239
|
console.log(chalk.red(` • ${warning}`));
|
|
182
240
|
}
|
|
183
241
|
return { success: false, exitCode: 1 };
|
|
184
242
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
243
|
+
const outputPath = options?.output || targetPath;
|
|
244
|
+
if (!options?.dryRun) {
|
|
245
|
+
// Create backup
|
|
246
|
+
const backupPath = `${outputPath}.backup`;
|
|
247
|
+
writeFileSync(backupPath, packContent, 'utf8');
|
|
248
|
+
// Save migrated pack
|
|
249
|
+
const migratedYaml = yaml.dump(migrationResult.migrated, {
|
|
250
|
+
indent: 2,
|
|
251
|
+
lineWidth: 120,
|
|
252
|
+
noRefs: true
|
|
253
|
+
});
|
|
254
|
+
writeFileSync(outputPath, migratedYaml, 'utf8');
|
|
255
|
+
}
|
|
256
|
+
spinner.succeed(chalk.green('✅ Migration successful'));
|
|
257
|
+
console.log(chalk.blue(`\n🔄 Migration v1 → v2`));
|
|
258
|
+
if (options?.dryRun) {
|
|
259
|
+
console.log(chalk.yellow(' ⚠️ Dry run mode - no files written'));
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
console.log(chalk.gray(`📁 Output: ${outputPath}`));
|
|
263
|
+
console.log(chalk.gray(`📁 Backup: ${outputPath}.backup`));
|
|
264
|
+
}
|
|
265
|
+
console.log(chalk.blue('\n🔧 Changes applied:'));
|
|
266
|
+
for (const change of migrationResult.changes.slice(0, 10)) {
|
|
267
|
+
console.log(chalk.green(` • ${change}`));
|
|
268
|
+
}
|
|
269
|
+
if (migrationResult.changes.length > 10) {
|
|
270
|
+
console.log(chalk.gray(` ... and ${migrationResult.changes.length - 10} more changes`));
|
|
271
|
+
}
|
|
209
272
|
if (migrationResult.warnings.length > 0) {
|
|
210
|
-
console.log(chalk.yellow('\n⚠️
|
|
273
|
+
console.log(chalk.yellow('\n⚠️ Warnings:'));
|
|
211
274
|
for (const warning of migrationResult.warnings) {
|
|
212
275
|
console.log(chalk.yellow(` • ${warning}`));
|
|
213
276
|
}
|
|
214
277
|
}
|
|
215
|
-
console.log(chalk.blue('\n
|
|
278
|
+
console.log(chalk.blue('\n💡 Next: qa360 pack validate'));
|
|
216
279
|
return { success: true, exitCode: 0 };
|
|
217
280
|
}
|
|
218
281
|
catch (error) {
|
|
219
|
-
spinner.fail(chalk.red('❌
|
|
220
|
-
console.log(chalk.red(`\n🔍
|
|
282
|
+
spinner.fail(chalk.red('❌ Migration error'));
|
|
283
|
+
console.log(chalk.red(`\n🔍 Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
221
284
|
return { success: false, exitCode: 1 };
|
|
222
285
|
}
|
|
223
286
|
}
|
|
287
|
+
/**
|
|
288
|
+
* Detect pack version
|
|
289
|
+
*/
|
|
290
|
+
detectPackVersion(pack) {
|
|
291
|
+
if (pack.version === 2)
|
|
292
|
+
return 2;
|
|
293
|
+
if (pack.version === 1)
|
|
294
|
+
return 1;
|
|
295
|
+
// Heuristic detection
|
|
296
|
+
if (pack.gates && Array.isArray(pack.gates))
|
|
297
|
+
return 1;
|
|
298
|
+
if (pack.gates && typeof pack.gates === 'object' && !Array.isArray(pack.gates)) {
|
|
299
|
+
const firstGate = Object.values(pack.gates)[0];
|
|
300
|
+
if (firstGate?.test_files || firstGate?.adapter)
|
|
301
|
+
return 2;
|
|
302
|
+
}
|
|
303
|
+
return 'unknown';
|
|
304
|
+
}
|
|
224
305
|
/**
|
|
225
306
|
* Apply automatic fixes to pack
|
|
226
307
|
*/
|
|
@@ -228,51 +309,42 @@ export class QA360Pack {
|
|
|
228
309
|
const fixes = [];
|
|
229
310
|
// Add version if missing
|
|
230
311
|
if (!pack.version) {
|
|
231
|
-
pack.version =
|
|
232
|
-
fixes.push('
|
|
312
|
+
pack.version = 2; // Default to v2 now
|
|
313
|
+
fixes.push('Added version: 2');
|
|
233
314
|
}
|
|
234
315
|
// Add default execution if missing
|
|
235
|
-
if (!pack.execution) {
|
|
316
|
+
if (!pack.execution && pack.version === 1) {
|
|
236
317
|
pack.execution = {
|
|
237
318
|
retry_on: ['ECONNRESET', '502'],
|
|
238
319
|
max_retries: 1,
|
|
239
320
|
fail_on_readiness: true,
|
|
240
321
|
timeout: 30000
|
|
241
322
|
};
|
|
242
|
-
fixes.push('
|
|
323
|
+
fixes.push('Added default execution configuration');
|
|
243
324
|
}
|
|
244
|
-
// Add default
|
|
245
|
-
if (!pack.
|
|
246
|
-
pack.
|
|
247
|
-
|
|
248
|
-
|
|
325
|
+
// Add default execution for v2
|
|
326
|
+
if (!pack.execution && pack.version === 2) {
|
|
327
|
+
pack.execution = {
|
|
328
|
+
on_failure: 'stop',
|
|
329
|
+
default_timeout: 30000,
|
|
330
|
+
default_retries: 1
|
|
249
331
|
};
|
|
250
|
-
fixes.push('
|
|
332
|
+
fixes.push('Added default execution configuration (v2)');
|
|
251
333
|
}
|
|
252
334
|
// Fix name pattern if needed
|
|
253
335
|
if (pack.name && !/^[a-zA-Z0-9_-]+$/.test(pack.name)) {
|
|
254
336
|
const oldName = pack.name;
|
|
255
337
|
pack.name = pack.name.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
256
|
-
fixes.push(`
|
|
257
|
-
}
|
|
258
|
-
// Add default gates if missing
|
|
259
|
-
if (!pack.gates || pack.gates.length === 0) {
|
|
260
|
-
pack.gates = ['api_smoke'];
|
|
261
|
-
fixes.push('Ajout gate par défaut: api_smoke');
|
|
262
|
-
}
|
|
263
|
-
// Add security.secrets_leak if security section exists
|
|
264
|
-
if (pack.security && pack.security.secrets_leak === undefined) {
|
|
265
|
-
pack.security.secrets_leak = 0;
|
|
266
|
-
fixes.push('Ajout security.secrets_leak: 0');
|
|
338
|
+
fixes.push(`Fixed name: "${oldName}" → "${pack.name}"`);
|
|
267
339
|
}
|
|
268
340
|
return fixes;
|
|
269
341
|
}
|
|
270
342
|
/**
|
|
271
|
-
* Show pack summary
|
|
343
|
+
* Show v1 pack summary
|
|
272
344
|
*/
|
|
273
345
|
showPackSummary(pack) {
|
|
274
|
-
console.log(chalk.blue('\n📋
|
|
275
|
-
console.log(chalk.gray(` 📦
|
|
346
|
+
console.log(chalk.blue('\n📋 Pack summary:'));
|
|
347
|
+
console.log(chalk.gray(` 📦 Name: ${pack.name}`));
|
|
276
348
|
console.log(chalk.gray(` 🎯 Gates: ${pack.gates.join(', ')}`));
|
|
277
349
|
if (pack.targets?.api) {
|
|
278
350
|
console.log(chalk.gray(` 🌐 API: ${pack.targets.api.baseUrl}`));
|
|
@@ -292,14 +364,41 @@ export class QA360Pack {
|
|
|
292
364
|
}
|
|
293
365
|
if (pack.environment) {
|
|
294
366
|
const envCount = Object.keys(pack.environment).length;
|
|
295
|
-
console.log(chalk.gray(` 🔧 Variables: ${envCount}
|
|
367
|
+
console.log(chalk.gray(` 🔧 Variables: ${envCount} defined`));
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Show v2 pack summary
|
|
372
|
+
*/
|
|
373
|
+
showPackV2Summary(pack, validation) {
|
|
374
|
+
console.log(chalk.blue('\n📋 Pack v2 summary:'));
|
|
375
|
+
console.log(chalk.gray(` 📦 Name: ${pack.name}`));
|
|
376
|
+
console.log(chalk.gray(` 🎯 Gates: ${Object.keys(pack.gates).join(', ')}`));
|
|
377
|
+
if (validation?.info) {
|
|
378
|
+
const { info } = validation;
|
|
379
|
+
if (info.totalTests !== undefined) {
|
|
380
|
+
console.log(chalk.gray(` 🧪 Tests: ${info.totalTests} found`));
|
|
381
|
+
}
|
|
382
|
+
if (info.frameworks && info.frameworks.length > 0) {
|
|
383
|
+
console.log(chalk.gray(` 🔧 Frameworks: ${info.frameworks.join(', ')}`));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (pack.auth?.profiles) {
|
|
387
|
+
const profileNames = Object.keys(pack.auth.profiles);
|
|
388
|
+
if (profileNames.length > 0) {
|
|
389
|
+
console.log(chalk.gray(` 🔐 Auth profiles: ${profileNames.join(', ')}`));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (pack.variables) {
|
|
393
|
+
const varCount = Object.keys(pack.variables).length;
|
|
394
|
+
console.log(chalk.gray(` 🔧 Variables: ${varCount} defined`));
|
|
296
395
|
}
|
|
297
396
|
}
|
|
298
397
|
}
|
|
299
398
|
// Command handlers
|
|
300
|
-
export async function packValidateCommand(packPath) {
|
|
399
|
+
export async function packValidateCommand(packPath, options) {
|
|
301
400
|
const pack = new QA360Pack();
|
|
302
|
-
const result = await pack.validate(packPath);
|
|
401
|
+
const result = await pack.validate(packPath, options);
|
|
303
402
|
process.exit(result.exitCode);
|
|
304
403
|
}
|
|
305
404
|
export async function packLintCommand(packPath) {
|
|
@@ -307,8 +406,8 @@ export async function packLintCommand(packPath) {
|
|
|
307
406
|
const result = await pack.lint(packPath);
|
|
308
407
|
process.exit(result.exitCode);
|
|
309
408
|
}
|
|
310
|
-
export async function packMigrateCommand(packPath) {
|
|
409
|
+
export async function packMigrateCommand(packPath, options) {
|
|
311
410
|
const pack = new QA360Pack();
|
|
312
|
-
const result = await pack.migrate(packPath);
|
|
411
|
+
const result = await pack.migrate(packPath, options);
|
|
313
412
|
process.exit(result.exitCode);
|
|
314
413
|
}
|