@fiscozen/input 3.2.0 → 3.3.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.
@@ -1,1696 +1,1797 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2
- import { mount } from '@vue/test-utils'
3
- import { FzCurrencyInput } from '..'
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { mount } from "@vue/test-utils";
3
+ import { FzCurrencyInput } from "..";
4
4
 
5
- describe('FzCurrencyInput', () => {
5
+ describe("FzCurrencyInput", () => {
6
6
  beforeEach(() => {
7
- vi.clearAllMocks()
8
- })
7
+ vi.clearAllMocks();
8
+ });
9
9
 
10
10
  afterEach(() => {
11
- vi.restoreAllMocks()
12
- })
11
+ vi.restoreAllMocks();
12
+ });
13
13
 
14
- describe('Currency formatting', () => {
15
- it('renders floating numbers as currency with thousand separators', async () => {
16
- let modelValue: number | undefined = 1234.56
17
- let wrapper: ReturnType<typeof mount> | null = null
14
+ describe("Currency formatting", () => {
15
+ it("renders floating numbers as currency with thousand separators", async () => {
16
+ let modelValue: number | undefined = 1234.56;
17
+ let wrapper: ReturnType<typeof mount> | null = null;
18
18
  wrapper = mount(FzCurrencyInput, {
19
19
  props: {
20
- label: 'Label',
20
+ label: "Label",
21
21
  modelValue,
22
- 'onUpdate:modelValue': (e) => {
23
- modelValue = e as number
24
- if (wrapper) wrapper.setProps({ modelValue })
22
+ "onUpdate:modelValue": (e) => {
23
+ modelValue = e as number;
24
+ if (wrapper) wrapper.setProps({ modelValue });
25
25
  },
26
26
  },
27
- })
27
+ });
28
28
 
29
- const inputElement = wrapper.find('input')
30
- await inputElement.trigger('blur')
31
- await new Promise((resolve) => window.setTimeout(resolve, 100))
29
+ const inputElement = wrapper.find("input");
30
+ await inputElement.trigger("blur");
31
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
32
32
  // With grouping, should show "1.234,56"
33
- expect(inputElement.element.value).toBe('1.234,56')
34
- })
33
+ expect(inputElement.element.value).toBe("1.234,56");
34
+ });
35
35
 
36
- it('should not allow inputs other than digits and separators', async () => {
37
- let modelValue: number | undefined = 0
38
- let wrapper: ReturnType<typeof mount> | null = null
36
+ it("should not allow inputs other than digits and separators", async () => {
37
+ let modelValue: number | undefined = 0;
38
+ let wrapper: ReturnType<typeof mount> | null = null;
39
39
  wrapper = mount(FzCurrencyInput, {
40
40
  props: {
41
- label: 'Label',
41
+ label: "Label",
42
42
  modelValue,
43
- 'onUpdate:modelValue': (e) => {
44
- modelValue = e as number
45
- if (wrapper) wrapper.setProps({ modelValue })
43
+ "onUpdate:modelValue": (e) => {
44
+ modelValue = e as number;
45
+ if (wrapper) wrapper.setProps({ modelValue });
46
46
  },
47
47
  },
48
- })
48
+ });
49
49
 
50
- const inputElement = wrapper.find('input')
51
- await inputElement.trigger('focus')
52
- await inputElement.setValue('as12,3')
53
- await inputElement.trigger('input')
54
- await new Promise((resolve) => window.setTimeout(resolve, 100))
50
+ const inputElement = wrapper.find("input");
51
+ await inputElement.trigger("focus");
52
+ await inputElement.setValue("as12,3");
53
+ await inputElement.trigger("input");
54
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
55
55
  // During typing when focused, shows raw normalized value (converted . to ,)
56
- expect(inputElement.element.value).toBe('12,3')
57
- await inputElement.trigger('blur')
58
- await new Promise((resolve) => window.setTimeout(resolve, 100))
56
+ expect(inputElement.element.value).toBe("12,3");
57
+ await inputElement.trigger("blur");
58
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
59
59
  // On blur, formats with minimumFractionDigits
60
- expect(inputElement.element.value).toBe('12,30')
61
- })
60
+ expect(inputElement.element.value).toBe("12,30");
61
+ });
62
62
 
63
- it('should allow to set value at 0', async () => {
63
+ it("should allow to set value at 0", async () => {
64
64
  const wrapper = mount(FzCurrencyInput, {
65
65
  props: {
66
- label: 'Label',
66
+ label: "Label",
67
67
  modelValue: 10,
68
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
68
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
69
69
  },
70
- })
71
-
72
- const inputElement = wrapper.find('input')
73
- await inputElement.trigger('blur')
74
- await new Promise((resolve) => window.setTimeout(resolve, 100))
75
- expect(inputElement.element.value).toBe('10,00')
76
- wrapper.setProps({ modelValue: 0 })
77
- await new Promise((resolve) => window.setTimeout(resolve, 100))
78
- expect(inputElement.element.value).toBe('0,00')
79
- })
80
- })
81
-
82
-
83
- describe('Value constraints', () => {
84
- it('should limit value according to min/max setting', async () => {
70
+ });
71
+
72
+ const inputElement = wrapper.find("input");
73
+ await inputElement.trigger("blur");
74
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
75
+ expect(inputElement.element.value).toBe("10,00");
76
+ wrapper.setProps({ modelValue: 0 });
77
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
78
+ expect(inputElement.element.value).toBe("0,00");
79
+ });
80
+ });
81
+
82
+ describe("Value constraints", () => {
83
+ it("should limit value according to min/max setting", async () => {
85
84
  const wrapper = mount(FzCurrencyInput, {
86
85
  props: {
87
- label: 'Label',
86
+ label: "Label",
88
87
  modelValue: 10,
89
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
88
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
90
89
  min: 2,
91
90
  max: 20,
92
91
  },
93
- })
92
+ });
93
+
94
+ const inputElement = wrapper.find("input");
95
+ await inputElement.trigger("blur");
96
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
97
+ expect(inputElement.element.value).toBe("10,00");
94
98
 
95
- const inputElement = wrapper.find('input')
96
- await inputElement.trigger('blur')
97
- await new Promise((resolve) => window.setTimeout(resolve, 100))
98
- expect(inputElement.element.value).toBe('10,00')
99
-
100
99
  // Set value below min and trigger blur to apply clamping
101
- await inputElement.setValue('1')
102
- await inputElement.trigger('blur')
103
- await new Promise((resolve) => window.setTimeout(resolve, 100))
104
- expect(inputElement.element.value).toBe('2,00')
105
-
100
+ await inputElement.setValue("1");
101
+ await inputElement.trigger("blur");
102
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
103
+ expect(inputElement.element.value).toBe("2,00");
104
+
106
105
  // Set value above max and trigger blur to apply clamping
107
- await inputElement.setValue('23')
108
- await inputElement.trigger('blur')
109
- await new Promise((resolve) => window.setTimeout(resolve, 100))
110
- expect(inputElement.element.value).toBe('20,00')
111
- })
106
+ await inputElement.setValue("23");
107
+ await inputElement.trigger("blur");
108
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
109
+ expect(inputElement.element.value).toBe("20,00");
110
+ });
112
111
 
113
- it('should allow typing values outside min/max range temporarily', async () => {
112
+ it("should allow typing values outside min/max range temporarily", async () => {
114
113
  const wrapper = mount(FzCurrencyInput, {
115
114
  props: {
116
- label: 'Label',
115
+ label: "Label",
117
116
  modelValue: 50,
118
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
117
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
119
118
  min: 2,
120
119
  max: 100,
121
120
  },
122
- })
121
+ });
122
+
123
+ const inputElement = wrapper.find("input");
124
+ await inputElement.trigger("focus");
123
125
 
124
- const inputElement = wrapper.find('input')
125
- await inputElement.trigger('focus')
126
-
127
126
  // Type a value above max - should be allowed during typing
128
- await inputElement.setValue('150')
129
- await inputElement.trigger('input')
130
- await new Promise((resolve) => window.setTimeout(resolve, 100))
127
+ await inputElement.setValue("150");
128
+ await inputElement.trigger("input");
129
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
131
130
  // During typing, value should NOT be clamped - user can type "150" even with max=100
132
- expect(inputElement.element.value).toBe('150')
131
+ expect(inputElement.element.value).toBe("150");
133
132
  // v-model should also reflect the unclamped value during typing
134
- expect(wrapper.props('modelValue')).toBe(150)
135
-
133
+ expect(wrapper.props("modelValue")).toBe(150);
134
+
136
135
  // Only after blur, value should be clamped to max
137
- await inputElement.trigger('blur')
138
- await new Promise((resolve) => window.setTimeout(resolve, 100))
139
- expect(inputElement.element.value).toBe('100,00')
140
- expect(wrapper.props('modelValue')).toBe(100)
141
-
136
+ await inputElement.trigger("blur");
137
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
138
+ expect(inputElement.element.value).toBe("100,00");
139
+ expect(wrapper.props("modelValue")).toBe(100);
140
+
142
141
  // Test with value below min
143
- await inputElement.trigger('focus')
144
- await inputElement.setValue('1')
145
- await inputElement.trigger('input')
146
- await new Promise((resolve) => window.setTimeout(resolve, 100))
142
+ await inputElement.trigger("focus");
143
+ await inputElement.setValue("1");
144
+ await inputElement.trigger("input");
145
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
147
146
  // During typing, value should NOT be clamped
148
- expect(inputElement.element.value).toBe('1')
149
- expect(wrapper.props('modelValue')).toBe(1)
150
-
147
+ expect(inputElement.element.value).toBe("1");
148
+ expect(wrapper.props("modelValue")).toBe(1);
149
+
151
150
  // Only after blur, value should be clamped to min
152
- await inputElement.trigger('blur')
153
- await new Promise((resolve) => window.setTimeout(resolve, 100))
154
- expect(inputElement.element.value).toBe('2,00')
155
- expect(wrapper.props('modelValue')).toBe(2)
156
- })
157
- })
158
-
159
- describe('Step quantization', () => {
160
- it('should step correctly when using quantization via the step prop', async () => {
151
+ await inputElement.trigger("blur");
152
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
153
+ expect(inputElement.element.value).toBe("2,00");
154
+ expect(wrapper.props("modelValue")).toBe(2);
155
+ });
156
+ });
157
+
158
+ describe("Step quantization", () => {
159
+ it("should step correctly when using quantization via the step prop", async () => {
161
160
  const wrapper = mount(FzCurrencyInput, {
162
161
  props: {
163
- label: 'Label',
162
+ label: "Label",
164
163
  modelValue: 1,
165
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
164
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
166
165
  step: 4,
167
166
  },
168
- })
169
-
170
- const inputElement = wrapper.find('input')
171
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
172
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
173
-
174
- await inputElement.trigger('blur')
175
- await new Promise((resolve) => window.setTimeout(resolve, 100))
176
- expect(inputElement.element.value).toBe('1,00')
177
- await arrowUp.trigger('click')
178
- await wrapper.vm.$nextTick()
179
- await new Promise((resolve) => window.setTimeout(resolve, 150))
180
- expect(inputElement.element.value).toBe('5,00')
181
- await arrowDown.trigger('click')
182
- await wrapper.vm.$nextTick()
183
- await new Promise((resolve) => window.setTimeout(resolve, 150))
184
- expect(inputElement.element.value).toBe('1,00')
185
- await arrowDown.trigger('click')
186
- await wrapper.vm.$nextTick()
187
- await new Promise((resolve) => window.setTimeout(resolve, 150))
167
+ });
168
+
169
+ const inputElement = wrapper.find("input");
170
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
171
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
172
+
173
+ await inputElement.trigger("blur");
174
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
175
+ expect(inputElement.element.value).toBe("1,00");
176
+ await arrowUp.trigger("click");
177
+ await wrapper.vm.$nextTick();
178
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
179
+ expect(inputElement.element.value).toBe("5,00");
180
+ await arrowDown.trigger("click");
181
+ await wrapper.vm.$nextTick();
182
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
183
+ expect(inputElement.element.value).toBe("1,00");
184
+ await arrowDown.trigger("click");
185
+ await wrapper.vm.$nextTick();
186
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
188
187
  // Step down from 1 by 4 = -3 (no clamping applied in stepUpDown)
189
- expect(inputElement.element.value).toBe('-3,00')
190
- })
188
+ expect(inputElement.element.value).toBe("-3,00");
189
+ });
191
190
 
192
- it('should enforce quantization via the forceStep prop', async () => {
191
+ it("should enforce quantization via the forceStep prop", async () => {
193
192
  const wrapper = mount(FzCurrencyInput, {
194
193
  props: {
195
- label: 'Label',
194
+ label: "Label",
196
195
  modelValue: 8,
197
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
196
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
198
197
  step: 4,
199
198
  forceStep: true,
200
199
  },
201
- })
202
-
203
- const inputElement = wrapper.find('input')
204
- await inputElement.trigger('blur')
205
- await new Promise((resolve) => window.setTimeout(resolve, 100))
206
- expect(inputElement.element.value).toBe('8,00')
207
- await wrapper.setProps({ modelValue: 5 })
208
- await inputElement.trigger('blur')
209
- await new Promise((resolve) => window.setTimeout(resolve, 100))
210
- expect(inputElement.element.value).toBe('4,00')
211
- await wrapper.setProps({ modelValue: -7 })
212
- await inputElement.trigger('blur')
213
- await new Promise((resolve) => window.setTimeout(resolve, 100))
214
- expect(inputElement.element.value).toBe('-8,00')
215
- })
216
- })
217
-
218
- describe('Step controls', () => {
219
- it('should have step controls always visible with default step of 1', async () => {
200
+ });
201
+
202
+ const inputElement = wrapper.find("input");
203
+ await inputElement.trigger("blur");
204
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
205
+ expect(inputElement.element.value).toBe("8,00");
206
+ await wrapper.setProps({ modelValue: 5 });
207
+ await inputElement.trigger("blur");
208
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
209
+ expect(inputElement.element.value).toBe("4,00");
210
+ await wrapper.setProps({ modelValue: -7 });
211
+ await inputElement.trigger("blur");
212
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
213
+ expect(inputElement.element.value).toBe("-8,00");
214
+ });
215
+ });
216
+
217
+ describe("Step controls", () => {
218
+ it("should have step controls always visible with default step of 1", async () => {
220
219
  const wrapper = mount(FzCurrencyInput, {
221
220
  props: {
222
- label: 'Label',
221
+ label: "Label",
223
222
  modelValue: 10,
224
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
223
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
225
224
  },
226
- })
225
+ });
227
226
 
228
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
229
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
227
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
228
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
230
229
 
231
- expect(arrowUp.exists()).toBe(true)
232
- expect(arrowDown.exists()).toBe(true)
230
+ expect(arrowUp.exists()).toBe(true);
231
+ expect(arrowDown.exists()).toBe(true);
233
232
 
234
- const inputElement = wrapper.find('input')
235
- await inputElement.trigger('blur')
236
- await new Promise((resolve) => window.setTimeout(resolve, 100))
237
- expect(inputElement.element.value).toBe('10,00')
233
+ const inputElement = wrapper.find("input");
234
+ await inputElement.trigger("blur");
235
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
236
+ expect(inputElement.element.value).toBe("10,00");
238
237
 
239
- await arrowUp.trigger('click')
240
- await wrapper.vm.$nextTick()
241
- await new Promise((resolve) => window.setTimeout(resolve, 150))
242
- expect(inputElement.element.value).toBe('11,00')
238
+ await arrowUp.trigger("click");
239
+ await wrapper.vm.$nextTick();
240
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
241
+ expect(inputElement.element.value).toBe("11,00");
243
242
 
244
- await arrowDown.trigger('click')
245
- await wrapper.vm.$nextTick()
246
- await new Promise((resolve) => window.setTimeout(resolve, 150))
243
+ await arrowDown.trigger("click");
244
+ await wrapper.vm.$nextTick();
245
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
247
246
  // After arrowDown, value should be 10 (11 - 1)
248
- expect(inputElement.element.value).toBe('10,00')
249
- })
247
+ expect(inputElement.element.value).toBe("10,00");
248
+ });
250
249
 
251
- it('should use custom step value when provided', async () => {
250
+ it("should use custom step value when provided", async () => {
252
251
  const wrapper = mount(FzCurrencyInput, {
253
252
  props: {
254
- label: 'Label',
253
+ label: "Label",
255
254
  modelValue: 10,
256
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
255
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
257
256
  step: 5,
258
257
  },
259
- })
258
+ });
260
259
 
261
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
262
- const inputElement = wrapper.find('input')
260
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
261
+ const inputElement = wrapper.find("input");
263
262
 
264
- await inputElement.trigger('blur')
265
- await new Promise((resolve) => window.setTimeout(resolve, 100))
266
- expect(inputElement.element.value).toBe('10,00')
263
+ await inputElement.trigger("blur");
264
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
265
+ expect(inputElement.element.value).toBe("10,00");
267
266
 
268
- await arrowUp.trigger('click')
269
- await new Promise((resolve) => window.setTimeout(resolve, 100))
270
- expect(inputElement.element.value).toBe('15,00')
271
- })
272
- })
267
+ await arrowUp.trigger("click");
268
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
269
+ expect(inputElement.element.value).toBe("15,00");
270
+ });
271
+ });
273
272
 
274
- describe('Accessibility', () => {
275
- describe('Step controls accessibility', () => {
276
- it('should apply default aria-labels for step controls', async () => {
273
+ describe("Accessibility", () => {
274
+ describe("Step controls accessibility", () => {
275
+ it("should apply default aria-labels for step controls", async () => {
277
276
  const wrapper = mount(FzCurrencyInput, {
278
277
  props: {
279
- label: 'Label',
278
+ label: "Label",
280
279
  modelValue: 10,
281
280
  step: 2,
282
281
  },
283
- })
282
+ });
284
283
 
285
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
286
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
284
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
285
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
287
286
 
288
- expect(arrowUp.attributes('aria-label')).toBe('Incrementa di 2')
289
- expect(arrowDown.attributes('aria-label')).toBe('Decrementa di 2')
290
- expect(arrowUp.attributes('role')).toBe('button')
291
- expect(arrowDown.attributes('role')).toBe('button')
292
- expect(arrowUp.attributes('tabindex')).toBe('0')
293
- expect(arrowDown.attributes('tabindex')).toBe('0')
294
- })
287
+ expect(arrowUp.attributes("aria-label")).toBe("Incrementa di 2");
288
+ expect(arrowDown.attributes("aria-label")).toBe("Decrementa di 2");
289
+ expect(arrowUp.attributes("role")).toBe("button");
290
+ expect(arrowDown.attributes("role")).toBe("button");
291
+ expect(arrowUp.attributes("tabindex")).toBe("0");
292
+ expect(arrowDown.attributes("tabindex")).toBe("0");
293
+ });
295
294
 
296
- it('should use custom aria-labels when provided', async () => {
295
+ it("should use custom aria-labels when provided", async () => {
297
296
  const wrapper = mount(FzCurrencyInput, {
298
297
  props: {
299
- label: 'Label',
298
+ label: "Label",
300
299
  modelValue: 10,
301
300
  step: 2,
302
- stepUpAriaLabel: 'Custom increment',
303
- stepDownAriaLabel: 'Custom decrement',
301
+ stepUpAriaLabel: "Custom increment",
302
+ stepDownAriaLabel: "Custom decrement",
304
303
  },
305
- })
304
+ });
306
305
 
307
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
308
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
306
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
307
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
309
308
 
310
- expect(arrowUp.attributes('aria-label')).toBe('Custom increment')
311
- expect(arrowDown.attributes('aria-label')).toBe('Custom decrement')
312
- })
309
+ expect(arrowUp.attributes("aria-label")).toBe("Custom increment");
310
+ expect(arrowDown.attributes("aria-label")).toBe("Custom decrement");
311
+ });
313
312
 
314
- it('should disable step controls when disabled', async () => {
313
+ it("should disable step controls when disabled", async () => {
315
314
  const wrapper = mount(FzCurrencyInput, {
316
315
  props: {
317
- label: 'Label',
316
+ label: "Label",
318
317
  modelValue: 10,
319
318
  disabled: true,
320
319
  },
321
- })
320
+ });
322
321
 
323
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
324
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
322
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
323
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
325
324
 
326
- expect(arrowUp.attributes('aria-disabled')).toBe('true')
327
- expect(arrowDown.attributes('aria-disabled')).toBe('true')
328
- expect(arrowUp.attributes('tabindex')).toBeUndefined()
329
- expect(arrowDown.attributes('tabindex')).toBeUndefined()
330
- })
325
+ expect(arrowUp.attributes("aria-disabled")).toBe("true");
326
+ expect(arrowDown.attributes("aria-disabled")).toBe("true");
327
+ expect(arrowUp.attributes("tabindex")).toBeUndefined();
328
+ expect(arrowDown.attributes("tabindex")).toBeUndefined();
329
+ });
331
330
 
332
- it('should disable step controls when readonly', async () => {
331
+ it("should disable step controls when readonly", async () => {
333
332
  const wrapper = mount(FzCurrencyInput, {
334
333
  props: {
335
- label: 'Label',
334
+ label: "Label",
336
335
  modelValue: 10,
337
336
  readonly: true,
338
337
  },
339
- })
338
+ });
340
339
 
341
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
342
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
340
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
341
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
343
342
 
344
- expect(arrowUp.attributes('aria-disabled')).toBe('true')
345
- expect(arrowDown.attributes('aria-disabled')).toBe('true')
346
- expect(arrowUp.attributes('tabindex')).toBeUndefined()
347
- expect(arrowDown.attributes('tabindex')).toBeUndefined()
348
- })
343
+ expect(arrowUp.attributes("aria-disabled")).toBe("true");
344
+ expect(arrowDown.attributes("aria-disabled")).toBe("true");
345
+ expect(arrowUp.attributes("tabindex")).toBeUndefined();
346
+ expect(arrowDown.attributes("tabindex")).toBeUndefined();
347
+ });
349
348
 
350
- it('should trigger step on Enter key', async () => {
349
+ it("should trigger step on Enter key", async () => {
351
350
  const wrapper = mount(FzCurrencyInput, {
352
351
  props: {
353
- label: 'Label',
352
+ label: "Label",
354
353
  modelValue: 10,
355
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
354
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
356
355
  },
357
- })
356
+ });
358
357
 
359
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
360
- const inputElement = wrapper.find('input')
358
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
359
+ const inputElement = wrapper.find("input");
361
360
 
362
- await inputElement.trigger('blur')
363
- await new Promise((resolve) => window.setTimeout(resolve, 100))
364
- expect(inputElement.element.value).toBe('10,00')
361
+ await inputElement.trigger("blur");
362
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
363
+ expect(inputElement.element.value).toBe("10,00");
365
364
 
366
- await arrowUp.trigger('keydown', { key: 'Enter' })
367
- await new Promise((resolve) => window.setTimeout(resolve, 100))
368
- expect(inputElement.element.value).toBe('11,00')
369
- })
365
+ await arrowUp.trigger("keydown", { key: "Enter" });
366
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
367
+ expect(inputElement.element.value).toBe("11,00");
368
+ });
370
369
 
371
- it('should trigger step on Space key', async () => {
370
+ it("should trigger step on Space key", async () => {
372
371
  const wrapper = mount(FzCurrencyInput, {
373
372
  props: {
374
- label: 'Label',
373
+ label: "Label",
375
374
  modelValue: 10,
376
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
375
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
377
376
  },
378
- })
377
+ });
379
378
 
380
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
381
- const inputElement = wrapper.find('input')
379
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
380
+ const inputElement = wrapper.find("input");
382
381
 
383
- await inputElement.trigger('blur')
384
- await new Promise((resolve) => window.setTimeout(resolve, 100))
385
- expect(inputElement.element.value).toBe('10,00')
382
+ await inputElement.trigger("blur");
383
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
384
+ expect(inputElement.element.value).toBe("10,00");
386
385
 
387
- await arrowDown.trigger('keydown', { key: ' ' })
388
- await new Promise((resolve) => window.setTimeout(resolve, 100))
389
- expect(inputElement.element.value).toBe('9,00')
390
- })
391
- })
386
+ await arrowDown.trigger("keydown", { key: " " });
387
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
388
+ expect(inputElement.element.value).toBe("9,00");
389
+ });
390
+ });
392
391
 
393
- describe('Valid icon accessibility', () => {
394
- it('should display valid icon with aria-hidden when valid is true', async () => {
392
+ describe("Valid icon accessibility", () => {
393
+ it("should display valid icon with aria-hidden when valid is true", async () => {
395
394
  const wrapper = mount(FzCurrencyInput, {
396
395
  props: {
397
- label: 'Label',
396
+ label: "Label",
398
397
  modelValue: 10,
399
398
  valid: true,
400
399
  },
401
- })
400
+ });
402
401
 
403
- const validIcon = wrapper.find('.fa-check')
404
- expect(validIcon.exists()).toBe(true)
405
- expect(validIcon.attributes('aria-hidden')).toBe('true')
406
- })
402
+ const validIcon = wrapper.find(".fa-check");
403
+ expect(validIcon.exists()).toBe(true);
404
+ expect(validIcon.attributes("aria-hidden")).toBe("true");
405
+ });
407
406
 
408
- it('should display valid icon alongside step controls', async () => {
407
+ it("should display valid icon alongside step controls", async () => {
409
408
  const wrapper = mount(FzCurrencyInput, {
410
409
  props: {
411
- label: 'Label',
410
+ label: "Label",
412
411
  modelValue: 10,
413
412
  valid: true,
414
413
  step: 2,
415
414
  },
416
- })
415
+ });
417
416
 
418
- const validIcon = wrapper.find('.fa-check')
419
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
420
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
417
+ const validIcon = wrapper.find(".fa-check");
418
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
419
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
421
420
 
422
- expect(validIcon.exists()).toBe(true)
423
- expect(arrowUp.exists()).toBe(true)
424
- expect(arrowDown.exists()).toBe(true)
425
- })
426
- })
427
- })
421
+ expect(validIcon.exists()).toBe(true);
422
+ expect(arrowUp.exists()).toBe(true);
423
+ expect(arrowDown.exists()).toBe(true);
424
+ });
425
+ });
426
+ });
428
427
 
429
- describe('v-model retrocompatibility', () => {
430
- it('should accept number values', async () => {
428
+ describe("v-model retrocompatibility", () => {
429
+ it("should accept number values", async () => {
431
430
  const wrapper = mount(FzCurrencyInput, {
432
431
  props: {
433
- label: 'Label',
432
+ label: "Label",
434
433
  modelValue: 123.45,
435
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
434
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
436
435
  },
437
- })
436
+ });
438
437
 
439
- const inputElement = wrapper.find('input')
440
- await inputElement.trigger('blur')
441
- await new Promise((resolve) => window.setTimeout(resolve, 100))
442
- expect(inputElement.element.value).toBe('123,45')
443
- })
438
+ const inputElement = wrapper.find("input");
439
+ await inputElement.trigger("blur");
440
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
441
+ expect(inputElement.element.value).toBe("123,45");
442
+ });
444
443
 
445
- it('should accept string values with console.warn', async () => {
446
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
444
+ it("should accept string values with console.warn", async () => {
445
+ const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
447
446
 
448
- let modelValue: number | string | undefined = '123.45'
449
- let wrapper: ReturnType<typeof mount> | null = null
447
+ let modelValue: number | string | undefined = "123.45";
448
+ let wrapper: ReturnType<typeof mount> | null = null;
450
449
  wrapper = mount(FzCurrencyInput, {
451
450
  props: {
452
- label: 'Label',
451
+ label: "Label",
453
452
  modelValue,
454
- 'onUpdate:modelValue': (e) => {
455
- modelValue = e as number
456
- if (wrapper) wrapper.setProps({ modelValue })
453
+ "onUpdate:modelValue": (e) => {
454
+ modelValue = e as number;
455
+ if (wrapper) wrapper.setProps({ modelValue });
457
456
  },
458
457
  },
459
- })
458
+ });
460
459
 
461
- await wrapper.vm.$nextTick()
462
- await new Promise((resolve) => window.setTimeout(resolve, 100))
460
+ await wrapper.vm.$nextTick();
461
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
463
462
 
464
463
  expect(consoleSpy).toHaveBeenCalledWith(
465
- expect.stringContaining('[FzCurrencyInput] String values in v-model are deprecated')
466
- )
464
+ expect.stringContaining(
465
+ "[FzCurrencyInput] String values in v-model are deprecated",
466
+ ),
467
+ );
467
468
 
468
- const inputElement = wrapper.find('input')
469
- await inputElement.trigger('blur')
470
- await new Promise((resolve) => window.setTimeout(resolve, 100))
471
- expect(inputElement.element.value).toBe('123,45')
469
+ const inputElement = wrapper.find("input");
470
+ await inputElement.trigger("blur");
471
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
472
+ expect(inputElement.element.value).toBe("123,45");
472
473
 
473
- consoleSpy.mockRestore()
474
- })
474
+ consoleSpy.mockRestore();
475
+ });
475
476
 
476
- it('should accept undefined values', async () => {
477
+ it("should accept undefined values", async () => {
477
478
  const wrapper = mount(FzCurrencyInput, {
478
479
  props: {
479
- label: 'Label',
480
+ label: "Label",
480
481
  modelValue: undefined,
481
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
482
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
482
483
  },
483
- })
484
+ });
484
485
 
485
- const inputElement = wrapper.find('input')
486
- expect(inputElement.element.value).toBe('')
487
- })
486
+ const inputElement = wrapper.find("input");
487
+ expect(inputElement.element.value).toBe("");
488
+ });
488
489
 
489
- it('should emit number | undefined only', async () => {
490
- let emittedValue: number | undefined | string
490
+ it("should emit number | undefined only", async () => {
491
+ let emittedValue: number | undefined | string;
491
492
 
492
493
  const wrapper = mount(FzCurrencyInput, {
493
494
  props: {
494
- label: 'Label',
495
+ label: "Label",
495
496
  modelValue: undefined,
496
- 'onUpdate:modelValue': (e: number | string | null | undefined) => {
497
- emittedValue = e as number | undefined
498
- wrapper.setProps({ modelValue: e })
499
- },
497
+ "onUpdate:modelValue": (e: number | string | null | undefined) => {
498
+ emittedValue = e as number | undefined;
499
+ wrapper.setProps({ modelValue: e });
500
+ },
500
501
  },
501
- })
502
+ });
502
503
 
503
- const inputElement = wrapper.find('input')
504
- await inputElement.setValue('123.45')
505
- await inputElement.trigger('input')
506
- await new Promise((resolve) => window.setTimeout(resolve, 100))
504
+ const inputElement = wrapper.find("input");
505
+ await inputElement.setValue("123.45");
506
+ await inputElement.trigger("input");
507
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
507
508
 
508
- expect(typeof emittedValue).toBe('number')
509
- expect(emittedValue).toBe(123.45)
510
- })
509
+ expect(typeof emittedValue).toBe("number");
510
+ expect(emittedValue).toBe(123.45);
511
+ });
511
512
 
512
- it('should handle string values with comma as decimal separator (Italian format)', async () => {
513
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
513
+ it("should handle string values with comma as decimal separator (Italian format)", async () => {
514
+ const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
514
515
 
515
- let modelValue: number | string | undefined = '1234,56'
516
- let wrapper: ReturnType<typeof mount> | null = null
516
+ let modelValue: number | string | undefined = "1234,56";
517
+ let wrapper: ReturnType<typeof mount> | null = null;
517
518
  wrapper = mount(FzCurrencyInput, {
518
519
  props: {
519
- label: 'Label',
520
+ label: "Label",
520
521
  modelValue,
521
- 'onUpdate:modelValue': (e) => {
522
- modelValue = e as number
523
- if (wrapper) wrapper.setProps({ modelValue })
522
+ "onUpdate:modelValue": (e) => {
523
+ modelValue = e as number;
524
+ if (wrapper) wrapper.setProps({ modelValue });
524
525
  },
525
526
  },
526
- })
527
+ });
527
528
 
528
- await wrapper.vm.$nextTick()
529
- await new Promise((resolve) => window.setTimeout(resolve, 100))
529
+ await wrapper.vm.$nextTick();
530
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
530
531
 
531
- expect(consoleSpy).toHaveBeenCalled()
532
- const inputElement = wrapper.find('input')
533
- await inputElement.trigger('blur')
534
- await new Promise((resolve) => window.setTimeout(resolve, 100))
532
+ expect(consoleSpy).toHaveBeenCalled();
533
+ const inputElement = wrapper.find("input");
534
+ await inputElement.trigger("blur");
535
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
535
536
  // String with comma as decimal separator should be parsed correctly and formatted with thousand separators
536
- expect(inputElement.element.value).toBe('1.234,56')
537
+ expect(inputElement.element.value).toBe("1.234,56");
537
538
 
538
- consoleSpy.mockRestore()
539
- })
539
+ consoleSpy.mockRestore();
540
+ });
540
541
 
541
- it('should handle string values that would fail with Number() or parseInt/parseFloat', async () => {
542
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
542
+ it("should handle string values that would fail with Number() or parseInt/parseFloat", async () => {
543
+ const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
543
544
 
544
545
  // Simulate the scenario: data[`${firstValue}_amount`] is a string like "1234,56"
545
546
  // Number("1234,56") would return NaN, parseFloat would return NaN,
546
547
  // parseInt would return 1234 (incorrect, stops at comma)
547
548
  // But the component should handle it correctly using its parse function
548
- const stringValue = '1234,56'
549
-
549
+ const stringValue = "1234,56";
550
+
550
551
  // Verify that Number() fails with this format (returns NaN)
551
- expect(Number(stringValue)).toBeNaN()
552
+ expect(Number(stringValue)).toBeNaN();
552
553
  // parseInt and parseFloat stop at comma, returning only the integer part (incorrect)
553
- expect(parseInt(stringValue)).toBe(1234)
554
- expect(parseFloat(stringValue)).toBe(1234)
554
+ expect(parseInt(stringValue)).toBe(1234);
555
+ expect(parseFloat(stringValue)).toBe(1234);
555
556
 
556
- let modelValue: number | string | undefined = stringValue
557
- let wrapper: ReturnType<typeof mount> | null = null
557
+ let modelValue: number | string | undefined = stringValue;
558
+ let wrapper: ReturnType<typeof mount> | null = null;
558
559
  wrapper = mount(FzCurrencyInput, {
559
560
  props: {
560
- label: 'Label',
561
+ label: "Label",
561
562
  modelValue,
562
- 'onUpdate:modelValue': (e) => {
563
- modelValue = e as number
564
- if (wrapper) wrapper.setProps({ modelValue })
563
+ "onUpdate:modelValue": (e) => {
564
+ modelValue = e as number;
565
+ if (wrapper) wrapper.setProps({ modelValue });
565
566
  },
566
567
  },
567
- })
568
+ });
568
569
 
569
- await wrapper.vm.$nextTick()
570
- await new Promise((resolve) => window.setTimeout(resolve, 100))
570
+ await wrapper.vm.$nextTick();
571
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
571
572
 
572
- expect(consoleSpy).toHaveBeenCalled()
573
- const inputElement = wrapper.find('input')
574
- await inputElement.trigger('blur')
575
- await new Promise((resolve) => window.setTimeout(resolve, 100))
573
+ expect(consoleSpy).toHaveBeenCalled();
574
+ const inputElement = wrapper.find("input");
575
+ await inputElement.trigger("blur");
576
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
576
577
  // Component should parse and display the value correctly despite Number()/parseFloat failing
577
578
  // The component's parse function replaces comma with dot, so "1234,56" becomes 1234.56
578
579
  // Then formats with thousand separators
579
- expect(inputElement.element.value).toBe('1.234,56')
580
+ expect(inputElement.element.value).toBe("1.234,56");
580
581
 
581
- consoleSpy.mockRestore()
582
- })
582
+ consoleSpy.mockRestore();
583
+ });
583
584
 
584
585
  it('should correctly parse Italian format string "1.234,56" with thousand separators', async () => {
585
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
586
+ const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
586
587
 
587
588
  // Test the exact scenario: "1.234,56" should become 1234.56
588
589
  // Process: "1.234,56" → remove dots → "1234,56" → replace comma → "1234.56" → 1234.56
589
- const stringValue = '1.234,56'
590
-
590
+ const stringValue = "1.234,56";
591
+
591
592
  // Verify that Number() and parseFloat fail with this format
592
- expect(Number(stringValue)).toBeNaN()
593
- expect(parseFloat(stringValue)).toBe(1.234) // Stops at second dot
593
+ expect(Number(stringValue)).toBeNaN();
594
+ expect(parseFloat(stringValue)).toBe(1.234); // Stops at second dot
594
595
 
595
- let modelValue: number | string | undefined = stringValue
596
- let wrapper: ReturnType<typeof mount> | null = null
596
+ let modelValue: number | string | undefined = stringValue;
597
+ let wrapper: ReturnType<typeof mount> | null = null;
597
598
  wrapper = mount(FzCurrencyInput, {
598
599
  props: {
599
- label: 'Label',
600
+ label: "Label",
600
601
  modelValue,
601
- 'onUpdate:modelValue': (e) => {
602
- modelValue = e as number
603
- if (wrapper) wrapper.setProps({ modelValue })
602
+ "onUpdate:modelValue": (e) => {
603
+ modelValue = e as number;
604
+ if (wrapper) wrapper.setProps({ modelValue });
604
605
  },
605
606
  },
606
- })
607
+ });
607
608
 
608
- await wrapper.vm.$nextTick()
609
- await new Promise((resolve) => window.setTimeout(resolve, 100))
609
+ await wrapper.vm.$nextTick();
610
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
610
611
 
611
- expect(consoleSpy).toHaveBeenCalled()
612
- const inputElement = wrapper.find('input')
613
- await inputElement.trigger('blur')
614
- await new Promise((resolve) => window.setTimeout(resolve, 100))
612
+ expect(consoleSpy).toHaveBeenCalled();
613
+ const inputElement = wrapper.find("input");
614
+ await inputElement.trigger("blur");
615
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
615
616
  // Should be formatted as "1.234,56" (Italian format with thousand separators)
616
- expect(inputElement.element.value).toBe('1.234,56')
617
+ expect(inputElement.element.value).toBe("1.234,56");
617
618
  // Verify the numeric value in v-model is correct (1234.56)
618
619
  // modelValue should be a number after parsing
619
- expect(typeof modelValue).toBe('number')
620
- expect(modelValue).toBeCloseTo(1234.56, 2)
621
-
622
- consoleSpy.mockRestore()
623
- })
624
- })
625
-
626
- describe('Edge cases', () => {
627
- describe('String values', () => {
628
- it('should handle string with non-numeric characters', async () => {
629
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
630
-
631
- let modelValue: number | string | undefined = 'abc123'
632
- let wrapper: ReturnType<typeof mount> | null = null
620
+ expect(typeof modelValue).toBe("number");
621
+ expect(modelValue).toBeCloseTo(1234.56, 2);
622
+
623
+ consoleSpy.mockRestore();
624
+ });
625
+ });
626
+
627
+ describe("Edge cases", () => {
628
+ describe("String values", () => {
629
+ it("should handle string with non-numeric characters", async () => {
630
+ const consoleSpy = vi
631
+ .spyOn(console, "warn")
632
+ .mockImplementation(() => {});
633
+
634
+ let modelValue: number | string | undefined = "abc123";
635
+ let wrapper: ReturnType<typeof mount> | null = null;
633
636
  wrapper = mount(FzCurrencyInput, {
634
637
  props: {
635
- label: 'Label',
638
+ label: "Label",
636
639
  modelValue,
637
- 'onUpdate:modelValue': (e) => {
638
- modelValue = e as number
639
- if (wrapper) wrapper.setProps({ modelValue })
640
+ "onUpdate:modelValue": (e) => {
641
+ modelValue = e as number;
642
+ if (wrapper) wrapper.setProps({ modelValue });
640
643
  },
641
644
  },
642
- })
645
+ });
643
646
 
644
- await wrapper.vm.$nextTick()
645
- await new Promise((resolve) => window.setTimeout(resolve, 100))
647
+ await wrapper.vm.$nextTick();
648
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
646
649
 
647
- expect(consoleSpy).toHaveBeenCalled()
648
- const inputElement = wrapper.find('input')
649
- await inputElement.trigger('blur')
650
- await new Promise((resolve) => window.setTimeout(resolve, 100))
650
+ expect(consoleSpy).toHaveBeenCalled();
651
+ const inputElement = wrapper.find("input");
652
+ await inputElement.trigger("blur");
653
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
651
654
  // Non-numeric string should result in empty (undefined)
652
- expect(inputElement.element.value).toBe('')
655
+ expect(inputElement.element.value).toBe("");
653
656
 
654
- consoleSpy.mockRestore()
655
- })
657
+ consoleSpy.mockRestore();
658
+ });
656
659
 
657
- it('should handle string with only non-numeric characters', async () => {
658
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
660
+ it("should handle string with only non-numeric characters", async () => {
661
+ const consoleSpy = vi
662
+ .spyOn(console, "warn")
663
+ .mockImplementation(() => {});
659
664
 
660
- let modelValue: number | string | undefined = 'abc'
661
- let wrapper: ReturnType<typeof mount> | null = null
665
+ let modelValue: number | string | undefined = "abc";
666
+ let wrapper: ReturnType<typeof mount> | null = null;
662
667
  wrapper = mount(FzCurrencyInput, {
663
668
  props: {
664
- label: 'Label',
669
+ label: "Label",
665
670
  modelValue,
666
- 'onUpdate:modelValue': (e) => {
667
- modelValue = e as number
668
- if (wrapper) wrapper.setProps({ modelValue })
671
+ "onUpdate:modelValue": (e) => {
672
+ modelValue = e as number;
673
+ if (wrapper) wrapper.setProps({ modelValue });
669
674
  },
670
675
  },
671
- })
676
+ });
672
677
 
673
- await wrapper.vm.$nextTick()
674
- await new Promise((resolve) => window.setTimeout(resolve, 100))
678
+ await wrapper.vm.$nextTick();
679
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
675
680
 
676
- expect(consoleSpy).toHaveBeenCalled()
677
- const inputElement = wrapper.find('input')
678
- await inputElement.trigger('blur')
679
- await new Promise((resolve) => window.setTimeout(resolve, 100))
681
+ expect(consoleSpy).toHaveBeenCalled();
682
+ const inputElement = wrapper.find("input");
683
+ await inputElement.trigger("blur");
684
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
680
685
  // Non-numeric string should result in empty (undefined)
681
- expect(inputElement.element.value).toBe('')
686
+ expect(inputElement.element.value).toBe("");
682
687
 
683
- consoleSpy.mockRestore()
684
- })
688
+ consoleSpy.mockRestore();
689
+ });
685
690
 
686
- it('should handle string with negative sign', async () => {
687
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
691
+ it("should handle string with negative sign", async () => {
692
+ const consoleSpy = vi
693
+ .spyOn(console, "warn")
694
+ .mockImplementation(() => {});
688
695
 
689
- let modelValue: number | string | undefined = '-123.45'
690
- let wrapper: ReturnType<typeof mount> | null = null
696
+ let modelValue: number | string | undefined = "-123.45";
697
+ let wrapper: ReturnType<typeof mount> | null = null;
691
698
  wrapper = mount(FzCurrencyInput, {
692
699
  props: {
693
- label: 'Label',
700
+ label: "Label",
694
701
  modelValue,
695
- 'onUpdate:modelValue': (e) => {
696
- modelValue = e as number
697
- if (wrapper) wrapper.setProps({ modelValue })
702
+ "onUpdate:modelValue": (e) => {
703
+ modelValue = e as number;
704
+ if (wrapper) wrapper.setProps({ modelValue });
698
705
  },
699
706
  },
700
- })
707
+ });
701
708
 
702
- await wrapper.vm.$nextTick()
703
- await new Promise((resolve) => window.setTimeout(resolve, 100))
709
+ await wrapper.vm.$nextTick();
710
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
704
711
 
705
- expect(consoleSpy).toHaveBeenCalled()
706
- const inputElement = wrapper.find('input')
707
- await inputElement.trigger('blur')
708
- await new Promise((resolve) => window.setTimeout(resolve, 100))
709
- expect(inputElement.element.value).toBe('-123,45')
712
+ expect(consoleSpy).toHaveBeenCalled();
713
+ const inputElement = wrapper.find("input");
714
+ await inputElement.trigger("blur");
715
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
716
+ expect(inputElement.element.value).toBe("-123,45");
710
717
 
711
- consoleSpy.mockRestore()
712
- })
718
+ consoleSpy.mockRestore();
719
+ });
713
720
 
714
- it('should handle string with thousand separators', async () => {
715
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
721
+ it("should handle string with thousand separators", async () => {
722
+ const consoleSpy = vi
723
+ .spyOn(console, "warn")
724
+ .mockImplementation(() => {});
716
725
 
717
- let modelValue: number | string | undefined = '1.234.567,89'
718
- let wrapper: ReturnType<typeof mount> | null = null
726
+ let modelValue: number | string | undefined = "1.234.567,89";
727
+ let wrapper: ReturnType<typeof mount> | null = null;
719
728
  wrapper = mount(FzCurrencyInput, {
720
729
  props: {
721
- label: 'Label',
730
+ label: "Label",
722
731
  modelValue,
723
- 'onUpdate:modelValue': (e) => {
724
- modelValue = e as number
725
- if (wrapper) wrapper.setProps({ modelValue })
732
+ "onUpdate:modelValue": (e) => {
733
+ modelValue = e as number;
734
+ if (wrapper) wrapper.setProps({ modelValue });
726
735
  },
727
736
  },
728
- })
737
+ });
729
738
 
730
- await wrapper.vm.$nextTick()
731
- await new Promise((resolve) => window.setTimeout(resolve, 100))
739
+ await wrapper.vm.$nextTick();
740
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
732
741
 
733
- expect(consoleSpy).toHaveBeenCalled()
734
- const inputElement = wrapper.find('input')
735
- await inputElement.trigger('blur')
736
- await new Promise((resolve) => window.setTimeout(resolve, 100))
742
+ expect(consoleSpy).toHaveBeenCalled();
743
+ const inputElement = wrapper.find("input");
744
+ await inputElement.trigger("blur");
745
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
737
746
  // Should be formatted with thousand separators and preserve original decimals
738
- expect(inputElement.element.value).toBe('1.234.567,89')
747
+ expect(inputElement.element.value).toBe("1.234.567,89");
739
748
  // Verify the numeric value in v-model is correct
740
- expect(typeof modelValue).toBe('number')
741
- expect(modelValue).toBeCloseTo(1234567.89, 2)
749
+ expect(typeof modelValue).toBe("number");
750
+ expect(modelValue).toBeCloseTo(1234567.89, 2);
742
751
 
743
- consoleSpy.mockRestore()
744
- })
752
+ consoleSpy.mockRestore();
753
+ });
745
754
 
746
- it('should handle empty string', async () => {
747
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
755
+ it("should handle empty string", async () => {
756
+ const consoleSpy = vi
757
+ .spyOn(console, "warn")
758
+ .mockImplementation(() => {});
748
759
 
749
- let modelValue: number | string | undefined = ''
750
- let wrapper: ReturnType<typeof mount> | null = null
760
+ let modelValue: number | string | undefined = "";
761
+ let wrapper: ReturnType<typeof mount> | null = null;
751
762
  wrapper = mount(FzCurrencyInput, {
752
763
  props: {
753
- label: 'Label',
764
+ label: "Label",
754
765
  modelValue,
755
- 'onUpdate:modelValue': (e) => {
756
- modelValue = e as number
757
- if (wrapper) wrapper.setProps({ modelValue })
766
+ "onUpdate:modelValue": (e) => {
767
+ modelValue = e as number;
768
+ if (wrapper) wrapper.setProps({ modelValue });
758
769
  },
759
770
  },
760
- })
771
+ });
761
772
 
762
- await wrapper.vm.$nextTick()
763
- await new Promise((resolve) => window.setTimeout(resolve, 100))
773
+ await wrapper.vm.$nextTick();
774
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
764
775
 
765
- const inputElement = wrapper.find('input')
766
- expect(inputElement.element.value).toBe('')
776
+ const inputElement = wrapper.find("input");
777
+ expect(inputElement.element.value).toBe("");
767
778
 
768
- consoleSpy.mockRestore()
769
- })
779
+ consoleSpy.mockRestore();
780
+ });
770
781
 
771
- it('should handle string with only whitespace', async () => {
772
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
782
+ it("should handle string with only whitespace", async () => {
783
+ const consoleSpy = vi
784
+ .spyOn(console, "warn")
785
+ .mockImplementation(() => {});
773
786
 
774
- let modelValue: number | string | undefined = ' '
775
- let wrapper: ReturnType<typeof mount> | null = null
787
+ let modelValue: number | string | undefined = " ";
788
+ let wrapper: ReturnType<typeof mount> | null = null;
776
789
  wrapper = mount(FzCurrencyInput, {
777
790
  props: {
778
- label: 'Label',
791
+ label: "Label",
779
792
  modelValue,
780
- 'onUpdate:modelValue': (e) => {
781
- modelValue = e as number
782
- if (wrapper) wrapper.setProps({ modelValue })
793
+ "onUpdate:modelValue": (e) => {
794
+ modelValue = e as number;
795
+ if (wrapper) wrapper.setProps({ modelValue });
783
796
  },
784
797
  },
785
- })
798
+ });
786
799
 
787
- await wrapper.vm.$nextTick()
788
- await new Promise((resolve) => window.setTimeout(resolve, 100))
800
+ await wrapper.vm.$nextTick();
801
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
789
802
 
790
- const inputElement = wrapper.find('input')
791
- await inputElement.trigger('blur')
792
- await new Promise((resolve) => window.setTimeout(resolve, 100))
803
+ const inputElement = wrapper.find("input");
804
+ await inputElement.trigger("blur");
805
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
793
806
  // Whitespace-only string should result in empty (undefined)
794
- expect(inputElement.element.value).toBe('')
807
+ expect(inputElement.element.value).toBe("");
795
808
 
796
- consoleSpy.mockRestore()
797
- })
798
- })
809
+ consoleSpy.mockRestore();
810
+ });
811
+ });
799
812
 
800
- describe('Null values', () => {
801
- it('should handle null value', async () => {
802
- let modelValue: number | null | undefined = null
803
- let wrapper: ReturnType<typeof mount> | null = null
813
+ describe("Null values", () => {
814
+ it("should handle null value", async () => {
815
+ let modelValue: number | null | undefined = null;
816
+ let wrapper: ReturnType<typeof mount> | null = null;
804
817
  wrapper = mount(FzCurrencyInput, {
805
818
  props: {
806
- label: 'Label',
819
+ label: "Label",
807
820
  modelValue,
808
- 'onUpdate:modelValue': (e) => {
809
- modelValue = e as number | null | undefined
810
- if (wrapper) wrapper.setProps({ modelValue })
821
+ "onUpdate:modelValue": (e) => {
822
+ modelValue = e as number | null | undefined;
823
+ if (wrapper) wrapper.setProps({ modelValue });
811
824
  },
812
825
  },
813
- })
826
+ });
814
827
 
815
- const inputElement = wrapper.find('input')
816
- await inputElement.trigger('blur')
817
- await new Promise((resolve) => window.setTimeout(resolve, 100))
818
- expect(inputElement.element.value).toBe('')
819
- })
828
+ const inputElement = wrapper.find("input");
829
+ await inputElement.trigger("blur");
830
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
831
+ expect(inputElement.element.value).toBe("");
832
+ });
820
833
 
821
- it('should handle nullOnEmpty prop', async () => {
822
- let modelValue: number | null | undefined = undefined
823
- let wrapper: ReturnType<typeof mount> | null = null
834
+ it("should handle nullOnEmpty prop", async () => {
835
+ let modelValue: number | null | undefined = undefined;
836
+ let wrapper: ReturnType<typeof mount> | null = null;
824
837
  wrapper = mount(FzCurrencyInput, {
825
838
  props: {
826
- label: 'Label',
839
+ label: "Label",
827
840
  modelValue,
828
841
  nullOnEmpty: true,
829
- 'onUpdate:modelValue': (e) => {
830
- modelValue = e as number | null | undefined
831
- if (wrapper) wrapper.setProps({ modelValue })
842
+ "onUpdate:modelValue": (e) => {
843
+ modelValue = e as number | null | undefined;
844
+ if (wrapper) wrapper.setProps({ modelValue });
832
845
  },
833
846
  },
834
- })
847
+ });
835
848
 
836
- const inputElement = wrapper.find('input')
837
- await inputElement.setValue('')
838
- await inputElement.trigger('blur')
839
- await new Promise((resolve) => window.setTimeout(resolve, 100))
849
+ const inputElement = wrapper.find("input");
850
+ await inputElement.setValue("");
851
+ await inputElement.trigger("blur");
852
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
840
853
  // With nullOnEmpty, empty should emit null (but display as empty)
841
- expect(inputElement.element.value).toBe('')
842
- })
854
+ expect(inputElement.element.value).toBe("");
855
+ });
843
856
 
844
- it('should preserve zero value when nullOnEmpty is enabled', async () => {
845
- let emittedValue: number | undefined
846
- let modelValue: number | null | undefined = undefined
847
- let wrapper: ReturnType<typeof mount> | null = null
857
+ it("should preserve zero value when nullOnEmpty is enabled", async () => {
858
+ let emittedValue: number | undefined;
859
+ let modelValue: number | null | undefined = undefined;
860
+ let wrapper: ReturnType<typeof mount> | null = null;
848
861
  wrapper = mount(FzCurrencyInput, {
849
862
  props: {
850
- label: 'Label',
863
+ label: "Label",
851
864
  modelValue,
852
865
  nullOnEmpty: true,
853
- 'onUpdate:modelValue': (e: number | string | null | undefined) => {
854
- emittedValue = e as number | undefined
855
- modelValue = e as number | null | undefined
856
- if (wrapper) wrapper.setProps({ modelValue })
866
+ "onUpdate:modelValue": (e: number | string | null | undefined) => {
867
+ emittedValue = e as number | undefined;
868
+ modelValue = e as number | null | undefined;
869
+ if (wrapper) wrapper.setProps({ modelValue });
857
870
  },
858
871
  },
859
- })
872
+ });
873
+
874
+ const inputElement = wrapper.find("input");
860
875
 
861
- const inputElement = wrapper.find('input')
862
-
863
876
  // Set "0" - should remain 0, not become null
864
- await inputElement.trigger('focus')
865
- await inputElement.setValue('0')
866
- await inputElement.trigger('blur')
867
- await new Promise((resolve) => window.setTimeout(resolve, 100))
868
-
869
- expect(inputElement.element.value).toBe('0,00')
870
- expect(emittedValue).toBe(0)
871
- expect(emittedValue).not.toBeNull()
872
- })
873
-
874
- it('should preserve zero value with separators when nullOnEmpty is enabled', async () => {
875
- let emittedValue: number | undefined
876
- let modelValue: number | null | undefined = undefined
877
- let wrapper: ReturnType<typeof mount> | null = null
877
+ await inputElement.trigger("focus");
878
+ await inputElement.setValue("0");
879
+ await inputElement.trigger("blur");
880
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
881
+
882
+ expect(inputElement.element.value).toBe("0,00");
883
+ expect(emittedValue).toBe(0);
884
+ expect(emittedValue).not.toBeNull();
885
+ });
886
+
887
+ it("should preserve zero value with separators when nullOnEmpty is enabled", async () => {
888
+ let emittedValue: number | undefined;
889
+ let modelValue: number | null | undefined = undefined;
890
+ let wrapper: ReturnType<typeof mount> | null = null;
878
891
  wrapper = mount(FzCurrencyInput, {
879
892
  props: {
880
- label: 'Label',
893
+ label: "Label",
881
894
  modelValue,
882
895
  nullOnEmpty: true,
883
- 'onUpdate:modelValue': (e: number | string | null | undefined) => {
884
- emittedValue = e as number | undefined
885
- modelValue = e as number | null | undefined
886
- if (wrapper) wrapper.setProps({ modelValue })
896
+ "onUpdate:modelValue": (e: number | string | null | undefined) => {
897
+ emittedValue = e as number | undefined;
898
+ modelValue = e as number | null | undefined;
899
+ if (wrapper) wrapper.setProps({ modelValue });
887
900
  },
888
901
  },
889
- })
902
+ });
903
+
904
+ const inputElement = wrapper.find("input");
890
905
 
891
- const inputElement = wrapper.find('input')
892
-
893
906
  // Set "0,00" - should remain 0, not become null
894
- await inputElement.trigger('focus')
895
- await inputElement.setValue('0,00')
896
- await inputElement.trigger('blur')
897
- await new Promise((resolve) => window.setTimeout(resolve, 100))
898
-
899
- expect(inputElement.element.value).toBe('0,00')
900
- expect(emittedValue).toBe(0)
901
- expect(emittedValue).not.toBeNull()
902
- })
903
- })
904
-
905
- describe('Negative values', () => {
906
- it('should handle negative number in v-model', async () => {
907
+ await inputElement.trigger("focus");
908
+ await inputElement.setValue("0,00");
909
+ await inputElement.trigger("blur");
910
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
911
+
912
+ expect(inputElement.element.value).toBe("0,00");
913
+ expect(emittedValue).toBe(0);
914
+ expect(emittedValue).not.toBeNull();
915
+ });
916
+ });
917
+
918
+ describe("Negative values", () => {
919
+ it("should handle negative number in v-model", async () => {
907
920
  const wrapper = mount(FzCurrencyInput, {
908
921
  props: {
909
- label: 'Label',
922
+ label: "Label",
910
923
  modelValue: -123.45,
911
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
924
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
912
925
  },
913
- })
926
+ });
914
927
 
915
- const inputElement = wrapper.find('input')
916
- await inputElement.trigger('blur')
917
- await new Promise((resolve) => window.setTimeout(resolve, 100))
918
- expect(inputElement.element.value).toBe('-123,45')
919
- })
928
+ const inputElement = wrapper.find("input");
929
+ await inputElement.trigger("blur");
930
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
931
+ expect(inputElement.element.value).toBe("-123,45");
932
+ });
920
933
 
921
- it('should handle negative values with step controls', async () => {
934
+ it("should handle negative values with step controls", async () => {
922
935
  const wrapper = mount(FzCurrencyInput, {
923
936
  props: {
924
- label: 'Label',
937
+ label: "Label",
925
938
  modelValue: -10,
926
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
939
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
927
940
  step: 5,
928
941
  },
929
- })
942
+ });
930
943
 
931
- const inputElement = wrapper.find('input')
932
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
933
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
944
+ const inputElement = wrapper.find("input");
945
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
946
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
934
947
 
935
- await inputElement.trigger('blur')
936
- await new Promise((resolve) => window.setTimeout(resolve, 100))
937
- expect(inputElement.element.value).toBe('-10,00')
948
+ await inputElement.trigger("blur");
949
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
950
+ expect(inputElement.element.value).toBe("-10,00");
938
951
 
939
- await arrowUp.trigger('click')
940
- await wrapper.vm.$nextTick()
941
- await new Promise((resolve) => window.setTimeout(resolve, 150))
942
- expect(inputElement.element.value).toBe('-5,00')
952
+ await arrowUp.trigger("click");
953
+ await wrapper.vm.$nextTick();
954
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
955
+ expect(inputElement.element.value).toBe("-5,00");
943
956
 
944
- await arrowDown.trigger('click')
945
- await wrapper.vm.$nextTick()
946
- await new Promise((resolve) => window.setTimeout(resolve, 150))
947
- expect(inputElement.element.value).toBe('-10,00')
957
+ await arrowDown.trigger("click");
958
+ await wrapper.vm.$nextTick();
959
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
960
+ expect(inputElement.element.value).toBe("-10,00");
948
961
 
949
- await arrowDown.trigger('click')
950
- await wrapper.vm.$nextTick()
951
- await new Promise((resolve) => window.setTimeout(resolve, 150))
952
- expect(inputElement.element.value).toBe('-15,00')
953
- })
962
+ await arrowDown.trigger("click");
963
+ await wrapper.vm.$nextTick();
964
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
965
+ expect(inputElement.element.value).toBe("-15,00");
966
+ });
954
967
 
955
- it('should handle negative values crossing zero with step controls', async () => {
968
+ it("should handle negative values crossing zero with step controls", async () => {
956
969
  const wrapper = mount(FzCurrencyInput, {
957
970
  props: {
958
- label: 'Label',
971
+ label: "Label",
959
972
  modelValue: -2,
960
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
973
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
961
974
  step: 5,
962
975
  },
963
- })
976
+ });
964
977
 
965
- const inputElement = wrapper.find('input')
966
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
978
+ const inputElement = wrapper.find("input");
979
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
967
980
 
968
- await inputElement.trigger('blur')
969
- await new Promise((resolve) => window.setTimeout(resolve, 100))
970
- expect(inputElement.element.value).toBe('-2,00')
981
+ await inputElement.trigger("blur");
982
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
983
+ expect(inputElement.element.value).toBe("-2,00");
971
984
 
972
- await arrowUp.trigger('click')
973
- await wrapper.vm.$nextTick()
974
- await new Promise((resolve) => window.setTimeout(resolve, 150))
975
- expect(inputElement.element.value).toBe('3,00')
976
- })
985
+ await arrowUp.trigger("click");
986
+ await wrapper.vm.$nextTick();
987
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
988
+ expect(inputElement.element.value).toBe("3,00");
989
+ });
977
990
 
978
- it('should allow typing negative values directly', async () => {
979
- let modelValue: number | undefined = undefined
991
+ it("should allow typing negative values directly", async () => {
992
+ let modelValue: number | undefined = undefined;
980
993
  const wrapper = mount(FzCurrencyInput, {
981
994
  props: {
982
- label: 'Label',
995
+ label: "Label",
983
996
  modelValue,
984
- 'onUpdate:modelValue': (e) => {
985
- modelValue = e as number
986
- wrapper.setProps({ modelValue })
997
+ "onUpdate:modelValue": (e) => {
998
+ modelValue = e as number;
999
+ wrapper.setProps({ modelValue });
987
1000
  },
988
1001
  },
989
- })
1002
+ });
1003
+
1004
+ const inputElement = wrapper.find("input");
1005
+ await inputElement.trigger("focus");
990
1006
 
991
- const inputElement = wrapper.find('input')
992
- await inputElement.trigger('focus')
993
-
994
1007
  // Type negative value
995
- await inputElement.setValue('-123,45')
996
- await inputElement.trigger('input')
997
- await new Promise((resolve) => window.setTimeout(resolve, 100))
998
-
1008
+ await inputElement.setValue("-123,45");
1009
+ await inputElement.trigger("input");
1010
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1011
+
999
1012
  // During typing, should show normalized value with minus sign
1000
- expect(inputElement.element.value).toBe('-123,45')
1001
- expect(modelValue).toBe(-123.45)
1002
-
1013
+ expect(inputElement.element.value).toBe("-123,45");
1014
+ expect(modelValue).toBe(-123.45);
1015
+
1003
1016
  // On blur, should format correctly
1004
- await inputElement.trigger('blur')
1005
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1006
- expect(inputElement.element.value).toBe('-123,45')
1007
- expect(modelValue).toBe(-123.45)
1008
- })
1009
-
1010
- it('should normalize minus sign to beginning only', async () => {
1011
- let modelValue: number | undefined = undefined
1017
+ await inputElement.trigger("blur");
1018
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1019
+ expect(inputElement.element.value).toBe("-123,45");
1020
+ expect(modelValue).toBe(-123.45);
1021
+ });
1022
+
1023
+ it("should normalize minus sign to beginning only", async () => {
1024
+ let modelValue: number | undefined = undefined;
1012
1025
  const wrapper = mount(FzCurrencyInput, {
1013
1026
  props: {
1014
- label: 'Label',
1027
+ label: "Label",
1015
1028
  modelValue,
1016
- 'onUpdate:modelValue': (e) => {
1017
- modelValue = e as number
1018
- wrapper.setProps({ modelValue })
1029
+ "onUpdate:modelValue": (e) => {
1030
+ modelValue = e as number;
1031
+ wrapper.setProps({ modelValue });
1019
1032
  },
1020
1033
  },
1021
- })
1034
+ });
1035
+
1036
+ const inputElement = wrapper.find("input");
1037
+ await inputElement.trigger("focus");
1022
1038
 
1023
- const inputElement = wrapper.find('input')
1024
- await inputElement.trigger('focus')
1025
-
1026
1039
  // Simulate pasting or typing value with minus in middle (should be normalized)
1027
1040
  // This tests normalizeInput function behavior
1028
- await inputElement.setValue('123-45')
1029
- await inputElement.trigger('input')
1030
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1031
-
1041
+ await inputElement.setValue("123-45");
1042
+ await inputElement.trigger("input");
1043
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1044
+
1032
1045
  // Minus in middle should be removed, value should be positive
1033
- expect(inputElement.element.value).toBe('12345')
1034
- expect(modelValue).toBe(12345)
1035
-
1046
+ expect(inputElement.element.value).toBe("12345");
1047
+ expect(modelValue).toBe(12345);
1048
+
1036
1049
  // Test with minus at beginning (should be preserved)
1037
- await inputElement.setValue('-123,45')
1038
- await inputElement.trigger('input')
1039
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1040
- expect(inputElement.element.value).toBe('-123,45')
1041
- expect(modelValue).toBe(-123.45)
1042
- })
1043
- })
1044
-
1045
- describe('Decimal values', () => {
1046
- it('should handle values with many decimal places', async () => {
1047
- let modelValue: number | undefined = 123.456789
1048
- let wrapper: ReturnType<typeof mount> | null = null
1050
+ await inputElement.setValue("-123,45");
1051
+ await inputElement.trigger("input");
1052
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1053
+ expect(inputElement.element.value).toBe("-123,45");
1054
+ expect(modelValue).toBe(-123.45);
1055
+ });
1056
+ });
1057
+
1058
+ describe("Decimal values", () => {
1059
+ it("should handle values with many decimal places", async () => {
1060
+ let modelValue: number | undefined = 123.456789;
1061
+ let wrapper: ReturnType<typeof mount> | null = null;
1049
1062
  wrapper = mount(FzCurrencyInput, {
1050
1063
  props: {
1051
- label: 'Label',
1064
+ label: "Label",
1052
1065
  modelValue,
1053
- 'onUpdate:modelValue': (e) => {
1054
- modelValue = e as number
1055
- if (wrapper) wrapper.setProps({ modelValue })
1066
+ "onUpdate:modelValue": (e) => {
1067
+ modelValue = e as number;
1068
+ if (wrapper) wrapper.setProps({ modelValue });
1056
1069
  },
1057
1070
  maximumFractionDigits: 2,
1058
1071
  },
1059
- })
1072
+ });
1060
1073
 
1061
- const inputElement = wrapper.find('input')
1062
- await inputElement.trigger('blur')
1063
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1074
+ const inputElement = wrapper.find("input");
1075
+ await inputElement.trigger("blur");
1076
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1064
1077
  // Decimals are truncated (not rounded), so 123.456789 -> 123.45 -> 123,45
1065
- expect(inputElement.element.value).toBe('123,45')
1066
- })
1078
+ expect(inputElement.element.value).toBe("123,45");
1079
+ });
1067
1080
 
1068
- it('should handle values with minimumFractionDigits', async () => {
1081
+ it("should handle values with minimumFractionDigits", async () => {
1069
1082
  const wrapper = mount(FzCurrencyInput, {
1070
1083
  props: {
1071
- label: 'Label',
1084
+ label: "Label",
1072
1085
  modelValue: 123,
1073
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1086
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1074
1087
  minimumFractionDigits: 2,
1075
1088
  },
1076
- })
1089
+ });
1077
1090
 
1078
- const inputElement = wrapper.find('input')
1079
- await inputElement.trigger('blur')
1080
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1081
- expect(inputElement.element.value).toBe('123,00')
1082
- })
1083
- })
1091
+ const inputElement = wrapper.find("input");
1092
+ await inputElement.trigger("blur");
1093
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1094
+ expect(inputElement.element.value).toBe("123,00");
1095
+ });
1096
+ });
1084
1097
 
1085
- describe('Min/Max constraints with step controls', () => {
1086
- it('should allow step controls to go below min (clamping happens on blur)', async () => {
1098
+ describe("Min/Max constraints with step controls", () => {
1099
+ it("should allow step controls to go below min (clamping happens on blur)", async () => {
1087
1100
  const wrapper = mount(FzCurrencyInput, {
1088
1101
  props: {
1089
- label: 'Label',
1102
+ label: "Label",
1090
1103
  modelValue: 10,
1091
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1104
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1092
1105
  min: 10,
1093
1106
  max: 100,
1094
1107
  step: 2,
1095
1108
  },
1096
- })
1109
+ });
1097
1110
 
1098
- const inputElement = wrapper.find('input')
1099
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
1111
+ const inputElement = wrapper.find("input");
1112
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
1100
1113
 
1101
- await inputElement.trigger('blur')
1102
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1103
- expect(inputElement.element.value).toBe('10,00')
1114
+ await inputElement.trigger("blur");
1115
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1116
+ expect(inputElement.element.value).toBe("10,00");
1104
1117
 
1105
- await arrowDown.trigger('click')
1106
- await wrapper.vm.$nextTick()
1107
- await new Promise((resolve) => window.setTimeout(resolve, 150))
1118
+ await arrowDown.trigger("click");
1119
+ await wrapper.vm.$nextTick();
1120
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
1108
1121
  // Step controls now apply clamping immediately, so value is clamped to min
1109
- expect(inputElement.element.value).toBe('10,00')
1110
- expect(wrapper.props('modelValue')).toBe(10)
1122
+ expect(inputElement.element.value).toBe("10,00");
1123
+ expect(wrapper.props("modelValue")).toBe(10);
1111
1124
 
1112
1125
  // Verify that manually typing below min still gets clamped on blur
1113
- await inputElement.setValue('8')
1114
- await inputElement.trigger('blur')
1115
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1116
- expect(inputElement.element.value).toBe('10,00')
1117
- })
1126
+ await inputElement.setValue("8");
1127
+ await inputElement.trigger("blur");
1128
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1129
+ expect(inputElement.element.value).toBe("10,00");
1130
+ });
1118
1131
 
1119
- it('should clamp step controls to max when they would exceed it', async () => {
1132
+ it("should clamp step controls to max when they would exceed it", async () => {
1120
1133
  const wrapper = mount(FzCurrencyInput, {
1121
1134
  props: {
1122
- label: 'Label',
1135
+ label: "Label",
1123
1136
  modelValue: 99,
1124
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1137
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1125
1138
  min: 10,
1126
1139
  max: 100,
1127
1140
  step: 2,
1128
1141
  },
1129
- })
1142
+ });
1130
1143
 
1131
- const inputElement = wrapper.find('input')
1132
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
1144
+ const inputElement = wrapper.find("input");
1145
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
1133
1146
 
1134
- await inputElement.trigger('blur')
1135
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1136
- expect(inputElement.element.value).toBe('99,00')
1147
+ await inputElement.trigger("blur");
1148
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1149
+ expect(inputElement.element.value).toBe("99,00");
1137
1150
 
1138
1151
  // Click arrow up: 99 + 2 = 101, which exceeds max (100), so should clamp to 100
1139
- await arrowUp.trigger('click')
1140
- await wrapper.vm.$nextTick()
1141
- await new Promise((resolve) => window.setTimeout(resolve, 150))
1152
+ await arrowUp.trigger("click");
1153
+ await wrapper.vm.$nextTick();
1154
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
1142
1155
  // Step controls now apply clamping immediately, so value is clamped to max
1143
- expect(inputElement.element.value).toBe('100,00')
1144
- expect(wrapper.props('modelValue')).toBe(100)
1156
+ expect(inputElement.element.value).toBe("100,00");
1157
+ expect(wrapper.props("modelValue")).toBe(100);
1145
1158
 
1146
1159
  // Verify that clicking again doesn't go above max
1147
- await arrowUp.trigger('click')
1148
- await wrapper.vm.$nextTick()
1149
- await new Promise((resolve) => window.setTimeout(resolve, 150))
1150
- expect(inputElement.element.value).toBe('100,00')
1151
- expect(wrapper.props('modelValue')).toBe(100)
1152
- })
1153
- })
1154
-
1155
- describe('Extreme values', () => {
1156
- it('should handle very large numbers', async () => {
1157
- let modelValue: number | undefined = 999999999.99
1158
- let wrapper: ReturnType<typeof mount> | null = null
1160
+ await arrowUp.trigger("click");
1161
+ await wrapper.vm.$nextTick();
1162
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
1163
+ expect(inputElement.element.value).toBe("100,00");
1164
+ expect(wrapper.props("modelValue")).toBe(100);
1165
+ });
1166
+ });
1167
+
1168
+ describe("Extreme values", () => {
1169
+ it("should handle very large numbers", async () => {
1170
+ let modelValue: number | undefined = 999999999.99;
1171
+ let wrapper: ReturnType<typeof mount> | null = null;
1159
1172
  wrapper = mount(FzCurrencyInput, {
1160
1173
  props: {
1161
- label: 'Label',
1174
+ label: "Label",
1162
1175
  modelValue,
1163
- 'onUpdate:modelValue': (e) => {
1164
- modelValue = e as number
1165
- if (wrapper) wrapper.setProps({ modelValue })
1176
+ "onUpdate:modelValue": (e) => {
1177
+ modelValue = e as number;
1178
+ if (wrapper) wrapper.setProps({ modelValue });
1166
1179
  },
1167
1180
  },
1168
- })
1181
+ });
1169
1182
 
1170
- const inputElement = wrapper.find('input')
1171
- await inputElement.trigger('blur')
1172
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1183
+ const inputElement = wrapper.find("input");
1184
+ await inputElement.trigger("blur");
1185
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1173
1186
  // Should be formatted with thousand separators
1174
- expect(inputElement.element.value).toBe('999.999.999,99')
1175
- })
1187
+ expect(inputElement.element.value).toBe("999.999.999,99");
1188
+ });
1176
1189
 
1177
- it('should handle very small numbers', async () => {
1190
+ it("should handle very small numbers", async () => {
1178
1191
  const wrapper = mount(FzCurrencyInput, {
1179
1192
  props: {
1180
- label: 'Label',
1193
+ label: "Label",
1181
1194
  modelValue: 0.01,
1182
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1195
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1183
1196
  },
1184
- })
1197
+ });
1185
1198
 
1186
- const inputElement = wrapper.find('input')
1187
- await inputElement.trigger('blur')
1188
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1189
- expect(inputElement.element.value).toBe('0,01')
1190
- })
1199
+ const inputElement = wrapper.find("input");
1200
+ await inputElement.trigger("blur");
1201
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1202
+ expect(inputElement.element.value).toBe("0,01");
1203
+ });
1191
1204
 
1192
- it('should handle zero', async () => {
1205
+ it("should handle zero", async () => {
1193
1206
  const wrapper = mount(FzCurrencyInput, {
1194
1207
  props: {
1195
- label: 'Label',
1208
+ label: "Label",
1196
1209
  modelValue: 0,
1197
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1210
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1198
1211
  },
1199
- })
1200
-
1201
- const inputElement = wrapper.find('input')
1202
- await inputElement.trigger('blur')
1203
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1204
- expect(inputElement.element.value).toBe('0,00')
1205
- })
1206
- })
1207
-
1208
- describe('Step quantization edge cases', () => {
1209
- it('should handle forceStep with negative values', async () => {
1210
- let modelValue: number | undefined = -3
1211
- let wrapper: ReturnType<typeof mount> | null = null
1212
+ });
1213
+
1214
+ const inputElement = wrapper.find("input");
1215
+ await inputElement.trigger("blur");
1216
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1217
+ expect(inputElement.element.value).toBe("0,00");
1218
+ });
1219
+ });
1220
+
1221
+ describe("Step quantization edge cases", () => {
1222
+ it("should handle forceStep with negative values", async () => {
1223
+ let modelValue: number | undefined = -3;
1224
+ let wrapper: ReturnType<typeof mount> | null = null;
1212
1225
  wrapper = mount(FzCurrencyInput, {
1213
1226
  props: {
1214
- label: 'Label',
1227
+ label: "Label",
1215
1228
  modelValue,
1216
- 'onUpdate:modelValue': (e) => {
1217
- modelValue = e as number
1218
- if (wrapper) wrapper.setProps({ modelValue })
1229
+ "onUpdate:modelValue": (e) => {
1230
+ modelValue = e as number;
1231
+ if (wrapper) wrapper.setProps({ modelValue });
1219
1232
  },
1220
1233
  step: 4,
1221
1234
  forceStep: true,
1222
1235
  },
1223
- })
1236
+ });
1224
1237
 
1225
- const inputElement = wrapper.find('input')
1226
- await inputElement.setValue('-3')
1227
- await inputElement.trigger('blur')
1228
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1238
+ const inputElement = wrapper.find("input");
1239
+ await inputElement.setValue("-3");
1240
+ await inputElement.trigger("blur");
1241
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1229
1242
  // -3 with step 4: remainder is -3, which is 3 in absolute value
1230
1243
  // 3 >= 2 (step/2), so rounds to -3 + (-4) - (-3) = -4
1231
1244
  // But actually, -3 is closer to 0 than to -4, so it might round to 0 or stay -3
1232
1245
  // Let's check the actual behavior: -3 % 4 = -3, Math.abs(-3) = 3, 3 >= 2, so -3 + (-4) - (-3) = -4
1233
1246
  // However, the actual implementation might behave differently
1234
1247
  // Testing actual behavior: if it stays -3, that's because the rounding logic might be different
1235
- const actualValue = inputElement.element.value
1248
+ const actualValue = inputElement.element.value;
1236
1249
  // Accept either -3, -4, 0, or 4 depending on implementation
1237
- expect(['-3,00', '-4,00', '-0,00', '0,00', '4,00']).toContain(actualValue)
1238
- })
1250
+ expect(["-3,00", "-4,00", "-0,00", "0,00", "4,00"]).toContain(
1251
+ actualValue,
1252
+ );
1253
+ });
1239
1254
 
1240
- it('should handle forceStep with value exactly on step', async () => {
1255
+ it("should handle forceStep with value exactly on step", async () => {
1241
1256
  const wrapper = mount(FzCurrencyInput, {
1242
1257
  props: {
1243
- label: 'Label',
1258
+ label: "Label",
1244
1259
  modelValue: 8,
1245
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1260
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1246
1261
  step: 4,
1247
1262
  forceStep: true,
1248
1263
  },
1249
- })
1264
+ });
1250
1265
 
1251
- const inputElement = wrapper.find('input')
1252
- await inputElement.trigger('blur')
1253
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1254
- expect(inputElement.element.value).toBe('8,00')
1255
- })
1266
+ const inputElement = wrapper.find("input");
1267
+ await inputElement.trigger("blur");
1268
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1269
+ expect(inputElement.element.value).toBe("8,00");
1270
+ });
1256
1271
 
1257
- it('should handle forceStep with decimal step', async () => {
1258
- let modelValue: number | undefined = 1.3
1259
- let wrapper: ReturnType<typeof mount> | null = null
1272
+ it("should handle forceStep with decimal step", async () => {
1273
+ let modelValue: number | undefined = 1.3;
1274
+ let wrapper: ReturnType<typeof mount> | null = null;
1260
1275
  wrapper = mount(FzCurrencyInput, {
1261
1276
  props: {
1262
- label: 'Label',
1277
+ label: "Label",
1263
1278
  modelValue,
1264
- 'onUpdate:modelValue': (e) => {
1265
- modelValue = e as number
1266
- if (wrapper) wrapper.setProps({ modelValue })
1279
+ "onUpdate:modelValue": (e) => {
1280
+ modelValue = e as number;
1281
+ if (wrapper) wrapper.setProps({ modelValue });
1267
1282
  },
1268
1283
  step: 0.5,
1269
1284
  forceStep: true,
1270
1285
  },
1271
- })
1286
+ });
1272
1287
 
1273
- const inputElement = wrapper.find('input')
1274
- await inputElement.setValue('1.3')
1275
- await inputElement.trigger('blur')
1276
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1288
+ const inputElement = wrapper.find("input");
1289
+ await inputElement.setValue("1.3");
1290
+ await inputElement.trigger("blur");
1291
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1277
1292
  // 1.3 with step 0.5: remainder is 0.3, which is < 0.25 (step/2), so rounds down to 1.0
1278
1293
  // Actually: 1.3 % 0.5 = 0.3, Math.abs(0.3) = 0.3, 0.3 >= 0.25, so rounds up to 1.5
1279
- expect(inputElement.element.value).toBe('1,50')
1280
- })
1294
+ expect(inputElement.element.value).toBe("1,50");
1295
+ });
1281
1296
 
1282
- it('should handle forceStep with decimal value and integer step', async () => {
1283
- let modelValue: number | undefined = 1.7
1284
- let wrapper: ReturnType<typeof mount> | null = null
1297
+ it("should handle forceStep with decimal value and integer step", async () => {
1298
+ let modelValue: number | undefined = 1.7;
1299
+ let wrapper: ReturnType<typeof mount> | null = null;
1285
1300
  wrapper = mount(FzCurrencyInput, {
1286
1301
  props: {
1287
- label: 'Label',
1302
+ label: "Label",
1288
1303
  modelValue,
1289
- 'onUpdate:modelValue': (e) => {
1290
- modelValue = e as number
1291
- if (wrapper) wrapper.setProps({ modelValue })
1304
+ "onUpdate:modelValue": (e) => {
1305
+ modelValue = e as number;
1306
+ if (wrapper) wrapper.setProps({ modelValue });
1292
1307
  },
1293
1308
  step: 2,
1294
1309
  forceStep: true,
1295
1310
  },
1296
- })
1311
+ });
1297
1312
 
1298
- const inputElement = wrapper.find('input')
1299
- await inputElement.setValue('1.7')
1300
- await inputElement.trigger('blur')
1301
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1313
+ const inputElement = wrapper.find("input");
1314
+ await inputElement.setValue("1.7");
1315
+ await inputElement.trigger("blur");
1316
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1302
1317
  // 1.7 with step 2: remainder is 1.7, which is < 1 (step/2), so rounds down to 0
1303
1318
  // Actually: 1.7 % 2 = 1.7, Math.abs(1.7) = 1.7, 1.7 >= 1, so rounds up to 2
1304
- expect(inputElement.element.value).toBe('2,00')
1305
- })
1319
+ expect(inputElement.element.value).toBe("2,00");
1320
+ });
1306
1321
 
1307
- it('should handle step controls with decimal step', async () => {
1322
+ it("should handle step controls with decimal step", async () => {
1308
1323
  const wrapper = mount(FzCurrencyInput, {
1309
1324
  props: {
1310
- label: 'Label',
1325
+ label: "Label",
1311
1326
  modelValue: 10.5,
1312
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1327
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1313
1328
  step: 0.25,
1314
1329
  },
1315
- })
1330
+ });
1316
1331
 
1317
- const inputElement = wrapper.find('input')
1318
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
1319
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
1332
+ const inputElement = wrapper.find("input");
1333
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
1334
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
1320
1335
 
1321
- await inputElement.trigger('blur')
1322
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1323
- expect(inputElement.element.value).toBe('10,50')
1336
+ await inputElement.trigger("blur");
1337
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1338
+ expect(inputElement.element.value).toBe("10,50");
1324
1339
 
1325
- await arrowUp.trigger('click')
1326
- await wrapper.vm.$nextTick()
1327
- await new Promise((resolve) => window.setTimeout(resolve, 150))
1328
- expect(inputElement.element.value).toBe('10,75')
1340
+ await arrowUp.trigger("click");
1341
+ await wrapper.vm.$nextTick();
1342
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
1343
+ expect(inputElement.element.value).toBe("10,75");
1329
1344
 
1330
- await arrowDown.trigger('click')
1331
- await wrapper.vm.$nextTick()
1332
- await new Promise((resolve) => window.setTimeout(resolve, 150))
1333
- expect(inputElement.element.value).toBe('10,50')
1334
- })
1345
+ await arrowDown.trigger("click");
1346
+ await wrapper.vm.$nextTick();
1347
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
1348
+ expect(inputElement.element.value).toBe("10,50");
1349
+ });
1335
1350
 
1336
- it('should handle step controls producing decimal values', async () => {
1351
+ it("should handle step controls producing decimal values", async () => {
1337
1352
  const wrapper = mount(FzCurrencyInput, {
1338
1353
  props: {
1339
- label: 'Label',
1354
+ label: "Label",
1340
1355
  modelValue: 10,
1341
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1356
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1342
1357
  step: 0.1,
1343
1358
  },
1344
- })
1359
+ });
1345
1360
 
1346
- const inputElement = wrapper.find('input')
1347
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
1361
+ const inputElement = wrapper.find("input");
1362
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
1348
1363
 
1349
- await inputElement.trigger('blur')
1350
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1351
- expect(inputElement.element.value).toBe('10,00')
1364
+ await inputElement.trigger("blur");
1365
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1366
+ expect(inputElement.element.value).toBe("10,00");
1352
1367
 
1353
- await arrowUp.trigger('click')
1354
- await wrapper.vm.$nextTick()
1355
- await new Promise((resolve) => window.setTimeout(resolve, 150))
1356
- expect(inputElement.element.value).toBe('10,10')
1357
- })
1368
+ await arrowUp.trigger("click");
1369
+ await wrapper.vm.$nextTick();
1370
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
1371
+ expect(inputElement.element.value).toBe("10,10");
1372
+ });
1358
1373
 
1359
- it('should handle forceStep with small decimal step', async () => {
1374
+ it("should handle forceStep with small decimal step", async () => {
1360
1375
  const wrapper = mount(FzCurrencyInput, {
1361
1376
  props: {
1362
- label: 'Label',
1377
+ label: "Label",
1363
1378
  modelValue: 1.23,
1364
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1379
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1365
1380
  step: 0.01,
1366
1381
  forceStep: true,
1367
1382
  },
1368
- })
1383
+ });
1369
1384
 
1370
- const inputElement = wrapper.find('input')
1371
- await inputElement.setValue('1.23')
1372
- await inputElement.trigger('blur')
1373
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1385
+ const inputElement = wrapper.find("input");
1386
+ await inputElement.setValue("1.23");
1387
+ await inputElement.trigger("blur");
1388
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1374
1389
  // 1.23 with step 0.01: should round to nearest 0.01, which is 1.23 itself
1375
- expect(inputElement.element.value).toBe('1,23')
1376
- })
1390
+ expect(inputElement.element.value).toBe("1,23");
1391
+ });
1377
1392
 
1378
- it('should handle forceStep rounding decimal to nearest step', async () => {
1379
- let modelValue: number | undefined = 1.234
1380
- let wrapper: ReturnType<typeof mount> | null = null
1393
+ it("should handle forceStep rounding decimal to nearest step", async () => {
1394
+ let modelValue: number | undefined = 1.234;
1395
+ let wrapper: ReturnType<typeof mount> | null = null;
1381
1396
  wrapper = mount(FzCurrencyInput, {
1382
1397
  props: {
1383
- label: 'Label',
1398
+ label: "Label",
1384
1399
  modelValue,
1385
- 'onUpdate:modelValue': (e) => {
1386
- modelValue = e as number
1387
- if (wrapper) wrapper.setProps({ modelValue })
1400
+ "onUpdate:modelValue": (e) => {
1401
+ modelValue = e as number;
1402
+ if (wrapper) wrapper.setProps({ modelValue });
1388
1403
  },
1389
1404
  step: 0.05,
1390
1405
  forceStep: true,
1391
1406
  },
1392
- })
1407
+ });
1393
1408
 
1394
- const inputElement = wrapper.find('input')
1395
- await inputElement.setValue('1.234')
1396
- await inputElement.trigger('blur')
1397
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1409
+ const inputElement = wrapper.find("input");
1410
+ await inputElement.setValue("1.234");
1411
+ await inputElement.trigger("blur");
1412
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1398
1413
  // 1.234 with step 0.05: rounds to nearest step multiple
1399
1414
  // The actual behavior rounds to 1.25 (closer to 1.25 than to 1.20)
1400
- expect(inputElement.element.value).toBe('1,25')
1401
- })
1415
+ expect(inputElement.element.value).toBe("1,25");
1416
+ });
1402
1417
 
1403
- it('should round invalid step value on blur (e.g., 3 with step 2)', async () => {
1418
+ it("should round invalid step value on blur (e.g., 3 with step 2)", async () => {
1404
1419
  const wrapper = mount(FzCurrencyInput, {
1405
1420
  props: {
1406
- label: 'Label',
1421
+ label: "Label",
1407
1422
  modelValue: undefined,
1408
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1423
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1409
1424
  step: 2,
1410
1425
  forceStep: true,
1411
1426
  min: 0,
1412
1427
  },
1413
- })
1428
+ });
1414
1429
 
1415
- const inputElement = wrapper.find('input')
1430
+ const inputElement = wrapper.find("input");
1416
1431
  // User types "3" which is not a valid step (step is 2)
1417
- await inputElement.setValue('3')
1418
- await inputElement.trigger('blur')
1419
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1432
+ await inputElement.setValue("3");
1433
+ await inputElement.trigger("blur");
1434
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1420
1435
  // 3 with step 2: remainder is 1, which is >= 1 (step/2), so rounds up to 4
1421
- expect(inputElement.element.value).toBe('4,00')
1422
- expect(wrapper.props('modelValue')).toBe(4)
1423
- })
1436
+ expect(inputElement.element.value).toBe("4,00");
1437
+ expect(wrapper.props("modelValue")).toBe(4);
1438
+ });
1424
1439
 
1425
- it('should round invalid step value down when closer to lower step', async () => {
1440
+ it("should round invalid step value down when closer to lower step", async () => {
1426
1441
  const wrapper = mount(FzCurrencyInput, {
1427
1442
  props: {
1428
- label: 'Label',
1443
+ label: "Label",
1429
1444
  modelValue: undefined,
1430
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1445
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1431
1446
  step: 2,
1432
1447
  forceStep: true,
1433
1448
  min: 0,
1434
1449
  },
1435
- })
1450
+ });
1436
1451
 
1437
- const inputElement = wrapper.find('input')
1452
+ const inputElement = wrapper.find("input");
1438
1453
  // User types "1" which is not a valid step (step is 2)
1439
- await inputElement.setValue('1')
1440
- await inputElement.trigger('blur')
1441
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1454
+ await inputElement.setValue("1");
1455
+ await inputElement.trigger("blur");
1456
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1442
1457
  // 1 with step 2: remainder is 1, which is >= 1 (step/2), so rounds up to 2
1443
1458
  // Actually: 1 % 2 = 1, Math.abs(1) = 1, 1 >= 1, so rounds up to 2
1444
- expect(inputElement.element.value).toBe('2,00')
1445
- expect(wrapper.props('modelValue')).toBe(2)
1446
- })
1459
+ expect(inputElement.element.value).toBe("2,00");
1460
+ expect(wrapper.props("modelValue")).toBe(2);
1461
+ });
1447
1462
 
1448
- it('should increment value correctly with step arrows when forceStep is true', async () => {
1463
+ it("should increment value correctly with step arrows when forceStep is true", async () => {
1449
1464
  const wrapper = mount(FzCurrencyInput, {
1450
1465
  props: {
1451
- label: 'Label',
1466
+ label: "Label",
1452
1467
  modelValue: 0,
1453
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1468
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1454
1469
  step: 2,
1455
1470
  forceStep: true,
1456
1471
  min: 0,
1457
1472
  },
1458
- })
1473
+ });
1459
1474
 
1460
- const inputElement = wrapper.find('input')
1461
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
1462
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
1475
+ const inputElement = wrapper.find("input");
1476
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
1477
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
1463
1478
 
1464
- await inputElement.trigger('blur')
1465
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1466
- expect(inputElement.element.value).toBe('0,00')
1479
+ await inputElement.trigger("blur");
1480
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1481
+ expect(inputElement.element.value).toBe("0,00");
1467
1482
 
1468
1483
  // Click arrow up: should increment by step (2)
1469
- await arrowUp.trigger('click')
1470
- await wrapper.vm.$nextTick()
1471
- await new Promise((resolve) => window.setTimeout(resolve, 150))
1472
- expect(inputElement.element.value).toBe('2,00')
1473
- expect(wrapper.props('modelValue')).toBe(2)
1484
+ await arrowUp.trigger("click");
1485
+ await wrapper.vm.$nextTick();
1486
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
1487
+ expect(inputElement.element.value).toBe("2,00");
1488
+ expect(wrapper.props("modelValue")).toBe(2);
1474
1489
 
1475
1490
  // Click arrow up again: should increment by step (2) to 4
1476
- await arrowUp.trigger('click')
1477
- await wrapper.vm.$nextTick()
1478
- await new Promise((resolve) => window.setTimeout(resolve, 150))
1479
- expect(inputElement.element.value).toBe('4,00')
1480
- expect(wrapper.props('modelValue')).toBe(4)
1491
+ await arrowUp.trigger("click");
1492
+ await wrapper.vm.$nextTick();
1493
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
1494
+ expect(inputElement.element.value).toBe("4,00");
1495
+ expect(wrapper.props("modelValue")).toBe(4);
1481
1496
 
1482
1497
  // Click arrow down: should decrement by step (2) to 2
1483
- await arrowDown.trigger('click')
1484
- await wrapper.vm.$nextTick()
1485
- await new Promise((resolve) => window.setTimeout(resolve, 150))
1486
- expect(inputElement.element.value).toBe('2,00')
1487
- expect(wrapper.props('modelValue')).toBe(2)
1488
- })
1489
-
1490
- it('should increment value correctly with step arrows when forceStep is false', async () => {
1498
+ await arrowDown.trigger("click");
1499
+ await wrapper.vm.$nextTick();
1500
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
1501
+ expect(inputElement.element.value).toBe("2,00");
1502
+ expect(wrapper.props("modelValue")).toBe(2);
1503
+ });
1504
+
1505
+ it("should increment value correctly with step arrows when forceStep is false", async () => {
1491
1506
  const wrapper = mount(FzCurrencyInput, {
1492
1507
  props: {
1493
- label: 'Label',
1508
+ label: "Label",
1494
1509
  modelValue: 1,
1495
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1510
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1496
1511
  step: 2,
1497
1512
  forceStep: false,
1498
1513
  min: 0,
1499
1514
  },
1500
- })
1515
+ });
1501
1516
 
1502
- const inputElement = wrapper.find('input')
1503
- const arrowUp = wrapper.find('.fz__currencyinput__arrowup')
1504
- const arrowDown = wrapper.find('.fz__currencyinput__arrowdown')
1517
+ const inputElement = wrapper.find("input");
1518
+ const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
1519
+ const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
1505
1520
 
1506
- await inputElement.trigger('blur')
1507
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1508
- expect(inputElement.element.value).toBe('1,00')
1521
+ await inputElement.trigger("blur");
1522
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1523
+ expect(inputElement.element.value).toBe("1,00");
1509
1524
 
1510
1525
  // Click arrow up: should increment by step (2) from current value (1) to 3
1511
- await arrowUp.trigger('click')
1512
- await wrapper.vm.$nextTick()
1513
- await new Promise((resolve) => window.setTimeout(resolve, 150))
1514
- expect(inputElement.element.value).toBe('3,00')
1515
- expect(wrapper.props('modelValue')).toBe(3)
1526
+ await arrowUp.trigger("click");
1527
+ await wrapper.vm.$nextTick();
1528
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
1529
+ expect(inputElement.element.value).toBe("3,00");
1530
+ expect(wrapper.props("modelValue")).toBe(3);
1516
1531
 
1517
1532
  // Click arrow down: should decrement by step (2) from current value (3) to 1
1518
- await arrowDown.trigger('click')
1519
- await wrapper.vm.$nextTick()
1520
- await new Promise((resolve) => window.setTimeout(resolve, 150))
1521
- expect(inputElement.element.value).toBe('1,00')
1522
- expect(wrapper.props('modelValue')).toBe(1)
1523
- })
1524
- })
1525
-
1526
- describe('Floating-point precision (regression)', () => {
1533
+ await arrowDown.trigger("click");
1534
+ await wrapper.vm.$nextTick();
1535
+ await new Promise((resolve) => window.setTimeout(resolve, 150));
1536
+ expect(inputElement.element.value).toBe("1,00");
1537
+ expect(wrapper.props("modelValue")).toBe(1);
1538
+ });
1539
+ });
1540
+
1541
+ describe("Floating-point precision (regression)", () => {
1527
1542
  it.each([
1528
- ['40,30', 40.3, '40,30'],
1529
- ['40,20', 40.2, '40,20'],
1530
- ['40,40', 40.4, '40,40'],
1531
- ['299,96', 299.96, '299,96'],
1532
- ['0,30', 0.3, '0,30'],
1533
- ['10,30', 10.3, '10,30'],
1534
- ['99,99', 99.99, '99,99'],
1535
- ])('should preserve "%s" without floating-point drift', async (input, expectedModel, expectedDisplay) => {
1536
- let modelValue: number | string | undefined = undefined
1537
- let wrapper: ReturnType<typeof mount> | null = null
1538
- wrapper = mount(FzCurrencyInput, {
1539
- props: {
1540
- label: 'Label',
1541
- modelValue,
1542
- 'onUpdate:modelValue': (e) => {
1543
- modelValue = e as number
1544
- if (wrapper) wrapper.setProps({ modelValue })
1543
+ ["40,30", 40.3, "40,30"],
1544
+ ["40,20", 40.2, "40,20"],
1545
+ ["40,40", 40.4, "40,40"],
1546
+ ["299,96", 299.96, "299,96"],
1547
+ ["0,30", 0.3, "0,30"],
1548
+ ["10,30", 10.3, "10,30"],
1549
+ ["99,99", 99.99, "99,99"],
1550
+ ])(
1551
+ 'should preserve "%s" without floating-point drift',
1552
+ async (input, expectedModel, expectedDisplay) => {
1553
+ let modelValue: number | string | undefined = undefined;
1554
+ let wrapper: ReturnType<typeof mount> | null = null;
1555
+ wrapper = mount(FzCurrencyInput, {
1556
+ props: {
1557
+ label: "Label",
1558
+ modelValue,
1559
+ "onUpdate:modelValue": (e) => {
1560
+ modelValue = e as number;
1561
+ if (wrapper) wrapper.setProps({ modelValue });
1562
+ },
1545
1563
  },
1546
- },
1547
- })
1564
+ });
1548
1565
 
1549
- const inputElement = wrapper.find('input')
1566
+ const inputElement = wrapper.find("input");
1550
1567
 
1551
- // Focus, type the value, blur
1552
- await inputElement.trigger('focus')
1553
- await inputElement.setValue(input)
1554
- await wrapper.vm.$nextTick()
1555
- await inputElement.trigger('blur')
1556
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1568
+ // Focus, type the value, blur
1569
+ await inputElement.trigger("focus");
1570
+ await inputElement.setValue(input);
1571
+ await wrapper.vm.$nextTick();
1572
+ await inputElement.trigger("blur");
1573
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1557
1574
 
1558
- expect(wrapper.props('modelValue')).toBe(expectedModel)
1559
- expect(inputElement.element.value).toBe(expectedDisplay)
1560
- })
1575
+ expect(wrapper.props("modelValue")).toBe(expectedModel);
1576
+ expect(inputElement.element.value).toBe(expectedDisplay);
1577
+ },
1578
+ );
1561
1579
 
1562
1580
  it.each([
1563
- [40.3, '40,30'],
1564
- [40.2, '40,20'],
1565
- [40.4, '40,40'],
1566
- [299.96, '299,96'],
1567
- [1234567.89, '1.234.567,89'],
1568
- ])('should display correct raw value on focus for v-model %f', async (numericValue, expectedRaw) => {
1569
- const wrapper = mount(FzCurrencyInput, {
1570
- props: {
1571
- label: 'Label',
1572
- modelValue: numericValue,
1573
- 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
1574
- },
1575
- })
1581
+ [40.3, "40,30"],
1582
+ [40.2, "40,20"],
1583
+ [40.4, "40,40"],
1584
+ [299.96, "299,96"],
1585
+ [1234567.89, "1.234.567,89"],
1586
+ ])(
1587
+ "should display correct raw value on focus for v-model %f",
1588
+ async (numericValue, expectedRaw) => {
1589
+ const wrapper = mount(FzCurrencyInput, {
1590
+ props: {
1591
+ label: "Label",
1592
+ modelValue: numericValue,
1593
+ "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
1594
+ },
1595
+ });
1576
1596
 
1577
- await wrapper.vm.$nextTick()
1578
- await new Promise((resolve) => window.setTimeout(resolve, 100))
1597
+ await wrapper.vm.$nextTick();
1598
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1579
1599
 
1580
- const inputElement = wrapper.find('input')
1600
+ const inputElement = wrapper.find("input");
1581
1601
 
1582
- // Focus to see raw value (without thousand separators)
1583
- await inputElement.trigger('focus')
1584
- await wrapper.vm.$nextTick()
1602
+ // Focus to see raw value (without thousand separators)
1603
+ await inputElement.trigger("focus");
1604
+ await wrapper.vm.$nextTick();
1585
1605
 
1586
- const rawExpected = expectedRaw.replace(/\./g, '')
1587
- expect(inputElement.element.value).toBe(rawExpected)
1588
- })
1589
- })
1590
- })
1606
+ const rawExpected = expectedRaw.replace(/\./g, "");
1607
+ expect(inputElement.element.value).toBe(rawExpected);
1608
+ },
1609
+ );
1610
+ });
1611
+ });
1591
1612
 
1592
- describe('Slot forwarding', () => {
1593
- it('should not render empty help text span when helpText slot is not provided', async () => {
1613
+ describe("Slot forwarding", () => {
1614
+ it("should not render empty help text span when helpText slot is not provided", async () => {
1594
1615
  const wrapper = mount(FzCurrencyInput, {
1595
1616
  props: {
1596
- label: 'Label',
1617
+ label: "Label",
1597
1618
  modelValue: 10,
1598
1619
  },
1599
- })
1620
+ });
1600
1621
 
1601
- await wrapper.vm.$nextTick()
1622
+ await wrapper.vm.$nextTick();
1602
1623
 
1603
- const helpSpan = wrapper.find(`[id$="-help"]`)
1604
- expect(helpSpan.exists()).toBe(false)
1605
- })
1624
+ const helpSpan = wrapper.find(`[id$="-help"]`);
1625
+ expect(helpSpan.exists()).toBe(false);
1626
+ });
1606
1627
 
1607
- it('should render help text span when helpText slot is provided', async () => {
1628
+ it("should render help text span when helpText slot is provided", async () => {
1608
1629
  const wrapper = mount(FzCurrencyInput, {
1609
1630
  props: {
1610
- label: 'Label',
1631
+ label: "Label",
1611
1632
  modelValue: 10,
1612
1633
  },
1613
1634
  slots: {
1614
- helpText: 'Enter an amount',
1635
+ helpText: "Enter an amount",
1615
1636
  },
1616
- })
1637
+ });
1617
1638
 
1618
- await wrapper.vm.$nextTick()
1639
+ await wrapper.vm.$nextTick();
1619
1640
 
1620
- const helpSpan = wrapper.find(`[id$="-help"]`)
1621
- expect(helpSpan.exists()).toBe(true)
1622
- expect(helpSpan.text()).toContain('Enter an amount')
1623
- })
1641
+ const helpSpan = wrapper.find(`[id$="-help"]`);
1642
+ expect(helpSpan.exists()).toBe(true);
1643
+ expect(helpSpan.text()).toContain("Enter an amount");
1644
+ });
1624
1645
 
1625
- it('should render label from label prop when label slot is not provided', async () => {
1646
+ it("should render label from label prop when label slot is not provided", async () => {
1626
1647
  const wrapper = mount(FzCurrencyInput, {
1627
1648
  props: {
1628
- label: 'Amount',
1649
+ label: "Amount",
1629
1650
  modelValue: 10,
1630
1651
  },
1631
- })
1652
+ });
1632
1653
 
1633
- await wrapper.vm.$nextTick()
1654
+ await wrapper.vm.$nextTick();
1634
1655
 
1635
- const label = wrapper.find('label')
1636
- expect(label.exists()).toBe(true)
1637
- expect(label.text()).toContain('Amount')
1638
- })
1656
+ const label = wrapper.find("label");
1657
+ expect(label.exists()).toBe(true);
1658
+ expect(label.text()).toContain("Amount");
1659
+ });
1639
1660
 
1640
- it('should render custom label slot instead of label prop', async () => {
1661
+ it("should render custom label slot instead of label prop", async () => {
1641
1662
  const wrapper = mount(FzCurrencyInput, {
1642
1663
  props: {
1643
- label: 'Amount',
1664
+ label: "Amount",
1644
1665
  modelValue: 10,
1645
1666
  },
1646
1667
  slots: {
1647
- label: '<strong>Custom Label</strong>',
1668
+ label: "<strong>Custom Label</strong>",
1648
1669
  },
1649
- })
1670
+ });
1650
1671
 
1651
- await wrapper.vm.$nextTick()
1672
+ await wrapper.vm.$nextTick();
1652
1673
 
1653
- const strong = wrapper.find('strong')
1654
- expect(strong.exists()).toBe(true)
1655
- expect(strong.text()).toBe('Custom Label')
1674
+ const strong = wrapper.find("strong");
1675
+ expect(strong.exists()).toBe(true);
1676
+ expect(strong.text()).toBe("Custom Label");
1656
1677
 
1657
- const defaultLabel = wrapper.find('label')
1658
- expect(defaultLabel.exists()).toBe(false)
1659
- })
1678
+ const defaultLabel = wrapper.find("label");
1679
+ expect(defaultLabel.exists()).toBe(false);
1680
+ });
1660
1681
 
1661
- it('should not render error message container when errorMessage slot is not provided', async () => {
1682
+ it("should not render error message container when errorMessage slot is not provided", async () => {
1662
1683
  const wrapper = mount(FzCurrencyInput, {
1663
1684
  props: {
1664
- label: 'Label',
1685
+ label: "Label",
1665
1686
  modelValue: 10,
1666
1687
  error: true,
1667
1688
  },
1668
- })
1689
+ });
1669
1690
 
1670
- await wrapper.vm.$nextTick()
1691
+ await wrapper.vm.$nextTick();
1671
1692
 
1672
- const errorContainer = wrapper.find('[role="alert"]')
1673
- expect(errorContainer.exists()).toBe(false)
1674
- })
1693
+ const errorContainer = wrapper.find('[role="alert"]');
1694
+ expect(errorContainer.exists()).toBe(false);
1695
+ });
1675
1696
 
1676
- it('should render error message when error is true and errorMessage slot is provided', async () => {
1697
+ it("should render error message when error is true and errorMessage slot is provided", async () => {
1677
1698
  const wrapper = mount(FzCurrencyInput, {
1678
1699
  props: {
1679
- label: 'Label',
1700
+ label: "Label",
1680
1701
  modelValue: 10,
1681
1702
  error: true,
1682
1703
  },
1683
1704
  slots: {
1684
- errorMessage: 'Invalid amount',
1705
+ errorMessage: "Invalid amount",
1706
+ },
1707
+ });
1708
+
1709
+ await wrapper.vm.$nextTick();
1710
+
1711
+ const errorContainer = wrapper.find('[role="alert"]');
1712
+ expect(errorContainer.exists()).toBe(true);
1713
+ expect(errorContainer.text()).toContain("Invalid amount");
1714
+ });
1715
+ });
1716
+
1717
+ describe("Clearable", () => {
1718
+ it("does not show clear icon when clearable is false (default)", () => {
1719
+ const wrapper = mount(FzCurrencyInput, {
1720
+ props: {
1721
+ label: "Label",
1722
+ modelValue: 100,
1723
+ },
1724
+ });
1725
+
1726
+ expect(wrapper.find('[aria-label="Cancella"]').exists()).toBe(false);
1727
+ });
1728
+
1729
+ it("shows clear icon when clearable is true and model has value", async () => {
1730
+ const wrapper = mount(FzCurrencyInput, {
1731
+ props: {
1732
+ label: "Label",
1733
+ clearable: true,
1734
+ modelValue: 100,
1735
+ },
1736
+ });
1737
+
1738
+ await wrapper.find("input").trigger("blur");
1739
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1740
+
1741
+ expect(wrapper.find('[aria-label="Cancella"]').exists()).toBe(true);
1742
+ });
1743
+
1744
+ it("does not show clear icon when clearable is true but model is empty", () => {
1745
+ const wrapper = mount(FzCurrencyInput, {
1746
+ props: {
1747
+ label: "Label",
1748
+ clearable: true,
1749
+ modelValue: undefined,
1750
+ },
1751
+ });
1752
+
1753
+ expect(wrapper.find('[aria-label="Cancella"]').exists()).toBe(false);
1754
+ });
1755
+
1756
+ it("clears model when clear icon is clicked", async () => {
1757
+ let modelValue: number | undefined = 100;
1758
+ let wrapper: ReturnType<typeof mount> | null = null;
1759
+ wrapper = mount(FzCurrencyInput, {
1760
+ props: {
1761
+ label: "Label",
1762
+ clearable: true,
1763
+ modelValue,
1764
+ "onUpdate:modelValue": (e) => {
1765
+ modelValue = e as number;
1766
+ if (wrapper) wrapper.setProps({ modelValue });
1767
+ },
1685
1768
  },
1686
- })
1769
+ });
1770
+
1771
+ await wrapper.find("input").trigger("blur");
1772
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1773
+
1774
+ await wrapper.find('[aria-label="Cancella"]').trigger("click");
1775
+ await wrapper.vm.$nextTick();
1687
1776
 
1688
- await wrapper.vm.$nextTick()
1777
+ expect(wrapper.emitted("fzcurrencyinput:clear")).toBeTruthy();
1778
+ });
1779
+
1780
+ it("coexists with step controls", async () => {
1781
+ const wrapper = mount(FzCurrencyInput, {
1782
+ props: {
1783
+ label: "Label",
1784
+ clearable: true,
1785
+ modelValue: 100,
1786
+ },
1787
+ });
1689
1788
 
1690
- const errorContainer = wrapper.find('[role="alert"]')
1691
- expect(errorContainer.exists()).toBe(true)
1692
- expect(errorContainer.text()).toContain('Invalid amount')
1693
- })
1694
- })
1695
- })
1789
+ await wrapper.find("input").trigger("blur");
1790
+ await new Promise((resolve) => window.setTimeout(resolve, 100));
1696
1791
 
1792
+ expect(wrapper.find('[aria-label="Cancella"]').exists()).toBe(true);
1793
+ expect(wrapper.find(".fz__currencyinput__arrowup").exists()).toBe(true);
1794
+ expect(wrapper.find(".fz__currencyinput__arrowdown").exists()).toBe(true);
1795
+ });
1796
+ });
1797
+ });