-
Notifications
You must be signed in to change notification settings - Fork 314
Expand file tree
/
Copy pathDocsChecker.test.ts
More file actions
196 lines (168 loc) · 6.67 KB
/
DocsChecker.test.ts
File metadata and controls
196 lines (168 loc) · 6.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import { AbsoluteFilePath } from "@fern-api/fs-utils";
import { NOOP_LOGGER } from "@fern-api/logger";
import { randomUUID } from "crypto";
import { mkdir, rm, writeFile } from "fs/promises";
import { tmpdir } from "os";
import { join } from "path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { loadFernYml } from "../config/fern-yml/loadFernYml.js";
import { DocsChecker } from "../docs/checker/DocsChecker.js";
import { WorkspaceLoader } from "../workspace/WorkspaceLoader.js";
import { createTestContext } from "./utils/createTestContext.js";
import { loadWorkspace } from "./utils/loadWorkspace.js";
describe("DocsChecker", () => {
let testDir: AbsoluteFilePath;
beforeEach(async () => {
testDir = AbsoluteFilePath.of(join(tmpdir(), `fern-docs-checker-test-${randomUUID()}`));
await mkdir(testDir, { recursive: true });
});
afterEach(async () => {
await rm(testDir, { recursive: true, force: true });
});
describe("workspace without docs", () => {
it("returns empty result when no docs configured", async () => {
const workspace = await loadWorkspace("simple-api");
const cwd = AbsoluteFilePath.of(join(__dirname, "fixtures/simple-api"));
const checker = new DocsChecker({
context: await createTestContext({ cwd })
});
const result = await checker.check({ workspace });
expect(result.violations).toHaveLength(0);
expect(result.hasErrors).toBe(false);
expect(result.hasWarnings).toBe(false);
expect(result.errorCount).toBe(0);
expect(result.warningCount).toBe(0);
expect(result.elapsedMillis).toBe(0);
});
});
describe("workspace with docs", () => {
it("returns result for valid docs configuration", async () => {
await writeDocsWorkspace(testDir);
const workspace = await loadTempWorkspace(testDir);
const checker = new DocsChecker({
context: await createTestContext({ cwd: testDir })
});
const result = await checker.check({ workspace });
// Docs check should return a well-formed result.
expect(result).toHaveProperty("violations");
expect(result).toHaveProperty("hasErrors");
expect(result).toHaveProperty("hasWarnings");
expect(result).toHaveProperty("errorCount");
expect(result).toHaveProperty("warningCount");
expect(result).toHaveProperty("elapsedMillis");
expect(result.elapsedMillis).toBeGreaterThanOrEqual(0);
});
it("includes elapsed time in result", async () => {
await writeDocsWorkspace(testDir);
const workspace = await loadTempWorkspace(testDir);
const checker = new DocsChecker({
context: await createTestContext({ cwd: testDir })
});
const result = await checker.check({ workspace });
expect(result.elapsedMillis).toBeGreaterThanOrEqual(0);
});
});
describe("strict mode", () => {
it("includes warnings in violations when strict is true", async () => {
await writeDocsWorkspace(testDir);
const workspace = await loadTempWorkspace(testDir);
const checker = new DocsChecker({
context: await createTestContext({ cwd: testDir })
});
const resultStrict = await checker.check({ workspace, strict: true });
const resultNormal = await checker.check({ workspace, strict: false });
// In strict mode, warnings are included in violations.
// In normal mode, only errors are included.
// The warning count should be the same regardless of strict mode.
expect(resultStrict.warningCount).toBe(resultNormal.warningCount);
// Non-strict should filter out warnings from violations list.
const normalWarnings = resultNormal.violations.filter((v) => v.severity === "warning");
expect(normalWarnings).toHaveLength(0);
});
});
describe("resolved violations shape", () => {
it("violations have expected fields", async () => {
await mkdir(join(testDir, "pages"), { recursive: true });
await writeFile(
join(testDir, "pages", "test.mdx"),
`---
title: Test Page
---
Check out [broken link](/does-not-exist).
`
);
await writeFile(
join(testDir, "fern.yml"),
`
edition: 2026-01-01
org: acme
api:
specs:
- openapi: openapi.yml
docs:
instances:
- url: acme.docs.buildwithfern.com
navigation:
- page: Test Page
path: ./pages/test.mdx
`
);
await writeMinimalOpenApi(testDir);
const workspace = await loadTempWorkspace(testDir);
const checker = new DocsChecker({
context: await createTestContext({ cwd: testDir })
});
const result = await checker.check({ workspace, strict: true });
// If there are violations, they should have the expected shape.
for (const violation of result.violations) {
expect(violation.severity).toBeDefined();
expect(violation.message).toBeDefined();
expect(typeof violation.displayRelativeFilepath).toBe("string");
expect(typeof violation.line).toBe("number");
expect(typeof violation.column).toBe("number");
}
});
});
});
async function loadTempWorkspace(cwd: AbsoluteFilePath): Promise<import("../workspace/Workspace.js").Workspace> {
const fernYml = await loadFernYml({ cwd });
const loader = new WorkspaceLoader({ cwd, logger: NOOP_LOGGER });
const result = await loader.load({ fernYml });
if (!result.success) {
throw new Error(`Failed to load workspace: ${JSON.stringify(result.issues)}`);
}
return result.workspace;
}
async function writeDocsWorkspace(dir: AbsoluteFilePath): Promise<void> {
await mkdir(join(dir, "pages"), { recursive: true });
await writeFile(join(dir, "pages", "welcome.mdx"), "# Welcome\n");
await writeFile(
join(dir, "fern.yml"),
`
edition: 2026-01-01
org: acme
api:
specs:
- openapi: openapi.yml
docs:
instances:
- url: acme.docs.buildwithfern.com
navigation:
- page: Welcome
path: ./pages/welcome.mdx
`
);
await writeMinimalOpenApi(dir);
}
async function writeMinimalOpenApi(dir: AbsoluteFilePath): Promise<void> {
await writeFile(
join(dir, "openapi.yml"),
`
openapi: 3.0.0
info:
title: Test
version: 1.0.0
paths: {}
`
);
}