quality-intelligence-engine 2.2.0
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 +123 -0
- package/dist/bin/qi.js +27 -0
- 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 +122 -0
- package/dist/src/failure-analysis/failure-analyzer.js +38 -0
- package/dist/src/final-run.js +130 -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 +20 -0
- package/dist/src/intelligence/failure-fingerprinting/failure.store.js +39 -0
- package/dist/src/intelligence/failure-fingerprinting/failure.tracker.js +15 -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 +47 -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 +121 -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 +23 -0
|
@@ -0,0 +1,121 @@
|
|
|
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
|
+
filePath;
|
|
19
|
+
operation;
|
|
20
|
+
originalError;
|
|
21
|
+
constructor(message, filePath, operation, originalError) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.filePath = filePath;
|
|
24
|
+
this.operation = operation;
|
|
25
|
+
this.originalError = originalError;
|
|
26
|
+
this.name = 'FileOperationError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.FileOperationError = FileOperationError;
|
|
30
|
+
/**
|
|
31
|
+
* Safely read and parse a JSON file with proper error handling
|
|
32
|
+
* @param filePath - Absolute or relative path to the JSON file
|
|
33
|
+
* @returns Parsed JSON object
|
|
34
|
+
* @throws FileOperationError if file cannot be read or parsed
|
|
35
|
+
*/
|
|
36
|
+
function readJsonFile(filePath) {
|
|
37
|
+
try {
|
|
38
|
+
// Check if file exists
|
|
39
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
40
|
+
throw new FileOperationError(`File not found: ${filePath}`, filePath, 'read');
|
|
41
|
+
}
|
|
42
|
+
// Read file content
|
|
43
|
+
let content;
|
|
44
|
+
try {
|
|
45
|
+
content = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
throw new FileOperationError(`Failed to read file: ${filePath}`, filePath, 'read', error);
|
|
49
|
+
}
|
|
50
|
+
// Parse JSON
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(content);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw new FileOperationError(`Invalid JSON in file: ${filePath}`, filePath, 'parse', error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error instanceof FileOperationError) {
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
throw new FileOperationError(`Unexpected error reading ${filePath}: ${error.message}`, filePath, 'read', error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Safely write JSON to a file with proper error handling
|
|
67
|
+
* @param filePath - Absolute or relative path to the JSON file
|
|
68
|
+
* @param data - Data to write (will be JSON.stringified)
|
|
69
|
+
* @param pretty - Whether to pretty-print the JSON (default: true)
|
|
70
|
+
*/
|
|
71
|
+
function writeJsonFile(filePath, data, pretty = true) {
|
|
72
|
+
try {
|
|
73
|
+
// Ensure directory exists
|
|
74
|
+
const dir = path_1.default.dirname(filePath);
|
|
75
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
76
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
// Stringify data
|
|
79
|
+
const content = pretty
|
|
80
|
+
? JSON.stringify(data, null, 2)
|
|
81
|
+
: JSON.stringify(data);
|
|
82
|
+
// Write to file
|
|
83
|
+
try {
|
|
84
|
+
fs_1.default.writeFileSync(filePath, content, 'utf-8');
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
throw new FileOperationError(`Failed to write file: ${filePath}`, filePath, 'write', error);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (error instanceof FileOperationError) {
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
throw new FileOperationError(`Unexpected error writing ${filePath}: ${error.message}`, filePath, 'write', error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Check if a file exists
|
|
99
|
+
* @param filePath - Path to check
|
|
100
|
+
* @returns true if file exists
|
|
101
|
+
*/
|
|
102
|
+
function fileExists(filePath) {
|
|
103
|
+
return fs_1.default.existsSync(filePath);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Ensure a directory exists, creating it if necessary
|
|
107
|
+
* @param dirPath - Directory path to ensure
|
|
108
|
+
*/
|
|
109
|
+
function ensureDirectory(dirPath) {
|
|
110
|
+
if (!fs_1.default.existsSync(dirPath)) {
|
|
111
|
+
fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Resolve a path relative to the project root
|
|
116
|
+
* @param relativePath - Path relative to project root
|
|
117
|
+
* @returns Absolute path
|
|
118
|
+
*/
|
|
119
|
+
function resolvePath(...segments) {
|
|
120
|
+
return path_1.default.join(process.cwd(), ...segments);
|
|
121
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/v2-test-run.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
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const v2_intelligence_pipeline_1 = require("./v2/pipeline/v2-intelligence.pipeline");
|
|
10
|
+
function pickOneFailureFolder() {
|
|
11
|
+
const base = "playwright-results";
|
|
12
|
+
if (!fs_1.default.existsSync(base))
|
|
13
|
+
return null;
|
|
14
|
+
const entries = fs_1.default.readdirSync(base, { withFileTypes: true });
|
|
15
|
+
const folder = entries.find(e => e.isDirectory());
|
|
16
|
+
return folder ? path_1.default.join(base, folder.name) : null;
|
|
17
|
+
}
|
|
18
|
+
async function run() {
|
|
19
|
+
const failureFolder = pickOneFailureFolder();
|
|
20
|
+
if (!failureFolder) {
|
|
21
|
+
console.error("No Playwright failure folders found.");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const result = await (0, v2_intelligence_pipeline_1.runV2Intelligence)(failureFolder, "NUMERIC_MISMATCH", "Expected 10, received 6");
|
|
25
|
+
console.log("==================================================");
|
|
26
|
+
console.log("V2 TRACE INTELLIGENCE OUTPUT");
|
|
27
|
+
console.log("==================================================");
|
|
28
|
+
console.log(`Failure Folder : ${failureFolder}`);
|
|
29
|
+
console.log(`Failure Type : NUMERIC_MISMATCH`);
|
|
30
|
+
console.log("\nEvidence:");
|
|
31
|
+
if (result.trace.screenshots.length > 0) {
|
|
32
|
+
console.log(" Screenshots:");
|
|
33
|
+
result.trace.screenshots.forEach(s => console.log(` - ${s}`));
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.log(" Screenshots: none");
|
|
37
|
+
}
|
|
38
|
+
if (result.trace.traceFile) {
|
|
39
|
+
console.log(` Trace File : ${result.trace.traceFile}`);
|
|
40
|
+
console.log(` Open with : npx playwright show-trace ${result.trace.traceFile}`);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.log(" Trace File : none");
|
|
44
|
+
}
|
|
45
|
+
console.log("\nSelf-Healing:");
|
|
46
|
+
if (result.healing) {
|
|
47
|
+
console.log(` Reason : ${result.healing.reason}`);
|
|
48
|
+
console.log(` SuggestedFix : ${result.healing.suggestedFix}`);
|
|
49
|
+
console.log(` Confidence : ${result.healing.confidence}`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log(" Not safe to auto-heal");
|
|
53
|
+
}
|
|
54
|
+
console.log("\nAI Advice:");
|
|
55
|
+
console.log(` Summary: ${result.llm.summary}`);
|
|
56
|
+
result.llm.fixRecommendation.forEach(r => console.log(` - ${r}`));
|
|
57
|
+
console.log("==================================================");
|
|
58
|
+
}
|
|
59
|
+
run();
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "quality-intelligence-engine",
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "AI-powered failure intelligence for Playwright test runs",
|
|
5
|
+
"bin": {
|
|
6
|
+
"qi": "dist/bin/qi.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@playwright/test": "^1.58.0",
|
|
20
|
+
"ts-node": "^10.9.2",
|
|
21
|
+
"typescript": "^5.3.3"
|
|
22
|
+
}
|
|
23
|
+
}
|