@jmruthers/pace-core 0.5.73 → 0.5.75
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/{DataTable-INW5YIFV.js → DataTable-HWZQGASI.js} +8 -8
- package/dist/{PublicLoadingSpinner-DLpF5bbs.d.ts → PublicLoadingSpinner-BKNBT6b6.d.ts} +2 -2
- package/dist/RBACService-C4udt_Zp.d.ts +528 -0
- package/dist/{UnifiedAuthProvider-6SYT5WFN.js → UnifiedAuthProvider-3NKDOSOK.js} +6 -4
- package/dist/UnifiedAuthProvider-Bj6YCf7c.d.ts +113 -0
- package/dist/{chunk-2PRPDH66.js → chunk-2CHATWBF.js} +5 -7
- package/dist/chunk-2CHATWBF.js.map +1 -0
- package/dist/{chunk-43C63KLH.js → chunk-2DFZ432F.js} +496 -30
- package/dist/chunk-2DFZ432F.js.map +1 -0
- package/dist/{chunk-M4UMXYNK.js → chunk-33PHABLB.js} +36 -3
- package/dist/chunk-33PHABLB.js.map +1 -0
- package/dist/chunk-5F3NDPJV.js +232 -0
- package/dist/chunk-5F3NDPJV.js.map +1 -0
- package/dist/chunk-A4FUBC7B.js +17 -0
- package/dist/chunk-A4FUBC7B.js.map +1 -0
- package/dist/{chunk-SMJZMKYN.js → chunk-A6HBIY5P.js} +2 -11
- package/dist/{chunk-SMJZMKYN.js.map → chunk-A6HBIY5P.js.map} +1 -1
- package/dist/{chunk-GBC5PC3N.js → chunk-CY3AHGO4.js} +6256 -1937
- package/dist/chunk-CY3AHGO4.js.map +1 -0
- package/dist/{chunk-BYG6OSTC.js → chunk-DAXLNIDY.js} +48 -50
- package/dist/chunk-DAXLNIDY.js.map +1 -0
- package/dist/{chunk-VKOCWWVY.js → chunk-L3RV2ALE.js} +1 -6
- package/dist/{chunk-VKOCWWVY.js.map → chunk-L3RV2ALE.js.map} +1 -1
- package/dist/chunk-LW7MMEAQ.js +59 -0
- package/dist/chunk-LW7MMEAQ.js.map +1 -0
- package/dist/{chunk-LANO5IFV.js → chunk-NTNILOBC.js} +7 -9
- package/dist/chunk-NTNILOBC.js.map +1 -0
- package/dist/chunk-PYUXFQJ3.js +11 -0
- package/dist/chunk-PYUXFQJ3.js.map +1 -0
- package/dist/chunk-URUTVZ7N.js +27 -0
- package/dist/chunk-URUTVZ7N.js.map +1 -0
- package/dist/chunk-WN6XJWOS.js +2468 -0
- package/dist/chunk-WN6XJWOS.js.map +1 -0
- package/dist/{chunk-3SP4P7NS.js → chunk-XLZ7U46Z.js} +59 -1
- package/dist/chunk-XLZ7U46Z.js.map +1 -0
- package/dist/{chunk-UC2BWIK7.js → chunk-ZTT2AXMX.js} +9 -14
- package/dist/chunk-ZTT2AXMX.js.map +1 -0
- package/dist/components.d.ts +4 -5
- package/dist/components.js +32 -39
- package/dist/components.js.map +1 -1
- package/dist/hooks.d.ts +3 -3
- package/dist/hooks.js +9 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +156 -10
- package/dist/index.js +188 -93
- package/dist/index.js.map +1 -1
- package/dist/{organisation-t-vvQC3g.d.ts → organisation-BtshODVF.d.ts} +4 -3
- package/dist/providers.d.ts +27 -38
- package/dist/providers.js +33 -23
- package/dist/rbac/index.d.ts +61 -5
- package/dist/rbac/index.js +13 -14
- package/dist/styles/index.js +2 -2
- package/dist/theming/runtime.js +1 -3
- package/dist/types.d.ts +3 -3
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/{unified-CMPjE_fv.d.ts → unified-CM7T0aTK.d.ts} +1 -1
- package/dist/useInactivityTracker-MRUU55XI.js +10 -0
- package/dist/useInactivityTracker-MRUU55XI.js.map +1 -0
- package/dist/{usePublicRouteParams-Ua1Vz-HG.d.ts → usePublicRouteParams-B-CumWRc.d.ts} +3 -3
- package/dist/utils.js +7 -9
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +3 -3
- package/docs/api/interfaces/CardProps.md +2 -2
- 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/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/EventLogoProps.md +2 -2
- 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/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +2 -2
- 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 +28 -17
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +2 -2
- 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/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +2 -2
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +5 -11
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.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 +524 -440
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +14 -14
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +179 -52
- package/docs/architecture/services.md +30 -32
- package/docs/breaking-changes.md +2 -5
- package/docs/implementation-guides/data-tables.md +82 -1
- package/docs/migration/service-architecture.md +121 -260
- package/docs/rbac/README-rbac-rls-integration.md +48 -38
- package/{src/rbac/examples → examples/RBAC}/CompleteRBACExample.tsx +3 -2
- package/{src/rbac/examples → examples/RBAC}/EventBasedApp.tsx +5 -4
- package/{src/components/examples → examples/RBAC}/PermissionExample.tsx +7 -6
- package/examples/RBAC/__tests__/PermissionExample.test.tsx +150 -0
- package/examples/RBAC/index.ts +13 -0
- package/examples/README.md +37 -0
- package/examples/index.ts +22 -0
- package/{src/examples → examples/public-pages}/CorrectPublicPageImplementation.tsx +1 -1
- package/{src/examples → examples/public-pages}/PublicEventPage.tsx +1 -1
- package/{src/examples → examples/public-pages}/PublicPageApp.tsx +1 -1
- package/{src/examples → examples/public-pages}/PublicPageUsageExample.tsx +1 -1
- package/examples/public-pages/__tests__/PublicPageUsageExample.test.tsx +159 -0
- package/examples/public-pages/index.ts +14 -0
- package/package.json +22 -18
- package/src/__tests__/TEST_GUIDE_CURSOR.md +650 -9
- package/src/__tests__/helpers/README.md +255 -0
- package/src/__tests__/helpers/index.ts +62 -0
- package/src/__tests__/helpers/supabaseMock.ts +27 -3
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -8
- package/src/components/DataTable/components/DataTableCore.tsx +37 -3
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +55 -0
- package/src/components/DataTable/core/ColumnManager.ts +10 -0
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +254 -0
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +193 -0
- package/src/components/DataTable/examples/__tests__/HierarchicalExample.test.tsx +45 -0
- package/src/components/DataTable/examples/__tests__/PerformanceExample.test.tsx +117 -0
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/Dialog/examples/__tests__/HtmlDialogExample.test.tsx +71 -0
- package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +122 -0
- package/src/components/EventSelector/EventSelector.tsx +1 -1
- package/src/components/Header/Header.test.tsx +35 -1
- package/src/components/Header/Header.tsx +3 -1
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -3
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +24 -4
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +3 -2
- package/src/components/Toast/Toast.test.tsx +1 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/hooks/__tests__/useFocusManagement.unit.test.ts +220 -0
- package/src/hooks/__tests__/useIsMobile.unit.test.ts +117 -0
- package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +295 -0
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +29 -19
- package/src/hooks/__tests__/useRBAC.unit.test.ts +7 -3
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +115 -19
- package/src/hooks/useEventTheme.test.ts +350 -0
- package/src/hooks/useEventTheme.ts +1 -1
- package/src/hooks/useEvents.ts +61 -0
- package/src/hooks/useOrganisationSecurity.test.ts +4 -4
- package/src/hooks/useOrganisationSecurity.ts +2 -2
- package/src/hooks/useOrganisations.ts +64 -0
- package/src/hooks/useSecureDataAccess.test.ts +9 -5
- package/src/hooks/useSecureDataAccess.ts +2 -2
- package/src/index.ts +18 -3
- package/src/providers/AuthProvider.tsx +8 -292
- package/src/providers/EventProvider.tsx +15 -425
- package/src/providers/InactivityProvider.tsx +8 -231
- package/src/providers/OrganisationProvider.test.simple.tsx +3 -2
- package/src/providers/OrganisationProvider.tsx +11 -890
- package/src/providers/UnifiedAuthProvider.tsx +8 -320
- package/src/providers/__tests__/AuthProvider.test.tsx +18 -17
- package/src/providers/__tests__/EventProvider.test.tsx +253 -2
- package/src/providers/__tests__/InactivityProvider.test-helper.tsx +65 -0
- package/src/providers/__tests__/InactivityProvider.test.tsx +46 -114
- package/src/providers/__tests__/OrganisationProvider.test.tsx +313 -3
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +383 -2
- package/src/providers/index.ts +8 -7
- package/src/providers/services/EventServiceProvider.tsx +3 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +3 -0
- package/src/rbac/hooks/usePermissions.test.ts +296 -0
- package/src/rbac/hooks/useRBAC.test.ts +9 -5
- package/src/rbac/hooks/useRBAC.ts +3 -3
- package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +688 -0
- package/src/rbac/providers/__tests__/RBACProvider.test.tsx +507 -0
- package/src/services/AuthService.ts +19 -4
- package/src/services/__tests__/AuthService.test.ts +288 -0
- package/src/styles/core.css +2 -0
- package/src/types/__tests__/guards.test.ts +246 -0
- package/src/types/guards.ts +1 -0
- package/src/types/organisation.ts +3 -2
- package/src/validation/__tests__/sanitization.unit.test.ts +250 -0
- package/src/validation/__tests__/schemaUtils.unit.test.ts +451 -0
- package/src/validation/__tests__/user.unit.test.ts +440 -0
- package/dist/RBACProvider-BO4ilsQB.d.ts +0 -63
- package/dist/UnifiedAuthProvider-D02AMXgO.d.ts +0 -103
- package/dist/chunk-2PRPDH66.js.map +0 -1
- package/dist/chunk-3SP4P7NS.js.map +0 -1
- package/dist/chunk-43C63KLH.js.map +0 -1
- package/dist/chunk-5A4RL4BC.js +0 -5670
- package/dist/chunk-5A4RL4BC.js.map +0 -1
- package/dist/chunk-BYG6OSTC.js.map +0 -1
- package/dist/chunk-CDDYJCYU.js +0 -79
- package/dist/chunk-CDDYJCYU.js.map +0 -1
- package/dist/chunk-F24P24TZ.js +0 -17
- package/dist/chunk-F24P24TZ.js.map +0 -1
- package/dist/chunk-GBC5PC3N.js.map +0 -1
- package/dist/chunk-LANO5IFV.js.map +0 -1
- package/dist/chunk-M4UMXYNK.js.map +0 -1
- package/dist/chunk-RJNE764D.js +0 -953
- package/dist/chunk-RJNE764D.js.map +0 -1
- package/dist/chunk-UC2BWIK7.js.map +0 -1
- package/dist/rbac/cli/policy-manager.js +0 -278
- package/dist/rbac/cli/policy-manager.js.map +0 -1
- package/docs/api/interfaces/EventContextType.md +0 -96
- package/docs/api/interfaces/EventProviderProps.md +0 -19
- package/src/providers/OrganisationProvider.test.tsx +0 -164
- package/src/providers/UnifiedAuthProvider.test.tsx +0 -124
- package/src/providers/__tests__/AuthProvider.test.tsx.backup +0 -771
- package/src/providers/__tests__/EventProvider.test.tsx.backup +0 -824
- package/src/providers/__tests__/OrganisationProvider.test.tsx.backup +0 -820
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx.backup +0 -911
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx.backup2 +0 -166
- package/src/rbac/cli/__tests__/policy-manager.test.ts +0 -339
- package/src/rbac/cli/policy-manager.ts +0 -443
- package/dist/{DataTable-INW5YIFV.js.map → DataTable-HWZQGASI.js.map} +0 -0
- package/dist/{UnifiedAuthProvider-6SYT5WFN.js.map → UnifiedAuthProvider-3NKDOSOK.js.map} +0 -0
- package/dist/{validation-PM_iOaTI.d.ts → validation-D8VcbTzC.d.ts} +2 -2
- /package/src/utils/{appNameResolver.test.ts.backup → appNameResolver.test 2.ts} +0 -0
|
@@ -1,911 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file UnifiedAuthProvider Tests
|
|
3
|
-
* @description Comprehensive tests for UnifiedAuthProvider component
|
|
4
|
-
* @package @jmruthers/pace-core
|
|
5
|
-
* @module Providers/__tests__
|
|
6
|
-
* @since 0.1.0
|
|
7
|
-
*
|
|
8
|
-
* Comprehensive test suite for UnifiedAuthProvider component covering all critical functionality.
|
|
9
|
-
* Follows testing guidelines with proper structure, naming, and best practices.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import React from 'react';
|
|
13
|
-
import { render, screen, waitFor, act } from '@testing-library/react';
|
|
14
|
-
import userEvent from '@testing-library/user-event';
|
|
15
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
16
|
-
import { BrowserRouter } from 'react-router-dom';
|
|
17
|
-
import { UnifiedAuthProvider, useUnifiedAuth } from '../UnifiedAuthProvider';
|
|
18
|
-
import { createMockSupabaseClient, testDataGenerators } from '../../__tests__/helpers/test-utils';
|
|
19
|
-
|
|
20
|
-
// Mock the debug logger
|
|
21
|
-
vi.mock('../../utils/debugLogger', () => ({
|
|
22
|
-
DebugLogger: {
|
|
23
|
-
log: vi.fn(),
|
|
24
|
-
},
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
// Mock the AuthProvider
|
|
28
|
-
const mockAuthState = {
|
|
29
|
-
user: null as any,
|
|
30
|
-
session: null as any,
|
|
31
|
-
isAuthenticated: false,
|
|
32
|
-
authLoading: true,
|
|
33
|
-
authError: null as any,
|
|
34
|
-
signIn: vi.fn(),
|
|
35
|
-
signOut: vi.fn(),
|
|
36
|
-
signUp: vi.fn(),
|
|
37
|
-
resetPassword: vi.fn(),
|
|
38
|
-
updatePassword: vi.fn(),
|
|
39
|
-
refreshSession: vi.fn(),
|
|
40
|
-
supabase: null as any,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
vi.mock('../AuthProvider', () => ({
|
|
44
|
-
AuthProvider: ({ children }: { children: React.ReactNode }) => <div data-testid="auth-provider">{children}</div>,
|
|
45
|
-
useAuth: () => mockAuthState,
|
|
46
|
-
}));
|
|
47
|
-
|
|
48
|
-
// Mock the RBAC provider
|
|
49
|
-
const mockRBACState = {
|
|
50
|
-
rbacLoading: false,
|
|
51
|
-
rbacError: null,
|
|
52
|
-
hasPermission: vi.fn(() => true),
|
|
53
|
-
hasRole: vi.fn(() => true),
|
|
54
|
-
hasAccessLevel: vi.fn(() => 'ADMIN'),
|
|
55
|
-
validatePermission: vi.fn(() => Promise.resolve(true)),
|
|
56
|
-
canAccess: vi.fn(() => true),
|
|
57
|
-
permissions: [],
|
|
58
|
-
roles: [],
|
|
59
|
-
accessLevel: 'ADMIN',
|
|
60
|
-
userEventAccess: null,
|
|
61
|
-
setSelectedEventId: vi.fn(),
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
vi.mock('../../rbac/providers/RBACProvider', () => ({
|
|
65
|
-
RBACProvider: ({ children }: { children: React.ReactNode }) => <div data-testid="rbac-provider">{children}</div>,
|
|
66
|
-
useRBAC: () => mockRBACState,
|
|
67
|
-
}));
|
|
68
|
-
|
|
69
|
-
// Mock the inactivity provider
|
|
70
|
-
const mockInactivityState = {
|
|
71
|
-
showInactivityWarning: false,
|
|
72
|
-
inactivityTimeRemaining: 0,
|
|
73
|
-
isIdle: false,
|
|
74
|
-
timeRemaining: 0,
|
|
75
|
-
showWarning: false,
|
|
76
|
-
isTracking: false,
|
|
77
|
-
resetActivity: vi.fn(),
|
|
78
|
-
startTracking: vi.fn(),
|
|
79
|
-
stopTracking: vi.fn(),
|
|
80
|
-
handleIdleLogout: vi.fn(),
|
|
81
|
-
handleStaySignedIn: vi.fn(),
|
|
82
|
-
handleSignOutNow: vi.fn(),
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
vi.mock('../InactivityProvider', () => ({
|
|
86
|
-
InactivityProvider: ({ children }: { children: React.ReactNode }) => <div data-testid="inactivity-provider">{children}</div>,
|
|
87
|
-
useInactivity: () => mockInactivityState,
|
|
88
|
-
}));
|
|
89
|
-
|
|
90
|
-
// Mock react-router-dom
|
|
91
|
-
vi.mock('react-router-dom', () => ({
|
|
92
|
-
BrowserRouter: ({ children }: { children: React.ReactNode }) => <div data-testid="browser-router">{children}</div>,
|
|
93
|
-
useNavigate: () => vi.fn(),
|
|
94
|
-
}));
|
|
95
|
-
|
|
96
|
-
// Test component that uses the unified auth context
|
|
97
|
-
const TestComponent = () => {
|
|
98
|
-
const auth = useUnifiedAuth();
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<div data-testid="test-component">
|
|
102
|
-
<div data-testid="user">{auth.user?.email || 'No user'}</div>
|
|
103
|
-
<div data-testid="isAuthenticated">{auth.isAuthenticated ? 'true' : 'false'}</div>
|
|
104
|
-
<div data-testid="isLoading">{auth.isLoading ? 'true' : 'false'}</div>
|
|
105
|
-
<div data-testid="hasErrors">{auth.hasErrors ? 'true' : 'false'}</div>
|
|
106
|
-
<div data-testid="appName">{auth.appName}</div>
|
|
107
|
-
<div data-testid="hasPermission">{auth.hasPermission('test:permission') ? 'true' : 'false'}</div>
|
|
108
|
-
<div data-testid="hasRole">{auth.hasRole('admin') ? 'true' : 'false'}</div>
|
|
109
|
-
<div data-testid="accessLevel">{auth.accessLevel}</div>
|
|
110
|
-
<div data-testid="showInactivityWarning">{auth.showInactivityWarning ? 'true' : 'false'}</div>
|
|
111
|
-
<div data-testid="isIdle">{auth.isIdle ? 'true' : 'false'}</div>
|
|
112
|
-
<button onClick={() => auth.signIn('test@example.com', 'password')}>
|
|
113
|
-
Sign In
|
|
114
|
-
</button>
|
|
115
|
-
<button onClick={() => auth.signOut()}>
|
|
116
|
-
Sign Out
|
|
117
|
-
</button>
|
|
118
|
-
<button onClick={() => auth.signUp('test@example.com', 'password')}>
|
|
119
|
-
Sign Up
|
|
120
|
-
</button>
|
|
121
|
-
<button onClick={() => auth.resetPassword('test@example.com')}>
|
|
122
|
-
Reset Password
|
|
123
|
-
</button>
|
|
124
|
-
<button onClick={() => auth.updatePassword('newpassword')}>
|
|
125
|
-
Update Password
|
|
126
|
-
</button>
|
|
127
|
-
<button onClick={() => auth.refreshSession()}>
|
|
128
|
-
Refresh Session
|
|
129
|
-
</button>
|
|
130
|
-
<button onClick={() => auth.setSelectedEventId('event-123')}>
|
|
131
|
-
Set Event
|
|
132
|
-
</button>
|
|
133
|
-
<button onClick={() => auth.resetActivity()}>
|
|
134
|
-
Reset Activity
|
|
135
|
-
</button>
|
|
136
|
-
</div>
|
|
137
|
-
);
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
// Wrapper component
|
|
141
|
-
const TestWrapper = ({
|
|
142
|
-
children,
|
|
143
|
-
supabaseClient,
|
|
144
|
-
appName = 'test-app',
|
|
145
|
-
enableRBAC = false,
|
|
146
|
-
persistState = true,
|
|
147
|
-
enablePersistence = true,
|
|
148
|
-
requireOrganisationContext = true,
|
|
149
|
-
idleTimeoutMs = 30000,
|
|
150
|
-
warnBeforeMs = 60000,
|
|
151
|
-
onIdleLogout = vi.fn(),
|
|
152
|
-
renderInactivityWarning,
|
|
153
|
-
dangerouslyDisableInactivity = false,
|
|
154
|
-
...props
|
|
155
|
-
}: {
|
|
156
|
-
children: React.ReactNode;
|
|
157
|
-
supabaseClient?: any;
|
|
158
|
-
appName?: string;
|
|
159
|
-
enableRBAC?: boolean;
|
|
160
|
-
persistState?: boolean;
|
|
161
|
-
enablePersistence?: boolean;
|
|
162
|
-
requireOrganisationContext?: boolean;
|
|
163
|
-
idleTimeoutMs?: number;
|
|
164
|
-
warnBeforeMs?: number;
|
|
165
|
-
onIdleLogout?: (reason: 'inactivity') => void;
|
|
166
|
-
renderInactivityWarning?: (args: any) => React.ReactNode;
|
|
167
|
-
dangerouslyDisableInactivity?: boolean;
|
|
168
|
-
[key: string]: any;
|
|
169
|
-
}) => (
|
|
170
|
-
<BrowserRouter>
|
|
171
|
-
<UnifiedAuthProvider
|
|
172
|
-
supabaseClient={supabaseClient}
|
|
173
|
-
appName={appName}
|
|
174
|
-
enableRBAC={enableRBAC}
|
|
175
|
-
persistState={persistState}
|
|
176
|
-
enablePersistence={enablePersistence}
|
|
177
|
-
requireOrganisationContext={requireOrganisationContext}
|
|
178
|
-
idleTimeoutMs={idleTimeoutMs}
|
|
179
|
-
warnBeforeMs={warnBeforeMs}
|
|
180
|
-
onIdleLogout={onIdleLogout}
|
|
181
|
-
renderInactivityWarning={renderInactivityWarning}
|
|
182
|
-
dangerouslyDisableInactivity={dangerouslyDisableInactivity}
|
|
183
|
-
{...props}
|
|
184
|
-
>
|
|
185
|
-
{children}
|
|
186
|
-
</UnifiedAuthProvider>
|
|
187
|
-
</BrowserRouter>
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
describe('[component] UnifiedAuthProvider', () => {
|
|
191
|
-
let mockSupabaseClient: any;
|
|
192
|
-
|
|
193
|
-
beforeEach(() => {
|
|
194
|
-
vi.clearAllMocks();
|
|
195
|
-
|
|
196
|
-
// Create mock Supabase client
|
|
197
|
-
mockSupabaseClient = createMockSupabaseClient();
|
|
198
|
-
|
|
199
|
-
// Reset mock states
|
|
200
|
-
mockAuthState.user = null;
|
|
201
|
-
mockAuthState.session = null;
|
|
202
|
-
mockAuthState.isAuthenticated = false;
|
|
203
|
-
mockAuthState.authLoading = true;
|
|
204
|
-
mockAuthState.authError = null;
|
|
205
|
-
mockAuthState.supabase = mockSupabaseClient;
|
|
206
|
-
|
|
207
|
-
// Mock auth state
|
|
208
|
-
mockSupabaseClient.auth.getUser = vi.fn().mockResolvedValue({
|
|
209
|
-
data: { user: testDataGenerators.user({ id: 'user-1' }) },
|
|
210
|
-
error: null
|
|
211
|
-
});
|
|
212
|
-
mockSupabaseClient.auth.getSession = vi.fn().mockResolvedValue({
|
|
213
|
-
data: { session: testDataGenerators.session() },
|
|
214
|
-
error: null
|
|
215
|
-
});
|
|
216
|
-
mockSupabaseClient.auth.onAuthStateChange = vi.fn(() => ({
|
|
217
|
-
data: { subscription: { unsubscribe: vi.fn() } }
|
|
218
|
-
}));
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
afterEach(() => {
|
|
222
|
-
vi.restoreAllMocks();
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
describe('Rendering', () => {
|
|
226
|
-
it('renders children without crashing', () => {
|
|
227
|
-
render(
|
|
228
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
229
|
-
<div>Test content</div>
|
|
230
|
-
</TestWrapper>
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
expect(screen.getByText('Test content')).toBeInTheDocument();
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('renders without supabase client', () => {
|
|
237
|
-
render(
|
|
238
|
-
<TestWrapper>
|
|
239
|
-
<div>Test content</div>
|
|
240
|
-
</TestWrapper>
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
expect(screen.getByText('Test content')).toBeInTheDocument();
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('renders with custom app name', () => {
|
|
247
|
-
render(
|
|
248
|
-
<TestWrapper supabaseClient={mockSupabaseClient} appName="custom-app">
|
|
249
|
-
<TestComponent />
|
|
250
|
-
</TestWrapper>
|
|
251
|
-
);
|
|
252
|
-
|
|
253
|
-
expect(screen.getByTestId('appName')).toHaveTextContent('Custom App');
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
it('renders with all provider components', () => {
|
|
257
|
-
render(
|
|
258
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
259
|
-
<div>Test content</div>
|
|
260
|
-
</TestWrapper>
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
// Should have all providers in the hierarchy
|
|
264
|
-
expect(screen.getByTestId('auth-provider')).toBeInTheDocument();
|
|
265
|
-
expect(screen.getByTestId('rbac-provider')).toBeInTheDocument();
|
|
266
|
-
expect(screen.getByTestId('inactivity-provider')).toBeInTheDocument();
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
describe('Context Composition', () => {
|
|
271
|
-
it('provides combined auth context', () => {
|
|
272
|
-
render(
|
|
273
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
274
|
-
<TestComponent />
|
|
275
|
-
</TestWrapper>
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
expect(screen.getByTestId('user')).toBeInTheDocument();
|
|
279
|
-
expect(screen.getByTestId('isAuthenticated')).toBeInTheDocument();
|
|
280
|
-
expect(screen.getByTestId('isLoading')).toBeInTheDocument();
|
|
281
|
-
expect(screen.getByTestId('hasErrors')).toBeInTheDocument();
|
|
282
|
-
expect(screen.getByTestId('appName')).toBeInTheDocument();
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it('includes RBAC provider when enabled', () => {
|
|
286
|
-
render(
|
|
287
|
-
<TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
|
|
288
|
-
<TestComponent />
|
|
289
|
-
</TestWrapper>
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
expect(screen.getByTestId('rbac-provider')).toBeInTheDocument();
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('includes inactivity provider', () => {
|
|
296
|
-
render(
|
|
297
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
298
|
-
<TestComponent />
|
|
299
|
-
</TestWrapper>
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
expect(screen.getByTestId('inactivity-provider')).toBeInTheDocument();
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
describe('Authentication Integration', () => {
|
|
307
|
-
it('handles successful authentication', async () => {
|
|
308
|
-
const mockUser = testDataGenerators.user({ email: 'test@example.com' });
|
|
309
|
-
const mockSession = testDataGenerators.session({ user: mockUser });
|
|
310
|
-
|
|
311
|
-
// Update mock state
|
|
312
|
-
mockAuthState.user = mockUser;
|
|
313
|
-
mockAuthState.session = mockSession;
|
|
314
|
-
mockAuthState.isAuthenticated = true;
|
|
315
|
-
mockAuthState.authLoading = false;
|
|
316
|
-
mockAuthState.authError = null;
|
|
317
|
-
|
|
318
|
-
render(
|
|
319
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
320
|
-
<TestComponent />
|
|
321
|
-
</TestWrapper>
|
|
322
|
-
);
|
|
323
|
-
|
|
324
|
-
await waitFor(() => {
|
|
325
|
-
expect(screen.getByTestId('user')).toHaveTextContent('test@example.com');
|
|
326
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('true');
|
|
327
|
-
expect(screen.getByTestId('isLoading')).toHaveTextContent('false');
|
|
328
|
-
expect(screen.getByTestId('hasErrors')).toHaveTextContent('false');
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
it('handles authentication errors', async () => {
|
|
333
|
-
const mockError = new Error('Auth error');
|
|
334
|
-
|
|
335
|
-
// Update mock state
|
|
336
|
-
mockAuthState.user = null;
|
|
337
|
-
mockAuthState.session = null;
|
|
338
|
-
mockAuthState.isAuthenticated = false;
|
|
339
|
-
mockAuthState.authLoading = false;
|
|
340
|
-
mockAuthState.authError = mockError;
|
|
341
|
-
|
|
342
|
-
render(
|
|
343
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
344
|
-
<TestComponent />
|
|
345
|
-
</TestWrapper>
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
await waitFor(() => {
|
|
349
|
-
expect(screen.getByTestId('user')).toHaveTextContent('No user');
|
|
350
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('false');
|
|
351
|
-
expect(screen.getByTestId('isLoading')).toHaveTextContent('false');
|
|
352
|
-
expect(screen.getByTestId('hasErrors')).toHaveTextContent('true');
|
|
353
|
-
});
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
it('handles loading states', async () => {
|
|
357
|
-
// Update mock state
|
|
358
|
-
mockAuthState.user = null;
|
|
359
|
-
mockAuthState.session = null;
|
|
360
|
-
mockAuthState.isAuthenticated = false;
|
|
361
|
-
mockAuthState.authLoading = true;
|
|
362
|
-
mockAuthState.authError = null;
|
|
363
|
-
|
|
364
|
-
render(
|
|
365
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
366
|
-
<TestComponent />
|
|
367
|
-
</TestWrapper>
|
|
368
|
-
);
|
|
369
|
-
|
|
370
|
-
expect(screen.getByTestId('isLoading')).toHaveTextContent('true');
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
describe('RBAC Integration', () => {
|
|
375
|
-
it('provides RBAC context when enabled', () => {
|
|
376
|
-
render(
|
|
377
|
-
<TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
|
|
378
|
-
<TestComponent />
|
|
379
|
-
</TestWrapper>
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
expect(screen.getByTestId('hasPermission')).toHaveTextContent('true');
|
|
383
|
-
expect(screen.getByTestId('hasRole')).toHaveTextContent('true');
|
|
384
|
-
expect(screen.getByTestId('accessLevel')).toHaveTextContent('ADMIN');
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
it('does not provide RBAC context when disabled', () => {
|
|
388
|
-
render(
|
|
389
|
-
<TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={false}>
|
|
390
|
-
<TestComponent />
|
|
391
|
-
</TestWrapper>
|
|
392
|
-
);
|
|
393
|
-
|
|
394
|
-
// RBAC should still be available but may return different values
|
|
395
|
-
expect(screen.getByTestId('hasPermission')).toBeInTheDocument();
|
|
396
|
-
expect(screen.getByTestId('hasRole')).toBeInTheDocument();
|
|
397
|
-
expect(screen.getByTestId('accessLevel')).toBeInTheDocument();
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
it('handles RBAC errors', async () => {
|
|
401
|
-
// Update RBAC mock state
|
|
402
|
-
mockRBACState.rbacError = new Error('RBAC error');
|
|
403
|
-
mockRBACState.rbacLoading = false;
|
|
404
|
-
|
|
405
|
-
render(
|
|
406
|
-
<TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
|
|
407
|
-
<TestComponent />
|
|
408
|
-
</TestWrapper>
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
await waitFor(() => {
|
|
412
|
-
expect(screen.getByTestId('hasErrors')).toHaveTextContent('true');
|
|
413
|
-
});
|
|
414
|
-
});
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
describe('Inactivity Integration', () => {
|
|
418
|
-
it('provides inactivity context', () => {
|
|
419
|
-
render(
|
|
420
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
421
|
-
<TestComponent />
|
|
422
|
-
</TestWrapper>
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
expect(screen.getByTestId('showInactivityWarning')).toHaveTextContent('false');
|
|
426
|
-
expect(screen.getByTestId('isIdle')).toHaveTextContent('false');
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
it('handles inactivity warning state', async () => {
|
|
430
|
-
// Update inactivity mock state
|
|
431
|
-
mockInactivityState.showInactivityWarning = true;
|
|
432
|
-
mockInactivityState.inactivityTimeRemaining = 30000;
|
|
433
|
-
|
|
434
|
-
render(
|
|
435
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
436
|
-
<TestComponent />
|
|
437
|
-
</TestWrapper>
|
|
438
|
-
);
|
|
439
|
-
|
|
440
|
-
await waitFor(() => {
|
|
441
|
-
expect(screen.getByTestId('showInactivityWarning')).toHaveTextContent('true');
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
it('handles idle state', async () => {
|
|
446
|
-
// Update inactivity mock state
|
|
447
|
-
mockInactivityState.isIdle = true;
|
|
448
|
-
|
|
449
|
-
render(
|
|
450
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
451
|
-
<TestComponent />
|
|
452
|
-
</TestWrapper>
|
|
453
|
-
);
|
|
454
|
-
|
|
455
|
-
await waitFor(() => {
|
|
456
|
-
expect(screen.getByTestId('isIdle')).toHaveTextContent('true');
|
|
457
|
-
});
|
|
458
|
-
});
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
describe('Loading States', () => {
|
|
462
|
-
it('combines loading states from all providers', async () => {
|
|
463
|
-
// Update mock states
|
|
464
|
-
mockAuthState.authLoading = true;
|
|
465
|
-
mockRBACState.rbacLoading = false;
|
|
466
|
-
|
|
467
|
-
render(
|
|
468
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
469
|
-
<TestComponent />
|
|
470
|
-
</TestWrapper>
|
|
471
|
-
);
|
|
472
|
-
|
|
473
|
-
expect(screen.getByTestId('isLoading')).toHaveTextContent('true');
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
it('handles mixed loading states', async () => {
|
|
477
|
-
// Update mock states
|
|
478
|
-
mockAuthState.authLoading = false;
|
|
479
|
-
mockRBACState.rbacLoading = true;
|
|
480
|
-
|
|
481
|
-
render(
|
|
482
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
483
|
-
<TestComponent />
|
|
484
|
-
</TestWrapper>
|
|
485
|
-
);
|
|
486
|
-
|
|
487
|
-
expect(screen.getByTestId('isLoading')).toHaveTextContent('true');
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
it('shows no loading when all providers are ready', async () => {
|
|
491
|
-
// Update mock states
|
|
492
|
-
mockAuthState.authLoading = false;
|
|
493
|
-
mockRBACState.rbacLoading = false;
|
|
494
|
-
|
|
495
|
-
render(
|
|
496
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
497
|
-
<TestComponent />
|
|
498
|
-
</TestWrapper>
|
|
499
|
-
);
|
|
500
|
-
|
|
501
|
-
expect(screen.getByTestId('isLoading')).toHaveTextContent('false');
|
|
502
|
-
});
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
describe('Error States', () => {
|
|
506
|
-
it('combines error states from all providers', async () => {
|
|
507
|
-
// Update mock states
|
|
508
|
-
mockAuthState.authError = new Error('Auth error');
|
|
509
|
-
mockRBACState.rbacError = null;
|
|
510
|
-
|
|
511
|
-
render(
|
|
512
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
513
|
-
<TestComponent />
|
|
514
|
-
</TestWrapper>
|
|
515
|
-
);
|
|
516
|
-
|
|
517
|
-
await waitFor(() => {
|
|
518
|
-
expect(screen.getByTestId('hasErrors')).toHaveTextContent('true');
|
|
519
|
-
});
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
it('handles no errors state', async () => {
|
|
523
|
-
// Update mock states
|
|
524
|
-
mockAuthState.authError = null;
|
|
525
|
-
mockRBACState.rbacError = null;
|
|
526
|
-
|
|
527
|
-
render(
|
|
528
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
529
|
-
<TestComponent />
|
|
530
|
-
</TestWrapper>
|
|
531
|
-
);
|
|
532
|
-
|
|
533
|
-
await waitFor(() => {
|
|
534
|
-
expect(screen.getByTestId('hasErrors')).toHaveTextContent('false');
|
|
535
|
-
});
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
it('handles multiple errors', async () => {
|
|
539
|
-
// Update mock states
|
|
540
|
-
mockAuthState.authError = new Error('Auth error');
|
|
541
|
-
mockRBACState.rbacError = new Error('RBAC error');
|
|
542
|
-
|
|
543
|
-
render(
|
|
544
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
545
|
-
<TestComponent />
|
|
546
|
-
</TestWrapper>
|
|
547
|
-
);
|
|
548
|
-
|
|
549
|
-
await waitFor(() => {
|
|
550
|
-
expect(screen.getByTestId('hasErrors')).toHaveTextContent('true');
|
|
551
|
-
});
|
|
552
|
-
});
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
describe('Authentication Methods', () => {
|
|
556
|
-
it('handles sign in', async () => {
|
|
557
|
-
render(
|
|
558
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
559
|
-
<TestComponent />
|
|
560
|
-
</TestWrapper>
|
|
561
|
-
);
|
|
562
|
-
|
|
563
|
-
const signInButton = screen.getByText('Sign In');
|
|
564
|
-
await userEvent.click(signInButton);
|
|
565
|
-
|
|
566
|
-
expect(mockAuthState.signIn).toHaveBeenCalledWith('test@example.com', 'password');
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
it('handles sign out', async () => {
|
|
570
|
-
render(
|
|
571
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
572
|
-
<TestComponent />
|
|
573
|
-
</TestWrapper>
|
|
574
|
-
);
|
|
575
|
-
|
|
576
|
-
const signOutButton = screen.getByText('Sign Out');
|
|
577
|
-
await userEvent.click(signOutButton);
|
|
578
|
-
|
|
579
|
-
expect(mockAuthState.signOut).toHaveBeenCalled();
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
it('handles sign up', async () => {
|
|
583
|
-
render(
|
|
584
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
585
|
-
<TestComponent />
|
|
586
|
-
</TestWrapper>
|
|
587
|
-
);
|
|
588
|
-
|
|
589
|
-
const signUpButton = screen.getByText('Sign Up');
|
|
590
|
-
await userEvent.click(signUpButton);
|
|
591
|
-
|
|
592
|
-
expect(mockAuthState.signUp).toHaveBeenCalledWith('test@example.com', 'password');
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
it('handles password reset', async () => {
|
|
596
|
-
render(
|
|
597
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
598
|
-
<TestComponent />
|
|
599
|
-
</TestWrapper>
|
|
600
|
-
);
|
|
601
|
-
|
|
602
|
-
const resetButton = screen.getByText('Reset Password');
|
|
603
|
-
await userEvent.click(resetButton);
|
|
604
|
-
|
|
605
|
-
expect(mockAuthState.resetPassword).toHaveBeenCalledWith('test@example.com');
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
it('handles password update', async () => {
|
|
609
|
-
render(
|
|
610
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
611
|
-
<TestComponent />
|
|
612
|
-
</TestWrapper>
|
|
613
|
-
);
|
|
614
|
-
|
|
615
|
-
const updateButton = screen.getByText('Update Password');
|
|
616
|
-
await userEvent.click(updateButton);
|
|
617
|
-
|
|
618
|
-
expect(mockAuthState.updatePassword).toHaveBeenCalledWith('newpassword');
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
it('handles session refresh', async () => {
|
|
622
|
-
render(
|
|
623
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
624
|
-
<TestComponent />
|
|
625
|
-
</TestWrapper>
|
|
626
|
-
);
|
|
627
|
-
|
|
628
|
-
const refreshButton = screen.getByText('Refresh Session');
|
|
629
|
-
await userEvent.click(refreshButton);
|
|
630
|
-
|
|
631
|
-
expect(mockAuthState.refreshSession).toHaveBeenCalled();
|
|
632
|
-
});
|
|
633
|
-
});
|
|
634
|
-
|
|
635
|
-
describe('RBAC Methods', () => {
|
|
636
|
-
it('handles permission checking', () => {
|
|
637
|
-
render(
|
|
638
|
-
<TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
|
|
639
|
-
<TestComponent />
|
|
640
|
-
</TestWrapper>
|
|
641
|
-
);
|
|
642
|
-
|
|
643
|
-
expect(screen.getByTestId('hasPermission')).toHaveTextContent('true');
|
|
644
|
-
expect(mockRBACState.hasPermission).toHaveBeenCalledWith('test:permission');
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
it('handles role checking', () => {
|
|
648
|
-
render(
|
|
649
|
-
<TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
|
|
650
|
-
<TestComponent />
|
|
651
|
-
</TestWrapper>
|
|
652
|
-
);
|
|
653
|
-
|
|
654
|
-
expect(screen.getByTestId('hasRole')).toHaveTextContent('true');
|
|
655
|
-
expect(mockRBACState.hasRole).toHaveBeenCalledWith('admin');
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
it('handles event ID setting', async () => {
|
|
659
|
-
render(
|
|
660
|
-
<TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
|
|
661
|
-
<TestComponent />
|
|
662
|
-
</TestWrapper>
|
|
663
|
-
);
|
|
664
|
-
|
|
665
|
-
const setEventButton = screen.getByText('Set Event');
|
|
666
|
-
await userEvent.click(setEventButton);
|
|
667
|
-
|
|
668
|
-
expect(mockRBACState.setSelectedEventId).toHaveBeenCalledWith('event-123');
|
|
669
|
-
});
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
describe('Inactivity Methods', () => {
|
|
673
|
-
it('handles activity reset', async () => {
|
|
674
|
-
render(
|
|
675
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
676
|
-
<TestComponent />
|
|
677
|
-
</TestWrapper>
|
|
678
|
-
);
|
|
679
|
-
|
|
680
|
-
const resetButton = screen.getByText('Reset Activity');
|
|
681
|
-
await userEvent.click(resetButton);
|
|
682
|
-
|
|
683
|
-
expect(mockInactivityState.resetActivity).toHaveBeenCalled();
|
|
684
|
-
});
|
|
685
|
-
});
|
|
686
|
-
|
|
687
|
-
describe('Configuration Options', () => {
|
|
688
|
-
it('handles custom configuration', () => {
|
|
689
|
-
render(
|
|
690
|
-
<TestWrapper
|
|
691
|
-
supabaseClient={mockSupabaseClient}
|
|
692
|
-
persistState={false}
|
|
693
|
-
enablePersistence={false}
|
|
694
|
-
requireOrganisationContext={false}
|
|
695
|
-
enableRBAC={true}
|
|
696
|
-
idleTimeoutMs={120000}
|
|
697
|
-
warnBeforeMs={60000}
|
|
698
|
-
>
|
|
699
|
-
<div>Test content</div>
|
|
700
|
-
</TestWrapper>
|
|
701
|
-
);
|
|
702
|
-
|
|
703
|
-
expect(screen.getByText('Test content')).toBeInTheDocument();
|
|
704
|
-
});
|
|
705
|
-
|
|
706
|
-
it('uses default configuration', () => {
|
|
707
|
-
render(
|
|
708
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
709
|
-
<div>Test content</div>
|
|
710
|
-
</TestWrapper>
|
|
711
|
-
);
|
|
712
|
-
|
|
713
|
-
expect(screen.getByText('Test content')).toBeInTheDocument();
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
it('handles inactivity configuration', () => {
|
|
717
|
-
const customOnIdleLogout = vi.fn();
|
|
718
|
-
const customRenderWarning = vi.fn(() => <div>Custom Warning</div>);
|
|
719
|
-
|
|
720
|
-
render(
|
|
721
|
-
<TestWrapper
|
|
722
|
-
supabaseClient={mockSupabaseClient}
|
|
723
|
-
idleTimeoutMs={60000}
|
|
724
|
-
warnBeforeMs={30000}
|
|
725
|
-
onIdleLogout={customOnIdleLogout}
|
|
726
|
-
renderInactivityWarning={customRenderWarning}
|
|
727
|
-
>
|
|
728
|
-
<div>Test content</div>
|
|
729
|
-
</TestWrapper>
|
|
730
|
-
);
|
|
731
|
-
|
|
732
|
-
expect(screen.getByText('Test content')).toBeInTheDocument();
|
|
733
|
-
});
|
|
734
|
-
});
|
|
735
|
-
|
|
736
|
-
describe('useUnifiedAuth Hook', () => {
|
|
737
|
-
it('throws error when used outside provider', () => {
|
|
738
|
-
// Suppress console.error for this test
|
|
739
|
-
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
740
|
-
|
|
741
|
-
expect(() => {
|
|
742
|
-
render(<TestComponent />);
|
|
743
|
-
}).toThrow('useUnifiedAuth must be used within a UnifiedAuthProvider');
|
|
744
|
-
|
|
745
|
-
consoleSpy.mockRestore();
|
|
746
|
-
});
|
|
747
|
-
|
|
748
|
-
it('provides all required context values', () => {
|
|
749
|
-
const TestContextComponent = () => {
|
|
750
|
-
const auth = useUnifiedAuth();
|
|
751
|
-
|
|
752
|
-
return (
|
|
753
|
-
<div>
|
|
754
|
-
<div data-testid="hasUser">{auth.user !== undefined ? 'true' : 'false'}</div>
|
|
755
|
-
<div data-testid="hasSession">{auth.session !== undefined ? 'true' : 'false'}</div>
|
|
756
|
-
<div data-testid="hasSupabase">{auth.supabase !== undefined ? 'true' : 'false'}</div>
|
|
757
|
-
<div data-testid="hasAppName">{typeof auth.appName === 'string' ? 'true' : 'false'}</div>
|
|
758
|
-
<div data-testid="hasIsLoading">{typeof auth.isLoading === 'boolean' ? 'true' : 'false'}</div>
|
|
759
|
-
<div data-testid="hasHasErrors">{typeof auth.hasErrors === 'boolean' ? 'true' : 'false'}</div>
|
|
760
|
-
<div data-testid="hasSignIn">{typeof auth.signIn === 'function' ? 'true' : 'false'}</div>
|
|
761
|
-
<div data-testid="hasSignOut">{typeof auth.signOut === 'function' ? 'true' : 'false'}</div>
|
|
762
|
-
<div data-testid="hasHasPermission">{typeof auth.hasPermission === 'function' ? 'true' : 'false'}</div>
|
|
763
|
-
<div data-testid="hasHasRole">{typeof auth.hasRole === 'function' ? 'true' : 'false'}</div>
|
|
764
|
-
<div data-testid="hasHasAccessLevel">{typeof auth.hasAccessLevel === 'function' ? 'true' : 'false'}</div>
|
|
765
|
-
<div data-testid="hasValidatePermission">{typeof auth.validatePermission === 'function' ? 'true' : 'false'}</div>
|
|
766
|
-
<div data-testid="hasCanAccess">{typeof auth.canAccess === 'function' ? 'true' : 'false'}</div>
|
|
767
|
-
<div data-testid="hasSetSelectedEventId">{typeof auth.setSelectedEventId === 'function' ? 'true' : 'false'}</div>
|
|
768
|
-
<div data-testid="hasResetActivity">{typeof auth.resetActivity === 'function' ? 'true' : 'false'}</div>
|
|
769
|
-
<div data-testid="hasStartTracking">{typeof auth.startTracking === 'function' ? 'true' : 'false'}</div>
|
|
770
|
-
<div data-testid="hasStopTracking">{typeof auth.stopTracking === 'function' ? 'true' : 'false'}</div>
|
|
771
|
-
</div>
|
|
772
|
-
);
|
|
773
|
-
};
|
|
774
|
-
|
|
775
|
-
render(
|
|
776
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
777
|
-
<TestContextComponent />
|
|
778
|
-
</TestWrapper>
|
|
779
|
-
);
|
|
780
|
-
|
|
781
|
-
expect(screen.getByTestId('hasUser')).toHaveTextContent('true');
|
|
782
|
-
expect(screen.getByTestId('hasSession')).toHaveTextContent('true');
|
|
783
|
-
expect(screen.getByTestId('hasSupabase')).toHaveTextContent('true');
|
|
784
|
-
expect(screen.getByTestId('hasAppName')).toHaveTextContent('true');
|
|
785
|
-
expect(screen.getByTestId('hasIsLoading')).toHaveTextContent('true');
|
|
786
|
-
expect(screen.getByTestId('hasHasErrors')).toHaveTextContent('true');
|
|
787
|
-
expect(screen.getByTestId('hasSignIn')).toHaveTextContent('true');
|
|
788
|
-
expect(screen.getByTestId('hasSignOut')).toHaveTextContent('true');
|
|
789
|
-
expect(screen.getByTestId('hasHasPermission')).toHaveTextContent('true');
|
|
790
|
-
expect(screen.getByTestId('hasHasRole')).toHaveTextContent('true');
|
|
791
|
-
expect(screen.getByTestId('hasHasAccessLevel')).toHaveTextContent('true');
|
|
792
|
-
expect(screen.getByTestId('hasValidatePermission')).toHaveTextContent('true');
|
|
793
|
-
expect(screen.getByTestId('hasCanAccess')).toHaveTextContent('true');
|
|
794
|
-
expect(screen.getByTestId('hasSetSelectedEventId')).toHaveTextContent('true');
|
|
795
|
-
expect(screen.getByTestId('hasResetActivity')).toHaveTextContent('true');
|
|
796
|
-
expect(screen.getByTestId('hasStartTracking')).toHaveTextContent('true');
|
|
797
|
-
expect(screen.getByTestId('hasStopTracking')).toHaveTextContent('true');
|
|
798
|
-
});
|
|
799
|
-
});
|
|
800
|
-
|
|
801
|
-
describe('Provider Composition', () => {
|
|
802
|
-
it('maintains proper provider hierarchy', () => {
|
|
803
|
-
render(
|
|
804
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
805
|
-
<div>Test content</div>
|
|
806
|
-
</TestWrapper>
|
|
807
|
-
);
|
|
808
|
-
|
|
809
|
-
// Should have all providers in the hierarchy
|
|
810
|
-
expect(screen.getByText('Test content')).toBeInTheDocument();
|
|
811
|
-
});
|
|
812
|
-
|
|
813
|
-
it('passes props correctly to child providers', () => {
|
|
814
|
-
const TestPropsComponent = () => {
|
|
815
|
-
const auth = useUnifiedAuth();
|
|
816
|
-
return (
|
|
817
|
-
<div>
|
|
818
|
-
<div data-testid="appName">{auth.appName}</div>
|
|
819
|
-
</div>
|
|
820
|
-
);
|
|
821
|
-
};
|
|
822
|
-
|
|
823
|
-
render(
|
|
824
|
-
<TestWrapper supabaseClient={mockSupabaseClient} appName="custom-app">
|
|
825
|
-
<TestPropsComponent />
|
|
826
|
-
</TestWrapper>
|
|
827
|
-
);
|
|
828
|
-
|
|
829
|
-
expect(screen.getByTestId('appName')).toHaveTextContent('Custom App');
|
|
830
|
-
});
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
describe('Context Value Stability', () => {
|
|
834
|
-
it('maintains stable context value references', () => {
|
|
835
|
-
const TestStabilityComponent = () => {
|
|
836
|
-
const auth = useUnifiedAuth();
|
|
837
|
-
const [renderCount, setRenderCount] = React.useState(0);
|
|
838
|
-
|
|
839
|
-
React.useEffect(() => {
|
|
840
|
-
// Limit render count to prevent infinite loops
|
|
841
|
-
if (renderCount < 5) {
|
|
842
|
-
setRenderCount(prev => prev + 1);
|
|
843
|
-
}
|
|
844
|
-
});
|
|
845
|
-
|
|
846
|
-
return (
|
|
847
|
-
<div>
|
|
848
|
-
<div data-testid="renderCount">{renderCount}</div>
|
|
849
|
-
<div data-testid="hasStableSignIn">{typeof auth.signIn === 'function' ? 'true' : 'false'}</div>
|
|
850
|
-
<div data-testid="hasStableHasPermission">{typeof auth.hasPermission === 'function' ? 'true' : 'false'}</div>
|
|
851
|
-
</div>
|
|
852
|
-
);
|
|
853
|
-
};
|
|
854
|
-
|
|
855
|
-
render(
|
|
856
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
857
|
-
<TestStabilityComponent />
|
|
858
|
-
</TestWrapper>
|
|
859
|
-
);
|
|
860
|
-
|
|
861
|
-
// Should render multiple times but maintain stable function references
|
|
862
|
-
expect(screen.getByTestId('hasStableSignIn')).toHaveTextContent('true');
|
|
863
|
-
expect(screen.getByTestId('hasStableHasPermission')).toHaveTextContent('true');
|
|
864
|
-
}, 5000);
|
|
865
|
-
});
|
|
866
|
-
|
|
867
|
-
describe('Cleanup', () => {
|
|
868
|
-
it('handles component unmount gracefully', () => {
|
|
869
|
-
const { unmount } = render(
|
|
870
|
-
<TestWrapper supabaseClient={mockSupabaseClient}>
|
|
871
|
-
<div>Test content</div>
|
|
872
|
-
</TestWrapper>
|
|
873
|
-
);
|
|
874
|
-
|
|
875
|
-
// Should not throw errors on unmount
|
|
876
|
-
expect(() => unmount()).not.toThrow();
|
|
877
|
-
});
|
|
878
|
-
});
|
|
879
|
-
|
|
880
|
-
describe('Edge Cases', () => {
|
|
881
|
-
it('handles missing required props', () => {
|
|
882
|
-
expect(() => {
|
|
883
|
-
render(
|
|
884
|
-
<TestWrapper>
|
|
885
|
-
<div>Test content</div>
|
|
886
|
-
</TestWrapper>
|
|
887
|
-
);
|
|
888
|
-
}).not.toThrow();
|
|
889
|
-
});
|
|
890
|
-
|
|
891
|
-
it('handles null supabase client', () => {
|
|
892
|
-
expect(() => {
|
|
893
|
-
render(
|
|
894
|
-
<TestWrapper supabaseClient={null}>
|
|
895
|
-
<div>Test content</div>
|
|
896
|
-
</TestWrapper>
|
|
897
|
-
);
|
|
898
|
-
}).not.toThrow();
|
|
899
|
-
});
|
|
900
|
-
|
|
901
|
-
it('handles undefined app name', () => {
|
|
902
|
-
expect(() => {
|
|
903
|
-
render(
|
|
904
|
-
<TestWrapper supabaseClient={mockSupabaseClient} appName={undefined as any}>
|
|
905
|
-
<div>Test content</div>
|
|
906
|
-
</TestWrapper>
|
|
907
|
-
);
|
|
908
|
-
}).not.toThrow();
|
|
909
|
-
});
|
|
910
|
-
});
|
|
911
|
-
});
|