@getmicdrop/svelte-components 5.5.4 → 5.5.5
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/dist/calendar/AboutShow/AboutShow.spec.d.ts +2 -0
- package/dist/calendar/AboutShow/AboutShow.spec.d.ts.map +1 -0
- package/dist/calendar/AboutShow/AboutShow.spec.js +791 -0
- package/dist/calendar/Calendar/MiniMonthCalendar.spec.d.ts +2 -0
- package/dist/calendar/Calendar/MiniMonthCalendar.spec.d.ts.map +1 -0
- package/dist/calendar/Calendar/MiniMonthCalendar.spec.js +1191 -0
- package/dist/calendar/FAQs/FAQs.spec.d.ts +2 -0
- package/dist/calendar/FAQs/FAQs.spec.d.ts.map +1 -0
- package/dist/calendar/FAQs/FAQs.spec.js +238 -0
- package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.d.ts +2 -0
- package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.d.ts.map +1 -0
- package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.js +420 -0
- package/dist/calendar/OrderSummary/OrderSummary.spec.d.ts +2 -0
- package/dist/calendar/OrderSummary/OrderSummary.spec.d.ts.map +1 -0
- package/dist/calendar/OrderSummary/OrderSummary.spec.js +808 -0
- package/dist/calendar/PublicCard/PublicCard.spec.d.ts +2 -0
- package/dist/calendar/PublicCard/PublicCard.spec.d.ts.map +1 -0
- package/dist/calendar/PublicCard/PublicCard.spec.js +301 -0
- package/dist/calendar/ShowCard/ShowCard.spec.d.ts +2 -0
- package/dist/calendar/ShowCard/ShowCard.spec.d.ts.map +1 -0
- package/dist/calendar/ShowCard/ShowCard.spec.js +714 -0
- package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.d.ts +2 -0
- package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.d.ts.map +1 -0
- package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.js +241 -0
- package/dist/components/Layout/Section.spec.d.ts +2 -0
- package/dist/components/Layout/Section.spec.d.ts.map +1 -0
- package/dist/components/Layout/Section.spec.js +149 -0
- package/dist/components/Layout/Sidebar.spec.d.ts +2 -0
- package/dist/components/Layout/Sidebar.spec.d.ts.map +1 -0
- package/dist/components/Layout/Sidebar.spec.js +186 -0
- package/dist/components/Layout/Stack.spec.js +3 -3
- package/dist/constants/formOptions.spec.js +9 -5
- package/dist/datetime/__tests__/format.test.js +1 -1
- package/dist/datetime/__tests__/parse.test.js +1 -1
- package/dist/datetime/__tests__/timezone.test.js +124 -2
- package/dist/datetime/parse.js +1 -1
- package/dist/forms/createFieldTracker.spec.d.ts +2 -0
- package/dist/forms/createFieldTracker.spec.d.ts.map +1 -0
- package/dist/forms/createFieldTracker.spec.js +343 -0
- package/dist/forms/createFormStore.spec.d.ts +2 -0
- package/dist/forms/createFormStore.spec.d.ts.map +1 -0
- package/dist/forms/createFormStore.spec.js +689 -0
- package/dist/forms/createFormStore.svelte.js +0 -1
- package/dist/index.d.ts +4 -112
- package/dist/index.js +4 -190
- package/dist/patterns/data/DataGrid.spec.d.ts +2 -0
- package/dist/patterns/data/DataGrid.spec.d.ts.map +1 -0
- package/dist/patterns/data/DataGrid.spec.js +159 -0
- package/dist/patterns/data/DataList.spec.d.ts +2 -0
- package/dist/patterns/data/DataList.spec.d.ts.map +1 -0
- package/dist/patterns/data/DataList.spec.js +158 -0
- package/dist/patterns/data/DataTable.spec.d.ts +2 -0
- package/dist/patterns/data/DataTable.spec.d.ts.map +1 -0
- package/dist/patterns/data/DataTable.spec.js +196 -0
- package/dist/patterns/forms/FormActions.spec.js +10 -3
- package/dist/patterns/forms/FormGrid.spec.d.ts +2 -0
- package/dist/patterns/forms/FormGrid.spec.d.ts.map +1 -0
- package/dist/patterns/forms/FormGrid.spec.js +125 -0
- package/dist/patterns/forms/FormSection.spec.d.ts +2 -0
- package/dist/patterns/forms/FormSection.spec.d.ts.map +1 -0
- package/dist/patterns/forms/FormSection.spec.js +153 -0
- package/dist/patterns/layout/Sidebar.spec.d.ts +2 -0
- package/dist/patterns/layout/Sidebar.spec.d.ts.map +1 -0
- package/dist/patterns/layout/Sidebar.spec.js +159 -0
- package/dist/patterns/navigation/BottomNav.svelte +4 -4
- package/dist/patterns/navigation/Header.spec.js +33 -24
- package/dist/patterns/page/PageHeader.spec.d.ts +2 -0
- package/dist/patterns/page/PageHeader.spec.d.ts.map +1 -0
- package/dist/patterns/page/PageHeader.spec.js +167 -0
- package/dist/patterns/page/PageLayout.spec.d.ts +2 -0
- package/dist/patterns/page/PageLayout.spec.d.ts.map +1 -0
- package/dist/patterns/page/PageLayout.spec.js +145 -0
- package/dist/patterns/page/PageLoader.spec.js +5 -2
- package/dist/patterns/page/SectionHeader.spec.d.ts +2 -0
- package/dist/patterns/page/SectionHeader.spec.d.ts.map +1 -0
- package/dist/patterns/page/SectionHeader.spec.js +197 -0
- package/dist/presets/badges.spec.d.ts +2 -0
- package/dist/presets/badges.spec.d.ts.map +1 -0
- package/dist/presets/badges.spec.js +172 -0
- package/dist/presets/buttons.spec.d.ts +2 -0
- package/dist/presets/buttons.spec.d.ts.map +1 -0
- package/dist/presets/buttons.spec.js +135 -0
- package/dist/primitives/Accordion/Accordion.spec.d.ts +2 -0
- package/dist/primitives/Accordion/Accordion.spec.d.ts.map +1 -0
- package/dist/primitives/Accordion/Accordion.spec.js +83 -0
- package/dist/primitives/Accordion/AccordionItem.spec.d.ts +2 -0
- package/dist/primitives/Accordion/AccordionItem.spec.d.ts.map +1 -0
- package/dist/primitives/Accordion/AccordionItem.spec.js +661 -0
- package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte +107 -0
- package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte.d.ts +35 -0
- package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte.d.ts.map +1 -0
- package/dist/primitives/Alert/Alert.spec.js +5 -2
- package/dist/primitives/Avatar/Avatar.spec.d.ts +2 -0
- package/dist/primitives/Avatar/Avatar.spec.d.ts.map +1 -0
- package/dist/primitives/Avatar/Avatar.spec.js +211 -0
- package/dist/primitives/Badges/Badge.spec.js +109 -68
- package/dist/primitives/BottomSheet/BottomSheet.spec.js +36 -27
- package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte +13 -0
- package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte.d.ts +7 -0
- package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte.d.ts.map +1 -0
- package/dist/primitives/Breadcrumb/Breadcrumb.spec.js +15 -13
- package/dist/primitives/Breadcrumb/Breadcrumb.svelte +5 -5
- package/dist/primitives/Button/Button.spec.js +83 -71
- package/dist/primitives/Button/ButtonSaveDemo.spec.js +100 -2
- package/dist/primitives/Button/ButtonVariantShowcase.spec.d.ts +2 -0
- package/dist/primitives/Button/ButtonVariantShowcase.spec.d.ts.map +1 -0
- package/dist/primitives/Button/ButtonVariantShowcase.spec.js +202 -0
- package/dist/primitives/Card.spec.js +1 -1
- package/dist/primitives/Checkbox/Checkbox.spec.d.ts +2 -0
- package/dist/primitives/Checkbox/Checkbox.spec.d.ts.map +1 -0
- package/dist/primitives/Checkbox/Checkbox.spec.js +252 -0
- package/dist/primitives/DarkModeToggle.spec.js +84 -51
- package/dist/primitives/Drawer/Drawer.spec.d.ts +2 -0
- package/dist/primitives/Drawer/Drawer.spec.d.ts.map +1 -0
- package/dist/primitives/Drawer/Drawer.spec.js +212 -0
- package/dist/primitives/Dropdown/Dropdown.spec.d.ts +2 -0
- package/dist/primitives/Dropdown/Dropdown.spec.d.ts.map +1 -0
- package/dist/primitives/Dropdown/Dropdown.spec.js +366 -0
- package/dist/primitives/Dropdown/DropdownItem.spec.d.ts +2 -0
- package/dist/primitives/Dropdown/DropdownItem.spec.d.ts.map +1 -0
- package/dist/primitives/Dropdown/DropdownItem.spec.js +182 -0
- package/dist/primitives/Icons/iconTestUtils.spec.d.ts +2 -0
- package/dist/primitives/Icons/iconTestUtils.spec.d.ts.map +1 -0
- package/dist/primitives/Icons/iconTestUtils.spec.js +235 -0
- package/dist/primitives/Input/Input.spec.js +14 -14
- package/dist/primitives/Input/Input.svelte +1 -14
- package/dist/primitives/Input/Input.svelte.d.ts.map +1 -1
- package/dist/primitives/Input/Select.spec.js +11 -17
- package/dist/primitives/Input/Textarea.spec.d.ts +2 -0
- package/dist/primitives/Input/Textarea.spec.d.ts.map +1 -0
- package/dist/primitives/Input/Textarea.spec.js +255 -0
- package/dist/primitives/Label/Label.spec.d.ts +2 -0
- package/dist/primitives/Label/Label.spec.d.ts.map +1 -0
- package/dist/primitives/Label/Label.spec.js +157 -0
- package/dist/primitives/Modal/Modal.spec.js +29 -25
- package/dist/primitives/Modal/ModalTestWrapper.svelte +65 -0
- package/dist/primitives/Modal/ModalTestWrapper.svelte.d.ts +23 -0
- package/dist/primitives/Modal/ModalTestWrapper.svelte.d.ts.map +1 -0
- package/dist/primitives/NumberInput/NumberInput.spec.d.ts +2 -0
- package/dist/primitives/NumberInput/NumberInput.spec.d.ts.map +1 -0
- package/dist/primitives/NumberInput/NumberInput.spec.js +235 -0
- package/dist/primitives/Pagination/Pagination.spec.d.ts +2 -0
- package/dist/primitives/Pagination/Pagination.spec.d.ts.map +1 -0
- package/dist/primitives/Pagination/Pagination.spec.js +266 -0
- package/dist/primitives/Radio/Radio.spec.d.ts +2 -0
- package/dist/primitives/Radio/Radio.spec.d.ts.map +1 -0
- package/dist/primitives/Radio/Radio.spec.js +206 -0
- package/dist/primitives/Skeleton/CardPlaceholder.spec.d.ts +2 -0
- package/dist/primitives/Skeleton/CardPlaceholder.spec.d.ts.map +1 -0
- package/dist/primitives/Skeleton/CardPlaceholder.spec.js +156 -0
- package/dist/primitives/Skeleton/ImagePlaceholder.spec.d.ts +2 -0
- package/dist/primitives/Skeleton/ImagePlaceholder.spec.d.ts.map +1 -0
- package/dist/primitives/Skeleton/ImagePlaceholder.spec.js +120 -0
- package/dist/primitives/Skeleton/ListPlaceholder.spec.d.ts +2 -0
- package/dist/primitives/Skeleton/ListPlaceholder.spec.d.ts.map +1 -0
- package/dist/primitives/Skeleton/ListPlaceholder.spec.js +220 -0
- package/dist/primitives/Skeleton/Skeleton.spec.d.ts +2 -0
- package/dist/primitives/Skeleton/Skeleton.spec.d.ts.map +1 -0
- package/dist/primitives/Skeleton/Skeleton.spec.js +173 -0
- package/dist/primitives/Spinner/Spinner.spec.js +25 -29
- package/dist/primitives/Tabs/TabItem.spec.d.ts +2 -0
- package/dist/primitives/Tabs/TabItem.spec.d.ts.map +1 -0
- package/dist/primitives/Tabs/TabItem.spec.js +130 -0
- package/dist/primitives/Tabs/Tabs.spec.d.ts +2 -0
- package/dist/primitives/Tabs/Tabs.spec.d.ts.map +1 -0
- package/dist/primitives/Tabs/Tabs.spec.js +295 -0
- package/dist/primitives/Tabs/TabsWithItems.test.svelte +18 -0
- package/dist/primitives/Tabs/TabsWithItems.test.svelte.d.ts +16 -0
- package/dist/primitives/Tabs/TabsWithItems.test.svelte.d.ts.map +1 -0
- package/dist/primitives/Toggle.spec.js +93 -77
- package/dist/primitives/Typography/Typography.spec.d.ts +2 -0
- package/dist/primitives/Typography/Typography.spec.d.ts.map +1 -0
- package/dist/primitives/Typography/Typography.spec.js +183 -0
- package/dist/primitives/ValidationError.spec.js +1 -1
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.js +3 -0
- package/dist/recipes/CropImage/CropImage.spec.js +1 -9
- package/dist/recipes/ImageUploader/ImageUploader.spec.d.ts +2 -0
- package/dist/recipes/ImageUploader/ImageUploader.spec.d.ts.map +1 -0
- package/dist/recipes/ImageUploader/ImageUploader.spec.js +1351 -0
- package/dist/recipes/SuperLogin/SuperLogin.spec.d.ts +2 -0
- package/dist/recipes/SuperLogin/SuperLogin.spec.d.ts.map +1 -0
- package/dist/recipes/SuperLogin/SuperLogin.spec.js +1436 -0
- package/dist/recipes/feedback/EmptyState/EmptyState.spec.d.ts +2 -0
- package/dist/recipes/feedback/EmptyState/EmptyState.spec.d.ts.map +1 -0
- package/dist/recipes/feedback/EmptyState/EmptyState.spec.js +202 -0
- package/dist/recipes/feedback/ErrorDisplay.spec.js +6 -6
- package/dist/recipes/feedback/StatusIndicator/StatusIndicator.spec.js +21 -17
- package/dist/recipes/fields/CheckboxField.spec.d.ts +2 -0
- package/dist/recipes/fields/CheckboxField.spec.d.ts.map +1 -0
- package/dist/recipes/fields/CheckboxField.spec.js +135 -0
- package/dist/recipes/fields/FormField.spec.d.ts +2 -0
- package/dist/recipes/fields/FormField.spec.d.ts.map +1 -0
- package/dist/recipes/fields/FormField.spec.js +159 -0
- package/dist/recipes/fields/RadioGroup.spec.d.ts +2 -0
- package/dist/recipes/fields/RadioGroup.spec.d.ts.map +1 -0
- package/dist/recipes/fields/RadioGroup.spec.js +199 -0
- package/dist/recipes/fields/SelectField.spec.d.ts +2 -0
- package/dist/recipes/fields/SelectField.spec.d.ts.map +1 -0
- package/dist/recipes/fields/SelectField.spec.js +188 -0
- package/dist/recipes/fields/TextareaField.spec.d.ts +2 -0
- package/dist/recipes/fields/TextareaField.spec.d.ts.map +1 -0
- package/dist/recipes/fields/TextareaField.spec.js +205 -0
- package/dist/recipes/fields/ToggleField.spec.d.ts +2 -0
- package/dist/recipes/fields/ToggleField.spec.d.ts.map +1 -0
- package/dist/recipes/fields/ToggleField.spec.js +153 -0
- package/dist/recipes/inputs/MultiSelect.spec.js +4 -3
- package/dist/recipes/inputs/MultiSelect.svelte +10 -3
- package/dist/recipes/inputs/MultiSelect.svelte.d.ts +2 -0
- package/dist/recipes/inputs/MultiSelect.svelte.d.ts.map +1 -1
- package/dist/recipes/inputs/OTPInput.spec.js +52 -39
- package/dist/recipes/inputs/PasswordInput.spec.d.ts +2 -0
- package/dist/recipes/inputs/PasswordInput.spec.d.ts.map +1 -0
- package/dist/recipes/inputs/PasswordInput.spec.js +410 -0
- package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.spec.js +253 -173
- package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte +71 -0
- package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte.d.ts +9 -0
- package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte.d.ts.map +1 -0
- package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.spec.js +1246 -300
- package/dist/recipes/inputs/Search.spec.d.ts +2 -0
- package/dist/recipes/inputs/Search.spec.d.ts.map +1 -0
- package/dist/recipes/inputs/Search.spec.js +177 -0
- package/dist/recipes/inputs/SelectDropdown.spec.d.ts +2 -0
- package/dist/recipes/inputs/SelectDropdown.spec.d.ts.map +1 -0
- package/dist/recipes/inputs/SelectDropdown.spec.js +512 -0
- package/dist/recipes/modals/AlertModal.spec.d.ts +2 -0
- package/dist/recipes/modals/AlertModal.spec.d.ts.map +1 -0
- package/dist/recipes/modals/AlertModal.spec.js +432 -0
- package/dist/recipes/modals/ConfirmationModal.spec.js +36 -21
- package/dist/recipes/modals/InputModal.spec.d.ts +2 -0
- package/dist/recipes/modals/InputModal.spec.d.ts.map +1 -0
- package/dist/recipes/modals/InputModal.spec.js +872 -0
- package/dist/recipes/modals/ModalTestWrapper.spec.d.ts +2 -0
- package/dist/recipes/modals/ModalTestWrapper.spec.d.ts.map +1 -0
- package/dist/recipes/modals/ModalTestWrapper.spec.js +502 -0
- package/dist/recipes/modals/StatusModal.spec.d.ts +2 -0
- package/dist/recipes/modals/StatusModal.spec.d.ts.map +1 -0
- package/dist/recipes/modals/StatusModal.spec.js +599 -0
- package/dist/services/ShowService.spec.js +18 -15
- package/dist/stories/ButtonAuditDashboard.spec.d.ts +2 -0
- package/dist/stories/ButtonAuditDashboard.spec.d.ts.map +1 -0
- package/dist/stories/ButtonAuditDashboard.spec.js +913 -0
- package/dist/stories/ButtonAuditReview.spec.d.ts +2 -0
- package/dist/stories/ButtonAuditReview.spec.d.ts.map +1 -0
- package/dist/stories/ButtonAuditReview.spec.js +422 -0
- package/dist/stories/ButtonGridView.spec.d.ts +2 -0
- package/dist/stories/ButtonGridView.spec.d.ts.map +1 -0
- package/dist/stories/ButtonGridView.spec.js +667 -0
- package/dist/stories/ButtonShowcase.spec.d.ts +2 -0
- package/dist/stories/ButtonShowcase.spec.d.ts.map +1 -0
- package/dist/stories/ButtonShowcase.spec.js +499 -0
- package/dist/stories/PatternsGallery.spec.d.ts +2 -0
- package/dist/stories/PatternsGallery.spec.d.ts.map +1 -0
- package/dist/stories/PatternsGallery.spec.js +514 -0
- package/dist/stories/PrimitivesGallery.spec.d.ts +2 -0
- package/dist/stories/PrimitivesGallery.spec.d.ts.map +1 -0
- package/dist/stories/PrimitivesGallery.spec.js +813 -0
- package/dist/stories/RecipesGallery.spec.d.ts +2 -0
- package/dist/stories/RecipesGallery.spec.d.ts.map +1 -0
- package/dist/stories/RecipesGallery.spec.js +299 -0
- package/dist/stripe/useStripeTheme.spec.d.ts +2 -0
- package/dist/stripe/useStripeTheme.spec.d.ts.map +1 -0
- package/dist/stripe/useStripeTheme.spec.js +793 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +6 -5
- package/dist/telemetry.spec.js +495 -12
- package/dist/tokens/__tests__/colors.test.d.ts +2 -0
- package/dist/tokens/__tests__/colors.test.d.ts.map +1 -0
- package/dist/tokens/__tests__/colors.test.js +152 -0
- package/dist/tokens/__tests__/radius.test.d.ts +2 -0
- package/dist/tokens/__tests__/radius.test.d.ts.map +1 -0
- package/dist/tokens/__tests__/radius.test.js +118 -0
- package/dist/tokens/__tests__/shadows.test.d.ts +2 -0
- package/dist/tokens/__tests__/shadows.test.d.ts.map +1 -0
- package/dist/tokens/__tests__/shadows.test.js +105 -0
- package/dist/tokens/__tests__/spacing.test.js +11 -8
- package/dist/tokens/__tests__/typography.test.d.ts +2 -0
- package/dist/tokens/__tests__/typography.test.d.ts.map +1 -0
- package/dist/tokens/__tests__/typography.test.js +156 -0
- package/dist/tokens/__tests__/z-index.test.d.ts +2 -0
- package/dist/tokens/__tests__/z-index.test.d.ts.map +1 -0
- package/dist/tokens/__tests__/z-index.test.js +121 -0
- package/dist/utils/apiConfig.spec.js +102 -1
- package/dist/utils/formatters.spec.d.ts +2 -0
- package/dist/utils/formatters.spec.d.ts.map +1 -0
- package/dist/utils/formatters.spec.js +82 -0
- package/dist/utils/transitions.spec.d.ts +2 -0
- package/dist/utils/transitions.spec.d.ts.map +1 -0
- package/dist/utils/transitions.spec.js +130 -0
- package/package.json +8 -3
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
|
|
2
|
+
import { render, screen, cleanup, fireEvent, waitFor } from '@testing-library/svelte';
|
|
3
|
+
import ShowCard from './ShowCard.svelte';
|
|
4
|
+
|
|
5
|
+
describe('ShowCard Component', () => {
|
|
6
|
+
const mockEvent = {
|
|
7
|
+
name: 'Rock Concert',
|
|
8
|
+
startDateTime: '2024-03-15T20:00:00',
|
|
9
|
+
endDateTime: '2024-03-15T23:00:00',
|
|
10
|
+
doorsOpenTime: '2024-03-15T19:00:00',
|
|
11
|
+
displayStartTime: true,
|
|
12
|
+
displayEndTime: true,
|
|
13
|
+
venue: {
|
|
14
|
+
name: 'The Music Hall',
|
|
15
|
+
address: '123 Main St, New York, NY 10001',
|
|
16
|
+
googleLocationNameCache: 'The Music Hall, New York'
|
|
17
|
+
},
|
|
18
|
+
ageRequirement: 21
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const mockShowtimes = [
|
|
22
|
+
{ day: 'Friday', date: '2024-03-15', time: '19:30:00' },
|
|
23
|
+
{ day: 'Saturday', date: '2024-03-16', time: '20:00:00' },
|
|
24
|
+
{ day: 'Sunday', date: '2024-03-17', time: '18:00:00' }
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
// Mock window.location
|
|
29
|
+
Object.defineProperty(window, 'location', {
|
|
30
|
+
writable: true,
|
|
31
|
+
value: { href: 'https://example.com/event/123' }
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Mock clipboard API
|
|
35
|
+
Object.defineProperty(navigator, 'clipboard', {
|
|
36
|
+
writable: true,
|
|
37
|
+
value: {
|
|
38
|
+
writeText: vi.fn().mockResolvedValue(undefined)
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Mock share API
|
|
43
|
+
Object.defineProperty(navigator, 'share', {
|
|
44
|
+
writable: true,
|
|
45
|
+
value: undefined
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
Object.defineProperty(navigator, 'canShare', {
|
|
49
|
+
writable: true,
|
|
50
|
+
value: undefined
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
cleanup();
|
|
56
|
+
vi.restoreAllMocks();
|
|
57
|
+
vi.clearAllTimers();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('rendering', () => {
|
|
61
|
+
it('renders the component wrapper', () => {
|
|
62
|
+
const { container } = render(ShowCard, { props: { event: mockEvent } });
|
|
63
|
+
const wrapper = container.querySelector('.rounded-lg');
|
|
64
|
+
expect(wrapper).toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('renders event name when showTitle is true', () => {
|
|
68
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
69
|
+
expect(screen.getByText('Rock Concert')).toBeInTheDocument();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('does not render event name when showTitle is false', () => {
|
|
73
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: false } });
|
|
74
|
+
expect(screen.queryByText('Rock Concert')).not.toBeInTheDocument();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('renders with empty event object', () => {
|
|
78
|
+
const { container } = render(ShowCard, { props: { event: {} } });
|
|
79
|
+
expect(container.querySelector('.rounded-lg')).toBeInTheDocument();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('renders without event prop', () => {
|
|
83
|
+
const { container } = render(ShowCard);
|
|
84
|
+
expect(container.querySelector('.rounded-lg')).toBeInTheDocument();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('date and time formatting', () => {
|
|
89
|
+
it('formats startDateTime correctly', () => {
|
|
90
|
+
const { container } = render(ShowCard, { props: { event: mockEvent } });
|
|
91
|
+
// Check that some part of the formatted date appears
|
|
92
|
+
expect(container.textContent).toMatch(/March|Mar/i);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('displays doors open time when provided', () => {
|
|
96
|
+
render(ShowCard, {
|
|
97
|
+
props: {
|
|
98
|
+
event: {
|
|
99
|
+
...mockEvent,
|
|
100
|
+
displayStartTime: true,
|
|
101
|
+
doorsOpenTime: '2024-03-15T19:00:00'
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
expect(screen.getByText(/Doors open/i)).toBeInTheDocument();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('hides doors open time when displayStartTime is false', () => {
|
|
109
|
+
render(ShowCard, {
|
|
110
|
+
props: {
|
|
111
|
+
event: {
|
|
112
|
+
...mockEvent,
|
|
113
|
+
displayStartTime: false,
|
|
114
|
+
doorsOpenTime: '2024-03-15T19:00:00'
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
expect(screen.queryByText(/Doors open/i)).not.toBeInTheDocument();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('displays end time when provided', () => {
|
|
122
|
+
render(ShowCard, {
|
|
123
|
+
props: {
|
|
124
|
+
event: {
|
|
125
|
+
...mockEvent,
|
|
126
|
+
displayEndTime: true,
|
|
127
|
+
endDateTime: '2024-03-15T23:00:00'
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
expect(screen.getByText(/Ends/i)).toBeInTheDocument();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('hides end time when displayEndTime is false', () => {
|
|
135
|
+
render(ShowCard, {
|
|
136
|
+
props: {
|
|
137
|
+
event: {
|
|
138
|
+
...mockEvent,
|
|
139
|
+
displayEndTime: false,
|
|
140
|
+
endDateTime: '2024-03-15T23:00:00'
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
expect(screen.queryByText(/Ends/i)).not.toBeInTheDocument();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('handles invalid date gracefully', () => {
|
|
148
|
+
const { container } = render(ShowCard, {
|
|
149
|
+
props: { event: { startDateTime: 'invalid-date' } }
|
|
150
|
+
});
|
|
151
|
+
expect(container).toBeInTheDocument();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('handles missing date', () => {
|
|
155
|
+
const { container } = render(ShowCard, {
|
|
156
|
+
props: { event: { startDateTime: null } }
|
|
157
|
+
});
|
|
158
|
+
expect(container).toBeInTheDocument();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('uses custom formatDateTime function', () => {
|
|
162
|
+
const customFormat = vi.fn(() => 'Custom Date Format');
|
|
163
|
+
render(ShowCard, {
|
|
164
|
+
props: {
|
|
165
|
+
event: mockEvent,
|
|
166
|
+
formatDateTime: customFormat
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
expect(customFormat).toHaveBeenCalled();
|
|
170
|
+
expect(screen.getByText('Custom Date Format')).toBeInTheDocument();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('uses custom formatTimeline function', () => {
|
|
174
|
+
const customFormat = vi.fn(() => 'Custom Time');
|
|
175
|
+
render(ShowCard, {
|
|
176
|
+
props: {
|
|
177
|
+
event: { ...mockEvent, displayStartTime: true },
|
|
178
|
+
formatTimeline: customFormat
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
expect(customFormat).toHaveBeenCalled();
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe('share functionality', () => {
|
|
186
|
+
it('renders share button when showTitle is true', () => {
|
|
187
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
188
|
+
const shareButton = screen.getByLabelText('Share event');
|
|
189
|
+
expect(shareButton).toBeInTheDocument();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('does not render share button when showTitle is false', () => {
|
|
193
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: false } });
|
|
194
|
+
expect(screen.queryByLabelText('Share event')).not.toBeInTheDocument();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('uses native share API when available', async () => {
|
|
198
|
+
const shareMock = vi.fn().mockResolvedValue(undefined);
|
|
199
|
+
const canShareMock = vi.fn().mockReturnValue(true);
|
|
200
|
+
|
|
201
|
+
Object.defineProperty(navigator, 'share', { value: shareMock });
|
|
202
|
+
Object.defineProperty(navigator, 'canShare', { value: canShareMock });
|
|
203
|
+
|
|
204
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
205
|
+
const shareButton = screen.getByLabelText('Share event');
|
|
206
|
+
|
|
207
|
+
await fireEvent.click(shareButton);
|
|
208
|
+
|
|
209
|
+
expect(canShareMock).toHaveBeenCalledWith({
|
|
210
|
+
title: 'Rock Concert',
|
|
211
|
+
text: 'Check out Rock Concert at The Music Hall!',
|
|
212
|
+
url: 'https://example.com/event/123'
|
|
213
|
+
});
|
|
214
|
+
expect(shareMock).toHaveBeenCalled();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('falls back to clipboard when share API not available', async () => {
|
|
218
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
219
|
+
const shareButton = screen.getByLabelText('Share event');
|
|
220
|
+
|
|
221
|
+
await fireEvent.click(shareButton);
|
|
222
|
+
|
|
223
|
+
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('https://example.com/event/123');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('shows success indicator after clipboard copy', async () => {
|
|
227
|
+
vi.useFakeTimers();
|
|
228
|
+
|
|
229
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
230
|
+
const shareButton = screen.getByLabelText('Share event');
|
|
231
|
+
|
|
232
|
+
await fireEvent.click(shareButton);
|
|
233
|
+
|
|
234
|
+
// Should show check icon
|
|
235
|
+
await waitFor(() => {
|
|
236
|
+
const checkIcon = shareButton.querySelector('.text-green-600, .text-green-500');
|
|
237
|
+
expect(checkIcon).toBeInTheDocument();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// After 2 seconds, should revert to share icon
|
|
241
|
+
vi.advanceTimersByTime(2000);
|
|
242
|
+
|
|
243
|
+
await waitFor(() => {
|
|
244
|
+
const shareIcon = shareButton.querySelector('.text-gray-500, .text-gray-400');
|
|
245
|
+
expect(shareIcon).toBeInTheDocument();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
vi.useRealTimers();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('handles share API abort error gracefully', async () => {
|
|
252
|
+
const shareMock = vi.fn().mockRejectedValue({ name: 'AbortError' });
|
|
253
|
+
const canShareMock = vi.fn().mockReturnValue(true);
|
|
254
|
+
|
|
255
|
+
Object.defineProperty(navigator, 'share', { value: shareMock });
|
|
256
|
+
Object.defineProperty(navigator, 'canShare', { value: canShareMock });
|
|
257
|
+
|
|
258
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
259
|
+
const shareButton = screen.getByLabelText('Share event');
|
|
260
|
+
|
|
261
|
+
await fireEvent.click(shareButton);
|
|
262
|
+
|
|
263
|
+
// AbortError should NOT fall back to clipboard (user cancelled)
|
|
264
|
+
expect(navigator.clipboard.writeText).not.toHaveBeenCalled();
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('handles share API error by falling back to clipboard', async () => {
|
|
268
|
+
const shareMock = vi.fn().mockRejectedValue(new Error('Share failed'));
|
|
269
|
+
const canShareMock = vi.fn().mockReturnValue(true);
|
|
270
|
+
|
|
271
|
+
Object.defineProperty(navigator, 'share', { value: shareMock });
|
|
272
|
+
Object.defineProperty(navigator, 'canShare', { value: canShareMock });
|
|
273
|
+
|
|
274
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
275
|
+
const shareButton = screen.getByLabelText('Share event');
|
|
276
|
+
|
|
277
|
+
await fireEvent.click(shareButton);
|
|
278
|
+
|
|
279
|
+
// Should fall back to clipboard
|
|
280
|
+
expect(navigator.clipboard.writeText).toHaveBeenCalled();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('handles clipboard error gracefully', async () => {
|
|
284
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
285
|
+
navigator.clipboard.writeText = vi.fn().mockRejectedValue(new Error('Clipboard failed'));
|
|
286
|
+
|
|
287
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
288
|
+
const shareButton = screen.getByLabelText('Share event');
|
|
289
|
+
|
|
290
|
+
await fireEvent.click(shareButton);
|
|
291
|
+
|
|
292
|
+
await waitFor(() => {
|
|
293
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
consoleErrorSpy.mockRestore();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('handles missing venue name in share text', async () => {
|
|
300
|
+
const eventWithoutVenue = { ...mockEvent, venue: null };
|
|
301
|
+
|
|
302
|
+
render(ShowCard, { props: { event: eventWithoutVenue, showTitle: true } });
|
|
303
|
+
const shareButton = screen.getByLabelText('Share event');
|
|
304
|
+
|
|
305
|
+
await fireEvent.click(shareButton);
|
|
306
|
+
|
|
307
|
+
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('https://example.com/event/123');
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe('showtimes section', () => {
|
|
312
|
+
it('renders showtimes when multiple are provided', () => {
|
|
313
|
+
render(ShowCard, { props: { event: mockEvent, showtimes: mockShowtimes } });
|
|
314
|
+
expect(screen.getByText('More showtimes')).toBeInTheDocument();
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('does not render showtimes section with single showtime', () => {
|
|
318
|
+
render(ShowCard, { props: { event: mockEvent, showtimes: [mockShowtimes[0]] } });
|
|
319
|
+
expect(screen.queryByText('More showtimes')).not.toBeInTheDocument();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('does not render showtimes section with empty array', () => {
|
|
323
|
+
render(ShowCard, { props: { event: mockEvent, showtimes: [] } });
|
|
324
|
+
expect(screen.queryByText('More showtimes')).not.toBeInTheDocument();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('renders all showtimes', () => {
|
|
328
|
+
render(ShowCard, { props: { event: mockEvent, showtimes: mockShowtimes } });
|
|
329
|
+
expect(screen.getByText('Friday')).toBeInTheDocument();
|
|
330
|
+
expect(screen.getByText('Saturday')).toBeInTheDocument();
|
|
331
|
+
expect(screen.getByText('Sunday')).toBeInTheDocument();
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('calls onShowtimeSelect when showtime is clicked', async () => {
|
|
335
|
+
const onShowtimeSelect = vi.fn();
|
|
336
|
+
render(ShowCard, {
|
|
337
|
+
props: {
|
|
338
|
+
event: mockEvent,
|
|
339
|
+
showtimes: mockShowtimes,
|
|
340
|
+
onShowtimeSelect
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Find all buttons (share button + showtime buttons)
|
|
345
|
+
const buttons = screen.getAllByRole('button');
|
|
346
|
+
// First button is share, rest are showtimes
|
|
347
|
+
const showtimeButton = buttons[1];
|
|
348
|
+
|
|
349
|
+
await fireEvent.click(showtimeButton);
|
|
350
|
+
|
|
351
|
+
expect(onShowtimeSelect).toHaveBeenCalled();
|
|
352
|
+
expect(onShowtimeSelect.mock.calls[0][0]).toHaveProperty('date');
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('updates selected date when showtime is clicked', async () => {
|
|
356
|
+
render(ShowCard, {
|
|
357
|
+
props: {
|
|
358
|
+
event: mockEvent,
|
|
359
|
+
showtimes: mockShowtimes
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const buttons = screen.getAllByRole('button');
|
|
364
|
+
const firstShowtime = buttons[1];
|
|
365
|
+
|
|
366
|
+
await fireEvent.click(firstShowtime);
|
|
367
|
+
|
|
368
|
+
// Selected state should be reflected in aria-pressed
|
|
369
|
+
expect(firstShowtime).toHaveAttribute('aria-pressed', 'true');
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('does not throw when onShowtimeSelect is not provided', async () => {
|
|
373
|
+
render(ShowCard, {
|
|
374
|
+
props: {
|
|
375
|
+
event: mockEvent,
|
|
376
|
+
showtimes: mockShowtimes
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
const buttons = screen.getAllByRole('button');
|
|
381
|
+
const showtimeButton = buttons[1];
|
|
382
|
+
|
|
383
|
+
await expect(fireEvent.click(showtimeButton)).resolves.not.toThrow();
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
describe('venue section', () => {
|
|
388
|
+
it('renders venue name and address', () => {
|
|
389
|
+
render(ShowCard, { props: { event: mockEvent } });
|
|
390
|
+
expect(screen.getByText('The Music Hall')).toBeInTheDocument();
|
|
391
|
+
expect(screen.getByText('123 Main St, New York, NY 10001')).toBeInTheDocument();
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('does not render venue section when venue is missing', () => {
|
|
395
|
+
const eventWithoutVenue = { ...mockEvent, venue: null };
|
|
396
|
+
render(ShowCard, { props: { event: eventWithoutVenue } });
|
|
397
|
+
expect(screen.queryByText('The Music Hall')).not.toBeInTheDocument();
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('renders Google Maps link with correct URL', () => {
|
|
401
|
+
render(ShowCard, { props: { event: mockEvent } });
|
|
402
|
+
const link = screen.getByText('The Music Hall').closest('a');
|
|
403
|
+
expect(link).toHaveAttribute('href', expect.stringContaining('google.com/maps'));
|
|
404
|
+
expect(link).toHaveAttribute('href', expect.stringContaining('The%20Music%20Hall%2C%20New%20York'));
|
|
405
|
+
expect(link).toHaveAttribute('target', '_blank');
|
|
406
|
+
expect(link).toHaveAttribute('rel', 'noopener noreferrer');
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('falls back to address in Google Maps URL when googleLocationNameCache is missing', () => {
|
|
410
|
+
const eventWithoutCache = {
|
|
411
|
+
...mockEvent,
|
|
412
|
+
venue: {
|
|
413
|
+
name: 'The Music Hall',
|
|
414
|
+
address: '123 Main St, New York, NY 10001'
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
render(ShowCard, { props: { event: eventWithoutCache } });
|
|
418
|
+
const link = screen.getByText('The Music Hall').closest('a');
|
|
419
|
+
expect(link).toHaveAttribute('href', expect.stringContaining('123%20Main%20St'));
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('falls back to venue name in Google Maps URL when address is missing', () => {
|
|
423
|
+
const eventWithoutAddress = {
|
|
424
|
+
...mockEvent,
|
|
425
|
+
venue: {
|
|
426
|
+
name: 'The Music Hall'
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
render(ShowCard, { props: { event: eventWithoutAddress } });
|
|
430
|
+
const link = screen.getByText('The Music Hall').closest('a');
|
|
431
|
+
expect(link).toHaveAttribute('href', expect.stringContaining('The%20Music%20Hall'));
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('handles missing address gracefully', () => {
|
|
435
|
+
const eventWithoutAddress = {
|
|
436
|
+
...mockEvent,
|
|
437
|
+
venue: {
|
|
438
|
+
name: 'The Music Hall',
|
|
439
|
+
googleLocationNameCache: 'The Music Hall, New York'
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
render(ShowCard, { props: { event: eventWithoutAddress } });
|
|
443
|
+
expect(screen.getByText('The Music Hall')).toBeInTheDocument();
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
describe('age restrictions', () => {
|
|
448
|
+
it('displays age requirement when provided', () => {
|
|
449
|
+
render(ShowCard, { props: { event: { ...mockEvent, ageRequirement: 21 } } });
|
|
450
|
+
expect(screen.getByText('21+')).toBeInTheDocument();
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('displays "All ages" for ageRequirement 0', () => {
|
|
454
|
+
const event = { ...mockEvent, hasAgeRestriction: true, ageRequirement: 0 };
|
|
455
|
+
render(ShowCard, { props: { event } });
|
|
456
|
+
expect(screen.getByText('All ages')).toBeInTheDocument();
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('displays "All ages" for ageRequirement 1', () => {
|
|
460
|
+
const event = { ...mockEvent, hasAgeRestriction: true, ageRequirement: 1 };
|
|
461
|
+
render(ShowCard, { props: { event } });
|
|
462
|
+
expect(screen.getByText('All ages')).toBeInTheDocument();
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('displays minimum age when ageRequirement not set', () => {
|
|
466
|
+
const event = { ...mockEvent, ageRequirement: null, minimumAge: 18 };
|
|
467
|
+
render(ShowCard, { props: { event } });
|
|
468
|
+
expect(screen.getByText('18+')).toBeInTheDocument();
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('displays "All ages" for minimumAge 1', () => {
|
|
472
|
+
const event = { ...mockEvent, hasAgeRestriction: true, ageRequirement: null, minimumAge: 1 };
|
|
473
|
+
render(ShowCard, { props: { event } });
|
|
474
|
+
expect(screen.getByText('All ages')).toBeInTheDocument();
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('falls back to ageRestriction when others not set', () => {
|
|
478
|
+
const event = {
|
|
479
|
+
...mockEvent,
|
|
480
|
+
hasAgeRestriction: true,
|
|
481
|
+
ageRequirement: null,
|
|
482
|
+
minimumAge: null,
|
|
483
|
+
ageRestriction: 16
|
|
484
|
+
};
|
|
485
|
+
render(ShowCard, { props: { event } });
|
|
486
|
+
expect(screen.getByText('16+')).toBeInTheDocument();
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('displays "All ages" for ageRestriction 1', () => {
|
|
490
|
+
const event = {
|
|
491
|
+
...mockEvent,
|
|
492
|
+
hasAgeRestriction: true,
|
|
493
|
+
ageRequirement: null,
|
|
494
|
+
minimumAge: null,
|
|
495
|
+
ageRestriction: 1
|
|
496
|
+
};
|
|
497
|
+
render(ShowCard, { props: { event } });
|
|
498
|
+
expect(screen.getByText('All ages')).toBeInTheDocument();
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('does not display age info when no age fields present', () => {
|
|
502
|
+
const eventWithoutAge = {
|
|
503
|
+
...mockEvent,
|
|
504
|
+
hasAgeRestriction: false,
|
|
505
|
+
ageRequirement: null,
|
|
506
|
+
minimumAge: null,
|
|
507
|
+
ageRestriction: null
|
|
508
|
+
};
|
|
509
|
+
render(ShowCard, { props: { event: eventWithoutAge } });
|
|
510
|
+
expect(screen.queryByText(/\d+\+|All ages/)).not.toBeInTheDocument();
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('displays age info when hasAgeRestriction is true', () => {
|
|
514
|
+
const event = {
|
|
515
|
+
...mockEvent,
|
|
516
|
+
hasAgeRestriction: true,
|
|
517
|
+
ageRequirement: 18
|
|
518
|
+
};
|
|
519
|
+
render(ShowCard, { props: { event } });
|
|
520
|
+
expect(screen.getByText('18+')).toBeInTheDocument();
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('prefers ageRequirement over minimumAge', () => {
|
|
524
|
+
const event = {
|
|
525
|
+
...mockEvent,
|
|
526
|
+
ageRequirement: 21,
|
|
527
|
+
minimumAge: 18
|
|
528
|
+
};
|
|
529
|
+
render(ShowCard, { props: { event } });
|
|
530
|
+
expect(screen.getByText('21+')).toBeInTheDocument();
|
|
531
|
+
expect(screen.queryByText('18+')).not.toBeInTheDocument();
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
describe('accessibility', () => {
|
|
536
|
+
it('has proper heading hierarchy', () => {
|
|
537
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
538
|
+
const heading = screen.getByRole('heading', { level: 2 });
|
|
539
|
+
expect(heading).toHaveTextContent('Rock Concert');
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('share button has aria-label', () => {
|
|
543
|
+
render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
544
|
+
const shareButton = screen.getByLabelText('Share event');
|
|
545
|
+
expect(shareButton).toBeInTheDocument();
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it('venue link is keyboard accessible', () => {
|
|
549
|
+
render(ShowCard, { props: { event: mockEvent } });
|
|
550
|
+
const link = screen.getByText('The Music Hall').closest('a');
|
|
551
|
+
expect(link).toBeInTheDocument();
|
|
552
|
+
expect(link.tagName).toBe('A');
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
describe('styling', () => {
|
|
557
|
+
it('has correct base classes', () => {
|
|
558
|
+
const { container } = render(ShowCard, { props: { event: mockEvent } });
|
|
559
|
+
const wrapper = container.querySelector('.rounded-lg');
|
|
560
|
+
expect(wrapper).toHaveClass('h-fit', 'rounded-lg', 'py-4');
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it('has dark mode support', () => {
|
|
564
|
+
const { container } = render(ShowCard, { props: { event: mockEvent } });
|
|
565
|
+
const wrapper = container.querySelector('.rounded-lg');
|
|
566
|
+
expect(wrapper.className).toContain('dark:bg-gray-800');
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
it('has border on medium screens and up', () => {
|
|
570
|
+
const { container } = render(ShowCard, { props: { event: mockEvent } });
|
|
571
|
+
const wrapper = container.querySelector('.rounded-lg');
|
|
572
|
+
expect(wrapper.className).toContain('md:border');
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it('applies hover styles to share button', () => {
|
|
576
|
+
const { container } = render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
577
|
+
const shareButton = screen.getByLabelText('Share event');
|
|
578
|
+
expect(shareButton.className).toContain('hover:bg-gray-100');
|
|
579
|
+
expect(shareButton.className).toContain('dark:hover:bg-gray-700');
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('applies hover styles to venue link', () => {
|
|
583
|
+
const { container } = render(ShowCard, { props: { event: mockEvent } });
|
|
584
|
+
const venueName = screen.getByText('The Music Hall');
|
|
585
|
+
expect(venueName.className).toContain('group-hover:text-blue-700');
|
|
586
|
+
expect(venueName.className).toContain('dark:group-hover:text-blue-500');
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('has border between sections', () => {
|
|
590
|
+
const { container } = render(ShowCard, {
|
|
591
|
+
props: {
|
|
592
|
+
event: mockEvent,
|
|
593
|
+
showtimes: mockShowtimes
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
const borders = container.querySelectorAll('.border-t');
|
|
597
|
+
expect(borders.length).toBeGreaterThan(0);
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
describe('edge cases', () => {
|
|
602
|
+
it('handles undefined props gracefully', () => {
|
|
603
|
+
const { container } = render(ShowCard);
|
|
604
|
+
expect(container.querySelector('.rounded-lg')).toBeInTheDocument();
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
it('handles null showtimes', () => {
|
|
608
|
+
const { container } = render(ShowCard, {
|
|
609
|
+
props: { event: mockEvent, showtimes: null }
|
|
610
|
+
});
|
|
611
|
+
expect(container.querySelector('.rounded-lg')).toBeInTheDocument();
|
|
612
|
+
expect(screen.queryByText('More showtimes')).not.toBeInTheDocument();
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
it('handles event with only name', () => {
|
|
616
|
+
render(ShowCard, { props: { event: { name: 'Simple Event' }, showTitle: true } });
|
|
617
|
+
expect(screen.getByText('Simple Event')).toBeInTheDocument();
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it('handles missing venue properties', () => {
|
|
621
|
+
const event = {
|
|
622
|
+
...mockEvent,
|
|
623
|
+
venue: { name: 'Minimal Venue' }
|
|
624
|
+
};
|
|
625
|
+
render(ShowCard, { props: { event } });
|
|
626
|
+
expect(screen.getByText('Minimal Venue')).toBeInTheDocument();
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it('handles SSR environment without window', () => {
|
|
630
|
+
const originalWindow = global.window;
|
|
631
|
+
// @ts-ignore
|
|
632
|
+
delete global.window;
|
|
633
|
+
|
|
634
|
+
const { container } = render(ShowCard, { props: { event: mockEvent } });
|
|
635
|
+
expect(container.querySelector('.rounded-lg')).toBeInTheDocument();
|
|
636
|
+
|
|
637
|
+
global.window = originalWindow;
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it('handles SSR environment without navigator', () => {
|
|
641
|
+
const originalNavigator = global.navigator;
|
|
642
|
+
// @ts-ignore
|
|
643
|
+
delete global.navigator;
|
|
644
|
+
|
|
645
|
+
const { container } = render(ShowCard, { props: { event: mockEvent, showTitle: true } });
|
|
646
|
+
expect(container.querySelector('.rounded-lg')).toBeInTheDocument();
|
|
647
|
+
|
|
648
|
+
global.navigator = originalNavigator;
|
|
649
|
+
});
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
describe('integration', () => {
|
|
653
|
+
it('renders complete event card with all sections', () => {
|
|
654
|
+
render(ShowCard, {
|
|
655
|
+
props: {
|
|
656
|
+
event: mockEvent,
|
|
657
|
+
showtimes: mockShowtimes,
|
|
658
|
+
showTitle: true
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
// Title
|
|
663
|
+
expect(screen.getByText('Rock Concert')).toBeInTheDocument();
|
|
664
|
+
|
|
665
|
+
// Share button
|
|
666
|
+
expect(screen.getByLabelText('Share event')).toBeInTheDocument();
|
|
667
|
+
|
|
668
|
+
// Showtimes
|
|
669
|
+
expect(screen.getByText('More showtimes')).toBeInTheDocument();
|
|
670
|
+
|
|
671
|
+
// Venue
|
|
672
|
+
expect(screen.getByText('The Music Hall')).toBeInTheDocument();
|
|
673
|
+
|
|
674
|
+
// Age restriction
|
|
675
|
+
expect(screen.getByText('21+')).toBeInTheDocument();
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
it('handles complex user interaction flow', async () => {
|
|
679
|
+
vi.useFakeTimers();
|
|
680
|
+
const onShowtimeSelect = vi.fn();
|
|
681
|
+
|
|
682
|
+
render(ShowCard, {
|
|
683
|
+
props: {
|
|
684
|
+
event: mockEvent,
|
|
685
|
+
showtimes: mockShowtimes,
|
|
686
|
+
showTitle: true,
|
|
687
|
+
onShowtimeSelect
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
// Click share button
|
|
692
|
+
const shareButton = screen.getByLabelText('Share event');
|
|
693
|
+
await fireEvent.click(shareButton);
|
|
694
|
+
expect(navigator.clipboard.writeText).toHaveBeenCalled();
|
|
695
|
+
|
|
696
|
+
// Wait for success indicator
|
|
697
|
+
await waitFor(() => {
|
|
698
|
+
const checkIcon = shareButton.querySelector('.text-green-600, .text-green-500');
|
|
699
|
+
expect(checkIcon).toBeInTheDocument();
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
// Select a showtime
|
|
703
|
+
const buttons = screen.getAllByRole('button');
|
|
704
|
+
await fireEvent.click(buttons[1]);
|
|
705
|
+
expect(onShowtimeSelect).toHaveBeenCalled();
|
|
706
|
+
|
|
707
|
+
// Click venue link (verify it's interactive)
|
|
708
|
+
const venueLink = screen.getByText('The Music Hall').closest('a');
|
|
709
|
+
expect(venueLink).toHaveAttribute('href');
|
|
710
|
+
|
|
711
|
+
vi.useRealTimers();
|
|
712
|
+
});
|
|
713
|
+
});
|
|
714
|
+
});
|