@zap-wunschlachen/wl-shared-components 1.0.15 → 1.0.17

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 (235) 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 +149 -149
  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 +34 -28
  24. package/src/components/Appointment/Card/Actions.vue +73 -72
  25. package/src/components/Appointment/Card/Card.css +80 -80
  26. package/src/components/Appointment/Card/Card.vue +87 -87
  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/Loader/Loader.css +51 -51
  89. package/src/components/Loader/Loader.vue +1 -1
  90. package/src/components/Modal/Modal.css +5 -5
  91. package/src/components/Modal/Modal.vue +22 -22
  92. package/src/components/NotificationBubble/NotificationBubble.css +4 -4
  93. package/src/components/NotificationBubble/NotificationBubble.vue +90 -90
  94. package/src/components/OtpInput/OtpInput.css +39 -39
  95. package/src/components/OtpInput/OtpInput.vue +143 -143
  96. package/src/components/PhoneInput/PhoneInput.css +31 -31
  97. package/src/components/PhoneInput/PhoneInput.vue +113 -113
  98. package/src/components/Select/Select.css +150 -150
  99. package/src/components/Select/Select.vue +304 -304
  100. package/src/components/TextArea/TextArea.css +3 -3
  101. package/src/components/TextArea/TextArea.vue +126 -126
  102. package/src/components/TickBox/TickBox.css +49 -49
  103. package/src/components/TickBox/TickBox.vue +126 -126
  104. package/src/components/index.ts +23 -23
  105. package/src/constants/iconEnums.ts +3 -3
  106. package/src/i18n/i18n.ts +15 -15
  107. package/src/i18n/locales/de.json +27 -27
  108. package/src/i18n/locales/en.json +27 -27
  109. package/src/index.ts +34 -31
  110. package/src/main.ts +11 -11
  111. package/src/plugins/vuetify.ts +131 -131
  112. package/src/shims-vue.d.ts +10 -10
  113. package/src/stories/Accordion.stories.ts +650 -650
  114. package/src/stories/Audio.stories.ts +28 -28
  115. package/src/stories/Button.stories.ts +263 -263
  116. package/src/stories/CheckBox.stories.ts +348 -348
  117. package/src/stories/DateInput.stories.ts +53 -53
  118. package/src/stories/Dialog.stories.ts +147 -147
  119. package/src/stories/EditField.stories.ts +78 -78
  120. package/src/stories/IconBullet/IconBullet.stories.ts +201 -201
  121. package/src/stories/IconBullet/IconBulletList.stories.ts +275 -275
  122. package/src/stories/Input.stories.ts +351 -351
  123. package/src/stories/Laboratory/Cards/AppointmentCard/AppointmentCard.stories.ts +260 -260
  124. package/src/stories/Laboratory/Cards/DocumentCard/DocumentCard.stories.ts +176 -176
  125. package/src/stories/Laboratory/Cards/DocumentCard/DocumentCardItem.stories.ts +119 -119
  126. package/src/stories/Laboratory/Cards/InfoCard/InfoCard.stories.ts +320 -320
  127. package/src/stories/Laboratory/Cards/TicketCard/TicketCard.stories.ts +335 -335
  128. package/src/stories/Laboratory/Chat/ChatBoxImage.stories.ts +82 -82
  129. package/src/stories/Laboratory/Chat/ChatMessage.stories.ts +198 -198
  130. package/src/stories/Laboratory/Chat/ChatMessageBadge.stories.ts +204 -204
  131. package/src/stories/Laboratory/Chat/ChatNotification.stories.ts +144 -144
  132. package/src/stories/Laboratory/Chat/ProgressLinear.stories.ts +186 -186
  133. package/src/stories/Laboratory/Chat/StatusNotification.stories.ts +111 -111
  134. package/src/stories/Laboratory/MainColumnsBar.stories.ts +48 -48
  135. package/src/stories/Laboratory/ProgressCircle.stories.ts +261 -261
  136. package/src/stories/Laboratory/SelectionColumnBar.stories.ts +234 -234
  137. package/src/stories/Laboratory/TagLabel.stories.ts +418 -418
  138. package/src/stories/Laboratory/TagLabelGroup.stories.ts +234 -234
  139. package/src/stories/Laboratory/Timeline.stories.ts +403 -403
  140. package/src/stories/NotificationBubble.stories.ts +194 -194
  141. package/src/stories/OtpInput.stories.ts +100 -100
  142. package/src/stories/PhoneInput.stories.ts +52 -52
  143. package/src/stories/Select.stories.ts +419 -419
  144. package/src/stories/TextArea.stories.ts +112 -112
  145. package/src/stories/TickBox.stories.ts +294 -294
  146. package/src/stories/v-icon.stories.ts +91 -91
  147. package/src/types/index.ts +1 -0
  148. package/src/utils/index.ts +100 -100
  149. package/src/vite-env.d.ts +1 -1
  150. package/tests/e2e/README.md +220 -220
  151. package/tests/e2e/accessibility.spec.ts +638 -638
  152. package/tests/e2e/accordion.spec.ts +42 -42
  153. package/tests/e2e/additional-components.spec.ts +437 -437
  154. package/tests/e2e/all-components.spec.ts +135 -135
  155. package/tests/e2e/appointment-card.spec.ts +816 -816
  156. package/tests/e2e/button-fixed.spec.ts +58 -58
  157. package/tests/e2e/button.spec.ts +76 -76
  158. package/tests/e2e/checkbox.spec.ts +50 -50
  159. package/tests/e2e/date-input.spec.ts +46 -46
  160. package/tests/e2e/debug.spec.ts +51 -51
  161. package/tests/e2e/dialog.spec.ts +58 -58
  162. package/tests/e2e/input.spec.ts +55 -55
  163. package/tests/e2e/laboratory-components.spec.ts +320 -320
  164. package/tests/e2e/otp-input.spec.ts +50 -50
  165. package/tests/e2e/select.spec.ts +52 -52
  166. package/tests/e2e/storybook-utils.ts +59 -59
  167. package/tests/e2e/test-basic.spec.ts +33 -33
  168. package/tests/e2e/visual-regression.spec.ts +350 -350
  169. package/tests/unit/components/Accordion/AccordionGroup.spec.ts.skip +342 -342
  170. package/tests/unit/components/Accordion/AccordionItem.spec.ts.skip +383 -383
  171. package/tests/unit/components/Appointment/Card/Actions.spec.ts +407 -407
  172. package/tests/unit/components/Appointment/Card/Card.spec.ts +485 -485
  173. package/tests/unit/components/Appointment/Card/Details.spec.ts +397 -397
  174. package/tests/unit/components/Audio/Audio.spec.ts +403 -403
  175. package/tests/unit/components/Audio/Waveform.spec.ts +483 -483
  176. package/tests/unit/components/Core/Button.spec.ts +336 -336
  177. package/tests/unit/components/Core/Checkbox.spec.ts +544 -544
  178. package/tests/unit/components/Core/DateInput.spec.ts +690 -690
  179. package/tests/unit/components/Core/Dialog.spec.ts +485 -485
  180. package/tests/unit/components/Core/EditField.spec.ts +782 -782
  181. package/tests/unit/components/Core/Input.spec.ts +512 -512
  182. package/tests/unit/components/Core/Modal.spec.ts +518 -518
  183. package/tests/unit/components/Core/NotificationBubble.spec.ts +606 -606
  184. package/tests/unit/components/Core/OtpInput.spec.ts +708 -708
  185. package/tests/unit/components/Core/PhoneInput.spec.ts +619 -619
  186. package/tests/unit/components/Core/Select.spec.ts +712 -712
  187. package/tests/unit/components/Core/TextArea.spec.ts +565 -565
  188. package/tests/unit/components/Core/TickBox.spec.ts +779 -779
  189. package/tests/unit/components/IconBullet/IconBullet.spec.ts +356 -356
  190. package/tests/unit/components/IconBullet/IconBulletList.spec.ts +371 -371
  191. package/tests/unit/components/Icons/Audio/CloudFailed.spec.ts +108 -108
  192. package/tests/unit/components/Icons/Audio/CloudSaved.spec.ts +149 -149
  193. package/tests/unit/components/Icons/Audio/Delete.spec.ts +158 -158
  194. package/tests/unit/components/Icons/Audio/Pause.spec.ts +208 -208
  195. package/tests/unit/components/Icons/Audio/Play.spec.ts +217 -217
  196. package/tests/unit/components/Icons/CalendarNotification.spec.ts +186 -186
  197. package/tests/unit/components/Icons/Chair.spec.ts +234 -234
  198. package/tests/unit/components/Icons/ChairNotification.spec.ts +311 -311
  199. package/tests/unit/components/Icons/Circle.spec.ts +255 -255
  200. package/tests/unit/components/Icons/FavIcon.spec.ts +251 -251
  201. package/tests/unit/components/Icons/FilledCircle.spec.ts +274 -274
  202. package/tests/unit/components/Icons/Group3.spec.ts +355 -355
  203. package/tests/unit/components/Icons/RingNotification.spec.ts +393 -393
  204. package/tests/unit/components/Icons/calendar.spec.ts +286 -286
  205. package/tests/unit/components/Icons/checkbox.spec.ts +315 -315
  206. package/tests/unit/components/Icons/outlineChecked.spec.ts +434 -434
  207. package/tests/unit/components/Icons/play.spec.ts +308 -308
  208. package/tests/unit/components/Laboratory/AppointmentCard.spec.ts +167 -167
  209. package/tests/unit/components/Laboratory/ChatBoxImage.spec.ts +179 -179
  210. package/tests/unit/components/Laboratory/ChatMessage.spec.ts +263 -263
  211. package/tests/unit/components/Laboratory/ChatMessageBadge.spec.ts +282 -282
  212. package/tests/unit/components/Laboratory/ChatNotification.spec.ts +256 -256
  213. package/tests/unit/components/Laboratory/DocumentCard.spec.ts +228 -228
  214. package/tests/unit/components/Laboratory/DocumentCardItem.spec.ts +236 -236
  215. package/tests/unit/components/Laboratory/InfoCard.spec.ts +308 -308
  216. package/tests/unit/components/Laboratory/MainColumnsBar.spec.ts +251 -251
  217. package/tests/unit/components/Laboratory/ProgressCircle.spec.ts +290 -290
  218. package/tests/unit/components/Laboratory/ProgressLinear.spec.ts +275 -275
  219. package/tests/unit/components/Laboratory/SelectionColumnBar.spec.ts +288 -288
  220. package/tests/unit/components/Laboratory/StatusNotification.spec.ts +296 -296
  221. package/tests/unit/components/Laboratory/TagLabel.spec.ts +353 -353
  222. package/tests/unit/components/Laboratory/TagLabelGroup.spec.ts +377 -377
  223. package/tests/unit/components/Laboratory/TicketCard.spec.ts +351 -351
  224. package/tests/unit/components/Laboratory/TimeLineEvent.spec.ts +381 -381
  225. package/tests/unit/components/Laboratory/Timeline.spec.ts +419 -419
  226. package/tests/unit/constants/iconEnums.spec.ts +39 -39
  227. package/tests/unit/i18n/i18n.spec.ts +88 -88
  228. package/tests/unit/plugins/vuetify.spec.ts +220 -220
  229. package/tests/unit/setup.ts +189 -189
  230. package/tests/unit/src/components/index.spec.ts.skip +192 -192
  231. package/tests/unit/src/index.spec.ts.skip +182 -182
  232. package/tests/unit/src/main.spec.ts +151 -151
  233. package/tsconfig.json +26 -26
  234. package/vite.config.ts +29 -29
  235. 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
  });