@zap-wunschlachen/wl-shared-components 1.0.76 → 1.0.78
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 +229 -229
- package/.github/workflows/static.yml +61 -61
- package/.github/workflows/update-snapshots.yml +37 -37
- package/.prettierrc.json +8 -8
- 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 +139 -139
- package/README.md +56 -56
- package/docs/assets.md +62 -62
- package/heroicons.ts +75 -75
- package/index.html +19 -19
- package/package.json +71 -71
- package/playwright.config.ts +48 -48
- package/public/background.svg +60 -60
- package/public/style.css +187 -187
- package/public/technologies.svg +22 -22
- package/scripts/check-translations.ts +352 -352
- package/src/assets/css/base.css +242 -242
- package/src/assets/css/variables.css +176 -176
- package/src/components/Accordion/Accordion.css +65 -65
- package/src/components/Accordion/AccordionGroup.vue +88 -88
- package/src/components/Accordion/AccordionItem.vue +272 -272
- package/src/components/Accordion/presets/default.css +4 -4
- package/src/components/Accordion/presets/elevated.css +25 -25
- package/src/components/Accordion/presets/filled.css +26 -26
- package/src/components/Accordion/presets/index.css +5 -5
- package/src/components/Accordion/presets/plain.css +34 -34
- 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 +20 -20
- package/src/components/Appointment/Card/AnamneseNotification.vue +23 -23
- package/src/components/Appointment/Card/Card.css +99 -99
- package/src/components/Appointment/Card/Card.vue +97 -97
- package/src/components/Appointment/Card/Details.css +62 -62
- package/src/components/Appointment/Card/Details.vue +44 -44
- package/src/components/Audio/Audio.vue +187 -187
- package/src/components/Audio/Waveform.vue +118 -118
- package/src/components/Banner/Banner.css +29 -29
- package/src/components/Banner/Banner.vue +89 -89
- package/src/components/Button/Button.vue +257 -257
- package/src/components/CheckBox/CheckBox.css +234 -234
- package/src/components/CheckBox/Checkbox.vue +184 -184
- package/src/components/DateInput/DateInput.css +2 -2
- package/src/components/DateInput/DateInput.vue +376 -370
- package/src/components/Dialog/Dialog.css +6 -6
- package/src/components/Dialog/Dialog.vue +46 -46
- package/src/components/EditField/EditField.css +19 -19
- package/src/components/EditField/EditField.vue +211 -211
- package/src/components/ErrorPage/ErrorPage.css +172 -172
- package/src/components/IconBullet/IconBullet.vue +104 -104
- package/src/components/IconBullet/IconBulletList.vue +55 -55
- package/src/components/Icons/AdvanceAppointments.vue +161 -161
- package/src/components/Icons/Audio/CloudFailed.vue +27 -27
- package/src/components/Icons/Audio/CloudSaved.vue +28 -28
- package/src/components/Icons/Audio/Delete.vue +22 -22
- package/src/components/Icons/Audio/Pause.vue +25 -25
- package/src/components/Icons/Audio/Play.vue +22 -22
- package/src/components/Icons/Calendar.vue +28 -28
- package/src/components/Icons/CalendarNotification.vue +137 -137
- package/src/components/Icons/Chair.vue +43 -43
- package/src/components/Icons/ChairNotification.vue +46 -46
- package/src/components/Icons/Circle.vue +66 -66
- package/src/components/Icons/FavIcon.vue +69 -69
- package/src/components/Icons/FilledCircle.vue +11 -11
- package/src/components/Icons/Group3.vue +57 -57
- package/src/components/Icons/Play.vue +16 -16
- package/src/components/Icons/RingNotification.vue +65 -65
- package/src/components/Icons/SolidArrowRight.vue +14 -14
- package/src/components/Icons/checkbox.vue +19 -19
- package/src/components/Icons/outlineChecked.vue +38 -38
- package/src/components/Input/Input.css +234 -234
- package/src/components/Input/Input.vue +281 -281
- 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 +78 -78
- package/src/components/MaintenanceBanner/MaintenanceBanner.css +353 -353
- package/src/components/MaintenanceBanner/MaintenanceBanner.vue +140 -140
- package/src/components/MaintenanceBanner/MaintenanceIllustration.vue +54 -54
- package/src/components/Modal/Modal.css +5 -5
- package/src/components/Modal/Modal.vue +29 -29
- package/src/components/NotificationBubble/NotificationBubble.css +4 -4
- package/src/components/NotificationBubble/NotificationBubble.vue +90 -90
- package/src/components/OtpInput/OtpInput.css +43 -43
- package/src/components/OtpInput/OtpInput.vue +181 -181
- package/src/components/PhoneInput/PhoneInput.css +151 -126
- package/src/components/PhoneInput/PhoneInput.vue +230 -139
- package/src/components/RadioGroup/RadioGroup.css +65 -0
- package/src/components/RadioGroup/RadioGroup.vue +134 -0
- package/src/components/Select/Select.css +172 -172
- package/src/components/Select/Select.vue +377 -377
- package/src/components/SelectAutocomplete/SelectAutocomplete.css +172 -172
- package/src/components/SelectAutocomplete/SelectAutocomplete.vue +414 -414
- package/src/components/TextArea/TextArea.css +269 -269
- package/src/components/TextArea/TextArea.vue +207 -207
- package/src/components/TickBox/TickBox.css +116 -116
- package/src/components/TickBox/TickBox.vue +172 -172
- package/src/components/Tile/Tile.css +106 -106
- package/src/components/Tile/Tile.vue +173 -173
- package/src/components/accessibility.css +218 -218
- package/src/components/index.ts +110 -109
- 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 +43 -43
- package/src/main.ts +11 -11
- package/src/pages/AccordionGroupPage.vue +873 -873
- package/src/pages/AllPage.vue +2483 -2365
- package/src/pages/SelectPage.vue +1302 -1302
- package/src/pages/TilePage.vue +902 -902
- package/src/plugins/vuetify.ts +54 -54
- package/src/shims-vue.d.ts +30 -30
- package/src/utils/index.ts +733 -733
- package/src/vite-env.d.ts +1 -1
- package/tests/unit/accessibility/component-a11y.spec.ts +657 -657
- package/tests/unit/components/Accordion/AccordionGroup.spec.ts +228 -228
- package/tests/unit/components/Accordion/AccordionItem.spec.ts +257 -257
- package/tests/unit/components/Appointment/AnamneseNotification.spec.ts +176 -176
- package/tests/unit/components/Appointment/Card/Actions.spec.ts +436 -436
- package/tests/unit/components/Appointment/Card/Card.spec.ts +531 -531
- package/tests/unit/components/Appointment/Card/Details.spec.ts +395 -395
- 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 -177
- package/tests/unit/components/Core/AnamneseAnswerDialog.spec.ts +344 -0
- package/tests/unit/components/Core/Banner.spec.ts +187 -0
- package/tests/unit/components/Core/Button.spec.ts +346 -346
- package/tests/unit/components/Core/Checkbox.spec.ts +544 -544
- package/tests/unit/components/Core/DateInput.spec.ts +702 -702
- package/tests/unit/components/Core/Dialog.spec.ts +448 -448
- package/tests/unit/components/Core/EditField.spec.ts +541 -541
- package/tests/unit/components/Core/Input.spec.ts +512 -512
- package/tests/unit/components/Core/List.spec.ts +163 -0
- package/tests/unit/components/Core/ListItem.spec.ts +205 -0
- 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 +757 -619
- package/tests/unit/components/Core/RadioGroup.spec.ts +318 -0
- package/tests/unit/components/Core/Select.spec.ts +712 -712
- package/tests/unit/components/Core/SelectAutocomplete.spec.ts +361 -0
- package/tests/unit/components/Core/TextArea.spec.ts +565 -565
- package/tests/unit/components/Core/TickBox.spec.ts +836 -836
- package/tests/unit/components/Core/Tile.spec.ts +286 -0
- package/tests/unit/components/DateInput/DateInput.spec.ts +128 -0
- package/tests/unit/components/ErrorPage/ErrorPage.spec.ts +313 -313
- package/tests/unit/components/ErrorPage/ErrorPageLogo.spec.ts +153 -153
- 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 +186 -186
- 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 +193 -193
- package/tests/unit/components/Icons/Chair.spec.ts +241 -241
- package/tests/unit/components/Icons/ChairNotification.spec.ts +318 -318
- package/tests/unit/components/Icons/Circle.spec.ts +255 -255
- package/tests/unit/components/Icons/FavIcon.spec.ts +259 -259
- package/tests/unit/components/Icons/FilledCircle.spec.ts +274 -274
- package/tests/unit/components/Icons/Group3.spec.ts +362 -362
- package/tests/unit/components/Icons/Logo.spec.ts +229 -229
- package/tests/unit/components/Icons/MiniLogo.spec.ts +38 -38
- package/tests/unit/components/Icons/RingNotification.spec.ts +400 -400
- package/tests/unit/components/Icons/SolidArrowRight.spec.ts +49 -49
- package/tests/unit/components/Icons/calendar.spec.ts +293 -293
- package/tests/unit/components/Icons/checkbox.spec.ts +315 -315
- package/tests/unit/components/Icons/outlineChecked.spec.ts +441 -441
- package/tests/unit/components/Icons/play.spec.ts +315 -315
- 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 -197
- package/tests/unit/components/MaintenanceBanner/MaintenanceBanner.spec.ts +302 -302
- 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 +182 -182
- package/tests/unit/setup.ts +237 -237
- 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 +111 -111
- package/tests/unit/utils/accessibility.spec.ts +318 -318
- package/tests/unit/utils/anamnese.spec.ts +531 -0
- package/tsconfig.json +26 -26
- package/vite.config.ts +29 -29
- package/vitest.config.ts +91 -91
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import { nextTick } from 'vue';
|
|
4
|
+
import List from '@components/List/List.vue';
|
|
5
|
+
|
|
6
|
+
describe('List', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
vi.clearAllMocks();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// ─── Default Rendering ────────────────────────────────────────────
|
|
12
|
+
describe('Default Rendering', () => {
|
|
13
|
+
it('renders with data-testid="root"', () => {
|
|
14
|
+
const wrapper = mount(List);
|
|
15
|
+
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('applies wl-list CSS class', () => {
|
|
19
|
+
const wrapper = mount(List);
|
|
20
|
+
const root = wrapper.find('[data-testid="root"]');
|
|
21
|
+
expect(root.classes()).toContain('wl-list');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('renders root element', () => {
|
|
25
|
+
const wrapper = mount(List);
|
|
26
|
+
expect(wrapper.element).toBeTruthy();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// ─── Props Passthrough ────────────────────────────────────────────
|
|
31
|
+
// Note: Global stubs from setup.ts don't have `name` properties,
|
|
32
|
+
// so findComponent({ name: 'v-list' }) fails. We verify props
|
|
33
|
+
// via wrapper.vm instead of via the internal v-list stub attributes.
|
|
34
|
+
describe('Props Passthrough', () => {
|
|
35
|
+
it('accepts density prop', () => {
|
|
36
|
+
const wrapper = mount(List, { props: { density: 'compact' } });
|
|
37
|
+
expect(wrapper.vm.density).toBe('compact');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('defaults density to "default"', () => {
|
|
41
|
+
const wrapper = mount(List);
|
|
42
|
+
expect(wrapper.vm.density).toBe('default');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('accepts disabled prop', () => {
|
|
46
|
+
const wrapper = mount(List, { props: { disabled: true } });
|
|
47
|
+
expect(wrapper.vm.disabled).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('defaults disabled to false', () => {
|
|
51
|
+
const wrapper = mount(List);
|
|
52
|
+
expect(wrapper.vm.disabled).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('accepts nav prop', () => {
|
|
56
|
+
const wrapper = mount(List, { props: { nav: true } });
|
|
57
|
+
expect(wrapper.vm.nav).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('accepts color props', () => {
|
|
61
|
+
const wrapper = mount(List, {
|
|
62
|
+
props: {
|
|
63
|
+
color: 'primary',
|
|
64
|
+
baseColor: 'secondary',
|
|
65
|
+
activeColor: 'accent',
|
|
66
|
+
bgColor: 'white',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
expect(wrapper.vm.color).toBe('primary');
|
|
70
|
+
expect(wrapper.vm.baseColor).toBe('secondary');
|
|
71
|
+
expect(wrapper.vm.activeColor).toBe('accent');
|
|
72
|
+
expect(wrapper.vm.bgColor).toBe('white');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// ─── v-model:selected ─────────────────────────────────────────────
|
|
77
|
+
describe('v-model:selected', () => {
|
|
78
|
+
it('accepts selected prop', () => {
|
|
79
|
+
const wrapper = mount(List, { props: { selected: ['item1'] } });
|
|
80
|
+
expect(wrapper.vm.selected).toEqual(['item1']);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('emits update:selected when internal selected changes', async () => {
|
|
84
|
+
const wrapper = mount(List, { props: { selected: [] } });
|
|
85
|
+
// Simulate internal change
|
|
86
|
+
(wrapper.vm as any).internalSelected = ['new'];
|
|
87
|
+
await nextTick();
|
|
88
|
+
expect(wrapper.emitted('update:selected')).toBeTruthy();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('syncs external selected changes to internal state', async () => {
|
|
92
|
+
const wrapper = mount(List, { props: { selected: ['a'] } });
|
|
93
|
+
await wrapper.setProps({ selected: ['b'] });
|
|
94
|
+
expect((wrapper.vm as any).internalSelected).toEqual(['b']);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// ─── v-model:opened ───────────────────────────────────────────────
|
|
99
|
+
describe('v-model:opened', () => {
|
|
100
|
+
it('accepts opened prop', () => {
|
|
101
|
+
const wrapper = mount(List, { props: { opened: ['group1'] } });
|
|
102
|
+
expect(wrapper.vm.opened).toEqual(['group1']);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('emits update:opened when internal opened changes', async () => {
|
|
106
|
+
const wrapper = mount(List, { props: { opened: [] } });
|
|
107
|
+
(wrapper.vm as any).internalOpened = ['group1'];
|
|
108
|
+
await nextTick();
|
|
109
|
+
expect(wrapper.emitted('update:opened')).toBeTruthy();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ─── Default Slot ─────────────────────────────────────────────────
|
|
114
|
+
describe('Default Slot', () => {
|
|
115
|
+
it('renders slot content', () => {
|
|
116
|
+
const wrapper = mount(List, {
|
|
117
|
+
slots: { default: '<li class="item">Item 1</li>' },
|
|
118
|
+
});
|
|
119
|
+
expect(wrapper.find('.item').text()).toBe('Item 1');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ─── Expose ───────────────────────────────────────────────────────
|
|
124
|
+
describe('Expose', () => {
|
|
125
|
+
it('exposes $el ref', () => {
|
|
126
|
+
const wrapper = mount(List);
|
|
127
|
+
expect(wrapper.vm.$el).toBeDefined();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ─── WCAG Accessibility ───────────────────────────────────────────
|
|
132
|
+
describe('WCAG Accessibility', () => {
|
|
133
|
+
it('accepts tag prop (WCAG 1.3.1)', () => {
|
|
134
|
+
const wrapper = mount(List, { props: { tag: 'nav' } });
|
|
135
|
+
expect(wrapper.vm.tag).toBe('nav');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('accepts ariaLabel prop (WCAG 4.1.2)', () => {
|
|
139
|
+
const wrapper = mount(List, { props: { ariaLabel: 'Navigation menu' } });
|
|
140
|
+
expect(wrapper.vm.ariaLabel).toBe('Navigation menu');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('does not set aria-label when not provided', () => {
|
|
144
|
+
const wrapper = mount(List);
|
|
145
|
+
const root = wrapper.find('[data-testid="root"]');
|
|
146
|
+
expect(root.attributes('aria-label')).toBeUndefined();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ─── Edge Cases ───────────────────────────────────────────────────
|
|
151
|
+
describe('Edge Cases', () => {
|
|
152
|
+
it('handles undefined selected/opened', () => {
|
|
153
|
+
const wrapper = mount(List);
|
|
154
|
+
expect(wrapper.vm.selected).toBeUndefined();
|
|
155
|
+
expect(wrapper.vm.opened).toBeUndefined();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('handles mandatory prop', () => {
|
|
159
|
+
const wrapper = mount(List, { props: { mandatory: true } });
|
|
160
|
+
expect(wrapper.vm.mandatory).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import ListItem from '@components/ListItem/ListItem.vue';
|
|
4
|
+
|
|
5
|
+
const mountListItem = (props: Record<string, any> = {}, options: Record<string, any> = {}) => {
|
|
6
|
+
return mount(ListItem, {
|
|
7
|
+
props,
|
|
8
|
+
...options,
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
describe('ListItem', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vi.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// ─── Default Rendering ────────────────────────────────────────────
|
|
18
|
+
describe('Default Rendering', () => {
|
|
19
|
+
it('renders with data-testid="root"', () => {
|
|
20
|
+
const wrapper = mountListItem();
|
|
21
|
+
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('applies wl-list-item CSS class', () => {
|
|
25
|
+
const wrapper = mountListItem();
|
|
26
|
+
expect(wrapper.find('.wl-list-item').exists()).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('renders root element', () => {
|
|
30
|
+
const wrapper = mountListItem();
|
|
31
|
+
expect(wrapper.element).toBeTruthy();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// ─── Props ────────────────────────────────────────────────────────
|
|
36
|
+
describe('Props', () => {
|
|
37
|
+
it('accepts title prop', () => {
|
|
38
|
+
const wrapper = mountListItem({ title: 'Item Title' });
|
|
39
|
+
expect(wrapper.vm.title).toBe('Item Title');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('accepts subtitle prop', () => {
|
|
43
|
+
const wrapper = mountListItem({ subtitle: 'Sub text' });
|
|
44
|
+
expect(wrapper.vm.subtitle).toBe('Sub text');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('accepts value prop', () => {
|
|
48
|
+
const wrapper = mountListItem({ value: 'item1' });
|
|
49
|
+
expect(wrapper.vm.value).toBe('item1');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('accepts disabled prop', () => {
|
|
53
|
+
const wrapper = mountListItem({ disabled: true });
|
|
54
|
+
expect(wrapper.vm.disabled).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('defaults disabled to false', () => {
|
|
58
|
+
const wrapper = mountListItem();
|
|
59
|
+
expect(wrapper.vm.disabled).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('defaults density to "default"', () => {
|
|
63
|
+
const wrapper = mountListItem();
|
|
64
|
+
expect(wrapper.vm.density).toBe('default');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('accepts icon props', () => {
|
|
68
|
+
const wrapper = mountListItem({
|
|
69
|
+
prependIcon: 'mdi-star',
|
|
70
|
+
appendIcon: 'mdi-chevron-right',
|
|
71
|
+
});
|
|
72
|
+
expect(wrapper.vm.prependIcon).toBe('mdi-star');
|
|
73
|
+
expect(wrapper.vm.appendIcon).toBe('mdi-chevron-right');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('accepts color props', () => {
|
|
77
|
+
const wrapper = mountListItem({
|
|
78
|
+
color: 'primary',
|
|
79
|
+
baseColor: 'secondary',
|
|
80
|
+
activeColor: 'accent',
|
|
81
|
+
});
|
|
82
|
+
expect(wrapper.vm.color).toBe('primary');
|
|
83
|
+
expect(wrapper.vm.baseColor).toBe('secondary');
|
|
84
|
+
expect(wrapper.vm.activeColor).toBe('accent');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// ─── Link Props ───────────────────────────────────────────────────
|
|
89
|
+
describe('Link Props', () => {
|
|
90
|
+
it('accepts href prop', () => {
|
|
91
|
+
const wrapper = mountListItem({ href: 'https://example.com' });
|
|
92
|
+
expect(wrapper.vm.href).toBe('https://example.com');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('accepts to prop as string', () => {
|
|
96
|
+
const wrapper = mountListItem({ to: '/dashboard' });
|
|
97
|
+
expect(wrapper.vm.to).toBe('/dashboard');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('accepts to prop as object', () => {
|
|
101
|
+
const to = { name: 'dashboard', params: { id: 1 } };
|
|
102
|
+
const wrapper = mountListItem({ to });
|
|
103
|
+
expect(wrapper.vm.to).toEqual(to);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('accepts link prop', () => {
|
|
107
|
+
const wrapper = mountListItem({ link: true });
|
|
108
|
+
expect(wrapper.vm.link).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('defaults link props to undefined', () => {
|
|
112
|
+
const wrapper = mountListItem();
|
|
113
|
+
expect(wrapper.vm.href).toBeUndefined();
|
|
114
|
+
expect(wrapper.vm.to).toBeUndefined();
|
|
115
|
+
expect(wrapper.vm.link).toBeUndefined();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// ─── Slots ────────────────────────────────────────────────────────
|
|
120
|
+
describe('Slots', () => {
|
|
121
|
+
it('renders default slot', () => {
|
|
122
|
+
const wrapper = mountListItem({}, {
|
|
123
|
+
slots: { default: '<span class="content">Content</span>' },
|
|
124
|
+
});
|
|
125
|
+
expect(wrapper.find('.content').text()).toBe('Content');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('renders title slot when provided', () => {
|
|
129
|
+
const wrapper = mountListItem({}, {
|
|
130
|
+
slots: { title: '<h3 class="custom-title">Title</h3>' },
|
|
131
|
+
});
|
|
132
|
+
// The slot content may or may not render depending on the stub.
|
|
133
|
+
// We verify the component doesn't error when slot is provided.
|
|
134
|
+
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('renders prepend slot when provided', () => {
|
|
138
|
+
const wrapper = mountListItem({}, {
|
|
139
|
+
slots: { prepend: '<div class="custom-prepend">Icon</div>' },
|
|
140
|
+
});
|
|
141
|
+
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// ─── Expose ───────────────────────────────────────────────────────
|
|
146
|
+
describe('Expose', () => {
|
|
147
|
+
it('exposes $el ref', () => {
|
|
148
|
+
const wrapper = mountListItem();
|
|
149
|
+
expect(wrapper.vm.$el).toBeDefined();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// ─── WCAG Accessibility ───────────────────────────────────────────
|
|
154
|
+
describe('WCAG Accessibility', () => {
|
|
155
|
+
it('accepts ariaLabel prop (WCAG 4.1.2)', () => {
|
|
156
|
+
const wrapper = mountListItem({ ariaLabel: 'Dashboard link' });
|
|
157
|
+
expect(wrapper.vm.ariaLabel).toBe('Dashboard link');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('defaults ariaLabel to undefined', () => {
|
|
161
|
+
const wrapper = mountListItem();
|
|
162
|
+
expect(wrapper.vm.ariaLabel).toBeUndefined();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('aria-label not rendered on root when not provided', () => {
|
|
166
|
+
const wrapper = mountListItem();
|
|
167
|
+
const root = wrapper.find('[data-testid="root"]');
|
|
168
|
+
expect(root.attributes('aria-label')).toBeUndefined();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('supports href for native link navigation (WCAG 2.1.1)', () => {
|
|
172
|
+
const wrapper = mountListItem({ href: '/page' });
|
|
173
|
+
expect(wrapper.vm.href).toBe('/page');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('supports to for router link navigation (WCAG 2.1.1)', () => {
|
|
177
|
+
const wrapper = mountListItem({ to: '/route' });
|
|
178
|
+
expect(wrapper.vm.to).toBe('/route');
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// ─── Edge Cases ───────────────────────────────────────────────────
|
|
183
|
+
describe('Edge Cases', () => {
|
|
184
|
+
it('handles numeric title/subtitle', () => {
|
|
185
|
+
const wrapper = mountListItem({ title: 42, subtitle: 100 });
|
|
186
|
+
expect(wrapper.vm.title).toBe(42);
|
|
187
|
+
expect(wrapper.vm.subtitle).toBe(100);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('handles active prop', () => {
|
|
191
|
+
const wrapper = mountListItem({ active: true });
|
|
192
|
+
expect(wrapper.vm.active).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('handles ripple prop', () => {
|
|
196
|
+
const wrapper = mountListItem({ ripple: false });
|
|
197
|
+
expect(wrapper.vm.ripple).toBe(false);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('handles rounded prop', () => {
|
|
201
|
+
const wrapper = mountListItem({ rounded: 'lg' });
|
|
202
|
+
expect(wrapper.vm.rounded).toBe('lg');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|