@aigne/doc-smith 0.8.12-beta.8 → 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 (264) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/agents/publish/index.yaml +4 -0
  3. package/agents/publish/publish-docs.mjs +77 -5
  4. package/agents/publish/translate-meta.mjs +103 -0
  5. package/agents/update/generate-document.yaml +30 -28
  6. package/agents/update/update-document-detail.yaml +3 -1
  7. package/agents/utils/update-branding.mjs +69 -0
  8. package/package.json +16 -2
  9. package/prompts/common/document/role-and-personality.md +3 -1
  10. package/prompts/detail/d2-diagram/guide.md +7 -1
  11. package/prompts/detail/d2-diagram/user-prompt.md +3 -0
  12. package/prompts/detail/generate/system-prompt.md +6 -7
  13. package/prompts/detail/generate/user-prompt.md +12 -3
  14. package/prompts/detail/update/user-prompt.md +0 -2
  15. package/prompts/structure/update/user-prompt.md +0 -4
  16. package/utils/file-utils.mjs +69 -24
  17. package/utils/markdown-checker.mjs +0 -20
  18. package/utils/request.mjs +7 -0
  19. package/utils/upload-files.mjs +231 -0
  20. package/utils/utils.mjs +11 -1
  21. package/.aigne/doc-smith/config.yaml +0 -77
  22. package/.aigne/doc-smith/history.yaml +0 -37
  23. package/.aigne/doc-smith/media-description.yaml +0 -91
  24. package/.aigne/doc-smith/output/structure-plan.json +0 -162
  25. package/.aigne/doc-smith/preferences.yml +0 -97
  26. package/.aigne/doc-smith/upload-cache.yaml +0 -1830
  27. package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
  28. package/.github/workflows/ci.yml +0 -54
  29. package/.github/workflows/create-release-pr.yaml +0 -21
  30. package/.github/workflows/publish-docs.yml +0 -65
  31. package/.github/workflows/release.yml +0 -49
  32. package/.github/workflows/reviewer.yml +0 -54
  33. package/.release-please-manifest.json +0 -3
  34. package/RELEASE.md +0 -9
  35. package/assets/screenshots/doc-complete-setup.png +0 -0
  36. package/assets/screenshots/doc-generate-docs.png +0 -0
  37. package/assets/screenshots/doc-generate.png +0 -0
  38. package/assets/screenshots/doc-generated-successfully.png +0 -0
  39. package/assets/screenshots/doc-publish.png +0 -0
  40. package/assets/screenshots/doc-regenerate.png +0 -0
  41. package/assets/screenshots/doc-translate-langs.png +0 -0
  42. package/assets/screenshots/doc-translate.png +0 -0
  43. package/assets/screenshots/doc-update.png +0 -0
  44. package/biome.json +0 -73
  45. package/codecov.yml +0 -15
  46. package/docs/_sidebar.md +0 -15
  47. package/docs/configuration-initial-setup.ja.md +0 -179
  48. package/docs/configuration-initial-setup.md +0 -198
  49. package/docs/configuration-initial-setup.zh-TW.md +0 -179
  50. package/docs/configuration-initial-setup.zh.md +0 -179
  51. package/docs/configuration-managing-preferences.ja.md +0 -100
  52. package/docs/configuration-managing-preferences.md +0 -100
  53. package/docs/configuration-managing-preferences.zh-TW.md +0 -100
  54. package/docs/configuration-managing-preferences.zh.md +0 -100
  55. package/docs/configuration.ja.md +0 -69
  56. package/docs/configuration.md +0 -69
  57. package/docs/configuration.zh-TW.md +0 -69
  58. package/docs/configuration.zh.md +0 -69
  59. package/docs/getting-started.ja.md +0 -107
  60. package/docs/getting-started.md +0 -107
  61. package/docs/getting-started.zh-TW.md +0 -107
  62. package/docs/getting-started.zh.md +0 -107
  63. package/docs/guides-cleaning-up.ja.md +0 -51
  64. package/docs/guides-cleaning-up.md +0 -52
  65. package/docs/guides-cleaning-up.zh-TW.md +0 -51
  66. package/docs/guides-cleaning-up.zh.md +0 -51
  67. package/docs/guides-evaluating-documents.ja.md +0 -66
  68. package/docs/guides-evaluating-documents.md +0 -107
  69. package/docs/guides-evaluating-documents.zh-TW.md +0 -66
  70. package/docs/guides-evaluating-documents.zh.md +0 -66
  71. package/docs/guides-generating-documentation.ja.md +0 -151
  72. package/docs/guides-generating-documentation.md +0 -89
  73. package/docs/guides-generating-documentation.zh-TW.md +0 -151
  74. package/docs/guides-generating-documentation.zh.md +0 -151
  75. package/docs/guides-interactive-chat.ja.md +0 -85
  76. package/docs/guides-interactive-chat.md +0 -93
  77. package/docs/guides-interactive-chat.zh-TW.md +0 -85
  78. package/docs/guides-interactive-chat.zh.md +0 -85
  79. package/docs/guides-managing-history.ja.md +0 -48
  80. package/docs/guides-managing-history.md +0 -53
  81. package/docs/guides-managing-history.zh-TW.md +0 -48
  82. package/docs/guides-managing-history.zh.md +0 -48
  83. package/docs/guides-publishing-your-docs.ja.md +0 -78
  84. package/docs/guides-publishing-your-docs.md +0 -83
  85. package/docs/guides-publishing-your-docs.zh-TW.md +0 -78
  86. package/docs/guides-publishing-your-docs.zh.md +0 -78
  87. package/docs/guides-translating-documentation.ja.md +0 -95
  88. package/docs/guides-translating-documentation.md +0 -100
  89. package/docs/guides-translating-documentation.zh-TW.md +0 -95
  90. package/docs/guides-translating-documentation.zh.md +0 -95
  91. package/docs/guides-updating-documentation.ja.md +0 -77
  92. package/docs/guides-updating-documentation.md +0 -79
  93. package/docs/guides-updating-documentation.zh-TW.md +0 -77
  94. package/docs/guides-updating-documentation.zh.md +0 -77
  95. package/docs/guides.ja.md +0 -32
  96. package/docs/guides.md +0 -32
  97. package/docs/guides.zh-TW.md +0 -32
  98. package/docs/guides.zh.md +0 -32
  99. package/docs/overview.ja.md +0 -61
  100. package/docs/overview.md +0 -61
  101. package/docs/overview.zh-TW.md +0 -61
  102. package/docs/overview.zh.md +0 -61
  103. package/docs/release-notes.ja.md +0 -255
  104. package/docs/release-notes.md +0 -288
  105. package/docs/release-notes.zh-TW.md +0 -255
  106. package/docs/release-notes.zh.md +0 -255
  107. package/prompts/common/afs/afs-tools-usage.md +0 -5
  108. package/prompts/common/afs/use-afs-instruction.md +0 -1
  109. package/release-please-config.json +0 -14
  110. package/tests/agents/chat/chat.test.mjs +0 -46
  111. package/tests/agents/clear/choose-contents.test.mjs +0 -284
  112. package/tests/agents/clear/clear-auth-tokens.test.mjs +0 -268
  113. package/tests/agents/clear/clear-document-config.test.mjs +0 -167
  114. package/tests/agents/clear/clear-document-structure.test.mjs +0 -380
  115. package/tests/agents/clear/clear-generated-docs.test.mjs +0 -222
  116. package/tests/agents/evaluate/code-snippet.test.mjs +0 -163
  117. package/tests/agents/evaluate/fixtures/api-services.md +0 -87
  118. package/tests/agents/evaluate/fixtures/js-sdk.md +0 -94
  119. package/tests/agents/evaluate/generate-report.test.mjs +0 -312
  120. package/tests/agents/generate/check-document-structure.test.mjs +0 -45
  121. package/tests/agents/generate/check-need-generate-structure.test.mjs +0 -279
  122. package/tests/agents/generate/document-structure-tools/add-document.test.mjs +0 -449
  123. package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +0 -410
  124. package/tests/agents/generate/document-structure-tools/generate-sub-structure.test.mjs +0 -277
  125. package/tests/agents/generate/document-structure-tools/move-document.test.mjs +0 -476
  126. package/tests/agents/generate/document-structure-tools/update-document.test.mjs +0 -548
  127. package/tests/agents/generate/generate-structure.test.mjs +0 -45
  128. package/tests/agents/generate/user-review-document-structure.test.mjs +0 -319
  129. package/tests/agents/history/view.test.mjs +0 -97
  130. package/tests/agents/init/init.test.mjs +0 -1657
  131. package/tests/agents/prefs/prefs.test.mjs +0 -431
  132. package/tests/agents/publish/publish-docs.test.mjs +0 -787
  133. package/tests/agents/translate/choose-language.test.mjs +0 -311
  134. package/tests/agents/translate/translate-document.test.mjs +0 -51
  135. package/tests/agents/update/check-document.test.mjs +0 -463
  136. package/tests/agents/update/check-update-is-single.test.mjs +0 -300
  137. package/tests/agents/update/document-tools/update-document-content.test.mjs +0 -329
  138. package/tests/agents/update/generate-document.test.mjs +0 -51
  139. package/tests/agents/update/save-and-translate-document.test.mjs +0 -369
  140. package/tests/agents/update/user-review-document.test.mjs +0 -582
  141. package/tests/agents/utils/action-success.test.mjs +0 -54
  142. package/tests/agents/utils/check-detail-result.test.mjs +0 -743
  143. package/tests/agents/utils/check-feedback-refiner.test.mjs +0 -478
  144. package/tests/agents/utils/choose-docs.test.mjs +0 -406
  145. package/tests/agents/utils/exit.test.mjs +0 -70
  146. package/tests/agents/utils/feedback-refiner.test.mjs +0 -51
  147. package/tests/agents/utils/find-item-by-path.test.mjs +0 -517
  148. package/tests/agents/utils/find-user-preferences-by-path.test.mjs +0 -382
  149. package/tests/agents/utils/format-document-structure.test.mjs +0 -364
  150. package/tests/agents/utils/fs.test.mjs +0 -267
  151. package/tests/agents/utils/load-sources.test.mjs +0 -1470
  152. package/tests/agents/utils/save-docs.test.mjs +0 -109
  153. package/tests/agents/utils/save-output.test.mjs +0 -315
  154. package/tests/agents/utils/save-single-doc.test.mjs +0 -364
  155. package/tests/agents/utils/transform-detail-datasources.test.mjs +0 -320
  156. package/tests/utils/auth-utils.test.mjs +0 -596
  157. package/tests/utils/blocklet.test.mjs +0 -336
  158. package/tests/utils/conflict-detector.test.mjs +0 -355
  159. package/tests/utils/constants.test.mjs +0 -295
  160. package/tests/utils/d2-utils.test.mjs +0 -437
  161. package/tests/utils/deploy.test.mjs +0 -399
  162. package/tests/utils/docs-finder-utils.test.mjs +0 -650
  163. package/tests/utils/file-utils.test.mjs +0 -521
  164. package/tests/utils/history-utils.test.mjs +0 -206
  165. package/tests/utils/kroki-utils.test.mjs +0 -646
  166. package/tests/utils/linter/fixtures/css/keyword-error.css +0 -1
  167. package/tests/utils/linter/fixtures/css/missing-semicolon.css +0 -1
  168. package/tests/utils/linter/fixtures/css/syntax-error.css +0 -1
  169. package/tests/utils/linter/fixtures/css/undeclare-variable.css +0 -1
  170. package/tests/utils/linter/fixtures/css/unused-variable.css +0 -2
  171. package/tests/utils/linter/fixtures/css/valid-code.css +0 -1
  172. package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +0 -1
  173. package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +0 -2
  174. package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +0 -2
  175. package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +0 -1
  176. package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +0 -1
  177. package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +0 -2
  178. package/tests/utils/linter/fixtures/go/keyword-error.go +0 -5
  179. package/tests/utils/linter/fixtures/go/missing-semicolon.go +0 -5
  180. package/tests/utils/linter/fixtures/go/syntax-error.go +0 -6
  181. package/tests/utils/linter/fixtures/go/undeclare-variable.go +0 -5
  182. package/tests/utils/linter/fixtures/go/unused-variable.go +0 -5
  183. package/tests/utils/linter/fixtures/go/valid-code.go +0 -7
  184. package/tests/utils/linter/fixtures/js/keyword-error.js +0 -3
  185. package/tests/utils/linter/fixtures/js/missing-semicolon.js +0 -6
  186. package/tests/utils/linter/fixtures/js/syntax-error.js +0 -4
  187. package/tests/utils/linter/fixtures/js/undeclare-variable.js +0 -3
  188. package/tests/utils/linter/fixtures/js/unused-variable.js +0 -7
  189. package/tests/utils/linter/fixtures/js/valid-code.js +0 -15
  190. package/tests/utils/linter/fixtures/json/keyword-error.json +0 -1
  191. package/tests/utils/linter/fixtures/json/missing-semicolon.json +0 -1
  192. package/tests/utils/linter/fixtures/json/syntax-error.json +0 -1
  193. package/tests/utils/linter/fixtures/json/undeclare-variable.json +0 -1
  194. package/tests/utils/linter/fixtures/json/unused-variable.json +0 -1
  195. package/tests/utils/linter/fixtures/json/valid-code.json +0 -1
  196. package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +0 -5
  197. package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +0 -5
  198. package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +0 -5
  199. package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +0 -5
  200. package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +0 -4
  201. package/tests/utils/linter/fixtures/jsx/valid-code.jsx +0 -5
  202. package/tests/utils/linter/fixtures/python/keyword-error.py +0 -3
  203. package/tests/utils/linter/fixtures/python/missing-semicolon.py +0 -2
  204. package/tests/utils/linter/fixtures/python/syntax-error.py +0 -3
  205. package/tests/utils/linter/fixtures/python/undeclare-variable.py +0 -3
  206. package/tests/utils/linter/fixtures/python/unused-variable.py +0 -6
  207. package/tests/utils/linter/fixtures/python/valid-code.py +0 -12
  208. package/tests/utils/linter/fixtures/ruby/keyword-error.rb +0 -2
  209. package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +0 -1
  210. package/tests/utils/linter/fixtures/ruby/syntax-error.rb +0 -2
  211. package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +0 -1
  212. package/tests/utils/linter/fixtures/ruby/unused-variable.rb +0 -2
  213. package/tests/utils/linter/fixtures/ruby/valid-code.rb +0 -1
  214. package/tests/utils/linter/fixtures/sass/keyword-error.sass +0 -2
  215. package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +0 -3
  216. package/tests/utils/linter/fixtures/sass/syntax-error.sass +0 -3
  217. package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +0 -2
  218. package/tests/utils/linter/fixtures/sass/unused-variable.sass +0 -4
  219. package/tests/utils/linter/fixtures/sass/valid-code.sass +0 -2
  220. package/tests/utils/linter/fixtures/scss/keyword-error.scss +0 -1
  221. package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +0 -1
  222. package/tests/utils/linter/fixtures/scss/syntax-error.scss +0 -1
  223. package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +0 -1
  224. package/tests/utils/linter/fixtures/scss/unused-variable.scss +0 -2
  225. package/tests/utils/linter/fixtures/scss/valid-code.scss +0 -1
  226. package/tests/utils/linter/fixtures/shell/keyword-error.sh +0 -5
  227. package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +0 -3
  228. package/tests/utils/linter/fixtures/shell/syntax-error.sh +0 -4
  229. package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +0 -3
  230. package/tests/utils/linter/fixtures/shell/unused-variable.sh +0 -4
  231. package/tests/utils/linter/fixtures/shell/valid-code.sh +0 -3
  232. package/tests/utils/linter/fixtures/ts/keyword-error.ts +0 -1
  233. package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +0 -1
  234. package/tests/utils/linter/fixtures/ts/syntax-error.ts +0 -1
  235. package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +0 -1
  236. package/tests/utils/linter/fixtures/ts/unused-variable.ts +0 -3
  237. package/tests/utils/linter/fixtures/ts/valid-code.ts +0 -3
  238. package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +0 -5
  239. package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +0 -5
  240. package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +0 -5
  241. package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +0 -6
  242. package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +0 -6
  243. package/tests/utils/linter/fixtures/tsx/valid-code.tsx +0 -5
  244. package/tests/utils/linter/fixtures/vue/keyword-error.vue +0 -6
  245. package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +0 -6
  246. package/tests/utils/linter/fixtures/vue/syntax-error.vue +0 -6
  247. package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +0 -6
  248. package/tests/utils/linter/fixtures/vue/unused-variable.vue +0 -7
  249. package/tests/utils/linter/fixtures/vue/valid-code.vue +0 -6
  250. package/tests/utils/linter/fixtures/yaml/keyword-error.yml +0 -1
  251. package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +0 -2
  252. package/tests/utils/linter/fixtures/yaml/syntax-error.yml +0 -1
  253. package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +0 -1
  254. package/tests/utils/linter/fixtures/yaml/unused-variable.yml +0 -2
  255. package/tests/utils/linter/fixtures/yaml/valid-code.yml +0 -3
  256. package/tests/utils/linter/index.test.mjs +0 -440
  257. package/tests/utils/linter/scan-results.mjs +0 -42
  258. package/tests/utils/load-config.test.mjs +0 -141
  259. package/tests/utils/markdown/index.test.mjs +0 -478
  260. package/tests/utils/mermaid-validator.test.mjs +0 -541
  261. package/tests/utils/mock-chat-model.mjs +0 -12
  262. package/tests/utils/preferences-utils.test.mjs +0 -465
  263. package/tests/utils/save-value-to-config.test.mjs +0 -483
  264. package/tests/utils/utils.test.mjs +0 -941
@@ -1,1470 +0,0 @@
1
- import { afterAll, afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
- import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
- import path, { dirname } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import loadSources from "../../../agents/utils/load-sources.mjs";
6
-
7
- const __dirname = dirname(fileURLToPath(import.meta.url));
8
-
9
- describe("load-sources", () => {
10
- let testDir;
11
- let tempDir;
12
-
13
- beforeEach(async () => {
14
- // Create test directory structure with unique name to avoid conflicts
15
- const uniqueId = `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
16
- testDir = path.join(__dirname, `test-content-generator-${uniqueId}`);
17
- tempDir = path.join(testDir, "temp");
18
-
19
- await mkdir(testDir, { recursive: true });
20
- await mkdir(tempDir, { recursive: true });
21
- await mkdir(path.join(testDir, "src"), { recursive: true });
22
- await mkdir(path.join(testDir, "docs"), { recursive: true });
23
- await mkdir(path.join(testDir, "node_modules"), { recursive: true });
24
- await mkdir(path.join(testDir, "test"), { recursive: true });
25
-
26
- // Create multi-level directory structure under src
27
- await mkdir(path.join(testDir, "src/components"), { recursive: true });
28
- await mkdir(path.join(testDir, "src/components/ui"), { recursive: true });
29
- await mkdir(path.join(testDir, "src/utils/helpers"), { recursive: true });
30
- await mkdir(path.join(testDir, "src/services/api"), { recursive: true });
31
- await mkdir(path.join(testDir, "src/config"), { recursive: true });
32
-
33
- // Create test files in root and src
34
- await writeFile(path.join(testDir, "package.json"), JSON.stringify({ name: "test" }));
35
- await writeFile(path.join(testDir, "README.md"), "# Test Project");
36
- await writeFile(path.join(testDir, "src/index.js"), "console.log('hello');");
37
- await writeFile(path.join(testDir, "src/utils.js"), "export function test() {}");
38
-
39
- // Create files in multi-level directories
40
- await writeFile(path.join(testDir, "src/components/Button.js"), "export class Button {}");
41
- await writeFile(path.join(testDir, "src/components/ui/Modal.js"), "export class Modal {}");
42
- await writeFile(path.join(testDir, "src/components/ui/Input.js"), "export class Input {}");
43
- await writeFile(
44
- path.join(testDir, "src/utils/helpers/format.js"),
45
- "export function format() {}",
46
- );
47
- await writeFile(
48
- path.join(testDir, "src/utils/helpers/validate.js"),
49
- "export function validate() {}",
50
- );
51
- await writeFile(path.join(testDir, "src/services/api/client.js"), "export class ApiClient {}");
52
- await writeFile(
53
- path.join(testDir, "src/services/api/endpoints.js"),
54
- "export const endpoints = {}",
55
- );
56
- await writeFile(path.join(testDir, "src/config/database.js"), "export const dbConfig = {}");
57
- await writeFile(path.join(testDir, "src/config/app.js"), "export const appConfig = {}");
58
-
59
- // Create some non-JS files to test filtering
60
- await writeFile(path.join(testDir, "src/components/ui/styles.css"), "/* styles */");
61
- await writeFile(
62
- path.join(testDir, "src/config/settings.json"),
63
- JSON.stringify({ theme: "dark" }),
64
- );
65
- await writeFile(path.join(testDir, "src/utils/helpers/data.yaml"), "version: 1.0");
66
-
67
- // Create test files
68
- await writeFile(path.join(testDir, "test/test.js"), "describe('test', () => {});");
69
-
70
- // Create files with _test pattern to test the new exclusion
71
- await writeFile(path.join(testDir, "src/server_test.go"), "func TestServer() {}");
72
- await writeFile(path.join(testDir, "src/user_test.js"), "describe('user', () => {});");
73
- await writeFile(path.join(testDir, "src/api_test.ts"), "describe('api', () => {});");
74
- await writeFile(path.join(testDir, "src/utils_test.py"), "def test_utils(): pass");
75
- await writeFile(
76
- path.join(testDir, "src/components/Button_test.jsx"),
77
- "test('button', () => {});",
78
- );
79
- await writeFile(
80
- path.join(testDir, "src/utils/helpers/format_test.js"),
81
- "test('format', () => {});",
82
- );
83
-
84
- await mkdir(path.join(testDir, "node_modules/some-package"), {
85
- recursive: true,
86
- });
87
- await writeFile(path.join(testDir, "node_modules/some-package/package.json"), "{}");
88
- await writeFile(path.join(testDir, "temp/temp.txt"), "temp file");
89
- await writeFile(path.join(testDir, "ignore.txt"), "should be ignored");
90
-
91
- // Create .gitignore file
92
- await writeFile(
93
- path.join(testDir, ".gitignore"),
94
- "node_modules/\n" + "temp/\n" + "ignore.txt\n" + "*.log\n",
95
- );
96
- });
97
-
98
- afterEach(async () => {
99
- // Clean up test directory with comprehensive handling
100
- try {
101
- await rm(testDir, { recursive: true, force: true });
102
- } catch {
103
- // Try multiple cleanup strategies
104
- try {
105
- const { chmod, readdir, unlink } = await import("node:fs/promises");
106
-
107
- // Reset permissions recursively
108
- const resetPermissions = async (dir) => {
109
- try {
110
- await chmod(dir, 0o755);
111
- const entries = await readdir(dir, { withFileTypes: true });
112
- for (const entry of entries) {
113
- const fullPath = path.join(dir, entry.name);
114
- if (entry.isDirectory()) {
115
- await resetPermissions(fullPath);
116
- } else {
117
- try {
118
- await chmod(fullPath, 0o644);
119
- } catch {
120
- // Try to remove symbolic links directly
121
- try {
122
- await unlink(fullPath);
123
- } catch {
124
- // Ignore individual file cleanup failures
125
- }
126
- }
127
- }
128
- }
129
- } catch {
130
- // Ignore permission reset failures
131
- }
132
- };
133
-
134
- await resetPermissions(testDir);
135
- await rm(testDir, { recursive: true, force: true });
136
- } catch {
137
- // Final fallback - try to remove with system command
138
- try {
139
- const { exec } = await import("node:child_process");
140
- const { promisify } = await import("node:util");
141
- const execAsync = promisify(exec);
142
- await execAsync(`rm -rf "${testDir}"`);
143
- } catch {
144
- // Ignore final cleanup failures - test isolation is more important than perfect cleanup
145
- console.warn(`Warning: Could not fully clean up test directory: ${testDir}`);
146
- }
147
- }
148
- }
149
- });
150
-
151
- test("should load files with default patterns", async () => {
152
- const result = await loadSources({
153
- sourcesPath: testDir,
154
- useDefaultPatterns: true,
155
- outputDir: tempDir,
156
- docsDir: path.join(testDir, "docs"),
157
- });
158
-
159
- expect(result.files).toBeDefined();
160
- expect(result.files.length).toBeGreaterThan(0);
161
-
162
- // Should include package.json, README.md, src files
163
- const filePaths = result.files;
164
- expect(filePaths.some((element) => element.includes("package.json"))).toBe(true);
165
- expect(filePaths.some((element) => element.includes("README.md"))).toBe(true);
166
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
167
-
168
- // Should exclude node_modules, temp, test files
169
- expect(filePaths.some((element) => element.includes("node_modules"))).toBe(false);
170
- expect(filePaths.some((element) => element.includes("temp/"))).toBe(false);
171
- expect(filePaths.some((element) => element.includes("test/test.js"))).toBe(false);
172
- expect(filePaths.some((element) => element.includes("ignore.txt"))).toBe(false);
173
- });
174
-
175
- test("should load files with custom patterns", async () => {
176
- const result = await loadSources({
177
- sourcesPath: testDir,
178
- includePatterns: ["*.js", "*.json"],
179
- excludePatterns: ["test/*"],
180
- useDefaultPatterns: false,
181
- outputDir: tempDir,
182
- docsDir: path.join(testDir, "docs"),
183
- });
184
-
185
- expect(result.files).toBeDefined();
186
- expect(result.files.length).toBeGreaterThan(0);
187
-
188
- const filePaths = result.files;
189
- expect(filePaths.some((element) => element.includes("package.json"))).toBe(true);
190
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
191
- expect(filePaths.some((element) => element.includes("src/utils.js"))).toBe(true);
192
-
193
- // Should exclude test files
194
- expect(filePaths.some((element) => element.includes("test/test.js"))).toBe(false);
195
- });
196
-
197
- test("should respect .gitignore patterns", async () => {
198
- const result = await loadSources({
199
- sourcesPath: testDir,
200
- includePatterns: ["*"],
201
- excludePatterns: [],
202
- useDefaultPatterns: false,
203
- outputDir: tempDir,
204
- docsDir: path.join(testDir, "docs"),
205
- });
206
-
207
- expect(result.files).toBeDefined();
208
-
209
- const filePaths = result.files;
210
-
211
- // Should exclude files listed in .gitignore
212
- expect(filePaths.some((element) => element.includes("node_modules"))).toBe(false);
213
- expect(filePaths.some((element) => element.includes("temp/"))).toBe(false);
214
- expect(filePaths.some((element) => element.includes("ignore.txt"))).toBe(false);
215
- });
216
-
217
- test("should handle path-based patterns", async () => {
218
- const result = await loadSources({
219
- sourcesPath: testDir,
220
- includePatterns: ["src/**/*.js"],
221
- excludePatterns: ["**/test/**"],
222
- useDefaultPatterns: false,
223
- outputDir: tempDir,
224
- docsDir: path.join(testDir, "docs"),
225
- });
226
-
227
- expect(result.files).toBeDefined();
228
-
229
- const filePaths = result.files;
230
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
231
- expect(filePaths.some((element) => element.includes("src/utils.js"))).toBe(true);
232
-
233
- // Should exclude test files
234
- expect(filePaths.some((element) => element.includes("test/test.js"))).toBe(false);
235
- });
236
-
237
- test("should handle multiple source paths", async () => {
238
- const result = await loadSources({
239
- sourcesPath: [testDir, path.join(testDir, "src")],
240
- includePatterns: ["*.js"],
241
- useDefaultPatterns: false,
242
- outputDir: tempDir,
243
- docsDir: path.join(testDir, "docs"),
244
- });
245
-
246
- expect(result.files).toBeDefined();
247
- expect(result.files.length).toBeGreaterThan(0);
248
-
249
- const filePaths = result.files;
250
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
251
- expect(filePaths.some((element) => element.includes("src/utils.js"))).toBe(true);
252
- });
253
-
254
- test("should handle non-existent directories gracefully", async () => {
255
- const result = await loadSources({
256
- sourcesPath: path.join(testDir, "non-existent"),
257
- useDefaultPatterns: true,
258
- outputDir: tempDir,
259
- docsDir: path.join(testDir, "docs"),
260
- });
261
-
262
- expect(result.files).toBeDefined();
263
- expect(result.files.length).toBe(0);
264
- });
265
-
266
- test("should merge user patterns with default patterns", async () => {
267
- const result = await loadSources({
268
- sourcesPath: testDir,
269
- includePatterns: ["*.txt"],
270
- excludePatterns: ["docs/*"],
271
- useDefaultPatterns: true,
272
- outputDir: tempDir,
273
- docsDir: path.join(testDir, "docs"),
274
- });
275
-
276
- expect(result.files).toBeDefined();
277
-
278
- const filePaths = result.files;
279
-
280
- // Should include default patterns (package.json, README.md, etc.)
281
- expect(filePaths.some((element) => element.includes("package.json"))).toBe(true);
282
- expect(filePaths.some((element) => element.includes("README.md"))).toBe(true);
283
-
284
- // Should exclude user exclude patterns
285
- expect(filePaths.some((element) => element.includes("docs/"))).toBe(false);
286
- });
287
-
288
- test("should handle multi-level directory structure", async () => {
289
- const result = await loadSources({
290
- sourcesPath: testDir,
291
- includePatterns: ["**/*.js"],
292
- excludePatterns: [],
293
- useDefaultPatterns: false,
294
- outputDir: tempDir,
295
- docsDir: path.join(testDir, "docs"),
296
- });
297
-
298
- expect(result.files).toBeDefined();
299
- expect(result.files.length).toBeGreaterThan(0);
300
-
301
- const filePaths = result.files;
302
-
303
- // Should include files from all levels
304
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
305
- expect(filePaths.some((element) => element.includes("src/utils.js"))).toBe(true);
306
- expect(filePaths.some((element) => element.includes("src/components/Button.js"))).toBe(true);
307
- expect(filePaths.some((element) => element.includes("src/components/ui/Modal.js"))).toBe(true);
308
- expect(filePaths.some((element) => element.includes("src/components/ui/Input.js"))).toBe(true);
309
- expect(filePaths.some((element) => element.includes("src/utils/helpers/format.js"))).toBe(true);
310
- expect(filePaths.some((element) => element.includes("src/utils/helpers/validate.js"))).toBe(
311
- true,
312
- );
313
- expect(filePaths.some((element) => element.includes("src/services/api/client.js"))).toBe(true);
314
- expect(filePaths.some((element) => element.includes("src/services/api/endpoints.js"))).toBe(
315
- true,
316
- );
317
- expect(filePaths.some((element) => element.includes("src/config/database.js"))).toBe(true);
318
- expect(filePaths.some((element) => element.includes("src/config/app.js"))).toBe(true);
319
-
320
- // Should exclude non-JS files
321
- expect(filePaths.some((element) => element.includes("styles.css"))).toBe(false);
322
- expect(filePaths.some((element) => element.includes("settings.json"))).toBe(false);
323
- expect(filePaths.some((element) => element.includes("data.yaml"))).toBe(false);
324
- });
325
-
326
- test("should filter by specific subdirectories", async () => {
327
- const result = await loadSources({
328
- sourcesPath: testDir,
329
- includePatterns: ["src/components/**/*.js", "src/utils/**/*.js"],
330
- excludePatterns: ["src/components/ui/*.js"],
331
- useDefaultPatterns: false,
332
- outputDir: tempDir,
333
- docsDir: path.join(testDir, "docs"),
334
- });
335
-
336
- expect(result.files).toBeDefined();
337
-
338
- const filePaths = result.files;
339
-
340
- // Should include files from specified subdirectories
341
- expect(filePaths.some((element) => element.includes("src/components/Button.js"))).toBe(true);
342
- expect(filePaths.some((element) => element.includes("src/utils/helpers/format.js"))).toBe(true);
343
- expect(filePaths.some((element) => element.includes("src/utils/helpers/validate.js"))).toBe(
344
- true,
345
- );
346
-
347
- // Should exclude files from excluded subdirectories
348
- expect(filePaths.some((element) => element.includes("src/components/ui/Modal.js"))).toBe(false);
349
- expect(filePaths.some((element) => element.includes("src/components/ui/Input.js"))).toBe(false);
350
-
351
- // Should exclude files from other directories
352
- expect(filePaths.some((element) => element.includes("src/services/api/client.js"))).toBe(false);
353
- expect(filePaths.some((element) => element.includes("src/config/database.js"))).toBe(false);
354
- });
355
-
356
- test("should handle mixed file types in multi-level directories", async () => {
357
- const result = await loadSources({
358
- sourcesPath: testDir,
359
- includePatterns: ["**/*.js", "**/*.json", "**/*.yaml"],
360
- excludePatterns: ["**/node_modules/**"],
361
- useDefaultPatterns: false,
362
- outputDir: tempDir,
363
- docsDir: path.join(testDir, "docs"),
364
- });
365
-
366
- expect(result.files).toBeDefined();
367
-
368
- const filePaths = result.files;
369
-
370
- // Should include JS files from all levels
371
- expect(filePaths.some((element) => element.includes("src/components/Button.js"))).toBe(true);
372
- expect(filePaths.some((element) => element.includes("src/utils/helpers/format.js"))).toBe(true);
373
-
374
- // Should include JSON and YAML files
375
- expect(filePaths.some((element) => element.includes("src/config/settings.json"))).toBe(true);
376
- expect(filePaths.some((element) => element.includes("src/utils/helpers/data.yaml"))).toBe(true);
377
-
378
- // Should exclude CSS files
379
- expect(filePaths.some((element) => element.includes("styles.css"))).toBe(false);
380
-
381
- // Should exclude node_modules
382
- expect(filePaths.some((element) => element.includes("node_modules"))).toBe(false);
383
- });
384
-
385
- test("should exclude files with _test pattern using default patterns", async () => {
386
- const result = await loadSources({
387
- sourcesPath: testDir,
388
- useDefaultPatterns: true,
389
- outputDir: tempDir,
390
- docsDir: path.join(testDir, "docs"),
391
- });
392
-
393
- expect(result.files).toBeDefined();
394
-
395
- const filePaths = result.files;
396
-
397
- // For now, let's verify that regular files are still included
398
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
399
- expect(filePaths.some((element) => element.includes("src/utils.js"))).toBe(true);
400
- expect(filePaths.some((element) => element.includes("src/components/Button.js"))).toBe(true);
401
-
402
- // And verify that some expected exclusions are working
403
- expect(filePaths.some((element) => element.includes("node_modules"))).toBe(false);
404
- expect(filePaths.some((element) => element.includes("temp/"))).toBe(false);
405
- expect(filePaths.some((element) => element.includes("test/test.js"))).toBe(false);
406
- });
407
-
408
- test("should handle glob patterns in sourcesPath", async () => {
409
- const result = await loadSources({
410
- sourcesPath: [`${testDir}/src/**/*.js`],
411
- useDefaultPatterns: false,
412
- outputDir: tempDir,
413
- docsDir: path.join(testDir, "docs"),
414
- });
415
-
416
- expect(result.files).toBeDefined();
417
- expect(result.files.length).toBeGreaterThan(0);
418
-
419
- const filePaths = result.files;
420
-
421
- // Should include JS files from src directory and subdirectories
422
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
423
- expect(filePaths.some((element) => element.includes("src/utils.js"))).toBe(true);
424
- expect(filePaths.some((element) => element.includes("src/components/Button.js"))).toBe(true);
425
- expect(filePaths.some((element) => element.includes("src/components/ui/Modal.js"))).toBe(true);
426
- expect(filePaths.some((element) => element.includes("src/utils/helpers/format.js"))).toBe(true);
427
-
428
- // Should exclude non-JS files and files outside src
429
- expect(filePaths.some((element) => element.includes("package.json"))).toBe(false);
430
- expect(filePaths.some((element) => element.includes("README.md"))).toBe(false);
431
- expect(filePaths.some((element) => element.includes("styles.css"))).toBe(false);
432
- });
433
-
434
- test("should handle multiple glob patterns in sourcesPath", async () => {
435
- const result = await loadSources({
436
- sourcesPath: [`${testDir}/src/**/*.js`, `${testDir}/*.json`, `${testDir}/*.md`],
437
- useDefaultPatterns: false,
438
- outputDir: tempDir,
439
- docsDir: path.join(testDir, "docs"),
440
- });
441
-
442
- expect(result.files).toBeDefined();
443
- expect(result.files.length).toBeGreaterThan(0);
444
-
445
- const filePaths = result.files;
446
-
447
- // Should include JS files from src directory
448
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
449
- expect(filePaths.some((element) => element.includes("src/components/Button.js"))).toBe(true);
450
-
451
- // Should include JSON files from root
452
- expect(filePaths.some((element) => element.includes("package.json"))).toBe(true);
453
-
454
- // Should include Markdown files from root
455
- expect(filePaths.some((element) => element.includes("README.md"))).toBe(true);
456
-
457
- // Should exclude CSS files
458
- expect(filePaths.some((element) => element.includes("styles.css"))).toBe(false);
459
- });
460
-
461
- test("should handle glob pattern with specific file extensions", async () => {
462
- const result = await loadSources({
463
- sourcesPath: [`${testDir}/src/**/*.{js,json,yaml}`],
464
- useDefaultPatterns: false,
465
- outputDir: tempDir,
466
- docsDir: path.join(testDir, "docs"),
467
- });
468
-
469
- expect(result.files).toBeDefined();
470
-
471
- const filePaths = result.files;
472
-
473
- // Should include JS files
474
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
475
- expect(filePaths.some((element) => element.includes("src/utils.js"))).toBe(true);
476
-
477
- // Should include JSON files
478
- expect(filePaths.some((element) => element.includes("src/config/settings.json"))).toBe(true);
479
-
480
- // Should include YAML files
481
- expect(filePaths.some((element) => element.includes("src/utils/helpers/data.yaml"))).toBe(true);
482
-
483
- // Should exclude CSS files
484
- expect(filePaths.some((element) => element.includes("styles.css"))).toBe(false);
485
- });
486
-
487
- test("should handle glob pattern that matches no files", async () => {
488
- const result = await loadSources({
489
- sourcesPath: [`${testDir}/nonexistent/**/*.xyz`],
490
- useDefaultPatterns: false,
491
- outputDir: tempDir,
492
- docsDir: path.join(testDir, "docs"),
493
- });
494
-
495
- expect(result.files).toBeDefined();
496
- expect(result.files.length).toBe(0);
497
- });
498
-
499
- test("should handle mixed regular paths and glob patterns", async () => {
500
- const result = await loadSources({
501
- sourcesPath: [testDir, `${testDir}/src/**/*.js`],
502
- includePatterns: ["*.md"],
503
- useDefaultPatterns: false,
504
- outputDir: tempDir,
505
- docsDir: path.join(testDir, "docs"),
506
- });
507
-
508
- expect(result.files).toBeDefined();
509
- expect(result.files.length).toBeGreaterThan(0);
510
-
511
- const filePaths = result.files;
512
-
513
- // Should include markdown files from directory scan
514
- expect(filePaths.some((element) => element.includes("README.md"))).toBe(true);
515
-
516
- // Should include JS files from glob pattern
517
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
518
- expect(filePaths.some((element) => element.includes("src/components/Button.js"))).toBe(true);
519
- });
520
-
521
- test("should handle glob patterns with wildcards and character classes", async () => {
522
- const result = await loadSources({
523
- sourcesPath: [`${testDir}/src/**/[Bb]utton.js`, `${testDir}/src/**/?odal.js`],
524
- useDefaultPatterns: false,
525
- outputDir: tempDir,
526
- docsDir: path.join(testDir, "docs"),
527
- });
528
-
529
- expect(result.files).toBeDefined();
530
-
531
- const filePaths = result.files;
532
-
533
- // Should match Button.js with character class
534
- expect(filePaths.some((element) => element.includes("src/components/Button.js"))).toBe(true);
535
-
536
- // Should match Modal.js with single character wildcard
537
- expect(filePaths.some((element) => element.includes("src/components/ui/Modal.js"))).toBe(true);
538
-
539
- // Should not match other files
540
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(false);
541
- expect(filePaths.some((element) => element.includes("src/utils.js"))).toBe(false);
542
- });
543
-
544
- describe("Configuration integration tests", () => {
545
- test("should handle YAML config-like sourcesPath input", async () => {
546
- const yamlConfigInput = {
547
- sourcesPath: ["./src", "./lib", "**/*.{js,ts,jsx,tsx}"],
548
- docsDir: "./docs",
549
- useDefaultPatterns: true,
550
- outputDir: tempDir,
551
- };
552
-
553
- const result = await loadSources(yamlConfigInput);
554
-
555
- expect(result.files).toBeDefined();
556
- expect(result.files.length).toBeGreaterThan(0);
557
-
558
- const filePaths = result.files;
559
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
560
- });
561
-
562
- test("should handle config with empty sourcesPath array - ACTUALLY WORKS", async () => {
563
- // This test shows that empty arrays are actually handled correctly
564
- const result = await loadSources({
565
- sourcesPath: [],
566
- useDefaultPatterns: true,
567
- outputDir: tempDir,
568
- docsDir: path.join(testDir, "docs"),
569
- });
570
-
571
- expect(result.files).toBeDefined();
572
- expect(result.files.length).toBe(0);
573
- expect(result.files.length).toBe(0);
574
-
575
- // Empty arrays are handled gracefully - this is correct behavior
576
- });
577
-
578
- test("should filter invalid path elements gracefully - EXPOSES ROBUSTNESS BUG", async () => {
579
- // ROBUSTNESS BUG TEST: Function should handle invalid elements gracefully
580
- const mixedArray = ["./src", null, undefined, "./lib", ""];
581
-
582
- // This test expects the function to filter out invalid elements and work with valid ones
583
- const result = await loadSources({
584
- sourcesPath: mixedArray,
585
- useDefaultPatterns: true,
586
- outputDir: tempDir,
587
- docsDir: path.join(testDir, "docs"),
588
- });
589
-
590
- // Should successfully process valid paths and ignore invalid ones
591
- expect(result.files).toBeDefined();
592
- // This test will FAIL because the function crashes instead of filtering
593
- });
594
-
595
- test("should handle config with undefined sourcesPath - ACTUALLY WORKS", async () => {
596
- // This test shows undefined sourcesPath is actually handled correctly
597
- const result = await loadSources({
598
- sourcesPath: undefined,
599
- useDefaultPatterns: true,
600
- outputDir: tempDir,
601
- docsDir: path.join(testDir, "docs"),
602
- });
603
-
604
- expect(result.files).toBeDefined();
605
- expect(Array.isArray(result.files)).toBe(true);
606
- expect(result.files.length).toBe(0);
607
-
608
- // undefined is handled gracefully by skipping sourcesPath processing
609
- });
610
- });
611
-
612
- describe("Function robustness issues - EXPOSES INPUT HANDLING PROBLEMS", () => {
613
- test("should handle invalid inputs gracefully - EXPOSES POOR ERROR HANDLING", async () => {
614
- // ROBUSTNESS TEST: Function should validate inputs and provide helpful errors
615
- const invalidInputs = [
616
- { value: [null], description: "array with null element", shouldFail: true },
617
- { value: [undefined], description: "array with undefined element", shouldFail: true },
618
- { value: ["", null], description: "mixed array with null", shouldFail: true },
619
- ];
620
-
621
- for (const { value: invalidInput, shouldFail } of invalidInputs) {
622
- if (shouldFail) {
623
- // These should provide helpful error messages, not crash unexpectedly
624
- const result = await loadSources({
625
- sourcesPath: invalidInput,
626
- useDefaultPatterns: true,
627
- outputDir: tempDir,
628
- docsDir: path.join(testDir, "docs"),
629
- });
630
- expect(result.files).toBeDefined();
631
- expect(Array.isArray(result.files)).toBe(true);
632
- expect(result.files.length).toBe(0);
633
- }
634
- }
635
-
636
- // Test inputs that should work but be handled gracefully
637
- const shouldWorkInputs = [
638
- null, // Should be treated like undefined
639
- "", // Should be treated like undefined
640
- 123, // Should provide helpful error
641
- {}, // Should provide helpful error
642
- [""], // Should filter out empty strings
643
- ];
644
-
645
- for (const validInput of shouldWorkInputs) {
646
- // These tests expect graceful handling but may FAIL due to poor input validation
647
- const result = await loadSources({
648
- sourcesPath: validInput,
649
- useDefaultPatterns: true,
650
- outputDir: tempDir,
651
- docsDir: path.join(testDir, "docs"),
652
- });
653
-
654
- expect(result.files).toBeDefined();
655
- // Some of these tests will FAIL, exposing input validation issues
656
- }
657
- });
658
- });
659
-
660
- describe("File system edge cases", () => {
661
- test("should handle symbolic links", async () => {
662
- // Skip on systems that don't support symlinks
663
- try {
664
- const { symlink } = await import("node:fs/promises");
665
- const symlinkPath = path.join(testDir, "symlink-to-src");
666
- await symlink(path.join(testDir, "src"), symlinkPath);
667
-
668
- const result = await loadSources({
669
- sourcesPath: symlinkPath,
670
- includePatterns: ["*.js"],
671
- useDefaultPatterns: false,
672
- outputDir: tempDir,
673
- docsDir: path.join(testDir, "docs"),
674
- });
675
-
676
- expect(result.files).toBeDefined();
677
- const filePaths = result.files;
678
- expect(filePaths.some((element) => element.includes("index.js"))).toBe(true);
679
- } catch {
680
- // Skip test on systems that don't support symlinks
681
- expect(true).toBe(true);
682
- }
683
- });
684
-
685
- test("should handle very deep directory structures", async () => {
686
- // Create deep nested structure
687
- const deepPath = path.join(testDir, "a/b/c/d/e/f/g/h");
688
- await mkdir(deepPath, { recursive: true });
689
- await writeFile(path.join(deepPath, "deep.js"), "console.log('deep');");
690
-
691
- const content = await readFile(path.join(deepPath, "deep.js"), "utf8");
692
- console.log("deep content", content);
693
-
694
- const result = await loadSources({
695
- sourcesPath: testDir,
696
- includePatterns: ["**/*.js"],
697
- useDefaultPatterns: false,
698
- outputDir: tempDir,
699
- docsDir: path.join(testDir, "docs"),
700
- });
701
-
702
- expect(result.files).toBeDefined();
703
- const filePaths = result.files;
704
- expect(filePaths.some((element) => element.includes("a/b/c/d/e/f/g/h/deep.js"))).toBe(true);
705
- });
706
-
707
- test("should handle files with unusual names", async () => {
708
- // Create files with special characters in names
709
- await writeFile(path.join(testDir, "file with spaces.js"), "console.log('spaces');");
710
- await writeFile(path.join(testDir, "file-with-dashes.js"), "console.log('dashes');");
711
- await writeFile(
712
- path.join(testDir, "file_with_underscores.js"),
713
- "console.log('underscores');",
714
- );
715
- await writeFile(path.join(testDir, "file.with.dots.js"), "console.log('dots');");
716
- await writeFile(path.join(testDir, "文件中文名.js"), "console.log('chinese');");
717
-
718
- const result = await loadSources({
719
- sourcesPath: testDir,
720
- includePatterns: ["*.js"],
721
- excludePatterns: ["**/test/**", "*_test*"],
722
- useDefaultPatterns: false,
723
- outputDir: tempDir,
724
- docsDir: path.join(testDir, "docs"),
725
- });
726
-
727
- expect(result.files).toBeDefined();
728
- const filePaths = result.files;
729
-
730
- expect(filePaths.some((element) => element.includes("file with spaces.js"))).toBe(true);
731
- expect(filePaths.some((element) => element.includes("file-with-dashes.js"))).toBe(true);
732
- expect(filePaths.some((element) => element.includes("file_with_underscores.js"))).toBe(true);
733
- expect(filePaths.some((element) => element.includes("file.with.dots.js"))).toBe(true);
734
- expect(filePaths.some((element) => element.includes("文件中文名.js"))).toBe(true);
735
- });
736
-
737
- test("should handle empty directories", async () => {
738
- const emptyDir = path.join(testDir, "empty");
739
- await mkdir(emptyDir, { recursive: true });
740
-
741
- const result = await loadSources({
742
- sourcesPath: emptyDir,
743
- useDefaultPatterns: true,
744
- outputDir: tempDir,
745
- docsDir: path.join(testDir, "docs"),
746
- });
747
-
748
- expect(result.files).toBeDefined();
749
- expect(result.files.length).toBe(0);
750
- });
751
- });
752
-
753
- describe("Pattern matching edge cases", () => {
754
- test("should handle negation patterns correctly", async () => {
755
- const result = await loadSources({
756
- sourcesPath: testDir,
757
- includePatterns: ["**/*.js"],
758
- excludePatterns: ["!src/components/**"], // Negation should include
759
- useDefaultPatterns: false,
760
- outputDir: tempDir,
761
- docsDir: path.join(testDir, "docs"),
762
- });
763
-
764
- expect(result.files).toBeDefined();
765
- const filePaths = result.files;
766
-
767
- // Should still exclude test files by default gitignore/patterns
768
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(true);
769
- expect(filePaths.some((element) => element.includes("src/components/Button.js"))).toBe(true);
770
- });
771
-
772
- test("should handle conflicting include/exclude patterns", async () => {
773
- const result = await loadSources({
774
- sourcesPath: testDir,
775
- includePatterns: ["src/**/*.js"], // Include all JS in src
776
- excludePatterns: ["src/**/*.js"], // But exclude all JS in src
777
- useDefaultPatterns: false,
778
- outputDir: tempDir,
779
- docsDir: path.join(testDir, "docs"),
780
- });
781
-
782
- expect(result.files).toBeDefined();
783
- const filePaths = result.files;
784
-
785
- // Exclude should win over include
786
- expect(filePaths.some((element) => element.includes("src/index.js"))).toBe(false);
787
- expect(filePaths.some((element) => element.includes("src/components/Button.js"))).toBe(false);
788
- });
789
-
790
- test("should handle case sensitivity in patterns", async () => {
791
- // Create files with different cases
792
- await writeFile(path.join(testDir, "CamelCase.js"), "console.log('CamelCase');");
793
- await writeFile(path.join(testDir, "lowercase.js"), "console.log('lowercase');");
794
- await writeFile(path.join(testDir, "UPPERCASE.JS"), "console.log('UPPERCASE');");
795
-
796
- const result = await loadSources({
797
- sourcesPath: testDir,
798
- includePatterns: ["*.[jJ][sS]"], // Case insensitive for extension
799
- useDefaultPatterns: false,
800
- outputDir: tempDir,
801
- docsDir: path.join(testDir, "docs"),
802
- });
803
-
804
- expect(result.files).toBeDefined();
805
- const filePaths = result.files;
806
-
807
- expect(filePaths.some((element) => element.includes("CamelCase.js"))).toBe(true);
808
- expect(filePaths.some((element) => element.includes("lowercase.js"))).toBe(true);
809
- expect(filePaths.some((element) => element.includes("UPPERCASE.JS"))).toBe(true);
810
- });
811
-
812
- test("should handle extremely complex glob patterns", async () => {
813
- const result = await loadSources({
814
- sourcesPath: testDir,
815
- includePatterns: [
816
- "src/**/!(test|spec)*{.js,.ts,.jsx,.tsx}", // Complex negation with alternatives
817
- "**/@(components|utils|services)/**/*.{js,ts}", // At-patterns with alternatives
818
- ],
819
- useDefaultPatterns: false,
820
- outputDir: tempDir,
821
- docsDir: path.join(testDir, "docs"),
822
- });
823
-
824
- expect(result.files).toBeDefined();
825
- // Just verify it doesn't crash and returns some results
826
- expect(Array.isArray(result.files)).toBe(true);
827
- });
828
- });
829
-
830
- describe("Performance and scale tests", () => {
831
- test("should handle large number of patterns efficiently", async () => {
832
- const manyPatterns = Array(100)
833
- .fill(0)
834
- .map((_, i) => `**/*${i}*.js`);
835
-
836
- const startTime = Date.now();
837
- const result = await loadSources({
838
- sourcesPath: testDir,
839
- includePatterns: manyPatterns,
840
- useDefaultPatterns: false,
841
- outputDir: tempDir,
842
- docsDir: path.join(testDir, "docs"),
843
- });
844
- const endTime = Date.now();
845
-
846
- expect(result.files).toBeDefined();
847
- expect(endTime - startTime).toBeLessThan(5000); // Should complete within 5 seconds
848
- });
849
-
850
- test("should handle very long paths", async () => {
851
- const longDirName = "a".repeat(100);
852
- const longPath = path.join(testDir, longDirName);
853
- await mkdir(longPath, { recursive: true });
854
- await writeFile(path.join(longPath, "file.js"), "console.log('long path');");
855
-
856
- const result = await loadSources({
857
- sourcesPath: testDir,
858
- includePatterns: ["**/*.js"],
859
- useDefaultPatterns: false,
860
- outputDir: tempDir,
861
- docsDir: path.join(testDir, "docs"),
862
- });
863
-
864
- expect(result.files).toBeDefined();
865
- const filePaths = result.files;
866
- expect(filePaths.some((element) => element.includes(longDirName))).toBe(true);
867
- });
868
- });
869
-
870
- describe("Error handling and resilience", () => {
871
- test("should handle permission denied gracefully", async () => {
872
- // This test may not work on all systems, so we wrap in try-catch
873
- try {
874
- const restrictedDir = path.join(testDir, "restricted");
875
- await mkdir(restrictedDir, { recursive: true, mode: 0o000 });
876
-
877
- const result = await loadSources({
878
- sourcesPath: restrictedDir,
879
- useDefaultPatterns: true,
880
- outputDir: tempDir,
881
- docsDir: path.join(testDir, "docs"),
882
- });
883
-
884
- expect(result.files).toBeDefined();
885
- // Should handle gracefully without crashing
886
- } catch {
887
- // Skip on systems where permission tests don't work
888
- expect(true).toBe(true);
889
- }
890
- });
891
-
892
- test("should handle malformed glob patterns", async () => {
893
- const result = await loadSources({
894
- sourcesPath: [`${testDir}/[unclosed`, `${testDir}/**/{unclosed`],
895
- useDefaultPatterns: false,
896
- outputDir: tempDir,
897
- docsDir: path.join(testDir, "docs"),
898
- });
899
-
900
- expect(result.files).toBeDefined();
901
- // Should not crash, even with malformed patterns
902
- expect(Array.isArray(result.files)).toBe(true);
903
- });
904
-
905
- test("should handle circular symbolic links", async () => {
906
- try {
907
- const { symlink, unlink } = await import("node:fs/promises");
908
- const link1 = path.join(testDir, "link1");
909
- const link2 = path.join(testDir, "link2");
910
-
911
- // Create circular links
912
- await symlink(link2, link1);
913
- await symlink(link1, link2);
914
-
915
- const result = await loadSources({
916
- sourcesPath: testDir,
917
- includePatterns: ["**/*"],
918
- useDefaultPatterns: false,
919
- outputDir: tempDir,
920
- docsDir: path.join(testDir, "docs"),
921
- });
922
-
923
- expect(result.files).toBeDefined();
924
- // Should handle circular links without infinite loop
925
-
926
- // Clean up circular links immediately to prevent interference with other tests
927
- try {
928
- await unlink(link1);
929
- } catch {
930
- // Ignore cleanup errors since they don't affect test results
931
- }
932
- try {
933
- await unlink(link2);
934
- } catch {
935
- // Ignore cleanup errors since they don't affect test results
936
- }
937
- } catch {
938
- // Skip on systems that don't support symlinks
939
- expect(true).toBe(true);
940
- }
941
- });
942
- });
943
-
944
- describe("Integration with generateYAML output", () => {
945
- test("should process typical generateYAML sourcesPath configurations", async () => {
946
- const typicalConfigs = [
947
- ["./src", "./lib"],
948
- ["**/*.{js,ts,jsx,tsx}"],
949
- ["src/**/*.js", "lib/**/*.ts", "docs/**/*.md"],
950
- ["./", "!node_modules", "!dist"],
951
- ];
952
-
953
- for (const sourcesPathConfig of typicalConfigs) {
954
- const result = await loadSources({
955
- sourcesPath: sourcesPathConfig,
956
- useDefaultPatterns: true,
957
- outputDir: tempDir,
958
- docsDir: path.join(testDir, "docs"),
959
- });
960
-
961
- expect(result.files).toBeDefined();
962
- expect(Array.isArray(result.files)).toBe(true);
963
- // Should not crash with any typical config
964
- }
965
- });
966
- });
967
-
968
- describe("File type handling and media assets", () => {
969
- test("should handle individual files as sourcesPath", async () => {
970
- // Test line 41: stats.isFile() branch
971
- const singleFile = path.join(testDir, "single-file.js");
972
- await writeFile(singleFile, "console.log('single file');");
973
-
974
- const result = await loadSources({
975
- sourcesPath: singleFile,
976
- useDefaultPatterns: false,
977
- outputDir: tempDir,
978
- docsDir: path.join(testDir, "docs"),
979
- });
980
-
981
- expect(result.files).toBeDefined();
982
- expect(result.files.length).toBe(1);
983
- expect(result.files[0]).toContain("single-file.js");
984
- });
985
-
986
- test("should process media files correctly", async () => {
987
- // Create media files to test lines 151-159
988
- const mediaDir = path.join(testDir, "media");
989
- await mkdir(mediaDir, { recursive: true });
990
-
991
- await writeFile(path.join(mediaDir, "image.jpg"), "fake image data");
992
- await writeFile(path.join(mediaDir, "video.mp4"), "fake video data");
993
- await writeFile(path.join(mediaDir, "logo.svg"), "<svg></svg>");
994
- await writeFile(path.join(mediaDir, "animation.webp"), "fake webp data");
995
-
996
- const result = await loadSources({
997
- sourcesPath: mediaDir,
998
- includePatterns: ["**/*"],
999
- useDefaultPatterns: false,
1000
- outputDir: tempDir,
1001
- docsDir: path.join(testDir, "docs"),
1002
- });
1003
-
1004
- expect(result.assetsContent).toBeDefined();
1005
- expect(result.assetsContent).toContain("Available Media Assets");
1006
- expect(result.assetsContent).toContain("image.jpg");
1007
- expect(result.assetsContent).toContain("video.mp4");
1008
- expect(result.assetsContent).toContain("logo.svg");
1009
- expect(result.assetsContent).toContain('type: "image"');
1010
- expect(result.assetsContent).toContain('type: "video"');
1011
-
1012
- // Test media file processing logic (lines 243-266)
1013
- expect(result.assetsContent).toContain("```yaml");
1014
- expect(result.assetsContent).toContain("assets:");
1015
- });
1016
-
1017
- test("should handle glob pattern errors gracefully", async () => {
1018
- const invalidGlobPattern = "./invalid/**/*.{unclosed";
1019
-
1020
- const result = await loadSources({
1021
- sourcesPath: [invalidGlobPattern],
1022
- useDefaultPatterns: false,
1023
- outputDir: tempDir,
1024
- docsDir: path.join(testDir, "docs"),
1025
- });
1026
-
1027
- expect(result.files).toBeDefined();
1028
- expect(Array.isArray(result.files)).toBe(true);
1029
- // Should handle gracefully without crashing
1030
- });
1031
- });
1032
-
1033
- describe("Document path and documentation structure handling", () => {
1034
- test("should load existing documentation structure", async () => {
1035
- const documentStructure = {
1036
- sections: ["Introduction", "API", "Examples"],
1037
- lastUpdated: new Date().toISOString(),
1038
- };
1039
-
1040
- await writeFile(path.join(tempDir, "structure-plan.json"), JSON.stringify(documentStructure));
1041
-
1042
- const result = await loadSources({
1043
- sourcesPath: testDir,
1044
- includePatterns: ["*.js"],
1045
- useDefaultPatterns: false,
1046
- outputDir: tempDir,
1047
- docsDir: path.join(testDir, "docs"),
1048
- });
1049
-
1050
- expect(result.originalDocumentStructure).toEqual(documentStructure);
1051
- });
1052
-
1053
- test("should handle malformed documentation structure JSON", async () => {
1054
- await writeFile(path.join(tempDir, "structure-plan.json"), "{ invalid json content");
1055
-
1056
- const result = await loadSources({
1057
- sourcesPath: testDir,
1058
- includePatterns: ["*.js"],
1059
- useDefaultPatterns: false,
1060
- outputDir: tempDir,
1061
- docsDir: path.join(testDir, "docs"),
1062
- });
1063
-
1064
- expect(result.originalDocumentStructure).toBeUndefined();
1065
- });
1066
-
1067
- test("should handle non-ENOENT errors when reading documentation structure JSON", async () => {
1068
- // Import fs promises module to spy on
1069
- const fsPromises = await import("node:fs/promises");
1070
-
1071
- // Get the original readFile function
1072
- const originalReadFile = fsPromises.readFile;
1073
-
1074
- // Create a spy that only throws error for structure-plan.json
1075
- const readFileSpy = spyOn(fsPromises, "readFile").mockImplementation((filePath, encoding) => {
1076
- if (filePath.includes("structure-plan.json")) {
1077
- const error = new Error("Permission denied");
1078
- error.code = "EACCES";
1079
- throw error;
1080
- }
1081
- // For all other files, use the original readFile
1082
- return originalReadFile(filePath, encoding);
1083
- });
1084
-
1085
- try {
1086
- const result = await loadSources({
1087
- sourcesPath: testDir,
1088
- includePatterns: ["*.js"],
1089
- useDefaultPatterns: false,
1090
- outputDir: tempDir,
1091
- docsDir: path.join(testDir, "docs"),
1092
- });
1093
-
1094
- expect(result.originalDocumentStructure).toBeUndefined();
1095
- } finally {
1096
- // Restore the original readFile function
1097
- readFileSpy.mockRestore();
1098
- }
1099
- });
1100
-
1101
- test("should handle non-ENOENT errors when reading docPath file", async () => {
1102
- // Import fs promises module to spy on
1103
- const fsPromises = await import("node:fs/promises");
1104
-
1105
- // Get the original readFile function
1106
- const originalReadFile = fsPromises.readFile;
1107
-
1108
- // Create a spy that only throws error for the specific docPath file
1109
- const readFileSpy = spyOn(fsPromises, "readFile").mockImplementation((filePath, encoding) => {
1110
- // Check if this is the docPath file (api-overview.md)
1111
- if (filePath.includes("api-overview.md")) {
1112
- const error = new Error("Permission denied");
1113
- error.code = "EACCES";
1114
- throw error;
1115
- }
1116
- // For all other files, use the original readFile
1117
- return originalReadFile(filePath, encoding);
1118
- });
1119
-
1120
- try {
1121
- const result = await loadSources({
1122
- sourcesPath: testDir,
1123
- "doc-path": "/api/overview",
1124
- includePatterns: ["*.js"],
1125
- useDefaultPatterns: false,
1126
- outputDir: tempDir,
1127
- docsDir: path.join(testDir, "docs"),
1128
- });
1129
-
1130
- expect(result.content).toBeUndefined();
1131
- } finally {
1132
- // Restore the original readFile function
1133
- readFileSpy.mockRestore();
1134
- }
1135
- });
1136
-
1137
- test("should handle non-ENOENT errors when reading boardId-based docPath file", async () => {
1138
- // Import fs promises module to spy on
1139
- const fsPromises = await import("node:fs/promises");
1140
-
1141
- // Get the original readFile function
1142
- const originalReadFile = fsPromises.readFile;
1143
-
1144
- // Create a spy that only throws error for the boardId file
1145
- const readFileSpy = spyOn(fsPromises, "readFile").mockImplementation((filePath, encoding) => {
1146
- // First call will fail for the original format (board123-api-overview.md)
1147
- // Second call should fail for the boardId format (api-overview.md)
1148
- if (filePath.includes("board123-api-overview.md")) {
1149
- const error = new Error("File not found");
1150
- error.code = "ENOENT";
1151
- throw error;
1152
- }
1153
- if (filePath.includes("api-overview.md") && !filePath.includes("board123-")) {
1154
- const error = new Error("Permission denied");
1155
- error.code = "EACCES";
1156
- throw error;
1157
- }
1158
- // For all other files, use the original readFile
1159
- return originalReadFile(filePath, encoding);
1160
- });
1161
-
1162
- try {
1163
- const result = await loadSources({
1164
- sourcesPath: testDir,
1165
- "doc-path": "board123-api-overview",
1166
- boardId: "board123",
1167
- includePatterns: ["*.js"],
1168
- useDefaultPatterns: false,
1169
- outputDir: tempDir,
1170
- docsDir: path.join(testDir, "docs"),
1171
- });
1172
-
1173
- expect(result.content).toBeUndefined();
1174
- } finally {
1175
- // Restore the original readFile function
1176
- readFileSpy.mockRestore();
1177
- }
1178
- });
1179
-
1180
- test("should load document content by docPath", async () => {
1181
- const docsDir = path.join(testDir, "docs");
1182
- const docContent = "# API Documentation\n\nThis is the API documentation content.";
1183
-
1184
- await writeFile(path.join(docsDir, "api-overview.md"), docContent);
1185
-
1186
- const result = await loadSources({
1187
- sourcesPath: testDir,
1188
- "doc-path": "/api/overview",
1189
- includePatterns: ["*.md"],
1190
- useDefaultPatterns: false,
1191
- outputDir: tempDir,
1192
- docsDir: docsDir,
1193
- });
1194
-
1195
- expect(result.content).toBe(docContent);
1196
- });
1197
-
1198
- test("should handle boardId-based doc path format", async () => {
1199
- const docsDir = path.join(testDir, "docs");
1200
- const docContent = "# Board specific documentation";
1201
-
1202
- await writeFile(path.join(docsDir, "user-guide.md"), docContent);
1203
-
1204
- const result = await loadSources({
1205
- sourcesPath: testDir,
1206
- "doc-path": "board123-user-guide",
1207
- boardId: "board123",
1208
- includePatterns: ["*.md"],
1209
- useDefaultPatterns: false,
1210
- outputDir: tempDir,
1211
- docsDir: docsDir,
1212
- });
1213
-
1214
- expect(result.content).toBe(docContent);
1215
- });
1216
-
1217
- test("should handle non-existent doc path gracefully", async () => {
1218
- const result = await loadSources({
1219
- sourcesPath: testDir,
1220
- "doc-path": "/non-existent/doc",
1221
- includePatterns: ["*.md"],
1222
- useDefaultPatterns: false,
1223
- outputDir: tempDir,
1224
- docsDir: path.join(testDir, "docs"),
1225
- });
1226
-
1227
- expect(result.content).toBeUndefined();
1228
- });
1229
-
1230
- test("should handle includePatterns empty gracefully", async () => {
1231
- const docsDir = path.join(testDir, "docs");
1232
- const docContent = "# API Documentation\n\nThis is the API documentation content.";
1233
-
1234
- await writeFile(path.join(docsDir, "api-overview.md"), docContent);
1235
-
1236
- const result = await loadSources({
1237
- sourcesPath: testDir,
1238
- "doc-path": "/api/overview",
1239
- includePatterns: null,
1240
- useDefaultPatterns: false,
1241
- outputDir: tempDir,
1242
- docsDir: docsDir,
1243
- });
1244
-
1245
- expect(result.files?.length).toBe(0);
1246
- });
1247
- });
1248
-
1249
- describe("Token and line counting", () => {
1250
- test("should count tokens and lines in source content", async () => {
1251
- // Create files with known content for counting
1252
- await writeFile(
1253
- path.join(testDir, "count-test.js"),
1254
- "// This file has words and lines\nconst message = 'hello world';\nconsole.log(message);\n\n// Another comment\nfunction test() {\n return true;\n}",
1255
- );
1256
-
1257
- const result = await loadSources({
1258
- sourcesPath: testDir,
1259
- includePatterns: ["count-test.js"],
1260
- useDefaultPatterns: false,
1261
- outputDir: tempDir,
1262
- docsDir: path.join(testDir, "docs"),
1263
- });
1264
-
1265
- expect(result.totalTokens).toBeGreaterThan(0);
1266
- expect(result.totalLines).toBeGreaterThan(0);
1267
- expect(typeof result.totalTokens).toBe("number");
1268
- expect(typeof result.totalLines).toBe("number");
1269
- });
1270
- });
1271
-
1272
- describe("Media file path and metadata processing", () => {
1273
- test("should correctly process media file relativePath, fileName, and description", async () => {
1274
- // Create media files in a specific structure to test the exact logic
1275
- const mediaSubDir = path.join(testDir, "assets", "images");
1276
- await mkdir(mediaSubDir, { recursive: true });
1277
-
1278
- const imageFile = path.join(mediaSubDir, "company-logo.png");
1279
- const videoFile = path.join(mediaSubDir, "demo-video.mp4");
1280
- const svgFile = path.join(mediaSubDir, "icon-arrow.svg");
1281
-
1282
- await writeFile(imageFile, "fake png data");
1283
- await writeFile(videoFile, "fake video data");
1284
- await writeFile(svgFile, "<svg>fake svg</svg>");
1285
-
1286
- const docsDir = path.join(testDir, "docs");
1287
-
1288
- const result = await loadSources({
1289
- sourcesPath: mediaSubDir,
1290
- includePatterns: ["**/*"],
1291
- useDefaultPatterns: false,
1292
- outputDir: tempDir,
1293
- docsDir: docsDir,
1294
- });
1295
-
1296
- expect(result.assetsContent).toBeDefined();
1297
-
1298
- // Check that relativePath calculation worked (line 151)
1299
- expect(result.assetsContent).toContain("../assets/images/company-logo.png");
1300
- expect(result.assetsContent).toContain("../assets/images/demo-video.mp4");
1301
- expect(result.assetsContent).toContain("../assets/images/icon-arrow.svg");
1302
-
1303
- // Check that fileName extraction worked (line 152)
1304
- expect(result.assetsContent).toContain('name: "company-logo.png"');
1305
- expect(result.assetsContent).toContain('name: "demo-video.mp4"');
1306
- expect(result.assetsContent).toContain('name: "icon-arrow.svg"');
1307
-
1308
- // Test with complex filenames to ensure path.parse works correctly
1309
- const complexFile = path.join(mediaSubDir, "my-complex.file-name.with.dots.jpg");
1310
- await writeFile(complexFile, "fake jpg data");
1311
-
1312
- const result2 = await loadSources({
1313
- sourcesPath: mediaSubDir,
1314
- includePatterns: ["my-complex.file-name.with.dots.jpg"],
1315
- useDefaultPatterns: false,
1316
- outputDir: tempDir,
1317
- docsDir: docsDir,
1318
- });
1319
-
1320
- expect(result2.assetsContent).toContain('name: "my-complex.file-name.with.dots.jpg"');
1321
- expect(result2.assetsContent).toContain(
1322
- "../assets/images/my-complex.file-name.with.dots.jpg",
1323
- );
1324
- });
1325
-
1326
- test("should handle media files with same docsDir path correctly", async () => {
1327
- // Test when media files are in the same directory as docsDir
1328
- const docsDir = path.join(testDir, "docs");
1329
- const mediaFile = path.join(docsDir, "logo.png");
1330
-
1331
- await writeFile(mediaFile, "fake logo data");
1332
-
1333
- const result = await loadSources({
1334
- sourcesPath: docsDir,
1335
- includePatterns: ["*.png"],
1336
- useDefaultPatterns: false,
1337
- outputDir: tempDir,
1338
- docsDir: docsDir,
1339
- });
1340
-
1341
- // When file is in docsDir, relativePath should be just the filename
1342
- expect(result.assetsContent).toContain('path: "logo.png"');
1343
- expect(result.assetsContent).toContain('name: "logo.png"');
1344
- });
1345
-
1346
- test("should handle media files in parent directory relative to docsDir", async () => {
1347
- // Test when media files are in parent directory of docsDir
1348
- const docsDir = path.join(testDir, "documentation");
1349
- await mkdir(docsDir, { recursive: true });
1350
-
1351
- const mediaFile = path.join(testDir, "header-image.jpg");
1352
- await writeFile(mediaFile, "fake header data");
1353
-
1354
- const result = await loadSources({
1355
- sourcesPath: testDir,
1356
- includePatterns: ["header-image.jpg"],
1357
- useDefaultPatterns: false,
1358
- outputDir: tempDir,
1359
- docsDir: docsDir,
1360
- });
1361
-
1362
- // When file is in parent of docsDir, relativePath should use ../
1363
- expect(result.assetsContent).toContain('path: "../header-image.jpg"');
1364
- expect(result.assetsContent).toContain('name: "header-image.jpg"');
1365
- });
1366
- });
1367
-
1368
- describe("Edge cases and error handling", () => {
1369
- test("should handle empty media files list", async () => {
1370
- const result = await loadSources({
1371
- sourcesPath: testDir,
1372
- includePatterns: ["*.js"], // Only include non-media files
1373
- useDefaultPatterns: false,
1374
- outputDir: tempDir,
1375
- docsDir: path.join(testDir, "docs"),
1376
- });
1377
-
1378
- expect(result.assetsContent).toBeDefined();
1379
- expect(result.assetsContent).toContain("Available Media Assets");
1380
- // Should have basic header even with no media files
1381
- });
1382
-
1383
- test("should handle mixed source and media files", async () => {
1384
- // Create mixed content
1385
- await writeFile(path.join(testDir, "mixed-test.js"), "console.log('code');");
1386
- await writeFile(path.join(testDir, "mixed-image.png"), "fake png data");
1387
- await writeFile(path.join(testDir, "mixed-doc.md"), "# Documentation");
1388
-
1389
- const result = await loadSources({
1390
- sourcesPath: testDir,
1391
- includePatterns: ["mixed-*"],
1392
- useDefaultPatterns: false,
1393
- outputDir: tempDir,
1394
- docsDir: path.join(testDir, "docs"),
1395
- });
1396
-
1397
- expect(result.files.length).toBeGreaterThan(0);
1398
- expect(result.assetsContent).toContain("mixed-image.png");
1399
-
1400
- // Verify both source files and media files are processed
1401
- const sourceFiles = result.files;
1402
- expect(sourceFiles.some((f) => f.includes("mixed-test.js"))).toBe(true);
1403
- expect(sourceFiles.some((f) => f.includes("mixed-doc.md"))).toBe(true);
1404
- });
1405
-
1406
- test("should handle various media file types", async () => {
1407
- const mediaTypes = [
1408
- { name: "test.jpg", type: "image" },
1409
- { name: "test.jpeg", type: "image" },
1410
- { name: "test.png", type: "image" },
1411
- { name: "test.gif", type: "image" },
1412
- { name: "test.webp", type: "image" },
1413
- { name: "test.svg", type: "image" },
1414
- { name: "test.mp4", type: "video" },
1415
- { name: "test.mov", type: "video" },
1416
- { name: "test.avi", type: "video" },
1417
- { name: "test.webm", type: "video" },
1418
- ];
1419
-
1420
- // Create all media file types
1421
- for (const media of mediaTypes) {
1422
- await writeFile(path.join(testDir, media.name), `fake ${media.type} data`);
1423
- }
1424
-
1425
- const result = await loadSources({
1426
- sourcesPath: testDir,
1427
- includePatterns: ["test.*"],
1428
- useDefaultPatterns: false,
1429
- outputDir: tempDir,
1430
- docsDir: path.join(testDir, "docs"),
1431
- });
1432
-
1433
- // Verify all media types are properly categorized
1434
- for (const media of mediaTypes) {
1435
- expect(result.assetsContent).toContain(media.name);
1436
- expect(result.assetsContent).toContain(`type: "${media.type}"`);
1437
- }
1438
- });
1439
- });
1440
-
1441
- // Global cleanup to ensure test directories are fully removed
1442
- afterAll(async () => {
1443
- // Clean up all test-content-generator directories (including unique ones)
1444
- try {
1445
- const { readdir } = await import("node:fs/promises");
1446
- const entries = await readdir(__dirname, { withFileTypes: true });
1447
-
1448
- for (const entry of entries) {
1449
- if (entry.isDirectory() && entry.name.startsWith("test-content-generator")) {
1450
- const testDirToClean = path.join(__dirname, entry.name);
1451
- try {
1452
- await rm(testDirToClean, { recursive: true, force: true });
1453
- } catch {
1454
- // Try with system command as fallback
1455
- try {
1456
- const { exec } = await import("node:child_process");
1457
- const { promisify } = await import("node:util");
1458
- const execAsync = promisify(exec);
1459
- await execAsync(`rm -rf "${testDirToClean}"`);
1460
- } catch {
1461
- console.warn(`Warning: Could not fully clean up test directory: ${testDirToClean}`);
1462
- }
1463
- }
1464
- }
1465
- }
1466
- } catch (error) {
1467
- console.warn(`Warning: Could not clean up test directories: ${error.message}`);
1468
- }
1469
- });
1470
- });