docusaurus-plugin-generate-schema-docs 1.3.0 → 1.5.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 +28 -0
- package/__tests__/ExampleDataLayer.test.js +13 -0
- package/__tests__/__fixtures__/static/schemas/anchor/parent-event-anchor.json +29 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/add-to-cart-event.json +45 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/choice-event.json +78 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/complex-event.json +193 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/components/dataLayer.json +56 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/components/product.json +126 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/purchase-event.json +73 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/root-any-of-event.json +40 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/root-choice-event.json +54 -0
- package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +79 -0
- package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +8 -2
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +20 -5
- package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +8 -2
- package/__tests__/components/PropertiesTable.test.js +34 -1
- package/__tests__/components/PropertyRow.test.js +5 -5
- package/__tests__/generateEventDocs.anchor.test.js +71 -0
- package/__tests__/helpers/example-helper.test.js +71 -0
- package/__tests__/helpers/getConstraints.test.js +72 -27
- package/__tests__/helpers/schemaToTableData.test.js +28 -33
- package/__tests__/helpers/validator.test.js +115 -0
- package/__tests__/validateSchemas-integration.test.js +29 -0
- package/__tests__/validateSchemas.test.js +18 -102
- package/components/ExampleDataLayer.js +14 -5
- package/components/PropertiesTable.js +22 -7
- package/components/PropertiesTable.module.css +27 -0
- package/components/PropertyRow.js +24 -25
- package/components/SchemaRows.css +6 -0
- package/components/SchemaViewer.js +2 -2
- package/components/WordWrapButton.js +31 -0
- package/components/wordWrapButton.module.css +8 -0
- package/generateEventDocs.js +2 -1
- package/helpers/buildExampleFromSchema.js +49 -52
- package/helpers/example-helper.js +41 -0
- package/helpers/file-system.js +1 -2
- package/helpers/getConstraints.js +1 -0
- package/helpers/schema-doc-template.js +8 -1
- package/helpers/schema-processing.js +2 -15
- package/helpers/schemaToExamples.js +46 -74
- package/helpers/schemaToTableData.js +4 -3
- package/helpers/validator.js +108 -0
- package/index.js +18 -9
- package/package.json +1 -2
- package/validateSchemas.js +70 -53
- package/__tests__/helpers/loadSchema.test.js +0 -20
- package/helpers/loadSchema.js +0 -11
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getExamples,
|
|
3
|
+
getSingleExampleValue,
|
|
4
|
+
} from '../../helpers/example-helper';
|
|
5
|
+
|
|
6
|
+
describe('example-helper', () => {
|
|
7
|
+
describe('getExamples', () => {
|
|
8
|
+
it('returns "const" as the only example when present', () => {
|
|
9
|
+
const schema = {
|
|
10
|
+
examples: ['example1'],
|
|
11
|
+
const: 'const-value',
|
|
12
|
+
};
|
|
13
|
+
expect(getExamples(schema)).toEqual(['const-value']);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('aggregates "examples", "example", and "default"', () => {
|
|
17
|
+
const schema = {
|
|
18
|
+
examples: ['example1'],
|
|
19
|
+
example: 'example2',
|
|
20
|
+
default: 'default-value',
|
|
21
|
+
};
|
|
22
|
+
expect(getExamples(schema)).toEqual([
|
|
23
|
+
'example1',
|
|
24
|
+
'example2',
|
|
25
|
+
'default-value',
|
|
26
|
+
]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('handles duplicates', () => {
|
|
30
|
+
const schema = {
|
|
31
|
+
examples: ['example1'],
|
|
32
|
+
example: 'example1',
|
|
33
|
+
default: 'example1',
|
|
34
|
+
};
|
|
35
|
+
expect(getExamples(schema)).toEqual(['example1']);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('getSingleExampleValue', () => {
|
|
40
|
+
it('returns "const" when present', () => {
|
|
41
|
+
const schema = {
|
|
42
|
+
const: 'const-value',
|
|
43
|
+
examples: ['example1'],
|
|
44
|
+
};
|
|
45
|
+
expect(getSingleExampleValue(schema)).toBe('const-value');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('returns the first "examples" when "const" is not present', () => {
|
|
49
|
+
const schema = {
|
|
50
|
+
examples: ['example1', 'example2'],
|
|
51
|
+
default: 'default-value',
|
|
52
|
+
};
|
|
53
|
+
expect(getSingleExampleValue(schema)).toBe('example1');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('returns "example" when "const" and "examples" are not present', () => {
|
|
57
|
+
const schema = {
|
|
58
|
+
example: 'example',
|
|
59
|
+
default: 'default-value',
|
|
60
|
+
};
|
|
61
|
+
expect(getSingleExampleValue(schema)).toBe('example');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('returns "default" as a fallback', () => {
|
|
65
|
+
const schema = {
|
|
66
|
+
default: 'default-value',
|
|
67
|
+
};
|
|
68
|
+
expect(getSingleExampleValue(schema)).toBe('default-value');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -2,8 +2,11 @@ import { getConstraints } from '../../helpers/getConstraints';
|
|
|
2
2
|
|
|
3
3
|
describe('getConstraints', () => {
|
|
4
4
|
it('should return "required" if isReq is true', () => {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
expect(getConstraints({}, true)).toEqual(['required']);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('should return an empty array if isReq is false and there are no other constraints', () => {
|
|
9
|
+
expect(getConstraints({}, false)).toEqual([]);
|
|
7
10
|
});
|
|
8
11
|
|
|
9
12
|
it('should handle simple key-value constraints', () => {
|
|
@@ -12,47 +15,89 @@ describe('getConstraints', () => {
|
|
|
12
15
|
maxLength: 10,
|
|
13
16
|
minimum: 0,
|
|
14
17
|
maximum: 100,
|
|
18
|
+
exclusiveMinimum: 0,
|
|
19
|
+
exclusiveMaximum: 100,
|
|
15
20
|
minItems: 1,
|
|
16
21
|
maxItems: 5,
|
|
22
|
+
minProperties: 2,
|
|
23
|
+
maxProperties: 3,
|
|
24
|
+
multipleOf: 2,
|
|
25
|
+
format: 'email',
|
|
26
|
+
minContains: 1,
|
|
27
|
+
maxContains: 3,
|
|
17
28
|
};
|
|
18
|
-
const
|
|
19
|
-
expect(constraints).toEqual([
|
|
29
|
+
const expected = [
|
|
20
30
|
'minLength: 1',
|
|
21
31
|
'maxLength: 10',
|
|
22
32
|
'minimum: 0',
|
|
23
33
|
'maximum: 100',
|
|
34
|
+
'exclusiveMinimum: 0',
|
|
35
|
+
'exclusiveMaximum: 100',
|
|
24
36
|
'minItems: 1',
|
|
25
37
|
'maxItems: 5',
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
'minProperties: 2',
|
|
39
|
+
'maxProperties: 3',
|
|
40
|
+
'multipleOf: 2',
|
|
41
|
+
'format: email',
|
|
42
|
+
'minContains: 1',
|
|
43
|
+
'maxContains: 3',
|
|
44
|
+
];
|
|
45
|
+
expect(getConstraints(prop, false)).toEqual(
|
|
46
|
+
expect.arrayContaining(expected),
|
|
47
|
+
);
|
|
33
48
|
});
|
|
34
49
|
|
|
35
|
-
it('should handle
|
|
36
|
-
const prop = {
|
|
37
|
-
|
|
38
|
-
|
|
50
|
+
it('should handle special-cased constraints', () => {
|
|
51
|
+
const prop = {
|
|
52
|
+
pattern: '^[a-z]+$',
|
|
53
|
+
uniqueItems: true,
|
|
54
|
+
additionalProperties: false,
|
|
55
|
+
propertyNames: { pattern: '^[A-Z][a-zA-Z0-9]*$' },
|
|
56
|
+
dependentRequired: {
|
|
57
|
+
prop1: ['prop2', 'prop3'],
|
|
58
|
+
},
|
|
59
|
+
contains: { type: 'string' },
|
|
60
|
+
enum: ['a', 'b', 'c'],
|
|
61
|
+
const: 'hello',
|
|
62
|
+
};
|
|
63
|
+
const expected = [
|
|
64
|
+
'pattern: /^[a-z]+$/',
|
|
65
|
+
'uniqueItems: true',
|
|
66
|
+
'additionalProperties: false',
|
|
67
|
+
'propertyNames: {"pattern":"^[A-Z][a-zA-Z0-9]*$"}',
|
|
68
|
+
'dependentRequired: prop1 -> [prop2, prop3]',
|
|
69
|
+
'contains: {"type":"string"}',
|
|
70
|
+
'enum: [a, b, c]',
|
|
71
|
+
'const: "hello"',
|
|
72
|
+
];
|
|
73
|
+
expect(getConstraints(prop, false)).toEqual(
|
|
74
|
+
expect.arrayContaining(expected),
|
|
75
|
+
);
|
|
39
76
|
});
|
|
40
77
|
|
|
41
|
-
it('should handle
|
|
42
|
-
const prop = {
|
|
43
|
-
|
|
44
|
-
|
|
78
|
+
it('should handle default constraint', () => {
|
|
79
|
+
const prop = {
|
|
80
|
+
default: 'default value',
|
|
81
|
+
};
|
|
82
|
+
const expected = ['default: "default value"'];
|
|
83
|
+
expect(getConstraints(prop, false)).toEqual(expected);
|
|
45
84
|
});
|
|
46
85
|
|
|
47
|
-
it('should
|
|
48
|
-
const prop = {
|
|
49
|
-
|
|
50
|
-
|
|
86
|
+
it('should not include a constraint for a value of undefined', () => {
|
|
87
|
+
const prop = {
|
|
88
|
+
minLength: undefined,
|
|
89
|
+
};
|
|
90
|
+
expect(getConstraints(prop, false)).toEqual([]);
|
|
51
91
|
});
|
|
52
92
|
|
|
53
|
-
it('should handle
|
|
54
|
-
const prop = {
|
|
55
|
-
|
|
56
|
-
|
|
93
|
+
it('should handle a mix of constraints', () => {
|
|
94
|
+
const prop = {
|
|
95
|
+
minLength: 5,
|
|
96
|
+
pattern: '^[a-zA-Z0-9]*$',
|
|
97
|
+
};
|
|
98
|
+
const expected = ['minLength: 5', 'pattern: /^[a-zA-Z0-9]*$/'];
|
|
99
|
+
expect(getConstraints(prop, true)).toEqual(
|
|
100
|
+
expect.arrayContaining(['required', ...expected]),
|
|
101
|
+
);
|
|
57
102
|
});
|
|
58
103
|
});
|
|
@@ -118,39 +118,6 @@ describe('schemaToTableData', () => {
|
|
|
118
118
|
expect(choiceRow.choiceType).toBe('oneOf');
|
|
119
119
|
});
|
|
120
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
121
|
it('handles "oneOf" nested inside a property', () => {
|
|
155
122
|
const schema = {
|
|
156
123
|
properties: {
|
|
@@ -219,4 +186,32 @@ describe('schemaToTableData', () => {
|
|
|
219
186
|
expect(paramD.name).toBe('param_d');
|
|
220
187
|
expect(paramD.level).toBe(0); // This is the core of the test
|
|
221
188
|
});
|
|
189
|
+
|
|
190
|
+
it('uses "const" as example if "examples" and "example" are not present', () => {
|
|
191
|
+
const schema = {
|
|
192
|
+
properties: {
|
|
193
|
+
prop_with_const: {
|
|
194
|
+
type: 'string',
|
|
195
|
+
const: 'const-value',
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const tableData = schemaToTableData(schema);
|
|
201
|
+
expect(tableData[0].examples).toEqual(['const-value']);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('uses "default" as example if "examples", "example", and "const" are not present', () => {
|
|
205
|
+
const schema = {
|
|
206
|
+
properties: {
|
|
207
|
+
prop_with_default: {
|
|
208
|
+
type: 'string',
|
|
209
|
+
default: 'default-value',
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const tableData = schemaToTableData(schema);
|
|
215
|
+
expect(tableData[0].examples).toEqual(['default-value']);
|
|
216
|
+
});
|
|
222
217
|
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createValidator } from '../../helpers/validator';
|
|
2
|
+
|
|
3
|
+
describe('createValidator', () => {
|
|
4
|
+
it('creates a validator that returns true for valid data with no schema version (draft-07)', async () => {
|
|
5
|
+
const schema = {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
name: {
|
|
9
|
+
type: 'string',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
required: ['name'],
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const validator = await createValidator(schema);
|
|
16
|
+
const result = validator({ name: 'test' });
|
|
17
|
+
|
|
18
|
+
expect(result.valid).toBe(true);
|
|
19
|
+
expect(result.errors).toEqual([]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('creates a validator that returns false for invalid data with no schema version (draft-07)', async () => {
|
|
23
|
+
const schema = {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
name: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ['name'],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const validator = await createValidator(schema);
|
|
34
|
+
const result = validator({ name: 123 });
|
|
35
|
+
|
|
36
|
+
expect(result.valid).toBe(false);
|
|
37
|
+
expect(result.errors).not.toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('creates a validator that can validate against a draft-04 schema', async () => {
|
|
41
|
+
const schema = {
|
|
42
|
+
$schema: 'http://json-schema.org/draft-04/schema#',
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {
|
|
45
|
+
name: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
required: ['name'],
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const validator = await createValidator(schema);
|
|
53
|
+
const result = validator({ name: 'test' });
|
|
54
|
+
|
|
55
|
+
expect(result.valid).toBe(true);
|
|
56
|
+
expect(result.errors).toEqual([]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('creates a validator that can validate against a draft-07 schema', async () => {
|
|
60
|
+
const schema = {
|
|
61
|
+
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
62
|
+
type: 'object',
|
|
63
|
+
properties: {
|
|
64
|
+
name: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
required: ['name'],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const validator = await createValidator(schema);
|
|
72
|
+
const result = validator({ name: 'test' });
|
|
73
|
+
|
|
74
|
+
expect(result.valid).toBe(true);
|
|
75
|
+
expect(result.errors).toEqual([]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('creates a validator that can validate against a 2019-09 schema', async () => {
|
|
79
|
+
const schema = {
|
|
80
|
+
$schema: 'https://json-schema.org/draft/2019-09/schema',
|
|
81
|
+
type: 'object',
|
|
82
|
+
properties: {
|
|
83
|
+
name: {
|
|
84
|
+
type: 'string',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
required: ['name'],
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const validator = await createValidator(schema);
|
|
91
|
+
const result = validator({ name: 'test' });
|
|
92
|
+
|
|
93
|
+
expect(result.valid).toBe(true);
|
|
94
|
+
expect(result.errors).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('creates a validator that can validate against a 2020-12 schema', async () => {
|
|
98
|
+
const schema = {
|
|
99
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {
|
|
102
|
+
name: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
required: ['name'],
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const validator = await createValidator(schema);
|
|
110
|
+
const result = validator({ name: 'test' });
|
|
111
|
+
|
|
112
|
+
expect(result.valid).toBe(true);
|
|
113
|
+
expect(result.errors).toEqual([]);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import validateSchemas from '../validateSchemas';
|
|
7
|
+
import { getPathsForVersion } from '../helpers/path-helpers';
|
|
8
|
+
|
|
9
|
+
describe('validateSchemas - Integration', () => {
|
|
10
|
+
let consoleErrorSpy;
|
|
11
|
+
let consoleLogSpy;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
// Spy on console.error and console.log to keep test output clean
|
|
15
|
+
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
16
|
+
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
jest.restoreAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should return true for the "next" version schemas', async () => {
|
|
24
|
+
const siteDir = path.resolve(__dirname, '../../../demo');
|
|
25
|
+
const { schemaDir } = getPathsForVersion('next', siteDir);
|
|
26
|
+
const result = await validateSchemas(schemaDir);
|
|
27
|
+
expect(result).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -14,124 +14,40 @@ describe('validateSchemas', () => {
|
|
|
14
14
|
|
|
15
15
|
beforeEach(() => {
|
|
16
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
17
|
});
|
|
20
18
|
|
|
21
19
|
afterEach(() => {
|
|
22
20
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
23
|
-
jest.restoreAllMocks();
|
|
24
21
|
});
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
const
|
|
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(
|
|
23
|
+
it('should return true for a complex set of valid schemas', async () => {
|
|
24
|
+
const fixtureDir = path.resolve(
|
|
34
25
|
__dirname,
|
|
35
26
|
'__fixtures__',
|
|
36
27
|
'validateSchemas',
|
|
37
|
-
|
|
28
|
+
'complex-validation',
|
|
38
29
|
);
|
|
39
|
-
const
|
|
40
|
-
|
|
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);
|
|
30
|
+
const schemaDir = path.join(tmpDir, 'schemas');
|
|
31
|
+
fs.mkdirSync(schemaDir, { recursive: true });
|
|
32
|
+
fs.cpSync(fixtureDir, schemaDir, { recursive: true });
|
|
33
|
+
const result = await validateSchemas(schemaDir);
|
|
48
34
|
expect(result).toBe(true);
|
|
49
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
50
|
-
'OK Schema valid-schema.json produced a valid example.',
|
|
51
|
-
);
|
|
52
35
|
});
|
|
53
36
|
|
|
54
37
|
it('should return false if an example fails validation', async () => {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
61
|
-
'x Schema invalid-example-schema.json example data failed validation:',
|
|
38
|
+
const fixturePath = path.resolve(
|
|
39
|
+
__dirname,
|
|
40
|
+
'__fixtures__',
|
|
41
|
+
'validateSchemas',
|
|
42
|
+
'invalid-example-schema.json',
|
|
62
43
|
);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
message: 'must be number',
|
|
69
|
-
}),
|
|
70
|
-
]),
|
|
44
|
+
const schemaDir = path.join(tmpDir, 'schemas');
|
|
45
|
+
fs.mkdirSync(schemaDir, { recursive: true });
|
|
46
|
+
fs.copyFileSync(
|
|
47
|
+
fixturePath,
|
|
48
|
+
path.join(schemaDir, 'invalid-example-schema.json'),
|
|
71
49
|
);
|
|
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);
|
|
50
|
+
const result = await validateSchemas(schemaDir);
|
|
79
51
|
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' } },
|
|
124
|
-
};
|
|
125
|
-
const schemaB = {
|
|
126
|
-
$id: 'duplicate-id',
|
|
127
|
-
type: 'object',
|
|
128
|
-
properties: { b: { type: 'string' } },
|
|
129
|
-
};
|
|
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
52
|
});
|
|
137
53
|
});
|
|
@@ -5,7 +5,7 @@ import TabItem from '@theme/TabItem';
|
|
|
5
5
|
import Heading from '@theme/Heading';
|
|
6
6
|
import { schemaToExamples } from '../helpers/schemaToExamples';
|
|
7
7
|
|
|
8
|
-
const generateCodeSnippet = (example, schema) => {
|
|
8
|
+
const generateCodeSnippet = (example, schema, dataLayerName = 'dataLayer') => {
|
|
9
9
|
const clearableProperties = findClearableProperties(schema || {});
|
|
10
10
|
let codeSnippet = '';
|
|
11
11
|
const propertiesToClear = clearableProperties.filter(
|
|
@@ -17,14 +17,22 @@ const generateCodeSnippet = (example, schema) => {
|
|
|
17
17
|
propertiesToClear.forEach((prop) => {
|
|
18
18
|
resetObject[prop] = null;
|
|
19
19
|
});
|
|
20
|
-
codeSnippet += `window.
|
|
20
|
+
codeSnippet += `window.${dataLayerName}.push(${JSON.stringify(
|
|
21
|
+
resetObject,
|
|
22
|
+
null,
|
|
23
|
+
2,
|
|
24
|
+
)});\n`;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
|
-
codeSnippet += `window.
|
|
27
|
+
codeSnippet += `window.${dataLayerName}.push(${JSON.stringify(
|
|
28
|
+
example,
|
|
29
|
+
null,
|
|
30
|
+
2,
|
|
31
|
+
)});`;
|
|
24
32
|
return codeSnippet;
|
|
25
33
|
};
|
|
26
34
|
|
|
27
|
-
export default function ExampleDataLayer({ schema }) {
|
|
35
|
+
export default function ExampleDataLayer({ schema, dataLayerName }) {
|
|
28
36
|
const exampleGroups = schemaToExamples(schema);
|
|
29
37
|
|
|
30
38
|
if (!exampleGroups || exampleGroups.length === 0) {
|
|
@@ -36,6 +44,7 @@ export default function ExampleDataLayer({ schema }) {
|
|
|
36
44
|
const codeSnippet = generateCodeSnippet(
|
|
37
45
|
exampleGroups[0].options[0].example,
|
|
38
46
|
schema,
|
|
47
|
+
dataLayerName,
|
|
39
48
|
);
|
|
40
49
|
return <CodeBlock language="javascript">{codeSnippet}</CodeBlock>;
|
|
41
50
|
}
|
|
@@ -51,7 +60,7 @@ export default function ExampleDataLayer({ schema }) {
|
|
|
51
60
|
{group.options.map(({ title, example }, index) => (
|
|
52
61
|
<TabItem value={index} label={title} key={index}>
|
|
53
62
|
<CodeBlock language="javascript">
|
|
54
|
-
{generateCodeSnippet(example, schema)}
|
|
63
|
+
{generateCodeSnippet(example, schema, dataLayerName)}
|
|
55
64
|
</CodeBlock>
|
|
56
65
|
</TabItem>
|
|
57
66
|
))}
|
|
@@ -1,17 +1,32 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import SchemaRows from './SchemaRows';
|
|
3
3
|
import TableHeader from './TableHeader';
|
|
4
|
+
import WordWrapButton from './WordWrapButton';
|
|
4
5
|
import { schemaToTableData } from '../helpers/schemaToTableData';
|
|
6
|
+
import styles from './PropertiesTable.module.css';
|
|
5
7
|
|
|
6
8
|
export default function PropertiesTable({ schema }) {
|
|
9
|
+
const [isWordWrapOn, setIsWordWrapOn] = useState(true);
|
|
7
10
|
const tableData = schemaToTableData(schema);
|
|
8
11
|
|
|
9
12
|
return (
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
<div
|
|
14
|
+
className={`${styles.tableWrapper} ${
|
|
15
|
+
!isWordWrapOn ? styles.noWordWrap : ''
|
|
16
|
+
}`}
|
|
17
|
+
>
|
|
18
|
+
<div className={styles.buttonGroup}>
|
|
19
|
+
<WordWrapButton
|
|
20
|
+
onClick={() => setIsWordWrapOn((w) => !w)}
|
|
21
|
+
isEnabled={isWordWrapOn}
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
<table>
|
|
25
|
+
<TableHeader />
|
|
26
|
+
<tbody>
|
|
27
|
+
<SchemaRows tableData={tableData} />
|
|
28
|
+
</tbody>
|
|
29
|
+
</table>
|
|
30
|
+
</div>
|
|
16
31
|
);
|
|
17
32
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.tableWrapper {
|
|
2
|
+
position: relative;
|
|
3
|
+
margin-top: 1rem;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.tableWrapper code {
|
|
7
|
+
white-space: pre-wrap;
|
|
8
|
+
word-break: break-word;
|
|
9
|
+
min-width: auto;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.tableWrapper td {
|
|
13
|
+
min-width: 5.3rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.noWordWrap code {
|
|
17
|
+
white-space: pre;
|
|
18
|
+
word-break: normal;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.buttonGroup {
|
|
22
|
+
position: absolute;
|
|
23
|
+
right: 0;
|
|
24
|
+
top: -2.7rem;
|
|
25
|
+
display: flex;
|
|
26
|
+
gap: 0.5rem;
|
|
27
|
+
}
|