@fiscozen/input 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/coverage/FzCurrencyInput.vue.html +640 -0
- package/coverage/FzInput.vue.html +709 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +494 -0
- package/coverage/coverage-final.json +4 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +146 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/coverage/useInputStyle.ts.html +343 -0
- package/dist/input.js +267 -8459
- package/dist/input.umd.cjs +1 -557
- package/dist/src/FzCurrencyInput.vue.d.ts +26 -4
- package/dist/src/FzInput.vue.d.ts +4 -0
- package/dist/src/types.d.ts +11 -0
- package/package.json +6 -6
- package/src/FzCurrencyInput.vue +7 -0
- package/src/FzInput.vue +482 -573
- package/src/__tests__/FzCurrencyInput.spec.ts +1169 -1068
- package/src/__tests__/FzInput.spec.ts +125 -0
- package/src/types.ts +11 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vite.config.ts +1 -1
- package/dist/input.css +0 -2
|
@@ -1,1696 +1,1797 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from
|
|
2
|
-
import { mount } from
|
|
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(
|
|
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(
|
|
15
|
-
it(
|
|
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:
|
|
20
|
+
label: "Label",
|
|
21
21
|
modelValue,
|
|
22
|
-
|
|
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(
|
|
30
|
-
await inputElement.trigger(
|
|
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(
|
|
34
|
-
})
|
|
33
|
+
expect(inputElement.element.value).toBe("1.234,56");
|
|
34
|
+
});
|
|
35
35
|
|
|
36
|
-
it(
|
|
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:
|
|
41
|
+
label: "Label",
|
|
42
42
|
modelValue,
|
|
43
|
-
|
|
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(
|
|
51
|
-
await inputElement.trigger(
|
|
52
|
-
await inputElement.setValue(
|
|
53
|
-
await inputElement.trigger(
|
|
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(
|
|
57
|
-
await inputElement.trigger(
|
|
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(
|
|
61
|
-
})
|
|
60
|
+
expect(inputElement.element.value).toBe("12,30");
|
|
61
|
+
});
|
|
62
62
|
|
|
63
|
-
it(
|
|
63
|
+
it("should allow to set value at 0", async () => {
|
|
64
64
|
const wrapper = mount(FzCurrencyInput, {
|
|
65
65
|
props: {
|
|
66
|
-
label:
|
|
66
|
+
label: "Label",
|
|
67
67
|
modelValue: 10,
|
|
68
|
-
|
|
68
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
69
69
|
},
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
const inputElement = wrapper.find(
|
|
73
|
-
await inputElement.trigger(
|
|
74
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
75
|
-
expect(inputElement.element.value).toBe(
|
|
76
|
-
wrapper.setProps({ modelValue: 0 })
|
|
77
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
78
|
-
expect(inputElement.element.value).toBe(
|
|
79
|
-
})
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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:
|
|
86
|
+
label: "Label",
|
|
88
87
|
modelValue: 10,
|
|
89
|
-
|
|
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(
|
|
102
|
-
await inputElement.trigger(
|
|
103
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
104
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
108
|
-
await inputElement.trigger(
|
|
109
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
110
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
112
|
+
it("should allow typing values outside min/max range temporarily", async () => {
|
|
114
113
|
const wrapper = mount(FzCurrencyInput, {
|
|
115
114
|
props: {
|
|
116
|
-
label:
|
|
115
|
+
label: "Label",
|
|
117
116
|
modelValue: 50,
|
|
118
|
-
|
|
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(
|
|
129
|
-
await inputElement.trigger(
|
|
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(
|
|
131
|
+
expect(inputElement.element.value).toBe("150");
|
|
133
132
|
// v-model should also reflect the unclamped value during typing
|
|
134
|
-
expect(wrapper.props(
|
|
135
|
-
|
|
133
|
+
expect(wrapper.props("modelValue")).toBe(150);
|
|
134
|
+
|
|
136
135
|
// Only after blur, value should be clamped to max
|
|
137
|
-
await inputElement.trigger(
|
|
138
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
139
|
-
expect(inputElement.element.value).toBe(
|
|
140
|
-
expect(wrapper.props(
|
|
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(
|
|
144
|
-
await inputElement.setValue(
|
|
145
|
-
await inputElement.trigger(
|
|
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(
|
|
149
|
-
expect(wrapper.props(
|
|
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(
|
|
153
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
154
|
-
expect(inputElement.element.value).toBe(
|
|
155
|
-
expect(wrapper.props(
|
|
156
|
-
})
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
describe(
|
|
160
|
-
it(
|
|
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:
|
|
162
|
+
label: "Label",
|
|
164
163
|
modelValue: 1,
|
|
165
|
-
|
|
164
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
166
165
|
step: 4,
|
|
167
166
|
},
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
const inputElement = wrapper.find(
|
|
171
|
-
const arrowUp = wrapper.find(
|
|
172
|
-
const arrowDown = wrapper.find(
|
|
173
|
-
|
|
174
|
-
await inputElement.trigger(
|
|
175
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
176
|
-
expect(inputElement.element.value).toBe(
|
|
177
|
-
await arrowUp.trigger(
|
|
178
|
-
await wrapper.vm.$nextTick()
|
|
179
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
180
|
-
expect(inputElement.element.value).toBe(
|
|
181
|
-
await arrowDown.trigger(
|
|
182
|
-
await wrapper.vm.$nextTick()
|
|
183
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
184
|
-
expect(inputElement.element.value).toBe(
|
|
185
|
-
await arrowDown.trigger(
|
|
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(
|
|
190
|
-
})
|
|
188
|
+
expect(inputElement.element.value).toBe("-3,00");
|
|
189
|
+
});
|
|
191
190
|
|
|
192
|
-
it(
|
|
191
|
+
it("should enforce quantization via the forceStep prop", async () => {
|
|
193
192
|
const wrapper = mount(FzCurrencyInput, {
|
|
194
193
|
props: {
|
|
195
|
-
label:
|
|
194
|
+
label: "Label",
|
|
196
195
|
modelValue: 8,
|
|
197
|
-
|
|
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(
|
|
204
|
-
await inputElement.trigger(
|
|
205
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
206
|
-
expect(inputElement.element.value).toBe(
|
|
207
|
-
await wrapper.setProps({ modelValue: 5 })
|
|
208
|
-
await inputElement.trigger(
|
|
209
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
210
|
-
expect(inputElement.element.value).toBe(
|
|
211
|
-
await wrapper.setProps({ modelValue: -7 })
|
|
212
|
-
await inputElement.trigger(
|
|
213
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
214
|
-
expect(inputElement.element.value).toBe(
|
|
215
|
-
})
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
describe(
|
|
219
|
-
it(
|
|
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:
|
|
221
|
+
label: "Label",
|
|
223
222
|
modelValue: 10,
|
|
224
|
-
|
|
223
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
225
224
|
},
|
|
226
|
-
})
|
|
225
|
+
});
|
|
227
226
|
|
|
228
|
-
const arrowUp = wrapper.find(
|
|
229
|
-
const arrowDown = wrapper.find(
|
|
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(
|
|
235
|
-
await inputElement.trigger(
|
|
236
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
237
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
240
|
-
await wrapper.vm.$nextTick()
|
|
241
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
242
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
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(
|
|
249
|
-
})
|
|
247
|
+
expect(inputElement.element.value).toBe("10,00");
|
|
248
|
+
});
|
|
250
249
|
|
|
251
|
-
it(
|
|
250
|
+
it("should use custom step value when provided", async () => {
|
|
252
251
|
const wrapper = mount(FzCurrencyInput, {
|
|
253
252
|
props: {
|
|
254
|
-
label:
|
|
253
|
+
label: "Label",
|
|
255
254
|
modelValue: 10,
|
|
256
|
-
|
|
255
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
257
256
|
step: 5,
|
|
258
257
|
},
|
|
259
|
-
})
|
|
258
|
+
});
|
|
260
259
|
|
|
261
|
-
const arrowUp = wrapper.find(
|
|
262
|
-
const inputElement = wrapper.find(
|
|
260
|
+
const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
|
|
261
|
+
const inputElement = wrapper.find("input");
|
|
263
262
|
|
|
264
|
-
await inputElement.trigger(
|
|
265
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
266
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
269
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
270
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
275
|
-
describe(
|
|
276
|
-
it(
|
|
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:
|
|
278
|
+
label: "Label",
|
|
280
279
|
modelValue: 10,
|
|
281
280
|
step: 2,
|
|
282
281
|
},
|
|
283
|
-
})
|
|
282
|
+
});
|
|
284
283
|
|
|
285
|
-
const arrowUp = wrapper.find(
|
|
286
|
-
const arrowDown = wrapper.find(
|
|
284
|
+
const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
|
|
285
|
+
const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
|
|
287
286
|
|
|
288
|
-
expect(arrowUp.attributes(
|
|
289
|
-
expect(arrowDown.attributes(
|
|
290
|
-
expect(arrowUp.attributes(
|
|
291
|
-
expect(arrowDown.attributes(
|
|
292
|
-
expect(arrowUp.attributes(
|
|
293
|
-
expect(arrowDown.attributes(
|
|
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(
|
|
295
|
+
it("should use custom aria-labels when provided", async () => {
|
|
297
296
|
const wrapper = mount(FzCurrencyInput, {
|
|
298
297
|
props: {
|
|
299
|
-
label:
|
|
298
|
+
label: "Label",
|
|
300
299
|
modelValue: 10,
|
|
301
300
|
step: 2,
|
|
302
|
-
stepUpAriaLabel:
|
|
303
|
-
stepDownAriaLabel:
|
|
301
|
+
stepUpAriaLabel: "Custom increment",
|
|
302
|
+
stepDownAriaLabel: "Custom decrement",
|
|
304
303
|
},
|
|
305
|
-
})
|
|
304
|
+
});
|
|
306
305
|
|
|
307
|
-
const arrowUp = wrapper.find(
|
|
308
|
-
const arrowDown = wrapper.find(
|
|
306
|
+
const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
|
|
307
|
+
const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
|
|
309
308
|
|
|
310
|
-
expect(arrowUp.attributes(
|
|
311
|
-
expect(arrowDown.attributes(
|
|
312
|
-
})
|
|
309
|
+
expect(arrowUp.attributes("aria-label")).toBe("Custom increment");
|
|
310
|
+
expect(arrowDown.attributes("aria-label")).toBe("Custom decrement");
|
|
311
|
+
});
|
|
313
312
|
|
|
314
|
-
it(
|
|
313
|
+
it("should disable step controls when disabled", async () => {
|
|
315
314
|
const wrapper = mount(FzCurrencyInput, {
|
|
316
315
|
props: {
|
|
317
|
-
label:
|
|
316
|
+
label: "Label",
|
|
318
317
|
modelValue: 10,
|
|
319
318
|
disabled: true,
|
|
320
319
|
},
|
|
321
|
-
})
|
|
320
|
+
});
|
|
322
321
|
|
|
323
|
-
const arrowUp = wrapper.find(
|
|
324
|
-
const arrowDown = wrapper.find(
|
|
322
|
+
const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
|
|
323
|
+
const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
|
|
325
324
|
|
|
326
|
-
expect(arrowUp.attributes(
|
|
327
|
-
expect(arrowDown.attributes(
|
|
328
|
-
expect(arrowUp.attributes(
|
|
329
|
-
expect(arrowDown.attributes(
|
|
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(
|
|
331
|
+
it("should disable step controls when readonly", async () => {
|
|
333
332
|
const wrapper = mount(FzCurrencyInput, {
|
|
334
333
|
props: {
|
|
335
|
-
label:
|
|
334
|
+
label: "Label",
|
|
336
335
|
modelValue: 10,
|
|
337
336
|
readonly: true,
|
|
338
337
|
},
|
|
339
|
-
})
|
|
338
|
+
});
|
|
340
339
|
|
|
341
|
-
const arrowUp = wrapper.find(
|
|
342
|
-
const arrowDown = wrapper.find(
|
|
340
|
+
const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
|
|
341
|
+
const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
|
|
343
342
|
|
|
344
|
-
expect(arrowUp.attributes(
|
|
345
|
-
expect(arrowDown.attributes(
|
|
346
|
-
expect(arrowUp.attributes(
|
|
347
|
-
expect(arrowDown.attributes(
|
|
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(
|
|
349
|
+
it("should trigger step on Enter key", async () => {
|
|
351
350
|
const wrapper = mount(FzCurrencyInput, {
|
|
352
351
|
props: {
|
|
353
|
-
label:
|
|
352
|
+
label: "Label",
|
|
354
353
|
modelValue: 10,
|
|
355
|
-
|
|
354
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
356
355
|
},
|
|
357
|
-
})
|
|
356
|
+
});
|
|
358
357
|
|
|
359
|
-
const arrowUp = wrapper.find(
|
|
360
|
-
const inputElement = wrapper.find(
|
|
358
|
+
const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
|
|
359
|
+
const inputElement = wrapper.find("input");
|
|
361
360
|
|
|
362
|
-
await inputElement.trigger(
|
|
363
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
364
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
367
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
368
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
370
|
+
it("should trigger step on Space key", async () => {
|
|
372
371
|
const wrapper = mount(FzCurrencyInput, {
|
|
373
372
|
props: {
|
|
374
|
-
label:
|
|
373
|
+
label: "Label",
|
|
375
374
|
modelValue: 10,
|
|
376
|
-
|
|
375
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
377
376
|
},
|
|
378
|
-
})
|
|
377
|
+
});
|
|
379
378
|
|
|
380
|
-
const arrowDown = wrapper.find(
|
|
381
|
-
const inputElement = wrapper.find(
|
|
379
|
+
const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
|
|
380
|
+
const inputElement = wrapper.find("input");
|
|
382
381
|
|
|
383
|
-
await inputElement.trigger(
|
|
384
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
385
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
388
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
389
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
394
|
-
it(
|
|
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:
|
|
396
|
+
label: "Label",
|
|
398
397
|
modelValue: 10,
|
|
399
398
|
valid: true,
|
|
400
399
|
},
|
|
401
|
-
})
|
|
400
|
+
});
|
|
402
401
|
|
|
403
|
-
const validIcon = wrapper.find(
|
|
404
|
-
expect(validIcon.exists()).toBe(true)
|
|
405
|
-
expect(validIcon.attributes(
|
|
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(
|
|
407
|
+
it("should display valid icon alongside step controls", async () => {
|
|
409
408
|
const wrapper = mount(FzCurrencyInput, {
|
|
410
409
|
props: {
|
|
411
|
-
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(
|
|
419
|
-
const arrowUp = wrapper.find(
|
|
420
|
-
const arrowDown = wrapper.find(
|
|
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(
|
|
430
|
-
it(
|
|
428
|
+
describe("v-model retrocompatibility", () => {
|
|
429
|
+
it("should accept number values", async () => {
|
|
431
430
|
const wrapper = mount(FzCurrencyInput, {
|
|
432
431
|
props: {
|
|
433
|
-
label:
|
|
432
|
+
label: "Label",
|
|
434
433
|
modelValue: 123.45,
|
|
435
|
-
|
|
434
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
436
435
|
},
|
|
437
|
-
})
|
|
436
|
+
});
|
|
438
437
|
|
|
439
|
-
const inputElement = wrapper.find(
|
|
440
|
-
await inputElement.trigger(
|
|
441
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
442
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
446
|
-
const consoleSpy = vi.spyOn(console,
|
|
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 =
|
|
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:
|
|
451
|
+
label: "Label",
|
|
453
452
|
modelValue,
|
|
454
|
-
|
|
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(
|
|
466
|
-
|
|
464
|
+
expect.stringContaining(
|
|
465
|
+
"[FzCurrencyInput] String values in v-model are deprecated",
|
|
466
|
+
),
|
|
467
|
+
);
|
|
467
468
|
|
|
468
|
-
const inputElement = wrapper.find(
|
|
469
|
-
await inputElement.trigger(
|
|
470
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
471
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
477
|
+
it("should accept undefined values", async () => {
|
|
477
478
|
const wrapper = mount(FzCurrencyInput, {
|
|
478
479
|
props: {
|
|
479
|
-
label:
|
|
480
|
+
label: "Label",
|
|
480
481
|
modelValue: undefined,
|
|
481
|
-
|
|
482
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
482
483
|
},
|
|
483
|
-
})
|
|
484
|
+
});
|
|
484
485
|
|
|
485
|
-
const inputElement = wrapper.find(
|
|
486
|
-
expect(inputElement.element.value).toBe(
|
|
487
|
-
})
|
|
486
|
+
const inputElement = wrapper.find("input");
|
|
487
|
+
expect(inputElement.element.value).toBe("");
|
|
488
|
+
});
|
|
488
489
|
|
|
489
|
-
it(
|
|
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:
|
|
495
|
+
label: "Label",
|
|
495
496
|
modelValue: undefined,
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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(
|
|
504
|
-
await inputElement.setValue(
|
|
505
|
-
await inputElement.trigger(
|
|
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(
|
|
509
|
-
expect(emittedValue).toBe(123.45)
|
|
510
|
-
})
|
|
509
|
+
expect(typeof emittedValue).toBe("number");
|
|
510
|
+
expect(emittedValue).toBe(123.45);
|
|
511
|
+
});
|
|
511
512
|
|
|
512
|
-
it(
|
|
513
|
-
const consoleSpy = vi.spyOn(console,
|
|
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 =
|
|
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:
|
|
520
|
+
label: "Label",
|
|
520
521
|
modelValue,
|
|
521
|
-
|
|
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(
|
|
533
|
-
await inputElement.trigger(
|
|
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(
|
|
537
|
+
expect(inputElement.element.value).toBe("1.234,56");
|
|
537
538
|
|
|
538
|
-
consoleSpy.mockRestore()
|
|
539
|
-
})
|
|
539
|
+
consoleSpy.mockRestore();
|
|
540
|
+
});
|
|
540
541
|
|
|
541
|
-
it(
|
|
542
|
-
const consoleSpy = vi.spyOn(console,
|
|
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 =
|
|
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:
|
|
561
|
+
label: "Label",
|
|
561
562
|
modelValue,
|
|
562
|
-
|
|
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(
|
|
574
|
-
await inputElement.trigger(
|
|
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(
|
|
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,
|
|
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 =
|
|
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:
|
|
600
|
+
label: "Label",
|
|
600
601
|
modelValue,
|
|
601
|
-
|
|
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(
|
|
613
|
-
await inputElement.trigger(
|
|
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(
|
|
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(
|
|
620
|
-
expect(modelValue).toBeCloseTo(1234.56, 2)
|
|
621
|
-
|
|
622
|
-
consoleSpy.mockRestore()
|
|
623
|
-
})
|
|
624
|
-
})
|
|
625
|
-
|
|
626
|
-
describe(
|
|
627
|
-
describe(
|
|
628
|
-
it(
|
|
629
|
-
const consoleSpy = vi
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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:
|
|
638
|
+
label: "Label",
|
|
636
639
|
modelValue,
|
|
637
|
-
|
|
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(
|
|
649
|
-
await inputElement.trigger(
|
|
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(
|
|
658
|
-
const consoleSpy = vi
|
|
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 =
|
|
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:
|
|
669
|
+
label: "Label",
|
|
665
670
|
modelValue,
|
|
666
|
-
|
|
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(
|
|
678
|
-
await inputElement.trigger(
|
|
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(
|
|
687
|
-
const consoleSpy = vi
|
|
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 =
|
|
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:
|
|
700
|
+
label: "Label",
|
|
694
701
|
modelValue,
|
|
695
|
-
|
|
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(
|
|
707
|
-
await inputElement.trigger(
|
|
708
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
709
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
715
|
-
const consoleSpy = vi
|
|
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 =
|
|
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:
|
|
730
|
+
label: "Label",
|
|
722
731
|
modelValue,
|
|
723
|
-
|
|
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(
|
|
735
|
-
await inputElement.trigger(
|
|
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(
|
|
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(
|
|
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(
|
|
747
|
-
const consoleSpy = vi
|
|
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:
|
|
764
|
+
label: "Label",
|
|
754
765
|
modelValue,
|
|
755
|
-
|
|
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(
|
|
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(
|
|
772
|
-
const consoleSpy = vi
|
|
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:
|
|
791
|
+
label: "Label",
|
|
779
792
|
modelValue,
|
|
780
|
-
|
|
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(
|
|
791
|
-
await inputElement.trigger(
|
|
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(
|
|
801
|
-
it(
|
|
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:
|
|
819
|
+
label: "Label",
|
|
807
820
|
modelValue,
|
|
808
|
-
|
|
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(
|
|
816
|
-
await inputElement.trigger(
|
|
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(
|
|
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:
|
|
839
|
+
label: "Label",
|
|
827
840
|
modelValue,
|
|
828
841
|
nullOnEmpty: true,
|
|
829
|
-
|
|
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(
|
|
837
|
-
await inputElement.setValue(
|
|
838
|
-
await inputElement.trigger(
|
|
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(
|
|
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:
|
|
863
|
+
label: "Label",
|
|
851
864
|
modelValue,
|
|
852
865
|
nullOnEmpty: true,
|
|
853
|
-
|
|
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(
|
|
865
|
-
await inputElement.setValue(
|
|
866
|
-
await inputElement.trigger(
|
|
867
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
868
|
-
|
|
869
|
-
expect(inputElement.element.value).toBe(
|
|
870
|
-
expect(emittedValue).toBe(0)
|
|
871
|
-
expect(emittedValue).not.toBeNull()
|
|
872
|
-
})
|
|
873
|
-
|
|
874
|
-
it(
|
|
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:
|
|
893
|
+
label: "Label",
|
|
881
894
|
modelValue,
|
|
882
895
|
nullOnEmpty: true,
|
|
883
|
-
|
|
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(
|
|
895
|
-
await inputElement.setValue(
|
|
896
|
-
await inputElement.trigger(
|
|
897
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
898
|
-
|
|
899
|
-
expect(inputElement.element.value).toBe(
|
|
900
|
-
expect(emittedValue).toBe(0)
|
|
901
|
-
expect(emittedValue).not.toBeNull()
|
|
902
|
-
})
|
|
903
|
-
})
|
|
904
|
-
|
|
905
|
-
describe(
|
|
906
|
-
it(
|
|
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:
|
|
922
|
+
label: "Label",
|
|
910
923
|
modelValue: -123.45,
|
|
911
|
-
|
|
924
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
912
925
|
},
|
|
913
|
-
})
|
|
926
|
+
});
|
|
914
927
|
|
|
915
|
-
const inputElement = wrapper.find(
|
|
916
|
-
await inputElement.trigger(
|
|
917
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
918
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
934
|
+
it("should handle negative values with step controls", async () => {
|
|
922
935
|
const wrapper = mount(FzCurrencyInput, {
|
|
923
936
|
props: {
|
|
924
|
-
label:
|
|
937
|
+
label: "Label",
|
|
925
938
|
modelValue: -10,
|
|
926
|
-
|
|
939
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
927
940
|
step: 5,
|
|
928
941
|
},
|
|
929
|
-
})
|
|
942
|
+
});
|
|
930
943
|
|
|
931
|
-
const inputElement = wrapper.find(
|
|
932
|
-
const arrowUp = wrapper.find(
|
|
933
|
-
const arrowDown = wrapper.find(
|
|
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(
|
|
936
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
937
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
940
|
-
await wrapper.vm.$nextTick()
|
|
941
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
942
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
945
|
-
await wrapper.vm.$nextTick()
|
|
946
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
947
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
950
|
-
await wrapper.vm.$nextTick()
|
|
951
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
952
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
968
|
+
it("should handle negative values crossing zero with step controls", async () => {
|
|
956
969
|
const wrapper = mount(FzCurrencyInput, {
|
|
957
970
|
props: {
|
|
958
|
-
label:
|
|
971
|
+
label: "Label",
|
|
959
972
|
modelValue: -2,
|
|
960
|
-
|
|
973
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
961
974
|
step: 5,
|
|
962
975
|
},
|
|
963
|
-
})
|
|
976
|
+
});
|
|
964
977
|
|
|
965
|
-
const inputElement = wrapper.find(
|
|
966
|
-
const arrowUp = wrapper.find(
|
|
978
|
+
const inputElement = wrapper.find("input");
|
|
979
|
+
const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
|
|
967
980
|
|
|
968
|
-
await inputElement.trigger(
|
|
969
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
970
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
973
|
-
await wrapper.vm.$nextTick()
|
|
974
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
975
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
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:
|
|
995
|
+
label: "Label",
|
|
983
996
|
modelValue,
|
|
984
|
-
|
|
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(
|
|
996
|
-
await inputElement.trigger(
|
|
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(
|
|
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(
|
|
1005
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1006
|
-
expect(inputElement.element.value).toBe(
|
|
1007
|
-
expect(modelValue).toBe(-123.45)
|
|
1008
|
-
})
|
|
1009
|
-
|
|
1010
|
-
it(
|
|
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:
|
|
1027
|
+
label: "Label",
|
|
1015
1028
|
modelValue,
|
|
1016
|
-
|
|
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(
|
|
1029
|
-
await inputElement.trigger(
|
|
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(
|
|
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(
|
|
1038
|
-
await inputElement.trigger(
|
|
1039
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1040
|
-
expect(inputElement.element.value).toBe(
|
|
1041
|
-
expect(modelValue).toBe(-123.45)
|
|
1042
|
-
})
|
|
1043
|
-
})
|
|
1044
|
-
|
|
1045
|
-
describe(
|
|
1046
|
-
it(
|
|
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:
|
|
1064
|
+
label: "Label",
|
|
1052
1065
|
modelValue,
|
|
1053
|
-
|
|
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(
|
|
1062
|
-
await inputElement.trigger(
|
|
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(
|
|
1066
|
-
})
|
|
1078
|
+
expect(inputElement.element.value).toBe("123,45");
|
|
1079
|
+
});
|
|
1067
1080
|
|
|
1068
|
-
it(
|
|
1081
|
+
it("should handle values with minimumFractionDigits", async () => {
|
|
1069
1082
|
const wrapper = mount(FzCurrencyInput, {
|
|
1070
1083
|
props: {
|
|
1071
|
-
label:
|
|
1084
|
+
label: "Label",
|
|
1072
1085
|
modelValue: 123,
|
|
1073
|
-
|
|
1086
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
1074
1087
|
minimumFractionDigits: 2,
|
|
1075
1088
|
},
|
|
1076
|
-
})
|
|
1089
|
+
});
|
|
1077
1090
|
|
|
1078
|
-
const inputElement = wrapper.find(
|
|
1079
|
-
await inputElement.trigger(
|
|
1080
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1081
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
1086
|
-
it(
|
|
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:
|
|
1102
|
+
label: "Label",
|
|
1090
1103
|
modelValue: 10,
|
|
1091
|
-
|
|
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(
|
|
1099
|
-
const arrowDown = wrapper.find(
|
|
1111
|
+
const inputElement = wrapper.find("input");
|
|
1112
|
+
const arrowDown = wrapper.find(".fz__currencyinput__arrowdown");
|
|
1100
1113
|
|
|
1101
|
-
await inputElement.trigger(
|
|
1102
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1103
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
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(
|
|
1110
|
-
expect(wrapper.props(
|
|
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(
|
|
1114
|
-
await inputElement.trigger(
|
|
1115
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1116
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
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:
|
|
1135
|
+
label: "Label",
|
|
1123
1136
|
modelValue: 99,
|
|
1124
|
-
|
|
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(
|
|
1132
|
-
const arrowUp = wrapper.find(
|
|
1144
|
+
const inputElement = wrapper.find("input");
|
|
1145
|
+
const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
|
|
1133
1146
|
|
|
1134
|
-
await inputElement.trigger(
|
|
1135
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1136
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
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(
|
|
1144
|
-
expect(wrapper.props(
|
|
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(
|
|
1148
|
-
await wrapper.vm.$nextTick()
|
|
1149
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
1150
|
-
expect(inputElement.element.value).toBe(
|
|
1151
|
-
expect(wrapper.props(
|
|
1152
|
-
})
|
|
1153
|
-
})
|
|
1154
|
-
|
|
1155
|
-
describe(
|
|
1156
|
-
it(
|
|
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:
|
|
1174
|
+
label: "Label",
|
|
1162
1175
|
modelValue,
|
|
1163
|
-
|
|
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(
|
|
1171
|
-
await inputElement.trigger(
|
|
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(
|
|
1175
|
-
})
|
|
1187
|
+
expect(inputElement.element.value).toBe("999.999.999,99");
|
|
1188
|
+
});
|
|
1176
1189
|
|
|
1177
|
-
it(
|
|
1190
|
+
it("should handle very small numbers", async () => {
|
|
1178
1191
|
const wrapper = mount(FzCurrencyInput, {
|
|
1179
1192
|
props: {
|
|
1180
|
-
label:
|
|
1193
|
+
label: "Label",
|
|
1181
1194
|
modelValue: 0.01,
|
|
1182
|
-
|
|
1195
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
1183
1196
|
},
|
|
1184
|
-
})
|
|
1197
|
+
});
|
|
1185
1198
|
|
|
1186
|
-
const inputElement = wrapper.find(
|
|
1187
|
-
await inputElement.trigger(
|
|
1188
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1189
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
1205
|
+
it("should handle zero", async () => {
|
|
1193
1206
|
const wrapper = mount(FzCurrencyInput, {
|
|
1194
1207
|
props: {
|
|
1195
|
-
label:
|
|
1208
|
+
label: "Label",
|
|
1196
1209
|
modelValue: 0,
|
|
1197
|
-
|
|
1210
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
1198
1211
|
},
|
|
1199
|
-
})
|
|
1200
|
-
|
|
1201
|
-
const inputElement = wrapper.find(
|
|
1202
|
-
await inputElement.trigger(
|
|
1203
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1204
|
-
expect(inputElement.element.value).toBe(
|
|
1205
|
-
})
|
|
1206
|
-
})
|
|
1207
|
-
|
|
1208
|
-
describe(
|
|
1209
|
-
it(
|
|
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:
|
|
1227
|
+
label: "Label",
|
|
1215
1228
|
modelValue,
|
|
1216
|
-
|
|
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(
|
|
1226
|
-
await inputElement.setValue(
|
|
1227
|
-
await inputElement.trigger(
|
|
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([
|
|
1238
|
-
|
|
1250
|
+
expect(["-3,00", "-4,00", "-0,00", "0,00", "4,00"]).toContain(
|
|
1251
|
+
actualValue,
|
|
1252
|
+
);
|
|
1253
|
+
});
|
|
1239
1254
|
|
|
1240
|
-
it(
|
|
1255
|
+
it("should handle forceStep with value exactly on step", async () => {
|
|
1241
1256
|
const wrapper = mount(FzCurrencyInput, {
|
|
1242
1257
|
props: {
|
|
1243
|
-
label:
|
|
1258
|
+
label: "Label",
|
|
1244
1259
|
modelValue: 8,
|
|
1245
|
-
|
|
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(
|
|
1252
|
-
await inputElement.trigger(
|
|
1253
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1254
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
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:
|
|
1277
|
+
label: "Label",
|
|
1263
1278
|
modelValue,
|
|
1264
|
-
|
|
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(
|
|
1274
|
-
await inputElement.setValue(
|
|
1275
|
-
await inputElement.trigger(
|
|
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(
|
|
1280
|
-
})
|
|
1294
|
+
expect(inputElement.element.value).toBe("1,50");
|
|
1295
|
+
});
|
|
1281
1296
|
|
|
1282
|
-
it(
|
|
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:
|
|
1302
|
+
label: "Label",
|
|
1288
1303
|
modelValue,
|
|
1289
|
-
|
|
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(
|
|
1299
|
-
await inputElement.setValue(
|
|
1300
|
-
await inputElement.trigger(
|
|
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(
|
|
1305
|
-
})
|
|
1319
|
+
expect(inputElement.element.value).toBe("2,00");
|
|
1320
|
+
});
|
|
1306
1321
|
|
|
1307
|
-
it(
|
|
1322
|
+
it("should handle step controls with decimal step", async () => {
|
|
1308
1323
|
const wrapper = mount(FzCurrencyInput, {
|
|
1309
1324
|
props: {
|
|
1310
|
-
label:
|
|
1325
|
+
label: "Label",
|
|
1311
1326
|
modelValue: 10.5,
|
|
1312
|
-
|
|
1327
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
1313
1328
|
step: 0.25,
|
|
1314
1329
|
},
|
|
1315
|
-
})
|
|
1330
|
+
});
|
|
1316
1331
|
|
|
1317
|
-
const inputElement = wrapper.find(
|
|
1318
|
-
const arrowUp = wrapper.find(
|
|
1319
|
-
const arrowDown = wrapper.find(
|
|
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(
|
|
1322
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1323
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
1326
|
-
await wrapper.vm.$nextTick()
|
|
1327
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
1328
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
1331
|
-
await wrapper.vm.$nextTick()
|
|
1332
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
1333
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
1351
|
+
it("should handle step controls producing decimal values", async () => {
|
|
1337
1352
|
const wrapper = mount(FzCurrencyInput, {
|
|
1338
1353
|
props: {
|
|
1339
|
-
label:
|
|
1354
|
+
label: "Label",
|
|
1340
1355
|
modelValue: 10,
|
|
1341
|
-
|
|
1356
|
+
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
|
|
1342
1357
|
step: 0.1,
|
|
1343
1358
|
},
|
|
1344
|
-
})
|
|
1359
|
+
});
|
|
1345
1360
|
|
|
1346
|
-
const inputElement = wrapper.find(
|
|
1347
|
-
const arrowUp = wrapper.find(
|
|
1361
|
+
const inputElement = wrapper.find("input");
|
|
1362
|
+
const arrowUp = wrapper.find(".fz__currencyinput__arrowup");
|
|
1348
1363
|
|
|
1349
|
-
await inputElement.trigger(
|
|
1350
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1351
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
1354
|
-
await wrapper.vm.$nextTick()
|
|
1355
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
1356
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
1374
|
+
it("should handle forceStep with small decimal step", async () => {
|
|
1360
1375
|
const wrapper = mount(FzCurrencyInput, {
|
|
1361
1376
|
props: {
|
|
1362
|
-
label:
|
|
1377
|
+
label: "Label",
|
|
1363
1378
|
modelValue: 1.23,
|
|
1364
|
-
|
|
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(
|
|
1371
|
-
await inputElement.setValue(
|
|
1372
|
-
await inputElement.trigger(
|
|
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(
|
|
1376
|
-
})
|
|
1390
|
+
expect(inputElement.element.value).toBe("1,23");
|
|
1391
|
+
});
|
|
1377
1392
|
|
|
1378
|
-
it(
|
|
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:
|
|
1398
|
+
label: "Label",
|
|
1384
1399
|
modelValue,
|
|
1385
|
-
|
|
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(
|
|
1395
|
-
await inputElement.setValue(
|
|
1396
|
-
await inputElement.trigger(
|
|
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(
|
|
1401
|
-
})
|
|
1415
|
+
expect(inputElement.element.value).toBe("1,25");
|
|
1416
|
+
});
|
|
1402
1417
|
|
|
1403
|
-
it(
|
|
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:
|
|
1421
|
+
label: "Label",
|
|
1407
1422
|
modelValue: undefined,
|
|
1408
|
-
|
|
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(
|
|
1430
|
+
const inputElement = wrapper.find("input");
|
|
1416
1431
|
// User types "3" which is not a valid step (step is 2)
|
|
1417
|
-
await inputElement.setValue(
|
|
1418
|
-
await inputElement.trigger(
|
|
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(
|
|
1422
|
-
expect(wrapper.props(
|
|
1423
|
-
})
|
|
1436
|
+
expect(inputElement.element.value).toBe("4,00");
|
|
1437
|
+
expect(wrapper.props("modelValue")).toBe(4);
|
|
1438
|
+
});
|
|
1424
1439
|
|
|
1425
|
-
it(
|
|
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:
|
|
1443
|
+
label: "Label",
|
|
1429
1444
|
modelValue: undefined,
|
|
1430
|
-
|
|
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(
|
|
1452
|
+
const inputElement = wrapper.find("input");
|
|
1438
1453
|
// User types "1" which is not a valid step (step is 2)
|
|
1439
|
-
await inputElement.setValue(
|
|
1440
|
-
await inputElement.trigger(
|
|
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(
|
|
1445
|
-
expect(wrapper.props(
|
|
1446
|
-
})
|
|
1459
|
+
expect(inputElement.element.value).toBe("2,00");
|
|
1460
|
+
expect(wrapper.props("modelValue")).toBe(2);
|
|
1461
|
+
});
|
|
1447
1462
|
|
|
1448
|
-
it(
|
|
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:
|
|
1466
|
+
label: "Label",
|
|
1452
1467
|
modelValue: 0,
|
|
1453
|
-
|
|
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(
|
|
1461
|
-
const arrowUp = wrapper.find(
|
|
1462
|
-
const arrowDown = wrapper.find(
|
|
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(
|
|
1465
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1466
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
1470
|
-
await wrapper.vm.$nextTick()
|
|
1471
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
1472
|
-
expect(inputElement.element.value).toBe(
|
|
1473
|
-
expect(wrapper.props(
|
|
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(
|
|
1477
|
-
await wrapper.vm.$nextTick()
|
|
1478
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
1479
|
-
expect(inputElement.element.value).toBe(
|
|
1480
|
-
expect(wrapper.props(
|
|
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(
|
|
1484
|
-
await wrapper.vm.$nextTick()
|
|
1485
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
1486
|
-
expect(inputElement.element.value).toBe(
|
|
1487
|
-
expect(wrapper.props(
|
|
1488
|
-
})
|
|
1489
|
-
|
|
1490
|
-
it(
|
|
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:
|
|
1508
|
+
label: "Label",
|
|
1494
1509
|
modelValue: 1,
|
|
1495
|
-
|
|
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(
|
|
1503
|
-
const arrowUp = wrapper.find(
|
|
1504
|
-
const arrowDown = wrapper.find(
|
|
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(
|
|
1507
|
-
await new Promise((resolve) => window.setTimeout(resolve, 100))
|
|
1508
|
-
expect(inputElement.element.value).toBe(
|
|
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(
|
|
1512
|
-
await wrapper.vm.$nextTick()
|
|
1513
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
1514
|
-
expect(inputElement.element.value).toBe(
|
|
1515
|
-
expect(wrapper.props(
|
|
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(
|
|
1519
|
-
await wrapper.vm.$nextTick()
|
|
1520
|
-
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
|
1521
|
-
expect(inputElement.element.value).toBe(
|
|
1522
|
-
expect(wrapper.props(
|
|
1523
|
-
})
|
|
1524
|
-
})
|
|
1525
|
-
|
|
1526
|
-
describe(
|
|
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
|
-
[
|
|
1529
|
-
[
|
|
1530
|
-
[
|
|
1531
|
-
[
|
|
1532
|
-
[
|
|
1533
|
-
[
|
|
1534
|
-
[
|
|
1535
|
-
])(
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
modelValue
|
|
1544
|
-
|
|
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
|
-
|
|
1566
|
+
const inputElement = wrapper.find("input");
|
|
1550
1567
|
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
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
|
-
|
|
1559
|
-
|
|
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,
|
|
1564
|
-
[40.2,
|
|
1565
|
-
[40.4,
|
|
1566
|
-
[299.96,
|
|
1567
|
-
[1234567.89,
|
|
1568
|
-
])(
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
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
|
-
|
|
1578
|
-
|
|
1597
|
+
await wrapper.vm.$nextTick();
|
|
1598
|
+
await new Promise((resolve) => window.setTimeout(resolve, 100));
|
|
1579
1599
|
|
|
1580
|
-
|
|
1600
|
+
const inputElement = wrapper.find("input");
|
|
1581
1601
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1602
|
+
// Focus to see raw value (without thousand separators)
|
|
1603
|
+
await inputElement.trigger("focus");
|
|
1604
|
+
await wrapper.vm.$nextTick();
|
|
1585
1605
|
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1606
|
+
const rawExpected = expectedRaw.replace(/\./g, "");
|
|
1607
|
+
expect(inputElement.element.value).toBe(rawExpected);
|
|
1608
|
+
},
|
|
1609
|
+
);
|
|
1610
|
+
});
|
|
1611
|
+
});
|
|
1591
1612
|
|
|
1592
|
-
describe(
|
|
1593
|
-
it(
|
|
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:
|
|
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(
|
|
1628
|
+
it("should render help text span when helpText slot is provided", async () => {
|
|
1608
1629
|
const wrapper = mount(FzCurrencyInput, {
|
|
1609
1630
|
props: {
|
|
1610
|
-
label:
|
|
1631
|
+
label: "Label",
|
|
1611
1632
|
modelValue: 10,
|
|
1612
1633
|
},
|
|
1613
1634
|
slots: {
|
|
1614
|
-
helpText:
|
|
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(
|
|
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(
|
|
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:
|
|
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(
|
|
1636
|
-
expect(label.exists()).toBe(true)
|
|
1637
|
-
expect(label.text()).toContain(
|
|
1638
|
-
})
|
|
1656
|
+
const label = wrapper.find("label");
|
|
1657
|
+
expect(label.exists()).toBe(true);
|
|
1658
|
+
expect(label.text()).toContain("Amount");
|
|
1659
|
+
});
|
|
1639
1660
|
|
|
1640
|
-
it(
|
|
1661
|
+
it("should render custom label slot instead of label prop", async () => {
|
|
1641
1662
|
const wrapper = mount(FzCurrencyInput, {
|
|
1642
1663
|
props: {
|
|
1643
|
-
label:
|
|
1664
|
+
label: "Amount",
|
|
1644
1665
|
modelValue: 10,
|
|
1645
1666
|
},
|
|
1646
1667
|
slots: {
|
|
1647
|
-
label:
|
|
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(
|
|
1654
|
-
expect(strong.exists()).toBe(true)
|
|
1655
|
-
expect(strong.text()).toBe(
|
|
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(
|
|
1658
|
-
expect(defaultLabel.exists()).toBe(false)
|
|
1659
|
-
})
|
|
1678
|
+
const defaultLabel = wrapper.find("label");
|
|
1679
|
+
expect(defaultLabel.exists()).toBe(false);
|
|
1680
|
+
});
|
|
1660
1681
|
|
|
1661
|
-
it(
|
|
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:
|
|
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(
|
|
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:
|
|
1700
|
+
label: "Label",
|
|
1680
1701
|
modelValue: 10,
|
|
1681
1702
|
error: true,
|
|
1682
1703
|
},
|
|
1683
1704
|
slots: {
|
|
1684
|
-
errorMessage:
|
|
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
|
-
|
|
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
|
-
|
|
1691
|
-
|
|
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
|
+
});
|