@zap-wunschlachen/wl-shared-components 1.0.35 → 1.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/playwright.yml +205 -205
- package/.github/workflows/static.yml +61 -61
- package/.github/workflows/update-snapshots.yml +37 -37
- package/.prettierrc +5 -5
- package/.storybook/main.ts +18 -18
- package/.storybook/preview.ts +37 -37
- package/.storybook/storyWrapper.vue +18 -18
- package/.storybook/withVuetifyTheme.decorator.ts +21 -21
- package/App.vue +43 -38
- package/README.md +56 -56
- package/heroicons.ts +75 -75
- package/index.html +19 -19
- package/package.json +67 -67
- package/playwright.config.ts +48 -48
- package/public/audio/test.aac +0 -0
- package/public/background.svg +60 -60
- package/public/style.css +187 -187
- package/public/technologies.svg +22 -22
- package/src/assets/css/base.css +232 -232
- package/src/assets/css/variables.css +109 -109
- package/src/components/Accordion/Accordion.css +59 -59
- package/src/components/Accordion/AccordionGroup.vue +51 -51
- package/src/components/Accordion/AccordionItem.vue +66 -66
- package/src/components/Appointment/Card/Actions.css +54 -54
- package/src/components/Appointment/Card/Actions.vue +99 -99
- package/src/components/Appointment/Card/AnamneseNotification.css +15 -15
- package/src/components/Appointment/Card/AnamneseNotification.vue +23 -23
- package/src/components/Appointment/Card/Card.css +80 -80
- package/src/components/Appointment/Card/Card.vue +93 -93
- package/src/components/Appointment/Card/Details.css +50 -50
- package/src/components/Appointment/Card/Details.vue +43 -43
- package/src/components/Audio/Audio.vue +187 -187
- package/src/components/Audio/Waveform.vue +118 -117
- package/src/components/Button/Button.vue +174 -136
- package/src/components/CheckBox/CheckBox.css +214 -185
- package/src/components/CheckBox/Checkbox.vue +138 -130
- package/src/components/DateInput/DateInput.css +2 -2
- package/src/components/DateInput/DateInput.vue +262 -262
- package/src/components/Dialog/Dialog.css +6 -6
- package/src/components/Dialog/Dialog.vue +38 -29
- package/src/components/EditField/EditField.css +19 -19
- package/src/components/EditField/EditField.vue +202 -202
- package/src/components/ErrorPage/ErrorPage.css +172 -172
- package/src/components/IconBullet/IconBullet.vue +86 -86
- package/src/components/IconBullet/IconBulletList.vue +41 -41
- package/src/components/Icons/AdvanceAppointments.vue +153 -153
- package/src/components/Icons/Audio/CloudFailed.vue +20 -20
- package/src/components/Icons/Audio/CloudSaved.vue +21 -21
- package/src/components/Icons/Audio/Delete.vue +15 -15
- package/src/components/Icons/Audio/Pause.vue +18 -18
- package/src/components/Icons/Audio/Play.vue +15 -15
- package/src/components/Icons/CalendarNotification.vue +126 -126
- package/src/components/Icons/Chair.vue +32 -32
- package/src/components/Icons/ChairNotification.vue +35 -35
- package/src/components/Icons/Circle.vue +66 -66
- package/src/components/Icons/FavIcon.vue +22 -22
- package/src/components/Icons/FilledCircle.vue +11 -11
- package/src/components/Icons/Group3.vue +46 -46
- package/src/components/Icons/RingNotification.vue +54 -54
- package/src/components/Icons/SolidArrowRight.vue +14 -14
- package/src/components/Icons/calendar.vue +17 -17
- package/src/components/Icons/checkbox.vue +19 -19
- package/src/components/Icons/outlineChecked.vue +27 -27
- package/src/components/Icons/play.vue +5 -5
- package/src/components/Input/Input.css +187 -187
- package/src/components/Input/Input.vue +253 -247
- package/src/components/Laboratory/AppointmentCard/AppointmentCard.css +7 -7
- package/src/components/Laboratory/AppointmentCard/AppointmentCard.vue +116 -116
- package/src/components/Laboratory/ChatBoxImage/ChatBoxImage.vue +81 -81
- package/src/components/Laboratory/ChatMessage/ChatMessage.vue +113 -113
- package/src/components/Laboratory/ChatMessage/ChatMessageBadge.css +4 -4
- package/src/components/Laboratory/ChatMessage/ChatMessageBadge.vue +99 -99
- package/src/components/Laboratory/ChatNotification/ChatNotification.vue +130 -130
- package/src/components/Laboratory/DocumentCard/DocumentCard.css +3 -3
- package/src/components/Laboratory/DocumentCard/DocumentCard.vue +50 -50
- package/src/components/Laboratory/DocumentCard/DocumentCardItem.vue +53 -53
- package/src/components/Laboratory/InfoCard/InfoCard.vue +162 -162
- package/src/components/Laboratory/MainColumnsBar/MainColumnsBar.vue +102 -102
- package/src/components/Laboratory/ProgressCircle/ProgressCircle.vue +152 -152
- package/src/components/Laboratory/ProgressLinear/ProgressLinear.css +33 -33
- package/src/components/Laboratory/ProgressLinear/ProgressLinear.vue +75 -75
- package/src/components/Laboratory/SelectionColumnBar/SelectionColumnBar.vue +92 -92
- package/src/components/Laboratory/StatusNotification/StatusNotification.vue +49 -49
- package/src/components/Laboratory/TagLabel/TagLabel.vue +126 -126
- package/src/components/Laboratory/TagLabelGroup/TagLabelGroup.vue +97 -97
- package/src/components/Laboratory/TicketCard/TicketCard.css +3 -3
- package/src/components/Laboratory/TicketCard/TicketCard.vue +143 -143
- package/src/components/Laboratory/TimeLine/TimeLineEvent.css +18 -18
- package/src/components/Laboratory/TimeLine/TimeLineEvent.vue +119 -119
- package/src/components/Laboratory/TimeLine/Timeline.css +4 -4
- package/src/components/Laboratory/TimeLine/Timeline.vue +30 -30
- package/src/components/Loader/Loader.css +71 -51
- package/src/components/Loader/Loader.vue +1 -0
- package/src/components/MaintenanceBanner/MaintenanceBanner.css +353 -353
- package/src/components/MaintenanceBanner/MaintenanceBanner.vue +127 -127
- package/src/components/MaintenanceBanner/MaintenanceIllustration.vue +54 -54
- package/src/components/Modal/Modal.css +5 -5
- package/src/components/Modal/Modal.vue +22 -22
- package/src/components/NotificationBubble/NotificationBubble.css +4 -4
- package/src/components/NotificationBubble/NotificationBubble.vue +90 -90
- package/src/components/OtpInput/OtpInput.css +39 -39
- package/src/components/OtpInput/OtpInput.vue +151 -151
- package/src/components/PhoneInput/PhoneInput.css +31 -31
- package/src/components/PhoneInput/PhoneInput.vue +113 -113
- package/src/components/Select/Select.css +150 -150
- package/src/components/Select/Select.vue +316 -315
- package/src/components/TextArea/TextArea.css +3 -3
- package/src/components/TextArea/TextArea.vue +126 -126
- package/src/components/TickBox/TickBox.css +49 -49
- package/src/components/TickBox/TickBox.vue +126 -126
- package/src/components/accessibility.css +218 -0
- package/src/components/index.ts +28 -28
- package/src/constants/iconEnums.ts +3 -3
- package/src/i18n/i18n.ts +15 -15
- package/src/i18n/locales/de.json +30 -30
- package/src/i18n/locales/en.json +30 -30
- package/src/index.ts +34 -34
- package/src/main.ts +11 -11
- package/src/plugins/vuetify.ts +141 -141
- package/src/shims-vue.d.ts +10 -10
- package/src/stories/Accordion.stories.ts +650 -650
- package/src/stories/Audio.stories.ts +28 -28
- package/src/stories/Button.stories.ts +263 -263
- package/src/stories/CheckBox.stories.ts +348 -348
- package/src/stories/DateInput.stories.ts +53 -53
- package/src/stories/Dialog.stories.ts +147 -147
- package/src/stories/EditField.stories.ts +78 -78
- package/src/stories/IconBullet/IconBullet.stories.ts +201 -201
- package/src/stories/IconBullet/IconBulletList.stories.ts +275 -275
- package/src/stories/Input.stories.ts +351 -351
- package/src/stories/Laboratory/Cards/AppointmentCard/AppointmentCard.stories.ts +260 -260
- package/src/stories/Laboratory/Cards/DocumentCard/DocumentCard.stories.ts +176 -176
- package/src/stories/Laboratory/Cards/DocumentCard/DocumentCardItem.stories.ts +119 -119
- package/src/stories/Laboratory/Cards/InfoCard/InfoCard.stories.ts +320 -320
- package/src/stories/Laboratory/Cards/TicketCard/TicketCard.stories.ts +335 -335
- package/src/stories/Laboratory/Chat/ChatBoxImage.stories.ts +82 -82
- package/src/stories/Laboratory/Chat/ChatMessage.stories.ts +198 -198
- package/src/stories/Laboratory/Chat/ChatMessageBadge.stories.ts +204 -204
- package/src/stories/Laboratory/Chat/ChatNotification.stories.ts +144 -144
- package/src/stories/Laboratory/Chat/ProgressLinear.stories.ts +186 -186
- package/src/stories/Laboratory/Chat/StatusNotification.stories.ts +111 -111
- package/src/stories/Laboratory/MainColumnsBar.stories.ts +48 -48
- package/src/stories/Laboratory/ProgressCircle.stories.ts +261 -261
- package/src/stories/Laboratory/SelectionColumnBar.stories.ts +234 -234
- package/src/stories/Laboratory/TagLabel.stories.ts +418 -418
- package/src/stories/Laboratory/TagLabelGroup.stories.ts +234 -234
- package/src/stories/Laboratory/Timeline.stories.ts +403 -403
- package/src/stories/NotificationBubble.stories.ts +194 -194
- package/src/stories/OtpInput.stories.ts +100 -100
- package/src/stories/PhoneInput.stories.ts +52 -52
- package/src/stories/Select.stories.ts +419 -419
- package/src/stories/TextArea.stories.ts +112 -112
- package/src/stories/TickBox.stories.ts +294 -294
- package/src/stories/v-icon.stories.ts +91 -91
- package/src/utils/index.ts +109 -109
- package/src/vite-env.d.ts +1 -1
- package/tests/e2e/README.md +220 -220
- package/tests/e2e/accessibility.spec.ts +638 -638
- package/tests/e2e/accordion.spec.ts +42 -42
- package/tests/e2e/additional-components.spec.ts +437 -437
- package/tests/e2e/all-components.spec.ts +135 -135
- package/tests/e2e/appointment-card.spec.ts +816 -816
- package/tests/e2e/button-fixed.spec.ts +58 -58
- package/tests/e2e/button.spec.ts +76 -76
- package/tests/e2e/checkbox.spec.ts +50 -50
- package/tests/e2e/date-input.spec.ts +46 -46
- package/tests/e2e/debug.spec.ts +51 -51
- package/tests/e2e/dialog.spec.ts +58 -58
- package/tests/e2e/input.spec.ts +55 -55
- package/tests/e2e/laboratory-components.spec.ts +320 -320
- package/tests/e2e/otp-input.spec.ts +50 -50
- package/tests/e2e/select.spec.ts +52 -52
- package/tests/e2e/storybook-utils.ts +59 -59
- package/tests/e2e/test-basic.spec.ts +33 -33
- package/tests/e2e/visual-regression.spec.ts +350 -350
- package/tests/unit/accessibility/component-a11y.spec.ts +469 -0
- package/tests/unit/components/Accordion/AccordionGroup.spec.ts +228 -0
- package/tests/unit/components/Accordion/AccordionGroup.spec.ts.skip +342 -342
- package/tests/unit/components/Accordion/AccordionItem.spec.ts +292 -0
- package/tests/unit/components/Accordion/AccordionItem.spec.ts.skip +383 -383
- package/tests/unit/components/Appointment/AnamneseNotification.spec.ts +176 -0
- package/tests/unit/components/Appointment/Card/Actions.spec.ts +407 -407
- package/tests/unit/components/Appointment/Card/Card.spec.ts +485 -485
- package/tests/unit/components/Appointment/Card/Details.spec.ts +397 -397
- package/tests/unit/components/Audio/Audio.spec.ts +403 -403
- package/tests/unit/components/Audio/Waveform.spec.ts +483 -483
- package/tests/unit/components/Background/Background.spec.ts +177 -0
- package/tests/unit/components/Core/Button.spec.ts +336 -336
- package/tests/unit/components/Core/Checkbox.spec.ts +544 -544
- package/tests/unit/components/Core/DateInput.spec.ts +690 -690
- package/tests/unit/components/Core/Dialog.spec.ts +485 -485
- package/tests/unit/components/Core/EditField.spec.ts +782 -782
- package/tests/unit/components/Core/Input.spec.ts +512 -512
- package/tests/unit/components/Core/Modal.spec.ts +518 -518
- package/tests/unit/components/Core/NotificationBubble.spec.ts +606 -606
- package/tests/unit/components/Core/OtpInput.spec.ts +708 -708
- package/tests/unit/components/Core/PhoneInput.spec.ts +619 -619
- package/tests/unit/components/Core/Select.spec.ts +712 -712
- package/tests/unit/components/Core/TextArea.spec.ts +565 -565
- package/tests/unit/components/Core/TickBox.spec.ts +779 -779
- package/tests/unit/components/ErrorPage/ErrorPage.spec.ts +313 -0
- package/tests/unit/components/ErrorPage/ErrorPageLogo.spec.ts +153 -0
- package/tests/unit/components/IconBullet/IconBullet.spec.ts +356 -356
- package/tests/unit/components/IconBullet/IconBulletList.spec.ts +371 -371
- package/tests/unit/components/Icons/AdvanceAppointments.spec.ts +61 -0
- package/tests/unit/components/Icons/Audio/CloudFailed.spec.ts +108 -108
- package/tests/unit/components/Icons/Audio/CloudSaved.spec.ts +149 -149
- package/tests/unit/components/Icons/Audio/Delete.spec.ts +158 -158
- package/tests/unit/components/Icons/Audio/Pause.spec.ts +208 -208
- package/tests/unit/components/Icons/Audio/Play.spec.ts +217 -217
- package/tests/unit/components/Icons/CalendarNotification.spec.ts +186 -186
- package/tests/unit/components/Icons/Chair.spec.ts +234 -234
- package/tests/unit/components/Icons/ChairNotification.spec.ts +311 -311
- package/tests/unit/components/Icons/Circle.spec.ts +255 -255
- package/tests/unit/components/Icons/FavIcon.spec.ts +251 -251
- package/tests/unit/components/Icons/FilledCircle.spec.ts +274 -274
- package/tests/unit/components/Icons/Group3.spec.ts +355 -355
- package/tests/unit/components/Icons/Logo.spec.ts +228 -0
- package/tests/unit/components/Icons/MiniLogo.spec.ts +38 -0
- package/tests/unit/components/Icons/RingNotification.spec.ts +393 -393
- package/tests/unit/components/Icons/SolidArrowRight.spec.ts +49 -0
- package/tests/unit/components/Icons/calendar.spec.ts +286 -286
- package/tests/unit/components/Icons/checkbox.spec.ts +315 -315
- package/tests/unit/components/Icons/outlineChecked.spec.ts +434 -434
- package/tests/unit/components/Icons/play.spec.ts +308 -308
- package/tests/unit/components/Laboratory/AppointmentCard.spec.ts +167 -167
- package/tests/unit/components/Laboratory/ChatBoxImage.spec.ts +179 -179
- package/tests/unit/components/Laboratory/ChatMessage.spec.ts +263 -263
- package/tests/unit/components/Laboratory/ChatMessageBadge.spec.ts +282 -282
- package/tests/unit/components/Laboratory/ChatNotification.spec.ts +256 -256
- package/tests/unit/components/Laboratory/DocumentCard.spec.ts +228 -228
- package/tests/unit/components/Laboratory/DocumentCardItem.spec.ts +236 -236
- package/tests/unit/components/Laboratory/InfoCard.spec.ts +308 -308
- package/tests/unit/components/Laboratory/MainColumnsBar.spec.ts +251 -251
- package/tests/unit/components/Laboratory/ProgressCircle.spec.ts +290 -290
- package/tests/unit/components/Laboratory/ProgressLinear.spec.ts +275 -275
- package/tests/unit/components/Laboratory/SelectionColumnBar.spec.ts +288 -288
- package/tests/unit/components/Laboratory/StatusNotification.spec.ts +296 -296
- package/tests/unit/components/Laboratory/TagLabel.spec.ts +353 -353
- package/tests/unit/components/Laboratory/TagLabelGroup.spec.ts +377 -377
- package/tests/unit/components/Laboratory/TicketCard.spec.ts +351 -351
- package/tests/unit/components/Laboratory/TimeLineEvent.spec.ts +381 -381
- package/tests/unit/components/Laboratory/Timeline.spec.ts +419 -419
- package/tests/unit/components/Loader/Loader.spec.ts +197 -0
- package/tests/unit/components/MaintenanceBanner/MaintenanceBanner.spec.ts +302 -0
- package/tests/unit/constants/iconEnums.spec.ts +39 -39
- package/tests/unit/i18n/i18n.spec.ts +88 -88
- package/tests/unit/plugins/vuetify.spec.ts +220 -220
- package/tests/unit/setup.ts +189 -189
- package/tests/unit/src/components/index.spec.ts.skip +192 -192
- package/tests/unit/src/index.spec.ts.skip +182 -182
- package/tests/unit/src/main.spec.ts +151 -151
- package/tests/unit/utils/accessibility.spec.ts +318 -0
- package/tsconfig.json +26 -26
- package/vite.config.ts +29 -29
- package/vitest.config.ts +83 -83
|
@@ -1,713 +1,713 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { mount, flushPromises } from '@vue/test-utils';
|
|
3
|
-
import { nextTick } from 'vue';
|
|
4
|
-
import Select from '@components/Select/Select.vue';
|
|
5
|
-
|
|
6
|
-
describe('Select', () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
vi.clearAllMocks();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
// Test default behavior and rendering
|
|
12
|
-
describe('Default Behavior', () => {
|
|
13
|
-
it('renders with default props', () => {
|
|
14
|
-
const wrapper = mount(Select);
|
|
15
|
-
|
|
16
|
-
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
17
|
-
expect(wrapper.vm.disabled).toBe(false);
|
|
18
|
-
expect(wrapper.vm.error).toBe(false);
|
|
19
|
-
expect(wrapper.vm.clearable).toBe(false);
|
|
20
|
-
expect(wrapper.vm.label).toBe('Label');
|
|
21
|
-
expect(wrapper.vm.chips).toBe(false);
|
|
22
|
-
expect(wrapper.vm.multiple).toBe(false);
|
|
23
|
-
expect(wrapper.vm.returnObject).toBe(true);
|
|
24
|
-
expect(wrapper.vm.density).toBe('compact');
|
|
25
|
-
expect(wrapper.vm.variant).toBe('outlined');
|
|
26
|
-
expect(wrapper.vm.menuIcon).toBe('');
|
|
27
|
-
expect(wrapper.vm.clearIcon).toBe('x-mark');
|
|
28
|
-
expect(wrapper.vm.closableChips).toBe(false);
|
|
29
|
-
expect(wrapper.vm.borderOnHover).toBe(false);
|
|
30
|
-
expect(Array.isArray(wrapper.vm.items)).toBe(true);
|
|
31
|
-
expect(wrapper.vm.items).toEqual([]);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('applies wl-select class', () => {
|
|
35
|
-
const wrapper = mount(Select);
|
|
36
|
-
expect(wrapper.find('.wl-select').exists()).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('has data-testid for testing', () => {
|
|
40
|
-
const wrapper = mount(Select);
|
|
41
|
-
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// Test v-model functionality
|
|
46
|
-
describe('v-model', () => {
|
|
47
|
-
it('initializes internalValue with modelValue prop', () => {
|
|
48
|
-
const wrapper = mount(Select, {
|
|
49
|
-
props: {
|
|
50
|
-
modelValue: 'test-value'
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
expect(wrapper.vm.internalValue).toBe('test-value');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('updates internalValue when modelValue prop changes', async () => {
|
|
58
|
-
const wrapper = mount(Select, {
|
|
59
|
-
props: {
|
|
60
|
-
modelValue: 'initial'
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
await wrapper.setProps({ modelValue: 'updated' });
|
|
65
|
-
|
|
66
|
-
expect(wrapper.vm.internalValue).toBe('updated');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('emits update:modelValue when internalValue changes', async () => {
|
|
70
|
-
const wrapper = mount(Select);
|
|
71
|
-
|
|
72
|
-
wrapper.vm.internalValue = 'new-value';
|
|
73
|
-
await nextTick();
|
|
74
|
-
|
|
75
|
-
expect(wrapper.emitted('update:modelValue')).toBeTruthy();
|
|
76
|
-
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['new-value']);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('handles array modelValue for multiple selection', () => {
|
|
80
|
-
const wrapper = mount(Select, {
|
|
81
|
-
props: {
|
|
82
|
-
modelValue: ['option1', 'option2'],
|
|
83
|
-
multiple: true
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
expect(wrapper.vm.internalValue).toEqual(['option1', 'option2']);
|
|
88
|
-
expect(wrapper.vm.multiple).toBe(true);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('handles object modelValue', () => {
|
|
92
|
-
const objectValue = { id: 1, name: 'Test Object' };
|
|
93
|
-
const wrapper = mount(Select, {
|
|
94
|
-
props: {
|
|
95
|
-
modelValue: objectValue
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
expect(wrapper.vm.internalValue).toEqual(objectValue);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// Test items and selection
|
|
104
|
-
describe('Items and Selection', () => {
|
|
105
|
-
const testItems = [
|
|
106
|
-
{ title: 'Option 1', value: 'opt1' },
|
|
107
|
-
{ title: 'Option 2', value: 'opt2' },
|
|
108
|
-
{ title: 'Option 3', value: 'opt3' }
|
|
109
|
-
];
|
|
110
|
-
|
|
111
|
-
it('accepts items array', () => {
|
|
112
|
-
const wrapper = mount(Select, {
|
|
113
|
-
props: {
|
|
114
|
-
items: testItems
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
expect(wrapper.vm.items).toEqual(testItems);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('handles empty items array', () => {
|
|
122
|
-
const wrapper = mount(Select, {
|
|
123
|
-
props: {
|
|
124
|
-
items: []
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
expect(wrapper.vm.items).toEqual([]);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('uses title and value properties correctly', () => {
|
|
132
|
-
const wrapper = mount(Select, {
|
|
133
|
-
props: {
|
|
134
|
-
items: testItems
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// The component uses hardcoded 'title' and 'value' for item-title and item-value
|
|
139
|
-
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('handles returnObject prop', () => {
|
|
143
|
-
const wrapper = mount(Select, {
|
|
144
|
-
props: {
|
|
145
|
-
returnObject: false
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
expect(wrapper.vm.returnObject).toBe(false);
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
// Test state props
|
|
154
|
-
describe('State Props', () => {
|
|
155
|
-
it('applies disabled state', () => {
|
|
156
|
-
const wrapper = mount(Select, {
|
|
157
|
-
props: {
|
|
158
|
-
disabled: true
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
expect(wrapper.vm.disabled).toBe(true);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('applies error state', () => {
|
|
166
|
-
const wrapper = mount(Select, {
|
|
167
|
-
props: {
|
|
168
|
-
error: true
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
expect(wrapper.vm.error).toBe(true);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('applies clearable state', () => {
|
|
176
|
-
const wrapper = mount(Select, {
|
|
177
|
-
props: {
|
|
178
|
-
clearable: true
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
expect(wrapper.vm.clearable).toBe(true);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('applies border on hover', () => {
|
|
186
|
-
const wrapper = mount(Select, {
|
|
187
|
-
props: {
|
|
188
|
-
borderOnHover: true
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
expect(wrapper.find('.border-on-hover').exists()).toBe(true);
|
|
193
|
-
expect(wrapper.vm.borderOnHover).toBe(true);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// Test chip functionality
|
|
198
|
-
describe('Chips', () => {
|
|
199
|
-
it('enables chips display', () => {
|
|
200
|
-
const wrapper = mount(Select, {
|
|
201
|
-
props: {
|
|
202
|
-
chips: true
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
expect(wrapper.vm.chips).toBe(true);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('enables closable chips', () => {
|
|
210
|
-
const wrapper = mount(Select, {
|
|
211
|
-
props: {
|
|
212
|
-
chips: true,
|
|
213
|
-
closableChips: true
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
expect(wrapper.vm.chips).toBe(true);
|
|
218
|
-
expect(wrapper.vm.closableChips).toBe(true);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('works with multiple selection and chips', () => {
|
|
222
|
-
const wrapper = mount(Select, {
|
|
223
|
-
props: {
|
|
224
|
-
multiple: true,
|
|
225
|
-
chips: true,
|
|
226
|
-
closableChips: true
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
expect(wrapper.vm.multiple).toBe(true);
|
|
231
|
-
expect(wrapper.vm.chips).toBe(true);
|
|
232
|
-
expect(wrapper.vm.closableChips).toBe(true);
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
// Test icons
|
|
237
|
-
describe('Icons', () => {
|
|
238
|
-
it('handles append icon', () => {
|
|
239
|
-
const wrapper = mount(Select, {
|
|
240
|
-
props: {
|
|
241
|
-
appendIcon: 'mdi-arrow-down'
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
expect(wrapper.vm.appendIcon).toBe('mdi-arrow-down');
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('handles append inner icon', () => {
|
|
249
|
-
const wrapper = mount(Select, {
|
|
250
|
-
props: {
|
|
251
|
-
appendInnerIcon: 'mdi-magnify'
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
expect(wrapper.vm.appendInnerIcon).toBe('mdi-magnify');
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it('handles prepend icon', () => {
|
|
259
|
-
const wrapper = mount(Select, {
|
|
260
|
-
props: {
|
|
261
|
-
prependIcon: 'mdi-home'
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
expect(wrapper.vm.prependIcon).toBe('mdi-home');
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
it('handles prepend inner icon', () => {
|
|
269
|
-
const wrapper = mount(Select, {
|
|
270
|
-
props: {
|
|
271
|
-
prependInnerIcon: 'mdi-search'
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
expect(wrapper.vm.prependInnerIcon).toBe('mdi-search');
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
it('uses custom menu icon', () => {
|
|
279
|
-
const wrapper = mount(Select, {
|
|
280
|
-
props: {
|
|
281
|
-
menuIcon: 'mdi-chevron-down'
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
expect(wrapper.vm.menuIcon).toBe('mdi-chevron-down');
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('uses custom clear icon', () => {
|
|
289
|
-
const wrapper = mount(Select, {
|
|
290
|
-
props: {
|
|
291
|
-
clearIcon: 'mdi-close'
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
expect(wrapper.vm.clearIcon).toBe('mdi-close');
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it('handles all icons together', () => {
|
|
299
|
-
const wrapper = mount(Select, {
|
|
300
|
-
props: {
|
|
301
|
-
appendIcon: 'mdi-arrow-down',
|
|
302
|
-
appendInnerIcon: 'mdi-magnify',
|
|
303
|
-
prependIcon: 'mdi-home',
|
|
304
|
-
prependInnerIcon: 'mdi-search',
|
|
305
|
-
menuIcon: 'mdi-chevron-down',
|
|
306
|
-
clearIcon: 'mdi-close'
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
expect(wrapper.vm.appendIcon).toBe('mdi-arrow-down');
|
|
311
|
-
expect(wrapper.vm.appendInnerIcon).toBe('mdi-magnify');
|
|
312
|
-
expect(wrapper.vm.prependIcon).toBe('mdi-home');
|
|
313
|
-
expect(wrapper.vm.prependInnerIcon).toBe('mdi-search');
|
|
314
|
-
expect(wrapper.vm.menuIcon).toBe('mdi-chevron-down');
|
|
315
|
-
expect(wrapper.vm.clearIcon).toBe('mdi-close');
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
// Test click events
|
|
320
|
-
describe('Click Events', () => {
|
|
321
|
-
it('emits click:append event', () => {
|
|
322
|
-
const wrapper = mount(Select);
|
|
323
|
-
const mockEvent = { target: {} };
|
|
324
|
-
|
|
325
|
-
wrapper.vm.onClickAppend(mockEvent);
|
|
326
|
-
|
|
327
|
-
expect(wrapper.emitted('click:append')).toBeTruthy();
|
|
328
|
-
expect(wrapper.emitted('click:append')?.[0]).toEqual([mockEvent]);
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
it('emits click:appendInner event', () => {
|
|
332
|
-
const wrapper = mount(Select);
|
|
333
|
-
const mockEvent = { target: {} };
|
|
334
|
-
|
|
335
|
-
wrapper.vm.onClickAppendInner(mockEvent);
|
|
336
|
-
|
|
337
|
-
expect(wrapper.emitted('click:appendInner')).toBeTruthy();
|
|
338
|
-
expect(wrapper.emitted('click:appendInner')?.[0]).toEqual([mockEvent]);
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
it('emits click:clear event', () => {
|
|
342
|
-
const wrapper = mount(Select);
|
|
343
|
-
const mockEvent = { target: {} };
|
|
344
|
-
|
|
345
|
-
wrapper.vm.onClickClear(mockEvent);
|
|
346
|
-
|
|
347
|
-
expect(wrapper.emitted('click:clear')).toBeTruthy();
|
|
348
|
-
expect(wrapper.emitted('click:clear')?.[0]).toEqual([mockEvent]);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it('emits click:prepend event', () => {
|
|
352
|
-
const wrapper = mount(Select);
|
|
353
|
-
const mockEvent = { target: {} };
|
|
354
|
-
|
|
355
|
-
wrapper.vm.onClickPrepend(mockEvent);
|
|
356
|
-
|
|
357
|
-
expect(wrapper.emitted('click:prepend')).toBeTruthy();
|
|
358
|
-
expect(wrapper.emitted('click:prepend')?.[0]).toEqual([mockEvent]);
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
it('emits click:prependInner event', () => {
|
|
362
|
-
const wrapper = mount(Select);
|
|
363
|
-
const mockEvent = { target: {} };
|
|
364
|
-
|
|
365
|
-
wrapper.vm.onClickPrependInner(mockEvent);
|
|
366
|
-
|
|
367
|
-
expect(wrapper.emitted('click:prependInner')).toBeTruthy();
|
|
368
|
-
expect(wrapper.emitted('click:prependInner')?.[0]).toEqual([mockEvent]);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
it('emits update:search event', () => {
|
|
372
|
-
const wrapper = mount(Select);
|
|
373
|
-
const searchValue = 'test search';
|
|
374
|
-
|
|
375
|
-
wrapper.vm.onUpdateSearch(searchValue);
|
|
376
|
-
|
|
377
|
-
expect(wrapper.emitted('update:search')).toBeTruthy();
|
|
378
|
-
expect(wrapper.emitted('update:search')?.[0]).toEqual([searchValue]);
|
|
379
|
-
});
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
// Test slots
|
|
383
|
-
describe('Slots', () => {
|
|
384
|
-
it('renders menu slot', () => {
|
|
385
|
-
const wrapper = mount(Select, {
|
|
386
|
-
slots: {
|
|
387
|
-
menu: '<div data-testid="menu-slot">Custom Menu</div>'
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
expect(wrapper.find('[data-testid="menu-slot"]').exists()).toBe(true);
|
|
392
|
-
expect(wrapper.find('[data-testid="menu-slot"]').text()).toBe('Custom Menu');
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
it('renders prepend slot', () => {
|
|
396
|
-
const wrapper = mount(Select, {
|
|
397
|
-
slots: {
|
|
398
|
-
prepend: '<div data-testid="prepend-slot">Prepend Content</div>'
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
expect(wrapper.find('[data-testid="prepend-slot"]').exists()).toBe(true);
|
|
403
|
-
expect(wrapper.find('[data-testid="prepend-slot"]').text()).toBe('Prepend Content');
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
it('renders prepend-inner slot', () => {
|
|
407
|
-
const wrapper = mount(Select, {
|
|
408
|
-
slots: {
|
|
409
|
-
'prepend-inner': '<div data-testid="prepend-inner-slot">Prepend Inner</div>'
|
|
410
|
-
}
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
expect(wrapper.find('[data-testid="prepend-inner-slot"]').exists()).toBe(true);
|
|
414
|
-
expect(wrapper.find('[data-testid="prepend-inner-slot"]').text()).toBe('Prepend Inner');
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
it('renders append slot', () => {
|
|
418
|
-
const wrapper = mount(Select, {
|
|
419
|
-
slots: {
|
|
420
|
-
append: '<div data-testid="append-slot">Append Content</div>'
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
expect(wrapper.find('[data-testid="append-slot"]').exists()).toBe(true);
|
|
425
|
-
expect(wrapper.find('[data-testid="append-slot"]').text()).toBe('Append Content');
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it('renders append-inner slot', () => {
|
|
429
|
-
const wrapper = mount(Select, {
|
|
430
|
-
slots: {
|
|
431
|
-
'append-inner': '<div data-testid="append-inner-slot">Append Inner</div>'
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
expect(wrapper.find('[data-testid="append-inner-slot"]').exists()).toBe(true);
|
|
436
|
-
expect(wrapper.find('[data-testid="append-inner-slot"]').text()).toBe('Append Inner');
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
it('renders multiple slots simultaneously', () => {
|
|
440
|
-
const wrapper = mount(Select, {
|
|
441
|
-
slots: {
|
|
442
|
-
menu: '<div data-testid="menu-slot">Menu</div>',
|
|
443
|
-
prepend: '<div data-testid="prepend-slot">Prepend</div>',
|
|
444
|
-
'prepend-inner': '<div data-testid="prepend-inner-slot">Prepend Inner</div>',
|
|
445
|
-
append: '<div data-testid="append-slot">Append</div>',
|
|
446
|
-
'append-inner': '<div data-testid="append-inner-slot">Append Inner</div>'
|
|
447
|
-
}
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
expect(wrapper.find('[data-testid="menu-slot"]').exists()).toBe(true);
|
|
451
|
-
expect(wrapper.find('[data-testid="prepend-slot"]').exists()).toBe(true);
|
|
452
|
-
expect(wrapper.find('[data-testid="prepend-inner-slot"]').exists()).toBe(true);
|
|
453
|
-
expect(wrapper.find('[data-testid="append-slot"]').exists()).toBe(true);
|
|
454
|
-
expect(wrapper.find('[data-testid="append-inner-slot"]').exists()).toBe(true);
|
|
455
|
-
});
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
// Test exposed methods
|
|
459
|
-
describe('Exposed Methods', () => {
|
|
460
|
-
it('exposes focus, blur, and select methods', () => {
|
|
461
|
-
const wrapper = mount(Select);
|
|
462
|
-
|
|
463
|
-
expect(wrapper.vm.focus).toBeDefined();
|
|
464
|
-
expect(wrapper.vm.blur).toBeDefined();
|
|
465
|
-
expect(wrapper.vm.select).toBeDefined();
|
|
466
|
-
expect(typeof wrapper.vm.focus).toBe('function');
|
|
467
|
-
expect(typeof wrapper.vm.blur).toBe('function');
|
|
468
|
-
expect(typeof wrapper.vm.select).toBe('function');
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
it('getNativeInput returns input element with stub', () => {
|
|
472
|
-
const wrapper = mount(Select);
|
|
473
|
-
const nativeInput = wrapper.vm.getNativeInput();
|
|
474
|
-
|
|
475
|
-
// Since our stub provides a real input element, getNativeInput will return it
|
|
476
|
-
expect(nativeInput).toBeTruthy();
|
|
477
|
-
expect(nativeInput.tagName).toBe('INPUT');
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
it('focus method works with mocked input', async () => {
|
|
481
|
-
const wrapper = mount(Select);
|
|
482
|
-
|
|
483
|
-
// Get the real input element
|
|
484
|
-
const nativeInput = wrapper.vm.getNativeInput();
|
|
485
|
-
const focusSpy = vi.spyOn(nativeInput, 'focus');
|
|
486
|
-
|
|
487
|
-
wrapper.vm.focus();
|
|
488
|
-
await nextTick();
|
|
489
|
-
|
|
490
|
-
expect(focusSpy).toHaveBeenCalled();
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
it('blur method works with mocked input', async () => {
|
|
494
|
-
const wrapper = mount(Select);
|
|
495
|
-
|
|
496
|
-
// Get the real input element
|
|
497
|
-
const nativeInput = wrapper.vm.getNativeInput();
|
|
498
|
-
const blurSpy = vi.spyOn(nativeInput, 'blur');
|
|
499
|
-
|
|
500
|
-
wrapper.vm.blur();
|
|
501
|
-
await nextTick();
|
|
502
|
-
|
|
503
|
-
expect(blurSpy).toHaveBeenCalled();
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
it('select method works with mocked input', async () => {
|
|
507
|
-
const wrapper = mount(Select);
|
|
508
|
-
|
|
509
|
-
// Get the real input element
|
|
510
|
-
const nativeInput = wrapper.vm.getNativeInput();
|
|
511
|
-
const selectSpy = vi.spyOn(nativeInput, 'select');
|
|
512
|
-
|
|
513
|
-
wrapper.vm.select();
|
|
514
|
-
await nextTick();
|
|
515
|
-
|
|
516
|
-
expect(selectSpy).toHaveBeenCalled();
|
|
517
|
-
});
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
// Test accessibility and styling
|
|
521
|
-
describe('Accessibility and Styling', () => {
|
|
522
|
-
it('applies proper label', () => {
|
|
523
|
-
const wrapper = mount(Select, {
|
|
524
|
-
props: {
|
|
525
|
-
label: 'Select an option'
|
|
526
|
-
}
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
expect(wrapper.vm.label).toBe('Select an option');
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
it('maintains accessibility with disabled state', () => {
|
|
533
|
-
const wrapper = mount(Select, {
|
|
534
|
-
props: {
|
|
535
|
-
disabled: true,
|
|
536
|
-
label: 'Disabled Select'
|
|
537
|
-
}
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
expect(wrapper.vm.disabled).toBe(true);
|
|
541
|
-
expect(wrapper.vm.label).toBe('Disabled Select');
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
it('applies variant styling', () => {
|
|
545
|
-
const variants = ['outlined', 'filled', 'underlined', 'solo', 'solo-inverted'];
|
|
546
|
-
|
|
547
|
-
variants.forEach(variant => {
|
|
548
|
-
const wrapper = mount(Select, {
|
|
549
|
-
props: { variant }
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
expect(wrapper.vm.variant).toBe(variant);
|
|
553
|
-
});
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
it('applies density correctly', () => {
|
|
557
|
-
const densities = ['compact', 'comfortable', 'default'];
|
|
558
|
-
|
|
559
|
-
densities.forEach(density => {
|
|
560
|
-
const wrapper = mount(Select, {
|
|
561
|
-
props: { density }
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
expect(wrapper.vm.density).toBe(density);
|
|
565
|
-
});
|
|
566
|
-
});
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
// Test edge cases
|
|
570
|
-
describe('Edge Cases', () => {
|
|
571
|
-
it('handles undefined modelValue', () => {
|
|
572
|
-
const wrapper = mount(Select, {
|
|
573
|
-
props: {
|
|
574
|
-
modelValue: undefined
|
|
575
|
-
}
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
expect(wrapper.vm.internalValue).toBeUndefined();
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
it('handles null modelValue', () => {
|
|
582
|
-
const wrapper = mount(Select, {
|
|
583
|
-
props: {
|
|
584
|
-
modelValue: null
|
|
585
|
-
}
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
expect(wrapper.vm.internalValue).toBeNull();
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
it('handles empty string modelValue', () => {
|
|
592
|
-
const wrapper = mount(Select, {
|
|
593
|
-
props: {
|
|
594
|
-
modelValue: ''
|
|
595
|
-
}
|
|
596
|
-
});
|
|
597
|
-
|
|
598
|
-
expect(wrapper.vm.internalValue).toBe('');
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
it('handles large items array', () => {
|
|
602
|
-
const largeItems = Array.from({ length: 1000 }, (_, i) => ({
|
|
603
|
-
title: `Option ${i}`,
|
|
604
|
-
value: `opt${i}`
|
|
605
|
-
}));
|
|
606
|
-
|
|
607
|
-
const wrapper = mount(Select, {
|
|
608
|
-
props: {
|
|
609
|
-
items: largeItems
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
expect(wrapper.vm.items).toHaveLength(1000);
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
it('handles complex object items', () => {
|
|
617
|
-
const complexItems = [
|
|
618
|
-
{
|
|
619
|
-
title: 'Complex Item 1',
|
|
620
|
-
value: { id: 1, data: { nested: 'value1' } },
|
|
621
|
-
metadata: { category: 'test' }
|
|
622
|
-
},
|
|
623
|
-
{
|
|
624
|
-
title: 'Complex Item 2',
|
|
625
|
-
value: { id: 2, data: { nested: 'value2' } },
|
|
626
|
-
metadata: { category: 'production' }
|
|
627
|
-
}
|
|
628
|
-
];
|
|
629
|
-
|
|
630
|
-
const wrapper = mount(Select, {
|
|
631
|
-
props: {
|
|
632
|
-
items: complexItems
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
expect(wrapper.vm.items).toEqual(complexItems);
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
it('handles rapid prop changes', async () => {
|
|
640
|
-
const wrapper = mount(Select, {
|
|
641
|
-
props: {
|
|
642
|
-
modelValue: 'initial'
|
|
643
|
-
}
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
await wrapper.setProps({ modelValue: 'change1' });
|
|
647
|
-
await wrapper.setProps({ modelValue: 'change2' });
|
|
648
|
-
await wrapper.setProps({ modelValue: 'change3' });
|
|
649
|
-
|
|
650
|
-
expect(wrapper.vm.internalValue).toBe('change3');
|
|
651
|
-
});
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
// Test complex scenarios
|
|
655
|
-
describe('Complex Scenarios', () => {
|
|
656
|
-
it('works with all features enabled', () => {
|
|
657
|
-
const testItems = [
|
|
658
|
-
{ title: 'Option 1', value: 'opt1' },
|
|
659
|
-
{ title: 'Option 2', value: 'opt2' }
|
|
660
|
-
];
|
|
661
|
-
|
|
662
|
-
const wrapper = mount(Select, {
|
|
663
|
-
props: {
|
|
664
|
-
label: 'Full Featured Select',
|
|
665
|
-
items: testItems,
|
|
666
|
-
modelValue: ['opt1'],
|
|
667
|
-
multiple: true,
|
|
668
|
-
chips: true,
|
|
669
|
-
closableChips: true,
|
|
670
|
-
clearable: true,
|
|
671
|
-
disabled: false,
|
|
672
|
-
error: false,
|
|
673
|
-
variant: 'outlined',
|
|
674
|
-
density: 'comfortable',
|
|
675
|
-
returnObject: false,
|
|
676
|
-
borderOnHover: true,
|
|
677
|
-
appendIcon: 'mdi-chevron-down',
|
|
678
|
-
prependIcon: 'mdi-magnify',
|
|
679
|
-
clearIcon: 'mdi-close',
|
|
680
|
-
menuIcon: 'mdi-menu-down'
|
|
681
|
-
}
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
expect(wrapper.vm.multiple).toBe(true);
|
|
685
|
-
expect(wrapper.vm.chips).toBe(true);
|
|
686
|
-
expect(wrapper.vm.closableChips).toBe(true);
|
|
687
|
-
expect(wrapper.vm.clearable).toBe(true);
|
|
688
|
-
expect(wrapper.vm.borderOnHover).toBe(true);
|
|
689
|
-
expect(wrapper.vm.returnObject).toBe(false);
|
|
690
|
-
expect(wrapper.find('.border-on-hover').exists()).toBe(true);
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
it('handles switching between single and multiple modes', async () => {
|
|
694
|
-
const wrapper = mount(Select, {
|
|
695
|
-
props: {
|
|
696
|
-
modelValue: 'single-value',
|
|
697
|
-
multiple: false
|
|
698
|
-
}
|
|
699
|
-
});
|
|
700
|
-
|
|
701
|
-
expect(wrapper.vm.internalValue).toBe('single-value');
|
|
702
|
-
expect(wrapper.vm.multiple).toBe(false);
|
|
703
|
-
|
|
704
|
-
await wrapper.setProps({
|
|
705
|
-
modelValue: ['array-value'],
|
|
706
|
-
multiple: true
|
|
707
|
-
});
|
|
708
|
-
|
|
709
|
-
expect(wrapper.vm.internalValue).toEqual(['array-value']);
|
|
710
|
-
expect(wrapper.vm.multiple).toBe(true);
|
|
711
|
-
});
|
|
712
|
-
});
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { mount, flushPromises } from '@vue/test-utils';
|
|
3
|
+
import { nextTick } from 'vue';
|
|
4
|
+
import Select from '@components/Select/Select.vue';
|
|
5
|
+
|
|
6
|
+
describe('Select', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
vi.clearAllMocks();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// Test default behavior and rendering
|
|
12
|
+
describe('Default Behavior', () => {
|
|
13
|
+
it('renders with default props', () => {
|
|
14
|
+
const wrapper = mount(Select);
|
|
15
|
+
|
|
16
|
+
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
17
|
+
expect(wrapper.vm.disabled).toBe(false);
|
|
18
|
+
expect(wrapper.vm.error).toBe(false);
|
|
19
|
+
expect(wrapper.vm.clearable).toBe(false);
|
|
20
|
+
expect(wrapper.vm.label).toBe('Label');
|
|
21
|
+
expect(wrapper.vm.chips).toBe(false);
|
|
22
|
+
expect(wrapper.vm.multiple).toBe(false);
|
|
23
|
+
expect(wrapper.vm.returnObject).toBe(true);
|
|
24
|
+
expect(wrapper.vm.density).toBe('compact');
|
|
25
|
+
expect(wrapper.vm.variant).toBe('outlined');
|
|
26
|
+
expect(wrapper.vm.menuIcon).toBe('');
|
|
27
|
+
expect(wrapper.vm.clearIcon).toBe('x-mark');
|
|
28
|
+
expect(wrapper.vm.closableChips).toBe(false);
|
|
29
|
+
expect(wrapper.vm.borderOnHover).toBe(false);
|
|
30
|
+
expect(Array.isArray(wrapper.vm.items)).toBe(true);
|
|
31
|
+
expect(wrapper.vm.items).toEqual([]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('applies wl-select class', () => {
|
|
35
|
+
const wrapper = mount(Select);
|
|
36
|
+
expect(wrapper.find('.wl-select').exists()).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('has data-testid for testing', () => {
|
|
40
|
+
const wrapper = mount(Select);
|
|
41
|
+
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Test v-model functionality
|
|
46
|
+
describe('v-model', () => {
|
|
47
|
+
it('initializes internalValue with modelValue prop', () => {
|
|
48
|
+
const wrapper = mount(Select, {
|
|
49
|
+
props: {
|
|
50
|
+
modelValue: 'test-value'
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(wrapper.vm.internalValue).toBe('test-value');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('updates internalValue when modelValue prop changes', async () => {
|
|
58
|
+
const wrapper = mount(Select, {
|
|
59
|
+
props: {
|
|
60
|
+
modelValue: 'initial'
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await wrapper.setProps({ modelValue: 'updated' });
|
|
65
|
+
|
|
66
|
+
expect(wrapper.vm.internalValue).toBe('updated');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('emits update:modelValue when internalValue changes', async () => {
|
|
70
|
+
const wrapper = mount(Select);
|
|
71
|
+
|
|
72
|
+
wrapper.vm.internalValue = 'new-value';
|
|
73
|
+
await nextTick();
|
|
74
|
+
|
|
75
|
+
expect(wrapper.emitted('update:modelValue')).toBeTruthy();
|
|
76
|
+
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['new-value']);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('handles array modelValue for multiple selection', () => {
|
|
80
|
+
const wrapper = mount(Select, {
|
|
81
|
+
props: {
|
|
82
|
+
modelValue: ['option1', 'option2'],
|
|
83
|
+
multiple: true
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(wrapper.vm.internalValue).toEqual(['option1', 'option2']);
|
|
88
|
+
expect(wrapper.vm.multiple).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('handles object modelValue', () => {
|
|
92
|
+
const objectValue = { id: 1, name: 'Test Object' };
|
|
93
|
+
const wrapper = mount(Select, {
|
|
94
|
+
props: {
|
|
95
|
+
modelValue: objectValue
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(wrapper.vm.internalValue).toEqual(objectValue);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Test items and selection
|
|
104
|
+
describe('Items and Selection', () => {
|
|
105
|
+
const testItems = [
|
|
106
|
+
{ title: 'Option 1', value: 'opt1' },
|
|
107
|
+
{ title: 'Option 2', value: 'opt2' },
|
|
108
|
+
{ title: 'Option 3', value: 'opt3' }
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
it('accepts items array', () => {
|
|
112
|
+
const wrapper = mount(Select, {
|
|
113
|
+
props: {
|
|
114
|
+
items: testItems
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(wrapper.vm.items).toEqual(testItems);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('handles empty items array', () => {
|
|
122
|
+
const wrapper = mount(Select, {
|
|
123
|
+
props: {
|
|
124
|
+
items: []
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(wrapper.vm.items).toEqual([]);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('uses title and value properties correctly', () => {
|
|
132
|
+
const wrapper = mount(Select, {
|
|
133
|
+
props: {
|
|
134
|
+
items: testItems
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// The component uses hardcoded 'title' and 'value' for item-title and item-value
|
|
139
|
+
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('handles returnObject prop', () => {
|
|
143
|
+
const wrapper = mount(Select, {
|
|
144
|
+
props: {
|
|
145
|
+
returnObject: false
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(wrapper.vm.returnObject).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Test state props
|
|
154
|
+
describe('State Props', () => {
|
|
155
|
+
it('applies disabled state', () => {
|
|
156
|
+
const wrapper = mount(Select, {
|
|
157
|
+
props: {
|
|
158
|
+
disabled: true
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
expect(wrapper.vm.disabled).toBe(true);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('applies error state', () => {
|
|
166
|
+
const wrapper = mount(Select, {
|
|
167
|
+
props: {
|
|
168
|
+
error: true
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
expect(wrapper.vm.error).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('applies clearable state', () => {
|
|
176
|
+
const wrapper = mount(Select, {
|
|
177
|
+
props: {
|
|
178
|
+
clearable: true
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(wrapper.vm.clearable).toBe(true);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('applies border on hover', () => {
|
|
186
|
+
const wrapper = mount(Select, {
|
|
187
|
+
props: {
|
|
188
|
+
borderOnHover: true
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(wrapper.find('.border-on-hover').exists()).toBe(true);
|
|
193
|
+
expect(wrapper.vm.borderOnHover).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Test chip functionality
|
|
198
|
+
describe('Chips', () => {
|
|
199
|
+
it('enables chips display', () => {
|
|
200
|
+
const wrapper = mount(Select, {
|
|
201
|
+
props: {
|
|
202
|
+
chips: true
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
expect(wrapper.vm.chips).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('enables closable chips', () => {
|
|
210
|
+
const wrapper = mount(Select, {
|
|
211
|
+
props: {
|
|
212
|
+
chips: true,
|
|
213
|
+
closableChips: true
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
expect(wrapper.vm.chips).toBe(true);
|
|
218
|
+
expect(wrapper.vm.closableChips).toBe(true);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('works with multiple selection and chips', () => {
|
|
222
|
+
const wrapper = mount(Select, {
|
|
223
|
+
props: {
|
|
224
|
+
multiple: true,
|
|
225
|
+
chips: true,
|
|
226
|
+
closableChips: true
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
expect(wrapper.vm.multiple).toBe(true);
|
|
231
|
+
expect(wrapper.vm.chips).toBe(true);
|
|
232
|
+
expect(wrapper.vm.closableChips).toBe(true);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Test icons
|
|
237
|
+
describe('Icons', () => {
|
|
238
|
+
it('handles append icon', () => {
|
|
239
|
+
const wrapper = mount(Select, {
|
|
240
|
+
props: {
|
|
241
|
+
appendIcon: 'mdi-arrow-down'
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
expect(wrapper.vm.appendIcon).toBe('mdi-arrow-down');
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('handles append inner icon', () => {
|
|
249
|
+
const wrapper = mount(Select, {
|
|
250
|
+
props: {
|
|
251
|
+
appendInnerIcon: 'mdi-magnify'
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
expect(wrapper.vm.appendInnerIcon).toBe('mdi-magnify');
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('handles prepend icon', () => {
|
|
259
|
+
const wrapper = mount(Select, {
|
|
260
|
+
props: {
|
|
261
|
+
prependIcon: 'mdi-home'
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
expect(wrapper.vm.prependIcon).toBe('mdi-home');
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('handles prepend inner icon', () => {
|
|
269
|
+
const wrapper = mount(Select, {
|
|
270
|
+
props: {
|
|
271
|
+
prependInnerIcon: 'mdi-search'
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
expect(wrapper.vm.prependInnerIcon).toBe('mdi-search');
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('uses custom menu icon', () => {
|
|
279
|
+
const wrapper = mount(Select, {
|
|
280
|
+
props: {
|
|
281
|
+
menuIcon: 'mdi-chevron-down'
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
expect(wrapper.vm.menuIcon).toBe('mdi-chevron-down');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('uses custom clear icon', () => {
|
|
289
|
+
const wrapper = mount(Select, {
|
|
290
|
+
props: {
|
|
291
|
+
clearIcon: 'mdi-close'
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
expect(wrapper.vm.clearIcon).toBe('mdi-close');
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('handles all icons together', () => {
|
|
299
|
+
const wrapper = mount(Select, {
|
|
300
|
+
props: {
|
|
301
|
+
appendIcon: 'mdi-arrow-down',
|
|
302
|
+
appendInnerIcon: 'mdi-magnify',
|
|
303
|
+
prependIcon: 'mdi-home',
|
|
304
|
+
prependInnerIcon: 'mdi-search',
|
|
305
|
+
menuIcon: 'mdi-chevron-down',
|
|
306
|
+
clearIcon: 'mdi-close'
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
expect(wrapper.vm.appendIcon).toBe('mdi-arrow-down');
|
|
311
|
+
expect(wrapper.vm.appendInnerIcon).toBe('mdi-magnify');
|
|
312
|
+
expect(wrapper.vm.prependIcon).toBe('mdi-home');
|
|
313
|
+
expect(wrapper.vm.prependInnerIcon).toBe('mdi-search');
|
|
314
|
+
expect(wrapper.vm.menuIcon).toBe('mdi-chevron-down');
|
|
315
|
+
expect(wrapper.vm.clearIcon).toBe('mdi-close');
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Test click events
|
|
320
|
+
describe('Click Events', () => {
|
|
321
|
+
it('emits click:append event', () => {
|
|
322
|
+
const wrapper = mount(Select);
|
|
323
|
+
const mockEvent = { target: {} };
|
|
324
|
+
|
|
325
|
+
wrapper.vm.onClickAppend(mockEvent);
|
|
326
|
+
|
|
327
|
+
expect(wrapper.emitted('click:append')).toBeTruthy();
|
|
328
|
+
expect(wrapper.emitted('click:append')?.[0]).toEqual([mockEvent]);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('emits click:appendInner event', () => {
|
|
332
|
+
const wrapper = mount(Select);
|
|
333
|
+
const mockEvent = { target: {} };
|
|
334
|
+
|
|
335
|
+
wrapper.vm.onClickAppendInner(mockEvent);
|
|
336
|
+
|
|
337
|
+
expect(wrapper.emitted('click:appendInner')).toBeTruthy();
|
|
338
|
+
expect(wrapper.emitted('click:appendInner')?.[0]).toEqual([mockEvent]);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('emits click:clear event', () => {
|
|
342
|
+
const wrapper = mount(Select);
|
|
343
|
+
const mockEvent = { target: {} };
|
|
344
|
+
|
|
345
|
+
wrapper.vm.onClickClear(mockEvent);
|
|
346
|
+
|
|
347
|
+
expect(wrapper.emitted('click:clear')).toBeTruthy();
|
|
348
|
+
expect(wrapper.emitted('click:clear')?.[0]).toEqual([mockEvent]);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('emits click:prepend event', () => {
|
|
352
|
+
const wrapper = mount(Select);
|
|
353
|
+
const mockEvent = { target: {} };
|
|
354
|
+
|
|
355
|
+
wrapper.vm.onClickPrepend(mockEvent);
|
|
356
|
+
|
|
357
|
+
expect(wrapper.emitted('click:prepend')).toBeTruthy();
|
|
358
|
+
expect(wrapper.emitted('click:prepend')?.[0]).toEqual([mockEvent]);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('emits click:prependInner event', () => {
|
|
362
|
+
const wrapper = mount(Select);
|
|
363
|
+
const mockEvent = { target: {} };
|
|
364
|
+
|
|
365
|
+
wrapper.vm.onClickPrependInner(mockEvent);
|
|
366
|
+
|
|
367
|
+
expect(wrapper.emitted('click:prependInner')).toBeTruthy();
|
|
368
|
+
expect(wrapper.emitted('click:prependInner')?.[0]).toEqual([mockEvent]);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('emits update:search event', () => {
|
|
372
|
+
const wrapper = mount(Select);
|
|
373
|
+
const searchValue = 'test search';
|
|
374
|
+
|
|
375
|
+
wrapper.vm.onUpdateSearch(searchValue);
|
|
376
|
+
|
|
377
|
+
expect(wrapper.emitted('update:search')).toBeTruthy();
|
|
378
|
+
expect(wrapper.emitted('update:search')?.[0]).toEqual([searchValue]);
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Test slots
|
|
383
|
+
describe('Slots', () => {
|
|
384
|
+
it('renders menu slot', () => {
|
|
385
|
+
const wrapper = mount(Select, {
|
|
386
|
+
slots: {
|
|
387
|
+
menu: '<div data-testid="menu-slot">Custom Menu</div>'
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
expect(wrapper.find('[data-testid="menu-slot"]').exists()).toBe(true);
|
|
392
|
+
expect(wrapper.find('[data-testid="menu-slot"]').text()).toBe('Custom Menu');
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('renders prepend slot', () => {
|
|
396
|
+
const wrapper = mount(Select, {
|
|
397
|
+
slots: {
|
|
398
|
+
prepend: '<div data-testid="prepend-slot">Prepend Content</div>'
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
expect(wrapper.find('[data-testid="prepend-slot"]').exists()).toBe(true);
|
|
403
|
+
expect(wrapper.find('[data-testid="prepend-slot"]').text()).toBe('Prepend Content');
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('renders prepend-inner slot', () => {
|
|
407
|
+
const wrapper = mount(Select, {
|
|
408
|
+
slots: {
|
|
409
|
+
'prepend-inner': '<div data-testid="prepend-inner-slot">Prepend Inner</div>'
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
expect(wrapper.find('[data-testid="prepend-inner-slot"]').exists()).toBe(true);
|
|
414
|
+
expect(wrapper.find('[data-testid="prepend-inner-slot"]').text()).toBe('Prepend Inner');
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('renders append slot', () => {
|
|
418
|
+
const wrapper = mount(Select, {
|
|
419
|
+
slots: {
|
|
420
|
+
append: '<div data-testid="append-slot">Append Content</div>'
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
expect(wrapper.find('[data-testid="append-slot"]').exists()).toBe(true);
|
|
425
|
+
expect(wrapper.find('[data-testid="append-slot"]').text()).toBe('Append Content');
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('renders append-inner slot', () => {
|
|
429
|
+
const wrapper = mount(Select, {
|
|
430
|
+
slots: {
|
|
431
|
+
'append-inner': '<div data-testid="append-inner-slot">Append Inner</div>'
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
expect(wrapper.find('[data-testid="append-inner-slot"]').exists()).toBe(true);
|
|
436
|
+
expect(wrapper.find('[data-testid="append-inner-slot"]').text()).toBe('Append Inner');
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('renders multiple slots simultaneously', () => {
|
|
440
|
+
const wrapper = mount(Select, {
|
|
441
|
+
slots: {
|
|
442
|
+
menu: '<div data-testid="menu-slot">Menu</div>',
|
|
443
|
+
prepend: '<div data-testid="prepend-slot">Prepend</div>',
|
|
444
|
+
'prepend-inner': '<div data-testid="prepend-inner-slot">Prepend Inner</div>',
|
|
445
|
+
append: '<div data-testid="append-slot">Append</div>',
|
|
446
|
+
'append-inner': '<div data-testid="append-inner-slot">Append Inner</div>'
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
expect(wrapper.find('[data-testid="menu-slot"]').exists()).toBe(true);
|
|
451
|
+
expect(wrapper.find('[data-testid="prepend-slot"]').exists()).toBe(true);
|
|
452
|
+
expect(wrapper.find('[data-testid="prepend-inner-slot"]').exists()).toBe(true);
|
|
453
|
+
expect(wrapper.find('[data-testid="append-slot"]').exists()).toBe(true);
|
|
454
|
+
expect(wrapper.find('[data-testid="append-inner-slot"]').exists()).toBe(true);
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Test exposed methods
|
|
459
|
+
describe('Exposed Methods', () => {
|
|
460
|
+
it('exposes focus, blur, and select methods', () => {
|
|
461
|
+
const wrapper = mount(Select);
|
|
462
|
+
|
|
463
|
+
expect(wrapper.vm.focus).toBeDefined();
|
|
464
|
+
expect(wrapper.vm.blur).toBeDefined();
|
|
465
|
+
expect(wrapper.vm.select).toBeDefined();
|
|
466
|
+
expect(typeof wrapper.vm.focus).toBe('function');
|
|
467
|
+
expect(typeof wrapper.vm.blur).toBe('function');
|
|
468
|
+
expect(typeof wrapper.vm.select).toBe('function');
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('getNativeInput returns input element with stub', () => {
|
|
472
|
+
const wrapper = mount(Select);
|
|
473
|
+
const nativeInput = wrapper.vm.getNativeInput();
|
|
474
|
+
|
|
475
|
+
// Since our stub provides a real input element, getNativeInput will return it
|
|
476
|
+
expect(nativeInput).toBeTruthy();
|
|
477
|
+
expect(nativeInput.tagName).toBe('INPUT');
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('focus method works with mocked input', async () => {
|
|
481
|
+
const wrapper = mount(Select);
|
|
482
|
+
|
|
483
|
+
// Get the real input element
|
|
484
|
+
const nativeInput = wrapper.vm.getNativeInput();
|
|
485
|
+
const focusSpy = vi.spyOn(nativeInput, 'focus');
|
|
486
|
+
|
|
487
|
+
wrapper.vm.focus();
|
|
488
|
+
await nextTick();
|
|
489
|
+
|
|
490
|
+
expect(focusSpy).toHaveBeenCalled();
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('blur method works with mocked input', async () => {
|
|
494
|
+
const wrapper = mount(Select);
|
|
495
|
+
|
|
496
|
+
// Get the real input element
|
|
497
|
+
const nativeInput = wrapper.vm.getNativeInput();
|
|
498
|
+
const blurSpy = vi.spyOn(nativeInput, 'blur');
|
|
499
|
+
|
|
500
|
+
wrapper.vm.blur();
|
|
501
|
+
await nextTick();
|
|
502
|
+
|
|
503
|
+
expect(blurSpy).toHaveBeenCalled();
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it('select method works with mocked input', async () => {
|
|
507
|
+
const wrapper = mount(Select);
|
|
508
|
+
|
|
509
|
+
// Get the real input element
|
|
510
|
+
const nativeInput = wrapper.vm.getNativeInput();
|
|
511
|
+
const selectSpy = vi.spyOn(nativeInput, 'select');
|
|
512
|
+
|
|
513
|
+
wrapper.vm.select();
|
|
514
|
+
await nextTick();
|
|
515
|
+
|
|
516
|
+
expect(selectSpy).toHaveBeenCalled();
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
// Test accessibility and styling
|
|
521
|
+
describe('Accessibility and Styling', () => {
|
|
522
|
+
it('applies proper label', () => {
|
|
523
|
+
const wrapper = mount(Select, {
|
|
524
|
+
props: {
|
|
525
|
+
label: 'Select an option'
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
expect(wrapper.vm.label).toBe('Select an option');
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it('maintains accessibility with disabled state', () => {
|
|
533
|
+
const wrapper = mount(Select, {
|
|
534
|
+
props: {
|
|
535
|
+
disabled: true,
|
|
536
|
+
label: 'Disabled Select'
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
expect(wrapper.vm.disabled).toBe(true);
|
|
541
|
+
expect(wrapper.vm.label).toBe('Disabled Select');
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('applies variant styling', () => {
|
|
545
|
+
const variants = ['outlined', 'filled', 'underlined', 'solo', 'solo-inverted'];
|
|
546
|
+
|
|
547
|
+
variants.forEach(variant => {
|
|
548
|
+
const wrapper = mount(Select, {
|
|
549
|
+
props: { variant }
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
expect(wrapper.vm.variant).toBe(variant);
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('applies density correctly', () => {
|
|
557
|
+
const densities = ['compact', 'comfortable', 'default'];
|
|
558
|
+
|
|
559
|
+
densities.forEach(density => {
|
|
560
|
+
const wrapper = mount(Select, {
|
|
561
|
+
props: { density }
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
expect(wrapper.vm.density).toBe(density);
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
// Test edge cases
|
|
570
|
+
describe('Edge Cases', () => {
|
|
571
|
+
it('handles undefined modelValue', () => {
|
|
572
|
+
const wrapper = mount(Select, {
|
|
573
|
+
props: {
|
|
574
|
+
modelValue: undefined
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
expect(wrapper.vm.internalValue).toBeUndefined();
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
it('handles null modelValue', () => {
|
|
582
|
+
const wrapper = mount(Select, {
|
|
583
|
+
props: {
|
|
584
|
+
modelValue: null
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
expect(wrapper.vm.internalValue).toBeNull();
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
it('handles empty string modelValue', () => {
|
|
592
|
+
const wrapper = mount(Select, {
|
|
593
|
+
props: {
|
|
594
|
+
modelValue: ''
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
expect(wrapper.vm.internalValue).toBe('');
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
it('handles large items array', () => {
|
|
602
|
+
const largeItems = Array.from({ length: 1000 }, (_, i) => ({
|
|
603
|
+
title: `Option ${i}`,
|
|
604
|
+
value: `opt${i}`
|
|
605
|
+
}));
|
|
606
|
+
|
|
607
|
+
const wrapper = mount(Select, {
|
|
608
|
+
props: {
|
|
609
|
+
items: largeItems
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
expect(wrapper.vm.items).toHaveLength(1000);
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it('handles complex object items', () => {
|
|
617
|
+
const complexItems = [
|
|
618
|
+
{
|
|
619
|
+
title: 'Complex Item 1',
|
|
620
|
+
value: { id: 1, data: { nested: 'value1' } },
|
|
621
|
+
metadata: { category: 'test' }
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
title: 'Complex Item 2',
|
|
625
|
+
value: { id: 2, data: { nested: 'value2' } },
|
|
626
|
+
metadata: { category: 'production' }
|
|
627
|
+
}
|
|
628
|
+
];
|
|
629
|
+
|
|
630
|
+
const wrapper = mount(Select, {
|
|
631
|
+
props: {
|
|
632
|
+
items: complexItems
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
expect(wrapper.vm.items).toEqual(complexItems);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
it('handles rapid prop changes', async () => {
|
|
640
|
+
const wrapper = mount(Select, {
|
|
641
|
+
props: {
|
|
642
|
+
modelValue: 'initial'
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
await wrapper.setProps({ modelValue: 'change1' });
|
|
647
|
+
await wrapper.setProps({ modelValue: 'change2' });
|
|
648
|
+
await wrapper.setProps({ modelValue: 'change3' });
|
|
649
|
+
|
|
650
|
+
expect(wrapper.vm.internalValue).toBe('change3');
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
// Test complex scenarios
|
|
655
|
+
describe('Complex Scenarios', () => {
|
|
656
|
+
it('works with all features enabled', () => {
|
|
657
|
+
const testItems = [
|
|
658
|
+
{ title: 'Option 1', value: 'opt1' },
|
|
659
|
+
{ title: 'Option 2', value: 'opt2' }
|
|
660
|
+
];
|
|
661
|
+
|
|
662
|
+
const wrapper = mount(Select, {
|
|
663
|
+
props: {
|
|
664
|
+
label: 'Full Featured Select',
|
|
665
|
+
items: testItems,
|
|
666
|
+
modelValue: ['opt1'],
|
|
667
|
+
multiple: true,
|
|
668
|
+
chips: true,
|
|
669
|
+
closableChips: true,
|
|
670
|
+
clearable: true,
|
|
671
|
+
disabled: false,
|
|
672
|
+
error: false,
|
|
673
|
+
variant: 'outlined',
|
|
674
|
+
density: 'comfortable',
|
|
675
|
+
returnObject: false,
|
|
676
|
+
borderOnHover: true,
|
|
677
|
+
appendIcon: 'mdi-chevron-down',
|
|
678
|
+
prependIcon: 'mdi-magnify',
|
|
679
|
+
clearIcon: 'mdi-close',
|
|
680
|
+
menuIcon: 'mdi-menu-down'
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
expect(wrapper.vm.multiple).toBe(true);
|
|
685
|
+
expect(wrapper.vm.chips).toBe(true);
|
|
686
|
+
expect(wrapper.vm.closableChips).toBe(true);
|
|
687
|
+
expect(wrapper.vm.clearable).toBe(true);
|
|
688
|
+
expect(wrapper.vm.borderOnHover).toBe(true);
|
|
689
|
+
expect(wrapper.vm.returnObject).toBe(false);
|
|
690
|
+
expect(wrapper.find('.border-on-hover').exists()).toBe(true);
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
it('handles switching between single and multiple modes', async () => {
|
|
694
|
+
const wrapper = mount(Select, {
|
|
695
|
+
props: {
|
|
696
|
+
modelValue: 'single-value',
|
|
697
|
+
multiple: false
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
expect(wrapper.vm.internalValue).toBe('single-value');
|
|
702
|
+
expect(wrapper.vm.multiple).toBe(false);
|
|
703
|
+
|
|
704
|
+
await wrapper.setProps({
|
|
705
|
+
modelValue: ['array-value'],
|
|
706
|
+
multiple: true
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
expect(wrapper.vm.internalValue).toEqual(['array-value']);
|
|
710
|
+
expect(wrapper.vm.multiple).toBe(true);
|
|
711
|
+
});
|
|
712
|
+
});
|
|
713
713
|
});
|