jsdoczoom 0.4.11 → 0.4.13

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.
Files changed (61) hide show
  1. package/dist/{src/drilldown.js → drilldown.js} +9 -6
  2. package/package.json +2 -2
  3. package/dist/test/barrel.test.js +0 -77
  4. package/dist/test/cache.test.js +0 -222
  5. package/dist/test/cli.test.js +0 -479
  6. package/dist/test/drilldown-barrel.test.js +0 -383
  7. package/dist/test/drilldown.test.js +0 -469
  8. package/dist/test/errors.test.js +0 -26
  9. package/dist/test/eslint-engine.test.js +0 -130
  10. package/dist/test/eslint-plugin.test.js +0 -291
  11. package/dist/test/file-discovery.test.js +0 -72
  12. package/dist/test/jsdoc-parser.test.js +0 -353
  13. package/dist/test/lint.test.js +0 -413
  14. package/dist/test/selector.test.js +0 -93
  15. package/dist/test/type-declarations.test.js +0 -321
  16. package/dist/test/validate.test.js +0 -361
  17. package/types/test/barrel.test.d.ts +0 -1
  18. package/types/test/cache.test.d.ts +0 -8
  19. package/types/test/cli.test.d.ts +0 -1
  20. package/types/test/drilldown-barrel.test.d.ts +0 -1
  21. package/types/test/drilldown.test.d.ts +0 -1
  22. package/types/test/errors.test.d.ts +0 -1
  23. package/types/test/eslint-engine.test.d.ts +0 -6
  24. package/types/test/eslint-plugin.test.d.ts +0 -1
  25. package/types/test/file-discovery.test.d.ts +0 -1
  26. package/types/test/jsdoc-parser.test.d.ts +0 -1
  27. package/types/test/lint.test.d.ts +0 -9
  28. package/types/test/selector.test.d.ts +0 -1
  29. package/types/test/type-declarations.test.d.ts +0 -1
  30. package/types/test/validate.test.d.ts +0 -1
  31. /package/dist/{src/barrel.js → barrel.js} +0 -0
  32. /package/dist/{src/cache.js → cache.js} +0 -0
  33. /package/dist/{src/cli.js → cli.js} +0 -0
  34. /package/dist/{src/errors.js → errors.js} +0 -0
  35. /package/dist/{src/eslint-engine.js → eslint-engine.js} +0 -0
  36. /package/dist/{src/eslint-plugin.js → eslint-plugin.js} +0 -0
  37. /package/dist/{src/file-discovery.js → file-discovery.js} +0 -0
  38. /package/dist/{src/index.js → index.js} +0 -0
  39. /package/dist/{src/jsdoc-parser.js → jsdoc-parser.js} +0 -0
  40. /package/dist/{src/lint.js → lint.js} +0 -0
  41. /package/dist/{src/selector.js → selector.js} +0 -0
  42. /package/dist/{src/skill-text.js → skill-text.js} +0 -0
  43. /package/dist/{src/type-declarations.js → type-declarations.js} +0 -0
  44. /package/dist/{src/types.js → types.js} +0 -0
  45. /package/dist/{src/validate.js → validate.js} +0 -0
  46. /package/types/{src/barrel.d.ts → barrel.d.ts} +0 -0
  47. /package/types/{src/cache.d.ts → cache.d.ts} +0 -0
  48. /package/types/{src/cli.d.ts → cli.d.ts} +0 -0
  49. /package/types/{src/drilldown.d.ts → drilldown.d.ts} +0 -0
  50. /package/types/{src/errors.d.ts → errors.d.ts} +0 -0
  51. /package/types/{src/eslint-engine.d.ts → eslint-engine.d.ts} +0 -0
  52. /package/types/{src/eslint-plugin.d.ts → eslint-plugin.d.ts} +0 -0
  53. /package/types/{src/file-discovery.d.ts → file-discovery.d.ts} +0 -0
  54. /package/types/{src/index.d.ts → index.d.ts} +0 -0
  55. /package/types/{src/jsdoc-parser.d.ts → jsdoc-parser.d.ts} +0 -0
  56. /package/types/{src/lint.d.ts → lint.d.ts} +0 -0
  57. /package/types/{src/selector.d.ts → selector.d.ts} +0 -0
  58. /package/types/{src/skill-text.d.ts → skill-text.d.ts} +0 -0
  59. /package/types/{src/type-declarations.d.ts → type-declarations.d.ts} +0 -0
  60. /package/types/{src/types.d.ts → types.d.ts} +0 -0
  61. /package/types/{src/validate.d.ts → validate.d.ts} +0 -0
@@ -1,479 +0,0 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import { dirname, resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
- import { main } from "../src/cli.js";
7
-
8
- /**
9
- * Verifies flag parsing, selector dispatch, stdin piping, JSON output
10
- * formatting, exit codes, and error serialization by capturing stdout
11
- * and stderr writes during CLI invocation.
12
- *
13
- * @summary Integration tests for CLI argument parsing, output format, and exit codes
14
- */
15
- const __dirname = dirname(fileURLToPath(import.meta.url));
16
- const fixturesDir = resolve(__dirname, "fixtures");
17
- const leafFilesDir = resolve(fixturesDir, "leaf-files");
18
- const depthDir = resolve(fixturesDir, "depth-advancement");
19
- /**
20
- * Helper to capture stdout and stderr writes during a test.
21
- */
22
- function captureOutput() {
23
- const stdout = [];
24
- const stderr = [];
25
- const origStdoutWrite = process.stdout.write;
26
- const origStderrWrite = process.stderr.write;
27
- process.stdout.write = (chunk) => {
28
- stdout.push(
29
- typeof chunk === "string" ? chunk : Buffer.from(chunk).toString(),
30
- );
31
- return true;
32
- };
33
- process.stderr.write = (chunk) => {
34
- stderr.push(
35
- typeof chunk === "string" ? chunk : Buffer.from(chunk).toString(),
36
- );
37
- return true;
38
- };
39
- return {
40
- stdout,
41
- stderr,
42
- restore: () => {
43
- process.stdout.write = origStdoutWrite;
44
- process.stderr.write = origStderrWrite;
45
- },
46
- getStdout: () => stdout.join(""),
47
- getStderr: () => stderr.join(""),
48
- };
49
- }
50
- /** Extract the key from an output item (either next_id or id). */
51
- function itemKey(item) {
52
- if ("next_id" in item && typeof item.next_id === "string")
53
- return item.next_id;
54
- if ("id" in item && typeof item.id === "string") return item.id;
55
- return "";
56
- }
57
- /** Extract path from an output item key. */
58
- function pathFromItem(item) {
59
- const key = itemKey(item);
60
- const atIndex = key.lastIndexOf("@");
61
- return atIndex === -1 ? key : key.substring(0, atIndex);
62
- }
63
- describe("cli", () => {
64
- let capture;
65
- let cwdSpy;
66
- beforeEach(() => {
67
- process.exitCode = 0;
68
- capture = captureOutput();
69
- cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(leafFilesDir);
70
- });
71
- afterEach(() => {
72
- capture.restore();
73
- cwdSpy.mockRestore();
74
- process.exitCode = 0;
75
- });
76
- describe("flags", () => {
77
- it("--help prints help text and exits 0", async () => {
78
- await main(["--help"]);
79
- expect(capture.getStdout()).toContain("Usage: jsdoczoom");
80
- expect(process.exitCode).toBe(0);
81
- });
82
- it("-h prints help text and exits 0", async () => {
83
- await main(["-h"]);
84
- expect(capture.getStdout()).toContain("Usage: jsdoczoom");
85
- expect(process.exitCode).toBe(0);
86
- });
87
- it("-c runs check (validation) mode", async () => {
88
- await main(["-c", "two-summaries.ts"]);
89
- const output = JSON.parse(capture.getStdout());
90
- expect(output.success).toBe(true);
91
- expect(output.message).toBe("All files passed validation");
92
- expect(process.exitCode).toBe(0);
93
- });
94
- it("--check runs check (validation) mode", async () => {
95
- await main(["--check", "two-summaries.ts"]);
96
- const output = JSON.parse(capture.getStdout());
97
- expect(output.success).toBe(true);
98
- });
99
- it("-v prints version number", async () => {
100
- await main(["-v"]);
101
- const output = capture.getStdout().trim();
102
- expect(output).toMatch(/^\d+\.\d+\.\d+$/);
103
- expect(process.exitCode).toBe(0);
104
- });
105
- it("--version prints version number", async () => {
106
- await main(["--version"]);
107
- const output = capture.getStdout().trim();
108
- expect(output).toMatch(/^\d+\.\d+\.\d+$/);
109
- expect(process.exitCode).toBe(0);
110
- });
111
- it("--pretty outputs indented JSON (2-space indent)", async () => {
112
- await main(["--pretty", "two-summaries.ts"]);
113
- const raw = capture.getStdout();
114
- // Pretty JSON starts with "{\n" and contains indented lines
115
- expect(raw).toContain("\n ");
116
- const parsed = JSON.parse(raw);
117
- // Verify it's valid JSON object with items and truncated
118
- expect(parsed).toHaveProperty("items");
119
- expect(parsed).toHaveProperty("truncated");
120
- // Verify the indentation matches 2-space indent
121
- expect(raw.trimEnd()).toBe(JSON.stringify(parsed, null, 2));
122
- });
123
- it("default output is compact JSON (no extra whitespace)", async () => {
124
- await main(["two-summaries.ts"]);
125
- const raw = capture.getStdout();
126
- const parsed = JSON.parse(raw);
127
- // Compact JSON: single line, no indentation
128
- expect(raw.trimEnd()).toBe(JSON.stringify(parsed));
129
- });
130
- it("--pretty works with check mode", async () => {
131
- await main(["--pretty", "-c", "two-summaries.ts"]);
132
- const raw = capture.getStdout();
133
- const parsed = JSON.parse(raw);
134
- expect(parsed.success).toBe(true);
135
- expect(raw.trimEnd()).toBe(JSON.stringify(parsed, null, 2));
136
- });
137
- });
138
- describe("selectors", () => {
139
- it("bare invocation (no args, no stdin) uses default selector", async () => {
140
- cwdSpy.mockReturnValue(depthDir);
141
- await main([]);
142
- const output = JSON.parse(capture.getStdout());
143
- expect(output).toHaveProperty("items");
144
- // depth-advancement dir has one-summary.ts and three-summaries.ts
145
- expect(output.items.length).toBeGreaterThanOrEqual(2);
146
- const paths = output.items.map((e) => pathFromItem(e));
147
- expect(paths).toContain("one-summary.ts");
148
- expect(paths).toContain("three-summaries.ts");
149
- });
150
- it("directory path discovers files and produces drilldown output", async () => {
151
- cwdSpy.mockReturnValue(fixturesDir);
152
- await main(["leaf-files"]);
153
- const output = JSON.parse(capture.getStdout());
154
- expect(output).toHaveProperty("items");
155
- expect(output.items.length).toBeGreaterThan(0);
156
- });
157
- it("selector argument is parsed and passed to drilldown", async () => {
158
- await main(["two-summaries.ts@2"]);
159
- const output = JSON.parse(capture.getStdout());
160
- expect(output).toHaveProperty("items");
161
- expect(output.items).toHaveLength(1);
162
- // Level 2 (description) has next_id pointing to @3
163
- expect(output.items[0].next_id).toBe("two-summaries.ts@3");
164
- });
165
- });
166
- describe("stdin", () => {
167
- it("when stdin is provided, file paths are read one per line", async () => {
168
- const stdin = "two-summaries.ts\none-summary.ts";
169
- await main([], stdin);
170
- const output = JSON.parse(capture.getStdout());
171
- expect(output).toHaveProperty("items");
172
- expect(output.items.length).toBe(2);
173
- });
174
- it("stdin paths are processed via drilldownFiles", async () => {
175
- const stdin = "two-summaries.ts";
176
- await main([], stdin);
177
- const output = JSON.parse(capture.getStdout());
178
- expect(output).toHaveProperty("items");
179
- expect(output.items).toHaveLength(1);
180
- expect(pathFromItem(output.items[0])).toBe("two-summaries.ts");
181
- });
182
- it("stdin with -c processes via validateFiles", async () => {
183
- const stdin = "two-summaries.ts\none-summary.ts";
184
- await main(["-c"], stdin);
185
- const output = JSON.parse(capture.getStdout());
186
- expect(output.success).toBe(true);
187
- });
188
- it("stdin combined with @depth suffix applies depth to all paths", async () => {
189
- const stdin = "two-summaries.ts";
190
- await main(["@2"], stdin);
191
- const output = JSON.parse(capture.getStdout());
192
- expect(output).toHaveProperty("items");
193
- expect(output.items).toHaveLength(1);
194
- // Level 2 (description) → next_id points to @3
195
- expect(output.items[0].next_id).toBe("two-summaries.ts@3");
196
- });
197
- it("stdin ignores blank lines", async () => {
198
- const stdin = "two-summaries.ts\n\n\none-summary.ts\n";
199
- await main([], stdin);
200
- const output = JSON.parse(capture.getStdout());
201
- expect(output.items.length).toBe(2);
202
- });
203
- it("stdin trims whitespace from paths", async () => {
204
- const stdin = " two-summaries.ts \n one-summary.ts ";
205
- await main([], stdin);
206
- const output = JSON.parse(capture.getStdout());
207
- expect(output.items.length).toBe(2);
208
- });
209
- it("when both a full selector and stdin are provided, stdin takes priority (depth still extracted from arg)", async () => {
210
- // Provide a selector with a pattern and depth; stdin takes priority for file list
211
- // but depth should be extracted from the selector argument
212
- const stdin = "two-summaries.ts";
213
- await main(["some-other-pattern.ts@2"], stdin);
214
- const output = JSON.parse(capture.getStdout());
215
- expect(output).toHaveProperty("items");
216
- expect(output.items).toHaveLength(1);
217
- // Depth 2 should be applied from the selector argument → next_id @3
218
- expect(output.items[0].next_id).toBe("two-summaries.ts@3");
219
- });
220
- });
221
- describe("errors", () => {
222
- it("error JSON { error: { code, message } } written to stderr on failure", async () => {
223
- await main(["nonexistent-file-that-does-not-exist.ts"]);
224
- const errOutput = capture.getStderr();
225
- const parsed = JSON.parse(errOutput);
226
- expect(parsed).toHaveProperty("error");
227
- expect(parsed.error).toHaveProperty("code");
228
- expect(parsed.error).toHaveProperty("message");
229
- });
230
- it("exit code 1 on errors", async () => {
231
- await main(["nonexistent-file-that-does-not-exist.ts"]);
232
- expect(process.exitCode).toBe(1);
233
- });
234
- it("exit code 2 on validation failure", async () => {
235
- // description-only.ts has no @summary tag, which fails validation
236
- await main(["-c", "description-only.ts"]);
237
- expect(process.exitCode).toBe(2);
238
- // Stdout should have VALIDATION_FAILED error nested in the result
239
- const output = JSON.parse(capture.getStdout());
240
- expect(output.error.code).toBe("VALIDATION_FAILED");
241
- });
242
- it("exit code 0 on success", async () => {
243
- await main(["two-summaries.ts"]);
244
- expect(process.exitCode).toBe(0);
245
- });
246
- it("unexpected exceptions caught as INTERNAL_ERROR", async () => {
247
- // Mock drilldown to throw a non-JsdocError
248
- cwdSpy.mockImplementation(() => {
249
- throw new TypeError("something unexpected");
250
- });
251
- await main(["two-summaries.ts"]);
252
- const errOutput = capture.getStderr();
253
- const parsed = JSON.parse(errOutput);
254
- expect(parsed.error.code).toBe("INTERNAL_ERROR");
255
- expect(parsed.error.message).toBe("something unexpected");
256
- expect(process.exitCode).toBe(1);
257
- });
258
- it("error output on stderr is always compact JSON regardless of --pretty", async () => {
259
- await main(["--pretty", "nonexistent-file-that-does-not-exist.ts"]);
260
- const errOutput = capture.getStderr();
261
- // Should be compact (single line, no indentation)
262
- expect(errOutput.trimEnd().split("\n")).toHaveLength(1);
263
- const parsed = JSON.parse(errOutput);
264
- expect(errOutput.trimEnd()).toBe(JSON.stringify(parsed));
265
- });
266
- });
267
- describe("output", () => {
268
- it("normal mode output is valid JSON object on stdout", async () => {
269
- await main(["two-summaries.ts"]);
270
- const output = JSON.parse(capture.getStdout());
271
- expect(output).toHaveProperty("items");
272
- expect(output).toHaveProperty("truncated");
273
- expect(output.items).toHaveLength(1);
274
- // Item should have either next_id or id, plus text
275
- const item = output.items[0];
276
- expect(item).toHaveProperty("text");
277
- expect("next_id" in item || "id" in item).toBe(true);
278
- });
279
- it("validation mode output is valid JSON object on stdout", async () => {
280
- await main(["-c", "two-summaries.ts"]);
281
- const output = JSON.parse(capture.getStdout());
282
- expect(typeof output).toBe("object");
283
- expect(Array.isArray(output)).toBe(false);
284
- expect(output.success).toBe(true);
285
- expect(output.message).toBe("All files passed validation");
286
- });
287
- it("output always ends with a newline", async () => {
288
- await main(["two-summaries.ts"]);
289
- const raw = capture.getStdout();
290
- expect(raw.endsWith("\n")).toBe(true);
291
- });
292
- it("validation success writes result to stdout only, no stderr", async () => {
293
- await main(["-c", "two-summaries.ts"]);
294
- const output = JSON.parse(capture.getStdout());
295
- expect(output.success).toBe(true);
296
- expect(capture.getStderr()).toBe("");
297
- expect(process.exitCode).toBe(0);
298
- });
299
- it("validation failure writes single consolidated result to stdout", async () => {
300
- await main(["-c", "description-only.ts"]);
301
- // stdout has the validation result with groups, success: false, and nested error
302
- const output = JSON.parse(capture.getStdout());
303
- expect(output.success).toBe(false);
304
- expect(output.missing_summary).toBeDefined();
305
- expect(output.missing_summary.files).toEqual(["description-only.ts"]);
306
- // Error metadata is nested in the stdout result, not on stderr
307
- expect(output.error.code).toBe("VALIDATION_FAILED");
308
- expect(capture.getStderr()).toBe("");
309
- expect(process.exitCode).toBe(2);
310
- });
311
- });
312
- describe("lint mode", () => {
313
- /** A fully valid file that passes all lint rules */
314
- const VALID_LINT_FILE = [
315
- "/**",
316
- " * File description explaining the purpose of this module.",
317
- " * @summary File summary for the module",
318
- " */",
319
- "",
320
- "/** The primary numeric value used for calculations */",
321
- "export const x = 1;",
322
- "",
323
- ].join("\n");
324
- /**
325
- * Create a temp dir with a valid file and mock cwd to it.
326
- * Returns the directory path.
327
- */
328
- function setupCleanLintDir() {
329
- const tmpDir = fs.mkdtempSync(
330
- resolve(os.tmpdir(), "jsdoczoom-cli-lint-"),
331
- );
332
- fs.writeFileSync(resolve(tmpDir, "clean.ts"), VALID_LINT_FILE);
333
- cwdSpy.mockReturnValue(tmpDir);
334
- return tmpDir;
335
- }
336
- it("-l flag runs lint mode", async () => {
337
- // two-summaries.ts has an export without JSDoc, so lint will find issues
338
- await main(["-l", "two-summaries.ts"]);
339
- const output = JSON.parse(capture.getStdout());
340
- expect(output).toHaveProperty("files");
341
- expect(output).toHaveProperty("summary");
342
- expect(output.summary).toHaveProperty("totalFiles");
343
- expect(output.summary).toHaveProperty("filesWithIssues");
344
- expect(output.summary).toHaveProperty("totalDiagnostics");
345
- });
346
- it("--lint flag runs lint mode", async () => {
347
- await main(["--lint", "two-summaries.ts"]);
348
- const output = JSON.parse(capture.getStdout());
349
- expect(output).toHaveProperty("files");
350
- expect(output).toHaveProperty("summary");
351
- });
352
- it("lint success exits 0", async () => {
353
- const tmpDir = setupCleanLintDir();
354
- try {
355
- await main(["-l", "clean.ts"]);
356
- expect(process.exitCode).toBe(0);
357
- } finally {
358
- fs.rmSync(tmpDir, { recursive: true });
359
- }
360
- });
361
- it("lint failure exits 2", async () => {
362
- // description-only.ts has no @summary, which triggers lint rules
363
- await main(["-l", "description-only.ts"]);
364
- expect(process.exitCode).toBe(2);
365
- });
366
- it("-l output is valid JSON with files and summary", async () => {
367
- await main(["-l", "description-only.ts"]);
368
- const output = JSON.parse(capture.getStdout());
369
- expect(Array.isArray(output.files)).toBe(true);
370
- expect(typeof output.summary.totalFiles).toBe("number");
371
- expect(typeof output.summary.filesWithIssues).toBe("number");
372
- expect(typeof output.summary.totalDiagnostics).toBe("number");
373
- // Should have diagnostics since export const value lacks JSDoc
374
- expect(output.files.length).toBeGreaterThan(0);
375
- expect(output.files[0]).toHaveProperty("filePath");
376
- expect(output.files[0]).toHaveProperty("diagnostics");
377
- expect(Array.isArray(output.files[0].diagnostics)).toBe(true);
378
- });
379
- it("-c and -l together produces error", async () => {
380
- await main(["-c", "-l", "two-summaries.ts"]);
381
- const errOutput = capture.getStderr();
382
- const parsed = JSON.parse(errOutput);
383
- expect(parsed.error.code).toBe("INVALID_SELECTOR");
384
- expect(parsed.error.message).toContain("Cannot use -c and -l together");
385
- expect(process.exitCode).toBe(1);
386
- });
387
- it("stdin piped paths with -l flag", async () => {
388
- const stdin = "two-summaries.ts\none-summary.ts";
389
- await main(["-l"], stdin);
390
- const output = JSON.parse(capture.getStdout());
391
- expect(output).toHaveProperty("files");
392
- expect(output).toHaveProperty("summary");
393
- expect(output.summary.totalFiles).toBe(2);
394
- });
395
- it("--pretty with -l flag outputs indented JSON", async () => {
396
- await main(["--pretty", "-l", "two-summaries.ts"]);
397
- const raw = capture.getStdout();
398
- expect(raw).toContain("\n ");
399
- const parsed = JSON.parse(raw);
400
- expect(raw.trimEnd()).toBe(JSON.stringify(parsed, null, 2));
401
- expect(parsed).toHaveProperty("files");
402
- expect(parsed).toHaveProperty("summary");
403
- });
404
- it("--no-gitignore with -l flag", async () => {
405
- const tmpDir = setupCleanLintDir();
406
- try {
407
- await main(["-l", "--no-gitignore", "*.ts"]);
408
- const output = JSON.parse(capture.getStdout());
409
- expect(output).toHaveProperty("files");
410
- expect(output).toHaveProperty("summary");
411
- expect(process.exitCode).toBe(0);
412
- } finally {
413
- fs.rmSync(tmpDir, { recursive: true });
414
- }
415
- });
416
- it("help text includes -c/--check and -l/--lint options", async () => {
417
- await main(["--help"]);
418
- const helpText = capture.getStdout();
419
- expect(helpText).toContain("-c, --check");
420
- expect(helpText).toContain("-l, --lint");
421
- expect(helpText).toContain("comprehensive JSDoc quality");
422
- });
423
- it("lint clean result writes only to stdout, no stderr", async () => {
424
- const tmpDir = setupCleanLintDir();
425
- try {
426
- await main(["-l", "clean.ts"]);
427
- expect(capture.getStderr()).toBe("");
428
- expect(process.exitCode).toBe(0);
429
- } finally {
430
- fs.rmSync(tmpDir, { recursive: true });
431
- }
432
- });
433
- it("lint output always ends with a newline", async () => {
434
- await main(["-l", "two-summaries.ts"]);
435
- const raw = capture.getStdout();
436
- expect(raw.endsWith("\n")).toBe(true);
437
- });
438
- });
439
- describe("--explain-rule", () => {
440
- it("prints explanation for known rule", async () => {
441
- await main(["--explain-rule", "jsdoc/informative-docs"]);
442
- const output = capture.getStdout();
443
- expect(output).toContain("jsdoc/informative-docs");
444
- expect(output).toContain("Passes with");
445
- expect(process.exitCode).toBe(0);
446
- });
447
- it("errors for unknown rule", async () => {
448
- await main(["--explain-rule", "nonexistent/rule"]);
449
- const errOutput = capture.getStderr();
450
- expect(errOutput).toContain("Unknown rule");
451
- expect(errOutput).toContain("nonexistent/rule");
452
- expect(process.exitCode).toBe(1);
453
- });
454
- });
455
- describe("cache flags", () => {
456
- it("--disable-cache flag is accepted without error", async () => {
457
- await main(["--disable-cache", "two-summaries.ts"]);
458
- const output = JSON.parse(capture.getStdout());
459
- expect(output).toHaveProperty("items");
460
- expect(process.exitCode).toBe(0);
461
- });
462
- it("--cache-directory flag is accepted without error", async () => {
463
- await main([
464
- "--cache-directory",
465
- "/tmp/custom-cache",
466
- "two-summaries.ts",
467
- ]);
468
- const output = JSON.parse(capture.getStdout());
469
- expect(output).toHaveProperty("items");
470
- expect(process.exitCode).toBe(0);
471
- });
472
- it("help text includes cache flags", async () => {
473
- await main(["--help"]);
474
- const helpText = capture.getStdout();
475
- expect(helpText).toContain("--disable-cache");
476
- expect(helpText).toContain("--cache-directory");
477
- });
478
- });
479
- });