tdd-enforcer 0.2.6 → 0.2.7

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.
@@ -1,302 +1,312 @@
1
- import { describe, it, expect, beforeEach, vi } from "vitest";
2
- import { handleTddOn, handleTddOff, handleTddStatus, handleTddReset } from "./index.js";
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import {
3
+ handleTddOff,
4
+ handleTddOn,
5
+ handleTddReset,
6
+ handleTddStatus,
7
+ } from "./index.js";
3
8
 
4
9
  const config = {
5
- blockedInRed: ["tests/**/*.test.ts"],
6
- blockedInGreen: ["src/**/*.ts"],
7
- testCommands: ["npm test"],
8
- timeoutSeconds: 30,
10
+ blockedInRed: ["tests/**/*.test.ts"],
11
+ blockedInGreen: ["src/**/*.ts"],
12
+ testCommands: ["npm test"],
13
+ timeoutSeconds: 30,
9
14
  };
10
15
 
11
16
  // ── Helpers ─────────────────────────────────────────────────────────────────
12
17
 
13
18
  function makeCtx(dir = "/test") {
14
- const notifications: Array<{ message: string; type: string }> = [];
15
- return {
16
- cwd: dir,
17
- ui: { notify: (message: string, type: string) => notifications.push({ message, type }) },
18
- notifications, // stored alongside for easy assertion
19
- } as any;
19
+ const notifications: Array<{ message: string; type: string }> = [];
20
+ return {
21
+ cwd: dir,
22
+ ui: {
23
+ notify: (message: string, type: string) =>
24
+ notifications.push({ message, type }),
25
+ },
26
+ notifications, // stored alongside for easy assertion
27
+ } as any;
20
28
  }
21
29
 
22
- function captureNotifications(ctx: any): Array<{ message: string; type: string }> {
23
- return ctx.notifications;
30
+ function _captureNotifications(
31
+ ctx: any,
32
+ ): Array<{ message: string; type: string }> {
33
+ return ctx.notifications;
24
34
  }
25
35
 
26
36
  // ── handleTddOn ─────────────────────────────────────────────────────────────
27
37
 
28
38
  describe("handleTddOn", () => {
29
- let mockLoadTddState: ReturnType<typeof vi.fn>;
30
- let mockSnapshot: ReturnType<typeof vi.fn>;
31
- let mockSavePhaseState: ReturnType<typeof vi.fn>;
32
- let mockTddLog: ReturnType<typeof vi.fn>;
33
-
34
- function makeDeps(overrides = {}) {
35
- return {
36
- loadTddState: mockLoadTddState,
37
- snapshot: mockSnapshot,
38
- savePhaseState: mockSavePhaseState,
39
- tddLog: mockTddLog,
40
- ...overrides,
41
- };
42
- }
43
-
44
- beforeEach(() => {
45
- mockLoadTddState = vi.fn();
46
- mockSnapshot = vi.fn().mockReturnValue("hash123");
47
- mockSavePhaseState = vi.fn();
48
- mockTddLog = vi.fn();
49
- });
50
-
51
- it("enables TDD, takes snapshot, notifies user", async () => {
52
- mockLoadTddState.mockReturnValue({
53
- ok: true,
54
- state: { enabled: false, current: "red" },
55
- config,
56
- });
57
-
58
- const ctx = makeCtx();
59
- await handleTddOn(ctx, makeDeps());
60
-
61
- expect(mockSnapshot).toHaveBeenCalledWith("/test", "red");
62
- expect(mockSavePhaseState).toHaveBeenCalledWith("/test", {
63
- enabled: true,
64
- current: "red",
65
- });
66
- expect(ctx.notifications).toHaveLength(1);
67
- expect(ctx.notifications[0].message).toContain("TDD enabled");
68
- expect(ctx.notifications[0].type).toBe("info");
69
- });
70
-
71
- it("shows already enabled when TDD already on", async () => {
72
- mockLoadTddState.mockReturnValue({
73
- ok: true,
74
- state: { enabled: true, current: "red" },
75
- config,
76
- });
77
-
78
- const ctx = makeCtx();
79
- await handleTddOn(ctx, makeDeps());
80
-
81
- expect(ctx.notifications).toHaveLength(1);
82
- expect(ctx.notifications[0].message).toContain("already enabled");
83
- expect(ctx.notifications[0].type).toBe("info");
84
- expect(mockSnapshot).not.toHaveBeenCalled();
85
- });
86
-
87
- it("shows error when setup invalid", async () => {
88
- mockLoadTddState.mockReturnValue({
89
- ok: false,
90
- reason: "Missing .pi/tdd/",
91
- });
92
-
93
- const ctx = makeCtx();
94
- await handleTddOn(ctx, makeDeps());
95
-
96
- expect(ctx.notifications).toHaveLength(1);
97
- expect(ctx.notifications[0].message).toContain("Missing .pi/tdd/");
98
- expect(ctx.notifications[0].type).toBe("error");
99
- });
39
+ let mockLoadTddState: ReturnType<typeof vi.fn>;
40
+ let mockSnapshot: ReturnType<typeof vi.fn>;
41
+ let mockSavePhaseState: ReturnType<typeof vi.fn>;
42
+ let mockTddLog: ReturnType<typeof vi.fn>;
43
+
44
+ function makeDeps(overrides = {}) {
45
+ return {
46
+ loadTddState: mockLoadTddState,
47
+ snapshot: mockSnapshot,
48
+ savePhaseState: mockSavePhaseState,
49
+ tddLog: mockTddLog,
50
+ ...overrides,
51
+ };
52
+ }
53
+
54
+ beforeEach(() => {
55
+ mockLoadTddState = vi.fn();
56
+ mockSnapshot = vi.fn().mockReturnValue("hash123");
57
+ mockSavePhaseState = vi.fn();
58
+ mockTddLog = vi.fn();
59
+ });
60
+
61
+ it("enables TDD, takes snapshot, notifies user", async () => {
62
+ mockLoadTddState.mockReturnValue({
63
+ ok: true,
64
+ state: { enabled: false, current: "red" },
65
+ config,
66
+ });
67
+
68
+ const ctx = makeCtx();
69
+ await handleTddOn(ctx, makeDeps());
70
+
71
+ expect(mockSnapshot).toHaveBeenCalledWith("/test", "red");
72
+ expect(mockSavePhaseState).toHaveBeenCalledWith("/test", {
73
+ enabled: true,
74
+ current: "red",
75
+ });
76
+ expect(ctx.notifications).toHaveLength(1);
77
+ expect(ctx.notifications[0].message).toContain("TDD enabled");
78
+ expect(ctx.notifications[0].type).toBe("info");
79
+ });
80
+
81
+ it("shows already enabled when TDD already on", async () => {
82
+ mockLoadTddState.mockReturnValue({
83
+ ok: true,
84
+ state: { enabled: true, current: "red" },
85
+ config,
86
+ });
87
+
88
+ const ctx = makeCtx();
89
+ await handleTddOn(ctx, makeDeps());
90
+
91
+ expect(ctx.notifications).toHaveLength(1);
92
+ expect(ctx.notifications[0].message).toContain("already enabled");
93
+ expect(ctx.notifications[0].type).toBe("info");
94
+ expect(mockSnapshot).not.toHaveBeenCalled();
95
+ });
96
+
97
+ it("shows error when setup invalid", async () => {
98
+ mockLoadTddState.mockReturnValue({
99
+ ok: false,
100
+ reason: "Missing .pi/tdd/",
101
+ });
102
+
103
+ const ctx = makeCtx();
104
+ await handleTddOn(ctx, makeDeps());
105
+
106
+ expect(ctx.notifications).toHaveLength(1);
107
+ expect(ctx.notifications[0].message).toContain("Missing .pi/tdd/");
108
+ expect(ctx.notifications[0].type).toBe("error");
109
+ });
100
110
  });
101
111
 
102
112
  // ── handleTddOff ────────────────────────────────────────────────────────────
103
113
 
104
114
  describe("handleTddOff", () => {
105
- let mockLoadTddState: ReturnType<typeof vi.fn>;
106
- let mockSavePhaseState: ReturnType<typeof vi.fn>;
107
- let mockTddLog: ReturnType<typeof vi.fn>;
108
-
109
- function makeDeps(overrides = {}) {
110
- return {
111
- loadTddState: mockLoadTddState,
112
- savePhaseState: mockSavePhaseState,
113
- tddLog: mockTddLog,
114
- ...overrides,
115
- };
116
- }
117
-
118
- beforeEach(() => {
119
- mockLoadTddState = vi.fn();
120
- mockSavePhaseState = vi.fn();
121
- mockTddLog = vi.fn();
122
- });
123
-
124
- it("disables TDD, notifies user", async () => {
125
- mockLoadTddState.mockReturnValue({
126
- ok: true,
127
- state: { enabled: true, current: "red" },
128
- config,
129
- });
130
-
131
- const ctx = makeCtx();
132
- await handleTddOff(ctx, makeDeps());
133
-
134
- expect(mockSavePhaseState).toHaveBeenCalledWith("/test", {
135
- enabled: false,
136
- current: "red",
137
- });
138
- expect(ctx.notifications).toHaveLength(1);
139
- expect(ctx.notifications[0].message).toContain("disabled");
140
- expect(ctx.notifications[0].type).toBe("info");
141
- });
142
-
143
- it("shows already disabled when TDD already off", async () => {
144
- mockLoadTddState.mockReturnValue({
145
- ok: true,
146
- state: { enabled: false, current: "red" },
147
- config,
148
- });
149
-
150
- const ctx = makeCtx();
151
- await handleTddOff(ctx, makeDeps());
152
-
153
- expect(ctx.notifications).toHaveLength(1);
154
- expect(ctx.notifications[0].message).toContain("already disabled");
155
- expect(ctx.notifications[0].type).toBe("info");
156
- expect(mockSavePhaseState).not.toHaveBeenCalled();
157
- });
158
-
159
- it("shows error when setup invalid", async () => {
160
- mockLoadTddState.mockReturnValue({
161
- ok: false,
162
- reason: "Missing .pi/tdd/",
163
- });
164
-
165
- const ctx = makeCtx();
166
- await handleTddOff(ctx, makeDeps());
167
-
168
- expect(ctx.notifications).toHaveLength(1);
169
- expect(ctx.notifications[0].message).toContain("Missing .pi/tdd/");
170
- expect(ctx.notifications[0].type).toBe("error");
171
- });
115
+ let mockLoadTddState: ReturnType<typeof vi.fn>;
116
+ let mockSavePhaseState: ReturnType<typeof vi.fn>;
117
+ let mockTddLog: ReturnType<typeof vi.fn>;
118
+
119
+ function makeDeps(overrides = {}) {
120
+ return {
121
+ loadTddState: mockLoadTddState,
122
+ savePhaseState: mockSavePhaseState,
123
+ tddLog: mockTddLog,
124
+ ...overrides,
125
+ };
126
+ }
127
+
128
+ beforeEach(() => {
129
+ mockLoadTddState = vi.fn();
130
+ mockSavePhaseState = vi.fn();
131
+ mockTddLog = vi.fn();
132
+ });
133
+
134
+ it("disables TDD, notifies user", async () => {
135
+ mockLoadTddState.mockReturnValue({
136
+ ok: true,
137
+ state: { enabled: true, current: "red" },
138
+ config,
139
+ });
140
+
141
+ const ctx = makeCtx();
142
+ await handleTddOff(ctx, makeDeps());
143
+
144
+ expect(mockSavePhaseState).toHaveBeenCalledWith("/test", {
145
+ enabled: false,
146
+ current: "red",
147
+ });
148
+ expect(ctx.notifications).toHaveLength(1);
149
+ expect(ctx.notifications[0].message).toContain("disabled");
150
+ expect(ctx.notifications[0].type).toBe("info");
151
+ });
152
+
153
+ it("shows already disabled when TDD already off", async () => {
154
+ mockLoadTddState.mockReturnValue({
155
+ ok: true,
156
+ state: { enabled: false, current: "red" },
157
+ config,
158
+ });
159
+
160
+ const ctx = makeCtx();
161
+ await handleTddOff(ctx, makeDeps());
162
+
163
+ expect(ctx.notifications).toHaveLength(1);
164
+ expect(ctx.notifications[0].message).toContain("already disabled");
165
+ expect(ctx.notifications[0].type).toBe("info");
166
+ expect(mockSavePhaseState).not.toHaveBeenCalled();
167
+ });
168
+
169
+ it("shows error when setup invalid", async () => {
170
+ mockLoadTddState.mockReturnValue({
171
+ ok: false,
172
+ reason: "Missing .pi/tdd/",
173
+ });
174
+
175
+ const ctx = makeCtx();
176
+ await handleTddOff(ctx, makeDeps());
177
+
178
+ expect(ctx.notifications).toHaveLength(1);
179
+ expect(ctx.notifications[0].message).toContain("Missing .pi/tdd/");
180
+ expect(ctx.notifications[0].type).toBe("error");
181
+ });
172
182
  });
173
183
 
174
184
  // ── handleTddStatus ─────────────────────────────────────────────────────────
175
185
 
176
186
  describe("handleTddStatus", () => {
177
- let mockLoadTddState: ReturnType<typeof vi.fn>;
178
- let mockTddLog: ReturnType<typeof vi.fn>;
179
-
180
- function makeDeps(overrides = {}) {
181
- return {
182
- loadTddState: mockLoadTddState,
183
- tddLog: mockTddLog,
184
- ...overrides,
185
- };
186
- }
187
-
188
- beforeEach(() => {
189
- mockLoadTddState = vi.fn();
190
- mockTddLog = vi.fn();
191
- });
192
-
193
- it("shows status when TDD enabled", async () => {
194
- mockLoadTddState.mockReturnValue({
195
- ok: true,
196
- state: { enabled: true, current: "green" },
197
- config,
198
- });
199
-
200
- const ctx = makeCtx();
201
- await handleTddStatus(ctx, makeDeps());
202
-
203
- expect(ctx.notifications).toHaveLength(1);
204
- expect(ctx.notifications[0].message).toContain("enabled");
205
- expect(ctx.notifications[0].message).toContain("GREEN");
206
- expect(ctx.notifications[0].type).toBe("info");
207
- });
208
-
209
- it("shows status when TDD disabled", async () => {
210
- mockLoadTddState.mockReturnValue({
211
- ok: true,
212
- state: { enabled: false, current: "red" },
213
- config,
214
- });
215
-
216
- const ctx = makeCtx();
217
- await handleTddStatus(ctx, makeDeps());
218
-
219
- expect(ctx.notifications).toHaveLength(1);
220
- expect(ctx.notifications[0].message).toContain("disabled");
221
- expect(ctx.notifications[0].message).toContain("RED");
222
- expect(ctx.notifications[0].type).toBe("info");
223
- });
224
-
225
- it("shows error when setup invalid", async () => {
226
- mockLoadTddState.mockReturnValue({
227
- ok: false,
228
- reason: "Missing .pi/tdd/",
229
- });
230
-
231
- const ctx = makeCtx();
232
- await handleTddStatus(ctx, makeDeps());
233
-
234
- expect(ctx.notifications).toHaveLength(1);
235
- expect(ctx.notifications[0].message).toContain("Missing .pi/tdd/");
236
- expect(ctx.notifications[0].type).toBe("error");
237
- });
187
+ let mockLoadTddState: ReturnType<typeof vi.fn>;
188
+ let mockTddLog: ReturnType<typeof vi.fn>;
189
+
190
+ function makeDeps(overrides = {}) {
191
+ return {
192
+ loadTddState: mockLoadTddState,
193
+ tddLog: mockTddLog,
194
+ ...overrides,
195
+ };
196
+ }
197
+
198
+ beforeEach(() => {
199
+ mockLoadTddState = vi.fn();
200
+ mockTddLog = vi.fn();
201
+ });
202
+
203
+ it("shows status when TDD enabled", async () => {
204
+ mockLoadTddState.mockReturnValue({
205
+ ok: true,
206
+ state: { enabled: true, current: "green" },
207
+ config,
208
+ });
209
+
210
+ const ctx = makeCtx();
211
+ await handleTddStatus(ctx, makeDeps());
212
+
213
+ expect(ctx.notifications).toHaveLength(1);
214
+ expect(ctx.notifications[0].message).toContain("enabled");
215
+ expect(ctx.notifications[0].message).toContain("GREEN");
216
+ expect(ctx.notifications[0].type).toBe("info");
217
+ });
218
+
219
+ it("shows status when TDD disabled", async () => {
220
+ mockLoadTddState.mockReturnValue({
221
+ ok: true,
222
+ state: { enabled: false, current: "red" },
223
+ config,
224
+ });
225
+
226
+ const ctx = makeCtx();
227
+ await handleTddStatus(ctx, makeDeps());
228
+
229
+ expect(ctx.notifications).toHaveLength(1);
230
+ expect(ctx.notifications[0].message).toContain("disabled");
231
+ expect(ctx.notifications[0].message).toContain("RED");
232
+ expect(ctx.notifications[0].type).toBe("info");
233
+ });
234
+
235
+ it("shows error when setup invalid", async () => {
236
+ mockLoadTddState.mockReturnValue({
237
+ ok: false,
238
+ reason: "Missing .pi/tdd/",
239
+ });
240
+
241
+ const ctx = makeCtx();
242
+ await handleTddStatus(ctx, makeDeps());
243
+
244
+ expect(ctx.notifications).toHaveLength(1);
245
+ expect(ctx.notifications[0].message).toContain("Missing .pi/tdd/");
246
+ expect(ctx.notifications[0].type).toBe("error");
247
+ });
238
248
  });
239
249
 
240
250
  // ── handleTddReset ──────────────────────────────────────────────────────────
241
251
 
242
252
  describe("handleTddReset", () => {
243
- let mockLoadTddState: ReturnType<typeof vi.fn>;
244
- let mockResetGit: ReturnType<typeof vi.fn>;
245
- let mockSnapshot: ReturnType<typeof vi.fn>;
246
- let mockSavePhaseState: ReturnType<typeof vi.fn>;
247
- let mockTddLog: ReturnType<typeof vi.fn>;
248
-
249
- function makeDeps(overrides = {}) {
250
- return {
251
- loadTddState: mockLoadTddState,
252
- resetGit: mockResetGit,
253
- snapshot: mockSnapshot,
254
- savePhaseState: mockSavePhaseState,
255
- tddLog: mockTddLog,
256
- ...overrides,
257
- };
258
- }
259
-
260
- beforeEach(() => {
261
- mockLoadTddState = vi.fn();
262
- mockResetGit = vi.fn();
263
- mockSnapshot = vi.fn().mockReturnValue("hash123");
264
- mockSavePhaseState = vi.fn();
265
- mockTddLog = vi.fn();
266
- });
267
-
268
- it("nukes git, re-inits, snapshots, resets state to RED disabled", async () => {
269
- mockLoadTddState.mockReturnValue({
270
- ok: true,
271
- state: { enabled: true, current: "green" },
272
- config,
273
- });
274
-
275
- const ctx = makeCtx();
276
- await handleTddReset(ctx, makeDeps());
277
-
278
- expect(mockResetGit).toHaveBeenCalledWith("/test");
279
- expect(mockSnapshot).toHaveBeenCalledWith("/test", "red");
280
- expect(mockSavePhaseState).toHaveBeenCalledWith("/test", {
281
- enabled: false,
282
- current: "red",
283
- });
284
- expect(ctx.notifications).toHaveLength(1);
285
- expect(ctx.notifications[0].message).toContain("reset");
286
- expect(ctx.notifications[0].type).toBe("warning");
287
- });
288
-
289
- it("shows error when setup invalid", async () => {
290
- mockLoadTddState.mockReturnValue({
291
- ok: false,
292
- reason: "Missing .pi/tdd/",
293
- });
294
-
295
- const ctx = makeCtx();
296
- await handleTddReset(ctx, makeDeps());
297
-
298
- expect(ctx.notifications).toHaveLength(1);
299
- expect(ctx.notifications[0].message).toContain("Missing .pi/tdd/");
300
- expect(ctx.notifications[0].type).toBe("error");
301
- });
253
+ let mockLoadTddState: ReturnType<typeof vi.fn>;
254
+ let mockResetGit: ReturnType<typeof vi.fn>;
255
+ let mockSnapshot: ReturnType<typeof vi.fn>;
256
+ let mockSavePhaseState: ReturnType<typeof vi.fn>;
257
+ let mockTddLog: ReturnType<typeof vi.fn>;
258
+
259
+ function makeDeps(overrides = {}) {
260
+ return {
261
+ loadTddState: mockLoadTddState,
262
+ resetGit: mockResetGit,
263
+ snapshot: mockSnapshot,
264
+ savePhaseState: mockSavePhaseState,
265
+ tddLog: mockTddLog,
266
+ ...overrides,
267
+ };
268
+ }
269
+
270
+ beforeEach(() => {
271
+ mockLoadTddState = vi.fn();
272
+ mockResetGit = vi.fn();
273
+ mockSnapshot = vi.fn().mockReturnValue("hash123");
274
+ mockSavePhaseState = vi.fn();
275
+ mockTddLog = vi.fn();
276
+ });
277
+
278
+ it("nukes git, re-inits, snapshots, resets state to RED disabled", async () => {
279
+ mockLoadTddState.mockReturnValue({
280
+ ok: true,
281
+ state: { enabled: true, current: "green" },
282
+ config,
283
+ });
284
+
285
+ const ctx = makeCtx();
286
+ await handleTddReset(ctx, makeDeps());
287
+
288
+ expect(mockResetGit).toHaveBeenCalledWith("/test");
289
+ expect(mockSnapshot).toHaveBeenCalledWith("/test", "red");
290
+ expect(mockSavePhaseState).toHaveBeenCalledWith("/test", {
291
+ enabled: false,
292
+ current: "red",
293
+ });
294
+ expect(ctx.notifications).toHaveLength(1);
295
+ expect(ctx.notifications[0].message).toContain("reset");
296
+ expect(ctx.notifications[0].type).toBe("warning");
297
+ });
298
+
299
+ it("shows error when setup invalid", async () => {
300
+ mockLoadTddState.mockReturnValue({
301
+ ok: false,
302
+ reason: "Missing .pi/tdd/",
303
+ });
304
+
305
+ const ctx = makeCtx();
306
+ await handleTddReset(ctx, makeDeps());
307
+
308
+ expect(ctx.notifications).toHaveLength(1);
309
+ expect(ctx.notifications[0].message).toContain("Missing .pi/tdd/");
310
+ expect(ctx.notifications[0].type).toBe("error");
311
+ });
302
312
  });