@htlkg/components 0.0.1
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/dist/composables/index.js +388 -0
- package/dist/composables/index.js.map +1 -0
- package/package.json +41 -0
- package/src/composables/index.ts +6 -0
- package/src/composables/useForm.test.ts +229 -0
- package/src/composables/useForm.ts +130 -0
- package/src/composables/useFormValidation.test.ts +189 -0
- package/src/composables/useFormValidation.ts +83 -0
- package/src/composables/useModal.property.test.ts +164 -0
- package/src/composables/useModal.ts +43 -0
- package/src/composables/useNotifications.test.ts +166 -0
- package/src/composables/useNotifications.ts +81 -0
- package/src/composables/useTable.property.test.ts +198 -0
- package/src/composables/useTable.ts +134 -0
- package/src/composables/useTabs.property.test.ts +247 -0
- package/src/composables/useTabs.ts +101 -0
- package/src/data/Chart.demo.vue +340 -0
- package/src/data/Chart.md +525 -0
- package/src/data/Chart.vue +133 -0
- package/src/data/DataList.md +80 -0
- package/src/data/DataList.test.ts +69 -0
- package/src/data/DataList.vue +46 -0
- package/src/data/SearchableSelect.md +107 -0
- package/src/data/SearchableSelect.vue +124 -0
- package/src/data/Table.demo.vue +296 -0
- package/src/data/Table.md +588 -0
- package/src/data/Table.property.test.ts +548 -0
- package/src/data/Table.test.ts +562 -0
- package/src/data/Table.unit.test.ts +544 -0
- package/src/data/Table.vue +321 -0
- package/src/data/index.ts +5 -0
- package/src/domain/BrandCard.md +81 -0
- package/src/domain/BrandCard.vue +63 -0
- package/src/domain/BrandSelector.md +84 -0
- package/src/domain/BrandSelector.vue +65 -0
- package/src/domain/ProductBadge.md +60 -0
- package/src/domain/ProductBadge.vue +47 -0
- package/src/domain/UserAvatar.md +84 -0
- package/src/domain/UserAvatar.vue +60 -0
- package/src/domain/domain-components.property.test.ts +449 -0
- package/src/domain/index.ts +4 -0
- package/src/forms/DateRange.demo.vue +273 -0
- package/src/forms/DateRange.md +337 -0
- package/src/forms/DateRange.vue +110 -0
- package/src/forms/JsonSchemaForm.demo.vue +549 -0
- package/src/forms/JsonSchemaForm.md +112 -0
- package/src/forms/JsonSchemaForm.property.test.ts +817 -0
- package/src/forms/JsonSchemaForm.test.ts +601 -0
- package/src/forms/JsonSchemaForm.unit.test.ts +801 -0
- package/src/forms/JsonSchemaForm.vue +615 -0
- package/src/forms/index.ts +3 -0
- package/src/index.ts +17 -0
- package/src/navigation/Breadcrumbs.demo.vue +142 -0
- package/src/navigation/Breadcrumbs.md +102 -0
- package/src/navigation/Breadcrumbs.test.ts +69 -0
- package/src/navigation/Breadcrumbs.vue +58 -0
- package/src/navigation/Stepper.demo.vue +337 -0
- package/src/navigation/Stepper.md +174 -0
- package/src/navigation/Stepper.vue +146 -0
- package/src/navigation/Tabs.demo.vue +293 -0
- package/src/navigation/Tabs.md +163 -0
- package/src/navigation/Tabs.test.ts +176 -0
- package/src/navigation/Tabs.vue +104 -0
- package/src/navigation/index.ts +5 -0
- package/src/overlays/Alert.demo.vue +377 -0
- package/src/overlays/Alert.md +248 -0
- package/src/overlays/Alert.test.ts +166 -0
- package/src/overlays/Alert.vue +70 -0
- package/src/overlays/Drawer.md +140 -0
- package/src/overlays/Drawer.test.ts +92 -0
- package/src/overlays/Drawer.vue +76 -0
- package/src/overlays/Modal.demo.vue +149 -0
- package/src/overlays/Modal.md +385 -0
- package/src/overlays/Modal.test.ts +128 -0
- package/src/overlays/Modal.vue +86 -0
- package/src/overlays/Notification.md +150 -0
- package/src/overlays/Notification.test.ts +96 -0
- package/src/overlays/Notification.vue +58 -0
- package/src/overlays/index.ts +4 -0
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import JsonSchemaForm from './JsonSchemaForm.vue';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Unit tests for JsonSchemaForm
|
|
7
|
+
* Tests individual functions and methods in isolation
|
|
8
|
+
*/
|
|
9
|
+
describe('JsonSchemaForm unit tests', () => {
|
|
10
|
+
describe('getFieldLabel()', () => {
|
|
11
|
+
it('should return field title from schema', () => {
|
|
12
|
+
const schema = {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
username: { type: 'string', title: 'User Name' }
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
20
|
+
props: { schema, modelValue: {} }
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const vm = wrapper.vm as any;
|
|
24
|
+
expect(vm.getFieldLabel('username')).toBe('User Name');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should return field name when title is not provided', () => {
|
|
28
|
+
const schema = {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
email: { type: 'string' }
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
36
|
+
props: { schema, modelValue: {} }
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const vm = wrapper.vm as any;
|
|
40
|
+
expect(vm.getFieldLabel('email')).toBe('email');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should handle missing field gracefully', () => {
|
|
44
|
+
const schema = {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
50
|
+
props: { schema, modelValue: {} }
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const vm = wrapper.vm as any;
|
|
54
|
+
expect(vm.getFieldLabel('nonexistent')).toBe('nonexistent');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('getFieldDescription()', () => {
|
|
59
|
+
it('should return field description from schema', () => {
|
|
60
|
+
const schema = {
|
|
61
|
+
type: 'object',
|
|
62
|
+
properties: {
|
|
63
|
+
password: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'Must be at least 8 characters'
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
71
|
+
props: { schema, modelValue: {} }
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const vm = wrapper.vm as any;
|
|
75
|
+
expect(vm.getFieldDescription('password')).toBe('Must be at least 8 characters');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should return empty string when description is not provided', () => {
|
|
79
|
+
const schema = {
|
|
80
|
+
type: 'object',
|
|
81
|
+
properties: {
|
|
82
|
+
field: { type: 'string' }
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
87
|
+
props: { schema, modelValue: {} }
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const vm = wrapper.vm as any;
|
|
91
|
+
expect(vm.getFieldDescription('field')).toBe('');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe('isRequired()', () => {
|
|
96
|
+
it('should return true for required fields', () => {
|
|
97
|
+
const schema = {
|
|
98
|
+
type: 'object',
|
|
99
|
+
properties: {
|
|
100
|
+
email: { type: 'string' }
|
|
101
|
+
},
|
|
102
|
+
required: ['email']
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
106
|
+
props: { schema, modelValue: {} }
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const vm = wrapper.vm as any;
|
|
110
|
+
expect(vm.isRequired('email')).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should return false for optional fields', () => {
|
|
114
|
+
const schema = {
|
|
115
|
+
type: 'object',
|
|
116
|
+
properties: {
|
|
117
|
+
nickname: { type: 'string' }
|
|
118
|
+
},
|
|
119
|
+
required: []
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
123
|
+
props: { schema, modelValue: {} }
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const vm = wrapper.vm as any;
|
|
127
|
+
expect(vm.isRequired('nickname')).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should return false when required array is not defined', () => {
|
|
131
|
+
const schema = {
|
|
132
|
+
type: 'object',
|
|
133
|
+
properties: {
|
|
134
|
+
field: { type: 'string' }
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
139
|
+
props: { schema, modelValue: {} }
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const vm = wrapper.vm as any;
|
|
143
|
+
expect(vm.isRequired('field')).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe('getWidget()', () => {
|
|
148
|
+
it('should return widget from uiSchema when specified', () => {
|
|
149
|
+
const schema = {
|
|
150
|
+
type: 'object',
|
|
151
|
+
properties: {
|
|
152
|
+
password: { type: 'string' }
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const uiSchema = {
|
|
157
|
+
password: { 'ui:widget': 'password' }
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
161
|
+
props: { schema, uiSchema, modelValue: {} }
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const vm = wrapper.vm as any;
|
|
165
|
+
expect(vm.getWidget('password')).toBe('password');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should infer toggle widget for boolean type', () => {
|
|
169
|
+
const schema = {
|
|
170
|
+
type: 'object',
|
|
171
|
+
properties: {
|
|
172
|
+
active: { type: 'boolean' }
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
177
|
+
props: { schema, modelValue: {} }
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const vm = wrapper.vm as any;
|
|
181
|
+
expect(vm.getWidget('active')).toBe('toggle');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should infer number widget for number type', () => {
|
|
185
|
+
const schema = {
|
|
186
|
+
type: 'object',
|
|
187
|
+
properties: {
|
|
188
|
+
age: { type: 'number' }
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
193
|
+
props: { schema, modelValue: {} }
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const vm = wrapper.vm as any;
|
|
197
|
+
expect(vm.getWidget('age')).toBe('number');
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should NOT auto-infer slider for numbers with min/max', () => {
|
|
201
|
+
const schema = {
|
|
202
|
+
type: 'object',
|
|
203
|
+
properties: {
|
|
204
|
+
volume: { type: 'number', minimum: 0, maximum: 100 }
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
209
|
+
props: { schema, modelValue: {} }
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const vm = wrapper.vm as any;
|
|
213
|
+
// Should be 'number', not 'slider' (slider must be explicit in uiSchema)
|
|
214
|
+
expect(vm.getWidget('volume')).toBe('number');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should infer select widget for enum', () => {
|
|
218
|
+
const schema = {
|
|
219
|
+
type: 'object',
|
|
220
|
+
properties: {
|
|
221
|
+
status: { type: 'string', enum: ['active', 'inactive'] }
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
226
|
+
props: { schema, modelValue: {} }
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const vm = wrapper.vm as any;
|
|
230
|
+
expect(vm.getWidget('status')).toBe('select');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should infer email widget for email format', () => {
|
|
234
|
+
const schema = {
|
|
235
|
+
type: 'object',
|
|
236
|
+
properties: {
|
|
237
|
+
email: { type: 'string', format: 'email' }
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
242
|
+
props: { schema, modelValue: {} }
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const vm = wrapper.vm as any;
|
|
246
|
+
expect(vm.getWidget('email')).toBe('email');
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should infer url widget for uri format', () => {
|
|
250
|
+
const schema = {
|
|
251
|
+
type: 'object',
|
|
252
|
+
properties: {
|
|
253
|
+
website: { type: 'string', format: 'uri' }
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
258
|
+
props: { schema, modelValue: {} }
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const vm = wrapper.vm as any;
|
|
262
|
+
expect(vm.getWidget('website')).toBe('url');
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('should infer textarea for long strings', () => {
|
|
266
|
+
const schema = {
|
|
267
|
+
type: 'object',
|
|
268
|
+
properties: {
|
|
269
|
+
description: { type: 'string', minLength: 150 }
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
274
|
+
props: { schema, modelValue: {} }
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const vm = wrapper.vm as any;
|
|
278
|
+
expect(vm.getWidget('description')).toBe('textarea');
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should return text as default widget', () => {
|
|
282
|
+
const schema = {
|
|
283
|
+
type: 'object',
|
|
284
|
+
properties: {
|
|
285
|
+
field: { type: 'string' }
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
290
|
+
props: { schema, modelValue: {} }
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const vm = wrapper.vm as any;
|
|
294
|
+
expect(vm.getWidget('field')).toBe('text');
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
describe('getInputType()', () => {
|
|
299
|
+
it('should return password for password widget', () => {
|
|
300
|
+
const schema = {
|
|
301
|
+
type: 'object',
|
|
302
|
+
properties: {
|
|
303
|
+
pwd: { type: 'string' }
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const uiSchema = {
|
|
308
|
+
pwd: { 'ui:widget': 'password' }
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
312
|
+
props: { schema, uiSchema, modelValue: {} }
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const vm = wrapper.vm as any;
|
|
316
|
+
expect(vm.getInputType('pwd')).toBe('password');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('should return email for email widget', () => {
|
|
320
|
+
const schema = {
|
|
321
|
+
type: 'object',
|
|
322
|
+
properties: {
|
|
323
|
+
email: { type: 'string', format: 'email' }
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
328
|
+
props: { schema, modelValue: {} }
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
const vm = wrapper.vm as any;
|
|
332
|
+
expect(vm.getInputType('email')).toBe('email');
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should return number for number widget', () => {
|
|
336
|
+
const schema = {
|
|
337
|
+
type: 'object',
|
|
338
|
+
properties: {
|
|
339
|
+
age: { type: 'number' }
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
344
|
+
props: { schema, modelValue: {} }
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const vm = wrapper.vm as any;
|
|
348
|
+
expect(vm.getInputType('age')).toBe('number');
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should return text as default', () => {
|
|
352
|
+
const schema = {
|
|
353
|
+
type: 'object',
|
|
354
|
+
properties: {
|
|
355
|
+
name: { type: 'string' }
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
360
|
+
props: { schema, modelValue: {} }
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const vm = wrapper.vm as any;
|
|
364
|
+
expect(vm.getInputType('name')).toBe('text');
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
describe('getFieldColor()', () => {
|
|
369
|
+
it('should return gray for untouched fields', () => {
|
|
370
|
+
const schema = {
|
|
371
|
+
type: 'object',
|
|
372
|
+
properties: {
|
|
373
|
+
field: { type: 'string' }
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
378
|
+
props: { schema, modelValue: {} }
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
const vm = wrapper.vm as any;
|
|
382
|
+
expect(vm.getFieldColor('field')).toBe('gray');
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should return red for fields with errors', () => {
|
|
386
|
+
const schema = {
|
|
387
|
+
type: 'object',
|
|
388
|
+
properties: {
|
|
389
|
+
email: { type: 'string', format: 'email', minLength: 1 }
|
|
390
|
+
},
|
|
391
|
+
required: ['email']
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
395
|
+
props: { schema, modelValue: { email: 'invalid' } }
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
const vm = wrapper.vm as any;
|
|
399
|
+
|
|
400
|
+
// Trigger validation
|
|
401
|
+
vm.validateField('email');
|
|
402
|
+
|
|
403
|
+
expect(vm.errors.email).toBeDefined();
|
|
404
|
+
expect(vm.getFieldColor('email')).toBe('red');
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('should return green for valid touched required fields', () => {
|
|
408
|
+
const schema = {
|
|
409
|
+
type: 'object',
|
|
410
|
+
properties: {
|
|
411
|
+
name: { type: 'string', minLength: 1 }
|
|
412
|
+
},
|
|
413
|
+
required: ['name']
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
417
|
+
props: { schema, modelValue: { name: 'John' } }
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
const vm = wrapper.vm as any;
|
|
421
|
+
|
|
422
|
+
// Mark as touched
|
|
423
|
+
vm.touched.name = true;
|
|
424
|
+
|
|
425
|
+
expect(vm.getFieldColor('name')).toBe('green');
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('should return gray for empty optional fields', () => {
|
|
429
|
+
const schema = {
|
|
430
|
+
type: 'object',
|
|
431
|
+
properties: {
|
|
432
|
+
nickname: { type: 'string' }
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
437
|
+
props: { schema, modelValue: {} }
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const vm = wrapper.vm as any;
|
|
441
|
+
vm.touched.nickname = true;
|
|
442
|
+
|
|
443
|
+
expect(vm.getFieldColor('nickname')).toBe('gray');
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
describe('getPlaceholder()', () => {
|
|
448
|
+
it('should return placeholder from uiSchema', () => {
|
|
449
|
+
const schema = {
|
|
450
|
+
type: 'object',
|
|
451
|
+
properties: {
|
|
452
|
+
email: { type: 'string' }
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
const uiSchema = {
|
|
457
|
+
email: { 'ui:placeholder': 'Enter your email' }
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
461
|
+
props: { schema, uiSchema, modelValue: {} }
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
const vm = wrapper.vm as any;
|
|
465
|
+
expect(vm.getPlaceholder('email')).toBe('Enter your email');
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it('should return empty string when placeholder is not defined', () => {
|
|
469
|
+
const schema = {
|
|
470
|
+
type: 'object',
|
|
471
|
+
properties: {
|
|
472
|
+
field: { type: 'string' }
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
477
|
+
props: { schema, modelValue: {} }
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
const vm = wrapper.vm as any;
|
|
481
|
+
expect(vm.getPlaceholder('field')).toBe('');
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
describe('getSelectOptions()', () => {
|
|
486
|
+
it('should return options from enum', () => {
|
|
487
|
+
const schema = {
|
|
488
|
+
type: 'object',
|
|
489
|
+
properties: {
|
|
490
|
+
status: {
|
|
491
|
+
type: 'string',
|
|
492
|
+
enum: ['active', 'inactive', 'pending']
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
498
|
+
props: { schema, modelValue: {} }
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
const vm = wrapper.vm as any;
|
|
502
|
+
const options = vm.getSelectOptions('status');
|
|
503
|
+
|
|
504
|
+
expect(options).toHaveLength(3);
|
|
505
|
+
expect(options[0]).toEqual({ id: 'active', name: 'active', label: 'active' });
|
|
506
|
+
expect(options[1]).toEqual({ id: 'inactive', name: 'inactive', label: 'inactive' });
|
|
507
|
+
expect(options[2]).toEqual({ id: 'pending', name: 'pending', label: 'pending' });
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it('should return empty array when enum is not defined', () => {
|
|
511
|
+
const schema = {
|
|
512
|
+
type: 'object',
|
|
513
|
+
properties: {
|
|
514
|
+
field: { type: 'string' }
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
519
|
+
props: { schema, modelValue: {} }
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
const vm = wrapper.vm as any;
|
|
523
|
+
expect(vm.getSelectOptions('field')).toEqual([]);
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it('should handle numeric enum values', () => {
|
|
527
|
+
const schema = {
|
|
528
|
+
type: 'object',
|
|
529
|
+
properties: {
|
|
530
|
+
priority: { type: 'number', enum: [1, 2, 3] }
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
535
|
+
props: { schema, modelValue: {} }
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
const vm = wrapper.vm as any;
|
|
539
|
+
const options = vm.getSelectOptions('priority');
|
|
540
|
+
|
|
541
|
+
expect(options).toHaveLength(3);
|
|
542
|
+
expect(options[0]).toEqual({ id: '1', name: '1', label: '1' });
|
|
543
|
+
});
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
describe('getSelectedOption()', () => {
|
|
547
|
+
it('should return selected option from current value', () => {
|
|
548
|
+
const schema = {
|
|
549
|
+
type: 'object',
|
|
550
|
+
properties: {
|
|
551
|
+
status: { type: 'string', enum: ['active', 'inactive'] }
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
556
|
+
props: {
|
|
557
|
+
schema,
|
|
558
|
+
modelValue: { status: 'active' }
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
const vm = wrapper.vm as any;
|
|
563
|
+
const selected = vm.getSelectedOption('status');
|
|
564
|
+
|
|
565
|
+
expect(selected).toEqual({ id: 'active', name: 'active', label: 'active' });
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it('should return first option when value is not set', () => {
|
|
569
|
+
const schema = {
|
|
570
|
+
type: 'object',
|
|
571
|
+
properties: {
|
|
572
|
+
status: { type: 'string', enum: ['active', 'inactive'] }
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
577
|
+
props: { schema, modelValue: {} }
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
const vm = wrapper.vm as any;
|
|
581
|
+
const selected = vm.getSelectedOption('status');
|
|
582
|
+
|
|
583
|
+
expect(selected).toEqual({ id: 'active', name: 'active', label: 'active' });
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
describe('updateField()', () => {
|
|
588
|
+
it('should mark field as touched', () => {
|
|
589
|
+
const schema = {
|
|
590
|
+
type: 'object',
|
|
591
|
+
properties: {
|
|
592
|
+
name: { type: 'string' }
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
597
|
+
props: { schema, modelValue: {} }
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
const vm = wrapper.vm as any;
|
|
601
|
+
|
|
602
|
+
expect(vm.touched.name).toBeUndefined();
|
|
603
|
+
|
|
604
|
+
vm.updateField('name', 'John');
|
|
605
|
+
|
|
606
|
+
expect(vm.touched.name).toBe(true);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it('should emit update:modelValue', () => {
|
|
610
|
+
const schema = {
|
|
611
|
+
type: 'object',
|
|
612
|
+
properties: {
|
|
613
|
+
age: { type: 'number' }
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
618
|
+
props: { schema, modelValue: {} }
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
const vm = wrapper.vm as any;
|
|
622
|
+
|
|
623
|
+
vm.updateField('age', 25);
|
|
624
|
+
|
|
625
|
+
// Should emit update:modelValue
|
|
626
|
+
const updateEvents = wrapper.emitted('update:modelValue');
|
|
627
|
+
expect(updateEvents).toBeDefined();
|
|
628
|
+
const lastUpdate = updateEvents?.[updateEvents.length - 1]?.[0] as any;
|
|
629
|
+
expect(lastUpdate.age).toBe(25);
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it('should trigger field validation', () => {
|
|
633
|
+
const schema = {
|
|
634
|
+
type: 'object',
|
|
635
|
+
properties: {
|
|
636
|
+
email: { type: 'string', format: 'email', minLength: 1 }
|
|
637
|
+
},
|
|
638
|
+
required: ['email']
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
642
|
+
props: { schema, modelValue: {} }
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
const vm = wrapper.vm as any;
|
|
646
|
+
|
|
647
|
+
// Update with invalid email
|
|
648
|
+
vm.updateField('email', 'invalid');
|
|
649
|
+
|
|
650
|
+
// Should have validation error
|
|
651
|
+
expect(vm.errors.email).toBeDefined();
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
describe('validate()', () => {
|
|
656
|
+
it('should return empty array for valid data', () => {
|
|
657
|
+
const schema = {
|
|
658
|
+
type: 'object',
|
|
659
|
+
properties: {
|
|
660
|
+
name: { type: 'string', minLength: 1 }
|
|
661
|
+
},
|
|
662
|
+
required: ['name']
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
666
|
+
props: {
|
|
667
|
+
schema,
|
|
668
|
+
modelValue: { name: 'John' }
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
const vm = wrapper.vm as any;
|
|
673
|
+
const errors = vm.validate();
|
|
674
|
+
|
|
675
|
+
expect(errors).toEqual([]);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
it('should return errors for invalid data', () => {
|
|
679
|
+
const schema = {
|
|
680
|
+
type: 'object',
|
|
681
|
+
properties: {
|
|
682
|
+
email: { type: 'string', format: 'email', minLength: 1 }
|
|
683
|
+
},
|
|
684
|
+
required: ['email']
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
688
|
+
props: {
|
|
689
|
+
schema,
|
|
690
|
+
modelValue: { email: 'invalid' }
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
const vm = wrapper.vm as any;
|
|
695
|
+
const errors = vm.validate();
|
|
696
|
+
|
|
697
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
698
|
+
expect(errors[0].field).toBe('email');
|
|
699
|
+
expect(errors[0].message).toContain('email');
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
it('should populate errors object', () => {
|
|
703
|
+
const schema = {
|
|
704
|
+
type: 'object',
|
|
705
|
+
properties: {
|
|
706
|
+
name: { type: 'string', minLength: 1 }
|
|
707
|
+
},
|
|
708
|
+
required: ['name']
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
712
|
+
props: { schema, modelValue: {} }
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
const vm = wrapper.vm as any;
|
|
716
|
+
vm.validate();
|
|
717
|
+
|
|
718
|
+
expect(vm.errors.name).toBeDefined();
|
|
719
|
+
expect(vm.errors.name).toContain('required');
|
|
720
|
+
});
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
describe('reset()', () => {
|
|
724
|
+
it('should emit empty modelValue', () => {
|
|
725
|
+
const schema = {
|
|
726
|
+
type: 'object',
|
|
727
|
+
properties: {
|
|
728
|
+
name: { type: 'string' }
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
733
|
+
props: {
|
|
734
|
+
schema,
|
|
735
|
+
modelValue: { name: 'John' }
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
const vm = wrapper.vm as any;
|
|
740
|
+
|
|
741
|
+
expect(vm.formData.name).toBe('John');
|
|
742
|
+
|
|
743
|
+
vm.reset();
|
|
744
|
+
|
|
745
|
+
// Should emit empty object
|
|
746
|
+
const updateEvents = wrapper.emitted('update:modelValue');
|
|
747
|
+
expect(updateEvents).toBeDefined();
|
|
748
|
+
const lastUpdate = updateEvents?.[updateEvents.length - 1]?.[0] as any;
|
|
749
|
+
expect(Object.keys(lastUpdate).length).toBe(0);
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
it('should clear errors', () => {
|
|
753
|
+
const schema = {
|
|
754
|
+
type: 'object',
|
|
755
|
+
properties: {
|
|
756
|
+
email: { type: 'string', format: 'email', minLength: 1 }
|
|
757
|
+
},
|
|
758
|
+
required: ['email']
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
762
|
+
props: { schema, modelValue: {} }
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
const vm = wrapper.vm as any;
|
|
766
|
+
|
|
767
|
+
// Create error
|
|
768
|
+
vm.validate();
|
|
769
|
+
expect(Object.keys(vm.errors).length).toBeGreaterThan(0);
|
|
770
|
+
|
|
771
|
+
// Reset
|
|
772
|
+
vm.reset();
|
|
773
|
+
|
|
774
|
+
expect(Object.keys(vm.errors).length).toBe(0);
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
it('should clear touched state', () => {
|
|
778
|
+
const schema = {
|
|
779
|
+
type: 'object',
|
|
780
|
+
properties: {
|
|
781
|
+
field: { type: 'string' }
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
const wrapper = mount(JsonSchemaForm, {
|
|
786
|
+
props: { schema, modelValue: {} }
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
const vm = wrapper.vm as any;
|
|
790
|
+
|
|
791
|
+
// Mark as touched
|
|
792
|
+
vm.updateField('field', 'value');
|
|
793
|
+
expect(vm.touched.field).toBe(true);
|
|
794
|
+
|
|
795
|
+
// Reset
|
|
796
|
+
vm.reset();
|
|
797
|
+
|
|
798
|
+
expect(Object.keys(vm.touched).length).toBe(0);
|
|
799
|
+
});
|
|
800
|
+
});
|
|
801
|
+
});
|