api-tests-coverage 1.0.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 +703 -0
- package/config.yaml.example +227 -0
- package/dist/action/src/index.d.ts +2 -0
- package/dist/action/src/index.d.ts.map +1 -0
- package/dist/action/src/index.js +349 -0
- package/dist/action/src/prComment.d.ts +34 -0
- package/dist/action/src/prComment.d.ts.map +1 -0
- package/dist/action/src/prComment.js +146 -0
- package/dist/src/ast/astAnalysisOrchestrator.d.ts +36 -0
- package/dist/src/ast/astAnalysisOrchestrator.d.ts.map +1 -0
- package/dist/src/ast/astAnalysisOrchestrator.js +123 -0
- package/dist/src/ast/astTypes.d.ts +105 -0
- package/dist/src/ast/astTypes.d.ts.map +1 -0
- package/dist/src/ast/astTypes.js +9 -0
- package/dist/src/ast/languageAnalyzer.d.ts +46 -0
- package/dist/src/ast/languageAnalyzer.d.ts.map +1 -0
- package/dist/src/ast/languageAnalyzer.js +9 -0
- package/dist/src/ast/languageCapabilities.d.ts +24 -0
- package/dist/src/ast/languageCapabilities.d.ts.map +1 -0
- package/dist/src/ast/languageCapabilities.js +92 -0
- package/dist/src/ast/parseFile.d.ts +16 -0
- package/dist/src/ast/parseFile.d.ts.map +1 -0
- package/dist/src/ast/parseFile.js +65 -0
- package/dist/src/ast/parserRegistry.d.ts +39 -0
- package/dist/src/ast/parserRegistry.d.ts.map +1 -0
- package/dist/src/ast/parserRegistry.js +66 -0
- package/dist/src/buildSummary.d.ts +26 -0
- package/dist/src/buildSummary.d.ts.map +1 -0
- package/dist/src/buildSummary.js +193 -0
- package/dist/src/businessCoverage.d.ts +68 -0
- package/dist/src/businessCoverage.d.ts.map +1 -0
- package/dist/src/businessCoverage.js +290 -0
- package/dist/src/compatibilityCoverage.d.ts +83 -0
- package/dist/src/compatibilityCoverage.d.ts.map +1 -0
- package/dist/src/compatibilityCoverage.js +501 -0
- package/dist/src/config/defaultConfig.d.ts +9 -0
- package/dist/src/config/defaultConfig.d.ts.map +1 -0
- package/dist/src/config/defaultConfig.js +97 -0
- package/dist/src/config/index.d.ts +12 -0
- package/dist/src/config/index.d.ts.map +1 -0
- package/dist/src/config/index.js +37 -0
- package/dist/src/config/loadConfig.d.ts +29 -0
- package/dist/src/config/loadConfig.d.ts.map +1 -0
- package/dist/src/config/loadConfig.js +135 -0
- package/dist/src/config/mergeConfig.d.ts +15 -0
- package/dist/src/config/mergeConfig.d.ts.map +1 -0
- package/dist/src/config/mergeConfig.js +57 -0
- package/dist/src/config/schema.d.ts +15 -0
- package/dist/src/config/schema.d.ts.map +1 -0
- package/dist/src/config/schema.js +30 -0
- package/dist/src/config/types.d.ts +175 -0
- package/dist/src/config/types.d.ts.map +1 -0
- package/dist/src/config/types.js +9 -0
- package/dist/src/config/validateConfig.d.ts +22 -0
- package/dist/src/config/validateConfig.d.ts.map +1 -0
- package/dist/src/config/validateConfig.js +171 -0
- package/dist/src/config.d.ts +168 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +204 -0
- package/dist/src/coverage/deep-analysis/callGraph.d.ts +67 -0
- package/dist/src/coverage/deep-analysis/callGraph.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/callGraph.js +275 -0
- package/dist/src/coverage/deep-analysis/deepEndpointResolver.d.ts +23 -0
- package/dist/src/coverage/deep-analysis/deepEndpointResolver.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/deepEndpointResolver.js +394 -0
- package/dist/src/coverage/deep-analysis/index.d.ts +17 -0
- package/dist/src/coverage/deep-analysis/index.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/index.js +63 -0
- package/dist/src/coverage/deep-analysis/resolveAssertions.d.ts +60 -0
- package/dist/src/coverage/deep-analysis/resolveAssertions.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveAssertions.js +121 -0
- package/dist/src/coverage/deep-analysis/resolveConstants.d.ts +36 -0
- package/dist/src/coverage/deep-analysis/resolveConstants.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveConstants.js +92 -0
- package/dist/src/coverage/deep-analysis/resolveEnums.d.ts +55 -0
- package/dist/src/coverage/deep-analysis/resolveEnums.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveEnums.js +152 -0
- package/dist/src/coverage/deep-analysis/resolveMethodChains.d.ts +70 -0
- package/dist/src/coverage/deep-analysis/resolveMethodChains.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveMethodChains.js +152 -0
- package/dist/src/coverage/deep-analysis/resolvePaths.d.ts +80 -0
- package/dist/src/coverage/deep-analysis/resolvePaths.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolvePaths.js +216 -0
- package/dist/src/coverage/deep-analysis/resolveRequestWrappers.d.ts +71 -0
- package/dist/src/coverage/deep-analysis/resolveRequestWrappers.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveRequestWrappers.js +226 -0
- package/dist/src/coverage/deep-analysis/symbolTable.d.ts +58 -0
- package/dist/src/coverage/deep-analysis/symbolTable.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/symbolTable.js +230 -0
- package/dist/src/coverage/deep-analysis/types.d.ts +122 -0
- package/dist/src/coverage/deep-analysis/types.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/types.js +21 -0
- package/dist/src/discovery/fileClassifier.d.ts +50 -0
- package/dist/src/discovery/fileClassifier.d.ts.map +1 -0
- package/dist/src/discovery/fileClassifier.js +238 -0
- package/dist/src/discovery/projectDiscovery.d.ts +66 -0
- package/dist/src/discovery/projectDiscovery.d.ts.map +1 -0
- package/dist/src/discovery/projectDiscovery.js +287 -0
- package/dist/src/endpointCoverage.d.ts +70 -0
- package/dist/src/endpointCoverage.d.ts.map +1 -0
- package/dist/src/endpointCoverage.js +381 -0
- package/dist/src/errorCoverage.d.ts +93 -0
- package/dist/src/errorCoverage.d.ts.map +1 -0
- package/dist/src/errorCoverage.js +698 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +1441 -0
- package/dist/src/inference/businessRuleInference.d.ts +63 -0
- package/dist/src/inference/businessRuleInference.d.ts.map +1 -0
- package/dist/src/inference/businessRuleInference.js +268 -0
- package/dist/src/inference/integrationFlowInference.d.ts +56 -0
- package/dist/src/inference/integrationFlowInference.d.ts.map +1 -0
- package/dist/src/inference/integrationFlowInference.js +266 -0
- package/dist/src/integrationCoverage.d.ts +72 -0
- package/dist/src/integrationCoverage.d.ts.map +1 -0
- package/dist/src/integrationCoverage.js +317 -0
- package/dist/src/intelligence/index.d.ts +20 -0
- package/dist/src/intelligence/index.d.ts.map +1 -0
- package/dist/src/intelligence/index.js +105 -0
- package/dist/src/intelligence/linkageEngine.d.ts +20 -0
- package/dist/src/intelligence/linkageEngine.d.ts.map +1 -0
- package/dist/src/intelligence/linkageEngine.js +522 -0
- package/dist/src/intelligence/markdownReporter.d.ts +12 -0
- package/dist/src/intelligence/markdownReporter.d.ts.map +1 -0
- package/dist/src/intelligence/markdownReporter.js +265 -0
- package/dist/src/intelligence/riskScoring.d.ts +53 -0
- package/dist/src/intelligence/riskScoring.d.ts.map +1 -0
- package/dist/src/intelligence/riskScoring.js +181 -0
- package/dist/src/intelligence/types.d.ts +121 -0
- package/dist/src/intelligence/types.d.ts.map +1 -0
- package/dist/src/intelligence/types.js +8 -0
- package/dist/src/languageDetection.d.ts +100 -0
- package/dist/src/languageDetection.d.ts.map +1 -0
- package/dist/src/languageDetection.js +349 -0
- package/dist/src/languages/java/index.d.ts +16 -0
- package/dist/src/languages/java/index.d.ts.map +1 -0
- package/dist/src/languages/java/index.js +103 -0
- package/dist/src/languages/java/parser.d.ts +7 -0
- package/dist/src/languages/java/parser.d.ts.map +1 -0
- package/dist/src/languages/java/parser.js +50 -0
- package/dist/src/languages/java/semanticBuilder.d.ts +21 -0
- package/dist/src/languages/java/semanticBuilder.d.ts.map +1 -0
- package/dist/src/languages/java/semanticBuilder.js +358 -0
- package/dist/src/languages/javascript/annotationExtractor.d.ts +20 -0
- package/dist/src/languages/javascript/annotationExtractor.d.ts.map +1 -0
- package/dist/src/languages/javascript/annotationExtractor.js +94 -0
- package/dist/src/languages/javascript/assertionResolver.d.ts +18 -0
- package/dist/src/languages/javascript/assertionResolver.d.ts.map +1 -0
- package/dist/src/languages/javascript/assertionResolver.js +150 -0
- package/dist/src/languages/javascript/callResolver.d.ts +23 -0
- package/dist/src/languages/javascript/callResolver.d.ts.map +1 -0
- package/dist/src/languages/javascript/callResolver.js +236 -0
- package/dist/src/languages/javascript/httpInteractionExtractor.d.ts +23 -0
- package/dist/src/languages/javascript/httpInteractionExtractor.d.ts.map +1 -0
- package/dist/src/languages/javascript/httpInteractionExtractor.js +205 -0
- package/dist/src/languages/javascript/index.d.ts +20 -0
- package/dist/src/languages/javascript/index.d.ts.map +1 -0
- package/dist/src/languages/javascript/index.js +136 -0
- package/dist/src/languages/javascript/parser.d.ts +14 -0
- package/dist/src/languages/javascript/parser.d.ts.map +1 -0
- package/dist/src/languages/javascript/parser.js +38 -0
- package/dist/src/languages/javascript/symbolResolver.d.ts +31 -0
- package/dist/src/languages/javascript/symbolResolver.d.ts.map +1 -0
- package/dist/src/languages/javascript/symbolResolver.js +183 -0
- package/dist/src/languages/kotlin/index.d.ts +16 -0
- package/dist/src/languages/kotlin/index.d.ts.map +1 -0
- package/dist/src/languages/kotlin/index.js +151 -0
- package/dist/src/languages/kotlin/parser.d.ts +11 -0
- package/dist/src/languages/kotlin/parser.d.ts.map +1 -0
- package/dist/src/languages/kotlin/parser.js +74 -0
- package/dist/src/languages/python/index.d.ts +15 -0
- package/dist/src/languages/python/index.d.ts.map +1 -0
- package/dist/src/languages/python/index.js +293 -0
- package/dist/src/languages/ruby/index.d.ts +15 -0
- package/dist/src/languages/ruby/index.d.ts.map +1 -0
- package/dist/src/languages/ruby/index.js +274 -0
- package/dist/src/languages/shared/treeSitterUtils.d.ts +43 -0
- package/dist/src/languages/shared/treeSitterUtils.d.ts.map +1 -0
- package/dist/src/languages/shared/treeSitterUtils.js +100 -0
- package/dist/src/languages/typescript/index.d.ts +14 -0
- package/dist/src/languages/typescript/index.d.ts.map +1 -0
- package/dist/src/languages/typescript/index.js +25 -0
- package/dist/src/lib/index.d.ts +228 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js +486 -0
- package/dist/src/mcp/client/index.d.ts +37 -0
- package/dist/src/mcp/client/index.d.ts.map +1 -0
- package/dist/src/mcp/client/index.js +235 -0
- package/dist/src/mcp/config.d.ts +50 -0
- package/dist/src/mcp/config.d.ts.map +1 -0
- package/dist/src/mcp/config.js +125 -0
- package/dist/src/mcp/events.d.ts +24 -0
- package/dist/src/mcp/events.d.ts.map +1 -0
- package/dist/src/mcp/events.js +48 -0
- package/dist/src/mcp/fallback/index.d.ts +50 -0
- package/dist/src/mcp/fallback/index.d.ts.map +1 -0
- package/dist/src/mcp/fallback/index.js +216 -0
- package/dist/src/mcp/index.d.ts +67 -0
- package/dist/src/mcp/index.d.ts.map +1 -0
- package/dist/src/mcp/index.js +212 -0
- package/dist/src/mcp/normalizer.d.ts +21 -0
- package/dist/src/mcp/normalizer.d.ts.map +1 -0
- package/dist/src/mcp/normalizer.js +99 -0
- package/dist/src/mcp/prompts/index.d.ts +86 -0
- package/dist/src/mcp/prompts/index.d.ts.map +1 -0
- package/dist/src/mcp/prompts/index.js +304 -0
- package/dist/src/mcp/templates/index.d.ts +35 -0
- package/dist/src/mcp/templates/index.d.ts.map +1 -0
- package/dist/src/mcp/templates/index.js +143 -0
- package/dist/src/mcp/testing/mock-server/index.d.ts +47 -0
- package/dist/src/mcp/testing/mock-server/index.d.ts.map +1 -0
- package/dist/src/mcp/testing/mock-server/index.js +157 -0
- package/dist/src/mcp/types.d.ts +127 -0
- package/dist/src/mcp/types.d.ts.map +1 -0
- package/dist/src/mcp/types.js +8 -0
- package/dist/src/observability.d.ts +138 -0
- package/dist/src/observability.d.ts.map +1 -0
- package/dist/src/observability.js +519 -0
- package/dist/src/parameterCoverage.d.ts +75 -0
- package/dist/src/parameterCoverage.d.ts.map +1 -0
- package/dist/src/parameterCoverage.js +629 -0
- package/dist/src/perfResilienceCoverage.d.ts +155 -0
- package/dist/src/perfResilienceCoverage.d.ts.map +1 -0
- package/dist/src/perfResilienceCoverage.js +670 -0
- package/dist/src/pluginLoader.d.ts +51 -0
- package/dist/src/pluginLoader.d.ts.map +1 -0
- package/dist/src/pluginLoader.js +72 -0
- package/dist/src/publishing.d.ts +63 -0
- package/dist/src/publishing.d.ts.map +1 -0
- package/dist/src/publishing.js +379 -0
- package/dist/src/qualityGate.d.ts +58 -0
- package/dist/src/qualityGate.d.ts.map +1 -0
- package/dist/src/qualityGate.js +118 -0
- package/dist/src/reporting.d.ts +41 -0
- package/dist/src/reporting.d.ts.map +1 -0
- package/dist/src/reporting.js +278 -0
- package/dist/src/screenshots.d.ts +71 -0
- package/dist/src/screenshots.d.ts.map +1 -0
- package/dist/src/screenshots.js +141 -0
- package/dist/src/security/gate/index.d.ts +11 -0
- package/dist/src/security/gate/index.d.ts.map +1 -0
- package/dist/src/security/gate/index.js +65 -0
- package/dist/src/security/index.d.ts +30 -0
- package/dist/src/security/index.d.ts.map +1 -0
- package/dist/src/security/index.js +342 -0
- package/dist/src/security/normalizers/semgrep.d.ts +10 -0
- package/dist/src/security/normalizers/semgrep.d.ts.map +1 -0
- package/dist/src/security/normalizers/semgrep.js +104 -0
- package/dist/src/security/normalizers/trivy.d.ts +10 -0
- package/dist/src/security/normalizers/trivy.d.ts.map +1 -0
- package/dist/src/security/normalizers/trivy.js +78 -0
- package/dist/src/security/normalizers/zap.d.ts +10 -0
- package/dist/src/security/normalizers/zap.d.ts.map +1 -0
- package/dist/src/security/normalizers/zap.js +104 -0
- package/dist/src/security/scanners/semgrep.d.ts +6 -0
- package/dist/src/security/scanners/semgrep.d.ts.map +1 -0
- package/dist/src/security/scanners/semgrep.js +125 -0
- package/dist/src/security/scanners/trivy.d.ts +6 -0
- package/dist/src/security/scanners/trivy.d.ts.map +1 -0
- package/dist/src/security/scanners/trivy.js +115 -0
- package/dist/src/security/scanners/zap.d.ts +6 -0
- package/dist/src/security/scanners/zap.d.ts.map +1 -0
- package/dist/src/security/scanners/zap.js +135 -0
- package/dist/src/security/types.d.ts +146 -0
- package/dist/src/security/types.d.ts.map +1 -0
- package/dist/src/security/types.js +6 -0
- package/dist/src/securityCoverage.d.ts +116 -0
- package/dist/src/securityCoverage.d.ts.map +1 -0
- package/dist/src/securityCoverage.js +725 -0
- package/dist/src/summary/buildSummary.d.ts +28 -0
- package/dist/src/summary/buildSummary.d.ts.map +1 -0
- package/dist/src/summary/buildSummary.js +257 -0
- package/dist/src/summary/evaluateMetrics.d.ts +31 -0
- package/dist/src/summary/evaluateMetrics.d.ts.map +1 -0
- package/dist/src/summary/evaluateMetrics.js +118 -0
- package/dist/src/summary/index.d.ts +10 -0
- package/dist/src/summary/index.d.ts.map +1 -0
- package/dist/src/summary/index.js +22 -0
- package/dist/src/summary/markdownRenderer.d.ts +139 -0
- package/dist/src/summary/markdownRenderer.d.ts.map +1 -0
- package/dist/src/summary/markdownRenderer.js +459 -0
- package/dist/src/summary/prSummary.d.ts +24 -0
- package/dist/src/summary/prSummary.d.ts.map +1 -0
- package/dist/src/summary/prSummary.js +233 -0
- package/dist/src/summary/summaryTypes.d.ts +35 -0
- package/dist/src/summary/summaryTypes.d.ts.map +1 -0
- package/dist/src/summary/summaryTypes.js +27 -0
- package/package.json +84 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchesBranchPattern = matchesBranchPattern;
|
|
4
|
+
exports.resolveThresholdsForBranch = resolveThresholdsForBranch;
|
|
5
|
+
exports.effectiveThresholdForCategory = effectiveThresholdForCategory;
|
|
6
|
+
exports.evaluateQualityGate = evaluateQualityGate;
|
|
7
|
+
// ─── Branch matching ──────────────────────────────────────────────────────────
|
|
8
|
+
/**
|
|
9
|
+
* Determine whether a branch name matches a glob-style pattern.
|
|
10
|
+
* Supports `*` (single segment) and `**` (any segments).
|
|
11
|
+
*/
|
|
12
|
+
function matchesBranchPattern(branchName, pattern) {
|
|
13
|
+
const regexStr = pattern
|
|
14
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
15
|
+
.replace(/\*\*/g, '.+')
|
|
16
|
+
.replace(/\*/g, '[^/]+');
|
|
17
|
+
return new RegExp(`^${regexStr}$`).test(branchName);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the effective threshold map for the given branch, merging
|
|
21
|
+
* branch-aware overrides on top of the global threshold config.
|
|
22
|
+
*/
|
|
23
|
+
function resolveThresholdsForBranch(config, branchName) {
|
|
24
|
+
const baseThresholds = {};
|
|
25
|
+
// Start from the global thresholds config
|
|
26
|
+
if (config.thresholds) {
|
|
27
|
+
for (const [key, value] of Object.entries(config.thresholds)) {
|
|
28
|
+
if (value !== undefined) {
|
|
29
|
+
baseThresholds[key] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Apply branch-aware overrides if a branch is specified
|
|
34
|
+
if (branchName && config.thresholdsByBranch) {
|
|
35
|
+
for (const [pattern, overrides] of Object.entries(config.thresholdsByBranch)) {
|
|
36
|
+
if (matchesBranchPattern(branchName, pattern)) {
|
|
37
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
38
|
+
if (value !== undefined) {
|
|
39
|
+
baseThresholds[key] = value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
break; // first matching pattern wins
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return baseThresholds;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Compute the effective per-category threshold for a given category,
|
|
50
|
+
* using the 'global' key as the default when no category-specific value exists.
|
|
51
|
+
*/
|
|
52
|
+
function effectiveThresholdForCategory(thresholds, category) {
|
|
53
|
+
if (thresholds[category] !== undefined)
|
|
54
|
+
return thresholds[category];
|
|
55
|
+
if (thresholds['global'] !== undefined)
|
|
56
|
+
return thresholds['global'];
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
// ─── Core evaluation ──────────────────────────────────────────────────────────
|
|
60
|
+
/**
|
|
61
|
+
* Evaluate all coverage results against configured thresholds.
|
|
62
|
+
*
|
|
63
|
+
* Default behaviour when no thresholds are configured: every category must
|
|
64
|
+
* reach 100% (spec requirement: "default threshold = 100%").
|
|
65
|
+
*
|
|
66
|
+
* @param results Coverage results from one or more analysis passes.
|
|
67
|
+
* @param config Quality-gate / threshold configuration.
|
|
68
|
+
* @param branchName Current git branch name (used for branch-aware thresholds).
|
|
69
|
+
*/
|
|
70
|
+
function evaluateQualityGate(results, config, branchName) {
|
|
71
|
+
var _a, _b, _c;
|
|
72
|
+
const gateEnabled = ((_a = config.qualityGate) === null || _a === void 0 ? void 0 : _a.enabled) !== false; // default enabled
|
|
73
|
+
const mode = (_c = (_b = config.qualityGate) === null || _b === void 0 ? void 0 : _b.mode) !== null && _c !== void 0 ? _c : 'strict';
|
|
74
|
+
const resolvedThresholds = resolveThresholdsForBranch(config, branchName);
|
|
75
|
+
const actual = {};
|
|
76
|
+
for (const r of results) {
|
|
77
|
+
actual[r.type] = r.coveragePercent;
|
|
78
|
+
}
|
|
79
|
+
const failures = [];
|
|
80
|
+
if (gateEnabled) {
|
|
81
|
+
for (const result of results) {
|
|
82
|
+
const category = result.type;
|
|
83
|
+
let threshold;
|
|
84
|
+
if (Object.keys(resolvedThresholds).length > 0) {
|
|
85
|
+
const t = effectiveThresholdForCategory(resolvedThresholds, category);
|
|
86
|
+
if (t === undefined)
|
|
87
|
+
continue; // no threshold for this category
|
|
88
|
+
threshold = t;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// No threshold config at all → default to 100%
|
|
92
|
+
threshold = 100;
|
|
93
|
+
}
|
|
94
|
+
if (result.coveragePercent < threshold) {
|
|
95
|
+
failures.push({
|
|
96
|
+
category,
|
|
97
|
+
expected: threshold,
|
|
98
|
+
actual: result.coveragePercent,
|
|
99
|
+
gap: Number((threshold - result.coveragePercent).toFixed(2)),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// In "warn" mode we still compute failures but mark as passed
|
|
105
|
+
const passed = mode === 'warn' ? true : failures.length === 0;
|
|
106
|
+
// Determine the threshold representation for the result
|
|
107
|
+
const thresholdForResult = Object.keys(resolvedThresholds).length === 1 && resolvedThresholds['global'] !== undefined
|
|
108
|
+
? resolvedThresholds['global']
|
|
109
|
+
: Object.keys(resolvedThresholds).length > 0
|
|
110
|
+
? resolvedThresholds
|
|
111
|
+
: 100;
|
|
112
|
+
return {
|
|
113
|
+
passed,
|
|
114
|
+
threshold: thresholdForResult,
|
|
115
|
+
actual,
|
|
116
|
+
failures,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ObservabilityInfo } from './observability';
|
|
2
|
+
/**
|
|
3
|
+
* Standardized coverage result object produced by each coverage command.
|
|
4
|
+
* All analysis commands return one of these so the reporting module can
|
|
5
|
+
* handle them uniformly.
|
|
6
|
+
*/
|
|
7
|
+
export interface CoverageResult {
|
|
8
|
+
/** Identifies which analysis produced this result (e.g. "endpoint", "parameter", "business", "integration") */
|
|
9
|
+
type: string;
|
|
10
|
+
/** Total number of items analysed (endpoints, parameters, rules, flows…) */
|
|
11
|
+
totalItems: number;
|
|
12
|
+
/** Number of items that are considered covered */
|
|
13
|
+
coveredItems: number;
|
|
14
|
+
/** Coverage percentage (0‑100, two decimal places) */
|
|
15
|
+
coveragePercent: number;
|
|
16
|
+
/** Arbitrary per-type detail data serialisable to JSON */
|
|
17
|
+
details: unknown;
|
|
18
|
+
}
|
|
19
|
+
export type ReportFormat = 'json' | 'html' | 'csv' | 'junit';
|
|
20
|
+
/**
|
|
21
|
+
* Parse a comma-separated format string (e.g. "json,html") into an array of
|
|
22
|
+
* validated `ReportFormat` values. Unknown tokens are silently ignored.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseFormats(raw: string): ReportFormat[];
|
|
25
|
+
/**
|
|
26
|
+
* Write coverage reports in the requested formats to `reportsDir`.
|
|
27
|
+
* The directory is created if it does not exist.
|
|
28
|
+
*
|
|
29
|
+
* @param results Array of standardised coverage results
|
|
30
|
+
* @param formats Which output formats to generate
|
|
31
|
+
* @param reportsDir Absolute path to the output directory
|
|
32
|
+
* @param thresholds Optional per-type threshold percentages (used in HTML and JUnit outputs)
|
|
33
|
+
* @param observability Optional observability metadata embedded in JSON and HTML outputs
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateMultiFormatReports(results: CoverageResult[], formats: ReportFormat[], reportsDir: string, thresholds?: Record<string, number>, observability?: ObservabilityInfo): void;
|
|
36
|
+
/**
|
|
37
|
+
* Check whether any coverage result falls below its threshold.
|
|
38
|
+
* Returns an array of failure descriptions (empty means all pass).
|
|
39
|
+
*/
|
|
40
|
+
export declare function checkThresholds(results: CoverageResult[], thresholds: Record<string, number>): string[];
|
|
41
|
+
//# sourceMappingURL=reporting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporting.d.ts","sourceRoot":"","sources":["../../src/reporting.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAIzD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,+GAA+G;IAC/G,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,eAAe,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAC;CAClB;AAID,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAE7D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAMxD;AAwND;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,cAAc,EAAE,EACzB,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACvC,aAAa,CAAC,EAAE,iBAAiB,GAChC,IAAI,CAqBN;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,cAAc,EAAE,EACzB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC,MAAM,EAAE,CAYV"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseFormats = parseFormats;
|
|
37
|
+
exports.generateMultiFormatReports = generateMultiFormatReports;
|
|
38
|
+
exports.checkThresholds = checkThresholds;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
/**
|
|
42
|
+
* Parse a comma-separated format string (e.g. "json,html") into an array of
|
|
43
|
+
* validated `ReportFormat` values. Unknown tokens are silently ignored.
|
|
44
|
+
*/
|
|
45
|
+
function parseFormats(raw) {
|
|
46
|
+
const valid = ['json', 'html', 'csv', 'junit'];
|
|
47
|
+
return raw
|
|
48
|
+
.split(',')
|
|
49
|
+
.map((f) => f.trim().toLowerCase())
|
|
50
|
+
.filter((f) => valid.includes(f));
|
|
51
|
+
}
|
|
52
|
+
// ─── Report writers ───────────────────────────────────────────────────────────
|
|
53
|
+
/** Write `reports/coverage-summary.json`, merging with any existing file so that
|
|
54
|
+
* successive per-type runs accumulate a complete summary across all 8 metrics. */
|
|
55
|
+
function writeJson(results, reportsDir, observability) {
|
|
56
|
+
var _a;
|
|
57
|
+
const outPath = path.join(reportsDir, 'coverage-summary.json');
|
|
58
|
+
// Load existing summary/details so that successive single-type runs accumulate
|
|
59
|
+
// all coverage types into one file rather than overwriting each other.
|
|
60
|
+
let existingSummary = [];
|
|
61
|
+
let existingDetails = {};
|
|
62
|
+
try {
|
|
63
|
+
if (fs.existsSync(outPath)) {
|
|
64
|
+
const existing = JSON.parse(fs.readFileSync(outPath, 'utf-8'));
|
|
65
|
+
if (Array.isArray(existing.summary)) {
|
|
66
|
+
existingSummary = existing.summary;
|
|
67
|
+
}
|
|
68
|
+
if (existing.details && typeof existing.details === 'object') {
|
|
69
|
+
existingDetails = existing.details;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Corrupt file — start fresh
|
|
75
|
+
}
|
|
76
|
+
// New results replace any existing entry for the same type; others are kept.
|
|
77
|
+
const newTypes = new Set(results.map((r) => r.type));
|
|
78
|
+
const mergedSummary = [
|
|
79
|
+
...existingSummary.filter((s) => !newTypes.has(s.type)),
|
|
80
|
+
...results.map((r) => ({
|
|
81
|
+
type: r.type,
|
|
82
|
+
totalItems: r.totalItems,
|
|
83
|
+
coveredItems: r.coveredItems,
|
|
84
|
+
coveragePercent: r.coveragePercent,
|
|
85
|
+
})),
|
|
86
|
+
];
|
|
87
|
+
const mergedDetails = {
|
|
88
|
+
...existingDetails,
|
|
89
|
+
...results.reduce((acc, r) => {
|
|
90
|
+
acc[r.type] = r.details;
|
|
91
|
+
return acc;
|
|
92
|
+
}, {}),
|
|
93
|
+
};
|
|
94
|
+
const payload = {
|
|
95
|
+
generatedAt: new Date().toISOString(),
|
|
96
|
+
summary: mergedSummary,
|
|
97
|
+
details: mergedDetails,
|
|
98
|
+
};
|
|
99
|
+
if (observability) {
|
|
100
|
+
payload.observability = {
|
|
101
|
+
metricsUrl: observability.metricsUrl,
|
|
102
|
+
note: observability.metricsUrl
|
|
103
|
+
? `Prometheus metrics available at ${observability.metricsUrl}`
|
|
104
|
+
: 'Prometheus metrics export not enabled (use --metrics-port to enable)',
|
|
105
|
+
metricNames: observability.metricNames,
|
|
106
|
+
tracing: {
|
|
107
|
+
enabled: observability.tracingEnabled,
|
|
108
|
+
otlpEndpoint: observability.otlpEndpoint,
|
|
109
|
+
note: observability.tracingEnabled
|
|
110
|
+
? `Traces are exported to ${(_a = observability.otlpEndpoint) !== null && _a !== void 0 ? _a : 'in-memory (no OTLP endpoint configured)'}`
|
|
111
|
+
: 'Tracing not enabled (use --trace to enable)',
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
fs.writeFileSync(outPath, JSON.stringify(payload, null, 2), 'utf-8');
|
|
116
|
+
}
|
|
117
|
+
/** Write `reports/coverage-summary.html` */
|
|
118
|
+
function writeHtml(results, reportsDir, thresholds = {}, observability) {
|
|
119
|
+
const rows = results
|
|
120
|
+
.map((r) => {
|
|
121
|
+
var _a;
|
|
122
|
+
const threshold = (_a = thresholds[r.type]) !== null && _a !== void 0 ? _a : 0;
|
|
123
|
+
const belowThreshold = r.coveragePercent < threshold;
|
|
124
|
+
const rowClass = belowThreshold ? 'below' : r.coveragePercent >= 80 ? 'good' : 'warn';
|
|
125
|
+
const thresholdCell = threshold > 0
|
|
126
|
+
? `<td class="${belowThreshold ? 'fail' : 'pass'}">${threshold}% ${belowThreshold ? '❌' : '✅'}</td>`
|
|
127
|
+
: '<td>—</td>';
|
|
128
|
+
return ` <tr class="${rowClass}">
|
|
129
|
+
<td>${r.type}</td>
|
|
130
|
+
<td>${r.coveredItems}/${r.totalItems}</td>
|
|
131
|
+
<td>${r.coveragePercent}%</td>
|
|
132
|
+
${thresholdCell}
|
|
133
|
+
</tr>`;
|
|
134
|
+
})
|
|
135
|
+
.join('\n');
|
|
136
|
+
const observabilitySection = observability
|
|
137
|
+
? `
|
|
138
|
+
<hr>
|
|
139
|
+
<h2>Observability</h2>
|
|
140
|
+
${observability.metricsUrl
|
|
141
|
+
? `<p>📊 Prometheus metrics available at <a href="${observability.metricsUrl}">${observability.metricsUrl}</a></p>
|
|
142
|
+
<p>Metric names (replace <code><type></code> with the coverage type, e.g. <code>endpoint</code>):</p>
|
|
143
|
+
<ul>
|
|
144
|
+
<li><code>api_coverage_total{coverage_type="<type>"}</code> — total items</li>
|
|
145
|
+
<li><code>api_coverage_covered{coverage_type="<type>"}</code> — covered items</li>
|
|
146
|
+
<li><code>api_coverage_ratio{coverage_type="<type>"}</code> — coverage ratio (0–1)</li>
|
|
147
|
+
<li><code>api_coverage_threshold_failure{coverage_type="<type>"}</code> — 1 if below threshold</li>
|
|
148
|
+
</ul>`
|
|
149
|
+
: '<p>Prometheus metrics export not enabled. Use <code>--metrics-port <port></code> to enable.</p>'}
|
|
150
|
+
${observability.tracingEnabled
|
|
151
|
+
? `<p>🔭 OpenTelemetry tracing enabled${observability.otlpEndpoint ? ` → <code>${observability.otlpEndpoint}</code>` : ' (in-memory only)'}. View traces in Jaeger or Grafana Tempo.</p>`
|
|
152
|
+
: '<p>OpenTelemetry tracing not enabled. Use <code>--trace</code> to enable.</p>'}`
|
|
153
|
+
: '';
|
|
154
|
+
const html = `<!DOCTYPE html>
|
|
155
|
+
<html lang="en">
|
|
156
|
+
<head>
|
|
157
|
+
<meta charset="UTF-8">
|
|
158
|
+
<title>Coverage Summary Report</title>
|
|
159
|
+
<style>
|
|
160
|
+
body { font-family: sans-serif; padding: 2rem; background: #fafafa; }
|
|
161
|
+
h1 { margin-bottom: 0.25rem; }
|
|
162
|
+
.meta { color: #666; font-size: 0.9rem; margin-bottom: 1.5rem; }
|
|
163
|
+
table { border-collapse: collapse; width: 100%; max-width: 800px; }
|
|
164
|
+
th, td { border: 1px solid #ccc; padding: 0.5rem 1rem; text-align: left; }
|
|
165
|
+
th { background: #f0f0f0; }
|
|
166
|
+
tr.good { background: #e6ffe6; }
|
|
167
|
+
tr.warn { background: #fff9e6; }
|
|
168
|
+
tr.below { background: #ffe6e6; }
|
|
169
|
+
td.pass { color: #2a7a2a; font-weight: bold; }
|
|
170
|
+
td.fail { color: #aa2222; font-weight: bold; }
|
|
171
|
+
</style>
|
|
172
|
+
</head>
|
|
173
|
+
<body>
|
|
174
|
+
<h1>Coverage Summary Report</h1>
|
|
175
|
+
<p class="meta">Generated: ${new Date().toISOString()}</p>
|
|
176
|
+
<table>
|
|
177
|
+
<thead>
|
|
178
|
+
<tr>
|
|
179
|
+
<th>Coverage Type</th>
|
|
180
|
+
<th>Covered / Total</th>
|
|
181
|
+
<th>Coverage %</th>
|
|
182
|
+
<th>Threshold</th>
|
|
183
|
+
</tr>
|
|
184
|
+
</thead>
|
|
185
|
+
<tbody>
|
|
186
|
+
${rows}
|
|
187
|
+
</tbody>
|
|
188
|
+
</table>${observabilitySection}
|
|
189
|
+
</body>
|
|
190
|
+
</html>`;
|
|
191
|
+
const outPath = path.join(reportsDir, 'coverage-summary.html');
|
|
192
|
+
fs.writeFileSync(outPath, html, 'utf-8');
|
|
193
|
+
}
|
|
194
|
+
/** Write `reports/coverage-summary.csv` */
|
|
195
|
+
function writeCsv(results, reportsDir) {
|
|
196
|
+
const lines = ['type,total,covered,percent'];
|
|
197
|
+
for (const r of results) {
|
|
198
|
+
lines.push(`${r.type},${r.totalItems},${r.coveredItems},${r.coveragePercent}`);
|
|
199
|
+
}
|
|
200
|
+
const outPath = path.join(reportsDir, 'coverage-summary.csv');
|
|
201
|
+
fs.writeFileSync(outPath, lines.join('\n') + '\n', 'utf-8');
|
|
202
|
+
}
|
|
203
|
+
/** Write `reports/coverage-summary-junit.xml` (JUnit-compatible XML) */
|
|
204
|
+
function writeJunit(results, reportsDir, thresholds = {}) {
|
|
205
|
+
const testCases = results
|
|
206
|
+
.map((r) => {
|
|
207
|
+
var _a;
|
|
208
|
+
const threshold = (_a = thresholds[r.type]) !== null && _a !== void 0 ? _a : 0;
|
|
209
|
+
const passed = r.coveragePercent >= threshold;
|
|
210
|
+
const name = `${r.type} coverage`;
|
|
211
|
+
if (passed) {
|
|
212
|
+
return ` <testcase name="${name}" classname="CoverageThreshold" time="0"/>`;
|
|
213
|
+
}
|
|
214
|
+
const diff = (threshold - r.coveragePercent).toFixed(2);
|
|
215
|
+
return ` <testcase name="${name}" classname="CoverageThreshold" time="0">
|
|
216
|
+
<failure message="${r.type} coverage ${r.coveragePercent}% is below threshold ${threshold}% (gap: ${diff}%)">
|
|
217
|
+
Coverage: ${r.coveragePercent}% / Threshold: ${threshold}% / Gap: ${diff}%
|
|
218
|
+
</failure>
|
|
219
|
+
</testcase>`;
|
|
220
|
+
})
|
|
221
|
+
.join('\n');
|
|
222
|
+
const failures = results.filter((r) => { var _a; return r.coveragePercent < ((_a = thresholds[r.type]) !== null && _a !== void 0 ? _a : 0); }).length;
|
|
223
|
+
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
224
|
+
<testsuites name="API Coverage" tests="${results.length}" failures="${failures}" time="0">
|
|
225
|
+
<testsuite name="Coverage Thresholds" tests="${results.length}" failures="${failures}" time="0">
|
|
226
|
+
${testCases}
|
|
227
|
+
</testsuite>
|
|
228
|
+
</testsuites>`;
|
|
229
|
+
const outPath = path.join(reportsDir, 'coverage-summary-junit.xml');
|
|
230
|
+
fs.writeFileSync(outPath, xml, 'utf-8');
|
|
231
|
+
}
|
|
232
|
+
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
233
|
+
/**
|
|
234
|
+
* Write coverage reports in the requested formats to `reportsDir`.
|
|
235
|
+
* The directory is created if it does not exist.
|
|
236
|
+
*
|
|
237
|
+
* @param results Array of standardised coverage results
|
|
238
|
+
* @param formats Which output formats to generate
|
|
239
|
+
* @param reportsDir Absolute path to the output directory
|
|
240
|
+
* @param thresholds Optional per-type threshold percentages (used in HTML and JUnit outputs)
|
|
241
|
+
* @param observability Optional observability metadata embedded in JSON and HTML outputs
|
|
242
|
+
*/
|
|
243
|
+
function generateMultiFormatReports(results, formats, reportsDir, thresholds = {}, observability) {
|
|
244
|
+
if (!fs.existsSync(reportsDir)) {
|
|
245
|
+
fs.mkdirSync(reportsDir, { recursive: true });
|
|
246
|
+
}
|
|
247
|
+
for (const fmt of formats) {
|
|
248
|
+
switch (fmt) {
|
|
249
|
+
case 'json':
|
|
250
|
+
writeJson(results, reportsDir, observability);
|
|
251
|
+
break;
|
|
252
|
+
case 'html':
|
|
253
|
+
writeHtml(results, reportsDir, thresholds, observability);
|
|
254
|
+
break;
|
|
255
|
+
case 'csv':
|
|
256
|
+
writeCsv(results, reportsDir);
|
|
257
|
+
break;
|
|
258
|
+
case 'junit':
|
|
259
|
+
writeJunit(results, reportsDir, thresholds);
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Check whether any coverage result falls below its threshold.
|
|
266
|
+
* Returns an array of failure descriptions (empty means all pass).
|
|
267
|
+
*/
|
|
268
|
+
function checkThresholds(results, thresholds) {
|
|
269
|
+
const failures = [];
|
|
270
|
+
for (const result of results) {
|
|
271
|
+
const threshold = thresholds[result.type];
|
|
272
|
+
if (threshold !== undefined && result.coveragePercent < threshold) {
|
|
273
|
+
const gap = (threshold - result.coveragePercent).toFixed(2);
|
|
274
|
+
failures.push(`${result.type} coverage ${result.coveragePercent}% is below threshold ${threshold}% (gap: ${gap}%)`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return failures;
|
|
278
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export interface ScreenshotResult {
|
|
2
|
+
page: string;
|
|
3
|
+
filePath: string | null;
|
|
4
|
+
captured: boolean;
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ScreenshotCaptureConfig {
|
|
8
|
+
/** Whether screenshot capture is enabled */
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
/** If true, missing screenshots cause a build failure */
|
|
11
|
+
strict?: boolean;
|
|
12
|
+
/** Dashboard URL to screenshot (defaults to http://localhost:4173) */
|
|
13
|
+
dashboardUrl?: string;
|
|
14
|
+
/** Output directory for screenshots */
|
|
15
|
+
outputDir?: string;
|
|
16
|
+
/** Build ID to include in path */
|
|
17
|
+
buildId?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Dashboard pages to capture */
|
|
20
|
+
export declare const SCREENSHOT_PAGES: readonly [{
|
|
21
|
+
readonly name: "overview";
|
|
22
|
+
readonly path: "/";
|
|
23
|
+
}, {
|
|
24
|
+
readonly name: "endpoint-coverage";
|
|
25
|
+
readonly path: "/endpoint";
|
|
26
|
+
}, {
|
|
27
|
+
readonly name: "parameter-coverage";
|
|
28
|
+
readonly path: "/parameter";
|
|
29
|
+
}, {
|
|
30
|
+
readonly name: "business-coverage";
|
|
31
|
+
readonly path: "/business";
|
|
32
|
+
}, {
|
|
33
|
+
readonly name: "integration-flows";
|
|
34
|
+
readonly path: "/integration";
|
|
35
|
+
}, {
|
|
36
|
+
readonly name: "security-coverage";
|
|
37
|
+
readonly path: "/security";
|
|
38
|
+
}, {
|
|
39
|
+
readonly name: "error-coverage";
|
|
40
|
+
readonly path: "/error";
|
|
41
|
+
}, {
|
|
42
|
+
readonly name: "performance-resilience";
|
|
43
|
+
readonly path: "/performance";
|
|
44
|
+
}, {
|
|
45
|
+
readonly name: "compatibility";
|
|
46
|
+
readonly path: "/compatibility";
|
|
47
|
+
}, {
|
|
48
|
+
readonly name: "ai-analysis";
|
|
49
|
+
readonly path: "/ai";
|
|
50
|
+
}];
|
|
51
|
+
/**
|
|
52
|
+
* Attempt to capture screenshots of the dashboard using Playwright or Puppeteer.
|
|
53
|
+
*
|
|
54
|
+
* This function **never throws** when `strict` is false (the default).
|
|
55
|
+
* When headless screenshot tooling is unavailable, it returns results with
|
|
56
|
+
* `captured: false` for each page and includes an informative error message.
|
|
57
|
+
*
|
|
58
|
+
* When `strict: true`, a thrown error will propagate if any screenshot fails.
|
|
59
|
+
*/
|
|
60
|
+
export declare function captureScreenshots(config: ScreenshotCaptureConfig): Promise<ScreenshotResult[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Return the relative paths of successfully captured screenshots,
|
|
63
|
+
* suitable for embedding into HTML.
|
|
64
|
+
*/
|
|
65
|
+
export declare function getScreenshotRelativePaths(results: ScreenshotResult[], siteDir: string, basePath?: string): string[];
|
|
66
|
+
/**
|
|
67
|
+
* Check whether screenshots are available.
|
|
68
|
+
* Returns `true` when at least one screenshot was captured.
|
|
69
|
+
*/
|
|
70
|
+
export declare function hasScreenshots(results: ScreenshotResult[]): boolean;
|
|
71
|
+
//# sourceMappingURL=screenshots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshots.d.ts","sourceRoot":"","sources":["../../src/screenshots.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,4CAA4C;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yDAAyD;IACzD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,iCAAiC;AACjC,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWnB,CAAC;AAIX;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAqC7B;AAgDD;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,gBAAgB,EAAE,EAC3B,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAY,GACrB,MAAM,EAAE,CAOV;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAEnE"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.SCREENSHOT_PAGES = void 0;
|
|
37
|
+
exports.captureScreenshots = captureScreenshots;
|
|
38
|
+
exports.getScreenshotRelativePaths = getScreenshotRelativePaths;
|
|
39
|
+
exports.hasScreenshots = hasScreenshots;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
/** Dashboard pages to capture */
|
|
43
|
+
exports.SCREENSHOT_PAGES = [
|
|
44
|
+
{ name: 'overview', path: '/' },
|
|
45
|
+
{ name: 'endpoint-coverage', path: '/endpoint' },
|
|
46
|
+
{ name: 'parameter-coverage', path: '/parameter' },
|
|
47
|
+
{ name: 'business-coverage', path: '/business' },
|
|
48
|
+
{ name: 'integration-flows', path: '/integration' },
|
|
49
|
+
{ name: 'security-coverage', path: '/security' },
|
|
50
|
+
{ name: 'error-coverage', path: '/error' },
|
|
51
|
+
{ name: 'performance-resilience', path: '/performance' },
|
|
52
|
+
{ name: 'compatibility', path: '/compatibility' },
|
|
53
|
+
{ name: 'ai-analysis', path: '/ai' },
|
|
54
|
+
];
|
|
55
|
+
// ─── Capture ──────────────────────────────────────────────────────────────────
|
|
56
|
+
/**
|
|
57
|
+
* Attempt to capture screenshots of the dashboard using Playwright or Puppeteer.
|
|
58
|
+
*
|
|
59
|
+
* This function **never throws** when `strict` is false (the default).
|
|
60
|
+
* When headless screenshot tooling is unavailable, it returns results with
|
|
61
|
+
* `captured: false` for each page and includes an informative error message.
|
|
62
|
+
*
|
|
63
|
+
* When `strict: true`, a thrown error will propagate if any screenshot fails.
|
|
64
|
+
*/
|
|
65
|
+
async function captureScreenshots(config) {
|
|
66
|
+
var _a, _b, _c;
|
|
67
|
+
if (config.enabled === false) {
|
|
68
|
+
return exports.SCREENSHOT_PAGES.map((p) => ({
|
|
69
|
+
page: p.name,
|
|
70
|
+
filePath: null,
|
|
71
|
+
captured: false,
|
|
72
|
+
error: 'Screenshot capture is disabled',
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
const baseUrl = (_a = config.dashboardUrl) !== null && _a !== void 0 ? _a : 'http://localhost:4173';
|
|
76
|
+
const siteDir = (_b = config.outputDir) !== null && _b !== void 0 ? _b : 'site';
|
|
77
|
+
const buildId = (_c = config.buildId) !== null && _c !== void 0 ? _c : 'latest';
|
|
78
|
+
const screenshotDir = path.join(siteDir, 'assets', 'screenshots', buildId);
|
|
79
|
+
fs.mkdirSync(screenshotDir, { recursive: true });
|
|
80
|
+
const results = [];
|
|
81
|
+
for (const page of exports.SCREENSHOT_PAGES) {
|
|
82
|
+
const filePath = path.join(screenshotDir, `${page.name}.png`);
|
|
83
|
+
try {
|
|
84
|
+
await captureOnePage(baseUrl + page.path, filePath);
|
|
85
|
+
results.push({ page: page.name, filePath, captured: true });
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
89
|
+
if (config.strict) {
|
|
90
|
+
throw new Error(`Failed to capture screenshot for page "${page.name}": ${error}`);
|
|
91
|
+
}
|
|
92
|
+
results.push({ page: page.name, filePath: null, captured: false, error });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return results;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Attempt to capture a single page screenshot.
|
|
99
|
+
* Tries Playwright first, then falls back to a clear "unavailable" error.
|
|
100
|
+
*/
|
|
101
|
+
async function captureOnePage(url, outputPath) {
|
|
102
|
+
// Try Playwright (chromium) — it is an optional peer dependency
|
|
103
|
+
let playwright;
|
|
104
|
+
try {
|
|
105
|
+
// Dynamic require to avoid hard dependency
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
107
|
+
playwright = require('playwright');
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
throw new Error('Playwright is not installed. Install it with: npm install --save-dev playwright');
|
|
111
|
+
}
|
|
112
|
+
const browser = await playwright.chromium.launch({ headless: true });
|
|
113
|
+
try {
|
|
114
|
+
const page = await browser.newPage();
|
|
115
|
+
await page.goto(url, { waitUntil: 'networkidle', timeout: 15000 });
|
|
116
|
+
await page.screenshot({ path: outputPath, fullPage: true });
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
await browser.close();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// ─── Helper ───────────────────────────────────────────────────────────────────
|
|
123
|
+
/**
|
|
124
|
+
* Return the relative paths of successfully captured screenshots,
|
|
125
|
+
* suitable for embedding into HTML.
|
|
126
|
+
*/
|
|
127
|
+
function getScreenshotRelativePaths(results, siteDir, basePath = '/') {
|
|
128
|
+
return results
|
|
129
|
+
.filter((r) => r.captured && r.filePath !== null)
|
|
130
|
+
.map((r) => {
|
|
131
|
+
const rel = path.relative(siteDir, r.filePath).replace(/\\/g, '/');
|
|
132
|
+
return `${basePath}${rel}`;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check whether screenshots are available.
|
|
137
|
+
* Returns `true` when at least one screenshot was captured.
|
|
138
|
+
*/
|
|
139
|
+
function hasScreenshots(results) {
|
|
140
|
+
return results.some((r) => r.captured);
|
|
141
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security gate evaluator.
|
|
3
|
+
* Evaluates normalized findings against configured thresholds and returns a pass/fail result.
|
|
4
|
+
*/
|
|
5
|
+
import { SecurityFinding, SecurityGateConfig, SecurityGateResult } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Evaluate the security gate against normalized findings.
|
|
8
|
+
* Returns a SecurityGateResult with pass/fail status and detailed reasons.
|
|
9
|
+
*/
|
|
10
|
+
export declare function evaluateSecurityGate(findings: SecurityFinding[], config: SecurityGateConfig): SecurityGateResult;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/security/gate/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,UAAU,CAAC;AAWlB;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,eAAe,EAAE,EAC3B,MAAM,EAAE,kBAAkB,GACzB,kBAAkB,CA+EpB"}
|