@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,650 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
- import * as fs from "node:fs/promises";
3
- import * as path from "node:path";
4
- import {
5
- addFeedbackToItems,
6
- fileNameToFlatPath,
7
- findItemByFlatName,
8
- findItemByPath,
9
- generateFileName,
10
- getActionText,
11
- getMainLanguageFiles,
12
- processSelectedFiles,
13
- readFileContent,
14
- } from "../../utils/docs-finder-utils.mjs";
15
-
16
- describe("docs-finder-utils", () => {
17
- let readdirSpy;
18
- let readFileSpy;
19
- let accessSpy;
20
- let joinSpy;
21
- let consoleWarnSpy;
22
-
23
- beforeEach(() => {
24
- // Mock file system operations
25
- readdirSpy = spyOn(fs, "readdir").mockResolvedValue([]);
26
- readFileSpy = spyOn(fs, "readFile").mockResolvedValue("test content");
27
- accessSpy = spyOn(fs, "access").mockResolvedValue(undefined);
28
- joinSpy = spyOn(path, "join").mockImplementation((...paths) => paths.join("/"));
29
-
30
- // Mock console methods
31
- consoleWarnSpy = spyOn(console, "warn").mockImplementation(() => {});
32
- });
33
-
34
- afterEach(() => {
35
- readdirSpy?.mockRestore();
36
- readFileSpy?.mockRestore();
37
- accessSpy?.mockRestore();
38
- joinSpy?.mockRestore();
39
- consoleWarnSpy?.mockRestore();
40
- });
41
-
42
- // UTILITY FUNCTIONS TESTS
43
- describe("getActionText", () => {
44
- test("should return 'update' action for non-translate", () => {
45
- const result = getActionText(false, "Please {action} the docs");
46
- expect(result).toBe("Please update the docs");
47
- });
48
-
49
- test("should return 'translate' action for translate", () => {
50
- const result = getActionText(true, "Please {action} the docs");
51
- expect(result).toBe("Please translate the docs");
52
- });
53
-
54
- test("should handle multiple action placeholders", () => {
55
- const result = getActionText(false, "{action} docs, then {action} more");
56
- expect(result).toBe("update docs, then {action} more");
57
- });
58
-
59
- test("should handle text without action placeholder", () => {
60
- const result = getActionText(true, "No placeholders here");
61
- expect(result).toBe("No placeholders here");
62
- });
63
-
64
- test("should handle empty text", () => {
65
- const result = getActionText(false, "");
66
- expect(result).toBe("");
67
- });
68
- });
69
-
70
- describe("fileNameToFlatPath", () => {
71
- test("should remove .md extension", () => {
72
- const result = fileNameToFlatPath("overview.md");
73
- expect(result).toBe("overview");
74
- });
75
-
76
- test("should remove language suffix and .md extension", () => {
77
- const result = fileNameToFlatPath("overview.zh.md");
78
- expect(result).toBe("overview");
79
- });
80
-
81
- test("should remove complex language suffix", () => {
82
- const result = fileNameToFlatPath("overview.zh-CN.md");
83
- expect(result).toBe("overview");
84
- });
85
-
86
- test("should handle files without language suffix", () => {
87
- const result = fileNameToFlatPath("getting-started.md");
88
- expect(result).toBe("getting-started");
89
- });
90
-
91
- test("should handle multiple dots in filename", () => {
92
- const result = fileNameToFlatPath("api.v1.guide.fr.md");
93
- expect(result).toBe("api.v1.guide");
94
- });
95
-
96
- test("should handle files without .md extension", () => {
97
- const result = fileNameToFlatPath("overview");
98
- expect(result).toBe("overview");
99
- });
100
- });
101
-
102
- describe("findItemByFlatName", () => {
103
- const documentStructure = [
104
- { path: "/getting-started", title: "Getting Started" },
105
- { path: "/api/overview", title: "API Overview" },
106
- { path: "/guides/advanced", title: "Advanced Guide" },
107
- ];
108
-
109
- test("should find item by exact flat name match", () => {
110
- const result = findItemByFlatName(documentStructure, "getting-started");
111
- expect(result).toEqual({ path: "/getting-started", title: "Getting Started" });
112
- });
113
-
114
- test("should find item with nested path", () => {
115
- const result = findItemByFlatName(documentStructure, "api-overview");
116
- expect(result).toEqual({ path: "/api/overview", title: "API Overview" });
117
- });
118
-
119
- test("should find item with deep nested path", () => {
120
- const result = findItemByFlatName(documentStructure, "guides-advanced");
121
- expect(result).toEqual({ path: "/guides/advanced", title: "Advanced Guide" });
122
- });
123
-
124
- test("should return undefined for non-existent item", () => {
125
- const result = findItemByFlatName(documentStructure, "non-existent");
126
- expect(result).toBeUndefined();
127
- });
128
-
129
- test("should handle empty documentation structure", () => {
130
- const result = findItemByFlatName([], "any-name");
131
- expect(result).toBeUndefined();
132
- });
133
- });
134
-
135
- describe("addFeedbackToItems", () => {
136
- const items = [
137
- { path: "/guide1", title: "Guide 1" },
138
- { path: "/guide2", title: "Guide 2" },
139
- ];
140
-
141
- test("should add feedback to all items", () => {
142
- const result = addFeedbackToItems(items, "Please improve");
143
- expect(result).toEqual([
144
- { path: "/guide1", title: "Guide 1", feedback: "Please improve" },
145
- { path: "/guide2", title: "Guide 2", feedback: "Please improve" },
146
- ]);
147
- });
148
-
149
- test("should trim feedback text", () => {
150
- const result = addFeedbackToItems(items, " Please improve ");
151
- expect(result).toEqual([
152
- { path: "/guide1", title: "Guide 1", feedback: "Please improve" },
153
- { path: "/guide2", title: "Guide 2", feedback: "Please improve" },
154
- ]);
155
- });
156
-
157
- test("should return original items for empty feedback", () => {
158
- const result = addFeedbackToItems(items, "");
159
- expect(result).toEqual(items);
160
- });
161
-
162
- test("should return original items for whitespace-only feedback", () => {
163
- const result = addFeedbackToItems(items, " ");
164
- expect(result).toEqual(items);
165
- });
166
-
167
- test("should return original items for null feedback", () => {
168
- const result = addFeedbackToItems(items, null);
169
- expect(result).toEqual(items);
170
- });
171
-
172
- test("should return original items for undefined feedback", () => {
173
- const result = addFeedbackToItems(items, undefined);
174
- expect(result).toEqual(items);
175
- });
176
-
177
- test("should handle empty items array", () => {
178
- const result = addFeedbackToItems([], "feedback");
179
- expect(result).toEqual([]);
180
- });
181
- });
182
-
183
- // FILE OPERATIONS TESTS
184
- describe("readFileContent", () => {
185
- test("should read file content successfully", async () => {
186
- readFileSpy.mockResolvedValue("file content");
187
-
188
- const result = await readFileContent("/docs", "test.md");
189
-
190
- expect(result).toBe("file content");
191
- expect(joinSpy).toHaveBeenCalledWith("/docs", "test.md");
192
- expect(readFileSpy).toHaveBeenCalledWith("/docs/test.md", "utf-8");
193
- });
194
-
195
- test("should return null and warn on read error", async () => {
196
- readFileSpy.mockRejectedValue(new Error("File not found"));
197
-
198
- const result = await readFileContent("/docs", "missing.md");
199
-
200
- expect(result).toBeNull();
201
- expect(consoleWarnSpy).toHaveBeenCalledWith(
202
- "⚠️ Could not read content from missing.md:",
203
- "File not found",
204
- );
205
- });
206
-
207
- test("should handle different file paths", async () => {
208
- readFileSpy.mockResolvedValue("content");
209
-
210
- await readFileContent("/path/to/docs", "nested/file.md");
211
-
212
- expect(joinSpy).toHaveBeenCalledWith("/path/to/docs", "nested/file.md");
213
- });
214
- });
215
-
216
- describe("getMainLanguageFiles", () => {
217
- test("should filter English files correctly", async () => {
218
- readdirSpy.mockResolvedValue([
219
- "overview.md",
220
- "overview.zh.md",
221
- "guide.md",
222
- "guide.fr.md",
223
- "_sidebar.md",
224
- "README.txt",
225
- ]);
226
-
227
- const result = await getMainLanguageFiles("/docs", "en");
228
-
229
- expect(result).toEqual(["guide.md", "overview.md"]);
230
- });
231
-
232
- test("should filter non-English files correctly", async () => {
233
- readdirSpy.mockResolvedValue([
234
- "overview.md",
235
- "overview.zh.md",
236
- "guide.md",
237
- "guide.zh.md",
238
- "intro.fr.md",
239
- "_sidebar.md",
240
- ]);
241
-
242
- const result = await getMainLanguageFiles("/docs", "zh");
243
-
244
- expect(result).toEqual(["guide.zh.md", "overview.zh.md"]);
245
- });
246
-
247
- test("should sort files by documentation structure order", async () => {
248
- readdirSpy.mockResolvedValue(["guide.md", "overview.md", "advanced.md"]);
249
-
250
- const documentStructure = [{ path: "/overview" }, { path: "/guide" }, { path: "/advanced" }];
251
-
252
- const result = await getMainLanguageFiles("/docs", "en", documentStructure);
253
-
254
- expect(result).toEqual(["overview.md", "guide.md", "advanced.md"]);
255
- });
256
-
257
- test("should handle files not in documentation structure", async () => {
258
- readdirSpy.mockResolvedValue(["guide.md", "extra.md", "overview.md"]);
259
-
260
- const documentStructure = [{ path: "/overview" }, { path: "/guide" }];
261
-
262
- const result = await getMainLanguageFiles("/docs", "en", documentStructure);
263
-
264
- expect(result).toEqual(["overview.md", "guide.md", "extra.md"]);
265
- });
266
-
267
- test("should handle complex language suffixes", async () => {
268
- readdirSpy.mockResolvedValue(["overview.zh-CN.md", "guide.zh-TW.md", "intro.zh.md"]);
269
-
270
- const result = await getMainLanguageFiles("/docs", "zh");
271
-
272
- expect(result).toEqual(["intro.zh.md"]);
273
- });
274
-
275
- test("should exclude _sidebar.md files", async () => {
276
- readdirSpy.mockResolvedValue(["_sidebar.md", "_sidebar.zh.md", "overview.md"]);
277
-
278
- const result = await getMainLanguageFiles("/docs", "en");
279
-
280
- expect(result).toEqual(["overview.md"]);
281
- });
282
- });
283
-
284
- // COMPLEX ITEM FINDING TESTS
285
- describe("findItemByPath", () => {
286
- const documentStructure = [
287
- { path: "/getting-started", title: "Getting Started", description: "Intro" },
288
- { path: "/api/overview", title: "API Overview", category: "API" },
289
- { path: "/guides/advanced", title: "Advanced Guide", tags: ["advanced"] },
290
- ];
291
-
292
- test("should find item by direct path match", async () => {
293
- const result = await findItemByPath(documentStructure, "/getting-started", null, null);
294
-
295
- expect(result).toEqual({
296
- path: "/getting-started",
297
- title: "Getting Started",
298
- description: "Intro",
299
- });
300
- });
301
-
302
- test("should find item by .md filename", async () => {
303
- readFileSpy.mockResolvedValue("file content");
304
-
305
- const result = await findItemByPath(
306
- documentStructure,
307
- "api-overview.md",
308
- null,
309
- "/docs",
310
- "en",
311
- );
312
-
313
- expect(result).toEqual({
314
- path: "/api/overview",
315
- title: "API Overview",
316
- category: "API",
317
- content: "file content",
318
- });
319
- expect(readFileSpy).toHaveBeenCalledWith("/docs/api-overview.md", "utf-8");
320
- });
321
-
322
- test("should find item by boardId-flattened format", async () => {
323
- const result = await findItemByPath(
324
- documentStructure,
325
- "board123-guides-advanced",
326
- "board123",
327
- null,
328
- );
329
-
330
- expect(result).toEqual({
331
- path: "/guides/advanced",
332
- title: "Advanced Guide",
333
- tags: ["advanced"],
334
- });
335
- });
336
-
337
- test("should handle non-English locale for filename generation", async () => {
338
- readFileSpy.mockResolvedValue("Chinese content");
339
-
340
- const result = await findItemByPath(
341
- documentStructure,
342
- "/getting-started",
343
- null,
344
- "/docs",
345
- "zh",
346
- );
347
-
348
- expect(result).toEqual({
349
- path: "/getting-started",
350
- title: "Getting Started",
351
- description: "Intro",
352
- content: "Chinese content",
353
- });
354
- expect(readFileSpy).toHaveBeenCalledWith("/docs/getting-started.zh.md", "utf-8");
355
- });
356
-
357
- test("should return null for non-existent path", async () => {
358
- const result = await findItemByPath(documentStructure, "/non-existent", null, null);
359
- expect(result).toBeNull();
360
- });
361
-
362
- test("should return null for invalid boardId format", async () => {
363
- const result = await findItemByPath(documentStructure, "invalid-format", "board123", null);
364
- expect(result).toBeNull();
365
- });
366
-
367
- test("should handle file read errors gracefully", async () => {
368
- readFileSpy.mockRejectedValue(new Error("File not found"));
369
-
370
- const result = await findItemByPath(documentStructure, "/getting-started", null, "/docs");
371
-
372
- expect(result).toEqual({
373
- path: "/getting-started",
374
- title: "Getting Started",
375
- description: "Intro",
376
- });
377
- expect(consoleWarnSpy).toHaveBeenCalled();
378
- });
379
-
380
- test("should not read content when docsDir is not provided", async () => {
381
- const result = await findItemByPath(documentStructure, "/getting-started", null, null);
382
-
383
- expect(result).toEqual({
384
- path: "/getting-started",
385
- title: "Getting Started",
386
- description: "Intro",
387
- });
388
- expect(readFileSpy).not.toHaveBeenCalled();
389
- });
390
-
391
- test("should handle .md filename with language suffix", async () => {
392
- readFileSpy.mockResolvedValue("localized content");
393
-
394
- const result = await findItemByPath(
395
- documentStructure,
396
- "getting-started.zh.md",
397
- null,
398
- "/docs",
399
- );
400
-
401
- expect(result).toEqual({
402
- path: "/getting-started",
403
- title: "Getting Started",
404
- description: "Intro",
405
- content: "localized content",
406
- });
407
- });
408
-
409
- test("should handle boardId with special characters", async () => {
410
- const specialStructure = [{ path: "/test-guide", title: "Test Guide" }];
411
-
412
- const result = await findItemByPath(
413
- specialStructure,
414
- "special-board_123-test-guide",
415
- "special-board_123",
416
- null,
417
- );
418
-
419
- expect(result).toEqual({
420
- path: "/test-guide",
421
- title: "Test Guide",
422
- });
423
- });
424
- });
425
-
426
- describe("processSelectedFiles", () => {
427
- const documentStructure = [
428
- { path: "/overview", title: "Overview" },
429
- { path: "/api/guide", title: "API Guide" },
430
- { path: "/advanced", title: "Advanced" },
431
- ];
432
-
433
- test("should process selected files successfully", async () => {
434
- readFileSpy
435
- .mockResolvedValueOnce("overview content")
436
- .mockResolvedValueOnce("api guide content");
437
-
438
- const selectedFiles = ["overview.md", "api-guide.md"];
439
- const result = await processSelectedFiles(selectedFiles, documentStructure, "/docs");
440
-
441
- expect(result).toEqual([
442
- {
443
- path: "/overview",
444
- title: "Overview",
445
- content: "overview content",
446
- },
447
- {
448
- path: "/api/guide",
449
- title: "API Guide",
450
- content: "api guide content",
451
- },
452
- ]);
453
- });
454
-
455
- test("should handle files with language suffixes", async () => {
456
- readFileSpy.mockResolvedValue("localized content");
457
-
458
- const selectedFiles = ["overview.zh.md"];
459
- const result = await processSelectedFiles(selectedFiles, documentStructure, "/docs");
460
-
461
- expect(result).toEqual([
462
- {
463
- path: "/overview",
464
- title: "Overview",
465
- content: "localized content",
466
- },
467
- ]);
468
- });
469
-
470
- test("should handle files without content", async () => {
471
- readFileSpy.mockResolvedValue(null);
472
-
473
- const selectedFiles = ["overview.md"];
474
- const result = await processSelectedFiles(selectedFiles, documentStructure, "/docs");
475
-
476
- expect(result).toEqual([
477
- {
478
- path: "/overview",
479
- title: "Overview",
480
- },
481
- ]);
482
- });
483
-
484
- test("should warn for files not in documentation structure", async () => {
485
- readFileSpy.mockResolvedValue("content");
486
-
487
- const selectedFiles = ["unknown.md"];
488
- const result = await processSelectedFiles(selectedFiles, documentStructure, "/docs");
489
-
490
- expect(result).toEqual([]);
491
- expect(consoleWarnSpy).toHaveBeenCalledWith(
492
- "⚠️ No documentation structure item found for file: unknown.md",
493
- );
494
- });
495
-
496
- test("should handle mixed valid and invalid files", async () => {
497
- readFileSpy
498
- .mockResolvedValueOnce("overview content")
499
- .mockResolvedValueOnce("unknown content");
500
-
501
- const selectedFiles = ["overview.md", "unknown.md"];
502
- const result = await processSelectedFiles(selectedFiles, documentStructure, "/docs");
503
-
504
- expect(result).toEqual([
505
- {
506
- path: "/overview",
507
- title: "Overview",
508
- content: "overview content",
509
- },
510
- ]);
511
- expect(consoleWarnSpy).toHaveBeenCalledWith(
512
- "⚠️ No documentation structure item found for file: unknown.md",
513
- );
514
- });
515
-
516
- test("should handle empty selected files array", async () => {
517
- const result = await processSelectedFiles([], documentStructure, "/docs");
518
- expect(result).toEqual([]);
519
- });
520
-
521
- test("should handle file read errors", async () => {
522
- readFileSpy.mockRejectedValue(new Error("Read error"));
523
-
524
- const selectedFiles = ["overview.md"];
525
- const result = await processSelectedFiles(selectedFiles, documentStructure, "/docs");
526
-
527
- expect(result).toEqual([
528
- {
529
- path: "/overview",
530
- title: "Overview",
531
- },
532
- ]);
533
- });
534
-
535
- test("should preserve item properties", async () => {
536
- const complexStructure = [
537
- {
538
- path: "/overview",
539
- title: "Overview",
540
- description: "Main overview",
541
- category: "intro",
542
- tags: ["getting-started"],
543
- order: 1,
544
- },
545
- ];
546
-
547
- readFileSpy.mockResolvedValue("content");
548
-
549
- const selectedFiles = ["overview.md"];
550
- const result = await processSelectedFiles(selectedFiles, complexStructure, "/docs");
551
-
552
- expect(result).toEqual([
553
- {
554
- path: "/overview",
555
- title: "Overview",
556
- description: "Main overview",
557
- category: "intro",
558
- tags: ["getting-started"],
559
- order: 1,
560
- content: "content",
561
- },
562
- ]);
563
- });
564
- });
565
-
566
- // EDGE CASES AND ERROR HANDLING
567
- describe("edge cases", () => {
568
- test("getActionText should handle case-sensitive action placeholder", () => {
569
- const result = getActionText(true, "Please {ACTION} or {action} the docs");
570
- expect(result).toBe("Please {ACTION} or translate the docs");
571
- });
572
-
573
- test("fileNameToFlatPath should handle edge case filenames", () => {
574
- expect(fileNameToFlatPath("")).toBe("");
575
- expect(fileNameToFlatPath("file.with.many.dots.md")).toBe("file.with.many"); // removes last .dots as language suffix
576
- expect(fileNameToFlatPath("file-without-extension")).toBe("file-without-extension");
577
- expect(fileNameToFlatPath(".hidden.md")).toBe(""); // .hidden is treated as language suffix and removed
578
- });
579
-
580
- test("findItemByFlatName should handle paths with leading/trailing slashes", () => {
581
- const documentStructure = [
582
- { path: "no-leading-slash", title: "No Leading" },
583
- { path: "/normal-path", title: "Normal" },
584
- { path: "/trailing-slash/", title: "Trailing" },
585
- ];
586
-
587
- const result1 = findItemByFlatName(documentStructure, "no-leading-slash");
588
- const result2 = findItemByFlatName(documentStructure, "normal-path");
589
- const result3 = findItemByFlatName(documentStructure, "trailing-slash");
590
-
591
- expect(result1).toEqual({ path: "no-leading-slash", title: "No Leading" });
592
- expect(result2).toEqual({ path: "/normal-path", title: "Normal" });
593
- expect(result3).toBeUndefined(); // trailing slash breaks the flattening
594
- });
595
-
596
- test("addFeedbackToItems should handle items with existing feedback", () => {
597
- const itemsWithFeedback = [{ path: "/guide", title: "Guide", feedback: "Old feedback" }];
598
-
599
- const result = addFeedbackToItems(itemsWithFeedback, "New feedback");
600
-
601
- expect(result).toEqual([{ path: "/guide", title: "Guide", feedback: "New feedback" }]);
602
- });
603
-
604
- test("getMainLanguageFiles should handle empty directory", async () => {
605
- readdirSpy.mockResolvedValue([]);
606
-
607
- const result = await getMainLanguageFiles("/empty", "en");
608
-
609
- expect(result).toEqual([]);
610
- });
611
-
612
- test("getMainLanguageFiles should handle readdir errors", async () => {
613
- accessSpy.mockResolvedValue(undefined); // Directory exists
614
- readdirSpy.mockRejectedValue(new Error("Permission denied"));
615
-
616
- await expect(getMainLanguageFiles("/denied", "en")).rejects.toThrow("Permission denied");
617
- });
618
-
619
- test("getMainLanguageFiles should throw non-ENOENT access errors", async () => {
620
- const permissionError = new Error("Permission denied");
621
- permissionError.code = "EACCES";
622
- accessSpy.mockRejectedValue(permissionError);
623
-
624
- await expect(getMainLanguageFiles("/denied", "en")).rejects.toThrow("Permission denied");
625
- });
626
-
627
- test("processSelectedFiles should handle empty documentation structure", async () => {
628
- readFileSpy.mockResolvedValue("content");
629
-
630
- const result = await processSelectedFiles(["test.md"], [], "/docs");
631
-
632
- expect(result).toEqual([]);
633
- expect(consoleWarnSpy).toHaveBeenCalledWith(
634
- "⚠️ No documentation structure item found for file: test.md",
635
- );
636
- });
637
-
638
- test("fileNameToFlatPath should handle files with multiple language suffixes", () => {
639
- expect(fileNameToFlatPath("file.zh-CN.md")).toBe("file");
640
- expect(fileNameToFlatPath("file.en-US.md")).toBe("file");
641
- expect(fileNameToFlatPath("file.fr-FR.md")).toBe("file");
642
- });
643
-
644
- test("generateFileName should handle special characters in flatName", () => {
645
- expect(generateFileName("api-v1-guide", "en")).toBe("api-v1-guide.md");
646
- expect(generateFileName("api-v1-guide", "zh")).toBe("api-v1-guide.zh.md");
647
- expect(generateFileName("test_special-chars", "fr")).toBe("test_special-chars.fr.md");
648
- });
649
- });
650
- });