@zap-wunschlachen/wl-shared-components 1.0.38 → 1.0.40
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 +33 -33
- 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/background.svg +60 -60
- package/public/style.css +187 -187
- package/public/technologies.svg +22 -22
- package/src/assets/css/base.css +234 -232
- package/src/assets/css/variables.css +112 -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 -118
- package/src/components/Button/Button.vue +174 -174
- package/src/components/CheckBox/CheckBox.css +214 -214
- package/src/components/CheckBox/Checkbox.vue +138 -138
- 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 -38
- 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/{calendar.vue → Calendar.vue} +17 -17
- 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/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 -253
- 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 -71
- 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 -316
- 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 -218
- package/src/components/index.ts +29 -29
- 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 +116 -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 -469
- package/tests/unit/components/Accordion/AccordionGroup.spec.ts +228 -228
- package/tests/unit/components/Accordion/AccordionGroup.spec.ts.skip +342 -342
- package/tests/unit/components/Accordion/AccordionItem.spec.ts +292 -292
- package/tests/unit/components/Accordion/AccordionItem.spec.ts.skip +383 -383
- package/tests/unit/components/Appointment/AnamneseNotification.spec.ts +176 -176
- 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 -177
- 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 -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 +61 -61
- 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 -228
- package/tests/unit/components/Icons/MiniLogo.spec.ts +38 -38
- package/tests/unit/components/Icons/RingNotification.spec.ts +393 -393
- package/tests/unit/components/Icons/SolidArrowRight.spec.ts +49 -49
- 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 -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 +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 -318
- package/tsconfig.json +26 -26
- package/vite.config.ts +29 -29
- package/vitest.config.ts +83 -83
|
@@ -1,398 +1,398 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { mount, VueWrapper } from '@vue/test-utils';
|
|
3
|
-
import Details from '../../../../../src/components/Appointment/Card/Details.vue';
|
|
4
|
-
import type { AppointmentData, Dentist } from '../../../../../src/types';
|
|
5
|
-
|
|
6
|
-
describe('Appointment Card Details', () => {
|
|
7
|
-
let wrapper: VueWrapper;
|
|
8
|
-
|
|
9
|
-
const createMockDentist = (overrides: Partial<Dentist> = {}): Dentist => ({
|
|
10
|
-
name: 'Dr. Test Dentist',
|
|
11
|
-
gender: 'Male',
|
|
12
|
-
imageSrc: 'https://example.com/dentist.jpg',
|
|
13
|
-
...overrides
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const createMockAppointment = (overrides: Partial<AppointmentData> = {}): AppointmentData => ({
|
|
17
|
-
id: '123',
|
|
18
|
-
template_name: 'Test Template',
|
|
19
|
-
description: 'Test Description',
|
|
20
|
-
dentist: createMockDentist(),
|
|
21
|
-
start: new Date().toISOString(),
|
|
22
|
-
type: 1,
|
|
23
|
-
address: 'Test Address 123',
|
|
24
|
-
district: 'Test District',
|
|
25
|
-
...overrides
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
const appointment = createMockAppointment();
|
|
30
|
-
wrapper = mount(Details, {
|
|
31
|
-
props: { appointment },
|
|
32
|
-
global: {
|
|
33
|
-
stubs: {
|
|
34
|
-
Icon: {
|
|
35
|
-
template: '<div class="icon-stub" :name="name" :size="size"></div>',
|
|
36
|
-
props: ['name', 'size']
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('Component Rendering', () => {
|
|
44
|
-
it('should render the component when dentist is provided', () => {
|
|
45
|
-
expect(wrapper.find('.details-grid').exists()).toBe(true);
|
|
46
|
-
expect(wrapper.find('.dentist-section').exists()).toBe(true);
|
|
47
|
-
expect(wrapper.find('.location-section').exists()).toBe(true);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should not render anything when dentist is not provided', async () => {
|
|
51
|
-
const appointmentWithoutDentist = createMockAppointment({ dentist: undefined as any });
|
|
52
|
-
await wrapper.setProps({ appointment: appointmentWithoutDentist });
|
|
53
|
-
|
|
54
|
-
expect(wrapper.find('.details-grid').exists()).toBe(false);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe('Dentist Information', () => {
|
|
59
|
-
it('should display single dentist information', () => {
|
|
60
|
-
expect(wrapper.text()).toContain('Dr. Test Dentist');
|
|
61
|
-
expect(wrapper.text()).toContain('Male');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should display dentist image when imageSrc is provided', () => {
|
|
65
|
-
const dentistImage = wrapper.find('.dentist-image');
|
|
66
|
-
expect(dentistImage.exists()).toBe(true);
|
|
67
|
-
expect(dentistImage.attributes('src')).toBe('https://example.com/dentist.jpg');
|
|
68
|
-
expect(dentistImage.attributes('alt')).toBe('Dentist photo');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('should not display dentist image when imageSrc is not provided', async () => {
|
|
72
|
-
const dentistWithoutImage = createMockDentist({ imageSrc: '' });
|
|
73
|
-
const appointmentWithoutImage = createMockAppointment({ dentist: dentistWithoutImage });
|
|
74
|
-
await wrapper.setProps({ appointment: appointmentWithoutImage });
|
|
75
|
-
|
|
76
|
-
expect(wrapper.find('.dentist-image').exists()).toBe(false);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should handle multiple dentists as array', async () => {
|
|
80
|
-
const multipleDentists = [
|
|
81
|
-
createMockDentist({ name: 'Dr. First Dentist', gender: 'Female' }),
|
|
82
|
-
createMockDentist({ name: 'Dr. Second Dentist', gender: 'Male' })
|
|
83
|
-
];
|
|
84
|
-
const appointmentWithMultipleDentists = createMockAppointment({
|
|
85
|
-
dentist: multipleDentists as any
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
await wrapper.setProps({ appointment: appointmentWithMultipleDentists });
|
|
89
|
-
|
|
90
|
-
expect(wrapper.text()).toContain('Dr. First Dentist');
|
|
91
|
-
expect(wrapper.text()).toContain('Dr. Second Dentist');
|
|
92
|
-
expect(wrapper.text()).toContain('Female');
|
|
93
|
-
expect(wrapper.text()).toContain('Male');
|
|
94
|
-
|
|
95
|
-
const dentistItems = wrapper.findAll('.dentist-item');
|
|
96
|
-
expect(dentistItems).toHaveLength(2);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should display dentist images for multiple dentists when provided', async () => {
|
|
100
|
-
const multipleDentists = [
|
|
101
|
-
createMockDentist({ name: 'Dr. First', imageSrc: 'https://example.com/dentist1.jpg' }),
|
|
102
|
-
createMockDentist({ name: 'Dr. Second', imageSrc: 'https://example.com/dentist2.jpg' })
|
|
103
|
-
];
|
|
104
|
-
const appointmentWithMultipleDentists = createMockAppointment({
|
|
105
|
-
dentist: multipleDentists as any
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
await wrapper.setProps({ appointment: appointmentWithMultipleDentists });
|
|
109
|
-
|
|
110
|
-
const dentistImages = wrapper.findAll('.dentist-image');
|
|
111
|
-
expect(dentistImages).toHaveLength(2);
|
|
112
|
-
expect(dentistImages[0].attributes('src')).toBe('https://example.com/dentist1.jpg');
|
|
113
|
-
expect(dentistImages[1].attributes('src')).toBe('https://example.com/dentist2.jpg');
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe('Location Information', () => {
|
|
118
|
-
it('should display appointment address', () => {
|
|
119
|
-
expect(wrapper.text()).toContain('Test Address 123');
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should display appointment district', () => {
|
|
123
|
-
expect(wrapper.text()).toContain('Test District');
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should display default address when address is not provided', async () => {
|
|
127
|
-
const appointmentWithoutAddress = createMockAppointment({ address: undefined });
|
|
128
|
-
await wrapper.setProps({ appointment: appointmentWithoutAddress });
|
|
129
|
-
|
|
130
|
-
expect(wrapper.text()).toContain('Unbekannter Ort');
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('should display default district when district is not provided', async () => {
|
|
134
|
-
const appointmentWithoutDistrict = createMockAppointment({ district: undefined });
|
|
135
|
-
await wrapper.setProps({ appointment: appointmentWithoutDistrict });
|
|
136
|
-
|
|
137
|
-
expect(wrapper.text()).toContain('Unbekannter Stadtteil');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('should render location icon', () => {
|
|
141
|
-
const locationIcon = wrapper.find('.icon-stub');
|
|
142
|
-
expect(locationIcon.exists()).toBe(true);
|
|
143
|
-
expect(locationIcon.attributes('name')).toBe('heroicons:map-pin');
|
|
144
|
-
expect(locationIcon.attributes('size')).toBe('24');
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
describe('CSS Classes and Styling', () => {
|
|
149
|
-
it('should apply correct CSS classes to dentist elements', () => {
|
|
150
|
-
expect(wrapper.find('.dentist-section').exists()).toBe(true);
|
|
151
|
-
expect(wrapper.find('.dentist-list').exists()).toBe(true);
|
|
152
|
-
expect(wrapper.find('.dentist-item').exists()).toBe(true);
|
|
153
|
-
expect(wrapper.find('.dentist-info').exists()).toBe(true);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it('should apply correct CSS classes to location elements', () => {
|
|
157
|
-
expect(wrapper.find('.location-section').exists()).toBe(true);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('should apply correct text styling classes', () => {
|
|
161
|
-
const smallTexts = wrapper.findAll('.p-small');
|
|
162
|
-
expect(smallTexts.length).toBeGreaterThan(0);
|
|
163
|
-
|
|
164
|
-
const dentalBlueTexts = wrapper.findAll('.text-dental-blue-3');
|
|
165
|
-
expect(dentalBlueTexts.length).toBeGreaterThan(0);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('should apply icon styling class', () => {
|
|
169
|
-
const iconTextDental = wrapper.find('.icon-text-dental');
|
|
170
|
-
expect(iconTextDental.exists()).toBe(true);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe('Edge Cases', () => {
|
|
175
|
-
it('should handle null appointment gracefully', async () => {
|
|
176
|
-
const nullAppointment = null as any;
|
|
177
|
-
wrapper = mount(Details, {
|
|
178
|
-
props: { appointment: nullAppointment },
|
|
179
|
-
global: {
|
|
180
|
-
stubs: {
|
|
181
|
-
Icon: {
|
|
182
|
-
template: '<div class="icon-stub" :name="name" :size="size"></div>',
|
|
183
|
-
props: ['name', 'size']
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
expect(wrapper.find('.details-grid').exists()).toBe(false);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it('should handle empty address and district', async () => {
|
|
192
|
-
const appointmentWithEmptyStrings = createMockAppointment({
|
|
193
|
-
address: '',
|
|
194
|
-
district: ''
|
|
195
|
-
});
|
|
196
|
-
await wrapper.setProps({ appointment: appointmentWithEmptyStrings });
|
|
197
|
-
|
|
198
|
-
expect(wrapper.text()).toContain('Unbekannter Ort');
|
|
199
|
-
expect(wrapper.text()).toContain('Unbekannter Stadtteil');
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('should handle dentist array with mixed imageSrc values', async () => {
|
|
203
|
-
const mixedDentists = [
|
|
204
|
-
createMockDentist({ name: 'Dr. With Image', imageSrc: 'https://example.com/image.jpg' }),
|
|
205
|
-
createMockDentist({ name: 'Dr. Without Image', imageSrc: '' })
|
|
206
|
-
];
|
|
207
|
-
const appointmentWithMixedDentists = createMockAppointment({
|
|
208
|
-
dentist: mixedDentists as any
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
await wrapper.setProps({ appointment: appointmentWithMixedDentists });
|
|
212
|
-
|
|
213
|
-
const dentistImages = wrapper.findAll('.dentist-image');
|
|
214
|
-
expect(dentistImages).toHaveLength(1); // Only one dentist has an image
|
|
215
|
-
expect(dentistImages[0].attributes('src')).toBe('https://example.com/image.jpg');
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
describe('Props', () => {
|
|
220
|
-
it('should accept dentistImageSrc prop', async () => {
|
|
221
|
-
await wrapper.setProps({ dentistImageSrc: 'https://example.com/custom-image.jpg' });
|
|
222
|
-
// Note: The component doesn't currently use this prop, but we test that it's accepted
|
|
223
|
-
expect(wrapper.props('dentistImageSrc')).toBe('https://example.com/custom-image.jpg');
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it('should update when appointment prop changes', async () => {
|
|
227
|
-
const newAppointment = createMockAppointment({
|
|
228
|
-
dentist: createMockDentist({ name: 'Dr. New Dentist' }),
|
|
229
|
-
address: 'New Address 456',
|
|
230
|
-
district: 'New District'
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
await wrapper.setProps({ appointment: newAppointment });
|
|
234
|
-
|
|
235
|
-
expect(wrapper.text()).toContain('Dr. New Dentist');
|
|
236
|
-
expect(wrapper.text()).toContain('New Address 456');
|
|
237
|
-
expect(wrapper.text()).toContain('New District');
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
describe('Accessibility', () => {
|
|
242
|
-
beforeEach(() => {
|
|
243
|
-
const appointment = createMockAppointment();
|
|
244
|
-
wrapper = mount(Details, {
|
|
245
|
-
props: { appointment },
|
|
246
|
-
global: {
|
|
247
|
-
stubs: {
|
|
248
|
-
Icon: {
|
|
249
|
-
template: '<div class="icon-stub" :aria-hidden="true" :name="name" :size="size" role="img"></div>',
|
|
250
|
-
props: ['name', 'size']
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('should have semantic structure for dentist information', () => {
|
|
258
|
-
const dentistSection = wrapper.find('.dentist-section');
|
|
259
|
-
expect(dentistSection.exists()).toBe(true);
|
|
260
|
-
|
|
261
|
-
const dentistList = wrapper.find('.dentist-list');
|
|
262
|
-
expect(dentistList.exists()).toBe(true);
|
|
263
|
-
|
|
264
|
-
const dentistInfo = wrapper.find('.dentist-info');
|
|
265
|
-
expect(dentistInfo.exists()).toBe(true);
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
it('should provide alt text for dentist images', () => {
|
|
269
|
-
const dentistImage = wrapper.find('.dentist-image');
|
|
270
|
-
if (dentistImage.exists()) {
|
|
271
|
-
expect(dentistImage.attributes('alt')).toBe('Dentist photo');
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it('should use appropriate text hierarchy', () => {
|
|
276
|
-
const smallTexts = wrapper.findAll('.p-small');
|
|
277
|
-
expect(smallTexts.length).toBeGreaterThan(0);
|
|
278
|
-
|
|
279
|
-
// Check that dentist name is properly structured
|
|
280
|
-
const dentistInfo = wrapper.find('.dentist-info');
|
|
281
|
-
if (dentistInfo.exists()) {
|
|
282
|
-
const nameElement = dentistInfo.find('.p-small');
|
|
283
|
-
expect(nameElement.exists()).toBe(true);
|
|
284
|
-
expect(nameElement.text()).toBe('Dr. Test Dentist');
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('should have proper icon accessibility attributes', () => {
|
|
289
|
-
const icons = wrapper.findAll('.icon-stub');
|
|
290
|
-
|
|
291
|
-
icons.forEach(icon => {
|
|
292
|
-
expect(icon.attributes('aria-hidden')).toBe('true');
|
|
293
|
-
expect(icon.attributes('role')).toBe('img');
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
it('should provide semantic meaning for location information', () => {
|
|
298
|
-
const locationSection = wrapper.find('.location-section');
|
|
299
|
-
expect(locationSection.exists()).toBe(true);
|
|
300
|
-
|
|
301
|
-
// Location should have descriptive text
|
|
302
|
-
expect(wrapper.text()).toContain('Test Address 123');
|
|
303
|
-
expect(wrapper.text()).toContain('Test District');
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it('should use meaningful text for missing information', async () => {
|
|
307
|
-
const appointmentWithoutLocation = createMockAppointment({
|
|
308
|
-
address: undefined,
|
|
309
|
-
district: undefined
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
await wrapper.setProps({ appointment: appointmentWithoutLocation });
|
|
313
|
-
|
|
314
|
-
expect(wrapper.text()).toContain('Unbekannter Ort');
|
|
315
|
-
expect(wrapper.text()).toContain('Unbekannter Stadtteil');
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
it('should handle multiple dentists accessibly', async () => {
|
|
319
|
-
const multipleDentists = [
|
|
320
|
-
createMockDentist({ name: 'Dr. First Dentist', gender: 'Female' }),
|
|
321
|
-
createMockDentist({ name: 'Dr. Second Dentist', gender: 'Male' })
|
|
322
|
-
];
|
|
323
|
-
const appointmentWithMultipleDentists = createMockAppointment({
|
|
324
|
-
dentist: multipleDentists as any
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
await wrapper.setProps({ appointment: appointmentWithMultipleDentists });
|
|
328
|
-
|
|
329
|
-
const dentistItems = wrapper.findAll('.dentist-item');
|
|
330
|
-
expect(dentistItems).toHaveLength(2);
|
|
331
|
-
|
|
332
|
-
// Each dentist should have proper structure
|
|
333
|
-
dentistItems.forEach((item, index) => {
|
|
334
|
-
const dentistInfo = item.find('.dentist-info');
|
|
335
|
-
expect(dentistInfo.exists()).toBe(true);
|
|
336
|
-
|
|
337
|
-
const nameElements = dentistInfo.findAll('.p-small');
|
|
338
|
-
expect(nameElements.length).toBeGreaterThanOrEqual(1);
|
|
339
|
-
});
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it('should have proper contrast and text styling classes', () => {
|
|
343
|
-
// Check for dental blue styling class which should provide proper contrast
|
|
344
|
-
const blueTexts = wrapper.findAll('.text-dental-blue-3');
|
|
345
|
-
expect(blueTexts.length).toBeGreaterThan(0);
|
|
346
|
-
|
|
347
|
-
blueTexts.forEach(text => {
|
|
348
|
-
expect(text.classes()).toContain('text-dental-blue-3');
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
it('should provide screen reader friendly information structure', () => {
|
|
353
|
-
// Information should be in logical reading order
|
|
354
|
-
const dentistSection = wrapper.find('.dentist-section');
|
|
355
|
-
const locationSection = wrapper.find('.location-section');
|
|
356
|
-
|
|
357
|
-
expect(dentistSection.exists()).toBe(true);
|
|
358
|
-
expect(locationSection.exists()).toBe(true);
|
|
359
|
-
|
|
360
|
-
// Both sections should contain meaningful content
|
|
361
|
-
expect(dentistSection.text().trim()).toBeTruthy();
|
|
362
|
-
expect(locationSection.text().trim()).toBeTruthy();
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
it('should use semantic HTML structure', () => {
|
|
366
|
-
const detailsGrid = wrapper.find('.details-grid');
|
|
367
|
-
expect(detailsGrid.exists()).toBe(true);
|
|
368
|
-
|
|
369
|
-
// Should have logical sectioning
|
|
370
|
-
expect(wrapper.find('.dentist-section').exists()).toBe(true);
|
|
371
|
-
expect(wrapper.find('.location-section').exists()).toBe(true);
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
it('should handle missing dentist data gracefully for accessibility', async () => {
|
|
375
|
-
const appointmentWithoutDentist = createMockAppointment({ dentist: undefined as any });
|
|
376
|
-
await wrapper.setProps({ appointment: appointmentWithoutDentist });
|
|
377
|
-
|
|
378
|
-
// Component should not render when no dentist data
|
|
379
|
-
expect(wrapper.find('.details-grid').exists()).toBe(false);
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
it('should provide accessible image handling', async () => {
|
|
383
|
-
const dentistWithImage = createMockDentist({
|
|
384
|
-
imageSrc: 'https://example.com/dentist.jpg',
|
|
385
|
-
name: 'Dr. Image Test'
|
|
386
|
-
});
|
|
387
|
-
const appointmentWithImage = createMockAppointment({ dentist: dentistWithImage });
|
|
388
|
-
|
|
389
|
-
await wrapper.setProps({ appointment: appointmentWithImage });
|
|
390
|
-
|
|
391
|
-
const image = wrapper.find('.dentist-image');
|
|
392
|
-
if (image.exists()) {
|
|
393
|
-
expect(image.attributes('alt')).toBe('Dentist photo');
|
|
394
|
-
expect(image.attributes('src')).toBe('https://example.com/dentist.jpg');
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
});
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { mount, VueWrapper } from '@vue/test-utils';
|
|
3
|
+
import Details from '../../../../../src/components/Appointment/Card/Details.vue';
|
|
4
|
+
import type { AppointmentData, Dentist } from '../../../../../src/types';
|
|
5
|
+
|
|
6
|
+
describe('Appointment Card Details', () => {
|
|
7
|
+
let wrapper: VueWrapper;
|
|
8
|
+
|
|
9
|
+
const createMockDentist = (overrides: Partial<Dentist> = {}): Dentist => ({
|
|
10
|
+
name: 'Dr. Test Dentist',
|
|
11
|
+
gender: 'Male',
|
|
12
|
+
imageSrc: 'https://example.com/dentist.jpg',
|
|
13
|
+
...overrides
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const createMockAppointment = (overrides: Partial<AppointmentData> = {}): AppointmentData => ({
|
|
17
|
+
id: '123',
|
|
18
|
+
template_name: 'Test Template',
|
|
19
|
+
description: 'Test Description',
|
|
20
|
+
dentist: createMockDentist(),
|
|
21
|
+
start: new Date().toISOString(),
|
|
22
|
+
type: 1,
|
|
23
|
+
address: 'Test Address 123',
|
|
24
|
+
district: 'Test District',
|
|
25
|
+
...overrides
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
const appointment = createMockAppointment();
|
|
30
|
+
wrapper = mount(Details, {
|
|
31
|
+
props: { appointment },
|
|
32
|
+
global: {
|
|
33
|
+
stubs: {
|
|
34
|
+
Icon: {
|
|
35
|
+
template: '<div class="icon-stub" :name="name" :size="size"></div>',
|
|
36
|
+
props: ['name', 'size']
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('Component Rendering', () => {
|
|
44
|
+
it('should render the component when dentist is provided', () => {
|
|
45
|
+
expect(wrapper.find('.details-grid').exists()).toBe(true);
|
|
46
|
+
expect(wrapper.find('.dentist-section').exists()).toBe(true);
|
|
47
|
+
expect(wrapper.find('.location-section').exists()).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should not render anything when dentist is not provided', async () => {
|
|
51
|
+
const appointmentWithoutDentist = createMockAppointment({ dentist: undefined as any });
|
|
52
|
+
await wrapper.setProps({ appointment: appointmentWithoutDentist });
|
|
53
|
+
|
|
54
|
+
expect(wrapper.find('.details-grid').exists()).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('Dentist Information', () => {
|
|
59
|
+
it('should display single dentist information', () => {
|
|
60
|
+
expect(wrapper.text()).toContain('Dr. Test Dentist');
|
|
61
|
+
expect(wrapper.text()).toContain('Male');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should display dentist image when imageSrc is provided', () => {
|
|
65
|
+
const dentistImage = wrapper.find('.dentist-image');
|
|
66
|
+
expect(dentistImage.exists()).toBe(true);
|
|
67
|
+
expect(dentistImage.attributes('src')).toBe('https://example.com/dentist.jpg');
|
|
68
|
+
expect(dentistImage.attributes('alt')).toBe('Dentist photo');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should not display dentist image when imageSrc is not provided', async () => {
|
|
72
|
+
const dentistWithoutImage = createMockDentist({ imageSrc: '' });
|
|
73
|
+
const appointmentWithoutImage = createMockAppointment({ dentist: dentistWithoutImage });
|
|
74
|
+
await wrapper.setProps({ appointment: appointmentWithoutImage });
|
|
75
|
+
|
|
76
|
+
expect(wrapper.find('.dentist-image').exists()).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should handle multiple dentists as array', async () => {
|
|
80
|
+
const multipleDentists = [
|
|
81
|
+
createMockDentist({ name: 'Dr. First Dentist', gender: 'Female' }),
|
|
82
|
+
createMockDentist({ name: 'Dr. Second Dentist', gender: 'Male' })
|
|
83
|
+
];
|
|
84
|
+
const appointmentWithMultipleDentists = createMockAppointment({
|
|
85
|
+
dentist: multipleDentists as any
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
await wrapper.setProps({ appointment: appointmentWithMultipleDentists });
|
|
89
|
+
|
|
90
|
+
expect(wrapper.text()).toContain('Dr. First Dentist');
|
|
91
|
+
expect(wrapper.text()).toContain('Dr. Second Dentist');
|
|
92
|
+
expect(wrapper.text()).toContain('Female');
|
|
93
|
+
expect(wrapper.text()).toContain('Male');
|
|
94
|
+
|
|
95
|
+
const dentistItems = wrapper.findAll('.dentist-item');
|
|
96
|
+
expect(dentistItems).toHaveLength(2);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should display dentist images for multiple dentists when provided', async () => {
|
|
100
|
+
const multipleDentists = [
|
|
101
|
+
createMockDentist({ name: 'Dr. First', imageSrc: 'https://example.com/dentist1.jpg' }),
|
|
102
|
+
createMockDentist({ name: 'Dr. Second', imageSrc: 'https://example.com/dentist2.jpg' })
|
|
103
|
+
];
|
|
104
|
+
const appointmentWithMultipleDentists = createMockAppointment({
|
|
105
|
+
dentist: multipleDentists as any
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await wrapper.setProps({ appointment: appointmentWithMultipleDentists });
|
|
109
|
+
|
|
110
|
+
const dentistImages = wrapper.findAll('.dentist-image');
|
|
111
|
+
expect(dentistImages).toHaveLength(2);
|
|
112
|
+
expect(dentistImages[0].attributes('src')).toBe('https://example.com/dentist1.jpg');
|
|
113
|
+
expect(dentistImages[1].attributes('src')).toBe('https://example.com/dentist2.jpg');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('Location Information', () => {
|
|
118
|
+
it('should display appointment address', () => {
|
|
119
|
+
expect(wrapper.text()).toContain('Test Address 123');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should display appointment district', () => {
|
|
123
|
+
expect(wrapper.text()).toContain('Test District');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should display default address when address is not provided', async () => {
|
|
127
|
+
const appointmentWithoutAddress = createMockAppointment({ address: undefined });
|
|
128
|
+
await wrapper.setProps({ appointment: appointmentWithoutAddress });
|
|
129
|
+
|
|
130
|
+
expect(wrapper.text()).toContain('Unbekannter Ort');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should display default district when district is not provided', async () => {
|
|
134
|
+
const appointmentWithoutDistrict = createMockAppointment({ district: undefined });
|
|
135
|
+
await wrapper.setProps({ appointment: appointmentWithoutDistrict });
|
|
136
|
+
|
|
137
|
+
expect(wrapper.text()).toContain('Unbekannter Stadtteil');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should render location icon', () => {
|
|
141
|
+
const locationIcon = wrapper.find('.icon-stub');
|
|
142
|
+
expect(locationIcon.exists()).toBe(true);
|
|
143
|
+
expect(locationIcon.attributes('name')).toBe('heroicons:map-pin');
|
|
144
|
+
expect(locationIcon.attributes('size')).toBe('24');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe('CSS Classes and Styling', () => {
|
|
149
|
+
it('should apply correct CSS classes to dentist elements', () => {
|
|
150
|
+
expect(wrapper.find('.dentist-section').exists()).toBe(true);
|
|
151
|
+
expect(wrapper.find('.dentist-list').exists()).toBe(true);
|
|
152
|
+
expect(wrapper.find('.dentist-item').exists()).toBe(true);
|
|
153
|
+
expect(wrapper.find('.dentist-info').exists()).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should apply correct CSS classes to location elements', () => {
|
|
157
|
+
expect(wrapper.find('.location-section').exists()).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should apply correct text styling classes', () => {
|
|
161
|
+
const smallTexts = wrapper.findAll('.p-small');
|
|
162
|
+
expect(smallTexts.length).toBeGreaterThan(0);
|
|
163
|
+
|
|
164
|
+
const dentalBlueTexts = wrapper.findAll('.text-dental-blue-3');
|
|
165
|
+
expect(dentalBlueTexts.length).toBeGreaterThan(0);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should apply icon styling class', () => {
|
|
169
|
+
const iconTextDental = wrapper.find('.icon-text-dental');
|
|
170
|
+
expect(iconTextDental.exists()).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('Edge Cases', () => {
|
|
175
|
+
it('should handle null appointment gracefully', async () => {
|
|
176
|
+
const nullAppointment = null as any;
|
|
177
|
+
wrapper = mount(Details, {
|
|
178
|
+
props: { appointment: nullAppointment },
|
|
179
|
+
global: {
|
|
180
|
+
stubs: {
|
|
181
|
+
Icon: {
|
|
182
|
+
template: '<div class="icon-stub" :name="name" :size="size"></div>',
|
|
183
|
+
props: ['name', 'size']
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
expect(wrapper.find('.details-grid').exists()).toBe(false);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should handle empty address and district', async () => {
|
|
192
|
+
const appointmentWithEmptyStrings = createMockAppointment({
|
|
193
|
+
address: '',
|
|
194
|
+
district: ''
|
|
195
|
+
});
|
|
196
|
+
await wrapper.setProps({ appointment: appointmentWithEmptyStrings });
|
|
197
|
+
|
|
198
|
+
expect(wrapper.text()).toContain('Unbekannter Ort');
|
|
199
|
+
expect(wrapper.text()).toContain('Unbekannter Stadtteil');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should handle dentist array with mixed imageSrc values', async () => {
|
|
203
|
+
const mixedDentists = [
|
|
204
|
+
createMockDentist({ name: 'Dr. With Image', imageSrc: 'https://example.com/image.jpg' }),
|
|
205
|
+
createMockDentist({ name: 'Dr. Without Image', imageSrc: '' })
|
|
206
|
+
];
|
|
207
|
+
const appointmentWithMixedDentists = createMockAppointment({
|
|
208
|
+
dentist: mixedDentists as any
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
await wrapper.setProps({ appointment: appointmentWithMixedDentists });
|
|
212
|
+
|
|
213
|
+
const dentistImages = wrapper.findAll('.dentist-image');
|
|
214
|
+
expect(dentistImages).toHaveLength(1); // Only one dentist has an image
|
|
215
|
+
expect(dentistImages[0].attributes('src')).toBe('https://example.com/image.jpg');
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('Props', () => {
|
|
220
|
+
it('should accept dentistImageSrc prop', async () => {
|
|
221
|
+
await wrapper.setProps({ dentistImageSrc: 'https://example.com/custom-image.jpg' });
|
|
222
|
+
// Note: The component doesn't currently use this prop, but we test that it's accepted
|
|
223
|
+
expect(wrapper.props('dentistImageSrc')).toBe('https://example.com/custom-image.jpg');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should update when appointment prop changes', async () => {
|
|
227
|
+
const newAppointment = createMockAppointment({
|
|
228
|
+
dentist: createMockDentist({ name: 'Dr. New Dentist' }),
|
|
229
|
+
address: 'New Address 456',
|
|
230
|
+
district: 'New District'
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
await wrapper.setProps({ appointment: newAppointment });
|
|
234
|
+
|
|
235
|
+
expect(wrapper.text()).toContain('Dr. New Dentist');
|
|
236
|
+
expect(wrapper.text()).toContain('New Address 456');
|
|
237
|
+
expect(wrapper.text()).toContain('New District');
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe('Accessibility', () => {
|
|
242
|
+
beforeEach(() => {
|
|
243
|
+
const appointment = createMockAppointment();
|
|
244
|
+
wrapper = mount(Details, {
|
|
245
|
+
props: { appointment },
|
|
246
|
+
global: {
|
|
247
|
+
stubs: {
|
|
248
|
+
Icon: {
|
|
249
|
+
template: '<div class="icon-stub" :aria-hidden="true" :name="name" :size="size" role="img"></div>',
|
|
250
|
+
props: ['name', 'size']
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should have semantic structure for dentist information', () => {
|
|
258
|
+
const dentistSection = wrapper.find('.dentist-section');
|
|
259
|
+
expect(dentistSection.exists()).toBe(true);
|
|
260
|
+
|
|
261
|
+
const dentistList = wrapper.find('.dentist-list');
|
|
262
|
+
expect(dentistList.exists()).toBe(true);
|
|
263
|
+
|
|
264
|
+
const dentistInfo = wrapper.find('.dentist-info');
|
|
265
|
+
expect(dentistInfo.exists()).toBe(true);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should provide alt text for dentist images', () => {
|
|
269
|
+
const dentistImage = wrapper.find('.dentist-image');
|
|
270
|
+
if (dentistImage.exists()) {
|
|
271
|
+
expect(dentistImage.attributes('alt')).toBe('Dentist photo');
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should use appropriate text hierarchy', () => {
|
|
276
|
+
const smallTexts = wrapper.findAll('.p-small');
|
|
277
|
+
expect(smallTexts.length).toBeGreaterThan(0);
|
|
278
|
+
|
|
279
|
+
// Check that dentist name is properly structured
|
|
280
|
+
const dentistInfo = wrapper.find('.dentist-info');
|
|
281
|
+
if (dentistInfo.exists()) {
|
|
282
|
+
const nameElement = dentistInfo.find('.p-small');
|
|
283
|
+
expect(nameElement.exists()).toBe(true);
|
|
284
|
+
expect(nameElement.text()).toBe('Dr. Test Dentist');
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should have proper icon accessibility attributes', () => {
|
|
289
|
+
const icons = wrapper.findAll('.icon-stub');
|
|
290
|
+
|
|
291
|
+
icons.forEach(icon => {
|
|
292
|
+
expect(icon.attributes('aria-hidden')).toBe('true');
|
|
293
|
+
expect(icon.attributes('role')).toBe('img');
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should provide semantic meaning for location information', () => {
|
|
298
|
+
const locationSection = wrapper.find('.location-section');
|
|
299
|
+
expect(locationSection.exists()).toBe(true);
|
|
300
|
+
|
|
301
|
+
// Location should have descriptive text
|
|
302
|
+
expect(wrapper.text()).toContain('Test Address 123');
|
|
303
|
+
expect(wrapper.text()).toContain('Test District');
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('should use meaningful text for missing information', async () => {
|
|
307
|
+
const appointmentWithoutLocation = createMockAppointment({
|
|
308
|
+
address: undefined,
|
|
309
|
+
district: undefined
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
await wrapper.setProps({ appointment: appointmentWithoutLocation });
|
|
313
|
+
|
|
314
|
+
expect(wrapper.text()).toContain('Unbekannter Ort');
|
|
315
|
+
expect(wrapper.text()).toContain('Unbekannter Stadtteil');
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should handle multiple dentists accessibly', async () => {
|
|
319
|
+
const multipleDentists = [
|
|
320
|
+
createMockDentist({ name: 'Dr. First Dentist', gender: 'Female' }),
|
|
321
|
+
createMockDentist({ name: 'Dr. Second Dentist', gender: 'Male' })
|
|
322
|
+
];
|
|
323
|
+
const appointmentWithMultipleDentists = createMockAppointment({
|
|
324
|
+
dentist: multipleDentists as any
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
await wrapper.setProps({ appointment: appointmentWithMultipleDentists });
|
|
328
|
+
|
|
329
|
+
const dentistItems = wrapper.findAll('.dentist-item');
|
|
330
|
+
expect(dentistItems).toHaveLength(2);
|
|
331
|
+
|
|
332
|
+
// Each dentist should have proper structure
|
|
333
|
+
dentistItems.forEach((item, index) => {
|
|
334
|
+
const dentistInfo = item.find('.dentist-info');
|
|
335
|
+
expect(dentistInfo.exists()).toBe(true);
|
|
336
|
+
|
|
337
|
+
const nameElements = dentistInfo.findAll('.p-small');
|
|
338
|
+
expect(nameElements.length).toBeGreaterThanOrEqual(1);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should have proper contrast and text styling classes', () => {
|
|
343
|
+
// Check for dental blue styling class which should provide proper contrast
|
|
344
|
+
const blueTexts = wrapper.findAll('.text-dental-blue-3');
|
|
345
|
+
expect(blueTexts.length).toBeGreaterThan(0);
|
|
346
|
+
|
|
347
|
+
blueTexts.forEach(text => {
|
|
348
|
+
expect(text.classes()).toContain('text-dental-blue-3');
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('should provide screen reader friendly information structure', () => {
|
|
353
|
+
// Information should be in logical reading order
|
|
354
|
+
const dentistSection = wrapper.find('.dentist-section');
|
|
355
|
+
const locationSection = wrapper.find('.location-section');
|
|
356
|
+
|
|
357
|
+
expect(dentistSection.exists()).toBe(true);
|
|
358
|
+
expect(locationSection.exists()).toBe(true);
|
|
359
|
+
|
|
360
|
+
// Both sections should contain meaningful content
|
|
361
|
+
expect(dentistSection.text().trim()).toBeTruthy();
|
|
362
|
+
expect(locationSection.text().trim()).toBeTruthy();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should use semantic HTML structure', () => {
|
|
366
|
+
const detailsGrid = wrapper.find('.details-grid');
|
|
367
|
+
expect(detailsGrid.exists()).toBe(true);
|
|
368
|
+
|
|
369
|
+
// Should have logical sectioning
|
|
370
|
+
expect(wrapper.find('.dentist-section').exists()).toBe(true);
|
|
371
|
+
expect(wrapper.find('.location-section').exists()).toBe(true);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should handle missing dentist data gracefully for accessibility', async () => {
|
|
375
|
+
const appointmentWithoutDentist = createMockAppointment({ dentist: undefined as any });
|
|
376
|
+
await wrapper.setProps({ appointment: appointmentWithoutDentist });
|
|
377
|
+
|
|
378
|
+
// Component should not render when no dentist data
|
|
379
|
+
expect(wrapper.find('.details-grid').exists()).toBe(false);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('should provide accessible image handling', async () => {
|
|
383
|
+
const dentistWithImage = createMockDentist({
|
|
384
|
+
imageSrc: 'https://example.com/dentist.jpg',
|
|
385
|
+
name: 'Dr. Image Test'
|
|
386
|
+
});
|
|
387
|
+
const appointmentWithImage = createMockAppointment({ dentist: dentistWithImage });
|
|
388
|
+
|
|
389
|
+
await wrapper.setProps({ appointment: appointmentWithImage });
|
|
390
|
+
|
|
391
|
+
const image = wrapper.find('.dentist-image');
|
|
392
|
+
if (image.exists()) {
|
|
393
|
+
expect(image.attributes('alt')).toBe('Dentist photo');
|
|
394
|
+
expect(image.attributes('src')).toBe('https://example.com/dentist.jpg');
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
398
|
});
|