@aigne/doc-smith 0.8.12-beta.7 → 0.8.12-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/agents/clear/choose-contents.mjs +14 -1
  3. package/agents/clear/clear-media-description.mjs +129 -0
  4. package/agents/clear/index.yaml +3 -1
  5. package/agents/evaluate/code-snippet.mjs +28 -24
  6. package/agents/evaluate/document-structure.yaml +0 -4
  7. package/agents/evaluate/document.yaml +1 -5
  8. package/agents/generate/index.yaml +1 -0
  9. package/agents/init/index.mjs +10 -0
  10. package/agents/media/batch-generate-media-description.yaml +44 -0
  11. package/agents/media/generate-media-description.yaml +47 -0
  12. package/agents/media/load-media-description.mjs +238 -0
  13. package/agents/publish/index.yaml +4 -0
  14. package/agents/publish/publish-docs.mjs +77 -5
  15. package/agents/publish/translate-meta.mjs +103 -0
  16. package/agents/update/generate-document.yaml +30 -28
  17. package/agents/update/index.yaml +1 -0
  18. package/agents/update/update-document-detail.yaml +3 -1
  19. package/agents/utils/load-sources.mjs +103 -53
  20. package/agents/utils/update-branding.mjs +69 -0
  21. package/aigne.yaml +6 -0
  22. package/assets/report-template/report.html +34 -34
  23. package/package.json +17 -2
  24. package/prompts/common/document/role-and-personality.md +3 -1
  25. package/prompts/detail/d2-diagram/guide.md +7 -1
  26. package/prompts/detail/d2-diagram/user-prompt.md +3 -0
  27. package/prompts/detail/generate/system-prompt.md +6 -7
  28. package/prompts/detail/generate/user-prompt.md +12 -3
  29. package/prompts/detail/update/user-prompt.md +0 -2
  30. package/prompts/evaluate/document-structure.md +6 -7
  31. package/prompts/evaluate/document.md +16 -25
  32. package/prompts/media/media-description/system-prompt.md +35 -0
  33. package/prompts/media/media-description/user-prompt.md +8 -0
  34. package/prompts/structure/update/user-prompt.md +0 -4
  35. package/utils/constants/index.mjs +0 -107
  36. package/utils/file-utils.mjs +86 -0
  37. package/utils/markdown-checker.mjs +0 -20
  38. package/utils/request.mjs +7 -0
  39. package/utils/upload-files.mjs +231 -0
  40. package/utils/utils.mjs +11 -1
  41. package/.aigne/doc-smith/config.yaml +0 -77
  42. package/.aigne/doc-smith/history.yaml +0 -37
  43. package/.aigne/doc-smith/output/structure-plan.json +0 -162
  44. package/.aigne/doc-smith/preferences.yml +0 -97
  45. package/.aigne/doc-smith/upload-cache.yaml +0 -1893
  46. package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
  47. package/.github/workflows/ci.yml +0 -54
  48. package/.github/workflows/create-release-pr.yaml +0 -21
  49. package/.github/workflows/publish-docs.yml +0 -65
  50. package/.github/workflows/release.yml +0 -49
  51. package/.github/workflows/reviewer.yml +0 -54
  52. package/.release-please-manifest.json +0 -3
  53. package/RELEASE.md +0 -9
  54. package/assets/screenshots/doc-complete-setup.png +0 -0
  55. package/assets/screenshots/doc-generate-docs.png +0 -0
  56. package/assets/screenshots/doc-generate.png +0 -0
  57. package/assets/screenshots/doc-generated-successfully.png +0 -0
  58. package/assets/screenshots/doc-publish.png +0 -0
  59. package/assets/screenshots/doc-regenerate.png +0 -0
  60. package/assets/screenshots/doc-translate-langs.png +0 -0
  61. package/assets/screenshots/doc-translate.png +0 -0
  62. package/assets/screenshots/doc-update.png +0 -0
  63. package/biome.json +0 -73
  64. package/codecov.yml +0 -15
  65. package/docs/_sidebar.md +0 -15
  66. package/docs/configuration-initial-setup.ja.md +0 -179
  67. package/docs/configuration-initial-setup.md +0 -179
  68. package/docs/configuration-initial-setup.zh-TW.md +0 -179
  69. package/docs/configuration-initial-setup.zh.md +0 -179
  70. package/docs/configuration-managing-preferences.ja.md +0 -100
  71. package/docs/configuration-managing-preferences.md +0 -100
  72. package/docs/configuration-managing-preferences.zh-TW.md +0 -100
  73. package/docs/configuration-managing-preferences.zh.md +0 -100
  74. package/docs/configuration.ja.md +0 -96
  75. package/docs/configuration.md +0 -96
  76. package/docs/configuration.zh-TW.md +0 -96
  77. package/docs/configuration.zh.md +0 -96
  78. package/docs/getting-started.ja.md +0 -88
  79. package/docs/getting-started.md +0 -88
  80. package/docs/getting-started.zh-TW.md +0 -88
  81. package/docs/getting-started.zh.md +0 -88
  82. package/docs/guides-cleaning-up.ja.md +0 -51
  83. package/docs/guides-cleaning-up.md +0 -51
  84. package/docs/guides-cleaning-up.zh-TW.md +0 -51
  85. package/docs/guides-cleaning-up.zh.md +0 -51
  86. package/docs/guides-evaluating-documents.ja.md +0 -66
  87. package/docs/guides-evaluating-documents.md +0 -66
  88. package/docs/guides-evaluating-documents.zh-TW.md +0 -66
  89. package/docs/guides-evaluating-documents.zh.md +0 -66
  90. package/docs/guides-generating-documentation.ja.md +0 -151
  91. package/docs/guides-generating-documentation.md +0 -151
  92. package/docs/guides-generating-documentation.zh-TW.md +0 -151
  93. package/docs/guides-generating-documentation.zh.md +0 -151
  94. package/docs/guides-interactive-chat.ja.md +0 -85
  95. package/docs/guides-interactive-chat.md +0 -85
  96. package/docs/guides-interactive-chat.zh-TW.md +0 -85
  97. package/docs/guides-interactive-chat.zh.md +0 -85
  98. package/docs/guides-managing-history.ja.md +0 -48
  99. package/docs/guides-managing-history.md +0 -48
  100. package/docs/guides-managing-history.zh-TW.md +0 -48
  101. package/docs/guides-managing-history.zh.md +0 -48
  102. package/docs/guides-publishing-your-docs.ja.md +0 -78
  103. package/docs/guides-publishing-your-docs.md +0 -78
  104. package/docs/guides-publishing-your-docs.zh-TW.md +0 -78
  105. package/docs/guides-publishing-your-docs.zh.md +0 -78
  106. package/docs/guides-translating-documentation.ja.md +0 -95
  107. package/docs/guides-translating-documentation.md +0 -95
  108. package/docs/guides-translating-documentation.zh-TW.md +0 -95
  109. package/docs/guides-translating-documentation.zh.md +0 -95
  110. package/docs/guides-updating-documentation.ja.md +0 -77
  111. package/docs/guides-updating-documentation.md +0 -77
  112. package/docs/guides-updating-documentation.zh-TW.md +0 -77
  113. package/docs/guides-updating-documentation.zh.md +0 -77
  114. package/docs/guides.ja.md +0 -32
  115. package/docs/guides.md +0 -32
  116. package/docs/guides.zh-TW.md +0 -32
  117. package/docs/guides.zh.md +0 -32
  118. package/docs/overview.ja.md +0 -61
  119. package/docs/overview.md +0 -61
  120. package/docs/overview.zh-TW.md +0 -61
  121. package/docs/overview.zh.md +0 -61
  122. package/docs/release-notes.ja.md +0 -255
  123. package/docs/release-notes.md +0 -255
  124. package/docs/release-notes.zh-TW.md +0 -255
  125. package/docs/release-notes.zh.md +0 -255
  126. package/media.md +0 -19
  127. package/prompts/common/afs/afs-tools-usage.md +0 -5
  128. package/prompts/common/afs/use-afs-instruction.md +0 -1
  129. package/release-please-config.json +0 -14
  130. package/tests/agents/chat/chat.test.mjs +0 -46
  131. package/tests/agents/clear/choose-contents.test.mjs +0 -284
  132. package/tests/agents/clear/clear-auth-tokens.test.mjs +0 -268
  133. package/tests/agents/clear/clear-document-config.test.mjs +0 -167
  134. package/tests/agents/clear/clear-document-structure.test.mjs +0 -380
  135. package/tests/agents/clear/clear-generated-docs.test.mjs +0 -222
  136. package/tests/agents/evaluate/code-snippet.test.mjs +0 -163
  137. package/tests/agents/evaluate/fixtures/api-services.md +0 -87
  138. package/tests/agents/evaluate/fixtures/js-sdk.md +0 -94
  139. package/tests/agents/evaluate/generate-report.test.mjs +0 -312
  140. package/tests/agents/generate/check-document-structure.test.mjs +0 -45
  141. package/tests/agents/generate/check-need-generate-structure.test.mjs +0 -279
  142. package/tests/agents/generate/document-structure-tools/add-document.test.mjs +0 -449
  143. package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +0 -410
  144. package/tests/agents/generate/document-structure-tools/generate-sub-structure.test.mjs +0 -277
  145. package/tests/agents/generate/document-structure-tools/move-document.test.mjs +0 -476
  146. package/tests/agents/generate/document-structure-tools/update-document.test.mjs +0 -548
  147. package/tests/agents/generate/generate-structure.test.mjs +0 -45
  148. package/tests/agents/generate/user-review-document-structure.test.mjs +0 -319
  149. package/tests/agents/history/view.test.mjs +0 -97
  150. package/tests/agents/init/init.test.mjs +0 -1657
  151. package/tests/agents/prefs/prefs.test.mjs +0 -431
  152. package/tests/agents/publish/publish-docs.test.mjs +0 -787
  153. package/tests/agents/translate/choose-language.test.mjs +0 -311
  154. package/tests/agents/translate/translate-document.test.mjs +0 -51
  155. package/tests/agents/update/check-document.test.mjs +0 -463
  156. package/tests/agents/update/check-update-is-single.test.mjs +0 -300
  157. package/tests/agents/update/document-tools/update-document-content.test.mjs +0 -329
  158. package/tests/agents/update/generate-document.test.mjs +0 -51
  159. package/tests/agents/update/save-and-translate-document.test.mjs +0 -369
  160. package/tests/agents/update/user-review-document.test.mjs +0 -582
  161. package/tests/agents/utils/action-success.test.mjs +0 -54
  162. package/tests/agents/utils/check-detail-result.test.mjs +0 -743
  163. package/tests/agents/utils/check-feedback-refiner.test.mjs +0 -478
  164. package/tests/agents/utils/choose-docs.test.mjs +0 -406
  165. package/tests/agents/utils/exit.test.mjs +0 -70
  166. package/tests/agents/utils/feedback-refiner.test.mjs +0 -51
  167. package/tests/agents/utils/find-item-by-path.test.mjs +0 -517
  168. package/tests/agents/utils/find-user-preferences-by-path.test.mjs +0 -382
  169. package/tests/agents/utils/format-document-structure.test.mjs +0 -364
  170. package/tests/agents/utils/fs.test.mjs +0 -267
  171. package/tests/agents/utils/load-sources.test.mjs +0 -1470
  172. package/tests/agents/utils/save-docs.test.mjs +0 -109
  173. package/tests/agents/utils/save-output.test.mjs +0 -315
  174. package/tests/agents/utils/save-single-doc.test.mjs +0 -364
  175. package/tests/agents/utils/transform-detail-datasources.test.mjs +0 -320
  176. package/tests/utils/auth-utils.test.mjs +0 -596
  177. package/tests/utils/blocklet.test.mjs +0 -336
  178. package/tests/utils/conflict-detector.test.mjs +0 -355
  179. package/tests/utils/constants.test.mjs +0 -295
  180. package/tests/utils/d2-utils.test.mjs +0 -437
  181. package/tests/utils/deploy.test.mjs +0 -399
  182. package/tests/utils/docs-finder-utils.test.mjs +0 -650
  183. package/tests/utils/file-utils.test.mjs +0 -521
  184. package/tests/utils/history-utils.test.mjs +0 -206
  185. package/tests/utils/kroki-utils.test.mjs +0 -646
  186. package/tests/utils/linter/fixtures/css/keyword-error.css +0 -1
  187. package/tests/utils/linter/fixtures/css/missing-semicolon.css +0 -1
  188. package/tests/utils/linter/fixtures/css/syntax-error.css +0 -1
  189. package/tests/utils/linter/fixtures/css/undeclare-variable.css +0 -1
  190. package/tests/utils/linter/fixtures/css/unused-variable.css +0 -2
  191. package/tests/utils/linter/fixtures/css/valid-code.css +0 -1
  192. package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +0 -1
  193. package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +0 -2
  194. package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +0 -2
  195. package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +0 -1
  196. package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +0 -1
  197. package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +0 -2
  198. package/tests/utils/linter/fixtures/go/keyword-error.go +0 -5
  199. package/tests/utils/linter/fixtures/go/missing-semicolon.go +0 -5
  200. package/tests/utils/linter/fixtures/go/syntax-error.go +0 -6
  201. package/tests/utils/linter/fixtures/go/undeclare-variable.go +0 -5
  202. package/tests/utils/linter/fixtures/go/unused-variable.go +0 -5
  203. package/tests/utils/linter/fixtures/go/valid-code.go +0 -7
  204. package/tests/utils/linter/fixtures/js/keyword-error.js +0 -3
  205. package/tests/utils/linter/fixtures/js/missing-semicolon.js +0 -6
  206. package/tests/utils/linter/fixtures/js/syntax-error.js +0 -4
  207. package/tests/utils/linter/fixtures/js/undeclare-variable.js +0 -3
  208. package/tests/utils/linter/fixtures/js/unused-variable.js +0 -7
  209. package/tests/utils/linter/fixtures/js/valid-code.js +0 -15
  210. package/tests/utils/linter/fixtures/json/keyword-error.json +0 -1
  211. package/tests/utils/linter/fixtures/json/missing-semicolon.json +0 -1
  212. package/tests/utils/linter/fixtures/json/syntax-error.json +0 -1
  213. package/tests/utils/linter/fixtures/json/undeclare-variable.json +0 -1
  214. package/tests/utils/linter/fixtures/json/unused-variable.json +0 -1
  215. package/tests/utils/linter/fixtures/json/valid-code.json +0 -1
  216. package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +0 -5
  217. package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +0 -5
  218. package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +0 -5
  219. package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +0 -5
  220. package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +0 -4
  221. package/tests/utils/linter/fixtures/jsx/valid-code.jsx +0 -5
  222. package/tests/utils/linter/fixtures/python/keyword-error.py +0 -3
  223. package/tests/utils/linter/fixtures/python/missing-semicolon.py +0 -2
  224. package/tests/utils/linter/fixtures/python/syntax-error.py +0 -3
  225. package/tests/utils/linter/fixtures/python/undeclare-variable.py +0 -3
  226. package/tests/utils/linter/fixtures/python/unused-variable.py +0 -6
  227. package/tests/utils/linter/fixtures/python/valid-code.py +0 -12
  228. package/tests/utils/linter/fixtures/ruby/keyword-error.rb +0 -2
  229. package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +0 -1
  230. package/tests/utils/linter/fixtures/ruby/syntax-error.rb +0 -2
  231. package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +0 -1
  232. package/tests/utils/linter/fixtures/ruby/unused-variable.rb +0 -2
  233. package/tests/utils/linter/fixtures/ruby/valid-code.rb +0 -1
  234. package/tests/utils/linter/fixtures/sass/keyword-error.sass +0 -2
  235. package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +0 -3
  236. package/tests/utils/linter/fixtures/sass/syntax-error.sass +0 -3
  237. package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +0 -2
  238. package/tests/utils/linter/fixtures/sass/unused-variable.sass +0 -4
  239. package/tests/utils/linter/fixtures/sass/valid-code.sass +0 -2
  240. package/tests/utils/linter/fixtures/scss/keyword-error.scss +0 -1
  241. package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +0 -1
  242. package/tests/utils/linter/fixtures/scss/syntax-error.scss +0 -1
  243. package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +0 -1
  244. package/tests/utils/linter/fixtures/scss/unused-variable.scss +0 -2
  245. package/tests/utils/linter/fixtures/scss/valid-code.scss +0 -1
  246. package/tests/utils/linter/fixtures/shell/keyword-error.sh +0 -5
  247. package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +0 -3
  248. package/tests/utils/linter/fixtures/shell/syntax-error.sh +0 -4
  249. package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +0 -3
  250. package/tests/utils/linter/fixtures/shell/unused-variable.sh +0 -4
  251. package/tests/utils/linter/fixtures/shell/valid-code.sh +0 -3
  252. package/tests/utils/linter/fixtures/ts/keyword-error.ts +0 -1
  253. package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +0 -1
  254. package/tests/utils/linter/fixtures/ts/syntax-error.ts +0 -1
  255. package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +0 -1
  256. package/tests/utils/linter/fixtures/ts/unused-variable.ts +0 -3
  257. package/tests/utils/linter/fixtures/ts/valid-code.ts +0 -3
  258. package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +0 -5
  259. package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +0 -5
  260. package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +0 -5
  261. package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +0 -6
  262. package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +0 -6
  263. package/tests/utils/linter/fixtures/tsx/valid-code.tsx +0 -5
  264. package/tests/utils/linter/fixtures/vue/keyword-error.vue +0 -6
  265. package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +0 -6
  266. package/tests/utils/linter/fixtures/vue/syntax-error.vue +0 -6
  267. package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +0 -6
  268. package/tests/utils/linter/fixtures/vue/unused-variable.vue +0 -7
  269. package/tests/utils/linter/fixtures/vue/valid-code.vue +0 -6
  270. package/tests/utils/linter/fixtures/yaml/keyword-error.yml +0 -1
  271. package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +0 -2
  272. package/tests/utils/linter/fixtures/yaml/syntax-error.yml +0 -1
  273. package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +0 -1
  274. package/tests/utils/linter/fixtures/yaml/unused-variable.yml +0 -2
  275. package/tests/utils/linter/fixtures/yaml/valid-code.yml +0 -3
  276. package/tests/utils/linter/index.test.mjs +0 -440
  277. package/tests/utils/linter/scan-results.mjs +0 -42
  278. package/tests/utils/load-config.test.mjs +0 -141
  279. package/tests/utils/markdown/index.test.mjs +0 -478
  280. package/tests/utils/mermaid-validator.test.mjs +0 -541
  281. package/tests/utils/mock-chat-model.mjs +0 -12
  282. package/tests/utils/preferences-utils.test.mjs +0 -465
  283. package/tests/utils/save-value-to-config.test.mjs +0 -483
  284. package/tests/utils/utils.test.mjs +0 -941
@@ -1,1657 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { promises as fs } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import { parse as parseYAML } from "yaml";
6
- import init, { generateYAML } from "../../../agents/init/index.mjs";
7
-
8
- describe("generateYAML", () => {
9
- // Helper function to parse YAML and verify it's valid
10
- function parseAndValidateYAML(yamlString) {
11
- let config;
12
- expect(() => {
13
- config = parseYAML(yamlString);
14
- }).not.toThrow(); // YAML should be parseable
15
- expect(config).toBeDefined();
16
- return config;
17
- }
18
- describe("Complete valid user scenarios", () => {
19
- test("should handle typical developer-focused configuration", () => {
20
- const input = {
21
- // Question 1: Document Purpose (checkbox, at least 1)
22
- documentPurpose: ["getStarted", "findAnswers"],
23
-
24
- // Question 2: Target Audience (checkbox, at least 1)
25
- targetAudienceTypes: ["developers"],
26
-
27
- // Question 3: Reader Knowledge Level (select, single choice)
28
- readerKnowledgeLevel: "domainFamiliar",
29
-
30
- // Question 4: Documentation Depth (select, single choice)
31
- documentationDepth: "balancedCoverage",
32
-
33
- // Question 5: Primary Language (select, single choice)
34
- locale: "en",
35
-
36
- // Question 6: Translation Languages (checkbox, optional)
37
- translateLanguages: ["zh", "ja"],
38
-
39
- // Question 7: Documentation Directory (input, with default)
40
- docsDir: ".aigne/doc-smith/docs",
41
-
42
- // Question 8: Source Paths (multiple inputs, with default)
43
- sourcesPath: ["./src", "./lib"],
44
-
45
- // Project Info (from getProjectInfo)
46
- projectName: "My Awesome Project",
47
- projectDesc: "A comprehensive library for developers",
48
- projectLogo: "assets/logo.png",
49
- };
50
-
51
- const result = generateYAML(input);
52
-
53
- // Parse and validate YAML structure
54
- const config = parseAndValidateYAML(result);
55
-
56
- // Verify project information
57
- expect(config.projectName).toBe("My Awesome Project");
58
- expect(config.projectDesc).toBe("A comprehensive library for developers");
59
- expect(config.projectLogo).toBe("assets/logo.png");
60
-
61
- // Verify document purpose array
62
- expect(Array.isArray(config.documentPurpose)).toBe(true);
63
- expect(config.documentPurpose).toEqual(["getStarted", "findAnswers"]);
64
-
65
- // Verify target audience array
66
- expect(Array.isArray(config.targetAudienceTypes)).toBe(true);
67
- expect(config.targetAudienceTypes).toEqual(["developers"]);
68
-
69
- // Verify single value fields
70
- expect(config.readerKnowledgeLevel).toBe("domainFamiliar");
71
- expect(config.documentationDepth).toBe("balancedCoverage");
72
- expect(config.locale).toBe("en");
73
-
74
- // Verify translation languages array
75
- expect(Array.isArray(config.translateLanguages)).toBe(true);
76
- expect(config.translateLanguages).toEqual(["zh", "ja"]);
77
-
78
- // Verify paths
79
- expect(config.docsDir).toBe(".aigne/doc-smith/docs");
80
- expect(Array.isArray(config.sourcesPath)).toBe(true);
81
- expect(config.sourcesPath).toEqual(["./src", "./lib"]);
82
-
83
- // Verify comments are present (using string matching for comments)
84
- expect(result).toContain("# Project information for documentation publishing");
85
- expect(result).toContain("# Documentation Configuration");
86
- });
87
-
88
- test("should handle end-user focused minimal configuration", () => {
89
- const input = {
90
- documentPurpose: ["getStarted"],
91
- targetAudienceTypes: ["endUsers"],
92
- readerKnowledgeLevel: "completeBeginners",
93
- documentationDepth: "essentialOnly",
94
- locale: "en",
95
- translateLanguages: [],
96
- docsDir: "./docs",
97
- sourcesPath: ["./"],
98
- projectName: "User-Friendly App",
99
- projectDesc: "Simple app for everyone",
100
- projectLogo: "",
101
- };
102
-
103
- const result = generateYAML(input);
104
-
105
- // Parse and validate YAML structure
106
- const config = parseAndValidateYAML(result);
107
-
108
- // Verify configuration values
109
- expect(config.documentPurpose).toEqual(["getStarted"]);
110
- expect(config.targetAudienceTypes).toEqual(["endUsers"]);
111
- expect(config.readerKnowledgeLevel).toBe("completeBeginners");
112
- expect(config.documentationDepth).toBe("essentialOnly");
113
- expect(config.locale).toBe("en");
114
- expect(config.translateLanguages).toEqual(undefined);
115
- expect(config.docsDir).toBe("./docs");
116
- expect(config.sourcesPath).toEqual(["./"]);
117
- expect(config.projectName).toBe("User-Friendly App");
118
- expect(config.projectDesc).toBe("Simple app for everyone");
119
- expect(config.projectLogo).toBe("");
120
-
121
- // Verify comments for empty translateLanguages (string matching for comments)
122
- expect(result).toContain("# translateLanguages: # List of languages to translate");
123
- });
124
-
125
- test("should handle mixed purpose with priority selection", () => {
126
- // Simulates user selecting mixedPurpose first, then choosing top 2 priorities
127
- const input = {
128
- documentPurpose: ["completeTasks", "findAnswers"], // The 2 priorities selected after mixedPurpose
129
- targetAudienceTypes: ["developers", "devops"],
130
- readerKnowledgeLevel: "experiencedUsers",
131
- documentationDepth: "comprehensive",
132
- locale: "zh-CN",
133
- translateLanguages: ["en"],
134
- docsDir: "./documentation",
135
- sourcesPath: ["./src/**/*.js", "./lib/**/*.ts"],
136
- projectName: "Enterprise Solution",
137
- projectDesc: "Advanced enterprise-grade solution",
138
- projectLogo: "brand/logo.svg",
139
- };
140
-
141
- const result = generateYAML(input);
142
-
143
- expect(result).toContain("- completeTasks");
144
- expect(result).toContain("- findAnswers");
145
- expect(result).toContain("- developers");
146
- expect(result).toContain("- devops");
147
- expect(result).toContain("locale: zh-CN");
148
- expect(result).toContain("- en");
149
- });
150
- });
151
-
152
- describe("Document Purpose combinations", () => {
153
- test("should handle single purpose selection", () => {
154
- const validPurposes = [
155
- "getStarted",
156
- "completeTasks",
157
- "findAnswers",
158
- "understandSystem",
159
- "solveProblems",
160
- ];
161
-
162
- validPurposes.forEach((purpose) => {
163
- const input = {
164
- documentPurpose: [purpose],
165
- targetAudienceTypes: ["developers"],
166
- locale: "en",
167
- };
168
-
169
- const result = generateYAML(input);
170
- const config = parseAndValidateYAML(result);
171
-
172
- expect(config.documentPurpose).toEqual([purpose]);
173
- });
174
- });
175
-
176
- test("should handle multiple purpose combinations", () => {
177
- const combinations = [
178
- ["getStarted", "completeTasks"],
179
- ["findAnswers", "solveProblems"],
180
- ["understandSystem", "completeTasks", "findAnswers"],
181
- ];
182
-
183
- combinations.forEach((purposes) => {
184
- const input = {
185
- documentPurpose: purposes,
186
- targetAudienceTypes: ["developers"],
187
- locale: "en",
188
- };
189
-
190
- const result = generateYAML(input);
191
- const config = parseAndValidateYAML(result);
192
-
193
- expect(config.documentPurpose).toEqual(purposes);
194
- });
195
- });
196
- });
197
-
198
- describe("Target Audience combinations", () => {
199
- test("should handle all valid audience types", () => {
200
- const validAudiences = [
201
- "endUsers",
202
- "developers",
203
- "devops",
204
- "decisionMakers",
205
- "supportTeams",
206
- "mixedTechnical",
207
- ];
208
-
209
- validAudiences.forEach((audience) => {
210
- const input = {
211
- documentPurpose: ["getStarted"],
212
- targetAudienceTypes: [audience],
213
- locale: "en",
214
- };
215
-
216
- const result = generateYAML(input);
217
- expect(result).toContain(`- ${audience}`);
218
- });
219
- });
220
-
221
- test("should handle mixed audience selections", () => {
222
- const input = {
223
- documentPurpose: ["completeTasks"],
224
- targetAudienceTypes: ["developers", "devops", "supportTeams"],
225
- locale: "en",
226
- };
227
-
228
- const result = generateYAML(input);
229
- expect(result).toContain("- developers");
230
- expect(result).toContain("- devops");
231
- expect(result).toContain("- supportTeams");
232
- });
233
- });
234
-
235
- describe("Knowledge Level scenarios", () => {
236
- test("should handle all valid knowledge levels", () => {
237
- const validLevels = [
238
- "completeBeginners",
239
- "domainFamiliar",
240
- "hasBasicKnowledge",
241
- "experiencedUsers",
242
- "emergencyTroubleshooting",
243
- "exploringEvaluating",
244
- ];
245
-
246
- validLevels.forEach((level) => {
247
- const input = {
248
- documentPurpose: ["getStarted"],
249
- targetAudienceTypes: ["developers"],
250
- readerKnowledgeLevel: level,
251
- locale: "en",
252
- docsDir: "./docs", // Provide required field to avoid YAML generation bug
253
- };
254
-
255
- const result = generateYAML(input);
256
- const config = parseAndValidateYAML(result);
257
-
258
- expect(config.readerKnowledgeLevel).toBe(level);
259
- });
260
- });
261
- });
262
-
263
- describe("Documentation Depth scenarios", () => {
264
- test("should handle all valid depth levels", () => {
265
- const validDepths = ["essentialOnly", "balancedCoverage", "comprehensive", "aiDecide"];
266
-
267
- validDepths.forEach((depth) => {
268
- const input = {
269
- documentPurpose: ["getStarted"],
270
- targetAudienceTypes: ["developers"],
271
- documentationDepth: depth,
272
- locale: "en",
273
- };
274
-
275
- const result = generateYAML(input);
276
- expect(result).toContain(`documentationDepth: ${depth}`);
277
- });
278
- });
279
- });
280
-
281
- describe("Locale and translation scenarios", () => {
282
- test("should handle all supported locales", () => {
283
- const supportedLocales = [
284
- "en",
285
- "zh",
286
- "ja",
287
- "ko",
288
- "es",
289
- "fr",
290
- "de",
291
- "it",
292
- "pt",
293
- "ru",
294
- "ar",
295
- "hi",
296
- "th",
297
- "vi",
298
- "id",
299
- "ms",
300
- "tl",
301
- "tr",
302
- "pl",
303
- "nl",
304
- "sv",
305
- "da",
306
- "no",
307
- "fi",
308
- "hu",
309
- "cs",
310
- "sk",
311
- "ro",
312
- "bg",
313
- "hr",
314
- "sl",
315
- "et",
316
- "lv",
317
- "lt",
318
- "mt",
319
- "el",
320
- "he",
321
- "fa",
322
- "ur",
323
- "bn",
324
- "ta",
325
- "te",
326
- "kn",
327
- "ml",
328
- "gu",
329
- "pa",
330
- "or",
331
- "as",
332
- "ne",
333
- "si",
334
- ];
335
-
336
- // Test a subset to keep test reasonable
337
- const testLocales = supportedLocales.slice(0, 10);
338
-
339
- testLocales.forEach((locale) => {
340
- const input = {
341
- documentPurpose: ["getStarted"],
342
- targetAudienceTypes: ["developers"],
343
- locale: locale,
344
- };
345
-
346
- const result = generateYAML(input);
347
- const config = parseAndValidateYAML(result);
348
-
349
- expect(config.locale).toBe(locale);
350
- });
351
- });
352
-
353
- test("should handle complex locale formats", () => {
354
- const complexLocales = ["zh-CN", "zh-TW", "en-US", "en-GB", "pt-BR", "es-ES", "fr-CA"];
355
-
356
- complexLocales.forEach((locale) => {
357
- const input = {
358
- documentPurpose: ["getStarted"],
359
- targetAudienceTypes: ["developers"],
360
- locale: locale,
361
- };
362
-
363
- const result = generateYAML(input);
364
- expect(result).toContain(`locale: ${locale}`);
365
- });
366
- });
367
-
368
- test("should handle translation language combinations", () => {
369
- const input = {
370
- documentPurpose: ["getStarted"],
371
- targetAudienceTypes: ["developers"],
372
- locale: "en",
373
- translateLanguages: ["zh", "ja", "ko", "es", "fr"],
374
- };
375
-
376
- const result = generateYAML(input);
377
- expect(result).toContain("translateLanguages:");
378
- expect(result).toContain("- zh");
379
- expect(result).toContain("- ja");
380
- expect(result).toContain("- ko");
381
- expect(result).toContain("- es");
382
- expect(result).toContain("- fr");
383
- });
384
-
385
- test("should handle empty translation languages", () => {
386
- const input = {
387
- documentPurpose: ["getStarted"],
388
- targetAudienceTypes: ["developers"],
389
- locale: "en",
390
- translateLanguages: [],
391
- };
392
-
393
- const result = generateYAML(input);
394
- expect(result).toContain("# translateLanguages: # List of languages to translate");
395
- expect(result).toContain("# - zh # Example: Chinese translation");
396
- expect(result).toContain("# - en # Example: English translation");
397
- });
398
- });
399
-
400
- describe("Project info from GitHub/Git scenarios - Strict YAML validation", () => {
401
- test("should handle typical GitHub project names with various formats", () => {
402
- const githubProjectNames = [
403
- // Common GitHub naming patterns
404
- "awesome-project",
405
- "my_awesome_project",
406
- "AwesomeProject",
407
- "project-v2.0.1",
408
- "project_name_2024",
409
- "some-org.awesome-project",
410
- "project.config.js",
411
- "@scoped/package-name",
412
- "react-native-component",
413
- "vue3-typescript-starter",
414
- "nestjs-api-boilerplate",
415
- "k8s-deployment-tools",
416
- ];
417
-
418
- githubProjectNames.forEach((projectName) => {
419
- const input = {
420
- documentPurpose: ["getStarted"],
421
- targetAudienceTypes: ["developers"],
422
- locale: "en",
423
- docsDir: "./docs",
424
- projectName,
425
- projectDesc: `Description for ${projectName}`,
426
- projectLogo: "assets/logo.png",
427
- };
428
-
429
- const result = generateYAML(input);
430
- const config = parseAndValidateYAML(result);
431
-
432
- expect(config.projectName).toBe(projectName);
433
- expect(config.projectDesc).toBe(`Description for ${projectName}`);
434
- });
435
- });
436
-
437
- test("should handle project names with special characters and symbols", () => {
438
- const specialCharacterNames = [
439
- // YAML potentially problematic characters
440
- "project: with colon",
441
- 'project "with quotes"',
442
- "project 'with single quotes'",
443
- "project [with brackets]",
444
- "project {with braces}",
445
- "project | with pipe",
446
- "project > with gt",
447
- "project < with lt",
448
- "project & with ampersand",
449
- "project % with percent",
450
- "project # with hash",
451
- "project @ with at",
452
- "project ! with exclamation",
453
- "project ? with question",
454
- "project * with asterisk",
455
- "project ~ with tilde",
456
- "project ` with backtick",
457
- "project \\ with backslash",
458
- "project / with slash",
459
- ];
460
-
461
- specialCharacterNames.forEach((projectName) => {
462
- const input = {
463
- documentPurpose: ["getStarted"],
464
- targetAudienceTypes: ["developers"],
465
- locale: "en",
466
- docsDir: "./docs",
467
- projectName,
468
- projectDesc: "Project with special characters",
469
- projectLogo: "logo.png",
470
- };
471
-
472
- const result = generateYAML(input);
473
- const config = parseAndValidateYAML(result);
474
-
475
- // YAML should be valid and preserve the exact project name
476
- expect(config.projectName).toBe(projectName);
477
- });
478
- });
479
-
480
- test("should handle project descriptions with complex formatting", () => {
481
- const complexDescriptions = [
482
- // Multi-line with various formatting
483
- "Line 1: Overview\nLine 2: Features\n\nLine 4: Usage",
484
- "Description with\ttabs\tand\nnewlines",
485
- "Description with 'single quotes' and \"double quotes\"",
486
- "Description with [links](http://example.com) and *emphasis*",
487
- "Description with #hashtags and @mentions",
488
- 'JSON-like content: {"key": "value", "array": [1, 2, 3]}',
489
- "YAML-like content:\n key: value\n list:\n - item1\n - item2",
490
- "Code snippets: `npm install` and ```javascript\nconsole.log('hello');\n```",
491
- "URLs: https://github.com/user/repo and http://example.com:8080/path?query=value",
492
- "Email: user@example.com and file paths: /usr/local/bin/app",
493
- ];
494
-
495
- complexDescriptions.forEach((projectDesc) => {
496
- const input = {
497
- documentPurpose: ["getStarted"],
498
- targetAudienceTypes: ["developers"],
499
- locale: "en",
500
- docsDir: "./docs",
501
- projectName: "Test Project",
502
- projectDesc,
503
- projectLogo: "logo.png",
504
- };
505
-
506
- const result = generateYAML(input);
507
- const config = parseAndValidateYAML(result);
508
-
509
- // YAML should be valid and preserve the exact description
510
- expect(config.projectDesc).toBe(projectDesc);
511
- });
512
- });
513
-
514
- test("should handle international and emoji-rich project info", () => {
515
- const internationalCases = [
516
- {
517
- projectName: "项目名称-中文",
518
- projectDesc: "这是一个中文项目描述,包含中文标点符号:,。!?",
519
- locale: "zh-CN",
520
- },
521
- {
522
- projectName: "プロジェクト名前-日本語",
523
- projectDesc: "これは日本語のプロジェクト説明です。ひらがな、カタカナ、漢字を含みます。",
524
- locale: "ja",
525
- },
526
- {
527
- projectName: "proyecto-español",
528
- projectDesc: "Descripción del proyecto con acentos: ñáéíóúü y signos ¡¿",
529
- locale: "es",
530
- },
531
- {
532
- projectName: "проект-русский",
533
- projectDesc: "Описание проекта на русском языке с кириллицей",
534
- locale: "ru",
535
- },
536
- {
537
- projectName: "مشروع-عربي",
538
- projectDesc: "وصف المشروع باللغة العربية من اليمين إلى اليسار",
539
- locale: "ar",
540
- },
541
- {
542
- projectName: "🚀 Awesome Project 🎉",
543
- projectDesc: "Project with emojis: 📱💻🔥⚡🌟✨🎯🚀💡🔧⭐🎨🎪🎭",
544
- locale: "en",
545
- },
546
- {
547
- projectName: "Mixed語言Project混合नाम",
548
- projectDesc: "Multi-language混合description with हिंदी, 中文, English, and العربية",
549
- locale: "en",
550
- },
551
- ];
552
-
553
- internationalCases.forEach(({ projectName, projectDesc, locale }) => {
554
- const input = {
555
- documentPurpose: ["getStarted"],
556
- targetAudienceTypes: ["developers"],
557
- locale,
558
- docsDir: "./docs",
559
- projectName,
560
- projectDesc,
561
- projectLogo: "assets/logo.svg",
562
- };
563
-
564
- const result = generateYAML(input);
565
- const config = parseAndValidateYAML(result);
566
-
567
- expect(config.projectName).toBe(projectName);
568
- expect(config.projectDesc).toBe(projectDesc);
569
- expect(config.locale).toBe(locale);
570
- });
571
- });
572
-
573
- test("should handle project logos with various path formats", () => {
574
- const logoPaths = [
575
- // Different path formats that might come from GitHub
576
- "logo.png",
577
- "assets/logo.svg",
578
- "docs/images/logo-128x128.png",
579
- "public/icons/favicon.ico",
580
- ".github/logo.jpg",
581
- "static/brand/logo_dark.png",
582
- "src/assets/images/logo@2x.png",
583
- "media/logos/company-logo.webp",
584
- "https://raw.githubusercontent.com/user/repo/main/logo.png",
585
- "https://github.com/user/repo/blob/main/assets/logo.svg?raw=true",
586
- // Paths with special characters
587
- "assets/logo with spaces.png",
588
- "logos/logo-企业.svg",
589
- "images/логотип.png",
590
- "assets/logo_v2.0-beta.png",
591
- "brand/logo[dark].png",
592
- "icons/logo{small}.ico",
593
- ];
594
-
595
- logoPaths.forEach((projectLogo) => {
596
- const input = {
597
- documentPurpose: ["getStarted"],
598
- targetAudienceTypes: ["developers"],
599
- locale: "en",
600
- docsDir: "./docs",
601
- projectName: "Test Project",
602
- projectDesc: "Project with various logo paths",
603
- projectLogo,
604
- };
605
-
606
- const result = generateYAML(input);
607
- const config = parseAndValidateYAML(result);
608
-
609
- expect(config.projectLogo).toBe(projectLogo);
610
- });
611
- });
612
-
613
- test("should handle edge cases from GitHub repository parsing", () => {
614
- const edgeCases = [
615
- {
616
- name: "Empty values",
617
- projectName: "",
618
- projectDesc: "",
619
- projectLogo: "",
620
- },
621
- {
622
- name: "Very long values",
623
- projectName: "a".repeat(200),
624
- projectDesc:
625
- "This is a very long description that might come from a detailed README file. ".repeat(
626
- 50,
627
- ),
628
- projectLogo:
629
- "assets/very/deep/path/to/a/logo/file/that/might/exist/in/some/repository/structure/logo.png",
630
- },
631
- {
632
- name: "YAML reserved words",
633
- projectName: "true",
634
- projectDesc: "false",
635
- projectLogo: "null",
636
- },
637
- {
638
- name: "Numeric strings",
639
- projectName: "123",
640
- projectDesc: "456.789",
641
- projectLogo: "0x123.png",
642
- },
643
- {
644
- name: "Boolean-like strings",
645
- projectName: "yes",
646
- projectDesc: "no",
647
- projectLogo: "on.svg",
648
- },
649
- ];
650
-
651
- edgeCases.forEach(({ projectName, projectDesc, projectLogo }) => {
652
- const input = {
653
- documentPurpose: ["getStarted"],
654
- targetAudienceTypes: ["developers"],
655
- locale: "en",
656
- docsDir: "./docs",
657
- projectName,
658
- projectDesc,
659
- projectLogo,
660
- };
661
-
662
- const result = generateYAML(input);
663
- const config = parseAndValidateYAML(result);
664
-
665
- // All values should be preserved exactly as input
666
- expect(config.projectName).toBe(projectName);
667
- expect(config.projectDesc).toBe(projectDesc);
668
- expect(config.projectLogo).toBe(projectLogo);
669
- });
670
- });
671
-
672
- test("should handle GitHub-specific metadata patterns", () => {
673
- const githubPatterns = [
674
- {
675
- projectName: "facebook/react",
676
- projectDesc:
677
- "A declarative, efficient, and flexible JavaScript library for building user interfaces.",
678
- projectLogo:
679
- "https://github.com/facebook/react/blob/main/fixtures/attribute-behavior/src/logo.svg",
680
- },
681
- {
682
- projectName: "microsoft/vscode",
683
- projectDesc:
684
- "Visual Studio Code - The editor you love. Built for developers, by developers.",
685
- projectLogo: "resources/linux/code.png",
686
- },
687
- {
688
- projectName: "vercel/next.js",
689
- projectDesc: "The React Framework – created and maintained by @vercel.",
690
- projectLogo: "docs/assets/next-logo.svg",
691
- },
692
- {
693
- projectName: "nodejs/node",
694
- projectDesc: "Node.js JavaScript runtime ✨🐢🚀✨",
695
- projectLogo: "src/res/node.ico",
696
- },
697
- ];
698
-
699
- githubPatterns.forEach(({ projectName, projectDesc, projectLogo }) => {
700
- const input = {
701
- documentPurpose: ["getStarted"],
702
- targetAudienceTypes: ["developers"],
703
- locale: "en",
704
- docsDir: "./docs",
705
- projectName,
706
- projectDesc,
707
- projectLogo,
708
- };
709
-
710
- const result = generateYAML(input);
711
- const config = parseAndValidateYAML(result);
712
-
713
- expect(config.projectName).toBe(projectName);
714
- expect(config.projectDesc).toBe(projectDesc);
715
- expect(config.projectLogo).toBe(projectLogo);
716
- });
717
- });
718
-
719
- test("should handle malformed or unusual project info", () => {
720
- const malformedCases = [
721
- {
722
- name: "HTML-like content",
723
- projectName: "<script>alert('xss')</script>",
724
- projectDesc: "<h1>Title</h1><p>Description with <a href='#'>links</a></p>",
725
- projectLogo: "<img src='logo.png' alt='Logo'/>",
726
- },
727
- {
728
- name: "Markdown content",
729
- projectName: "# My Project",
730
- projectDesc:
731
- "## Description\n\n- Feature 1\n- Feature 2\n\n```js\nconsole.log('hello');\n```",
732
- projectLogo: "![Logo](logo.png)",
733
- },
734
- {
735
- name: "Control characters",
736
- projectName: "Project\x00Name",
737
- projectDesc: "Description\x01with\x02control\x03characters",
738
- projectLogo: "logo\x04.png",
739
- },
740
- ];
741
-
742
- malformedCases.forEach(({ projectName, projectDesc, projectLogo }) => {
743
- const input = {
744
- documentPurpose: ["getStarted"],
745
- targetAudienceTypes: ["developers"],
746
- locale: "en",
747
- docsDir: "./docs",
748
- projectName,
749
- projectDesc,
750
- projectLogo,
751
- };
752
-
753
- const result = generateYAML(input);
754
- const config = parseAndValidateYAML(result);
755
-
756
- // Even malformed content should be handled safely
757
- expect(config.projectName).toBe(projectName);
758
- expect(config.projectDesc).toBe(projectDesc);
759
- expect(config.projectLogo).toBe(projectLogo);
760
- });
761
- });
762
- });
763
-
764
- describe("User input path scenarios", () => {
765
- test("should handle documentation directory variations", () => {
766
- const docDirVariations = [
767
- "./docs",
768
- ".aigne/doc-smith/docs",
769
- "/absolute/path/to/docs",
770
- "~/user/docs",
771
- "../relative/docs",
772
- "docs with spaces",
773
- "文档目录",
774
- ];
775
-
776
- docDirVariations.forEach((docsDir) => {
777
- const input = {
778
- documentPurpose: ["getStarted"],
779
- targetAudienceTypes: ["developers"],
780
- locale: "en",
781
- docsDir: docsDir,
782
- };
783
-
784
- const result = generateYAML(input);
785
- expect(result).toContain("docsDir:");
786
- expect(result).toBeDefined();
787
- });
788
- });
789
-
790
- test("should handle various source path patterns", () => {
791
- const input = {
792
- documentPurpose: ["getStarted"],
793
- targetAudienceTypes: ["developers"],
794
- locale: "en",
795
- sourcesPath: [
796
- "./src",
797
- "./lib",
798
- "packages/*/src",
799
- "apps/**/*.ts",
800
- "!**/*.test.js",
801
- "src with spaces",
802
- "源代码/模块",
803
- ],
804
- };
805
-
806
- const result = generateYAML(input);
807
- expect(result).toContain("sourcesPath:");
808
- expect(result).toContain("- ./src");
809
- expect(result).toContain("- ./lib");
810
- expect(result).toContain("- packages/*/src");
811
- expect(result).toBeDefined();
812
- });
813
-
814
- test("should handle single source path", () => {
815
- const input = {
816
- documentPurpose: ["getStarted"],
817
- targetAudienceTypes: ["developers"],
818
- locale: "en",
819
- sourcesPath: ["./"],
820
- };
821
-
822
- const result = generateYAML(input);
823
- expect(result).toContain("sourcesPath:");
824
- expect(result).toContain("- ./");
825
- });
826
-
827
- test("should handle complex glob patterns from user input", () => {
828
- const input = {
829
- documentPurpose: ["findAnswers"],
830
- targetAudienceTypes: ["developers"],
831
- locale: "en",
832
- sourcesPath: [
833
- "**/*.{js,ts,jsx,tsx}",
834
- "src/**/*.{vue,svelte}",
835
- "!**/node_modules/**",
836
- "{packages,apps}/**/*.py",
837
- "docs/**/*.md",
838
- ],
839
- };
840
-
841
- const result = generateYAML(input);
842
- expect(result).toContain("sourcesPath:");
843
- expect(result).toBeDefined();
844
- expect(typeof result).toBe("string");
845
- });
846
- });
847
-
848
- describe("Edge cases that should not occur from init() but may happen", () => {
849
- test("should handle undefined required fields - exposes bug", () => {
850
- // This could happen if init() has bugs or incomplete validation
851
- const input = {
852
- documentPurpose: ["getStarted"],
853
- targetAudienceTypes: ["developers"],
854
- locale: undefined, // Should not happen from init()
855
- docsDir: undefined, // Should not happen from init()
856
- };
857
-
858
- const result = generateYAML(input);
859
-
860
- // This test will FAIL because the function generates invalid YAML
861
- // The YAML should be parseable and contain proper default values
862
- const config = parseAndValidateYAML(result);
863
-
864
- // These expectations will FAIL because function generates invalid "{}" values
865
- expect(config.locale).toBe("en"); // Should default to "en"
866
- expect(config.docsDir).toBe("./aigne/doc-smith/docs"); // Should default to "./docs"
867
-
868
- // Verify other fields work correctly
869
- expect(config.documentPurpose).toEqual(["getStarted"]);
870
- expect(config.targetAudienceTypes).toEqual(["developers"]);
871
- });
872
-
873
- test("should handle empty arrays from validation failures", () => {
874
- // This could happen if validation fails but doesn't stop execution
875
- const input = {
876
- documentPurpose: [], // Should not happen due to validation
877
- targetAudienceTypes: [], // Should not happen due to validation
878
- locale: "en",
879
- docsDir: "./docs",
880
- };
881
-
882
- const result = generateYAML(input);
883
- expect(result).toContain("documentPurpose: []");
884
- expect(result).toContain("targetAudienceTypes: []");
885
- });
886
- });
887
-
888
- describe("YAML output structure validation", () => {
889
- test("should maintain consistent structure across different inputs", () => {
890
- const inputs = [
891
- {
892
- documentPurpose: ["getStarted"],
893
- targetAudienceTypes: ["endUsers"],
894
- locale: "en",
895
- },
896
- {
897
- documentPurpose: ["findAnswers", "completeTasks"],
898
- targetAudienceTypes: ["developers", "devops"],
899
- locale: "zh-CN",
900
- translateLanguages: ["en", "ja"],
901
- },
902
- ];
903
-
904
- inputs.forEach((input) => {
905
- const result = generateYAML(input);
906
-
907
- // Should always include these sections
908
- expect(result).toContain("# Project information for documentation publishing");
909
- expect(result).toContain("# Documentation Configuration");
910
- expect(result).toContain("projectName:");
911
- expect(result).toContain("documentPurpose:");
912
- expect(result).toContain("targetAudienceTypes:");
913
- expect(result).toContain("locale:");
914
- expect(result).toContain("sourcesPath:");
915
- });
916
- });
917
-
918
- test("should include all necessary comments and examples", () => {
919
- const input = {
920
- documentPurpose: ["getStarted"],
921
- targetAudienceTypes: ["developers"],
922
- locale: "en",
923
- translateLanguages: [],
924
- };
925
-
926
- const result = generateYAML(input);
927
-
928
- expect(result).toContain("# Purpose: What's the main outcome");
929
- expect(result).toContain("# Available options (uncomment and modify as needed):");
930
- expect(result).toContain("# Target Audience: Who will be reading this most often?");
931
- expect(result).toContain("# Reader Knowledge Level:");
932
- expect(result).toContain("# Documentation Depth:");
933
- expect(result).toContain("# Custom Rules:");
934
- expect(result).toContain("# Glossary:");
935
- });
936
- });
937
- });
938
-
939
- describe("init", () => {
940
- // Helper function to create mock prompts
941
- function createMockPrompts(responses) {
942
- return {
943
- checkbox: (options) => {
944
- const key = options.message.match(/\[(\d+)\/\d+\]/)?.[1] || "default";
945
- const response = responses[`checkbox_${key}`] || responses["checkbox"] || [];
946
- return Promise.resolve(response);
947
- },
948
- select: (options) => {
949
- const key = options.message.match(/\[(\d+)\/\d+\]/)?.[1] || "default";
950
- const response = responses[`select_${key}`] || responses["select"] || "";
951
- return Promise.resolve(response);
952
- },
953
- input: (options) => {
954
- const key = options.message.match(/\[(\d+)\/\d+\]/)?.[1] || "default";
955
- const response = responses[`input_${key}`] || responses["input"] || options.default || "";
956
- return Promise.resolve(response);
957
- },
958
- search: () => {
959
- const response = responses["search"] || "";
960
- return Promise.resolve(response);
961
- },
962
- };
963
- }
964
-
965
- // Helper function to create temporary directory
966
- async function createTempDir() {
967
- const tempDir = join(tmpdir(), `aigne-test-${Date.now()}`);
968
- await fs.mkdir(tempDir, { recursive: true });
969
- return tempDir;
970
- }
971
-
972
- // Helper function to cleanup temp directory
973
- async function cleanupTempDir(tempDir) {
974
- try {
975
- await fs.rm(tempDir, { recursive: true, force: true });
976
- } catch {
977
- // Ignore cleanup errors since they don't affect test results
978
- }
979
- }
980
-
981
- describe("Complete init workflow", () => {
982
- test("should complete full init workflow with typical developer responses", async () => {
983
- const tempDir = await createTempDir();
984
-
985
- try {
986
- const mockResponses = {
987
- checkbox_1: ["getStarted", "findAnswers"], // Document purpose
988
- checkbox_2: ["developers"], // Target audience
989
- select_3: "domainFamiliar", // Reader knowledge level
990
- select_4: "balancedCoverage", // Documentation depth
991
- select_5: "en", // Primary language
992
- checkbox_6: ["zh", "ja"], // Translation languages
993
- input_7: join(tempDir, "docs"), // Documentation directory
994
- search: "", // Source paths (empty to finish)
995
- input_9: "Custom rules for documentation", // Custom rules (last step)
996
- };
997
-
998
- const mockPrompts = createMockPrompts(mockResponses);
999
- const options = { prompts: mockPrompts };
1000
-
1001
- const result = await init(
1002
- {
1003
- outputPath: tempDir,
1004
- fileName: "config.yaml",
1005
- skipIfExists: false,
1006
- },
1007
- options,
1008
- );
1009
-
1010
- // Check that function completed successfully
1011
- expect(result).toBeDefined();
1012
-
1013
- // Check that config file was created
1014
- const configPath = join(tempDir, "config.yaml");
1015
- const configExists = await fs
1016
- .access(configPath)
1017
- .then(() => true)
1018
- .catch(() => false);
1019
- expect(configExists).toBe(true);
1020
-
1021
- // Verify the generated config content
1022
- const configContent = await fs.readFile(configPath, "utf8");
1023
- const config = parseYAML(configContent);
1024
-
1025
- expect(config.documentPurpose).toEqual(["getStarted", "findAnswers"]);
1026
- expect(config.targetAudienceTypes).toEqual(["developers"]);
1027
- expect(config.rules).toBe("Custom rules for documentation");
1028
- expect(config.readerKnowledgeLevel).toBe("domainFamiliar");
1029
- expect(config.documentationDepth).toBe("balancedCoverage");
1030
- expect(config.locale).toBe("en");
1031
- expect(config.translateLanguages).toEqual(["zh", "ja"]);
1032
- expect(config.docsDir).toBe(join(tempDir, "docs"));
1033
- expect(config.sourcesPath).toEqual(["./"]); // Default when no paths provided
1034
- } finally {
1035
- await cleanupTempDir(tempDir);
1036
- }
1037
- });
1038
-
1039
- test("should handle mixed purpose workflow with priority selection", async () => {
1040
- const tempDir = await createTempDir();
1041
-
1042
- try {
1043
- const mockResponses = {
1044
- checkbox_1: ["mixedPurpose"], // Document purpose - triggers follow-up
1045
- checkbox: ["completeTasks", "findAnswers"], // Top priorities after mixedPurpose
1046
- checkbox_2: ["developers", "devops"], // Target audience
1047
- select_3: "experiencedUsers", // Reader knowledge level
1048
- select_4: "comprehensive", // Documentation depth
1049
- select_5: "zh-CN", // Primary language
1050
- checkbox_6: ["en"], // Translation languages
1051
- input_7: join(tempDir, "documentation"), // Documentation directory
1052
- search: "", // Source paths (empty to finish)
1053
- input_9: "Custom rules for documentation", // Custom rules (last step)
1054
- };
1055
-
1056
- const mockPrompts = createMockPrompts(mockResponses);
1057
- const options = { prompts: mockPrompts };
1058
-
1059
- const result = await init(
1060
- {
1061
- outputPath: tempDir,
1062
- fileName: "test-config.yaml",
1063
- skipIfExists: false,
1064
- },
1065
- options,
1066
- );
1067
-
1068
- expect(result).toBeDefined();
1069
-
1070
- // Verify the generated config
1071
- const configPath = join(tempDir, "test-config.yaml");
1072
- const configContent = await fs.readFile(configPath, "utf8");
1073
- const config = parseYAML(configContent);
1074
-
1075
- expect(config.documentPurpose).toEqual(["completeTasks", "findAnswers"]);
1076
- expect(config.targetAudienceTypes).toEqual(["developers", "devops"]);
1077
- expect(config.rules).toBe("Custom rules for documentation");
1078
- expect(config.readerKnowledgeLevel).toBe("experiencedUsers");
1079
- expect(config.documentationDepth).toBe("comprehensive");
1080
- expect(config.locale).toBe("zh-CN");
1081
- expect(config.translateLanguages).toEqual(["en"]);
1082
- } finally {
1083
- await cleanupTempDir(tempDir);
1084
- }
1085
- });
1086
-
1087
- test("should handle end-user focused minimal configuration", async () => {
1088
- const tempDir = await createTempDir();
1089
-
1090
- try {
1091
- const mockResponses = {
1092
- checkbox_1: ["getStarted"], // Document purpose
1093
- checkbox_2: ["endUsers"], // Target audience
1094
- select_3: "completeBeginners", // Reader knowledge level
1095
- select_4: "essentialOnly", // Documentation depth
1096
- select_5: "en", // Primary language
1097
- checkbox_6: [], // No translation languages
1098
- input_7: join(tempDir, "simple-docs"), // Documentation directory
1099
- search: "", // Source paths (empty to finish)
1100
- input_9: "Custom rules for documentation", // Custom rules (last step)
1101
- };
1102
-
1103
- const mockPrompts = createMockPrompts(mockResponses);
1104
- const options = { prompts: mockPrompts };
1105
-
1106
- const result = await init(
1107
- {
1108
- outputPath: tempDir,
1109
- fileName: "simple-config.yaml",
1110
- skipIfExists: false,
1111
- },
1112
- options,
1113
- );
1114
-
1115
- expect(result).toBeDefined();
1116
-
1117
- const configPath = join(tempDir, "simple-config.yaml");
1118
- const configContent = await fs.readFile(configPath, "utf8");
1119
- const config = parseYAML(configContent);
1120
-
1121
- expect(config.documentPurpose).toEqual(["getStarted"]);
1122
- expect(config.targetAudienceTypes).toEqual(["endUsers"]);
1123
- expect(config.rules).toBe("Custom rules for documentation");
1124
- expect(config.readerKnowledgeLevel).toBe("completeBeginners");
1125
- expect(config.documentationDepth).toBe("essentialOnly");
1126
- expect(config.locale).toBe("en");
1127
- expect(config.translateLanguages).toBeUndefined();
1128
- } finally {
1129
- await cleanupTempDir(tempDir);
1130
- }
1131
- });
1132
- });
1133
-
1134
- describe("Source paths handling", () => {
1135
- test("should handle multiple source paths input when search returns valid paths", async () => {
1136
- const tempDir = await createTempDir();
1137
-
1138
- try {
1139
- let searchCallCount = 0;
1140
- const sourcePaths = ["./src", "./lib", "./packages", ""];
1141
-
1142
- const mockPrompts = {
1143
- checkbox: () => Promise.resolve(["getStarted"]),
1144
- select: () => Promise.resolve("en"),
1145
- input: () => Promise.resolve(join(tempDir, "docs")),
1146
- search: () => {
1147
- const response = sourcePaths[searchCallCount];
1148
- searchCallCount++;
1149
- return Promise.resolve(response);
1150
- },
1151
- };
1152
-
1153
- const options = { prompts: mockPrompts };
1154
-
1155
- // First let's create the directories that will be searched for
1156
- await fs.mkdir(join(process.cwd(), "src"), { recursive: true }).catch(() => {
1157
- // Ignore if directory already exists
1158
- });
1159
- await fs.mkdir(join(process.cwd(), "lib"), { recursive: true }).catch(() => {
1160
- // Ignore if directory already exists
1161
- });
1162
- await fs.mkdir(join(process.cwd(), "packages"), { recursive: true }).catch(() => {
1163
- // Ignore if directory already exists
1164
- });
1165
-
1166
- try {
1167
- const result = await init(
1168
- { outputPath: tempDir, fileName: "config.yaml", skipIfExists: false },
1169
- options,
1170
- );
1171
-
1172
- expect(result).toBeDefined();
1173
-
1174
- const configPath = join(tempDir, "config.yaml");
1175
- const configContent = await fs.readFile(configPath, "utf8");
1176
- const config = parseYAML(configContent);
1177
-
1178
- // Should contain the paths that were added before empty string
1179
- expect(config.sourcesPath.length).toBeGreaterThan(0);
1180
- } finally {
1181
- // Clean up test directories
1182
- await fs.rm(join(process.cwd(), "src"), { recursive: true, force: true }).catch(() => {
1183
- // Ignore cleanup errors since directories may not exist
1184
- });
1185
- await fs.rm(join(process.cwd(), "lib"), { recursive: true, force: true }).catch(() => {
1186
- // Ignore cleanup errors since directories may not exist
1187
- });
1188
- await fs
1189
- .rm(join(process.cwd(), "packages"), { recursive: true, force: true })
1190
- .catch(() => {
1191
- // Ignore cleanup errors since directories may not exist
1192
- });
1193
- }
1194
- } finally {
1195
- await cleanupTempDir(tempDir);
1196
- }
1197
- });
1198
-
1199
- test("should use default source path when no paths provided", async () => {
1200
- const tempDir = await createTempDir();
1201
-
1202
- try {
1203
- const mockPrompts = {
1204
- checkbox: () => Promise.resolve(["getStarted"]),
1205
- select: () => Promise.resolve("en"),
1206
- input: () => Promise.resolve(join(tempDir, "docs")),
1207
- search: () => Promise.resolve(""), // Immediately finish without adding paths
1208
- };
1209
-
1210
- const options = { prompts: mockPrompts };
1211
-
1212
- const result = await init(
1213
- { outputPath: tempDir, fileName: "config.yaml", skipIfExists: false },
1214
- options,
1215
- );
1216
-
1217
- expect(result).toBeDefined();
1218
-
1219
- const configPath = join(tempDir, "config.yaml");
1220
- const configContent = await fs.readFile(configPath, "utf8");
1221
- const config = parseYAML(configContent);
1222
-
1223
- expect(config.sourcesPath).toEqual(["./"]); // Default value
1224
- } finally {
1225
- await cleanupTempDir(tempDir);
1226
- }
1227
- });
1228
- });
1229
-
1230
- describe("Skip existing configuration", () => {
1231
- test("should skip if config exists and skipIfExists is true", async () => {
1232
- const tempDir = await createTempDir();
1233
-
1234
- try {
1235
- // Create existing config file
1236
- const configPath = join(tempDir, "config.yaml");
1237
- const existingConfig = 'projectName: "Existing Project"\nlocale: "zh"';
1238
- await fs.writeFile(configPath, existingConfig, "utf8");
1239
-
1240
- const mockPrompts = {
1241
- checkbox: () => Promise.resolve(["getStarted"]),
1242
- select: () => Promise.resolve("en"),
1243
- input: () => Promise.resolve("docs"),
1244
- search: () => Promise.resolve(""),
1245
- };
1246
-
1247
- const options = { prompts: mockPrompts };
1248
-
1249
- const result = await init(
1250
- { outputPath: tempDir, fileName: "config.yaml", skipIfExists: true },
1251
- options,
1252
- );
1253
-
1254
- expect(result).toBeDefined();
1255
-
1256
- // Config should remain unchanged
1257
- const configContent = await fs.readFile(configPath, "utf8");
1258
- expect(configContent).toBe(existingConfig);
1259
- } finally {
1260
- await cleanupTempDir(tempDir);
1261
- }
1262
- });
1263
-
1264
- test("should not skip if config file is empty even when skipIfExists is true", async () => {
1265
- const tempDir = await createTempDir();
1266
-
1267
- try {
1268
- // Create empty config file
1269
- const configPath = join(tempDir, "config.yaml");
1270
- await fs.writeFile(configPath, "", "utf8");
1271
-
1272
- const mockPrompts = {
1273
- checkbox: () => Promise.resolve(["getStarted"]),
1274
- select: () => Promise.resolve("en"),
1275
- input: () => Promise.resolve(join(tempDir, "docs")),
1276
- search: () => Promise.resolve(""),
1277
- };
1278
-
1279
- const options = { prompts: mockPrompts };
1280
-
1281
- const result = await init(
1282
- { outputPath: tempDir, fileName: "config.yaml", skipIfExists: true },
1283
- options,
1284
- );
1285
-
1286
- expect(result).toBeDefined();
1287
-
1288
- // Config should be generated since original was empty
1289
- const configContent = await fs.readFile(configPath, "utf8");
1290
- expect(configContent).toContain("projectName:");
1291
- expect(configContent).toContain("locale: en");
1292
- } finally {
1293
- await cleanupTempDir(tempDir);
1294
- }
1295
- });
1296
- });
1297
-
1298
- describe("Validation error handling", () => {
1299
- test("should handle empty document purpose validation", async () => {
1300
- const tempDir = await createTempDir();
1301
-
1302
- try {
1303
- // Mock prompts that will trigger validation errors by calling validate function
1304
- let validateCalled = false;
1305
- const mockPrompts = {
1306
- checkbox: (options) => {
1307
- if (options.message.includes("[1/9]") && options.validate) {
1308
- // Test the validation function directly
1309
- const validationResult = options.validate([]);
1310
- expect(validationResult).toBe(
1311
- "Please choose at least one goal for your documentation.",
1312
- );
1313
- validateCalled = true;
1314
- // Return valid result after testing validation
1315
- return Promise.resolve(["getStarted"]);
1316
- }
1317
- return Promise.resolve(["getStarted"]);
1318
- },
1319
- select: () => Promise.resolve("en"),
1320
- input: () => Promise.resolve(join(tempDir, "docs")),
1321
- search: () => Promise.resolve(""),
1322
- };
1323
-
1324
- const options = { prompts: mockPrompts };
1325
-
1326
- const result = await init(
1327
- { outputPath: tempDir, fileName: "config.yaml", skipIfExists: false },
1328
- options,
1329
- );
1330
-
1331
- expect(result).toBeDefined();
1332
- expect(validateCalled).toBe(true);
1333
- } finally {
1334
- await cleanupTempDir(tempDir);
1335
- }
1336
- });
1337
-
1338
- test("should handle empty target audience validation", async () => {
1339
- const tempDir = await createTempDir();
1340
-
1341
- try {
1342
- let audienceValidateCalled = false;
1343
- const mockPrompts = {
1344
- checkbox: (options) => {
1345
- if (options.message.includes("[1/9]")) {
1346
- return Promise.resolve(["getStarted"]); // Valid document purpose
1347
- }
1348
- if (options.message.includes("[2/9]") && options.validate) {
1349
- // Test the validation function for target audience
1350
- const validationResult = options.validate([]);
1351
- expect(validationResult).toBe("Please choose at least one audience.");
1352
- audienceValidateCalled = true;
1353
- return Promise.resolve(["developers"]); // Valid result after testing
1354
- }
1355
- return Promise.resolve(["developers"]);
1356
- },
1357
- select: () => Promise.resolve("en"),
1358
- input: () => Promise.resolve(join(tempDir, "docs")),
1359
- search: () => Promise.resolve(""),
1360
- };
1361
-
1362
- const options = { prompts: mockPrompts };
1363
-
1364
- const result = await init(
1365
- { outputPath: tempDir, fileName: "config.yaml", skipIfExists: false },
1366
- options,
1367
- );
1368
-
1369
- expect(result).toBeDefined();
1370
- expect(audienceValidateCalled).toBe(true);
1371
- } finally {
1372
- await cleanupTempDir(tempDir);
1373
- }
1374
- });
1375
-
1376
- test("should handle mixed purpose priority validation errors", async () => {
1377
- const tempDir = await createTempDir();
1378
-
1379
- try {
1380
- let priorityValidateCalled = false;
1381
- const mockPrompts = {
1382
- checkbox: (options) => {
1383
- if (options.message.includes("[1/9]")) {
1384
- return Promise.resolve(["mixedPurpose"]); // Trigger follow-up question
1385
- }
1386
- // This is the follow-up priority selection
1387
- if (options.message.includes("Which is most important?") && options.validate) {
1388
- // Test validation for empty selection
1389
- let validationResult = options.validate([]);
1390
- expect(validationResult).toBe("Please choose at least one priority.");
1391
-
1392
- // Test validation for too many selections
1393
- validationResult = options.validate(["getStarted", "completeTasks", "findAnswers"]);
1394
- expect(validationResult).toBe("Please choose maximum 2 priorities.");
1395
-
1396
- // Test validation for valid selection
1397
- validationResult = options.validate(["getStarted", "completeTasks"]);
1398
- expect(validationResult).toBe(true);
1399
-
1400
- priorityValidateCalled = true;
1401
- return Promise.resolve(["getStarted", "completeTasks"]); // Valid selection
1402
- }
1403
- return Promise.resolve(["getStarted", "completeTasks"]);
1404
- },
1405
- select: () => Promise.resolve("en"),
1406
- input: () => Promise.resolve(join(tempDir, "docs")),
1407
- search: () => Promise.resolve(""),
1408
- };
1409
-
1410
- const options = { prompts: mockPrompts };
1411
-
1412
- const result = await init(
1413
- { outputPath: tempDir, fileName: "config.yaml", skipIfExists: false },
1414
- options,
1415
- );
1416
-
1417
- expect(result).toBeDefined();
1418
- expect(priorityValidateCalled).toBe(true);
1419
- } finally {
1420
- await cleanupTempDir(tempDir);
1421
- }
1422
- });
1423
-
1424
- test("should handle file write errors", async () => {
1425
- const invalidPath = "/invalid/path/that/does/not/exist";
1426
-
1427
- const mockPrompts = {
1428
- checkbox: () => Promise.resolve(["getStarted"]),
1429
- select: () => Promise.resolve("en"),
1430
- input: () => Promise.resolve("docs"),
1431
- search: () => Promise.resolve(""),
1432
- };
1433
-
1434
- const options = { prompts: mockPrompts };
1435
-
1436
- const result = await init(
1437
- { outputPath: invalidPath, fileName: "config.yaml", skipIfExists: false },
1438
- options,
1439
- );
1440
-
1441
- expect(result).toHaveProperty("inputGeneratorStatus", false);
1442
- expect(result).toHaveProperty("inputGeneratorError");
1443
- expect(typeof result.inputGeneratorError).toBe("string");
1444
- });
1445
- });
1446
-
1447
- describe("checkOnly parameter", () => {
1448
- test("should exit silently when checkOnly is true and config exists", async () => {
1449
- const tempDir = await createTempDir();
1450
-
1451
- try {
1452
- // Create existing config file
1453
- const configPath = join(tempDir, "config.yaml");
1454
- const existingConfig = `projectName: "Test Project"
1455
- locale: "en"
1456
- documentPurpose:
1457
- - getStarted
1458
- targetAudienceTypes:
1459
- - developers`;
1460
- await fs.writeFile(configPath, existingConfig, "utf8");
1461
-
1462
- // The function should continue normally when config exists
1463
- // No mocking needed as it doesn't log or exit in this case
1464
- const result = await init(
1465
- {
1466
- outputPath: tempDir,
1467
- fileName: "config.yaml",
1468
- checkOnly: true,
1469
- },
1470
- { prompts: {} }, // Options not needed for checkOnly
1471
- );
1472
-
1473
- // Should return loaded config
1474
- expect(result).toBeDefined();
1475
- expect(result.projectName).toBe("Test Project");
1476
- expect(result.locale).toBe("en");
1477
- } finally {
1478
- await cleanupTempDir(tempDir);
1479
- }
1480
- });
1481
-
1482
- test("should show reminder and exit when checkOnly is true and config doesn't exist", async () => {
1483
- const tempDir = await createTempDir();
1484
-
1485
- try {
1486
- // Mock console.log and process.exit
1487
- const originalLog = console.log;
1488
- const originalExit = process.exit;
1489
- const logMessages = [];
1490
- let exitCalled = false;
1491
- let exitCode = null;
1492
-
1493
- console.log = (...args) => logMessages.push(args.join(" "));
1494
- process.exit = (code) => {
1495
- exitCalled = true;
1496
- exitCode = code;
1497
- throw new Error("process.exit called"); // Prevent actual exit
1498
- };
1499
-
1500
- try {
1501
- await expect(
1502
- init(
1503
- {
1504
- outputPath: tempDir,
1505
- fileName: "config.yaml",
1506
- checkOnly: true,
1507
- },
1508
- { prompts: {} },
1509
- ),
1510
- ).rejects.toThrow("process.exit called");
1511
-
1512
- // Should log reminder messages
1513
- expect(logMessages.some((msg) => msg.includes("No configuration found"))).toBe(true);
1514
- expect(logMessages.some((msg) => msg.includes("aigne doc init"))).toBe(true);
1515
-
1516
- // Should call process.exit(0)
1517
- expect(exitCalled).toBe(true);
1518
- expect(exitCode).toBe(0);
1519
- } finally {
1520
- console.log = originalLog;
1521
- process.exit = originalExit;
1522
- }
1523
- } finally {
1524
- await cleanupTempDir(tempDir);
1525
- }
1526
- });
1527
-
1528
- test("should not interfere with normal workflow when checkOnly is false", async () => {
1529
- const tempDir = await createTempDir();
1530
-
1531
- try {
1532
- const mockPrompts = createMockPrompts({
1533
- checkbox_1: ["getStarted"],
1534
- checkbox_2: ["developers"],
1535
- select_3: "domainFamiliar",
1536
- select_4: "balancedCoverage",
1537
- select_5: "en",
1538
- checkbox_6: [],
1539
- input_7: join(tempDir, "docs"),
1540
- search: "",
1541
- });
1542
-
1543
- const options = { prompts: mockPrompts };
1544
-
1545
- const result = await init(
1546
- {
1547
- outputPath: tempDir,
1548
- fileName: "config.yaml",
1549
- checkOnly: false, // Explicit false
1550
- },
1551
- options,
1552
- );
1553
-
1554
- expect(result).toBeDefined();
1555
-
1556
- // Check that config file was created normally
1557
- const configPath = join(tempDir, "config.yaml");
1558
- const configExists = await fs
1559
- .access(configPath)
1560
- .then(() => true)
1561
- .catch(() => false);
1562
- expect(configExists).toBe(true);
1563
- } finally {
1564
- await cleanupTempDir(tempDir);
1565
- }
1566
- });
1567
- });
1568
-
1569
- describe("Advanced source path scenarios", () => {
1570
- test("should handle source path validation and duplicate detection", async () => {
1571
- const tempDir = await createTempDir();
1572
-
1573
- try {
1574
- let searchCallCount = 0;
1575
- const responses = [
1576
- "invalid/path/that/does/not/exist", // Should trigger validation error
1577
- "invalid/path/that/does/not/exist", // Duplicate path
1578
- join(tempDir, "valid-path"), // Valid path after creating it
1579
- join(tempDir, "valid-path"), // Duplicate of valid path
1580
- "**/*.glob", // Glob pattern (should be accepted)
1581
- "**/*.glob", // Duplicate glob pattern
1582
- "", // End input
1583
- ];
1584
-
1585
- // Create a valid directory for testing
1586
- await fs.mkdir(join(tempDir, "valid-path"), { recursive: true });
1587
-
1588
- const mockPrompts = {
1589
- checkbox: () => Promise.resolve(["getStarted"]),
1590
- select: () => Promise.resolve("en"),
1591
- input: () => Promise.resolve(join(tempDir, "docs")),
1592
- search: () => {
1593
- const response = responses[searchCallCount];
1594
- searchCallCount++;
1595
- return Promise.resolve(response);
1596
- },
1597
- };
1598
-
1599
- const options = { prompts: mockPrompts };
1600
-
1601
- const result = await init(
1602
- { outputPath: tempDir, fileName: "config.yaml", skipIfExists: false },
1603
- options,
1604
- );
1605
-
1606
- expect(result).toBeDefined();
1607
-
1608
- const configPath = join(tempDir, "config.yaml");
1609
- const configContent = await fs.readFile(configPath, "utf8");
1610
- const config = parseYAML(configContent);
1611
-
1612
- // Should contain the valid paths that were successfully added
1613
- expect(config.sourcesPath.length).toBeGreaterThan(0);
1614
- } finally {
1615
- await cleanupTempDir(tempDir);
1616
- }
1617
- });
1618
-
1619
- test("should handle search source function callback", async () => {
1620
- const tempDir = await createTempDir();
1621
-
1622
- try {
1623
- const mockPrompts = {
1624
- checkbox: () => Promise.resolve(["getStarted"]),
1625
- select: () => Promise.resolve("en"),
1626
- input: () => Promise.resolve(join(tempDir, "docs")),
1627
- search: (options) => {
1628
- // Test the source callback function
1629
- if (options?.source) {
1630
- // Call the source function with different inputs to test the logic
1631
- const sourceResults1 = options.source("");
1632
- const sourceResults2 = options.source("src");
1633
- const sourceResults3 = options.source("**/*.js");
1634
-
1635
- // Verify these are promises
1636
- expect(sourceResults1).toBeInstanceOf(Promise);
1637
- expect(sourceResults2).toBeInstanceOf(Promise);
1638
- expect(sourceResults3).toBeInstanceOf(Promise);
1639
- }
1640
- return Promise.resolve(""); // End input
1641
- },
1642
- };
1643
-
1644
- const options = { prompts: mockPrompts };
1645
-
1646
- const result = await init(
1647
- { outputPath: tempDir, fileName: "config.yaml", skipIfExists: false },
1648
- options,
1649
- );
1650
-
1651
- expect(result).toBeDefined();
1652
- } finally {
1653
- await cleanupTempDir(tempDir);
1654
- }
1655
- });
1656
- });
1657
- });