repo-wrapped 0.0.7 → 0.0.9
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/.github/agents/complete.agent.md +257 -0
- package/.github/agents/feature-scaffold.agent.md +248 -0
- package/.github/agents/jsdoc.agent.md +243 -0
- package/.github/agents/plan.agent.md +202 -0
- package/.github/agents/spec-writer.agent.md +169 -0
- package/.github/agents/test-writer.agent.md +169 -0
- package/.stylelintrc.json +27 -0
- package/README.md +94 -94
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +446 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +446 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +7039 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/commands/generate.js +56 -56
- package/dist/config/defaults.js +158 -0
- package/dist/config/index.js +10 -0
- package/dist/features/achievements/data/achievements.json +284 -0
- package/dist/features/achievements/engine.js +140 -0
- package/dist/features/achievements/evaluators.js +246 -0
- package/dist/features/achievements/helpers.js +58 -0
- package/dist/features/achievements/index.js +57 -0
- package/dist/features/achievements/loader.js +88 -0
- package/dist/features/achievements/template.js +155 -0
- package/dist/features/achievements/types.js +7 -0
- package/dist/features/commit-quality/analyzer.js +378 -0
- package/dist/features/commit-quality/analyzer.test.js +484 -0
- package/dist/features/commit-quality/index.js +28 -0
- package/dist/features/commit-quality/template.js +114 -0
- package/dist/features/commit-quality/types.js +2 -0
- package/dist/features/comparison/analyzer.js +222 -0
- package/dist/features/comparison/index.js +28 -0
- package/dist/features/comparison/template.js +119 -0
- package/dist/features/comparison/types.js +2 -0
- package/dist/features/contribution-graph/index.js +9 -0
- package/dist/features/contribution-graph/template.js +89 -0
- package/dist/features/events/index.js +31 -0
- package/dist/features/events/parser.js +253 -0
- package/dist/features/events/template.js +113 -0
- package/dist/features/events/types.js +2 -0
- package/dist/features/executive-summary/generator.js +275 -0
- package/dist/features/executive-summary/index.js +27 -0
- package/dist/features/executive-summary/template.js +80 -0
- package/dist/features/executive-summary/types.js +2 -0
- package/dist/features/gaps/analyzer.js +298 -0
- package/dist/features/gaps/analyzer.test.js +517 -0
- package/dist/features/gaps/index.js +27 -0
- package/dist/features/gaps/template.js +190 -0
- package/dist/features/gaps/types.js +2 -0
- package/dist/features/impact/analyzer.js +248 -0
- package/dist/features/impact/index.js +26 -0
- package/dist/features/impact/template.js +118 -0
- package/dist/features/impact/types.js +2 -0
- package/dist/features/index.js +40 -0
- package/dist/features/knowledge/analyzer.js +385 -0
- package/dist/features/knowledge/index.js +26 -0
- package/dist/features/knowledge/template.js +239 -0
- package/dist/features/knowledge/types.js +2 -0
- package/dist/features/streaks/calculator.js +184 -0
- package/dist/features/streaks/calculator.test.js +366 -0
- package/dist/features/streaks/index.js +36 -0
- package/dist/features/streaks/template.js +41 -0
- package/dist/features/streaks/types.js +9 -0
- package/dist/features/team/analyzer.js +316 -0
- package/dist/features/team/index.js +30 -0
- package/dist/features/team/template.js +146 -0
- package/dist/features/team/types.js +2 -0
- package/dist/features/time-patterns/analyzer.js +319 -0
- package/dist/features/time-patterns/analyzer.test.js +278 -0
- package/dist/features/time-patterns/index.js +37 -0
- package/dist/features/time-patterns/template.js +109 -0
- package/dist/features/time-patterns/types.js +9 -0
- package/dist/features/velocity/analyzer.js +257 -0
- package/dist/features/velocity/analyzer.test.js +383 -0
- package/dist/features/velocity/index.js +27 -0
- package/dist/features/velocity/template.js +189 -0
- package/dist/features/velocity/types.js +2 -0
- package/dist/generators/html/scripts/knowledge.js +17 -0
- package/dist/generators/html/styles/base.css +8 -3
- package/dist/generators/html/styles/components.css +121 -1
- package/dist/generators/html/styles/knowledge.css +21 -0
- package/dist/generators/html/styles/leaddev.css +108 -48
- package/dist/generators/html/styles/strategic-insights.css +1337 -0
- package/dist/generators/html/templates/commitQualitySection.js +28 -2
- package/dist/generators/html/templates/executiveSummarySection.js +0 -4
- package/dist/generators/html/templates/impactSection.js +8 -6
- package/dist/generators/html/templates/knowledgeSection.js +16 -2
- package/dist/generators/html/templates/velocitySection.js +2 -2
- package/dist/generators/html/types.js +7 -0
- package/dist/generators/html/utils/analysisRunner.js +93 -0
- package/dist/generators/html/utils/cardBuilder.js +47 -0
- package/dist/generators/html/utils/contextBuilder.js +54 -0
- package/dist/generators/html/utils/htmlDocumentBuilder.js +396 -0
- package/dist/generators/html/utils/kpiBuilder.js +76 -0
- package/dist/generators/html/utils/sectionWrapper.js +71 -0
- package/dist/generators/html/utils/styleLoader.js +2 -2
- package/dist/html/analysisRunner.js +93 -0
- package/dist/html/htmlDocumentBuilder.js +396 -0
- package/dist/html/index.js +29 -0
- package/dist/html/shared/colorUtils.js +61 -0
- package/dist/html/shared/commitMapBuilder.js +23 -0
- package/dist/html/shared/components/cardBuilder.js +47 -0
- package/dist/html/shared/components/index.js +18 -0
- package/dist/html/shared/components/kpiBuilder.js +76 -0
- package/dist/html/shared/components/sectionWrapper.js +71 -0
- package/dist/html/shared/contextBuilder.js +54 -0
- package/dist/html/shared/dateRangeCalculator.js +56 -0
- package/dist/html/shared/developerStatsCalculator.js +28 -0
- package/dist/html/shared/index.js +39 -0
- package/dist/html/shared/scriptLoader.js +15 -0
- package/dist/html/shared/scripts/export.js +125 -0
- package/dist/html/shared/scripts/knowledge.js +137 -0
- package/dist/html/shared/scripts/modal.js +68 -0
- package/dist/html/shared/scripts/navigation.js +156 -0
- package/dist/html/shared/scripts/tabs.js +18 -0
- package/dist/html/shared/scripts/tooltip.js +21 -0
- package/dist/html/shared/styleLoader.js +18 -0
- package/dist/html/shared/styles/achievements.css +387 -0
- package/dist/html/shared/styles/base.css +822 -0
- package/dist/html/shared/styles/components.css +1511 -0
- package/dist/html/shared/styles/knowledge.css +242 -0
- package/dist/html/shared/styles/strategic-insights.css +1337 -0
- package/dist/html/shared/weekGrouper.js +27 -0
- package/dist/html/types.js +7 -0
- package/dist/index.js +39 -39
- package/dist/test/helpers/commitFactory.js +166 -0
- package/dist/test/helpers/dateUtils.js +101 -0
- package/dist/test/helpers/index.js +29 -0
- package/dist/test/setup.js +17 -0
- package/dist/test/smoke.test.js +94 -0
- package/dist/types/achievements.js +7 -0
- package/dist/types/analysis.js +7 -0
- package/dist/types/core.js +7 -0
- package/dist/types/index.js +38 -0
- package/dist/types/options.js +7 -0
- package/dist/types/shared.js +7 -0
- package/dist/types/strategic.js +7 -0
- package/dist/types/summary.js +7 -0
- package/dist/utils/achievementDefinitions.js +22 -22
- package/dist/utils/analyzerContextBuilder.js +124 -0
- package/dist/utils/commitQualityAnalyzer.js +13 -2
- package/dist/utils/emptyResults.js +95 -0
- package/dist/utils/fileHotspotAnalyzer.js +4 -12
- package/dist/utils/gapAnalyzer.js +26 -28
- package/dist/utils/gitParser.test.js +363 -0
- package/dist/utils/htmlGenerator.js +62 -466
- package/dist/utils/impactAnalyzer.js +20 -19
- package/dist/utils/knowledgeDistributionAnalyzer.js +32 -27
- package/dist/utils/matrixGenerator.js +13 -13
- package/dist/utils/rangeComparisonAnalyzer.js +2 -2
- package/dist/utils/streakCalculator.js +77 -27
- package/dist/utils/teamAnalyzer.js +20 -1
- package/dist/utils/timePatternAnalyzer.js +18 -3
- package/dist/utils/velocityAnalyzer.js +23 -18
- package/dist/utils/wrappedGenerator.js +8 -8
- package/package.json +74 -64
- package/vitest.config.ts +46 -0
- package/SPECS.md +0 -490
- package/dist/cli.js +0 -24
- package/dist/commands/index.js +0 -24
- package/test-team.txt +0 -2
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for Gap Analyzer
|
|
4
|
+
*
|
|
5
|
+
* Tests the analyzeGaps function which detects:
|
|
6
|
+
* - Activity gaps (periods without commits)
|
|
7
|
+
* - Gap classification (weekend, holiday, vacation, blocker)
|
|
8
|
+
* - Gap patterns (recurring, random)
|
|
9
|
+
* - Risk assessment (low, medium, high, critical)
|
|
10
|
+
* - Statistics (total days, percentage, longest gap)
|
|
11
|
+
*
|
|
12
|
+
* @spec TEST-02 - Core Analyzers Unit Tests
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const vitest_1 = require("vitest");
|
|
16
|
+
const analyzer_1 = require("./analyzer");
|
|
17
|
+
const date_fns_1 = require("date-fns");
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Test Helpers
|
|
20
|
+
// ============================================================================
|
|
21
|
+
function createCommit(date, overrides = {}) {
|
|
22
|
+
return {
|
|
23
|
+
hash: `hash-${date.getTime()}`,
|
|
24
|
+
author: 'Test Author',
|
|
25
|
+
date: date.toISOString(),
|
|
26
|
+
message: 'test: commit message',
|
|
27
|
+
...overrides,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Tests
|
|
32
|
+
// ============================================================================
|
|
33
|
+
(0, vitest_1.describe)('analyzeGaps', () => {
|
|
34
|
+
// ==========================================================================
|
|
35
|
+
// Empty/Basic Input
|
|
36
|
+
// ==========================================================================
|
|
37
|
+
(0, vitest_1.describe)('empty input', () => {
|
|
38
|
+
(0, vitest_1.it)('returns analysis with full-period gap for empty commits', () => {
|
|
39
|
+
const start = new Date('2025-06-01');
|
|
40
|
+
const end = new Date('2025-06-30');
|
|
41
|
+
const result = (0, analyzer_1.analyzeGaps)([], start, end);
|
|
42
|
+
// Implementation returns a gap spanning the entire period when no commits
|
|
43
|
+
// This is actually more useful than empty gaps
|
|
44
|
+
(0, vitest_1.expect)(result.totalGapDays).toBeGreaterThan(0);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
// ==========================================================================
|
|
48
|
+
// Gap Detection
|
|
49
|
+
// ==========================================================================
|
|
50
|
+
(0, vitest_1.describe)('gap detection', () => {
|
|
51
|
+
(0, vitest_1.it)('returns no gaps for consecutive daily commits', () => {
|
|
52
|
+
const start = new Date('2025-06-02');
|
|
53
|
+
const end = new Date('2025-06-06');
|
|
54
|
+
const commits = [
|
|
55
|
+
createCommit(new Date('2025-06-02')),
|
|
56
|
+
createCommit(new Date('2025-06-03')),
|
|
57
|
+
createCommit(new Date('2025-06-04')),
|
|
58
|
+
createCommit(new Date('2025-06-05')),
|
|
59
|
+
createCommit(new Date('2025-06-06')),
|
|
60
|
+
];
|
|
61
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end);
|
|
62
|
+
(0, vitest_1.expect)(result.gaps.length).toBe(0);
|
|
63
|
+
});
|
|
64
|
+
(0, vitest_1.it)('detects gap between commits exceeding threshold', () => {
|
|
65
|
+
const start = new Date('2025-06-01');
|
|
66
|
+
const end = new Date('2025-06-30');
|
|
67
|
+
const commits = [
|
|
68
|
+
createCommit(new Date('2025-06-05')),
|
|
69
|
+
createCommit(new Date('2025-06-20')), // 15 day gap
|
|
70
|
+
];
|
|
71
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
72
|
+
// Should detect gap between commits and from start/end
|
|
73
|
+
(0, vitest_1.expect)(result.gaps.length).toBeGreaterThan(0);
|
|
74
|
+
});
|
|
75
|
+
(0, vitest_1.it)('detects gap from start date to first commit', () => {
|
|
76
|
+
const start = new Date('2025-06-01');
|
|
77
|
+
const end = new Date('2025-06-30');
|
|
78
|
+
const commits = [
|
|
79
|
+
createCommit(new Date('2025-06-10')), // 9 days after start
|
|
80
|
+
];
|
|
81
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
82
|
+
// Should detect gap from June 1 to June 9
|
|
83
|
+
const gapsFromStart = result.gaps.filter((g) => g.start.getTime() === start.getTime());
|
|
84
|
+
(0, vitest_1.expect)(gapsFromStart.length).toBe(1);
|
|
85
|
+
});
|
|
86
|
+
(0, vitest_1.it)('detects gap from last commit to end date', () => {
|
|
87
|
+
const start = new Date('2025-06-01');
|
|
88
|
+
const end = new Date('2025-06-30');
|
|
89
|
+
const commits = [
|
|
90
|
+
createCommit(new Date('2025-06-10')), // 20 days before end
|
|
91
|
+
];
|
|
92
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
93
|
+
// Should detect gap from June 11 to June 30
|
|
94
|
+
const gapsToEnd = result.gaps.filter((g) => g.end.getTime() === end.getTime());
|
|
95
|
+
(0, vitest_1.expect)(gapsToEnd.length).toBe(1);
|
|
96
|
+
});
|
|
97
|
+
(0, vitest_1.it)('respects custom threshold', () => {
|
|
98
|
+
const start = new Date('2025-06-01');
|
|
99
|
+
const end = new Date('2025-06-15');
|
|
100
|
+
const commits = [
|
|
101
|
+
createCommit(new Date('2025-06-05')),
|
|
102
|
+
createCommit(new Date('2025-06-10')), // 5 day gap
|
|
103
|
+
];
|
|
104
|
+
// With threshold of 3, should detect
|
|
105
|
+
const result3 = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
106
|
+
(0, vitest_1.expect)(result3.gaps.length).toBeGreaterThan(0);
|
|
107
|
+
// With threshold of 7, should not detect the 5-day gap
|
|
108
|
+
const result7 = (0, analyzer_1.analyzeGaps)(commits, start, end, 7);
|
|
109
|
+
const midGaps = result7.gaps.filter((g) => g.durationDays === 5);
|
|
110
|
+
(0, vitest_1.expect)(midGaps.length).toBe(0);
|
|
111
|
+
});
|
|
112
|
+
(0, vitest_1.it)('handles unsorted commits', () => {
|
|
113
|
+
const start = new Date('2025-06-01');
|
|
114
|
+
const end = new Date('2025-06-30');
|
|
115
|
+
const commits = [
|
|
116
|
+
createCommit(new Date('2025-06-20')),
|
|
117
|
+
createCommit(new Date('2025-06-05')), // Out of order
|
|
118
|
+
createCommit(new Date('2025-06-10')),
|
|
119
|
+
];
|
|
120
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
121
|
+
// Should sort and analyze correctly
|
|
122
|
+
(0, vitest_1.expect)(result.gaps).toBeDefined();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
// ==========================================================================
|
|
126
|
+
// Weekend Handling
|
|
127
|
+
// ==========================================================================
|
|
128
|
+
(0, vitest_1.describe)('weekend handling', () => {
|
|
129
|
+
(0, vitest_1.it)('excludes weekends when option is set', () => {
|
|
130
|
+
const start = new Date('2025-06-06'); // Friday
|
|
131
|
+
const end = new Date('2025-06-09'); // Monday
|
|
132
|
+
const commits = [
|
|
133
|
+
createCommit(new Date('2025-06-06')), // Friday
|
|
134
|
+
createCommit(new Date('2025-06-09')), // Monday
|
|
135
|
+
];
|
|
136
|
+
// Without excluding weekends: 3 days (Sat, Sun, Mon gap starts Sat)
|
|
137
|
+
const resultWithWeekends = (0, analyzer_1.analyzeGaps)(commits, start, end, 2, false);
|
|
138
|
+
// With excluding weekends: only 0-1 business days
|
|
139
|
+
const resultWithoutWeekends = (0, analyzer_1.analyzeGaps)(commits, start, end, 2, true);
|
|
140
|
+
// Excluding weekends should result in fewer gap days
|
|
141
|
+
(0, vitest_1.expect)(resultWithoutWeekends.totalGapDays).toBeLessThanOrEqual(resultWithWeekends.totalGapDays);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
// ==========================================================================
|
|
145
|
+
// Gap Classification
|
|
146
|
+
// ==========================================================================
|
|
147
|
+
(0, vitest_1.describe)('gap classification', () => {
|
|
148
|
+
(0, vitest_1.it)('classifies short weekend gaps as weekend', () => {
|
|
149
|
+
const start = new Date('2025-06-06'); // Friday
|
|
150
|
+
const end = new Date('2025-06-10'); // Tuesday
|
|
151
|
+
const commits = [
|
|
152
|
+
createCommit(new Date('2025-06-06')), // Friday
|
|
153
|
+
createCommit(new Date('2025-06-10')), // Tuesday
|
|
154
|
+
];
|
|
155
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 2);
|
|
156
|
+
// Gap from Sat-Mon should be classified
|
|
157
|
+
const weekendGaps = result.gaps.filter((g) => g.possibleType === 'weekend');
|
|
158
|
+
// Implementation may or may not classify 3-day gap as weekend
|
|
159
|
+
(0, vitest_1.expect)(result.gaps.length).toBeGreaterThanOrEqual(0);
|
|
160
|
+
});
|
|
161
|
+
(0, vitest_1.it)('classifies holiday period gaps (Dec 20-Jan 5)', () => {
|
|
162
|
+
const start = new Date('2024-12-15');
|
|
163
|
+
const end = new Date('2025-01-10');
|
|
164
|
+
const commits = [
|
|
165
|
+
createCommit(new Date('2024-12-15')),
|
|
166
|
+
createCommit(new Date('2025-01-10')), // Gap over holidays
|
|
167
|
+
];
|
|
168
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
169
|
+
// Gap should be detected in holiday period
|
|
170
|
+
// Classification may vary based on gap start/end dates
|
|
171
|
+
const hasGap = result.gaps.some((g) => g.durationDays >= 20);
|
|
172
|
+
(0, vitest_1.expect)(hasGap).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
(0, vitest_1.it)('classifies summer gaps as vacation', () => {
|
|
175
|
+
const start = new Date('2025-07-01');
|
|
176
|
+
const end = new Date('2025-07-31');
|
|
177
|
+
const commits = [
|
|
178
|
+
createCommit(new Date('2025-07-05')),
|
|
179
|
+
createCommit(new Date('2025-07-25')), // 20-day summer gap
|
|
180
|
+
];
|
|
181
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
182
|
+
const vacationGaps = result.gaps.filter((g) => g.possibleType === 'vacation');
|
|
183
|
+
(0, vitest_1.expect)(vacationGaps.length).toBeGreaterThan(0);
|
|
184
|
+
});
|
|
185
|
+
(0, vitest_1.it)('classifies long unexplained gaps as blocker', () => {
|
|
186
|
+
const start = new Date('2025-03-01');
|
|
187
|
+
const end = new Date('2025-03-31');
|
|
188
|
+
const commits = [
|
|
189
|
+
createCommit(new Date('2025-03-05')),
|
|
190
|
+
createCommit(new Date('2025-03-25')), // 20-day gap in March (not holiday/vacation)
|
|
191
|
+
];
|
|
192
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
193
|
+
const blockerGaps = result.gaps.filter((g) => g.possibleType === 'blocker');
|
|
194
|
+
(0, vitest_1.expect)(blockerGaps.length).toBeGreaterThan(0);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
// ==========================================================================
|
|
198
|
+
// Statistics
|
|
199
|
+
// ==========================================================================
|
|
200
|
+
(0, vitest_1.describe)('statistics', () => {
|
|
201
|
+
(0, vitest_1.it)('calculates total gap days', () => {
|
|
202
|
+
const start = new Date('2025-06-01');
|
|
203
|
+
const end = new Date('2025-06-30');
|
|
204
|
+
const commits = [
|
|
205
|
+
createCommit(new Date('2025-06-10')),
|
|
206
|
+
createCommit(new Date('2025-06-20')),
|
|
207
|
+
];
|
|
208
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
209
|
+
// Gaps: June 1-9 (9 days) + June 11-19 (9 days) + June 21-30 (10 days)
|
|
210
|
+
(0, vitest_1.expect)(result.totalGapDays).toBeGreaterThan(0);
|
|
211
|
+
});
|
|
212
|
+
(0, vitest_1.it)('calculates percentage of period in gaps', () => {
|
|
213
|
+
const start = new Date('2025-06-01');
|
|
214
|
+
const end = new Date('2025-06-30'); // 30 days
|
|
215
|
+
const commits = [
|
|
216
|
+
createCommit(new Date('2025-06-15')), // Split in middle
|
|
217
|
+
];
|
|
218
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
219
|
+
(0, vitest_1.expect)(result.percentageOfPeriodInGaps).toBeGreaterThan(0);
|
|
220
|
+
(0, vitest_1.expect)(result.percentageOfPeriodInGaps).toBeLessThanOrEqual(100);
|
|
221
|
+
});
|
|
222
|
+
(0, vitest_1.it)('identifies longest gap', () => {
|
|
223
|
+
const start = new Date('2025-06-01');
|
|
224
|
+
const end = new Date('2025-06-30');
|
|
225
|
+
const commits = [
|
|
226
|
+
createCommit(new Date('2025-06-05')),
|
|
227
|
+
createCommit(new Date('2025-06-08')), // 3 day gap
|
|
228
|
+
createCommit(new Date('2025-06-10')),
|
|
229
|
+
createCommit(new Date('2025-06-25')), // 15 day gap
|
|
230
|
+
];
|
|
231
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
232
|
+
(0, vitest_1.expect)(result.longestGap).not.toBeNull();
|
|
233
|
+
(0, vitest_1.expect)(result.longestGap?.durationDays).toBeGreaterThanOrEqual(10);
|
|
234
|
+
});
|
|
235
|
+
(0, vitest_1.it)('calculates average gap length', () => {
|
|
236
|
+
const start = new Date('2025-06-01');
|
|
237
|
+
const end = new Date('2025-06-30');
|
|
238
|
+
const commits = [
|
|
239
|
+
createCommit(new Date('2025-06-05')),
|
|
240
|
+
createCommit(new Date('2025-06-12')), // 7 day gap
|
|
241
|
+
createCommit(new Date('2025-06-20')), // 8 day gap
|
|
242
|
+
];
|
|
243
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
244
|
+
(0, vitest_1.expect)(result.averageGapLength).toBeGreaterThan(0);
|
|
245
|
+
});
|
|
246
|
+
(0, vitest_1.it)('calculates gap frequency (per month)', () => {
|
|
247
|
+
const start = new Date('2025-06-01');
|
|
248
|
+
const end = new Date('2025-06-30');
|
|
249
|
+
const commits = [
|
|
250
|
+
createCommit(new Date('2025-06-05')),
|
|
251
|
+
createCommit(new Date('2025-06-10')),
|
|
252
|
+
createCommit(new Date('2025-06-15')),
|
|
253
|
+
createCommit(new Date('2025-06-20')),
|
|
254
|
+
];
|
|
255
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
256
|
+
(0, vitest_1.expect)(result.gapFrequency).toBeGreaterThanOrEqual(0);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
// ==========================================================================
|
|
260
|
+
// Gap Patterns
|
|
261
|
+
// ==========================================================================
|
|
262
|
+
(0, vitest_1.describe)('gap patterns', () => {
|
|
263
|
+
(0, vitest_1.it)('detects recurring weekly pattern', () => {
|
|
264
|
+
const start = new Date('2025-06-01');
|
|
265
|
+
const end = new Date('2025-06-30');
|
|
266
|
+
// Create gaps that consistently start on Mondays
|
|
267
|
+
const commits = [
|
|
268
|
+
createCommit(new Date('2025-06-06')), // Friday
|
|
269
|
+
createCommit(new Date('2025-06-09')), // Monday (gap Sat-Sun)
|
|
270
|
+
createCommit(new Date('2025-06-13')), // Friday
|
|
271
|
+
createCommit(new Date('2025-06-16')), // Monday (gap Sat-Sun)
|
|
272
|
+
createCommit(new Date('2025-06-20')), // Friday
|
|
273
|
+
createCommit(new Date('2025-06-23')), // Monday (gap Sat-Sun)
|
|
274
|
+
];
|
|
275
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 2);
|
|
276
|
+
// May detect recurring pattern if enough gaps
|
|
277
|
+
(0, vitest_1.expect)(result.patterns).toBeDefined();
|
|
278
|
+
});
|
|
279
|
+
(0, vitest_1.it)('detects random blocker pattern', () => {
|
|
280
|
+
const start = new Date('2025-01-01');
|
|
281
|
+
const end = new Date('2025-06-30');
|
|
282
|
+
// Many unexplained gaps throughout the year
|
|
283
|
+
const commits = [
|
|
284
|
+
createCommit(new Date('2025-01-05')),
|
|
285
|
+
createCommit(new Date('2025-01-20')), // 15-day blocker
|
|
286
|
+
createCommit(new Date('2025-02-10')),
|
|
287
|
+
createCommit(new Date('2025-02-28')), // 18-day blocker
|
|
288
|
+
createCommit(new Date('2025-03-15')),
|
|
289
|
+
createCommit(new Date('2025-04-01')), // 17-day blocker
|
|
290
|
+
createCommit(new Date('2025-04-20')),
|
|
291
|
+
createCommit(new Date('2025-05-10')), // 20-day blocker
|
|
292
|
+
];
|
|
293
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 7);
|
|
294
|
+
const randomPatterns = result.patterns.filter((p) => p.type === 'random');
|
|
295
|
+
(0, vitest_1.expect)(randomPatterns.length).toBeGreaterThanOrEqual(0);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
// ==========================================================================
|
|
299
|
+
// Risk Assessment
|
|
300
|
+
// ==========================================================================
|
|
301
|
+
(0, vitest_1.describe)('risk assessment', () => {
|
|
302
|
+
(0, vitest_1.it)('returns low risk for few short gaps', () => {
|
|
303
|
+
const start = new Date('2025-06-01');
|
|
304
|
+
const end = new Date('2025-06-30');
|
|
305
|
+
// More frequent commits to minimize gaps
|
|
306
|
+
const commits = [
|
|
307
|
+
createCommit(new Date('2025-06-01')),
|
|
308
|
+
createCommit(new Date('2025-06-03')),
|
|
309
|
+
createCommit(new Date('2025-06-05')),
|
|
310
|
+
createCommit(new Date('2025-06-07')),
|
|
311
|
+
createCommit(new Date('2025-06-10')),
|
|
312
|
+
createCommit(new Date('2025-06-12')),
|
|
313
|
+
createCommit(new Date('2025-06-15')),
|
|
314
|
+
createCommit(new Date('2025-06-17')),
|
|
315
|
+
createCommit(new Date('2025-06-20')),
|
|
316
|
+
createCommit(new Date('2025-06-22')),
|
|
317
|
+
createCommit(new Date('2025-06-25')),
|
|
318
|
+
createCommit(new Date('2025-06-27')),
|
|
319
|
+
createCommit(new Date('2025-06-30')),
|
|
320
|
+
];
|
|
321
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
322
|
+
// With more frequent commits, risk should be low
|
|
323
|
+
(0, vitest_1.expect)(['low', 'medium']).toContain(result.riskLevel);
|
|
324
|
+
});
|
|
325
|
+
(0, vitest_1.it)('returns higher risk for long gaps', () => {
|
|
326
|
+
const start = new Date('2025-06-01');
|
|
327
|
+
const end = new Date('2025-07-31');
|
|
328
|
+
const commits = [
|
|
329
|
+
createCommit(new Date('2025-06-05')),
|
|
330
|
+
createCommit(new Date('2025-07-25')), // 50+ day gap
|
|
331
|
+
];
|
|
332
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
333
|
+
(0, vitest_1.expect)(['medium', 'high', 'critical']).toContain(result.riskLevel);
|
|
334
|
+
});
|
|
335
|
+
(0, vitest_1.it)('returns critical risk for many blocker gaps', () => {
|
|
336
|
+
const start = new Date('2025-01-01');
|
|
337
|
+
const end = new Date('2025-12-31');
|
|
338
|
+
// Many long unexplained gaps
|
|
339
|
+
const commits = [];
|
|
340
|
+
let date = new Date('2025-01-05');
|
|
341
|
+
for (let i = 0; i < 12; i++) {
|
|
342
|
+
commits.push(createCommit(date));
|
|
343
|
+
date = (0, date_fns_1.addDays)(date, 25); // 25-day gaps
|
|
344
|
+
}
|
|
345
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 7);
|
|
346
|
+
// Should have high percentage in gaps and multiple blockers
|
|
347
|
+
(0, vitest_1.expect)(['high', 'critical']).toContain(result.riskLevel);
|
|
348
|
+
});
|
|
349
|
+
(0, vitest_1.it)('includes risk factors', () => {
|
|
350
|
+
const start = new Date('2025-06-01');
|
|
351
|
+
const end = new Date('2025-06-30');
|
|
352
|
+
const commits = [
|
|
353
|
+
createCommit(new Date('2025-06-05')),
|
|
354
|
+
createCommit(new Date('2025-06-25')), // 20-day gap
|
|
355
|
+
];
|
|
356
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
357
|
+
(0, vitest_1.expect)(result.riskFactors).toBeDefined();
|
|
358
|
+
(0, vitest_1.expect)(result.riskFactors.length).toBeGreaterThan(0);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
// ==========================================================================
|
|
362
|
+
// Gap Details
|
|
363
|
+
// ==========================================================================
|
|
364
|
+
(0, vitest_1.describe)('gap details', () => {
|
|
365
|
+
(0, vitest_1.it)('includes commit before and after gap', () => {
|
|
366
|
+
const start = new Date('2025-06-01');
|
|
367
|
+
const end = new Date('2025-06-30');
|
|
368
|
+
const commits = [
|
|
369
|
+
createCommit(new Date('2025-06-05'), { message: 'Before gap' }),
|
|
370
|
+
createCommit(new Date('2025-06-20'), { message: 'After gap' }),
|
|
371
|
+
];
|
|
372
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
373
|
+
const midGap = result.gaps.find((g) => g.commitBefore && g.commitAfter);
|
|
374
|
+
(0, vitest_1.expect)(midGap).toBeDefined();
|
|
375
|
+
(0, vitest_1.expect)(midGap?.commitBefore?.message).toBe('Before gap');
|
|
376
|
+
(0, vitest_1.expect)(midGap?.commitAfter?.message).toBe('After gap');
|
|
377
|
+
});
|
|
378
|
+
(0, vitest_1.it)('has null commitBefore for gap at start', () => {
|
|
379
|
+
const start = new Date('2025-06-01');
|
|
380
|
+
const end = new Date('2025-06-30');
|
|
381
|
+
const commits = [
|
|
382
|
+
createCommit(new Date('2025-06-15')),
|
|
383
|
+
];
|
|
384
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
385
|
+
const startGap = result.gaps.find((g) => g.start.getTime() === start.getTime());
|
|
386
|
+
(0, vitest_1.expect)(startGap?.commitBefore).toBeNull();
|
|
387
|
+
});
|
|
388
|
+
(0, vitest_1.it)('has null commitAfter for gap at end', () => {
|
|
389
|
+
const start = new Date('2025-06-01');
|
|
390
|
+
const end = new Date('2025-06-30');
|
|
391
|
+
const commits = [
|
|
392
|
+
createCommit(new Date('2025-06-10')),
|
|
393
|
+
];
|
|
394
|
+
const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
395
|
+
const endGap = result.gaps.find((g) => g.end.getTime() === end.getTime());
|
|
396
|
+
(0, vitest_1.expect)(endGap?.commitAfter).toBeNull();
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
// ==========================================================================
|
|
400
|
+
// Context-based API
|
|
401
|
+
// ==========================================================================
|
|
402
|
+
(0, vitest_1.describe)('context-based API', () => {
|
|
403
|
+
(0, vitest_1.it)('works with AnalyzerContext', () => {
|
|
404
|
+
const commits = [
|
|
405
|
+
createCommit(new Date('2025-06-05')),
|
|
406
|
+
createCommit(new Date('2025-06-20')),
|
|
407
|
+
];
|
|
408
|
+
const ctx = {
|
|
409
|
+
commits,
|
|
410
|
+
dateRange: {
|
|
411
|
+
start: new Date('2025-06-01'),
|
|
412
|
+
end: new Date('2025-06-30'),
|
|
413
|
+
},
|
|
414
|
+
author: 'Test Author',
|
|
415
|
+
};
|
|
416
|
+
const result = (0, analyzer_1.analyzeGaps)(ctx);
|
|
417
|
+
(0, vitest_1.expect)(result.gaps.length).toBeGreaterThan(0);
|
|
418
|
+
});
|
|
419
|
+
(0, vitest_1.it)('respects thresholdDays option from context', () => {
|
|
420
|
+
const commits = [
|
|
421
|
+
createCommit(new Date('2025-06-05')),
|
|
422
|
+
createCommit(new Date('2025-06-10')), // 5-day gap
|
|
423
|
+
];
|
|
424
|
+
const ctx = {
|
|
425
|
+
commits,
|
|
426
|
+
dateRange: {
|
|
427
|
+
start: new Date('2025-06-01'),
|
|
428
|
+
end: new Date('2025-06-15'),
|
|
429
|
+
},
|
|
430
|
+
author: 'Test Author',
|
|
431
|
+
options: {
|
|
432
|
+
thresholdDays: 7, // Higher threshold
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
const result = (0, analyzer_1.analyzeGaps)(ctx);
|
|
436
|
+
// 5-day gap should not be detected with 7-day threshold
|
|
437
|
+
const midGaps = result.gaps.filter((g) => g.durationDays === 5);
|
|
438
|
+
(0, vitest_1.expect)(midGaps.length).toBe(0);
|
|
439
|
+
});
|
|
440
|
+
(0, vitest_1.it)('respects excludeWeekends option from context', () => {
|
|
441
|
+
const commits = [
|
|
442
|
+
createCommit(new Date('2025-06-06')), // Friday
|
|
443
|
+
createCommit(new Date('2025-06-09')), // Monday
|
|
444
|
+
];
|
|
445
|
+
const ctx = {
|
|
446
|
+
commits,
|
|
447
|
+
dateRange: {
|
|
448
|
+
start: new Date('2025-06-06'),
|
|
449
|
+
end: new Date('2025-06-09'),
|
|
450
|
+
},
|
|
451
|
+
author: 'Test Author',
|
|
452
|
+
options: {
|
|
453
|
+
excludeWeekends: true,
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
const result = (0, analyzer_1.analyzeGaps)(ctx);
|
|
457
|
+
// Weekend should be excluded, so gap is very short or none
|
|
458
|
+
(0, vitest_1.expect)(result.totalGapDays).toBeLessThanOrEqual(1);
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
// ============================================================================
|
|
463
|
+
// formatGapInsights Tests
|
|
464
|
+
// ============================================================================
|
|
465
|
+
(0, vitest_1.describe)('formatGapInsights', () => {
|
|
466
|
+
(0, vitest_1.it)('returns positive message for no gaps', () => {
|
|
467
|
+
const analysis = {
|
|
468
|
+
gaps: [],
|
|
469
|
+
totalGapDays: 0,
|
|
470
|
+
percentageOfPeriodInGaps: 0,
|
|
471
|
+
longestGap: null,
|
|
472
|
+
averageGapLength: 0,
|
|
473
|
+
gapFrequency: 0,
|
|
474
|
+
patterns: [],
|
|
475
|
+
riskLevel: 'low',
|
|
476
|
+
riskFactors: [],
|
|
477
|
+
};
|
|
478
|
+
const insights = (0, analyzer_1.formatGapInsights)(analysis);
|
|
479
|
+
(0, vitest_1.expect)(insights.some((i) => i.includes('No significant'))).toBe(true);
|
|
480
|
+
});
|
|
481
|
+
(0, vitest_1.it)('includes gap count', () => {
|
|
482
|
+
const start = new Date('2025-06-01');
|
|
483
|
+
const end = new Date('2025-06-30');
|
|
484
|
+
const commits = [
|
|
485
|
+
createCommit(new Date('2025-06-10')),
|
|
486
|
+
createCommit(new Date('2025-06-20')),
|
|
487
|
+
];
|
|
488
|
+
const analysis = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
489
|
+
const insights = (0, analyzer_1.formatGapInsights)(analysis);
|
|
490
|
+
(0, vitest_1.expect)(insights.some((i) => i.includes('gap'))).toBe(true);
|
|
491
|
+
});
|
|
492
|
+
(0, vitest_1.it)('includes total gap days and percentage', () => {
|
|
493
|
+
const start = new Date('2025-06-01');
|
|
494
|
+
const end = new Date('2025-06-30');
|
|
495
|
+
const commits = [createCommit(new Date('2025-06-15'))];
|
|
496
|
+
const analysis = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
497
|
+
const insights = (0, analyzer_1.formatGapInsights)(analysis);
|
|
498
|
+
(0, vitest_1.expect)(insights.some((i) => i.includes('days'))).toBe(true);
|
|
499
|
+
(0, vitest_1.expect)(insights.some((i) => i.includes('%'))).toBe(true);
|
|
500
|
+
});
|
|
501
|
+
(0, vitest_1.it)('includes longest gap information', () => {
|
|
502
|
+
const start = new Date('2025-06-01');
|
|
503
|
+
const end = new Date('2025-06-30');
|
|
504
|
+
const commits = [createCommit(new Date('2025-06-15'))];
|
|
505
|
+
const analysis = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
506
|
+
const insights = (0, analyzer_1.formatGapInsights)(analysis);
|
|
507
|
+
(0, vitest_1.expect)(insights.some((i) => i.toLowerCase().includes('longest'))).toBe(true);
|
|
508
|
+
});
|
|
509
|
+
(0, vitest_1.it)('includes risk level', () => {
|
|
510
|
+
const start = new Date('2025-06-01');
|
|
511
|
+
const end = new Date('2025-06-30');
|
|
512
|
+
const commits = [createCommit(new Date('2025-06-15'))];
|
|
513
|
+
const analysis = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
|
|
514
|
+
const insights = (0, analyzer_1.formatGapInsights)(analysis);
|
|
515
|
+
(0, vitest_1.expect)(insights.some((i) => i.toLowerCase().includes('risk'))).toBe(true);
|
|
516
|
+
});
|
|
517
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.buildGapSection = exports.formatGapInsights = exports.analyzeGaps = void 0;
|
|
18
|
+
/**
|
|
19
|
+
* Gaps Feature
|
|
20
|
+
* @module features/gaps
|
|
21
|
+
*/
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
23
|
+
var analyzer_1 = require("./analyzer");
|
|
24
|
+
Object.defineProperty(exports, "analyzeGaps", { enumerable: true, get: function () { return analyzer_1.analyzeGaps; } });
|
|
25
|
+
Object.defineProperty(exports, "formatGapInsights", { enumerable: true, get: function () { return analyzer_1.formatGapInsights; } });
|
|
26
|
+
var template_1 = require("./template");
|
|
27
|
+
Object.defineProperty(exports, "buildGapSection", { enumerable: true, get: function () { return template_1.buildGapSection; } });
|