docusaurus-plugin-generate-schema-docs 1.5.4 → 1.7.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/__tests__/__fixtures__/static/schemas/battle-test-event.json +771 -0
- package/__tests__/__fixtures__/static/schemas/conditional-event.json +52 -0
- package/__tests__/__fixtures__/static/schemas/nested-conditional-event.json +50 -0
- package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +3 -2
- package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +4 -2
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +3 -2
- package/__tests__/components/ConditionalRows.test.js +150 -0
- package/__tests__/components/ConnectorLines.visualRegression.test.js +93 -0
- package/__tests__/components/FoldableRows.test.js +7 -4
- package/__tests__/components/SchemaRows.test.js +31 -0
- package/__tests__/components/__snapshots__/ConnectorLines.visualRegression.test.js.snap +7 -0
- package/__tests__/generateEventDocs.anchor.test.js +7 -0
- package/__tests__/generateEventDocs.nested.test.js +7 -0
- package/__tests__/generateEventDocs.partials.test.js +134 -0
- package/__tests__/generateEventDocs.test.js +7 -0
- package/__tests__/helpers/buildExampleFromSchema.test.js +49 -0
- package/__tests__/helpers/schemaToExamples.test.js +75 -0
- package/__tests__/helpers/schemaToTableData.battleTest.test.js +704 -0
- package/__tests__/helpers/schemaToTableData.hierarchicalLines.test.js +190 -7
- package/__tests__/helpers/schemaToTableData.test.js +263 -2
- package/__tests__/helpers/validator.test.js +6 -6
- package/components/ConditionalRows.js +156 -0
- package/components/FoldableRows.js +88 -61
- package/components/PropertiesTable.js +1 -1
- package/components/PropertyRow.js +24 -8
- package/components/SchemaRows.css +115 -0
- package/components/SchemaRows.js +31 -4
- package/generateEventDocs.js +55 -37
- package/helpers/buildExampleFromSchema.js +11 -0
- package/helpers/choice-index-template.js +2 -1
- package/helpers/continuingLinesStyle.js +169 -0
- package/helpers/schema-doc-template.js +2 -5
- package/helpers/schema-processing.js +3 -0
- package/helpers/schemaToExamples.js +75 -2
- package/helpers/schemaToTableData.js +252 -26
- package/helpers/update-schema-ids.js +3 -3
- package/helpers/validator.js +7 -19
- package/package.json +3 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*
|
|
4
|
+
* Tests that top and bottom partials are correctly injected into generated MDX
|
|
5
|
+
* when matching partial files exist in the partials directory, and that the
|
|
6
|
+
* generated import paths use the event name (not the component prefix).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import generateEventDocs from '../generateEventDocs';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
|
|
13
|
+
jest.mock('fs', () => {
|
|
14
|
+
const memfs = require('memfs');
|
|
15
|
+
return memfs;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('generateEventDocs (partials)', () => {
|
|
19
|
+
const fixturesDir = path.resolve(__dirname, '__fixtures__');
|
|
20
|
+
const options = {
|
|
21
|
+
organizationName: 'test-org',
|
|
22
|
+
projectName: 'test-project',
|
|
23
|
+
siteDir: fixturesDir,
|
|
24
|
+
url: 'https://tracking-docs-demo.buchert.digital',
|
|
25
|
+
};
|
|
26
|
+
const outputDir = path.join(fixturesDir, 'docs');
|
|
27
|
+
const partialsDir = path.join(outputDir, 'partials');
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
fs.vol.reset();
|
|
31
|
+
const realFs = jest.requireActual('fs');
|
|
32
|
+
|
|
33
|
+
function readDirRecursive(dir) {
|
|
34
|
+
const files = realFs.readdirSync(dir, { withFileTypes: true });
|
|
35
|
+
for (const file of files) {
|
|
36
|
+
const filePath = path.join(dir, file.name);
|
|
37
|
+
if (file.isDirectory()) {
|
|
38
|
+
fs.vol.mkdirSync(filePath, { recursive: true });
|
|
39
|
+
readDirRecursive(filePath);
|
|
40
|
+
} else {
|
|
41
|
+
fs.vol.writeFileSync(filePath, realFs.readFileSync(filePath));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
readDirRecursive(fixturesDir);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('injects top partial when _<eventName>.mdx exists in partials dir', async () => {
|
|
49
|
+
console.log = jest.fn();
|
|
50
|
+
fs.vol.mkdirSync(partialsDir, { recursive: true });
|
|
51
|
+
fs.vol.writeFileSync(
|
|
52
|
+
path.join(partialsDir, '_add-to-cart-event.mdx'),
|
|
53
|
+
'## Top content',
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
await generateEventDocs(options);
|
|
57
|
+
|
|
58
|
+
const output = fs.readFileSync(
|
|
59
|
+
path.join(outputDir, 'add-to-cart-event.mdx'),
|
|
60
|
+
'utf-8',
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
expect(output).toContain(
|
|
64
|
+
"import TopPartial from '@site/docs/partials/_add-to-cart-event.mdx'",
|
|
65
|
+
);
|
|
66
|
+
expect(output).toContain('<TopPartial />');
|
|
67
|
+
expect(output).not.toContain('_Top.mdx');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('injects bottom partial when _<eventName>_bottom.mdx exists in partials dir', async () => {
|
|
71
|
+
console.log = jest.fn();
|
|
72
|
+
fs.vol.mkdirSync(partialsDir, { recursive: true });
|
|
73
|
+
fs.vol.writeFileSync(
|
|
74
|
+
path.join(partialsDir, '_add-to-cart-event_bottom.mdx'),
|
|
75
|
+
'## Bottom content',
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
await generateEventDocs(options);
|
|
79
|
+
|
|
80
|
+
const output = fs.readFileSync(
|
|
81
|
+
path.join(outputDir, 'add-to-cart-event.mdx'),
|
|
82
|
+
'utf-8',
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
expect(output).toContain(
|
|
86
|
+
"import BottomPartial from '@site/docs/partials/_add-to-cart-event_bottom.mdx'",
|
|
87
|
+
);
|
|
88
|
+
expect(output).toContain('<BottomPartial />');
|
|
89
|
+
expect(output).not.toContain('_Bottom.mdx');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('injects both top and bottom partials when both exist', async () => {
|
|
93
|
+
console.log = jest.fn();
|
|
94
|
+
fs.vol.mkdirSync(partialsDir, { recursive: true });
|
|
95
|
+
fs.vol.writeFileSync(
|
|
96
|
+
path.join(partialsDir, '_add-to-cart-event.mdx'),
|
|
97
|
+
'## Top content',
|
|
98
|
+
);
|
|
99
|
+
fs.vol.writeFileSync(
|
|
100
|
+
path.join(partialsDir, '_add-to-cart-event_bottom.mdx'),
|
|
101
|
+
'## Bottom content',
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
await generateEventDocs(options);
|
|
105
|
+
|
|
106
|
+
const output = fs.readFileSync(
|
|
107
|
+
path.join(outputDir, 'add-to-cart-event.mdx'),
|
|
108
|
+
'utf-8',
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(output).toContain(
|
|
112
|
+
"import TopPartial from '@site/docs/partials/_add-to-cart-event.mdx'",
|
|
113
|
+
);
|
|
114
|
+
expect(output).toContain('<TopPartial />');
|
|
115
|
+
expect(output).toContain(
|
|
116
|
+
"import BottomPartial from '@site/docs/partials/_add-to-cart-event_bottom.mdx'",
|
|
117
|
+
);
|
|
118
|
+
expect(output).toContain('<BottomPartial />');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('omits partial imports when no partial files exist', async () => {
|
|
122
|
+
console.log = jest.fn();
|
|
123
|
+
|
|
124
|
+
await generateEventDocs(options);
|
|
125
|
+
|
|
126
|
+
const output = fs.readFileSync(
|
|
127
|
+
path.join(outputDir, 'add-to-cart-event.mdx'),
|
|
128
|
+
'utf-8',
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
expect(output).not.toContain('TopPartial');
|
|
132
|
+
expect(output).not.toContain('BottomPartial');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -80,11 +80,18 @@ describe('generateEventDocs (non-versioned)', () => {
|
|
|
80
80
|
'utf-8',
|
|
81
81
|
);
|
|
82
82
|
expect(rootChoiceA).toMatchSnapshot();
|
|
83
|
+
// Inline oneOf options must link to the parent schema file, not a temp output file
|
|
84
|
+
expect(rootChoiceA).toContain(
|
|
85
|
+
'custom_edit_url: https://github.com/test-org/test-project/edit/main/__fixtures__/static/schemas/root-choice-event.json',
|
|
86
|
+
);
|
|
83
87
|
|
|
84
88
|
const rootChoiceB = fs.readFileSync(
|
|
85
89
|
path.join(choiceEventDir, '02-option-b.mdx'),
|
|
86
90
|
'utf-8',
|
|
87
91
|
);
|
|
88
92
|
expect(rootChoiceB).toMatchSnapshot();
|
|
93
|
+
expect(rootChoiceB).toContain(
|
|
94
|
+
'custom_edit_url: https://github.com/test-org/test-project/edit/main/__fixtures__/static/schemas/root-choice-event.json',
|
|
95
|
+
);
|
|
89
96
|
});
|
|
90
97
|
});
|
|
@@ -185,4 +185,53 @@ describe('buildExampleFromSchema', () => {
|
|
|
185
185
|
},
|
|
186
186
|
});
|
|
187
187
|
});
|
|
188
|
+
|
|
189
|
+
it('should handle if/then/else by defaulting to then branch', () => {
|
|
190
|
+
const schema = {
|
|
191
|
+
type: 'object',
|
|
192
|
+
properties: {
|
|
193
|
+
event: { type: 'string', examples: ['form_submit'] },
|
|
194
|
+
},
|
|
195
|
+
if: { properties: { event: { const: 'form_submit' } } },
|
|
196
|
+
then: {
|
|
197
|
+
properties: {
|
|
198
|
+
form_name: { type: 'string', examples: ['contact'] },
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
else: {
|
|
202
|
+
properties: {
|
|
203
|
+
error_code: { type: 'integer', examples: [404] },
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const example = buildExampleFromSchema(schema);
|
|
209
|
+
expect(example).toHaveProperty('event', 'form_submit');
|
|
210
|
+
expect(example).toHaveProperty('form_name', 'contact');
|
|
211
|
+
expect(example).not.toHaveProperty('error_code');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should handle nested if/then/else inside a property', () => {
|
|
215
|
+
const schema = {
|
|
216
|
+
type: 'object',
|
|
217
|
+
properties: {
|
|
218
|
+
shipping: {
|
|
219
|
+
type: 'object',
|
|
220
|
+
properties: {
|
|
221
|
+
method: { type: 'string', examples: ['express'] },
|
|
222
|
+
},
|
|
223
|
+
if: { properties: { method: { const: 'express' } } },
|
|
224
|
+
then: {
|
|
225
|
+
properties: {
|
|
226
|
+
priority: { type: 'string', examples: ['high'] },
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const example = buildExampleFromSchema(schema);
|
|
234
|
+
expect(example.shipping).toHaveProperty('method', 'express');
|
|
235
|
+
expect(example.shipping).toHaveProperty('priority', 'high');
|
|
236
|
+
});
|
|
188
237
|
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { schemaToExamples } from '../../helpers/schemaToExamples';
|
|
2
2
|
import choiceEventSchema from '../__fixtures__/static/schemas/choice-event.json';
|
|
3
|
+
import conditionalEventSchema from '../__fixtures__/static/schemas/conditional-event.json';
|
|
4
|
+
import nestedConditionalEventSchema from '../__fixtures__/static/schemas/nested-conditional-event.json';
|
|
3
5
|
|
|
4
6
|
describe('schemaToExamples', () => {
|
|
5
7
|
it('should generate examples for all options in a complex schema', () => {
|
|
@@ -53,4 +55,77 @@ describe('schemaToExamples', () => {
|
|
|
53
55
|
expect(userIdIntegerOption).toBeDefined();
|
|
54
56
|
expect(userIdIntegerOption.example).toHaveProperty('payment_method');
|
|
55
57
|
});
|
|
58
|
+
|
|
59
|
+
describe('if/then/else conditional examples', () => {
|
|
60
|
+
it('generates two examples for schema with if/then/else', () => {
|
|
61
|
+
const groups = schemaToExamples(conditionalEventSchema);
|
|
62
|
+
const conditionalGroup = groups.find((g) => g.property === 'conditional');
|
|
63
|
+
expect(conditionalGroup).toBeDefined();
|
|
64
|
+
expect(conditionalGroup.options).toHaveLength(2);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('then example includes then properties merged with base', () => {
|
|
68
|
+
const groups = schemaToExamples(conditionalEventSchema);
|
|
69
|
+
const conditionalGroup = groups.find((g) => g.property === 'conditional');
|
|
70
|
+
const thenOption = conditionalGroup.options.find(
|
|
71
|
+
(o) => o.title === 'When condition is met',
|
|
72
|
+
);
|
|
73
|
+
expect(thenOption).toBeDefined();
|
|
74
|
+
expect(thenOption.example).toHaveProperty('event');
|
|
75
|
+
expect(thenOption.example).toHaveProperty('postal_code', '90210');
|
|
76
|
+
expect(thenOption.example).toHaveProperty('state', 'CA');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('else example includes else properties merged with base', () => {
|
|
80
|
+
const groups = schemaToExamples(conditionalEventSchema);
|
|
81
|
+
const conditionalGroup = groups.find((g) => g.property === 'conditional');
|
|
82
|
+
const elseOption = conditionalGroup.options.find(
|
|
83
|
+
(o) => o.title === 'When condition is not met',
|
|
84
|
+
);
|
|
85
|
+
expect(elseOption).toBeDefined();
|
|
86
|
+
expect(elseOption.example).toHaveProperty('event');
|
|
87
|
+
expect(elseOption.example).toHaveProperty('postal_code', 'K1A 0B1');
|
|
88
|
+
expect(elseOption.example).not.toHaveProperty('state');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('generates only one example when else is absent', () => {
|
|
92
|
+
const schema = {
|
|
93
|
+
type: 'object',
|
|
94
|
+
properties: {
|
|
95
|
+
status: { type: 'string', examples: ['active'] },
|
|
96
|
+
},
|
|
97
|
+
if: { properties: { status: { const: 'active' } } },
|
|
98
|
+
then: {
|
|
99
|
+
properties: {
|
|
100
|
+
active_since: { type: 'string', examples: ['2024-01-01'] },
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
const groups = schemaToExamples(schema);
|
|
105
|
+
const conditionalGroup = groups.find((g) => g.property === 'conditional');
|
|
106
|
+
expect(conditionalGroup).toBeDefined();
|
|
107
|
+
expect(conditionalGroup.options).toHaveLength(1);
|
|
108
|
+
expect(conditionalGroup.options[0].title).toBe('When condition is met');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('detects nested conditional points inside properties', () => {
|
|
112
|
+
const groups = schemaToExamples(nestedConditionalEventSchema);
|
|
113
|
+
const conditionalGroup = groups.find((g) => g.property === 'conditional');
|
|
114
|
+
expect(conditionalGroup).toBeDefined();
|
|
115
|
+
expect(conditionalGroup.options).toHaveLength(2);
|
|
116
|
+
|
|
117
|
+
const thenOption = conditionalGroup.options.find(
|
|
118
|
+
(o) => o.title === 'When condition is met',
|
|
119
|
+
);
|
|
120
|
+
expect(thenOption.example.shipping).toHaveProperty(
|
|
121
|
+
'priority_level',
|
|
122
|
+
'high',
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const elseOption = conditionalGroup.options.find(
|
|
126
|
+
(o) => o.title === 'When condition is not met',
|
|
127
|
+
);
|
|
128
|
+
expect(elseOption.example.shipping).toHaveProperty('estimated_days', 5);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
56
131
|
});
|