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,469 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { dirname, relative, resolve } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { describe, expect, it } from "vitest";
5
- import { drilldown, drilldownFiles } from "../src/drilldown.js";
6
- import { JsdocError } from "../src/errors.js";
7
-
8
- /**
9
- * Verifies the fixed 4-level drill-down model (summary, description, type
10
- * declarations, full file) for both path and glob selectors. Levels are
11
- * 1-indexed. Covers depth advancement, null-level skipping, clamping,
12
- * alphabetical ordering, PARSE_ERROR handling in glob vs path contexts,
13
- * and drilldownFiles behavior.
14
- *
15
- * @summary Tests for progressive drill-down depth advancement and file processing
16
- */
17
- const __dirname = dirname(fileURLToPath(import.meta.url));
18
- const fixturesDir = resolve(__dirname, "fixtures");
19
- const leafFilesDir = resolve(fixturesDir, "leaf-files");
20
- const depthDir = resolve(fixturesDir, "depth-advancement");
21
- /** Cache disabled for test isolation. */
22
- const NO_CACHE = { enabled: false, directory: "" };
23
- function pathSelector(pattern, depth) {
24
- return { type: "path", pattern, depth };
25
- }
26
- function globSelector(pattern, depth) {
27
- return { type: "glob", pattern, depth };
28
- }
29
- function isOutputItem(entry) {
30
- return "text" in entry;
31
- }
32
- function isOutputErrorItem(entry) {
33
- return "error" in entry;
34
- }
35
- function hasNextId(entry) {
36
- return "next_id" in entry;
37
- }
38
- function hasId(entry) {
39
- return "id" in entry;
40
- }
41
- /** Extract the key (next_id or id) from an entry for path extraction and sorting. */
42
- function entryKey(entry) {
43
- if ("next_id" in entry) return entry.next_id;
44
- return entry.id;
45
- }
46
- function pathFromEntry(entry) {
47
- const key = entryKey(entry);
48
- const atIndex = key.lastIndexOf("@");
49
- return atIndex === -1 ? key : key.substring(0, atIndex);
50
- }
51
- describe("drilldown", () => {
52
- // Fixed levels (1-indexed): 1=summary, 2=description, 3=type declarations, 4=full file
53
- // one-summary.ts has @summary + description → all 4 levels populated
54
- it("path selector returns summary with next_id pointing to level 2", async () => {
55
- const results = await drilldown(
56
- pathSelector("one-summary.ts"),
57
- leafFilesDir,
58
- true,
59
- 100,
60
- NO_CACHE,
61
- );
62
- expect(results.items).toHaveLength(1);
63
- const entry = results.items[0];
64
- expect(isOutputItem(entry)).toBe(true);
65
- expect(hasNextId(entry)).toBe(true);
66
- if (hasNextId(entry)) {
67
- expect(entry.next_id).toBe("one-summary.ts@2");
68
- expect(entry.text).toBe("Single summary line");
69
- }
70
- });
71
- it("depth 2 returns description with next_id pointing to level 3", async () => {
72
- // one-summary.ts has @summary + description:
73
- // depth 1 = summary, depth 2 = description
74
- const results = await drilldown(
75
- pathSelector("one-summary.ts", 2),
76
- leafFilesDir,
77
- true,
78
- 100,
79
- NO_CACHE,
80
- );
81
- expect(results.items).toHaveLength(1);
82
- const entry = results.items[0];
83
- expect(isOutputItem(entry)).toBe(true);
84
- expect(hasNextId(entry)).toBe(true);
85
- if (hasNextId(entry)) {
86
- expect(entry.next_id).toBe("one-summary.ts@3");
87
- expect(entry.text).toBe("Module description as free-text.");
88
- }
89
- });
90
- it("depth 3 returns type declarations with next_id pointing to level 4", async () => {
91
- // one-summary.ts: summary + description → type declarations is depth 3
92
- const results = await drilldown(
93
- pathSelector("one-summary.ts", 3),
94
- leafFilesDir,
95
- true,
96
- 100,
97
- NO_CACHE,
98
- );
99
- expect(results.items).toHaveLength(1);
100
- const entry = results.items[0];
101
- expect(isOutputItem(entry)).toBe(true);
102
- expect(hasNextId(entry)).toBe(true);
103
- if (hasNextId(entry)) {
104
- expect(entry.next_id).toBe("one-summary.ts@4");
105
- // Type declarations output — not full file content
106
- expect(entry.text).not.toBe(
107
- readFileSync(resolve(leafFilesDir, "one-summary.ts"), "utf-8"),
108
- );
109
- }
110
- });
111
- it("depth 4 returns full file content (terminal, has id)", async () => {
112
- // one-summary.ts: terminal level is 4
113
- const results = await drilldown(
114
- pathSelector("one-summary.ts", 4),
115
- leafFilesDir,
116
- true,
117
- 100,
118
- NO_CACHE,
119
- );
120
- expect(results.items).toHaveLength(1);
121
- const entry = results.items[0];
122
- expect(isOutputItem(entry)).toBe(true);
123
- expect(hasNextId(entry)).toBe(false);
124
- if (hasId(entry) && isOutputItem(entry)) {
125
- expect(entry.id).toBe("one-summary.ts@4");
126
- const expectedContent = readFileSync(
127
- resolve(leafFilesDir, "one-summary.ts"),
128
- "utf-8",
129
- );
130
- expect(entry.text).toBe(expectedContent);
131
- }
132
- });
133
- it("depth clamping (F@99 returns terminal level 4)", async () => {
134
- const results = await drilldown(
135
- pathSelector("one-summary.ts", 99),
136
- leafFilesDir,
137
- true,
138
- 100,
139
- NO_CACHE,
140
- );
141
- expect(results.items).toHaveLength(1);
142
- const entry = results.items[0];
143
- expect(isOutputItem(entry)).toBe(true);
144
- expect(hasNextId(entry)).toBe(false);
145
- if (hasId(entry) && isOutputItem(entry)) {
146
- expect(entry.id).toBe("one-summary.ts@4");
147
- const expectedContent = readFileSync(
148
- resolve(leafFilesDir, "one-summary.ts"),
149
- "utf-8",
150
- );
151
- expect(entry.text).toBe(expectedContent);
152
- }
153
- });
154
- it("file without description skips level 2, advancing to type declarations", async () => {
155
- // summary-only.ts has @summary but no description
156
- // Level 1 = summary, Level 2 = null (no description), Level 3 = type decls, Level 4 = full file
157
- const results1 = await drilldown(
158
- pathSelector("summary-only.ts", 1),
159
- leafFilesDir,
160
- true,
161
- 100,
162
- NO_CACHE,
163
- );
164
- expect(results1.items).toHaveLength(1);
165
- const entry1 = results1.items[0];
166
- expect(isOutputItem(entry1)).toBe(true);
167
- expect(hasNextId(entry1)).toBe(true);
168
- if (hasNextId(entry1)) {
169
- expect(entry1.next_id).toBe("summary-only.ts@2");
170
- expect(entry1.text).toBe("Summary without description");
171
- }
172
- // Depth 2 has no description, advances to level 3 (type declarations)
173
- const results2 = await drilldown(
174
- pathSelector("summary-only.ts", 2),
175
- leafFilesDir,
176
- true,
177
- 100,
178
- NO_CACHE,
179
- );
180
- expect(results2.items).toHaveLength(1);
181
- const entry2 = results2.items[0];
182
- expect(isOutputItem(entry2)).toBe(true);
183
- expect(hasNextId(entry2)).toBe(true);
184
- if (hasNextId(entry2)) {
185
- expect(entry2.next_id).toBe("summary-only.ts@4");
186
- }
187
- // Depth 99 clamps to terminal (level 4)
188
- const results99 = await drilldown(
189
- pathSelector("summary-only.ts", 99),
190
- leafFilesDir,
191
- true,
192
- 100,
193
- NO_CACHE,
194
- );
195
- expect(results99.items).toHaveLength(1);
196
- const entry99 = results99.items[0];
197
- expect(isOutputItem(entry99)).toBe(true);
198
- expect(hasNextId(entry99)).toBe(false);
199
- if (hasId(entry99) && isOutputItem(entry99)) {
200
- expect(entry99.id).toBe("summary-only.ts@4");
201
- }
202
- });
203
- it("glob returns all matching files at requested depth independently", async () => {
204
- // Use a glob that matches multiple files in leaf-files with summaries
205
- const results = await drilldown(
206
- globSelector("*-summary.ts"),
207
- leafFilesDir,
208
- true,
209
- 100,
210
- NO_CACHE,
211
- );
212
- // Should match one-summary.ts and two-summaries.ts (both have summaries)
213
- expect(results.items.length).toBeGreaterThanOrEqual(2);
214
- for (const entry of results.items) {
215
- expect(isOutputItem(entry)).toBe(true);
216
- // Files with summaries have next_id at depth 1
217
- expect(hasNextId(entry)).toBe(true);
218
- if (hasNextId(entry)) {
219
- expect(entry.next_id).toMatch(/@2$/);
220
- }
221
- }
222
- });
223
- it("glob includes files without summaries", async () => {
224
- const results = await drilldown(
225
- globSelector("*.ts"),
226
- leafFilesDir,
227
- true,
228
- 100,
229
- NO_CACHE,
230
- );
231
- const paths = results.items.map((r) => pathFromEntry(r));
232
- // no-jsdoc.ts has no JSDoc — still included (starts at type decls)
233
- expect(paths).toContain("no-jsdoc.ts");
234
- // description-only.ts has no @summary — still included (starts at description)
235
- expect(paths).toContain("description-only.ts");
236
- });
237
- it("glob NO_FILES_MATCHED when glob matches zero .ts/.tsx files on disk", async () => {
238
- await expect(
239
- drilldown(
240
- globSelector("nonexistent/**/*.ts"),
241
- leafFilesDir,
242
- true,
243
- 100,
244
- NO_CACHE,
245
- ),
246
- ).rejects.toThrow(JsdocError);
247
- try {
248
- await drilldown(
249
- globSelector("nonexistent/**/*.ts"),
250
- leafFilesDir,
251
- true,
252
- 100,
253
- NO_CACHE,
254
- );
255
- } catch (error) {
256
- expect(error).toBeInstanceOf(JsdocError);
257
- expect(error.code).toBe("NO_FILES_MATCHED");
258
- }
259
- });
260
- it("file without summaries advances from level 1 to level 3 (type declarations)", async () => {
261
- const results = await drilldown(
262
- globSelector("no-jsdoc.ts"),
263
- leafFilesDir,
264
- true,
265
- 100,
266
- NO_CACHE,
267
- );
268
- expect(results.items).toHaveLength(1);
269
- const entry = results.items[0];
270
- expect(isOutputItem(entry)).toBe(true);
271
- // No summary (level 1), no description (level 2) → advances to level 3 (type decls)
272
- expect(hasNextId(entry)).toBe(true);
273
- if (hasNextId(entry)) {
274
- expect(entry.next_id).toBe("no-jsdoc.ts@4");
275
- }
276
- });
277
- it("path selector on file without summaries advances to level 3", async () => {
278
- const results = await drilldown(
279
- pathSelector("no-jsdoc.ts"),
280
- leafFilesDir,
281
- true,
282
- 100,
283
- NO_CACHE,
284
- );
285
- expect(results.items).toHaveLength(1);
286
- const entry = results.items[0];
287
- expect(isOutputItem(entry)).toBe(true);
288
- expect(hasNextId(entry)).toBe(true);
289
- if (hasNextId(entry)) {
290
- expect(entry.next_id).toBe("no-jsdoc.ts@4");
291
- }
292
- });
293
- it("alphabetical ordering by path", async () => {
294
- const results = await drilldown(
295
- globSelector("*.ts"),
296
- leafFilesDir,
297
- true,
298
- 100,
299
- NO_CACHE,
300
- );
301
- const paths = results.items.map((r) => pathFromEntry(r));
302
- const sorted = [...paths].sort();
303
- expect(paths).toEqual(sorted);
304
- });
305
- it("PARSE_ERROR in glob context produces OutputErrorItem (partial results)", async () => {
306
- // Use a glob that matches both syntax-error.ts and valid files
307
- const results = await drilldown(
308
- globSelector("*.ts"),
309
- leafFilesDir,
310
- true,
311
- 100,
312
- NO_CACHE,
313
- );
314
- // Should have results (some valid files + the error entry for syntax-error.ts)
315
- expect(results.items.length).toBeGreaterThan(1);
316
- const errorEntry = results.items.find(
317
- (r) => hasId(r) && r.id === "syntax-error.ts",
318
- );
319
- expect(errorEntry).toBeDefined();
320
- if (errorEntry && isOutputErrorItem(errorEntry)) {
321
- // Error item id has no depth suffix
322
- expect(errorEntry.id).toBe("syntax-error.ts");
323
- expect(errorEntry.error.code).toBe("PARSE_ERROR");
324
- }
325
- // Valid files should still be present
326
- const validEntries = results.items.filter((r) => isOutputItem(r));
327
- expect(validEntries.length).toBeGreaterThan(0);
328
- });
329
- it("PARSE_ERROR for path selector throws fatal error", async () => {
330
- await expect(
331
- drilldown(
332
- pathSelector("syntax-error.ts"),
333
- leafFilesDir,
334
- true,
335
- 100,
336
- NO_CACHE,
337
- ),
338
- ).rejects.toThrow(JsdocError);
339
- try {
340
- await drilldown(
341
- pathSelector("syntax-error.ts"),
342
- leafFilesDir,
343
- true,
344
- 100,
345
- NO_CACHE,
346
- );
347
- } catch (error) {
348
- expect(error).toBeInstanceOf(JsdocError);
349
- expect(error.code).toBe("PARSE_ERROR");
350
- }
351
- });
352
- it("independent depth advancement (glob @2 returns description for both files)", async () => {
353
- // Both files have @summary + description
354
- // At depth 2, both show description (next_id → @3)
355
- const results = await drilldown(
356
- globSelector("*.ts", 2),
357
- depthDir,
358
- true,
359
- 100,
360
- NO_CACHE,
361
- );
362
- expect(results.items).toHaveLength(2);
363
- const oneSum = results.items.find(
364
- (r) => hasNextId(r) && r.next_id.startsWith("one-summary.ts@"),
365
- );
366
- const threeSum = results.items.find(
367
- (r) => hasNextId(r) && r.next_id.startsWith("three-summaries.ts@"),
368
- );
369
- expect(oneSum).toBeDefined();
370
- expect(threeSum).toBeDefined();
371
- if (oneSum && hasNextId(oneSum)) {
372
- expect(oneSum.next_id).toBe("one-summary.ts@3");
373
- }
374
- if (threeSum && hasNextId(threeSum)) {
375
- expect(threeSum.next_id).toBe("three-summaries.ts@3");
376
- }
377
- });
378
- it("../ path resolution uses resolved path in output", async () => {
379
- // From leaf-files, use ../ to access depth-advancement/one-summary.ts
380
- const selector = pathSelector("../depth-advancement/one-summary.ts", 1);
381
- const results = await drilldown(
382
- selector,
383
- leafFilesDir,
384
- true,
385
- 100,
386
- NO_CACHE,
387
- );
388
- expect(results.items).toHaveLength(1);
389
- const entry = results.items[0];
390
- expect(isOutputItem(entry)).toBe(true);
391
- if (hasNextId(entry)) {
392
- // The path should be relative to leafFilesDir, using ../
393
- expect(pathFromEntry(entry)).toBe(
394
- relative(leafFilesDir, resolve(depthDir, "one-summary.ts")),
395
- );
396
- expect(entry.next_id).toContain("@2");
397
- }
398
- });
399
- });
400
- describe("drilldownFiles", () => {
401
- it("processes explicit file list at given depth", async () => {
402
- const files = [resolve(leafFilesDir, "one-summary.ts")];
403
- const results = await drilldownFiles(files, 1, leafFilesDir, 100, NO_CACHE);
404
- expect(results.items).toHaveLength(1);
405
- const entry = results.items[0];
406
- expect(isOutputItem(entry)).toBe(true);
407
- if (hasNextId(entry)) {
408
- expect(entry.next_id).toBe("one-summary.ts@2");
409
- expect(entry.text).toBe("Single summary line");
410
- }
411
- });
412
- it("filters to .ts/.tsx only", async () => {
413
- const files = [
414
- resolve(leafFilesDir, "one-summary.ts"),
415
- resolve(leafFilesDir, "one-summary.js"), // Non-existent .js file should be filtered
416
- "/some/file.json",
417
- ];
418
- const results = await drilldownFiles(files, 1, leafFilesDir, 100, NO_CACHE);
419
- // Only the .ts file should be processed
420
- expect(results.items).toHaveLength(1);
421
- expect(pathFromEntry(results.items[0])).toBe("one-summary.ts");
422
- });
423
- it("includes files without summaries", async () => {
424
- const files = [
425
- resolve(leafFilesDir, "one-summary.ts"),
426
- resolve(leafFilesDir, "no-jsdoc.ts"),
427
- ];
428
- const results = await drilldownFiles(files, 1, leafFilesDir, 100, NO_CACHE);
429
- expect(results.items).toHaveLength(2);
430
- const paths = results.items.map((r) => pathFromEntry(r));
431
- expect(paths).toContain("one-summary.ts");
432
- expect(paths).toContain("no-jsdoc.ts");
433
- });
434
- it("each file advances independently through its own levels", async () => {
435
- const files = [
436
- resolve(depthDir, "one-summary.ts"),
437
- resolve(depthDir, "three-summaries.ts"),
438
- ];
439
- // Both files have @summary + description
440
- // At depth 2, both show description (next_id → @3)
441
- const results = await drilldownFiles(files, 2, depthDir, 100, NO_CACHE);
442
- expect(results.items).toHaveLength(2);
443
- const oneSum = results.items.find(
444
- (r) => hasNextId(r) && r.next_id.startsWith("one-summary.ts@"),
445
- );
446
- const threeSum = results.items.find(
447
- (r) => hasNextId(r) && r.next_id.startsWith("three-summaries.ts@"),
448
- );
449
- expect(oneSum).toBeDefined();
450
- expect(threeSum).toBeDefined();
451
- if (oneSum && hasNextId(oneSum)) {
452
- expect(oneSum.next_id).toBe("one-summary.ts@3");
453
- }
454
- if (threeSum && hasNextId(threeSum)) {
455
- expect(threeSum.next_id).toBe("three-summaries.ts@3");
456
- }
457
- });
458
- it("alphabetical ordering by path", async () => {
459
- const files = [
460
- resolve(leafFilesDir, "two-summaries.ts"),
461
- resolve(leafFilesDir, "one-summary.ts"),
462
- resolve(leafFilesDir, "exported-types.ts"),
463
- ];
464
- const results = await drilldownFiles(files, 1, leafFilesDir, 100, NO_CACHE);
465
- const paths = results.items.map((r) => pathFromEntry(r));
466
- const sorted = [...paths].sort();
467
- expect(paths).toEqual(sorted);
468
- });
469
- });
@@ -1,26 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { JsdocError } from "../src/errors.js";
3
-
4
- /**
5
- * Verifies that JsdocError stores code and message properties, extends
6
- * Error, and serializes to the documented JSON shape via toJSON().
7
- *
8
- * @summary Tests for JsdocError construction and JSON serialization
9
- */
10
- describe("JsdocError", () => {
11
- it("stores code and message properties", () => {
12
- const err = new JsdocError("PARSE_ERROR", "failed to parse");
13
- expect(err.code).toBe("PARSE_ERROR");
14
- expect(err.message).toBe("failed to parse");
15
- expect(err).toBeInstanceOf(Error);
16
- });
17
- it("toJSON() returns { error: { code, message } } shape", () => {
18
- const err = new JsdocError("FILE_NOT_FOUND", "not found");
19
- expect(err.toJSON()).toEqual({
20
- error: {
21
- code: "FILE_NOT_FOUND",
22
- message: "not found",
23
- },
24
- });
25
- });
26
- });
@@ -1,130 +0,0 @@
1
- /**
2
- * Tests for ESLint validation engine.
3
- *
4
- * @summary Unit and integration tests for eslint-engine module
5
- */
6
- import { describe, expect, it } from "vitest";
7
- import {
8
- createLintLinter,
9
- createValidationLinter,
10
- lintFileForLint,
11
- lintFileForValidation,
12
- mapToValidationStatus,
13
- } from "../src/eslint-engine.js";
14
-
15
- describe("mapToValidationStatus", () => {
16
- it("syntax error maps to syntax_error", () => {
17
- const messages = [{ ruleId: null, fatal: true }];
18
- expect(mapToValidationStatus(messages)).toBe("syntax_error");
19
- });
20
- it("missing JSDoc maps to missing_jsdoc", () => {
21
- const messages = [{ ruleId: "jsdoczoom/require-file-jsdoc" }];
22
- expect(mapToValidationStatus(messages)).toBe("missing_jsdoc");
23
- });
24
- it("missing summary maps to missing_summary", () => {
25
- const messages = [
26
- { ruleId: "jsdoczoom/require-file-summary", messageId: "missingSummary" },
27
- ];
28
- expect(mapToValidationStatus(messages)).toBe("missing_summary");
29
- });
30
- it("multiple summary maps to multiple_summary", () => {
31
- const messages = [
32
- {
33
- ruleId: "jsdoczoom/require-file-summary",
34
- messageId: "multipleSummary",
35
- },
36
- ];
37
- expect(mapToValidationStatus(messages)).toBe("multiple_summary");
38
- });
39
- it("missing description maps to missing_description", () => {
40
- const messages = [{ ruleId: "jsdoczoom/require-file-description" }];
41
- expect(mapToValidationStatus(messages)).toBe("missing_description");
42
- });
43
- it("valid file maps to valid", () => {
44
- const messages = [];
45
- expect(mapToValidationStatus(messages)).toBe("valid");
46
- });
47
- it("priority ordering - missing_jsdoc wins over missing_description", () => {
48
- const messages = [
49
- { ruleId: "jsdoczoom/require-file-description" },
50
- { ruleId: "jsdoczoom/require-file-jsdoc" },
51
- ];
52
- expect(mapToValidationStatus(messages)).toBe("missing_jsdoc");
53
- });
54
- });
55
- describe("ESLint integration tests", () => {
56
- const validationLinter = createValidationLinter();
57
- const lintLinter = createLintLinter();
58
- const validFile =
59
- "/** Description here.\n * @summary File summary\n */\nexport const x = 1;\n";
60
- const missingJsdoc = "export const x = 1;\n";
61
- const missingParam =
62
- "/** Desc.\n * @summary S\n */\nexport function foo(x: string): void {}\n";
63
- const missingReturns =
64
- "/** Desc.\n * @summary S\n */\nexport function foo(): string { return 'a'; }\n";
65
- const unusedVar =
66
- "/** This file demonstrates JSDoc-only constraint.\n * @summary JSDoc-only test\n */\nvar unused = 1;\n";
67
- const informativeDocs =
68
- "/** Desc.\n * @summary S\n */\n/** x */\nexport const x = 1;\n";
69
- it("createValidationLinter + lintFileForValidation: valid file returns empty messages", async () => {
70
- const messages = await lintFileForValidation(
71
- validationLinter,
72
- validFile,
73
- "test.ts",
74
- );
75
- expect(messages).toEqual([]);
76
- });
77
- it("createValidationLinter + lintFileForValidation: file missing JSDoc returns messages with jsdoczoom rules", async () => {
78
- const messages = await lintFileForValidation(
79
- validationLinter,
80
- missingJsdoc,
81
- "test.ts",
82
- );
83
- expect(messages.length).toBeGreaterThan(0);
84
- expect(messages.some((m) => m.ruleId?.startsWith("jsdoczoom/"))).toBe(true);
85
- });
86
- it("createLintLinter + lintFileForLint: missing @param gets lint diagnostic", async () => {
87
- const diagnostics = await lintFileForLint(
88
- lintLinter,
89
- missingParam,
90
- "test.ts",
91
- );
92
- expect(diagnostics.some((d) => d.rule === "jsdoc/require-param")).toBe(
93
- true,
94
- );
95
- });
96
- it("createLintLinter + lintFileForLint: file missing @returns gets lint diagnostic", async () => {
97
- const diagnostics = await lintFileForLint(
98
- lintLinter,
99
- missingReturns,
100
- "test.ts",
101
- );
102
- expect(diagnostics.some((d) => d.rule === "jsdoc/require-returns")).toBe(
103
- true,
104
- );
105
- });
106
- it("JSDoc-only constraint test: unused var produces zero diagnostics from BOTH validate and lint configs", async () => {
107
- const validationMessages = await lintFileForValidation(
108
- validationLinter,
109
- unusedVar,
110
- "test.ts",
111
- );
112
- const lintDiagnostics = await lintFileForLint(
113
- lintLinter,
114
- unusedVar,
115
- "test.ts",
116
- );
117
- expect(validationMessages).toEqual([]);
118
- expect(lintDiagnostics).toEqual([]);
119
- });
120
- it("createLintLinter: informative-docs violation caught", async () => {
121
- const diagnostics = await lintFileForLint(
122
- lintLinter,
123
- informativeDocs,
124
- "test.ts",
125
- );
126
- expect(diagnostics.some((d) => d.rule === "jsdoc/informative-docs")).toBe(
127
- true,
128
- );
129
- });
130
- });