@fiscozen/input 0.1.17 → 1.0.0-next.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/README.md +718 -1
- 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 +5282 -2948
- package/dist/input.umd.cjs +15 -10
- package/dist/src/FzCurrencyInput.vue.d.ts +82 -46
- package/dist/src/FzInput.vue.d.ts +141 -42
- package/dist/src/index.d.ts +1 -0
- package/dist/src/types.d.ts +172 -36
- package/dist/src/useInputStyle.d.ts +23 -8
- package/dist/src/utils.d.ts +21 -0
- package/dist/style.css +1 -0
- package/package.json +6 -6
- package/src/FzCurrencyInput.vue +746 -106
- package/src/FzInput.vue +467 -97
- package/src/__tests__/FzCurrencyInput.test.ts +1528 -0
- package/src/__tests__/FzInput.test.ts +1005 -0
- package/src/index.ts +3 -0
- package/src/types.ts +171 -46
- package/src/useInputStyle.ts +96 -38
- package/src/utils.ts +64 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/src/__tests__/FzCurrencyInput.spec.ts +0 -204
- package/src/__tests__/FzInput.spec.ts +0 -181
|
@@ -0,0 +1,1005 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import { FzInput } from '..'
|
|
4
|
+
|
|
5
|
+
const NUMBER_OF_INPUTS = 1000
|
|
6
|
+
|
|
7
|
+
describe('FzInput', () => {
|
|
8
|
+
describe('Rendering', () => {
|
|
9
|
+
it('renders label', async () => {
|
|
10
|
+
const wrapper = mount(FzInput, {
|
|
11
|
+
props: {
|
|
12
|
+
label: 'Label',
|
|
13
|
+
},
|
|
14
|
+
slots: {},
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
expect(wrapper.text()).toContain('Label')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('renders leftIcon', async () => {
|
|
21
|
+
const wrapper = mount(FzInput, {
|
|
22
|
+
props: {
|
|
23
|
+
label: 'Label',
|
|
24
|
+
leftIcon: 'calendar-lines',
|
|
25
|
+
},
|
|
26
|
+
slots: {},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
expect(wrapper.find('.fa-calendar-lines')).toBeTruthy()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('renders rightIcon', async () => {
|
|
33
|
+
const wrapper = mount(FzInput, {
|
|
34
|
+
props: {
|
|
35
|
+
label: 'Label',
|
|
36
|
+
rightIcon: 'credit-card',
|
|
37
|
+
},
|
|
38
|
+
slots: {},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
expect(wrapper.find('.fa-credit-card')).toBeTruthy()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('renders helpText', async () => {
|
|
45
|
+
const wrapper = mount(FzInput, {
|
|
46
|
+
props: {
|
|
47
|
+
label: 'Label',
|
|
48
|
+
},
|
|
49
|
+
slots: {
|
|
50
|
+
helpText: 'This is a helper text',
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
await wrapper.vm.$nextTick()
|
|
55
|
+
expect(wrapper.text()).toContain('This is a helper text')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('renders errorMessage', async () => {
|
|
59
|
+
const wrapper = mount(FzInput, {
|
|
60
|
+
props: {
|
|
61
|
+
label: 'Label',
|
|
62
|
+
error: true,
|
|
63
|
+
},
|
|
64
|
+
slots: {
|
|
65
|
+
errorMessage: 'This is an error message',
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
await wrapper.vm.$nextTick()
|
|
70
|
+
expect(wrapper.text()).toContain('This is an error message')
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
describe('Input types', () => {
|
|
75
|
+
it('renders email type', async () => {
|
|
76
|
+
const wrapper = mount(FzInput, {
|
|
77
|
+
props: {
|
|
78
|
+
label: 'Label',
|
|
79
|
+
type: 'email',
|
|
80
|
+
},
|
|
81
|
+
slots: {},
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
expect(wrapper.find('input').attributes('type')).toBe('email')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('renders tel type', async () => {
|
|
88
|
+
const wrapper = mount(FzInput, {
|
|
89
|
+
props: {
|
|
90
|
+
label: 'Label',
|
|
91
|
+
type: 'tel',
|
|
92
|
+
},
|
|
93
|
+
slots: {},
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
expect(wrapper.find('input').attributes('type')).toBe('tel')
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('renders password type', async () => {
|
|
100
|
+
const wrapper = mount(FzInput, {
|
|
101
|
+
props: {
|
|
102
|
+
label: 'Label',
|
|
103
|
+
type: 'password',
|
|
104
|
+
},
|
|
105
|
+
slots: {},
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
expect(wrapper.find('input').attributes('type')).toBe('password')
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
describe('Input states', () => {
|
|
113
|
+
it('renders disabled', async () => {
|
|
114
|
+
const wrapper = mount(FzInput, {
|
|
115
|
+
props: {
|
|
116
|
+
label: 'Label',
|
|
117
|
+
disabled: true,
|
|
118
|
+
},
|
|
119
|
+
slots: {},
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
expect(wrapper.find('input').attributes('disabled')).toBe('')
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('renders required', async () => {
|
|
126
|
+
const wrapper = mount(FzInput, {
|
|
127
|
+
props: {
|
|
128
|
+
label: 'Label',
|
|
129
|
+
required: true,
|
|
130
|
+
},
|
|
131
|
+
slots: {},
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
await wrapper.vm.$nextTick()
|
|
135
|
+
|
|
136
|
+
expect(wrapper.find('input').attributes('required')).toBe('')
|
|
137
|
+
expect(wrapper.text()).toContain('*')
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
describe('Events', () => {
|
|
142
|
+
it('emits fzinput:right-icon-click event', async () => {
|
|
143
|
+
const wrapper = mount(FzInput, {
|
|
144
|
+
props: {
|
|
145
|
+
label: 'Label',
|
|
146
|
+
rightIcon: 'eye',
|
|
147
|
+
},
|
|
148
|
+
slots: {},
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
await wrapper.find('.fa-eye').trigger('click')
|
|
152
|
+
|
|
153
|
+
expect(wrapper.emitted('fzinput:right-icon-click')).toBeTruthy()
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('emits fzinput:left-icon-click event', async () => {
|
|
157
|
+
const wrapper = mount(FzInput, {
|
|
158
|
+
props: {
|
|
159
|
+
label: 'Label',
|
|
160
|
+
leftIcon: 'eye',
|
|
161
|
+
},
|
|
162
|
+
slots: {},
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
await wrapper.find('.fa-eye').trigger('click')
|
|
166
|
+
|
|
167
|
+
expect(wrapper.emitted('fzinput:left-icon-click')).toBeTruthy()
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('does not emit fzinput:right-icon-click event when disabled', async () => {
|
|
171
|
+
const wrapper = mount(FzInput, {
|
|
172
|
+
props: {
|
|
173
|
+
label: 'Label',
|
|
174
|
+
rightIcon: 'eye',
|
|
175
|
+
disabled: true,
|
|
176
|
+
},
|
|
177
|
+
slots: {},
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
await wrapper.find('.fa-eye').trigger('click')
|
|
181
|
+
|
|
182
|
+
expect(wrapper.emitted('fzinput:right-icon-click')).toBeFalsy()
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('does not emit fzinput:right-icon-click event when readonly', async () => {
|
|
186
|
+
const wrapper = mount(FzInput, {
|
|
187
|
+
props: {
|
|
188
|
+
label: 'Label',
|
|
189
|
+
rightIcon: 'eye',
|
|
190
|
+
readonly: true,
|
|
191
|
+
},
|
|
192
|
+
slots: {},
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
await wrapper.find('.fa-eye').trigger('click')
|
|
196
|
+
|
|
197
|
+
expect(wrapper.emitted('fzinput:right-icon-click')).toBeFalsy()
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('does not emit fzinput:left-icon-click event when disabled', async () => {
|
|
201
|
+
const wrapper = mount(FzInput, {
|
|
202
|
+
props: {
|
|
203
|
+
label: 'Label',
|
|
204
|
+
leftIcon: 'eye',
|
|
205
|
+
disabled: true,
|
|
206
|
+
},
|
|
207
|
+
slots: {},
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
await wrapper.find('.fa-eye').trigger('click')
|
|
211
|
+
|
|
212
|
+
expect(wrapper.emitted('fzinput:left-icon-click')).toBeFalsy()
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('does not emit fzinput:left-icon-click event when readonly', async () => {
|
|
216
|
+
const wrapper = mount(FzInput, {
|
|
217
|
+
props: {
|
|
218
|
+
label: 'Label',
|
|
219
|
+
leftIcon: 'eye',
|
|
220
|
+
readonly: true,
|
|
221
|
+
},
|
|
222
|
+
slots: {},
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
await wrapper.find('.fa-eye').trigger('click')
|
|
226
|
+
|
|
227
|
+
expect(wrapper.emitted('fzinput:left-icon-click')).toBeFalsy()
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('does not emit fzinput:second-right-icon-click event when disabled', async () => {
|
|
231
|
+
const wrapper = mount(FzInput, {
|
|
232
|
+
props: {
|
|
233
|
+
label: 'Label',
|
|
234
|
+
secondRightIcon: 'eye',
|
|
235
|
+
disabled: true,
|
|
236
|
+
},
|
|
237
|
+
slots: {},
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
await wrapper.find('.fa-eye').trigger('click')
|
|
241
|
+
|
|
242
|
+
expect(wrapper.emitted('fzinput:second-right-icon-click')).toBeFalsy()
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('does not emit fzinput:second-right-icon-click event when readonly', async () => {
|
|
246
|
+
const wrapper = mount(FzInput, {
|
|
247
|
+
props: {
|
|
248
|
+
label: 'Label',
|
|
249
|
+
secondRightIcon: 'eye',
|
|
250
|
+
readonly: true,
|
|
251
|
+
},
|
|
252
|
+
slots: {},
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
await wrapper.find('.fa-eye').trigger('click')
|
|
256
|
+
|
|
257
|
+
expect(wrapper.emitted('fzinput:second-right-icon-click')).toBeFalsy()
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
it('does not emit fzinput:right-icon-click event when disabled and rightIconButton is true', async () => {
|
|
261
|
+
const wrapper = mount(FzInput, {
|
|
262
|
+
props: {
|
|
263
|
+
label: 'Label',
|
|
264
|
+
rightIcon: 'eye',
|
|
265
|
+
rightIconButton: true,
|
|
266
|
+
disabled: true,
|
|
267
|
+
},
|
|
268
|
+
slots: {},
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
const button = wrapper.findComponent({ name: 'FzIconButton' })
|
|
272
|
+
await button.trigger('click')
|
|
273
|
+
|
|
274
|
+
expect(wrapper.emitted('fzinput:right-icon-click')).toBeFalsy()
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it('does not emit fzinput:second-right-icon-click event when disabled and secondRightIconButton is true', async () => {
|
|
278
|
+
const wrapper = mount(FzInput, {
|
|
279
|
+
props: {
|
|
280
|
+
label: 'Label',
|
|
281
|
+
secondRightIcon: 'eye',
|
|
282
|
+
secondRightIconButton: true,
|
|
283
|
+
disabled: true,
|
|
284
|
+
},
|
|
285
|
+
slots: {},
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
const buttons = wrapper.findAllComponents({ name: 'FzIconButton' })
|
|
289
|
+
await buttons[0].trigger('click')
|
|
290
|
+
|
|
291
|
+
expect(wrapper.emitted('fzinput:second-right-icon-click')).toBeFalsy()
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
describe('Accessibility', () => {
|
|
296
|
+
describe('Input ARIA attributes', () => {
|
|
297
|
+
it('applies aria-required when required is true', async () => {
|
|
298
|
+
const wrapper = mount(FzInput, {
|
|
299
|
+
props: {
|
|
300
|
+
label: 'Label',
|
|
301
|
+
required: true,
|
|
302
|
+
},
|
|
303
|
+
slots: {},
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
await wrapper.vm.$nextTick()
|
|
307
|
+
|
|
308
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
309
|
+
expect(input.getAttribute('aria-required')).toBe('true')
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
it('applies aria-required="false" when required is false', async () => {
|
|
313
|
+
const wrapper = mount(FzInput, {
|
|
314
|
+
props: {
|
|
315
|
+
label: 'Label',
|
|
316
|
+
required: false,
|
|
317
|
+
},
|
|
318
|
+
slots: {},
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
await wrapper.vm.$nextTick()
|
|
322
|
+
|
|
323
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
324
|
+
expect(input.getAttribute('aria-required')).toBe('false')
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
it('applies aria-invalid when error is true', async () => {
|
|
328
|
+
const wrapper = mount(FzInput, {
|
|
329
|
+
props: {
|
|
330
|
+
label: 'Label',
|
|
331
|
+
error: true,
|
|
332
|
+
},
|
|
333
|
+
slots: {
|
|
334
|
+
errorMessage: 'Error message',
|
|
335
|
+
},
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
await wrapper.vm.$nextTick()
|
|
339
|
+
|
|
340
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
341
|
+
expect(input.getAttribute('aria-invalid')).toBe('true')
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
it('applies aria-invalid="false" when error is false', async () => {
|
|
345
|
+
const wrapper = mount(FzInput, {
|
|
346
|
+
props: {
|
|
347
|
+
label: 'Label',
|
|
348
|
+
error: false,
|
|
349
|
+
},
|
|
350
|
+
slots: {},
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
await wrapper.vm.$nextTick()
|
|
354
|
+
|
|
355
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
356
|
+
expect(input.getAttribute('aria-invalid')).toBe('false')
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
it('applies aria-disabled when disabled is true', async () => {
|
|
360
|
+
const wrapper = mount(FzInput, {
|
|
361
|
+
props: {
|
|
362
|
+
label: 'Label',
|
|
363
|
+
disabled: true,
|
|
364
|
+
},
|
|
365
|
+
slots: {},
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
await wrapper.vm.$nextTick()
|
|
369
|
+
|
|
370
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
371
|
+
expect(input.getAttribute('aria-disabled')).toBe('true')
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
it('applies aria-disabled="false" when disabled is false', async () => {
|
|
375
|
+
const wrapper = mount(FzInput, {
|
|
376
|
+
props: {
|
|
377
|
+
label: 'Label',
|
|
378
|
+
disabled: false,
|
|
379
|
+
},
|
|
380
|
+
slots: {},
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
await wrapper.vm.$nextTick()
|
|
384
|
+
|
|
385
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
386
|
+
expect(input.getAttribute('aria-disabled')).toBe('false')
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('applies aria-labelledby when label is provided', async () => {
|
|
390
|
+
const wrapper = mount(FzInput, {
|
|
391
|
+
props: {
|
|
392
|
+
label: 'Test Label',
|
|
393
|
+
},
|
|
394
|
+
slots: {},
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
await wrapper.vm.$nextTick()
|
|
398
|
+
|
|
399
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
400
|
+
const labelId = input.getAttribute('aria-labelledby')
|
|
401
|
+
expect(labelId).toBeTruthy()
|
|
402
|
+
|
|
403
|
+
// Verify label element exists with matching id
|
|
404
|
+
const label = wrapper.find('label').element as HTMLLabelElement
|
|
405
|
+
expect(label.getAttribute('id')).toBe(labelId)
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
it('does not apply aria-labelledby when label is not provided', async () => {
|
|
409
|
+
const wrapper = mount(FzInput, {
|
|
410
|
+
props: {},
|
|
411
|
+
slots: {},
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
await wrapper.vm.$nextTick()
|
|
415
|
+
|
|
416
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
417
|
+
expect(input.getAttribute('aria-labelledby')).toBeNull()
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
it('does not apply aria-labelledby when custom label slot is provided', async () => {
|
|
421
|
+
const wrapper = mount(FzInput, {
|
|
422
|
+
props: {
|
|
423
|
+
label: 'Test Label',
|
|
424
|
+
},
|
|
425
|
+
slots: {
|
|
426
|
+
label: () => 'Custom Label Slot',
|
|
427
|
+
},
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
await wrapper.vm.$nextTick()
|
|
431
|
+
|
|
432
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
433
|
+
const ariaLabelledBy = input.getAttribute('aria-labelledby')
|
|
434
|
+
|
|
435
|
+
// aria-labelledby should not be set because the default label element
|
|
436
|
+
// with id="${uniqueId}-label" doesn't exist when custom slot is used
|
|
437
|
+
expect(ariaLabelledBy).toBeNull()
|
|
438
|
+
|
|
439
|
+
// Verify default label element is not rendered
|
|
440
|
+
const defaultLabel = wrapper.find('label')
|
|
441
|
+
expect(defaultLabel.exists()).toBe(false)
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
it('applies aria-describedby when helpText slot is provided', async () => {
|
|
445
|
+
const wrapper = mount(FzInput, {
|
|
446
|
+
props: {
|
|
447
|
+
label: 'Label',
|
|
448
|
+
},
|
|
449
|
+
slots: {
|
|
450
|
+
helpText: 'Help text',
|
|
451
|
+
},
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
await wrapper.vm.$nextTick()
|
|
455
|
+
|
|
456
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
457
|
+
const describedBy = input.getAttribute('aria-describedby')
|
|
458
|
+
expect(describedBy).toBeTruthy()
|
|
459
|
+
|
|
460
|
+
// Verify help text element exists with matching id
|
|
461
|
+
const helpText = wrapper.find(`#${describedBy}`)
|
|
462
|
+
expect(helpText.exists()).toBe(true)
|
|
463
|
+
expect(helpText.text()).toContain('Help text')
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
it('applies aria-describedby when errorMessage slot is provided', async () => {
|
|
467
|
+
const wrapper = mount(FzInput, {
|
|
468
|
+
props: {
|
|
469
|
+
label: 'Label',
|
|
470
|
+
error: true,
|
|
471
|
+
},
|
|
472
|
+
slots: {
|
|
473
|
+
errorMessage: 'Error message',
|
|
474
|
+
},
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
await wrapper.vm.$nextTick()
|
|
478
|
+
|
|
479
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
480
|
+
const describedBy = input.getAttribute('aria-describedby')
|
|
481
|
+
expect(describedBy).toBeTruthy()
|
|
482
|
+
|
|
483
|
+
// Verify error message element exists with matching id
|
|
484
|
+
const errorMessage = wrapper.find(`#${describedBy}`)
|
|
485
|
+
expect(errorMessage.exists()).toBe(true)
|
|
486
|
+
expect(errorMessage.text()).toContain('Error message')
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
it('does not apply aria-describedby when neither helpText nor errorMessage are provided', async () => {
|
|
490
|
+
const wrapper = mount(FzInput, {
|
|
491
|
+
props: {
|
|
492
|
+
label: 'Label',
|
|
493
|
+
},
|
|
494
|
+
slots: {},
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
await wrapper.vm.$nextTick()
|
|
498
|
+
|
|
499
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
500
|
+
expect(input.getAttribute('aria-describedby')).toBeNull()
|
|
501
|
+
})
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
describe('Error message accessibility', () => {
|
|
505
|
+
it('applies role="alert" to error message container', async () => {
|
|
506
|
+
const wrapper = mount(FzInput, {
|
|
507
|
+
props: {
|
|
508
|
+
label: 'Label',
|
|
509
|
+
error: true,
|
|
510
|
+
},
|
|
511
|
+
slots: {
|
|
512
|
+
errorMessage: 'Error message',
|
|
513
|
+
},
|
|
514
|
+
})
|
|
515
|
+
|
|
516
|
+
await wrapper.vm.$nextTick()
|
|
517
|
+
|
|
518
|
+
const errorContainer = wrapper.find('[role="alert"]')
|
|
519
|
+
expect(errorContainer.exists()).toBe(true)
|
|
520
|
+
expect(errorContainer.text()).toContain('Error message')
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
it('does not render error container when error is false', async () => {
|
|
524
|
+
const wrapper = mount(FzInput, {
|
|
525
|
+
props: {
|
|
526
|
+
label: 'Label',
|
|
527
|
+
error: false,
|
|
528
|
+
},
|
|
529
|
+
slots: {
|
|
530
|
+
errorMessage: 'Error message',
|
|
531
|
+
},
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
await wrapper.vm.$nextTick()
|
|
535
|
+
|
|
536
|
+
const errorContainer = wrapper.find('[role="alert"]')
|
|
537
|
+
expect(errorContainer.exists()).toBe(false)
|
|
538
|
+
})
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
describe('Decorative icons accessibility', () => {
|
|
542
|
+
it('applies aria-hidden="true" to valid checkmark icon', async () => {
|
|
543
|
+
const wrapper = mount(FzInput, {
|
|
544
|
+
props: {
|
|
545
|
+
label: 'Label',
|
|
546
|
+
valid: true,
|
|
547
|
+
},
|
|
548
|
+
slots: {},
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
await wrapper.vm.$nextTick()
|
|
552
|
+
|
|
553
|
+
// Find the check icon (FzIcon with name="check")
|
|
554
|
+
const checkIcons = wrapper.findAllComponents({ name: 'FzIcon' })
|
|
555
|
+
const checkIcon = checkIcons.find((icon) => icon.props('name') === 'check')
|
|
556
|
+
|
|
557
|
+
expect(checkIcon?.exists()).toBe(true)
|
|
558
|
+
const rootElement = checkIcon?.element as HTMLElement
|
|
559
|
+
expect(rootElement.getAttribute('aria-hidden')).toBe('true')
|
|
560
|
+
})
|
|
561
|
+
|
|
562
|
+
it('applies aria-hidden="true" to error icon', async () => {
|
|
563
|
+
const wrapper = mount(FzInput, {
|
|
564
|
+
props: {
|
|
565
|
+
label: 'Label',
|
|
566
|
+
error: true,
|
|
567
|
+
},
|
|
568
|
+
slots: {
|
|
569
|
+
errorMessage: 'Error message',
|
|
570
|
+
},
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
await wrapper.vm.$nextTick()
|
|
574
|
+
|
|
575
|
+
// Find the error icon (FzIcon with name="circle-xmark")
|
|
576
|
+
const errorIcons = wrapper.findAllComponents({ name: 'FzIcon' })
|
|
577
|
+
const errorIcon = errorIcons.find((icon) => icon.props('name') === 'circle-xmark')
|
|
578
|
+
|
|
579
|
+
expect(errorIcon?.exists()).toBe(true)
|
|
580
|
+
const rootElement = errorIcon?.element as HTMLElement
|
|
581
|
+
expect(rootElement.getAttribute('aria-hidden')).toBe('true')
|
|
582
|
+
})
|
|
583
|
+
})
|
|
584
|
+
|
|
585
|
+
describe('Container accessibility', () => {
|
|
586
|
+
it('applies tabindex="0" to container when not disabled', async () => {
|
|
587
|
+
const wrapper = mount(FzInput, {
|
|
588
|
+
props: {
|
|
589
|
+
label: 'Label',
|
|
590
|
+
},
|
|
591
|
+
slots: {},
|
|
592
|
+
})
|
|
593
|
+
|
|
594
|
+
await wrapper.vm.$nextTick()
|
|
595
|
+
|
|
596
|
+
const container = wrapper.find('.fz-input > div').element as HTMLElement
|
|
597
|
+
expect(container.getAttribute('tabindex')).toBe('0')
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
it('removes tabindex from container when disabled', async () => {
|
|
601
|
+
const wrapper = mount(FzInput, {
|
|
602
|
+
props: {
|
|
603
|
+
label: 'Label',
|
|
604
|
+
disabled: true,
|
|
605
|
+
},
|
|
606
|
+
slots: {},
|
|
607
|
+
})
|
|
608
|
+
|
|
609
|
+
await wrapper.vm.$nextTick()
|
|
610
|
+
|
|
611
|
+
const container = wrapper.find('.fz-input > div').element as HTMLElement
|
|
612
|
+
expect(container.getAttribute('tabindex')).toBeNull()
|
|
613
|
+
})
|
|
614
|
+
|
|
615
|
+
it('removes tabindex from container when readonly', async () => {
|
|
616
|
+
const wrapper = mount(FzInput, {
|
|
617
|
+
props: {
|
|
618
|
+
label: 'Label',
|
|
619
|
+
readonly: true,
|
|
620
|
+
},
|
|
621
|
+
slots: {},
|
|
622
|
+
})
|
|
623
|
+
|
|
624
|
+
await wrapper.vm.$nextTick()
|
|
625
|
+
|
|
626
|
+
const container = wrapper.find('.fz-input > div').element as HTMLElement
|
|
627
|
+
expect(container.getAttribute('tabindex')).toBeNull()
|
|
628
|
+
})
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
describe('Left icon accessibility', () => {
|
|
632
|
+
it('applies accessibility attributes when leftIconAriaLabel is provided', async () => {
|
|
633
|
+
const wrapper = mount(FzInput, {
|
|
634
|
+
props: {
|
|
635
|
+
label: 'Label',
|
|
636
|
+
leftIcon: 'calendar-lines',
|
|
637
|
+
leftIconAriaLabel: 'Open calendar',
|
|
638
|
+
},
|
|
639
|
+
slots: {},
|
|
640
|
+
})
|
|
641
|
+
|
|
642
|
+
await wrapper.vm.$nextTick()
|
|
643
|
+
|
|
644
|
+
// Find the FzIcon component wrapper div (root element)
|
|
645
|
+
const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
|
|
646
|
+
expect(iconComponent.exists()).toBe(true)
|
|
647
|
+
|
|
648
|
+
// Get the root element (div wrapper)
|
|
649
|
+
const rootElement = iconComponent.element as HTMLElement
|
|
650
|
+
|
|
651
|
+
// Verify attributes are on the root element
|
|
652
|
+
expect(rootElement.getAttribute('role')).toBe('button')
|
|
653
|
+
expect(rootElement.getAttribute('aria-label')).toBe('Open calendar')
|
|
654
|
+
expect(rootElement.getAttribute('tabindex')).toBe('0')
|
|
655
|
+
})
|
|
656
|
+
|
|
657
|
+
it('does not apply accessibility attributes when leftIconAriaLabel is not provided', async () => {
|
|
658
|
+
const wrapper = mount(FzInput, {
|
|
659
|
+
props: {
|
|
660
|
+
label: 'Label',
|
|
661
|
+
leftIcon: 'calendar-lines',
|
|
662
|
+
},
|
|
663
|
+
slots: {},
|
|
664
|
+
})
|
|
665
|
+
|
|
666
|
+
await wrapper.vm.$nextTick()
|
|
667
|
+
|
|
668
|
+
// Find the FzIcon component wrapper
|
|
669
|
+
const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
|
|
670
|
+
expect(iconComponent.exists()).toBe(true)
|
|
671
|
+
|
|
672
|
+
// Get the root element (div wrapper)
|
|
673
|
+
const rootElement = iconComponent.element as HTMLElement
|
|
674
|
+
|
|
675
|
+
// Verify attributes are not on the root element
|
|
676
|
+
expect(rootElement.getAttribute('role')).toBeNull()
|
|
677
|
+
expect(rootElement.getAttribute('aria-label')).toBeNull()
|
|
678
|
+
expect(rootElement.getAttribute('tabindex')).toBeNull()
|
|
679
|
+
})
|
|
680
|
+
|
|
681
|
+
it('removes tabindex when disabled and leftIconAriaLabel is provided', async () => {
|
|
682
|
+
const wrapper = mount(FzInput, {
|
|
683
|
+
props: {
|
|
684
|
+
label: 'Label',
|
|
685
|
+
leftIcon: 'calendar-lines',
|
|
686
|
+
leftIconAriaLabel: 'Open calendar',
|
|
687
|
+
disabled: true,
|
|
688
|
+
},
|
|
689
|
+
slots: {},
|
|
690
|
+
})
|
|
691
|
+
|
|
692
|
+
await wrapper.vm.$nextTick()
|
|
693
|
+
|
|
694
|
+
// Find the FzIcon component wrapper
|
|
695
|
+
const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
|
|
696
|
+
expect(iconComponent.exists()).toBe(true)
|
|
697
|
+
|
|
698
|
+
// Get the root element (div wrapper)
|
|
699
|
+
const rootElement = iconComponent.element as HTMLElement
|
|
700
|
+
|
|
701
|
+
// Verify attributes are on the root element
|
|
702
|
+
expect(rootElement.getAttribute('role')).toBe('button')
|
|
703
|
+
expect(rootElement.getAttribute('aria-label')).toBe('Open calendar')
|
|
704
|
+
expect(rootElement.getAttribute('tabindex')).toBeNull() // Removed when disabled
|
|
705
|
+
expect(rootElement.getAttribute('aria-disabled')).toBe('true')
|
|
706
|
+
})
|
|
707
|
+
|
|
708
|
+
it('has keyboard handler when leftIconAriaLabel is provided', async () => {
|
|
709
|
+
const wrapper = mount(FzInput, {
|
|
710
|
+
props: {
|
|
711
|
+
label: 'Label',
|
|
712
|
+
leftIcon: 'calendar-lines',
|
|
713
|
+
leftIconAriaLabel: 'Open calendar',
|
|
714
|
+
},
|
|
715
|
+
slots: {},
|
|
716
|
+
})
|
|
717
|
+
|
|
718
|
+
await wrapper.vm.$nextTick()
|
|
719
|
+
|
|
720
|
+
// Find the FzIcon component wrapper
|
|
721
|
+
const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
|
|
722
|
+
expect(iconComponent.exists()).toBe(true)
|
|
723
|
+
|
|
724
|
+
// Get the root element (div wrapper)
|
|
725
|
+
const rootElement = iconComponent.element as HTMLElement
|
|
726
|
+
|
|
727
|
+
// Verify icon is keyboard accessible (has tabindex)
|
|
728
|
+
expect(rootElement.getAttribute('tabindex')).toBe('0')
|
|
729
|
+
// Keyboard interaction is tested in Storybook play functions
|
|
730
|
+
})
|
|
731
|
+
})
|
|
732
|
+
|
|
733
|
+
describe('Right icon accessibility', () => {
|
|
734
|
+
it('applies accessibility attributes when rightIconAriaLabel is provided', async () => {
|
|
735
|
+
const wrapper = mount(FzInput, {
|
|
736
|
+
props: {
|
|
737
|
+
label: 'Label',
|
|
738
|
+
rightIcon: 'eye',
|
|
739
|
+
rightIconAriaLabel: 'Toggle visibility',
|
|
740
|
+
},
|
|
741
|
+
slots: {},
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
await wrapper.vm.$nextTick()
|
|
745
|
+
|
|
746
|
+
// Find the FzIcon component wrapper (not the inner img)
|
|
747
|
+
const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
|
|
748
|
+
expect(iconComponent.exists()).toBe(true)
|
|
749
|
+
|
|
750
|
+
// Get the root element (div wrapper)
|
|
751
|
+
const rootElement = iconComponent.element as HTMLElement
|
|
752
|
+
|
|
753
|
+
// Verify attributes are on the root element
|
|
754
|
+
expect(rootElement.getAttribute('role')).toBe('button')
|
|
755
|
+
expect(rootElement.getAttribute('aria-label')).toBe('Toggle visibility')
|
|
756
|
+
expect(rootElement.getAttribute('tabindex')).toBe('0')
|
|
757
|
+
})
|
|
758
|
+
|
|
759
|
+
it('does not apply accessibility attributes when rightIconAriaLabel is not provided', async () => {
|
|
760
|
+
const wrapper = mount(FzInput, {
|
|
761
|
+
props: {
|
|
762
|
+
label: 'Label',
|
|
763
|
+
rightIcon: 'eye',
|
|
764
|
+
},
|
|
765
|
+
slots: {},
|
|
766
|
+
})
|
|
767
|
+
|
|
768
|
+
await wrapper.vm.$nextTick()
|
|
769
|
+
|
|
770
|
+
// Find the FzIcon component wrapper
|
|
771
|
+
const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
|
|
772
|
+
expect(iconComponent.exists()).toBe(true)
|
|
773
|
+
|
|
774
|
+
// Get the root element (div wrapper)
|
|
775
|
+
const rootElement = iconComponent.element as HTMLElement
|
|
776
|
+
|
|
777
|
+
// Verify attributes are not on the root element
|
|
778
|
+
expect(rootElement.getAttribute('role')).toBeNull()
|
|
779
|
+
expect(rootElement.getAttribute('aria-label')).toBeNull()
|
|
780
|
+
expect(rootElement.getAttribute('tabindex')).toBeNull()
|
|
781
|
+
})
|
|
782
|
+
|
|
783
|
+
it('removes tabindex when disabled and rightIconAriaLabel is provided', async () => {
|
|
784
|
+
const wrapper = mount(FzInput, {
|
|
785
|
+
props: {
|
|
786
|
+
label: 'Label',
|
|
787
|
+
rightIcon: 'eye',
|
|
788
|
+
rightIconAriaLabel: 'Toggle visibility',
|
|
789
|
+
disabled: true,
|
|
790
|
+
},
|
|
791
|
+
slots: {},
|
|
792
|
+
})
|
|
793
|
+
|
|
794
|
+
await wrapper.vm.$nextTick()
|
|
795
|
+
|
|
796
|
+
// Find the FzIcon component wrapper
|
|
797
|
+
const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
|
|
798
|
+
expect(iconComponent.exists()).toBe(true)
|
|
799
|
+
|
|
800
|
+
// Get the root element (div wrapper)
|
|
801
|
+
const rootElement = iconComponent.element as HTMLElement
|
|
802
|
+
|
|
803
|
+
// Verify attributes are on the root element
|
|
804
|
+
expect(rootElement.getAttribute('role')).toBe('button')
|
|
805
|
+
expect(rootElement.getAttribute('aria-label')).toBe('Toggle visibility')
|
|
806
|
+
expect(rootElement.getAttribute('tabindex')).toBeNull() // Removed when disabled
|
|
807
|
+
expect(rootElement.getAttribute('aria-disabled')).toBe('true')
|
|
808
|
+
})
|
|
809
|
+
|
|
810
|
+
it('removes tabindex when readonly and rightIconAriaLabel is provided', async () => {
|
|
811
|
+
const wrapper = mount(FzInput, {
|
|
812
|
+
props: {
|
|
813
|
+
label: 'Label',
|
|
814
|
+
rightIcon: 'eye',
|
|
815
|
+
rightIconAriaLabel: 'Toggle visibility',
|
|
816
|
+
readonly: true,
|
|
817
|
+
},
|
|
818
|
+
slots: {},
|
|
819
|
+
})
|
|
820
|
+
|
|
821
|
+
await wrapper.vm.$nextTick()
|
|
822
|
+
|
|
823
|
+
// Find the FzIcon component wrapper
|
|
824
|
+
const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
|
|
825
|
+
expect(iconComponent.exists()).toBe(true)
|
|
826
|
+
|
|
827
|
+
// Get the root element (div wrapper)
|
|
828
|
+
const rootElement = iconComponent.element as HTMLElement
|
|
829
|
+
|
|
830
|
+
// Verify attributes are on the root element
|
|
831
|
+
expect(rootElement.getAttribute('role')).toBe('button')
|
|
832
|
+
expect(rootElement.getAttribute('aria-label')).toBe('Toggle visibility')
|
|
833
|
+
expect(rootElement.getAttribute('tabindex')).toBeNull() // Removed when readonly
|
|
834
|
+
expect(rootElement.getAttribute('aria-disabled')).toBe('true')
|
|
835
|
+
})
|
|
836
|
+
|
|
837
|
+
it('does not apply accessibility attributes when rightIconButton is true', async () => {
|
|
838
|
+
const wrapper = mount(FzInput, {
|
|
839
|
+
props: {
|
|
840
|
+
label: 'Label',
|
|
841
|
+
rightIcon: 'eye',
|
|
842
|
+
rightIconButton: true,
|
|
843
|
+
rightIconAriaLabel: 'Toggle visibility',
|
|
844
|
+
},
|
|
845
|
+
slots: {},
|
|
846
|
+
})
|
|
847
|
+
|
|
848
|
+
await wrapper.vm.$nextTick()
|
|
849
|
+
|
|
850
|
+
// When rightIconButton is true, FzIconButton is used instead of FzIcon
|
|
851
|
+
const iconButton = wrapper.findComponent({ name: 'FzIconButton' })
|
|
852
|
+
expect(iconButton.exists()).toBe(true)
|
|
853
|
+
})
|
|
854
|
+
|
|
855
|
+
it('has keyboard handler when rightIconAriaLabel is provided', async () => {
|
|
856
|
+
const wrapper = mount(FzInput, {
|
|
857
|
+
props: {
|
|
858
|
+
label: 'Label',
|
|
859
|
+
rightIcon: 'eye',
|
|
860
|
+
rightIconAriaLabel: 'Toggle visibility',
|
|
861
|
+
},
|
|
862
|
+
slots: {},
|
|
863
|
+
})
|
|
864
|
+
|
|
865
|
+
await wrapper.vm.$nextTick()
|
|
866
|
+
|
|
867
|
+
// Find the FzIcon component wrapper
|
|
868
|
+
const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
|
|
869
|
+
expect(iconComponent.exists()).toBe(true)
|
|
870
|
+
|
|
871
|
+
// Get the root element (div wrapper)
|
|
872
|
+
const rootElement = iconComponent.element as HTMLElement
|
|
873
|
+
|
|
874
|
+
// Verify icon is keyboard accessible (has tabindex)
|
|
875
|
+
expect(rootElement.getAttribute('tabindex')).toBe('0')
|
|
876
|
+
// Keyboard interaction is tested in Storybook play functions
|
|
877
|
+
})
|
|
878
|
+
})
|
|
879
|
+
|
|
880
|
+
describe('Second right icon accessibility', () => {
|
|
881
|
+
it('applies accessibility attributes when secondRightIconAriaLabel is provided', async () => {
|
|
882
|
+
const wrapper = mount(FzInput, {
|
|
883
|
+
props: {
|
|
884
|
+
label: 'Label',
|
|
885
|
+
secondRightIcon: 'info-circle',
|
|
886
|
+
secondRightIconAriaLabel: 'Show information',
|
|
887
|
+
},
|
|
888
|
+
slots: {},
|
|
889
|
+
})
|
|
890
|
+
|
|
891
|
+
await wrapper.vm.$nextTick()
|
|
892
|
+
|
|
893
|
+
// Find all FzIcon components and get the one with secondRightIcon
|
|
894
|
+
const iconComponents = wrapper.findAllComponents({ name: 'FzIcon' })
|
|
895
|
+
const secondIconComponent = iconComponents.find((icon) =>
|
|
896
|
+
icon.props('name') === 'info-circle'
|
|
897
|
+
)
|
|
898
|
+
|
|
899
|
+
expect(secondIconComponent?.exists()).toBe(true)
|
|
900
|
+
|
|
901
|
+
// Get the root element (div wrapper)
|
|
902
|
+
const rootElement = secondIconComponent?.element as HTMLElement
|
|
903
|
+
|
|
904
|
+
// Verify attributes are on the root element
|
|
905
|
+
expect(rootElement.getAttribute('role')).toBe('button')
|
|
906
|
+
expect(rootElement.getAttribute('aria-label')).toBe('Show information')
|
|
907
|
+
expect(rootElement.getAttribute('tabindex')).toBe('0')
|
|
908
|
+
})
|
|
909
|
+
|
|
910
|
+
it('removes tabindex when readonly and secondRightIconAriaLabel is provided', async () => {
|
|
911
|
+
const wrapper = mount(FzInput, {
|
|
912
|
+
props: {
|
|
913
|
+
label: 'Label',
|
|
914
|
+
secondRightIcon: 'info-circle',
|
|
915
|
+
secondRightIconAriaLabel: 'Show information',
|
|
916
|
+
readonly: true,
|
|
917
|
+
},
|
|
918
|
+
slots: {},
|
|
919
|
+
})
|
|
920
|
+
|
|
921
|
+
await wrapper.vm.$nextTick()
|
|
922
|
+
|
|
923
|
+
// Find all FzIcon components and get the one with secondRightIcon
|
|
924
|
+
const iconComponents = wrapper.findAllComponents({ name: 'FzIcon' })
|
|
925
|
+
const secondIconComponent = iconComponents.find((icon) =>
|
|
926
|
+
icon.props('name') === 'info-circle'
|
|
927
|
+
)
|
|
928
|
+
|
|
929
|
+
expect(secondIconComponent?.exists()).toBe(true)
|
|
930
|
+
|
|
931
|
+
// Get the root element (div wrapper)
|
|
932
|
+
const rootElement = secondIconComponent?.element as HTMLElement
|
|
933
|
+
|
|
934
|
+
// Verify attributes are on the root element
|
|
935
|
+
expect(rootElement.getAttribute('role')).toBe('button')
|
|
936
|
+
expect(rootElement.getAttribute('aria-label')).toBe('Show information')
|
|
937
|
+
expect(rootElement.getAttribute('tabindex')).toBeNull() // Removed when readonly
|
|
938
|
+
expect(rootElement.getAttribute('aria-disabled')).toBe('true')
|
|
939
|
+
})
|
|
940
|
+
})
|
|
941
|
+
|
|
942
|
+
describe('Right icons order', () => {
|
|
943
|
+
it('renders valid checkmark as last icon when all icons are present', async () => {
|
|
944
|
+
const wrapper = mount(FzInput, {
|
|
945
|
+
props: {
|
|
946
|
+
label: 'Label',
|
|
947
|
+
valid: true,
|
|
948
|
+
secondRightIcon: 'info-circle',
|
|
949
|
+
rightIcon: 'envelope',
|
|
950
|
+
},
|
|
951
|
+
slots: {},
|
|
952
|
+
})
|
|
953
|
+
|
|
954
|
+
await wrapper.vm.$nextTick()
|
|
955
|
+
|
|
956
|
+
// Find all FzIcon components in the right-icon slot
|
|
957
|
+
const iconComponents = wrapper.findAllComponents({ name: 'FzIcon' })
|
|
958
|
+
const validIcon = iconComponents.find((icon) => icon.props('name') === 'check')
|
|
959
|
+
const secondIcon = iconComponents.find((icon) => icon.props('name') === 'info-circle')
|
|
960
|
+
const rightIcon = iconComponents.find((icon) => icon.props('name') === 'envelope')
|
|
961
|
+
|
|
962
|
+
expect(validIcon?.exists()).toBe(true)
|
|
963
|
+
expect(secondIcon?.exists()).toBe(true)
|
|
964
|
+
expect(rightIcon?.exists()).toBe(true)
|
|
965
|
+
|
|
966
|
+
// Get the container div that wraps all right icons
|
|
967
|
+
const rightIconContainer = wrapper.find('.fz-input > div > div.flex.items-center.gap-4')
|
|
968
|
+
expect(rightIconContainer.exists()).toBe(true)
|
|
969
|
+
|
|
970
|
+
// Get all icon elements in order
|
|
971
|
+
const icons = rightIconContainer.findAllComponents({ name: 'FzIcon' })
|
|
972
|
+
expect(icons.length).toBeGreaterThanOrEqual(3)
|
|
973
|
+
|
|
974
|
+
// Verify order: secondRightIcon, rightIcon, valid (check)
|
|
975
|
+
const iconNames = icons.map((icon) => icon.props('name'))
|
|
976
|
+
const secondIndex = iconNames.indexOf('info-circle')
|
|
977
|
+
const rightIndex = iconNames.indexOf('envelope')
|
|
978
|
+
const validIndex = iconNames.indexOf('check')
|
|
979
|
+
|
|
980
|
+
expect(secondIndex).toBeLessThan(rightIndex)
|
|
981
|
+
expect(rightIndex).toBeLessThan(validIndex)
|
|
982
|
+
})
|
|
983
|
+
})
|
|
984
|
+
})
|
|
985
|
+
|
|
986
|
+
describe('Edge cases', () => {
|
|
987
|
+
it(`renders ${NUMBER_OF_INPUTS} input with different ids`, async () => {
|
|
988
|
+
const wrapperList = Array.from({ length: NUMBER_OF_INPUTS }).map((_, i) =>
|
|
989
|
+
mount(FzInput, {
|
|
990
|
+
props: {
|
|
991
|
+
label: `Label ${i}`,
|
|
992
|
+
},
|
|
993
|
+
slots: {},
|
|
994
|
+
}),
|
|
995
|
+
)
|
|
996
|
+
|
|
997
|
+
await Promise.all(wrapperList.map((w) => w.vm.$nextTick()))
|
|
998
|
+
|
|
999
|
+
const ids = wrapperList.map((w) => w.find('input').attributes('id'))
|
|
1000
|
+
|
|
1001
|
+
expect(new Set(ids).size).toBe(NUMBER_OF_INPUTS)
|
|
1002
|
+
})
|
|
1003
|
+
})
|
|
1004
|
+
})
|
|
1005
|
+
|