docusaurus-plugin-generate-schema-docs 1.5.4 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/__fixtures__/static/schemas/battle-test-event.json +771 -0
- package/__tests__/__fixtures__/static/schemas/conditional-event.json +52 -0
- package/__tests__/__fixtures__/static/schemas/nested-conditional-event.json +50 -0
- package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +3 -2
- package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +4 -2
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +3 -2
- package/__tests__/components/ConditionalRows.test.js +150 -0
- package/__tests__/components/ConnectorLines.visualRegression.test.js +93 -0
- package/__tests__/components/FoldableRows.test.js +7 -4
- package/__tests__/components/SchemaRows.test.js +31 -0
- package/__tests__/components/__snapshots__/ConnectorLines.visualRegression.test.js.snap +7 -0
- package/__tests__/generateEventDocs.anchor.test.js +7 -0
- package/__tests__/generateEventDocs.nested.test.js +7 -0
- package/__tests__/generateEventDocs.partials.test.js +134 -0
- package/__tests__/generateEventDocs.test.js +7 -0
- package/__tests__/helpers/buildExampleFromSchema.test.js +49 -0
- package/__tests__/helpers/schemaToExamples.test.js +75 -0
- package/__tests__/helpers/schemaToTableData.battleTest.test.js +704 -0
- package/__tests__/helpers/schemaToTableData.hierarchicalLines.test.js +190 -7
- package/__tests__/helpers/schemaToTableData.test.js +263 -2
- package/__tests__/helpers/validator.test.js +6 -6
- package/components/ConditionalRows.js +156 -0
- package/components/FoldableRows.js +88 -61
- package/components/PropertiesTable.js +1 -1
- package/components/PropertyRow.js +24 -8
- package/components/SchemaRows.css +115 -0
- package/components/SchemaRows.js +31 -4
- package/generateEventDocs.js +55 -37
- package/helpers/buildExampleFromSchema.js +11 -0
- package/helpers/choice-index-template.js +2 -1
- package/helpers/continuingLinesStyle.js +169 -0
- package/helpers/schema-doc-template.js +2 -5
- package/helpers/schema-processing.js +3 -0
- package/helpers/schemaToExamples.js +75 -2
- package/helpers/schemaToTableData.js +252 -26
- package/helpers/update-schema-ids.js +3 -3
- package/helpers/validator.js +7 -19
- package/package.json +3 -2
|
@@ -181,7 +181,7 @@ describe('schemaToTableData - hierarchical lines', () => {
|
|
|
181
181
|
expect(nameRow.continuingLevels).not.toContain(0);
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
-
it('
|
|
184
|
+
it('preserves ancestor continuingLevels through last children', () => {
|
|
185
185
|
const schema = {
|
|
186
186
|
properties: {
|
|
187
187
|
user_data: {
|
|
@@ -211,8 +211,9 @@ describe('schemaToTableData - hierarchical lines', () => {
|
|
|
211
211
|
|
|
212
212
|
const tableData = schemaToTableData(schema);
|
|
213
213
|
|
|
214
|
-
// addresses is last at level 1,
|
|
215
|
-
//
|
|
214
|
+
// addresses is last at level 1, but user_data has a sibling (other_field),
|
|
215
|
+
// so the level 0 line must continue through all of user_data's descendants
|
|
216
|
+
// to visually connect to other_field in the flat table.
|
|
216
217
|
const streetRow = tableData.find(
|
|
217
218
|
(row) => row.name === 'street' && row.level === 2,
|
|
218
219
|
);
|
|
@@ -220,8 +221,8 @@ describe('schemaToTableData - hierarchical lines', () => {
|
|
|
220
221
|
(row) => row.name === 'city' && row.level === 2,
|
|
221
222
|
);
|
|
222
223
|
|
|
223
|
-
expect(streetRow.continuingLevels).
|
|
224
|
-
expect(cityRow.continuingLevels).
|
|
224
|
+
expect(streetRow.continuingLevels).toContain(0);
|
|
225
|
+
expect(cityRow.continuingLevels).toContain(0);
|
|
225
226
|
});
|
|
226
227
|
|
|
227
228
|
it('maintains continuingLevels through multiple nesting levels', () => {
|
|
@@ -521,11 +522,12 @@ describe('schemaToTableData - hierarchical lines', () => {
|
|
|
521
522
|
expect(addressesRow.isLastInGroup).toBe(true);
|
|
522
523
|
|
|
523
524
|
// street: level 2, inside addresses (which is last at level 1)
|
|
524
|
-
// should
|
|
525
|
+
// should STILL have level 0 in continuingLevels because user_data has a
|
|
526
|
+
// sibling (string_field), so the level 0 line continues through descendants
|
|
525
527
|
const streetRow = tableData.find(
|
|
526
528
|
(row) => row.name === 'street' && row.level === 2,
|
|
527
529
|
);
|
|
528
|
-
expect(streetRow.continuingLevels).
|
|
530
|
+
expect(streetRow.continuingLevels).toContain(0);
|
|
529
531
|
|
|
530
532
|
// string_field: level 0, no children, IS last
|
|
531
533
|
const stringFieldRow = tableData.find(
|
|
@@ -536,4 +538,185 @@ describe('schemaToTableData - hierarchical lines', () => {
|
|
|
536
538
|
expect(stringFieldRow.isLastInGroup).toBe(true);
|
|
537
539
|
});
|
|
538
540
|
});
|
|
541
|
+
|
|
542
|
+
describe('conditional rows (if/then/else)', () => {
|
|
543
|
+
const conditionalSchema = {
|
|
544
|
+
properties: {
|
|
545
|
+
method: {
|
|
546
|
+
type: 'string',
|
|
547
|
+
enum: ['express', 'standard'],
|
|
548
|
+
},
|
|
549
|
+
},
|
|
550
|
+
required: ['method'],
|
|
551
|
+
if: {
|
|
552
|
+
properties: { method: { const: 'express' } },
|
|
553
|
+
required: ['method'],
|
|
554
|
+
},
|
|
555
|
+
then: {
|
|
556
|
+
description: 'Express shipping',
|
|
557
|
+
properties: {
|
|
558
|
+
priority: { type: 'string' },
|
|
559
|
+
guaranteed_by: { type: 'string', format: 'date' },
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
else: {
|
|
563
|
+
description: 'Standard shipping',
|
|
564
|
+
properties: {
|
|
565
|
+
estimated_days: { type: 'integer' },
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
it('condition rows are never isLastInGroup (branches always follow)', () => {
|
|
571
|
+
const tableData = schemaToTableData(conditionalSchema);
|
|
572
|
+
const conditional = tableData.find((r) => r.type === 'conditional');
|
|
573
|
+
conditional.condition.rows.forEach((row) => {
|
|
574
|
+
expect(row.isLastInGroup).toBe(false);
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
it('then branch last property is not isLastInGroup when else exists', () => {
|
|
579
|
+
const tableData = schemaToTableData(conditionalSchema);
|
|
580
|
+
const conditional = tableData.find((r) => r.type === 'conditional');
|
|
581
|
+
const thenBranch = conditional.branches.find((b) => b.title === 'Then');
|
|
582
|
+
const guaranteedBy = thenBranch.rows.find(
|
|
583
|
+
(r) => r.name === 'guaranteed_by',
|
|
584
|
+
);
|
|
585
|
+
expect(guaranteedBy.isLastInGroup).toBe(false);
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('else branch last property IS isLastInGroup (nothing follows)', () => {
|
|
589
|
+
const tableData = schemaToTableData(conditionalSchema);
|
|
590
|
+
const conditional = tableData.find((r) => r.type === 'conditional');
|
|
591
|
+
const elseBranch = conditional.branches.find((b) => b.title === 'Else');
|
|
592
|
+
const estimatedDays = elseBranch.rows.find(
|
|
593
|
+
(r) => r.name === 'estimated_days',
|
|
594
|
+
);
|
|
595
|
+
expect(estimatedDays.isLastInGroup).toBe(true);
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
it('then-only conditional marks last then property as isLastInGroup', () => {
|
|
599
|
+
const thenOnlySchema = {
|
|
600
|
+
properties: {
|
|
601
|
+
status: { type: 'string' },
|
|
602
|
+
},
|
|
603
|
+
if: {
|
|
604
|
+
properties: { status: { const: 'active' } },
|
|
605
|
+
},
|
|
606
|
+
then: {
|
|
607
|
+
properties: {
|
|
608
|
+
expires: { type: 'string' },
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
};
|
|
612
|
+
const tableData = schemaToTableData(thenOnlySchema);
|
|
613
|
+
const conditional = tableData.find((r) => r.type === 'conditional');
|
|
614
|
+
const thenBranch = conditional.branches[0];
|
|
615
|
+
const expires = thenBranch.rows.find((r) => r.name === 'expires');
|
|
616
|
+
expect(expires.isLastInGroup).toBe(true);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it('properties before a conditional are not isLastInGroup', () => {
|
|
620
|
+
const tableData = schemaToTableData(conditionalSchema);
|
|
621
|
+
const methodRow = tableData.find(
|
|
622
|
+
(r) => r.type === 'property' && r.name === 'method',
|
|
623
|
+
);
|
|
624
|
+
expect(methodRow.isLastInGroup).toBe(false);
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
it('does not add spurious ancestor lines when parent is last in group', () => {
|
|
628
|
+
// Wrap conditionalSchema inside a parent that is the last property
|
|
629
|
+
const wrappedSchema = {
|
|
630
|
+
properties: {
|
|
631
|
+
shipping: {
|
|
632
|
+
type: 'object',
|
|
633
|
+
...conditionalSchema,
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
};
|
|
637
|
+
const tableData = schemaToTableData(wrappedSchema);
|
|
638
|
+
const conditional = tableData.find((r) => r.type === 'conditional');
|
|
639
|
+
|
|
640
|
+
// shipping is the only/last root property — level 0 should NOT be in
|
|
641
|
+
// continuingLevels (no root-level siblings to connect to)
|
|
642
|
+
expect(conditional.continuingLevels).not.toContain(0);
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
it('includes ancestor lines when parent has siblings', () => {
|
|
646
|
+
const wrappedSchema = {
|
|
647
|
+
properties: {
|
|
648
|
+
shipping: {
|
|
649
|
+
type: 'object',
|
|
650
|
+
...conditionalSchema,
|
|
651
|
+
},
|
|
652
|
+
total: { type: 'number' },
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
const tableData = schemaToTableData(wrappedSchema);
|
|
656
|
+
const conditional = tableData.find((r) => r.type === 'conditional');
|
|
657
|
+
|
|
658
|
+
// shipping is NOT last (total follows) — level 0 should be in
|
|
659
|
+
// continuingLevels for the root-level tree line
|
|
660
|
+
expect(conditional.continuingLevels).toContain(0);
|
|
661
|
+
});
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
describe('complex choice property (oneOf with object options)', () => {
|
|
665
|
+
it('marks last property in last option as isLastInGroup', () => {
|
|
666
|
+
const schema = {
|
|
667
|
+
properties: {
|
|
668
|
+
contact_method: {
|
|
669
|
+
description: 'How to reach the user',
|
|
670
|
+
oneOf: [
|
|
671
|
+
{
|
|
672
|
+
title: 'Email Contact',
|
|
673
|
+
type: 'object',
|
|
674
|
+
properties: {
|
|
675
|
+
channel: { type: 'string', const: 'email' },
|
|
676
|
+
email_address: { type: 'string' },
|
|
677
|
+
},
|
|
678
|
+
required: ['channel', 'email_address'],
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
title: 'SMS Contact',
|
|
682
|
+
type: 'object',
|
|
683
|
+
properties: {
|
|
684
|
+
channel: { type: 'string', const: 'sms' },
|
|
685
|
+
phone_number: { type: 'string' },
|
|
686
|
+
},
|
|
687
|
+
required: ['channel', 'phone_number'],
|
|
688
|
+
},
|
|
689
|
+
],
|
|
690
|
+
},
|
|
691
|
+
preferences: {
|
|
692
|
+
type: 'object',
|
|
693
|
+
properties: {
|
|
694
|
+
marketing_consent: { type: 'boolean' },
|
|
695
|
+
},
|
|
696
|
+
},
|
|
697
|
+
},
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
const data = schemaToTableData(schema);
|
|
701
|
+
const choiceRow = data.find((r) => r.type === 'choice');
|
|
702
|
+
expect(choiceRow).toBeDefined();
|
|
703
|
+
|
|
704
|
+
// SMS Contact is the last option
|
|
705
|
+
const smsOption = choiceRow.options[1];
|
|
706
|
+
expect(smsOption.title).toBe('SMS Contact');
|
|
707
|
+
|
|
708
|
+
const phoneRow = smsOption.rows.find((r) => r.name === 'phone_number');
|
|
709
|
+
expect(phoneRow).toBeDefined();
|
|
710
|
+
expect(phoneRow.isLastInGroup).toBe(true);
|
|
711
|
+
|
|
712
|
+
// Email Contact is NOT the last option
|
|
713
|
+
const emailOption = choiceRow.options[0];
|
|
714
|
+
const emailAddressRow = emailOption.rows.find(
|
|
715
|
+
(r) => r.name === 'email_address',
|
|
716
|
+
);
|
|
717
|
+
expect(emailAddressRow).toBeDefined();
|
|
718
|
+
// email_address is the last prop in a non-last option — should NOT be isLastInGroup
|
|
719
|
+
expect(emailAddressRow.isLastInGroup).toBe(false);
|
|
720
|
+
});
|
|
721
|
+
});
|
|
539
722
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { schemaToTableData } from '../../helpers/schemaToTableData';
|
|
2
2
|
import choiceEventSchema from '../__fixtures__/static/schemas/choice-event.json';
|
|
3
3
|
import rootAnyOfEventSchema from '../__fixtures__/static/schemas/root-any-of-event.json';
|
|
4
|
+
import conditionalEventSchema from '../__fixtures__/static/schemas/conditional-event.json';
|
|
5
|
+
import nestedConditionalEventSchema from '../__fixtures__/static/schemas/nested-conditional-event.json';
|
|
4
6
|
|
|
5
7
|
describe('schemaToTableData', () => {
|
|
6
8
|
it('handles "oneOf" and "anyOf" correctly for a complex schema', () => {
|
|
@@ -19,7 +21,7 @@ describe('schemaToTableData', () => {
|
|
|
19
21
|
expect(eventProp.propertyType).toBe('string');
|
|
20
22
|
expect(eventProp.required).toBe(true);
|
|
21
23
|
|
|
22
|
-
// 2. Test 'user_id' property (oneOf)
|
|
24
|
+
// 2. Test 'user_id' property (oneOf with scalar options — simple choice, no property row)
|
|
23
25
|
const userIdChoice = tableData.find(
|
|
24
26
|
(row) =>
|
|
25
27
|
row.type === 'choice' &&
|
|
@@ -133,7 +135,7 @@ describe('schemaToTableData', () => {
|
|
|
133
135
|
|
|
134
136
|
const tableData = schemaToTableData(schema);
|
|
135
137
|
|
|
136
|
-
//
|
|
138
|
+
// Scalar oneOf options — renders as a single simple choice row (no property row).
|
|
137
139
|
expect(tableData).toHaveLength(1);
|
|
138
140
|
|
|
139
141
|
const choiceRow = tableData[0];
|
|
@@ -214,4 +216,263 @@ describe('schemaToTableData', () => {
|
|
|
214
216
|
const tableData = schemaToTableData(schema);
|
|
215
217
|
expect(tableData[0].examples).toEqual(['default-value']);
|
|
216
218
|
});
|
|
219
|
+
|
|
220
|
+
describe('if/then/else conditional support', () => {
|
|
221
|
+
it('creates a conditional row for schema with if/then/else at root level', () => {
|
|
222
|
+
const tableData = schemaToTableData(conditionalEventSchema);
|
|
223
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
224
|
+
expect(conditionalRow).toBeDefined();
|
|
225
|
+
expect(conditionalRow.level).toBe(0);
|
|
226
|
+
expect(conditionalRow.condition).toBeDefined();
|
|
227
|
+
expect(conditionalRow.branches).toBeDefined();
|
|
228
|
+
expect(conditionalRow.branches).toHaveLength(2); // then + else
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('flags condition rows with isCondition: true', () => {
|
|
232
|
+
const tableData = schemaToTableData(conditionalEventSchema);
|
|
233
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
234
|
+
expect(conditionalRow.condition.rows.length).toBeGreaterThan(0);
|
|
235
|
+
conditionalRow.condition.rows.forEach((row) => {
|
|
236
|
+
expect(row.isCondition).toBe(true);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('processes then branch rows correctly', () => {
|
|
241
|
+
const tableData = schemaToTableData(conditionalEventSchema);
|
|
242
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
243
|
+
const thenBranch = conditionalRow.branches.find(
|
|
244
|
+
(b) => b.title === 'Then',
|
|
245
|
+
);
|
|
246
|
+
expect(thenBranch).toBeDefined();
|
|
247
|
+
// then has postal_code + state
|
|
248
|
+
expect(thenBranch.rows.length).toBe(2);
|
|
249
|
+
const postalCode = thenBranch.rows.find((r) => r.name === 'postal_code');
|
|
250
|
+
expect(postalCode).toBeDefined();
|
|
251
|
+
expect(postalCode.propertyType).toBe('string');
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('processes else branch rows correctly', () => {
|
|
255
|
+
const tableData = schemaToTableData(conditionalEventSchema);
|
|
256
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
257
|
+
const elseBranch = conditionalRow.branches.find(
|
|
258
|
+
(b) => b.title === 'Else',
|
|
259
|
+
);
|
|
260
|
+
expect(elseBranch).toBeDefined();
|
|
261
|
+
// else has postal_code only
|
|
262
|
+
expect(elseBranch.rows.length).toBe(1);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('handles if/then without else', () => {
|
|
266
|
+
const schema = {
|
|
267
|
+
type: 'object',
|
|
268
|
+
properties: { status: { type: 'string' } },
|
|
269
|
+
if: { properties: { status: { const: 'active' } } },
|
|
270
|
+
then: {
|
|
271
|
+
properties: { active_since: { type: 'string' } },
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
const tableData = schemaToTableData(schema);
|
|
275
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
276
|
+
expect(conditionalRow).toBeDefined();
|
|
277
|
+
expect(conditionalRow.branches).toHaveLength(1);
|
|
278
|
+
expect(conditionalRow.branches[0].title).toBe('Then');
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('renders regular properties alongside conditional rows', () => {
|
|
282
|
+
const tableData = schemaToTableData(conditionalEventSchema);
|
|
283
|
+
const propRows = tableData.filter((r) => r.type === 'property');
|
|
284
|
+
// event + country
|
|
285
|
+
expect(propRows.length).toBe(2);
|
|
286
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
287
|
+
expect(conditionalRow).toBeDefined();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('creates nested conditional row inside a property sub-schema', () => {
|
|
291
|
+
const tableData = schemaToTableData(nestedConditionalEventSchema);
|
|
292
|
+
|
|
293
|
+
// Should have event property + shipping property + shipping's children + conditional
|
|
294
|
+
const shippingProp = tableData.find(
|
|
295
|
+
(r) => r.type === 'property' && r.name === 'shipping',
|
|
296
|
+
);
|
|
297
|
+
expect(shippingProp).toBeDefined();
|
|
298
|
+
expect(shippingProp.hasChildren).toBe(true);
|
|
299
|
+
|
|
300
|
+
// method property at level 1
|
|
301
|
+
const methodProp = tableData.find(
|
|
302
|
+
(r) => r.type === 'property' && r.name === 'method',
|
|
303
|
+
);
|
|
304
|
+
expect(methodProp).toBeDefined();
|
|
305
|
+
expect(methodProp.level).toBe(1);
|
|
306
|
+
|
|
307
|
+
// Conditional row should be nested at level 1 (inside shipping)
|
|
308
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
309
|
+
expect(conditionalRow).toBeDefined();
|
|
310
|
+
expect(conditionalRow.level).toBe(1);
|
|
311
|
+
expect(conditionalRow.branches).toHaveLength(2);
|
|
312
|
+
|
|
313
|
+
// Then branch has priority_level
|
|
314
|
+
const thenBranch = conditionalRow.branches.find(
|
|
315
|
+
(b) => b.title === 'Then',
|
|
316
|
+
);
|
|
317
|
+
expect(thenBranch.rows.length).toBe(1);
|
|
318
|
+
expect(thenBranch.rows[0].name).toBe('priority_level');
|
|
319
|
+
|
|
320
|
+
// Else branch has estimated_days
|
|
321
|
+
const elseBranch = conditionalRow.branches.find(
|
|
322
|
+
(b) => b.title === 'Else',
|
|
323
|
+
);
|
|
324
|
+
expect(elseBranch.rows.length).toBe(1);
|
|
325
|
+
expect(elseBranch.rows[0].name).toBe('estimated_days');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('handles if/then/else inside array items', () => {
|
|
329
|
+
const schema = {
|
|
330
|
+
type: 'object',
|
|
331
|
+
properties: {
|
|
332
|
+
items: {
|
|
333
|
+
type: 'array',
|
|
334
|
+
items: {
|
|
335
|
+
type: 'object',
|
|
336
|
+
properties: {
|
|
337
|
+
kind: { type: 'string' },
|
|
338
|
+
},
|
|
339
|
+
if: { properties: { kind: { const: 'digital' } } },
|
|
340
|
+
then: {
|
|
341
|
+
properties: {
|
|
342
|
+
download_url: { type: 'string' },
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
else: {
|
|
346
|
+
properties: {
|
|
347
|
+
weight: { type: 'number' },
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
const tableData = schemaToTableData(schema);
|
|
355
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
356
|
+
expect(conditionalRow).toBeDefined();
|
|
357
|
+
// items is at level 0, array items content is at level 1
|
|
358
|
+
expect(conditionalRow.level).toBe(1);
|
|
359
|
+
expect(conditionalRow.branches).toHaveLength(2);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('preserves parent continuingLevels in nested conditional branch rows', () => {
|
|
363
|
+
// Schema where the conditional is NOT the last child - the parent
|
|
364
|
+
// property has siblings after it, so continuing lines must flow through
|
|
365
|
+
const schema = {
|
|
366
|
+
type: 'object',
|
|
367
|
+
properties: {
|
|
368
|
+
shipping: {
|
|
369
|
+
type: 'object',
|
|
370
|
+
description: 'Shipping details.',
|
|
371
|
+
properties: {
|
|
372
|
+
method: {
|
|
373
|
+
type: 'string',
|
|
374
|
+
examples: ['express'],
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
required: ['method'],
|
|
378
|
+
if: {
|
|
379
|
+
properties: { method: { const: 'express' } },
|
|
380
|
+
required: ['method'],
|
|
381
|
+
},
|
|
382
|
+
then: {
|
|
383
|
+
properties: {
|
|
384
|
+
priority_level: {
|
|
385
|
+
type: 'string',
|
|
386
|
+
examples: ['high'],
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
else: {
|
|
391
|
+
properties: {
|
|
392
|
+
estimated_days: { type: 'integer', examples: [5] },
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
total: {
|
|
397
|
+
type: 'number',
|
|
398
|
+
description: 'Order total.',
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
const tableData = schemaToTableData(schema);
|
|
404
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
405
|
+
|
|
406
|
+
// shipping is at level 0 and is NOT the last root property (total follows)
|
|
407
|
+
// So the conditional row and its branch rows should have level 0
|
|
408
|
+
// in continuingLevels to draw the parent's continuing line.
|
|
409
|
+
expect(conditionalRow.continuingLevels).toContain(0);
|
|
410
|
+
|
|
411
|
+
const thenBranch = conditionalRow.branches.find(
|
|
412
|
+
(b) => b.title === 'Then',
|
|
413
|
+
);
|
|
414
|
+
const priorityLevel = thenBranch.rows.find(
|
|
415
|
+
(r) => r.name === 'priority_level',
|
|
416
|
+
);
|
|
417
|
+
expect(priorityLevel.continuingLevels).toContain(0);
|
|
418
|
+
|
|
419
|
+
const conditionMethod = conditionalRow.condition.rows.find(
|
|
420
|
+
(r) => r.name === 'method',
|
|
421
|
+
);
|
|
422
|
+
expect(conditionMethod.continuingLevels).toContain(0);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('does not add spurious parent level to continuingLevels when parent is last in group', () => {
|
|
426
|
+
// In the nested-conditional-event, shipping is the last root property.
|
|
427
|
+
// Level 0 should NOT appear in continuingLevels for any conditional
|
|
428
|
+
// or branch rows — the immediate parent connector is handled at the
|
|
429
|
+
// component level (ConditionalRows pushes level-1 to ancestorLevels).
|
|
430
|
+
const tableData = schemaToTableData(nestedConditionalEventSchema);
|
|
431
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
432
|
+
|
|
433
|
+
// The conditional row should have level 1 (for property sibling lines)
|
|
434
|
+
// but NOT level 0 (shipping is the last root property)
|
|
435
|
+
expect(conditionalRow.continuingLevels).toContain(1);
|
|
436
|
+
expect(conditionalRow.continuingLevels).not.toContain(0);
|
|
437
|
+
|
|
438
|
+
// Then branch rows inherit continuingLevels without the parent level
|
|
439
|
+
const thenBranch = conditionalRow.branches.find(
|
|
440
|
+
(b) => b.title === 'Then',
|
|
441
|
+
);
|
|
442
|
+
thenBranch.rows.forEach((row) => {
|
|
443
|
+
expect(row.continuingLevels).not.toContain(0);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// Else branch rows also should NOT have the parent line
|
|
447
|
+
const elseBranch = conditionalRow.branches.find(
|
|
448
|
+
(b) => b.title === 'Else',
|
|
449
|
+
);
|
|
450
|
+
elseBranch.rows.forEach((row) => {
|
|
451
|
+
expect(row.continuingLevels).not.toContain(0);
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('marks condition (if) rows with their natural isLastInGroup values', () => {
|
|
456
|
+
const tableData = schemaToTableData(nestedConditionalEventSchema);
|
|
457
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
458
|
+
// Condition rows keep their natural isLastInGroup so the tree within the
|
|
459
|
+
// If block is self-contained and doesn't create orphan lines into Then/Else.
|
|
460
|
+
conditionalRow.condition.rows.forEach((row) => {
|
|
461
|
+
expect(row.isCondition).toBe(true);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('correctly calculates isLastInGroup with conditional as last element', () => {
|
|
466
|
+
const tableData = schemaToTableData(conditionalEventSchema);
|
|
467
|
+
// The conditional row should be the last element at root level
|
|
468
|
+
const conditionalRow = tableData.find((r) => r.type === 'conditional');
|
|
469
|
+
expect(conditionalRow.isLastInGroup).toBe(true);
|
|
470
|
+
|
|
471
|
+
// The last property before conditional should NOT be last in group
|
|
472
|
+
const countryProp = tableData.find(
|
|
473
|
+
(r) => r.type === 'property' && r.name === 'country',
|
|
474
|
+
);
|
|
475
|
+
expect(countryProp.isLastInGroup).toBe(false);
|
|
476
|
+
});
|
|
477
|
+
});
|
|
217
478
|
});
|
|
@@ -12,7 +12,7 @@ describe('createValidator', () => {
|
|
|
12
12
|
required: ['name'],
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
const validator = await createValidator(schema);
|
|
15
|
+
const validator = await createValidator([], schema);
|
|
16
16
|
const result = validator({ name: 'test' });
|
|
17
17
|
|
|
18
18
|
expect(result.valid).toBe(true);
|
|
@@ -30,7 +30,7 @@ describe('createValidator', () => {
|
|
|
30
30
|
required: ['name'],
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
const validator = await createValidator(schema);
|
|
33
|
+
const validator = await createValidator([], schema);
|
|
34
34
|
const result = validator({ name: 123 });
|
|
35
35
|
|
|
36
36
|
expect(result.valid).toBe(false);
|
|
@@ -49,7 +49,7 @@ describe('createValidator', () => {
|
|
|
49
49
|
required: ['name'],
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
const validator = await createValidator(schema);
|
|
52
|
+
const validator = await createValidator([], schema);
|
|
53
53
|
const result = validator({ name: 'test' });
|
|
54
54
|
|
|
55
55
|
expect(result.valid).toBe(true);
|
|
@@ -68,7 +68,7 @@ describe('createValidator', () => {
|
|
|
68
68
|
required: ['name'],
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
-
const validator = await createValidator(schema);
|
|
71
|
+
const validator = await createValidator([], schema);
|
|
72
72
|
const result = validator({ name: 'test' });
|
|
73
73
|
|
|
74
74
|
expect(result.valid).toBe(true);
|
|
@@ -87,7 +87,7 @@ describe('createValidator', () => {
|
|
|
87
87
|
required: ['name'],
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
-
const validator = await createValidator(schema);
|
|
90
|
+
const validator = await createValidator([], schema);
|
|
91
91
|
const result = validator({ name: 'test' });
|
|
92
92
|
|
|
93
93
|
expect(result.valid).toBe(true);
|
|
@@ -106,7 +106,7 @@ describe('createValidator', () => {
|
|
|
106
106
|
required: ['name'],
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
const validator = await createValidator(schema);
|
|
109
|
+
const validator = await createValidator([], schema);
|
|
110
110
|
const result = validator({ name: 'test' });
|
|
111
111
|
|
|
112
112
|
expect(result.valid).toBe(true);
|