@strapi/typescript-utils 4.2.0-beta.4 → 4.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.
@@ -0,0 +1,703 @@
1
+ 'use strict';
2
+
3
+ jest.mock('../../../generators/schemas/imports', () => ({ addImport: jest.fn() }));
4
+
5
+ const consoleWarnMock = jest.spyOn(console, 'warn').mockImplementation();
6
+
7
+ const ts = require('typescript');
8
+
9
+ const attributeToPropertySignature = require('../../../generators/schemas/attributes');
10
+ const {
11
+ getAttributeType,
12
+ getAttributeModifiers,
13
+ } = require('../../../generators/schemas/attributes');
14
+ const { addImport } = require('../../../generators/schemas/imports');
15
+
16
+ // TODO: emit definition (to a string) & also check snapshots based on that. It would allow checking both the structure & the output.
17
+ describe('Attributes', () => {
18
+ afterEach(() => {
19
+ jest.resetAllMocks();
20
+ });
21
+
22
+ describe('Attribute to Property Signature', () => {
23
+ const schema = { uid: 'api::foo.foo' };
24
+ const attributeName = 'foo';
25
+
26
+ const toPropertySignature = attribute => {
27
+ return attributeToPropertySignature(schema, attributeName, attribute);
28
+ };
29
+
30
+ const defaultAssertion = node => {
31
+ expect(node.kind).toBe(ts.SyntaxKind.PropertySignature);
32
+ expect(node.name.escapedText).toBe(attributeName);
33
+ expect(node.type.kind).toBe(ts.SyntaxKind.IntersectionType);
34
+ };
35
+
36
+ test('Invalid attribute type', () => {
37
+ const attribute = { type: 'invalid' };
38
+ const prop = toPropertySignature(attribute);
39
+
40
+ expect(prop).toBeNull();
41
+ });
42
+
43
+ test('Attribute without type argument', () => {
44
+ const attribute = { type: 'string' };
45
+ const prop = toPropertySignature(attribute);
46
+
47
+ defaultAssertion(prop);
48
+
49
+ expect(prop.type.types).toHaveLength(1);
50
+ expect(prop.type.types[0].kind).toBe(ts.SyntaxKind.TypeReference);
51
+ expect(prop.type.types[0].typeName.escapedText).toBe('StringAttribute');
52
+ expect(prop.type.types[0].typeArguments).toBeUndefined();
53
+ });
54
+
55
+ test('Attribute with type argument', () => {
56
+ const attribute = { type: 'component', component: 'default.comp' };
57
+ const prop = toPropertySignature(attribute);
58
+
59
+ defaultAssertion(prop);
60
+
61
+ expect(prop.type.types).toHaveLength(1);
62
+ expect(prop.type.types[0].kind).toBe(ts.SyntaxKind.TypeReference);
63
+ expect(prop.type.types[0].typeName.escapedText).toBe('ComponentAttribute');
64
+ expect(prop.type.types[0].typeArguments).toHaveLength(1);
65
+ expect(prop.type.types[0].typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
66
+ expect(prop.type.types[0].typeArguments[0].text).toBe('default.comp');
67
+ });
68
+
69
+ test('Attribute with type argument and options', () => {
70
+ const attribute = {
71
+ type: 'enumeration',
72
+ enum: ['a', 'b'],
73
+ default: 'b',
74
+ configurable: false,
75
+ };
76
+ const prop = toPropertySignature(attribute);
77
+
78
+ defaultAssertion(prop);
79
+
80
+ expect(prop.type.types).toHaveLength(2);
81
+
82
+ const [attributeType, requiredOptionType] = prop.type.types;
83
+
84
+ expect(attributeType.kind).toBe(ts.SyntaxKind.TypeReference);
85
+ expect(attributeType.typeName.escapedText).toBe('EnumerationAttribute');
86
+ expect(attributeType.typeArguments).toHaveLength(1);
87
+ expect(attributeType.typeArguments[0].kind).toBe(ts.SyntaxKind.TupleType);
88
+ expect(attributeType.typeArguments[0].elements[0].text).toBe('a');
89
+ expect(attributeType.typeArguments[0].elements[1].text).toBe('b');
90
+
91
+ expect(requiredOptionType.kind).toBe(ts.SyntaxKind.TypeReference);
92
+ expect(requiredOptionType.typeName.escapedText).toBe('DefaultTo');
93
+ expect(requiredOptionType.typeArguments).toHaveLength(1);
94
+ expect(requiredOptionType.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
95
+ expect(requiredOptionType.typeArguments[0].text).toBe('b');
96
+ });
97
+ });
98
+
99
+ describe('Get Attribute Type / Mappers', () => {
100
+ test('If the attribute type is not valid then log an error and exit early without importing the type', () => {
101
+ const typeNode = getAttributeType('foo', { type: 'invalid', uid: 'api::foo.foo' });
102
+
103
+ expect(typeNode).toBeNull();
104
+ expect(consoleWarnMock).toHaveBeenCalledWith(
105
+ '"foo" attribute from "undefined" has an invalid type: "invalid"'
106
+ );
107
+ expect(addImport).not.toHaveBeenCalled();
108
+ });
109
+
110
+ test.each([
111
+ ['string', 'StringAttribute'],
112
+ ['text', 'TextAttribute'],
113
+ ['richtext', 'RichTextAttribute'],
114
+ ['password', 'PasswordAttribute'],
115
+ ['email', 'EmailAttribute'],
116
+ ['date', 'DateAttribute'],
117
+ ['time', 'TimeAttribute'],
118
+ ['datetime', 'DateTimeAttribute'],
119
+ ['timestamp', 'TimestampAttribute'],
120
+ ['integer', 'IntegerAttribute'],
121
+ ['biginteger', 'BigIntegerAttribute'],
122
+ ['float', 'FloatAttribute'],
123
+ ['decimal', 'DecimalAttribute'],
124
+ ['boolean', 'BooleanAttribute'],
125
+ ['json', 'JSONAttribute'],
126
+ ['media', 'MediaAttribute'],
127
+ ])('Basic %p attribute should map to a %p type', (type, expectedType) => {
128
+ const typeNode = getAttributeType('foo', { type });
129
+
130
+ expect(ts.isTypeNode(typeNode)).toBeTruthy();
131
+
132
+ expect(typeNode.kind).toBe(ts.SyntaxKind.TypeReference);
133
+ expect(typeNode.typeName.escapedText).toBe(expectedType);
134
+ expect(typeNode.typeArguments).toBeUndefined();
135
+
136
+ expect(consoleWarnMock).not.toHaveBeenCalled();
137
+ expect(addImport).toHaveBeenCalledWith(expectedType);
138
+ });
139
+
140
+ describe('Complex types (with generic type parameters)', () => {
141
+ const defaultAssertions = (typeNode, typeName) => {
142
+ expect(ts.isTypeNode(typeNode)).toBeTruthy();
143
+
144
+ expect(typeNode.kind).toBe(ts.SyntaxKind.TypeReference);
145
+ expect(typeNode.typeName.escapedText).toBe(typeName);
146
+
147
+ expect(consoleWarnMock).not.toHaveBeenCalled();
148
+ expect(addImport).toHaveBeenCalledWith(typeName);
149
+ };
150
+
151
+ describe('Enumeration', () => {
152
+ test('Enumeration with an enum property', () => {
153
+ const attribute = { type: 'enumeration', enum: ['a', 'b', 'c'] };
154
+ const typeNode = getAttributeType('foo', attribute);
155
+
156
+ defaultAssertions(typeNode, 'EnumerationAttribute');
157
+
158
+ expect(typeNode.typeArguments).toHaveLength(1);
159
+ expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.TupleType);
160
+
161
+ const tupleElements = typeNode.typeArguments[0].elements;
162
+
163
+ attribute.enum.forEach((value, index) => {
164
+ const element = tupleElements[index];
165
+
166
+ expect(element.kind).toBe(ts.SyntaxKind.StringLiteral);
167
+ expect(element.text).toBe(value);
168
+ });
169
+ });
170
+ });
171
+
172
+ describe('UID', () => {
173
+ test('UID with no options and no target field', () => {
174
+ const attribute = { type: 'uid' };
175
+ const typeNode = getAttributeType('foo', attribute);
176
+
177
+ defaultAssertions(typeNode, 'UIDAttribute');
178
+
179
+ expect(typeNode.typeArguments).toBeUndefined();
180
+ });
181
+
182
+ test('UID with a target field and no options', () => {
183
+ const attribute = { type: 'uid', targetField: 'bar' };
184
+ const typeNode = getAttributeType('foo', attribute, 'api::bar.bar');
185
+
186
+ defaultAssertions(typeNode, 'UIDAttribute');
187
+
188
+ expect(typeNode.typeArguments).not.toBeUndefined();
189
+ expect(typeNode.typeArguments).toHaveLength(2);
190
+
191
+ expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
192
+ expect(typeNode.typeArguments[0].text).toBe('api::bar.bar');
193
+
194
+ expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.StringLiteral);
195
+ expect(typeNode.typeArguments[1].text).toBe('bar');
196
+ });
197
+
198
+ test('UID with partial options and no target field', () => {
199
+ const attribute = { type: 'uid', options: { separator: '_' } };
200
+ const typeNode = getAttributeType('foo', attribute);
201
+
202
+ defaultAssertions(typeNode, 'UIDAttribute');
203
+
204
+ expect(typeNode.typeArguments).toHaveLength(3);
205
+
206
+ expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.UndefinedKeyword);
207
+ expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.UndefinedKeyword);
208
+
209
+ const optionsLiteralNode = typeNode.typeArguments[2];
210
+
211
+ expect(optionsLiteralNode.kind).toBe(ts.SyntaxKind.TypeLiteral);
212
+ expect(optionsLiteralNode.members).toHaveLength(1);
213
+
214
+ expect(optionsLiteralNode.members[0].kind).toBe(ts.SyntaxKind.PropertyDeclaration);
215
+
216
+ expect(optionsLiteralNode.members[0].name.kind).toBe(ts.SyntaxKind.Identifier);
217
+ expect(optionsLiteralNode.members[0].name.escapedText).toBe('separator');
218
+
219
+ expect(optionsLiteralNode.members[0].type.kind).toBe(ts.SyntaxKind.StringLiteral);
220
+ expect(optionsLiteralNode.members[0].type.text).toBe('_');
221
+ });
222
+
223
+ test('UID with options and a target field', () => {
224
+ const attribute = { type: 'uid', options: { separator: '_' }, targetField: 'bar' };
225
+ const typeNode = getAttributeType('foo', attribute, 'api::bar.bar');
226
+
227
+ defaultAssertions(typeNode, 'UIDAttribute');
228
+
229
+ expect(typeNode.typeArguments).toHaveLength(3);
230
+
231
+ expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
232
+ expect(typeNode.typeArguments[0].text).toBe('api::bar.bar');
233
+
234
+ expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.StringLiteral);
235
+ expect(typeNode.typeArguments[1].text).toBe('bar');
236
+
237
+ const optionsLiteralNode = typeNode.typeArguments[2];
238
+
239
+ expect(optionsLiteralNode.kind).toBe(ts.SyntaxKind.TypeLiteral);
240
+ expect(optionsLiteralNode.members).toHaveLength(1);
241
+
242
+ expect(optionsLiteralNode.members[0].kind).toBe(ts.SyntaxKind.PropertyDeclaration);
243
+
244
+ expect(optionsLiteralNode.members[0].name.kind).toBe(ts.SyntaxKind.Identifier);
245
+ expect(optionsLiteralNode.members[0].name.escapedText).toBe('separator');
246
+
247
+ expect(optionsLiteralNode.members[0].type.kind).toBe(ts.SyntaxKind.StringLiteral);
248
+ expect(optionsLiteralNode.members[0].type.text).toBe('_');
249
+ });
250
+ });
251
+
252
+ describe('Relation', () => {
253
+ test('Basic relation', () => {
254
+ const attribute = { type: 'relation', relation: 'oneToOne', target: 'api::bar.bar' };
255
+ const typeNode = getAttributeType('foo', attribute, 'api::foo.foo');
256
+
257
+ defaultAssertions(typeNode, 'RelationAttribute');
258
+
259
+ expect(typeNode.typeArguments).toHaveLength(3);
260
+
261
+ expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
262
+ expect(typeNode.typeArguments[0].text).toBe('api::foo.foo');
263
+
264
+ expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.StringLiteral);
265
+ expect(typeNode.typeArguments[1].text).toBe('oneToOne');
266
+
267
+ expect(typeNode.typeArguments[2].kind).toBe(ts.SyntaxKind.StringLiteral);
268
+ expect(typeNode.typeArguments[2].text).toBe('api::bar.bar');
269
+ });
270
+
271
+ test('Polymorphic relation', () => {
272
+ const attribute = { type: 'relation', relation: 'morphMany' };
273
+ const typeNode = getAttributeType('foo', attribute, 'api::foo.foo');
274
+
275
+ defaultAssertions(typeNode, 'RelationAttribute');
276
+
277
+ expect(typeNode.typeArguments).toHaveLength(2);
278
+
279
+ expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
280
+ expect(typeNode.typeArguments[0].text).toBe('api::foo.foo');
281
+
282
+ expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.StringLiteral);
283
+ expect(typeNode.typeArguments[1].text).toBe('morphMany');
284
+ });
285
+ });
286
+
287
+ describe('Component', () => {
288
+ test('Repeatable component', () => {
289
+ const attribute = { type: 'component', component: 'default.comp', repeatable: true };
290
+ const typeNode = getAttributeType('foo', attribute);
291
+
292
+ defaultAssertions(typeNode, 'ComponentAttribute');
293
+
294
+ expect(typeNode.typeArguments).toHaveLength(2);
295
+
296
+ expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
297
+ expect(typeNode.typeArguments[0].text).toBe('default.comp');
298
+
299
+ expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.TrueKeyword);
300
+ });
301
+
302
+ test('Non repeatable component', () => {
303
+ const attribute = { type: 'component', component: 'default.comp' };
304
+ const typeNode = getAttributeType('foo', attribute);
305
+
306
+ defaultAssertions(typeNode, 'ComponentAttribute');
307
+
308
+ expect(typeNode.typeArguments).toHaveLength(1);
309
+
310
+ expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
311
+ expect(typeNode.typeArguments[0].text).toBe('default.comp');
312
+ });
313
+ });
314
+
315
+ describe('Dynamic Zone', () => {
316
+ test('Dynamic Zone with an array of components (targets)', () => {
317
+ const attribute = { type: 'dynamiczone', components: ['default.comp1', 'default.comp2'] };
318
+ const typeNode = getAttributeType('foo', attribute);
319
+
320
+ defaultAssertions(typeNode, 'DynamicZoneAttribute');
321
+
322
+ expect(typeNode.typeArguments).toHaveLength(1);
323
+
324
+ const [typeArgument] = typeNode.typeArguments;
325
+
326
+ expect(typeArgument.kind).toBe(ts.SyntaxKind.TupleType);
327
+ expect(typeArgument.elements).toHaveLength(2);
328
+
329
+ expect(typeArgument.elements[0].kind).toBe(ts.SyntaxKind.StringLiteral);
330
+ expect(typeArgument.elements[0].text).toBe('default.comp1');
331
+
332
+ expect(typeArgument.elements[1].kind).toBe(ts.SyntaxKind.StringLiteral);
333
+ expect(typeArgument.elements[1].text).toBe('default.comp2');
334
+ });
335
+ });
336
+ });
337
+ });
338
+
339
+ describe('Get Attribute Modifiers', () => {
340
+ describe('Units', () => {
341
+ describe('Required', () => {
342
+ test('No required', () => {
343
+ const attribute = {};
344
+ const modifiers = getAttributeModifiers(attribute);
345
+
346
+ expect(modifiers).toHaveLength(0);
347
+ });
348
+
349
+ test('Required: false', () => {
350
+ const attribute = { required: false };
351
+ const modifiers = getAttributeModifiers(attribute);
352
+
353
+ expect(modifiers).toHaveLength(0);
354
+ });
355
+
356
+ test('Required: true', () => {
357
+ const attribute = { required: true };
358
+ const modifiers = getAttributeModifiers(attribute);
359
+
360
+ expect(modifiers).toHaveLength(1);
361
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
362
+ expect(modifiers[0].typeName.escapedText).toBe('RequiredAttribute');
363
+ });
364
+ });
365
+
366
+ describe('Private', () => {
367
+ test('No private', () => {
368
+ const attribute = {};
369
+ const modifiers = getAttributeModifiers(attribute);
370
+
371
+ expect(modifiers).toHaveLength(0);
372
+ });
373
+
374
+ test('Private: false', () => {
375
+ const attribute = { private: false };
376
+ const modifiers = getAttributeModifiers(attribute);
377
+
378
+ expect(modifiers).toHaveLength(0);
379
+ });
380
+
381
+ test('Private: true', () => {
382
+ const attribute = { private: true };
383
+ const modifiers = getAttributeModifiers(attribute);
384
+
385
+ expect(modifiers).toHaveLength(1);
386
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
387
+ expect(modifiers[0].typeName.escapedText).toBe('PrivateAttribute');
388
+ });
389
+ });
390
+
391
+ describe('Unique', () => {
392
+ test('No unique', () => {
393
+ const attribute = {};
394
+ const modifiers = getAttributeModifiers(attribute);
395
+
396
+ expect(modifiers).toHaveLength(0);
397
+ });
398
+
399
+ test('Unique: false', () => {
400
+ const attribute = { unique: false };
401
+ const modifiers = getAttributeModifiers(attribute);
402
+
403
+ expect(modifiers).toHaveLength(0);
404
+ });
405
+
406
+ test('Unique: true', () => {
407
+ const attribute = { unique: true };
408
+ const modifiers = getAttributeModifiers(attribute);
409
+
410
+ expect(modifiers).toHaveLength(1);
411
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
412
+ expect(modifiers[0].typeName.escapedText).toBe('UniqueAttribute');
413
+ });
414
+ });
415
+
416
+ describe('Configurable', () => {
417
+ test('No configurable', () => {
418
+ const attribute = {};
419
+ const modifiers = getAttributeModifiers(attribute);
420
+
421
+ expect(modifiers).toHaveLength(0);
422
+ });
423
+
424
+ test('Configurable: false', () => {
425
+ const attribute = { configurable: false };
426
+ const modifiers = getAttributeModifiers(attribute);
427
+
428
+ expect(modifiers).toHaveLength(0);
429
+ });
430
+
431
+ test('Configurable: true', () => {
432
+ const attribute = { configurable: true };
433
+ const modifiers = getAttributeModifiers(attribute);
434
+
435
+ expect(modifiers).toHaveLength(1);
436
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
437
+ expect(modifiers[0].typeName.escapedText).toBe('ConfigurableAttribute');
438
+ });
439
+ });
440
+
441
+ describe('Plugin Options', () => {
442
+ test('No plugin options', () => {
443
+ const attribute = {};
444
+ const modifiers = getAttributeModifiers(attribute);
445
+
446
+ expect(modifiers).toHaveLength(0);
447
+ });
448
+
449
+ test('Plugin Options: { foo: { enabled: true } }', () => {
450
+ const attribute = { pluginOptions: { foo: { enabled: true } } };
451
+ const modifiers = getAttributeModifiers(attribute);
452
+
453
+ expect(modifiers).toHaveLength(1);
454
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
455
+ expect(modifiers[0].typeName.escapedText).toBe('SetPluginOptions');
456
+ expect(modifiers[0].typeArguments).toHaveLength(1);
457
+ expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
458
+ expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
459
+ expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
460
+ ts.SyntaxKind.PropertyDeclaration
461
+ );
462
+ expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('foo');
463
+ expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
464
+ ts.SyntaxKind.TypeLiteral
465
+ );
466
+ expect(modifiers[0].typeArguments[0].members[0].type.members).toHaveLength(1);
467
+ expect(modifiers[0].typeArguments[0].members[0].type.members[0].kind).toBe(
468
+ ts.SyntaxKind.PropertyDeclaration
469
+ );
470
+ expect(modifiers[0].typeArguments[0].members[0].type.members[0].name.escapedText).toBe(
471
+ 'enabled'
472
+ );
473
+ expect(modifiers[0].typeArguments[0].members[0].type.members[0].type.kind).toBe(
474
+ ts.SyntaxKind.TrueKeyword
475
+ );
476
+ });
477
+ });
478
+
479
+ describe('Min / Max', () => {
480
+ test('No min or max', () => {
481
+ const attribute = {};
482
+ const modifiers = getAttributeModifiers(attribute);
483
+
484
+ expect(modifiers).toHaveLength(0);
485
+ });
486
+
487
+ test('Min: 2, no Max', () => {
488
+ const attribute = { min: 2 };
489
+ const modifiers = getAttributeModifiers(attribute);
490
+
491
+ expect(modifiers).toHaveLength(1);
492
+
493
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
494
+ expect(modifiers[0].typeName.escapedText).toBe('SetMinMax');
495
+
496
+ expect(modifiers[0].typeArguments).toHaveLength(1);
497
+ expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
498
+ expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
499
+
500
+ // Min
501
+ expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
502
+ ts.SyntaxKind.PropertyDeclaration
503
+ );
504
+ expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('min');
505
+ expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
506
+ ts.SyntaxKind.NumericLiteral
507
+ );
508
+ expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('2');
509
+ });
510
+
511
+ test('No Min, Max: 3', () => {
512
+ const attribute = { max: 3 };
513
+ const modifiers = getAttributeModifiers(attribute);
514
+
515
+ expect(modifiers).toHaveLength(1);
516
+
517
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
518
+ expect(modifiers[0].typeName.escapedText).toBe('SetMinMax');
519
+
520
+ expect(modifiers[0].typeArguments).toHaveLength(1);
521
+ expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
522
+ expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
523
+
524
+ // Min
525
+ expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
526
+ ts.SyntaxKind.PropertyDeclaration
527
+ );
528
+ expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('max');
529
+ expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
530
+ ts.SyntaxKind.NumericLiteral
531
+ );
532
+ expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('3');
533
+ });
534
+
535
+ test('Min: 4, Max: 12', () => {
536
+ const attribute = { min: 4, max: 12 };
537
+ const modifiers = getAttributeModifiers(attribute);
538
+
539
+ expect(modifiers).toHaveLength(1);
540
+
541
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
542
+ expect(modifiers[0].typeName.escapedText).toBe('SetMinMax');
543
+
544
+ expect(modifiers[0].typeArguments).toHaveLength(1);
545
+ expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
546
+ expect(modifiers[0].typeArguments[0].members).toHaveLength(2);
547
+
548
+ // Min
549
+ expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
550
+ ts.SyntaxKind.PropertyDeclaration
551
+ );
552
+ expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('min');
553
+ expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
554
+ ts.SyntaxKind.NumericLiteral
555
+ );
556
+ expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('4');
557
+
558
+ expect(modifiers[0].typeArguments[0].members[1].kind).toBe(
559
+ ts.SyntaxKind.PropertyDeclaration
560
+ );
561
+ expect(modifiers[0].typeArguments[0].members[1].name.escapedText).toBe('max');
562
+ expect(modifiers[0].typeArguments[0].members[1].type.kind).toBe(
563
+ ts.SyntaxKind.NumericLiteral
564
+ );
565
+ expect(modifiers[0].typeArguments[0].members[1].type.text).toBe('12');
566
+ });
567
+ });
568
+
569
+ describe('MinLength / MaxLength', () => {
570
+ test('No minLength or maxLength', () => {
571
+ const attribute = {};
572
+ const modifiers = getAttributeModifiers(attribute);
573
+
574
+ expect(modifiers).toHaveLength(0);
575
+ });
576
+
577
+ test('MinLength: 2, no MaxLength', () => {
578
+ const attribute = { minLength: 2 };
579
+ const modifiers = getAttributeModifiers(attribute);
580
+
581
+ expect(modifiers).toHaveLength(1);
582
+
583
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
584
+ expect(modifiers[0].typeName.escapedText).toBe('SetMinMaxLength');
585
+
586
+ expect(modifiers[0].typeArguments).toHaveLength(1);
587
+ expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
588
+ expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
589
+
590
+ // Min
591
+ expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
592
+ ts.SyntaxKind.PropertyDeclaration
593
+ );
594
+ expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('minLength');
595
+ expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
596
+ ts.SyntaxKind.NumericLiteral
597
+ );
598
+ expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('2');
599
+ });
600
+
601
+ test('No MinLength, MaxLength: 3', () => {
602
+ const attribute = { maxLength: 3 };
603
+ const modifiers = getAttributeModifiers(attribute);
604
+
605
+ expect(modifiers).toHaveLength(1);
606
+
607
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
608
+ expect(modifiers[0].typeName.escapedText).toBe('SetMinMaxLength');
609
+
610
+ expect(modifiers[0].typeArguments).toHaveLength(1);
611
+ expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
612
+ expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
613
+
614
+ // Min
615
+ expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
616
+ ts.SyntaxKind.PropertyDeclaration
617
+ );
618
+ expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('maxLength');
619
+ expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
620
+ ts.SyntaxKind.NumericLiteral
621
+ );
622
+ expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('3');
623
+ });
624
+
625
+ test('MinLength: 4, MaxLength: 12', () => {
626
+ const attribute = { minLength: 4, maxLength: 12 };
627
+ const modifiers = getAttributeModifiers(attribute);
628
+
629
+ expect(modifiers).toHaveLength(1);
630
+
631
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
632
+ expect(modifiers[0].typeName.escapedText).toBe('SetMinMaxLength');
633
+
634
+ expect(modifiers[0].typeArguments).toHaveLength(1);
635
+ expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
636
+ expect(modifiers[0].typeArguments[0].members).toHaveLength(2);
637
+
638
+ // Min
639
+ expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
640
+ ts.SyntaxKind.PropertyDeclaration
641
+ );
642
+ expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('minLength');
643
+ expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
644
+ ts.SyntaxKind.NumericLiteral
645
+ );
646
+ expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('4');
647
+
648
+ expect(modifiers[0].typeArguments[0].members[1].kind).toBe(
649
+ ts.SyntaxKind.PropertyDeclaration
650
+ );
651
+ expect(modifiers[0].typeArguments[0].members[1].name.escapedText).toBe('maxLength');
652
+ expect(modifiers[0].typeArguments[0].members[1].type.kind).toBe(
653
+ ts.SyntaxKind.NumericLiteral
654
+ );
655
+ expect(modifiers[0].typeArguments[0].members[1].type.text).toBe('12');
656
+ });
657
+ });
658
+
659
+ describe('Default', () => {
660
+ test('No default', () => {
661
+ const attribute = {};
662
+ const modifiers = getAttributeModifiers(attribute);
663
+
664
+ expect(modifiers).toHaveLength(0);
665
+ });
666
+
667
+ test('Default: true', () => {
668
+ const attribute = { default: true };
669
+ const modifiers = getAttributeModifiers(attribute);
670
+
671
+ expect(modifiers).toHaveLength(1);
672
+
673
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
674
+ expect(modifiers[0].typeName.escapedText).toBe('DefaultTo');
675
+
676
+ expect(modifiers[0].typeArguments).toHaveLength(1);
677
+ expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TrueKeyword);
678
+ });
679
+
680
+ test('Default: { enabled: true }', () => {
681
+ const attribute = { default: { enabled: true } };
682
+ const modifiers = getAttributeModifiers(attribute);
683
+
684
+ expect(modifiers).toHaveLength(1);
685
+
686
+ expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
687
+ expect(modifiers[0].typeName.escapedText).toBe('DefaultTo');
688
+
689
+ expect(modifiers[0].typeArguments).toHaveLength(1);
690
+ expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
691
+ expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
692
+ expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
693
+ ts.SyntaxKind.PropertyDeclaration
694
+ );
695
+ expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('enabled');
696
+ expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
697
+ ts.SyntaxKind.TrueKeyword
698
+ );
699
+ });
700
+ });
701
+ });
702
+ });
703
+ });