docusaurus-plugin-generate-schema-docs 1.8.2 → 1.8.4
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 +2 -0
- package/__tests__/__fixtures__/validateSchemas/main-schema-with-not-allof.json +11 -0
- package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +21 -3
- package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +26 -4
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +45 -6
- package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +16 -2
- package/__tests__/components/ConditionalRows.test.js +28 -0
- package/__tests__/components/FoldableRows.test.js +31 -290
- package/__tests__/components/PropertiesTable.test.js +66 -0
- package/__tests__/components/PropertyRow.test.js +297 -0
- package/__tests__/components/SchemaJsonViewer.test.js +194 -10
- package/__tests__/components/SchemaRows.test.js +62 -12
- package/__tests__/components/__snapshots__/ConnectorLines.visualRegression.test.js.snap +3 -3
- package/__tests__/generateEventDocs.test.js +3 -0
- package/__tests__/helpers/example-helper.test.js +12 -0
- package/__tests__/helpers/getConstraints.test.js +16 -0
- package/__tests__/helpers/processSchema.test.js +18 -0
- package/__tests__/helpers/schemaToTableData.test.js +112 -0
- package/__tests__/helpers/schemaTraversal.test.js +110 -0
- package/__tests__/syncGtm.test.js +227 -3
- package/__tests__/validateSchemas.test.js +50 -0
- package/components/ConditionalRows.js +6 -3
- package/components/FoldableRows.js +9 -3
- package/components/PropertiesTable.js +34 -3
- package/components/PropertyRow.js +118 -6
- package/components/SchemaJsonViewer.js +324 -4
- package/components/SchemaRows.css +138 -7
- package/components/SchemaRows.js +11 -1
- package/components/SchemaViewer.js +11 -2
- package/generateEventDocs.js +87 -1
- package/helpers/choice-index-template.js +6 -2
- package/helpers/example-helper.js +2 -2
- package/helpers/file-system.js +28 -0
- package/helpers/getConstraints.js +20 -0
- package/helpers/processSchema.js +32 -1
- package/helpers/schema-doc-template.js +11 -1
- package/helpers/schemaToExamples.js +29 -35
- package/helpers/schemaToTableData.js +68 -7
- package/helpers/schemaTraversal.cjs +148 -0
- package/package.json +1 -1
- package/scripts/sync-gtm.js +41 -28
- package/test-data/payloadContracts.js +35 -0
- package/validateSchemas.js +1 -1
|
@@ -1,9 +1,35 @@
|
|
|
1
1
|
import '@testing-library/jest-dom';
|
|
2
|
+
import fs from 'fs';
|
|
2
3
|
import React from 'react';
|
|
4
|
+
import path from 'path';
|
|
3
5
|
import { render } from '@testing-library/react';
|
|
4
6
|
import PropertyRow from '../../components/PropertyRow';
|
|
5
7
|
|
|
6
8
|
describe('PropertyRow', () => {
|
|
9
|
+
it('does not suppress first-column separators for nested tree cells in CSS', () => {
|
|
10
|
+
const cssPath = path.join(__dirname, '../../components/SchemaRows.css');
|
|
11
|
+
const css = fs.readFileSync(cssPath, 'utf8');
|
|
12
|
+
|
|
13
|
+
expect(css).not.toContain(
|
|
14
|
+
'.schema-table tbody tr + tr td.property-cell--tree',
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('removes the extra left border from full-width schema rows in CSS', () => {
|
|
19
|
+
const cssPath = path.join(__dirname, '../../components/SchemaRows.css');
|
|
20
|
+
const css = fs.readFileSync(cssPath, 'utf8');
|
|
21
|
+
|
|
22
|
+
expect(css).toContain(".schema-table td[colspan='5']");
|
|
23
|
+
expect(css).toContain('border-left: none;');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('defines an explicit neutral class for control rows in CSS', () => {
|
|
27
|
+
const cssPath = path.join(__dirname, '../../components/SchemaRows.css');
|
|
28
|
+
const css = fs.readFileSync(cssPath, 'utf8');
|
|
29
|
+
|
|
30
|
+
expect(css).toContain('.schema-row--control');
|
|
31
|
+
expect(css).toContain('background-color: transparent');
|
|
32
|
+
});
|
|
7
33
|
it('renders a basic property', () => {
|
|
8
34
|
const row = {
|
|
9
35
|
name: 'name',
|
|
@@ -30,6 +56,31 @@ describe('PropertyRow', () => {
|
|
|
30
56
|
expect(getByText('"John Doe"')).toBeInTheDocument();
|
|
31
57
|
});
|
|
32
58
|
|
|
59
|
+
it('renders array type values as comma-separated text', () => {
|
|
60
|
+
const row = {
|
|
61
|
+
name: 'additionalProperties',
|
|
62
|
+
level: 0,
|
|
63
|
+
required: false,
|
|
64
|
+
propertyType: ['string', 'number', 'integer', 'boolean', 'null'],
|
|
65
|
+
description: 'Catch-all values.',
|
|
66
|
+
examples: [],
|
|
67
|
+
constraints: [],
|
|
68
|
+
path: ['additionalProperties'],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const { getByText } = render(
|
|
72
|
+
<table>
|
|
73
|
+
<tbody>
|
|
74
|
+
<PropertyRow row={row} />
|
|
75
|
+
</tbody>
|
|
76
|
+
</table>,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(
|
|
80
|
+
getByText('string, number, integer, boolean, null'),
|
|
81
|
+
).toBeInTheDocument();
|
|
82
|
+
});
|
|
83
|
+
|
|
33
84
|
it('marks required properties', () => {
|
|
34
85
|
const row = {
|
|
35
86
|
name: 'name',
|
|
@@ -51,10 +102,40 @@ describe('PropertyRow', () => {
|
|
|
51
102
|
);
|
|
52
103
|
|
|
53
104
|
expect(container.querySelector('.required-row')).toBeInTheDocument();
|
|
105
|
+
expect(
|
|
106
|
+
container.querySelector('.property-cell--required'),
|
|
107
|
+
).toBeInTheDocument();
|
|
54
108
|
expect(container.querySelector('.required')).toBeInTheDocument();
|
|
55
109
|
expect(getByText('required')).toBeInTheDocument();
|
|
56
110
|
});
|
|
57
111
|
|
|
112
|
+
it('applies the same zebra class to the main row and continuation rows', () => {
|
|
113
|
+
const row = {
|
|
114
|
+
name: 'event',
|
|
115
|
+
level: 0,
|
|
116
|
+
required: true,
|
|
117
|
+
propertyType: 'string',
|
|
118
|
+
description: 'Event name.',
|
|
119
|
+
examples: ['purchase'],
|
|
120
|
+
constraints: ['required', 'const: "purchase"'],
|
|
121
|
+
path: ['event'],
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const { container } = render(
|
|
125
|
+
<table>
|
|
126
|
+
<tbody>
|
|
127
|
+
<PropertyRow row={row} stripeIndex={1} />
|
|
128
|
+
</tbody>
|
|
129
|
+
</table>,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const rows = container.querySelectorAll('tbody tr');
|
|
133
|
+
expect(rows).toHaveLength(2);
|
|
134
|
+
expect(rows[0]).toHaveClass('schema-row--zebra-odd');
|
|
135
|
+
expect(rows[1]).toHaveClass('schema-row--zebra-odd');
|
|
136
|
+
expect(rows[0]).not.toHaveClass('schema-row--zebra-even');
|
|
137
|
+
});
|
|
138
|
+
|
|
58
139
|
it('renders multiple constraints', () => {
|
|
59
140
|
const row = {
|
|
60
141
|
name: 'name',
|
|
@@ -156,6 +237,163 @@ describe('PropertyRow', () => {
|
|
|
156
237
|
expect(cells[2].innerHTML).toBe('');
|
|
157
238
|
});
|
|
158
239
|
|
|
240
|
+
it('renders additionalProperties as a schema keyword row and keeps its connector open', () => {
|
|
241
|
+
const row = {
|
|
242
|
+
name: 'additionalProperties',
|
|
243
|
+
level: 1,
|
|
244
|
+
required: false,
|
|
245
|
+
propertyType: 'string',
|
|
246
|
+
description: 'Catch-all values.',
|
|
247
|
+
examples: ['beta_tester'],
|
|
248
|
+
constraints: [],
|
|
249
|
+
path: ['user_properties', 'additionalProperties'],
|
|
250
|
+
hasChildren: false,
|
|
251
|
+
containerType: null,
|
|
252
|
+
continuingLevels: [],
|
|
253
|
+
isSchemaKeywordRow: true,
|
|
254
|
+
keepConnectorOpen: false,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const { container, getByText } = render(
|
|
258
|
+
<table>
|
|
259
|
+
<tbody>
|
|
260
|
+
<PropertyRow row={row} isLastInGroup={true} />
|
|
261
|
+
</tbody>
|
|
262
|
+
</table>,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
const keyword = getByText('additionalProperties');
|
|
266
|
+
expect(keyword).toBeInTheDocument();
|
|
267
|
+
expect(container.querySelector('.property-keyword')).toBeInTheDocument();
|
|
268
|
+
expect(
|
|
269
|
+
container.querySelector('.property-cell--keyword'),
|
|
270
|
+
).toBeInTheDocument();
|
|
271
|
+
expect(container.querySelector('.property-cell--tree')).toBeInTheDocument();
|
|
272
|
+
expect(
|
|
273
|
+
container.querySelector('.property-name--keyword'),
|
|
274
|
+
).toBeInTheDocument();
|
|
275
|
+
expect(
|
|
276
|
+
container.querySelector('.property-keyword-badge'),
|
|
277
|
+
).toBeInTheDocument();
|
|
278
|
+
expect(getByText('Schema constraint')).toBeInTheDocument();
|
|
279
|
+
expect(keyword).not.toHaveAttribute('title');
|
|
280
|
+
expect(keyword).toHaveAttribute(
|
|
281
|
+
'aria-describedby',
|
|
282
|
+
'schema-keyword-help-user_properties-additionalProperties',
|
|
283
|
+
);
|
|
284
|
+
expect(
|
|
285
|
+
container.querySelector('.property-keyword-tooltip'),
|
|
286
|
+
).toBeInTheDocument();
|
|
287
|
+
expect(
|
|
288
|
+
container.querySelector(
|
|
289
|
+
'#schema-keyword-help-user_properties-additionalProperties',
|
|
290
|
+
),
|
|
291
|
+
).toBeInTheDocument();
|
|
292
|
+
expect(
|
|
293
|
+
getByText(
|
|
294
|
+
'Controls properties not listed in properties and not matched by patternProperties.',
|
|
295
|
+
),
|
|
296
|
+
).toBeInTheDocument();
|
|
297
|
+
expect(container.querySelector('.is-last')).toBeInTheDocument();
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('defines keyword badge styles in CSS', () => {
|
|
301
|
+
const cssPath = path.join(__dirname, '../../components/SchemaRows.css');
|
|
302
|
+
const css = fs.readFileSync(cssPath, 'utf8');
|
|
303
|
+
|
|
304
|
+
expect(css).toContain('.property-keyword-badge');
|
|
305
|
+
expect(css).toContain('text-transform: uppercase');
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('renders patternProperties rows as schema keywords with tooltip help', () => {
|
|
309
|
+
const row = {
|
|
310
|
+
name: 'patternProperties /^custom_/',
|
|
311
|
+
level: 1,
|
|
312
|
+
required: false,
|
|
313
|
+
propertyType: 'number',
|
|
314
|
+
description: 'Numeric custom attribute values.',
|
|
315
|
+
examples: [5],
|
|
316
|
+
constraints: ['minimum: 0'],
|
|
317
|
+
path: ['attributes', 'patternProperties /^custom_/'],
|
|
318
|
+
hasChildren: false,
|
|
319
|
+
containerType: null,
|
|
320
|
+
continuingLevels: [],
|
|
321
|
+
isSchemaKeywordRow: true,
|
|
322
|
+
keepConnectorOpen: false,
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const { container, getByText } = render(
|
|
326
|
+
<table>
|
|
327
|
+
<tbody>
|
|
328
|
+
<PropertyRow row={row} isLastInGroup={true} />
|
|
329
|
+
</tbody>
|
|
330
|
+
</table>,
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
const keyword = getByText('patternProperties');
|
|
334
|
+
expect(getByText('/^custom_/')).toBeInTheDocument();
|
|
335
|
+
expect(
|
|
336
|
+
container.querySelector('.property-keyword-pattern'),
|
|
337
|
+
).toBeInTheDocument();
|
|
338
|
+
expect(keyword).toHaveAttribute(
|
|
339
|
+
'aria-describedby',
|
|
340
|
+
'schema-keyword-help-attributes-patternProperties___custom__',
|
|
341
|
+
);
|
|
342
|
+
expect(
|
|
343
|
+
container.ownerDocument.getElementById(
|
|
344
|
+
'schema-keyword-help-attributes-patternProperties___custom__',
|
|
345
|
+
),
|
|
346
|
+
).toBeInTheDocument();
|
|
347
|
+
expect(
|
|
348
|
+
getByText(
|
|
349
|
+
'Applies the subschema to property names that match the given regular expression.',
|
|
350
|
+
),
|
|
351
|
+
).toBeInTheDocument();
|
|
352
|
+
expect(container.querySelector('.is-last')).toBeInTheDocument();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('uses unique tooltip ids for repeated schema keyword rows', () => {
|
|
356
|
+
const firstRow = {
|
|
357
|
+
name: 'additionalProperties',
|
|
358
|
+
level: 1,
|
|
359
|
+
required: false,
|
|
360
|
+
propertyType: 'string',
|
|
361
|
+
description: 'User property values.',
|
|
362
|
+
examples: ['beta_tester'],
|
|
363
|
+
constraints: [],
|
|
364
|
+
path: ['user_properties', 'additionalProperties'],
|
|
365
|
+
hasChildren: false,
|
|
366
|
+
containerType: null,
|
|
367
|
+
continuingLevels: [],
|
|
368
|
+
isSchemaKeywordRow: true,
|
|
369
|
+
keepConnectorOpen: false,
|
|
370
|
+
};
|
|
371
|
+
const secondRow = {
|
|
372
|
+
...firstRow,
|
|
373
|
+
path: ['metadata', 'additionalProperties'],
|
|
374
|
+
description: 'Metadata values.',
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const { container, getAllByText } = render(
|
|
378
|
+
<table>
|
|
379
|
+
<tbody>
|
|
380
|
+
<PropertyRow row={firstRow} isLastInGroup={true} />
|
|
381
|
+
<PropertyRow row={secondRow} isLastInGroup={true} />
|
|
382
|
+
</tbody>
|
|
383
|
+
</table>,
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
const keywords = getAllByText('additionalProperties');
|
|
387
|
+
const firstTooltipId = keywords[0].getAttribute('aria-describedby');
|
|
388
|
+
const secondTooltipId = keywords[1].getAttribute('aria-describedby');
|
|
389
|
+
|
|
390
|
+
expect(firstTooltipId).toBeTruthy();
|
|
391
|
+
expect(secondTooltipId).toBeTruthy();
|
|
392
|
+
expect(firstTooltipId).not.toBe(secondTooltipId);
|
|
393
|
+
expect(container.querySelector(`#${firstTooltipId}`)).toBeInTheDocument();
|
|
394
|
+
expect(container.querySelector(`#${secondTooltipId}`)).toBeInTheDocument();
|
|
395
|
+
});
|
|
396
|
+
|
|
159
397
|
describe('hierarchical lines feature', () => {
|
|
160
398
|
it('renders {} symbol for object containers', () => {
|
|
161
399
|
const row = {
|
|
@@ -368,6 +606,65 @@ describe('PropertyRow', () => {
|
|
|
368
606
|
);
|
|
369
607
|
|
|
370
608
|
expect(container.querySelector('.level-2')).toBeInTheDocument();
|
|
609
|
+
expect(
|
|
610
|
+
container.querySelector('.property-cell--tree'),
|
|
611
|
+
).toBeInTheDocument();
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it('does not add a separator-suppression class to nested tree cells', () => {
|
|
615
|
+
const row = {
|
|
616
|
+
name: 'nested_item',
|
|
617
|
+
level: 2,
|
|
618
|
+
required: false,
|
|
619
|
+
propertyType: 'string',
|
|
620
|
+
description: '',
|
|
621
|
+
example: '',
|
|
622
|
+
constraints: [],
|
|
623
|
+
path: ['parent', 'nested_item'],
|
|
624
|
+
hasChildren: false,
|
|
625
|
+
containerType: null,
|
|
626
|
+
continuingLevels: [],
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
const { container } = render(
|
|
630
|
+
<table>
|
|
631
|
+
<tbody>
|
|
632
|
+
<PropertyRow row={row} />
|
|
633
|
+
</tbody>
|
|
634
|
+
</table>,
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
expect(
|
|
638
|
+
container.querySelector('.property-cell--tree-no-separator'),
|
|
639
|
+
).not.toBeInTheDocument();
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it('does not mark root-level cells as tree cells', () => {
|
|
643
|
+
const row = {
|
|
644
|
+
name: 'root_item',
|
|
645
|
+
level: 0,
|
|
646
|
+
required: false,
|
|
647
|
+
propertyType: 'string',
|
|
648
|
+
description: '',
|
|
649
|
+
example: '',
|
|
650
|
+
constraints: [],
|
|
651
|
+
path: ['root_item'],
|
|
652
|
+
hasChildren: false,
|
|
653
|
+
containerType: null,
|
|
654
|
+
continuingLevels: [],
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
const { container } = render(
|
|
658
|
+
<table>
|
|
659
|
+
<tbody>
|
|
660
|
+
<PropertyRow row={row} />
|
|
661
|
+
</tbody>
|
|
662
|
+
</table>,
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
expect(
|
|
666
|
+
container.querySelector('.property-cell--tree'),
|
|
667
|
+
).not.toBeInTheDocument();
|
|
371
668
|
});
|
|
372
669
|
|
|
373
670
|
it('applies padding-left based on level using rem units', () => {
|
|
@@ -1,17 +1,44 @@
|
|
|
1
|
+
/* eslint-disable @docusaurus/no-html-links */
|
|
1
2
|
import '@testing-library/jest-dom';
|
|
2
3
|
import React from 'react';
|
|
3
|
-
import { render } from '@testing-library/react';
|
|
4
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
4
5
|
import SchemaJsonViewer from '../../components/SchemaJsonViewer';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return
|
|
10
|
-
|
|
11
|
-
}
|
|
7
|
+
jest.mock(
|
|
8
|
+
'@docusaurus/Link',
|
|
9
|
+
() => {
|
|
10
|
+
return function DocusaurusLink({ children, ...props }) {
|
|
11
|
+
return <a {...props}>{children}</a>;
|
|
12
|
+
};
|
|
13
|
+
},
|
|
14
|
+
{ virtual: true },
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
jest.mock('@docusaurus/theme-common', () => ({
|
|
18
|
+
usePrismTheme: () => ({
|
|
19
|
+
plain: {
|
|
20
|
+
color: 'rgb(1, 2, 3)',
|
|
21
|
+
backgroundColor: 'rgb(4, 5, 6)',
|
|
22
|
+
},
|
|
23
|
+
styles: [
|
|
24
|
+
{
|
|
25
|
+
types: ['property'],
|
|
26
|
+
style: { color: 'rgb(0, 0, 255)' },
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
types: ['string'],
|
|
30
|
+
style: { color: 'rgb(255, 0, 0)' },
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
types: ['number'],
|
|
34
|
+
style: { color: 'rgb(0, 128, 0)' },
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
}),
|
|
38
|
+
}));
|
|
12
39
|
|
|
13
40
|
describe('SchemaJsonViewer', () => {
|
|
14
|
-
it('renders the schema in a
|
|
41
|
+
it('renders the schema in a syntax-highlighted pre block', () => {
|
|
15
42
|
const schema = {
|
|
16
43
|
type: 'object',
|
|
17
44
|
properties: {
|
|
@@ -29,8 +56,165 @@ describe('SchemaJsonViewer', () => {
|
|
|
29
56
|
const codeBlockElement = container.querySelector('pre');
|
|
30
57
|
expect(codeBlockElement).toBeInTheDocument();
|
|
31
58
|
expect(codeBlockElement).toHaveAttribute('data-language', 'json');
|
|
32
|
-
expect(codeBlockElement.textContent).toEqual(
|
|
33
|
-
JSON.stringify(schema, null, 2),
|
|
59
|
+
expect(codeBlockElement.textContent.replace(/\s+/g, '')).toEqual(
|
|
60
|
+
JSON.stringify(schema, null, 2).replace(/\s+/g, ''),
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('uses the prism theme for base json token styling', () => {
|
|
65
|
+
const schema = {
|
|
66
|
+
type: 'string',
|
|
67
|
+
examples: ['x'],
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
render(<SchemaJsonViewer schema={schema} />);
|
|
71
|
+
|
|
72
|
+
fireEvent.click(screen.getByText('View Raw JSON Schema'));
|
|
73
|
+
|
|
74
|
+
expect(screen.getByText('"type"')).toHaveStyle({ color: 'rgb(0, 0, 255)' });
|
|
75
|
+
expect(screen.getByText('"string"')).toHaveStyle({
|
|
76
|
+
color: 'rgb(255, 0, 0)',
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('navigates local $ref values inside the viewer and resets to root', () => {
|
|
81
|
+
const rootSchema = {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
component: { $ref: './components/referenced.json' },
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
const referencedSchema = {
|
|
88
|
+
title: 'Referenced Component',
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
prop: { type: 'string' },
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
render(
|
|
96
|
+
<SchemaJsonViewer
|
|
97
|
+
schema={rootSchema}
|
|
98
|
+
sourcePath="main-schema.json"
|
|
99
|
+
schemaSources={{
|
|
100
|
+
'main-schema.json': rootSchema,
|
|
101
|
+
'components/referenced.json': referencedSchema,
|
|
102
|
+
}}
|
|
103
|
+
/>,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
fireEvent.click(screen.getByText('View Raw JSON Schema'));
|
|
107
|
+
fireEvent.click(
|
|
108
|
+
screen.getByRole('button', { name: '"./components/referenced.json"' }),
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(screen.getAllByText(/Referenced Component/).length).toBeGreaterThan(
|
|
112
|
+
0,
|
|
34
113
|
);
|
|
114
|
+
expect(
|
|
115
|
+
screen.getByRole('button', { name: 'Back to root' }),
|
|
116
|
+
).toBeInTheDocument();
|
|
117
|
+
|
|
118
|
+
fireEvent.click(screen.getByRole('button', { name: 'Back to root' }));
|
|
119
|
+
|
|
120
|
+
expect(
|
|
121
|
+
screen.getByText('"./components/referenced.json"'),
|
|
122
|
+
).toBeInTheDocument();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('highlights schema meta keywords inside the raw json view', () => {
|
|
126
|
+
const schema = {
|
|
127
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
128
|
+
$id: 'https://example.com/event.json',
|
|
129
|
+
$anchor: 'eventRoot',
|
|
130
|
+
$comment: 'Internal authoring note',
|
|
131
|
+
$vocabulary: {
|
|
132
|
+
'https://json-schema.org/draft/2020-12/vocab/validation': true,
|
|
133
|
+
},
|
|
134
|
+
type: 'object',
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
render(<SchemaJsonViewer schema={schema} sourcePath="event.json" />);
|
|
138
|
+
|
|
139
|
+
fireEvent.click(screen.getByText('View Raw JSON Schema'));
|
|
140
|
+
|
|
141
|
+
expect(screen.getByText('"$schema"')).toHaveClass(
|
|
142
|
+
'schema-json-viewer__keyword',
|
|
143
|
+
'schema-json-viewer__keyword--meta',
|
|
144
|
+
);
|
|
145
|
+
expect(screen.getByText('"$anchor"')).toHaveClass(
|
|
146
|
+
'schema-json-viewer__keyword',
|
|
147
|
+
'schema-json-viewer__keyword--meta',
|
|
148
|
+
);
|
|
149
|
+
expect(screen.getByText('"$vocabulary"')).toHaveClass(
|
|
150
|
+
'schema-json-viewer__keyword',
|
|
151
|
+
'schema-json-viewer__keyword--meta',
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('highlights structural schema keywords inside the raw json view', () => {
|
|
156
|
+
const schema = {
|
|
157
|
+
type: 'object',
|
|
158
|
+
allOf: [{ $ref: './component.json' }],
|
|
159
|
+
properties: {
|
|
160
|
+
name: { type: 'string' },
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
render(<SchemaJsonViewer schema={schema} sourcePath="event.json" />);
|
|
165
|
+
|
|
166
|
+
fireEvent.click(screen.getByText('View Raw JSON Schema'));
|
|
167
|
+
|
|
168
|
+
expect(screen.getByText('"allOf"')).toHaveClass(
|
|
169
|
+
'schema-json-viewer__keyword',
|
|
170
|
+
'schema-json-viewer__keyword--structural',
|
|
171
|
+
);
|
|
172
|
+
expect(screen.getByText('"properties"')).toHaveClass(
|
|
173
|
+
'schema-json-viewer__keyword',
|
|
174
|
+
'schema-json-viewer__keyword--structural',
|
|
175
|
+
);
|
|
176
|
+
expect(screen.getByText('"$ref"')).toHaveClass(
|
|
177
|
+
'schema-json-viewer__keyword',
|
|
178
|
+
'schema-json-viewer__keyword--structural',
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('does not highlight payload property names inside properties as meta keywords', () => {
|
|
183
|
+
const schema = {
|
|
184
|
+
type: 'object',
|
|
185
|
+
properties: {
|
|
186
|
+
$schema: {
|
|
187
|
+
type: 'string',
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
render(<SchemaJsonViewer schema={schema} sourcePath="event.json" />);
|
|
193
|
+
|
|
194
|
+
fireEvent.click(screen.getByText('View Raw JSON Schema'));
|
|
195
|
+
|
|
196
|
+
expect(screen.getByText('"properties"')).toHaveClass(
|
|
197
|
+
'schema-json-viewer__keyword',
|
|
198
|
+
'schema-json-viewer__keyword--structural',
|
|
199
|
+
);
|
|
200
|
+
expect(screen.getByText('"$schema"')).toHaveClass('token property');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('renders external $ref values as new-tab links', () => {
|
|
204
|
+
const schema = {
|
|
205
|
+
$ref: 'https://example.com/schema.json',
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
render(<SchemaJsonViewer schema={schema} sourcePath="root.json" />);
|
|
209
|
+
|
|
210
|
+
fireEvent.click(screen.getByText('View Raw JSON Schema'));
|
|
211
|
+
const refLink = screen.getByRole('link', {
|
|
212
|
+
name: '"https://example.com/schema.json"',
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
expect(refLink).toHaveAttribute('href', 'https://example.com/schema.json');
|
|
216
|
+
expect(refLink).toHaveAttribute('target', '_blank');
|
|
217
|
+
expect(refLink).toHaveAttribute('rel', 'noreferrer');
|
|
218
|
+
expect(refLink).toHaveClass('schema-json-viewer__ref-link');
|
|
35
219
|
});
|
|
36
220
|
});
|
|
@@ -7,7 +7,9 @@ import SchemaRows from '../../components/SchemaRows';
|
|
|
7
7
|
jest.mock('../../components/PropertyRow', () => {
|
|
8
8
|
const MockPropertyRow = (props) => (
|
|
9
9
|
<tr>
|
|
10
|
-
<td>
|
|
10
|
+
<td>
|
|
11
|
+
Mocked PropertyRow: {props.row.name} ({props.stripeIndex})
|
|
12
|
+
</td>
|
|
11
13
|
</tr>
|
|
12
14
|
);
|
|
13
15
|
MockPropertyRow.displayName = 'MockPropertyRow';
|
|
@@ -17,7 +19,10 @@ jest.mock('../../components/PropertyRow', () => {
|
|
|
17
19
|
jest.mock('../../components/FoldableRows', () => {
|
|
18
20
|
const MockFoldableRows = (props) => (
|
|
19
21
|
<tr>
|
|
20
|
-
<td>
|
|
22
|
+
<td>
|
|
23
|
+
Mocked FoldableRows: {props.row.choiceType} ({props.stripeIndex},{' '}
|
|
24
|
+
{String(!!props.stripeState)})
|
|
25
|
+
</td>
|
|
21
26
|
</tr>
|
|
22
27
|
);
|
|
23
28
|
MockFoldableRows.displayName = 'MockFoldableRows';
|
|
@@ -27,7 +32,10 @@ jest.mock('../../components/FoldableRows', () => {
|
|
|
27
32
|
jest.mock('../../components/ConditionalRows', () => {
|
|
28
33
|
const MockConditionalRows = (props) => (
|
|
29
34
|
<tr>
|
|
30
|
-
<td>
|
|
35
|
+
<td>
|
|
36
|
+
Mocked ConditionalRows: {props.row.condition.title} ({props.stripeIndex}
|
|
37
|
+
, {String(!!props.stripeState)})
|
|
38
|
+
</td>
|
|
31
39
|
</tr>
|
|
32
40
|
);
|
|
33
41
|
MockConditionalRows.displayName = 'MockConditionalRows';
|
|
@@ -49,8 +57,8 @@ describe('SchemaRows', () => {
|
|
|
49
57
|
</table>,
|
|
50
58
|
);
|
|
51
59
|
|
|
52
|
-
expect(getByText('Mocked PropertyRow: name')).toBeInTheDocument();
|
|
53
|
-
expect(getByText('Mocked PropertyRow: age')).toBeInTheDocument();
|
|
60
|
+
expect(getByText('Mocked PropertyRow: name (0)')).toBeInTheDocument();
|
|
61
|
+
expect(getByText('Mocked PropertyRow: age (1)')).toBeInTheDocument();
|
|
54
62
|
});
|
|
55
63
|
|
|
56
64
|
it('renders nested properties from a flat list', () => {
|
|
@@ -68,8 +76,8 @@ describe('SchemaRows', () => {
|
|
|
68
76
|
);
|
|
69
77
|
|
|
70
78
|
// It should render both the parent and child property from the flat list
|
|
71
|
-
expect(getByText('Mocked PropertyRow: user')).toBeInTheDocument();
|
|
72
|
-
expect(getByText('Mocked PropertyRow: id')).toBeInTheDocument();
|
|
79
|
+
expect(getByText('Mocked PropertyRow: user (0)')).toBeInTheDocument();
|
|
80
|
+
expect(getByText('Mocked PropertyRow: id (1)')).toBeInTheDocument();
|
|
73
81
|
});
|
|
74
82
|
|
|
75
83
|
it('renders a FoldableRows for choice type items in tableData', () => {
|
|
@@ -90,7 +98,9 @@ describe('SchemaRows', () => {
|
|
|
90
98
|
</table>,
|
|
91
99
|
);
|
|
92
100
|
|
|
93
|
-
expect(
|
|
101
|
+
expect(
|
|
102
|
+
getByText('Mocked FoldableRows: oneOf (0, true)'),
|
|
103
|
+
).toBeInTheDocument();
|
|
94
104
|
});
|
|
95
105
|
|
|
96
106
|
it('renders a mix of properties and choices', () => {
|
|
@@ -113,9 +123,11 @@ describe('SchemaRows', () => {
|
|
|
113
123
|
</table>,
|
|
114
124
|
);
|
|
115
125
|
|
|
116
|
-
expect(getByText('Mocked PropertyRow: prop1')).toBeInTheDocument();
|
|
117
|
-
expect(
|
|
118
|
-
|
|
126
|
+
expect(getByText('Mocked PropertyRow: prop1 (0)')).toBeInTheDocument();
|
|
127
|
+
expect(
|
|
128
|
+
getByText('Mocked FoldableRows: anyOf (1, true)'),
|
|
129
|
+
).toBeInTheDocument();
|
|
130
|
+
expect(getByText('Mocked PropertyRow: prop2 (2)')).toBeInTheDocument();
|
|
119
131
|
});
|
|
120
132
|
|
|
121
133
|
it('renders a ConditionalRows for conditional type items in tableData', () => {
|
|
@@ -136,6 +148,44 @@ describe('SchemaRows', () => {
|
|
|
136
148
|
</table>,
|
|
137
149
|
);
|
|
138
150
|
|
|
139
|
-
expect(
|
|
151
|
+
expect(
|
|
152
|
+
getByText('Mocked ConditionalRows: If (0, true)'),
|
|
153
|
+
).toBeInTheDocument();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('increments stripe indices across logical rows', () => {
|
|
157
|
+
const tableData = [
|
|
158
|
+
{ type: 'property', name: 'prop1', path: ['prop1'] },
|
|
159
|
+
{
|
|
160
|
+
type: 'choice',
|
|
161
|
+
choiceType: 'anyOf',
|
|
162
|
+
path: ['choice'],
|
|
163
|
+
options: [],
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
type: 'conditional',
|
|
167
|
+
path: ['if/then/else'],
|
|
168
|
+
condition: { title: 'If', rows: [] },
|
|
169
|
+
branches: [],
|
|
170
|
+
},
|
|
171
|
+
{ type: 'property', name: 'prop2', path: ['prop2'] },
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const { getByText } = render(
|
|
175
|
+
<table>
|
|
176
|
+
<tbody>
|
|
177
|
+
<SchemaRows tableData={tableData} />
|
|
178
|
+
</tbody>
|
|
179
|
+
</table>,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
expect(getByText('Mocked PropertyRow: prop1 (0)')).toBeInTheDocument();
|
|
183
|
+
expect(
|
|
184
|
+
getByText('Mocked FoldableRows: anyOf (1, true)'),
|
|
185
|
+
).toBeInTheDocument();
|
|
186
|
+
expect(
|
|
187
|
+
getByText('Mocked ConditionalRows: If (2, true)'),
|
|
188
|
+
).toBeInTheDocument();
|
|
189
|
+
expect(getByText('Mocked PropertyRow: prop2 (3)')).toBeInTheDocument();
|
|
140
190
|
});
|
|
141
191
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
|
2
2
|
|
|
3
|
-
exports[`connector lines visual regressions keeps cvv row open when payment choice has following options 1`] = `"<td rowspan="1" style="padding-left: 1.75rem;" class="property-cell level-1"><span class="property-name"><strong>cvv</strong></span></td>"`;
|
|
3
|
+
exports[`connector lines visual regressions keeps cvv row open when payment choice has following options 1`] = `"<td rowspan="1" style="padding-left: 1.75rem;" class="property-cell property-cell--tree level-1"><span class="property-name"><strong>cvv</strong></span></td>"`;
|
|
4
4
|
|
|
5
|
-
exports[`connector lines visual regressions keeps user_id option row open when user conditional follows 1`] = `"<td rowspan="1" style="padding-left: 1.75rem;" class="property-cell level-1"><span class="property-name"><strong>user_id</strong></span></td>"`;
|
|
5
|
+
exports[`connector lines visual regressions keeps user_id option row open when user conditional follows 1`] = `"<td rowspan="1" style="padding-left: 1.75rem;" class="property-cell property-cell--tree level-1"><span class="property-name"><strong>user_id</strong></span></td>"`;
|
|
6
6
|
|
|
7
|
-
exports[`connector lines visual regressions keeps wallet_provider option row open when wallet_email follows 1`] = `"<td rowspan="2" style="padding-left: 1.75rem;" class="property-cell level-1"><span class="property-name"><strong>wallet_provider</strong></span></td>"`;
|
|
7
|
+
exports[`connector lines visual regressions keeps wallet_provider option row open when wallet_email follows 1`] = `"<td rowspan="2" style="padding-left: 1.75rem;" class="property-cell property-cell--required property-cell--tree level-1"><span class="property-name"><strong>wallet_provider</strong></span></td>"`;
|