@facet-coverage/core 0.1.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/LICENSE +21 -0
- package/README.md +422 -0
- package/bin/facet-coverage.js +3 -0
- package/dist/cli/commands/analyze.d.ts +13 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +161 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +10 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +67 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +11 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +120 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +10 -0
- package/dist/cli/commands/watch.d.ts.map +1 -0
- package/dist/cli/commands/watch.js +151 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +47 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/CoverageCalculator.d.ts +34 -0
- package/dist/core/CoverageCalculator.d.ts.map +1 -0
- package/dist/core/CoverageCalculator.js +167 -0
- package/dist/core/CoverageCalculator.js.map +1 -0
- package/dist/core/FacetParser.d.ts +39 -0
- package/dist/core/FacetParser.d.ts.map +1 -0
- package/dist/core/FacetParser.js +114 -0
- package/dist/core/FacetParser.js.map +1 -0
- package/dist/core/StructureReader.d.ts +37 -0
- package/dist/core/StructureReader.d.ts.map +1 -0
- package/dist/core/StructureReader.js +126 -0
- package/dist/core/StructureReader.js.map +1 -0
- package/dist/core/TestScanner.d.ts +36 -0
- package/dist/core/TestScanner.d.ts.map +1 -0
- package/dist/core/TestScanner.js +192 -0
- package/dist/core/TestScanner.js.map +1 -0
- package/dist/core/Validator.d.ts +33 -0
- package/dist/core/Validator.d.ts.map +1 -0
- package/dist/core/Validator.js +183 -0
- package/dist/core/Validator.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/playwright.d.ts +95 -0
- package/dist/integrations/playwright.d.ts.map +1 -0
- package/dist/integrations/playwright.js +168 -0
- package/dist/integrations/playwright.js.map +1 -0
- package/dist/reporters/HtmlReporter.d.ts +53 -0
- package/dist/reporters/HtmlReporter.d.ts.map +1 -0
- package/dist/reporters/HtmlReporter.js +484 -0
- package/dist/reporters/HtmlReporter.js.map +1 -0
- package/dist/reporters/JsonReporter.d.ts +21 -0
- package/dist/reporters/JsonReporter.d.ts.map +1 -0
- package/dist/reporters/JsonReporter.js +58 -0
- package/dist/reporters/JsonReporter.js.map +1 -0
- package/dist/reporters/MarkdownReporter.d.ts +25 -0
- package/dist/reporters/MarkdownReporter.d.ts.map +1 -0
- package/dist/reporters/MarkdownReporter.js +141 -0
- package/dist/reporters/MarkdownReporter.js.map +1 -0
- package/dist/reporters/index.d.ts +4 -0
- package/dist/reporters/index.d.ts.map +1 -0
- package/dist/reporters/index.js +10 -0
- package/dist/reporters/index.js.map +1 -0
- package/dist/types.d.ts +205 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +28 -0
- package/dist/types.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateCommand = generateCommand;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const FacetParser_js_1 = require("../../core/FacetParser.js");
|
|
7
|
+
/**
|
|
8
|
+
* Generate structure.json from facet markdown files
|
|
9
|
+
*/
|
|
10
|
+
async function generateCommand(dir, options = {}) {
|
|
11
|
+
const facetsDir = (0, path_1.resolve)(process.cwd(), dir);
|
|
12
|
+
if (!(0, fs_1.existsSync)(facetsDir)) {
|
|
13
|
+
console.error(`❌ Directory not found: ${facetsDir}`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
console.log(`💎 Generating structure from: ${facetsDir}`);
|
|
17
|
+
// Find all markdown files in the directory
|
|
18
|
+
const files = (0, fs_1.readdirSync)(facetsDir).filter(f => f.endsWith('.md'));
|
|
19
|
+
if (files.length === 0) {
|
|
20
|
+
console.error(`❌ No markdown files found in: ${facetsDir}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const parser = new FacetParser_js_1.FacetParser();
|
|
24
|
+
const facets = [];
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
const filePath = (0, path_1.join)(facetsDir, file);
|
|
27
|
+
const parsed = parser.parseFile(filePath);
|
|
28
|
+
// Determine facet type from filename (e.g., business.md -> business)
|
|
29
|
+
const type = options.type || (0, path_1.basename)(file, '.md');
|
|
30
|
+
console.log(` 📄 ${file}`);
|
|
31
|
+
for (const section of parsed.sections) {
|
|
32
|
+
// Only include top-level sections (h2 or the first h1 if no h2s)
|
|
33
|
+
if (section.level <= 2) {
|
|
34
|
+
const facetId = FacetParser_js_1.FacetParser.generateFacetId(file, section.slug);
|
|
35
|
+
facets.push({
|
|
36
|
+
id: facetId,
|
|
37
|
+
source: {
|
|
38
|
+
file: `facets/${file}`,
|
|
39
|
+
section: section.slug,
|
|
40
|
+
},
|
|
41
|
+
type,
|
|
42
|
+
title: section.title,
|
|
43
|
+
});
|
|
44
|
+
console.log(` ✓ ${facetId}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Determine feature name from parent directory
|
|
49
|
+
const featureDir = (0, path_1.dirname)(facetsDir);
|
|
50
|
+
const featureName = (0, path_1.basename)(featureDir);
|
|
51
|
+
// Build structure object
|
|
52
|
+
const structure = {
|
|
53
|
+
feature: featureName,
|
|
54
|
+
facets,
|
|
55
|
+
};
|
|
56
|
+
// Determine output path
|
|
57
|
+
const outputDir = options.output || (0, path_1.join)(featureDir, '.facet');
|
|
58
|
+
const outputPath = (0, path_1.join)(outputDir, 'structure.json');
|
|
59
|
+
// Ensure output directory exists
|
|
60
|
+
(0, fs_1.mkdirSync)(outputDir, { recursive: true });
|
|
61
|
+
// Write structure file
|
|
62
|
+
(0, fs_1.writeFileSync)(outputPath, JSON.stringify(structure, null, 2));
|
|
63
|
+
console.log(`\n✅ Generated: ${outputPath}`);
|
|
64
|
+
console.log(` Feature: ${featureName}`);
|
|
65
|
+
console.log(` Facets: ${facets.length}`);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface ValidateOptions {
|
|
2
|
+
config?: string;
|
|
3
|
+
strict?: boolean;
|
|
4
|
+
json?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Validate facet structure and test links
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateCommand(options?: ValidateOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":"AAMA,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BlF"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateCommand = validateCommand;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const Validator_js_1 = require("../../core/Validator.js");
|
|
7
|
+
const types_js_1 = require("../../types.js");
|
|
8
|
+
/**
|
|
9
|
+
* Validate facet structure and test links
|
|
10
|
+
*/
|
|
11
|
+
async function validateCommand(options = {}) {
|
|
12
|
+
const cwd = process.cwd();
|
|
13
|
+
// Load config
|
|
14
|
+
const config = await loadConfig(options.config, cwd);
|
|
15
|
+
// Enable strict mode if requested
|
|
16
|
+
if (options.strict) {
|
|
17
|
+
config.validation.requireAllTestsLinked = true;
|
|
18
|
+
}
|
|
19
|
+
console.log('💎 Validating Facet Coverage...\n');
|
|
20
|
+
const validator = new Validator_js_1.Validator(config);
|
|
21
|
+
const result = await validator.validate(cwd);
|
|
22
|
+
if (options.json) {
|
|
23
|
+
console.log(JSON.stringify(result, null, 2));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
printValidationResult(result);
|
|
27
|
+
}
|
|
28
|
+
// Exit with error code if validation failed
|
|
29
|
+
if (!result.valid) {
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Load configuration from file or use defaults
|
|
35
|
+
*/
|
|
36
|
+
async function loadConfig(configPath, cwd) {
|
|
37
|
+
const configFiles = [
|
|
38
|
+
configPath,
|
|
39
|
+
'facet.config.js',
|
|
40
|
+
'facet.config.mjs',
|
|
41
|
+
'facet.config.json',
|
|
42
|
+
].filter(Boolean);
|
|
43
|
+
for (const file of configFiles) {
|
|
44
|
+
const fullPath = (0, path_1.resolve)(cwd, file);
|
|
45
|
+
if ((0, fs_1.existsSync)(fullPath)) {
|
|
46
|
+
if (file.endsWith('.json')) {
|
|
47
|
+
const content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
|
|
48
|
+
return { ...types_js_1.defaultConfig, ...JSON.parse(content) };
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
try {
|
|
52
|
+
const imported = await import(fullPath);
|
|
53
|
+
return { ...types_js_1.defaultConfig, ...(imported.default || imported) };
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.warn(`Warning: Could not load config from ${file}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return types_js_1.defaultConfig;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Print validation results
|
|
65
|
+
*/
|
|
66
|
+
function printValidationResult(result) {
|
|
67
|
+
if (result.valid && result.warnings.length === 0) {
|
|
68
|
+
console.log('✅ Validation passed! No issues found.\n');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
// Print errors
|
|
72
|
+
if (result.errors.length > 0) {
|
|
73
|
+
console.log(`❌ Errors (${result.errors.length}):\n`);
|
|
74
|
+
for (const error of result.errors) {
|
|
75
|
+
const location = error.file ? ` in ${error.file}` : '';
|
|
76
|
+
const line = error.line ? `:${error.line}` : '';
|
|
77
|
+
const facet = error.facetId ? ` [${error.facetId}]` : '';
|
|
78
|
+
console.log(` ${getErrorIcon(error.type)} ${error.message}${location}${line}${facet}`);
|
|
79
|
+
}
|
|
80
|
+
console.log('');
|
|
81
|
+
}
|
|
82
|
+
// Print warnings
|
|
83
|
+
if (result.warnings.length > 0) {
|
|
84
|
+
console.log(`⚠️ Warnings (${result.warnings.length}):\n`);
|
|
85
|
+
for (const warning of result.warnings) {
|
|
86
|
+
const location = warning.file ? ` in ${warning.file}` : '';
|
|
87
|
+
const line = warning.line ? `:${warning.line}` : '';
|
|
88
|
+
const facet = warning.facetId ? ` [${warning.facetId}]` : '';
|
|
89
|
+
console.log(` ⚠️ ${warning.message}${location}${line}${facet}`);
|
|
90
|
+
}
|
|
91
|
+
console.log('');
|
|
92
|
+
}
|
|
93
|
+
// Summary
|
|
94
|
+
if (result.valid) {
|
|
95
|
+
console.log('✅ Validation passed with warnings.');
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log('❌ Validation failed. Please fix the errors above.');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get icon for error type
|
|
103
|
+
*/
|
|
104
|
+
function getErrorIcon(type) {
|
|
105
|
+
switch (type) {
|
|
106
|
+
case 'missing-source':
|
|
107
|
+
return '📄';
|
|
108
|
+
case 'missing-section':
|
|
109
|
+
return '📑';
|
|
110
|
+
case 'invalid-facet-id':
|
|
111
|
+
return '🏷️';
|
|
112
|
+
case 'orphan-test':
|
|
113
|
+
return '🔗';
|
|
114
|
+
case 'duplicate-id':
|
|
115
|
+
return '🔄';
|
|
116
|
+
default:
|
|
117
|
+
return '❌';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":";;AAeA,0CA0BC;AAzCD,2BAA8C;AAC9C,+BAA+B;AAC/B,0DAAoD;AAEpD,6CAA+C;AAQ/C;;GAEG;AACI,KAAK,UAAU,eAAe,CAAC,UAA2B,EAAE;IACjE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE1B,cAAc;IACd,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAErD,kCAAkC;IAClC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,CAAC,UAAU,CAAC,qBAAqB,GAAG,IAAI,CAAC;IACjD,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IAEjD,MAAM,SAAS,GAAG,IAAI,wBAAS,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAE7C,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED,4CAA4C;IAC5C,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,UAAU,CAAC,UAA8B,EAAE,GAAW;IACnE,MAAM,WAAW,GAAG;QAClB,UAAU;QACV,iBAAiB;QACjB,kBAAkB;QAClB,mBAAmB;KACpB,CAAC,MAAM,CAAC,OAAO,CAAa,CAAC;IAE9B,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAA,cAAO,EAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAEpC,IAAI,IAAA,eAAU,EAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC3B,MAAM,OAAO,GAAG,IAAA,iBAAY,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAChD,OAAO,EAAE,GAAG,wBAAa,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACtD,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC;oBACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;oBACxC,OAAO,EAAE,GAAG,wBAAa,EAAE,GAAG,CAAC,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,EAAE,CAAC;gBACjE,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,IAAI,CAAC,uCAAuC,IAAI,EAAE,CAAC,CAAC;gBAC9D,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,wBAAa,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,MAAwB;IACrD,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;QACvD,OAAO;IACT,CAAC;IAED,eAAe;IACf,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,MAAM,CAAC,CAAC;QAErD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAEzD,OAAO,CAAC,GAAG,CAAC,KAAK,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,GAAG,QAAQ,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IAED,iBAAiB;IACjB,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,QAAQ,CAAC,MAAM,MAAM,CAAC,CAAC;QAE3D,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAE7D,OAAO,CAAC,GAAG,CAAC,SAAS,OAAO,CAAC,OAAO,GAAG,QAAQ,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IAED,UAAU;IACV,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;IACpD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;IACnE,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,gBAAgB;YACnB,OAAO,IAAI,CAAC;QACd,KAAK,iBAAiB;YACpB,OAAO,IAAI,CAAC;QACd,KAAK,kBAAkB;YACrB,OAAO,KAAK,CAAC;QACf,KAAK,aAAa;YAChB,OAAO,IAAI,CAAC;QACd,KAAK,cAAc;YACjB,OAAO,IAAI,CAAC;QACd;YACE,OAAO,GAAG,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/watch.ts"],"names":[],"mappings":"AAWA,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqD5E"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.watchCommand = watchCommand;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const chokidar_1 = require("chokidar");
|
|
7
|
+
const CoverageCalculator_js_1 = require("../../core/CoverageCalculator.js");
|
|
8
|
+
const Validator_js_1 = require("../../core/Validator.js");
|
|
9
|
+
const JsonReporter_js_1 = require("../../reporters/JsonReporter.js");
|
|
10
|
+
const HtmlReporter_js_1 = require("../../reporters/HtmlReporter.js");
|
|
11
|
+
const MarkdownReporter_js_1 = require("../../reporters/MarkdownReporter.js");
|
|
12
|
+
const types_js_1 = require("../../types.js");
|
|
13
|
+
/**
|
|
14
|
+
* Watch for changes and re-run analysis
|
|
15
|
+
*/
|
|
16
|
+
async function watchCommand(options = {}) {
|
|
17
|
+
const cwd = process.cwd();
|
|
18
|
+
// Load config
|
|
19
|
+
const config = await loadConfig(options.config, cwd);
|
|
20
|
+
console.log('💎 Facet Coverage Watch Mode\n');
|
|
21
|
+
console.log('Watching for changes...\n');
|
|
22
|
+
// Initial run
|
|
23
|
+
await runAnalysis(config, cwd, options.validate);
|
|
24
|
+
// Watch patterns
|
|
25
|
+
const watchPatterns = [
|
|
26
|
+
...config.structureFiles,
|
|
27
|
+
'features/**/facets/**/*.md',
|
|
28
|
+
`${config.testDir}/**/*.{ts,js,tsx,jsx}`,
|
|
29
|
+
];
|
|
30
|
+
// Create watcher
|
|
31
|
+
const watcher = (0, chokidar_1.watch)(watchPatterns, {
|
|
32
|
+
cwd,
|
|
33
|
+
ignoreInitial: true,
|
|
34
|
+
persistent: true,
|
|
35
|
+
});
|
|
36
|
+
// Debounce timer
|
|
37
|
+
let debounceTimer = null;
|
|
38
|
+
watcher.on('all', (event, path) => {
|
|
39
|
+
// Clear existing timer
|
|
40
|
+
if (debounceTimer) {
|
|
41
|
+
clearTimeout(debounceTimer);
|
|
42
|
+
}
|
|
43
|
+
// Debounce to avoid multiple rapid runs
|
|
44
|
+
debounceTimer = setTimeout(async () => {
|
|
45
|
+
console.log(`\n📝 Change detected: ${path} (${event})`);
|
|
46
|
+
console.log('Re-analyzing...\n');
|
|
47
|
+
await runAnalysis(config, cwd, options.validate);
|
|
48
|
+
}, 300);
|
|
49
|
+
});
|
|
50
|
+
// Handle shutdown
|
|
51
|
+
process.on('SIGINT', () => {
|
|
52
|
+
console.log('\n\n👋 Stopping watch mode...');
|
|
53
|
+
watcher.close();
|
|
54
|
+
process.exit(0);
|
|
55
|
+
});
|
|
56
|
+
// Keep process alive
|
|
57
|
+
console.log('Press Ctrl+C to stop.\n');
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Run analysis
|
|
61
|
+
*/
|
|
62
|
+
async function runAnalysis(config, cwd, validate = false) {
|
|
63
|
+
const startTime = Date.now();
|
|
64
|
+
try {
|
|
65
|
+
// Optionally validate first
|
|
66
|
+
if (validate) {
|
|
67
|
+
const validator = new Validator_js_1.Validator(config);
|
|
68
|
+
const validationResult = await validator.validate(cwd);
|
|
69
|
+
if (!validationResult.valid) {
|
|
70
|
+
console.log('❌ Validation failed:');
|
|
71
|
+
for (const error of validationResult.errors) {
|
|
72
|
+
console.log(` - ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
console.log('');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Calculate coverage
|
|
79
|
+
const calculator = new CoverageCalculator_js_1.CoverageCalculator(config);
|
|
80
|
+
const report = await calculator.calculateCoverage(cwd);
|
|
81
|
+
// Generate reports
|
|
82
|
+
if (config.output.formats.includes('json')) {
|
|
83
|
+
const reporter = new JsonReporter_js_1.JsonReporter(config);
|
|
84
|
+
reporter.write(report, cwd);
|
|
85
|
+
}
|
|
86
|
+
if (config.output.formats.includes('html')) {
|
|
87
|
+
const reporter = new HtmlReporter_js_1.HtmlReporter(config);
|
|
88
|
+
reporter.write(report, cwd);
|
|
89
|
+
}
|
|
90
|
+
if (config.output.formats.includes('markdown')) {
|
|
91
|
+
const reporter = new MarkdownReporter_js_1.MarkdownReporter(config);
|
|
92
|
+
reporter.write(report, cwd);
|
|
93
|
+
}
|
|
94
|
+
// Print summary
|
|
95
|
+
const duration = Date.now() - startTime;
|
|
96
|
+
const icon = report.summary.percentage >= 80 ? '✅' : report.summary.percentage >= 50 ? '🟡' : '❌';
|
|
97
|
+
console.log('┌─────────────────────────────────────────────┐');
|
|
98
|
+
console.log(`│ ${icon} Coverage: ${String(report.summary.percentage + '%').padEnd(6)} (${report.summary.coveredFacets}/${report.summary.totalFacets} facets)`.padEnd(45) + ' │');
|
|
99
|
+
console.log('└─────────────────────────────────────────────┘');
|
|
100
|
+
// Show type breakdown
|
|
101
|
+
if (report.byType.length > 0) {
|
|
102
|
+
for (const type of report.byType) {
|
|
103
|
+
const typeIcon = type.percentage === 100 ? '✅' : type.percentage >= 75 ? '🟡' : '❌';
|
|
104
|
+
console.log(` ${typeIcon} ${type.type}: ${type.percentage}%`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
console.log(`\n⏱️ Analysis completed in ${duration}ms`);
|
|
108
|
+
// Check thresholds
|
|
109
|
+
const thresholdResult = calculator.checkThresholds(report);
|
|
110
|
+
if (!thresholdResult.passed) {
|
|
111
|
+
console.log('\n⚠️ Thresholds not met:');
|
|
112
|
+
for (const failure of thresholdResult.failures) {
|
|
113
|
+
console.log(` - ${failure}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error('❌ Error during analysis:', error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Load configuration from file or use defaults
|
|
123
|
+
*/
|
|
124
|
+
async function loadConfig(configPath, cwd) {
|
|
125
|
+
const configFiles = [
|
|
126
|
+
configPath,
|
|
127
|
+
'facet.config.js',
|
|
128
|
+
'facet.config.mjs',
|
|
129
|
+
'facet.config.json',
|
|
130
|
+
].filter(Boolean);
|
|
131
|
+
for (const file of configFiles) {
|
|
132
|
+
const fullPath = (0, path_1.resolve)(cwd, file);
|
|
133
|
+
if ((0, fs_1.existsSync)(fullPath)) {
|
|
134
|
+
if (file.endsWith('.json')) {
|
|
135
|
+
const content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
|
|
136
|
+
return { ...types_js_1.defaultConfig, ...JSON.parse(content) };
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
try {
|
|
140
|
+
const imported = await import(fullPath);
|
|
141
|
+
return { ...types_js_1.defaultConfig, ...(imported.default || imported) };
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
console.warn(`Warning: Could not load config from ${file}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return types_js_1.defaultConfig;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=watch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../../../src/cli/commands/watch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const commander_1 = require("commander");
|
|
4
|
+
const generate_js_1 = require("./commands/generate.js");
|
|
5
|
+
const analyze_js_1 = require("./commands/analyze.js");
|
|
6
|
+
const validate_js_1 = require("./commands/validate.js");
|
|
7
|
+
const watch_js_1 = require("./commands/watch.js");
|
|
8
|
+
const program = new commander_1.Command();
|
|
9
|
+
program
|
|
10
|
+
.name('facet')
|
|
11
|
+
.description('💎 Test every facet of your features - natural specifications with rigorous coverage tracking')
|
|
12
|
+
.version('0.1.0');
|
|
13
|
+
// Generate command
|
|
14
|
+
program
|
|
15
|
+
.command('generate <dir>')
|
|
16
|
+
.description('Generate structure.json from facet markdown files')
|
|
17
|
+
.option('-o, --output <path>', 'Output directory for structure.json')
|
|
18
|
+
.option('-t, --type <type>', 'Override facet type (default: derived from filename)')
|
|
19
|
+
.action(generate_js_1.generateCommand);
|
|
20
|
+
// Analyze command
|
|
21
|
+
program
|
|
22
|
+
.command('analyze')
|
|
23
|
+
.description('Analyze facet coverage')
|
|
24
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
25
|
+
.option('-f, --format <format>', 'Output format (json, html, markdown)')
|
|
26
|
+
.option('-t, --threshold <number>', 'Coverage threshold percentage')
|
|
27
|
+
.option('--json', 'Output results as JSON')
|
|
28
|
+
.option('--silent', 'Suppress console output')
|
|
29
|
+
.action(analyze_js_1.analyzeCommand);
|
|
30
|
+
// Validate command
|
|
31
|
+
program
|
|
32
|
+
.command('validate')
|
|
33
|
+
.description('Validate facet structure and test links')
|
|
34
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
35
|
+
.option('--strict', 'Enable strict validation (require all tests linked)')
|
|
36
|
+
.option('--json', 'Output results as JSON')
|
|
37
|
+
.action(validate_js_1.validateCommand);
|
|
38
|
+
// Watch command
|
|
39
|
+
program
|
|
40
|
+
.command('watch')
|
|
41
|
+
.description('Watch for changes and re-run analysis')
|
|
42
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
43
|
+
.option('-v, --validate', 'Run validation before analysis')
|
|
44
|
+
.action(watch_js_1.watchCommand);
|
|
45
|
+
// Parse arguments
|
|
46
|
+
program.parse();
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;AAAA,yCAAoC;AACpC,wDAAyD;AACzD,sDAAuD;AACvD,wDAAyD;AACzD,kDAAmD;AAEnD,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,+FAA+F,CAAC;KAC5G,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,mBAAmB;AACnB,OAAO;KACJ,OAAO,CAAC,gBAAgB,CAAC;KACzB,WAAW,CAAC,mDAAmD,CAAC;KAChE,MAAM,CAAC,qBAAqB,EAAE,qCAAqC,CAAC;KACpE,MAAM,CAAC,mBAAmB,EAAE,sDAAsD,CAAC;KACnF,MAAM,CAAC,6BAAe,CAAC,CAAC;AAE3B,kBAAkB;AAClB,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,wBAAwB,CAAC;KACrC,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;KACpD,MAAM,CAAC,uBAAuB,EAAE,sCAAsC,CAAC;KACvE,MAAM,CAAC,0BAA0B,EAAE,+BAA+B,CAAC;KACnE,MAAM,CAAC,QAAQ,EAAE,wBAAwB,CAAC;KAC1C,MAAM,CAAC,UAAU,EAAE,yBAAyB,CAAC;KAC7C,MAAM,CAAC,2BAAc,CAAC,CAAC;AAE1B,mBAAmB;AACnB,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;KACpD,MAAM,CAAC,UAAU,EAAE,qDAAqD,CAAC;KACzE,MAAM,CAAC,QAAQ,EAAE,wBAAwB,CAAC;KAC1C,MAAM,CAAC,6BAAe,CAAC,CAAC;AAE3B,gBAAgB;AAChB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,uCAAuC,CAAC;KACpD,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;KACpD,MAAM,CAAC,gBAAgB,EAAE,gCAAgC,CAAC;KAC1D,MAAM,CAAC,uBAAY,CAAC,CAAC;AAExB,kBAAkB;AAClB,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { FacetConfig, FacetCoverage, CoverageReport } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Calculates coverage metrics for facets
|
|
4
|
+
*/
|
|
5
|
+
export declare class CoverageCalculator {
|
|
6
|
+
private config;
|
|
7
|
+
private structureReader;
|
|
8
|
+
private testScanner;
|
|
9
|
+
constructor(config?: Partial<FacetConfig>);
|
|
10
|
+
/**
|
|
11
|
+
* Calculate complete coverage report
|
|
12
|
+
*/
|
|
13
|
+
calculateCoverage(cwd?: string): Promise<CoverageReport>;
|
|
14
|
+
/**
|
|
15
|
+
* Calculate coverage for a single feature
|
|
16
|
+
*/
|
|
17
|
+
private calculateFeatureCoverage;
|
|
18
|
+
/**
|
|
19
|
+
* Calculate coverage breakdown by facet type
|
|
20
|
+
*/
|
|
21
|
+
private calculateTypeCoverage;
|
|
22
|
+
/**
|
|
23
|
+
* Check if coverage meets configured thresholds
|
|
24
|
+
*/
|
|
25
|
+
checkThresholds(report: CoverageReport): {
|
|
26
|
+
passed: boolean;
|
|
27
|
+
failures: string[];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Get coverage for a specific facet
|
|
31
|
+
*/
|
|
32
|
+
getFacetCoverage(facetId: string, cwd?: string): Promise<FacetCoverage | null>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=CoverageCalculator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CoverageCalculator.d.ts","sourceRoot":"","sources":["../../src/core/CoverageCalculator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EAIX,aAAa,EAGb,cAAc,EACf,MAAM,aAAa,CAAC;AAKrB;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,WAAW,CAAc;gBAErB,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM;IAM7C;;OAEG;IACG,iBAAiB,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,cAAc,CAAC;IA+D7E;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAsChC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA6B7B;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG;QACvC,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB;IA0BD;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;CAYpG"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CoverageCalculator = void 0;
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
const types_js_1 = require("../types.js");
|
|
6
|
+
const StructureReader_js_1 = require("./StructureReader.js");
|
|
7
|
+
const TestScanner_js_1 = require("./TestScanner.js");
|
|
8
|
+
/**
|
|
9
|
+
* Calculates coverage metrics for facets
|
|
10
|
+
*/
|
|
11
|
+
class CoverageCalculator {
|
|
12
|
+
config;
|
|
13
|
+
structureReader;
|
|
14
|
+
testScanner;
|
|
15
|
+
constructor(config = {}) {
|
|
16
|
+
this.config = { ...types_js_1.defaultConfig, ...config };
|
|
17
|
+
this.structureReader = new StructureReader_js_1.StructureReader(this.config);
|
|
18
|
+
this.testScanner = new TestScanner_js_1.TestScanner(this.config);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Calculate complete coverage report
|
|
22
|
+
*/
|
|
23
|
+
async calculateCoverage(cwd = process.cwd()) {
|
|
24
|
+
// Get all structures and test links
|
|
25
|
+
const structures = await this.structureReader.readAllStructures(cwd);
|
|
26
|
+
const testLinks = await this.testScanner.scanAllTests(cwd);
|
|
27
|
+
// Build facet ID to test links map
|
|
28
|
+
const facetToTests = new Map();
|
|
29
|
+
for (const link of testLinks) {
|
|
30
|
+
for (const facetId of link.facetIds) {
|
|
31
|
+
const existing = facetToTests.get(facetId) || [];
|
|
32
|
+
existing.push(link);
|
|
33
|
+
facetToTests.set(facetId, existing);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Calculate feature-level coverage
|
|
37
|
+
const features = [];
|
|
38
|
+
const allFacets = [];
|
|
39
|
+
const uncovered = [];
|
|
40
|
+
for (const [structureFile, structure] of structures) {
|
|
41
|
+
const featureCoverage = this.calculateFeatureCoverage(structure, structureFile, facetToTests);
|
|
42
|
+
features.push(featureCoverage);
|
|
43
|
+
// Collect all facet coverage data
|
|
44
|
+
allFacets.push(...featureCoverage.facets);
|
|
45
|
+
// Collect uncovered facets
|
|
46
|
+
for (const fc of featureCoverage.facets) {
|
|
47
|
+
if (!fc.covered) {
|
|
48
|
+
uncovered.push(fc.facet);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Calculate overall statistics
|
|
53
|
+
const totalFacets = allFacets.length;
|
|
54
|
+
const coveredFacets = allFacets.filter(f => f.covered).length;
|
|
55
|
+
const uncoveredFacets = totalFacets - coveredFacets;
|
|
56
|
+
const percentage = totalFacets > 0 ? Math.round((coveredFacets / totalFacets) * 100) : 100;
|
|
57
|
+
// Calculate coverage by type
|
|
58
|
+
const byType = this.calculateTypeCoverage(allFacets);
|
|
59
|
+
return {
|
|
60
|
+
timestamp: new Date().toISOString(),
|
|
61
|
+
summary: {
|
|
62
|
+
totalFacets,
|
|
63
|
+
coveredFacets,
|
|
64
|
+
uncoveredFacets,
|
|
65
|
+
percentage,
|
|
66
|
+
},
|
|
67
|
+
byType,
|
|
68
|
+
features,
|
|
69
|
+
tests: testLinks,
|
|
70
|
+
uncovered,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Calculate coverage for a single feature
|
|
75
|
+
*/
|
|
76
|
+
calculateFeatureCoverage(structure, structureFile, facetToTests) {
|
|
77
|
+
const facetCoverages = [];
|
|
78
|
+
for (const facet of structure.facets) {
|
|
79
|
+
const coveredBy = facetToTests.get(facet.id) || [];
|
|
80
|
+
facetCoverages.push({
|
|
81
|
+
facet,
|
|
82
|
+
covered: coveredBy.length > 0,
|
|
83
|
+
coveredBy,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const totalFacets = facetCoverages.length;
|
|
87
|
+
const coveredFacets = facetCoverages.filter(f => f.covered).length;
|
|
88
|
+
const percentage = totalFacets > 0 ? Math.round((coveredFacets / totalFacets) * 100) : 100;
|
|
89
|
+
// Calculate by type for this feature
|
|
90
|
+
const byType = this.calculateTypeCoverage(facetCoverages);
|
|
91
|
+
// Get feature path (parent of .facet directory)
|
|
92
|
+
const facetDir = (0, path_1.dirname)(structureFile);
|
|
93
|
+
const featurePath = (0, path_1.dirname)(facetDir);
|
|
94
|
+
return {
|
|
95
|
+
feature: structure.feature,
|
|
96
|
+
path: featurePath,
|
|
97
|
+
totalFacets,
|
|
98
|
+
coveredFacets,
|
|
99
|
+
percentage,
|
|
100
|
+
byType,
|
|
101
|
+
facets: facetCoverages,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Calculate coverage breakdown by facet type
|
|
106
|
+
*/
|
|
107
|
+
calculateTypeCoverage(facetCoverages) {
|
|
108
|
+
const typeMap = new Map();
|
|
109
|
+
for (const fc of facetCoverages) {
|
|
110
|
+
const type = fc.facet.type;
|
|
111
|
+
const existing = typeMap.get(type) || { total: 0, covered: 0 };
|
|
112
|
+
existing.total++;
|
|
113
|
+
if (fc.covered) {
|
|
114
|
+
existing.covered++;
|
|
115
|
+
}
|
|
116
|
+
typeMap.set(type, existing);
|
|
117
|
+
}
|
|
118
|
+
const result = [];
|
|
119
|
+
for (const [type, stats] of typeMap) {
|
|
120
|
+
result.push({
|
|
121
|
+
type,
|
|
122
|
+
total: stats.total,
|
|
123
|
+
covered: stats.covered,
|
|
124
|
+
percentage: stats.total > 0 ? Math.round((stats.covered / stats.total) * 100) : 100,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// Sort by type name
|
|
128
|
+
result.sort((a, b) => a.type.localeCompare(b.type));
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if coverage meets configured thresholds
|
|
133
|
+
*/
|
|
134
|
+
checkThresholds(report) {
|
|
135
|
+
const failures = [];
|
|
136
|
+
// Check global threshold
|
|
137
|
+
if (report.summary.percentage < this.config.thresholds.global) {
|
|
138
|
+
failures.push(`Global coverage ${report.summary.percentage}% is below threshold of ${this.config.thresholds.global}%`);
|
|
139
|
+
}
|
|
140
|
+
// Check per-type thresholds
|
|
141
|
+
for (const [type, threshold] of Object.entries(this.config.thresholds.byType)) {
|
|
142
|
+
const typeCoverage = report.byType.find(t => t.type === type);
|
|
143
|
+
if (typeCoverage && typeCoverage.percentage < threshold) {
|
|
144
|
+
failures.push(`${type} coverage ${typeCoverage.percentage}% is below threshold of ${threshold}%`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
passed: failures.length === 0,
|
|
149
|
+
failures,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get coverage for a specific facet
|
|
154
|
+
*/
|
|
155
|
+
async getFacetCoverage(facetId, cwd = process.cwd()) {
|
|
156
|
+
const report = await this.calculateCoverage(cwd);
|
|
157
|
+
for (const feature of report.features) {
|
|
158
|
+
const facetCoverage = feature.facets.find(f => f.facet.id === facetId);
|
|
159
|
+
if (facetCoverage) {
|
|
160
|
+
return facetCoverage;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
exports.CoverageCalculator = CoverageCalculator;
|
|
167
|
+
//# sourceMappingURL=CoverageCalculator.js.map
|