@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.
- package/CHANGELOG.md +13 -0
- package/agents/publish/index.yaml +4 -0
- package/agents/publish/publish-docs.mjs +77 -5
- package/agents/publish/translate-meta.mjs +103 -0
- package/agents/update/generate-document.yaml +30 -28
- package/agents/update/update-document-detail.yaml +3 -1
- package/agents/utils/update-branding.mjs +69 -0
- package/package.json +16 -2
- package/prompts/common/document/role-and-personality.md +3 -1
- package/prompts/detail/d2-diagram/guide.md +7 -1
- package/prompts/detail/d2-diagram/user-prompt.md +3 -0
- package/prompts/detail/generate/system-prompt.md +6 -7
- package/prompts/detail/generate/user-prompt.md +12 -3
- package/prompts/detail/update/user-prompt.md +0 -2
- package/prompts/structure/update/user-prompt.md +0 -4
- package/utils/file-utils.mjs +69 -24
- package/utils/markdown-checker.mjs +0 -20
- package/utils/request.mjs +7 -0
- package/utils/upload-files.mjs +231 -0
- package/utils/utils.mjs +11 -1
- package/.aigne/doc-smith/config.yaml +0 -77
- package/.aigne/doc-smith/history.yaml +0 -37
- package/.aigne/doc-smith/media-description.yaml +0 -91
- package/.aigne/doc-smith/output/structure-plan.json +0 -162
- package/.aigne/doc-smith/preferences.yml +0 -97
- package/.aigne/doc-smith/upload-cache.yaml +0 -1830
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
- package/.github/workflows/ci.yml +0 -54
- package/.github/workflows/create-release-pr.yaml +0 -21
- package/.github/workflows/publish-docs.yml +0 -65
- package/.github/workflows/release.yml +0 -49
- package/.github/workflows/reviewer.yml +0 -54
- package/.release-please-manifest.json +0 -3
- package/RELEASE.md +0 -9
- package/assets/screenshots/doc-complete-setup.png +0 -0
- package/assets/screenshots/doc-generate-docs.png +0 -0
- package/assets/screenshots/doc-generate.png +0 -0
- package/assets/screenshots/doc-generated-successfully.png +0 -0
- package/assets/screenshots/doc-publish.png +0 -0
- package/assets/screenshots/doc-regenerate.png +0 -0
- package/assets/screenshots/doc-translate-langs.png +0 -0
- package/assets/screenshots/doc-translate.png +0 -0
- package/assets/screenshots/doc-update.png +0 -0
- package/biome.json +0 -73
- package/codecov.yml +0 -15
- package/docs/_sidebar.md +0 -15
- package/docs/configuration-initial-setup.ja.md +0 -179
- package/docs/configuration-initial-setup.md +0 -198
- package/docs/configuration-initial-setup.zh-TW.md +0 -179
- package/docs/configuration-initial-setup.zh.md +0 -179
- package/docs/configuration-managing-preferences.ja.md +0 -100
- package/docs/configuration-managing-preferences.md +0 -100
- package/docs/configuration-managing-preferences.zh-TW.md +0 -100
- package/docs/configuration-managing-preferences.zh.md +0 -100
- package/docs/configuration.ja.md +0 -69
- package/docs/configuration.md +0 -69
- package/docs/configuration.zh-TW.md +0 -69
- package/docs/configuration.zh.md +0 -69
- package/docs/getting-started.ja.md +0 -107
- package/docs/getting-started.md +0 -107
- package/docs/getting-started.zh-TW.md +0 -107
- package/docs/getting-started.zh.md +0 -107
- package/docs/guides-cleaning-up.ja.md +0 -51
- package/docs/guides-cleaning-up.md +0 -52
- package/docs/guides-cleaning-up.zh-TW.md +0 -51
- package/docs/guides-cleaning-up.zh.md +0 -51
- package/docs/guides-evaluating-documents.ja.md +0 -66
- package/docs/guides-evaluating-documents.md +0 -107
- package/docs/guides-evaluating-documents.zh-TW.md +0 -66
- package/docs/guides-evaluating-documents.zh.md +0 -66
- package/docs/guides-generating-documentation.ja.md +0 -151
- package/docs/guides-generating-documentation.md +0 -89
- package/docs/guides-generating-documentation.zh-TW.md +0 -151
- package/docs/guides-generating-documentation.zh.md +0 -151
- package/docs/guides-interactive-chat.ja.md +0 -85
- package/docs/guides-interactive-chat.md +0 -93
- package/docs/guides-interactive-chat.zh-TW.md +0 -85
- package/docs/guides-interactive-chat.zh.md +0 -85
- package/docs/guides-managing-history.ja.md +0 -48
- package/docs/guides-managing-history.md +0 -53
- package/docs/guides-managing-history.zh-TW.md +0 -48
- package/docs/guides-managing-history.zh.md +0 -48
- package/docs/guides-publishing-your-docs.ja.md +0 -78
- package/docs/guides-publishing-your-docs.md +0 -83
- package/docs/guides-publishing-your-docs.zh-TW.md +0 -78
- package/docs/guides-publishing-your-docs.zh.md +0 -78
- package/docs/guides-translating-documentation.ja.md +0 -95
- package/docs/guides-translating-documentation.md +0 -100
- package/docs/guides-translating-documentation.zh-TW.md +0 -95
- package/docs/guides-translating-documentation.zh.md +0 -95
- package/docs/guides-updating-documentation.ja.md +0 -77
- package/docs/guides-updating-documentation.md +0 -79
- package/docs/guides-updating-documentation.zh-TW.md +0 -77
- package/docs/guides-updating-documentation.zh.md +0 -77
- package/docs/guides.ja.md +0 -32
- package/docs/guides.md +0 -32
- package/docs/guides.zh-TW.md +0 -32
- package/docs/guides.zh.md +0 -32
- package/docs/overview.ja.md +0 -61
- package/docs/overview.md +0 -61
- package/docs/overview.zh-TW.md +0 -61
- package/docs/overview.zh.md +0 -61
- package/docs/release-notes.ja.md +0 -255
- package/docs/release-notes.md +0 -288
- package/docs/release-notes.zh-TW.md +0 -255
- package/docs/release-notes.zh.md +0 -255
- package/prompts/common/afs/afs-tools-usage.md +0 -5
- package/prompts/common/afs/use-afs-instruction.md +0 -1
- package/release-please-config.json +0 -14
- package/tests/agents/chat/chat.test.mjs +0 -46
- package/tests/agents/clear/choose-contents.test.mjs +0 -284
- package/tests/agents/clear/clear-auth-tokens.test.mjs +0 -268
- package/tests/agents/clear/clear-document-config.test.mjs +0 -167
- package/tests/agents/clear/clear-document-structure.test.mjs +0 -380
- package/tests/agents/clear/clear-generated-docs.test.mjs +0 -222
- package/tests/agents/evaluate/code-snippet.test.mjs +0 -163
- package/tests/agents/evaluate/fixtures/api-services.md +0 -87
- package/tests/agents/evaluate/fixtures/js-sdk.md +0 -94
- package/tests/agents/evaluate/generate-report.test.mjs +0 -312
- package/tests/agents/generate/check-document-structure.test.mjs +0 -45
- package/tests/agents/generate/check-need-generate-structure.test.mjs +0 -279
- package/tests/agents/generate/document-structure-tools/add-document.test.mjs +0 -449
- package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +0 -410
- package/tests/agents/generate/document-structure-tools/generate-sub-structure.test.mjs +0 -277
- package/tests/agents/generate/document-structure-tools/move-document.test.mjs +0 -476
- package/tests/agents/generate/document-structure-tools/update-document.test.mjs +0 -548
- package/tests/agents/generate/generate-structure.test.mjs +0 -45
- package/tests/agents/generate/user-review-document-structure.test.mjs +0 -319
- package/tests/agents/history/view.test.mjs +0 -97
- package/tests/agents/init/init.test.mjs +0 -1657
- package/tests/agents/prefs/prefs.test.mjs +0 -431
- package/tests/agents/publish/publish-docs.test.mjs +0 -787
- package/tests/agents/translate/choose-language.test.mjs +0 -311
- package/tests/agents/translate/translate-document.test.mjs +0 -51
- package/tests/agents/update/check-document.test.mjs +0 -463
- package/tests/agents/update/check-update-is-single.test.mjs +0 -300
- package/tests/agents/update/document-tools/update-document-content.test.mjs +0 -329
- package/tests/agents/update/generate-document.test.mjs +0 -51
- package/tests/agents/update/save-and-translate-document.test.mjs +0 -369
- package/tests/agents/update/user-review-document.test.mjs +0 -582
- package/tests/agents/utils/action-success.test.mjs +0 -54
- package/tests/agents/utils/check-detail-result.test.mjs +0 -743
- package/tests/agents/utils/check-feedback-refiner.test.mjs +0 -478
- package/tests/agents/utils/choose-docs.test.mjs +0 -406
- package/tests/agents/utils/exit.test.mjs +0 -70
- package/tests/agents/utils/feedback-refiner.test.mjs +0 -51
- package/tests/agents/utils/find-item-by-path.test.mjs +0 -517
- package/tests/agents/utils/find-user-preferences-by-path.test.mjs +0 -382
- package/tests/agents/utils/format-document-structure.test.mjs +0 -364
- package/tests/agents/utils/fs.test.mjs +0 -267
- package/tests/agents/utils/load-sources.test.mjs +0 -1470
- package/tests/agents/utils/save-docs.test.mjs +0 -109
- package/tests/agents/utils/save-output.test.mjs +0 -315
- package/tests/agents/utils/save-single-doc.test.mjs +0 -364
- package/tests/agents/utils/transform-detail-datasources.test.mjs +0 -320
- package/tests/utils/auth-utils.test.mjs +0 -596
- package/tests/utils/blocklet.test.mjs +0 -336
- package/tests/utils/conflict-detector.test.mjs +0 -355
- package/tests/utils/constants.test.mjs +0 -295
- package/tests/utils/d2-utils.test.mjs +0 -437
- package/tests/utils/deploy.test.mjs +0 -399
- package/tests/utils/docs-finder-utils.test.mjs +0 -650
- package/tests/utils/file-utils.test.mjs +0 -521
- package/tests/utils/history-utils.test.mjs +0 -206
- package/tests/utils/kroki-utils.test.mjs +0 -646
- package/tests/utils/linter/fixtures/css/keyword-error.css +0 -1
- package/tests/utils/linter/fixtures/css/missing-semicolon.css +0 -1
- package/tests/utils/linter/fixtures/css/syntax-error.css +0 -1
- package/tests/utils/linter/fixtures/css/undeclare-variable.css +0 -1
- package/tests/utils/linter/fixtures/css/unused-variable.css +0 -2
- package/tests/utils/linter/fixtures/css/valid-code.css +0 -1
- package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +0 -1
- package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +0 -2
- package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +0 -2
- package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +0 -1
- package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +0 -1
- package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +0 -2
- package/tests/utils/linter/fixtures/go/keyword-error.go +0 -5
- package/tests/utils/linter/fixtures/go/missing-semicolon.go +0 -5
- package/tests/utils/linter/fixtures/go/syntax-error.go +0 -6
- package/tests/utils/linter/fixtures/go/undeclare-variable.go +0 -5
- package/tests/utils/linter/fixtures/go/unused-variable.go +0 -5
- package/tests/utils/linter/fixtures/go/valid-code.go +0 -7
- package/tests/utils/linter/fixtures/js/keyword-error.js +0 -3
- package/tests/utils/linter/fixtures/js/missing-semicolon.js +0 -6
- package/tests/utils/linter/fixtures/js/syntax-error.js +0 -4
- package/tests/utils/linter/fixtures/js/undeclare-variable.js +0 -3
- package/tests/utils/linter/fixtures/js/unused-variable.js +0 -7
- package/tests/utils/linter/fixtures/js/valid-code.js +0 -15
- package/tests/utils/linter/fixtures/json/keyword-error.json +0 -1
- package/tests/utils/linter/fixtures/json/missing-semicolon.json +0 -1
- package/tests/utils/linter/fixtures/json/syntax-error.json +0 -1
- package/tests/utils/linter/fixtures/json/undeclare-variable.json +0 -1
- package/tests/utils/linter/fixtures/json/unused-variable.json +0 -1
- package/tests/utils/linter/fixtures/json/valid-code.json +0 -1
- package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +0 -5
- package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +0 -5
- package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +0 -5
- package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +0 -5
- package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +0 -4
- package/tests/utils/linter/fixtures/jsx/valid-code.jsx +0 -5
- package/tests/utils/linter/fixtures/python/keyword-error.py +0 -3
- package/tests/utils/linter/fixtures/python/missing-semicolon.py +0 -2
- package/tests/utils/linter/fixtures/python/syntax-error.py +0 -3
- package/tests/utils/linter/fixtures/python/undeclare-variable.py +0 -3
- package/tests/utils/linter/fixtures/python/unused-variable.py +0 -6
- package/tests/utils/linter/fixtures/python/valid-code.py +0 -12
- package/tests/utils/linter/fixtures/ruby/keyword-error.rb +0 -2
- package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +0 -1
- package/tests/utils/linter/fixtures/ruby/syntax-error.rb +0 -2
- package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +0 -1
- package/tests/utils/linter/fixtures/ruby/unused-variable.rb +0 -2
- package/tests/utils/linter/fixtures/ruby/valid-code.rb +0 -1
- package/tests/utils/linter/fixtures/sass/keyword-error.sass +0 -2
- package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +0 -3
- package/tests/utils/linter/fixtures/sass/syntax-error.sass +0 -3
- package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +0 -2
- package/tests/utils/linter/fixtures/sass/unused-variable.sass +0 -4
- package/tests/utils/linter/fixtures/sass/valid-code.sass +0 -2
- package/tests/utils/linter/fixtures/scss/keyword-error.scss +0 -1
- package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +0 -1
- package/tests/utils/linter/fixtures/scss/syntax-error.scss +0 -1
- package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +0 -1
- package/tests/utils/linter/fixtures/scss/unused-variable.scss +0 -2
- package/tests/utils/linter/fixtures/scss/valid-code.scss +0 -1
- package/tests/utils/linter/fixtures/shell/keyword-error.sh +0 -5
- package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +0 -3
- package/tests/utils/linter/fixtures/shell/syntax-error.sh +0 -4
- package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +0 -3
- package/tests/utils/linter/fixtures/shell/unused-variable.sh +0 -4
- package/tests/utils/linter/fixtures/shell/valid-code.sh +0 -3
- package/tests/utils/linter/fixtures/ts/keyword-error.ts +0 -1
- package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +0 -1
- package/tests/utils/linter/fixtures/ts/syntax-error.ts +0 -1
- package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +0 -1
- package/tests/utils/linter/fixtures/ts/unused-variable.ts +0 -3
- package/tests/utils/linter/fixtures/ts/valid-code.ts +0 -3
- package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +0 -5
- package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +0 -5
- package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +0 -5
- package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +0 -6
- package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +0 -6
- package/tests/utils/linter/fixtures/tsx/valid-code.tsx +0 -5
- package/tests/utils/linter/fixtures/vue/keyword-error.vue +0 -6
- package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +0 -6
- package/tests/utils/linter/fixtures/vue/syntax-error.vue +0 -6
- package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +0 -6
- package/tests/utils/linter/fixtures/vue/unused-variable.vue +0 -7
- package/tests/utils/linter/fixtures/vue/valid-code.vue +0 -6
- package/tests/utils/linter/fixtures/yaml/keyword-error.yml +0 -1
- package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +0 -2
- package/tests/utils/linter/fixtures/yaml/syntax-error.yml +0 -1
- package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +0 -1
- package/tests/utils/linter/fixtures/yaml/unused-variable.yml +0 -2
- package/tests/utils/linter/fixtures/yaml/valid-code.yml +0 -3
- package/tests/utils/linter/index.test.mjs +0 -440
- package/tests/utils/linter/scan-results.mjs +0 -42
- package/tests/utils/load-config.test.mjs +0 -141
- package/tests/utils/markdown/index.test.mjs +0 -478
- package/tests/utils/mermaid-validator.test.mjs +0 -541
- package/tests/utils/mock-chat-model.mjs +0 -12
- package/tests/utils/preferences-utils.test.mjs +0 -465
- package/tests/utils/save-value-to-config.test.mjs +0 -483
- 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
|
-
});
|