@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,252 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import FavIcon from '@components/Icons/FavIcon.vue';
|
|
4
|
+
|
|
5
|
+
describe('FavIcon Icon', () => {
|
|
6
|
+
describe('Rendering', () => {
|
|
7
|
+
it('renders as SVG element', () => {
|
|
8
|
+
const wrapper = mount(FavIcon);
|
|
9
|
+
expect(wrapper.find('svg').exists()).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('has correct SVG attributes', () => {
|
|
13
|
+
const wrapper = mount(FavIcon);
|
|
14
|
+
const svg = wrapper.find('svg');
|
|
15
|
+
|
|
16
|
+
expect(svg.attributes('width')).toBe('30');
|
|
17
|
+
expect(svg.attributes('height')).toBe('30');
|
|
18
|
+
expect(svg.attributes('viewBox')).toBe('0 0 30 30');
|
|
19
|
+
expect(svg.attributes('fill')).toBe('none');
|
|
20
|
+
expect(svg.attributes('xmlns')).toBe('http://www.w3.org/2000/svg');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('contains exactly 3 path elements', () => {
|
|
24
|
+
const wrapper = mount(FavIcon);
|
|
25
|
+
const paths = wrapper.findAll('path');
|
|
26
|
+
expect(paths).toHaveLength(3);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('Path Structure', () => {
|
|
31
|
+
it('paths represent favicon shape', () => {
|
|
32
|
+
const wrapper = mount(FavIcon);
|
|
33
|
+
const paths = wrapper.findAll('path');
|
|
34
|
+
|
|
35
|
+
const firstPathData = paths[0].attributes('d');
|
|
36
|
+
expect(firstPathData).toContain('M15 30C23.271');
|
|
37
|
+
expect(firstPathData).toContain('C6.72904 0 0 6.72904');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('paths have correct fill properties', () => {
|
|
41
|
+
const wrapper = mount(FavIcon);
|
|
42
|
+
const paths = wrapper.findAll('path');
|
|
43
|
+
|
|
44
|
+
paths.forEach(path => {
|
|
45
|
+
expect(path.attributes('fill')).toBe('#172774');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('paths form complete favicon shape', () => {
|
|
50
|
+
const wrapper = mount(FavIcon);
|
|
51
|
+
const paths = wrapper.findAll('path');
|
|
52
|
+
|
|
53
|
+
paths.forEach(path => {
|
|
54
|
+
const pathData = path.attributes('d');
|
|
55
|
+
expect(pathData).toBeTruthy();
|
|
56
|
+
expect(pathData.length).toBeGreaterThan(10);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('Visual Properties', () => {
|
|
62
|
+
it('uses correct brand color for fill', () => {
|
|
63
|
+
const wrapper = mount(FavIcon);
|
|
64
|
+
const paths = wrapper.findAll('path');
|
|
65
|
+
|
|
66
|
+
paths.forEach(path => {
|
|
67
|
+
expect(path.attributes('fill')).toBe('#172774');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('svg container has no fill initially', () => {
|
|
72
|
+
const wrapper = mount(FavIcon);
|
|
73
|
+
const svg = wrapper.find('svg');
|
|
74
|
+
|
|
75
|
+
expect(svg.attributes('fill')).toBe('none');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('Favicon Dimensions', () => {
|
|
80
|
+
it('uses standard favicon size', () => {
|
|
81
|
+
const wrapper = mount(FavIcon);
|
|
82
|
+
const svg = wrapper.find('svg');
|
|
83
|
+
|
|
84
|
+
expect(svg.attributes('width')).toBe('30');
|
|
85
|
+
expect(svg.attributes('height')).toBe('30');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('maintains square aspect ratio', () => {
|
|
89
|
+
const wrapper = mount(FavIcon);
|
|
90
|
+
const svg = wrapper.find('svg');
|
|
91
|
+
|
|
92
|
+
const width = svg.attributes('width');
|
|
93
|
+
const height = svg.attributes('height');
|
|
94
|
+
expect(width).toBe(height);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('has correct viewBox for 100x100 canvas', () => {
|
|
98
|
+
const wrapper = mount(FavIcon);
|
|
99
|
+
const svg = wrapper.find('svg');
|
|
100
|
+
|
|
101
|
+
expect(svg.attributes('viewBox')).toBe('0 0 30 30');
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('Path Geometry', () => {
|
|
106
|
+
it('first path starts from correct coordinates', () => {
|
|
107
|
+
const wrapper = mount(FavIcon);
|
|
108
|
+
const paths = wrapper.findAll('path');
|
|
109
|
+
|
|
110
|
+
const firstPathData = paths[0].attributes('d');
|
|
111
|
+
expect(firstPathData).toMatch(/^M15\s+30C/);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('contains multiple coordinate points across paths', () => {
|
|
115
|
+
const wrapper = mount(FavIcon);
|
|
116
|
+
const paths = wrapper.findAll('path');
|
|
117
|
+
|
|
118
|
+
let totalCoordinateMatches = 0;
|
|
119
|
+
paths.forEach(path => {
|
|
120
|
+
const pathData = path.attributes('d');
|
|
121
|
+
const coordinateMatches = pathData.match(/\d+\.?\d*\s+\d+\.?\d*/g) || [];
|
|
122
|
+
totalCoordinateMatches += coordinateMatches.length;
|
|
123
|
+
});
|
|
124
|
+
expect(totalCoordinateMatches).toBeGreaterThan(10);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('uses proper path commands', () => {
|
|
128
|
+
const wrapper = mount(FavIcon);
|
|
129
|
+
const paths = wrapper.findAll('path');
|
|
130
|
+
|
|
131
|
+
paths.forEach(path => {
|
|
132
|
+
const pathData = path.attributes('d');
|
|
133
|
+
// Should contain path commands like M, L, C, Z
|
|
134
|
+
expect(pathData).toMatch(/[MLCZ]/);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('Component Structure', () => {
|
|
140
|
+
it('is suitable for favicon usage', () => {
|
|
141
|
+
const wrapper = mount(FavIcon);
|
|
142
|
+
const svg = wrapper.find('svg');
|
|
143
|
+
|
|
144
|
+
// Should be square and reasonably sized
|
|
145
|
+
expect(svg.attributes('width')).toBe('30');
|
|
146
|
+
expect(svg.attributes('height')).toBe('30');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('has solid fill suitable for small sizes', () => {
|
|
150
|
+
const wrapper = mount(FavIcon);
|
|
151
|
+
const paths = wrapper.findAll('path');
|
|
152
|
+
|
|
153
|
+
// Should have solid fill rather than just stroke for visibility at small sizes
|
|
154
|
+
paths.forEach(path => {
|
|
155
|
+
expect(path.attributes('fill')).toBe('#172774');
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('Accessibility', () => {
|
|
161
|
+
it('svg is properly structured for screen readers', () => {
|
|
162
|
+
const wrapper = mount(FavIcon);
|
|
163
|
+
const svg = wrapper.find('svg');
|
|
164
|
+
|
|
165
|
+
expect(svg.element.tagName.toLowerCase()).toBe('svg');
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('Component Integration', () => {
|
|
170
|
+
it('can be mounted without props', () => {
|
|
171
|
+
expect(() => {
|
|
172
|
+
mount(FavIcon);
|
|
173
|
+
}).not.toThrow();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('renders consistently on multiple mounts', () => {
|
|
177
|
+
const wrapper1 = mount(FavIcon);
|
|
178
|
+
const wrapper2 = mount(FavIcon);
|
|
179
|
+
|
|
180
|
+
expect(wrapper1.html()).toBe(wrapper2.html());
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('maintains favicon integrity', () => {
|
|
184
|
+
const wrapper = mount(FavIcon);
|
|
185
|
+
const paths = wrapper.findAll('path');
|
|
186
|
+
|
|
187
|
+
expect(paths.length).toBe(3);
|
|
188
|
+
paths.forEach(path => {
|
|
189
|
+
expect(path.exists()).toBe(true);
|
|
190
|
+
expect(path.attributes('d')).toBeTruthy();
|
|
191
|
+
expect(path.attributes('fill')).toBeTruthy();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('suitable for use as application favicon', () => {
|
|
196
|
+
const wrapper = mount(FavIcon);
|
|
197
|
+
const svg = wrapper.find('svg');
|
|
198
|
+
|
|
199
|
+
// Standard favicon requirements
|
|
200
|
+
expect(svg.attributes('width')).toBe('30');
|
|
201
|
+
expect(svg.attributes('height')).toBe('30');
|
|
202
|
+
expect(wrapper.findAll('path')[0].attributes('fill')).toBe('#172774');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('Brand Identity', () => {
|
|
207
|
+
it('uses brand color consistently', () => {
|
|
208
|
+
const wrapper = mount(FavIcon);
|
|
209
|
+
const paths = wrapper.findAll('path');
|
|
210
|
+
|
|
211
|
+
const brandColor = '#172774';
|
|
212
|
+
paths.forEach(path => {
|
|
213
|
+
expect(path.attributes('fill')).toBe(brandColor);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('represents identifiable brand mark', () => {
|
|
218
|
+
const wrapper = mount(FavIcon);
|
|
219
|
+
const paths = wrapper.findAll('path');
|
|
220
|
+
|
|
221
|
+
// Should have substantial path data for brand recognition
|
|
222
|
+
paths.forEach(path => {
|
|
223
|
+
const pathData = path.attributes('d');
|
|
224
|
+
expect(pathData).toBeTruthy();
|
|
225
|
+
expect(typeof pathData).toBe('string');
|
|
226
|
+
expect(pathData.length).toBeGreaterThan(20);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe('Icon Semantics', () => {
|
|
232
|
+
it('designed for small size visibility', () => {
|
|
233
|
+
const wrapper = mount(FavIcon);
|
|
234
|
+
const paths = wrapper.findAll('path');
|
|
235
|
+
|
|
236
|
+
// Should use fill rather than complex strokes for small size clarity
|
|
237
|
+
paths.forEach(path => {
|
|
238
|
+
expect(path.attributes('fill')).toBeTruthy();
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('maintains visual clarity at favicon size', () => {
|
|
243
|
+
const wrapper = mount(FavIcon);
|
|
244
|
+
const svg = wrapper.find('svg');
|
|
245
|
+
|
|
246
|
+
// 100x100 is appropriate base size for favicon scaling
|
|
247
|
+
expect(svg.attributes('viewBox')).toBe('0 0 30 30');
|
|
248
|
+
expect(svg.attributes('width')).toBe('30');
|
|
249
|
+
expect(svg.attributes('height')).toBe('30');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import FilledCircle from '@components/Icons/FilledCircle.vue';
|
|
4
|
+
|
|
5
|
+
describe('FilledCircle Icon', () => {
|
|
6
|
+
describe('Rendering', () => {
|
|
7
|
+
it('renders as SVG element', () => {
|
|
8
|
+
const wrapper = mount(FilledCircle);
|
|
9
|
+
expect(wrapper.find('svg').exists()).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('has correct SVG attributes', () => {
|
|
13
|
+
const wrapper = mount(FilledCircle);
|
|
14
|
+
const svg = wrapper.find('svg');
|
|
15
|
+
|
|
16
|
+
expect(svg.attributes('width')).toBe('16');
|
|
17
|
+
expect(svg.attributes('height')).toBe('16');
|
|
18
|
+
expect(svg.attributes('viewBox')).toBe('0 0 16 16');
|
|
19
|
+
expect(svg.attributes('fill')).toBe('none');
|
|
20
|
+
expect(svg.attributes('xmlns')).toBe('http://www.w3.org/2000/svg');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('contains exactly 1 circle element', () => {
|
|
24
|
+
const wrapper = mount(FilledCircle);
|
|
25
|
+
const circles = wrapper.findAll('circle');
|
|
26
|
+
expect(circles).toHaveLength(1);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('Circle Properties', () => {
|
|
31
|
+
it('circle is centered in viewBox', () => {
|
|
32
|
+
const wrapper = mount(FilledCircle);
|
|
33
|
+
const circle = wrapper.find('circle');
|
|
34
|
+
|
|
35
|
+
expect(circle.attributes('cx')).toBe('8');
|
|
36
|
+
expect(circle.attributes('cy')).toBe('8');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('circle fills entire viewBox', () => {
|
|
40
|
+
const wrapper = mount(FilledCircle);
|
|
41
|
+
const circle = wrapper.find('circle');
|
|
42
|
+
|
|
43
|
+
expect(circle.attributes('r')).toBe('8');
|
|
44
|
+
// With radius 8 and center at (8,8), circle spans 0-16 in both dimensions
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('circle has correct fill color', () => {
|
|
48
|
+
const wrapper = mount(FilledCircle);
|
|
49
|
+
const circle = wrapper.find('circle');
|
|
50
|
+
|
|
51
|
+
expect(circle.attributes('fill')).toBe('#C1C1C2');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('circle has no stroke', () => {
|
|
55
|
+
const wrapper = mount(FilledCircle);
|
|
56
|
+
const circle = wrapper.find('circle');
|
|
57
|
+
|
|
58
|
+
expect(circle.attributes('stroke')).toBeUndefined();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('Visual Properties', () => {
|
|
63
|
+
it('uses gray fill color', () => {
|
|
64
|
+
const wrapper = mount(FilledCircle);
|
|
65
|
+
const circle = wrapper.find('circle');
|
|
66
|
+
|
|
67
|
+
expect(circle.attributes('fill')).toBe('#C1C1C2');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('svg container has no default fill', () => {
|
|
71
|
+
const wrapper = mount(FilledCircle);
|
|
72
|
+
const svg = wrapper.find('svg');
|
|
73
|
+
|
|
74
|
+
expect(svg.attributes('fill')).toBe('none');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('creates solid filled appearance', () => {
|
|
78
|
+
const wrapper = mount(FilledCircle);
|
|
79
|
+
const circle = wrapper.find('circle');
|
|
80
|
+
|
|
81
|
+
// Should have fill but no stroke for solid appearance
|
|
82
|
+
expect(circle.attributes('fill')).toBeTruthy();
|
|
83
|
+
expect(circle.attributes('stroke')).toBeUndefined();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('Dimensions', () => {
|
|
88
|
+
it('uses compact 16x16 size', () => {
|
|
89
|
+
const wrapper = mount(FilledCircle);
|
|
90
|
+
const svg = wrapper.find('svg');
|
|
91
|
+
|
|
92
|
+
expect(svg.attributes('width')).toBe('16');
|
|
93
|
+
expect(svg.attributes('height')).toBe('16');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('maintains square aspect ratio', () => {
|
|
97
|
+
const wrapper = mount(FilledCircle);
|
|
98
|
+
const svg = wrapper.find('svg');
|
|
99
|
+
|
|
100
|
+
const width = svg.attributes('width');
|
|
101
|
+
const height = svg.attributes('height');
|
|
102
|
+
expect(width).toBe(height);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('has matching viewBox dimensions', () => {
|
|
106
|
+
const wrapper = mount(FilledCircle);
|
|
107
|
+
const svg = wrapper.find('svg');
|
|
108
|
+
|
|
109
|
+
expect(svg.attributes('viewBox')).toBe('0 0 16 16');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('Circle Geometry', () => {
|
|
114
|
+
it('circle is perfectly centered', () => {
|
|
115
|
+
const wrapper = mount(FilledCircle);
|
|
116
|
+
const circle = wrapper.find('circle');
|
|
117
|
+
|
|
118
|
+
const cx = parseInt(circle.attributes('cx'));
|
|
119
|
+
const cy = parseInt(circle.attributes('cy'));
|
|
120
|
+
const r = parseInt(circle.attributes('r'));
|
|
121
|
+
|
|
122
|
+
// Center should be at half the viewBox dimensions
|
|
123
|
+
expect(cx).toBe(8);
|
|
124
|
+
expect(cy).toBe(8);
|
|
125
|
+
expect(r).toBe(8);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('circle touches all edges of viewBox', () => {
|
|
129
|
+
const wrapper = mount(FilledCircle);
|
|
130
|
+
const circle = wrapper.find('circle');
|
|
131
|
+
|
|
132
|
+
const cx = parseInt(circle.attributes('cx'));
|
|
133
|
+
const cy = parseInt(circle.attributes('cy'));
|
|
134
|
+
const r = parseInt(circle.attributes('r'));
|
|
135
|
+
|
|
136
|
+
// Circle should extend from 0 to 16 in both dimensions
|
|
137
|
+
expect(cx - r).toBe(0); // Left edge
|
|
138
|
+
expect(cx + r).toBe(16); // Right edge
|
|
139
|
+
expect(cy - r).toBe(0); // Top edge
|
|
140
|
+
expect(cy + r).toBe(16); // Bottom edge
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('forms perfect circle', () => {
|
|
144
|
+
const wrapper = mount(FilledCircle);
|
|
145
|
+
const circle = wrapper.find('circle');
|
|
146
|
+
|
|
147
|
+
// All radii should be equal
|
|
148
|
+
const r = circle.attributes('r');
|
|
149
|
+
expect(r).toBe('8');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('Component Structure', () => {
|
|
154
|
+
it('suitable for UI indicators', () => {
|
|
155
|
+
const wrapper = mount(FilledCircle);
|
|
156
|
+
const svg = wrapper.find('svg');
|
|
157
|
+
|
|
158
|
+
// Small size appropriate for bullets, status indicators, etc.
|
|
159
|
+
expect(svg.attributes('width')).toBe('16');
|
|
160
|
+
expect(svg.attributes('height')).toBe('16');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('uses neutral gray color', () => {
|
|
164
|
+
const wrapper = mount(FilledCircle);
|
|
165
|
+
const circle = wrapper.find('circle');
|
|
166
|
+
|
|
167
|
+
// Gray color suggests neutral/inactive state
|
|
168
|
+
expect(circle.attributes('fill')).toBe('#C1C1C2');
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('Accessibility', () => {
|
|
173
|
+
it('svg is properly structured for screen readers', () => {
|
|
174
|
+
const wrapper = mount(FilledCircle);
|
|
175
|
+
const svg = wrapper.find('svg');
|
|
176
|
+
|
|
177
|
+
expect(svg.element.tagName.toLowerCase()).toBe('svg');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('simple structure for clear recognition', () => {
|
|
181
|
+
const wrapper = mount(FilledCircle);
|
|
182
|
+
|
|
183
|
+
// Should have minimal, clear structure
|
|
184
|
+
expect(wrapper.findAll('circle')).toHaveLength(1);
|
|
185
|
+
expect(wrapper.findAll('path')).toHaveLength(0);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('Component Integration', () => {
|
|
190
|
+
it('can be mounted without props', () => {
|
|
191
|
+
expect(() => {
|
|
192
|
+
mount(FilledCircle);
|
|
193
|
+
}).not.toThrow();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('renders consistently on multiple mounts', () => {
|
|
197
|
+
const wrapper1 = mount(FilledCircle);
|
|
198
|
+
const wrapper2 = mount(FilledCircle);
|
|
199
|
+
|
|
200
|
+
expect(wrapper1.html()).toBe(wrapper2.html());
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('maintains circle integrity', () => {
|
|
204
|
+
const wrapper = mount(FilledCircle);
|
|
205
|
+
const circle = wrapper.find('circle');
|
|
206
|
+
|
|
207
|
+
expect(circle.exists()).toBe(true);
|
|
208
|
+
expect(circle.attributes('cx')).toBeTruthy();
|
|
209
|
+
expect(circle.attributes('cy')).toBeTruthy();
|
|
210
|
+
expect(circle.attributes('r')).toBeTruthy();
|
|
211
|
+
expect(circle.attributes('fill')).toBeTruthy();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('suitable for various UI contexts', () => {
|
|
215
|
+
const wrapper = mount(FilledCircle);
|
|
216
|
+
|
|
217
|
+
// Small, neutral, and simple - good for:
|
|
218
|
+
// - List bullets
|
|
219
|
+
// - Status indicators
|
|
220
|
+
// - Form elements
|
|
221
|
+
// - Navigation dots
|
|
222
|
+
expect(wrapper.find('svg').attributes('width')).toBe('16');
|
|
223
|
+
expect(wrapper.find('circle').attributes('fill')).toBe('#C1C1C2');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('Icon Semantics', () => {
|
|
228
|
+
it('represents filled state or bullet point', () => {
|
|
229
|
+
const wrapper = mount(FilledCircle);
|
|
230
|
+
const circle = wrapper.find('circle');
|
|
231
|
+
|
|
232
|
+
// Solid fill suggests "filled" or "active" state
|
|
233
|
+
expect(circle.attributes('fill')).toBe('#C1C1C2');
|
|
234
|
+
expect(circle.attributes('stroke')).toBeUndefined();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('uses appropriate size for inline usage', () => {
|
|
238
|
+
const wrapper = mount(FilledCircle);
|
|
239
|
+
const svg = wrapper.find('svg');
|
|
240
|
+
|
|
241
|
+
// 16px is good size for inline with text or as small indicator
|
|
242
|
+
expect(svg.attributes('width')).toBe('16');
|
|
243
|
+
expect(svg.attributes('height')).toBe('16');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('neutral color suggests inactive or default state', () => {
|
|
247
|
+
const wrapper = mount(FilledCircle);
|
|
248
|
+
const circle = wrapper.find('circle');
|
|
249
|
+
|
|
250
|
+
// Gray color typically indicates neutral/inactive state
|
|
251
|
+
const fillColor = circle.attributes('fill');
|
|
252
|
+
expect(fillColor).toBe('#C1C1C2');
|
|
253
|
+
expect(fillColor.toLowerCase()).toMatch(/^#[c-f]/); // Grayish color range
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('Visual Design', () => {
|
|
258
|
+
it('provides clear visual weight', () => {
|
|
259
|
+
const wrapper = mount(FilledCircle);
|
|
260
|
+
const circle = wrapper.find('circle');
|
|
261
|
+
|
|
262
|
+
// Solid fill provides clear visual presence
|
|
263
|
+
expect(circle.attributes('fill')).toBeTruthy();
|
|
264
|
+
expect(circle.attributes('fill')).not.toBe('none');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('maintains simplicity for scalability', () => {
|
|
268
|
+
const wrapper = mount(FilledCircle);
|
|
269
|
+
|
|
270
|
+
// Simple single-element design scales well
|
|
271
|
+
expect(wrapper.findAll('*')).toHaveLength(2); // svg + circle
|
|
272
|
+
expect(wrapper.find('circle').exists()).toBe(true);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
});
|