Skip to content
This repository was archived by the owner on Jun 27, 2023. It is now read-only.

Commit 13cac5a

Browse files
committed
test(composable): test useDynamicForm and fixed local serve
1 parent 532fe10 commit 13cac5a

File tree

5 files changed

+264
-20
lines changed

5 files changed

+264
-20
lines changed

demos/vue-3/src/views/General.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ export default defineComponent({
232232
readonly: true,
233233
}),
234234
},
235+
options: {
236+
customClass: 'flex flex-wrap',
237+
},
235238
}));
236239
237240
function handleSubmit(values) {

demos/vue-3/src/views/Home.vue

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
:to="demo.route"
1515
class="p-4 block rounded-md bg-gray-200 text-gray-400 hover:bg-gray-300 hover:text-gray-500 transition-colors"
1616
><p>{{ demo.name }}</p>
17-
<ul class="flex mt-4" v-if="demo.tags?.length > 0">
18-
<li v-for="(tag, $index) in demo.tags" :key="tag">
19-
<span :class="['chip', `bg-${colors[$index]}-100`]">{{
20-
tag
21-
}}</span>
17+
<ul class="flex flex-wrap mt-4" v-if="demo.tags?.length > 0">
18+
<li v-for="tag in demo.tags" :key="tag">
19+
<span
20+
:class="['chip inline-block mb-2', `bg-${colorsMap[tag]}-100`]"
21+
>{{ tag }}</span
22+
>
2223
</li>
2324
</ul>
2425
</router-link>
@@ -36,7 +37,18 @@ export default defineComponent({
3637
name: 'Home',
3738
setup() {
3839
const demos = ref([]);
39-
const colors = ref(['yellow', 'red', 'blue', 'green', 'yellow', 'gray']);
40+
const colorsMap = ref({
41+
'text-field': 'yellow',
42+
'email-field': 'blue',
43+
all: 'yellow',
44+
'password-field': 'green',
45+
'select-field': 'red',
46+
'number-field': 'pink',
47+
'textarea-field': 'purple',
48+
'custom-field': 'indigo',
49+
'radio-field': 'yellow',
50+
'checkbox-field': 'blue',
51+
});
4052
4153
setTimeout(() => {
4254
demos.value = [
@@ -45,11 +57,12 @@ export default defineComponent({
4557
{
4658
name: 'General',
4759
route: '/general',
60+
tags: ['all'],
4861
},
4962
{
5063
name: 'Text Fields',
5164
route: '/text-fields',
52-
tags: ['text-field', 'email-input', 'password-input'],
65+
tags: ['text-field', 'email-field', 'password-field'],
5366
},
5467
{
5568
name: 'Number Fields',
@@ -79,12 +92,12 @@ export default defineComponent({
7992
{
8093
name: 'Login',
8194
route: '/login',
82-
tags: ['email-input', 'password-input'],
95+
tags: ['email-field', 'password-field'],
8396
},
8497
{
8598
name: 'Custom Fields',
8699
route: '/custom-fields',
87-
tags: ['custom-input'],
100+
tags: ['custom-field'],
88101
},
89102
],
90103
];
@@ -113,7 +126,7 @@ export default defineComponent({
113126
beforeEnter,
114127
enter,
115128
leave,
116-
colors,
129+
colorsMap,
117130
};
118131
},
119132
});

src/components/dynamic-form/DynamicForm.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@
3636
</template>
3737

3838
<script lang="ts">
39-
import { defineComponent, PropType } from 'vue';
39+
import { defineComponent, inject, PropType } from 'vue';
4040
4141
import DynamicInput from '../dynamic-input/DynamicInput.vue';
4242
4343
import { DynamicForm } from '@/core/models';
4444
import { useDynamicForm } from '@/composables/useDynamicForm';
45+
import { dynamicFormsSymbol } from '@/useApi';
4546
4647
const props = {
4748
form: {
@@ -61,6 +62,7 @@ export default defineComponent({
6162
props,
6263
components,
6364
setup(props, ctx) {
65+
const { options } = inject(dynamicFormsSymbol);
6466
const {
6567
controls,
6668
valueChange,
@@ -74,7 +76,7 @@ export default defineComponent({
7476
onBlur,
7577
onValidate,
7678
forceValidation,
77-
} = useDynamicForm(props.form as DynamicForm, ctx);
79+
} = useDynamicForm(props.form as DynamicForm, ctx, options);
7880
7981
return {
8082
controls,
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import {
2+
CheckboxField,
3+
EmailField,
4+
FieldControl,
5+
PasswordField,
6+
TextField,
7+
} from '@/core/factories';
8+
import { dynamicFormsSymbol } from '@/useApi';
9+
import { provide, ref } from 'vue';
10+
import { useDynamicForm } from './useDynamicForm';
11+
12+
describe('UseDynamicForm', () => {
13+
let form;
14+
let ctx;
15+
beforeEach(() => {
16+
form = {
17+
id: 'login-demo',
18+
fields: {
19+
name: TextField({
20+
label: 'Text',
21+
value: 'Awiwi',
22+
}),
23+
email: EmailField({
24+
label: 'Email',
25+
}),
26+
password: PasswordField({
27+
label: 'Password',
28+
autocomplete: 'current-password',
29+
}),
30+
rememberMe: CheckboxField({
31+
label: 'Remember Me',
32+
value: true,
33+
}),
34+
},
35+
};
36+
ctx = {
37+
emit: (path, obj) => ({
38+
path,
39+
...obj,
40+
}),
41+
};
42+
});
43+
44+
it('should map controls based on form fields', () => {
45+
const { controls, mapControls } = useDynamicForm(form, ctx);
46+
47+
mapControls();
48+
expect(controls.value.length).toBe(4);
49+
expect(controls.value.map(control => control.name)).toStrictEqual([
50+
'name',
51+
'email',
52+
'password',
53+
'rememberMe',
54+
]);
55+
});
56+
57+
it('should be valid if all controls are valid', () => {
58+
const { mapControls, isValid } = useDynamicForm(form, ctx);
59+
mapControls();
60+
expect(isValid.value).toBe(true);
61+
});
62+
63+
it('should be invalid if any of the controls is invalid', () => {
64+
const { mapControls, isValid, findControlByName } = useDynamicForm(
65+
form,
66+
ctx,
67+
);
68+
mapControls();
69+
findControlByName('email').valid = false;
70+
71+
expect(isValid.value).toBeFalsy();
72+
});
73+
74+
it('should format the options', () => {
75+
form.options = {
76+
customClass: 'flex flex-wrap',
77+
customStyles: { position: 'absolute' },
78+
netlify: true,
79+
autocomplete: true,
80+
};
81+
const { formattedOptions } = useDynamicForm(form, ctx);
82+
83+
expect(formattedOptions.value.class).toBe('flex flex-wrap');
84+
expect(formattedOptions.value.style).toStrictEqual({
85+
position: 'absolute',
86+
});
87+
expect(formattedOptions.value['data-netlify']).toBe(true);
88+
expect(formattedOptions.value.autocomplete).toBe('on');
89+
});
90+
91+
it('should map controls in order depending on fieldOrder prop', () => {
92+
form.fieldOrder = ['email', 'password', 'name', 'rememberMe'];
93+
const { controls, mapControls } = useDynamicForm(form, ctx);
94+
95+
mapControls();
96+
expect(controls.value.map(control => control.name)).toStrictEqual([
97+
'email',
98+
'password',
99+
'name',
100+
'rememberMe',
101+
]);
102+
});
103+
104+
it('should map controls with value if empty = false', () => {
105+
const { controls, mapControls } = useDynamicForm(form, ctx);
106+
107+
mapControls();
108+
expect(controls.value.map(control => control.value)).toStrictEqual([
109+
'Awiwi',
110+
undefined,
111+
undefined,
112+
true,
113+
]);
114+
});
115+
116+
it('should map controls with not value if empty = true', () => {
117+
const { controls, mapControls } = useDynamicForm(form, ctx);
118+
119+
mapControls(true);
120+
expect(controls.value.map(control => control.value)).toStrictEqual([
121+
null,
122+
null,
123+
null,
124+
false,
125+
]);
126+
});
127+
128+
it('should return form values', () => {
129+
form.fields.email.value = '[email protected]';
130+
131+
const { mapControls, formValues } = useDynamicForm(form, ctx);
132+
mapControls();
133+
134+
expect(formValues.value).toEqual({
135+
name: 'Awiwi',
136+
137+
rememberMe: true,
138+
});
139+
});
140+
141+
it('should update the control to dirty if value change', () => {
142+
const mockInputEvent = { name: 'email', value: '[email protected]' };
143+
const { mapControls, valueChange, findControlByName } = useDynamicForm(
144+
form,
145+
ctx,
146+
);
147+
mapControls();
148+
valueChange(mockInputEvent);
149+
150+
expect(findControlByName('email').value).toBe('[email protected]');
151+
});
152+
153+
it('validateAll should set forceValidation to true', () => {
154+
const { mapControls, validateAll, forceValidation } = useDynamicForm(
155+
form,
156+
ctx,
157+
);
158+
mapControls();
159+
validateAll();
160+
expect(forceValidation.value).toBe(true);
161+
});
162+
163+
it('resetForm should set control values to null and set forceValidation to false', () => {
164+
const {
165+
mapControls,
166+
controls,
167+
forceValidation,
168+
resetForm,
169+
} = useDynamicForm(form, ctx);
170+
mapControls();
171+
resetForm();
172+
expect(controls.value.map(control => control.value)).toStrictEqual([
173+
null,
174+
null,
175+
null,
176+
false,
177+
]);
178+
expect(forceValidation.value).toBeFalsy();
179+
});
180+
181+
it('should emit submitted with formValues if hadleSubmit is called and is valid ', async () => {
182+
const spy = jest.spyOn(ctx, 'emit');
183+
const { mapControls, handleSubmit } = useDynamicForm(form, ctx);
184+
mapControls();
185+
186+
await handleSubmit();
187+
188+
expect(spy).toHaveBeenCalledWith('submitted', {
189+
name: 'Awiwi',
190+
rememberMe: true,
191+
});
192+
});
193+
194+
it('should emit error event with errors if hadleSubmit is called and is form is invalid ', async () => {
195+
const spy = jest.spyOn(ctx, 'emit');
196+
const { mapControls, handleSubmit, findControlByName } = useDynamicForm(
197+
form,
198+
ctx,
199+
);
200+
mapControls();
201+
const control = findControlByName('email');
202+
control.valid = false;
203+
control.errors = {
204+
required: { value: true, text: 'This field is required' },
205+
};
206+
207+
await handleSubmit();
208+
209+
expect(spy).toHaveBeenCalledWith('error', {
210+
email: {
211+
required: {
212+
text: 'This field is required',
213+
value: true,
214+
},
215+
},
216+
});
217+
});
218+
});

0 commit comments

Comments
 (0)