Skip to content

Commit 38d824e

Browse files
committed
test: add some validation for JSON doc output
1 parent 79ddd1b commit 38d824e

File tree

2 files changed

+153
-2
lines changed

2 files changed

+153
-2
lines changed

doc/api/environment_variables.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Environment Variables
22

3+
<!--introduced_in=v20.12.0-->
4+
35
Environment variables are variables associated to the environment the Node.js process runs in.
46

57
## CLI Environment Variables
@@ -20,8 +22,6 @@ Set of utilities for dealing with additional environment variables defined in `.
2022

2123
> Stability: 2 - Stable
2224
23-
<!--introduced_in=v20.12.0-->
24-
2525
### .env files
2626

2727
`.env` files (also known as dotenv files) are files that define environment variables,
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import '../common/index.mjs';
2+
3+
import assert from 'node:assert';
4+
import { existsSync } from 'node:fs';
5+
import fs from 'node:fs/promises';
6+
import path from 'node:path';
7+
8+
// This tests that `make doc` generates the JSON documentation properly.
9+
// Note that for this test to pass, `make doc` must be run first.
10+
11+
function validateModule(module) {
12+
assert.strictEqual(typeof module, 'object');
13+
assert.strictEqual(module.type, 'module');
14+
assert.ok(module.name);
15+
assert.ok(module.textRaw);
16+
}
17+
18+
function validateMisc(misc) {
19+
assert.strictEqual(typeof misc, 'object');
20+
assert.strictEqual(misc.type, 'misc');
21+
assert.ok(misc.name);
22+
assert.strictEqual(typeof misc.name, 'string');
23+
assert.ok(misc.textRaw);
24+
assert.strictEqual(typeof misc.textRaw, 'string');
25+
}
26+
27+
let numberOfDeprecatedSections = 0;
28+
let numberOfRemovedAPIs = 0;
29+
30+
const metaExpectedKeys = new Set([
31+
'added',
32+
'changes',
33+
'deprecated',
34+
'napiVersion',
35+
'removed',
36+
]);
37+
38+
function validateMeta(meta) {
39+
assert.partialDeepStrictEqual(metaExpectedKeys, new Set(Object.keys(meta)));
40+
assert.ok(!Object.hasOwn(meta, 'added') || Array.isArray(meta.added) || typeof meta.added === 'string');
41+
if (meta.deprecated) {
42+
numberOfDeprecatedSections++;
43+
assert.ok(Array.isArray(meta.deprecated) || typeof meta.deprecated === 'string');
44+
}
45+
if (meta.removed) {
46+
numberOfRemovedAPIs++;
47+
assert.ok(Array.isArray(meta.removed) || typeof meta.removed === 'string');
48+
}
49+
50+
assert.ok(!Object.hasOwn(meta, 'napiVersion') || Number(meta.napiVersion));
51+
assert.ok(Array.isArray(meta.changes));
52+
}
53+
54+
function findAllKeys(obj, allKeys = new Set()) {
55+
for (const [key, value] of Object.entries(obj)) {
56+
if (Number.isNaN(Number(key))) allKeys.add(key);
57+
if (typeof value === 'object') findAllKeys(value, allKeys);
58+
59+
if (key === 'miscs') {
60+
assert.ok(Array.isArray(value));
61+
assert.ok(value.length);
62+
value.forEach(validateMisc);
63+
} else if (key === 'modules') {
64+
assert.ok(Array.isArray(value));
65+
assert.ok(value.length);
66+
value.forEach(validateModule);
67+
} else if (key === 'meta') {
68+
validateMeta(value);
69+
}
70+
}
71+
return allKeys;
72+
}
73+
74+
const allExpectedKeys = new Set([
75+
'added',
76+
'changes',
77+
'classes',
78+
'classMethods',
79+
'commit',
80+
'ctors',
81+
'default',
82+
'deprecated',
83+
'desc',
84+
'description',
85+
'displayName',
86+
'events',
87+
'examples',
88+
'globals',
89+
'introduced_in',
90+
'meta',
91+
'methods',
92+
'miscs',
93+
'modules',
94+
'name',
95+
'napiVersion',
96+
'options',
97+
'params',
98+
'pr-url',
99+
'properties',
100+
'removed',
101+
'return',
102+
'shortDesc',
103+
'signatures',
104+
'source',
105+
'stability',
106+
'stabilityText',
107+
'textRaw',
108+
'type',
109+
'version',
110+
]);
111+
112+
for await (const dirent of await fs.opendir(new URL('../../out/doc/api/', import.meta.url))) {
113+
if (!dirent.name.endsWith('.md')) continue;
114+
115+
const jsonPath = path.join(dirent.parentPath, dirent.name.slice(0, -2) + 'json');
116+
const expectedSource = `doc/api/${dirent.name}`;
117+
if (dirent.name === 'quic.md') {
118+
assert.ok(!existsSync(jsonPath)); // QUIC documentation is not public yet
119+
continue;
120+
}
121+
122+
console.log('testing', jsonPath, 'based on', expectedSource);
123+
const json = JSON.parse(await fs.readFile(jsonPath, 'utf8'));
124+
125+
assert.strictEqual(json.type, 'module');
126+
assert.strictEqual(json.source, expectedSource);
127+
if (dirent.name !== 'index.md') {
128+
assert.ok(json.introduced_in || Object.values(json).at(-1)?.[0].introduced_in);
129+
}
130+
assert.deepStrictEqual(Object.keys(json), ['type', 'source', ...({
131+
'addons.md': ['introduced_in', 'miscs'],
132+
'cli.md': ['introduced_in', 'miscs'],
133+
'debugger.md': ['introduced_in', 'stability', 'stabilityText', 'miscs'],
134+
'deprecations.md': ['introduced_in', 'miscs'],
135+
'documentation.md': ['introduced_in', 'miscs'],
136+
'errors.md': ['introduced_in', 'classes', 'miscs'],
137+
'esm.md': ['introduced_in', 'meta', 'stability', 'stabilityText', 'properties', 'miscs'],
138+
'globals.md': ['introduced_in', 'stability', 'stabilityText', 'classes', 'methods', 'miscs'],
139+
'index.md': [],
140+
'intl.md': ['introduced_in', 'miscs'],
141+
'n-api.md': ['introduced_in', 'stability', 'stabilityText', 'miscs'],
142+
'packages.md': ['introduced_in', 'meta', 'miscs'],
143+
'process.md': ['globals'],
144+
'report.md': ['introduced_in', 'stability', 'stabilityText', 'meta', 'miscs'],
145+
}[dirent.name] ?? ['modules'])]);
146+
147+
assert.partialDeepStrictEqual(allExpectedKeys, findAllKeys(json));
148+
}
149+
150+
assert.strictEqual(numberOfDeprecatedSections, 39); // Increase this number every time a new API is deprecated.
151+
assert.strictEqual(numberOfRemovedAPIs, 46); // Increase this number every time a section is marked as removed.

0 commit comments

Comments
 (0)