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
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Quality Intelligence Engine
|
|
2
|
+
|
|
3
|
+
**Deterministic Test Failure Classification & Analysis for Playwright**
|
|
4
|
+
|
|
5
|
+
A rule-based intelligent system that automatically classifies test failures, provides root cause analysis, and delivers actionable insights—without relying on AI black boxes.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🎯 Overview
|
|
10
|
+
|
|
11
|
+
The Quality Intelligence Engine analyzes Playwright test failures and classifies them into:
|
|
12
|
+
|
|
13
|
+
- **API_BUG**: Backend/API failures (authentication errors, data mismatches, server errors)
|
|
14
|
+
- **UI_BUG**: Frontend/UI issues (visibility problems, rendering failures, element issues)
|
|
15
|
+
|
|
16
|
+
Each classification comes with:
|
|
17
|
+
- ✅ **Confidence score** (0-100%) - How certain we are about the classification
|
|
18
|
+
- ✅ **Root cause analysis** - What went wrong and why
|
|
19
|
+
- ✅ **Evidence** - Signals that led to the classification
|
|
20
|
+
- ✅ **Diagnosis** - Step-by-step breakdown of the failure
|
|
21
|
+
- ✅ **Suggested actions** - Concrete next steps to fix the issue
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🚀 Quick Start
|
|
26
|
+
|
|
27
|
+
### Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Run Analysis
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Run Playwright tests
|
|
37
|
+
npx playwright test
|
|
38
|
+
|
|
39
|
+
# Run Quality Intelligence Engine
|
|
40
|
+
npm start
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The engine will:
|
|
44
|
+
1. Read test results from `playwright-report/report.json`
|
|
45
|
+
2. Classify failures using the taxonomy
|
|
46
|
+
3. Generate detailed analysis reports
|
|
47
|
+
4. Output results to `output/` directory
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 📁 Project Structure
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
quality-intelligence-engine/
|
|
55
|
+
├── src/
|
|
56
|
+
│ ├── intelligence/ # Classification algorithms
|
|
57
|
+
│ ├── pipeline/ # Data processing
|
|
58
|
+
│ ├── utils/ # Utilities
|
|
59
|
+
│ ├── types.ts # Type definitions
|
|
60
|
+
│ ├── normalizer.ts # Input normalization
|
|
61
|
+
│ ├── configLoader.ts # Configuration
|
|
62
|
+
│ └── reporter.ts # Output generation
|
|
63
|
+
├── tests/ # Example test cases
|
|
64
|
+
├── config/ # Configuration files
|
|
65
|
+
└── playwright.config.ts # Playwright configuration
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## ⚙️ Configuration
|
|
71
|
+
|
|
72
|
+
Edit `config/agent.config.json`:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"engine": {
|
|
77
|
+
"mode": "standard",
|
|
78
|
+
"confidenceThresholds": {
|
|
79
|
+
"fail": 0.85,
|
|
80
|
+
"passRisk": 0.6
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 🏷️ Taxonomy System
|
|
89
|
+
|
|
90
|
+
Tests are tagged with their expected failure type:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
test('Data | NUMERIC_MISMATCH | inventory count wrong', async ({ page }) => {
|
|
94
|
+
// Test code...
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 📊 Confidence Scoring
|
|
101
|
+
|
|
102
|
+
Deterministic, explainable confidence calculations:
|
|
103
|
+
|
|
104
|
+
- **Base Confidence**: 0.6 - 0.95
|
|
105
|
+
- **Adjustments**: +0.05 - +0.1 based on signals
|
|
106
|
+
- **Maximum**: 0.95 (never 100% certain)
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 🔧 Development
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Run tests
|
|
114
|
+
npx playwright test
|
|
115
|
+
|
|
116
|
+
# Type check
|
|
117
|
+
npx tsc --noEmit
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
**Version**: 1.0.0 (Refactored)
|
|
123
|
+
**Last Updated**: 2026-01-29
|
package/dist/bin/qi.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
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 path_1 = __importDefault(require("path"));
|
|
8
|
+
/**
|
|
9
|
+
* CLI entry point for Quality Intelligence
|
|
10
|
+
* Compiled and executed as CommonJS
|
|
11
|
+
*/
|
|
12
|
+
async function main() {
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const playwrightResultsDir = args.find(a => !a.startsWith("--")) ?? "playwright-results";
|
|
15
|
+
const showIssues = args.includes("--issue") || args.includes("--issues");
|
|
16
|
+
// Resolve project root (works after npm install)
|
|
17
|
+
const projectRoot = path_1.default.resolve(__dirname, "..");
|
|
18
|
+
// IMPORTANT: call compiled JS, never TS
|
|
19
|
+
const finalRunPath = path_1.default.join(projectRoot, "src", "final-run.js");
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
21
|
+
const { runFinal } = require(finalRunPath);
|
|
22
|
+
runFinal(playwrightResultsDir, showIssues);
|
|
23
|
+
}
|
|
24
|
+
main().catch(err => {
|
|
25
|
+
console.error("QI failed:", err);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/adapters/playwright-folder.adapter.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.parsePlaywrightFailureFolders = parsePlaywrightFailureFolders;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
/**
|
|
11
|
+
* Improved Playwright failure folder parser
|
|
12
|
+
* Focus: reduce UNKNOWN classifications
|
|
13
|
+
*/
|
|
14
|
+
function parsePlaywrightFailureFolders(resultsDir) {
|
|
15
|
+
const fullDir = path_1.default.resolve(resultsDir);
|
|
16
|
+
if (!fs_1.default.existsSync(fullDir)) {
|
|
17
|
+
throw new Error(`Playwright results directory not found: ${fullDir}`);
|
|
18
|
+
}
|
|
19
|
+
const entries = fs_1.default.readdirSync(fullDir, { withFileTypes: true });
|
|
20
|
+
const failures = [];
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
if (!entry.isDirectory())
|
|
23
|
+
continue;
|
|
24
|
+
const name = entry.name;
|
|
25
|
+
if (!name.includes("FAIL"))
|
|
26
|
+
continue;
|
|
27
|
+
const lower = name.toLowerCase();
|
|
28
|
+
let classification = "UNKNOWN";
|
|
29
|
+
// 🔢 Numeric / data mismatches
|
|
30
|
+
if (lower.includes("numeric-mismatch") ||
|
|
31
|
+
lower.includes("count") ||
|
|
32
|
+
lower.includes("total") ||
|
|
33
|
+
lower.includes("sum") ||
|
|
34
|
+
lower.includes("price")) {
|
|
35
|
+
classification = "NUMERIC_MISMATCH";
|
|
36
|
+
}
|
|
37
|
+
// 🔐 Auth / permission issues
|
|
38
|
+
else if (lower.includes("unauthorized") ||
|
|
39
|
+
lower.includes("forbidden") ||
|
|
40
|
+
lower.includes("permission")) {
|
|
41
|
+
classification = "AUTH_BUG";
|
|
42
|
+
}
|
|
43
|
+
// ⏱ Timeouts / waits
|
|
44
|
+
else if (lower.includes("timeout") ||
|
|
45
|
+
lower.includes("timed-out") ||
|
|
46
|
+
lower.includes("wait")) {
|
|
47
|
+
classification = "TIMEOUT";
|
|
48
|
+
}
|
|
49
|
+
// 🧭 UI / locator issues
|
|
50
|
+
else if (lower.includes("locator") ||
|
|
51
|
+
lower.includes("not-visible") ||
|
|
52
|
+
lower.includes("hidden") ||
|
|
53
|
+
lower.includes("detached")) {
|
|
54
|
+
classification = "UI_STATE";
|
|
55
|
+
}
|
|
56
|
+
// 🔁 Retry / flaky signal
|
|
57
|
+
else if (lower.includes("retry")) {
|
|
58
|
+
classification = "FLAKY";
|
|
59
|
+
}
|
|
60
|
+
failures.push({
|
|
61
|
+
testName: name,
|
|
62
|
+
classification,
|
|
63
|
+
failureType: classification,
|
|
64
|
+
message: `Derived from Playwright folder name: ${name}`
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return failures;
|
|
68
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/adapters/playwright-json.adapter.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.parsePlaywrightJsonReport = parsePlaywrightJsonReport;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
/**
|
|
11
|
+
* Converts Playwright JSON report → FailureAnalysisResult[]
|
|
12
|
+
*/
|
|
13
|
+
function parsePlaywrightJsonReport(reportPath) {
|
|
14
|
+
const fullPath = path_1.default.resolve(reportPath);
|
|
15
|
+
const raw = fs_1.default.readFileSync(fullPath, "utf-8");
|
|
16
|
+
const report = JSON.parse(raw);
|
|
17
|
+
const failures = [];
|
|
18
|
+
for (const suite of report.suites ?? []) {
|
|
19
|
+
for (const spec of suite.specs ?? []) {
|
|
20
|
+
for (const test of spec.tests ?? []) {
|
|
21
|
+
for (const result of test.results ?? []) {
|
|
22
|
+
if (result.status === "failed") {
|
|
23
|
+
const error = result.error?.message ?? "";
|
|
24
|
+
let classification = "UNKNOWN";
|
|
25
|
+
if (error.includes("toHaveCount")) {
|
|
26
|
+
classification = "NUMERIC_MISMATCH";
|
|
27
|
+
}
|
|
28
|
+
else if (error.includes("timeout")) {
|
|
29
|
+
classification = "TIMEOUT";
|
|
30
|
+
}
|
|
31
|
+
else if (error.includes("locator")) {
|
|
32
|
+
classification = "LOCATOR_CHANGED";
|
|
33
|
+
}
|
|
34
|
+
failures.push({
|
|
35
|
+
testName: test.title,
|
|
36
|
+
classification,
|
|
37
|
+
failureType: classification,
|
|
38
|
+
message: error
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return failures;
|
|
46
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
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.readPlaywrightFailures = readPlaywrightFailures;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
function readPlaywrightFailures(reportDir) {
|
|
10
|
+
const reportFile = path_1.default.join(reportDir, 'report.json');
|
|
11
|
+
if (!fs_1.default.existsSync(reportFile)) {
|
|
12
|
+
throw new Error(`Playwright JSON report not found.\nExpected at: ${reportFile}`);
|
|
13
|
+
}
|
|
14
|
+
const raw = JSON.parse(fs_1.default.readFileSync(reportFile, 'utf-8'));
|
|
15
|
+
const failures = [];
|
|
16
|
+
for (const suite of raw.suites ?? []) {
|
|
17
|
+
for (const spec of suite.specs ?? []) {
|
|
18
|
+
for (const test of spec.tests ?? []) {
|
|
19
|
+
const failedResults = test.results?.filter((r) => r.status === 'failed');
|
|
20
|
+
if (!failedResults || failedResults.length === 0)
|
|
21
|
+
continue;
|
|
22
|
+
const firstFailure = failedResults[0];
|
|
23
|
+
const error = firstFailure.error;
|
|
24
|
+
failures.push({
|
|
25
|
+
testName: spec.title,
|
|
26
|
+
errorMessage: error?.message ?? 'Unknown error',
|
|
27
|
+
stack: error?.stack,
|
|
28
|
+
filePath: error?.location?.file ?? spec.file,
|
|
29
|
+
lineNumber: error?.location?.line ?? spec.line ?? 0,
|
|
30
|
+
retryCount: failedResults.length - 1
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return failures;
|
|
36
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runQiTest = runQiTest;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
function runQiTest(args) {
|
|
6
|
+
const playwrightArgs = [
|
|
7
|
+
'playwright',
|
|
8
|
+
'test',
|
|
9
|
+
'--config',
|
|
10
|
+
'playwright.config.ts',
|
|
11
|
+
...args
|
|
12
|
+
];
|
|
13
|
+
const child = (0, child_process_1.spawn)('npx', playwrightArgs, {
|
|
14
|
+
stdio: 'inherit',
|
|
15
|
+
shell: true
|
|
16
|
+
});
|
|
17
|
+
child.on('exit', code => {
|
|
18
|
+
process.exit(code ?? 0);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
// src/cli/qi.ts
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.runQiCli = runQiCli;
|
|
6
|
+
const runner_1 = require("../runner");
|
|
7
|
+
/**
|
|
8
|
+
* Core CLI runner (reusable)
|
|
9
|
+
*/
|
|
10
|
+
async function runQiCli(input) {
|
|
11
|
+
const results = await (0, runner_1.runQualityIntelligence)(input);
|
|
12
|
+
if (!Array.isArray(results) || results.length === 0) {
|
|
13
|
+
console.log("No failures detected.");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
for (const f of results) {
|
|
17
|
+
const testName = f.testName ?? "UNKNOWN_TEST";
|
|
18
|
+
const isExpected = testName.includes("EXPECTED");
|
|
19
|
+
console.log("=================================");
|
|
20
|
+
console.log(`Test Name : ${testName}${isExpected ? " (EXPECTED)" : ""}`);
|
|
21
|
+
console.log(`Failure Type : ${f.failureType ?? f.classification}`);
|
|
22
|
+
console.log(`Confidence : ${f.confidence ?? "N/A"}\n`);
|
|
23
|
+
if (f.rootCause?.length) {
|
|
24
|
+
console.log("Root Cause:");
|
|
25
|
+
for (const line of f.rootCause) {
|
|
26
|
+
console.log(` - ${line}`);
|
|
27
|
+
}
|
|
28
|
+
console.log();
|
|
29
|
+
}
|
|
30
|
+
if (f.evidence?.length) {
|
|
31
|
+
console.log("Evidence:");
|
|
32
|
+
for (const line of f.evidence) {
|
|
33
|
+
console.log(` - ${line}`);
|
|
34
|
+
}
|
|
35
|
+
console.log();
|
|
36
|
+
}
|
|
37
|
+
if (f.diagnosis?.length) {
|
|
38
|
+
console.log("Diagnosis:");
|
|
39
|
+
for (const line of f.diagnosis) {
|
|
40
|
+
console.log(` - ${line}`);
|
|
41
|
+
}
|
|
42
|
+
console.log();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* CLI ENTRY POINT
|
|
48
|
+
* Allows: npx qi <path>
|
|
49
|
+
*/
|
|
50
|
+
async function main() {
|
|
51
|
+
const input = process.argv[2] ?? "playwright-results";
|
|
52
|
+
await runQiCli(input);
|
|
53
|
+
}
|
|
54
|
+
if (process.argv[1]?.includes("qi")) {
|
|
55
|
+
main().catch(err => {
|
|
56
|
+
console.error("QI CLI failed:", err);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.divider = divider;
|
|
4
|
+
function divider(title, width = 60) {
|
|
5
|
+
const line = '━'.repeat(width);
|
|
6
|
+
if (!title) {
|
|
7
|
+
return `\n${line}\n`;
|
|
8
|
+
}
|
|
9
|
+
return `\n${line}\n${title}\n${line}\n`;
|
|
10
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logFailure = logFailure;
|
|
4
|
+
function logFailure(input) {
|
|
5
|
+
const confidencePercent = Math.round(input.confidence * 100);
|
|
6
|
+
console.log(`
|
|
7
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
8
|
+
❌ Automation Test Failure
|
|
9
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
10
|
+
|
|
11
|
+
Test Name : ${input.testName}
|
|
12
|
+
Failure Type : ${input.failureType}
|
|
13
|
+
Severity : ${input.severity}
|
|
14
|
+
Priority : ${input.priority}
|
|
15
|
+
Confidence : ${confidencePercent}%
|
|
16
|
+
|
|
17
|
+
Failure Location:
|
|
18
|
+
- Test File : ${input.file}
|
|
19
|
+
- Line Number : ${input.line}
|
|
20
|
+
|
|
21
|
+
Root Cause:
|
|
22
|
+
- ${input.rootCause}
|
|
23
|
+
`);
|
|
24
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadConfig = loadConfig;
|
|
4
|
+
const file_utils_1 = require("./utils/file-utils");
|
|
5
|
+
/**
|
|
6
|
+
* Validate configuration object structure
|
|
7
|
+
*/
|
|
8
|
+
function validateConfig(config) {
|
|
9
|
+
if (typeof config !== 'object' || config === null) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const cfg = config;
|
|
13
|
+
// Validate engine section
|
|
14
|
+
if (typeof cfg.engine !== 'object' ||
|
|
15
|
+
cfg.engine === null) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const engine = cfg.engine;
|
|
19
|
+
if (typeof engine.mode !== 'string' ||
|
|
20
|
+
typeof engine.confidenceThresholds !== 'object') {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Load and validate configuration
|
|
27
|
+
*/
|
|
28
|
+
function loadConfig() {
|
|
29
|
+
try {
|
|
30
|
+
const configPath = (0, file_utils_1.resolvePath)('config', 'agent.config.json');
|
|
31
|
+
const config = (0, file_utils_1.readJsonFile)(configPath);
|
|
32
|
+
if (!validateConfig(config)) {
|
|
33
|
+
throw new Error('Invalid configuration structure');
|
|
34
|
+
}
|
|
35
|
+
return config;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error('❌ Failed to load configuration:', error);
|
|
39
|
+
console.error('\n💡 Using default configuration');
|
|
40
|
+
// Return safe defaults
|
|
41
|
+
return {
|
|
42
|
+
engine: {
|
|
43
|
+
mode: 'standard',
|
|
44
|
+
confidenceThresholds: {
|
|
45
|
+
fail: 0.85,
|
|
46
|
+
passRisk: 0.6
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
output: {
|
|
50
|
+
writeMarkdown: true,
|
|
51
|
+
maxHistoryRuns: 10
|
|
52
|
+
},
|
|
53
|
+
passAnalysis: {
|
|
54
|
+
enable: true,
|
|
55
|
+
reportFlakyPass: true,
|
|
56
|
+
reportApiWarnings: true
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/console/issue-view.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.extractIssues = extractIssues;
|
|
8
|
+
exports.filterIssues = filterIssues;
|
|
9
|
+
exports.printIssues = printIssues;
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
/* ---------------- Issue Core ---------------- */
|
|
13
|
+
function extractIssues(groupedCounts, confidence) {
|
|
14
|
+
return Object.entries(groupedCounts).map(([type, count]) => ({
|
|
15
|
+
type,
|
|
16
|
+
occurrences: count,
|
|
17
|
+
confidence
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
/* ---------------- Decision Logic ---------------- */
|
|
21
|
+
function computeIssueDecision(issue) {
|
|
22
|
+
switch (issue.type) {
|
|
23
|
+
case "FLAKY":
|
|
24
|
+
return {
|
|
25
|
+
autoHeal: true,
|
|
26
|
+
reason: "Retry-based flaky behavior detected; stabilization is safe"
|
|
27
|
+
};
|
|
28
|
+
case "NUMERIC_MISMATCH":
|
|
29
|
+
return {
|
|
30
|
+
autoHeal: false,
|
|
31
|
+
reason: "Numeric mismatch likely backend or data-related"
|
|
32
|
+
};
|
|
33
|
+
default:
|
|
34
|
+
return {
|
|
35
|
+
autoHeal: false,
|
|
36
|
+
reason: "Insufficient signal to safely auto-heal"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/* ---------------- Owner Suggestion ---------------- */
|
|
41
|
+
function computeIssueOwner(issue) {
|
|
42
|
+
switch (issue.type) {
|
|
43
|
+
case "NUMERIC_MISMATCH":
|
|
44
|
+
return {
|
|
45
|
+
team: "Backend",
|
|
46
|
+
reason: "Mismatch usually originates from API data or backend calculations"
|
|
47
|
+
};
|
|
48
|
+
case "FLAKY":
|
|
49
|
+
return {
|
|
50
|
+
team: "QA",
|
|
51
|
+
reason: "Flaky behavior typically caused by timing, waits, or test stability"
|
|
52
|
+
};
|
|
53
|
+
case "UNKNOWN":
|
|
54
|
+
default:
|
|
55
|
+
return {
|
|
56
|
+
team: "Frontend",
|
|
57
|
+
reason: "Failure source unclear; frontend investigation needed first"
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/* ---------------- Evidence Picker ---------------- */
|
|
62
|
+
function pickIssueEvidence(failureFolder) {
|
|
63
|
+
if (!fs_1.default.existsSync(failureFolder))
|
|
64
|
+
return {};
|
|
65
|
+
const files = fs_1.default.readdirSync(failureFolder);
|
|
66
|
+
const screenshot = files.find(f => f.startsWith("test-failed") && f.endsWith(".png"));
|
|
67
|
+
const trace = files.includes("trace.zip") ? "trace.zip" : undefined;
|
|
68
|
+
return {
|
|
69
|
+
screenshot: screenshot
|
|
70
|
+
? path_1.default.join(failureFolder, screenshot)
|
|
71
|
+
: undefined,
|
|
72
|
+
trace: trace
|
|
73
|
+
? path_1.default.join(failureFolder, trace)
|
|
74
|
+
: undefined
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/* ---------------- Filtering ---------------- */
|
|
78
|
+
function filterIssues(issues, filter) {
|
|
79
|
+
let result = [...issues];
|
|
80
|
+
if (filter.keyword) {
|
|
81
|
+
const k = filter.keyword.toLowerCase();
|
|
82
|
+
result = result.filter(i => i.type.toLowerCase().includes(k));
|
|
83
|
+
}
|
|
84
|
+
result.sort((a, b) => b.occurrences * b.confidence -
|
|
85
|
+
a.occurrences * a.confidence);
|
|
86
|
+
if (filter.topN !== undefined) {
|
|
87
|
+
result = result.slice(0, filter.topN);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
/* ---------------- Console Output ---------------- */
|
|
92
|
+
function printIssues(issues, failureFolder) {
|
|
93
|
+
if (issues.length === 0) {
|
|
94
|
+
console.log("No matching issues found.");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const evidence = pickIssueEvidence(failureFolder);
|
|
98
|
+
issues.forEach((issue, index) => {
|
|
99
|
+
const decision = computeIssueDecision(issue);
|
|
100
|
+
const owner = computeIssueOwner(issue);
|
|
101
|
+
console.log(`================ ISSUE #${index + 1} =================`);
|
|
102
|
+
console.log(`Type : ${issue.type}`);
|
|
103
|
+
console.log(`Occurrences : ${issue.occurrences}`);
|
|
104
|
+
console.log(`Confidence : ${issue.confidence}`);
|
|
105
|
+
console.log("Decision:");
|
|
106
|
+
console.log(` - Auto-heal: ${decision.autoHeal ? "YES" : "NO"}`);
|
|
107
|
+
console.log(` - Reason : ${decision.reason}`);
|
|
108
|
+
console.log("Owner:");
|
|
109
|
+
console.log(` - Team : ${owner.team}`);
|
|
110
|
+
console.log(` - Why : ${owner.reason}`);
|
|
111
|
+
if (evidence.screenshot || evidence.trace) {
|
|
112
|
+
console.log("\nEvidence:");
|
|
113
|
+
if (evidence.screenshot) {
|
|
114
|
+
console.log(` Screenshot : ${evidence.screenshot}`);
|
|
115
|
+
}
|
|
116
|
+
if (evidence.trace) {
|
|
117
|
+
console.log(` Trace : npx playwright show-trace ${evidence.trace}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
console.log("======================================================");
|
|
121
|
+
});
|
|
122
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/failure-analysis/failure-analyzer.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.FailureAnalyzer = void 0;
|
|
5
|
+
/**
|
|
6
|
+
* FailureAnalyzer
|
|
7
|
+
* ----------------
|
|
8
|
+
* Phase 1 core analyzer.
|
|
9
|
+
* This class is intentionally simple and stable.
|
|
10
|
+
* Do NOT add history, trends, or intelligence here.
|
|
11
|
+
*/
|
|
12
|
+
class FailureAnalyzer {
|
|
13
|
+
analyze(input) {
|
|
14
|
+
if (!input || !Array.isArray(input)) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
return input.map((item) => {
|
|
18
|
+
// Minimal safe classification logic
|
|
19
|
+
if (item?.type === "NUMERIC_MISMATCH") {
|
|
20
|
+
return {
|
|
21
|
+
classification: "NUMERIC_MISMATCH",
|
|
22
|
+
message: item.message
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (item?.type === "API_BUG") {
|
|
26
|
+
return {
|
|
27
|
+
classification: "API_BUG",
|
|
28
|
+
message: item.message
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
classification: "UNKNOWN",
|
|
33
|
+
message: item?.message
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.FailureAnalyzer = FailureAnalyzer;
|