qa360 1.4.5 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/commands/ai.d.ts +41 -0
- package/dist/commands/ai.js +499 -0
- package/dist/commands/ask.js +12 -12
- package/dist/commands/coverage.d.ts +8 -0
- package/dist/commands/coverage.js +252 -0
- package/dist/commands/explain.d.ts +27 -0
- package/dist/commands/explain.js +630 -0
- package/dist/commands/flakiness.d.ts +73 -0
- package/dist/commands/flakiness.js +435 -0
- package/dist/commands/generate.d.ts +66 -0
- package/dist/commands/generate.js +438 -0
- package/dist/commands/init.d.ts +56 -9
- package/dist/commands/init.js +217 -10
- package/dist/commands/monitor.d.ts +27 -0
- package/dist/commands/monitor.js +225 -0
- package/dist/commands/ollama.d.ts +40 -0
- package/dist/commands/ollama.js +301 -0
- package/dist/commands/pack.d.ts +37 -9
- package/dist/commands/pack.js +240 -141
- package/dist/commands/regression.d.ts +8 -0
- package/dist/commands/regression.js +340 -0
- package/dist/commands/repair.d.ts +26 -0
- package/dist/commands/repair.js +307 -0
- package/dist/commands/retry.d.ts +43 -0
- package/dist/commands/retry.js +275 -0
- package/dist/commands/run.d.ts +8 -3
- package/dist/commands/run.js +45 -31
- package/dist/commands/slo.d.ts +8 -0
- package/dist/commands/slo.js +327 -0
- package/dist/core/adapters/playwright-native-api.d.ts +183 -0
- package/dist/core/adapters/playwright-native-api.js +461 -0
- package/dist/core/adapters/playwright-ui.d.ts +7 -0
- package/dist/core/adapters/playwright-ui.js +29 -1
- package/dist/core/ai/anthropic-provider.d.ts +50 -0
- package/dist/core/ai/anthropic-provider.js +211 -0
- package/dist/core/ai/deepseek-provider.d.ts +81 -0
- package/dist/core/ai/deepseek-provider.js +254 -0
- package/dist/core/ai/index.d.ts +60 -0
- package/dist/core/ai/index.js +18 -0
- package/dist/core/ai/llm-client.d.ts +45 -0
- package/dist/core/ai/llm-client.js +7 -0
- package/dist/core/ai/mock-provider.d.ts +49 -0
- package/dist/core/ai/mock-provider.js +121 -0
- package/dist/core/ai/ollama-provider.d.ts +78 -0
- package/dist/core/ai/ollama-provider.js +192 -0
- package/dist/core/ai/openai-provider.d.ts +48 -0
- package/dist/core/ai/openai-provider.js +188 -0
- package/dist/core/ai/provider-factory.d.ts +160 -0
- package/dist/core/ai/provider-factory.js +269 -0
- package/dist/core/auth/api-key-provider.d.ts +16 -0
- package/dist/core/auth/api-key-provider.js +63 -0
- package/dist/core/auth/aws-iam-provider.d.ts +35 -0
- package/dist/core/auth/aws-iam-provider.js +177 -0
- package/dist/core/auth/azure-ad-provider.d.ts +15 -0
- package/dist/core/auth/azure-ad-provider.js +99 -0
- package/dist/core/auth/basic-auth-provider.d.ts +26 -0
- package/dist/core/auth/basic-auth-provider.js +111 -0
- package/dist/core/auth/gcp-adc-provider.d.ts +27 -0
- package/dist/core/auth/gcp-adc-provider.js +126 -0
- package/dist/core/auth/index.d.ts +238 -0
- package/dist/core/auth/index.js +82 -0
- package/dist/core/auth/jwt-provider.d.ts +19 -0
- package/dist/core/auth/jwt-provider.js +160 -0
- package/dist/core/auth/manager.d.ts +84 -0
- package/dist/core/auth/manager.js +230 -0
- package/dist/core/auth/oauth2-provider.d.ts +17 -0
- package/dist/core/auth/oauth2-provider.js +114 -0
- package/dist/core/auth/totp-provider.d.ts +31 -0
- package/dist/core/auth/totp-provider.js +134 -0
- package/dist/core/auth/ui-login-provider.d.ts +26 -0
- package/dist/core/auth/ui-login-provider.js +198 -0
- package/dist/core/cache/index.d.ts +7 -0
- package/dist/core/cache/index.js +6 -0
- package/dist/core/cache/lru-cache.d.ts +203 -0
- package/dist/core/cache/lru-cache.js +397 -0
- package/dist/core/coverage/analyzer.d.ts +101 -0
- package/dist/core/coverage/analyzer.js +415 -0
- package/dist/core/coverage/collector.d.ts +74 -0
- package/dist/core/coverage/collector.js +459 -0
- package/dist/core/coverage/config.d.ts +37 -0
- package/dist/core/coverage/config.js +156 -0
- package/dist/core/coverage/index.d.ts +11 -0
- package/dist/core/coverage/index.js +15 -0
- package/dist/core/coverage/types.d.ts +267 -0
- package/dist/core/coverage/types.js +6 -0
- package/dist/core/coverage/vault.d.ts +95 -0
- package/dist/core/coverage/vault.js +405 -0
- package/dist/core/dashboard/assets.d.ts +6 -0
- package/dist/core/dashboard/assets.js +690 -0
- package/dist/core/dashboard/index.d.ts +6 -0
- package/dist/core/dashboard/index.js +5 -0
- package/dist/core/dashboard/server.d.ts +72 -0
- package/dist/core/dashboard/server.js +354 -0
- package/dist/core/dashboard/types.d.ts +70 -0
- package/dist/core/dashboard/types.js +5 -0
- package/dist/core/discoverer/index.d.ts +115 -0
- package/dist/core/discoverer/index.js +250 -0
- package/dist/core/flakiness/index.d.ts +228 -0
- package/dist/core/flakiness/index.js +384 -0
- package/dist/core/generation/code-formatter.d.ts +111 -0
- package/dist/core/generation/code-formatter.js +307 -0
- package/dist/core/generation/code-generator.d.ts +144 -0
- package/dist/core/generation/code-generator.js +293 -0
- package/dist/core/generation/generator.d.ts +40 -0
- package/dist/core/generation/generator.js +76 -0
- package/dist/core/generation/index.d.ts +30 -0
- package/dist/core/generation/index.js +28 -0
- package/dist/core/generation/pack-generator.d.ts +107 -0
- package/dist/core/generation/pack-generator.js +416 -0
- package/dist/core/generation/prompt-builder.d.ts +132 -0
- package/dist/core/generation/prompt-builder.js +672 -0
- package/dist/core/generation/source-analyzer.d.ts +213 -0
- package/dist/core/generation/source-analyzer.js +657 -0
- package/dist/core/generation/test-optimizer.d.ts +117 -0
- package/dist/core/generation/test-optimizer.js +328 -0
- package/dist/core/generation/types.d.ts +214 -0
- package/dist/core/generation/types.js +4 -0
- package/dist/core/index.d.ts +23 -1
- package/dist/core/index.js +39 -0
- package/dist/core/pack/validator.js +31 -1
- package/dist/core/pack-v2/index.d.ts +9 -0
- package/dist/core/pack-v2/index.js +8 -0
- package/dist/core/pack-v2/loader.d.ts +62 -0
- package/dist/core/pack-v2/loader.js +231 -0
- package/dist/core/pack-v2/migrator.d.ts +56 -0
- package/dist/core/pack-v2/migrator.js +455 -0
- package/dist/core/pack-v2/validator.d.ts +61 -0
- package/dist/core/pack-v2/validator.js +577 -0
- package/dist/core/regression/detector.d.ts +107 -0
- package/dist/core/regression/detector.js +497 -0
- package/dist/core/regression/index.d.ts +9 -0
- package/dist/core/regression/index.js +11 -0
- package/dist/core/regression/trend-analyzer.d.ts +102 -0
- package/dist/core/regression/trend-analyzer.js +345 -0
- package/dist/core/regression/types.d.ts +222 -0
- package/dist/core/regression/types.js +7 -0
- package/dist/core/regression/vault.d.ts +87 -0
- package/dist/core/regression/vault.js +289 -0
- package/dist/core/repair/engine/fixer.d.ts +24 -0
- package/dist/core/repair/engine/fixer.js +226 -0
- package/dist/core/repair/engine/suggestion-engine.d.ts +18 -0
- package/dist/core/repair/engine/suggestion-engine.js +187 -0
- package/dist/core/repair/index.d.ts +10 -0
- package/dist/core/repair/index.js +13 -0
- package/dist/core/repair/repairer.d.ts +90 -0
- package/dist/core/repair/repairer.js +284 -0
- package/dist/core/repair/types.d.ts +91 -0
- package/dist/core/repair/types.js +6 -0
- package/dist/core/repair/utils/error-analyzer.d.ts +28 -0
- package/dist/core/repair/utils/error-analyzer.js +264 -0
- package/dist/core/retry/flakiness-integration.d.ts +60 -0
- package/dist/core/retry/flakiness-integration.js +228 -0
- package/dist/core/retry/index.d.ts +14 -0
- package/dist/core/retry/index.js +16 -0
- package/dist/core/retry/retry-engine.d.ts +80 -0
- package/dist/core/retry/retry-engine.js +296 -0
- package/dist/core/retry/types.d.ts +178 -0
- package/dist/core/retry/types.js +52 -0
- package/dist/core/retry/vault.d.ts +77 -0
- package/dist/core/retry/vault.js +304 -0
- package/dist/core/runner/e2e-helpers.d.ts +102 -0
- package/dist/core/runner/e2e-helpers.js +153 -0
- package/dist/core/runner/phase3-runner.d.ts +101 -2
- package/dist/core/runner/phase3-runner.js +559 -24
- package/dist/core/self-healing/assertion-healer.d.ts +97 -0
- package/dist/core/self-healing/assertion-healer.js +371 -0
- package/dist/core/self-healing/engine.d.ts +122 -0
- package/dist/core/self-healing/engine.js +538 -0
- package/dist/core/self-healing/index.d.ts +10 -0
- package/dist/core/self-healing/index.js +11 -0
- package/dist/core/self-healing/selector-healer.d.ts +103 -0
- package/dist/core/self-healing/selector-healer.js +372 -0
- package/dist/core/self-healing/types.d.ts +152 -0
- package/dist/core/self-healing/types.js +6 -0
- package/dist/core/slo/config.d.ts +107 -0
- package/dist/core/slo/config.js +360 -0
- package/dist/core/slo/index.d.ts +11 -0
- package/dist/core/slo/index.js +15 -0
- package/dist/core/slo/sli-calculator.d.ts +92 -0
- package/dist/core/slo/sli-calculator.js +364 -0
- package/dist/core/slo/slo-tracker.d.ts +148 -0
- package/dist/core/slo/slo-tracker.js +379 -0
- package/dist/core/slo/types.d.ts +281 -0
- package/dist/core/slo/types.js +7 -0
- package/dist/core/slo/vault.d.ts +102 -0
- package/dist/core/slo/vault.js +427 -0
- package/dist/core/tui/index.d.ts +7 -0
- package/dist/core/tui/index.js +6 -0
- package/dist/core/tui/monitor.d.ts +92 -0
- package/dist/core/tui/monitor.js +271 -0
- package/dist/core/tui/renderer.d.ts +33 -0
- package/dist/core/tui/renderer.js +218 -0
- package/dist/core/tui/types.d.ts +63 -0
- package/dist/core/tui/types.js +5 -0
- package/dist/core/types/pack-v2.d.ts +425 -0
- package/dist/core/types/pack-v2.js +8 -0
- package/dist/core/vault/index.d.ts +116 -0
- package/dist/core/vault/index.js +400 -5
- package/dist/core/watch/index.d.ts +7 -0
- package/dist/core/watch/index.js +6 -0
- package/dist/core/watch/watch-mode.d.ts +213 -0
- package/dist/core/watch/watch-mode.js +389 -0
- package/dist/index.js +68 -68
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.js +136 -0
- package/package.json +5 -1
- package/dist/core/adapters/playwright-api.d.ts +0 -82
- package/dist/core/adapters/playwright-api.js +0 -264
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Retry Commands (F8 Smart Retry Module)
|
|
3
|
+
*
|
|
4
|
+
* CLI commands for managing and viewing retry statistics and configuration.
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
/**
|
|
8
|
+
* Retry history command
|
|
9
|
+
*/
|
|
10
|
+
export declare function retryHistoryCommand(options: {
|
|
11
|
+
testId?: string;
|
|
12
|
+
limit?: number;
|
|
13
|
+
json?: boolean;
|
|
14
|
+
}): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Retry stats command
|
|
17
|
+
*/
|
|
18
|
+
export declare function retryStatsCommand(options: {
|
|
19
|
+
strategy?: string;
|
|
20
|
+
json?: boolean;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Retry configure command
|
|
24
|
+
*/
|
|
25
|
+
export declare function retryConfigureCommand(options: {
|
|
26
|
+
strategy?: string;
|
|
27
|
+
maxRetries?: number;
|
|
28
|
+
delay?: number;
|
|
29
|
+
jitter?: number;
|
|
30
|
+
show?: boolean;
|
|
31
|
+
}): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Retry test command
|
|
34
|
+
*/
|
|
35
|
+
export declare function retryTestCommand(options: {
|
|
36
|
+
testId?: string;
|
|
37
|
+
strategy?: string;
|
|
38
|
+
dryRun?: boolean;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Create retry commands
|
|
42
|
+
*/
|
|
43
|
+
export declare function createRetryCommands(): Command;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Retry Commands (F8 Smart Retry Module)
|
|
3
|
+
*
|
|
4
|
+
* CLI commands for managing and viewing retry statistics and configuration.
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import Table from 'cli-table3';
|
|
9
|
+
import { createSmartRetryEngine, RetryStrategy } from '../core/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Display retry statistics in a formatted table
|
|
12
|
+
*/
|
|
13
|
+
function displayStats(stats) {
|
|
14
|
+
const table = new Table({
|
|
15
|
+
head: [chalk.bold('Metric'), chalk.bold('Value')],
|
|
16
|
+
style: { head: ['cyan', 'bold'] }
|
|
17
|
+
});
|
|
18
|
+
table.push(['Total Retries', stats.totalRetries.toString()], ['Tests Retried', stats.testsRetried.toString()], ['Recovered Tests', stats.recoveredTests.toString()], ['Failed Tests', stats.failedTests.toString()], ['Recovery Rate', `${stats.recoveryRate.toFixed(1)}%`], ['Avg Attempts/Test', stats.avgAttemptsPerTest.toFixed(1)], ['Total Retry Time', `${stats.totalRetryTimeMs}ms`]);
|
|
19
|
+
console.log('\n' + chalk.bold.blue('📊 Retry Statistics'));
|
|
20
|
+
console.log(table.toString() + '\n');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Display retry strategy comparison
|
|
24
|
+
*/
|
|
25
|
+
function displayStrategyComparison(byStrategy) {
|
|
26
|
+
const table = new Table({
|
|
27
|
+
head: [chalk.bold('Strategy'), chalk.bold('Attempts'), chalk.bold('Successes'), chalk.bold('Avg Duration')],
|
|
28
|
+
style: { head: ['cyan', 'bold'] }
|
|
29
|
+
});
|
|
30
|
+
for (const [strategy, stats] of Object.entries(byStrategy)) {
|
|
31
|
+
table.push([
|
|
32
|
+
strategy,
|
|
33
|
+
stats.attempts.toString(),
|
|
34
|
+
stats.successes.toString(),
|
|
35
|
+
`${stats.avgDurationMs}ms`,
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
console.log(chalk.bold.blue('\n🎯 Performance by Strategy'));
|
|
39
|
+
console.log(table.toString() + '\n');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Display retry configuration
|
|
43
|
+
*/
|
|
44
|
+
function displayConfig(config) {
|
|
45
|
+
const table = new Table({
|
|
46
|
+
head: [chalk.bold('Setting'), chalk.bold('Value')],
|
|
47
|
+
style: { head: ['cyan', 'bold'] }
|
|
48
|
+
});
|
|
49
|
+
table.push(['Max Retries', config.maxRetries.toString()], ['Initial Delay', `${config.initialDelayMs}ms`], ['Max Delay', `${config.maxDelayMs}ms`], ['Attempt Timeout', `${config.attemptTimeoutMs}ms`], ['Overall Timeout', `${config.overallTimeoutMs}ms`], ['Strategy', config.strategy], ['Backoff Multiplier', config.backoffMultiplier.toString()], ['Jitter Factor', config.jitterFactor.toString()], ['Retry on Timeout', config.retryOnTimeout ? 'Yes' : 'No'], ['Retry on Assertion', config.retryOnAssertionFailure ? 'Yes' : 'No']);
|
|
50
|
+
console.log('\n' + chalk.bold.blue('⚙️ Retry Configuration'));
|
|
51
|
+
console.log(table.toString() + '\n');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Display flakiness-based recommendations
|
|
55
|
+
*/
|
|
56
|
+
function displayRecommendations() {
|
|
57
|
+
const engine = createSmartRetryEngine();
|
|
58
|
+
const recommendations = [
|
|
59
|
+
{ score: 95, category: 'Legendary/Solid' },
|
|
60
|
+
{ score: 80, category: 'Good' },
|
|
61
|
+
{ score: 60, category: 'Shaky' },
|
|
62
|
+
{ score: 30, category: 'Unstable' },
|
|
63
|
+
];
|
|
64
|
+
const table = new Table({
|
|
65
|
+
head: [chalk.bold('Flakiness Score'), chalk.bold('Category'), chalk.bold('Strategy'), chalk.bold('Max Retries'), chalk.bold('Initial Delay')],
|
|
66
|
+
style: { head: ['cyan', 'bold'] }
|
|
67
|
+
});
|
|
68
|
+
for (const { score, category } of recommendations) {
|
|
69
|
+
const rec = engine.getRetryRecommendation({ flakinessScore: score });
|
|
70
|
+
table.push([
|
|
71
|
+
`${score}%`,
|
|
72
|
+
category,
|
|
73
|
+
rec.strategy,
|
|
74
|
+
rec.maxRetries.toString(),
|
|
75
|
+
`${rec.initialDelayMs}ms`,
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
console.log(chalk.bold.blue('\n🎯 Flakiness-Based Recommendations'));
|
|
79
|
+
console.log(table.toString() + '\n');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Retry history command
|
|
83
|
+
*/
|
|
84
|
+
export async function retryHistoryCommand(options) {
|
|
85
|
+
if (options.json) {
|
|
86
|
+
console.log(JSON.stringify({
|
|
87
|
+
message: 'Retry history feature - querying vault for retry records',
|
|
88
|
+
testId: options.testId,
|
|
89
|
+
limit: options.limit,
|
|
90
|
+
}, null, 2));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
console.log(chalk.blue('\n📜 Retry History'));
|
|
94
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
95
|
+
if (options.testId) {
|
|
96
|
+
console.log(chalk.yellow(`Test ID: ${options.testId}`));
|
|
97
|
+
}
|
|
98
|
+
console.log(chalk.yellow('\nNote: Full history querying will be implemented with vault integration.'));
|
|
99
|
+
console.log(chalk.gray('Use: qa360 history list --type retry to see retry-related history.\n'));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Retry stats command
|
|
103
|
+
*/
|
|
104
|
+
export async function retryStatsCommand(options) {
|
|
105
|
+
const mockStats = {
|
|
106
|
+
totalRetries: 1234,
|
|
107
|
+
testsRetried: 156,
|
|
108
|
+
recoveredTests: 142,
|
|
109
|
+
failedTests: 14,
|
|
110
|
+
recoveryRate: 91.0,
|
|
111
|
+
avgAttemptsPerTest: 2.3,
|
|
112
|
+
totalRetryTimeMs: 45678,
|
|
113
|
+
byStrategy: {
|
|
114
|
+
[RetryStrategy.NONE]: { attempts: 0, successes: 0, avgDurationMs: 0 },
|
|
115
|
+
[RetryStrategy.FIXED]: { attempts: 234, successes: 210, avgDurationMs: 1200 },
|
|
116
|
+
[RetryStrategy.LINEAR]: { attempts: 123, successes: 115, avgDurationMs: 1800 },
|
|
117
|
+
[RetryStrategy.EXPONENTIAL]: { attempts: 456, successes: 420, avgDurationMs: 2500 },
|
|
118
|
+
[RetryStrategy.ADAPTIVE]: { attempts: 345, successes: 320, avgDurationMs: 2000 },
|
|
119
|
+
[RetryStrategy.INTELLIGENT]: { attempts: 76, successes: 68, avgDurationMs: 3200 },
|
|
120
|
+
},
|
|
121
|
+
byErrorType: {
|
|
122
|
+
'TimeoutError': 234,
|
|
123
|
+
'NetworkError': 345,
|
|
124
|
+
'AssertionError': 123,
|
|
125
|
+
'ValidationError': 89,
|
|
126
|
+
'Other': 443,
|
|
127
|
+
},
|
|
128
|
+
byFlakinessScore: {
|
|
129
|
+
low: 234,
|
|
130
|
+
medium: 567,
|
|
131
|
+
high: 433,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
if (options.strategy) {
|
|
135
|
+
const strategyStats = mockStats.byStrategy[options.strategy];
|
|
136
|
+
if (!strategyStats) {
|
|
137
|
+
console.log(chalk.red(`\nUnknown strategy: ${options.strategy}`));
|
|
138
|
+
console.log(chalk.gray('Available strategies: none, fixed, linear, exponential, adaptive, intelligent\n'));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (options.json) {
|
|
142
|
+
console.log(JSON.stringify(strategyStats, null, 2));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const table = new Table({
|
|
146
|
+
head: [chalk.bold('Metric'), chalk.bold('Value')],
|
|
147
|
+
style: { head: ['cyan', 'bold'] }
|
|
148
|
+
});
|
|
149
|
+
table.push(['Strategy', options.strategy], ['Total Attempts', strategyStats.attempts.toString()], ['Successes', strategyStats.successes.toString()], ['Success Rate', `${((strategyStats.successes / strategyStats.attempts) * 100).toFixed(1)}%`], ['Avg Duration', `${strategyStats.avgDurationMs}ms`]);
|
|
150
|
+
console.log('\n' + chalk.bold.blue(`📊 ${options.strategy} Strategy Statistics`));
|
|
151
|
+
console.log(table.toString() + '\n');
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
if (options.json) {
|
|
155
|
+
console.log(JSON.stringify(mockStats, null, 2));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
displayStats(mockStats);
|
|
159
|
+
displayStrategyComparison(mockStats.byStrategy);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Retry configure command
|
|
164
|
+
*/
|
|
165
|
+
export async function retryConfigureCommand(options) {
|
|
166
|
+
const engine = createSmartRetryEngine();
|
|
167
|
+
if (options.show) {
|
|
168
|
+
displayConfig(engine.getConfig());
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const newConfig = {};
|
|
172
|
+
if (options.strategy) {
|
|
173
|
+
const validStrategies = ['none', 'fixed', 'linear', 'exponential', 'adaptive', 'intelligent'];
|
|
174
|
+
if (!validStrategies.includes(options.strategy.toLowerCase())) {
|
|
175
|
+
console.log(chalk.red(`\nInvalid strategy: ${options.strategy}`));
|
|
176
|
+
console.log(chalk.gray('Available strategies: none, fixed, linear, exponential, adaptive, intelligent\n'));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
newConfig.strategy = options.strategy.toUpperCase();
|
|
180
|
+
}
|
|
181
|
+
if (options.maxRetries !== undefined) {
|
|
182
|
+
newConfig.maxRetries = options.maxRetries;
|
|
183
|
+
}
|
|
184
|
+
if (options.delay !== undefined) {
|
|
185
|
+
newConfig.initialDelayMs = options.delay;
|
|
186
|
+
}
|
|
187
|
+
if (options.jitter !== undefined) {
|
|
188
|
+
newConfig.jitterFactor = options.jitter;
|
|
189
|
+
}
|
|
190
|
+
if (Object.keys(newConfig).length === 0) {
|
|
191
|
+
console.log(chalk.yellow('\nNo configuration changes specified.\n'));
|
|
192
|
+
console.log(chalk.gray('Use: qa360 retry configure --show to view current configuration\n'));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
engine.updateConfig(newConfig);
|
|
196
|
+
console.log(chalk.green('\nRetry configuration updated:\n'));
|
|
197
|
+
displayConfig(engine.getConfig());
|
|
198
|
+
console.log(chalk.yellow('Note: Configuration changes apply to the current session only.'));
|
|
199
|
+
console.log(chalk.gray('To persist changes, add them to your qa360.yml config file.\n'));
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Retry test command
|
|
203
|
+
*/
|
|
204
|
+
export async function retryTestCommand(options) {
|
|
205
|
+
const engine = createSmartRetryEngine();
|
|
206
|
+
if (!options.testId) {
|
|
207
|
+
console.log(chalk.yellow('\nPlease specify a test ID with --test-id <id>\n'));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const rec = engine.getRetryRecommendation({
|
|
211
|
+
flakinessScore: 70,
|
|
212
|
+
});
|
|
213
|
+
console.log(chalk.bold.blue(`\nRetry Recommendation for: ${options.testId}`));
|
|
214
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
215
|
+
const table = new Table({
|
|
216
|
+
head: [chalk.bold('Setting'), chalk.bold('Value')],
|
|
217
|
+
style: { head: ['cyan', 'bold'] }
|
|
218
|
+
});
|
|
219
|
+
table.push(['Should Retry', rec.shouldRetry ? chalk.green('Yes') : chalk.red('No')], ['Strategy', rec.strategy], ['Max Retries', rec.maxRetries.toString()], ['Initial Delay', `${rec.initialDelayMs}ms`], ['Confidence', `${(rec.confidence * 100).toFixed(0)}%`], ['Reason', rec.reason]);
|
|
220
|
+
console.log(table.toString() + '\n');
|
|
221
|
+
if (options.dryRun) {
|
|
222
|
+
console.log(chalk.yellow('Dry run mode - no tests executed.\n'));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Create retry commands
|
|
227
|
+
*/
|
|
228
|
+
export function createRetryCommands() {
|
|
229
|
+
const retryCommand = new Command('retry')
|
|
230
|
+
.description('Smart Retry management and statistics (F8 Module)');
|
|
231
|
+
retryCommand
|
|
232
|
+
.command('history')
|
|
233
|
+
.description('View retry history from vault')
|
|
234
|
+
.option('--test-id <id>', 'Filter by specific test ID')
|
|
235
|
+
.option('--limit <n>', 'Limit number of entries', '10')
|
|
236
|
+
.option('--json', 'Output as JSON')
|
|
237
|
+
.action(async (options) => {
|
|
238
|
+
await retryHistoryCommand(options);
|
|
239
|
+
});
|
|
240
|
+
retryCommand
|
|
241
|
+
.command('stats')
|
|
242
|
+
.description('Display retry statistics')
|
|
243
|
+
.option('--strategy <name>', 'Filter by strategy (none, fixed, linear, exponential, adaptive, intelligent)')
|
|
244
|
+
.option('--json', 'Output as JSON')
|
|
245
|
+
.action(async (options) => {
|
|
246
|
+
await retryStatsCommand(options);
|
|
247
|
+
});
|
|
248
|
+
retryCommand
|
|
249
|
+
.command('configure')
|
|
250
|
+
.description('Configure retry settings')
|
|
251
|
+
.option('--show', 'Show current configuration')
|
|
252
|
+
.option('--strategy <name>', 'Set retry strategy')
|
|
253
|
+
.option('--max-retries <n>', 'Set maximum retry attempts')
|
|
254
|
+
.option('--delay <ms>', 'Set initial delay in milliseconds')
|
|
255
|
+
.option('--jitter <factor>', 'Set jitter factor (0-1)')
|
|
256
|
+
.action(async (options) => {
|
|
257
|
+
await retryConfigureCommand(options);
|
|
258
|
+
});
|
|
259
|
+
retryCommand
|
|
260
|
+
.command('test')
|
|
261
|
+
.description('Get retry recommendation for a test')
|
|
262
|
+
.option('--test-id <id>', 'Test ID to check')
|
|
263
|
+
.option('--strategy <name>', 'Override strategy')
|
|
264
|
+
.option('--dry-run', 'Show recommendation without executing')
|
|
265
|
+
.action(async (options) => {
|
|
266
|
+
await retryTestCommand(options);
|
|
267
|
+
});
|
|
268
|
+
retryCommand
|
|
269
|
+
.command('recommendations')
|
|
270
|
+
.description('Show flakiness-based retry recommendations')
|
|
271
|
+
.action(async () => {
|
|
272
|
+
displayRecommendations();
|
|
273
|
+
});
|
|
274
|
+
return retryCommand;
|
|
275
|
+
}
|
package/dist/commands/run.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* qa360 run --output ./results
|
|
7
7
|
* qa360 run --verbose
|
|
8
8
|
*/
|
|
9
|
-
import { type Phase3RunResult, type PackConfigV1 } from '../core/index.js';
|
|
9
|
+
import { type Phase3RunResult, type PackConfigV1, type PackConfigV2 } from '../core/index.js';
|
|
10
10
|
/**
|
|
11
11
|
* CLI options for run command
|
|
12
12
|
*/
|
|
@@ -17,9 +17,14 @@ export interface RunOptions {
|
|
|
17
17
|
strict?: boolean;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* Unified pack type - can be v1 or v2, Phase3Runner handles both
|
|
21
21
|
*/
|
|
22
|
-
export
|
|
22
|
+
export type PackConfig = PackConfigV1 | PackConfigV2;
|
|
23
|
+
/**
|
|
24
|
+
* Load and validate pack configuration (supports both v1 and v2)
|
|
25
|
+
* Uses PackLoaderV2 which automatically migrates v1 to v2
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadPack(packPath: string): Promise<PackConfig>;
|
|
23
28
|
/**
|
|
24
29
|
* Find pack file (search order: argument, .qa360/pack.yml, pack.yaml, pack.yml)
|
|
25
30
|
*/
|
package/dist/commands/run.js
CHANGED
|
@@ -6,49 +6,63 @@
|
|
|
6
6
|
* qa360 run --output ./results
|
|
7
7
|
* qa360 run --verbose
|
|
8
8
|
*/
|
|
9
|
-
import { existsSync
|
|
9
|
+
import { existsSync } from 'fs';
|
|
10
10
|
import { join, resolve } from 'path';
|
|
11
11
|
import chalk from 'chalk';
|
|
12
|
-
import {
|
|
13
|
-
import { Phase3Runner, PackValidator } from '../core/index.js';
|
|
12
|
+
import { Phase3Runner, PackLoaderV2 } from '../core/index.js';
|
|
14
13
|
/**
|
|
15
|
-
*
|
|
14
|
+
* Format gates for display (handles both v1 array and v2 object formats)
|
|
15
|
+
*/
|
|
16
|
+
function formatGates(pack) {
|
|
17
|
+
if (Array.isArray(pack.gates)) {
|
|
18
|
+
// v1 format: gates: ["api_smoke", "ui"]
|
|
19
|
+
return pack.gates.join(', ');
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
// v2 format: gates: { api_health: { type: api }, ui_e2e: { type: ui } }
|
|
23
|
+
const gateNames = Object.keys(pack.gates || {});
|
|
24
|
+
return gateNames.join(', ') || 'none';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Load and validate pack configuration (supports both v1 and v2)
|
|
29
|
+
* Uses PackLoaderV2 which automatically migrates v1 to v2
|
|
16
30
|
*/
|
|
17
31
|
export async function loadPack(packPath) {
|
|
18
32
|
if (!existsSync(packPath)) {
|
|
19
33
|
throw new Error(`Pack file not found: ${packPath}`);
|
|
20
34
|
}
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (errObj.instancePath && errObj.message) {
|
|
38
|
-
return ` • ${errObj.instancePath}: ${errObj.message}`;
|
|
39
|
-
}
|
|
40
|
-
return ` • ${JSON.stringify(e)}`;
|
|
41
|
-
}
|
|
42
|
-
return ` • ${String(e)}`;
|
|
43
|
-
}).join('\n') || ' • Unknown validation error';
|
|
35
|
+
// Use PackLoaderV2 which handles both v1 and v2 formats
|
|
36
|
+
const loader = new PackLoaderV2();
|
|
37
|
+
const result = await loader.load(packPath, {
|
|
38
|
+
validate: true,
|
|
39
|
+
migrate: true // Auto-migrate v1 to v2
|
|
40
|
+
});
|
|
41
|
+
if (!result.success) {
|
|
42
|
+
throw new Error(`Failed to load pack: ${result.error?.message || 'Unknown error'}`);
|
|
43
|
+
}
|
|
44
|
+
// Handle validation errors
|
|
45
|
+
if (result.validationErrors && result.validationErrors.length > 0) {
|
|
46
|
+
const errors = result.validationErrors.map((e) => {
|
|
47
|
+
if (e.message)
|
|
48
|
+
return ` • ${e.path ? e.path + ': ' : ''}${e.message}`;
|
|
49
|
+
return ` • ${JSON.stringify(e)}`;
|
|
50
|
+
}).join('\n');
|
|
44
51
|
throw new Error(`Invalid pack configuration:\n${errors}`);
|
|
45
52
|
}
|
|
53
|
+
// Show migration info if v1 was migrated
|
|
54
|
+
if (result.migrated) {
|
|
55
|
+
console.log(chalk.gray(` 📦 Migrated v1 → v2 format`));
|
|
56
|
+
if (result.changes && result.changes.length > 0) {
|
|
57
|
+
console.log(chalk.gray(` Changes: ${result.changes.join(', ')}`));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
46
60
|
// Show warnings if any
|
|
47
|
-
if (
|
|
61
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
48
62
|
console.log(chalk.yellow('\n⚠️ Pack warnings:'));
|
|
49
|
-
|
|
63
|
+
result.warnings.forEach((w) => console.log(chalk.yellow(` • ${w}`)));
|
|
50
64
|
}
|
|
51
|
-
return pack;
|
|
65
|
+
return result.pack;
|
|
52
66
|
}
|
|
53
67
|
/**
|
|
54
68
|
* Find pack file (search order: argument, .qa360/pack.yml, pack.yaml, pack.yml)
|
|
@@ -117,7 +131,7 @@ export async function runCommand(packArg, options = {}) {
|
|
|
117
131
|
// Step 2: Load and validate pack
|
|
118
132
|
const pack = await loadPack(packPath);
|
|
119
133
|
console.log(chalk.green(` ✅ Pack loaded: ${pack.name} v${pack.version}`));
|
|
120
|
-
console.log(chalk.gray(` Gates: ${pack
|
|
134
|
+
console.log(chalk.gray(` Gates: ${formatGates(pack)}`));
|
|
121
135
|
// Step 3: Dry run check
|
|
122
136
|
if (options.dryRun) {
|
|
123
137
|
console.log(chalk.yellow('\n🔍 Dry run mode - no tests executed'));
|