@akinon/akiform-builder 0.8.0 → 1.0.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/dist/cjs/__tests__/akiform-builder.test.d.ts +2 -0
- package/dist/cjs/__tests__/akiform-builder.test.d.ts.map +1 -0
- package/dist/cjs/__tests__/akiform-builder.test.js +1258 -0
- package/dist/cjs/__tests__/field-builder.test.d.ts +2 -0
- package/dist/cjs/__tests__/field-builder.test.d.ts.map +1 -0
- package/dist/cjs/__tests__/field-builder.test.js +251 -0
- package/dist/cjs/index.css +7 -0
- package/dist/cjs/src/akiform-builder.d.ts +7 -0
- package/dist/cjs/src/akiform-builder.d.ts.map +1 -0
- package/dist/cjs/src/akiform-builder.js +291 -0
- package/dist/cjs/src/field-builder.d.ts +37 -0
- package/dist/cjs/src/field-builder.d.ts.map +1 -0
- package/dist/cjs/src/field-builder.js +94 -0
- package/dist/cjs/src/i18n/index.d.ts +5 -0
- package/dist/cjs/src/i18n/index.d.ts.map +1 -0
- package/dist/cjs/src/i18n/index.js +14 -0
- package/dist/cjs/src/i18n/translations/en.d.ts +7 -0
- package/dist/cjs/src/i18n/translations/en.d.ts.map +1 -0
- package/dist/cjs/src/i18n/translations/en.js +8 -0
- package/dist/cjs/src/i18n/translations/tr.d.ts +7 -0
- package/dist/cjs/src/i18n/translations/tr.d.ts.map +1 -0
- package/dist/cjs/src/i18n/translations/tr.js +8 -0
- package/dist/cjs/src/index.d.ts +4 -0
- package/dist/cjs/src/index.d.ts.map +1 -0
- package/dist/cjs/src/index.js +21 -0
- package/dist/cjs/src/types.d.ts +84 -0
- package/dist/cjs/src/types.d.ts.map +1 -0
- package/dist/cjs/src/types.js +2 -0
- package/dist/esm/__tests__/akiform-builder.test.d.ts +2 -0
- package/dist/esm/__tests__/akiform-builder.test.d.ts.map +1 -0
- package/dist/esm/__tests__/akiform-builder.test.js +1256 -0
- package/dist/esm/__tests__/field-builder.test.d.ts +2 -0
- package/dist/esm/__tests__/field-builder.test.d.ts.map +1 -0
- package/dist/esm/__tests__/field-builder.test.js +249 -0
- package/dist/esm/index.css +7 -0
- package/dist/esm/src/akiform-builder.d.ts +7 -0
- package/dist/esm/src/akiform-builder.d.ts.map +1 -0
- package/dist/esm/src/akiform-builder.js +288 -0
- package/dist/esm/src/field-builder.d.ts +37 -0
- package/dist/esm/src/field-builder.d.ts.map +1 -0
- package/dist/esm/src/field-builder.js +91 -0
- package/dist/esm/src/i18n/index.d.ts +5 -0
- package/dist/esm/src/i18n/index.d.ts.map +1 -0
- package/dist/esm/src/i18n/index.js +11 -0
- package/dist/esm/src/i18n/translations/en.d.ts +7 -0
- package/dist/esm/src/i18n/translations/en.d.ts.map +1 -0
- package/dist/esm/src/i18n/translations/en.js +6 -0
- package/dist/esm/src/i18n/translations/tr.d.ts +7 -0
- package/dist/esm/src/i18n/translations/tr.d.ts.map +1 -0
- package/dist/esm/src/i18n/translations/tr.js +6 -0
- package/dist/esm/src/index.d.ts +4 -0
- package/dist/esm/src/index.d.ts.map +1 -0
- package/dist/esm/src/index.js +3 -0
- package/dist/esm/src/types.d.ts +84 -0
- package/dist/esm/src/types.d.ts.map +1 -0
- package/dist/esm/src/types.js +1 -0
- package/package.json +17 -15
|
@@ -0,0 +1,1256 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
11
|
+
import { useController } from '@akinon/akiform';
|
|
12
|
+
import { akival } from '@akinon/akival';
|
|
13
|
+
import { fireEvent, render, screen, userEvent, waitFor } from '@akinon/utils';
|
|
14
|
+
import * as React from 'react';
|
|
15
|
+
import { act } from 'react';
|
|
16
|
+
import { AkiformBuilder, THROTTLE_DELAY } from '../src/akiform-builder';
|
|
17
|
+
import { field } from '../src/field-builder';
|
|
18
|
+
describe('AkiformBuilder', () => {
|
|
19
|
+
const mockOnSubmit = vi.fn();
|
|
20
|
+
const mockOnReset = vi.fn();
|
|
21
|
+
const mockOnValueChange = vi.fn();
|
|
22
|
+
const defaultFields = [
|
|
23
|
+
{
|
|
24
|
+
key: 'name',
|
|
25
|
+
label: 'Name',
|
|
26
|
+
type: 'text',
|
|
27
|
+
placeholder: 'Enter your name'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
key: 'age',
|
|
31
|
+
label: 'Age',
|
|
32
|
+
type: 'number',
|
|
33
|
+
placeholder: 'Enter your age'
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
vi.clearAllMocks();
|
|
38
|
+
});
|
|
39
|
+
describe('AkiformBuilder in uncontrolled mode', () => {
|
|
40
|
+
it('renders form fields correctly', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
41
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
|
+
render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: mockOnSubmit }));
|
|
43
|
+
}));
|
|
44
|
+
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
45
|
+
expect(screen.getByLabelText('Age')).toBeInTheDocument();
|
|
46
|
+
expect(screen.getByPlaceholderText('Enter your name')).toBeInTheDocument();
|
|
47
|
+
expect(screen.getByPlaceholderText('Enter your age')).toBeInTheDocument();
|
|
48
|
+
}));
|
|
49
|
+
it('handles form with custom field and onChange', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
50
|
+
const CustomComponent = ({ field, control }) => {
|
|
51
|
+
const { field: controllerField } = useController({
|
|
52
|
+
name: field.key,
|
|
53
|
+
control
|
|
54
|
+
});
|
|
55
|
+
return React.createElement("input", Object.assign({ "data-testid": "custom-input" }, controllerField));
|
|
56
|
+
};
|
|
57
|
+
const fields = [
|
|
58
|
+
{
|
|
59
|
+
key: 'customField',
|
|
60
|
+
label: 'Custom Field',
|
|
61
|
+
type: 'custom',
|
|
62
|
+
render: ({ field, formValues, control }) => (React.createElement(CustomComponent, { field: field, formValues: formValues, control: control }))
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
const onValueChangeMock = vi.fn();
|
|
66
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
67
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: mockOnSubmit, onValueChange: onValueChangeMock }));
|
|
68
|
+
}));
|
|
69
|
+
const customInput = screen.getByTestId('custom-input');
|
|
70
|
+
expect(customInput).toBeInTheDocument();
|
|
71
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
72
|
+
yield userEvent.type(customInput, 'test value');
|
|
73
|
+
}));
|
|
74
|
+
yield waitFor(() => {
|
|
75
|
+
expect(onValueChangeMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
76
|
+
customField: 'test value'
|
|
77
|
+
}));
|
|
78
|
+
});
|
|
79
|
+
}));
|
|
80
|
+
it('resets form when reset button is clicked', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
81
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
82
|
+
render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: mockOnSubmit, onReset: mockOnReset, showResetButton: true }));
|
|
83
|
+
}));
|
|
84
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
85
|
+
fireEvent.change(screen.getByLabelText('Name'), {
|
|
86
|
+
target: { value: 'John Doe' }
|
|
87
|
+
});
|
|
88
|
+
fireEvent.change(screen.getByLabelText('Age'), {
|
|
89
|
+
target: { value: '30' }
|
|
90
|
+
});
|
|
91
|
+
fireEvent.click(screen.getByText('RESET'));
|
|
92
|
+
}));
|
|
93
|
+
yield waitFor(() => {
|
|
94
|
+
expect(mockOnReset).toHaveBeenCalled();
|
|
95
|
+
expect(screen.getByLabelText('Name')).toHaveValue('');
|
|
96
|
+
expect(screen.getByLabelText('Age')).toHaveValue('');
|
|
97
|
+
});
|
|
98
|
+
}));
|
|
99
|
+
it('exposes reset method through ref', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
100
|
+
const ref = React.createRef();
|
|
101
|
+
const onResetMock = vi.fn();
|
|
102
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
103
|
+
render(React.createElement(AkiformBuilder, { ref: ref, fields: defaultFields, onSubmit: mockOnSubmit, onReset: onResetMock }));
|
|
104
|
+
}));
|
|
105
|
+
// Fill in some values
|
|
106
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
107
|
+
fireEvent.change(screen.getByLabelText('Name'), {
|
|
108
|
+
target: { value: 'John Doe' }
|
|
109
|
+
});
|
|
110
|
+
}));
|
|
111
|
+
// Call reset through ref
|
|
112
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
113
|
+
var _a;
|
|
114
|
+
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.reset();
|
|
115
|
+
}));
|
|
116
|
+
// Check if form is reset and onReset is called
|
|
117
|
+
expect(screen.getByLabelText('Name')).toHaveValue('');
|
|
118
|
+
expect(onResetMock).toHaveBeenCalled();
|
|
119
|
+
// Test partial reset
|
|
120
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
121
|
+
fireEvent.change(screen.getByLabelText('Name'), {
|
|
122
|
+
target: { value: 'John Doe' }
|
|
123
|
+
});
|
|
124
|
+
fireEvent.change(screen.getByLabelText('Age'), {
|
|
125
|
+
target: { value: '30' }
|
|
126
|
+
});
|
|
127
|
+
}));
|
|
128
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
129
|
+
var _a;
|
|
130
|
+
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.reset({ name: 'Jane Doe' });
|
|
131
|
+
}));
|
|
132
|
+
expect(screen.getByLabelText('Name')).toHaveValue('Jane Doe');
|
|
133
|
+
expect(screen.getByLabelText('Age')).toHaveValue('30');
|
|
134
|
+
}));
|
|
135
|
+
it('calls onValueChange when form values change in uncontrolled mode', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
136
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
137
|
+
render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: mockOnSubmit, onValueChange: mockOnValueChange }));
|
|
138
|
+
}));
|
|
139
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
140
|
+
fireEvent.change(screen.getByLabelText('Name'), {
|
|
141
|
+
target: { value: 'John Doe' }
|
|
142
|
+
});
|
|
143
|
+
}));
|
|
144
|
+
yield waitFor(() => {
|
|
145
|
+
expect(mockOnValueChange).toHaveBeenCalledWith(expect.objectContaining({
|
|
146
|
+
name: 'John Doe'
|
|
147
|
+
}));
|
|
148
|
+
});
|
|
149
|
+
}));
|
|
150
|
+
it('applies throttling in uncontrolled mode', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
151
|
+
vi.useFakeTimers();
|
|
152
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
153
|
+
render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: mockOnSubmit, onValueChange: mockOnValueChange }));
|
|
154
|
+
}));
|
|
155
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
156
|
+
fireEvent.change(screen.getByLabelText('Name'), {
|
|
157
|
+
target: { value: 'John Doe' }
|
|
158
|
+
});
|
|
159
|
+
}));
|
|
160
|
+
expect(mockOnValueChange).not.toHaveBeenCalled();
|
|
161
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
162
|
+
vi.advanceTimersByTime(THROTTLE_DELAY);
|
|
163
|
+
}));
|
|
164
|
+
expect(mockOnValueChange).toHaveBeenCalledWith(expect.objectContaining({ name: 'John Doe' }));
|
|
165
|
+
vi.useRealTimers();
|
|
166
|
+
}));
|
|
167
|
+
it('renders different field types correctly', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
168
|
+
const fields = [
|
|
169
|
+
{ key: 'name', label: 'Name', type: 'text' },
|
|
170
|
+
{ key: 'age', label: 'Age', type: 'number' },
|
|
171
|
+
{
|
|
172
|
+
key: 'gender',
|
|
173
|
+
label: 'Gender',
|
|
174
|
+
type: 'select',
|
|
175
|
+
options: [
|
|
176
|
+
{ value: 'male', label: 'Male' },
|
|
177
|
+
{ value: 'female', label: 'Female' }
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
{ key: 'subscribe', label: 'Subscribe', type: 'checkbox' },
|
|
181
|
+
{ key: 'birthdate', label: 'Birth Date', type: 'date' },
|
|
182
|
+
{ key: 'bio', label: 'Biography', type: 'textarea' }
|
|
183
|
+
];
|
|
184
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
185
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: mockOnSubmit }));
|
|
186
|
+
}));
|
|
187
|
+
expect(screen.getByLabelText('Name')).toHaveAttribute('type', 'text');
|
|
188
|
+
expect(screen.getByLabelText('Age')).toHaveClass('akinon-input-number-input');
|
|
189
|
+
expect(screen.getByLabelText('Gender')).toBeInTheDocument();
|
|
190
|
+
expect(screen.getByLabelText('Subscribe')).toHaveAttribute('type', 'checkbox');
|
|
191
|
+
expect(screen.getByLabelText('Birth Date')).toBeInTheDocument();
|
|
192
|
+
expect(screen.getByLabelText('Biography')).toBeInTheDocument();
|
|
193
|
+
}));
|
|
194
|
+
it('renders field array correctly', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
195
|
+
const fields = [
|
|
196
|
+
{
|
|
197
|
+
key: 'addresses',
|
|
198
|
+
label: 'Addresses',
|
|
199
|
+
type: 'fieldArray',
|
|
200
|
+
fields: [
|
|
201
|
+
{ key: 'street', label: 'Street', type: 'text' },
|
|
202
|
+
{ key: 'city', label: 'City', type: 'text' }
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
];
|
|
206
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
207
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: mockOnSubmit }));
|
|
208
|
+
}));
|
|
209
|
+
expect(screen.getByText('Add')).toBeInTheDocument();
|
|
210
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
211
|
+
fireEvent.click(screen.getByText('Add'));
|
|
212
|
+
}));
|
|
213
|
+
expect(screen.getByLabelText('Street')).toBeInTheDocument();
|
|
214
|
+
expect(screen.getByLabelText('City')).toBeInTheDocument();
|
|
215
|
+
expect(screen.getByText('Remove')).toBeInTheDocument();
|
|
216
|
+
}));
|
|
217
|
+
it('handles form with field array and nested validation', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
218
|
+
const fields = [
|
|
219
|
+
{
|
|
220
|
+
key: 'items',
|
|
221
|
+
label: 'Items',
|
|
222
|
+
type: 'fieldArray',
|
|
223
|
+
fields: [
|
|
224
|
+
{
|
|
225
|
+
key: 'name',
|
|
226
|
+
label: 'Item Name',
|
|
227
|
+
type: 'text',
|
|
228
|
+
validation: akival.string().required('Item name is required')
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
key: 'quantity',
|
|
232
|
+
label: 'Quantity',
|
|
233
|
+
type: 'number',
|
|
234
|
+
validation: akival.number().min(1, 'Quantity must be at least 1')
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
];
|
|
239
|
+
const onSubmitMock = vi.fn();
|
|
240
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
241
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: onSubmitMock }));
|
|
242
|
+
}));
|
|
243
|
+
// Add an item
|
|
244
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
245
|
+
fireEvent.click(screen.getByText('Add'));
|
|
246
|
+
}));
|
|
247
|
+
// Try to submit without filling required fields
|
|
248
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
249
|
+
fireEvent.submit(screen.getByTestId('akiform-builder'));
|
|
250
|
+
}));
|
|
251
|
+
yield waitFor(() => {
|
|
252
|
+
expect(screen.getByText('Item name is required')).toBeInTheDocument();
|
|
253
|
+
expect(onSubmitMock).not.toHaveBeenCalled();
|
|
254
|
+
});
|
|
255
|
+
// Fill in valid data
|
|
256
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
257
|
+
fireEvent.change(screen.getByLabelText('Item Name'), {
|
|
258
|
+
target: { value: 'Test Item' }
|
|
259
|
+
});
|
|
260
|
+
fireEvent.change(screen.getByLabelText('Quantity'), {
|
|
261
|
+
target: { value: '2' }
|
|
262
|
+
});
|
|
263
|
+
}));
|
|
264
|
+
// Submit the form
|
|
265
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
266
|
+
fireEvent.submit(screen.getByTestId('akiform-builder'));
|
|
267
|
+
}));
|
|
268
|
+
yield waitFor(() => {
|
|
269
|
+
expect(onSubmitMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
270
|
+
items: [{ name: 'Test Item', quantity: 2 }]
|
|
271
|
+
}), expect.anything());
|
|
272
|
+
});
|
|
273
|
+
}));
|
|
274
|
+
it('handles field array operations correctly', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
275
|
+
const fields = [
|
|
276
|
+
{
|
|
277
|
+
key: 'items',
|
|
278
|
+
label: 'Items',
|
|
279
|
+
type: 'fieldArray',
|
|
280
|
+
fields: [
|
|
281
|
+
{ key: 'name', label: 'Item Name', type: 'text' },
|
|
282
|
+
{ key: 'quantity', label: 'Quantity', type: 'number' }
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
];
|
|
286
|
+
const TestComponent = () => {
|
|
287
|
+
const [formValues, setFormValues] = React.useState({});
|
|
288
|
+
return (React.createElement(React.Fragment, null,
|
|
289
|
+
React.createElement(AkiformBuilder, { fields: fields, onSubmit: mockOnSubmit, onValueChange: values => {
|
|
290
|
+
setFormValues(values);
|
|
291
|
+
} }),
|
|
292
|
+
React.createElement("div", { "data-testid": "form-values" }, JSON.stringify(formValues))));
|
|
293
|
+
};
|
|
294
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
295
|
+
render(React.createElement(TestComponent, null));
|
|
296
|
+
}));
|
|
297
|
+
// Add an item
|
|
298
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
299
|
+
fireEvent.click(screen.getByText('Add'));
|
|
300
|
+
}));
|
|
301
|
+
// Fill in the fields
|
|
302
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
303
|
+
fireEvent.change(screen.getByLabelText('Item Name'), {
|
|
304
|
+
target: { value: 'Test Item' }
|
|
305
|
+
});
|
|
306
|
+
}));
|
|
307
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
308
|
+
fireEvent.change(screen.getByLabelText('Quantity'), {
|
|
309
|
+
target: { value: '5' }
|
|
310
|
+
});
|
|
311
|
+
}));
|
|
312
|
+
// Wait for the form values to update
|
|
313
|
+
yield waitFor(() => {
|
|
314
|
+
const formValuesElement = screen.getByTestId('form-values');
|
|
315
|
+
const formValues = JSON.parse(formValuesElement.textContent || '{}');
|
|
316
|
+
expect(formValues).toEqual(expect.objectContaining({
|
|
317
|
+
items: [{ name: 'Test Item', quantity: 5 }]
|
|
318
|
+
}));
|
|
319
|
+
});
|
|
320
|
+
// Submit the form
|
|
321
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
322
|
+
fireEvent.submit(screen.getByTestId('akiform-builder'));
|
|
323
|
+
}));
|
|
324
|
+
// Check the submitted values
|
|
325
|
+
yield waitFor(() => {
|
|
326
|
+
expect(mockOnSubmit).toHaveBeenCalledWith(expect.objectContaining({
|
|
327
|
+
items: [{ name: 'Test Item', quantity: 5 }]
|
|
328
|
+
}), expect.anything());
|
|
329
|
+
});
|
|
330
|
+
}));
|
|
331
|
+
it('applies layout options correctly', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
332
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
333
|
+
render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: mockOnSubmit, layout: "horizontal", layoutOptions: { labelCol: { span: 8 }, wrapperCol: { span: 16 } } }));
|
|
334
|
+
}));
|
|
335
|
+
const form = screen.getByTestId('akiform-builder');
|
|
336
|
+
expect(form).toHaveClass('akinon-form-horizontal');
|
|
337
|
+
}));
|
|
338
|
+
it('conditionally renders and disables fields', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
339
|
+
const fields = [
|
|
340
|
+
{ key: 'showField', label: 'Show Field', type: 'checkbox' },
|
|
341
|
+
{
|
|
342
|
+
key: 'conditionalField',
|
|
343
|
+
label: 'Conditional Field',
|
|
344
|
+
type: 'text',
|
|
345
|
+
config: { visible: values => values.showField }
|
|
346
|
+
},
|
|
347
|
+
{ key: 'enableField', label: 'Enable Field', type: 'checkbox' },
|
|
348
|
+
{
|
|
349
|
+
key: 'disableableField',
|
|
350
|
+
label: 'Disableable Field',
|
|
351
|
+
type: 'text',
|
|
352
|
+
config: { disabled: values => !values.enableField }
|
|
353
|
+
}
|
|
354
|
+
];
|
|
355
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
356
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: mockOnSubmit }));
|
|
357
|
+
}));
|
|
358
|
+
expect(screen.queryByLabelText('Conditional Field')).not.toBeInTheDocument();
|
|
359
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
360
|
+
fireEvent.click(screen.getByLabelText('Show Field'));
|
|
361
|
+
}));
|
|
362
|
+
expect(screen.getByLabelText('Conditional Field')).toBeInTheDocument();
|
|
363
|
+
expect(screen.getByLabelText('Disableable Field')).toBeDisabled();
|
|
364
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
365
|
+
fireEvent.click(screen.getByLabelText('Enable Field'));
|
|
366
|
+
}));
|
|
367
|
+
expect(screen.getByLabelText('Disableable Field')).not.toBeDisabled();
|
|
368
|
+
}));
|
|
369
|
+
it('applies custom validation', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
370
|
+
const fields = [
|
|
371
|
+
{
|
|
372
|
+
key: 'email',
|
|
373
|
+
label: 'Email',
|
|
374
|
+
type: 'text',
|
|
375
|
+
validation: akival.string().email('Invalid email format')
|
|
376
|
+
}
|
|
377
|
+
];
|
|
378
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
379
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: mockOnSubmit }));
|
|
380
|
+
}));
|
|
381
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
382
|
+
fireEvent.change(screen.getByLabelText('Email'), {
|
|
383
|
+
target: { value: 'invalid-email' }
|
|
384
|
+
});
|
|
385
|
+
fireEvent.submit(screen.getByTestId('akiform-builder'));
|
|
386
|
+
}));
|
|
387
|
+
yield waitFor(() => {
|
|
388
|
+
expect(screen.getByText('Invalid email format')).toBeInTheDocument();
|
|
389
|
+
});
|
|
390
|
+
}));
|
|
391
|
+
it('resets form using ref', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
392
|
+
const ref = React.createRef();
|
|
393
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
394
|
+
render(React.createElement(AkiformBuilder, { ref: ref, fields: defaultFields, onSubmit: mockOnSubmit }));
|
|
395
|
+
}));
|
|
396
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
397
|
+
fireEvent.change(screen.getByLabelText('Name'), {
|
|
398
|
+
target: { value: 'John Doe' }
|
|
399
|
+
});
|
|
400
|
+
fireEvent.change(screen.getByLabelText('Age'), {
|
|
401
|
+
target: { value: '30' }
|
|
402
|
+
});
|
|
403
|
+
}));
|
|
404
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
405
|
+
var _a;
|
|
406
|
+
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.reset();
|
|
407
|
+
}));
|
|
408
|
+
expect(screen.getByLabelText('Name')).toHaveValue('');
|
|
409
|
+
expect(screen.getByLabelText('Age')).toHaveValue('');
|
|
410
|
+
}));
|
|
411
|
+
it('displays error states', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
412
|
+
const fields = [
|
|
413
|
+
{
|
|
414
|
+
key: 'name',
|
|
415
|
+
label: 'Name',
|
|
416
|
+
type: 'text',
|
|
417
|
+
validation: akival.string().required('Name is required')
|
|
418
|
+
}
|
|
419
|
+
];
|
|
420
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
421
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: mockOnSubmit }));
|
|
422
|
+
}));
|
|
423
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
424
|
+
fireEvent.submit(screen.getByTestId('akiform-builder'));
|
|
425
|
+
}));
|
|
426
|
+
yield waitFor(() => {
|
|
427
|
+
expect(screen.getByText('Name is required')).toBeInTheDocument();
|
|
428
|
+
});
|
|
429
|
+
}));
|
|
430
|
+
it('handles form submission', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
431
|
+
const onSubmit = vi.fn();
|
|
432
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
433
|
+
render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: onSubmit }));
|
|
434
|
+
}));
|
|
435
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
436
|
+
fireEvent.change(screen.getByLabelText('Name'), {
|
|
437
|
+
target: { value: 'John Doe' }
|
|
438
|
+
});
|
|
439
|
+
fireEvent.change(screen.getByLabelText('Age'), {
|
|
440
|
+
target: { value: '30' }
|
|
441
|
+
});
|
|
442
|
+
fireEvent.submit(screen.getByTestId('akiform-builder'));
|
|
443
|
+
}));
|
|
444
|
+
yield waitFor(() => {
|
|
445
|
+
expect(onSubmit).toHaveBeenCalledWith(expect.objectContaining({
|
|
446
|
+
name: 'John Doe',
|
|
447
|
+
age: 30
|
|
448
|
+
}), expect.anything());
|
|
449
|
+
});
|
|
450
|
+
}));
|
|
451
|
+
it('handles form submission with errors', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
452
|
+
const fields = [
|
|
453
|
+
{
|
|
454
|
+
key: 'name',
|
|
455
|
+
label: 'Name',
|
|
456
|
+
type: 'text',
|
|
457
|
+
validation: akival.string().required('Name is required')
|
|
458
|
+
}
|
|
459
|
+
];
|
|
460
|
+
const onSubmit = vi.fn();
|
|
461
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
462
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: onSubmit }));
|
|
463
|
+
}));
|
|
464
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
465
|
+
fireEvent.submit(screen.getByTestId('akiform-builder'));
|
|
466
|
+
}));
|
|
467
|
+
yield waitFor(() => {
|
|
468
|
+
expect(screen.getByText('Name is required')).toBeInTheDocument();
|
|
469
|
+
expect(onSubmit).not.toHaveBeenCalled();
|
|
470
|
+
});
|
|
471
|
+
}));
|
|
472
|
+
it('handles form with initial values', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
473
|
+
const initialValues = { name: 'John Doe', age: 30 };
|
|
474
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
475
|
+
render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: mockOnSubmit, initialValues: initialValues }));
|
|
476
|
+
}));
|
|
477
|
+
yield waitFor(() => {
|
|
478
|
+
expect(screen.getByLabelText('Name')).toHaveValue('John Doe');
|
|
479
|
+
expect(screen.getByLabelText('Age')).toHaveValue('30');
|
|
480
|
+
});
|
|
481
|
+
}));
|
|
482
|
+
it('handles form with custom layout', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
483
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
484
|
+
render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: mockOnSubmit, layout: "vertical" }));
|
|
485
|
+
}));
|
|
486
|
+
const form = screen.getByTestId('akiform-builder');
|
|
487
|
+
expect(form).toHaveClass('akinon-form-vertical');
|
|
488
|
+
}));
|
|
489
|
+
it('renders custom field correctly', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
490
|
+
const CustomComponent = ({ field }) => (React.createElement("div", { "data-testid": "custom-field" }, field.label));
|
|
491
|
+
const fields = [
|
|
492
|
+
{
|
|
493
|
+
key: 'customField',
|
|
494
|
+
label: 'Custom Field',
|
|
495
|
+
type: 'custom',
|
|
496
|
+
render: ({ field }) => (React.createElement(CustomComponent, { field: field }))
|
|
497
|
+
}
|
|
498
|
+
];
|
|
499
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
500
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
501
|
+
}));
|
|
502
|
+
expect(screen.getByTestId('custom-field')).toHaveTextContent('Custom Field');
|
|
503
|
+
}));
|
|
504
|
+
it('cleans up throttle timeout on unmount', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
505
|
+
vi.useFakeTimers();
|
|
506
|
+
const { unmount } = render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: mockOnSubmit, onValueChange: mockOnValueChange }));
|
|
507
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
508
|
+
fireEvent.change(screen.getByLabelText('Name'), {
|
|
509
|
+
target: { value: 'John Doe' }
|
|
510
|
+
});
|
|
511
|
+
}));
|
|
512
|
+
unmount();
|
|
513
|
+
// Advance timers and ensure onValueChange is not called
|
|
514
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
515
|
+
vi.advanceTimersByTime(THROTTLE_DELAY + 100);
|
|
516
|
+
}));
|
|
517
|
+
expect(mockOnValueChange).not.toHaveBeenCalled();
|
|
518
|
+
vi.useRealTimers();
|
|
519
|
+
}));
|
|
520
|
+
it('handles custom field with undefined render prop', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
521
|
+
const fields = [
|
|
522
|
+
{
|
|
523
|
+
key: 'customField',
|
|
524
|
+
label: 'Custom Field',
|
|
525
|
+
type: 'custom',
|
|
526
|
+
render: undefined
|
|
527
|
+
}
|
|
528
|
+
];
|
|
529
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
530
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
531
|
+
}));
|
|
532
|
+
// The custom field should not be rendered, but the form should not crash
|
|
533
|
+
expect(screen.queryByLabelText('Custom Field')).not.toBeInTheDocument();
|
|
534
|
+
// The form should still be rendered
|
|
535
|
+
expect(screen.getByTestId('akiform-builder')).toBeInTheDocument();
|
|
536
|
+
}));
|
|
537
|
+
});
|
|
538
|
+
describe('AkiformBuilder in controlled mode', () => {
|
|
539
|
+
it('uses provided values in controlled mode', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
540
|
+
const fields = [
|
|
541
|
+
{ key: 'name', label: 'Name', type: 'text' },
|
|
542
|
+
{ key: 'age', label: 'Age', type: 'number' }
|
|
543
|
+
];
|
|
544
|
+
const controlledValues = { name: 'John Doe', age: 30 };
|
|
545
|
+
const onChangeMock = vi.fn();
|
|
546
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
547
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn(), controlled: true, values: controlledValues, onValueChange: onChangeMock }));
|
|
548
|
+
}));
|
|
549
|
+
expect(screen.getByLabelText('Name')).toHaveValue('John Doe');
|
|
550
|
+
expect(screen.getByLabelText('Age')).toHaveValue('30');
|
|
551
|
+
}));
|
|
552
|
+
it('calls onValueChange when form values change in controlled mode', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
553
|
+
const fields = [
|
|
554
|
+
{ key: 'name', label: 'Name', type: 'text' }
|
|
555
|
+
];
|
|
556
|
+
const onChangeMock = vi.fn();
|
|
557
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
558
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn(), controlled: true, values: { name: '' }, onValueChange: onChangeMock }));
|
|
559
|
+
}));
|
|
560
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
561
|
+
fireEvent.change(screen.getByLabelText('Name'), {
|
|
562
|
+
target: { value: 'Jane Doe' }
|
|
563
|
+
});
|
|
564
|
+
}));
|
|
565
|
+
yield waitFor(() => {
|
|
566
|
+
expect(onChangeMock).toHaveBeenCalledWith(expect.objectContaining({ name: 'Jane Doe' }));
|
|
567
|
+
});
|
|
568
|
+
}));
|
|
569
|
+
it('calls onValueChange immediately in controlled mode', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
570
|
+
const onValueChangeMock = vi.fn();
|
|
571
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
572
|
+
render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: mockOnSubmit, controlled: true, values: { name: 'John Doe', age: 30 }, onValueChange: onValueChangeMock }));
|
|
573
|
+
}));
|
|
574
|
+
expect(onValueChangeMock).toHaveBeenCalledWith(expect.objectContaining({ name: 'John Doe', age: 30 }));
|
|
575
|
+
}));
|
|
576
|
+
it('handles conditional rendering and disabling of fields', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
577
|
+
const fields = [
|
|
578
|
+
{ key: 'showField', label: 'Show Field', type: 'checkbox' },
|
|
579
|
+
{
|
|
580
|
+
key: 'conditionalField',
|
|
581
|
+
label: 'Conditional Field',
|
|
582
|
+
type: 'text',
|
|
583
|
+
config: { visible: values => values.showField }
|
|
584
|
+
},
|
|
585
|
+
{ key: 'enableField', label: 'Enable Field', type: 'checkbox' },
|
|
586
|
+
{
|
|
587
|
+
key: 'disableableField',
|
|
588
|
+
label: 'Disableable Field',
|
|
589
|
+
type: 'text',
|
|
590
|
+
config: { disabled: values => !values.enableField }
|
|
591
|
+
}
|
|
592
|
+
];
|
|
593
|
+
const onValueChangeMock = vi.fn();
|
|
594
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
595
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn(), controlled: true, values: { showField: false, enableField: false }, onValueChange: onValueChangeMock }));
|
|
596
|
+
}));
|
|
597
|
+
expect(screen.queryByLabelText('Conditional Field')).not.toBeInTheDocument();
|
|
598
|
+
expect(screen.getByLabelText('Disableable Field')).toBeDisabled();
|
|
599
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
600
|
+
fireEvent.click(screen.getByLabelText('Show Field'));
|
|
601
|
+
fireEvent.click(screen.getByLabelText('Enable Field'));
|
|
602
|
+
}));
|
|
603
|
+
expect(screen.getByLabelText('Conditional Field')).toBeInTheDocument();
|
|
604
|
+
expect(screen.getByLabelText('Disableable Field')).not.toBeDisabled();
|
|
605
|
+
expect(onValueChangeMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
606
|
+
showField: true,
|
|
607
|
+
enableField: true
|
|
608
|
+
}));
|
|
609
|
+
}));
|
|
610
|
+
});
|
|
611
|
+
describe('FieldArrayComponent', () => {
|
|
612
|
+
it('renders field array with initial values', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
613
|
+
const fields = [
|
|
614
|
+
{
|
|
615
|
+
key: 'items',
|
|
616
|
+
label: 'Items',
|
|
617
|
+
type: 'fieldArray',
|
|
618
|
+
fields: [
|
|
619
|
+
{ key: 'name', label: 'Item Name', type: 'text' },
|
|
620
|
+
{ key: 'quantity', label: 'Quantity', type: 'number' }
|
|
621
|
+
]
|
|
622
|
+
}
|
|
623
|
+
];
|
|
624
|
+
const initialValues = {
|
|
625
|
+
items: [
|
|
626
|
+
{ name: 'Item 1', quantity: 1 },
|
|
627
|
+
{ name: 'Item 2', quantity: 2 }
|
|
628
|
+
]
|
|
629
|
+
};
|
|
630
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
631
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn(), initialValues: initialValues }));
|
|
632
|
+
}));
|
|
633
|
+
expect(screen.getAllByLabelText('Item Name')).toHaveLength(2);
|
|
634
|
+
expect(screen.getAllByLabelText('Quantity')).toHaveLength(2);
|
|
635
|
+
expect(screen.getAllByText('Remove')).toHaveLength(2);
|
|
636
|
+
}));
|
|
637
|
+
it('adds and removes field array items', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
638
|
+
const fields = [
|
|
639
|
+
{
|
|
640
|
+
key: 'items',
|
|
641
|
+
label: 'Items',
|
|
642
|
+
type: 'fieldArray',
|
|
643
|
+
fields: [{ key: 'name', label: 'Item Name', type: 'text' }]
|
|
644
|
+
}
|
|
645
|
+
];
|
|
646
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
647
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
648
|
+
}));
|
|
649
|
+
// Add an item
|
|
650
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
651
|
+
fireEvent.click(screen.getByText('Add'));
|
|
652
|
+
}));
|
|
653
|
+
expect(screen.getByLabelText('Item Name')).toBeInTheDocument();
|
|
654
|
+
// Add another item
|
|
655
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
656
|
+
fireEvent.click(screen.getByText('Add'));
|
|
657
|
+
}));
|
|
658
|
+
expect(screen.getAllByLabelText('Item Name')).toHaveLength(2);
|
|
659
|
+
// Remove an item
|
|
660
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
661
|
+
fireEvent.click(screen.getAllByText('Remove')[0]);
|
|
662
|
+
}));
|
|
663
|
+
expect(screen.getAllByLabelText('Item Name')).toHaveLength(1);
|
|
664
|
+
}));
|
|
665
|
+
it('handles field array with conditional fields', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
666
|
+
const fields = [
|
|
667
|
+
{
|
|
668
|
+
key: 'items',
|
|
669
|
+
label: 'Items',
|
|
670
|
+
type: 'fieldArray',
|
|
671
|
+
fields: [
|
|
672
|
+
{ key: 'name', label: 'Item Name', type: 'text' },
|
|
673
|
+
{
|
|
674
|
+
key: 'description',
|
|
675
|
+
label: 'Description',
|
|
676
|
+
type: 'text',
|
|
677
|
+
config: {
|
|
678
|
+
visible: values => values.name && values.name.length > 5
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
]
|
|
682
|
+
}
|
|
683
|
+
];
|
|
684
|
+
const { rerender } = render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
685
|
+
// Add an item
|
|
686
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
687
|
+
fireEvent.click(screen.getByText('Add'));
|
|
688
|
+
}));
|
|
689
|
+
// Description should not be visible initially
|
|
690
|
+
expect(screen.queryByLabelText('Description')).not.toBeInTheDocument();
|
|
691
|
+
// Enter a name longer than 5 characters
|
|
692
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
693
|
+
fireEvent.change(screen.getByLabelText('Item Name'), {
|
|
694
|
+
target: { value: 'Long Name' }
|
|
695
|
+
});
|
|
696
|
+
}));
|
|
697
|
+
// Force a re-render to trigger the conditional rendering
|
|
698
|
+
rerender(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
699
|
+
// Description should now be visible
|
|
700
|
+
yield waitFor(() => {
|
|
701
|
+
expect(screen.getByLabelText('Description')).toBeInTheDocument();
|
|
702
|
+
});
|
|
703
|
+
}));
|
|
704
|
+
});
|
|
705
|
+
describe('Edge cases and uncovered scenarios', () => {
|
|
706
|
+
it('handles field array with no fields', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
707
|
+
const fields = [
|
|
708
|
+
{
|
|
709
|
+
key: 'emptyFieldArray',
|
|
710
|
+
label: 'Empty Field Array',
|
|
711
|
+
type: 'fieldArray',
|
|
712
|
+
fields: []
|
|
713
|
+
}
|
|
714
|
+
];
|
|
715
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
716
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
717
|
+
}));
|
|
718
|
+
// The field array should be rendered with an "Add" button
|
|
719
|
+
expect(screen.getByText('Add')).toBeInTheDocument();
|
|
720
|
+
}));
|
|
721
|
+
it('handles form submission with field array', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
722
|
+
const fields = [
|
|
723
|
+
{
|
|
724
|
+
key: 'items',
|
|
725
|
+
label: 'Items',
|
|
726
|
+
type: 'fieldArray',
|
|
727
|
+
fields: [
|
|
728
|
+
{ key: 'name', label: 'Item Name', type: 'text' },
|
|
729
|
+
{ key: 'quantity', label: 'Quantity', type: 'number' }
|
|
730
|
+
]
|
|
731
|
+
}
|
|
732
|
+
];
|
|
733
|
+
const onSubmitMock = vi.fn();
|
|
734
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
735
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: onSubmitMock }));
|
|
736
|
+
}));
|
|
737
|
+
// Add two items
|
|
738
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
739
|
+
fireEvent.click(screen.getByText('Add'));
|
|
740
|
+
fireEvent.click(screen.getByText('Add'));
|
|
741
|
+
}));
|
|
742
|
+
// Fill in the fields
|
|
743
|
+
const itemNames = screen.getAllByLabelText('Item Name');
|
|
744
|
+
const quantities = screen.getAllByLabelText('Quantity');
|
|
745
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
746
|
+
fireEvent.change(itemNames[0], { target: { value: 'Item 1' } });
|
|
747
|
+
fireEvent.change(quantities[0], { target: { value: '5' } });
|
|
748
|
+
fireEvent.change(itemNames[1], { target: { value: 'Item 2' } });
|
|
749
|
+
fireEvent.change(quantities[1], { target: { value: '10' } });
|
|
750
|
+
}));
|
|
751
|
+
// Submit the form
|
|
752
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
753
|
+
fireEvent.submit(screen.getByTestId('akiform-builder'));
|
|
754
|
+
}));
|
|
755
|
+
yield waitFor(() => {
|
|
756
|
+
expect(onSubmitMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
757
|
+
items: [
|
|
758
|
+
{ name: 'Item 1', quantity: 5 },
|
|
759
|
+
{ name: 'Item 2', quantity: 10 }
|
|
760
|
+
]
|
|
761
|
+
}), expect.anything());
|
|
762
|
+
});
|
|
763
|
+
}));
|
|
764
|
+
it('handles form with all field types', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
765
|
+
const fields = [
|
|
766
|
+
{ key: 'text', label: 'Text', type: 'text' },
|
|
767
|
+
{ key: 'number', label: 'Number', type: 'number' },
|
|
768
|
+
{
|
|
769
|
+
key: 'select',
|
|
770
|
+
label: 'Select',
|
|
771
|
+
type: 'select',
|
|
772
|
+
options: [{ value: 'option1', label: 'Option 1' }]
|
|
773
|
+
},
|
|
774
|
+
{ key: 'checkbox', label: 'Checkbox', type: 'checkbox' },
|
|
775
|
+
{ key: 'date', label: 'Date', type: 'date' },
|
|
776
|
+
{ key: 'textarea', label: 'Textarea', type: 'textarea' },
|
|
777
|
+
{
|
|
778
|
+
key: 'fieldArray',
|
|
779
|
+
label: 'Field Array',
|
|
780
|
+
type: 'fieldArray',
|
|
781
|
+
fields: [{ key: 'subfield', label: 'Subfield', type: 'text' }]
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
key: 'custom',
|
|
785
|
+
label: 'Custom',
|
|
786
|
+
type: 'custom',
|
|
787
|
+
render: () => React.createElement("div", { "data-testid": "custom-field" }, "Custom Field")
|
|
788
|
+
}
|
|
789
|
+
];
|
|
790
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
791
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
792
|
+
}));
|
|
793
|
+
expect(screen.getByLabelText('Text')).toBeInTheDocument();
|
|
794
|
+
expect(screen.getByLabelText('Number')).toBeInTheDocument();
|
|
795
|
+
expect(screen.getByLabelText('Select')).toBeInTheDocument();
|
|
796
|
+
expect(screen.getByLabelText('Checkbox')).toBeInTheDocument();
|
|
797
|
+
expect(screen.getByLabelText('Date')).toBeInTheDocument();
|
|
798
|
+
expect(screen.getByLabelText('Textarea')).toBeInTheDocument();
|
|
799
|
+
expect(screen.getByText('Add')).toBeInTheDocument(); // For field array
|
|
800
|
+
expect(screen.getByTestId('custom-field')).toBeInTheDocument();
|
|
801
|
+
}));
|
|
802
|
+
it('handles form with inline layout', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
803
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
804
|
+
render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: vi.fn(), layout: "inline" }));
|
|
805
|
+
}));
|
|
806
|
+
const form = screen.getByTestId('akiform-builder');
|
|
807
|
+
expect(form).toHaveClass('akinon-form-inline');
|
|
808
|
+
}));
|
|
809
|
+
it('handles form reset with custom reset handler', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
810
|
+
const onResetMock = vi.fn();
|
|
811
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
812
|
+
render(React.createElement(AkiformBuilder, { fields: defaultFields, onSubmit: vi.fn(), onReset: onResetMock, showResetButton: true }));
|
|
813
|
+
}));
|
|
814
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
815
|
+
fireEvent.click(screen.getByText('RESET'));
|
|
816
|
+
}));
|
|
817
|
+
expect(onResetMock).toHaveBeenCalled();
|
|
818
|
+
}));
|
|
819
|
+
});
|
|
820
|
+
describe('AkiformBuilder with FieldBuilder', () => {
|
|
821
|
+
it('renders form fields created with FieldBuilder', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
822
|
+
const fields = [
|
|
823
|
+
field()
|
|
824
|
+
.key('name')
|
|
825
|
+
.label('Name')
|
|
826
|
+
.type('text')
|
|
827
|
+
.placeholder('Enter your name')
|
|
828
|
+
.build(),
|
|
829
|
+
field()
|
|
830
|
+
.key('age')
|
|
831
|
+
.label('Age')
|
|
832
|
+
.type('number')
|
|
833
|
+
.placeholder('Enter your age')
|
|
834
|
+
.build(),
|
|
835
|
+
field()
|
|
836
|
+
.key('country')
|
|
837
|
+
.label('Country')
|
|
838
|
+
.type('select')
|
|
839
|
+
.options([
|
|
840
|
+
{ value: 'us', label: 'United States' },
|
|
841
|
+
{ value: 'ca', label: 'Canada' }
|
|
842
|
+
])
|
|
843
|
+
.build(),
|
|
844
|
+
field()
|
|
845
|
+
.key('subscribe')
|
|
846
|
+
.label('Subscribe to newsletter')
|
|
847
|
+
.type('checkbox')
|
|
848
|
+
.build(),
|
|
849
|
+
field().key('birthdate').label('Birth Date').type('date').build(),
|
|
850
|
+
field().key('description').label('Description').type('textarea').build()
|
|
851
|
+
];
|
|
852
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
853
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
854
|
+
}));
|
|
855
|
+
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
856
|
+
expect(screen.getByLabelText('Age')).toBeInTheDocument();
|
|
857
|
+
expect(screen.getByLabelText('Country')).toBeInTheDocument();
|
|
858
|
+
expect(screen.getByLabelText('Subscribe to newsletter')).toBeInTheDocument();
|
|
859
|
+
expect(screen.getByLabelText('Birth Date')).toBeInTheDocument();
|
|
860
|
+
expect(screen.getByLabelText('Description')).toBeInTheDocument();
|
|
861
|
+
}));
|
|
862
|
+
it('handles form submission with fields created by FieldBuilder', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
863
|
+
const fields = [
|
|
864
|
+
field()
|
|
865
|
+
.key('name')
|
|
866
|
+
.label('Name')
|
|
867
|
+
.type('text')
|
|
868
|
+
.validation(akival.string().required('Name is required'))
|
|
869
|
+
.build(),
|
|
870
|
+
field()
|
|
871
|
+
.key('age')
|
|
872
|
+
.label('Age')
|
|
873
|
+
.type('number')
|
|
874
|
+
.validation(akival.number().min(18, 'Must be at least 18'))
|
|
875
|
+
.build()
|
|
876
|
+
];
|
|
877
|
+
const onSubmitMock = vi.fn();
|
|
878
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
879
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: onSubmitMock }));
|
|
880
|
+
}));
|
|
881
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
882
|
+
fireEvent.change(screen.getByLabelText('Name'), {
|
|
883
|
+
target: { value: 'John Doe' }
|
|
884
|
+
});
|
|
885
|
+
fireEvent.change(screen.getByLabelText('Age'), {
|
|
886
|
+
target: { value: '25' }
|
|
887
|
+
});
|
|
888
|
+
fireEvent.submit(screen.getByTestId('akiform-builder'));
|
|
889
|
+
}));
|
|
890
|
+
yield waitFor(() => {
|
|
891
|
+
expect(onSubmitMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
892
|
+
name: 'John Doe',
|
|
893
|
+
age: 25
|
|
894
|
+
}), expect.anything());
|
|
895
|
+
});
|
|
896
|
+
}));
|
|
897
|
+
it('handles form with custom field created by FieldBuilder', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
898
|
+
const CustomComponent = ({ field }) => (React.createElement("div", { "data-testid": "custom-field" }, field.label));
|
|
899
|
+
const fields = [
|
|
900
|
+
field()
|
|
901
|
+
.key('customField')
|
|
902
|
+
.label('Custom Field')
|
|
903
|
+
.type('custom')
|
|
904
|
+
.render(({ field }) => React.createElement(CustomComponent, { field: field }))
|
|
905
|
+
.build()
|
|
906
|
+
];
|
|
907
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
908
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
909
|
+
}));
|
|
910
|
+
expect(screen.getByTestId('custom-field')).toHaveTextContent('Custom Field');
|
|
911
|
+
}));
|
|
912
|
+
it('handles form with field array created by FieldBuilder', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
913
|
+
const fields = [
|
|
914
|
+
field()
|
|
915
|
+
.key('addresses')
|
|
916
|
+
.label('Addresses')
|
|
917
|
+
.type('fieldArray')
|
|
918
|
+
.fields([
|
|
919
|
+
field().key('street').label('Street').type('text').build(),
|
|
920
|
+
field().key('city').label('City').type('text').build()
|
|
921
|
+
])
|
|
922
|
+
.build()
|
|
923
|
+
];
|
|
924
|
+
const onSubmitMock = vi.fn();
|
|
925
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
926
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: onSubmitMock }));
|
|
927
|
+
}));
|
|
928
|
+
// Add an address
|
|
929
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
930
|
+
fireEvent.click(screen.getByText('Add'));
|
|
931
|
+
}));
|
|
932
|
+
// Fill in the fields
|
|
933
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
934
|
+
fireEvent.change(screen.getByLabelText('Street'), {
|
|
935
|
+
target: { value: '123 Main St' }
|
|
936
|
+
});
|
|
937
|
+
fireEvent.change(screen.getByLabelText('City'), {
|
|
938
|
+
target: { value: 'Anytown' }
|
|
939
|
+
});
|
|
940
|
+
}));
|
|
941
|
+
// Submit the form
|
|
942
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
943
|
+
fireEvent.submit(screen.getByTestId('akiform-builder'));
|
|
944
|
+
}));
|
|
945
|
+
yield waitFor(() => {
|
|
946
|
+
expect(onSubmitMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
947
|
+
addresses: [{ street: '123 Main St', city: 'Anytown' }]
|
|
948
|
+
}), expect.anything());
|
|
949
|
+
});
|
|
950
|
+
}));
|
|
951
|
+
it('handles conditional rendering with FieldBuilder', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
952
|
+
const fields = [
|
|
953
|
+
field().key('showField').label('Show Field').type('checkbox').build(),
|
|
954
|
+
field()
|
|
955
|
+
.key('conditionalField')
|
|
956
|
+
.label('Conditional Field')
|
|
957
|
+
.type('text')
|
|
958
|
+
.config({ visible: (values) => values.showField })
|
|
959
|
+
.build()
|
|
960
|
+
];
|
|
961
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
962
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
963
|
+
}));
|
|
964
|
+
expect(screen.queryByLabelText('Conditional Field')).not.toBeInTheDocument();
|
|
965
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
966
|
+
fireEvent.click(screen.getByLabelText('Show Field'));
|
|
967
|
+
}));
|
|
968
|
+
expect(screen.getByLabelText('Conditional Field')).toBeInTheDocument();
|
|
969
|
+
}));
|
|
970
|
+
it('handles disabled fields with FieldBuilder', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
971
|
+
const fields = [
|
|
972
|
+
field()
|
|
973
|
+
.key('disabledField')
|
|
974
|
+
.label('Disabled Field')
|
|
975
|
+
.type('text')
|
|
976
|
+
.config({ disabled: true })
|
|
977
|
+
.build()
|
|
978
|
+
];
|
|
979
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
980
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
981
|
+
}));
|
|
982
|
+
expect(screen.getByLabelText('Disabled Field')).toBeDisabled();
|
|
983
|
+
}));
|
|
984
|
+
});
|
|
985
|
+
describe('Accessibility features', () => {
|
|
986
|
+
it('renders form with proper ARIA attributes', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
987
|
+
const fields = [
|
|
988
|
+
{
|
|
989
|
+
key: 'name',
|
|
990
|
+
label: 'Name',
|
|
991
|
+
type: 'text',
|
|
992
|
+
validation: akival.string().required()
|
|
993
|
+
},
|
|
994
|
+
{ key: 'age', label: 'Age', type: 'number' }
|
|
995
|
+
];
|
|
996
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
997
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
998
|
+
}));
|
|
999
|
+
const form = screen.getByRole('form');
|
|
1000
|
+
expect(form).toHaveAttribute('aria-label', 'Form');
|
|
1001
|
+
const nameInput = screen.getByLabelText('Name');
|
|
1002
|
+
expect(nameInput).toHaveAttribute('aria-required', 'true');
|
|
1003
|
+
expect(nameInput).toHaveAttribute('aria-invalid', 'false');
|
|
1004
|
+
const ageInput = screen.getByLabelText('Age');
|
|
1005
|
+
expect(ageInput).toHaveAttribute('aria-required', 'false');
|
|
1006
|
+
expect(ageInput).toHaveAttribute('aria-invalid', 'false');
|
|
1007
|
+
}));
|
|
1008
|
+
it('updates aria-invalid attribute when form is submitted with errors', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1009
|
+
const fields = [
|
|
1010
|
+
{
|
|
1011
|
+
key: 'name',
|
|
1012
|
+
label: 'Name',
|
|
1013
|
+
type: 'text',
|
|
1014
|
+
validation: akival.string().required('Name is required')
|
|
1015
|
+
}
|
|
1016
|
+
];
|
|
1017
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1018
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
1019
|
+
}));
|
|
1020
|
+
const nameInput = screen.getByLabelText('Name');
|
|
1021
|
+
expect(nameInput).toHaveAttribute('aria-invalid', 'false');
|
|
1022
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1023
|
+
fireEvent.submit(screen.getByRole('form'));
|
|
1024
|
+
}));
|
|
1025
|
+
yield waitFor(() => {
|
|
1026
|
+
expect(nameInput).toHaveAttribute('aria-invalid', 'true');
|
|
1027
|
+
expect(screen.getByText('Name is required')).toBeInTheDocument();
|
|
1028
|
+
});
|
|
1029
|
+
}));
|
|
1030
|
+
it('renders field array with proper ARIA attributes', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1031
|
+
const fields = [
|
|
1032
|
+
{
|
|
1033
|
+
key: 'addresses',
|
|
1034
|
+
label: 'Addresses',
|
|
1035
|
+
type: 'fieldArray',
|
|
1036
|
+
fields: [
|
|
1037
|
+
{ key: 'street', label: 'Street', type: 'text' },
|
|
1038
|
+
{ key: 'city', label: 'City', type: 'text' }
|
|
1039
|
+
]
|
|
1040
|
+
}
|
|
1041
|
+
];
|
|
1042
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1043
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
1044
|
+
}));
|
|
1045
|
+
const fieldArrayGroup = screen.getByRole('group', { name: 'Addresses' });
|
|
1046
|
+
expect(fieldArrayGroup).toBeInTheDocument();
|
|
1047
|
+
const addButton = screen.getByRole('button', { name: 'Add Addresses' });
|
|
1048
|
+
expect(addButton).toBeInTheDocument();
|
|
1049
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1050
|
+
fireEvent.click(addButton);
|
|
1051
|
+
}));
|
|
1052
|
+
const removeButton = screen.getByRole('button', {
|
|
1053
|
+
name: 'Remove Addresses 1'
|
|
1054
|
+
});
|
|
1055
|
+
expect(removeButton).toBeInTheDocument();
|
|
1056
|
+
}));
|
|
1057
|
+
it('supports keyboard navigation', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1058
|
+
const user = userEvent.setup();
|
|
1059
|
+
const fields = [
|
|
1060
|
+
{ key: 'name', label: 'Name', type: 'text' },
|
|
1061
|
+
{ key: 'age', label: 'Age', type: 'number' }
|
|
1062
|
+
];
|
|
1063
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1064
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn(), showResetButton: true }));
|
|
1065
|
+
}));
|
|
1066
|
+
const nameInput = screen.getByLabelText('Name');
|
|
1067
|
+
const ageInput = screen.getByLabelText('Age');
|
|
1068
|
+
const submitButton = screen.getByRole('button', { name: 'SUBMIT' });
|
|
1069
|
+
const resetButton = screen.getByRole('button', { name: 'RESET' });
|
|
1070
|
+
yield user.tab();
|
|
1071
|
+
expect(document.activeElement).toBe(nameInput);
|
|
1072
|
+
yield user.tab();
|
|
1073
|
+
expect(document.activeElement).toBe(ageInput);
|
|
1074
|
+
yield user.tab();
|
|
1075
|
+
expect(document.activeElement).toBe(submitButton);
|
|
1076
|
+
yield user.tab();
|
|
1077
|
+
expect(document.activeElement).toBe(resetButton);
|
|
1078
|
+
}));
|
|
1079
|
+
});
|
|
1080
|
+
describe('AkiformBuilder with sections', () => {
|
|
1081
|
+
it('renders form with sections', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1082
|
+
const fields = [
|
|
1083
|
+
field()
|
|
1084
|
+
.key('personalInfo')
|
|
1085
|
+
.label('Personal Information')
|
|
1086
|
+
.type('section')
|
|
1087
|
+
.fields([
|
|
1088
|
+
field().key('name').label('Name').type('text').build(),
|
|
1089
|
+
field().key('age').label('Age').type('number').build()
|
|
1090
|
+
])
|
|
1091
|
+
.defaultExpanded(true)
|
|
1092
|
+
.build(),
|
|
1093
|
+
field()
|
|
1094
|
+
.key('contactInfo')
|
|
1095
|
+
.label('Contact Information')
|
|
1096
|
+
.type('section')
|
|
1097
|
+
.fields([
|
|
1098
|
+
field().key('email').label('Email').type('text').build(),
|
|
1099
|
+
field().key('phone').label('Phone').type('text').build()
|
|
1100
|
+
])
|
|
1101
|
+
.build()
|
|
1102
|
+
];
|
|
1103
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1104
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
1105
|
+
}));
|
|
1106
|
+
expect(screen.getByText('Personal Information')).toBeInTheDocument();
|
|
1107
|
+
expect(screen.getByText('Contact Information')).toBeInTheDocument();
|
|
1108
|
+
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
1109
|
+
expect(screen.getByLabelText('Age')).toBeInTheDocument();
|
|
1110
|
+
// Check if the first section is expanded by default
|
|
1111
|
+
expect(screen.getByLabelText('Name')).toBeVisible();
|
|
1112
|
+
// Check if the second section is collapsed by default
|
|
1113
|
+
expect(screen.queryByLabelText('Email')).not.toBeInTheDocument();
|
|
1114
|
+
// Expand the second section
|
|
1115
|
+
const contactInfoSection = screen.getByText('Contact Information');
|
|
1116
|
+
yield userEvent.click(contactInfoSection);
|
|
1117
|
+
// Wait for the section to expand
|
|
1118
|
+
yield waitFor(() => {
|
|
1119
|
+
expect(screen.getByLabelText('Email')).toBeInTheDocument();
|
|
1120
|
+
});
|
|
1121
|
+
// Check if the Email field is now visible
|
|
1122
|
+
// const emailInput = screen.getByLabelText('Email');
|
|
1123
|
+
// expect(emailInput).toBeVisible();
|
|
1124
|
+
// Additional debug information
|
|
1125
|
+
// if (!emailInput.isConnected) {
|
|
1126
|
+
// console.error('Email input is not connected to the DOM');
|
|
1127
|
+
// }
|
|
1128
|
+
// console.log(
|
|
1129
|
+
// 'Email input visibility:',
|
|
1130
|
+
// window.getComputedStyle(emailInput).display
|
|
1131
|
+
// );
|
|
1132
|
+
}));
|
|
1133
|
+
it('handles form submission with sections', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1134
|
+
const onSubmitMock = vi.fn();
|
|
1135
|
+
const fields = [
|
|
1136
|
+
field()
|
|
1137
|
+
.key('personalInfo')
|
|
1138
|
+
.label('Personal Information')
|
|
1139
|
+
.type('section')
|
|
1140
|
+
.fields([
|
|
1141
|
+
field().key('name').label('Name').type('text').build(),
|
|
1142
|
+
field().key('age').label('Age').type('number').build()
|
|
1143
|
+
])
|
|
1144
|
+
.build(),
|
|
1145
|
+
field()
|
|
1146
|
+
.key('contactInfo')
|
|
1147
|
+
.label('Contact Information')
|
|
1148
|
+
.type('section')
|
|
1149
|
+
.fields([field().key('email').label('Email').type('text').build()])
|
|
1150
|
+
.build()
|
|
1151
|
+
];
|
|
1152
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1153
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: onSubmitMock }));
|
|
1154
|
+
}));
|
|
1155
|
+
// Expand both sections
|
|
1156
|
+
yield userEvent.click(screen.getByText('Personal Information'));
|
|
1157
|
+
yield userEvent.click(screen.getByText('Contact Information'));
|
|
1158
|
+
// Fill in the form
|
|
1159
|
+
yield userEvent.type(screen.getByLabelText('Name'), 'John Doe', {
|
|
1160
|
+
delay: 1
|
|
1161
|
+
});
|
|
1162
|
+
yield userEvent.type(screen.getByLabelText('Age'), '30', { delay: 1 });
|
|
1163
|
+
yield userEvent.type(screen.getByLabelText('Email'), 'john@example.com', {
|
|
1164
|
+
delay: 1
|
|
1165
|
+
});
|
|
1166
|
+
// Wait for submit button to become interactable
|
|
1167
|
+
yield waitFor(() => {
|
|
1168
|
+
expect(screen.getByRole('button', { name: 'Submit' })).toBeEnabled();
|
|
1169
|
+
});
|
|
1170
|
+
// Submit the form
|
|
1171
|
+
yield userEvent.click(screen.getByRole('button', { name: 'SUBMIT' }));
|
|
1172
|
+
// Validate submission
|
|
1173
|
+
expect(onSubmitMock).toHaveBeenCalledWith({
|
|
1174
|
+
name: 'John Doe',
|
|
1175
|
+
age: 30,
|
|
1176
|
+
email: 'john@example.com'
|
|
1177
|
+
}, expect.anything());
|
|
1178
|
+
}), 10000);
|
|
1179
|
+
it('handles conditional rendering within sections', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1180
|
+
const fields = [
|
|
1181
|
+
field()
|
|
1182
|
+
.key('personalInfo')
|
|
1183
|
+
.label('Personal Information')
|
|
1184
|
+
.type('section')
|
|
1185
|
+
.fields([
|
|
1186
|
+
field().key('name').label('Name').type('text').build(),
|
|
1187
|
+
field().key('showAge').label('Show Age').type('checkbox').build(),
|
|
1188
|
+
field()
|
|
1189
|
+
.key('age')
|
|
1190
|
+
.label('Age')
|
|
1191
|
+
.type('number')
|
|
1192
|
+
.config({ visible: (values) => values.showAge })
|
|
1193
|
+
.build()
|
|
1194
|
+
])
|
|
1195
|
+
.defaultExpanded(true)
|
|
1196
|
+
.build()
|
|
1197
|
+
];
|
|
1198
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1199
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
1200
|
+
}));
|
|
1201
|
+
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
1202
|
+
expect(screen.getByLabelText('Show Age')).toBeInTheDocument();
|
|
1203
|
+
expect(screen.queryByLabelText('Age')).not.toBeInTheDocument();
|
|
1204
|
+
yield userEvent.click(screen.getByLabelText('Show Age'));
|
|
1205
|
+
expect(yield screen.findByLabelText('Age')).toBeInTheDocument();
|
|
1206
|
+
}));
|
|
1207
|
+
});
|
|
1208
|
+
describe('AkiformBuilder with tooltip', () => {
|
|
1209
|
+
it('renders form field with tooltip as TooltipProps', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1210
|
+
const fields = [
|
|
1211
|
+
field()
|
|
1212
|
+
.key('name')
|
|
1213
|
+
.label('Name')
|
|
1214
|
+
.type('text')
|
|
1215
|
+
.tooltip({ title: 'Enter your full name' })
|
|
1216
|
+
.build()
|
|
1217
|
+
];
|
|
1218
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1219
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
1220
|
+
}));
|
|
1221
|
+
const tooltipTrigger = screen.getByLabelText('Name');
|
|
1222
|
+
expect(tooltipTrigger).toBeInTheDocument();
|
|
1223
|
+
// Simulate hovering over the field to show the tooltip
|
|
1224
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1225
|
+
fireEvent.mouseEnter(tooltipTrigger);
|
|
1226
|
+
}));
|
|
1227
|
+
// Wait for the tooltip to appear
|
|
1228
|
+
yield waitFor(() => {
|
|
1229
|
+
expect(screen.getByText('Enter your full name')).toBeInTheDocument();
|
|
1230
|
+
});
|
|
1231
|
+
}));
|
|
1232
|
+
it('renders form field with tooltip as string', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1233
|
+
const fields = [
|
|
1234
|
+
field()
|
|
1235
|
+
.key('email')
|
|
1236
|
+
.label('Email')
|
|
1237
|
+
.type('text')
|
|
1238
|
+
.tooltip('Enter your email address')
|
|
1239
|
+
.build()
|
|
1240
|
+
];
|
|
1241
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1242
|
+
render(React.createElement(AkiformBuilder, { fields: fields, onSubmit: vi.fn() }));
|
|
1243
|
+
}));
|
|
1244
|
+
const tooltipTrigger = screen.getByLabelText('Email');
|
|
1245
|
+
expect(tooltipTrigger).toBeInTheDocument();
|
|
1246
|
+
// Simulate hovering over the field to show the tooltip
|
|
1247
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1248
|
+
fireEvent.mouseEnter(tooltipTrigger);
|
|
1249
|
+
}));
|
|
1250
|
+
// Wait for the tooltip to appear
|
|
1251
|
+
yield waitFor(() => {
|
|
1252
|
+
expect(screen.getByText('Enter your email address')).toBeInTheDocument();
|
|
1253
|
+
});
|
|
1254
|
+
}));
|
|
1255
|
+
});
|
|
1256
|
+
});
|