Skip to content

Commit e27fb0c

Browse files
authored
test: create metadata parse tests (#619)
1 parent 77b2b4c commit e27fb0c

File tree

1 file changed

+272
-0
lines changed

1 file changed

+272
-0
lines changed
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
'use strict';
2+
3+
import assert from 'node:assert/strict';
4+
import { describe, it } from 'node:test';
5+
6+
import { u } from 'unist-builder';
7+
8+
import { parseApiDoc } from '../parse.mjs';
9+
10+
const file = { stem: 'fs', basename: 'fs.md' };
11+
const typeMap = {};
12+
13+
const h = (text, depth = 1) => u('heading', { depth }, [u('text', text)]);
14+
const yaml = content => u('html', `<!-- YAML\n${content}\n-->`);
15+
const stability = text => u('blockquote', [u('paragraph', [u('text', text)])]);
16+
17+
const findLink = entry => {
18+
const paragraph = entry.content.children.find(n => n.type === 'paragraph');
19+
return paragraph?.children?.find(n => n.type === 'link');
20+
};
21+
22+
describe('parseApiDoc', () => {
23+
describe('basic heading', () => {
24+
it('produces one entry for a single heading', () => {
25+
const tree = u('root', [
26+
h('fs'),
27+
u('paragraph', [u('text', 'Content.')]),
28+
]);
29+
const results = parseApiDoc({ file, tree }, typeMap);
30+
31+
assert.strictEqual(results.length, 1);
32+
});
33+
34+
it('sets api and api_doc_source from the file', () => {
35+
const tree = u('root', [h('fs')]);
36+
const [entry] = parseApiDoc({ file, tree }, typeMap);
37+
38+
assert.strictEqual(entry.api, 'fs');
39+
assert.strictEqual(entry.api_doc_source, 'doc/api/fs.md');
40+
});
41+
42+
it('generates a slug from the heading text', () => {
43+
const tree = u('root', [h('File System')]);
44+
const [entry] = parseApiDoc({ file, tree }, typeMap);
45+
46+
assert.strictEqual(entry.slug, 'file-system');
47+
});
48+
49+
it('populates heading data with text and depth', () => {
50+
const tree = u('root', [h('File System')]);
51+
const [entry] = parseApiDoc({ file, tree }, typeMap);
52+
53+
assert.strictEqual(entry.heading.data.text, 'File System');
54+
assert.strictEqual(entry.heading.data.depth, 1);
55+
});
56+
});
57+
58+
describe('multiple headings', () => {
59+
it('produces one entry per heading', () => {
60+
const tree = u('root', [
61+
h('Module'),
62+
u('paragraph', [u('text', 'Intro.')]),
63+
h('Class Foo', 2),
64+
u('paragraph', [u('text', 'Class docs.')]),
65+
h('foo.bar()', 2),
66+
u('paragraph', [u('text', 'Method docs.')]),
67+
]);
68+
const results = parseApiDoc({ file, tree }, typeMap);
69+
70+
assert.strictEqual(results.length, 3);
71+
});
72+
73+
it('assigns correct slugs to each entry', () => {
74+
const tree = u('root', [
75+
h('First'),
76+
u('paragraph', [u('text', 'Content A.')]),
77+
h('Second'),
78+
u('paragraph', [u('text', 'Content B.')]),
79+
]);
80+
const results = parseApiDoc({ file, tree }, typeMap);
81+
82+
assert.strictEqual(results[0].slug, 'first');
83+
assert.strictEqual(results[1].slug, 'second');
84+
});
85+
});
86+
87+
describe('YAML metadata', () => {
88+
it('extracts added_in', () => {
89+
const tree = u('root', [h('fs'), yaml('added: v0.1.0')]);
90+
const [entry] = parseApiDoc({ file, tree }, typeMap);
91+
92+
assert.strictEqual(entry.added_in, 'v0.1.0');
93+
});
94+
95+
it('extracts deprecated_in', () => {
96+
const tree = u('root', [
97+
h('oldMethod'),
98+
yaml('added: v1.0.0\ndeprecated: v2.0.0'),
99+
]);
100+
const [entry] = parseApiDoc({ file, tree }, typeMap);
101+
102+
assert.strictEqual(entry.added_in, 'v1.0.0');
103+
assert.strictEqual(entry.deprecated_in, 'v2.0.0');
104+
});
105+
106+
it('extracts removed_in', () => {
107+
const tree = u('root', [h('removedMethod'), yaml('removed: v3.0.0')]);
108+
const [entry] = parseApiDoc({ file, tree }, typeMap);
109+
110+
assert.strictEqual(entry.removed_in, 'v3.0.0');
111+
});
112+
113+
it('extracts changes', () => {
114+
const tree = u('root', [
115+
h('fs.readFile'),
116+
yaml(
117+
'added: v0.1.0\n' +
118+
'changes:\n' +
119+
' - version: v7.0.0\n' +
120+
' pr-url: https://github.com/nodejs/node/pull/7897\n' +
121+
' description: The callback is no longer optional.'
122+
),
123+
]);
124+
const [entry] = parseApiDoc({ file, tree }, typeMap);
125+
126+
assert.strictEqual(entry.changes.length, 1);
127+
assert.strictEqual(entry.changes[0].version, 'v7.0.0');
128+
assert.strictEqual(
129+
entry.changes[0]['pr-url'],
130+
'https://github.com/nodejs/node/pull/7897'
131+
);
132+
});
133+
134+
it('extracts tags from a plain comment', () => {
135+
const tree = u('root', [h('method'), u('html', '<!-- legacy -->')]);
136+
const [entry] = parseApiDoc({ file, tree }, typeMap);
137+
138+
assert.deepStrictEqual(entry.tags, ['legacy']);
139+
});
140+
141+
it('defaults to empty arrays when YAML block is absent', () => {
142+
const tree = u('root', [h('fs')]);
143+
const [entry] = parseApiDoc({ file, tree }, typeMap);
144+
145+
assert.deepStrictEqual(entry.changes, []);
146+
assert.deepStrictEqual(entry.tags, []);
147+
});
148+
});
149+
150+
describe('stability index', () => {
151+
it('captures stability index and description', () => {
152+
const tree = u('root', [h('fs'), stability('Stability: 2 - Stable')]);
153+
const [entry] = parseApiDoc({ file, tree }, typeMap);
154+
155+
assert.strictEqual(entry.stability.children.length, 1);
156+
assert.strictEqual(entry.stability.children[0].data.index, '2');
157+
assert.strictEqual(
158+
entry.stability.children[0].data.description,
159+
'Stable'
160+
);
161+
});
162+
163+
it('captures multi-word stability description', () => {
164+
const tree = u('root', [
165+
h('crypto'),
166+
stability('Stability: 1 - Experimental: This API is experimental.'),
167+
]);
168+
const [entry] = parseApiDoc({ file, tree }, typeMap);
169+
170+
assert.strictEqual(
171+
entry.stability.children[0].data.description,
172+
'Experimental: This API is experimental.'
173+
);
174+
});
175+
176+
it('ignores stability blockquotes in the documentation file', () => {
177+
const tree = u('root', [
178+
h('Stability Index'),
179+
stability('Stability: 2 - Stable'),
180+
]);
181+
const [entry] = parseApiDoc(
182+
{ file: { stem: 'documentation', basename: 'documentation.md' }, tree },
183+
typeMap
184+
);
185+
186+
assert.strictEqual(entry.stability.children.length, 0);
187+
});
188+
189+
it('has empty stability when no blockquote is present', () => {
190+
const tree = u('root', [h('fs')]);
191+
const [entry] = parseApiDoc({ file, tree }, typeMap);
192+
193+
assert.strictEqual(entry.stability.children.length, 0);
194+
});
195+
});
196+
197+
describe('link references', () => {
198+
it('resolves link references to their definitions', () => {
199+
const tree = u('root', [
200+
h('fs'),
201+
u('paragraph', [
202+
u('linkReference', { identifier: 'ref', referenceType: 'full' }, [
203+
u('text', 'a link'),
204+
]),
205+
]),
206+
u('definition', { identifier: 'ref', url: 'https://example.com' }),
207+
]);
208+
const [entry] = parseApiDoc({ file, tree }, typeMap);
209+
210+
assert.strictEqual(findLink(entry)?.url, 'https://example.com');
211+
});
212+
});
213+
214+
describe('type references', () => {
215+
it('transforms {type} references into links', () => {
216+
const tree = u('root', [
217+
h('fs'),
218+
u('paragraph', [u('text', '{string}')]),
219+
]);
220+
const [entry] = parseApiDoc({ file, tree }, typeMap);
221+
222+
assert.ok(
223+
findLink(entry) !== undefined,
224+
'expected a link node from type reference transformation'
225+
);
226+
});
227+
});
228+
229+
describe('URL normalization', () => {
230+
it('converts .md links to .html', () => {
231+
const tree = u('root', [
232+
h('fs'),
233+
u('paragraph', [
234+
u('link', { url: 'events.md' }, [u('text', 'events')]),
235+
]),
236+
]);
237+
const [entry] = parseApiDoc({ file, tree }, typeMap);
238+
239+
assert.strictEqual(findLink(entry)?.url, 'events.html');
240+
});
241+
242+
it('preserves hash fragments when converting .md links', () => {
243+
const tree = u('root', [
244+
h('fs'),
245+
u('paragraph', [
246+
u('link', { url: 'events.md#some-section' }, [u('text', 'events')]),
247+
]),
248+
]);
249+
const [entry] = parseApiDoc({ file, tree }, typeMap);
250+
251+
assert.strictEqual(findLink(entry)?.url, 'events.html#some-section');
252+
});
253+
});
254+
255+
describe('document without headings', () => {
256+
it('produces one entry for content with no headings', () => {
257+
const tree = u('root', [
258+
u('paragraph', [u('text', 'Just some text without any headings.')]),
259+
]);
260+
const results = parseApiDoc({ file, tree }, typeMap);
261+
262+
assert.strictEqual(results.length, 1);
263+
});
264+
265+
it('returns an empty array for an empty document', () => {
266+
const tree = u('root', []);
267+
const results = parseApiDoc({ file, tree }, typeMap);
268+
269+
assert.strictEqual(results.length, 0);
270+
});
271+
});
272+
});

0 commit comments

Comments
 (0)