docusaurus-plugin-generate-schema-docs 1.2.0 → 1.3.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/README.md +42 -6
- package/__tests__/ExampleDataLayer.test.js +78 -155
- package/__tests__/__fixtures__/static/schemas/add-to-cart-event.json +4 -15
- package/__tests__/__fixtures__/static/schemas/choice-event.json +72 -0
- package/__tests__/__fixtures__/static/schemas/components/dataLayer.json +52 -54
- package/__tests__/__fixtures__/static/schemas/components/product.json +124 -210
- package/__tests__/__fixtures__/static/schemas/nested/child-event.json +10 -0
- package/__tests__/__fixtures__/static/schemas/nested/grandchild-a.json +9 -0
- package/__tests__/__fixtures__/static/schemas/nested/grandchild-b.json +9 -0
- package/__tests__/__fixtures__/static/schemas/nested/parent-event.json +7 -0
- package/__tests__/__fixtures__/static/schemas/root-any-of-event.json +34 -0
- package/__tests__/__fixtures__/static/schemas/root-choice-event.json +36 -0
- package/__tests__/__fixtures__/validateSchemas/circular-schema.json +6 -6
- package/__tests__/__fixtures__/validateSchemas/components/referenced.json +9 -7
- package/__tests__/__fixtures__/validateSchemas/invalid-example-schema.json +7 -7
- package/__tests__/__fixtures__/validateSchemas/main-schema-with-missing-ref.json +7 -7
- package/__tests__/__fixtures__/validateSchemas/main-schema-with-ref.json +7 -7
- package/__tests__/__fixtures__/validateSchemas/no-example-schema.json +11 -11
- package/__tests__/__fixtures__/validateSchemas/schema-A.json +5 -5
- package/__tests__/__fixtures__/validateSchemas/schema-B.json +5 -5
- package/__tests__/__fixtures__/validateSchemas/valid-schema.json +7 -7
- package/__tests__/__fixtures_versioned__/static/schemas/1.1.1/add-to-cart-event.json +44 -0
- package/__tests__/__fixtures_versioned__/static/schemas/1.1.1/components/dataLayer.json +56 -0
- package/__tests__/__fixtures_versioned__/static/schemas/1.1.1/components/product.json +125 -0
- package/__tests__/__fixtures_versioned__/static/schemas/next/add-to-cart-event.json +44 -0
- package/__tests__/__fixtures_versioned__/static/schemas/next/components/dataLayer.json +56 -0
- package/__tests__/__fixtures_versioned__/static/schemas/next/components/product.json +125 -0
- package/__tests__/__fixtures_versioned__/versions.json +1 -0
- package/__tests__/__snapshots__/ExampleDataLayer.test.js.snap +117 -0
- package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +92 -0
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +113 -15
- package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +53 -0
- package/__tests__/components/FoldableRows.test.js +330 -0
- package/__tests__/components/PropertiesTable.test.js +31 -14
- package/__tests__/components/PropertyRow.test.js +471 -51
- package/__tests__/components/SchemaJsonViewer.test.js +23 -19
- package/__tests__/components/SchemaRows.test.js +96 -66
- package/__tests__/components/SchemaViewer.test.js +34 -17
- package/__tests__/components/TableHeader.test.js +12 -12
- package/__tests__/generateEventDocs.nested.test.js +80 -0
- package/__tests__/generateEventDocs.test.js +77 -71
- package/__tests__/generateEventDocs.versioned.test.js +69 -0
- package/__tests__/helpers/buildExampleFromSchema.test.js +160 -160
- package/__tests__/helpers/file-system.test.js +44 -0
- package/__tests__/helpers/getConstraints.test.js +48 -48
- package/__tests__/helpers/loadSchema.test.js +11 -5
- package/__tests__/helpers/path-helpers.test.js +34 -0
- package/__tests__/helpers/processSchema.test.js +42 -22
- package/__tests__/helpers/schema-processing.test.js +82 -0
- package/__tests__/helpers/schemaToExamples.test.js +56 -0
- package/__tests__/helpers/schemaToTableData.filtering.test.js +65 -0
- package/__tests__/helpers/schemaToTableData.hierarchicalLines.test.js +539 -0
- package/__tests__/helpers/schemaToTableData.test.js +222 -0
- package/__tests__/helpers/update-schema-ids.test.js +107 -0
- package/__tests__/update-schema-ids.test.js +39 -0
- package/__tests__/validateSchemas.test.js +125 -88
- package/components/ExampleDataLayer.js +59 -28
- package/components/FoldableRows.js +164 -0
- package/components/PropertiesTable.js +10 -7
- package/components/PropertyRow.js +169 -60
- package/components/SchemaJsonViewer.js +6 -6
- package/components/SchemaRows.css +236 -14
- package/components/SchemaRows.js +24 -41
- package/components/SchemaViewer.js +19 -13
- package/components/TableHeader.js +12 -12
- package/generateEventDocs.js +141 -61
- package/helpers/buildExampleFromSchema.js +58 -72
- package/helpers/choice-index-template.js +22 -0
- package/helpers/file-system.js +32 -0
- package/helpers/getConstraints.js +43 -44
- package/helpers/loadSchema.js +2 -2
- package/helpers/path-helpers.js +22 -0
- package/helpers/processSchema.js +19 -19
- package/helpers/{mdx-template.js → schema-doc-template.js} +12 -12
- package/helpers/schema-processing.js +75 -0
- package/helpers/schemaToExamples.js +99 -0
- package/helpers/schemaToTableData.js +311 -0
- package/helpers/update-schema-ids.js +47 -0
- package/index.js +143 -54
- package/package.json +1 -1
- package/validateSchemas.js +54 -71
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { schemaToTableData } from '../../helpers/schemaToTableData';
|
|
2
|
+
import choiceEventSchema from '../__fixtures__/static/schemas/choice-event.json';
|
|
3
|
+
import rootAnyOfEventSchema from '../__fixtures__/static/schemas/root-any-of-event.json';
|
|
4
|
+
|
|
5
|
+
describe('schemaToTableData', () => {
|
|
6
|
+
it('handles "oneOf" and "anyOf" correctly for a complex schema', () => {
|
|
7
|
+
const tableData = schemaToTableData(choiceEventSchema);
|
|
8
|
+
|
|
9
|
+
// Expect 3 top-level items: event property, user_id choice, payment_method property
|
|
10
|
+
const topLevelRows = tableData.filter((row) => row.level === 0);
|
|
11
|
+
expect(topLevelRows).toHaveLength(3);
|
|
12
|
+
|
|
13
|
+
// 1. Test 'event' property
|
|
14
|
+
const eventProp = tableData.find(
|
|
15
|
+
(row) =>
|
|
16
|
+
row.type === 'property' && row.name === 'event' && row.level === 0,
|
|
17
|
+
);
|
|
18
|
+
expect(eventProp).toBeDefined();
|
|
19
|
+
expect(eventProp.propertyType).toBe('string');
|
|
20
|
+
expect(eventProp.required).toBe(true);
|
|
21
|
+
|
|
22
|
+
// 2. Test 'user_id' property (oneOf)
|
|
23
|
+
const userIdChoice = tableData.find(
|
|
24
|
+
(row) =>
|
|
25
|
+
row.type === 'choice' &&
|
|
26
|
+
row.path.length === 1 &&
|
|
27
|
+
row.path[0] === 'user_id',
|
|
28
|
+
);
|
|
29
|
+
expect(userIdChoice).toBeDefined();
|
|
30
|
+
expect(userIdChoice.level).toBe(0);
|
|
31
|
+
expect(userIdChoice.choiceType).toBe('oneOf');
|
|
32
|
+
expect(userIdChoice.options).toHaveLength(2);
|
|
33
|
+
expect(userIdChoice.description).toBe("The user's ID.");
|
|
34
|
+
|
|
35
|
+
// Check the options for user_id
|
|
36
|
+
const userIdOption1 = userIdChoice.options[0];
|
|
37
|
+
expect(userIdOption1.title).toBe('User ID as String');
|
|
38
|
+
expect(userIdOption1.rows).toHaveLength(1);
|
|
39
|
+
const userIdOption1Prop = userIdOption1.rows[0];
|
|
40
|
+
expect(userIdOption1Prop.type).toBe('property');
|
|
41
|
+
expect(userIdOption1Prop.name).toBe('user_id');
|
|
42
|
+
expect(userIdOption1Prop.level).toBe(0);
|
|
43
|
+
expect(userIdOption1Prop.required).toBe(true);
|
|
44
|
+
|
|
45
|
+
const userIdOption2 = userIdChoice.options[1];
|
|
46
|
+
expect(userIdOption2.title).toBe('User ID as Integer');
|
|
47
|
+
const userIdOption2Prop = userIdOption2.rows[0];
|
|
48
|
+
expect(userIdOption2Prop.name).toBe('user_id');
|
|
49
|
+
expect(userIdOption2Prop.level).toBe(0);
|
|
50
|
+
expect(userIdOption2Prop.required).toBe(true);
|
|
51
|
+
|
|
52
|
+
// 3. Test 'payment_method' property (anyOf)
|
|
53
|
+
const paymentMethodProp = tableData.find(
|
|
54
|
+
(row) => row.name === 'payment_method' && row.level === 0,
|
|
55
|
+
);
|
|
56
|
+
expect(paymentMethodProp).toBeDefined();
|
|
57
|
+
expect(paymentMethodProp.type).toBe('property');
|
|
58
|
+
expect(paymentMethodProp.propertyType).toBe('object');
|
|
59
|
+
expect(paymentMethodProp.required).toBe(true);
|
|
60
|
+
|
|
61
|
+
const paymentMethodChoice = tableData.find(
|
|
62
|
+
(row) =>
|
|
63
|
+
row.type === 'choice' &&
|
|
64
|
+
row.path.length === 2 &&
|
|
65
|
+
row.path[0] === 'payment_method' &&
|
|
66
|
+
row.path[1] === 'anyOf',
|
|
67
|
+
);
|
|
68
|
+
expect(paymentMethodChoice).toBeDefined();
|
|
69
|
+
expect(paymentMethodChoice.level).toBe(1); // Should be nested
|
|
70
|
+
expect(paymentMethodChoice.choiceType).toBe('anyOf');
|
|
71
|
+
expect(paymentMethodChoice.options).toHaveLength(2);
|
|
72
|
+
|
|
73
|
+
// Check the options for payment_method
|
|
74
|
+
const paymentOption1 = paymentMethodChoice.options[0]; // Credit Card
|
|
75
|
+
expect(paymentOption1.title).toBe('Credit Card');
|
|
76
|
+
expect(paymentOption1.rows.length).toBe(3);
|
|
77
|
+
const cardPaymentType = paymentOption1.rows.find(
|
|
78
|
+
(r) => r.name === 'payment_type',
|
|
79
|
+
);
|
|
80
|
+
expect(cardPaymentType).toBeDefined();
|
|
81
|
+
expect(cardPaymentType.level).toBe(1);
|
|
82
|
+
expect(cardPaymentType.path).toEqual(['payment_type']);
|
|
83
|
+
|
|
84
|
+
const paymentOption2 = paymentMethodChoice.options[1]; // PayPal
|
|
85
|
+
expect(paymentOption2.title).toBe('PayPal');
|
|
86
|
+
expect(paymentOption2.rows.length).toBe(2);
|
|
87
|
+
const payPalEmail = paymentOption2.rows.find((r) => r.name === 'email');
|
|
88
|
+
expect(payPalEmail).toBeDefined();
|
|
89
|
+
expect(payPalEmail.level).toBe(1);
|
|
90
|
+
expect(payPalEmail.path).toEqual(['email']);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('handles schemas with both properties and oneOf', () => {
|
|
94
|
+
const schema = {
|
|
95
|
+
properties: {
|
|
96
|
+
common_prop: { type: 'string' },
|
|
97
|
+
},
|
|
98
|
+
oneOf: [
|
|
99
|
+
{
|
|
100
|
+
title: 'Option A',
|
|
101
|
+
properties: { prop_a: { type: 'string' } },
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const tableData = schemaToTableData(schema);
|
|
107
|
+
|
|
108
|
+
// It should contain one property row and one choice row.
|
|
109
|
+
expect(tableData).toHaveLength(2);
|
|
110
|
+
|
|
111
|
+
const propRow = tableData.find((row) => row.type === 'property');
|
|
112
|
+
const choiceRow = tableData.find((row) => row.type === 'choice');
|
|
113
|
+
|
|
114
|
+
expect(propRow).toBeDefined();
|
|
115
|
+
expect(propRow.name).toBe('common_prop');
|
|
116
|
+
|
|
117
|
+
expect(choiceRow).toBeDefined();
|
|
118
|
+
expect(choiceRow.choiceType).toBe('oneOf');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('correctly processes the "examples" array', () => {
|
|
122
|
+
const schema = {
|
|
123
|
+
properties: {
|
|
124
|
+
prop_with_examples: {
|
|
125
|
+
type: 'string',
|
|
126
|
+
examples: ['first-example', 'second-example'],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const tableData = schemaToTableData(schema);
|
|
132
|
+
|
|
133
|
+
expect(tableData).toHaveLength(1);
|
|
134
|
+
const propRow = tableData[0];
|
|
135
|
+
|
|
136
|
+
expect(propRow.name).toBe('prop_with_examples');
|
|
137
|
+
expect(propRow.example).toEqual(['first-example', 'second-example']);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('handles singular "example" if "examples" is not present', () => {
|
|
141
|
+
const schema = {
|
|
142
|
+
properties: {
|
|
143
|
+
prop_with_example: {
|
|
144
|
+
type: 'string',
|
|
145
|
+
example: 'singular-example',
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const tableData = schemaToTableData(schema);
|
|
151
|
+
expect(tableData[0].example).toBe('singular-example');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('handles "oneOf" nested inside a property', () => {
|
|
155
|
+
const schema = {
|
|
156
|
+
properties: {
|
|
157
|
+
user_id: {
|
|
158
|
+
description: "The user's ID.",
|
|
159
|
+
oneOf: [
|
|
160
|
+
{ title: 'User ID as String', type: 'string' },
|
|
161
|
+
{ title: 'User ID as Integer', type: 'integer' },
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const tableData = schemaToTableData(schema);
|
|
168
|
+
|
|
169
|
+
// Expect a single 'choice' row that correctly represents the property.
|
|
170
|
+
expect(tableData).toHaveLength(1);
|
|
171
|
+
|
|
172
|
+
const choiceRow = tableData[0];
|
|
173
|
+
expect(choiceRow.type).toBe('choice');
|
|
174
|
+
expect(choiceRow.level).toBe(0);
|
|
175
|
+
expect(choiceRow.path).toEqual(['user_id']);
|
|
176
|
+
expect(choiceRow.options).toHaveLength(2);
|
|
177
|
+
expect(choiceRow.description).toBe("The user's ID.");
|
|
178
|
+
|
|
179
|
+
// Check that the options' rows are generated correctly
|
|
180
|
+
const firstOptionRows = choiceRow.options[0].rows;
|
|
181
|
+
expect(firstOptionRows).toHaveLength(1);
|
|
182
|
+
expect(firstOptionRows[0].type).toBe('property');
|
|
183
|
+
expect(firstOptionRows[0].name).toBe('user_id');
|
|
184
|
+
expect(firstOptionRows[0].propertyType).toBe('string');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('handles "anyOf" at the root level correctly', () => {
|
|
188
|
+
const tableData = schemaToTableData(rootAnyOfEventSchema);
|
|
189
|
+
|
|
190
|
+
const topLevelRows = tableData.filter((row) => row.level === 0);
|
|
191
|
+
// event property + the choice itself
|
|
192
|
+
expect(topLevelRows).toHaveLength(2);
|
|
193
|
+
|
|
194
|
+
// Test 'event' property
|
|
195
|
+
const eventProp = tableData.find(
|
|
196
|
+
(row) =>
|
|
197
|
+
row.type === 'property' && row.name === 'event' && row.level === 0,
|
|
198
|
+
);
|
|
199
|
+
expect(eventProp).toBeDefined();
|
|
200
|
+
|
|
201
|
+
const rootChoice = tableData.find(
|
|
202
|
+
(row) => row.type === 'choice' && row.level === 0,
|
|
203
|
+
);
|
|
204
|
+
expect(rootChoice).toBeDefined();
|
|
205
|
+
expect(rootChoice.choiceType).toBe('anyOf');
|
|
206
|
+
expect(rootChoice.options).toHaveLength(2);
|
|
207
|
+
|
|
208
|
+
const optionC = rootChoice.options.find((o) => o.title === 'Has Param C');
|
|
209
|
+
expect(optionC).toBeDefined();
|
|
210
|
+
expect(optionC.rows).toHaveLength(1);
|
|
211
|
+
const paramC = optionC.rows[0];
|
|
212
|
+
expect(paramC.name).toBe('param_c');
|
|
213
|
+
expect(paramC.level).toBe(0); // This is the core of the test
|
|
214
|
+
|
|
215
|
+
const optionD = rootChoice.options.find((o) => o.title === 'Has Param D');
|
|
216
|
+
expect(optionD).toBeDefined();
|
|
217
|
+
expect(optionD.rows).toHaveLength(1);
|
|
218
|
+
const paramD = optionD.rows[0];
|
|
219
|
+
expect(paramD.name).toBe('param_d');
|
|
220
|
+
expect(paramD.level).toBe(0); // This is the core of the test
|
|
221
|
+
});
|
|
222
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { vol } from 'memfs';
|
|
2
|
+
import updateSchemaIds from '../../helpers/update-schema-ids';
|
|
3
|
+
|
|
4
|
+
// Mock the file system
|
|
5
|
+
jest.mock('fs', () => require('memfs').vol);
|
|
6
|
+
|
|
7
|
+
describe('updateSchemaIds', () => {
|
|
8
|
+
const siteDir = '/test-site';
|
|
9
|
+
const url = 'https://example.com';
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
vol.reset();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should recursively update schema IDs for all versions', () => {
|
|
16
|
+
// Create a mock file system
|
|
17
|
+
const versions = ['1.0.0', '2.0.0'];
|
|
18
|
+
const schemaFiles = {
|
|
19
|
+
'versions.json': JSON.stringify(versions),
|
|
20
|
+
'static/schemas/1.0.0/main.json': JSON.stringify({ title: 'Main 1.0.0' }),
|
|
21
|
+
'static/schemas/1.0.0/components/comp.json': JSON.stringify({
|
|
22
|
+
title: 'Comp 1.0.0',
|
|
23
|
+
}),
|
|
24
|
+
'static/schemas/2.0.0/main.json': JSON.stringify({ title: 'Main 2.0.0' }),
|
|
25
|
+
'static/schemas/2.0.0/components/comp.json': JSON.stringify({
|
|
26
|
+
title: 'Comp 2.0.0',
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
vol.fromJSON(schemaFiles, siteDir);
|
|
30
|
+
|
|
31
|
+
// Run the function
|
|
32
|
+
updateSchemaIds(siteDir, url);
|
|
33
|
+
|
|
34
|
+
// Check if the IDs were updated
|
|
35
|
+
const schema1 = JSON.parse(
|
|
36
|
+
vol.readFileSync(`${siteDir}/static/schemas/1.0.0/main.json`, 'utf-8'),
|
|
37
|
+
);
|
|
38
|
+
expect(schema1.$id).toBe('https://example.com/schemas/1.0.0/main.json');
|
|
39
|
+
|
|
40
|
+
const schema2 = JSON.parse(
|
|
41
|
+
vol.readFileSync(
|
|
42
|
+
`${siteDir}/static/schemas/1.0.0/components/comp.json`,
|
|
43
|
+
'utf-8',
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
expect(schema2.$id).toBe(
|
|
47
|
+
'https://example.com/schemas/1.0.0/components/comp.json',
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const schema3 = JSON.parse(
|
|
51
|
+
vol.readFileSync(`${siteDir}/static/schemas/2.0.0/main.json`, 'utf-8'),
|
|
52
|
+
);
|
|
53
|
+
expect(schema3.$id).toBe('https://example.com/schemas/2.0.0/main.json');
|
|
54
|
+
|
|
55
|
+
const schema4 = JSON.parse(
|
|
56
|
+
vol.readFileSync(
|
|
57
|
+
`${siteDir}/static/schemas/2.0.0/components/comp.json`,
|
|
58
|
+
'utf-8',
|
|
59
|
+
),
|
|
60
|
+
);
|
|
61
|
+
expect(schema4.$id).toBe(
|
|
62
|
+
'https://example.com/schemas/2.0.0/components/comp.json',
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should update schema IDs for a single version', () => {
|
|
67
|
+
// Create a mock file system
|
|
68
|
+
const versions = ['1.0.0', '2.0.0'];
|
|
69
|
+
const schemaFiles = {
|
|
70
|
+
'versions.json': JSON.stringify(versions),
|
|
71
|
+
'static/schemas/1.0.0/main.json': JSON.stringify({ title: 'Main 1.0.0' }),
|
|
72
|
+
'static/schemas/2.0.0/main.json': JSON.stringify({ title: 'Main 2.0.0' }),
|
|
73
|
+
};
|
|
74
|
+
vol.fromJSON(schemaFiles, siteDir);
|
|
75
|
+
|
|
76
|
+
// Run the function for a single version
|
|
77
|
+
updateSchemaIds(siteDir, url, '1.0.0');
|
|
78
|
+
|
|
79
|
+
// Check if only the specified version was updated
|
|
80
|
+
const schema1 = JSON.parse(
|
|
81
|
+
vol.readFileSync(`${siteDir}/static/schemas/1.0.0/main.json`, 'utf-8'),
|
|
82
|
+
);
|
|
83
|
+
expect(schema1.$id).toBe('https://example.com/schemas/1.0.0/main.json');
|
|
84
|
+
|
|
85
|
+
const schema2 = JSON.parse(
|
|
86
|
+
vol.readFileSync(`${siteDir}/static/schemas/2.0.0/main.json`, 'utf-8'),
|
|
87
|
+
);
|
|
88
|
+
expect(schema2.$id).toBeUndefined();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should not do anything if versions.json does not exist', () => {
|
|
92
|
+
// Create a mock file system without versions.json
|
|
93
|
+
const schemaFiles = {
|
|
94
|
+
'static/schemas/1.0.0/main.json': JSON.stringify({ title: 'Main 1.0.0' }),
|
|
95
|
+
};
|
|
96
|
+
vol.fromJSON(schemaFiles, siteDir);
|
|
97
|
+
|
|
98
|
+
// Run the function
|
|
99
|
+
updateSchemaIds(siteDir, url);
|
|
100
|
+
|
|
101
|
+
// Check that no files were modified
|
|
102
|
+
const schema1 = JSON.parse(
|
|
103
|
+
vol.readFileSync(`${siteDir}/static/schemas/1.0.0/main.json`, 'utf-8'),
|
|
104
|
+
);
|
|
105
|
+
expect(schema1.$id).toBeUndefined();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import updateSchemaIds from '../helpers/update-schema-ids';
|
|
8
|
+
|
|
9
|
+
describe('updateSchemaIds', () => {
|
|
10
|
+
const siteDir = path.resolve(__dirname, '__fixtures_versioned__');
|
|
11
|
+
const url = 'https://tracking-docs-demo.buchert.digital/';
|
|
12
|
+
|
|
13
|
+
// Create a temporary copy of the fixtures so we don't modify the originals
|
|
14
|
+
const tempSiteDir = path.resolve(__dirname, '__temp_fixtures_versioned__');
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
fs.cpSync(siteDir, tempSiteDir, { recursive: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
fs.rmSync(tempSiteDir, { recursive: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should update the $id of the schemas in the versioned directories', () => {
|
|
25
|
+
updateSchemaIds(tempSiteDir, url);
|
|
26
|
+
|
|
27
|
+
const schemaPath = path.join(
|
|
28
|
+
tempSiteDir,
|
|
29
|
+
'static/schemas',
|
|
30
|
+
'1.1.1',
|
|
31
|
+
'add-to-cart-event.json',
|
|
32
|
+
);
|
|
33
|
+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
|
|
34
|
+
|
|
35
|
+
expect(schema.$id).toBe(
|
|
36
|
+
'https://tracking-docs-demo.buchert.digital/schemas/1.1.1/add-to-cart-event.json',
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -8,93 +8,130 @@ import os from 'os';
|
|
|
8
8
|
import validateSchemas from '../validateSchemas';
|
|
9
9
|
|
|
10
10
|
describe('validateSchemas', () => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
11
|
+
let tmpDir;
|
|
12
|
+
let consoleErrorSpy;
|
|
13
|
+
let consoleLogSpy;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'schema-test-'));
|
|
17
|
+
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
18
|
+
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
23
|
+
jest.restoreAllMocks();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const writeSchema = (dir, fileName, content) => {
|
|
27
|
+
const filePath = path.join(dir, fileName);
|
|
28
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
29
|
+
fs.writeFileSync(filePath, JSON.stringify(content, null, 2));
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const loadFixture = (fixtureName) => {
|
|
33
|
+
const fixturePath = path.resolve(
|
|
34
|
+
__dirname,
|
|
35
|
+
'__fixtures__',
|
|
36
|
+
'validateSchemas',
|
|
37
|
+
fixtureName,
|
|
38
|
+
);
|
|
39
|
+
const schemaContent = fs.readFileSync(fixturePath, 'utf8');
|
|
40
|
+
return JSON.parse(schemaContent);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
it('should return true when all schemas are valid', async () => {
|
|
44
|
+
const validSchema = loadFixture('valid-schema.json');
|
|
45
|
+
writeSchema(tmpDir, 'valid-schema.json', validSchema);
|
|
46
|
+
|
|
47
|
+
const result = await validateSchemas(tmpDir);
|
|
48
|
+
expect(result).toBe(true);
|
|
49
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
50
|
+
'OK Schema valid-schema.json produced a valid example.',
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should return false if an example fails validation', async () => {
|
|
55
|
+
const invalidExampleSchema = loadFixture('invalid-example-schema.json');
|
|
56
|
+
writeSchema(tmpDir, 'invalid-example-schema.json', invalidExampleSchema);
|
|
57
|
+
|
|
58
|
+
const result = await validateSchemas(tmpDir);
|
|
59
|
+
expect(result).toBe(false);
|
|
60
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
61
|
+
'x Schema invalid-example-schema.json example data failed validation:',
|
|
62
|
+
);
|
|
63
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
64
|
+
expect.arrayContaining([
|
|
65
|
+
expect.objectContaining({
|
|
66
|
+
instancePath: '/age',
|
|
67
|
+
keyword: 'type',
|
|
68
|
+
message: 'must be number',
|
|
69
|
+
}),
|
|
70
|
+
]),
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should fail validation for missing required property', async () => {
|
|
75
|
+
const noValidExampleSchema = loadFixture('no-example-schema.json');
|
|
76
|
+
writeSchema(tmpDir, 'no-valid-example-schema.json', noValidExampleSchema);
|
|
77
|
+
|
|
78
|
+
const result = await validateSchemas(tmpDir);
|
|
79
|
+
expect(result).toBe(false);
|
|
80
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
81
|
+
'x Schema no-valid-example-schema.json does not produce a valid example.',
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should handle schemas with $refs correctly', async () => {
|
|
86
|
+
const componentSchema = loadFixture(
|
|
87
|
+
path.join('components', 'referenced.json'),
|
|
88
|
+
);
|
|
89
|
+
const mainSchema = loadFixture('main-schema-with-ref.json');
|
|
90
|
+
|
|
91
|
+
writeSchema(
|
|
92
|
+
path.join(tmpDir, 'components'),
|
|
93
|
+
'referenced.json',
|
|
94
|
+
componentSchema,
|
|
95
|
+
);
|
|
96
|
+
writeSchema(tmpDir, 'main-schema-with-ref.json', mainSchema);
|
|
97
|
+
|
|
98
|
+
const result = await validateSchemas(tmpDir);
|
|
99
|
+
expect(result).toBe(true);
|
|
100
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
101
|
+
'OK Schema main-schema-with-ref.json produced a valid example.',
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should reject if a referenced schema is missing', async () => {
|
|
106
|
+
const mainSchema = loadFixture('main-schema-with-missing-ref.json');
|
|
107
|
+
writeSchema(tmpDir, 'main-schema-with-missing-ref.json', mainSchema);
|
|
108
|
+
|
|
109
|
+
const expectedErrorPath = path.join(tmpDir, 'non-existent-component.json');
|
|
110
|
+
await expect(validateSchemas(tmpDir)).rejects.toThrow(
|
|
111
|
+
expect.objectContaining({
|
|
112
|
+
message: expect.stringContaining(
|
|
113
|
+
`Error opening file ${expectedErrorPath}`,
|
|
114
|
+
),
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should throw an error if duplicate schema IDs are found', async () => {
|
|
120
|
+
const schemaA = {
|
|
121
|
+
$id: 'duplicate-id',
|
|
122
|
+
type: 'object',
|
|
123
|
+
properties: { a: { type: 'string' } },
|
|
30
124
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return JSON.parse(schemaContent);
|
|
125
|
+
const schemaB = {
|
|
126
|
+
$id: 'duplicate-id',
|
|
127
|
+
type: 'object',
|
|
128
|
+
properties: { b: { type: 'string' } },
|
|
36
129
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should return false if an example fails validation', async () => {
|
|
48
|
-
const invalidExampleSchema = loadFixture('invalid-example-schema.json');
|
|
49
|
-
writeSchema(tmpDir, 'invalid-example-schema.json', invalidExampleSchema);
|
|
50
|
-
|
|
51
|
-
const result = await validateSchemas(tmpDir);
|
|
52
|
-
expect(result).toBe(false);
|
|
53
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith('❌ Schema invalid-example-schema.json example data failed validation:');
|
|
54
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.arrayContaining([
|
|
55
|
-
expect.objectContaining({
|
|
56
|
-
instancePath: '/age',
|
|
57
|
-
keyword: 'type',
|
|
58
|
-
message: 'must be number'
|
|
59
|
-
})
|
|
60
|
-
]));
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should fail validation for missing required property', async () => {
|
|
64
|
-
const noValidExampleSchema = loadFixture('no-example-schema.json');
|
|
65
|
-
writeSchema(tmpDir, 'no-valid-example-schema.json', noValidExampleSchema);
|
|
66
|
-
|
|
67
|
-
const result = await validateSchemas(tmpDir);
|
|
68
|
-
expect(result).toBe(false);
|
|
69
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith('❌ Schema no-valid-example-schema.json example data failed validation:');
|
|
70
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.arrayContaining([
|
|
71
|
-
expect.objectContaining({
|
|
72
|
-
keyword: 'required',
|
|
73
|
-
params: { missingProperty: 'some_prop' },
|
|
74
|
-
})
|
|
75
|
-
]));
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should handle schemas with $refs correctly', async () => {
|
|
79
|
-
const componentSchema = loadFixture(path.join('components', 'referenced.json'));
|
|
80
|
-
const mainSchema = loadFixture('main-schema-with-ref.json');
|
|
81
|
-
|
|
82
|
-
writeSchema(path.join(tmpDir, 'components'), 'referenced.json', componentSchema);
|
|
83
|
-
writeSchema(tmpDir, 'main-schema-with-ref.json', mainSchema);
|
|
84
|
-
|
|
85
|
-
const result = await validateSchemas(tmpDir);
|
|
86
|
-
expect(result).toBe(true);
|
|
87
|
-
expect(consoleLogSpy).toHaveBeenCalledWith('✅ Schema main-schema-with-ref.json produced a valid example.');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should reject if a referenced schema is missing', async () => {
|
|
91
|
-
const mainSchema = loadFixture('main-schema-with-missing-ref.json');
|
|
92
|
-
writeSchema(tmpDir, 'main-schema-with-missing-ref.json', mainSchema);
|
|
93
|
-
|
|
94
|
-
const expectedErrorPath = path.join(tmpDir, 'non-existent-component.json');
|
|
95
|
-
await expect(validateSchemas(tmpDir)).rejects.toThrow(expect.objectContaining({
|
|
96
|
-
message: expect.stringContaining(`Error opening file ${expectedErrorPath}`)
|
|
97
|
-
}));
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
});
|
|
130
|
+
writeSchema(path.join(tmpDir, 'A'), 'schema-A.json', schemaA);
|
|
131
|
+
writeSchema(path.join(tmpDir, 'B'), 'schema-B.json', schemaB);
|
|
132
|
+
|
|
133
|
+
await expect(validateSchemas(tmpDir)).rejects.toThrow(
|
|
134
|
+
'schema with key or id "duplicate-id" already exists',
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
});
|