@paths.design/caws-cli 7.0.2 → 8.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/dist/budget-derivation.js +5 -4
- package/dist/commands/diagnose.js +24 -19
- package/dist/commands/init.js +51 -4
- package/dist/commands/quality-gates.js +147 -9
- package/dist/commands/specs.js +148 -14
- package/dist/commands/status.js +2 -2
- package/dist/commands/tool.js +2 -4
- package/dist/config/index.js +17 -8
- package/dist/generators/working-spec.js +19 -6
- package/dist/scaffold/git-hooks.js +245 -46
- package/dist/scaffold/index.js +53 -7
- package/dist/templates/.caws/tools/README.md +21 -0
- package/dist/templates/.cursor/README.md +311 -0
- package/dist/templates/.cursor/hooks/audit.sh +55 -0
- package/dist/templates/.cursor/hooks/block-dangerous.sh +83 -0
- package/dist/templates/.cursor/hooks/caws-quality-check.sh +52 -0
- package/dist/templates/.cursor/hooks/caws-scope-guard.sh +130 -0
- package/dist/templates/.cursor/hooks/caws-tool-validation.sh +121 -0
- package/dist/templates/.cursor/hooks/format.sh +38 -0
- package/dist/templates/.cursor/hooks/naming-check.sh +64 -0
- package/dist/templates/.cursor/hooks/scan-secrets.sh +46 -0
- package/dist/templates/.cursor/hooks/scope-guard.sh +52 -0
- package/dist/templates/.cursor/hooks/validate-spec.sh +83 -0
- package/dist/templates/.cursor/hooks.json +59 -0
- package/dist/templates/.cursor/rules/00-claims-verification.mdc +144 -0
- package/dist/templates/.cursor/rules/01-working-style.mdc +50 -0
- package/dist/templates/.cursor/rules/02-quality-gates.mdc +370 -0
- package/dist/templates/.cursor/rules/03-naming-and-refactor.mdc +33 -0
- package/dist/templates/.cursor/rules/04-logging-language-style.mdc +23 -0
- package/dist/templates/.cursor/rules/05-safe-defaults-guards.mdc +23 -0
- package/dist/templates/.cursor/rules/06-typescript-conventions.mdc +36 -0
- package/dist/templates/.cursor/rules/07-process-ops.mdc +20 -0
- package/dist/templates/.cursor/rules/08-solid-and-architecture.mdc +16 -0
- package/dist/templates/.cursor/rules/09-docstrings.mdc +89 -0
- package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +390 -0
- package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +385 -0
- package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +516 -0
- package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +588 -0
- package/dist/templates/.cursor/rules/README.md +148 -0
- package/dist/templates/.github/copilot/instructions.md +311 -0
- package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +5 -0
- package/dist/templates/.idea/runConfigurations/CAWS_Validate.xml +5 -0
- package/dist/templates/.vscode/launch.json +56 -0
- package/dist/templates/.vscode/settings.json +93 -0
- package/dist/templates/.windsurf/workflows/caws-guided-development.md +92 -0
- package/dist/templates/COMMIT_CONVENTIONS.md +86 -0
- package/dist/templates/OIDC_SETUP.md +300 -0
- package/dist/templates/agents.md +1047 -0
- package/dist/templates/codemod/README.md +1 -0
- package/dist/templates/codemod/test.js +93 -0
- package/dist/templates/docs/README.md +150 -0
- package/dist/templates/scripts/quality-gates/check-god-objects.js +146 -0
- package/dist/templates/scripts/quality-gates/run-quality-gates.js +50 -0
- package/dist/templates/scripts/v3/analysis/todo_analyzer.py +1997 -0
- package/dist/tool-loader.js +6 -1
- package/dist/tool-validator.js +8 -2
- package/dist/utils/detection.js +4 -3
- package/dist/utils/git-lock.js +119 -0
- package/dist/utils/gitignore-updater.js +148 -0
- package/dist/utils/project-analysis.js +176 -16
- package/dist/utils/quality-gates.js +48 -7
- package/dist/utils/spec-resolver.js +27 -3
- package/dist/utils/yaml-validation.js +156 -0
- package/dist/validation/spec-validation.js +81 -2
- package/package.json +2 -2
- package/templates/.caws/schemas/waivers.schema.json +30 -0
- package/templates/.caws/schemas/working-spec.schema.json +133 -0
- package/templates/.caws/templates/working-spec.template.yml +74 -0
- package/templates/.caws/tools/README.md +21 -0
- package/templates/.caws/tools/scope-guard.js +208 -0
- package/templates/.caws/tools-allow.json +331 -0
- package/templates/.caws/waivers.yml +19 -0
- package/templates/.cursor/hooks/scope-guard.sh +2 -2
- package/templates/.cursor/hooks/validate-spec.sh +42 -7
- package/dist/budget-derivation.d.ts +0 -74
- package/dist/budget-derivation.d.ts.map +0 -1
- package/dist/cicd-optimizer.d.ts +0 -142
- package/dist/cicd-optimizer.d.ts.map +0 -1
- package/dist/commands/archive.d.ts +0 -50
- package/dist/commands/archive.d.ts.map +0 -1
- package/dist/commands/burnup.d.ts +0 -6
- package/dist/commands/burnup.d.ts.map +0 -1
- package/dist/commands/diagnose.d.ts +0 -52
- package/dist/commands/diagnose.d.ts.map +0 -1
- package/dist/commands/evaluate.d.ts +0 -8
- package/dist/commands/evaluate.d.ts.map +0 -1
- package/dist/commands/init.d.ts +0 -5
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/iterate.d.ts +0 -8
- package/dist/commands/iterate.d.ts.map +0 -1
- package/dist/commands/mode.d.ts +0 -24
- package/dist/commands/mode.d.ts.map +0 -1
- package/dist/commands/plan.d.ts +0 -49
- package/dist/commands/plan.d.ts.map +0 -1
- package/dist/commands/provenance.d.ts +0 -32
- package/dist/commands/provenance.d.ts.map +0 -1
- package/dist/commands/quality-gates.d.ts +0 -52
- package/dist/commands/quality-gates.d.ts.map +0 -1
- package/dist/commands/quality-monitor.d.ts +0 -17
- package/dist/commands/quality-monitor.d.ts.map +0 -1
- package/dist/commands/specs.d.ts +0 -71
- package/dist/commands/specs.d.ts.map +0 -1
- package/dist/commands/status.d.ts +0 -44
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/templates.d.ts +0 -74
- package/dist/commands/templates.d.ts.map +0 -1
- package/dist/commands/tool.d.ts +0 -13
- package/dist/commands/tool.d.ts.map +0 -1
- package/dist/commands/troubleshoot.d.ts +0 -8
- package/dist/commands/troubleshoot.d.ts.map +0 -1
- package/dist/commands/tutorial.d.ts +0 -55
- package/dist/commands/tutorial.d.ts.map +0 -1
- package/dist/commands/validate.d.ts +0 -15
- package/dist/commands/validate.d.ts.map +0 -1
- package/dist/commands/waivers.d.ts +0 -8
- package/dist/commands/waivers.d.ts.map +0 -1
- package/dist/commands/workflow.d.ts +0 -85
- package/dist/commands/workflow.d.ts.map +0 -1
- package/dist/config/index.d.ts +0 -29
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/modes.d.ts +0 -225
- package/dist/config/modes.d.ts.map +0 -1
- package/dist/constants/spec-types.d.ts +0 -41
- package/dist/constants/spec-types.d.ts.map +0 -1
- package/dist/error-handler.d.ts +0 -164
- package/dist/error-handler.d.ts.map +0 -1
- package/dist/generators/jest-config.d.ts +0 -32
- package/dist/generators/jest-config.d.ts.map +0 -1
- package/dist/generators/working-spec.d.ts +0 -13
- package/dist/generators/working-spec.d.ts.map +0 -1
- package/dist/index-new.d.ts +0 -5
- package/dist/index-new.d.ts.map +0 -1
- package/dist/index-new.js +0 -317
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.backup +0 -4711
- package/dist/minimal-cli.d.ts +0 -3
- package/dist/minimal-cli.d.ts.map +0 -1
- package/dist/policy/PolicyManager.d.ts +0 -104
- package/dist/policy/PolicyManager.d.ts.map +0 -1
- package/dist/scaffold/cursor-hooks.d.ts +0 -7
- package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
- package/dist/scaffold/git-hooks.d.ts +0 -20
- package/dist/scaffold/git-hooks.d.ts.map +0 -1
- package/dist/scaffold/index.d.ts +0 -20
- package/dist/scaffold/index.d.ts.map +0 -1
- package/dist/spec/SpecFileManager.d.ts +0 -146
- package/dist/spec/SpecFileManager.d.ts.map +0 -1
- package/dist/test-analysis.d.ts +0 -182
- package/dist/test-analysis.d.ts.map +0 -1
- package/dist/tool-interface.d.ts +0 -236
- package/dist/tool-interface.d.ts.map +0 -1
- package/dist/tool-loader.d.ts +0 -77
- package/dist/tool-loader.d.ts.map +0 -1
- package/dist/tool-validator.d.ts +0 -72
- package/dist/tool-validator.d.ts.map +0 -1
- package/dist/utils/detection.d.ts +0 -7
- package/dist/utils/detection.d.ts.map +0 -1
- package/dist/utils/finalization.d.ts +0 -17
- package/dist/utils/finalization.d.ts.map +0 -1
- package/dist/utils/project-analysis.d.ts +0 -14
- package/dist/utils/project-analysis.d.ts.map +0 -1
- package/dist/utils/quality-gates.d.ts +0 -49
- package/dist/utils/quality-gates.d.ts.map +0 -1
- package/dist/utils/spec-resolver.d.ts +0 -88
- package/dist/utils/spec-resolver.d.ts.map +0 -1
- package/dist/utils/typescript-detector.d.ts +0 -63
- package/dist/utils/typescript-detector.d.ts.map +0 -1
- package/dist/validation/spec-validation.d.ts +0 -43
- package/dist/validation/spec-validation.d.ts.map +0 -1
- package/dist/waivers-manager.d.ts +0 -167
- package/dist/waivers-manager.d.ts.map +0 -1
- package/templates/apps/tools/caws/COMPLETION_REPORT.md +0 -331
- package/templates/apps/tools/caws/MIGRATION_SUMMARY.md +0 -360
- package/templates/apps/tools/caws/README.md +0 -463
- package/templates/apps/tools/caws/TEST_STATUS.md +0 -365
- package/templates/apps/tools/caws/attest.js +0 -357
- package/templates/apps/tools/caws/ci-optimizer.js +0 -642
- package/templates/apps/tools/caws/config.ts +0 -245
- package/templates/apps/tools/caws/cross-functional.js +0 -876
- package/templates/apps/tools/caws/dashboard.js +0 -1112
- package/templates/apps/tools/caws/flake-detector.ts +0 -362
- package/templates/apps/tools/caws/gates.js +0 -198
- package/templates/apps/tools/caws/gates.ts +0 -271
- package/templates/apps/tools/caws/language-adapters.ts +0 -381
- package/templates/apps/tools/caws/language-support.d.ts +0 -367
- package/templates/apps/tools/caws/language-support.d.ts.map +0 -1
- package/templates/apps/tools/caws/language-support.js +0 -585
- package/templates/apps/tools/caws/legacy-assessment.ts +0 -408
- package/templates/apps/tools/caws/legacy-assessor.js +0 -764
- package/templates/apps/tools/caws/mutant-analyzer.js +0 -734
- package/templates/apps/tools/caws/perf-budgets.ts +0 -349
- package/templates/apps/tools/caws/prompt-lint.js.backup +0 -274
- package/templates/apps/tools/caws/property-testing.js +0 -707
- package/templates/apps/tools/caws/provenance.d.ts +0 -14
- package/templates/apps/tools/caws/provenance.d.ts.map +0 -1
- package/templates/apps/tools/caws/provenance.js +0 -132
- package/templates/apps/tools/caws/provenance.js.backup +0 -73
- package/templates/apps/tools/caws/provenance.ts +0 -211
- package/templates/apps/tools/caws/security-provenance.ts +0 -483
- package/templates/apps/tools/caws/shared/base-tool.ts +0 -281
- package/templates/apps/tools/caws/shared/config-manager.ts +0 -366
- package/templates/apps/tools/caws/shared/gate-checker.ts +0 -849
- package/templates/apps/tools/caws/shared/types.ts +0 -444
- package/templates/apps/tools/caws/shared/validator.ts +0 -305
- package/templates/apps/tools/caws/shared/waivers-manager.ts +0 -174
- package/templates/apps/tools/caws/spec-test-mapper.ts +0 -391
- package/templates/apps/tools/caws/test-quality.js +0 -578
- package/templates/apps/tools/caws/validate.js +0 -76
- package/templates/apps/tools/caws/validate.ts +0 -228
- package/templates/apps/tools/caws/waivers.js +0 -344
- /package/{templates/apps/tools/caws → dist/templates/.caws}/schemas/waivers.schema.json +0 -0
- /package/{templates/apps/tools/caws → dist/templates/.caws}/schemas/working-spec.schema.json +0 -0
- /package/{templates/apps/tools/caws → dist/templates/.caws}/templates/working-spec.template.yml +0 -0
- /package/{templates/apps/tools/caws → dist/templates/.caws/tools}/scope-guard.js +0 -0
- /package/{templates/apps/tools/caws → dist/templates/.caws}/tools-allow.json +0 -0
- /package/{templates/apps/tools/caws → dist/templates/.caws}/waivers.yml +0 -0
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Spec-to-Test Mapper
|
|
5
|
-
*
|
|
6
|
-
* Links acceptance criteria from working-spec.yaml to actual test cases
|
|
7
|
-
* for full traceability and coverage reporting.
|
|
8
|
-
*
|
|
9
|
-
* @author @darianrosebrook
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import * as fs from 'fs';
|
|
13
|
-
import * as path from 'path';
|
|
14
|
-
import * as yaml from 'js-yaml';
|
|
15
|
-
|
|
16
|
-
interface AcceptanceCriterion {
|
|
17
|
-
id: string;
|
|
18
|
-
given: string;
|
|
19
|
-
when: string;
|
|
20
|
-
then: string;
|
|
21
|
-
test_file?: string;
|
|
22
|
-
test_name?: string;
|
|
23
|
-
type?: 'unit' | 'integration' | 'e2e' | 'property-based';
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface WorkingSpec {
|
|
27
|
-
id: string;
|
|
28
|
-
title: string;
|
|
29
|
-
acceptance: AcceptanceCriterion[];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface TestMapping {
|
|
33
|
-
criterionId: string;
|
|
34
|
-
criterion: AcceptanceCriterion;
|
|
35
|
-
tests: TestReference[];
|
|
36
|
-
covered: boolean;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface TestReference {
|
|
40
|
-
file: string;
|
|
41
|
-
testName: string;
|
|
42
|
-
lineNumber?: number;
|
|
43
|
-
type: 'unit' | 'integration' | 'e2e' | 'property-based';
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface CoverageReport {
|
|
47
|
-
totalCriteria: number;
|
|
48
|
-
coveredCriteria: number;
|
|
49
|
-
coveragePercentage: number;
|
|
50
|
-
mappings: TestMapping[];
|
|
51
|
-
uncoveredCriteria: string[];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export class SpecTestMapper {
|
|
55
|
-
private workingSpecPath: string;
|
|
56
|
-
private testDir: string;
|
|
57
|
-
|
|
58
|
-
constructor(workingSpecPath: string = '.caws/working-spec.yaml', testDir: string = 'tests') {
|
|
59
|
-
this.workingSpecPath = workingSpecPath;
|
|
60
|
-
this.testDir = testDir;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Load working spec and parse acceptance criteria
|
|
65
|
-
*/
|
|
66
|
-
private loadWorkingSpec(): WorkingSpec {
|
|
67
|
-
if (!fs.existsSync(this.workingSpecPath)) {
|
|
68
|
-
throw new Error(`Working spec not found: ${this.workingSpecPath}`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const content = fs.readFileSync(this.workingSpecPath, 'utf-8');
|
|
72
|
-
const spec = yaml.load(content) as WorkingSpec;
|
|
73
|
-
|
|
74
|
-
if (!spec.acceptance || spec.acceptance.length === 0) {
|
|
75
|
-
throw new Error('No acceptance criteria found in working spec');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return spec;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Find all test files
|
|
83
|
-
*/
|
|
84
|
-
private findTestFiles(): string[] {
|
|
85
|
-
const files: string[] = [];
|
|
86
|
-
|
|
87
|
-
const walkDir = (dir: string): void => {
|
|
88
|
-
if (!fs.existsSync(dir)) return;
|
|
89
|
-
|
|
90
|
-
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
91
|
-
|
|
92
|
-
for (const item of items) {
|
|
93
|
-
const fullPath = path.join(dir, item.name);
|
|
94
|
-
|
|
95
|
-
if (item.isDirectory() && item.name !== 'node_modules') {
|
|
96
|
-
walkDir(fullPath);
|
|
97
|
-
} else if (
|
|
98
|
-
item.isFile() &&
|
|
99
|
-
(item.name.endsWith('.test.ts') ||
|
|
100
|
-
item.name.endsWith('.test.js') ||
|
|
101
|
-
item.name.endsWith('.spec.ts') ||
|
|
102
|
-
item.name.endsWith('.spec.js'))
|
|
103
|
-
) {
|
|
104
|
-
files.push(fullPath);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
walkDir(this.testDir);
|
|
110
|
-
return files;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Search for tests related to an acceptance criterion
|
|
115
|
-
*/
|
|
116
|
-
private findRelatedTests(criterion: AcceptanceCriterion): TestReference[] {
|
|
117
|
-
const testFiles = this.findTestFiles();
|
|
118
|
-
const tests: TestReference[] = [];
|
|
119
|
-
|
|
120
|
-
// Build search terms from the criterion
|
|
121
|
-
const searchTerms = [
|
|
122
|
-
criterion.id,
|
|
123
|
-
...this.extractKeywords(criterion.given),
|
|
124
|
-
...this.extractKeywords(criterion.when),
|
|
125
|
-
...this.extractKeywords(criterion.then),
|
|
126
|
-
];
|
|
127
|
-
|
|
128
|
-
for (const file of testFiles) {
|
|
129
|
-
const content = fs.readFileSync(file, 'utf-8');
|
|
130
|
-
|
|
131
|
-
// Check if criterion ID is explicitly referenced
|
|
132
|
-
if (content.includes(criterion.id) || content.includes(`[${criterion.id}]`)) {
|
|
133
|
-
const testName = this.extractTestName(content, criterion.id);
|
|
134
|
-
tests.push({
|
|
135
|
-
file: path.relative(process.cwd(), file),
|
|
136
|
-
testName: testName || `Tests for ${criterion.id}`,
|
|
137
|
-
type: this.classifyTest(file),
|
|
138
|
-
});
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Check for keyword matches
|
|
143
|
-
const keywordMatches = searchTerms.filter((term) =>
|
|
144
|
-
content.toLowerCase().includes(term.toLowerCase())
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
if (keywordMatches.length >= 3) {
|
|
148
|
-
// Threshold: at least 3 keyword matches
|
|
149
|
-
const testName = this.extractTestName(content) || this.extractDescribeName(content);
|
|
150
|
-
if (testName) {
|
|
151
|
-
tests.push({
|
|
152
|
-
file: path.relative(process.cwd(), file),
|
|
153
|
-
testName,
|
|
154
|
-
type: this.classifyTest(file),
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return tests;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Extract keywords from a text string
|
|
165
|
-
*/
|
|
166
|
-
private extractKeywords(text: string): string[] {
|
|
167
|
-
return text
|
|
168
|
-
.toLowerCase()
|
|
169
|
-
.replace(/[^\w\s]/g, ' ')
|
|
170
|
-
.split(/\s+/)
|
|
171
|
-
.filter((word) => word.length > 4) // Only meaningful words
|
|
172
|
-
.filter((word) => !['given', 'when', 'then', 'should', 'with'].includes(word));
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Extract test name from content
|
|
177
|
-
*/
|
|
178
|
-
private extractTestName(content: string, criterionId?: string): string | null {
|
|
179
|
-
const lines = content.split('\n');
|
|
180
|
-
|
|
181
|
-
for (const line of lines) {
|
|
182
|
-
if (criterionId && line.includes(criterionId)) {
|
|
183
|
-
const match = line.match(/it\s*\(\s*["'](.+?)["']/);
|
|
184
|
-
if (match) return match[1];
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const itMatch = line.match(/it\s*\(\s*["'](.+?)["']/);
|
|
188
|
-
if (itMatch) return itMatch[1];
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Extract describe block name
|
|
196
|
-
*/
|
|
197
|
-
private extractDescribeName(content: string): string | null {
|
|
198
|
-
const match = content.match(/describe\s*\(\s*["'](.+?)["']/);
|
|
199
|
-
return match ? match[1] : null;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Classify test type based on file path
|
|
204
|
-
*/
|
|
205
|
-
private classifyTest(filePath: string): 'unit' | 'integration' | 'e2e' | 'property-based' {
|
|
206
|
-
if (filePath.includes('e2e')) return 'e2e';
|
|
207
|
-
if (filePath.includes('integration')) return 'integration';
|
|
208
|
-
if (filePath.includes('property')) return 'property-based';
|
|
209
|
-
return 'unit';
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Generate coverage report
|
|
214
|
-
*/
|
|
215
|
-
async generateCoverageReport(): Promise<CoverageReport> {
|
|
216
|
-
const spec = this.loadWorkingSpec();
|
|
217
|
-
const mappings: TestMapping[] = [];
|
|
218
|
-
|
|
219
|
-
console.log(`\n🔍 Analyzing ${spec.acceptance.length} acceptance criteria...\n`);
|
|
220
|
-
|
|
221
|
-
for (const criterion of spec.acceptance) {
|
|
222
|
-
const tests = this.findRelatedTests(criterion);
|
|
223
|
-
const covered = tests.length > 0;
|
|
224
|
-
|
|
225
|
-
mappings.push({
|
|
226
|
-
criterionId: criterion.id,
|
|
227
|
-
criterion,
|
|
228
|
-
tests,
|
|
229
|
-
covered,
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
if (covered) {
|
|
233
|
-
console.log(`✅ ${criterion.id}: ${tests.length} related test(s) found`);
|
|
234
|
-
} else {
|
|
235
|
-
console.log(`❌ ${criterion.id}: No related tests found`);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const coveredCount = mappings.filter((m) => m.covered).length;
|
|
240
|
-
const coveragePercentage = (coveredCount / spec.acceptance.length) * 100;
|
|
241
|
-
const uncoveredCriteria = mappings.filter((m) => !m.covered).map((m) => m.criterionId);
|
|
242
|
-
|
|
243
|
-
return {
|
|
244
|
-
totalCriteria: spec.acceptance.length,
|
|
245
|
-
coveredCriteria: coveredCount,
|
|
246
|
-
coveragePercentage,
|
|
247
|
-
mappings,
|
|
248
|
-
uncoveredCriteria,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Generate markdown report
|
|
254
|
-
*/
|
|
255
|
-
generateMarkdownReport(report: CoverageReport): string {
|
|
256
|
-
let md = `# Acceptance Criteria Coverage Report\n\n`;
|
|
257
|
-
md += `**Coverage**: ${report.coveredCriteria}/${
|
|
258
|
-
report.totalCriteria
|
|
259
|
-
} (${report.coveragePercentage.toFixed(1)}%)\n\n`;
|
|
260
|
-
|
|
261
|
-
md += `## Coverage Summary\n\n`;
|
|
262
|
-
md += `| Criterion ID | Status | Related Tests |\n`;
|
|
263
|
-
md += `|--------------|--------|---------------|\n`;
|
|
264
|
-
|
|
265
|
-
for (const mapping of report.mappings) {
|
|
266
|
-
const status = mapping.covered ? '✅ Covered' : '❌ Not Covered';
|
|
267
|
-
const testCount = mapping.tests.length;
|
|
268
|
-
md += `| ${mapping.criterionId} | ${status} | ${testCount} |\n`;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
md += `\n## Detailed Mappings\n\n`;
|
|
272
|
-
|
|
273
|
-
for (const mapping of report.mappings) {
|
|
274
|
-
md += `### ${mapping.criterionId}\n\n`;
|
|
275
|
-
md += `**Given**: ${mapping.criterion.given}\n`;
|
|
276
|
-
md += `**When**: ${mapping.criterion.when}\n`;
|
|
277
|
-
md += `**Then**: ${mapping.criterion.then}\n\n`;
|
|
278
|
-
|
|
279
|
-
if (mapping.tests.length > 0) {
|
|
280
|
-
md += `**Related Tests**:\n\n`;
|
|
281
|
-
for (const test of mapping.tests) {
|
|
282
|
-
md += `- [${test.type}] \`${test.file}\`\n`;
|
|
283
|
-
md += ` - ${test.testName}\n`;
|
|
284
|
-
}
|
|
285
|
-
} else {
|
|
286
|
-
md += `⚠️ **No related tests found**\n\n`;
|
|
287
|
-
md += `**Recommendation**: Add test for this acceptance criterion.\n`;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
md += `\n---\n\n`;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (report.uncoveredCriteria.length > 0) {
|
|
294
|
-
md += `## Uncovered Criteria\n\n`;
|
|
295
|
-
md += `The following acceptance criteria have no related tests:\n\n`;
|
|
296
|
-
for (const id of report.uncoveredCriteria) {
|
|
297
|
-
md += `- ${id}\n`;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
return md;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Save report to file
|
|
306
|
-
*/
|
|
307
|
-
async saveReport(outputPath: string): Promise<void> {
|
|
308
|
-
const report = await this.generateCoverageReport();
|
|
309
|
-
const markdown = this.generateMarkdownReport(report);
|
|
310
|
-
|
|
311
|
-
fs.writeFileSync(outputPath, markdown, 'utf-8');
|
|
312
|
-
console.log(`\n📄 Report saved to: ${outputPath}`);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Display console report
|
|
317
|
-
*/
|
|
318
|
-
async displayReport(): Promise<void> {
|
|
319
|
-
const report = await this.generateCoverageReport();
|
|
320
|
-
|
|
321
|
-
console.log(`\n${'═'.repeat(80)}`);
|
|
322
|
-
console.log(` ACCEPTANCE CRITERIA COVERAGE REPORT`);
|
|
323
|
-
console.log(`${'═'.repeat(80)}\n`);
|
|
324
|
-
|
|
325
|
-
console.log(
|
|
326
|
-
`📊 Coverage: ${report.coveredCriteria}/${
|
|
327
|
-
report.totalCriteria
|
|
328
|
-
} (${report.coveragePercentage.toFixed(1)}%)\n`
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
if (report.uncoveredCriteria.length > 0) {
|
|
332
|
-
console.log(`❌ Uncovered Criteria (${report.uncoveredCriteria.length}):`);
|
|
333
|
-
for (const id of report.uncoveredCriteria) {
|
|
334
|
-
const mapping = report.mappings.find((m) => m.criterionId === id);
|
|
335
|
-
if (mapping) {
|
|
336
|
-
console.log(`\n ${id}:`);
|
|
337
|
-
console.log(` Given: ${mapping.criterion.given}`);
|
|
338
|
-
console.log(` When: ${mapping.criterion.when}`);
|
|
339
|
-
console.log(` Then: ${mapping.criterion.then}`);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
console.log();
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (report.coveredCriteria > 0) {
|
|
346
|
-
console.log(`✅ Covered Criteria (${report.coveredCriteria}):`);
|
|
347
|
-
for (const mapping of report.mappings.filter((m) => m.covered)) {
|
|
348
|
-
console.log(`\n ${mapping.criterionId}: ${mapping.tests.length} test(s)`);
|
|
349
|
-
for (const test of mapping.tests) {
|
|
350
|
-
console.log(` - [${test.type}] ${test.testName}`);
|
|
351
|
-
console.log(` 📁 ${test.file}`);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
console.log(`\n${'═'.repeat(80)}\n`);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// CLI interface
|
|
361
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
362
|
-
(async () => {
|
|
363
|
-
const command = process.argv[2];
|
|
364
|
-
const mapper = new SpecTestMapper();
|
|
365
|
-
|
|
366
|
-
switch (command) {
|
|
367
|
-
case 'report':
|
|
368
|
-
await mapper.displayReport();
|
|
369
|
-
break;
|
|
370
|
-
|
|
371
|
-
case 'save':
|
|
372
|
-
const outputPath = process.argv[3] || '.caws/spec-coverage-report.md';
|
|
373
|
-
await mapper.saveReport(outputPath);
|
|
374
|
-
break;
|
|
375
|
-
|
|
376
|
-
default:
|
|
377
|
-
console.log(`
|
|
378
|
-
Usage: spec-test-mapper <command> [options]
|
|
379
|
-
|
|
380
|
-
Commands:
|
|
381
|
-
report Display coverage report in console
|
|
382
|
-
save [output-path] Save coverage report to file (default: .caws/spec-coverage-report.md)
|
|
383
|
-
|
|
384
|
-
Examples:
|
|
385
|
-
npx tsx apps/tools/caws/spec-test-mapper.ts report
|
|
386
|
-
npx tsx apps/tools/caws/spec-test-mapper.ts save docs/spec-coverage.md
|
|
387
|
-
`);
|
|
388
|
-
process.exit(1);
|
|
389
|
-
}
|
|
390
|
-
})();
|
|
391
|
-
}
|