@zap-wunschlachen/wl-shared-components 1.0.0
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 +215 -0
- package/.github/workflows/static.yml +62 -0
- package/.prettierrc +5 -0
- package/.storybook/main.ts +18 -0
- package/.storybook/preview.ts +37 -0
- package/.storybook/storyWrapper.vue +18 -0
- package/.storybook/withVuetifyTheme.decorator.ts +21 -0
- package/App.vue +95 -0
- package/README.md +56 -0
- package/heroicons.ts +75 -0
- package/index.html +19 -0
- package/package.json +66 -0
- package/playwright.config.ts +35 -0
- package/public/audio/dummy_pink_noise.wav +0 -0
- package/public/background.svg +60 -0
- package/public/javascript.svg +1 -0
- package/public/style.css +187 -0
- package/public/technologies.svg +22 -0
- package/src/assets/css/base.css +235 -0
- package/src/assets/css/variables.css +96 -0
- package/src/assets/fonts/Outfit-Black.ttf +0 -0
- package/src/assets/fonts/Outfit-Bold.ttf +0 -0
- package/src/assets/fonts/Outfit-ExtraBold.ttf +0 -0
- package/src/assets/fonts/Outfit-ExtraLight.ttf +0 -0
- package/src/assets/fonts/Outfit-Light.ttf +0 -0
- package/src/assets/fonts/Outfit-Medium.ttf +0 -0
- package/src/assets/fonts/Outfit-Regular.ttf +0 -0
- package/src/assets/fonts/Outfit-SemiBold.ttf +0 -0
- package/src/assets/fonts/Outfit-Thin.ttf +0 -0
- package/src/components/Accordion/Accordion.css +59 -0
- package/src/components/Accordion/AccordionGroup.vue +51 -0
- package/src/components/Accordion/AccordionItem.vue +66 -0
- package/src/components/Appointment/Card/Actions.css +30 -0
- package/src/components/Appointment/Card/Actions.vue +66 -0
- package/src/components/Appointment/Card/Card.css +49 -0
- package/src/components/Appointment/Card/Card.vue +55 -0
- package/src/components/Appointment/Card/Details.css +51 -0
- package/src/components/Appointment/Card/Details.vue +44 -0
- package/src/components/Audio/Audio.vue +188 -0
- package/src/components/Audio/Waveform.vue +118 -0
- package/src/components/Button/Button.vue +119 -0
- package/src/components/CheckBox/CheckBox.css +185 -0
- package/src/components/CheckBox/Checkbox.vue +130 -0
- package/src/components/DateInput/DateInput.css +3 -0
- package/src/components/DateInput/DateInput.vue +263 -0
- package/src/components/Dialog/Dialog.css +6 -0
- package/src/components/Dialog/Dialog.vue +29 -0
- package/src/components/EditField/EditField.css +20 -0
- package/src/components/EditField/EditField.vue +202 -0
- package/src/components/IconBullet/IconBullet.vue +86 -0
- package/src/components/IconBullet/IconBulletList.vue +41 -0
- package/src/components/Icons/Audio/CloudFailed.vue +21 -0
- package/src/components/Icons/Audio/CloudSaved.vue +22 -0
- package/src/components/Icons/Audio/Delete.vue +16 -0
- package/src/components/Icons/Audio/Pause.vue +19 -0
- package/src/components/Icons/Audio/Play.vue +16 -0
- package/src/components/Icons/CalendarNotification.vue +126 -0
- package/src/components/Icons/Chair.vue +32 -0
- package/src/components/Icons/ChairNotification.vue +35 -0
- package/src/components/Icons/Circle.vue +66 -0
- package/src/components/Icons/FavIcon.vue +22 -0
- package/src/components/Icons/FilledCircle.vue +11 -0
- package/src/components/Icons/Group3.vue +46 -0
- package/src/components/Icons/RingNotification.vue +54 -0
- package/src/components/Icons/SolidArrowRight.vue +14 -0
- package/src/components/Icons/calendar.vue +17 -0
- package/src/components/Icons/checkbox.vue +19 -0
- package/src/components/Icons/outlineChecked.vue +27 -0
- package/src/components/Icons/play.vue +6 -0
- package/src/components/Input/Input.css +187 -0
- package/src/components/Input/Input.vue +247 -0
- package/src/components/Laboratory/AppointmentCard/AppointmentCard.css +7 -0
- package/src/components/Laboratory/AppointmentCard/AppointmentCard.vue +116 -0
- package/src/components/Laboratory/ChatBoxImage/ChatBoxImage.vue +81 -0
- package/src/components/Laboratory/ChatMessage/ChatMessage.vue +113 -0
- package/src/components/Laboratory/ChatMessage/ChatMessageBadge.css +4 -0
- package/src/components/Laboratory/ChatMessage/ChatMessageBadge.vue +99 -0
- package/src/components/Laboratory/ChatNotification/ChatNotification.vue +130 -0
- package/src/components/Laboratory/DocumentCard/DocumentCard.css +3 -0
- package/src/components/Laboratory/DocumentCard/DocumentCard.vue +50 -0
- package/src/components/Laboratory/DocumentCard/DocumentCardItem.vue +53 -0
- package/src/components/Laboratory/InfoCard/InfoCard.vue +162 -0
- package/src/components/Laboratory/MainColumnsBar/MainColumnsBar.vue +102 -0
- package/src/components/Laboratory/ProgressCircle/ProgressCircle.vue +152 -0
- package/src/components/Laboratory/ProgressLinear/ProgressLinear.css +33 -0
- package/src/components/Laboratory/ProgressLinear/ProgressLinear.vue +75 -0
- package/src/components/Laboratory/SelectionColumnBar/SelectionColumnBar.vue +92 -0
- package/src/components/Laboratory/StatusNotification/StatusNotification.vue +49 -0
- package/src/components/Laboratory/TagLabel/TagLabel.vue +126 -0
- package/src/components/Laboratory/TagLabelGroup/TagLabelGroup.vue +97 -0
- package/src/components/Laboratory/TicketCard/TicketCard.css +3 -0
- package/src/components/Laboratory/TicketCard/TicketCard.vue +143 -0
- package/src/components/Laboratory/TimeLine/TimeLineEvent.css +18 -0
- package/src/components/Laboratory/TimeLine/TimeLineEvent.vue +119 -0
- package/src/components/Laboratory/TimeLine/Timeline.css +4 -0
- package/src/components/Laboratory/TimeLine/Timeline.vue +30 -0
- package/src/components/Modal/Modal.css +6 -0
- package/src/components/Modal/Modal.vue +23 -0
- package/src/components/NotificationBubble/NotificationBubble.css +4 -0
- package/src/components/NotificationBubble/NotificationBubble.vue +90 -0
- package/src/components/OtpInput/OtpInput.css +39 -0
- package/src/components/OtpInput/OtpInput.vue +144 -0
- package/src/components/PhoneInput/PhoneInput.css +32 -0
- package/src/components/PhoneInput/PhoneInput.vue +114 -0
- package/src/components/Select/Select.css +150 -0
- package/src/components/Select/Select.vue +304 -0
- package/src/components/TextArea/TextArea.css +3 -0
- package/src/components/TextArea/TextArea.vue +126 -0
- package/src/components/TickBox/TickBox.css +49 -0
- package/src/components/TickBox/TickBox.vue +126 -0
- package/src/components/index.ts +20 -0
- package/src/constants/buttonEnums.ts +0 -0
- package/src/constants/iconEnums.ts +4 -0
- package/src/i18n/i18n.ts +16 -0
- package/src/i18n/locales/de.json +19 -0
- package/src/i18n/locales/en.json +19 -0
- package/src/index.ts +31 -0
- package/src/main.ts +11 -0
- package/src/plugins/vuetify.ts +131 -0
- package/src/shims-vue.d.ts +10 -0
- package/src/stories/Accordion.stories.ts +650 -0
- package/src/stories/Audio.stories.ts +29 -0
- package/src/stories/Button.stories.ts +263 -0
- package/src/stories/CheckBox.stories.ts +348 -0
- package/src/stories/DateInput.stories.ts +54 -0
- package/src/stories/Dialog.stories.ts +147 -0
- package/src/stories/EditField.stories.ts +79 -0
- package/src/stories/IconBullet/IconBullet.stories.ts +201 -0
- package/src/stories/IconBullet/IconBulletList.stories.ts +275 -0
- package/src/stories/Input.stories.ts +351 -0
- package/src/stories/Laboratory/Cards/AppointmentCard/AppointmentCard.stories.ts +260 -0
- package/src/stories/Laboratory/Cards/DocumentCard/DocumentCard.stories.ts +176 -0
- package/src/stories/Laboratory/Cards/DocumentCard/DocumentCardItem.stories.ts +119 -0
- package/src/stories/Laboratory/Cards/InfoCard/InfoCard.stories.ts +320 -0
- package/src/stories/Laboratory/Cards/TicketCard/TicketCard.stories.ts +335 -0
- package/src/stories/Laboratory/Chat/ChatBoxImage.stories.ts +82 -0
- package/src/stories/Laboratory/Chat/ChatMessage.stories.ts +198 -0
- package/src/stories/Laboratory/Chat/ChatMessageBadge.stories.ts +204 -0
- package/src/stories/Laboratory/Chat/ChatNotification.stories.ts +144 -0
- package/src/stories/Laboratory/Chat/ProgressLinear.stories.ts +186 -0
- package/src/stories/Laboratory/Chat/StatusNotification.stories.ts +111 -0
- package/src/stories/Laboratory/MainColumnsBar.stories.ts +48 -0
- package/src/stories/Laboratory/ProgressCircle.stories.ts +261 -0
- package/src/stories/Laboratory/SelectionColumnBar.stories.ts +234 -0
- package/src/stories/Laboratory/TagLabel.stories.ts +418 -0
- package/src/stories/Laboratory/TagLabelGroup.stories.ts +234 -0
- package/src/stories/Laboratory/Timeline.stories.ts +403 -0
- package/src/stories/NotificationBubble.stories.ts +194 -0
- package/src/stories/OtpInput.stories.ts +101 -0
- package/src/stories/PhoneInput.stories.ts +53 -0
- package/src/stories/Select.stories.ts +419 -0
- package/src/stories/TextArea.stories.ts +112 -0
- package/src/stories/TickBox.stories.ts +294 -0
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +1 -0
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +1 -0
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +1 -0
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +1 -0
- package/src/stories/assets/youtube.svg +1 -0
- package/src/stories/v-icon.stories.ts +91 -0
- package/src/types/index.ts +21 -0
- package/src/vite-env.d.ts +1 -0
- package/tests/e2e/README.md +221 -0
- package/tests/e2e/accessibility.spec.ts +639 -0
- package/tests/e2e/accordion.spec.ts +42 -0
- package/tests/e2e/additional-components.spec.ts +438 -0
- package/tests/e2e/all-components.spec.ts +135 -0
- package/tests/e2e/button-fixed.spec.ts +59 -0
- package/tests/e2e/button.spec.ts +76 -0
- package/tests/e2e/checkbox.spec.ts +50 -0
- package/tests/e2e/date-input.spec.ts +46 -0
- package/tests/e2e/debug.spec.ts +52 -0
- package/tests/e2e/dialog.spec.ts +58 -0
- package/tests/e2e/input.spec.ts +55 -0
- package/tests/e2e/laboratory-components.spec.ts +321 -0
- package/tests/e2e/otp-input.spec.ts +50 -0
- package/tests/e2e/select.spec.ts +52 -0
- package/tests/e2e/storybook-utils.ts +59 -0
- package/tests/e2e/test-basic.spec.ts +34 -0
- package/tests/e2e/visual-regression.spec.ts +351 -0
- package/tests/unit/components/Accordion/AccordionGroup.spec.ts +343 -0
- package/tests/unit/components/Accordion/AccordionItem.spec.ts +384 -0
- package/tests/unit/components/Audio/Audio.spec.ts +404 -0
- package/tests/unit/components/Audio/Waveform.spec.ts +484 -0
- package/tests/unit/components/Core/Button.spec.ts +337 -0
- package/tests/unit/components/Core/Checkbox.spec.ts +545 -0
- package/tests/unit/components/Core/DateInput.spec.ts +691 -0
- package/tests/unit/components/Core/Dialog.spec.ts +486 -0
- package/tests/unit/components/Core/EditField.spec.ts +783 -0
- package/tests/unit/components/Core/Input.spec.ts +513 -0
- package/tests/unit/components/Core/Modal.spec.ts +519 -0
- package/tests/unit/components/Core/NotificationBubble.spec.ts +607 -0
- package/tests/unit/components/Core/OtpInput.spec.ts +709 -0
- package/tests/unit/components/Core/PhoneInput.spec.ts +620 -0
- package/tests/unit/components/Core/Select.spec.ts +713 -0
- package/tests/unit/components/Core/TextArea.spec.ts +566 -0
- package/tests/unit/components/Core/TickBox.spec.ts +780 -0
- package/tests/unit/components/IconBullet/IconBullet.spec.ts +357 -0
- package/tests/unit/components/IconBullet/IconBulletList.spec.ts +372 -0
- package/tests/unit/components/Icons/Audio/CloudFailed.spec.ts +109 -0
- package/tests/unit/components/Icons/Audio/CloudSaved.spec.ts +150 -0
- package/tests/unit/components/Icons/Audio/Delete.spec.ts +159 -0
- package/tests/unit/components/Icons/Audio/Pause.spec.ts +209 -0
- package/tests/unit/components/Icons/Audio/Play.spec.ts +218 -0
- package/tests/unit/components/Icons/CalendarNotification.spec.ts +187 -0
- package/tests/unit/components/Icons/Chair.spec.ts +235 -0
- package/tests/unit/components/Icons/ChairNotification.spec.ts +312 -0
- package/tests/unit/components/Icons/Circle.spec.ts +256 -0
- package/tests/unit/components/Icons/FavIcon.spec.ts +252 -0
- package/tests/unit/components/Icons/FilledCircle.spec.ts +275 -0
- package/tests/unit/components/Icons/Group3.spec.ts +356 -0
- package/tests/unit/components/Icons/RingNotification.spec.ts +394 -0
- package/tests/unit/components/Icons/calendar.spec.ts +287 -0
- package/tests/unit/components/Icons/checkbox.spec.ts +316 -0
- package/tests/unit/components/Icons/outlineChecked.spec.ts +435 -0
- package/tests/unit/components/Icons/play.spec.ts +309 -0
- package/tests/unit/components/Laboratory/AppointmentCard.spec.ts +168 -0
- package/tests/unit/components/Laboratory/ChatBoxImage.spec.ts +180 -0
- package/tests/unit/components/Laboratory/ChatMessage.spec.ts +264 -0
- package/tests/unit/components/Laboratory/ChatMessageBadge.spec.ts +283 -0
- package/tests/unit/components/Laboratory/ChatNotification.spec.ts +257 -0
- package/tests/unit/components/Laboratory/DocumentCard.spec.ts +229 -0
- package/tests/unit/components/Laboratory/DocumentCardItem.spec.ts +237 -0
- package/tests/unit/components/Laboratory/InfoCard.spec.ts +309 -0
- package/tests/unit/components/Laboratory/MainColumnsBar.spec.ts +252 -0
- package/tests/unit/components/Laboratory/ProgressCircle.spec.ts +291 -0
- package/tests/unit/components/Laboratory/ProgressLinear.spec.ts +276 -0
- package/tests/unit/components/Laboratory/SelectionColumnBar.spec.ts +289 -0
- package/tests/unit/components/Laboratory/StatusNotification.spec.ts +297 -0
- package/tests/unit/components/Laboratory/TagLabel.spec.ts +354 -0
- package/tests/unit/components/Laboratory/TagLabelGroup.spec.ts +378 -0
- package/tests/unit/components/Laboratory/TicketCard.spec.ts +352 -0
- package/tests/unit/components/Laboratory/TimeLineEvent.spec.ts +382 -0
- package/tests/unit/components/Laboratory/Timeline.spec.ts +420 -0
- package/tests/unit/constants/iconEnums.spec.ts +40 -0
- package/tests/unit/i18n/i18n.spec.ts +89 -0
- package/tests/unit/plugins/vuetify.spec.ts +221 -0
- package/tests/unit/setup.ts +190 -0
- package/tests/unit/src/components/index.spec.ts +193 -0
- package/tests/unit/src/index.spec.ts +183 -0
- package/tests/unit/src/main.spec.ts +152 -0
- package/tsconfig.json +26 -0
- package/vite.config.ts +29 -0
- package/vitest.config.ts +84 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import Play from '@components/Icons/play.vue';
|
|
4
|
+
|
|
5
|
+
describe('Play Icon', () => {
|
|
6
|
+
describe('Rendering', () => {
|
|
7
|
+
it('renders as SVG element', () => {
|
|
8
|
+
const wrapper = mount(Play);
|
|
9
|
+
expect(wrapper.find('svg').exists()).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('has correct SVG attributes', () => {
|
|
13
|
+
const wrapper = mount(Play);
|
|
14
|
+
const svg = wrapper.find('svg');
|
|
15
|
+
|
|
16
|
+
expect(svg.attributes('xmlns')).toBe('http://www.w3.org/2000/svg');
|
|
17
|
+
expect(svg.attributes('width')).toBe('24');
|
|
18
|
+
expect(svg.attributes('height')).toBe('24');
|
|
19
|
+
expect(svg.attributes('viewBox')).toBe('0 0 24 24');
|
|
20
|
+
expect(svg.attributes('fill')).toBe('none');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('contains exactly 2 path elements', () => {
|
|
24
|
+
const wrapper = mount(Play);
|
|
25
|
+
const paths = wrapper.findAll('path');
|
|
26
|
+
expect(paths).toHaveLength(2);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('Path Structure', () => {
|
|
31
|
+
it('first path represents circle background', () => {
|
|
32
|
+
const wrapper = mount(Play);
|
|
33
|
+
const paths = wrapper.findAll('path');
|
|
34
|
+
|
|
35
|
+
const circlePath = paths[0];
|
|
36
|
+
expect(circlePath.attributes('d')).toContain('M21 12C21 16.9706');
|
|
37
|
+
expect(circlePath.attributes('stroke')).toBe('#172774');
|
|
38
|
+
expect(circlePath.attributes('stroke-width')).toBe('1.5');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('second path represents play triangle', () => {
|
|
42
|
+
const wrapper = mount(Play);
|
|
43
|
+
const paths = wrapper.findAll('path');
|
|
44
|
+
|
|
45
|
+
const trianglePath = paths[1];
|
|
46
|
+
expect(trianglePath.attributes('d')).toContain('M15.9099 11.6722');
|
|
47
|
+
expect(trianglePath.attributes('stroke')).toBe('#172774');
|
|
48
|
+
expect(trianglePath.attributes('stroke-width')).toBe('1.5');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('circle path forms complete circle', () => {
|
|
52
|
+
const wrapper = mount(Play);
|
|
53
|
+
const paths = wrapper.findAll('path');
|
|
54
|
+
|
|
55
|
+
const circlePath = paths[0];
|
|
56
|
+
const pathData = circlePath.attributes('d');
|
|
57
|
+
|
|
58
|
+
// Should contain circle definition with center at 12,12 and radius 9
|
|
59
|
+
expect(pathData).toContain('21 12C21 16.9706'); // Right side
|
|
60
|
+
expect(pathData).toContain('16.9706 21 12 21'); // Bottom
|
|
61
|
+
expect(pathData).toContain('7.02944 21 3 16.9706'); // Left side
|
|
62
|
+
expect(pathData).toContain('3 12C3 7.02944'); // Top
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('triangle path forms play arrow', () => {
|
|
66
|
+
const wrapper = mount(Play);
|
|
67
|
+
const paths = wrapper.findAll('path');
|
|
68
|
+
|
|
69
|
+
const trianglePath = paths[1];
|
|
70
|
+
const pathData = trianglePath.attributes('d');
|
|
71
|
+
|
|
72
|
+
// Should contain triangle vertices
|
|
73
|
+
expect(pathData).toContain('15.9099 11.6722'); // Right point
|
|
74
|
+
expect(pathData).toContain('10.3071 15.4405'); // Bottom left
|
|
75
|
+
expect(pathData).toContain('9.75 15.1127'); // Bottom edge
|
|
76
|
+
expect(pathData).toContain('8.88732C9.75 8.60139'); // Top edge
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('Visual Properties', () => {
|
|
81
|
+
it('all strokes use correct brand color', () => {
|
|
82
|
+
const wrapper = mount(Play);
|
|
83
|
+
const paths = wrapper.findAll('path');
|
|
84
|
+
|
|
85
|
+
paths.forEach(path => {
|
|
86
|
+
expect(path.attributes('stroke')).toBe('#172774');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('all paths have correct stroke width', () => {
|
|
91
|
+
const wrapper = mount(Play);
|
|
92
|
+
const paths = wrapper.findAll('path');
|
|
93
|
+
|
|
94
|
+
paths.forEach(path => {
|
|
95
|
+
expect(path.attributes('stroke-width')).toBe('1.5');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('all paths have rounded line caps and joins', () => {
|
|
100
|
+
const wrapper = mount(Play);
|
|
101
|
+
const paths = wrapper.findAll('path');
|
|
102
|
+
|
|
103
|
+
paths.forEach(path => {
|
|
104
|
+
expect(path.attributes('stroke-linecap')).toBe('round');
|
|
105
|
+
expect(path.attributes('stroke-linejoin')).toBe('round');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('paths have no fill (outline style)', () => {
|
|
110
|
+
const wrapper = mount(Play);
|
|
111
|
+
const svg = wrapper.find('svg');
|
|
112
|
+
|
|
113
|
+
expect(svg.attributes('fill')).toBe('none');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('Play Button Geometry', () => {
|
|
118
|
+
it('circle is centered in viewBox', () => {
|
|
119
|
+
const wrapper = mount(Play);
|
|
120
|
+
const paths = wrapper.findAll('path');
|
|
121
|
+
|
|
122
|
+
const circlePath = paths[0];
|
|
123
|
+
const pathData = circlePath.attributes('d');
|
|
124
|
+
|
|
125
|
+
// Circle centered at 12,12 with radius 9
|
|
126
|
+
expect(pathData).toContain('12C21 16.9706'); // Center coordinates
|
|
127
|
+
expect(pathData).toContain('21 12 21C7.02944'); // Center coordinates
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('play triangle is centered horizontally', () => {
|
|
131
|
+
const wrapper = mount(Play);
|
|
132
|
+
const paths = wrapper.findAll('path');
|
|
133
|
+
|
|
134
|
+
const trianglePath = paths[1];
|
|
135
|
+
const pathData = trianglePath.attributes('d');
|
|
136
|
+
|
|
137
|
+
// Triangle should be centered around x=12
|
|
138
|
+
expect(pathData).toContain('15.9099'); // Right point
|
|
139
|
+
expect(pathData).toContain('10.3071'); // Left side
|
|
140
|
+
expect(pathData).toContain('9.75'); // Left edge
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('play triangle points to the right', () => {
|
|
144
|
+
const wrapper = mount(Play);
|
|
145
|
+
const paths = wrapper.findAll('path');
|
|
146
|
+
|
|
147
|
+
const trianglePath = paths[1];
|
|
148
|
+
const pathData = trianglePath.attributes('d');
|
|
149
|
+
|
|
150
|
+
// Right-pointing triangle
|
|
151
|
+
expect(pathData).toMatch(/15\.9099.*11\.6722/); // Right point
|
|
152
|
+
expect(pathData).toMatch(/10\.3071.*15\.4405/); // Bottom left
|
|
153
|
+
expect(pathData).toMatch(/8\.88732/); // Top left
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('maintains proper play button proportions', () => {
|
|
157
|
+
const wrapper = mount(Play);
|
|
158
|
+
const svg = wrapper.find('svg');
|
|
159
|
+
|
|
160
|
+
// Standard play button is square
|
|
161
|
+
expect(svg.attributes('width')).toBe(svg.attributes('height'));
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('Component Structure', () => {
|
|
166
|
+
it('uses standard 24x24 size', () => {
|
|
167
|
+
const wrapper = mount(Play);
|
|
168
|
+
const svg = wrapper.find('svg');
|
|
169
|
+
|
|
170
|
+
expect(svg.attributes('width')).toBe('24');
|
|
171
|
+
expect(svg.attributes('height')).toBe('24');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('suitable for media control usage', () => {
|
|
175
|
+
const wrapper = mount(Play);
|
|
176
|
+
const paths = wrapper.findAll('path');
|
|
177
|
+
|
|
178
|
+
// Circle + triangle is standard play button design
|
|
179
|
+
expect(paths).toHaveLength(2);
|
|
180
|
+
expect(paths[0].attributes('d')).toContain('12C21'); // Circle
|
|
181
|
+
expect(paths[1].attributes('d')).toContain('15.9099'); // Triangle
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe('Accessibility', () => {
|
|
186
|
+
it('svg is properly structured for screen readers', () => {
|
|
187
|
+
const wrapper = mount(Play);
|
|
188
|
+
const svg = wrapper.find('svg');
|
|
189
|
+
|
|
190
|
+
expect(svg.element.tagName.toLowerCase()).toBe('svg');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('uses clear outline style for visibility', () => {
|
|
194
|
+
const wrapper = mount(Play);
|
|
195
|
+
const paths = wrapper.findAll('path');
|
|
196
|
+
|
|
197
|
+
// Outline style with good contrast
|
|
198
|
+
paths.forEach(path => {
|
|
199
|
+
expect(path.attributes('stroke')).toBe('#172774');
|
|
200
|
+
expect(path.attributes('stroke-width')).toBe('1.5');
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('Component Integration', () => {
|
|
206
|
+
it('can be mounted without props', () => {
|
|
207
|
+
expect(() => {
|
|
208
|
+
mount(Play);
|
|
209
|
+
}).not.toThrow();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('renders consistently on multiple mounts', () => {
|
|
213
|
+
const wrapper1 = mount(Play);
|
|
214
|
+
const wrapper2 = mount(Play);
|
|
215
|
+
|
|
216
|
+
expect(wrapper1.html()).toBe(wrapper2.html());
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('maintains play button structure integrity', () => {
|
|
220
|
+
const wrapper = mount(Play);
|
|
221
|
+
const paths = wrapper.findAll('path');
|
|
222
|
+
|
|
223
|
+
expect(paths).toHaveLength(2);
|
|
224
|
+
paths.forEach(path => {
|
|
225
|
+
expect(path.attributes('d')).toBeTruthy();
|
|
226
|
+
expect(path.attributes('stroke')).toBeTruthy();
|
|
227
|
+
expect(path.attributes('stroke-width')).toBeTruthy();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('suitable for media player controls', () => {
|
|
232
|
+
const wrapper = mount(Play);
|
|
233
|
+
|
|
234
|
+
// Standard media control size and appearance
|
|
235
|
+
expect(wrapper.find('svg').attributes('width')).toBe('24');
|
|
236
|
+
expect(wrapper.find('svg').attributes('height')).toBe('24');
|
|
237
|
+
expect(wrapper.findAll('path')).toHaveLength(2);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe('Icon Semantics', () => {
|
|
242
|
+
it('represents play action with universal design', () => {
|
|
243
|
+
const wrapper = mount(Play);
|
|
244
|
+
const paths = wrapper.findAll('path');
|
|
245
|
+
|
|
246
|
+
// Universal play symbol: circle + right-pointing triangle
|
|
247
|
+
const circlePath = paths[0].attributes('d');
|
|
248
|
+
const trianglePath = paths[1].attributes('d');
|
|
249
|
+
|
|
250
|
+
expect(circlePath).toMatch(/C21 16\.9706.*C3 7\.02944/); // Circle
|
|
251
|
+
expect(trianglePath).toMatch(/15\.9099.*11\.6722/); // Right-pointing
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('uses appropriate size for media controls', () => {
|
|
255
|
+
const wrapper = mount(Play);
|
|
256
|
+
const svg = wrapper.find('svg');
|
|
257
|
+
|
|
258
|
+
// 24px is standard for media control buttons
|
|
259
|
+
expect(svg.attributes('width')).toBe('24');
|
|
260
|
+
expect(svg.attributes('height')).toBe('24');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('provides clear visual affordance', () => {
|
|
264
|
+
const wrapper = mount(Play);
|
|
265
|
+
const paths = wrapper.findAll('path');
|
|
266
|
+
|
|
267
|
+
// Clear outline with good stroke weight
|
|
268
|
+
paths.forEach(path => {
|
|
269
|
+
expect(path.attributes('stroke')).toBeTruthy();
|
|
270
|
+
expect(parseFloat(path.attributes('stroke-width'))).toBeGreaterThan(1);
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe('Visual Design', () => {
|
|
276
|
+
it('provides recognizable play button appearance', () => {
|
|
277
|
+
const wrapper = mount(Play);
|
|
278
|
+
const paths = wrapper.findAll('path');
|
|
279
|
+
|
|
280
|
+
// Standard play button elements
|
|
281
|
+
expect(paths[0].attributes('d')).toContain('21 12C21'); // Circular boundary
|
|
282
|
+
expect(paths[1].attributes('d')).toContain('15.9099'); // Triangle pointing right
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('uses consistent stroke styling', () => {
|
|
286
|
+
const wrapper = mount(Play);
|
|
287
|
+
const paths = wrapper.findAll('path');
|
|
288
|
+
|
|
289
|
+
paths.forEach(path => {
|
|
290
|
+
expect(path.attributes('stroke')).toBe('#172774');
|
|
291
|
+
expect(path.attributes('stroke-width')).toBe('1.5');
|
|
292
|
+
expect(path.attributes('stroke-linecap')).toBe('round');
|
|
293
|
+
expect(path.attributes('stroke-linejoin')).toBe('round');
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('balances circle and triangle proportions', () => {
|
|
298
|
+
const wrapper = mount(Play);
|
|
299
|
+
const paths = wrapper.findAll('path');
|
|
300
|
+
|
|
301
|
+
const circlePath = paths[0].attributes('d');
|
|
302
|
+
const trianglePath = paths[1].attributes('d');
|
|
303
|
+
|
|
304
|
+
// Triangle should be proportional to circle
|
|
305
|
+
expect(circlePath).toContain('21 12'); // Circle radius ~9
|
|
306
|
+
expect(trianglePath).toMatch(/15\.9099.*9\.75/); // Triangle width ~6
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { mount, VueWrapper } from '@vue/test-utils';
|
|
3
|
+
import AppointmentCard from '../../../../src/components/Laboratory/AppointmentCard/AppointmentCard.vue';
|
|
4
|
+
|
|
5
|
+
describe('AppointmentCard', () => {
|
|
6
|
+
let wrapper: VueWrapper;
|
|
7
|
+
|
|
8
|
+
const defaultProps = {
|
|
9
|
+
date: '2024-01-15',
|
|
10
|
+
status: 'Done',
|
|
11
|
+
treatmentName: 'Root Canal',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
wrapper = mount(AppointmentCard, {
|
|
16
|
+
props: defaultProps,
|
|
17
|
+
global: {
|
|
18
|
+
stubs: {
|
|
19
|
+
'TagLabel': {
|
|
20
|
+
template: '<div data-testid="tag-label">{{ status }}</div>',
|
|
21
|
+
props: ['status']
|
|
22
|
+
},
|
|
23
|
+
'v-btn': {
|
|
24
|
+
template: '<button class="v-btn" :readonly="readonly" :variant="variant" :prepend-icon="prependIcon" :size="size" :style="$attrs.style"><slot /></button>',
|
|
25
|
+
props: ['readonly', 'variant', 'prependIcon', 'size', 'rounded', 'color', 'appendIcon']
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('Component Rendering', () => {
|
|
33
|
+
it('should render the component', () => {
|
|
34
|
+
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
35
|
+
expect(wrapper.find('.appointment-card').exists()).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should display the date', () => {
|
|
39
|
+
expect(wrapper.text()).toContain('2024-01-15');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should display the treatment name', () => {
|
|
43
|
+
expect(wrapper.text()).toContain('Root Canal');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should display status label', () => {
|
|
47
|
+
expect(wrapper.text()).toContain('Status:');
|
|
48
|
+
const tagLabel = wrapper.find('[data-testid="tag-label"]');
|
|
49
|
+
expect(tagLabel.exists()).toBe(true);
|
|
50
|
+
expect(tagLabel.text()).toBe('Done');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should display the default dentist name', () => {
|
|
54
|
+
expect(wrapper.text()).toContain('Martin Paetz');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should render two buttons', () => {
|
|
58
|
+
const buttons = wrapper.findAll('.v-btn');
|
|
59
|
+
expect(buttons).toHaveLength(2);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('Props', () => {
|
|
64
|
+
it('should accept custom dentist name', async () => {
|
|
65
|
+
await wrapper.setProps({ dentistName: 'Dr. Smith' });
|
|
66
|
+
expect(wrapper.text()).toContain('Dr. Smith');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should accept custom button text color', async () => {
|
|
70
|
+
await wrapper.setProps({ buttonTextColor: '--custom-color' });
|
|
71
|
+
const button = wrapper.find('.v-btn');
|
|
72
|
+
expect(button.element.style.color).toContain('var(--custom-color)');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should accept custom button background color', async () => {
|
|
76
|
+
await wrapper.setProps({ buttonBackgroundColor: 'rgba(255, 0, 0, 0.5)' });
|
|
77
|
+
const button = wrapper.find('.v-btn');
|
|
78
|
+
expect(button.element.style.backgroundColor).toBe('rgba(255, 0, 0, 0.5)');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should accept custom button border color', async () => {
|
|
82
|
+
await wrapper.setProps({ buttonBorderColor: '--border-color' });
|
|
83
|
+
const button = wrapper.find('.v-btn');
|
|
84
|
+
const style = button.attributes('style');
|
|
85
|
+
expect(style).toContain('border:');
|
|
86
|
+
expect(style).toContain('--border-color');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should update when date prop changes', async () => {
|
|
90
|
+
await wrapper.setProps({ date: '2024-12-25' });
|
|
91
|
+
expect(wrapper.text()).toContain('2024-12-25');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should update when status prop changes', async () => {
|
|
95
|
+
await wrapper.setProps({ status: 'In Progress' });
|
|
96
|
+
const tagLabel = wrapper.find('[data-testid="tag-label"]');
|
|
97
|
+
expect(tagLabel.text()).toBe('In Progress');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should update when treatment name changes', async () => {
|
|
101
|
+
await wrapper.setProps({ treatmentName: 'Crown Installation' });
|
|
102
|
+
expect(wrapper.text()).toContain('Crown Installation');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('Button Properties', () => {
|
|
107
|
+
it('should have readonly treatment button', () => {
|
|
108
|
+
const treatmentButton = wrapper.findAll('.v-btn')[0];
|
|
109
|
+
expect(treatmentButton.attributes('readonly')).toBe('');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should have readonly dentist button', () => {
|
|
113
|
+
const dentistButton = wrapper.findAll('.v-btn')[1];
|
|
114
|
+
expect(dentistButton.attributes('readonly')).toBe('');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should have correct icons on buttons', () => {
|
|
118
|
+
const buttons = wrapper.findAll('.v-btn');
|
|
119
|
+
expect(buttons[0].attributes('prepend-icon')).toBe('heroicons:document-check');
|
|
120
|
+
expect(buttons[1].attributes('prepend-icon')).toBe('heroicons:user-circle');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should have correct button sizes', () => {
|
|
124
|
+
const buttons = wrapper.findAll('.v-btn');
|
|
125
|
+
buttons.forEach(button => {
|
|
126
|
+
expect(button.attributes('size')).toBe('compact');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('Styling', () => {
|
|
132
|
+
it('should apply correct card styling', () => {
|
|
133
|
+
const card = wrapper.find('.v-card');
|
|
134
|
+
expect(card.attributes('elevation')).toBe('0');
|
|
135
|
+
expect(card.attributes('rounded')).toBe('lg');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should have correct button variants', () => {
|
|
139
|
+
const buttons = wrapper.findAll('.v-btn');
|
|
140
|
+
expect(buttons[0].attributes('variant')).toBe('outlined');
|
|
141
|
+
expect(buttons[1].attributes('variant')).toBe('text');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should apply text-none class to buttons', () => {
|
|
145
|
+
const buttons = wrapper.findAll('.v-btn');
|
|
146
|
+
buttons.forEach(button => {
|
|
147
|
+
expect(button.classes()).toContain('text-none');
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('Layout', () => {
|
|
153
|
+
it('should have flex layout for title', () => {
|
|
154
|
+
const titleDiv = wrapper.find('.d-flex.flex-row.align-center');
|
|
155
|
+
expect(titleDiv.exists()).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should have flex column layout for content', () => {
|
|
159
|
+
const contentDiv = wrapper.find('.d-flex.flex-column.ga-4');
|
|
160
|
+
expect(contentDiv.exists()).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should have mr-auto class for proper spacing', () => {
|
|
164
|
+
const elements = wrapper.findAll('.mr-auto');
|
|
165
|
+
expect(elements.length).toBeGreaterThan(0);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { mount, VueWrapper } from '@vue/test-utils';
|
|
3
|
+
import ChatBoxImage from '../../../../src/components/Laboratory/ChatBoxImage/ChatBoxImage.vue';
|
|
4
|
+
|
|
5
|
+
describe('ChatBoxImage', () => {
|
|
6
|
+
let wrapper: VueWrapper;
|
|
7
|
+
|
|
8
|
+
const defaultProps = {
|
|
9
|
+
paragraphText: 'Test paragraph content',
|
|
10
|
+
imageSources: [
|
|
11
|
+
'https://example.com/image1.jpg',
|
|
12
|
+
'https://example.com/image2.jpg'
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
wrapper = mount(ChatBoxImage, {
|
|
18
|
+
props: defaultProps,
|
|
19
|
+
global: {
|
|
20
|
+
stubs: {
|
|
21
|
+
'Button': {
|
|
22
|
+
template: '<button :style="$attrs.style" data-testid="button" @click="$emit(\'click\')">{{ label }}</button>',
|
|
23
|
+
props: ['label', 'density', 'size', 'variant']
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('Component Rendering', () => {
|
|
31
|
+
it('should render the component', () => {
|
|
32
|
+
expect(wrapper.find('.chat-box-image').exists()).toBe(true);
|
|
33
|
+
expect(wrapper.find('.v-card').exists()).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should display the paragraph text', () => {
|
|
37
|
+
expect(wrapper.text()).toContain('Test paragraph content');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should render images from imageSources prop', () => {
|
|
41
|
+
const images = wrapper.findAll('.v-img');
|
|
42
|
+
expect(images).toHaveLength(2);
|
|
43
|
+
expect(images[0].attributes('src')).toBe('https://example.com/image1.jpg');
|
|
44
|
+
expect(images[1].attributes('src')).toBe('https://example.com/image2.jpg');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should render two action buttons', () => {
|
|
48
|
+
const buttons = wrapper.findAll('[data-testid="button"]');
|
|
49
|
+
expect(buttons).toHaveLength(2);
|
|
50
|
+
expect(buttons[0].text()).toBe('Approved');
|
|
51
|
+
expect(buttons[1].text()).toBe('Not Approved');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('Props', () => {
|
|
56
|
+
it('should use default paragraph text when not provided', async () => {
|
|
57
|
+
await wrapper.setProps({ paragraphText: undefined });
|
|
58
|
+
expect(wrapper.text()).toContain('Lorem ipsum dolor sit amet, consetetur sadipscing elitr...');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should use default image sources when not provided', async () => {
|
|
62
|
+
await wrapper.setProps({ imageSources: undefined });
|
|
63
|
+
const images = wrapper.findAll('.v-img');
|
|
64
|
+
expect(images).toHaveLength(2);
|
|
65
|
+
expect(images[0].attributes('src')).toBe('https://cdn.vuetifyjs.com/images/parallax/material.jpg');
|
|
66
|
+
expect(images[1].attributes('src')).toBe('https://cdn.vuetifyjs.com/images/parallax/material.jpg');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should accept custom paragraph text', async () => {
|
|
70
|
+
const customText = 'Custom paragraph content for testing';
|
|
71
|
+
await wrapper.setProps({ paragraphText: customText });
|
|
72
|
+
expect(wrapper.text()).toContain(customText);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should accept custom image sources array', async () => {
|
|
76
|
+
const customImages = [
|
|
77
|
+
'https://custom.com/image1.jpg',
|
|
78
|
+
'https://custom.com/image2.jpg',
|
|
79
|
+
'https://custom.com/image3.jpg'
|
|
80
|
+
];
|
|
81
|
+
await wrapper.setProps({ imageSources: customImages });
|
|
82
|
+
|
|
83
|
+
const images = wrapper.findAll('.v-img');
|
|
84
|
+
expect(images).toHaveLength(3);
|
|
85
|
+
expect(images[0].attributes('src')).toBe('https://custom.com/image1.jpg');
|
|
86
|
+
expect(images[1].attributes('src')).toBe('https://custom.com/image2.jpg');
|
|
87
|
+
expect(images[2].attributes('src')).toBe('https://custom.com/image3.jpg');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should handle empty image sources array', async () => {
|
|
91
|
+
await wrapper.setProps({ imageSources: [] });
|
|
92
|
+
const images = wrapper.findAll('.v-img');
|
|
93
|
+
expect(images).toHaveLength(0);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('Events', () => {
|
|
98
|
+
it('should emit click:approved when Approved button is clicked', async () => {
|
|
99
|
+
const buttons = wrapper.findAll('[data-testid="button"]');
|
|
100
|
+
const approvedButton = buttons[0];
|
|
101
|
+
|
|
102
|
+
await approvedButton.trigger('click');
|
|
103
|
+
expect(wrapper.emitted('click:approved')).toBeTruthy();
|
|
104
|
+
// Due to event bubbling in the test environment, each click triggers twice
|
|
105
|
+
expect(wrapper.emitted('click:approved')).toHaveLength(2);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should emit click:not-approved when Not Approved button is clicked', async () => {
|
|
109
|
+
const buttons = wrapper.findAll('[data-testid="button"]');
|
|
110
|
+
const notApprovedButton = buttons[1];
|
|
111
|
+
|
|
112
|
+
await notApprovedButton.trigger('click');
|
|
113
|
+
expect(wrapper.emitted('click:not-approved')).toBeTruthy();
|
|
114
|
+
// Due to event bubbling in the test environment, each click triggers twice
|
|
115
|
+
expect(wrapper.emitted('click:not-approved')).toHaveLength(2);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('Button Properties', () => {
|
|
120
|
+
it('should have correct button styles', () => {
|
|
121
|
+
const buttons = wrapper.findAll('[data-testid="button"]');
|
|
122
|
+
const approvedButton = buttons[0];
|
|
123
|
+
const notApprovedButton = buttons[1];
|
|
124
|
+
|
|
125
|
+
expect(approvedButton.exists()).toBe(true);
|
|
126
|
+
expect(notApprovedButton.exists()).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should have correct button labels', () => {
|
|
130
|
+
const buttons = wrapper.findAll('[data-testid="button"]');
|
|
131
|
+
expect(buttons[0].text()).toBe('Approved');
|
|
132
|
+
expect(buttons[1].text()).toBe('Not Approved');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('Layout and Structure', () => {
|
|
137
|
+
it('should have card with correct elevation and rounded properties', () => {
|
|
138
|
+
const card = wrapper.find('.v-card');
|
|
139
|
+
expect(card.attributes('elevation')).toBe('0');
|
|
140
|
+
expect(card.attributes('rounded')).toBe('lg');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should have images with correct properties', () => {
|
|
144
|
+
const images = wrapper.findAll('.v-img');
|
|
145
|
+
images.forEach(image => {
|
|
146
|
+
expect(image.attributes('aspect-ratio')).toBe('16/9');
|
|
147
|
+
expect(image.attributes('rounded')).toBe('lg');
|
|
148
|
+
expect(image.attributes('cover')).toBeDefined();
|
|
149
|
+
expect(image.classes()).toContain('ma-2');
|
|
150
|
+
expect(image.classes()).toContain('pa-2');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should have flex layout for images', () => {
|
|
155
|
+
const imageContainer = wrapper.find('.d-flex.flex-wrap');
|
|
156
|
+
expect(imageContainer.exists()).toBe(true);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should have centered button layout', () => {
|
|
160
|
+
const buttonContainer = wrapper.find('.d-flex.flex-row.align-center.ga-3.w-100.justify-center');
|
|
161
|
+
expect(buttonContainer.exists()).toBe(true);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('Styling', () => {
|
|
166
|
+
it('should apply custom styling to images', () => {
|
|
167
|
+
const images = wrapper.findAll('.v-img');
|
|
168
|
+
images.forEach(image => {
|
|
169
|
+
// Check for the presence of the image styling classes
|
|
170
|
+
expect(image.classes()).toContain('ma-2');
|
|
171
|
+
expect(image.classes()).toContain('pa-2');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should have padding on the card', () => {
|
|
176
|
+
const card = wrapper.find('.v-card');
|
|
177
|
+
expect(card.classes()).toContain('pa-2');
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|