@programinglive/dev-workflow-mcp-server 1.0.8 → 1.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/ISSUE_TEMPLATE/bug_report.md +34 -34
- package/.github/ISSUE_TEMPLATE/feature_request.md +25 -25
- package/.github/pull_request_template.md +22 -22
- package/.husky/commit-msg +3 -3
- package/.husky/pre-commit +1 -1
- package/CHANGELOG.md +104 -97
- package/CODE_OF_CONDUCT.md +79 -79
- package/CONTRIBUTING.md +60 -60
- package/LICENSE +21 -21
- package/README.md +64 -63
- package/SECURITY.md +20 -20
- package/commitlint.config.js +3 -3
- package/package.json +91 -91
- package/tests/workflowState.test.js +231 -231
|
@@ -1,231 +1,231 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
WorkflowState,
|
|
8
|
-
getNextStep,
|
|
9
|
-
containsTestFilesInStatus,
|
|
10
|
-
createCommitMessageParts,
|
|
11
|
-
determineReleaseTypeFromCommit,
|
|
12
|
-
workingTreeSummary,
|
|
13
|
-
} from "../index.js";
|
|
14
|
-
|
|
15
|
-
async function withWorkflowState(callback) {
|
|
16
|
-
const tempDir = await mkdtemp(path.join(tmpdir(), "workflow-state-"));
|
|
17
|
-
const stateFile = path.join(tempDir, "state.json");
|
|
18
|
-
const state = new WorkflowState(stateFile);
|
|
19
|
-
await state.load();
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
await callback(state);
|
|
23
|
-
} finally {
|
|
24
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
test("WorkflowState persists data across save/load", async () => {
|
|
29
|
-
await withWorkflowState(async (state) => {
|
|
30
|
-
state.state.currentPhase = "testing";
|
|
31
|
-
state.state.taskDescription = "Test persistence";
|
|
32
|
-
state.state.bugFixed = true;
|
|
33
|
-
state.state.testsCreated = true;
|
|
34
|
-
state.state.readyCheckCompleted = true;
|
|
35
|
-
state.state.released = true;
|
|
36
|
-
state.state.releaseCommand = "npm run release";
|
|
37
|
-
state.state.releaseNotes = "Automated release";
|
|
38
|
-
state.state.commitAndPushCompleted = true;
|
|
39
|
-
state.state.lastCommitMessage = "test: persistence";
|
|
40
|
-
state.state.lastPushBranch = "main";
|
|
41
|
-
state.state.testsSkipped = true;
|
|
42
|
-
state.state.testsSkippedReason = "Legacy app uses manual QA";
|
|
43
|
-
await state.save();
|
|
44
|
-
|
|
45
|
-
const reloaded = new WorkflowState(state.stateFile);
|
|
46
|
-
await reloaded.load();
|
|
47
|
-
|
|
48
|
-
assert.equal(reloaded.state.currentPhase, "testing");
|
|
49
|
-
assert.equal(reloaded.state.taskDescription, "Test persistence");
|
|
50
|
-
assert.equal(reloaded.state.bugFixed, true);
|
|
51
|
-
assert.equal(reloaded.state.testsCreated, true);
|
|
52
|
-
assert.equal(reloaded.state.readyCheckCompleted, true);
|
|
53
|
-
assert.equal(reloaded.state.released, true);
|
|
54
|
-
assert.equal(reloaded.state.releaseCommand, "npm run release");
|
|
55
|
-
assert.equal(reloaded.state.releaseNotes, "Automated release");
|
|
56
|
-
assert.equal(reloaded.state.commitAndPushCompleted, true);
|
|
57
|
-
assert.equal(reloaded.state.lastCommitMessage, "test: persistence");
|
|
58
|
-
assert.equal(reloaded.state.lastPushBranch, "main");
|
|
59
|
-
assert.equal(reloaded.state.testsSkipped, true);
|
|
60
|
-
assert.equal(
|
|
61
|
-
reloaded.state.testsSkippedReason,
|
|
62
|
-
"Legacy app uses manual QA"
|
|
63
|
-
);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("WorkflowState reset keeps history but clears progress", async () => {
|
|
68
|
-
await withWorkflowState(async (state) => {
|
|
69
|
-
state.state.currentPhase = "ready";
|
|
70
|
-
state.state.testsCreated = true;
|
|
71
|
-
state.state.testsPassed = true;
|
|
72
|
-
state.state.documentationCreated = true;
|
|
73
|
-
state.state.readyCheckCompleted = true;
|
|
74
|
-
state.state.released = true;
|
|
75
|
-
state.state.commitAndPushCompleted = true;
|
|
76
|
-
state.state.testsSkipped = true;
|
|
77
|
-
state.state.testsSkippedReason = "Manual QA";
|
|
78
|
-
state.addToHistory({ taskDescription: "Sample task" });
|
|
79
|
-
|
|
80
|
-
state.reset();
|
|
81
|
-
|
|
82
|
-
assert.equal(state.state.currentPhase, "idle");
|
|
83
|
-
assert.equal(state.state.testsCreated, false);
|
|
84
|
-
assert.equal(state.state.testsPassed, false);
|
|
85
|
-
assert.equal(state.state.documentationCreated, false);
|
|
86
|
-
assert.equal(state.state.readyCheckCompleted, false);
|
|
87
|
-
assert.equal(state.state.released, false);
|
|
88
|
-
assert.equal(state.state.commitAndPushCompleted, false);
|
|
89
|
-
assert.equal(state.state.testsSkipped, false);
|
|
90
|
-
assert.equal(state.state.testsSkippedReason, "");
|
|
91
|
-
assert.equal(state.state.history.length, 1);
|
|
92
|
-
assert.equal(state.state.history[0].taskDescription, "Sample task");
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("getNextStep treats skipped tests as satisfied", () => {
|
|
97
|
-
const skippedState = {
|
|
98
|
-
bugFixed: true,
|
|
99
|
-
testsCreated: true,
|
|
100
|
-
testsPassed: true,
|
|
101
|
-
testsSkipped: true,
|
|
102
|
-
testsSkippedReason: "Legacy system",
|
|
103
|
-
documentationCreated: false,
|
|
104
|
-
readyCheckCompleted: false,
|
|
105
|
-
commitAndPushCompleted: false,
|
|
106
|
-
released: false,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
assert.equal(getNextStep(skippedState), "Create documentation");
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test("getNextStep guides workflow progression", () => {
|
|
113
|
-
const baseState = {
|
|
114
|
-
bugFixed: false,
|
|
115
|
-
testsCreated: false,
|
|
116
|
-
testsPassed: false,
|
|
117
|
-
documentationCreated: false,
|
|
118
|
-
readyCheckCompleted: false,
|
|
119
|
-
commitAndPushCompleted: false,
|
|
120
|
-
released: false,
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
assert.equal(
|
|
124
|
-
getNextStep(baseState),
|
|
125
|
-
"Mark feature/bug as fixed"
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
const afterFix = { ...baseState, bugFixed: true };
|
|
129
|
-
assert.equal(
|
|
130
|
-
getNextStep(afterFix),
|
|
131
|
-
"Create tests"
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
const afterTestsCreated = { ...afterFix, testsCreated: true };
|
|
135
|
-
assert.equal(getNextStep(afterTestsCreated), "Run tests");
|
|
136
|
-
|
|
137
|
-
const afterTests = { ...afterTestsCreated, testsPassed: true };
|
|
138
|
-
assert.equal(
|
|
139
|
-
getNextStep(afterTests),
|
|
140
|
-
"Create documentation"
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
const afterDocs = { ...afterTests, documentationCreated: true };
|
|
144
|
-
assert.equal(getNextStep(afterDocs), "Run 'check_ready_to_commit'");
|
|
145
|
-
|
|
146
|
-
const afterReadyCheck = { ...afterDocs, readyCheckCompleted: true };
|
|
147
|
-
assert.equal(getNextStep(afterReadyCheck), "Run 'commit_and_push'");
|
|
148
|
-
|
|
149
|
-
const afterCommit = { ...afterReadyCheck, commitAndPushCompleted: true };
|
|
150
|
-
assert.equal(getNextStep(afterCommit), "Run 'perform_release'");
|
|
151
|
-
|
|
152
|
-
const afterRelease = { ...afterCommit, released: true };
|
|
153
|
-
assert.equal(getNextStep(afterRelease), "Complete the task");
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
test("containsTestFilesInStatus detects modified test files", () => {
|
|
157
|
-
const statusOutput = [
|
|
158
|
-
" M src/app.test.js",
|
|
159
|
-
"A tests/helpers/util.js",
|
|
160
|
-
"R src/old.js -> src/new.spec.ts",
|
|
161
|
-
].join("\n");
|
|
162
|
-
|
|
163
|
-
assert.equal(containsTestFilesInStatus(statusOutput), true);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
test("containsTestFilesInStatus returns false when no test files present", () => {
|
|
167
|
-
const statusOutput = [
|
|
168
|
-
" M src/app.js",
|
|
169
|
-
"A docs/README.md",
|
|
170
|
-
"?? assets/logo.png",
|
|
171
|
-
].join("\n");
|
|
172
|
-
|
|
173
|
-
assert.equal(containsTestFilesInStatus(statusOutput), false);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test("createCommitMessageParts generates test prefixed summary with details", () => {
|
|
177
|
-
const changes = [
|
|
178
|
-
{ status: "M", path: "src/app.test.js" },
|
|
179
|
-
{ status: "A", path: "tests/helpers/util.js" },
|
|
180
|
-
];
|
|
181
|
-
|
|
182
|
-
const { summary, body } = createCommitMessageParts(changes);
|
|
183
|
-
|
|
184
|
-
assert.equal(summary.startsWith("test:"), true);
|
|
185
|
-
assert.equal(body.includes("- modified src/app.test.js"), true);
|
|
186
|
-
assert.equal(body.includes("- added tests/helpers/util.js"), true);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test("createCommitMessageParts respects provided summary when present", () => {
|
|
190
|
-
const changes = [{ status: "M", path: "docs/guide.md" }];
|
|
191
|
-
const provided = "docs: update guide";
|
|
192
|
-
|
|
193
|
-
const { summary, body } = createCommitMessageParts(changes, provided);
|
|
194
|
-
|
|
195
|
-
assert.equal(summary, provided);
|
|
196
|
-
assert.equal(body.includes("- modified docs/guide.md"), true);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
test("determineReleaseTypeFromCommit returns major for breaking change", () => {
|
|
200
|
-
const message = "feat!: overhaul API\n\nBREAKING CHANGE: new behavior";
|
|
201
|
-
assert.equal(determineReleaseTypeFromCommit(message), "major");
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
test("determineReleaseTypeFromCommit returns minor for feat", () => {
|
|
205
|
-
const message = "feat: add dashboard";
|
|
206
|
-
assert.equal(determineReleaseTypeFromCommit(message), "minor");
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test("determineReleaseTypeFromCommit returns patch for fix", () => {
|
|
210
|
-
const message = "fix: resolve login issue";
|
|
211
|
-
assert.equal(determineReleaseTypeFromCommit(message), "patch");
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test("determineReleaseTypeFromCommit defaults to patch for other commit types", () => {
|
|
215
|
-
const message = "chore: update dependencies";
|
|
216
|
-
assert.equal(determineReleaseTypeFromCommit(message), "patch");
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
test("workingTreeSummary reports no changes for empty output", () => {
|
|
220
|
-
const summary = workingTreeSummary("\n \n");
|
|
221
|
-
assert.equal(summary.hasChanges, false);
|
|
222
|
-
assert.deepEqual(summary.lines, []);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
test("workingTreeSummary lists trimmed status lines", () => {
|
|
226
|
-
const statusOutput = " M src/app.js\nA tests/new.test.js\n";
|
|
227
|
-
const summary = workingTreeSummary(statusOutput);
|
|
228
|
-
|
|
229
|
-
assert.equal(summary.hasChanges, true);
|
|
230
|
-
assert.deepEqual(summary.lines, ["M src/app.js", "A tests/new.test.js"]);
|
|
231
|
-
});
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import {
|
|
7
|
+
WorkflowState,
|
|
8
|
+
getNextStep,
|
|
9
|
+
containsTestFilesInStatus,
|
|
10
|
+
createCommitMessageParts,
|
|
11
|
+
determineReleaseTypeFromCommit,
|
|
12
|
+
workingTreeSummary,
|
|
13
|
+
} from "../index.js";
|
|
14
|
+
|
|
15
|
+
async function withWorkflowState(callback) {
|
|
16
|
+
const tempDir = await mkdtemp(path.join(tmpdir(), "workflow-state-"));
|
|
17
|
+
const stateFile = path.join(tempDir, "state.json");
|
|
18
|
+
const state = new WorkflowState(stateFile);
|
|
19
|
+
await state.load();
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
await callback(state);
|
|
23
|
+
} finally {
|
|
24
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
test("WorkflowState persists data across save/load", async () => {
|
|
29
|
+
await withWorkflowState(async (state) => {
|
|
30
|
+
state.state.currentPhase = "testing";
|
|
31
|
+
state.state.taskDescription = "Test persistence";
|
|
32
|
+
state.state.bugFixed = true;
|
|
33
|
+
state.state.testsCreated = true;
|
|
34
|
+
state.state.readyCheckCompleted = true;
|
|
35
|
+
state.state.released = true;
|
|
36
|
+
state.state.releaseCommand = "npm run release";
|
|
37
|
+
state.state.releaseNotes = "Automated release";
|
|
38
|
+
state.state.commitAndPushCompleted = true;
|
|
39
|
+
state.state.lastCommitMessage = "test: persistence";
|
|
40
|
+
state.state.lastPushBranch = "main";
|
|
41
|
+
state.state.testsSkipped = true;
|
|
42
|
+
state.state.testsSkippedReason = "Legacy app uses manual QA";
|
|
43
|
+
await state.save();
|
|
44
|
+
|
|
45
|
+
const reloaded = new WorkflowState(state.stateFile);
|
|
46
|
+
await reloaded.load();
|
|
47
|
+
|
|
48
|
+
assert.equal(reloaded.state.currentPhase, "testing");
|
|
49
|
+
assert.equal(reloaded.state.taskDescription, "Test persistence");
|
|
50
|
+
assert.equal(reloaded.state.bugFixed, true);
|
|
51
|
+
assert.equal(reloaded.state.testsCreated, true);
|
|
52
|
+
assert.equal(reloaded.state.readyCheckCompleted, true);
|
|
53
|
+
assert.equal(reloaded.state.released, true);
|
|
54
|
+
assert.equal(reloaded.state.releaseCommand, "npm run release");
|
|
55
|
+
assert.equal(reloaded.state.releaseNotes, "Automated release");
|
|
56
|
+
assert.equal(reloaded.state.commitAndPushCompleted, true);
|
|
57
|
+
assert.equal(reloaded.state.lastCommitMessage, "test: persistence");
|
|
58
|
+
assert.equal(reloaded.state.lastPushBranch, "main");
|
|
59
|
+
assert.equal(reloaded.state.testsSkipped, true);
|
|
60
|
+
assert.equal(
|
|
61
|
+
reloaded.state.testsSkippedReason,
|
|
62
|
+
"Legacy app uses manual QA"
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("WorkflowState reset keeps history but clears progress", async () => {
|
|
68
|
+
await withWorkflowState(async (state) => {
|
|
69
|
+
state.state.currentPhase = "ready";
|
|
70
|
+
state.state.testsCreated = true;
|
|
71
|
+
state.state.testsPassed = true;
|
|
72
|
+
state.state.documentationCreated = true;
|
|
73
|
+
state.state.readyCheckCompleted = true;
|
|
74
|
+
state.state.released = true;
|
|
75
|
+
state.state.commitAndPushCompleted = true;
|
|
76
|
+
state.state.testsSkipped = true;
|
|
77
|
+
state.state.testsSkippedReason = "Manual QA";
|
|
78
|
+
state.addToHistory({ taskDescription: "Sample task" });
|
|
79
|
+
|
|
80
|
+
state.reset();
|
|
81
|
+
|
|
82
|
+
assert.equal(state.state.currentPhase, "idle");
|
|
83
|
+
assert.equal(state.state.testsCreated, false);
|
|
84
|
+
assert.equal(state.state.testsPassed, false);
|
|
85
|
+
assert.equal(state.state.documentationCreated, false);
|
|
86
|
+
assert.equal(state.state.readyCheckCompleted, false);
|
|
87
|
+
assert.equal(state.state.released, false);
|
|
88
|
+
assert.equal(state.state.commitAndPushCompleted, false);
|
|
89
|
+
assert.equal(state.state.testsSkipped, false);
|
|
90
|
+
assert.equal(state.state.testsSkippedReason, "");
|
|
91
|
+
assert.equal(state.state.history.length, 1);
|
|
92
|
+
assert.equal(state.state.history[0].taskDescription, "Sample task");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("getNextStep treats skipped tests as satisfied", () => {
|
|
97
|
+
const skippedState = {
|
|
98
|
+
bugFixed: true,
|
|
99
|
+
testsCreated: true,
|
|
100
|
+
testsPassed: true,
|
|
101
|
+
testsSkipped: true,
|
|
102
|
+
testsSkippedReason: "Legacy system",
|
|
103
|
+
documentationCreated: false,
|
|
104
|
+
readyCheckCompleted: false,
|
|
105
|
+
commitAndPushCompleted: false,
|
|
106
|
+
released: false,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
assert.equal(getNextStep(skippedState), "Create documentation");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("getNextStep guides workflow progression", () => {
|
|
113
|
+
const baseState = {
|
|
114
|
+
bugFixed: false,
|
|
115
|
+
testsCreated: false,
|
|
116
|
+
testsPassed: false,
|
|
117
|
+
documentationCreated: false,
|
|
118
|
+
readyCheckCompleted: false,
|
|
119
|
+
commitAndPushCompleted: false,
|
|
120
|
+
released: false,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
assert.equal(
|
|
124
|
+
getNextStep(baseState),
|
|
125
|
+
"Mark feature/bug as fixed"
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const afterFix = { ...baseState, bugFixed: true };
|
|
129
|
+
assert.equal(
|
|
130
|
+
getNextStep(afterFix),
|
|
131
|
+
"Create tests"
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const afterTestsCreated = { ...afterFix, testsCreated: true };
|
|
135
|
+
assert.equal(getNextStep(afterTestsCreated), "Run tests");
|
|
136
|
+
|
|
137
|
+
const afterTests = { ...afterTestsCreated, testsPassed: true };
|
|
138
|
+
assert.equal(
|
|
139
|
+
getNextStep(afterTests),
|
|
140
|
+
"Create documentation"
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const afterDocs = { ...afterTests, documentationCreated: true };
|
|
144
|
+
assert.equal(getNextStep(afterDocs), "Run 'check_ready_to_commit'");
|
|
145
|
+
|
|
146
|
+
const afterReadyCheck = { ...afterDocs, readyCheckCompleted: true };
|
|
147
|
+
assert.equal(getNextStep(afterReadyCheck), "Run 'commit_and_push'");
|
|
148
|
+
|
|
149
|
+
const afterCommit = { ...afterReadyCheck, commitAndPushCompleted: true };
|
|
150
|
+
assert.equal(getNextStep(afterCommit), "Run 'perform_release'");
|
|
151
|
+
|
|
152
|
+
const afterRelease = { ...afterCommit, released: true };
|
|
153
|
+
assert.equal(getNextStep(afterRelease), "Complete the task");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("containsTestFilesInStatus detects modified test files", () => {
|
|
157
|
+
const statusOutput = [
|
|
158
|
+
" M src/app.test.js",
|
|
159
|
+
"A tests/helpers/util.js",
|
|
160
|
+
"R src/old.js -> src/new.spec.ts",
|
|
161
|
+
].join("\n");
|
|
162
|
+
|
|
163
|
+
assert.equal(containsTestFilesInStatus(statusOutput), true);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("containsTestFilesInStatus returns false when no test files present", () => {
|
|
167
|
+
const statusOutput = [
|
|
168
|
+
" M src/app.js",
|
|
169
|
+
"A docs/README.md",
|
|
170
|
+
"?? assets/logo.png",
|
|
171
|
+
].join("\n");
|
|
172
|
+
|
|
173
|
+
assert.equal(containsTestFilesInStatus(statusOutput), false);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("createCommitMessageParts generates test prefixed summary with details", () => {
|
|
177
|
+
const changes = [
|
|
178
|
+
{ status: "M", path: "src/app.test.js" },
|
|
179
|
+
{ status: "A", path: "tests/helpers/util.js" },
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
const { summary, body } = createCommitMessageParts(changes);
|
|
183
|
+
|
|
184
|
+
assert.equal(summary.startsWith("test:"), true);
|
|
185
|
+
assert.equal(body.includes("- modified src/app.test.js"), true);
|
|
186
|
+
assert.equal(body.includes("- added tests/helpers/util.js"), true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("createCommitMessageParts respects provided summary when present", () => {
|
|
190
|
+
const changes = [{ status: "M", path: "docs/guide.md" }];
|
|
191
|
+
const provided = "docs: update guide";
|
|
192
|
+
|
|
193
|
+
const { summary, body } = createCommitMessageParts(changes, provided);
|
|
194
|
+
|
|
195
|
+
assert.equal(summary, provided);
|
|
196
|
+
assert.equal(body.includes("- modified docs/guide.md"), true);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("determineReleaseTypeFromCommit returns major for breaking change", () => {
|
|
200
|
+
const message = "feat!: overhaul API\n\nBREAKING CHANGE: new behavior";
|
|
201
|
+
assert.equal(determineReleaseTypeFromCommit(message), "major");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("determineReleaseTypeFromCommit returns minor for feat", () => {
|
|
205
|
+
const message = "feat: add dashboard";
|
|
206
|
+
assert.equal(determineReleaseTypeFromCommit(message), "minor");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("determineReleaseTypeFromCommit returns patch for fix", () => {
|
|
210
|
+
const message = "fix: resolve login issue";
|
|
211
|
+
assert.equal(determineReleaseTypeFromCommit(message), "patch");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("determineReleaseTypeFromCommit defaults to patch for other commit types", () => {
|
|
215
|
+
const message = "chore: update dependencies";
|
|
216
|
+
assert.equal(determineReleaseTypeFromCommit(message), "patch");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("workingTreeSummary reports no changes for empty output", () => {
|
|
220
|
+
const summary = workingTreeSummary("\n \n");
|
|
221
|
+
assert.equal(summary.hasChanges, false);
|
|
222
|
+
assert.deepEqual(summary.lines, []);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("workingTreeSummary lists trimmed status lines", () => {
|
|
226
|
+
const statusOutput = " M src/app.js\nA tests/new.test.js\n";
|
|
227
|
+
const summary = workingTreeSummary(statusOutput);
|
|
228
|
+
|
|
229
|
+
assert.equal(summary.hasChanges, true);
|
|
230
|
+
assert.deepEqual(summary.lines, ["M src/app.js", "A tests/new.test.js"]);
|
|
231
|
+
});
|