quality-intelligence-engine 2.2.17 → 2.2.22
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/dist/bin/qi.js +8 -3
- package/dist/src/adapters/playwright-folder.adapter.js +68 -0
- package/dist/src/adapters/playwright-json.adapter.js +46 -0
- package/dist/src/adapters/playwright.adapter.js +36 -0
- package/dist/src/cli/qi-test.js +20 -0
- package/dist/src/cli/qi.js +59 -0
- package/dist/src/cli/ui/divider.js +10 -0
- package/dist/src/cli/ui/failureLogger.js +24 -0
- package/dist/src/configLoader.js +60 -0
- package/dist/src/console/issue-view.js +81 -0
- package/dist/src/failure-analysis/failure-analyzer.js +38 -0
- package/dist/src/final-run.js +131 -0
- package/dist/src/final-runner.js +27 -0
- package/dist/src/fixtures/networkCollector.js +18 -0
- package/dist/src/history/failure-history.store.js +25 -0
- package/dist/src/history/failure-history.types.js +4 -0
- package/dist/src/history/failure-trend.analyzer.js +31 -0
- package/dist/src/index.js +10 -0
- package/dist/src/integrations/ci/ci-annotator.js +26 -0
- package/dist/src/intelligence/confidence-calibration.engine.js +21 -0
- package/dist/src/intelligence/decision-intelligence/confidence.engine.js +16 -0
- package/dist/src/intelligence/decision-intelligence/decision.config.js +21 -0
- package/dist/src/intelligence/decision-intelligence/decision.engine.js +23 -0
- package/dist/src/intelligence/decision-intelligence/decision.types.js +2 -0
- package/dist/src/intelligence/failure-fingerprinting/failure.classifier.js +32 -0
- package/dist/src/intelligence/failure-fingerprinting/failure.store.js +17 -0
- package/dist/src/intelligence/failure-fingerprinting/failure.tracker.js +19 -0
- package/dist/src/intelligence/failure-fingerprinting/fingerprint.generator.js +28 -0
- package/dist/src/intelligence/failure-fingerprinting/fingerprint.types.js +2 -0
- package/dist/src/intelligence/flakiness-intelligence/flakiness.analyzer.js +21 -0
- package/dist/src/intelligence/flakiness-intelligence/flakiness.fingerprint.js +16 -0
- package/dist/src/intelligence/flakiness-intelligence/flakiness.store.js +24 -0
- package/dist/src/intelligence/flakiness-intelligence/flakiness.tracker.js +28 -0
- package/dist/src/intelligence/flakiness-intelligence/flakiness.types.js +2 -0
- package/dist/src/intelligence/intelligence.pipeline.js +17 -0
- package/dist/src/intelligence/llm-explainer.js +19 -0
- package/dist/src/intelligence/root-cause/rootcause.engine.js +25 -0
- package/dist/src/intelligence/trend-intelligence/trend.engine.js +25 -0
- package/dist/src/markdownWriter.js +64 -0
- package/dist/src/normalizer.js +17 -0
- package/dist/src/passAnalyzer.js +38 -0
- package/dist/src/pipeline/ai.summarizer.js +32 -0
- package/dist/src/pipeline/failure-analysis.pipeline.js +11 -0
- package/dist/src/pipeline/failure-grouping.pipeline.js +59 -0
- package/dist/src/playwright/qi.reporter.js +17 -0
- package/dist/src/reporter.js +46 -0
- package/dist/src/rules.js +32 -0
- package/dist/src/runManager.js +19 -0
- package/dist/src/runner.js +13 -0
- package/dist/src/runtime/networkCollector.js +34 -0
- package/dist/src/stackParser.js +17 -0
- package/dist/src/test-run.js +48 -0
- package/dist/src/types/analysis-result.js +2 -0
- package/dist/src/types/failure.types.js +3 -0
- package/dist/src/types/playwright-failure.js +2 -0
- package/dist/src/types.js +5 -0
- package/dist/src/utils/artifact.locator.js +35 -0
- package/dist/src/utils/confidence-constants.js +90 -0
- package/dist/src/utils/file-utils.js +118 -0
- package/dist/src/v2/llm/llm-advisor.js +17 -0
- package/dist/src/v2/pipeline/v2-intelligence.pipeline.js +17 -0
- package/dist/src/v2/self-healing/self-healer.js +44 -0
- package/dist/src/v2/trace-intelligence/trace-analyzer.js +33 -0
- package/dist/src/v2-test-run.js +59 -0
- package/package.json +1 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/pipeline/failure-grouping.pipeline.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.groupFailures = groupFailures;
|
|
5
|
+
const failure_history_store_1 = require("../history/failure-history.store");
|
|
6
|
+
const failure_trend_analyzer_1 = require("../history/failure-trend.analyzer");
|
|
7
|
+
function groupFailures(results) {
|
|
8
|
+
const groupedCounts = {};
|
|
9
|
+
let flakyCount = 0;
|
|
10
|
+
for (const r of results) {
|
|
11
|
+
const key = r.classification ?? "UNKNOWN";
|
|
12
|
+
groupedCounts[key] = (groupedCounts[key] || 0) + 1;
|
|
13
|
+
if (key === "FLAKY") {
|
|
14
|
+
flakyCount++;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
let dominantCategory = "UNKNOWN";
|
|
18
|
+
let dominantCount = 0;
|
|
19
|
+
for (const [category, count] of Object.entries(groupedCounts)) {
|
|
20
|
+
if (count > dominantCount) {
|
|
21
|
+
dominantCategory = category;
|
|
22
|
+
dominantCount = count;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/* ===============================
|
|
26
|
+
🔒 FALLBACK ENFORCEMENT (CRITICAL)
|
|
27
|
+
=============================== */
|
|
28
|
+
if (results.length === 0) {
|
|
29
|
+
groupedCounts["UNCLASSIFIED_FAILURE"] = 1;
|
|
30
|
+
dominantCategory = "UNCLASSIFIED_FAILURE";
|
|
31
|
+
dominantCount = 1;
|
|
32
|
+
}
|
|
33
|
+
const total = Math.max(1, results.length || dominantCount);
|
|
34
|
+
let confidence = dominantCount / total;
|
|
35
|
+
// Ensure non-zero confidence for real failures
|
|
36
|
+
if (dominantCategory === "UNCLASSIFIED_FAILURE" && confidence <= 0) {
|
|
37
|
+
confidence = 0.25;
|
|
38
|
+
}
|
|
39
|
+
// 🔻 FLAKY PENALTY (KEY LOGIC)
|
|
40
|
+
if (flakyCount > 0) {
|
|
41
|
+
const penalty = Math.min(0.3, flakyCount / total);
|
|
42
|
+
confidence = Math.max(0, confidence - penalty);
|
|
43
|
+
}
|
|
44
|
+
const summary = {
|
|
45
|
+
dominantCategory,
|
|
46
|
+
groupedCounts,
|
|
47
|
+
confidence,
|
|
48
|
+
flakyCount
|
|
49
|
+
};
|
|
50
|
+
(0, failure_history_store_1.saveFailureHistory)({
|
|
51
|
+
runId: Date.now().toString(),
|
|
52
|
+
timestamp: new Date().toISOString(),
|
|
53
|
+
dominantCategory,
|
|
54
|
+
groupedCounts,
|
|
55
|
+
confidence
|
|
56
|
+
});
|
|
57
|
+
summary.trend = (0, failure_trend_analyzer_1.analyzeFailureTrends)((0, failure_history_store_1.loadFailureHistory)());
|
|
58
|
+
return summary;
|
|
59
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const failure_tracker_1 = require("../intelligence/failure-fingerprinting/failure.tracker");
|
|
4
|
+
class QIReporter {
|
|
5
|
+
onTestEnd(test, result) {
|
|
6
|
+
if (result.status !== "failed")
|
|
7
|
+
return;
|
|
8
|
+
const errorText = result.error?.message ||
|
|
9
|
+
result.error?.stack ||
|
|
10
|
+
"unknown error";
|
|
11
|
+
(0, failure_tracker_1.trackFailure)({
|
|
12
|
+
error: errorText,
|
|
13
|
+
testName: test.title
|
|
14
|
+
}, process.env.QI_RUN_ID ?? "local-run");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.default = QIReporter;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printSummaryAndWriteReports = printSummaryAndWriteReports;
|
|
4
|
+
exports.printPassRisks = printPassRisks;
|
|
5
|
+
const markdownWriter_1 = require("./markdownWriter");
|
|
6
|
+
/**
|
|
7
|
+
* FAILURES: Print summary + write markdown reports
|
|
8
|
+
*/
|
|
9
|
+
function printSummaryAndWriteReports(grouped, run, config) {
|
|
10
|
+
let printed = 0;
|
|
11
|
+
grouped.forEach((items, key) => {
|
|
12
|
+
const sample = items[0];
|
|
13
|
+
// Respect confidence threshold
|
|
14
|
+
if (sample.confidence <
|
|
15
|
+
config.engine.confidenceThresholds.fail) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (printed === 0) {
|
|
19
|
+
console.log(`❌ Failures Detected (${grouped.size} unique issues)\n`);
|
|
20
|
+
}
|
|
21
|
+
printed++;
|
|
22
|
+
console.log(`${printed}) ${sample.category} (${items.length} occurrences)`);
|
|
23
|
+
console.log(` Location : ${sample.location.file}:${sample.location.line}`);
|
|
24
|
+
console.log(` API : ${sample.failedApi.endpoint} → ${sample.failedApi.status}`);
|
|
25
|
+
console.log(` Cause : ${sample.rootCause}`);
|
|
26
|
+
console.log(` Details : output/${run.runDir.split('/').pop()}/${key}/analysis.md\n`);
|
|
27
|
+
(0, markdownWriter_1.writeAnalysisMarkdown)(run.runDir, key, sample, items.length);
|
|
28
|
+
});
|
|
29
|
+
if (printed === 0) {
|
|
30
|
+
console.log('✅ No failures exceeded confidence threshold\n');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* PASSES: Print non-blocking risk warnings
|
|
35
|
+
*/
|
|
36
|
+
function printPassRisks(risks, _run) {
|
|
37
|
+
if (risks.length === 0)
|
|
38
|
+
return;
|
|
39
|
+
console.log(`⚠️ Pass Risks Detected (${risks.length})\n`);
|
|
40
|
+
risks.forEach((risk, i) => {
|
|
41
|
+
console.log(`${i + 1}) ${risk.test} → ${risk.riskType}`);
|
|
42
|
+
console.log(` Location : ${risk.location.file}`);
|
|
43
|
+
console.log(` Reason : ${risk.reason}`);
|
|
44
|
+
console.log(` Fix : ${risk.fix}\n`);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateDummyResult = generateDummyResult;
|
|
4
|
+
function generateDummyResult() {
|
|
5
|
+
return {
|
|
6
|
+
category: 'BACKEND_API_FAILURE',
|
|
7
|
+
confidence: 0.93,
|
|
8
|
+
location: {
|
|
9
|
+
file: 'AuthController.java',
|
|
10
|
+
line: 87
|
|
11
|
+
},
|
|
12
|
+
failedApi: {
|
|
13
|
+
method: 'GET',
|
|
14
|
+
endpoint: '/users/{id}',
|
|
15
|
+
status: 404,
|
|
16
|
+
response: 'User not found'
|
|
17
|
+
},
|
|
18
|
+
rootCause: 'The requested user ID does not exist in the database.',
|
|
19
|
+
retryGuidance: 'Automated CI retry is not advised (high confidence application failure).',
|
|
20
|
+
reproductionSteps: [
|
|
21
|
+
'Start backend service',
|
|
22
|
+
'Run: curl GET /users/{id}',
|
|
23
|
+
'Observe: 404 Not Found'
|
|
24
|
+
],
|
|
25
|
+
expected: 'User details are returned successfully.',
|
|
26
|
+
actual: 'API returns 404 and request fails in AuthController.java:87',
|
|
27
|
+
suggestedActions: [
|
|
28
|
+
'Validate user existence before API call',
|
|
29
|
+
'Add graceful handling for missing user records'
|
|
30
|
+
]
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createRunContext = createRunContext;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
function createRunContext() {
|
|
10
|
+
const now = new Date();
|
|
11
|
+
const runId = now
|
|
12
|
+
.toISOString()
|
|
13
|
+
.replace(/[:.]/g, '-')
|
|
14
|
+
.replace('T', '_')
|
|
15
|
+
.replace('Z', '');
|
|
16
|
+
const runDir = path_1.default.join(process.cwd(), 'output', `run-${runId}`);
|
|
17
|
+
fs_1.default.mkdirSync(runDir, { recursive: true });
|
|
18
|
+
return { runId, runDir };
|
|
19
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/runner.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.runQualityIntelligence = runQualityIntelligence;
|
|
5
|
+
const failure_analysis_pipeline_1 = require("./pipeline/failure-analysis.pipeline");
|
|
6
|
+
const failure_grouping_pipeline_1 = require("./pipeline/failure-grouping.pipeline");
|
|
7
|
+
const intelligence_pipeline_1 = require("./intelligence/intelligence.pipeline");
|
|
8
|
+
async function runQualityIntelligence(input) {
|
|
9
|
+
const analysisResults = await (0, failure_analysis_pipeline_1.runFailureAnalysisFromPlaywright)(input);
|
|
10
|
+
const summary = (0, failure_grouping_pipeline_1.groupFailures)(analysisResults);
|
|
11
|
+
const enriched = await (0, intelligence_pipeline_1.enrichWithIntelligence)(summary);
|
|
12
|
+
return enriched;
|
|
13
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.attachNetworkCollector = attachNetworkCollector;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
function attachNetworkCollector(page, testId) {
|
|
10
|
+
const failures = [];
|
|
11
|
+
page.on('response', response => {
|
|
12
|
+
if (response.status() >= 400) {
|
|
13
|
+
failures.push({
|
|
14
|
+
method: response.request().method(),
|
|
15
|
+
url: new URL(response.url()).pathname,
|
|
16
|
+
status: response.status()
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
page.on('requestfailed', request => {
|
|
21
|
+
failures.push({
|
|
22
|
+
method: request.method(),
|
|
23
|
+
url: new URL(request.url()).pathname,
|
|
24
|
+
error: request.failure()?.errorText
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
process.on('exit', () => {
|
|
28
|
+
if (failures.length === 0)
|
|
29
|
+
return;
|
|
30
|
+
const dir = path_1.default.join('test-results', testId);
|
|
31
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
32
|
+
fs_1.default.writeFileSync(path_1.default.join(dir, 'network.json'), JSON.stringify(failures, null, 2));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseStackTrace = parseStackTrace;
|
|
4
|
+
function parseStackTrace(stack) {
|
|
5
|
+
if (!stack || stack.length === 0) {
|
|
6
|
+
return { file: 'Unknown', line: -1 };
|
|
7
|
+
}
|
|
8
|
+
// Supports formats like: Class.method(File.java:87)
|
|
9
|
+
const match = stack[0].match(/\((.*):(\d+)\)/);
|
|
10
|
+
if (!match) {
|
|
11
|
+
return { file: 'Unknown', line: -1 };
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
file: match[1],
|
|
15
|
+
line: Number(match[2])
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/test-run.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const runner_1 = require("./runner");
|
|
5
|
+
function line(char = "=", count = 60) {
|
|
6
|
+
console.log(char.repeat(count));
|
|
7
|
+
}
|
|
8
|
+
async function main() {
|
|
9
|
+
const playwrightResultsDir = "playwright-results";
|
|
10
|
+
const result = await (0, runner_1.runQualityIntelligence)(playwrightResultsDir);
|
|
11
|
+
line();
|
|
12
|
+
console.log("QUALITY INTELLIGENCE – FINAL OUTPUT");
|
|
13
|
+
line();
|
|
14
|
+
console.log(`Primary Failure : ${result.dominantCategory}`);
|
|
15
|
+
console.log(`Confidence Score : ${result.confidence}`);
|
|
16
|
+
console.log(`Flaky Failures : ${result.flakyCount}`);
|
|
17
|
+
console.log(`Trend Signal : ${result.trend?.signal ?? "N/A"}`);
|
|
18
|
+
line("-");
|
|
19
|
+
console.log("Failure Breakdown:");
|
|
20
|
+
for (const [k, v] of Object.entries(result.groupedCounts)) {
|
|
21
|
+
console.log(` - ${k}: ${v}`);
|
|
22
|
+
}
|
|
23
|
+
line("-");
|
|
24
|
+
console.log("Confidence Diagnosis:");
|
|
25
|
+
for (const lineText of result.intelligence.confidence.explanation) {
|
|
26
|
+
console.log(` • ${lineText}`);
|
|
27
|
+
}
|
|
28
|
+
line("-");
|
|
29
|
+
console.log("AI Explanation:");
|
|
30
|
+
console.log(` Summary: ${result.intelligence.explanation.summary}`);
|
|
31
|
+
if (result.intelligence.explanation.reasoning?.length) {
|
|
32
|
+
console.log(" Reasoning:");
|
|
33
|
+
for (const r of result.intelligence.explanation.reasoning) {
|
|
34
|
+
console.log(` - ${r}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (result.intelligence.explanation.recommendedAction?.length) {
|
|
38
|
+
console.log(" Recommended Actions:");
|
|
39
|
+
for (const a of result.intelligence.explanation.recommendedAction) {
|
|
40
|
+
console.log(` - ${a}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
line();
|
|
44
|
+
}
|
|
45
|
+
main().catch(err => {
|
|
46
|
+
console.error("QUALITY INTELLIGENCE FAILED");
|
|
47
|
+
console.error(err);
|
|
48
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.locatePlaywrightArtifacts = locatePlaywrightArtifacts;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
function locatePlaywrightArtifacts() {
|
|
10
|
+
const resultsDir = path_1.default.resolve('test-results');
|
|
11
|
+
const screenshots = [];
|
|
12
|
+
let trace;
|
|
13
|
+
if (!fs_1.default.existsSync(resultsDir)) {
|
|
14
|
+
return { screenshots, trace };
|
|
15
|
+
}
|
|
16
|
+
const runDirs = fs_1.default.readdirSync(resultsDir);
|
|
17
|
+
for (const dir of runDirs) {
|
|
18
|
+
const fullDir = path_1.default.join(resultsDir, dir);
|
|
19
|
+
if (!fs_1.default.statSync(fullDir).isDirectory())
|
|
20
|
+
continue;
|
|
21
|
+
const files = fs_1.default.readdirSync(fullDir);
|
|
22
|
+
for (const file of files) {
|
|
23
|
+
const fullPath = path_1.default.join(fullDir, file);
|
|
24
|
+
// 🎥 Trace ZIP (take the latest retry if exists)
|
|
25
|
+
if (file === 'trace.zip') {
|
|
26
|
+
trace = fullPath;
|
|
27
|
+
}
|
|
28
|
+
// 📸 Screenshots
|
|
29
|
+
if (file.endsWith('.png')) {
|
|
30
|
+
screenshots.push(fullPath);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { trace, screenshots };
|
|
35
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Confidence Calculation Constants
|
|
4
|
+
*
|
|
5
|
+
* These constants define the base confidence levels and adjustments
|
|
6
|
+
* used throughout the quality intelligence engine.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.TAXONOMY_BASE_CONFIDENCE = exports.CONFIDENCE_THRESHOLDS = exports.CONFIDENCE_BOUNDS = exports.CONFIDENCE_ADJUSTMENTS = exports.BASE_CONFIDENCE = void 0;
|
|
10
|
+
exports.clampConfidence = clampConfidence;
|
|
11
|
+
exports.formatConfidence = formatConfidence;
|
|
12
|
+
/**
|
|
13
|
+
* Base confidence levels for different error types
|
|
14
|
+
*/
|
|
15
|
+
exports.BASE_CONFIDENCE = {
|
|
16
|
+
/** Strong backend failure signal (5xx errors) */
|
|
17
|
+
BACKEND_SERVER_ERROR: 0.95,
|
|
18
|
+
/** Backend API failure (any API error) */
|
|
19
|
+
BACKEND_API_ERROR: 0.9,
|
|
20
|
+
/** Test or automation failure */
|
|
21
|
+
TEST_ERROR: 0.75,
|
|
22
|
+
/** Unknown or weak signal */
|
|
23
|
+
UNKNOWN: 0.6,
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Confidence adjustments based on test context and signals
|
|
27
|
+
*/
|
|
28
|
+
exports.CONFIDENCE_ADJUSTMENTS = {
|
|
29
|
+
/** UI successfully rendered and reached assertion */
|
|
30
|
+
UI_RENDERED: 0.05,
|
|
31
|
+
/** Test involves authentication or login flow */
|
|
32
|
+
AUTH_FLOW_DETECTED: 0.05,
|
|
33
|
+
/** Explicit authentication failure detected */
|
|
34
|
+
EXPLICIT_AUTH_FAILURE: 0.1,
|
|
35
|
+
/** Test involves inventory or data operations */
|
|
36
|
+
DATA_OPERATION_DETECTED: 0.05,
|
|
37
|
+
/** Numeric mismatch detected in assertions */
|
|
38
|
+
NUMERIC_MISMATCH: 0.05,
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Maximum and minimum confidence bounds
|
|
42
|
+
*/
|
|
43
|
+
exports.CONFIDENCE_BOUNDS = {
|
|
44
|
+
/** Maximum confidence score (never 100% certain) */
|
|
45
|
+
MAX: 0.95,
|
|
46
|
+
/** Minimum confidence score for reporting */
|
|
47
|
+
MIN: 0.5,
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Confidence thresholds for decision making
|
|
51
|
+
*/
|
|
52
|
+
exports.CONFIDENCE_THRESHOLDS = {
|
|
53
|
+
/** Threshold for blocking CI/CD (high confidence failure) */
|
|
54
|
+
FAIL: 0.85,
|
|
55
|
+
/** Threshold for reporting pass risks */
|
|
56
|
+
PASS_RISK: 0.6,
|
|
57
|
+
/** Threshold for investigating (medium confidence) */
|
|
58
|
+
INVESTIGATE: 0.7,
|
|
59
|
+
/** Threshold for shipping (high confidence in safety) */
|
|
60
|
+
SHIP: 0.85,
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Taxonomy rule base confidence levels
|
|
64
|
+
*/
|
|
65
|
+
exports.TAXONOMY_BASE_CONFIDENCE = {
|
|
66
|
+
/** Numeric data mismatch */
|
|
67
|
+
NUMERIC_MISMATCH: 0.6,
|
|
68
|
+
/** Authentication failure */
|
|
69
|
+
AUTH_FAILURE: 0.7,
|
|
70
|
+
/** UI visibility issue */
|
|
71
|
+
VISIBILITY: 0.7,
|
|
72
|
+
/** Network timeout */
|
|
73
|
+
TIMEOUT: 0.65,
|
|
74
|
+
/** Element not found */
|
|
75
|
+
ELEMENT_NOT_FOUND: 0.7,
|
|
76
|
+
/** Default fallback */
|
|
77
|
+
DEFAULT: 0.7,
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Helper function to clamp confidence within bounds
|
|
81
|
+
*/
|
|
82
|
+
function clampConfidence(value) {
|
|
83
|
+
return Math.max(exports.CONFIDENCE_BOUNDS.MIN, Math.min(exports.CONFIDENCE_BOUNDS.MAX, value));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Helper function to format confidence as percentage string
|
|
87
|
+
*/
|
|
88
|
+
function formatConfidence(value) {
|
|
89
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
90
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FileOperationError = void 0;
|
|
7
|
+
exports.readJsonFile = readJsonFile;
|
|
8
|
+
exports.writeJsonFile = writeJsonFile;
|
|
9
|
+
exports.fileExists = fileExists;
|
|
10
|
+
exports.ensureDirectory = ensureDirectory;
|
|
11
|
+
exports.resolvePath = resolvePath;
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
/**
|
|
15
|
+
* Custom error class for file operations
|
|
16
|
+
*/
|
|
17
|
+
class FileOperationError extends Error {
|
|
18
|
+
constructor(message, filePath, operation, originalError) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.filePath = filePath;
|
|
21
|
+
this.operation = operation;
|
|
22
|
+
this.originalError = originalError;
|
|
23
|
+
this.name = 'FileOperationError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.FileOperationError = FileOperationError;
|
|
27
|
+
/**
|
|
28
|
+
* Safely read and parse a JSON file with proper error handling
|
|
29
|
+
* @param filePath - Absolute or relative path to the JSON file
|
|
30
|
+
* @returns Parsed JSON object
|
|
31
|
+
* @throws FileOperationError if file cannot be read or parsed
|
|
32
|
+
*/
|
|
33
|
+
function readJsonFile(filePath) {
|
|
34
|
+
try {
|
|
35
|
+
// Check if file exists
|
|
36
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
37
|
+
throw new FileOperationError(`File not found: ${filePath}`, filePath, 'read');
|
|
38
|
+
}
|
|
39
|
+
// Read file content
|
|
40
|
+
let content;
|
|
41
|
+
try {
|
|
42
|
+
content = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
throw new FileOperationError(`Failed to read file: ${filePath}`, filePath, 'read', error);
|
|
46
|
+
}
|
|
47
|
+
// Parse JSON
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(content);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
throw new FileOperationError(`Invalid JSON in file: ${filePath}`, filePath, 'parse', error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error instanceof FileOperationError) {
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
throw new FileOperationError(`Unexpected error reading ${filePath}: ${error.message}`, filePath, 'read', error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Safely write JSON to a file with proper error handling
|
|
64
|
+
* @param filePath - Absolute or relative path to the JSON file
|
|
65
|
+
* @param data - Data to write (will be JSON.stringified)
|
|
66
|
+
* @param pretty - Whether to pretty-print the JSON (default: true)
|
|
67
|
+
*/
|
|
68
|
+
function writeJsonFile(filePath, data, pretty = true) {
|
|
69
|
+
try {
|
|
70
|
+
// Ensure directory exists
|
|
71
|
+
const dir = path_1.default.dirname(filePath);
|
|
72
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
73
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
// Stringify data
|
|
76
|
+
const content = pretty
|
|
77
|
+
? JSON.stringify(data, null, 2)
|
|
78
|
+
: JSON.stringify(data);
|
|
79
|
+
// Write to file
|
|
80
|
+
try {
|
|
81
|
+
fs_1.default.writeFileSync(filePath, content, 'utf-8');
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw new FileOperationError(`Failed to write file: ${filePath}`, filePath, 'write', error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (error instanceof FileOperationError) {
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
throw new FileOperationError(`Unexpected error writing ${filePath}: ${error.message}`, filePath, 'write', error);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if a file exists
|
|
96
|
+
* @param filePath - Path to check
|
|
97
|
+
* @returns true if file exists
|
|
98
|
+
*/
|
|
99
|
+
function fileExists(filePath) {
|
|
100
|
+
return fs_1.default.existsSync(filePath);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Ensure a directory exists, creating it if necessary
|
|
104
|
+
* @param dirPath - Directory path to ensure
|
|
105
|
+
*/
|
|
106
|
+
function ensureDirectory(dirPath) {
|
|
107
|
+
if (!fs_1.default.existsSync(dirPath)) {
|
|
108
|
+
fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Resolve a path relative to the project root
|
|
113
|
+
* @param relativePath - Path relative to project root
|
|
114
|
+
* @returns Absolute path
|
|
115
|
+
*/
|
|
116
|
+
function resolvePath(...segments) {
|
|
117
|
+
return path_1.default.join(process.cwd(), ...segments);
|
|
118
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/v2/llm/llm-advisor.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.getLLMAdvice = getLLMAdvice;
|
|
5
|
+
/**
|
|
6
|
+
* Stub for ChatGPT / Azure / Local LLM
|
|
7
|
+
*/
|
|
8
|
+
async function getLLMAdvice(failureSummary) {
|
|
9
|
+
return {
|
|
10
|
+
summary: `AI analysis of failure: ${failureSummary}`,
|
|
11
|
+
fixRecommendation: [
|
|
12
|
+
"Check backend API response",
|
|
13
|
+
"Validate UI rendering timing",
|
|
14
|
+
"Review recent code changes"
|
|
15
|
+
]
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/v2/pipeline/v2-intelligence.pipeline.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.runV2Intelligence = runV2Intelligence;
|
|
5
|
+
const trace_analyzer_1 = require("../trace-intelligence/trace-analyzer");
|
|
6
|
+
const self_healer_1 = require("../self-healing/self-healer");
|
|
7
|
+
const llm_advisor_1 = require("../llm/llm-advisor");
|
|
8
|
+
async function runV2Intelligence(failureFolder, failureType, errorMessage) {
|
|
9
|
+
const trace = (0, trace_analyzer_1.analyzeTrace)(failureFolder);
|
|
10
|
+
const healing = (0, self_healer_1.attemptSelfHeal)(failureType, errorMessage);
|
|
11
|
+
const llm = await (0, llm_advisor_1.getLLMAdvice)(failureType);
|
|
12
|
+
return {
|
|
13
|
+
trace,
|
|
14
|
+
healing,
|
|
15
|
+
llm
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/v2/self-healing/self-healer.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.attemptSelfHeal = attemptSelfHeal;
|
|
5
|
+
/**
|
|
6
|
+
* Self-Healing Decision Engine (V2)
|
|
7
|
+
* Decides WHAT can be auto-fixed and WHAT should not
|
|
8
|
+
*/
|
|
9
|
+
function attemptSelfHeal(failureType, errorMessage) {
|
|
10
|
+
const msg = errorMessage.toLowerCase();
|
|
11
|
+
// 🔁 Flaky tests
|
|
12
|
+
if (failureType === "FLAKY") {
|
|
13
|
+
return {
|
|
14
|
+
reason: "Intermittent retry-based failure detected",
|
|
15
|
+
suggestedFix: "Add proper waits or retry assertions",
|
|
16
|
+
confidence: 0.6
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// 🧭 UI / Locator issues
|
|
20
|
+
if (failureType === "UI_STATE" || failureType === "LOCATOR_CHANGED") {
|
|
21
|
+
return {
|
|
22
|
+
reason: "UI element state or locator changed",
|
|
23
|
+
suggestedFix: "Re-locate element using role/text-based selectors",
|
|
24
|
+
confidence: 0.7
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
// 🔢 Numeric mismatch — CONDITIONAL healing
|
|
28
|
+
if (failureType === "NUMERIC_MISMATCH") {
|
|
29
|
+
// UI-level numeric problems (healable)
|
|
30
|
+
if (msg.includes("count") ||
|
|
31
|
+
msg.includes("items") ||
|
|
32
|
+
msg.includes("length") ||
|
|
33
|
+
msg.includes("round")) {
|
|
34
|
+
return {
|
|
35
|
+
reason: "UI count or rounding issue detected",
|
|
36
|
+
suggestedFix: "Update assertion logic or normalize UI values",
|
|
37
|
+
confidence: 0.55
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// Backend/API numeric issues (DO NOT HEAL)
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/v2/trace-intelligence/trace-analyzer.ts
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.analyzeTrace = analyzeTrace;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
function analyzeTrace(failureFolder) {
|
|
11
|
+
const fullPath = path_1.default.resolve(failureFolder);
|
|
12
|
+
if (!fs_1.default.existsSync(fullPath)) {
|
|
13
|
+
return {
|
|
14
|
+
page: "UNKNOWN",
|
|
15
|
+
failureStep: "Trace folder not found",
|
|
16
|
+
screenshots: [],
|
|
17
|
+
note: `Missing folder: ${failureFolder}`
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const files = fs_1.default.readdirSync(fullPath);
|
|
21
|
+
const screenshots = files
|
|
22
|
+
.filter(f => f.endsWith(".png") || f.endsWith(".jpg"))
|
|
23
|
+
.map(f => path_1.default.join(fullPath, f));
|
|
24
|
+
const traceFile = files.includes("trace.zip")
|
|
25
|
+
? path_1.default.join(fullPath, "trace.zip")
|
|
26
|
+
: undefined;
|
|
27
|
+
return {
|
|
28
|
+
page: "UNKNOWN_PAGE",
|
|
29
|
+
failureStep: "Action before failure",
|
|
30
|
+
screenshots,
|
|
31
|
+
traceFile
|
|
32
|
+
};
|
|
33
|
+
}
|