@jmruthers/pace-core 0.5.87 → 0.5.89

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 (243) hide show
  1. package/dist/{AuthService-Df3IozMG.d.ts → AuthService-DcTI5Ov4.d.ts} +9 -0
  2. package/dist/{DataTable-FA6EUX5M.js → DataTable-PWBMKMOG.js} +7 -7
  3. package/dist/{PublicLoadingSpinner-DecuJBX0.d.ts → PublicLoadingSpinner-BQXD1fbO.d.ts} +160 -130
  4. package/dist/{UnifiedAuthProvider-K2IZAY5F.js → UnifiedAuthProvider-5D3HEQND.js} +4 -4
  5. package/dist/{UnifiedAuthProvider-B391Aqum.d.ts → UnifiedAuthProvider-BVKmQd9u.d.ts} +4 -0
  6. package/dist/auth-DReDSLq9.d.ts +16 -0
  7. package/dist/{chunk-CBSD3BZ3.js → chunk-3RZBKQ5Y.js} +2 -6
  8. package/dist/{chunk-CBSD3BZ3.js.map → chunk-3RZBKQ5Y.js.map} +1 -1
  9. package/dist/{chunk-NTW3KGS4.js → chunk-6UHXQH7P.js} +5 -5
  10. package/dist/{chunk-ZFLOV3OM.js → chunk-7VJDS5QD.js} +401 -16
  11. package/dist/chunk-7VJDS5QD.js.map +1 -0
  12. package/dist/{chunk-YVUZWLQG.js → chunk-AQGF5OG7.js} +3 -3
  13. package/dist/{chunk-CVMVPYAL.js → chunk-BDZUMRBD.js} +3 -5
  14. package/dist/chunk-BDZUMRBD.js.map +1 -0
  15. package/dist/{chunk-KAY3K5TP.js → chunk-BNXBJOGL.js} +4 -4
  16. package/dist/{chunk-S3JKDMD5.js → chunk-CXKMRKRF.js} +4 -4
  17. package/dist/{chunk-5BN3YGNK.js → chunk-DP5X5ORK.js} +217 -27
  18. package/dist/chunk-DP5X5ORK.js.map +1 -0
  19. package/dist/{chunk-RIXPZJUB.js → chunk-KTPG5VCH.js} +2 -2
  20. package/dist/{chunk-2FQEQUJT.js → chunk-KWICIQVK.js} +4 -4
  21. package/dist/{chunk-WUXCWRL6.js → chunk-XJ2HZOBU.js} +6 -1
  22. package/dist/chunk-XJ2HZOBU.js.map +1 -0
  23. package/dist/{chunk-I7O3RSMN.js → chunk-YWAFPVJA.js} +1298 -769
  24. package/dist/chunk-YWAFPVJA.js.map +1 -0
  25. package/dist/{chunk-I2VVV5PQ.js → chunk-YY4YYM3E.js} +2 -2
  26. package/dist/components.d.ts +6 -55
  27. package/dist/components.js +24 -205
  28. package/dist/components.js.map +1 -1
  29. package/dist/{file-reference-9xUOnwyt.d.ts → file-reference-C9isKNPn.d.ts} +67 -2
  30. package/dist/hooks.js +9 -8
  31. package/dist/hooks.js.map +1 -1
  32. package/dist/index.d.ts +152 -26
  33. package/dist/index.js +64 -194
  34. package/dist/index.js.map +1 -1
  35. package/dist/providers.d.ts +5 -3
  36. package/dist/providers.js +3 -3
  37. package/dist/rbac/index.js +8 -8
  38. package/dist/types.d.ts +2 -1
  39. package/dist/types.js +3 -3
  40. package/dist/utils.js +2 -2
  41. package/docs/DOCUMENTATION_AUDIT.md +6 -6
  42. package/docs/DOCUMENTATION_STANDARD.md +137 -0
  43. package/docs/README.md +1 -1
  44. package/docs/api/classes/ColumnFactory.md +1 -1
  45. package/docs/api/classes/ErrorBoundary.md +1 -1
  46. package/docs/api/classes/InvalidScopeError.md +1 -1
  47. package/docs/api/classes/MissingUserContextError.md +1 -1
  48. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  49. package/docs/api/classes/PermissionDeniedError.md +1 -1
  50. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  51. package/docs/api/classes/RBACAuditManager.md +1 -1
  52. package/docs/api/classes/RBACCache.md +1 -1
  53. package/docs/api/classes/RBACEngine.md +1 -1
  54. package/docs/api/classes/RBACError.md +1 -1
  55. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  56. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  57. package/docs/api/classes/StorageUtils.md +83 -40
  58. package/docs/api/enums/FileCategory.md +56 -1
  59. package/docs/api/interfaces/AggregateConfig.md +1 -1
  60. package/docs/api/interfaces/ButtonProps.md +1 -1
  61. package/docs/api/interfaces/CardProps.md +1 -1
  62. package/docs/api/interfaces/ColorPalette.md +1 -1
  63. package/docs/api/interfaces/ColorShade.md +1 -1
  64. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  65. package/docs/api/interfaces/DataRecord.md +1 -1
  66. package/docs/api/interfaces/DataTableAction.md +1 -1
  67. package/docs/api/interfaces/DataTableColumn.md +1 -1
  68. package/docs/api/interfaces/DataTableProps.md +1 -1
  69. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  70. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  71. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  72. package/docs/api/interfaces/EventLogoProps.md +11 -11
  73. package/docs/api/interfaces/FileDisplayProps.md +10 -10
  74. package/docs/api/interfaces/FileMetadata.md +1 -1
  75. package/docs/api/interfaces/FileReference.md +1 -1
  76. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  77. package/docs/api/interfaces/FileUploadOptions.md +8 -8
  78. package/docs/api/interfaces/FileUploadProps.md +137 -42
  79. package/docs/api/interfaces/FooterProps.md +1 -1
  80. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  81. package/docs/api/interfaces/InputProps.md +1 -1
  82. package/docs/api/interfaces/LabelProps.md +1 -1
  83. package/docs/api/interfaces/LoginFormProps.md +1 -1
  84. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  85. package/docs/api/interfaces/NavigationContextType.md +1 -1
  86. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  87. package/docs/api/interfaces/NavigationItem.md +1 -1
  88. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  89. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  90. package/docs/api/interfaces/Organisation.md +1 -1
  91. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  92. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  93. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  94. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  95. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  96. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  97. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  98. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  99. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  100. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  101. package/docs/api/interfaces/PaletteData.md +1 -1
  102. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  103. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  104. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  105. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  106. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  107. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  108. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  109. package/docs/api/interfaces/RBACConfig.md +1 -1
  110. package/docs/api/interfaces/RBACLogger.md +1 -1
  111. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  112. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  113. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  114. package/docs/api/interfaces/RouteConfig.md +1 -1
  115. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  116. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  117. package/docs/api/interfaces/StorageConfig.md +1 -1
  118. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  119. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  120. package/docs/api/interfaces/StorageListOptions.md +1 -1
  121. package/docs/api/interfaces/StorageListResult.md +1 -1
  122. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  123. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  124. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  125. package/docs/api/interfaces/StyleImport.md +1 -1
  126. package/docs/api/interfaces/SwitchProps.md +1 -1
  127. package/docs/api/interfaces/ToastActionElement.md +1 -1
  128. package/docs/api/interfaces/ToastProps.md +1 -1
  129. package/docs/api/interfaces/UnifiedAuthContextType.md +83 -50
  130. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  131. package/docs/api/interfaces/UseEventLogoOptions.md +74 -0
  132. package/docs/api/interfaces/UseEventLogoReturn.md +81 -0
  133. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  134. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  135. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  136. package/docs/api/interfaces/UsePublicEventLogoReturn.md +6 -6
  137. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  138. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  139. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  140. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  141. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  142. package/docs/api/interfaces/UserEventAccess.md +11 -11
  143. package/docs/api/interfaces/UserMenuProps.md +1 -1
  144. package/docs/api/interfaces/UserProfile.md +1 -1
  145. package/docs/api/modules.md +290 -95
  146. package/docs/api-reference/components.md +1 -18
  147. package/docs/api-reference/hooks.md +1 -4
  148. package/docs/best-practices/testing.md +2 -0
  149. package/docs/documentation-index.md +1 -1
  150. package/docs/getting-started/faq.md +1 -1
  151. package/docs/implementation-guides/file-reference-system.md +592 -58
  152. package/docs/implementation-guides/file-upload-storage.md +137 -73
  153. package/docs/implementation-guides/public-pages-advanced.md +10 -0
  154. package/docs/rbac/super-admin-guide.md +18 -70
  155. package/docs/testing/README.md +2 -0
  156. package/package.json +1 -1
  157. package/src/__tests__/TEST_STANDARD.md +674 -0
  158. package/src/__tests__/helpers/test-utils.tsx +3 -2
  159. package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx.skip → DataTable.comprehensive.test.tsx} +17 -18
  160. package/src/components/DataTable/__tests__/{DataTable.test.tsx.skip → DataTable.test.tsx} +14 -22
  161. package/src/components/DataTable/__tests__/{ssr.strict-mode.test.tsx.skip → ssr.strict-mode.test.tsx} +42 -47
  162. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +1 -1
  163. package/src/components/DataTable/examples/__tests__/PerformanceExample.test.tsx +13 -4
  164. package/src/components/DataTable/utils/__tests__/COVERAGE_NOTE.md +1 -1
  165. package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +10 -6
  166. package/src/components/FileDisplay/FileDisplay.test.tsx +257 -0
  167. package/src/components/{FileDisplay.tsx → FileDisplay/FileDisplay.tsx} +111 -10
  168. package/src/components/FileDisplay/index.tsx +4 -0
  169. package/src/components/FileUpload/FileUpload.test.tsx +171 -621
  170. package/src/components/FileUpload/FileUpload.tsx +512 -168
  171. package/src/components/FileUpload/index.tsx +4 -0
  172. package/src/components/Progress/Progress.test.tsx +38 -0
  173. package/src/components/PublicLayout/EventLogo.tsx +6 -4
  174. package/src/components/Select/Select.test.tsx +1 -1
  175. package/src/components/SessionRestorationLoader.tsx +48 -0
  176. package/src/components/Toast/Toast.tsx +13 -8
  177. package/src/components/index.ts +16 -16
  178. package/src/hooks/__tests__/ServiceHooks.test.tsx +615 -0
  179. package/src/hooks/public/usePublicEventLogo.ts +16 -20
  180. package/src/hooks/useEventLogo.ts +316 -0
  181. package/src/hooks/useEvents.ts +0 -5
  182. package/src/hooks/useFileReference.test.ts +659 -0
  183. package/src/hooks/useFileReference.ts +207 -3
  184. package/src/hooks/useSessionRestoration.ts +64 -0
  185. package/src/index.ts +17 -5
  186. package/src/providers/{UnifiedAuthProvider.test.simple.tsx → UnifiedAuthProvider.smoke.test.tsx} +81 -60
  187. package/src/providers/services/AuthServiceProvider.tsx +27 -3
  188. package/src/providers/services/UnifiedAuthProvider.tsx +34 -5
  189. package/src/rbac/{engine.test.simple.ts → RBACEngine.smoke.test.ts} +17 -12
  190. package/src/services/AuthService.ts +142 -20
  191. package/src/services/EventService.ts +0 -4
  192. package/src/types/auth.ts +15 -0
  193. package/src/types/file-reference.ts +73 -1
  194. package/src/types/index.ts +1 -0
  195. package/src/utils/__tests__/organisationContext.unit.test.ts +2 -4
  196. package/src/utils/appNameResolver.simple.test.ts +99 -29
  197. package/src/utils/file-reference.test.ts +535 -0
  198. package/src/utils/file-reference.ts +200 -30
  199. package/src/utils/organisationContext.test.ts +5 -19
  200. package/src/utils/organisationContext.ts +3 -5
  201. package/src/utils/storage/README.md +269 -262
  202. package/src/utils/storage/config.ts +9 -0
  203. package/src/utils/storage/helpers.test.ts +735 -0
  204. package/src/utils/storage/helpers.ts +189 -16
  205. package/src/utils/storage/index.ts +3 -0
  206. package/src/validation/__tests__/sanitization.unit.test.ts +1 -1
  207. package/src/validation/__tests__/schemaUtils.unit.test.ts +1 -1
  208. package/src/validation/__tests__/user.unit.test.ts +1 -1
  209. package/dist/chunk-5BN3YGNK.js.map +0 -1
  210. package/dist/chunk-CVMVPYAL.js.map +0 -1
  211. package/dist/chunk-I7O3RSMN.js.map +0 -1
  212. package/dist/chunk-WUXCWRL6.js.map +0 -1
  213. package/dist/chunk-ZFLOV3OM.js.map +0 -1
  214. package/docs/CONTENT_AUDIT_REPORT.md +0 -253
  215. package/docs/STYLE_GUIDE.md +0 -37
  216. package/examples/RBAC/__tests__/PermissionExample.test.tsx +0 -150
  217. package/examples/public-pages/__tests__/PublicPageUsageExample.test.tsx +0 -159
  218. package/src/__tests__/TEST_GUIDE_CURSOR.md +0 -1605
  219. package/src/__tests__/TEST_GUIDE_HUMAN.md +0 -103
  220. package/src/components/FileUpload/FileUpload.example.tsx +0 -218
  221. package/src/components/FileUpload/index.ts +0 -6
  222. package/src/components/FileUpload.tsx +0 -176
  223. package/src/components/Progress/index.ts +0 -3
  224. package/src/components/PublicLayout/__tests__/EventLogo.test.tsx +0 -666
  225. package/src/components/SuperAdminGuard.tsx +0 -116
  226. package/src/components/__tests__/FileDisplay.test.tsx +0 -575
  227. package/src/components/__tests__/FileUpload.test.tsx +0 -446
  228. package/src/components/__tests__/SuperAdminGuard.test.tsx +0 -627
  229. package/src/components/examples/PermissionExample.tsx +0 -173
  230. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -583
  231. package/src/hooks/__tests__/usePublicEventLogo.unit.test.ts +0 -640
  232. package/src/types/__tests__/file-reference.test.ts +0 -447
  233. package/src/utils/__tests__/file-reference.test.ts +0 -383
  234. /package/dist/{DataTable-FA6EUX5M.js.map → DataTable-PWBMKMOG.js.map} +0 -0
  235. /package/dist/{UnifiedAuthProvider-K2IZAY5F.js.map → UnifiedAuthProvider-5D3HEQND.js.map} +0 -0
  236. /package/dist/{chunk-NTW3KGS4.js.map → chunk-6UHXQH7P.js.map} +0 -0
  237. /package/dist/{chunk-YVUZWLQG.js.map → chunk-AQGF5OG7.js.map} +0 -0
  238. /package/dist/{chunk-KAY3K5TP.js.map → chunk-BNXBJOGL.js.map} +0 -0
  239. /package/dist/{chunk-S3JKDMD5.js.map → chunk-CXKMRKRF.js.map} +0 -0
  240. /package/dist/{chunk-RIXPZJUB.js.map → chunk-KTPG5VCH.js.map} +0 -0
  241. /package/dist/{chunk-2FQEQUJT.js.map → chunk-KWICIQVK.js.map} +0 -0
  242. /package/dist/{chunk-I2VVV5PQ.js.map → chunk-YY4YYM3E.js.map} +0 -0
  243. /package/src/providers/{OrganisationProvider.test.simple.tsx → OrganisationProvider.context.test.tsx} +0 -0
@@ -1,640 +0,0 @@
1
- import React from 'react';
2
- import { renderHook, waitFor, act } from '@testing-library/react';
3
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
4
- import { usePublicEventLogo, clearPublicLogoCache, getPublicLogoCacheStats } from '../public/usePublicEventLogo';
5
- import type { SupabaseClient } from '@supabase/supabase-js';
6
- import type { Database } from '../../types/database';
7
-
8
- // Mock Supabase client
9
- const mockSupabaseClient = {
10
- rpc: vi.fn()
11
- } as unknown as SupabaseClient<Database>;
12
-
13
- // Mock fetch for image validation
14
- const mockFetch = vi.fn();
15
- global.fetch = mockFetch;
16
-
17
- describe('usePublicEventLogo', () => {
18
- beforeEach(() => {
19
- vi.clearAllMocks();
20
- clearPublicLogoCache();
21
- });
22
-
23
- afterEach(() => {
24
- clearPublicLogoCache();
25
- });
26
-
27
- describe('Basic Functionality', () => {
28
- it('should initialize with loading state false when no eventId', () => {
29
- const { result } = renderHook(() =>
30
- usePublicEventLogo(undefined, 'Test Event', 'org-123', { supabase: mockSupabaseClient })
31
- );
32
-
33
- expect(result.current.isLoading).toBe(false);
34
- expect(result.current.logoUrl).toBe(null);
35
- expect(result.current.error).toBe(null);
36
- expect(result.current.fallbackText).toBe('TE');
37
- });
38
-
39
- it('should initialize with loading state false when no organisationId', () => {
40
- const { result } = renderHook(() =>
41
- usePublicEventLogo('event-123', 'Test Event', undefined, { supabase: mockSupabaseClient })
42
- );
43
-
44
- expect(result.current.isLoading).toBe(false);
45
- expect(result.current.logoUrl).toBe(null);
46
- expect(result.current.error).toBe(null);
47
- expect(result.current.fallbackText).toBe('TE');
48
- });
49
-
50
- it('should initialize with loading state false when no supabase client', () => {
51
- const { result } = renderHook(() =>
52
- usePublicEventLogo('event-123', 'Test Event', 'org-123', { supabase: null as any })
53
- );
54
-
55
- expect(result.current.isLoading).toBe(false);
56
- expect(result.current.logoUrl).toBe(null);
57
- expect(result.current.error).toBe(null);
58
- expect(result.current.fallbackText).toBe('TE');
59
- });
60
-
61
- it('should fetch logo successfully', async () => {
62
- const mockLogoData = {
63
- logo_url: 'https://example.com/logo.png'
64
- };
65
-
66
- mockSupabaseClient.rpc.mockResolvedValueOnce({
67
- data: [mockLogoData],
68
- error: null
69
- });
70
-
71
- mockFetch.mockResolvedValueOnce({
72
- ok: true,
73
- status: 200
74
- });
75
-
76
- const { result } = renderHook(() =>
77
- usePublicEventLogo('event-123', 'Test Event', 'org-123', { supabase: mockSupabaseClient })
78
- );
79
-
80
- await waitFor(() => {
81
- expect(result.current.isLoading).toBe(false);
82
- }, { interval: 10 });
83
-
84
- expect(result.current.logoUrl).toBe('https://example.com/logo.png');
85
- expect(result.current.fallbackText).toBe('TE');
86
- expect(result.current.error).toBe(null);
87
- });
88
-
89
- it('should generate fallback text correctly', () => {
90
- const { result } = renderHook(() =>
91
- usePublicEventLogo('event-123', 'Test Event Name', 'org-123', { supabase: mockSupabaseClient })
92
- );
93
-
94
- expect(result.current.fallbackText).toBe('TEN');
95
- });
96
-
97
- it('should handle single word event name', () => {
98
- const { result } = renderHook(() =>
99
- usePublicEventLogo('event-123', 'Event', 'org-123', { supabase: mockSupabaseClient })
100
- );
101
-
102
- expect(result.current.fallbackText).toBe('E');
103
- });
104
-
105
- it('should handle empty event name', () => {
106
- const { result } = renderHook(() =>
107
- usePublicEventLogo('event-123', '', 'org-123', { supabase: mockSupabaseClient })
108
- );
109
-
110
- expect(result.current.fallbackText).toBe('EV');
111
- });
112
-
113
- it('should handle undefined event name', () => {
114
- const { result } = renderHook(() =>
115
- usePublicEventLogo('event-123', undefined, 'org-123', { supabase: mockSupabaseClient })
116
- );
117
-
118
- expect(result.current.fallbackText).toBe('EV');
119
- });
120
-
121
- it('should limit fallback text to 3 characters', () => {
122
- const { result } = renderHook(() =>
123
- usePublicEventLogo('event-123', 'Very Long Event Name', 'org-123', { supabase: mockSupabaseClient })
124
- );
125
-
126
- expect(result.current.fallbackText).toBe('VLE');
127
- });
128
- });
129
-
130
- describe('Caching', () => {
131
- it('should cache logo data', async () => {
132
- const mockLogoData = {
133
- logo_url: 'https://example.com/logo.png'
134
- };
135
-
136
- mockSupabaseClient.rpc.mockResolvedValueOnce({
137
- data: [mockLogoData],
138
- error: null
139
- });
140
-
141
- mockFetch.mockResolvedValueOnce({
142
- ok: true,
143
- status: 200
144
- });
145
-
146
- const { result, rerender } = renderHook(() =>
147
- usePublicEventLogo('event-123', 'Test Event', 'org-123', { supabase: mockSupabaseClient })
148
- );
149
-
150
- await waitFor(() => {
151
- expect(result.current.isLoading).toBe(false);
152
- }, { interval: 10 });
153
-
154
- // Rerender with same parameters - should use cache
155
- rerender();
156
-
157
- // Should not call RPC again
158
- expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(1);
159
- });
160
-
161
- it('should respect cache TTL', async () => {
162
- const mockLogoData = {
163
- logo_url: 'https://example.com/logo.png'
164
- };
165
-
166
- mockSupabaseClient.rpc.mockResolvedValue({
167
- data: [mockLogoData],
168
- error: null
169
- });
170
-
171
- mockFetch.mockResolvedValue({
172
- ok: true,
173
- status: 200
174
- });
175
-
176
- const { result } = renderHook(() =>
177
- usePublicEventLogo('event-123', 'Test Event', 'org-123', {
178
- supabase: mockSupabaseClient,
179
- cacheTtl: 100
180
- })
181
- );
182
-
183
- await waitFor(() => {
184
- expect(result.current.isLoading).toBe(false);
185
- }, { interval: 10 });
186
-
187
- // Wait for cache to expire
188
- await new Promise(resolve => setTimeout(resolve, 150));
189
-
190
- // Rerender should fetch again
191
- const { result: result2 } = renderHook(() =>
192
- usePublicEventLogo('event-123', 'Test Event', 'org-123', {
193
- supabase: mockSupabaseClient,
194
- cacheTtl: 100
195
- })
196
- );
197
-
198
- await waitFor(() => {
199
- expect(result2.current.isLoading).toBe(false);
200
- }, { interval: 10 });
201
-
202
- expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
203
- });
204
-
205
- it('should disable caching when requested', async () => {
206
- const mockLogoData = {
207
- logo_url: 'https://example.com/logo.png'
208
- };
209
-
210
- mockSupabaseClient.rpc.mockResolvedValueOnce({
211
- data: [mockLogoData],
212
- error: null
213
- });
214
-
215
- mockFetch.mockResolvedValueOnce({
216
- ok: true,
217
- status: 200
218
- });
219
-
220
- const { result } = renderHook(() =>
221
- usePublicEventLogo('event-123', 'Test Event', 'org-123', {
222
- supabase: mockSupabaseClient,
223
- enableCache: false
224
- })
225
- );
226
-
227
- await waitFor(() => {
228
- expect(result.current.isLoading).toBe(false);
229
- }, { interval: 10 });
230
-
231
- // When caching is disabled, the hook should not use cache
232
- // but it also doesn't refetch on rerender - only on parameter changes
233
- expect(result.current.logoUrl).toBe('https://example.com/logo.png');
234
- expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(1);
235
- });
236
- });
237
-
238
- describe('Error Handling', () => {
239
- it('should handle RPC errors', async () => {
240
- mockSupabaseClient.rpc.mockResolvedValueOnce({
241
- data: null,
242
- error: { message: 'Database error' }
243
- });
244
-
245
- const { result } = renderHook(() =>
246
- usePublicEventLogo('event-123', 'Test Event', 'org-123', { supabase: mockSupabaseClient })
247
- );
248
-
249
- await waitFor(() => {
250
- expect(result.current.isLoading).toBe(false);
251
- }, { interval: 10 });
252
-
253
- expect(result.current.error).toEqual(new Error('Database error'));
254
- expect(result.current.logoUrl).toBe(null);
255
- });
256
-
257
- it('should handle empty logo data', async () => {
258
- mockSupabaseClient.rpc.mockResolvedValueOnce({
259
- data: [],
260
- error: null
261
- });
262
-
263
- const { result } = renderHook(() =>
264
- usePublicEventLogo('event-123', 'Test Event', 'org-123', { supabase: mockSupabaseClient })
265
- );
266
-
267
- await waitFor(() => {
268
- expect(result.current.isLoading).toBe(false);
269
- }, { interval: 10 });
270
-
271
- expect(result.current.logoUrl).toBe(null);
272
- expect(result.current.error).toBe(null);
273
- });
274
-
275
- it('should handle null logo data', async () => {
276
- mockSupabaseClient.rpc.mockResolvedValueOnce({
277
- data: [null],
278
- error: null
279
- });
280
-
281
- const { result } = renderHook(() =>
282
- usePublicEventLogo('event-123', 'Test Event', 'org-123', { supabase: mockSupabaseClient })
283
- );
284
-
285
- await waitFor(() => {
286
- expect(result.current.isLoading).toBe(false);
287
- }, { interval: 10 });
288
-
289
- expect(result.current.logoUrl).toBe(null);
290
- expect(result.current.error).toBe(null);
291
- });
292
-
293
- it('should handle undefined logo URL', async () => {
294
- mockSupabaseClient.rpc.mockResolvedValueOnce({
295
- data: [{ logo_url: undefined }],
296
- error: null
297
- });
298
-
299
- const { result } = renderHook(() =>
300
- usePublicEventLogo('event-123', 'Test Event', 'org-123', { supabase: mockSupabaseClient })
301
- );
302
-
303
- await waitFor(() => {
304
- expect(result.current.isLoading).toBe(false);
305
- }, { interval: 10 });
306
-
307
- expect(result.current.logoUrl).toBe(null);
308
- expect(result.current.error).toBe(null);
309
- });
310
- });
311
-
312
- describe('Image Validation', () => {
313
- it('should validate image existence when enabled', async () => {
314
- const mockLogoData = {
315
- logo_url: 'https://example.com/logo.png'
316
- };
317
-
318
- mockSupabaseClient.rpc.mockResolvedValueOnce({
319
- data: [mockLogoData],
320
- error: null
321
- });
322
-
323
- mockFetch.mockResolvedValueOnce({
324
- ok: true,
325
- status: 200
326
- });
327
-
328
- const { result } = renderHook(() =>
329
- usePublicEventLogo('event-123', 'Test Event', 'org-123', {
330
- supabase: mockSupabaseClient,
331
- validateImage: true
332
- })
333
- );
334
-
335
- await waitFor(() => {
336
- expect(result.current.isLoading).toBe(false);
337
- }, { interval: 10 });
338
-
339
- expect(result.current.logoUrl).toBe('https://example.com/logo.png');
340
- expect(mockFetch).toHaveBeenCalledWith('https://example.com/logo.png', { method: 'HEAD' });
341
- });
342
-
343
- it('should skip image validation when disabled', async () => {
344
- const mockLogoData = {
345
- logo_url: 'https://example.com/logo.png'
346
- };
347
-
348
- mockSupabaseClient.rpc.mockResolvedValueOnce({
349
- data: [mockLogoData],
350
- error: null
351
- });
352
-
353
- const { result } = renderHook(() =>
354
- usePublicEventLogo('event-123', 'Test Event', 'org-123', {
355
- supabase: mockSupabaseClient,
356
- validateImage: false
357
- })
358
- );
359
-
360
- await waitFor(() => {
361
- expect(result.current.isLoading).toBe(false);
362
- }, { interval: 10 });
363
-
364
- expect(result.current.logoUrl).toBe('https://example.com/logo.png');
365
- expect(mockFetch).not.toHaveBeenCalled();
366
- });
367
-
368
- it('should handle image validation failure', async () => {
369
- const mockLogoData = {
370
- logo_url: 'https://example.com/logo.png'
371
- };
372
-
373
- mockSupabaseClient.rpc.mockResolvedValueOnce({
374
- data: [mockLogoData],
375
- error: null
376
- });
377
-
378
- mockFetch.mockResolvedValueOnce({
379
- ok: false,
380
- status: 404
381
- });
382
-
383
- const { result } = renderHook(() =>
384
- usePublicEventLogo('event-123', 'Test Event', 'org-123', {
385
- supabase: mockSupabaseClient,
386
- validateImage: true
387
- })
388
- );
389
-
390
- await waitFor(() => {
391
- expect(result.current.isLoading).toBe(false);
392
- }, { interval: 10 });
393
-
394
- expect(result.current.logoUrl).toBe(null);
395
- expect(result.current.error).toBe(null);
396
- });
397
-
398
- it('should handle image validation error', async () => {
399
- const mockLogoData = {
400
- logo_url: 'https://example.com/logo.png'
401
- };
402
-
403
- mockSupabaseClient.rpc.mockResolvedValueOnce({
404
- data: [mockLogoData],
405
- error: null
406
- });
407
-
408
- mockFetch.mockRejectedValueOnce(new Error('Network error'));
409
-
410
- const { result } = renderHook(() =>
411
- usePublicEventLogo('event-123', 'Test Event', 'org-123', {
412
- supabase: mockSupabaseClient,
413
- validateImage: true
414
- })
415
- );
416
-
417
- await waitFor(() => {
418
- expect(result.current.isLoading).toBe(false);
419
- }, { interval: 10 });
420
-
421
- expect(result.current.logoUrl).toBe(null);
422
- expect(result.current.error).toBe(null);
423
- });
424
- });
425
-
426
- describe('Custom Fallback Text Generator', () => {
427
- it('should use custom fallback text generator', () => {
428
- const customGenerator = (eventName: string) => `CUSTOM-${eventName.toUpperCase()}`;
429
-
430
- const { result } = renderHook(() =>
431
- usePublicEventLogo('event-123', 'Test Event', 'org-123', {
432
- supabase: mockSupabaseClient,
433
- generateFallbackText: customGenerator
434
- })
435
- );
436
-
437
- expect(result.current.fallbackText).toBe('CUSTOM-TEST EVENT');
438
- });
439
- });
440
-
441
- describe('Refetch Functionality', () => {
442
- it('should refetch logo when refetch is called', async () => {
443
- const mockLogoData = {
444
- logo_url: 'https://example.com/logo.png'
445
- };
446
-
447
- mockSupabaseClient.rpc.mockResolvedValue({
448
- data: [mockLogoData],
449
- error: null
450
- });
451
-
452
- mockFetch.mockResolvedValue({
453
- ok: true,
454
- status: 200
455
- });
456
-
457
- const { result } = renderHook(() =>
458
- usePublicEventLogo('event-123', 'Test Event', 'org-123', { supabase: mockSupabaseClient })
459
- );
460
-
461
- await waitFor(() => {
462
- expect(result.current.isLoading).toBe(false);
463
- }, { interval: 10 });
464
-
465
- // Call refetch
466
- await act(async () => {
467
- await result.current.refetch();
468
- });
469
-
470
- expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
471
- });
472
-
473
- it('should clear cache when refetch is called', async () => {
474
- const mockLogoData = {
475
- logo_url: 'https://example.com/logo.png'
476
- };
477
-
478
- mockSupabaseClient.rpc.mockResolvedValue({
479
- data: [mockLogoData],
480
- error: null
481
- });
482
-
483
- mockFetch.mockResolvedValue({
484
- ok: true,
485
- status: 200
486
- });
487
-
488
- const { result } = renderHook(() =>
489
- usePublicEventLogo('event-123', 'Test Event', 'org-123', { supabase: mockSupabaseClient })
490
- );
491
-
492
- await waitFor(() => {
493
- expect(result.current.isLoading).toBe(false);
494
- }, { interval: 10 });
495
-
496
- // Call refetch
497
- await act(async () => {
498
- await result.current.refetch();
499
- });
500
-
501
- // Should call RPC again (cache was cleared)
502
- expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
503
- });
504
-
505
- it('should not refetch when eventId is missing', async () => {
506
- const { result } = renderHook(() =>
507
- usePublicEventLogo(undefined, 'Test Event', 'org-123', { supabase: mockSupabaseClient })
508
- );
509
-
510
- await act(async () => {
511
- await result.current.refetch();
512
- });
513
-
514
- expect(mockSupabaseClient.rpc).not.toHaveBeenCalled();
515
- });
516
-
517
- it('should not refetch when organisationId is missing', async () => {
518
- const { result } = renderHook(() =>
519
- usePublicEventLogo('event-123', 'Test Event', undefined, { supabase: mockSupabaseClient })
520
- );
521
-
522
- await act(async () => {
523
- await result.current.refetch();
524
- });
525
-
526
- expect(mockSupabaseClient.rpc).not.toHaveBeenCalled();
527
- });
528
- });
529
-
530
- describe('Cache Management Utilities', () => {
531
- it('should clear public logo cache', () => {
532
- const stats = getPublicLogoCacheStats();
533
- expect(stats.size).toBe(0);
534
-
535
- clearPublicLogoCache();
536
-
537
- const statsAfter = getPublicLogoCacheStats();
538
- expect(statsAfter.size).toBe(0);
539
- });
540
-
541
- it('should get cache statistics', () => {
542
- const stats = getPublicLogoCacheStats();
543
- expect(stats).toEqual({
544
- size: 0,
545
- keys: []
546
- });
547
- });
548
- });
549
-
550
- describe('UUID Validation', () => {
551
- it('should warn about invalid organisationId format but still proceed', async () => {
552
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
553
-
554
- const mockLogoData = {
555
- logo_url: 'https://example.com/logo.png'
556
- };
557
-
558
- mockSupabaseClient.rpc.mockResolvedValueOnce({
559
- data: [mockLogoData],
560
- error: null
561
- });
562
-
563
- mockFetch.mockResolvedValueOnce({
564
- ok: true,
565
- status: 200
566
- });
567
-
568
- const { result } = renderHook(() =>
569
- usePublicEventLogo('event-123', 'Test Event', 'invalid-uuid', { supabase: mockSupabaseClient })
570
- );
571
-
572
- await waitFor(() => {
573
- expect(result.current.isLoading).toBe(false);
574
- }, { interval: 10 });
575
-
576
- expect(consoleSpy).toHaveBeenCalledWith(
577
- '[usePublicEventLogo] Invalid organisationId format (not a valid UUID):',
578
- 'invalid-uuid'
579
- );
580
-
581
- expect(result.current.logoUrl).toBe('https://example.com/logo.png');
582
-
583
- consoleSpy.mockRestore();
584
- });
585
- });
586
-
587
- describe('Edge Cases', () => {
588
- it('should handle rapid parameter changes', async () => {
589
- const mockLogoData = {
590
- logo_url: 'https://example.com/logo.png'
591
- };
592
-
593
- mockSupabaseClient.rpc.mockResolvedValue({
594
- data: [mockLogoData],
595
- error: null
596
- });
597
-
598
- mockFetch.mockResolvedValue({
599
- ok: true,
600
- status: 200
601
- });
602
-
603
- const { result, rerender } = renderHook(
604
- ({ eventId, organisationId }) =>
605
- usePublicEventLogo(eventId, 'Test Event', organisationId, { supabase: mockSupabaseClient }),
606
- { initialProps: { eventId: 'event-1', organisationId: 'org-1' } }
607
- );
608
-
609
- // Change parameters rapidly
610
- rerender({ eventId: 'event-2', organisationId: 'org-1' });
611
- rerender({ eventId: 'event-2', organisationId: 'org-2' });
612
- rerender({ eventId: 'event-3', organisationId: 'org-2' });
613
-
614
- await waitFor(() => {
615
- expect(result.current.isLoading).toBe(false);
616
- }, { interval: 10 });
617
-
618
- // Should have called RPC for the final parameters
619
- expect(mockSupabaseClient.rpc).toHaveBeenCalledWith('get_public_event_logo', {
620
- event_id_param: 'event-3',
621
- organisation_id_param: 'org-2'
622
- });
623
- });
624
-
625
- it('should handle parameters becoming undefined', async () => {
626
- const { result, rerender } = renderHook(
627
- ({ eventId, organisationId }) =>
628
- usePublicEventLogo(eventId, 'Test Event', organisationId, { supabase: mockSupabaseClient }),
629
- { initialProps: { eventId: 'event-1', organisationId: 'org-1' } }
630
- );
631
-
632
- // Change to undefined
633
- rerender({ eventId: undefined, organisationId: 'org-1' });
634
-
635
- expect(result.current.logoUrl).toBe(null);
636
- expect(result.current.isLoading).toBe(false);
637
- expect(result.current.error).toBe(null);
638
- });
639
- });
640
- });