@jmruthers/pace-core 0.5.120 → 0.5.123
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/{AuthService-D4646R4b.d.ts → AuthService-DYuQPJj6.d.ts} +0 -9
- package/dist/{DataTable-DGZDJUYM.js → DataTable-WTS4IRF2.js} +7 -8
- package/dist/{PublicLoadingSpinner-DgDWTFqn.d.ts → PublicLoadingSpinner-CaoRbHvJ.d.ts} +30 -4
- package/dist/{UnifiedAuthProvider-UACKFATV.js → UnifiedAuthProvider-6C47WIML.js} +3 -4
- package/dist/{chunk-D6BOFXYR.js → chunk-35ZDPMBM.js} +3 -3
- package/dist/{chunk-CGURJ27Z.js → chunk-4MXVZVNS.js} +2 -2
- package/dist/{chunk-ZYJ6O5CA.js → chunk-C43QIDN3.js} +2 -2
- package/dist/{chunk-VKOCWWVY.js → chunk-CX5M4ZAG.js} +1 -6
- package/dist/{chunk-VKOCWWVY.js.map → chunk-CX5M4ZAG.js.map} +1 -1
- package/dist/{chunk-HFBOFZ3Z.js → chunk-DHMFMXFV.js} +258 -243
- package/dist/chunk-DHMFMXFV.js.map +1 -0
- package/dist/{chunk-RIEJGKD3.js → chunk-ESJTIADP.js} +15 -6
- package/dist/{chunk-RIEJGKD3.js.map → chunk-ESJTIADP.js.map} +1 -1
- package/dist/{chunk-SMJZMKYN.js → chunk-GEVIB2UB.js} +43 -10
- package/dist/chunk-GEVIB2UB.js.map +1 -0
- package/dist/{chunk-TDNI6ZWL.js → chunk-IJOZZOGT.js} +7 -7
- package/dist/chunk-IJOZZOGT.js.map +1 -0
- package/dist/{chunk-GZRXOUBE.js → chunk-M6DDYFUD.js} +2 -2
- package/dist/chunk-M6DDYFUD.js.map +1 -0
- package/dist/{chunk-B4GZ2BXO.js → chunk-NZGLXZGP.js} +3 -3
- package/dist/{chunk-NZ32EONV.js → chunk-QWNJCQXZ.js} +2 -2
- package/dist/{chunk-FKFHZUGF.js → chunk-XN6GWKMV.js} +43 -56
- package/dist/chunk-XN6GWKMV.js.map +1 -0
- package/dist/{chunk-BHWIUEYH.js → chunk-ZBLK676C.js} +1 -61
- package/dist/chunk-ZBLK676C.js.map +1 -0
- package/dist/{chunk-QPI2CCBA.js → chunk-ZPJMYGEP.js} +149 -96
- package/dist/chunk-ZPJMYGEP.js.map +1 -0
- package/dist/components.d.ts +1 -1
- package/dist/components.js +11 -11
- package/dist/{formatting-B1jSqgl-.d.ts → formatting-DFcCxUEk.d.ts} +1 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +9 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +19 -17
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +2 -3
- package/dist/rbac/index.js +7 -8
- package/dist/styles/index.d.ts +1 -1
- package/dist/styles/index.js +5 -3
- package/dist/theming/runtime.d.ts +73 -1
- package/dist/theming/runtime.js +5 -5
- package/dist/{usePublicRouteParams-BdF8bZgs.d.ts → usePublicRouteParams-Dyt1tzI9.d.ts} +60 -8
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +5 -5
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +6 -6
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +6 -6
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +7 -7
- package/docs/api/interfaces/PublicErrorBoundaryState.md +5 -5
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +7 -7
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +51 -12
- package/docs/api/interfaces/PublicPageLayoutProps.md +72 -12
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +140 -30
- package/docs/best-practices/README.md +1 -1
- package/docs/implementation-guides/datatable-filtering.md +313 -0
- package/docs/implementation-guides/datatable-rbac-usage.md +317 -0
- package/docs/implementation-guides/hierarchical-datatable.md +850 -0
- package/docs/implementation-guides/large-datasets.md +281 -0
- package/docs/implementation-guides/performance.md +403 -0
- package/docs/implementation-guides/public-pages.md +4 -4
- package/docs/migration/quick-migration-guide.md +320 -0
- package/docs/rbac/quick-start.md +16 -16
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +1 -1
- package/docs/troubleshooting/debugging.md +1117 -0
- package/docs/troubleshooting/migration.md +918 -0
- package/examples/public-pages/CorrectPublicPageImplementation.tsx +30 -30
- package/examples/public-pages/PublicEventPage.tsx +41 -41
- package/examples/public-pages/PublicPageApp.tsx +33 -33
- package/examples/public-pages/PublicPageUsageExample.tsx +30 -30
- package/package.json +4 -4
- package/src/__tests__/hooks/usePermissions.test.ts +265 -0
- package/src/components/DataTable/DataTable.test.tsx +9 -38
- package/src/components/DataTable/DataTable.tsx +0 -7
- package/src/components/DataTable/components/DataTableCore.tsx +66 -136
- package/src/components/DataTable/components/DataTableModals.tsx +25 -22
- package/src/components/DataTable/components/EditableRow.tsx +118 -42
- package/src/components/DataTable/components/UnifiedTableBody.tsx +129 -76
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +33 -14
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +17 -5
- package/src/components/DataTable/utils/exportUtils.ts +3 -2
- package/src/components/DataTable/utils/flexibleImport.ts +27 -6
- package/src/components/Dialog/Dialog.tsx +1 -1
- package/src/components/Dialog/README.md +24 -24
- package/src/components/Dialog/examples/BasicHtmlTest.tsx +2 -2
- package/src/components/Dialog/examples/DebugHtmlExample.tsx +6 -6
- package/src/components/Dialog/examples/HtmlDialogExample.tsx +2 -2
- package/src/components/Dialog/examples/SimpleHtmlTest.tsx +3 -3
- package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +4 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +12 -1
- package/src/components/PublicLayout/EventLogo.tsx +175 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +22 -18
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +22 -14
- package/src/components/PublicLayout/PublicPageHeader.tsx +133 -40
- package/src/components/PublicLayout/PublicPageLayout.tsx +75 -72
- package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +1 -1
- package/src/components/PublicLayout/__tests__/PublicLoadingSpinner.test.tsx +8 -8
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +23 -16
- package/src/components/PublicLayout/__tests__/PublicPageLayout.test.tsx +86 -14
- package/src/examples/CorrectPublicPageImplementation.tsx +30 -30
- package/src/examples/PublicEventPage.tsx +41 -41
- package/src/examples/PublicPageApp.tsx +33 -33
- package/src/examples/PublicPageUsageExample.tsx +30 -30
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +583 -0
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +10 -3
- package/src/hooks/index.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +285 -0
- package/src/hooks/public/usePublicRouteParams.ts +21 -4
- package/src/hooks/useEventTheme.test.ts +119 -43
- package/src/hooks/useEventTheme.ts +84 -55
- package/src/index.ts +3 -1
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +630 -0
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +667 -0
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +647 -0
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +496 -0
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +496 -0
- package/src/rbac/secureClient.ts +4 -2
- package/src/services/EventService.ts +0 -66
- package/src/services/__tests__/EventService.eventColours.test.ts +44 -40
- package/src/styles/index.ts +1 -1
- package/src/theming/__tests__/parseEventColours.test.ts +209 -0
- package/src/theming/parseEventColours.ts +123 -0
- package/src/theming/runtime.ts +3 -0
- package/src/types/__tests__/file-reference.test.ts +447 -0
- package/src/types/database.generated.ts +1515 -424
- package/src/utils/formatDate.test.ts +11 -11
- package/src/utils/formatting.ts +3 -2
- package/dist/chunk-BHWIUEYH.js.map +0 -1
- package/dist/chunk-FKFHZUGF.js.map +0 -1
- package/dist/chunk-GZRXOUBE.js.map +0 -1
- package/dist/chunk-HFBOFZ3Z.js.map +0 -1
- package/dist/chunk-QPI2CCBA.js.map +0 -1
- package/dist/chunk-SMJZMKYN.js.map +0 -1
- package/dist/chunk-TDNI6ZWL.js.map +0 -1
- package/src/styles/semantic.css +0 -24
- /package/dist/{DataTable-DGZDJUYM.js.map → DataTable-WTS4IRF2.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-UACKFATV.js.map → UnifiedAuthProvider-6C47WIML.js.map} +0 -0
- /package/dist/{chunk-D6BOFXYR.js.map → chunk-35ZDPMBM.js.map} +0 -0
- /package/dist/{chunk-CGURJ27Z.js.map → chunk-4MXVZVNS.js.map} +0 -0
- /package/dist/{chunk-ZYJ6O5CA.js.map → chunk-C43QIDN3.js.map} +0 -0
- /package/dist/{chunk-B4GZ2BXO.js.map → chunk-NZGLXZGP.js.map} +0 -0
- /package/dist/{chunk-NZ32EONV.js.map → chunk-QWNJCQXZ.js.map} +0 -0
|
@@ -0,0 +1,583 @@
|
|
|
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 { usePublicEvent, clearPublicEventCache, getPublicEventCacheStats } from '../public/usePublicEvent';
|
|
5
|
+
import { usePublicPageContext } from '../../components/PublicLayout/PublicPageProvider';
|
|
6
|
+
|
|
7
|
+
// Mock the PublicPageProvider
|
|
8
|
+
vi.mock('../../components/PublicLayout/PublicPageProvider', () => ({
|
|
9
|
+
usePublicPageContext: vi.fn(() => ({
|
|
10
|
+
environment: {
|
|
11
|
+
supabaseUrl: 'https://test.supabase.co',
|
|
12
|
+
supabaseKey: 'test-anon-key'
|
|
13
|
+
}
|
|
14
|
+
}))
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
// Mock Supabase client
|
|
18
|
+
const mockSupabaseClient = {
|
|
19
|
+
rpc: vi.fn(),
|
|
20
|
+
from: vi.fn(() => ({
|
|
21
|
+
select: vi.fn(() => ({
|
|
22
|
+
eq: vi.fn(() => ({
|
|
23
|
+
eq: vi.fn(() => ({
|
|
24
|
+
not: vi.fn(() => ({
|
|
25
|
+
limit: vi.fn(() => ({
|
|
26
|
+
single: vi.fn()
|
|
27
|
+
}))
|
|
28
|
+
}))
|
|
29
|
+
}))
|
|
30
|
+
}))
|
|
31
|
+
}))
|
|
32
|
+
}))
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Helper to create table query mocks
|
|
36
|
+
const createTableQueryMock = () => ({
|
|
37
|
+
data: null,
|
|
38
|
+
error: null,
|
|
39
|
+
mockResolvedValueOnce: vi.fn(),
|
|
40
|
+
mockResolvedValue: vi.fn()
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Helper to create a complete table query chain
|
|
44
|
+
const createTableQueryChain = (finalResult: any) => {
|
|
45
|
+
const mockChain = {
|
|
46
|
+
select: vi.fn().mockReturnThis(),
|
|
47
|
+
eq: vi.fn().mockReturnThis(),
|
|
48
|
+
not: vi.fn().mockReturnThis(),
|
|
49
|
+
limit: vi.fn().mockReturnThis(),
|
|
50
|
+
single: vi.fn().mockResolvedValue(finalResult)
|
|
51
|
+
};
|
|
52
|
+
return mockChain;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Mock createClient
|
|
56
|
+
vi.mock('@supabase/supabase-js', () => ({
|
|
57
|
+
createClient: vi.fn(() => mockSupabaseClient)
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
// Mock environment variables
|
|
61
|
+
const originalEnv = import.meta.env;
|
|
62
|
+
|
|
63
|
+
describe('usePublicEvent', () => {
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
vi.clearAllMocks();
|
|
66
|
+
clearPublicEventCache();
|
|
67
|
+
|
|
68
|
+
// Reset environment
|
|
69
|
+
Object.defineProperty(import.meta, 'env', {
|
|
70
|
+
value: {
|
|
71
|
+
VITE_SUPABASE_URL: 'https://test.supabase.co',
|
|
72
|
+
VITE_SUPABASE_ANON_KEY: 'test-anon-key'
|
|
73
|
+
},
|
|
74
|
+
writable: true
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Mock window object
|
|
78
|
+
Object.defineProperty(window, 'location', {
|
|
79
|
+
value: { href: 'https://test.com' },
|
|
80
|
+
writable: true
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Re-establish mock implementations after clearAllMocks
|
|
84
|
+
mockSupabaseClient.rpc.mockImplementation(() =>
|
|
85
|
+
Promise.resolve({
|
|
86
|
+
data: null,
|
|
87
|
+
error: null
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Ensure the mock is properly configured
|
|
92
|
+
expect(mockSupabaseClient.rpc).toBeDefined();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
afterEach(() => {
|
|
96
|
+
vi.clearAllMocks();
|
|
97
|
+
clearPublicEventCache();
|
|
98
|
+
Object.defineProperty(import.meta, 'env', {
|
|
99
|
+
value: originalEnv,
|
|
100
|
+
writable: true
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('Basic Functionality', () => {
|
|
105
|
+
it('should initialize with loading state', () => {
|
|
106
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
107
|
+
|
|
108
|
+
expect(result.current.isLoading).toBe(true);
|
|
109
|
+
expect(result.current.event).toBe(null);
|
|
110
|
+
expect(result.current.error).toBe(null);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should fetch event data successfully via RPC', async () => {
|
|
114
|
+
const mockEventData = {
|
|
115
|
+
event_id: '123',
|
|
116
|
+
event_name: 'Test Event',
|
|
117
|
+
event_date: '2024-01-01',
|
|
118
|
+
event_venue: 'Test Venue',
|
|
119
|
+
event_participants: 100,
|
|
120
|
+
event_colours: { primary: '#000000' },
|
|
121
|
+
organisation_id: 'org-123',
|
|
122
|
+
event_days: 1,
|
|
123
|
+
event_typicalunit: 'km',
|
|
124
|
+
event_rounddown: false,
|
|
125
|
+
event_youthmultiplier: 1.0,
|
|
126
|
+
event_catering_email: 'test@example.com',
|
|
127
|
+
event_news: 'Test news',
|
|
128
|
+
event_billing: 'Test billing',
|
|
129
|
+
event_footer: 'Test footer',
|
|
130
|
+
event_email: 'event@example.com',
|
|
131
|
+
event_logo: null
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
135
|
+
data: [mockEventData],
|
|
136
|
+
error: null
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
140
|
+
|
|
141
|
+
await waitFor(() => {
|
|
142
|
+
expect(result.current.isLoading).toBe(false);
|
|
143
|
+
}, { interval: 10 });
|
|
144
|
+
|
|
145
|
+
expect(result.current.event).toEqual({
|
|
146
|
+
id: '123',
|
|
147
|
+
event_id: '123',
|
|
148
|
+
event_name: 'Test Event',
|
|
149
|
+
event_code: 'test-event',
|
|
150
|
+
event_date: '2024-01-01',
|
|
151
|
+
event_venue: 'Test Venue',
|
|
152
|
+
event_participants: 100,
|
|
153
|
+
event_logo: null,
|
|
154
|
+
event_colours: { primary: '#000000' },
|
|
155
|
+
organisation_id: 'org-123',
|
|
156
|
+
is_visible: true,
|
|
157
|
+
created_at: expect.any(String),
|
|
158
|
+
updated_at: expect.any(String),
|
|
159
|
+
name: 'Test Event',
|
|
160
|
+
start_date: '2024-01-01'
|
|
161
|
+
});
|
|
162
|
+
expect(result.current.error).toBe(null);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should handle event not found', async () => {
|
|
166
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
167
|
+
data: [],
|
|
168
|
+
error: null
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const { result } = renderHook(() => usePublicEvent('nonexistent-event'));
|
|
172
|
+
|
|
173
|
+
await waitFor(() => {
|
|
174
|
+
expect(result.current.isLoading).toBe(false);
|
|
175
|
+
}, { interval: 10 });
|
|
176
|
+
|
|
177
|
+
expect(result.current.event).toBe(null);
|
|
178
|
+
expect(result.current.error).toEqual(new Error('Event not found'));
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should handle invalid event code', async () => {
|
|
182
|
+
const { result } = renderHook(() => usePublicEvent(''));
|
|
183
|
+
|
|
184
|
+
await waitFor(() => {
|
|
185
|
+
expect(result.current.isLoading).toBe(false);
|
|
186
|
+
}, { interval: 10 });
|
|
187
|
+
|
|
188
|
+
expect(result.current.event).toBe(null);
|
|
189
|
+
expect(result.current.error).toBeInstanceOf(Error);
|
|
190
|
+
expect(result.current.error?.message).toContain('Invalid event code or Supabase client not available');
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('Caching', () => {
|
|
195
|
+
it('should cache event data', async () => {
|
|
196
|
+
const mockEventData = {
|
|
197
|
+
event_id: '123',
|
|
198
|
+
event_name: 'Test Event',
|
|
199
|
+
event_date: '2024-01-01',
|
|
200
|
+
event_venue: 'Test Venue',
|
|
201
|
+
event_participants: 100,
|
|
202
|
+
event_colours: { primary: '#000000' },
|
|
203
|
+
organisation_id: 'org-123',
|
|
204
|
+
event_days: 1,
|
|
205
|
+
event_typicalunit: 'km',
|
|
206
|
+
event_rounddown: false,
|
|
207
|
+
event_youthmultiplier: 1.0,
|
|
208
|
+
event_catering_email: 'test@example.com',
|
|
209
|
+
event_news: 'Test news',
|
|
210
|
+
event_billing: 'Test billing',
|
|
211
|
+
event_footer: 'Test footer',
|
|
212
|
+
event_email: 'event@example.com'
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
216
|
+
data: [mockEventData],
|
|
217
|
+
error: null
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const { result, rerender } = renderHook(() => usePublicEvent('test-event'));
|
|
221
|
+
|
|
222
|
+
await waitFor(() => {
|
|
223
|
+
expect(result.current.isLoading).toBe(false);
|
|
224
|
+
}, { interval: 10 });
|
|
225
|
+
|
|
226
|
+
// Rerender with same event code - should use cache
|
|
227
|
+
rerender();
|
|
228
|
+
|
|
229
|
+
// Should not call RPC again
|
|
230
|
+
expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(1);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should respect cache TTL', async () => {
|
|
234
|
+
const mockEventData = {
|
|
235
|
+
event_id: '123',
|
|
236
|
+
event_name: 'Test Event',
|
|
237
|
+
event_date: '2024-01-01',
|
|
238
|
+
event_venue: 'Test Venue',
|
|
239
|
+
event_participants: 100,
|
|
240
|
+
event_colours: { primary: '#000000' },
|
|
241
|
+
organisation_id: 'org-123',
|
|
242
|
+
event_days: 1,
|
|
243
|
+
event_typicalunit: 'km',
|
|
244
|
+
event_rounddown: false,
|
|
245
|
+
event_youthmultiplier: 1.0,
|
|
246
|
+
event_catering_email: 'test@example.com',
|
|
247
|
+
event_news: 'Test news',
|
|
248
|
+
event_billing: 'Test billing',
|
|
249
|
+
event_footer: 'Test footer',
|
|
250
|
+
event_email: 'event@example.com'
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
254
|
+
data: [mockEventData],
|
|
255
|
+
error: null
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const { result } = renderHook(() => usePublicEvent('test-event', { cacheTtl: 100 }));
|
|
259
|
+
|
|
260
|
+
await waitFor(() => {
|
|
261
|
+
expect(result.current.isLoading).toBe(false);
|
|
262
|
+
}, { interval: 10 });
|
|
263
|
+
|
|
264
|
+
// Wait for cache to expire
|
|
265
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
266
|
+
|
|
267
|
+
// Rerender should fetch again
|
|
268
|
+
const { result: result2 } = renderHook(() => usePublicEvent('test-event', { cacheTtl: 100 }));
|
|
269
|
+
|
|
270
|
+
await waitFor(() => {
|
|
271
|
+
expect(result2.current.isLoading).toBe(false);
|
|
272
|
+
}, { interval: 10 });
|
|
273
|
+
|
|
274
|
+
expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should disable caching when requested', async () => {
|
|
278
|
+
const mockEventData = {
|
|
279
|
+
event_id: '123',
|
|
280
|
+
event_name: 'Test Event',
|
|
281
|
+
event_date: '2024-01-01',
|
|
282
|
+
event_venue: 'Test Venue',
|
|
283
|
+
event_participants: 100,
|
|
284
|
+
event_colours: { primary: '#000000' },
|
|
285
|
+
organisation_id: 'org-123',
|
|
286
|
+
event_days: 1,
|
|
287
|
+
event_typicalunit: 'km',
|
|
288
|
+
event_rounddown: false,
|
|
289
|
+
event_youthmultiplier: 1.0,
|
|
290
|
+
event_catering_email: 'test@example.com',
|
|
291
|
+
event_news: 'Test news',
|
|
292
|
+
event_billing: 'Test billing',
|
|
293
|
+
event_footer: 'Test footer',
|
|
294
|
+
event_email: 'event@example.com',
|
|
295
|
+
event_logo: null
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// Clear any existing cache
|
|
299
|
+
const { clearPublicEventCache } = await import('../public/usePublicEvent');
|
|
300
|
+
clearPublicEventCache();
|
|
301
|
+
|
|
302
|
+
// Use mockImplementation to handle multiple calls
|
|
303
|
+
let callCount = 0;
|
|
304
|
+
mockSupabaseClient.rpc.mockImplementation(() => {
|
|
305
|
+
callCount++;
|
|
306
|
+
return Promise.resolve({
|
|
307
|
+
data: [mockEventData],
|
|
308
|
+
error: null
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const { result, rerender } = renderHook(
|
|
313
|
+
({ eventCode }) => usePublicEvent(eventCode, { enableCache: false }),
|
|
314
|
+
{ initialProps: { eventCode: 'test-event' } }
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
await waitFor(() => {
|
|
318
|
+
expect(result.current.isLoading).toBe(false);
|
|
319
|
+
}, { interval: 10 });
|
|
320
|
+
|
|
321
|
+
// Change the event code to force a new fetch
|
|
322
|
+
rerender({ eventCode: 'test-event-2' });
|
|
323
|
+
|
|
324
|
+
await waitFor(() => {
|
|
325
|
+
expect(result.current.isLoading).toBe(false);
|
|
326
|
+
}, { interval: 10 });
|
|
327
|
+
|
|
328
|
+
expect(callCount).toBe(2);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
describe('Error Handling', () => {
|
|
333
|
+
it('should handle RPC errors', async () => {
|
|
334
|
+
// Mock the RPC call to return an error object
|
|
335
|
+
const testError = { message: 'Database error', details: 'Test error details', hint: null, code: 'TEST_ERROR' };
|
|
336
|
+
mockSupabaseClient.rpc.mockImplementation(() =>
|
|
337
|
+
Promise.resolve({
|
|
338
|
+
data: null,
|
|
339
|
+
error: testError
|
|
340
|
+
})
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
344
|
+
|
|
345
|
+
await waitFor(() => {
|
|
346
|
+
expect(result.current.isLoading).toBe(false);
|
|
347
|
+
}, { interval: 10 });
|
|
348
|
+
|
|
349
|
+
expect(result.current.error).toBeInstanceOf(Error);
|
|
350
|
+
expect(result.current.error?.message).toBe('Database error');
|
|
351
|
+
expect(result.current.event).toBe(null);
|
|
352
|
+
|
|
353
|
+
// Verify the mock was called
|
|
354
|
+
expect(mockSupabaseClient.rpc).toHaveBeenCalledWith('get_public_event_by_code', {
|
|
355
|
+
event_code_param: 'test-event'
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should handle missing Supabase client', async () => {
|
|
360
|
+
// Mock the PublicPageProvider to return null environment
|
|
361
|
+
const mockUsePublicPageContext = vi.mocked(usePublicPageContext);
|
|
362
|
+
mockUsePublicPageContext.mockReturnValueOnce({
|
|
363
|
+
environment: {
|
|
364
|
+
supabaseUrl: '',
|
|
365
|
+
supabaseKey: ''
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// Mock createClient to return null when environment is empty
|
|
370
|
+
const { createClient } = await import('@supabase/supabase-js');
|
|
371
|
+
vi.mocked(createClient).mockReturnValueOnce(null as any);
|
|
372
|
+
|
|
373
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
374
|
+
|
|
375
|
+
await waitFor(() => {
|
|
376
|
+
expect(result.current.isLoading).toBe(false);
|
|
377
|
+
}, { interval: 10 });
|
|
378
|
+
|
|
379
|
+
expect(result.current.error).toBeInstanceOf(Error);
|
|
380
|
+
expect(result.current.error?.message).toContain('Invalid event code or Supabase client not available');
|
|
381
|
+
expect(result.current.event).toBe(null);
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
describe('Provider Context Integration', () => {
|
|
386
|
+
it('should use PublicPageContext when available', async () => {
|
|
387
|
+
const mockContext = {
|
|
388
|
+
environment: {
|
|
389
|
+
supabaseUrl: 'https://context.supabase.co',
|
|
390
|
+
supabaseKey: 'context-anon-key'
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
vi.mocked(usePublicPageContext).mockReturnValue(mockContext);
|
|
395
|
+
|
|
396
|
+
const mockEventData = {
|
|
397
|
+
event_id: '123',
|
|
398
|
+
event_name: 'Test Event',
|
|
399
|
+
event_date: '2024-01-01',
|
|
400
|
+
event_venue: 'Test Venue',
|
|
401
|
+
event_participants: 100,
|
|
402
|
+
event_colours: { primary: '#000000' },
|
|
403
|
+
organisation_id: 'org-123',
|
|
404
|
+
event_days: 1,
|
|
405
|
+
event_typicalunit: 'km',
|
|
406
|
+
event_rounddown: false,
|
|
407
|
+
event_youthmultiplier: 1.0,
|
|
408
|
+
event_catering_email: 'test@example.com',
|
|
409
|
+
event_news: 'Test news',
|
|
410
|
+
event_billing: 'Test billing',
|
|
411
|
+
event_footer: 'Test footer',
|
|
412
|
+
event_email: 'event@example.com',
|
|
413
|
+
event_logo: null
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
417
|
+
data: [mockEventData],
|
|
418
|
+
error: null
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
422
|
+
|
|
423
|
+
await waitFor(() => {
|
|
424
|
+
expect(result.current.isLoading).toBe(false);
|
|
425
|
+
}, { interval: 10 });
|
|
426
|
+
|
|
427
|
+
expect(result.current.event).toBeTruthy();
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
describe('Refetch Functionality', () => {
|
|
432
|
+
it('should refetch data when refetch is called', async () => {
|
|
433
|
+
const mockEventData = {
|
|
434
|
+
event_id: '123',
|
|
435
|
+
event_name: 'Test Event',
|
|
436
|
+
event_date: '2024-01-01',
|
|
437
|
+
event_venue: 'Test Venue',
|
|
438
|
+
event_participants: 100,
|
|
439
|
+
event_colours: { primary: '#000000' },
|
|
440
|
+
organisation_id: 'org-123',
|
|
441
|
+
event_days: 1,
|
|
442
|
+
event_typicalunit: 'km',
|
|
443
|
+
event_rounddown: false,
|
|
444
|
+
event_youthmultiplier: 1.0,
|
|
445
|
+
event_catering_email: 'test@example.com',
|
|
446
|
+
event_news: 'Test news',
|
|
447
|
+
event_billing: 'Test billing',
|
|
448
|
+
event_footer: 'Test footer',
|
|
449
|
+
event_email: 'event@example.com'
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
mockSupabaseClient.rpc.mockResolvedValue({
|
|
453
|
+
data: [mockEventData],
|
|
454
|
+
error: null
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
458
|
+
|
|
459
|
+
await waitFor(() => {
|
|
460
|
+
expect(result.current.isLoading).toBe(false);
|
|
461
|
+
}, { interval: 10 });
|
|
462
|
+
|
|
463
|
+
// Call refetch
|
|
464
|
+
await act(async () => {
|
|
465
|
+
await result.current.refetch();
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('should clear cache when refetch is called', async () => {
|
|
472
|
+
const mockEventData = {
|
|
473
|
+
event_id: '123',
|
|
474
|
+
event_name: 'Test Event',
|
|
475
|
+
event_date: '2024-01-01',
|
|
476
|
+
event_venue: 'Test Venue',
|
|
477
|
+
event_participants: 100,
|
|
478
|
+
event_colours: { primary: '#000000' },
|
|
479
|
+
organisation_id: 'org-123',
|
|
480
|
+
event_days: 1,
|
|
481
|
+
event_typicalunit: 'km',
|
|
482
|
+
event_rounddown: false,
|
|
483
|
+
event_youthmultiplier: 1.0,
|
|
484
|
+
event_catering_email: 'test@example.com',
|
|
485
|
+
event_news: 'Test news',
|
|
486
|
+
event_billing: 'Test billing',
|
|
487
|
+
event_footer: 'Test footer',
|
|
488
|
+
event_email: 'event@example.com'
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
mockSupabaseClient.rpc.mockResolvedValue({
|
|
492
|
+
data: [mockEventData],
|
|
493
|
+
error: null
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
497
|
+
|
|
498
|
+
await waitFor(() => {
|
|
499
|
+
expect(result.current.isLoading).toBe(false);
|
|
500
|
+
}, { interval: 10 });
|
|
501
|
+
|
|
502
|
+
// Call refetch
|
|
503
|
+
await act(async () => {
|
|
504
|
+
await result.current.refetch();
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// Should call RPC again (cache was cleared)
|
|
508
|
+
expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
describe('Cache Management Utilities', () => {
|
|
513
|
+
it('should clear public event cache', () => {
|
|
514
|
+
// Add some data to cache
|
|
515
|
+
const stats = getPublicEventCacheStats();
|
|
516
|
+
expect(stats.size).toBe(0);
|
|
517
|
+
|
|
518
|
+
// Clear cache
|
|
519
|
+
clearPublicEventCache();
|
|
520
|
+
|
|
521
|
+
const statsAfter = getPublicEventCacheStats();
|
|
522
|
+
expect(statsAfter.size).toBe(0);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it('should get cache statistics', () => {
|
|
526
|
+
const stats = getPublicEventCacheStats();
|
|
527
|
+
expect(stats).toEqual({
|
|
528
|
+
size: 0,
|
|
529
|
+
keys: []
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
describe('Edge Cases', () => {
|
|
535
|
+
it('should handle null event data from RPC', async () => {
|
|
536
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
537
|
+
data: null,
|
|
538
|
+
error: null
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
542
|
+
|
|
543
|
+
await waitFor(() => {
|
|
544
|
+
expect(result.current.isLoading).toBe(false);
|
|
545
|
+
}, { interval: 10 });
|
|
546
|
+
|
|
547
|
+
expect(result.current.event).toBe(null);
|
|
548
|
+
expect(result.current.error).toEqual(new Error('Event not found'));
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('should handle empty event data array from RPC', async () => {
|
|
552
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
553
|
+
data: [],
|
|
554
|
+
error: null
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
558
|
+
|
|
559
|
+
await waitFor(() => {
|
|
560
|
+
expect(result.current.isLoading).toBe(false);
|
|
561
|
+
}, { interval: 10 });
|
|
562
|
+
|
|
563
|
+
expect(result.current.event).toBe(null);
|
|
564
|
+
expect(result.current.error).toEqual(new Error('Event not found'));
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
it('should handle undefined event data from RPC', async () => {
|
|
568
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
569
|
+
data: [undefined],
|
|
570
|
+
error: null
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
574
|
+
|
|
575
|
+
await waitFor(() => {
|
|
576
|
+
expect(result.current.isLoading).toBe(false);
|
|
577
|
+
}, { interval: 10 });
|
|
578
|
+
|
|
579
|
+
expect(result.current.event).toBe(null);
|
|
580
|
+
expect(result.current.error).toEqual(new Error('Event not found'));
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
});
|
|
@@ -83,7 +83,7 @@ describe('usePublicRouteParams', () => {
|
|
|
83
83
|
|
|
84
84
|
it('should reject invalid event code formats', () => {
|
|
85
85
|
const invalidCodes = [
|
|
86
|
-
'
|
|
86
|
+
'a', // too short (1 character)
|
|
87
87
|
'a'.repeat(51), // too long
|
|
88
88
|
'test@event', // invalid character
|
|
89
89
|
'test event', // space
|
|
@@ -361,11 +361,14 @@ describe('usePublicRouteParams', () => {
|
|
|
361
361
|
expect(generatePublicRoutePath('test-event')).toBe('/public/event/test-event/index');
|
|
362
362
|
expect(generatePublicRoutePath('test-event', 'details')).toBe('/public/event/test-event/details');
|
|
363
363
|
expect(generatePublicRoutePath('test-event', 'registration')).toBe('/public/event/test-event/registration');
|
|
364
|
+
// 2-character codes are now valid
|
|
365
|
+
expect(generatePublicRoutePath('GG')).toBe('/public/event/GG/index');
|
|
366
|
+
expect(generatePublicRoutePath('ab', 'page')).toBe('/public/event/ab/page');
|
|
364
367
|
});
|
|
365
368
|
|
|
366
369
|
it('should throw error for invalid event code', () => {
|
|
367
370
|
expect(() => generatePublicRoutePath('invalid@code!')).toThrow('Invalid event code for route generation');
|
|
368
|
-
expect(() => generatePublicRoutePath('
|
|
371
|
+
expect(() => generatePublicRoutePath('a')).toThrow('Invalid event code for route generation'); // too short (1 character)
|
|
369
372
|
expect(() => generatePublicRoutePath('')).toThrow('Invalid event code for route generation');
|
|
370
373
|
});
|
|
371
374
|
});
|
|
@@ -379,11 +382,15 @@ describe('usePublicRouteParams', () => {
|
|
|
379
382
|
expect(extractEventCodeFromPath('/public/event/test-event-123')).toBe('test-event-123');
|
|
380
383
|
expect(extractEventCodeFromPath('/public/event/test_event')).toBe('test_event');
|
|
381
384
|
expect(extractEventCodeFromPath('/public/event/test123')).toBe('test123');
|
|
385
|
+
// 2-character codes are now valid
|
|
386
|
+
expect(extractEventCodeFromPath('/public/event/ab')).toBe('ab');
|
|
387
|
+
expect(extractEventCodeFromPath('/public/event/GG')).toBe('GG');
|
|
388
|
+
expect(extractEventCodeFromPath('/public/event/A1')).toBe('A1');
|
|
382
389
|
});
|
|
383
390
|
|
|
384
391
|
it('should return null for invalid paths', () => {
|
|
385
392
|
expect(extractEventCodeFromPath('/public/event/')).toBe(null);
|
|
386
|
-
expect(extractEventCodeFromPath('/public/event/
|
|
393
|
+
expect(extractEventCodeFromPath('/public/event/a')).toBe(null); // too short (1 character)
|
|
387
394
|
expect(extractEventCodeFromPath('/public/event/test@event')).toBe(null); // invalid character
|
|
388
395
|
expect(extractEventCodeFromPath('/public/event/test event')).toBe(null); // space
|
|
389
396
|
expect(extractEventCodeFromPath('/public/event/')).toBe(null);
|
package/src/hooks/index.ts
CHANGED
|
@@ -22,7 +22,7 @@ export { useFocusTrap } from './useFocusTrap';
|
|
|
22
22
|
export { useKeyboardShortcuts } from './useKeyboardShortcuts';
|
|
23
23
|
export { useIsMobile } from './useIsMobile';
|
|
24
24
|
|
|
25
|
-
// === EVENT
|
|
25
|
+
// === EVENT HOOKS ===
|
|
26
26
|
export { useEventTheme } from './useEventTheme';
|
|
27
27
|
|
|
28
28
|
// === DATA & STATE HOOKS ===
|