@cparra/apexdocs 3.0.0-rc.0 → 3.0.0

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 (107) hide show
  1. package/README.md +50 -571
  2. package/dist/cli/generate.js +73 -3094
  3. package/dist/defaults-BcE8DTat.js +13 -0
  4. package/dist/defaults-D07y_bq4.js +40 -0
  5. package/dist/defaults-gPzwP66p.js +14 -0
  6. package/dist/index.d.ts +35 -3
  7. package/dist/index.js +90 -2
  8. package/dist/logger-BEbUIfqN.js +3282 -0
  9. package/dist/logger-BGuf1PnL.js +3281 -0
  10. package/dist/logger-CWBRF2za.js +3284 -0
  11. package/dist/logger-CdBmDEN1.js +3283 -0
  12. package/dist/logger-Ce4QqPFR.js +3278 -0
  13. package/dist/logger-CyEVYaAC.js +3284 -0
  14. package/dist/logger-D7a83ycP.js +3277 -0
  15. package/dist/logger-DGaHeBKk.js +3279 -0
  16. package/dist/logger-Dqhl_lO_.js +3278 -0
  17. package/dist/logger-aySSWi0G.js +3280 -0
  18. package/dist/logger-qLCcAtiy.js +3284 -0
  19. package/examples/README.md +5 -0
  20. package/examples/docsify/README.md +17 -0
  21. package/examples/docsify/apexdocs.config.ts +13 -0
  22. package/examples/docsify/classes/ASampleClass.cls +57 -0
  23. package/examples/docsify/classes/CodeControl.cls +19 -0
  24. package/examples/docsify/classes/SampleClass.cls +95 -0
  25. package/examples/docsify/classes/SampleInterface.cls +17 -0
  26. package/examples/docsify/classes/SomeDto.cls +122 -0
  27. package/examples/docsify/docs/.nojekyll +0 -0
  28. package/examples/docsify/docs/README.md +25 -0
  29. package/examples/docsify/docs/_config.yml +1 -0
  30. package/examples/docsify/docs/index.html +22 -0
  31. package/examples/docsify/docs/miscellaneous/ASampleClass.md +88 -0
  32. package/examples/docsify/docs/miscellaneous/CodeControl.md +107 -0
  33. package/examples/docsify/docs/miscellaneous/SomeDto.md +244 -0
  34. package/examples/docsify/docs/sample-classes/SampleClass.md +171 -0
  35. package/examples/docsify/docs/sample-interfaces/SampleInterface.md +36 -0
  36. package/examples/docsify/package-lock.json +2459 -0
  37. package/examples/docsify/package.json +14 -0
  38. package/examples/imported/.forceignore +12 -0
  39. package/examples/imported/README.md +6 -0
  40. package/examples/imported/config/project-scratch-def.json +5 -0
  41. package/examples/imported/docs/index.md +109 -0
  42. package/examples/imported/docs/miscellaneous/BaseClass.md +13 -0
  43. package/examples/imported/docs/miscellaneous/MultiInheritanceClass.md +69 -0
  44. package/examples/imported/docs/miscellaneous/ParentInterface.md +12 -0
  45. package/examples/imported/docs/miscellaneous/ReferencedEnum.md +5 -0
  46. package/examples/imported/docs/miscellaneous/SampleException.md +21 -0
  47. package/examples/imported/docs/miscellaneous/SampleInterface.md +113 -0
  48. package/examples/imported/docs/miscellaneous/Url.md +308 -0
  49. package/examples/imported/docs/sample-enums/SampleEnum.md +33 -0
  50. package/examples/imported/docs/samplegroup/SampleClass.md +167 -0
  51. package/examples/imported/force-app/classes/BaseClass.cls +3 -0
  52. package/examples/imported/force-app/classes/MultiInheritanceClass.cls +1 -0
  53. package/examples/imported/force-app/classes/ParentInterface.cls +3 -0
  54. package/examples/imported/force-app/classes/ReferencedEnum.cls +3 -0
  55. package/examples/imported/force-app/classes/SampleClass.cls +72 -0
  56. package/examples/imported/force-app/classes/SampleInterface.cls +50 -0
  57. package/examples/imported/force-app/classes/Url.cls +196 -0
  58. package/examples/imported/package-lock.json +665 -0
  59. package/examples/imported/package.json +6 -0
  60. package/examples/imported/scripts/process-docs.mjs +16 -0
  61. package/examples/imported/sfdx-project.json +12 -0
  62. package/examples/markdown/README.md +7 -0
  63. package/examples/markdown-jsconfig/README.md +9 -0
  64. package/examples/markdown-jsconfig/apexdocs.config.mjs +1 -0
  65. package/examples/markdown-jsconfig/docs/index.md +1 -1
  66. package/examples/open-api/README.md +5 -0
  67. package/examples/open-api/docs/openapi.json +2 -570
  68. package/examples/vitepress/README.md +25 -0
  69. package/examples/vitepress/apexdocs.config.ts +2 -0
  70. package/examples/vitepress/force-app/main/default/classes/{SampleClass.cls → feature-a/SampleClass.cls} +1 -0
  71. package/examples/vitepress/force-app/main/default/classes/feature-a/SampleEnum.cls +30 -0
  72. package/examples/vitepress/force-app/main/default/classes/feature-a/SampleException.cls +17 -0
  73. package/package.json +2 -2
  74. package/src/application/Apexdocs.ts +16 -19
  75. package/src/application/__tests__/apex-file-reader.spec.ts +108 -67
  76. package/src/application/apex-file-reader.ts +1 -0
  77. package/src/application/generators/openapi.ts +17 -13
  78. package/src/cli/args.ts +12 -3
  79. package/src/cli/commands/markdown.ts +14 -9
  80. package/src/cli/commands/openapi.ts +5 -5
  81. package/src/cli/generate.ts +20 -4
  82. package/src/core/markdown/__test__/generating-class-docs.spec.ts +14 -257
  83. package/src/core/markdown/__test__/generating-docs.spec.ts +271 -4
  84. package/src/core/markdown/__test__/generating-enum-docs.spec.ts +4 -264
  85. package/src/core/markdown/__test__/generating-interface-docs.spec.ts +4 -232
  86. package/src/core/markdown/__test__/generating-reference-guide.spec.ts +17 -1
  87. package/src/core/markdown/__test__/test-helpers.ts +3 -1
  88. package/src/core/markdown/adapters/__tests__/interface-adapter.spec.ts +3 -1
  89. package/src/core/markdown/adapters/renderable-to-page-data.ts +6 -4
  90. package/src/core/markdown/generate-docs.ts +13 -15
  91. package/src/core/markdown/reflection/__test__/filter-scope.spec.ts +2 -18
  92. package/src/core/markdown/reflection/__test__/helpers.ts +18 -0
  93. package/src/core/markdown/reflection/__test__/remove-excluded-tags.spec.ts +200 -0
  94. package/src/core/markdown/reflection/remove-excluded-tags.ts +168 -0
  95. package/src/core/markdown/reflection/{sort-members.ts → sort-types-and-members.ts} +7 -5
  96. package/src/core/markdown/templates/reference-guide.ts +2 -2
  97. package/src/core/openapi/__tests__/open-api-docs-processor.spec.ts +6 -3
  98. package/src/core/openapi/open-api-docs-processor.ts +3 -3
  99. package/src/core/openapi/parser.ts +5 -2
  100. package/src/core/shared/types.d.ts +4 -2
  101. package/src/defaults.ts +15 -3
  102. package/src/index.ts +65 -4
  103. package/src/util/error-logger.ts +36 -36
  104. package/src/util/logger.ts +18 -11
  105. /package/examples/{vitepress/force-app/main/default → imported/force-app}/classes/SampleEnum.cls +0 -0
  106. /package/examples/{vitepress/force-app/main/default → imported/force-app}/classes/SampleException.cls +0 -0
  107. /package/examples/vitepress/force-app/main/default/classes/{SampleInterface.cls → feature-a/SampleInterface.cls} +0 -0
@@ -6,251 +6,6 @@ describe('When generating documentation for a class', () => {
6
6
  extendExpect();
7
7
  });
8
8
 
9
- describe('documentation content', () => {
10
- describe('type level information', () => {
11
- it('generates a heading with the class name', async () => {
12
- const input = 'public class MyClass {}';
13
-
14
- const output = `# MyClass Class`;
15
- const result = await generateDocs([apexBundleFromRawString(input)])();
16
- expect(result).documentationBundleHasLength(1);
17
- assertEither(result, (data) => expect(data).firstDocContains(output));
18
- });
19
-
20
- it('displays type level annotations', async () => {
21
- const input = `
22
- @NamespaceAccessible
23
- public class MyClass {
24
- @Deprecated
25
- public void myMethod() {}
26
- }
27
- `;
28
-
29
- const result = await generateDocs([apexBundleFromRawString(input)])();
30
- expect(result).documentationBundleHasLength(1);
31
- assertEither(result, (data) => expect(data).firstDocContains('NAMESPACEACCESSIBLE'));
32
- assertEither(result, (data) => expect(data).firstDocContains('DEPRECATED'));
33
- });
34
-
35
- it('displays metadata as annotations', async () => {
36
- const input = 'public class MyClass {}';
37
- const metadata = `
38
- <?xml version="1.0" encoding="UTF-8"?>
39
- <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
40
- <apiVersion>59.0</apiVersion>
41
- <status>Active</status>
42
- </ApexClass>
43
- `;
44
-
45
- const result = await generateDocs([apexBundleFromRawString(input, metadata)])();
46
-
47
- expect(result).documentationBundleHasLength(1);
48
- assertEither(result, (data) => expect(data).firstDocContains('APIVERSION'));
49
- assertEither(result, (data) => expect(data).firstDocContains('STATUS'));
50
- });
51
-
52
- it('displays the description', async () => {
53
- const input = `
54
- /**
55
- * This is a description
56
- */
57
- public class MyClass {}
58
- `;
59
-
60
- const result = await generateDocs([apexBundleFromRawString(input)])();
61
- expect(result).documentationBundleHasLength(1);
62
- assertEither(result, (data) => expect(data).firstDocContains('This is a description'));
63
- });
64
-
65
- it('display custom documentation tags', async () => {
66
- const input = `
67
- /**
68
- * @custom-tag My Value
69
- */
70
- public class MyClass {}
71
- `;
72
-
73
- const result = await generateDocs([apexBundleFromRawString(input)])();
74
- expect(result).documentationBundleHasLength(1);
75
- assertEither(result, (data) => expect(data).firstDocContains('Custom Tag'));
76
- assertEither(result, (data) => expect(data).firstDocContains('My Value'));
77
- });
78
-
79
- it('displays the group', async () => {
80
- const input = `
81
- /**
82
- * @group MyGroup
83
- */
84
- public class MyClass {}`;
85
-
86
- const result = await generateDocs([apexBundleFromRawString(input)])();
87
- expect(result).documentationBundleHasLength(1);
88
- assertEither(result, (data) => expect(data).firstDocContains('Group'));
89
- assertEither(result, (data) => expect(data).firstDocContains('MyGroup'));
90
- });
91
-
92
- it('displays the author', async () => {
93
- const input = `
94
- /**
95
- * @author John Doe
96
- */
97
- public class MyClass {}`;
98
-
99
- const result = await generateDocs([apexBundleFromRawString(input)])();
100
- expect(result).documentationBundleHasLength(1);
101
- assertEither(result, (data) => expect(data).firstDocContains('Author'));
102
- assertEither(result, (data) => expect(data).firstDocContains('John Doe'));
103
- });
104
-
105
- it('displays the date', async () => {
106
- const input = `
107
- /**
108
- * @date 2021-01-01
109
- */
110
- public class MyClass {}`;
111
-
112
- const result = await generateDocs([apexBundleFromRawString(input)])();
113
- expect(result).documentationBundleHasLength(1);
114
- assertEither(result, (data) => expect(data).firstDocContains('Date'));
115
- assertEither(result, (data) => expect(data).firstDocContains('2021-01-01'));
116
- });
117
-
118
- it('displays descriptions', async () => {
119
- const input = `
120
- /**
121
- * @description This is a description
122
- */
123
- public class MyClass {}`;
124
-
125
- const result = await generateDocs([apexBundleFromRawString(input)])();
126
- expect(result).documentationBundleHasLength(1);
127
- assertEither(result, (data) => expect(data).firstDocContains('This is a description'));
128
- });
129
-
130
- it('displays descriptions with links', async () => {
131
- const input1 = `
132
- /**
133
- * @description This is a description with a {@link ClassRef} reference
134
- */
135
- public enum MyClass {}
136
- `;
137
-
138
- const input2 = 'public class ClassRef {}';
139
-
140
- const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
141
- expect(result).documentationBundleHasLength(2);
142
- assertEither(result, (data) =>
143
- expect(data).firstDocContains('This is a description with a [ClassRef](ClassRef.md) reference'),
144
- );
145
- });
146
-
147
- it('displays descriptions with emails', async () => {
148
- const input = `
149
- /**
150
- * @description This is a description with an {@email test@testerson.com} email
151
- */
152
- public class MyClass {}
153
- `;
154
-
155
- const result = await generateDocs([apexBundleFromRawString(input)])();
156
- expect(result).documentationBundleHasLength(1);
157
- assertEither(result, (data) =>
158
- expect(data).firstDocContains(
159
- 'This is a description with an [test@testerson.com](mailto:test@testerson.com) email',
160
- ),
161
- );
162
- });
163
-
164
- it('displays sees with accurately resolved links', async () => {
165
- const input1 = `
166
- /**
167
- * @see ClassRef
168
- */
169
- public class MyClass {}
170
- `;
171
-
172
- const input2 = 'public class ClassRef {}';
173
-
174
- const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
175
- expect(result).documentationBundleHasLength(2);
176
- assertEither(result, (data) => expect(data).firstDocContains('See'));
177
- assertEither(result, (data) => expect(data).firstDocContains('[ClassRef](ClassRef.md)'));
178
- });
179
-
180
- it('displays sees without links when the reference is not found', async () => {
181
- const input = `
182
- /**
183
- * @see ClassRef
184
- */
185
- public class MyClass {}
186
- `;
187
-
188
- const result = await generateDocs([apexBundleFromRawString(input)])();
189
- expect(result).documentationBundleHasLength(1);
190
- assertEither(result, (data) => expect(data).firstDocContains('See'));
191
- assertEither(result, (data) => expect(data).firstDocContains('ClassRef'));
192
- });
193
-
194
- it('displays the namespace if present in the config', async () => {
195
- const input = 'public class MyClass {}';
196
-
197
- const result = await generateDocs([apexBundleFromRawString(input)], { namespace: 'MyNamespace' })();
198
- expect(result).documentationBundleHasLength(1);
199
- assertEither(result, (data) => expect(data).firstDocContains('## Namespace'));
200
- assertEither(result, (data) => expect(data).firstDocContains('MyNamespace'));
201
- });
202
-
203
- it('does not display the namespace if not present in the config', async () => {
204
- const input = 'public class MyClass {}';
205
-
206
- const result = await generateDocs([apexBundleFromRawString(input)])();
207
- expect(result).documentationBundleHasLength(1);
208
- assertEither(result, (data) => expect(data).firstDocContainsNot('## Namespace'));
209
- });
210
-
211
- it('displays a mermaid diagram', async () => {
212
- const input = `
213
- /**
214
- * @mermaid
215
- * \`\`\`mermaid
216
- * graph TD
217
- * A[Square Rect] -- Link text --> B((Circle))
218
- * A --> C(Round Rect)
219
- * B --> D{Rhombus}
220
- * C --> D
221
- * \`\`\`
222
- */
223
- public class MyClass {}
224
- `;
225
-
226
- const result = await generateDocs([apexBundleFromRawString(input)])();
227
- expect(result).documentationBundleHasLength(1);
228
- assertEither(result, (data) => expect(data).firstDocContains('```mermaid'));
229
- assertEither(result, (data) => expect(data).firstDocContains('graph TD'));
230
- });
231
-
232
- it('displays an example code block', async () => {
233
- const input = `
234
- /**
235
- * @example
236
- * \`\`\`apex
237
- * public class MyClass {
238
- * public void myMethod() {
239
- * System.debug('Hello, World!');
240
- * }
241
- * }
242
- * \`\`\`
243
- */
244
- public class MyClass {}`;
245
-
246
- const result = await generateDocs([apexBundleFromRawString(input)])();
247
- expect(result).documentationBundleHasLength(1);
248
- assertEither(result, (data) => expect(data).firstDocContains('```apex'));
249
- assertEither(result, (data) => expect(data).firstDocContains('public class MyClass'));
250
- });
251
- });
252
- });
253
-
254
9
  describe('member information', () => {
255
10
  it('displays the Method heading', async () => {
256
11
  const input = `
@@ -272,7 +27,7 @@ describe('When generating documentation for a class', () => {
272
27
  }
273
28
  `;
274
29
 
275
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: true })();
30
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })();
276
31
  expect(result).documentationBundleHasLength(1);
277
32
  assertEither(result, (data) => {
278
33
  const aMethodIndex = data.docs[0].content.indexOf('aMethod');
@@ -289,7 +44,7 @@ describe('When generating documentation for a class', () => {
289
44
  }
290
45
  `;
291
46
 
292
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: false })();
47
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })();
293
48
  expect(result).documentationBundleHasLength(1);
294
49
  assertEither(result, (data) => {
295
50
  const aMethodIndex = data.docs[0].content.indexOf('aMethod');
@@ -318,7 +73,7 @@ describe('When generating documentation for a class', () => {
318
73
  }
319
74
  `;
320
75
 
321
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: true })();
76
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })();
322
77
  expect(result).documentationBundleHasLength(1);
323
78
  assertEither(result, (data) => {
324
79
  const aPropertyIndex = data.docs[0].content.indexOf('aProperty');
@@ -335,7 +90,7 @@ describe('When generating documentation for a class', () => {
335
90
  }
336
91
  `;
337
92
 
338
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: false })();
93
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })();
339
94
  expect(result).documentationBundleHasLength(1);
340
95
  assertEither(result, (data) => {
341
96
  const aPropertyIndex = data.docs[0].content.indexOf('aProperty');
@@ -364,7 +119,7 @@ describe('When generating documentation for a class', () => {
364
119
  }
365
120
  `;
366
121
 
367
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: true })();
122
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })();
368
123
  expect(result).documentationBundleHasLength(1);
369
124
  assertEither(result, (data) => {
370
125
  const aFieldIndex = data.docs[0].content.indexOf('aField');
@@ -381,7 +136,7 @@ describe('When generating documentation for a class', () => {
381
136
  }
382
137
  `;
383
138
 
384
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: false })();
139
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })();
385
140
  expect(result).documentationBundleHasLength(1);
386
141
  assertEither(result, (data) => {
387
142
  const aFieldIndex = data.docs[0].content.indexOf('aField');
@@ -422,7 +177,7 @@ describe('When generating documentation for a class', () => {
422
177
  }
423
178
  `;
424
179
 
425
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: true })();
180
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })();
426
181
  expect(result).documentationBundleHasLength(1);
427
182
  assertEither(result, (data) => {
428
183
  const aInnerClassIndex = data.docs[0].content.indexOf('AInnerClass');
@@ -439,7 +194,7 @@ describe('When generating documentation for a class', () => {
439
194
  }
440
195
  `;
441
196
 
442
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: false })();
197
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })();
443
198
  expect(result).documentationBundleHasLength(1);
444
199
  assertEither(result, (data) => {
445
200
  const aInnerClassIndex = data.docs[0].content.indexOf('AInnerClass');
@@ -468,7 +223,7 @@ describe('When generating documentation for a class', () => {
468
223
  }
469
224
  `;
470
225
 
471
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: true })();
226
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })();
472
227
  expect(result).documentationBundleHasLength(1);
473
228
  assertEither(result, (data) => {
474
229
  const aInnerInterfaceIndex = data.docs[0].content.indexOf('AInnerInterface');
@@ -485,7 +240,7 @@ describe('When generating documentation for a class', () => {
485
240
  }
486
241
  `;
487
242
 
488
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: false })();
243
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })();
489
244
  expect(result).documentationBundleHasLength(1);
490
245
  assertEither(result, (data) => {
491
246
  const aInnerInterfaceIndex = data.docs[0].content.indexOf('AInnerInterface');
@@ -514,7 +269,7 @@ describe('When generating documentation for a class', () => {
514
269
  }
515
270
  `;
516
271
 
517
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: true })();
272
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: true })();
518
273
  expect(result).documentationBundleHasLength(1);
519
274
  assertEither(result, (data) => {
520
275
  const aInnerEnumIndex = data.docs[0].content.indexOf('AInnerEnum');
@@ -531,7 +286,7 @@ describe('When generating documentation for a class', () => {
531
286
  }
532
287
  `;
533
288
 
534
- const result = await generateDocs([apexBundleFromRawString(input)], { sortMembersAlphabetically: false })();
289
+ const result = await generateDocs([apexBundleFromRawString(input)], { sortAlphabetically: false })();
535
290
  expect(result).documentationBundleHasLength(1);
536
291
  assertEither(result, (data) => {
537
292
  const aInnerEnumIndex = data.docs[0].content.indexOf('AInnerEnum');
@@ -603,3 +358,5 @@ describe('When generating documentation for a class', () => {
603
358
  });
604
359
  });
605
360
  });
361
+
362
+ // TODO: Skips tags at the member level
@@ -84,10 +84,8 @@ describe('When generating documentation', () => {
84
84
  assertEither(result, (data) => expect(aSingleDoc(data).source.type).toBe(expected));
85
85
  }
86
86
  });
87
- });
88
87
 
89
- describe('the generated bundle', () => {
90
- it('does not return files out of scope', async () => {
88
+ it('do not return files out of scope', async () => {
91
89
  const input1 = 'global class MyClass {}';
92
90
  const input2 = 'public class AnotherClass {}';
93
91
 
@@ -97,7 +95,7 @@ describe('When generating documentation', () => {
97
95
  expect(result).documentationBundleHasLength(1);
98
96
  });
99
97
 
100
- it('does not return files that have an @ignore in the docs', async () => {
98
+ it('do not return files that have an @ignore in the docs', async () => {
101
99
  const input = `
102
100
  /**
103
101
  * @ignore
@@ -108,4 +106,273 @@ describe('When generating documentation', () => {
108
106
  expect(result).documentationBundleHasLength(0);
109
107
  });
110
108
  });
109
+
110
+ describe('the documentation content', () => {
111
+ it('includes a heading with the type name', async () => {
112
+ const properties: [string, string][] = [
113
+ ['public class MyClass {}', 'MyClass Class'],
114
+ ['public enum MyEnum {}', 'MyEnum Enum'],
115
+ ['public interface MyInterface {}', 'MyInterface Interface'],
116
+ ];
117
+
118
+ for (const [input, expected] of properties) {
119
+ const result = await generateDocs([apexBundleFromRawString(input)])();
120
+
121
+ expect(result).documentationBundleHasLength(1);
122
+ assertEither(result, (data) => expect(data).firstDocContains(expected));
123
+ }
124
+ });
125
+
126
+ it('displays type level annotations', async () => {
127
+ const input = `
128
+ @NamespaceAccessible
129
+ public class MyClass {
130
+ @Deprecated
131
+ public void myMethod() {}
132
+ }
133
+ `;
134
+
135
+ const result = await generateDocs([apexBundleFromRawString(input)])();
136
+
137
+ expect(result).documentationBundleHasLength(1);
138
+ assertEither(result, (data) => expect(data).firstDocContains('NAMESPACEACCESSIBLE'));
139
+ assertEither(result, (data) => expect(data).firstDocContains('DEPRECATED'));
140
+ });
141
+
142
+ it('displays metadata as annotations', async () => {
143
+ const input = 'public class MyClass {}';
144
+ const metadata = `
145
+ <?xml version="1.0" encoding="UTF-8"?>
146
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
147
+ <apiVersion>59.0</apiVersion>
148
+ <status>Active</status>
149
+ </ApexClass>
150
+ `;
151
+
152
+ const result = await generateDocs([apexBundleFromRawString(input, metadata)])();
153
+
154
+ expect(result).documentationBundleHasLength(1);
155
+ assertEither(result, (data) => expect(data).firstDocContains('APIVERSION'));
156
+ assertEither(result, (data) => expect(data).firstDocContains('STATUS'));
157
+ });
158
+
159
+ it('displays the description when no @description tag is used', async () => {
160
+ const input = `
161
+ /**
162
+ * This is a description
163
+ */
164
+ public class MyClass {}
165
+ `;
166
+
167
+ const result = await generateDocs([apexBundleFromRawString(input)])();
168
+
169
+ expect(result).documentationBundleHasLength(1);
170
+ assertEither(result, (data) => expect(data).firstDocContains('This is a description'));
171
+ });
172
+
173
+ it('displays the description when a @description tag is used', async () => {
174
+ const input = `
175
+ /**
176
+ * @description This is a description
177
+ */
178
+ public class MyClass {}`;
179
+
180
+ const result = await generateDocs([apexBundleFromRawString(input)])();
181
+ expect(result).documentationBundleHasLength(1);
182
+ assertEither(result, (data) => expect(data).firstDocContains('This is a description'));
183
+ });
184
+
185
+ it('display custom documentation tags', async () => {
186
+ const input = `
187
+ /**
188
+ * @custom-tag My Value
189
+ */
190
+ public class MyClass {}
191
+ `;
192
+
193
+ const result = await generateDocs([apexBundleFromRawString(input)])();
194
+ expect(result).documentationBundleHasLength(1);
195
+ assertEither(result, (data) => expect(data).firstDocContains('Custom Tag'));
196
+ assertEither(result, (data) => expect(data).firstDocContains('My Value'));
197
+ });
198
+
199
+ it('displays the group', async () => {
200
+ const input = `
201
+ /**
202
+ * @group MyGroup
203
+ */
204
+ public class MyClass {}`;
205
+
206
+ const result = await generateDocs([apexBundleFromRawString(input)])();
207
+ expect(result).documentationBundleHasLength(1);
208
+ assertEither(result, (data) => expect(data).firstDocContains('Group'));
209
+ assertEither(result, (data) => expect(data).firstDocContains('MyGroup'));
210
+ });
211
+
212
+ it('displays the author', async () => {
213
+ const input = `
214
+ /**
215
+ * @author John Doe
216
+ */
217
+ public class MyClass {}`;
218
+
219
+ const result = await generateDocs([apexBundleFromRawString(input)])();
220
+ expect(result).documentationBundleHasLength(1);
221
+ assertEither(result, (data) => expect(data).firstDocContains('Author'));
222
+ assertEither(result, (data) => expect(data).firstDocContains('John Doe'));
223
+ });
224
+
225
+ it('displays the date', async () => {
226
+ const input = `
227
+ /**
228
+ * @date 2021-01-01
229
+ */
230
+ public class MyClass {}`;
231
+
232
+ const result = await generateDocs([apexBundleFromRawString(input)])();
233
+ expect(result).documentationBundleHasLength(1);
234
+ assertEither(result, (data) => expect(data).firstDocContains('Date'));
235
+ assertEither(result, (data) => expect(data).firstDocContains('2021-01-01'));
236
+ });
237
+
238
+ it('displays descriptions with links', async () => {
239
+ const input1 = `
240
+ /**
241
+ * @description This is a description with a {@link ClassRef} reference
242
+ */
243
+ public enum MyClass {}
244
+ `;
245
+
246
+ const input2 = 'public class ClassRef {}';
247
+
248
+ const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
249
+ expect(result).documentationBundleHasLength(2);
250
+ assertEither(result, (data) =>
251
+ expect(data).firstDocContains('This is a description with a [ClassRef](ClassRef.md) reference'),
252
+ );
253
+ });
254
+
255
+ it('displays descriptions with emails', async () => {
256
+ const input = `
257
+ /**
258
+ * @description This is a description with an {@email test@testerson.com} email
259
+ */
260
+ public class MyClass {}
261
+ `;
262
+
263
+ const result = await generateDocs([apexBundleFromRawString(input)])();
264
+ expect(result).documentationBundleHasLength(1);
265
+ assertEither(result, (data) =>
266
+ expect(data).firstDocContains(
267
+ 'This is a description with an [test@testerson.com](mailto:test@testerson.com) email',
268
+ ),
269
+ );
270
+ });
271
+
272
+ it('displays @sees with accurately resolved links', async () => {
273
+ const input1 = `
274
+ /**
275
+ * @see ClassRef
276
+ */
277
+ public class MyClass {}
278
+ `;
279
+
280
+ const input2 = 'public class ClassRef {}';
281
+
282
+ const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
283
+ expect(result).documentationBundleHasLength(2);
284
+ assertEither(result, (data) => expect(data).firstDocContains('See'));
285
+ assertEither(result, (data) => expect(data).firstDocContains('[ClassRef](ClassRef.md)'));
286
+ });
287
+
288
+ it('displays @sees without links when the reference is not found', async () => {
289
+ const input = `
290
+ /**
291
+ * @see ClassRef
292
+ */
293
+ public class MyClass {}
294
+ `;
295
+
296
+ const result = await generateDocs([apexBundleFromRawString(input)])();
297
+
298
+ expect(result).documentationBundleHasLength(1);
299
+ assertEither(result, (data) => expect(data).firstDocContains('See'));
300
+ assertEither(result, (data) => expect(data).firstDocContains('ClassRef'));
301
+ });
302
+
303
+ it('displays the namespace if present in the config', async () => {
304
+ const input = 'public class MyClass {}';
305
+
306
+ const result = await generateDocs([apexBundleFromRawString(input)], { namespace: 'MyNamespace' })();
307
+ expect(result).documentationBundleHasLength(1);
308
+ assertEither(result, (data) => expect(data).firstDocContains('## Namespace'));
309
+ assertEither(result, (data) => expect(data).firstDocContains('MyNamespace'));
310
+ });
311
+
312
+ it('does not display the namespace if not present in the config', async () => {
313
+ const input = 'public class MyClass {}';
314
+
315
+ const result = await generateDocs([apexBundleFromRawString(input)])();
316
+ expect(result).documentationBundleHasLength(1);
317
+ assertEither(result, (data) => expect(data).firstDocContainsNot('## Namespace'));
318
+ });
319
+
320
+ it('displays a mermaid diagram', async () => {
321
+ const input = `
322
+ /**
323
+ * @mermaid
324
+ * \`\`\`mermaid
325
+ * graph TD
326
+ * A[Square Rect] -- Link text --> B((Circle))
327
+ * A --> C(Round Rect)
328
+ * B --> D{Rhombus}
329
+ * C --> D
330
+ * \`\`\`
331
+ */
332
+ public class MyClass {}
333
+ `;
334
+
335
+ const result = await generateDocs([apexBundleFromRawString(input)])();
336
+ expect(result).documentationBundleHasLength(1);
337
+ assertEither(result, (data) => expect(data).firstDocContains('```mermaid'));
338
+ assertEither(result, (data) => expect(data).firstDocContains('graph TD'));
339
+ });
340
+
341
+ it('displays an example code block', async () => {
342
+ const input = `
343
+ /**
344
+ * @example
345
+ * \`\`\`apex
346
+ * public class MyClass {
347
+ * public void myMethod() {
348
+ * System.debug('Hello, World!');
349
+ * }
350
+ * }
351
+ * \`\`\`
352
+ */
353
+ public class MyClass {}`;
354
+
355
+ const result = await generateDocs([apexBundleFromRawString(input)])();
356
+
357
+ expect(result).documentationBundleHasLength(1);
358
+ assertEither(result, (data) => expect(data).firstDocContains('```apex'));
359
+ assertEither(result, (data) => expect(data).firstDocContains('public class MyClass'));
360
+ });
361
+
362
+ it('does not display tags marked as excluded', async () => {
363
+ const input = `
364
+ /**
365
+ * @see ClassRef
366
+ */
367
+ public class MyClass {}
368
+ `;
369
+
370
+ const result = await generateDocs([apexBundleFromRawString(input)], {
371
+ excludeTags: ['see'],
372
+ })();
373
+
374
+ expect(result).documentationBundleHasLength(1);
375
+ assertEither(result, (data) => expect(data).firstDocContainsNot('See'));
376
+ });
377
+ });
111
378
  });