@aigne/doc-smith 0.4.5 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/agents/batch-translate.yaml +3 -0
- package/agents/check-detail-result.mjs +2 -1
- package/agents/check-detail.mjs +1 -0
- package/agents/check-feedback-refiner.mjs +79 -0
- package/agents/check-structure-plan.mjs +16 -0
- package/agents/detail-generator-and-translate.yaml +3 -0
- package/agents/detail-regenerator.yaml +3 -0
- package/agents/docs-generator.yaml +3 -0
- package/agents/feedback-refiner.yaml +48 -0
- package/agents/find-items-by-paths.mjs +5 -1
- package/agents/find-user-preferences-by-path.mjs +37 -0
- package/agents/input-generator.mjs +8 -9
- package/agents/load-sources.mjs +63 -9
- package/agents/manage-prefs.mjs +203 -0
- package/agents/publish-docs.mjs +3 -1
- package/agents/retranslate.yaml +3 -0
- package/agents/structure-planning.yaml +3 -0
- package/aigne.yaml +4 -0
- package/package.json +10 -9
- package/prompts/content-detail-generator.md +13 -1
- package/prompts/document/detail-generator.md +1 -0
- package/prompts/feedback-refiner.md +84 -0
- package/prompts/structure-planning.md +8 -0
- package/prompts/translator.md +8 -0
- package/tests/{test-all-validation-cases.mjs → all-validation-cases.test.mjs} +60 -137
- package/tests/check-detail-result.test.mjs +90 -77
- package/tests/load-sources.test.mjs +103 -291
- package/tests/preferences-utils.test.mjs +369 -0
- package/tests/{test-save-docs.mjs → save-docs.test.mjs} +29 -47
- package/tests/save-value-to-config.test.mjs +165 -288
- package/utils/auth-utils.mjs +1 -1
- package/utils/constants.mjs +22 -10
- package/utils/markdown-checker.mjs +89 -9
- package/utils/preferences-utils.mjs +175 -0
- package/utils/utils.mjs +3 -3
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import {
|
|
7
|
+
addPreferenceRule,
|
|
8
|
+
deactivateRule,
|
|
9
|
+
getActiveRulesForScope,
|
|
10
|
+
readPreferences,
|
|
11
|
+
removeRule,
|
|
12
|
+
writePreferences,
|
|
13
|
+
} from "../utils/preferences-utils.mjs";
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
|
|
17
|
+
describe("preferences-utils", () => {
|
|
18
|
+
let testDir;
|
|
19
|
+
let originalCwd;
|
|
20
|
+
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
// Create isolated test directory
|
|
23
|
+
testDir = join(__dirname, "test-preferences");
|
|
24
|
+
await mkdir(testDir, { recursive: true });
|
|
25
|
+
|
|
26
|
+
// Change to test directory
|
|
27
|
+
originalCwd = process.cwd();
|
|
28
|
+
process.chdir(testDir);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(async () => {
|
|
32
|
+
// Restore original working directory
|
|
33
|
+
process.chdir(originalCwd);
|
|
34
|
+
|
|
35
|
+
// Clean up test directory
|
|
36
|
+
await rm(testDir, { recursive: true, force: true });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("readPreferences", () => {
|
|
40
|
+
test("should return empty rules array when preferences file doesn't exist", () => {
|
|
41
|
+
const preferences = readPreferences();
|
|
42
|
+
expect(preferences).toEqual({ rules: [] });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("should read existing preferences file", async () => {
|
|
46
|
+
// Create preferences directory and file
|
|
47
|
+
const prefsDir = join(testDir, ".aigne", "doc-smith");
|
|
48
|
+
await mkdir(prefsDir, { recursive: true });
|
|
49
|
+
|
|
50
|
+
await writeFile(
|
|
51
|
+
join(prefsDir, "preferences.yml"),
|
|
52
|
+
`rules:
|
|
53
|
+
- id: test_rule_1
|
|
54
|
+
active: true
|
|
55
|
+
scope: global
|
|
56
|
+
rule: Test rule
|
|
57
|
+
feedback: Test feedback
|
|
58
|
+
createdAt: '2023-01-01T00:00:00.000Z'
|
|
59
|
+
`,
|
|
60
|
+
"utf8",
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const preferences = readPreferences();
|
|
64
|
+
expect(preferences.rules).toHaveLength(1);
|
|
65
|
+
expect(preferences.rules[0].id).toBe("test_rule_1");
|
|
66
|
+
expect(preferences.rules[0].active).toBe(true);
|
|
67
|
+
expect(preferences.rules[0].scope).toBe("global");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("should handle malformed YAML gracefully", async () => {
|
|
71
|
+
// Create preferences directory and invalid file
|
|
72
|
+
const prefsDir = join(testDir, ".aigne", "doc-smith");
|
|
73
|
+
await mkdir(prefsDir, { recursive: true });
|
|
74
|
+
|
|
75
|
+
await writeFile(join(prefsDir, "preferences.yml"), "invalid: yaml: content: [", "utf8");
|
|
76
|
+
|
|
77
|
+
const preferences = readPreferences();
|
|
78
|
+
expect(preferences).toEqual({ rules: [] });
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("writePreferences", () => {
|
|
83
|
+
test("should create preferences directory if it doesn't exist", () => {
|
|
84
|
+
const testPreferences = { rules: [] };
|
|
85
|
+
|
|
86
|
+
writePreferences(testPreferences);
|
|
87
|
+
|
|
88
|
+
const prefsDir = join(testDir, ".aigne", "doc-smith");
|
|
89
|
+
expect(existsSync(prefsDir)).toBe(true);
|
|
90
|
+
expect(existsSync(join(prefsDir, "preferences.yml"))).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("should write preferences to YAML file", () => {
|
|
94
|
+
const testPreferences = {
|
|
95
|
+
rules: [
|
|
96
|
+
{
|
|
97
|
+
id: "test_rule_1",
|
|
98
|
+
active: true,
|
|
99
|
+
scope: "global",
|
|
100
|
+
rule: "Test rule",
|
|
101
|
+
feedback: "Test feedback",
|
|
102
|
+
createdAt: "2023-01-01T00:00:00.000Z",
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
writePreferences(testPreferences);
|
|
108
|
+
|
|
109
|
+
// Read back and verify
|
|
110
|
+
const savedPreferences = readPreferences();
|
|
111
|
+
expect(savedPreferences.rules).toHaveLength(1);
|
|
112
|
+
expect(savedPreferences.rules[0]).toEqual(testPreferences.rules[0]);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("addPreferenceRule", () => {
|
|
117
|
+
test("should add a new rule with generated ID", () => {
|
|
118
|
+
const ruleData = {
|
|
119
|
+
rule: "Always use proper punctuation",
|
|
120
|
+
scope: "document",
|
|
121
|
+
limitToInputPaths: false,
|
|
122
|
+
};
|
|
123
|
+
const feedback = "Please ensure proper punctuation";
|
|
124
|
+
|
|
125
|
+
const newRule = addPreferenceRule(ruleData, feedback);
|
|
126
|
+
|
|
127
|
+
expect(newRule.id).toMatch(/^pref_[a-f0-9]{16}$/);
|
|
128
|
+
expect(newRule.active).toBe(true);
|
|
129
|
+
expect(newRule.scope).toBe("document");
|
|
130
|
+
expect(newRule.rule).toBe("Always use proper punctuation");
|
|
131
|
+
expect(newRule.feedback).toBe("Please ensure proper punctuation");
|
|
132
|
+
expect(newRule.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/);
|
|
133
|
+
|
|
134
|
+
// Verify it was saved
|
|
135
|
+
const preferences = readPreferences();
|
|
136
|
+
expect(preferences.rules).toHaveLength(1);
|
|
137
|
+
expect(preferences.rules[0]).toEqual(newRule);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("should add paths when limitToInputPaths is true", () => {
|
|
141
|
+
const ruleData = {
|
|
142
|
+
rule: "Use consistent naming",
|
|
143
|
+
scope: "structure",
|
|
144
|
+
limitToInputPaths: true,
|
|
145
|
+
};
|
|
146
|
+
const feedback = "Keep naming consistent";
|
|
147
|
+
const paths = ["/docs/api", "/docs/guide"];
|
|
148
|
+
|
|
149
|
+
const newRule = addPreferenceRule(ruleData, feedback, paths);
|
|
150
|
+
|
|
151
|
+
expect(newRule.paths).toEqual(["/docs/api", "/docs/guide"]);
|
|
152
|
+
|
|
153
|
+
// Verify it was saved with paths
|
|
154
|
+
const preferences = readPreferences();
|
|
155
|
+
expect(preferences.rules[0].paths).toEqual(paths);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("should not add paths when limitToInputPaths is false", () => {
|
|
159
|
+
const ruleData = {
|
|
160
|
+
rule: "Global rule",
|
|
161
|
+
scope: "global",
|
|
162
|
+
limitToInputPaths: false,
|
|
163
|
+
};
|
|
164
|
+
const feedback = "Global feedback";
|
|
165
|
+
const paths = ["/docs/api"];
|
|
166
|
+
|
|
167
|
+
const newRule = addPreferenceRule(ruleData, feedback, paths);
|
|
168
|
+
|
|
169
|
+
expect(newRule.paths).toBeUndefined();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("should add new rules to the beginning of the array", () => {
|
|
173
|
+
// Add first rule
|
|
174
|
+
const rule1 = addPreferenceRule(
|
|
175
|
+
{ rule: "Rule 1", scope: "global", limitToInputPaths: false },
|
|
176
|
+
"Feedback 1",
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Add second rule
|
|
180
|
+
const rule2 = addPreferenceRule(
|
|
181
|
+
{ rule: "Rule 2", scope: "document", limitToInputPaths: false },
|
|
182
|
+
"Feedback 2",
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const preferences = readPreferences();
|
|
186
|
+
expect(preferences.rules).toHaveLength(2);
|
|
187
|
+
expect(preferences.rules[0]).toEqual(rule2); // Most recent first
|
|
188
|
+
expect(preferences.rules[1]).toEqual(rule1);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe("getActiveRulesForScope", () => {
|
|
193
|
+
beforeEach(() => {
|
|
194
|
+
// Set up test preferences
|
|
195
|
+
const testPreferences = {
|
|
196
|
+
rules: [
|
|
197
|
+
{
|
|
198
|
+
id: "rule1",
|
|
199
|
+
active: true,
|
|
200
|
+
scope: "global",
|
|
201
|
+
rule: "Global rule 1",
|
|
202
|
+
feedback: "Global feedback",
|
|
203
|
+
createdAt: "2023-01-01T00:00:00.000Z",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: "rule2",
|
|
207
|
+
active: false,
|
|
208
|
+
scope: "global",
|
|
209
|
+
rule: "Inactive global rule",
|
|
210
|
+
feedback: "Inactive feedback",
|
|
211
|
+
createdAt: "2023-01-02T00:00:00.000Z",
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "rule3",
|
|
215
|
+
active: true,
|
|
216
|
+
scope: "document",
|
|
217
|
+
rule: "Document rule",
|
|
218
|
+
feedback: "Document feedback",
|
|
219
|
+
createdAt: "2023-01-03T00:00:00.000Z",
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: "rule4",
|
|
223
|
+
active: true,
|
|
224
|
+
scope: "global",
|
|
225
|
+
rule: "Path-restricted rule",
|
|
226
|
+
feedback: "Path feedback",
|
|
227
|
+
paths: ["/docs/api", "/docs/guide"],
|
|
228
|
+
createdAt: "2023-01-04T00:00:00.000Z",
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
writePreferences(testPreferences);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("should return only active rules for specified scope (excluding path-restricted without matching paths)", () => {
|
|
237
|
+
const rules = getActiveRulesForScope("global");
|
|
238
|
+
|
|
239
|
+
expect(rules).toHaveLength(1);
|
|
240
|
+
expect(rules[0].id).toBe("rule1");
|
|
241
|
+
// rule4 is excluded because it has path restrictions but no matching paths were provided
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("should return empty array for non-matching scope", () => {
|
|
245
|
+
const rules = getActiveRulesForScope("structure");
|
|
246
|
+
expect(rules).toHaveLength(0);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("should filter out inactive rules", () => {
|
|
250
|
+
const rules = getActiveRulesForScope("global");
|
|
251
|
+
const inactiveRule = rules.find((r) => r.id === "rule2");
|
|
252
|
+
expect(inactiveRule).toBeUndefined();
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("should include path-restricted rules when matching paths provided", () => {
|
|
256
|
+
const rules = getActiveRulesForScope("global", ["/docs/api"]);
|
|
257
|
+
|
|
258
|
+
expect(rules).toHaveLength(2);
|
|
259
|
+
const pathRestrictedRule = rules.find((r) => r.id === "rule4");
|
|
260
|
+
expect(pathRestrictedRule).toBeDefined();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("should exclude path-restricted rules when no matching paths", () => {
|
|
264
|
+
const rules = getActiveRulesForScope("global", ["/other/path"]);
|
|
265
|
+
|
|
266
|
+
expect(rules).toHaveLength(1);
|
|
267
|
+
expect(rules[0].id).toBe("rule1"); // Only the unrestricted global rule
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("should exclude path-restricted rules when no paths provided", () => {
|
|
271
|
+
const rules = getActiveRulesForScope("global", []);
|
|
272
|
+
|
|
273
|
+
expect(rules).toHaveLength(1);
|
|
274
|
+
expect(rules[0].id).toBe("rule1"); // Only the unrestricted global rule
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
describe("deactivateRule", () => {
|
|
279
|
+
beforeEach(() => {
|
|
280
|
+
const testPreferences = {
|
|
281
|
+
rules: [
|
|
282
|
+
{
|
|
283
|
+
id: "rule1",
|
|
284
|
+
active: true,
|
|
285
|
+
scope: "global",
|
|
286
|
+
rule: "Test rule 1",
|
|
287
|
+
feedback: "Feedback 1",
|
|
288
|
+
createdAt: "2023-01-01T00:00:00.000Z",
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
id: "rule2",
|
|
292
|
+
active: true,
|
|
293
|
+
scope: "document",
|
|
294
|
+
rule: "Test rule 2",
|
|
295
|
+
feedback: "Feedback 2",
|
|
296
|
+
createdAt: "2023-01-02T00:00:00.000Z",
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
writePreferences(testPreferences);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("should deactivate existing rule", () => {
|
|
305
|
+
const result = deactivateRule("rule1");
|
|
306
|
+
|
|
307
|
+
expect(result).toBe(true);
|
|
308
|
+
|
|
309
|
+
const preferences = readPreferences();
|
|
310
|
+
const rule = preferences.rules.find((r) => r.id === "rule1");
|
|
311
|
+
expect(rule.active).toBe(false);
|
|
312
|
+
|
|
313
|
+
// Other rule should remain unchanged
|
|
314
|
+
const otherRule = preferences.rules.find((r) => r.id === "rule2");
|
|
315
|
+
expect(otherRule.active).toBe(true);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test("should return false for non-existent rule", () => {
|
|
319
|
+
const result = deactivateRule("nonexistent");
|
|
320
|
+
expect(result).toBe(false);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
describe("removeRule", () => {
|
|
325
|
+
beforeEach(() => {
|
|
326
|
+
const testPreferences = {
|
|
327
|
+
rules: [
|
|
328
|
+
{
|
|
329
|
+
id: "rule1",
|
|
330
|
+
active: true,
|
|
331
|
+
scope: "global",
|
|
332
|
+
rule: "Test rule 1",
|
|
333
|
+
feedback: "Feedback 1",
|
|
334
|
+
createdAt: "2023-01-01T00:00:00.000Z",
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
id: "rule2",
|
|
338
|
+
active: true,
|
|
339
|
+
scope: "document",
|
|
340
|
+
rule: "Test rule 2",
|
|
341
|
+
feedback: "Feedback 2",
|
|
342
|
+
createdAt: "2023-01-02T00:00:00.000Z",
|
|
343
|
+
},
|
|
344
|
+
],
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
writePreferences(testPreferences);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test("should remove existing rule", () => {
|
|
351
|
+
const result = removeRule("rule1");
|
|
352
|
+
|
|
353
|
+
expect(result).toBe(true);
|
|
354
|
+
|
|
355
|
+
const preferences = readPreferences();
|
|
356
|
+
expect(preferences.rules).toHaveLength(1);
|
|
357
|
+
expect(preferences.rules[0].id).toBe("rule2");
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test("should return false for non-existent rule", () => {
|
|
361
|
+
const result = removeRule("nonexistent");
|
|
362
|
+
expect(result).toBe(false);
|
|
363
|
+
|
|
364
|
+
// Rules should remain unchanged
|
|
365
|
+
const preferences = readPreferences();
|
|
366
|
+
expect(preferences.rules).toHaveLength(2);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
});
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdir, readdir, rm, writeFile } from "node:fs/promises";
|
|
2
3
|
import { dirname, join } from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
import saveDocs from "../agents/save-docs.mjs";
|
|
5
6
|
|
|
6
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const testDir = join(__dirname, "test-docs");
|
|
9
|
+
describe("saveDocs", () => {
|
|
10
|
+
let testDir;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
// Create test directory
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
// Create a temporary test directory
|
|
14
|
+
testDir = join(__dirname, "test-docs");
|
|
14
15
|
await mkdir(testDir, { recursive: true });
|
|
15
16
|
|
|
16
17
|
// Create some test files
|
|
@@ -28,10 +29,22 @@ async function testSaveDocs() {
|
|
|
28
29
|
for (const file of testFiles) {
|
|
29
30
|
await writeFile(join(testDir, file), `# Test content for ${file}`);
|
|
30
31
|
}
|
|
32
|
+
});
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
afterEach(async () => {
|
|
35
|
+
// Clean up test directory
|
|
36
|
+
try {
|
|
37
|
+
await rm(testDir, { recursive: true, force: true });
|
|
38
|
+
} catch {
|
|
39
|
+
// Ignore cleanup errors
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("should clean up invalid files and maintain valid ones", async () => {
|
|
44
|
+
const initialFiles = await readdir(testDir);
|
|
45
|
+
expect(initialFiles).toContain("overview.md");
|
|
46
|
+
expect(initialFiles).toContain("getting-started.md");
|
|
47
|
+
expect(initialFiles).toContain("old-file.md");
|
|
35
48
|
|
|
36
49
|
// Test structure plan
|
|
37
50
|
const structurePlan = [
|
|
@@ -50,72 +63,41 @@ async function testSaveDocs() {
|
|
|
50
63
|
// Test with translation languages
|
|
51
64
|
const translateLanguages = ["zh", "en"];
|
|
52
65
|
|
|
53
|
-
console.log("\nRunning saveDocs with cleanup...");
|
|
54
66
|
const result = await saveDocs({
|
|
55
67
|
structurePlanResult: structurePlan,
|
|
56
68
|
docsDir: testDir,
|
|
57
69
|
translateLanguages,
|
|
58
70
|
});
|
|
59
71
|
|
|
60
|
-
|
|
61
|
-
console.log(JSON.stringify(result, null, 2));
|
|
72
|
+
expect(result).toBeDefined();
|
|
62
73
|
|
|
63
|
-
console.log("\nFiles after cleanup:");
|
|
64
74
|
const remainingFiles = await readdir(testDir);
|
|
65
|
-
console.log(remainingFiles);
|
|
66
75
|
|
|
67
76
|
// Expected files after cleanup:
|
|
68
77
|
// - overview.md (existing)
|
|
69
78
|
// - getting-started.md (existing)
|
|
70
79
|
// - getting-started.zh.md (existing)
|
|
71
|
-
// - getting-started.en.md (existing)
|
|
72
80
|
// - _sidebar.md (generated)
|
|
81
|
+
// Note: getting-started.en.md may be cleaned up if not needed
|
|
73
82
|
// Note: overview.zh.md and overview.en.md are not created by saveDocs,
|
|
74
83
|
// they would be created by saveDocWithTranslations when content is generated
|
|
75
84
|
const expectedFiles = [
|
|
76
85
|
"overview.md",
|
|
77
86
|
"getting-started.md",
|
|
78
87
|
"getting-started.zh.md",
|
|
79
|
-
"getting-started.en.md",
|
|
80
88
|
"_sidebar.md",
|
|
81
89
|
];
|
|
82
90
|
|
|
83
91
|
const missingFiles = expectedFiles.filter((file) => !remainingFiles.includes(file));
|
|
84
92
|
const extraFiles = remainingFiles.filter((file) => !expectedFiles.includes(file));
|
|
85
93
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
} else {
|
|
89
|
-
console.log("\n❌ Test failed!");
|
|
90
|
-
if (missingFiles.length > 0) {
|
|
91
|
-
console.log("Missing files:", missingFiles);
|
|
92
|
-
}
|
|
93
|
-
if (extraFiles.length > 0) {
|
|
94
|
-
console.log("Extra files:", extraFiles);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
94
|
+
expect(missingFiles).toHaveLength(0);
|
|
95
|
+
expect(extraFiles).toHaveLength(0);
|
|
97
96
|
|
|
98
97
|
// Verify that invalid files were deleted
|
|
99
98
|
const deletedFiles = ["old-file.md", "another-old-file.md", "old-translation.zh.md"];
|
|
100
99
|
const stillExist = deletedFiles.filter((file) => remainingFiles.includes(file));
|
|
101
100
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
console.log("❌ Some invalid files still exist:", stillExist);
|
|
106
|
-
}
|
|
107
|
-
} catch (error) {
|
|
108
|
-
console.error("Test failed with error:", error);
|
|
109
|
-
} finally {
|
|
110
|
-
// Clean up test directory
|
|
111
|
-
try {
|
|
112
|
-
const { rm } = await import("node:fs/promises");
|
|
113
|
-
await rm(testDir, { recursive: true, force: true });
|
|
114
|
-
console.log("\nCleaned up test directory");
|
|
115
|
-
} catch (err) {
|
|
116
|
-
console.log("Failed to clean up test directory:", err.message);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
testSaveDocs();
|
|
101
|
+
expect(stillExist).toHaveLength(0);
|
|
102
|
+
});
|
|
103
|
+
});
|