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,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assertion Healer
|
|
3
|
+
*
|
|
4
|
+
* Intelligently heals broken assertions in tests.
|
|
5
|
+
* Supports Jest, Chai, and Node.js assert styles.
|
|
6
|
+
*/
|
|
7
|
+
import type { AssertionUpdateResult, AssertionType, SelfHealingConfig } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Parsed assertion information
|
|
10
|
+
*/
|
|
11
|
+
interface ParsedAssertion {
|
|
12
|
+
type: AssertionType;
|
|
13
|
+
originalAssertion?: string;
|
|
14
|
+
actual: string;
|
|
15
|
+
expected: string;
|
|
16
|
+
negated: boolean;
|
|
17
|
+
matcher?: string;
|
|
18
|
+
line: number;
|
|
19
|
+
column: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Assertion healing options
|
|
23
|
+
*/
|
|
24
|
+
interface AssertionHealOptions {
|
|
25
|
+
/** Test file content */
|
|
26
|
+
fileContent: string;
|
|
27
|
+
/** Line number of failed assertion */
|
|
28
|
+
lineNumber: number;
|
|
29
|
+
/** Expected value */
|
|
30
|
+
expected: string;
|
|
31
|
+
/** Actual value received */
|
|
32
|
+
actual: string;
|
|
33
|
+
/** Error message from failure */
|
|
34
|
+
errorMessage?: string;
|
|
35
|
+
/** Test framework (jest, chai, assert) */
|
|
36
|
+
framework?: 'jest' | 'chai' | 'assert' | 'auto';
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Assertion Healer class
|
|
40
|
+
*/
|
|
41
|
+
export declare class AssertionHealer {
|
|
42
|
+
private config;
|
|
43
|
+
constructor(config: SelfHealingConfig);
|
|
44
|
+
/**
|
|
45
|
+
* Attempt to heal a broken assertion
|
|
46
|
+
*/
|
|
47
|
+
healAssertion(options: AssertionHealOptions): Promise<AssertionUpdateResult | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Parse the assertion from file content
|
|
50
|
+
*/
|
|
51
|
+
private parseAssertion;
|
|
52
|
+
/**
|
|
53
|
+
* Detect test framework from file content
|
|
54
|
+
*/
|
|
55
|
+
private detectFramework;
|
|
56
|
+
/**
|
|
57
|
+
* Parse Jest-style assertion
|
|
58
|
+
*/
|
|
59
|
+
private parseJestAssertion;
|
|
60
|
+
/**
|
|
61
|
+
* Parse Chai-style assertion
|
|
62
|
+
*/
|
|
63
|
+
private parseChaiAssertion;
|
|
64
|
+
/**
|
|
65
|
+
* Parse Node.js assert-style assertion
|
|
66
|
+
*/
|
|
67
|
+
private parseAssertAssertion;
|
|
68
|
+
/**
|
|
69
|
+
* Map Jest matcher to assertion type
|
|
70
|
+
*/
|
|
71
|
+
private mapJestMatcherToType;
|
|
72
|
+
/**
|
|
73
|
+
* Map Chai matcher to assertion type
|
|
74
|
+
*/
|
|
75
|
+
private mapChaiMatcherToType;
|
|
76
|
+
/**
|
|
77
|
+
* Calculate confidence for healing
|
|
78
|
+
*/
|
|
79
|
+
private calculateHealingConfidence;
|
|
80
|
+
/**
|
|
81
|
+
* Generate healed assertion
|
|
82
|
+
*/
|
|
83
|
+
private generateHealedAssertion;
|
|
84
|
+
/**
|
|
85
|
+
* Format a value for use in code
|
|
86
|
+
*/
|
|
87
|
+
private formatValue;
|
|
88
|
+
/**
|
|
89
|
+
* Calculate similarity between two strings (Levenshtein-based)
|
|
90
|
+
*/
|
|
91
|
+
private calculateStringSimilarity;
|
|
92
|
+
/**
|
|
93
|
+
* Parse multiple assertions from a file
|
|
94
|
+
*/
|
|
95
|
+
parseAssertionsFromFile(content: string): ParsedAssertion[];
|
|
96
|
+
}
|
|
97
|
+
export {};
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assertion Healer
|
|
3
|
+
*
|
|
4
|
+
* Intelligently heals broken assertions in tests.
|
|
5
|
+
* Supports Jest, Chai, and Node.js assert styles.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Assertion Healer class
|
|
9
|
+
*/
|
|
10
|
+
export class AssertionHealer {
|
|
11
|
+
config;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Attempt to heal a broken assertion
|
|
17
|
+
*/
|
|
18
|
+
async healAssertion(options) {
|
|
19
|
+
if (!this.config.features.assertionUpdates) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const parsed = this.parseAssertion(options);
|
|
23
|
+
if (!parsed) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
// Determine if healing is possible
|
|
27
|
+
const confidence = this.calculateHealingConfidence(parsed, options);
|
|
28
|
+
if (confidence < (this.config.confidenceThreshold || 0.7)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
// Generate healed assertion
|
|
32
|
+
const updatedAssertion = this.generateHealedAssertion(parsed, options);
|
|
33
|
+
return {
|
|
34
|
+
testFile: '', // Caller should fill this
|
|
35
|
+
lineNumber: options.lineNumber,
|
|
36
|
+
originalAssertion: parsed.originalAssertion || '',
|
|
37
|
+
updatedAssertion,
|
|
38
|
+
expected: options.expected,
|
|
39
|
+
actual: options.actual,
|
|
40
|
+
confidence,
|
|
41
|
+
assertionType: parsed.type
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parse the assertion from file content
|
|
46
|
+
*/
|
|
47
|
+
parseAssertion(options) {
|
|
48
|
+
const lines = options.fileContent.split('\n');
|
|
49
|
+
const line = lines[options.lineNumber - 1];
|
|
50
|
+
if (!line) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const framework = options.framework === 'auto' ? this.detectFramework(options.fileContent) : options.framework;
|
|
54
|
+
switch (framework) {
|
|
55
|
+
case 'jest':
|
|
56
|
+
return this.parseJestAssertion(line, options);
|
|
57
|
+
case 'chai':
|
|
58
|
+
return this.parseChaiAssertion(line, options);
|
|
59
|
+
case 'assert':
|
|
60
|
+
return this.parseAssertAssertion(line, options);
|
|
61
|
+
default:
|
|
62
|
+
// Try all parsers
|
|
63
|
+
return (this.parseJestAssertion(line, options) ||
|
|
64
|
+
this.parseChaiAssertion(line, options) ||
|
|
65
|
+
this.parseAssertAssertion(line, options));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Detect test framework from file content
|
|
70
|
+
*/
|
|
71
|
+
detectFramework(content) {
|
|
72
|
+
const hasJest = /\b(expect|describe|it|test)\(/.test(content);
|
|
73
|
+
const hasChai = /\b(expect|should)\./.test(content) && /\b(chai|assert)\b/.test(content);
|
|
74
|
+
const hasAssert = /\b(require\(['"]assert['"]\)|import.*assert)\b/.test(content);
|
|
75
|
+
if (hasJest && !hasChai)
|
|
76
|
+
return 'jest';
|
|
77
|
+
if (hasChai)
|
|
78
|
+
return 'chai';
|
|
79
|
+
if (hasAssert)
|
|
80
|
+
return 'assert';
|
|
81
|
+
return 'jest'; // Default
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Parse Jest-style assertion
|
|
85
|
+
*/
|
|
86
|
+
parseJestAssertion(line, options) {
|
|
87
|
+
const jestPattern = /expect\(([^)]+)\)\.\s*(not\.)?(to[\w]+)\(([^)]*)\)/;
|
|
88
|
+
const match = line.match(jestPattern);
|
|
89
|
+
if (!match) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const [, actual, negated, matcher, expected] = match;
|
|
93
|
+
const type = this.mapJestMatcherToType(matcher);
|
|
94
|
+
return {
|
|
95
|
+
type,
|
|
96
|
+
originalAssertion: line.trim(),
|
|
97
|
+
actual: actual.trim(),
|
|
98
|
+
expected: expected.trim() || options.expected,
|
|
99
|
+
negated: !!negated,
|
|
100
|
+
matcher,
|
|
101
|
+
line: options.lineNumber,
|
|
102
|
+
column: line.indexOf(match[0]) + 1
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Parse Chai-style assertion
|
|
107
|
+
*/
|
|
108
|
+
parseChaiAssertion(line, options) {
|
|
109
|
+
// expect(actual).to/match/have.property
|
|
110
|
+
const expectPattern = /expect\(([^)]+)\)\.\s*(to|should)\.\s*(not\.)?([\w.]+)\(([^)]*)\)/;
|
|
111
|
+
let match = line.match(expectPattern);
|
|
112
|
+
if (match) {
|
|
113
|
+
const [, actual, , negated, matcher, expected] = match;
|
|
114
|
+
const type = this.mapChaiMatcherToType(matcher);
|
|
115
|
+
return {
|
|
116
|
+
type,
|
|
117
|
+
originalAssertion: line.trim(),
|
|
118
|
+
actual: actual.trim(),
|
|
119
|
+
expected: expected.trim() || options.expected,
|
|
120
|
+
negated: !!negated,
|
|
121
|
+
matcher,
|
|
122
|
+
line: options.lineNumber,
|
|
123
|
+
column: line.indexOf(match[0]) + 1
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// should style
|
|
127
|
+
const shouldPattern = /([\w.]+)\.should\.\s*(not\.)?([\w.]+)\(([^)]*)\)/;
|
|
128
|
+
match = line.match(shouldPattern);
|
|
129
|
+
if (match) {
|
|
130
|
+
const [, actual, negated, matcher, expected] = match;
|
|
131
|
+
const type = this.mapChaiMatcherToType(matcher);
|
|
132
|
+
return {
|
|
133
|
+
type,
|
|
134
|
+
originalAssertion: line.trim(),
|
|
135
|
+
actual: actual.trim(),
|
|
136
|
+
expected: expected.trim() || options.expected,
|
|
137
|
+
negated: !!negated,
|
|
138
|
+
matcher,
|
|
139
|
+
line: options.lineNumber,
|
|
140
|
+
column: line.indexOf(match[0]) + 1
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Parse Node.js assert-style assertion
|
|
147
|
+
*/
|
|
148
|
+
parseAssertAssertion(line, options) {
|
|
149
|
+
// assert.strictEqual(actual, expected)
|
|
150
|
+
const strictEqualPattern = /assert\.(strictEqual|deepStrictEqual|equal)\(([^,]+),\s*([^)]+)\)/;
|
|
151
|
+
let match = line.match(strictEqualPattern);
|
|
152
|
+
if (match) {
|
|
153
|
+
const [, method, actual, expected] = match;
|
|
154
|
+
return {
|
|
155
|
+
type: 'equality',
|
|
156
|
+
originalAssertion: line.trim(),
|
|
157
|
+
actual: actual.trim(),
|
|
158
|
+
expected: expected.trim(),
|
|
159
|
+
negated: false,
|
|
160
|
+
matcher: method,
|
|
161
|
+
line: options.lineNumber,
|
|
162
|
+
column: line.indexOf(match[0]) + 1
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
// assert actual, expected
|
|
166
|
+
const simplePattern = new RegExp('^(assert\\.\\w+\\([^,]+),\\s*)');
|
|
167
|
+
const simpleMatch = line.match(simplePattern);
|
|
168
|
+
if (simpleMatch) {
|
|
169
|
+
return {
|
|
170
|
+
type: 'equality',
|
|
171
|
+
originalAssertion: line.trim(),
|
|
172
|
+
actual: simpleMatch[1],
|
|
173
|
+
expected: options.expected,
|
|
174
|
+
negated: false,
|
|
175
|
+
matcher: 'assert',
|
|
176
|
+
line: options.lineNumber,
|
|
177
|
+
column: 1
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Map Jest matcher to assertion type
|
|
184
|
+
*/
|
|
185
|
+
mapJestMatcherToType(matcher) {
|
|
186
|
+
const matcherMap = {
|
|
187
|
+
'toBe': 'equality',
|
|
188
|
+
'toEqual': 'equality',
|
|
189
|
+
'toStrictEqual': 'equality',
|
|
190
|
+
'toBeGreaterThan': 'count',
|
|
191
|
+
'toBeGreaterThanOrEqual': 'count',
|
|
192
|
+
'toBeLessThan': 'count',
|
|
193
|
+
'toBeLessThanOrEqual': 'count',
|
|
194
|
+
'toContain': 'containment',
|
|
195
|
+
'toContainEqual': 'containment',
|
|
196
|
+
'toMatch': 'containment',
|
|
197
|
+
'toMatchObject': 'containment',
|
|
198
|
+
'toBeTruthy': 'truthiness',
|
|
199
|
+
'toBeFalsy': 'truthiness',
|
|
200
|
+
'toBeNull': 'truthiness',
|
|
201
|
+
'toBeUndefined': 'truthiness',
|
|
202
|
+
'toBeDefined': 'truthiness',
|
|
203
|
+
'toHaveLength': 'count',
|
|
204
|
+
'toHaveProperty': 'containment'
|
|
205
|
+
};
|
|
206
|
+
return matcherMap[matcher] || 'equality';
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Map Chai matcher to assertion type
|
|
210
|
+
*/
|
|
211
|
+
mapChaiMatcherToType(matcher) {
|
|
212
|
+
const matcherMap = {
|
|
213
|
+
'equal': 'equality',
|
|
214
|
+
'deep.equal': 'equality',
|
|
215
|
+
'eql': 'equality',
|
|
216
|
+
'above': 'count',
|
|
217
|
+
'least': 'count',
|
|
218
|
+
'below': 'count',
|
|
219
|
+
'most': 'count',
|
|
220
|
+
'include': 'containment',
|
|
221
|
+
'contain': 'containment',
|
|
222
|
+
'contains': 'containment',
|
|
223
|
+
'match': 'containment',
|
|
224
|
+
'property': 'containment',
|
|
225
|
+
'true': 'truthiness',
|
|
226
|
+
'false': 'truthiness',
|
|
227
|
+
'null': 'truthiness',
|
|
228
|
+
'undefined': 'truthiness',
|
|
229
|
+
'exist': 'truthiness',
|
|
230
|
+
'empty': 'truthiness',
|
|
231
|
+
'lengthOf': 'count'
|
|
232
|
+
};
|
|
233
|
+
return matcherMap[matcher] || 'equality';
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Calculate confidence for healing
|
|
237
|
+
*/
|
|
238
|
+
calculateHealingConfidence(parsed, options) {
|
|
239
|
+
let confidence = 0.5;
|
|
240
|
+
// Type-specific adjustments
|
|
241
|
+
if (parsed.type === 'equality') {
|
|
242
|
+
// Simple value changes are high confidence
|
|
243
|
+
const diffScore = this.calculateStringSimilarity(options.expected, options.actual);
|
|
244
|
+
confidence = Math.max(confidence, diffScore * 0.9);
|
|
245
|
+
}
|
|
246
|
+
if (parsed.type === 'containment') {
|
|
247
|
+
// Adding missing items is medium-high confidence
|
|
248
|
+
confidence = 0.75;
|
|
249
|
+
}
|
|
250
|
+
if (parsed.type === 'count') {
|
|
251
|
+
// Adjusting numbers is high confidence
|
|
252
|
+
const expectedNum = parseInt(options.expected, 10);
|
|
253
|
+
const actualNum = parseInt(options.actual, 10);
|
|
254
|
+
if (!isNaN(expectedNum) && !isNaN(actualNum)) {
|
|
255
|
+
confidence = 0.85;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// Adjust for negation
|
|
259
|
+
if (parsed.negated) {
|
|
260
|
+
confidence *= 0.8; // Lower confidence for negated assertions
|
|
261
|
+
}
|
|
262
|
+
// Adjust for error message clarity
|
|
263
|
+
if (options.errorMessage) {
|
|
264
|
+
if (options.errorMessage.includes('Expected:') && options.errorMessage.includes('Received:')) {
|
|
265
|
+
confidence += 0.1;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return Math.min(confidence, 1.0);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Generate healed assertion
|
|
272
|
+
*/
|
|
273
|
+
generateHealedAssertion(parsed, options) {
|
|
274
|
+
const actualValue = this.formatValue(options.actual);
|
|
275
|
+
const expectedValue = this.formatValue(options.expected);
|
|
276
|
+
if (parsed.matcher?.includes('toBe') || parsed.matcher === 'equal') {
|
|
277
|
+
// Jest: expect(actual).toBe(expected)
|
|
278
|
+
// Chai: expect(actual).to.equal(expected)
|
|
279
|
+
const isJest = parsed.matcher?.startsWith('to');
|
|
280
|
+
const separator = isJest ? `.${parsed.matcher}(` : `.to.${parsed.matcher}(`;
|
|
281
|
+
return `expect(${parsed.actual}).${parsed.negated ? 'not.' : ''}${separator}${expectedValue})`;
|
|
282
|
+
}
|
|
283
|
+
if (parsed.matcher?.includes('Equal') || parsed.matcher === 'deep.equal' || parsed.matcher === 'eql') {
|
|
284
|
+
return `expect(${parsed.actual})${parsed.negated ? '.not.' : '.'}${parsed.matcher || 'toEqual'}(${expectedValue})`;
|
|
285
|
+
}
|
|
286
|
+
if (parsed.matcher?.includes('Contain') || parsed.matcher?.includes('include')) {
|
|
287
|
+
return `expect(${parsed.actual})${parsed.negated ? '.not.' : '.'}${parsed.matcher || 'toContain'}(${expectedValue})`;
|
|
288
|
+
}
|
|
289
|
+
// Default: update expected value in original
|
|
290
|
+
let result = parsed.originalAssertion || '';
|
|
291
|
+
// Replace the expected value
|
|
292
|
+
const lastComma = result.lastIndexOf(',');
|
|
293
|
+
if (lastComma !== -1) {
|
|
294
|
+
result = result.substring(0, lastComma + 1) + ' ' + expectedValue + result.substring(result.lastIndexOf(')'));
|
|
295
|
+
}
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Format a value for use in code
|
|
300
|
+
*/
|
|
301
|
+
formatValue(value) {
|
|
302
|
+
// Try to detect if it's already formatted
|
|
303
|
+
if (/^['"`{[\[]/.test(value)) {
|
|
304
|
+
return value;
|
|
305
|
+
}
|
|
306
|
+
// Numbers
|
|
307
|
+
if (/^\d+$/.test(value)) {
|
|
308
|
+
return value;
|
|
309
|
+
}
|
|
310
|
+
// Booleans
|
|
311
|
+
if (value === 'true' || value === 'false') {
|
|
312
|
+
return value;
|
|
313
|
+
}
|
|
314
|
+
// Null/undefined
|
|
315
|
+
if (value === 'null' || value === 'undefined') {
|
|
316
|
+
return value;
|
|
317
|
+
}
|
|
318
|
+
// Default: treat as string
|
|
319
|
+
return JSON.stringify(value);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Calculate similarity between two strings (Levenshtein-based)
|
|
323
|
+
*/
|
|
324
|
+
calculateStringSimilarity(str1, str2) {
|
|
325
|
+
const len1 = str1.length;
|
|
326
|
+
const len2 = str2.length;
|
|
327
|
+
if (len1 === 0)
|
|
328
|
+
return len2 === 0 ? 1 : 0;
|
|
329
|
+
if (len2 === 0)
|
|
330
|
+
return 0;
|
|
331
|
+
const matrix = [];
|
|
332
|
+
for (let i = 0; i <= len1; i++) {
|
|
333
|
+
matrix[i] = [i];
|
|
334
|
+
}
|
|
335
|
+
for (let j = 0; j <= len2; j++) {
|
|
336
|
+
matrix[0][j] = j;
|
|
337
|
+
}
|
|
338
|
+
for (let i = 1; i <= len1; i++) {
|
|
339
|
+
for (let j = 1; j <= len2; j++) {
|
|
340
|
+
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
|
341
|
+
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const maxLen = Math.max(len1, len2);
|
|
345
|
+
return 1 - matrix[len1][len2] / maxLen;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Parse multiple assertions from a file
|
|
349
|
+
*/
|
|
350
|
+
parseAssertionsFromFile(content) {
|
|
351
|
+
const assertions = [];
|
|
352
|
+
const lines = content.split('\n');
|
|
353
|
+
for (let i = 0; i < lines.length; i++) {
|
|
354
|
+
const line = lines[i];
|
|
355
|
+
const options = {
|
|
356
|
+
fileContent: content,
|
|
357
|
+
lineNumber: i + 1,
|
|
358
|
+
expected: '',
|
|
359
|
+
actual: '',
|
|
360
|
+
framework: 'auto'
|
|
361
|
+
};
|
|
362
|
+
const parsed = this.parseJestAssertion(line, options) ||
|
|
363
|
+
this.parseChaiAssertion(line, options) ||
|
|
364
|
+
this.parseAssertAssertion(line, options);
|
|
365
|
+
if (parsed) {
|
|
366
|
+
assertions.push(parsed);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return assertions;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-Healing Engine
|
|
3
|
+
*
|
|
4
|
+
* Main orchestration for self-healing functionality.
|
|
5
|
+
* Coordinates selector healing, assertion healing, and AI assistance.
|
|
6
|
+
*/
|
|
7
|
+
import type { SelfHealingConfig, SelfHealingSession, TestFailureContext, AiHealingSuggestion } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Self-Healing Engine class
|
|
10
|
+
*/
|
|
11
|
+
export declare class SelfHealingEngine {
|
|
12
|
+
private config;
|
|
13
|
+
private selectorHealer;
|
|
14
|
+
private assertionHealer;
|
|
15
|
+
private currentSession?;
|
|
16
|
+
constructor(config: SelfHealingConfig);
|
|
17
|
+
/**
|
|
18
|
+
* Start a new self-healing session
|
|
19
|
+
*/
|
|
20
|
+
startSession(): string;
|
|
21
|
+
/**
|
|
22
|
+
* End current session and return results
|
|
23
|
+
*/
|
|
24
|
+
endSession(): SelfHealingSession | null;
|
|
25
|
+
/**
|
|
26
|
+
* Attempt to heal a test failure
|
|
27
|
+
*/
|
|
28
|
+
healFailure(context: TestFailureContext): Promise<SelfHealingResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Heal a UI test failure (selector issues)
|
|
31
|
+
*/
|
|
32
|
+
private healUiFailure;
|
|
33
|
+
/**
|
|
34
|
+
* Heal an API test failure
|
|
35
|
+
*/
|
|
36
|
+
private healApiFailure;
|
|
37
|
+
/**
|
|
38
|
+
* Heal a generic test failure
|
|
39
|
+
*/
|
|
40
|
+
private healGenericFailure;
|
|
41
|
+
/**
|
|
42
|
+
* Try AI-assisted healing
|
|
43
|
+
*/
|
|
44
|
+
private tryAiHealing;
|
|
45
|
+
/**
|
|
46
|
+
* Build AI prompt from failure context
|
|
47
|
+
*/
|
|
48
|
+
private buildAiPrompt;
|
|
49
|
+
/**
|
|
50
|
+
* Extract code from AI response
|
|
51
|
+
*/
|
|
52
|
+
private extractCodeFromAiResponse;
|
|
53
|
+
/**
|
|
54
|
+
* Extract explanation from AI response
|
|
55
|
+
*/
|
|
56
|
+
private extractExplanationFromAiResponse;
|
|
57
|
+
/**
|
|
58
|
+
* Analyze API failure
|
|
59
|
+
*/
|
|
60
|
+
private analyzeApiFailure;
|
|
61
|
+
/**
|
|
62
|
+
* Detect API change type from context
|
|
63
|
+
*/
|
|
64
|
+
private detectApiChangeType;
|
|
65
|
+
/**
|
|
66
|
+
* Extract selector from error message
|
|
67
|
+
*/
|
|
68
|
+
private extractSelector;
|
|
69
|
+
/**
|
|
70
|
+
* Extract expected text from error message
|
|
71
|
+
*/
|
|
72
|
+
private extractExpectedText;
|
|
73
|
+
/**
|
|
74
|
+
* Infer selector error type from message
|
|
75
|
+
*/
|
|
76
|
+
private inferSelectorErrorType;
|
|
77
|
+
/**
|
|
78
|
+
* Extract line number from stack trace
|
|
79
|
+
*/
|
|
80
|
+
private extractLineNumber;
|
|
81
|
+
/**
|
|
82
|
+
* Format selector heal result
|
|
83
|
+
*/
|
|
84
|
+
private formatSelectorHeal;
|
|
85
|
+
/**
|
|
86
|
+
* Calculate session summary
|
|
87
|
+
*/
|
|
88
|
+
private calculateSummary;
|
|
89
|
+
/**
|
|
90
|
+
* Generate unique session ID
|
|
91
|
+
*/
|
|
92
|
+
private generateSessionId;
|
|
93
|
+
/**
|
|
94
|
+
* Get current session
|
|
95
|
+
*/
|
|
96
|
+
getSession(): SelfHealingSession | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Update configuration
|
|
99
|
+
*/
|
|
100
|
+
updateConfig(updates: Partial<SelfHealingConfig>): void;
|
|
101
|
+
/**
|
|
102
|
+
* Check if self-healing is enabled
|
|
103
|
+
*/
|
|
104
|
+
isEnabled(): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Get confidence threshold
|
|
107
|
+
*/
|
|
108
|
+
getConfidenceThreshold(): number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Self-healing result
|
|
112
|
+
*/
|
|
113
|
+
export interface SelfHealingResult {
|
|
114
|
+
success: boolean;
|
|
115
|
+
healed: boolean;
|
|
116
|
+
suggestion: AiHealingSuggestion | null;
|
|
117
|
+
reason: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Create a self-healing engine with default config
|
|
121
|
+
*/
|
|
122
|
+
export declare function createSelfHealingEngine(config?: Partial<SelfHealingConfig>): SelfHealingEngine;
|