@zap-wunschlachen/wl-shared-components 1.0.12 → 1.0.13

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.
Files changed (232) hide show
  1. package/.github/workflows/playwright.yml +205 -205
  2. package/.github/workflows/static.yml +61 -61
  3. package/.github/workflows/update-snapshots.yml +37 -37
  4. package/.prettierrc +5 -5
  5. package/.storybook/main.ts +18 -18
  6. package/.storybook/preview.ts +37 -37
  7. package/.storybook/storyWrapper.vue +18 -18
  8. package/.storybook/withVuetifyTheme.decorator.ts +21 -21
  9. package/App.vue +147 -147
  10. package/README.md +56 -56
  11. package/heroicons.ts +75 -75
  12. package/index.html +19 -19
  13. package/package.json +67 -67
  14. package/playwright.config.ts +48 -48
  15. package/public/background.svg +60 -60
  16. package/public/style.css +187 -187
  17. package/public/technologies.svg +22 -22
  18. package/src/assets/css/base.css +235 -235
  19. package/src/assets/css/variables.css +96 -96
  20. package/src/components/Accordion/Accordion.css +59 -59
  21. package/src/components/Accordion/AccordionGroup.vue +51 -51
  22. package/src/components/Accordion/AccordionItem.vue +66 -66
  23. package/src/components/Appointment/Card/Actions.css +28 -28
  24. package/src/components/Appointment/Card/Actions.vue +72 -72
  25. package/src/components/Appointment/Card/Card.css +80 -80
  26. package/src/components/Appointment/Card/Card.vue +87 -81
  27. package/src/components/Appointment/Card/Details.css +50 -50
  28. package/src/components/Appointment/Card/Details.vue +43 -43
  29. package/src/components/Audio/Audio.vue +187 -187
  30. package/src/components/Audio/Waveform.vue +117 -117
  31. package/src/components/Button/Button.vue +119 -119
  32. package/src/components/CheckBox/CheckBox.css +185 -185
  33. package/src/components/CheckBox/Checkbox.vue +130 -130
  34. package/src/components/DateInput/DateInput.css +2 -2
  35. package/src/components/DateInput/DateInput.vue +262 -262
  36. package/src/components/Dialog/Dialog.css +6 -6
  37. package/src/components/Dialog/Dialog.vue +29 -29
  38. package/src/components/EditField/EditField.css +19 -19
  39. package/src/components/EditField/EditField.vue +202 -202
  40. package/src/components/IconBullet/IconBullet.vue +86 -86
  41. package/src/components/IconBullet/IconBulletList.vue +41 -41
  42. package/src/components/Icons/AdvanceAppointments.vue +153 -153
  43. package/src/components/Icons/Audio/CloudFailed.vue +20 -20
  44. package/src/components/Icons/Audio/CloudSaved.vue +21 -21
  45. package/src/components/Icons/Audio/Delete.vue +15 -15
  46. package/src/components/Icons/Audio/Pause.vue +18 -18
  47. package/src/components/Icons/Audio/Play.vue +15 -15
  48. package/src/components/Icons/CalendarNotification.vue +126 -126
  49. package/src/components/Icons/Chair.vue +32 -32
  50. package/src/components/Icons/ChairNotification.vue +35 -35
  51. package/src/components/Icons/Circle.vue +66 -66
  52. package/src/components/Icons/FavIcon.vue +22 -22
  53. package/src/components/Icons/FilledCircle.vue +11 -11
  54. package/src/components/Icons/Group3.vue +46 -46
  55. package/src/components/Icons/RingNotification.vue +54 -54
  56. package/src/components/Icons/SolidArrowRight.vue +14 -14
  57. package/src/components/Icons/calendar.vue +17 -17
  58. package/src/components/Icons/checkbox.vue +19 -19
  59. package/src/components/Icons/outlineChecked.vue +27 -27
  60. package/src/components/Icons/play.vue +5 -5
  61. package/src/components/Input/Input.css +187 -187
  62. package/src/components/Input/Input.vue +247 -247
  63. package/src/components/Laboratory/AppointmentCard/AppointmentCard.css +7 -7
  64. package/src/components/Laboratory/AppointmentCard/AppointmentCard.vue +116 -116
  65. package/src/components/Laboratory/ChatBoxImage/ChatBoxImage.vue +81 -81
  66. package/src/components/Laboratory/ChatMessage/ChatMessage.vue +113 -113
  67. package/src/components/Laboratory/ChatMessage/ChatMessageBadge.css +4 -4
  68. package/src/components/Laboratory/ChatMessage/ChatMessageBadge.vue +99 -99
  69. package/src/components/Laboratory/ChatNotification/ChatNotification.vue +130 -130
  70. package/src/components/Laboratory/DocumentCard/DocumentCard.css +3 -3
  71. package/src/components/Laboratory/DocumentCard/DocumentCard.vue +50 -50
  72. package/src/components/Laboratory/DocumentCard/DocumentCardItem.vue +53 -53
  73. package/src/components/Laboratory/InfoCard/InfoCard.vue +162 -162
  74. package/src/components/Laboratory/MainColumnsBar/MainColumnsBar.vue +102 -102
  75. package/src/components/Laboratory/ProgressCircle/ProgressCircle.vue +152 -152
  76. package/src/components/Laboratory/ProgressLinear/ProgressLinear.css +33 -33
  77. package/src/components/Laboratory/ProgressLinear/ProgressLinear.vue +75 -75
  78. package/src/components/Laboratory/SelectionColumnBar/SelectionColumnBar.vue +92 -92
  79. package/src/components/Laboratory/StatusNotification/StatusNotification.vue +49 -49
  80. package/src/components/Laboratory/TagLabel/TagLabel.vue +126 -126
  81. package/src/components/Laboratory/TagLabelGroup/TagLabelGroup.vue +97 -97
  82. package/src/components/Laboratory/TicketCard/TicketCard.css +3 -3
  83. package/src/components/Laboratory/TicketCard/TicketCard.vue +143 -143
  84. package/src/components/Laboratory/TimeLine/TimeLineEvent.css +18 -18
  85. package/src/components/Laboratory/TimeLine/TimeLineEvent.vue +119 -119
  86. package/src/components/Laboratory/TimeLine/Timeline.css +4 -4
  87. package/src/components/Laboratory/TimeLine/Timeline.vue +30 -30
  88. package/src/components/Modal/Modal.css +5 -5
  89. package/src/components/Modal/Modal.vue +22 -22
  90. package/src/components/NotificationBubble/NotificationBubble.css +4 -4
  91. package/src/components/NotificationBubble/NotificationBubble.vue +90 -90
  92. package/src/components/OtpInput/OtpInput.css +39 -39
  93. package/src/components/OtpInput/OtpInput.vue +143 -143
  94. package/src/components/PhoneInput/PhoneInput.css +31 -31
  95. package/src/components/PhoneInput/PhoneInput.vue +113 -113
  96. package/src/components/Select/Select.css +150 -150
  97. package/src/components/Select/Select.vue +304 -304
  98. package/src/components/TextArea/TextArea.css +3 -3
  99. package/src/components/TextArea/TextArea.vue +126 -126
  100. package/src/components/TickBox/TickBox.css +49 -49
  101. package/src/components/TickBox/TickBox.vue +126 -126
  102. package/src/components/index.ts +22 -22
  103. package/src/constants/iconEnums.ts +3 -3
  104. package/src/i18n/i18n.ts +15 -15
  105. package/src/i18n/locales/de.json +27 -27
  106. package/src/i18n/locales/en.json +27 -27
  107. package/src/index.ts +31 -31
  108. package/src/main.ts +11 -11
  109. package/src/plugins/vuetify.ts +131 -131
  110. package/src/shims-vue.d.ts +10 -10
  111. package/src/stories/Accordion.stories.ts +650 -650
  112. package/src/stories/Audio.stories.ts +28 -28
  113. package/src/stories/Button.stories.ts +263 -263
  114. package/src/stories/CheckBox.stories.ts +348 -348
  115. package/src/stories/DateInput.stories.ts +53 -53
  116. package/src/stories/Dialog.stories.ts +147 -147
  117. package/src/stories/EditField.stories.ts +78 -78
  118. package/src/stories/IconBullet/IconBullet.stories.ts +201 -201
  119. package/src/stories/IconBullet/IconBulletList.stories.ts +275 -275
  120. package/src/stories/Input.stories.ts +351 -351
  121. package/src/stories/Laboratory/Cards/AppointmentCard/AppointmentCard.stories.ts +260 -260
  122. package/src/stories/Laboratory/Cards/DocumentCard/DocumentCard.stories.ts +176 -176
  123. package/src/stories/Laboratory/Cards/DocumentCard/DocumentCardItem.stories.ts +119 -119
  124. package/src/stories/Laboratory/Cards/InfoCard/InfoCard.stories.ts +320 -320
  125. package/src/stories/Laboratory/Cards/TicketCard/TicketCard.stories.ts +335 -335
  126. package/src/stories/Laboratory/Chat/ChatBoxImage.stories.ts +82 -82
  127. package/src/stories/Laboratory/Chat/ChatMessage.stories.ts +198 -198
  128. package/src/stories/Laboratory/Chat/ChatMessageBadge.stories.ts +204 -204
  129. package/src/stories/Laboratory/Chat/ChatNotification.stories.ts +144 -144
  130. package/src/stories/Laboratory/Chat/ProgressLinear.stories.ts +186 -186
  131. package/src/stories/Laboratory/Chat/StatusNotification.stories.ts +111 -111
  132. package/src/stories/Laboratory/MainColumnsBar.stories.ts +48 -48
  133. package/src/stories/Laboratory/ProgressCircle.stories.ts +261 -261
  134. package/src/stories/Laboratory/SelectionColumnBar.stories.ts +234 -234
  135. package/src/stories/Laboratory/TagLabel.stories.ts +418 -418
  136. package/src/stories/Laboratory/TagLabelGroup.stories.ts +234 -234
  137. package/src/stories/Laboratory/Timeline.stories.ts +403 -403
  138. package/src/stories/NotificationBubble.stories.ts +194 -194
  139. package/src/stories/OtpInput.stories.ts +100 -100
  140. package/src/stories/PhoneInput.stories.ts +52 -52
  141. package/src/stories/Select.stories.ts +419 -419
  142. package/src/stories/TextArea.stories.ts +112 -112
  143. package/src/stories/TickBox.stories.ts +294 -294
  144. package/src/stories/v-icon.stories.ts +91 -91
  145. package/src/utils/index.ts +41 -41
  146. package/src/vite-env.d.ts +1 -1
  147. package/tests/e2e/README.md +220 -220
  148. package/tests/e2e/accessibility.spec.ts +638 -638
  149. package/tests/e2e/accordion.spec.ts +42 -42
  150. package/tests/e2e/additional-components.spec.ts +437 -437
  151. package/tests/e2e/all-components.spec.ts +135 -135
  152. package/tests/e2e/appointment-card.spec.ts +816 -816
  153. package/tests/e2e/button-fixed.spec.ts +58 -58
  154. package/tests/e2e/button.spec.ts +76 -76
  155. package/tests/e2e/checkbox.spec.ts +50 -50
  156. package/tests/e2e/date-input.spec.ts +46 -46
  157. package/tests/e2e/debug.spec.ts +51 -51
  158. package/tests/e2e/dialog.spec.ts +58 -58
  159. package/tests/e2e/input.spec.ts +55 -55
  160. package/tests/e2e/laboratory-components.spec.ts +320 -320
  161. package/tests/e2e/otp-input.spec.ts +50 -50
  162. package/tests/e2e/select.spec.ts +52 -52
  163. package/tests/e2e/storybook-utils.ts +59 -59
  164. package/tests/e2e/test-basic.spec.ts +33 -33
  165. package/tests/e2e/visual-regression.spec.ts +350 -350
  166. package/tests/unit/components/Accordion/AccordionGroup.spec.ts.skip +342 -342
  167. package/tests/unit/components/Accordion/AccordionItem.spec.ts.skip +383 -383
  168. package/tests/unit/components/Appointment/Card/Actions.spec.ts +407 -407
  169. package/tests/unit/components/Appointment/Card/Card.spec.ts +485 -485
  170. package/tests/unit/components/Appointment/Card/Details.spec.ts +397 -397
  171. package/tests/unit/components/Audio/Audio.spec.ts +403 -403
  172. package/tests/unit/components/Audio/Waveform.spec.ts +483 -483
  173. package/tests/unit/components/Core/Button.spec.ts +336 -336
  174. package/tests/unit/components/Core/Checkbox.spec.ts +544 -544
  175. package/tests/unit/components/Core/DateInput.spec.ts +690 -690
  176. package/tests/unit/components/Core/Dialog.spec.ts +485 -485
  177. package/tests/unit/components/Core/EditField.spec.ts +782 -782
  178. package/tests/unit/components/Core/Input.spec.ts +512 -512
  179. package/tests/unit/components/Core/Modal.spec.ts +518 -518
  180. package/tests/unit/components/Core/NotificationBubble.spec.ts +606 -606
  181. package/tests/unit/components/Core/OtpInput.spec.ts +708 -708
  182. package/tests/unit/components/Core/PhoneInput.spec.ts +619 -619
  183. package/tests/unit/components/Core/Select.spec.ts +712 -712
  184. package/tests/unit/components/Core/TextArea.spec.ts +565 -565
  185. package/tests/unit/components/Core/TickBox.spec.ts +779 -779
  186. package/tests/unit/components/IconBullet/IconBullet.spec.ts +356 -356
  187. package/tests/unit/components/IconBullet/IconBulletList.spec.ts +371 -371
  188. package/tests/unit/components/Icons/Audio/CloudFailed.spec.ts +108 -108
  189. package/tests/unit/components/Icons/Audio/CloudSaved.spec.ts +149 -149
  190. package/tests/unit/components/Icons/Audio/Delete.spec.ts +158 -158
  191. package/tests/unit/components/Icons/Audio/Pause.spec.ts +208 -208
  192. package/tests/unit/components/Icons/Audio/Play.spec.ts +217 -217
  193. package/tests/unit/components/Icons/CalendarNotification.spec.ts +186 -186
  194. package/tests/unit/components/Icons/Chair.spec.ts +234 -234
  195. package/tests/unit/components/Icons/ChairNotification.spec.ts +311 -311
  196. package/tests/unit/components/Icons/Circle.spec.ts +255 -255
  197. package/tests/unit/components/Icons/FavIcon.spec.ts +251 -251
  198. package/tests/unit/components/Icons/FilledCircle.spec.ts +274 -274
  199. package/tests/unit/components/Icons/Group3.spec.ts +355 -355
  200. package/tests/unit/components/Icons/RingNotification.spec.ts +393 -393
  201. package/tests/unit/components/Icons/calendar.spec.ts +286 -286
  202. package/tests/unit/components/Icons/checkbox.spec.ts +315 -315
  203. package/tests/unit/components/Icons/outlineChecked.spec.ts +434 -434
  204. package/tests/unit/components/Icons/play.spec.ts +308 -308
  205. package/tests/unit/components/Laboratory/AppointmentCard.spec.ts +167 -167
  206. package/tests/unit/components/Laboratory/ChatBoxImage.spec.ts +179 -179
  207. package/tests/unit/components/Laboratory/ChatMessage.spec.ts +263 -263
  208. package/tests/unit/components/Laboratory/ChatMessageBadge.spec.ts +282 -282
  209. package/tests/unit/components/Laboratory/ChatNotification.spec.ts +256 -256
  210. package/tests/unit/components/Laboratory/DocumentCard.spec.ts +228 -228
  211. package/tests/unit/components/Laboratory/DocumentCardItem.spec.ts +236 -236
  212. package/tests/unit/components/Laboratory/InfoCard.spec.ts +308 -308
  213. package/tests/unit/components/Laboratory/MainColumnsBar.spec.ts +251 -251
  214. package/tests/unit/components/Laboratory/ProgressCircle.spec.ts +290 -290
  215. package/tests/unit/components/Laboratory/ProgressLinear.spec.ts +275 -275
  216. package/tests/unit/components/Laboratory/SelectionColumnBar.spec.ts +288 -288
  217. package/tests/unit/components/Laboratory/StatusNotification.spec.ts +296 -296
  218. package/tests/unit/components/Laboratory/TagLabel.spec.ts +353 -353
  219. package/tests/unit/components/Laboratory/TagLabelGroup.spec.ts +377 -377
  220. package/tests/unit/components/Laboratory/TicketCard.spec.ts +351 -351
  221. package/tests/unit/components/Laboratory/TimeLineEvent.spec.ts +381 -381
  222. package/tests/unit/components/Laboratory/Timeline.spec.ts +419 -419
  223. package/tests/unit/constants/iconEnums.spec.ts +39 -39
  224. package/tests/unit/i18n/i18n.spec.ts +88 -88
  225. package/tests/unit/plugins/vuetify.spec.ts +220 -220
  226. package/tests/unit/setup.ts +189 -189
  227. package/tests/unit/src/components/index.spec.ts.skip +192 -192
  228. package/tests/unit/src/index.spec.ts.skip +182 -182
  229. package/tests/unit/src/main.spec.ts +151 -151
  230. package/tsconfig.json +26 -26
  231. package/vite.config.ts +29 -29
  232. package/vitest.config.ts +83 -83
@@ -1,620 +1,620 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { mount } from '@vue/test-utils';
3
- import { nextTick } from 'vue';
4
- import PhoneInput from '@components/PhoneInput/PhoneInput.vue';
5
-
6
- // Mock v-phone-input
7
- vi.mock('v-phone-input', () => ({
8
- VPhoneInput: {
9
- template: '<div data-testid="v-phone-input"><slot /></div>',
10
- props: [
11
- 'include-countries', 'aria-label', 'variant', 'label', 'appendInnerIcon',
12
- 'placeHolder', 'display-format', 'default-country', 'hide-details',
13
- 'aria-invalid', 'countryIconMode'
14
- ]
15
- }
16
- }));
17
-
18
- describe('PhoneInput', () => {
19
- beforeEach(() => {
20
- vi.clearAllMocks();
21
- });
22
-
23
- // Test default behavior and rendering
24
- describe('Default Behavior', () => {
25
- it('renders with default props', () => {
26
- const wrapper = mount(PhoneInput);
27
-
28
- expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
29
- expect(wrapper.find('.wl-phone-input').exists()).toBe(true);
30
- expect(wrapper.vm.state).toBe('default');
31
- expect(wrapper.vm.placeholder).toBe('');
32
- expect(wrapper.vm.countriesCodes).toEqual([]);
33
- expect(wrapper.vm.defaultCountryCode).toBe('DE');
34
- expect(wrapper.vm.appendInnerIcon).toBe('heroicons:phone');
35
- expect(wrapper.vm.variant).toBe('outlined');
36
- expect(wrapper.vm.borderOnHover).toBe(false);
37
- });
38
-
39
- it('has data-testid for testing', () => {
40
- const wrapper = mount(PhoneInput);
41
- expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
42
- });
43
-
44
- it('applies wl-phone-input CSS class', () => {
45
- const wrapper = mount(PhoneInput);
46
- expect(wrapper.find('.wl-phone-input').exists()).toBe(true);
47
- });
48
-
49
- it('sets id attribute to "phone"', () => {
50
- const wrapper = mount(PhoneInput);
51
- // Since VPhoneInput is mocked, we can't directly test the id
52
- // but we can verify the component renders correctly
53
- expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
54
- });
55
- });
56
-
57
- // Test validation states
58
- describe('Validation States', () => {
59
- it('handles default state', () => {
60
- const wrapper = mount(PhoneInput, {
61
- props: {
62
- state: 'default'
63
- }
64
- });
65
-
66
- expect(wrapper.vm.state).toBe('default');
67
- });
68
-
69
- it('handles error state', () => {
70
- const wrapper = mount(PhoneInput, {
71
- props: {
72
- state: 'error'
73
- }
74
- });
75
-
76
- expect(wrapper.vm.state).toBe('error');
77
- });
78
-
79
- it('handles success state', () => {
80
- const wrapper = mount(PhoneInput, {
81
- props: {
82
- state: 'success'
83
- }
84
- });
85
-
86
- expect(wrapper.vm.state).toBe('success');
87
- });
88
-
89
- it('sets aria-invalid based on error state', () => {
90
- const errorWrapper = mount(PhoneInput, {
91
- props: {
92
- state: 'error'
93
- }
94
- });
95
-
96
- expect(errorWrapper.vm.state).toBe('error');
97
-
98
- const defaultWrapper = mount(PhoneInput, {
99
- props: {
100
- state: 'default'
101
- }
102
- });
103
-
104
- expect(defaultWrapper.vm.state).toBe('default');
105
- });
106
- });
107
-
108
- // Test placeholder functionality
109
- describe('Placeholder', () => {
110
- it('uses default empty placeholder', () => {
111
- const wrapper = mount(PhoneInput);
112
- expect(wrapper.vm.placeholder).toBe('');
113
- });
114
-
115
- it('accepts custom placeholder', () => {
116
- const wrapper = mount(PhoneInput, {
117
- props: {
118
- placeholder: 'Enter your phone number'
119
- }
120
- });
121
-
122
- expect(wrapper.vm.placeholder).toBe('Enter your phone number');
123
- });
124
-
125
- it('uses placeholder for aria-label', () => {
126
- const wrapper = mount(PhoneInput, {
127
- props: {
128
- placeholder: 'Phone number'
129
- }
130
- });
131
-
132
- expect(wrapper.vm.placeholder).toBe('Phone number');
133
- });
134
- });
135
-
136
- // Test country configuration
137
- describe('Country Configuration', () => {
138
- it('uses default country code DE', () => {
139
- const wrapper = mount(PhoneInput);
140
- expect(wrapper.vm.defaultCountryCode).toBe('DE');
141
- });
142
-
143
- it('accepts custom default country code', () => {
144
- const wrapper = mount(PhoneInput, {
145
- props: {
146
- defaultCountryCode: 'US'
147
- }
148
- });
149
-
150
- expect(wrapper.vm.defaultCountryCode).toBe('US');
151
- });
152
-
153
- it('uses empty countries list by default', () => {
154
- const wrapper = mount(PhoneInput);
155
- expect(wrapper.vm.countriesCodes).toEqual([]);
156
- });
157
-
158
- it('accepts custom countries list', () => {
159
- const countries = ['US', 'GB', 'FR', 'DE'];
160
- const wrapper = mount(PhoneInput, {
161
- props: {
162
- countriesCodes: countries
163
- }
164
- });
165
-
166
- expect(wrapper.vm.countriesCodes).toEqual(countries);
167
- });
168
-
169
- it('handles single country in list', () => {
170
- const wrapper = mount(PhoneInput, {
171
- props: {
172
- countriesCodes: ['US']
173
- }
174
- });
175
-
176
- expect(wrapper.vm.countriesCodes).toEqual(['US']);
177
- });
178
- });
179
-
180
- // Test icons and styling
181
- describe('Icons and Styling', () => {
182
- it('uses default phone icon', () => {
183
- const wrapper = mount(PhoneInput);
184
- expect(wrapper.vm.appendInnerIcon).toBe('heroicons:phone');
185
- });
186
-
187
- it('accepts custom append inner icon', () => {
188
- const wrapper = mount(PhoneInput, {
189
- props: {
190
- appendInnerIcon: 'mdi-phone'
191
- }
192
- });
193
-
194
- expect(wrapper.vm.appendInnerIcon).toBe('mdi-phone');
195
- });
196
-
197
- it('uses default outlined variant', () => {
198
- const wrapper = mount(PhoneInput);
199
- expect(wrapper.vm.variant).toBe('outlined');
200
- });
201
-
202
- it('accepts custom variant', () => {
203
- const variants = ['filled', 'outlined', 'underlined', 'solo'];
204
-
205
- variants.forEach(variant => {
206
- const wrapper = mount(PhoneInput, {
207
- props: { variant }
208
- });
209
-
210
- expect(wrapper.vm.variant).toBe(variant);
211
- });
212
- });
213
-
214
- it('handles border on hover', () => {
215
- const wrapper = mount(PhoneInput, {
216
- props: {
217
- borderOnHover: true
218
- }
219
- });
220
-
221
- expect(wrapper.find('.border-on-hover').exists()).toBe(true);
222
- expect(wrapper.vm.borderOnHover).toBe(true);
223
- });
224
-
225
- it('does not apply border-on-hover class by default', () => {
226
- const wrapper = mount(PhoneInput);
227
-
228
- expect(wrapper.find('.border-on-hover').exists()).toBe(false);
229
- expect(wrapper.vm.borderOnHover).toBe(false);
230
- });
231
- });
232
-
233
- // Test event emission
234
- describe('Event Emission', () => {
235
- it('emits update:phone event', async () => {
236
- const wrapper = mount(PhoneInput);
237
-
238
- // Simulate the event that would be emitted by VPhoneInput
239
- wrapper.vm.$emit('update:phone', '+1234567890');
240
-
241
- expect(wrapper.emitted('update:phone')).toBeTruthy();
242
- expect(wrapper.emitted('update:phone')?.[0]).toEqual(['+1234567890']);
243
- });
244
-
245
- it('handles different phone number formats', async () => {
246
- const wrapper = mount(PhoneInput);
247
-
248
- const phoneNumbers = [
249
- '+1 (555) 123-4567',
250
- '+49 30 12345678',
251
- '+44 20 7123 4567',
252
- '555-123-4567'
253
- ];
254
-
255
- phoneNumbers.forEach((phone, index) => {
256
- wrapper.vm.$emit('update:phone', phone);
257
- });
258
-
259
- expect(wrapper.emitted('update:phone')).toHaveLength(phoneNumbers.length);
260
- phoneNumbers.forEach((phone, index) => {
261
- expect(wrapper.emitted('update:phone')?.[index]).toEqual([phone]);
262
- });
263
- });
264
-
265
- it('handles empty phone number', async () => {
266
- const wrapper = mount(PhoneInput);
267
-
268
- wrapper.vm.$emit('update:phone', '');
269
-
270
- expect(wrapper.emitted('update:phone')?.[0]).toEqual(['']);
271
- });
272
- });
273
-
274
- // Test exposed methods
275
- describe('Exposed Methods', () => {
276
- it('exposes focus, blur, and select methods', () => {
277
- const wrapper = mount(PhoneInput);
278
-
279
- expect(wrapper.vm.focus).toBeDefined();
280
- expect(wrapper.vm.blur).toBeDefined();
281
- expect(wrapper.vm.select).toBeDefined();
282
- expect(typeof wrapper.vm.focus).toBe('function');
283
- expect(typeof wrapper.vm.blur).toBe('function');
284
- expect(typeof wrapper.vm.select).toBe('function');
285
- });
286
-
287
- it('getNativeInput returns null with mocked component', () => {
288
- const wrapper = mount(PhoneInput);
289
- const nativeInput = wrapper.vm.getNativeInput();
290
-
291
- expect(nativeInput).toBeUndefined();
292
- });
293
-
294
- it('focus method works with mocked input', async () => {
295
- const wrapper = mount(PhoneInput);
296
-
297
- // Get the real input element from the stub
298
- const nativeInput = wrapper.vm.getNativeInput();
299
- if (nativeInput) {
300
- const focusSpy = vi.spyOn(nativeInput, 'focus');
301
-
302
- wrapper.vm.focus();
303
- await nextTick();
304
-
305
- expect(focusSpy).toHaveBeenCalled();
306
- } else {
307
- // If no input is found, just verify the method exists
308
- expect(wrapper.vm.focus).toBeDefined();
309
- }
310
- });
311
-
312
- it('blur method works with mocked input', async () => {
313
- const wrapper = mount(PhoneInput);
314
-
315
- // Get the real input element from the stub
316
- const nativeInput = wrapper.vm.getNativeInput();
317
- if (nativeInput) {
318
- const blurSpy = vi.spyOn(nativeInput, 'blur');
319
-
320
- wrapper.vm.blur();
321
- await nextTick();
322
-
323
- expect(blurSpy).toHaveBeenCalled();
324
- } else {
325
- // If no input is found, just verify the method exists
326
- expect(wrapper.vm.blur).toBeDefined();
327
- }
328
- });
329
-
330
- it('select method works with mocked input', async () => {
331
- const wrapper = mount(PhoneInput);
332
-
333
- // Get the real input element from the stub
334
- const nativeInput = wrapper.vm.getNativeInput();
335
- if (nativeInput) {
336
- const selectSpy = vi.spyOn(nativeInput, 'select');
337
-
338
- wrapper.vm.select();
339
- await nextTick();
340
-
341
- expect(selectSpy).toHaveBeenCalled();
342
- } else {
343
- // If no input is found, just verify the method exists
344
- expect(wrapper.vm.select).toBeDefined();
345
- }
346
- });
347
-
348
- it('handles null inputRef gracefully', () => {
349
- const wrapper = mount(PhoneInput);
350
- wrapper.vm.inputRef = null;
351
-
352
- expect(() => wrapper.vm.focus()).not.toThrow();
353
- expect(() => wrapper.vm.blur()).not.toThrow();
354
- expect(() => wrapper.vm.select()).not.toThrow();
355
- });
356
- });
357
-
358
- // Test VPhoneInput configuration
359
- describe('VPhoneInput Configuration', () => {
360
- it('configures VPhoneInput with correct props', () => {
361
- const wrapper = mount(PhoneInput, {
362
- props: {
363
- placeholder: 'Enter phone',
364
- variant: 'filled',
365
- defaultCountryCode: 'US',
366
- appendInnerIcon: 'mdi-phone',
367
- borderOnHover: true
368
- }
369
- });
370
-
371
- // With mocked VPhoneInput, verify the component renders
372
- expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
373
- expect(wrapper.vm.placeholder).toBe('Enter phone');
374
- expect(wrapper.vm.variant).toBe('filled');
375
- expect(wrapper.vm.defaultCountryCode).toBe('US');
376
- expect(wrapper.vm.appendInnerIcon).toBe('mdi-phone');
377
- });
378
-
379
- it('sets display format to international', () => {
380
- const wrapper = mount(PhoneInput);
381
-
382
- // Verify component renders correctly (display-format is hardcoded in template)
383
- expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
384
- });
385
-
386
- it('hides details by default', () => {
387
- const wrapper = mount(PhoneInput);
388
-
389
- // hide-details is hardcoded in template
390
- expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
391
- });
392
-
393
- it('uses svg country icon mode', () => {
394
- const wrapper = mount(PhoneInput);
395
-
396
- // countryIconMode="svg" is hardcoded in template
397
- expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
398
- });
399
-
400
- it('sets empty label', () => {
401
- const wrapper = mount(PhoneInput);
402
-
403
- // label="" is hardcoded in template
404
- expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
405
- });
406
- });
407
-
408
- // Test accessibility
409
- describe('Accessibility', () => {
410
- it('has data-testid for testing', () => {
411
- const wrapper = mount(PhoneInput);
412
- expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
413
- });
414
-
415
- it('sets aria-label from placeholder', () => {
416
- const wrapper = mount(PhoneInput, {
417
- props: {
418
- placeholder: 'Enter your phone number'
419
- }
420
- });
421
-
422
- expect(wrapper.vm.placeholder).toBe('Enter your phone number');
423
- });
424
-
425
- it('handles aria-invalid for error state', () => {
426
- const wrapper = mount(PhoneInput, {
427
- props: {
428
- state: 'error'
429
- }
430
- });
431
-
432
- expect(wrapper.vm.state).toBe('error');
433
- });
434
-
435
- it('provides meaningful placeholder text', () => {
436
- const wrapper = mount(PhoneInput, {
437
- props: {
438
- placeholder: 'Phone number (including country code)'
439
- }
440
- });
441
-
442
- expect(wrapper.vm.placeholder).toBe('Phone number (including country code)');
443
- });
444
- });
445
-
446
- // Test edge cases
447
- describe('Edge Cases', () => {
448
- it('handles empty countries array', () => {
449
- const wrapper = mount(PhoneInput, {
450
- props: {
451
- countriesCodes: []
452
- }
453
- });
454
-
455
- expect(wrapper.vm.countriesCodes).toEqual([]);
456
- });
457
-
458
- it('handles invalid country codes gracefully', () => {
459
- const wrapper = mount(PhoneInput, {
460
- props: {
461
- countriesCodes: ['XX', 'YY', 'ZZ'], // Invalid country codes
462
- defaultCountryCode: 'XX'
463
- }
464
- });
465
-
466
- expect(wrapper.vm.countriesCodes).toEqual(['XX', 'YY', 'ZZ']);
467
- expect(wrapper.vm.defaultCountryCode).toBe('XX');
468
- });
469
-
470
- it('handles special characters in placeholder', () => {
471
- const specialPlaceholder = 'Téléphone (avec indicatif pays) +33...';
472
- const wrapper = mount(PhoneInput, {
473
- props: {
474
- placeholder: specialPlaceholder
475
- }
476
- });
477
-
478
- expect(wrapper.vm.placeholder).toBe(specialPlaceholder);
479
- });
480
-
481
- it('handles empty string props', () => {
482
- const wrapper = mount(PhoneInput, {
483
- props: {
484
- placeholder: '',
485
- defaultCountryCode: '',
486
- appendInnerIcon: '',
487
- variant: ''
488
- }
489
- });
490
-
491
- expect(wrapper.vm.placeholder).toBe('');
492
- expect(wrapper.vm.defaultCountryCode).toBe('');
493
- expect(wrapper.vm.appendInnerIcon).toBe('');
494
- expect(wrapper.vm.variant).toBe('');
495
- });
496
-
497
- it('handles large countries list', () => {
498
- // Create a large list of country codes
499
- const largeCountriesList = Array.from({ length: 100 }, (_, i) =>
500
- `C${i.toString().padStart(2, '0')}`
501
- );
502
-
503
- const wrapper = mount(PhoneInput, {
504
- props: {
505
- countriesCodes: largeCountriesList
506
- }
507
- });
508
-
509
- expect(wrapper.vm.countriesCodes).toHaveLength(100);
510
- });
511
- });
512
-
513
- // Test attributes inheritance
514
- describe('Attributes Inheritance', () => {
515
- it('inherits v-bind="$attrs"', () => {
516
- const wrapper = mount(PhoneInput, {
517
- attrs: {
518
- disabled: true,
519
- readonly: true,
520
- 'data-custom': 'test'
521
- }
522
- });
523
-
524
- // With mocked VPhoneInput, verify component renders
525
- expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
526
- });
527
-
528
- it('handles class inheritance', () => {
529
- const wrapper = mount(PhoneInput, {
530
- attrs: {
531
- class: 'custom-phone-class'
532
- }
533
- });
534
-
535
- // Should still have wl-phone-input class
536
- expect(wrapper.find('.wl-phone-input').exists()).toBe(true);
537
- });
538
- });
539
-
540
- // Test complex scenarios
541
- describe('Complex Scenarios', () => {
542
- it('works with all props configured', () => {
543
- const wrapper = mount(PhoneInput, {
544
- props: {
545
- state: 'success',
546
- placeholder: 'Enter international phone number',
547
- countriesCodes: ['US', 'GB', 'FR', 'DE', 'ES'],
548
- defaultCountryCode: 'US',
549
- appendInnerIcon: 'mdi-phone-classic',
550
- variant: 'filled',
551
- borderOnHover: true
552
- }
553
- });
554
-
555
- expect(wrapper.vm.state).toBe('success');
556
- expect(wrapper.vm.placeholder).toBe('Enter international phone number');
557
- expect(wrapper.vm.countriesCodes).toEqual(['US', 'GB', 'FR', 'DE', 'ES']);
558
- expect(wrapper.vm.defaultCountryCode).toBe('US');
559
- expect(wrapper.vm.appendInnerIcon).toBe('mdi-phone-classic');
560
- expect(wrapper.vm.variant).toBe('filled');
561
- expect(wrapper.vm.borderOnHover).toBe(true);
562
- expect(wrapper.find('.border-on-hover').exists()).toBe(true);
563
- });
564
-
565
- it('handles state changes', async () => {
566
- const wrapper = mount(PhoneInput, {
567
- props: {
568
- state: 'default'
569
- }
570
- });
571
-
572
- expect(wrapper.vm.state).toBe('default');
573
-
574
- await wrapper.setProps({ state: 'error' });
575
- expect(wrapper.vm.state).toBe('error');
576
-
577
- await wrapper.setProps({ state: 'success' });
578
- expect(wrapper.vm.state).toBe('success');
579
-
580
- await wrapper.setProps({ state: 'default' });
581
- expect(wrapper.vm.state).toBe('default');
582
- });
583
-
584
- it('handles rapid prop changes', async () => {
585
- const wrapper = mount(PhoneInput, {
586
- props: {
587
- placeholder: 'Initial',
588
- defaultCountryCode: 'DE'
589
- }
590
- });
591
-
592
- await wrapper.setProps({
593
- placeholder: 'Updated phone',
594
- defaultCountryCode: 'US',
595
- variant: 'filled'
596
- });
597
-
598
- expect(wrapper.vm.placeholder).toBe('Updated phone');
599
- expect(wrapper.vm.defaultCountryCode).toBe('US');
600
- expect(wrapper.vm.variant).toBe('filled');
601
- });
602
-
603
- it('maintains functionality across all validation states', () => {
604
- const states = ['default', 'error', 'success'];
605
-
606
- states.forEach(state => {
607
- const wrapper = mount(PhoneInput, {
608
- props: {
609
- state: state as any,
610
- placeholder: `Phone in ${state} state`
611
- }
612
- });
613
-
614
- expect(wrapper.vm.state).toBe(state);
615
- expect(wrapper.vm.placeholder).toBe(`Phone in ${state} state`);
616
- expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
617
- });
618
- });
619
- });
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import { nextTick } from 'vue';
4
+ import PhoneInput from '@components/PhoneInput/PhoneInput.vue';
5
+
6
+ // Mock v-phone-input
7
+ vi.mock('v-phone-input', () => ({
8
+ VPhoneInput: {
9
+ template: '<div data-testid="v-phone-input"><slot /></div>',
10
+ props: [
11
+ 'include-countries', 'aria-label', 'variant', 'label', 'appendInnerIcon',
12
+ 'placeHolder', 'display-format', 'default-country', 'hide-details',
13
+ 'aria-invalid', 'countryIconMode'
14
+ ]
15
+ }
16
+ }));
17
+
18
+ describe('PhoneInput', () => {
19
+ beforeEach(() => {
20
+ vi.clearAllMocks();
21
+ });
22
+
23
+ // Test default behavior and rendering
24
+ describe('Default Behavior', () => {
25
+ it('renders with default props', () => {
26
+ const wrapper = mount(PhoneInput);
27
+
28
+ expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
29
+ expect(wrapper.find('.wl-phone-input').exists()).toBe(true);
30
+ expect(wrapper.vm.state).toBe('default');
31
+ expect(wrapper.vm.placeholder).toBe('');
32
+ expect(wrapper.vm.countriesCodes).toEqual([]);
33
+ expect(wrapper.vm.defaultCountryCode).toBe('DE');
34
+ expect(wrapper.vm.appendInnerIcon).toBe('heroicons:phone');
35
+ expect(wrapper.vm.variant).toBe('outlined');
36
+ expect(wrapper.vm.borderOnHover).toBe(false);
37
+ });
38
+
39
+ it('has data-testid for testing', () => {
40
+ const wrapper = mount(PhoneInput);
41
+ expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
42
+ });
43
+
44
+ it('applies wl-phone-input CSS class', () => {
45
+ const wrapper = mount(PhoneInput);
46
+ expect(wrapper.find('.wl-phone-input').exists()).toBe(true);
47
+ });
48
+
49
+ it('sets id attribute to "phone"', () => {
50
+ const wrapper = mount(PhoneInput);
51
+ // Since VPhoneInput is mocked, we can't directly test the id
52
+ // but we can verify the component renders correctly
53
+ expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
54
+ });
55
+ });
56
+
57
+ // Test validation states
58
+ describe('Validation States', () => {
59
+ it('handles default state', () => {
60
+ const wrapper = mount(PhoneInput, {
61
+ props: {
62
+ state: 'default'
63
+ }
64
+ });
65
+
66
+ expect(wrapper.vm.state).toBe('default');
67
+ });
68
+
69
+ it('handles error state', () => {
70
+ const wrapper = mount(PhoneInput, {
71
+ props: {
72
+ state: 'error'
73
+ }
74
+ });
75
+
76
+ expect(wrapper.vm.state).toBe('error');
77
+ });
78
+
79
+ it('handles success state', () => {
80
+ const wrapper = mount(PhoneInput, {
81
+ props: {
82
+ state: 'success'
83
+ }
84
+ });
85
+
86
+ expect(wrapper.vm.state).toBe('success');
87
+ });
88
+
89
+ it('sets aria-invalid based on error state', () => {
90
+ const errorWrapper = mount(PhoneInput, {
91
+ props: {
92
+ state: 'error'
93
+ }
94
+ });
95
+
96
+ expect(errorWrapper.vm.state).toBe('error');
97
+
98
+ const defaultWrapper = mount(PhoneInput, {
99
+ props: {
100
+ state: 'default'
101
+ }
102
+ });
103
+
104
+ expect(defaultWrapper.vm.state).toBe('default');
105
+ });
106
+ });
107
+
108
+ // Test placeholder functionality
109
+ describe('Placeholder', () => {
110
+ it('uses default empty placeholder', () => {
111
+ const wrapper = mount(PhoneInput);
112
+ expect(wrapper.vm.placeholder).toBe('');
113
+ });
114
+
115
+ it('accepts custom placeholder', () => {
116
+ const wrapper = mount(PhoneInput, {
117
+ props: {
118
+ placeholder: 'Enter your phone number'
119
+ }
120
+ });
121
+
122
+ expect(wrapper.vm.placeholder).toBe('Enter your phone number');
123
+ });
124
+
125
+ it('uses placeholder for aria-label', () => {
126
+ const wrapper = mount(PhoneInput, {
127
+ props: {
128
+ placeholder: 'Phone number'
129
+ }
130
+ });
131
+
132
+ expect(wrapper.vm.placeholder).toBe('Phone number');
133
+ });
134
+ });
135
+
136
+ // Test country configuration
137
+ describe('Country Configuration', () => {
138
+ it('uses default country code DE', () => {
139
+ const wrapper = mount(PhoneInput);
140
+ expect(wrapper.vm.defaultCountryCode).toBe('DE');
141
+ });
142
+
143
+ it('accepts custom default country code', () => {
144
+ const wrapper = mount(PhoneInput, {
145
+ props: {
146
+ defaultCountryCode: 'US'
147
+ }
148
+ });
149
+
150
+ expect(wrapper.vm.defaultCountryCode).toBe('US');
151
+ });
152
+
153
+ it('uses empty countries list by default', () => {
154
+ const wrapper = mount(PhoneInput);
155
+ expect(wrapper.vm.countriesCodes).toEqual([]);
156
+ });
157
+
158
+ it('accepts custom countries list', () => {
159
+ const countries = ['US', 'GB', 'FR', 'DE'];
160
+ const wrapper = mount(PhoneInput, {
161
+ props: {
162
+ countriesCodes: countries
163
+ }
164
+ });
165
+
166
+ expect(wrapper.vm.countriesCodes).toEqual(countries);
167
+ });
168
+
169
+ it('handles single country in list', () => {
170
+ const wrapper = mount(PhoneInput, {
171
+ props: {
172
+ countriesCodes: ['US']
173
+ }
174
+ });
175
+
176
+ expect(wrapper.vm.countriesCodes).toEqual(['US']);
177
+ });
178
+ });
179
+
180
+ // Test icons and styling
181
+ describe('Icons and Styling', () => {
182
+ it('uses default phone icon', () => {
183
+ const wrapper = mount(PhoneInput);
184
+ expect(wrapper.vm.appendInnerIcon).toBe('heroicons:phone');
185
+ });
186
+
187
+ it('accepts custom append inner icon', () => {
188
+ const wrapper = mount(PhoneInput, {
189
+ props: {
190
+ appendInnerIcon: 'mdi-phone'
191
+ }
192
+ });
193
+
194
+ expect(wrapper.vm.appendInnerIcon).toBe('mdi-phone');
195
+ });
196
+
197
+ it('uses default outlined variant', () => {
198
+ const wrapper = mount(PhoneInput);
199
+ expect(wrapper.vm.variant).toBe('outlined');
200
+ });
201
+
202
+ it('accepts custom variant', () => {
203
+ const variants = ['filled', 'outlined', 'underlined', 'solo'];
204
+
205
+ variants.forEach(variant => {
206
+ const wrapper = mount(PhoneInput, {
207
+ props: { variant }
208
+ });
209
+
210
+ expect(wrapper.vm.variant).toBe(variant);
211
+ });
212
+ });
213
+
214
+ it('handles border on hover', () => {
215
+ const wrapper = mount(PhoneInput, {
216
+ props: {
217
+ borderOnHover: true
218
+ }
219
+ });
220
+
221
+ expect(wrapper.find('.border-on-hover').exists()).toBe(true);
222
+ expect(wrapper.vm.borderOnHover).toBe(true);
223
+ });
224
+
225
+ it('does not apply border-on-hover class by default', () => {
226
+ const wrapper = mount(PhoneInput);
227
+
228
+ expect(wrapper.find('.border-on-hover').exists()).toBe(false);
229
+ expect(wrapper.vm.borderOnHover).toBe(false);
230
+ });
231
+ });
232
+
233
+ // Test event emission
234
+ describe('Event Emission', () => {
235
+ it('emits update:phone event', async () => {
236
+ const wrapper = mount(PhoneInput);
237
+
238
+ // Simulate the event that would be emitted by VPhoneInput
239
+ wrapper.vm.$emit('update:phone', '+1234567890');
240
+
241
+ expect(wrapper.emitted('update:phone')).toBeTruthy();
242
+ expect(wrapper.emitted('update:phone')?.[0]).toEqual(['+1234567890']);
243
+ });
244
+
245
+ it('handles different phone number formats', async () => {
246
+ const wrapper = mount(PhoneInput);
247
+
248
+ const phoneNumbers = [
249
+ '+1 (555) 123-4567',
250
+ '+49 30 12345678',
251
+ '+44 20 7123 4567',
252
+ '555-123-4567'
253
+ ];
254
+
255
+ phoneNumbers.forEach((phone, index) => {
256
+ wrapper.vm.$emit('update:phone', phone);
257
+ });
258
+
259
+ expect(wrapper.emitted('update:phone')).toHaveLength(phoneNumbers.length);
260
+ phoneNumbers.forEach((phone, index) => {
261
+ expect(wrapper.emitted('update:phone')?.[index]).toEqual([phone]);
262
+ });
263
+ });
264
+
265
+ it('handles empty phone number', async () => {
266
+ const wrapper = mount(PhoneInput);
267
+
268
+ wrapper.vm.$emit('update:phone', '');
269
+
270
+ expect(wrapper.emitted('update:phone')?.[0]).toEqual(['']);
271
+ });
272
+ });
273
+
274
+ // Test exposed methods
275
+ describe('Exposed Methods', () => {
276
+ it('exposes focus, blur, and select methods', () => {
277
+ const wrapper = mount(PhoneInput);
278
+
279
+ expect(wrapper.vm.focus).toBeDefined();
280
+ expect(wrapper.vm.blur).toBeDefined();
281
+ expect(wrapper.vm.select).toBeDefined();
282
+ expect(typeof wrapper.vm.focus).toBe('function');
283
+ expect(typeof wrapper.vm.blur).toBe('function');
284
+ expect(typeof wrapper.vm.select).toBe('function');
285
+ });
286
+
287
+ it('getNativeInput returns null with mocked component', () => {
288
+ const wrapper = mount(PhoneInput);
289
+ const nativeInput = wrapper.vm.getNativeInput();
290
+
291
+ expect(nativeInput).toBeUndefined();
292
+ });
293
+
294
+ it('focus method works with mocked input', async () => {
295
+ const wrapper = mount(PhoneInput);
296
+
297
+ // Get the real input element from the stub
298
+ const nativeInput = wrapper.vm.getNativeInput();
299
+ if (nativeInput) {
300
+ const focusSpy = vi.spyOn(nativeInput, 'focus');
301
+
302
+ wrapper.vm.focus();
303
+ await nextTick();
304
+
305
+ expect(focusSpy).toHaveBeenCalled();
306
+ } else {
307
+ // If no input is found, just verify the method exists
308
+ expect(wrapper.vm.focus).toBeDefined();
309
+ }
310
+ });
311
+
312
+ it('blur method works with mocked input', async () => {
313
+ const wrapper = mount(PhoneInput);
314
+
315
+ // Get the real input element from the stub
316
+ const nativeInput = wrapper.vm.getNativeInput();
317
+ if (nativeInput) {
318
+ const blurSpy = vi.spyOn(nativeInput, 'blur');
319
+
320
+ wrapper.vm.blur();
321
+ await nextTick();
322
+
323
+ expect(blurSpy).toHaveBeenCalled();
324
+ } else {
325
+ // If no input is found, just verify the method exists
326
+ expect(wrapper.vm.blur).toBeDefined();
327
+ }
328
+ });
329
+
330
+ it('select method works with mocked input', async () => {
331
+ const wrapper = mount(PhoneInput);
332
+
333
+ // Get the real input element from the stub
334
+ const nativeInput = wrapper.vm.getNativeInput();
335
+ if (nativeInput) {
336
+ const selectSpy = vi.spyOn(nativeInput, 'select');
337
+
338
+ wrapper.vm.select();
339
+ await nextTick();
340
+
341
+ expect(selectSpy).toHaveBeenCalled();
342
+ } else {
343
+ // If no input is found, just verify the method exists
344
+ expect(wrapper.vm.select).toBeDefined();
345
+ }
346
+ });
347
+
348
+ it('handles null inputRef gracefully', () => {
349
+ const wrapper = mount(PhoneInput);
350
+ wrapper.vm.inputRef = null;
351
+
352
+ expect(() => wrapper.vm.focus()).not.toThrow();
353
+ expect(() => wrapper.vm.blur()).not.toThrow();
354
+ expect(() => wrapper.vm.select()).not.toThrow();
355
+ });
356
+ });
357
+
358
+ // Test VPhoneInput configuration
359
+ describe('VPhoneInput Configuration', () => {
360
+ it('configures VPhoneInput with correct props', () => {
361
+ const wrapper = mount(PhoneInput, {
362
+ props: {
363
+ placeholder: 'Enter phone',
364
+ variant: 'filled',
365
+ defaultCountryCode: 'US',
366
+ appendInnerIcon: 'mdi-phone',
367
+ borderOnHover: true
368
+ }
369
+ });
370
+
371
+ // With mocked VPhoneInput, verify the component renders
372
+ expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
373
+ expect(wrapper.vm.placeholder).toBe('Enter phone');
374
+ expect(wrapper.vm.variant).toBe('filled');
375
+ expect(wrapper.vm.defaultCountryCode).toBe('US');
376
+ expect(wrapper.vm.appendInnerIcon).toBe('mdi-phone');
377
+ });
378
+
379
+ it('sets display format to international', () => {
380
+ const wrapper = mount(PhoneInput);
381
+
382
+ // Verify component renders correctly (display-format is hardcoded in template)
383
+ expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
384
+ });
385
+
386
+ it('hides details by default', () => {
387
+ const wrapper = mount(PhoneInput);
388
+
389
+ // hide-details is hardcoded in template
390
+ expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
391
+ });
392
+
393
+ it('uses svg country icon mode', () => {
394
+ const wrapper = mount(PhoneInput);
395
+
396
+ // countryIconMode="svg" is hardcoded in template
397
+ expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
398
+ });
399
+
400
+ it('sets empty label', () => {
401
+ const wrapper = mount(PhoneInput);
402
+
403
+ // label="" is hardcoded in template
404
+ expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
405
+ });
406
+ });
407
+
408
+ // Test accessibility
409
+ describe('Accessibility', () => {
410
+ it('has data-testid for testing', () => {
411
+ const wrapper = mount(PhoneInput);
412
+ expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
413
+ });
414
+
415
+ it('sets aria-label from placeholder', () => {
416
+ const wrapper = mount(PhoneInput, {
417
+ props: {
418
+ placeholder: 'Enter your phone number'
419
+ }
420
+ });
421
+
422
+ expect(wrapper.vm.placeholder).toBe('Enter your phone number');
423
+ });
424
+
425
+ it('handles aria-invalid for error state', () => {
426
+ const wrapper = mount(PhoneInput, {
427
+ props: {
428
+ state: 'error'
429
+ }
430
+ });
431
+
432
+ expect(wrapper.vm.state).toBe('error');
433
+ });
434
+
435
+ it('provides meaningful placeholder text', () => {
436
+ const wrapper = mount(PhoneInput, {
437
+ props: {
438
+ placeholder: 'Phone number (including country code)'
439
+ }
440
+ });
441
+
442
+ expect(wrapper.vm.placeholder).toBe('Phone number (including country code)');
443
+ });
444
+ });
445
+
446
+ // Test edge cases
447
+ describe('Edge Cases', () => {
448
+ it('handles empty countries array', () => {
449
+ const wrapper = mount(PhoneInput, {
450
+ props: {
451
+ countriesCodes: []
452
+ }
453
+ });
454
+
455
+ expect(wrapper.vm.countriesCodes).toEqual([]);
456
+ });
457
+
458
+ it('handles invalid country codes gracefully', () => {
459
+ const wrapper = mount(PhoneInput, {
460
+ props: {
461
+ countriesCodes: ['XX', 'YY', 'ZZ'], // Invalid country codes
462
+ defaultCountryCode: 'XX'
463
+ }
464
+ });
465
+
466
+ expect(wrapper.vm.countriesCodes).toEqual(['XX', 'YY', 'ZZ']);
467
+ expect(wrapper.vm.defaultCountryCode).toBe('XX');
468
+ });
469
+
470
+ it('handles special characters in placeholder', () => {
471
+ const specialPlaceholder = 'Téléphone (avec indicatif pays) +33...';
472
+ const wrapper = mount(PhoneInput, {
473
+ props: {
474
+ placeholder: specialPlaceholder
475
+ }
476
+ });
477
+
478
+ expect(wrapper.vm.placeholder).toBe(specialPlaceholder);
479
+ });
480
+
481
+ it('handles empty string props', () => {
482
+ const wrapper = mount(PhoneInput, {
483
+ props: {
484
+ placeholder: '',
485
+ defaultCountryCode: '',
486
+ appendInnerIcon: '',
487
+ variant: ''
488
+ }
489
+ });
490
+
491
+ expect(wrapper.vm.placeholder).toBe('');
492
+ expect(wrapper.vm.defaultCountryCode).toBe('');
493
+ expect(wrapper.vm.appendInnerIcon).toBe('');
494
+ expect(wrapper.vm.variant).toBe('');
495
+ });
496
+
497
+ it('handles large countries list', () => {
498
+ // Create a large list of country codes
499
+ const largeCountriesList = Array.from({ length: 100 }, (_, i) =>
500
+ `C${i.toString().padStart(2, '0')}`
501
+ );
502
+
503
+ const wrapper = mount(PhoneInput, {
504
+ props: {
505
+ countriesCodes: largeCountriesList
506
+ }
507
+ });
508
+
509
+ expect(wrapper.vm.countriesCodes).toHaveLength(100);
510
+ });
511
+ });
512
+
513
+ // Test attributes inheritance
514
+ describe('Attributes Inheritance', () => {
515
+ it('inherits v-bind="$attrs"', () => {
516
+ const wrapper = mount(PhoneInput, {
517
+ attrs: {
518
+ disabled: true,
519
+ readonly: true,
520
+ 'data-custom': 'test'
521
+ }
522
+ });
523
+
524
+ // With mocked VPhoneInput, verify component renders
525
+ expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
526
+ });
527
+
528
+ it('handles class inheritance', () => {
529
+ const wrapper = mount(PhoneInput, {
530
+ attrs: {
531
+ class: 'custom-phone-class'
532
+ }
533
+ });
534
+
535
+ // Should still have wl-phone-input class
536
+ expect(wrapper.find('.wl-phone-input').exists()).toBe(true);
537
+ });
538
+ });
539
+
540
+ // Test complex scenarios
541
+ describe('Complex Scenarios', () => {
542
+ it('works with all props configured', () => {
543
+ const wrapper = mount(PhoneInput, {
544
+ props: {
545
+ state: 'success',
546
+ placeholder: 'Enter international phone number',
547
+ countriesCodes: ['US', 'GB', 'FR', 'DE', 'ES'],
548
+ defaultCountryCode: 'US',
549
+ appendInnerIcon: 'mdi-phone-classic',
550
+ variant: 'filled',
551
+ borderOnHover: true
552
+ }
553
+ });
554
+
555
+ expect(wrapper.vm.state).toBe('success');
556
+ expect(wrapper.vm.placeholder).toBe('Enter international phone number');
557
+ expect(wrapper.vm.countriesCodes).toEqual(['US', 'GB', 'FR', 'DE', 'ES']);
558
+ expect(wrapper.vm.defaultCountryCode).toBe('US');
559
+ expect(wrapper.vm.appendInnerIcon).toBe('mdi-phone-classic');
560
+ expect(wrapper.vm.variant).toBe('filled');
561
+ expect(wrapper.vm.borderOnHover).toBe(true);
562
+ expect(wrapper.find('.border-on-hover').exists()).toBe(true);
563
+ });
564
+
565
+ it('handles state changes', async () => {
566
+ const wrapper = mount(PhoneInput, {
567
+ props: {
568
+ state: 'default'
569
+ }
570
+ });
571
+
572
+ expect(wrapper.vm.state).toBe('default');
573
+
574
+ await wrapper.setProps({ state: 'error' });
575
+ expect(wrapper.vm.state).toBe('error');
576
+
577
+ await wrapper.setProps({ state: 'success' });
578
+ expect(wrapper.vm.state).toBe('success');
579
+
580
+ await wrapper.setProps({ state: 'default' });
581
+ expect(wrapper.vm.state).toBe('default');
582
+ });
583
+
584
+ it('handles rapid prop changes', async () => {
585
+ const wrapper = mount(PhoneInput, {
586
+ props: {
587
+ placeholder: 'Initial',
588
+ defaultCountryCode: 'DE'
589
+ }
590
+ });
591
+
592
+ await wrapper.setProps({
593
+ placeholder: 'Updated phone',
594
+ defaultCountryCode: 'US',
595
+ variant: 'filled'
596
+ });
597
+
598
+ expect(wrapper.vm.placeholder).toBe('Updated phone');
599
+ expect(wrapper.vm.defaultCountryCode).toBe('US');
600
+ expect(wrapper.vm.variant).toBe('filled');
601
+ });
602
+
603
+ it('maintains functionality across all validation states', () => {
604
+ const states = ['default', 'error', 'success'];
605
+
606
+ states.forEach(state => {
607
+ const wrapper = mount(PhoneInput, {
608
+ props: {
609
+ state: state as any,
610
+ placeholder: `Phone in ${state} state`
611
+ }
612
+ });
613
+
614
+ expect(wrapper.vm.state).toBe(state);
615
+ expect(wrapper.vm.placeholder).toBe(`Phone in ${state} state`);
616
+ expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
617
+ });
618
+ });
619
+ });
620
620
  });