@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.
Files changed (290) hide show
  1. package/dist/calendar/AboutShow/AboutShow.spec.d.ts +2 -0
  2. package/dist/calendar/AboutShow/AboutShow.spec.d.ts.map +1 -0
  3. package/dist/calendar/AboutShow/AboutShow.spec.js +791 -0
  4. package/dist/calendar/Calendar/MiniMonthCalendar.spec.d.ts +2 -0
  5. package/dist/calendar/Calendar/MiniMonthCalendar.spec.d.ts.map +1 -0
  6. package/dist/calendar/Calendar/MiniMonthCalendar.spec.js +1191 -0
  7. package/dist/calendar/FAQs/FAQs.spec.d.ts +2 -0
  8. package/dist/calendar/FAQs/FAQs.spec.d.ts.map +1 -0
  9. package/dist/calendar/FAQs/FAQs.spec.js +238 -0
  10. package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.d.ts +2 -0
  11. package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.d.ts.map +1 -0
  12. package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.js +420 -0
  13. package/dist/calendar/OrderSummary/OrderSummary.spec.d.ts +2 -0
  14. package/dist/calendar/OrderSummary/OrderSummary.spec.d.ts.map +1 -0
  15. package/dist/calendar/OrderSummary/OrderSummary.spec.js +808 -0
  16. package/dist/calendar/PublicCard/PublicCard.spec.d.ts +2 -0
  17. package/dist/calendar/PublicCard/PublicCard.spec.d.ts.map +1 -0
  18. package/dist/calendar/PublicCard/PublicCard.spec.js +301 -0
  19. package/dist/calendar/ShowCard/ShowCard.spec.d.ts +2 -0
  20. package/dist/calendar/ShowCard/ShowCard.spec.d.ts.map +1 -0
  21. package/dist/calendar/ShowCard/ShowCard.spec.js +714 -0
  22. package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.d.ts +2 -0
  23. package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.d.ts.map +1 -0
  24. package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.js +241 -0
  25. package/dist/components/Layout/Section.spec.d.ts +2 -0
  26. package/dist/components/Layout/Section.spec.d.ts.map +1 -0
  27. package/dist/components/Layout/Section.spec.js +149 -0
  28. package/dist/components/Layout/Sidebar.spec.d.ts +2 -0
  29. package/dist/components/Layout/Sidebar.spec.d.ts.map +1 -0
  30. package/dist/components/Layout/Sidebar.spec.js +186 -0
  31. package/dist/components/Layout/Stack.spec.js +3 -3
  32. package/dist/constants/formOptions.spec.js +9 -5
  33. package/dist/datetime/__tests__/format.test.js +1 -1
  34. package/dist/datetime/__tests__/parse.test.js +1 -1
  35. package/dist/datetime/__tests__/timezone.test.js +124 -2
  36. package/dist/datetime/parse.js +1 -1
  37. package/dist/forms/createFieldTracker.spec.d.ts +2 -0
  38. package/dist/forms/createFieldTracker.spec.d.ts.map +1 -0
  39. package/dist/forms/createFieldTracker.spec.js +343 -0
  40. package/dist/forms/createFormStore.spec.d.ts +2 -0
  41. package/dist/forms/createFormStore.spec.d.ts.map +1 -0
  42. package/dist/forms/createFormStore.spec.js +689 -0
  43. package/dist/forms/createFormStore.svelte.js +0 -1
  44. package/dist/index.d.ts +4 -112
  45. package/dist/index.js +4 -190
  46. package/dist/patterns/data/DataGrid.spec.d.ts +2 -0
  47. package/dist/patterns/data/DataGrid.spec.d.ts.map +1 -0
  48. package/dist/patterns/data/DataGrid.spec.js +159 -0
  49. package/dist/patterns/data/DataList.spec.d.ts +2 -0
  50. package/dist/patterns/data/DataList.spec.d.ts.map +1 -0
  51. package/dist/patterns/data/DataList.spec.js +158 -0
  52. package/dist/patterns/data/DataTable.spec.d.ts +2 -0
  53. package/dist/patterns/data/DataTable.spec.d.ts.map +1 -0
  54. package/dist/patterns/data/DataTable.spec.js +196 -0
  55. package/dist/patterns/forms/FormActions.spec.js +10 -3
  56. package/dist/patterns/forms/FormGrid.spec.d.ts +2 -0
  57. package/dist/patterns/forms/FormGrid.spec.d.ts.map +1 -0
  58. package/dist/patterns/forms/FormGrid.spec.js +125 -0
  59. package/dist/patterns/forms/FormSection.spec.d.ts +2 -0
  60. package/dist/patterns/forms/FormSection.spec.d.ts.map +1 -0
  61. package/dist/patterns/forms/FormSection.spec.js +153 -0
  62. package/dist/patterns/layout/Sidebar.spec.d.ts +2 -0
  63. package/dist/patterns/layout/Sidebar.spec.d.ts.map +1 -0
  64. package/dist/patterns/layout/Sidebar.spec.js +159 -0
  65. package/dist/patterns/navigation/BottomNav.svelte +4 -4
  66. package/dist/patterns/navigation/Header.spec.js +33 -24
  67. package/dist/patterns/page/PageHeader.spec.d.ts +2 -0
  68. package/dist/patterns/page/PageHeader.spec.d.ts.map +1 -0
  69. package/dist/patterns/page/PageHeader.spec.js +167 -0
  70. package/dist/patterns/page/PageLayout.spec.d.ts +2 -0
  71. package/dist/patterns/page/PageLayout.spec.d.ts.map +1 -0
  72. package/dist/patterns/page/PageLayout.spec.js +145 -0
  73. package/dist/patterns/page/PageLoader.spec.js +5 -2
  74. package/dist/patterns/page/SectionHeader.spec.d.ts +2 -0
  75. package/dist/patterns/page/SectionHeader.spec.d.ts.map +1 -0
  76. package/dist/patterns/page/SectionHeader.spec.js +197 -0
  77. package/dist/presets/badges.spec.d.ts +2 -0
  78. package/dist/presets/badges.spec.d.ts.map +1 -0
  79. package/dist/presets/badges.spec.js +172 -0
  80. package/dist/presets/buttons.spec.d.ts +2 -0
  81. package/dist/presets/buttons.spec.d.ts.map +1 -0
  82. package/dist/presets/buttons.spec.js +135 -0
  83. package/dist/primitives/Accordion/Accordion.spec.d.ts +2 -0
  84. package/dist/primitives/Accordion/Accordion.spec.d.ts.map +1 -0
  85. package/dist/primitives/Accordion/Accordion.spec.js +83 -0
  86. package/dist/primitives/Accordion/AccordionItem.spec.d.ts +2 -0
  87. package/dist/primitives/Accordion/AccordionItem.spec.d.ts.map +1 -0
  88. package/dist/primitives/Accordion/AccordionItem.spec.js +661 -0
  89. package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte +107 -0
  90. package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte.d.ts +35 -0
  91. package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte.d.ts.map +1 -0
  92. package/dist/primitives/Alert/Alert.spec.js +5 -2
  93. package/dist/primitives/Avatar/Avatar.spec.d.ts +2 -0
  94. package/dist/primitives/Avatar/Avatar.spec.d.ts.map +1 -0
  95. package/dist/primitives/Avatar/Avatar.spec.js +211 -0
  96. package/dist/primitives/Badges/Badge.spec.js +109 -68
  97. package/dist/primitives/BottomSheet/BottomSheet.spec.js +36 -27
  98. package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte +13 -0
  99. package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte.d.ts +7 -0
  100. package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte.d.ts.map +1 -0
  101. package/dist/primitives/Breadcrumb/Breadcrumb.spec.js +15 -13
  102. package/dist/primitives/Breadcrumb/Breadcrumb.svelte +5 -5
  103. package/dist/primitives/Button/Button.spec.js +83 -71
  104. package/dist/primitives/Button/ButtonSaveDemo.spec.js +100 -2
  105. package/dist/primitives/Button/ButtonVariantShowcase.spec.d.ts +2 -0
  106. package/dist/primitives/Button/ButtonVariantShowcase.spec.d.ts.map +1 -0
  107. package/dist/primitives/Button/ButtonVariantShowcase.spec.js +202 -0
  108. package/dist/primitives/Card.spec.js +1 -1
  109. package/dist/primitives/Checkbox/Checkbox.spec.d.ts +2 -0
  110. package/dist/primitives/Checkbox/Checkbox.spec.d.ts.map +1 -0
  111. package/dist/primitives/Checkbox/Checkbox.spec.js +252 -0
  112. package/dist/primitives/DarkModeToggle.spec.js +84 -51
  113. package/dist/primitives/Drawer/Drawer.spec.d.ts +2 -0
  114. package/dist/primitives/Drawer/Drawer.spec.d.ts.map +1 -0
  115. package/dist/primitives/Drawer/Drawer.spec.js +212 -0
  116. package/dist/primitives/Dropdown/Dropdown.spec.d.ts +2 -0
  117. package/dist/primitives/Dropdown/Dropdown.spec.d.ts.map +1 -0
  118. package/dist/primitives/Dropdown/Dropdown.spec.js +366 -0
  119. package/dist/primitives/Dropdown/DropdownItem.spec.d.ts +2 -0
  120. package/dist/primitives/Dropdown/DropdownItem.spec.d.ts.map +1 -0
  121. package/dist/primitives/Dropdown/DropdownItem.spec.js +182 -0
  122. package/dist/primitives/Icons/iconTestUtils.spec.d.ts +2 -0
  123. package/dist/primitives/Icons/iconTestUtils.spec.d.ts.map +1 -0
  124. package/dist/primitives/Icons/iconTestUtils.spec.js +235 -0
  125. package/dist/primitives/Input/Input.spec.js +14 -14
  126. package/dist/primitives/Input/Input.svelte +1 -14
  127. package/dist/primitives/Input/Input.svelte.d.ts.map +1 -1
  128. package/dist/primitives/Input/Select.spec.js +11 -17
  129. package/dist/primitives/Input/Textarea.spec.d.ts +2 -0
  130. package/dist/primitives/Input/Textarea.spec.d.ts.map +1 -0
  131. package/dist/primitives/Input/Textarea.spec.js +255 -0
  132. package/dist/primitives/Label/Label.spec.d.ts +2 -0
  133. package/dist/primitives/Label/Label.spec.d.ts.map +1 -0
  134. package/dist/primitives/Label/Label.spec.js +157 -0
  135. package/dist/primitives/Modal/Modal.spec.js +29 -25
  136. package/dist/primitives/Modal/ModalTestWrapper.svelte +65 -0
  137. package/dist/primitives/Modal/ModalTestWrapper.svelte.d.ts +23 -0
  138. package/dist/primitives/Modal/ModalTestWrapper.svelte.d.ts.map +1 -0
  139. package/dist/primitives/NumberInput/NumberInput.spec.d.ts +2 -0
  140. package/dist/primitives/NumberInput/NumberInput.spec.d.ts.map +1 -0
  141. package/dist/primitives/NumberInput/NumberInput.spec.js +235 -0
  142. package/dist/primitives/Pagination/Pagination.spec.d.ts +2 -0
  143. package/dist/primitives/Pagination/Pagination.spec.d.ts.map +1 -0
  144. package/dist/primitives/Pagination/Pagination.spec.js +266 -0
  145. package/dist/primitives/Radio/Radio.spec.d.ts +2 -0
  146. package/dist/primitives/Radio/Radio.spec.d.ts.map +1 -0
  147. package/dist/primitives/Radio/Radio.spec.js +206 -0
  148. package/dist/primitives/Skeleton/CardPlaceholder.spec.d.ts +2 -0
  149. package/dist/primitives/Skeleton/CardPlaceholder.spec.d.ts.map +1 -0
  150. package/dist/primitives/Skeleton/CardPlaceholder.spec.js +156 -0
  151. package/dist/primitives/Skeleton/ImagePlaceholder.spec.d.ts +2 -0
  152. package/dist/primitives/Skeleton/ImagePlaceholder.spec.d.ts.map +1 -0
  153. package/dist/primitives/Skeleton/ImagePlaceholder.spec.js +120 -0
  154. package/dist/primitives/Skeleton/ListPlaceholder.spec.d.ts +2 -0
  155. package/dist/primitives/Skeleton/ListPlaceholder.spec.d.ts.map +1 -0
  156. package/dist/primitives/Skeleton/ListPlaceholder.spec.js +220 -0
  157. package/dist/primitives/Skeleton/Skeleton.spec.d.ts +2 -0
  158. package/dist/primitives/Skeleton/Skeleton.spec.d.ts.map +1 -0
  159. package/dist/primitives/Skeleton/Skeleton.spec.js +173 -0
  160. package/dist/primitives/Spinner/Spinner.spec.js +25 -29
  161. package/dist/primitives/Tabs/TabItem.spec.d.ts +2 -0
  162. package/dist/primitives/Tabs/TabItem.spec.d.ts.map +1 -0
  163. package/dist/primitives/Tabs/TabItem.spec.js +130 -0
  164. package/dist/primitives/Tabs/Tabs.spec.d.ts +2 -0
  165. package/dist/primitives/Tabs/Tabs.spec.d.ts.map +1 -0
  166. package/dist/primitives/Tabs/Tabs.spec.js +295 -0
  167. package/dist/primitives/Tabs/TabsWithItems.test.svelte +18 -0
  168. package/dist/primitives/Tabs/TabsWithItems.test.svelte.d.ts +16 -0
  169. package/dist/primitives/Tabs/TabsWithItems.test.svelte.d.ts.map +1 -0
  170. package/dist/primitives/Toggle.spec.js +93 -77
  171. package/dist/primitives/Typography/Typography.spec.d.ts +2 -0
  172. package/dist/primitives/Typography/Typography.spec.d.ts.map +1 -0
  173. package/dist/primitives/Typography/Typography.spec.js +183 -0
  174. package/dist/primitives/ValidationError.spec.js +1 -1
  175. package/dist/primitives/index.d.ts +1 -0
  176. package/dist/primitives/index.js +3 -0
  177. package/dist/recipes/CropImage/CropImage.spec.js +1 -9
  178. package/dist/recipes/ImageUploader/ImageUploader.spec.d.ts +2 -0
  179. package/dist/recipes/ImageUploader/ImageUploader.spec.d.ts.map +1 -0
  180. package/dist/recipes/ImageUploader/ImageUploader.spec.js +1351 -0
  181. package/dist/recipes/SuperLogin/SuperLogin.spec.d.ts +2 -0
  182. package/dist/recipes/SuperLogin/SuperLogin.spec.d.ts.map +1 -0
  183. package/dist/recipes/SuperLogin/SuperLogin.spec.js +1436 -0
  184. package/dist/recipes/feedback/EmptyState/EmptyState.spec.d.ts +2 -0
  185. package/dist/recipes/feedback/EmptyState/EmptyState.spec.d.ts.map +1 -0
  186. package/dist/recipes/feedback/EmptyState/EmptyState.spec.js +202 -0
  187. package/dist/recipes/feedback/ErrorDisplay.spec.js +6 -6
  188. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.spec.js +21 -17
  189. package/dist/recipes/fields/CheckboxField.spec.d.ts +2 -0
  190. package/dist/recipes/fields/CheckboxField.spec.d.ts.map +1 -0
  191. package/dist/recipes/fields/CheckboxField.spec.js +135 -0
  192. package/dist/recipes/fields/FormField.spec.d.ts +2 -0
  193. package/dist/recipes/fields/FormField.spec.d.ts.map +1 -0
  194. package/dist/recipes/fields/FormField.spec.js +159 -0
  195. package/dist/recipes/fields/RadioGroup.spec.d.ts +2 -0
  196. package/dist/recipes/fields/RadioGroup.spec.d.ts.map +1 -0
  197. package/dist/recipes/fields/RadioGroup.spec.js +199 -0
  198. package/dist/recipes/fields/SelectField.spec.d.ts +2 -0
  199. package/dist/recipes/fields/SelectField.spec.d.ts.map +1 -0
  200. package/dist/recipes/fields/SelectField.spec.js +188 -0
  201. package/dist/recipes/fields/TextareaField.spec.d.ts +2 -0
  202. package/dist/recipes/fields/TextareaField.spec.d.ts.map +1 -0
  203. package/dist/recipes/fields/TextareaField.spec.js +205 -0
  204. package/dist/recipes/fields/ToggleField.spec.d.ts +2 -0
  205. package/dist/recipes/fields/ToggleField.spec.d.ts.map +1 -0
  206. package/dist/recipes/fields/ToggleField.spec.js +153 -0
  207. package/dist/recipes/inputs/MultiSelect.spec.js +4 -3
  208. package/dist/recipes/inputs/MultiSelect.svelte +10 -3
  209. package/dist/recipes/inputs/MultiSelect.svelte.d.ts +2 -0
  210. package/dist/recipes/inputs/MultiSelect.svelte.d.ts.map +1 -1
  211. package/dist/recipes/inputs/OTPInput.spec.js +52 -39
  212. package/dist/recipes/inputs/PasswordInput.spec.d.ts +2 -0
  213. package/dist/recipes/inputs/PasswordInput.spec.d.ts.map +1 -0
  214. package/dist/recipes/inputs/PasswordInput.spec.js +410 -0
  215. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.spec.js +253 -173
  216. package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte +71 -0
  217. package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte.d.ts +9 -0
  218. package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte.d.ts.map +1 -0
  219. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.spec.js +1246 -300
  220. package/dist/recipes/inputs/Search.spec.d.ts +2 -0
  221. package/dist/recipes/inputs/Search.spec.d.ts.map +1 -0
  222. package/dist/recipes/inputs/Search.spec.js +177 -0
  223. package/dist/recipes/inputs/SelectDropdown.spec.d.ts +2 -0
  224. package/dist/recipes/inputs/SelectDropdown.spec.d.ts.map +1 -0
  225. package/dist/recipes/inputs/SelectDropdown.spec.js +512 -0
  226. package/dist/recipes/modals/AlertModal.spec.d.ts +2 -0
  227. package/dist/recipes/modals/AlertModal.spec.d.ts.map +1 -0
  228. package/dist/recipes/modals/AlertModal.spec.js +432 -0
  229. package/dist/recipes/modals/ConfirmationModal.spec.js +36 -21
  230. package/dist/recipes/modals/InputModal.spec.d.ts +2 -0
  231. package/dist/recipes/modals/InputModal.spec.d.ts.map +1 -0
  232. package/dist/recipes/modals/InputModal.spec.js +872 -0
  233. package/dist/recipes/modals/ModalTestWrapper.spec.d.ts +2 -0
  234. package/dist/recipes/modals/ModalTestWrapper.spec.d.ts.map +1 -0
  235. package/dist/recipes/modals/ModalTestWrapper.spec.js +502 -0
  236. package/dist/recipes/modals/StatusModal.spec.d.ts +2 -0
  237. package/dist/recipes/modals/StatusModal.spec.d.ts.map +1 -0
  238. package/dist/recipes/modals/StatusModal.spec.js +599 -0
  239. package/dist/services/ShowService.spec.js +18 -15
  240. package/dist/stories/ButtonAuditDashboard.spec.d.ts +2 -0
  241. package/dist/stories/ButtonAuditDashboard.spec.d.ts.map +1 -0
  242. package/dist/stories/ButtonAuditDashboard.spec.js +913 -0
  243. package/dist/stories/ButtonAuditReview.spec.d.ts +2 -0
  244. package/dist/stories/ButtonAuditReview.spec.d.ts.map +1 -0
  245. package/dist/stories/ButtonAuditReview.spec.js +422 -0
  246. package/dist/stories/ButtonGridView.spec.d.ts +2 -0
  247. package/dist/stories/ButtonGridView.spec.d.ts.map +1 -0
  248. package/dist/stories/ButtonGridView.spec.js +667 -0
  249. package/dist/stories/ButtonShowcase.spec.d.ts +2 -0
  250. package/dist/stories/ButtonShowcase.spec.d.ts.map +1 -0
  251. package/dist/stories/ButtonShowcase.spec.js +499 -0
  252. package/dist/stories/PatternsGallery.spec.d.ts +2 -0
  253. package/dist/stories/PatternsGallery.spec.d.ts.map +1 -0
  254. package/dist/stories/PatternsGallery.spec.js +514 -0
  255. package/dist/stories/PrimitivesGallery.spec.d.ts +2 -0
  256. package/dist/stories/PrimitivesGallery.spec.d.ts.map +1 -0
  257. package/dist/stories/PrimitivesGallery.spec.js +813 -0
  258. package/dist/stories/RecipesGallery.spec.d.ts +2 -0
  259. package/dist/stories/RecipesGallery.spec.d.ts.map +1 -0
  260. package/dist/stories/RecipesGallery.spec.js +299 -0
  261. package/dist/stripe/useStripeTheme.spec.d.ts +2 -0
  262. package/dist/stripe/useStripeTheme.spec.d.ts.map +1 -0
  263. package/dist/stripe/useStripeTheme.spec.js +793 -0
  264. package/dist/telemetry.d.ts.map +1 -1
  265. package/dist/telemetry.js +6 -5
  266. package/dist/telemetry.spec.js +495 -12
  267. package/dist/tokens/__tests__/colors.test.d.ts +2 -0
  268. package/dist/tokens/__tests__/colors.test.d.ts.map +1 -0
  269. package/dist/tokens/__tests__/colors.test.js +152 -0
  270. package/dist/tokens/__tests__/radius.test.d.ts +2 -0
  271. package/dist/tokens/__tests__/radius.test.d.ts.map +1 -0
  272. package/dist/tokens/__tests__/radius.test.js +118 -0
  273. package/dist/tokens/__tests__/shadows.test.d.ts +2 -0
  274. package/dist/tokens/__tests__/shadows.test.d.ts.map +1 -0
  275. package/dist/tokens/__tests__/shadows.test.js +105 -0
  276. package/dist/tokens/__tests__/spacing.test.js +11 -8
  277. package/dist/tokens/__tests__/typography.test.d.ts +2 -0
  278. package/dist/tokens/__tests__/typography.test.d.ts.map +1 -0
  279. package/dist/tokens/__tests__/typography.test.js +156 -0
  280. package/dist/tokens/__tests__/z-index.test.d.ts +2 -0
  281. package/dist/tokens/__tests__/z-index.test.d.ts.map +1 -0
  282. package/dist/tokens/__tests__/z-index.test.js +121 -0
  283. package/dist/utils/apiConfig.spec.js +102 -1
  284. package/dist/utils/formatters.spec.d.ts +2 -0
  285. package/dist/utils/formatters.spec.d.ts.map +1 -0
  286. package/dist/utils/formatters.spec.js +82 -0
  287. package/dist/utils/transitions.spec.d.ts +2 -0
  288. package/dist/utils/transitions.spec.d.ts.map +1 -0
  289. package/dist/utils/transitions.spec.js +130 -0
  290. 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
+ });