docusaurus-plugin-generate-schema-docs 1.1.1 → 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.
Files changed (81) hide show
  1. package/README.md +60 -2
  2. package/__tests__/ExampleDataLayer.test.js +92 -0
  3. package/__tests__/__fixtures__/static/schemas/add-to-cart-event.json +44 -0
  4. package/__tests__/__fixtures__/static/schemas/choice-event.json +72 -0
  5. package/__tests__/__fixtures__/static/schemas/components/dataLayer.json +56 -0
  6. package/__tests__/__fixtures__/static/schemas/components/product.json +125 -0
  7. package/__tests__/__fixtures__/static/schemas/nested/child-event.json +10 -0
  8. package/__tests__/__fixtures__/static/schemas/nested/grandchild-a.json +9 -0
  9. package/__tests__/__fixtures__/static/schemas/nested/grandchild-b.json +9 -0
  10. package/__tests__/__fixtures__/static/schemas/nested/parent-event.json +7 -0
  11. package/__tests__/__fixtures__/static/schemas/root-any-of-event.json +34 -0
  12. package/__tests__/__fixtures__/static/schemas/root-choice-event.json +36 -0
  13. package/__tests__/__fixtures__/validateSchemas/circular-schema.json +8 -0
  14. package/__tests__/__fixtures__/validateSchemas/components/referenced.json +10 -0
  15. package/__tests__/__fixtures__/validateSchemas/invalid-example-schema.json +8 -0
  16. package/__tests__/__fixtures__/validateSchemas/main-schema-with-missing-ref.json +8 -0
  17. package/__tests__/__fixtures__/validateSchemas/main-schema-with-ref.json +8 -0
  18. package/__tests__/__fixtures__/validateSchemas/no-example-schema.json +12 -0
  19. package/__tests__/__fixtures__/validateSchemas/schema-A.json +7 -0
  20. package/__tests__/__fixtures__/validateSchemas/schema-B.json +7 -0
  21. package/__tests__/__fixtures__/validateSchemas/valid-schema.json +8 -0
  22. package/__tests__/__fixtures_versioned__/static/schemas/1.1.1/add-to-cart-event.json +44 -0
  23. package/__tests__/__fixtures_versioned__/static/schemas/1.1.1/components/dataLayer.json +56 -0
  24. package/__tests__/__fixtures_versioned__/static/schemas/1.1.1/components/product.json +125 -0
  25. package/__tests__/__fixtures_versioned__/static/schemas/next/add-to-cart-event.json +44 -0
  26. package/__tests__/__fixtures_versioned__/static/schemas/next/components/dataLayer.json +56 -0
  27. package/__tests__/__fixtures_versioned__/static/schemas/next/components/product.json +125 -0
  28. package/__tests__/__fixtures_versioned__/versions.json +1 -0
  29. package/__tests__/__snapshots__/ExampleDataLayer.test.js.snap +117 -0
  30. package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +92 -0
  31. package/__tests__/__snapshots__/generateEventDocs.test.js.snap +151 -0
  32. package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +53 -0
  33. package/__tests__/components/FoldableRows.test.js +330 -0
  34. package/__tests__/components/PropertiesTable.test.js +41 -0
  35. package/__tests__/components/PropertyRow.test.js +487 -0
  36. package/__tests__/components/SchemaJsonViewer.test.js +36 -0
  37. package/__tests__/components/SchemaRows.test.js +110 -0
  38. package/__tests__/components/SchemaViewer.test.js +44 -0
  39. package/__tests__/components/TableHeader.test.js +20 -0
  40. package/__tests__/generateEventDocs.nested.test.js +80 -0
  41. package/__tests__/generateEventDocs.test.js +90 -0
  42. package/__tests__/generateEventDocs.versioned.test.js +69 -0
  43. package/__tests__/helpers/buildExampleFromSchema.test.js +188 -0
  44. package/__tests__/helpers/file-system.test.js +44 -0
  45. package/__tests__/helpers/getConstraints.test.js +58 -0
  46. package/__tests__/helpers/loadSchema.test.js +20 -0
  47. package/__tests__/helpers/path-helpers.test.js +34 -0
  48. package/__tests__/helpers/processSchema.test.js +56 -0
  49. package/__tests__/helpers/schema-processing.test.js +82 -0
  50. package/__tests__/helpers/schemaToExamples.test.js +56 -0
  51. package/__tests__/helpers/schemaToTableData.filtering.test.js +65 -0
  52. package/__tests__/helpers/schemaToTableData.hierarchicalLines.test.js +539 -0
  53. package/__tests__/helpers/schemaToTableData.test.js +222 -0
  54. package/__tests__/helpers/update-schema-ids.test.js +107 -0
  55. package/__tests__/update-schema-ids.test.js +39 -0
  56. package/__tests__/validateSchemas.test.js +137 -0
  57. package/components/ExampleDataLayer.js +60 -27
  58. package/components/FoldableRows.js +164 -0
  59. package/components/PropertiesTable.js +12 -14
  60. package/components/PropertyRow.js +183 -0
  61. package/components/SchemaJsonViewer.js +8 -7
  62. package/components/SchemaRows.css +250 -0
  63. package/components/SchemaRows.js +24 -69
  64. package/components/SchemaViewer.js +21 -13
  65. package/components/TableHeader.js +15 -0
  66. package/generateEventDocs.js +141 -60
  67. package/helpers/buildExampleFromSchema.js +59 -73
  68. package/helpers/choice-index-template.js +22 -0
  69. package/helpers/file-system.js +32 -0
  70. package/helpers/getConstraints.js +52 -0
  71. package/helpers/loadSchema.js +11 -0
  72. package/helpers/path-helpers.js +22 -0
  73. package/helpers/processSchema.js +32 -0
  74. package/helpers/schema-doc-template.js +36 -0
  75. package/helpers/schema-processing.js +75 -0
  76. package/helpers/schemaToExamples.js +99 -0
  77. package/helpers/schemaToTableData.js +311 -0
  78. package/helpers/update-schema-ids.js +47 -0
  79. package/index.js +146 -47
  80. package/package.json +6 -3
  81. package/validateSchemas.js +56 -70
@@ -0,0 +1,539 @@
1
+ import { schemaToTableData } from '../../helpers/schemaToTableData';
2
+
3
+ describe('schemaToTableData - hierarchical lines', () => {
4
+ describe('hasChildren property', () => {
5
+ it('sets hasChildren to true for objects with nested properties', () => {
6
+ const schema = {
7
+ properties: {
8
+ user_data: {
9
+ type: 'object',
10
+ properties: {
11
+ name: { type: 'string' },
12
+ },
13
+ },
14
+ },
15
+ };
16
+
17
+ const tableData = schemaToTableData(schema);
18
+ const userDataRow = tableData.find((row) => row.name === 'user_data');
19
+
20
+ expect(userDataRow.hasChildren).toBe(true);
21
+ });
22
+
23
+ it('sets hasChildren to true for arrays with item properties', () => {
24
+ const schema = {
25
+ properties: {
26
+ items: {
27
+ type: 'array',
28
+ items: {
29
+ type: 'object',
30
+ properties: {
31
+ id: { type: 'string' },
32
+ },
33
+ },
34
+ },
35
+ },
36
+ };
37
+
38
+ const tableData = schemaToTableData(schema);
39
+ const itemsRow = tableData.find((row) => row.name === 'items');
40
+
41
+ expect(itemsRow.hasChildren).toBe(true);
42
+ });
43
+
44
+ it('sets hasChildren to false for simple properties', () => {
45
+ const schema = {
46
+ properties: {
47
+ name: { type: 'string' },
48
+ age: { type: 'number' },
49
+ },
50
+ };
51
+
52
+ const tableData = schemaToTableData(schema);
53
+
54
+ tableData.forEach((row) => {
55
+ expect(row.hasChildren).toBe(false);
56
+ });
57
+ });
58
+
59
+ it('sets hasChildren to true for properties with nested oneOf/anyOf', () => {
60
+ const schema = {
61
+ properties: {
62
+ payment_method: {
63
+ type: 'object',
64
+ anyOf: [
65
+ {
66
+ title: 'Credit Card',
67
+ properties: { card_number: { type: 'string' } },
68
+ },
69
+ ],
70
+ },
71
+ },
72
+ };
73
+
74
+ const tableData = schemaToTableData(schema);
75
+ const paymentRow = tableData.find(
76
+ (row) => row.name === 'payment_method' && row.type === 'property',
77
+ );
78
+
79
+ expect(paymentRow.hasChildren).toBe(true);
80
+ });
81
+ });
82
+
83
+ describe('containerType property', () => {
84
+ it('sets containerType to "object" for object properties with nested properties', () => {
85
+ const schema = {
86
+ properties: {
87
+ user_data: {
88
+ type: 'object',
89
+ properties: {
90
+ name: { type: 'string' },
91
+ },
92
+ },
93
+ },
94
+ };
95
+
96
+ const tableData = schemaToTableData(schema);
97
+ const userDataRow = tableData.find((row) => row.name === 'user_data');
98
+
99
+ expect(userDataRow.containerType).toBe('object');
100
+ });
101
+
102
+ it('sets containerType to "array" for arrays with item properties', () => {
103
+ const schema = {
104
+ properties: {
105
+ addresses: {
106
+ type: 'array',
107
+ items: {
108
+ type: 'object',
109
+ properties: {
110
+ street: { type: 'string' },
111
+ },
112
+ },
113
+ },
114
+ },
115
+ };
116
+
117
+ const tableData = schemaToTableData(schema);
118
+ const addressesRow = tableData.find((row) => row.name === 'addresses');
119
+
120
+ expect(addressesRow.containerType).toBe('array');
121
+ });
122
+
123
+ it('sets containerType to null for simple properties', () => {
124
+ const schema = {
125
+ properties: {
126
+ name: { type: 'string' },
127
+ },
128
+ };
129
+
130
+ const tableData = schemaToTableData(schema);
131
+ const nameRow = tableData.find((row) => row.name === 'name');
132
+
133
+ expect(nameRow.containerType).toBeNull();
134
+ });
135
+ });
136
+
137
+ describe('continuingLevels property', () => {
138
+ it('includes parent level in continuingLevels when parent has more siblings', () => {
139
+ const schema = {
140
+ properties: {
141
+ user_data: {
142
+ type: 'object',
143
+ properties: {
144
+ name: { type: 'string' },
145
+ age: { type: 'number' },
146
+ },
147
+ },
148
+ other_field: { type: 'string' },
149
+ },
150
+ };
151
+
152
+ const tableData = schemaToTableData(schema);
153
+
154
+ // name is not last (age comes after), so it should include level 0 in continuingLevels
155
+ // because user_data (at level 0) has siblings (other_field)
156
+ const nameRow = tableData.find(
157
+ (row) => row.name === 'name' && row.level === 1,
158
+ );
159
+
160
+ expect(nameRow.continuingLevels).toContain(0);
161
+ });
162
+
163
+ it('does not include parent level when parent is last sibling', () => {
164
+ const schema = {
165
+ properties: {
166
+ user_data: {
167
+ type: 'object',
168
+ properties: {
169
+ name: { type: 'string' },
170
+ },
171
+ },
172
+ },
173
+ };
174
+
175
+ const tableData = schemaToTableData(schema);
176
+ const nameRow = tableData.find(
177
+ (row) => row.name === 'name' && row.level === 1,
178
+ );
179
+
180
+ // user_data is the only/last property at level 0, so no continuing level
181
+ expect(nameRow.continuingLevels).not.toContain(0);
182
+ });
183
+
184
+ it('removes immediate parent level from continuingLevels when item is last', () => {
185
+ const schema = {
186
+ properties: {
187
+ user_data: {
188
+ type: 'object',
189
+ properties: {
190
+ attributes: {
191
+ type: 'object',
192
+ properties: {
193
+ segment: { type: 'string' },
194
+ },
195
+ },
196
+ addresses: {
197
+ type: 'array',
198
+ items: {
199
+ type: 'object',
200
+ properties: {
201
+ street: { type: 'string' },
202
+ city: { type: 'string' },
203
+ },
204
+ },
205
+ },
206
+ },
207
+ },
208
+ other_field: { type: 'string' },
209
+ },
210
+ };
211
+
212
+ const tableData = schemaToTableData(schema);
213
+
214
+ // addresses is last at level 1, so its children should NOT have level 0 in continuingLevels
215
+ // because the level 0 line stops at addresses
216
+ const streetRow = tableData.find(
217
+ (row) => row.name === 'street' && row.level === 2,
218
+ );
219
+ const cityRow = tableData.find(
220
+ (row) => row.name === 'city' && row.level === 2,
221
+ );
222
+
223
+ expect(streetRow.continuingLevels).not.toContain(0);
224
+ expect(cityRow.continuingLevels).not.toContain(0);
225
+ });
226
+
227
+ it('maintains continuingLevels through multiple nesting levels', () => {
228
+ const schema = {
229
+ properties: {
230
+ level0_first: {
231
+ type: 'object',
232
+ properties: {
233
+ level1_first: {
234
+ type: 'object',
235
+ properties: {
236
+ level2_item: { type: 'string' },
237
+ },
238
+ },
239
+ level1_second: { type: 'string' },
240
+ },
241
+ },
242
+ level0_second: { type: 'string' },
243
+ },
244
+ };
245
+
246
+ const tableData = schemaToTableData(schema);
247
+
248
+ // level2_item is inside level1_first (which is NOT last at level 1)
249
+ // and level0_first is NOT last at level 0
250
+ // so level2_item should have both levels 0 and 1... wait no
251
+ // level2_item's immediate parent is level1_first
252
+ // level1_first is NOT last (level1_second comes after)
253
+ // so level2_item should have level 1 in continuingLevels? No...
254
+ // The filter is lvl < level - 1, so for level 2, only lvl < 1 passes
255
+ // So only level 0 would be in the continuing gradient
256
+
257
+ const level2Row = tableData.find(
258
+ (row) => row.name === 'level2_item' && row.level === 2,
259
+ );
260
+
261
+ // level0_first is not last, so level 0 should be in continuingLevels
262
+ expect(level2Row.continuingLevels).toContain(0);
263
+ });
264
+
265
+ it('has empty continuingLevels for root level properties', () => {
266
+ const schema = {
267
+ properties: {
268
+ name: { type: 'string' },
269
+ age: { type: 'number' },
270
+ },
271
+ };
272
+
273
+ const tableData = schemaToTableData(schema);
274
+
275
+ tableData.forEach((row) => {
276
+ expect(row.continuingLevels).toEqual([]);
277
+ });
278
+ });
279
+ });
280
+
281
+ describe('isLastInGroup property', () => {
282
+ it('sets isLastInGroup based on sibling position only', () => {
283
+ const schema = {
284
+ properties: {
285
+ first: { type: 'string' },
286
+ middle: { type: 'string' },
287
+ last: { type: 'string' },
288
+ },
289
+ };
290
+
291
+ const tableData = schemaToTableData(schema);
292
+
293
+ const firstRow = tableData.find((row) => row.name === 'first');
294
+ const middleRow = tableData.find((row) => row.name === 'middle');
295
+ const lastRow = tableData.find((row) => row.name === 'last');
296
+
297
+ expect(firstRow.isLastInGroup).toBe(false);
298
+ expect(middleRow.isLastInGroup).toBe(false);
299
+ expect(lastRow.isLastInGroup).toBe(true);
300
+ });
301
+
302
+ it('sets isLastInGroup to true for parent with children when it is last sibling', () => {
303
+ const schema = {
304
+ properties: {
305
+ first: { type: 'string' },
306
+ user_data: {
307
+ type: 'object',
308
+ properties: {
309
+ name: { type: 'string' },
310
+ },
311
+ },
312
+ },
313
+ };
314
+
315
+ const tableData = schemaToTableData(schema);
316
+ const userDataRow = tableData.find((row) => row.name === 'user_data');
317
+
318
+ // user_data is last AND has children, but isLastInGroup should be true
319
+ // because it's the last sibling at its level
320
+ expect(userDataRow.isLastInGroup).toBe(true);
321
+ expect(userDataRow.hasChildren).toBe(true);
322
+ });
323
+
324
+ it('sets isLastInGroup correctly for array item children', () => {
325
+ const schema = {
326
+ properties: {
327
+ addresses: {
328
+ type: 'array',
329
+ items: {
330
+ type: 'object',
331
+ properties: {
332
+ street: { type: 'string' },
333
+ city: { type: 'string' },
334
+ zip: { type: 'string' },
335
+ },
336
+ },
337
+ },
338
+ },
339
+ };
340
+
341
+ const tableData = schemaToTableData(schema);
342
+
343
+ const streetRow = tableData.find((row) => row.name === 'street');
344
+ const cityRow = tableData.find((row) => row.name === 'city');
345
+ const zipRow = tableData.find((row) => row.name === 'zip');
346
+
347
+ expect(streetRow.isLastInGroup).toBe(false);
348
+ expect(cityRow.isLastInGroup).toBe(false);
349
+ expect(zipRow.isLastInGroup).toBe(true);
350
+ });
351
+ });
352
+
353
+ describe('choice rows', () => {
354
+ it('includes continuingLevels in choice rows', () => {
355
+ const schema = {
356
+ properties: {
357
+ user_id: {
358
+ description: 'User ID',
359
+ oneOf: [
360
+ { title: 'String', type: 'string' },
361
+ { title: 'Integer', type: 'integer' },
362
+ ],
363
+ },
364
+ other_field: { type: 'string' },
365
+ },
366
+ };
367
+
368
+ const tableData = schemaToTableData(schema);
369
+ const choiceRow = tableData.find((row) => row.type === 'choice');
370
+
371
+ expect(choiceRow).toBeDefined();
372
+ expect(choiceRow.continuingLevels).toBeDefined();
373
+ expect(Array.isArray(choiceRow.continuingLevels)).toBe(true);
374
+ });
375
+
376
+ it('passes continuingLevels to choice option rows', () => {
377
+ const schema = {
378
+ properties: {
379
+ payment_method: {
380
+ type: 'object',
381
+ anyOf: [
382
+ {
383
+ title: 'Credit Card',
384
+ properties: {
385
+ card_number: { type: 'string' },
386
+ },
387
+ },
388
+ ],
389
+ },
390
+ other_field: { type: 'string' },
391
+ },
392
+ };
393
+
394
+ const tableData = schemaToTableData(schema);
395
+ const choiceRow = tableData.find(
396
+ (row) => row.type === 'choice' && row.level === 1,
397
+ );
398
+
399
+ expect(choiceRow).toBeDefined();
400
+ expect(choiceRow.options).toBeDefined();
401
+ expect(choiceRow.options[0].rows).toBeDefined();
402
+
403
+ // The option rows should have their own continuingLevels
404
+ const optionRow = choiceRow.options[0].rows[0];
405
+ expect(optionRow.continuingLevels).toBeDefined();
406
+ });
407
+
408
+ // NEW TEST CASE START
409
+ it('sets isLastInGroup correctly based on option position (visual tree line fix)', () => {
410
+ const schema = {
411
+ properties: {
412
+ payment_method: {
413
+ type: 'object',
414
+ anyOf: [
415
+ {
416
+ title: 'Credit Card', // Not the last option
417
+ type: 'object',
418
+ properties: {
419
+ expiry_date: { type: 'string' }, // Should NOT be last in group visually
420
+ },
421
+ },
422
+ {
423
+ title: 'PayPal', // The last option
424
+ type: 'object',
425
+ properties: {
426
+ email: { type: 'string' }, // Should be last in group visually
427
+ },
428
+ },
429
+ ],
430
+ },
431
+ },
432
+ };
433
+
434
+ const tableData = schemaToTableData(schema);
435
+ const choiceRow = tableData.find((row) => row.type === 'choice');
436
+
437
+ const creditCardOption = choiceRow.options[0];
438
+ const payPalOption = choiceRow.options[1];
439
+
440
+ const expiryRow = creditCardOption.rows.find(
441
+ (r) => r.name === 'expiry_date',
442
+ );
443
+ const emailRow = payPalOption.rows.find((r) => r.name === 'email');
444
+
445
+ // Credit Card is not the last option, so its properties shouldn't close the visual branch
446
+ expect(expiryRow.isLastInGroup).toBe(false);
447
+
448
+ // PayPal is the last option, so its property should close the branch
449
+ expect(emailRow.isLastInGroup).toBe(true);
450
+ });
451
+ // NEW TEST CASE END
452
+ });
453
+
454
+ describe('complex nested schema', () => {
455
+ it('correctly calculates all hierarchical properties for deeply nested schema', () => {
456
+ const schema = {
457
+ properties: {
458
+ user_data: {
459
+ type: 'object',
460
+ properties: {
461
+ user_id: { type: 'string' },
462
+ attributes: {
463
+ type: 'object',
464
+ properties: {
465
+ segment: { type: 'string' },
466
+ score: { type: 'number' },
467
+ },
468
+ },
469
+ addresses: {
470
+ type: 'array',
471
+ items: {
472
+ type: 'object',
473
+ properties: {
474
+ street: { type: 'string' },
475
+ city: { type: 'string' },
476
+ },
477
+ },
478
+ },
479
+ },
480
+ },
481
+ string_field: { type: 'string' },
482
+ },
483
+ };
484
+
485
+ const tableData = schemaToTableData(schema);
486
+
487
+ // user_data: level 0, has children, object container, not last
488
+ const userDataRow = tableData.find(
489
+ (row) => row.name === 'user_data' && row.level === 0,
490
+ );
491
+ expect(userDataRow.hasChildren).toBe(true);
492
+ expect(userDataRow.containerType).toBe('object');
493
+ expect(userDataRow.isLastInGroup).toBe(false);
494
+ expect(userDataRow.continuingLevels).toEqual([]);
495
+
496
+ // attributes: level 1, has children, object container, not last (addresses comes after)
497
+ const attributesRow = tableData.find(
498
+ (row) => row.name === 'attributes' && row.level === 1,
499
+ );
500
+ expect(attributesRow.hasChildren).toBe(true);
501
+ expect(attributesRow.containerType).toBe('object');
502
+ expect(attributesRow.isLastInGroup).toBe(false);
503
+ expect(attributesRow.continuingLevels).toContain(0);
504
+
505
+ // segment: level 2, no children, not last
506
+ const segmentRow = tableData.find(
507
+ (row) => row.name === 'segment' && row.level === 2,
508
+ );
509
+ expect(segmentRow.hasChildren).toBe(false);
510
+ expect(segmentRow.containerType).toBeNull();
511
+ expect(segmentRow.isLastInGroup).toBe(false);
512
+ // Should have level 0 (user_data not last) but not level 1 (handled by ::before)
513
+ expect(segmentRow.continuingLevels).toContain(0);
514
+
515
+ // addresses: level 1, has children, array container, IS last
516
+ const addressesRow = tableData.find(
517
+ (row) => row.name === 'addresses' && row.level === 1,
518
+ );
519
+ expect(addressesRow.hasChildren).toBe(true);
520
+ expect(addressesRow.containerType).toBe('array');
521
+ expect(addressesRow.isLastInGroup).toBe(true);
522
+
523
+ // street: level 2, inside addresses (which is last at level 1)
524
+ // should NOT have level 0 in continuingLevels because addresses is last
525
+ const streetRow = tableData.find(
526
+ (row) => row.name === 'street' && row.level === 2,
527
+ );
528
+ expect(streetRow.continuingLevels).not.toContain(0);
529
+
530
+ // string_field: level 0, no children, IS last
531
+ const stringFieldRow = tableData.find(
532
+ (row) => row.name === 'string_field' && row.level === 0,
533
+ );
534
+ expect(stringFieldRow.hasChildren).toBe(false);
535
+ expect(stringFieldRow.containerType).toBeNull();
536
+ expect(stringFieldRow.isLastInGroup).toBe(true);
537
+ });
538
+ });
539
+ });