@jmruthers/pace-core 0.5.74 → 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-2QR5TER5.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-K4NRGXL4.js → UnifiedAuthProvider-3NKDOSOK.js} +6 -4
- package/dist/UnifiedAuthProvider-Bj6YCf7c.d.ts +113 -0
- package/dist/{chunk-UJMCGBLS.js → chunk-2CHATWBF.js} +5 -7
- package/dist/chunk-2CHATWBF.js.map +1 -0
- package/dist/{chunk-BKVGJVUR.js → chunk-2DFZ432F.js} +496 -30
- package/dist/chunk-2DFZ432F.js.map +1 -0
- package/dist/{chunk-LVQ26TCN.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-IHMMNKNA.js → chunk-CY3AHGO4.js} +6256 -1937
- package/dist/chunk-CY3AHGO4.js.map +1 -0
- package/dist/{chunk-H2TNUICK.js → chunk-DAXLNIDY.js} +47 -49
- 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-DG5Z55HH.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-ORSMVXO2.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/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/__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/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/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-3SP4P7NS.js.map +0 -1
- package/dist/chunk-B5LK25HV.js +0 -953
- package/dist/chunk-B5LK25HV.js.map +0 -1
- package/dist/chunk-BKVGJVUR.js.map +0 -1
- package/dist/chunk-C5Q5LRU5.js +0 -5691
- package/dist/chunk-C5Q5LRU5.js.map +0 -1
- package/dist/chunk-CDDYJCYU.js +0 -79
- package/dist/chunk-CDDYJCYU.js.map +0 -1
- package/dist/chunk-DG5Z55HH.js.map +0 -1
- package/dist/chunk-H2TNUICK.js.map +0 -1
- package/dist/chunk-IHMMNKNA.js.map +0 -1
- package/dist/chunk-LVQ26TCN.js.map +0 -1
- package/dist/chunk-ORSMVXO2.js.map +0 -1
- package/dist/chunk-UJMCGBLS.js.map +0 -1
- package/dist/chunk-V6BHACCH.js +0 -17
- package/dist/chunk-V6BHACCH.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-2QR5TER5.js.map → DataTable-HWZQGASI.js.map} +0 -0
- package/dist/{UnifiedAuthProvider-K4NRGXL4.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,771 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file AuthProvider Tests
|
|
3
|
-
* @description Comprehensive tests for AuthProvider component
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from 'react';
|
|
7
|
-
import { render, screen, waitFor, act } from '@testing-library/react';
|
|
8
|
-
import userEvent from '@testing-library/user-event';
|
|
9
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
10
|
-
import { AuthProvider, useAuth } from '../AuthProvider';
|
|
11
|
-
import { createMockSupabaseClient, testDataGenerators } from '../../__tests__/helpers/test-utils';
|
|
12
|
-
|
|
13
|
-
// Mock the debug logger
|
|
14
|
-
vi.mock('../../utils/debugLogger', () => ({
|
|
15
|
-
DebugLogger: {
|
|
16
|
-
log: vi.fn(),
|
|
17
|
-
},
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
// Test component that uses the auth context
|
|
21
|
-
const TestComponent = () => {
|
|
22
|
-
const auth = useAuth();
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<div>
|
|
26
|
-
<div data-testid="user">{auth.user?.email || 'No user'}</div>
|
|
27
|
-
<div data-testid="isAuthenticated">{auth.isAuthenticated ? 'true' : 'false'}</div>
|
|
28
|
-
<div data-testid="authLoading">{auth.authLoading ? 'true' : 'false'}</div>
|
|
29
|
-
<div data-testid="authError">{auth.authError?.message || 'No error'}</div>
|
|
30
|
-
<button onClick={() => auth.signIn('test@example.com', 'password')}>
|
|
31
|
-
Sign In
|
|
32
|
-
</button>
|
|
33
|
-
<button onClick={() => auth.signUp('test@example.com', 'password')}>
|
|
34
|
-
Sign Up
|
|
35
|
-
</button>
|
|
36
|
-
<button onClick={() => auth.signOut()}>
|
|
37
|
-
Sign Out
|
|
38
|
-
</button>
|
|
39
|
-
<button onClick={() => auth.resetPassword('test@example.com')}>
|
|
40
|
-
Reset Password
|
|
41
|
-
</button>
|
|
42
|
-
<button onClick={() => auth.updatePassword('newpassword')}>
|
|
43
|
-
Update Password
|
|
44
|
-
</button>
|
|
45
|
-
<button onClick={() => auth.refreshSession()}>
|
|
46
|
-
Refresh Session
|
|
47
|
-
</button>
|
|
48
|
-
</div>
|
|
49
|
-
);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
describe('AuthProvider', () => {
|
|
53
|
-
let mockSupabaseClient: any;
|
|
54
|
-
let mockAuthStateChange: any;
|
|
55
|
-
|
|
56
|
-
beforeEach(() => {
|
|
57
|
-
vi.clearAllMocks();
|
|
58
|
-
|
|
59
|
-
// Create mock auth state change listener
|
|
60
|
-
mockAuthStateChange = vi.fn();
|
|
61
|
-
|
|
62
|
-
// Create mock Supabase client
|
|
63
|
-
mockSupabaseClient = createMockSupabaseClient();
|
|
64
|
-
mockSupabaseClient.auth.onAuthStateChange = vi.fn(() => ({
|
|
65
|
-
data: { subscription: { unsubscribe: vi.fn() } }
|
|
66
|
-
}));
|
|
67
|
-
mockSupabaseClient.auth.getSession = vi.fn();
|
|
68
|
-
mockSupabaseClient.auth.getUser = vi.fn();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
afterEach(() => {
|
|
72
|
-
vi.restoreAllMocks();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe('Rendering', () => {
|
|
76
|
-
it('renders children without crashing', () => {
|
|
77
|
-
render(
|
|
78
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
79
|
-
<div>Test content</div>
|
|
80
|
-
</AuthProvider>
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
expect(screen.getByText('Test content')).toBeInTheDocument();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('renders without supabase client', () => {
|
|
87
|
-
render(
|
|
88
|
-
<AuthProvider>
|
|
89
|
-
<div>Test content</div>
|
|
90
|
-
</AuthProvider>
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
expect(screen.getByText('Test content')).toBeInTheDocument();
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe('Context Values', () => {
|
|
98
|
-
it('provides initial context values', () => {
|
|
99
|
-
render(
|
|
100
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
101
|
-
<TestComponent />
|
|
102
|
-
</AuthProvider>
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
expect(screen.getByTestId('user')).toHaveTextContent('No user');
|
|
106
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('false');
|
|
107
|
-
expect(screen.getByTestId('authLoading')).toHaveTextContent('true');
|
|
108
|
-
expect(screen.getByTestId('authError')).toHaveTextContent('No error');
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('provides supabase client in context', () => {
|
|
112
|
-
const TestSupabaseComponent = () => {
|
|
113
|
-
const { supabase } = useAuth();
|
|
114
|
-
return <div data-testid="supabase">{supabase ? 'Available' : 'Not available'}</div>;
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
render(
|
|
118
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
119
|
-
<TestSupabaseComponent />
|
|
120
|
-
</AuthProvider>
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
expect(screen.getByTestId('supabase')).toHaveTextContent('Available');
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
describe('Authentication State Management', () => {
|
|
128
|
-
it('handles successful session restoration', async () => {
|
|
129
|
-
const mockSession = testDataGenerators.session({
|
|
130
|
-
user: testDataGenerators.user({ email: 'test@example.com' })
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
mockSupabaseClient.auth.getSession.mockResolvedValue({
|
|
134
|
-
data: { session: mockSession },
|
|
135
|
-
error: null
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
render(
|
|
139
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
140
|
-
<TestComponent />
|
|
141
|
-
</AuthProvider>
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
await waitFor(() => {
|
|
145
|
-
expect(screen.getByTestId('user')).toHaveTextContent('test@example.com');
|
|
146
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('true');
|
|
147
|
-
expect(screen.getByTestId('authLoading')).toHaveTextContent('false');
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('handles session restoration with error', async () => {
|
|
152
|
-
const mockError = new Error('Session error');
|
|
153
|
-
mockSupabaseClient.auth.getSession.mockResolvedValue({
|
|
154
|
-
data: { session: null },
|
|
155
|
-
error: mockError
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
render(
|
|
159
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
160
|
-
<TestComponent />
|
|
161
|
-
</AuthProvider>
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
await waitFor(() => {
|
|
165
|
-
expect(screen.getByTestId('user')).toHaveTextContent('No user');
|
|
166
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('false');
|
|
167
|
-
expect(screen.getByTestId('authLoading')).toHaveTextContent('false');
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('handles user without session', async () => {
|
|
172
|
-
const mockUser = testDataGenerators.user({ email: 'test@example.com' });
|
|
173
|
-
|
|
174
|
-
mockSupabaseClient.auth.getSession.mockResolvedValue({
|
|
175
|
-
data: { session: null },
|
|
176
|
-
error: null
|
|
177
|
-
});
|
|
178
|
-
mockSupabaseClient.auth.getUser.mockResolvedValue({
|
|
179
|
-
data: { user: mockUser },
|
|
180
|
-
error: null
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
render(
|
|
184
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
185
|
-
<TestComponent />
|
|
186
|
-
</AuthProvider>
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
await waitFor(() => {
|
|
190
|
-
expect(screen.getByTestId('user')).toHaveTextContent('test@example.com');
|
|
191
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('false');
|
|
192
|
-
expect(screen.getByTestId('authLoading')).toHaveTextContent('false');
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('handles auth state changes', async () => {
|
|
197
|
-
const mockSession = testDataGenerators.session({
|
|
198
|
-
user: testDataGenerators.user({ email: 'test@example.com' })
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// Mock the auth state change callback
|
|
202
|
-
let authStateCallback: any;
|
|
203
|
-
mockSupabaseClient.auth.onAuthStateChange.mockImplementation((callback: any) => {
|
|
204
|
-
authStateCallback = callback;
|
|
205
|
-
return { data: { subscription: { unsubscribe: vi.fn() } } };
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
render(
|
|
209
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
210
|
-
<TestComponent />
|
|
211
|
-
</AuthProvider>
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
// Simulate SIGNED_IN event
|
|
215
|
-
await act(async () => {
|
|
216
|
-
authStateCallback('SIGNED_IN', mockSession);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
await waitFor(() => {
|
|
220
|
-
expect(screen.getByTestId('user')).toHaveTextContent('test@example.com');
|
|
221
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('true');
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('handles SIGNED_OUT event', async () => {
|
|
226
|
-
// Mock the auth state change callback
|
|
227
|
-
let authStateCallback: any;
|
|
228
|
-
mockSupabaseClient.auth.onAuthStateChange.mockImplementation((callback: any) => {
|
|
229
|
-
authStateCallback = callback;
|
|
230
|
-
return { data: { subscription: { unsubscribe: vi.fn() } } };
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
render(
|
|
234
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
235
|
-
<TestComponent />
|
|
236
|
-
</AuthProvider>
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
// Simulate SIGNED_OUT event
|
|
240
|
-
await act(async () => {
|
|
241
|
-
authStateCallback('SIGNED_OUT', null);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
await waitFor(() => {
|
|
245
|
-
expect(screen.getByTestId('user')).toHaveTextContent('No user');
|
|
246
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('false');
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
describe('Authentication Methods', () => {
|
|
252
|
-
beforeEach(() => {
|
|
253
|
-
mockSupabaseClient.auth.signInWithPassword = vi.fn();
|
|
254
|
-
mockSupabaseClient.auth.signUp = vi.fn();
|
|
255
|
-
mockSupabaseClient.auth.signOut = vi.fn();
|
|
256
|
-
mockSupabaseClient.auth.resetPasswordForEmail = vi.fn();
|
|
257
|
-
mockSupabaseClient.auth.updateUser = vi.fn();
|
|
258
|
-
mockSupabaseClient.auth.refreshSession = vi.fn();
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it('handles sign in with password', async () => {
|
|
262
|
-
const mockResult = { data: { user: testDataGenerators.user() }, error: null };
|
|
263
|
-
mockSupabaseClient.auth.signInWithPassword.mockResolvedValue(mockResult);
|
|
264
|
-
|
|
265
|
-
render(
|
|
266
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
267
|
-
<TestComponent />
|
|
268
|
-
</AuthProvider>
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
// Use fireEvent instead of userEvent to avoid CSS issues
|
|
272
|
-
const signInButton = screen.getByText('Sign In');
|
|
273
|
-
signInButton.click();
|
|
274
|
-
|
|
275
|
-
expect(mockSupabaseClient.auth.signInWithPassword).toHaveBeenCalledWith({
|
|
276
|
-
email: 'test@example.com',
|
|
277
|
-
password: 'password'
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('handles sign in without password', async () => {
|
|
282
|
-
const mockResult = { data: { user: testDataGenerators.user() }, error: null };
|
|
283
|
-
mockSupabaseClient.auth.signInWithPassword.mockResolvedValue(mockResult);
|
|
284
|
-
|
|
285
|
-
// Create a test component that calls signIn without password
|
|
286
|
-
const TestNoPasswordComponent = () => {
|
|
287
|
-
const { signIn } = useAuth();
|
|
288
|
-
|
|
289
|
-
const handleSignIn = () => {
|
|
290
|
-
signIn('test@example.com'); // No password provided
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
return (
|
|
294
|
-
<button onClick={handleSignIn}>Sign In No Password</button>
|
|
295
|
-
);
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
render(
|
|
299
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
300
|
-
<TestNoPasswordComponent />
|
|
301
|
-
</AuthProvider>
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
screen.getByText('Sign In No Password').click();
|
|
305
|
-
|
|
306
|
-
expect(mockSupabaseClient.auth.signInWithPassword).toHaveBeenCalledWith({
|
|
307
|
-
email: 'test@example.com',
|
|
308
|
-
password: ''
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it('handles sign up', async () => {
|
|
313
|
-
const mockResult = { data: { user: testDataGenerators.user() }, error: null };
|
|
314
|
-
mockSupabaseClient.auth.signUp.mockResolvedValue(mockResult);
|
|
315
|
-
|
|
316
|
-
render(
|
|
317
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
318
|
-
<TestComponent />
|
|
319
|
-
</AuthProvider>
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
screen.getByText('Sign Up').click();
|
|
323
|
-
|
|
324
|
-
expect(mockSupabaseClient.auth.signUp).toHaveBeenCalledWith({
|
|
325
|
-
email: 'test@example.com',
|
|
326
|
-
password: 'password'
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
it('handles sign out', async () => {
|
|
331
|
-
const mockResult = { error: null };
|
|
332
|
-
mockSupabaseClient.auth.signOut.mockResolvedValue(mockResult);
|
|
333
|
-
|
|
334
|
-
render(
|
|
335
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
336
|
-
<TestComponent />
|
|
337
|
-
</AuthProvider>
|
|
338
|
-
);
|
|
339
|
-
|
|
340
|
-
screen.getByText('Sign Out').click();
|
|
341
|
-
|
|
342
|
-
expect(mockSupabaseClient.auth.signOut).toHaveBeenCalled();
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
it('handles password reset', async () => {
|
|
346
|
-
const mockResult = { error: null };
|
|
347
|
-
mockSupabaseClient.auth.resetPasswordForEmail.mockResolvedValue(mockResult);
|
|
348
|
-
|
|
349
|
-
render(
|
|
350
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
351
|
-
<TestComponent />
|
|
352
|
-
</AuthProvider>
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
screen.getByText('Reset Password').click();
|
|
356
|
-
|
|
357
|
-
expect(mockSupabaseClient.auth.resetPasswordForEmail).toHaveBeenCalledWith('test@example.com');
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
it('handles password update', async () => {
|
|
361
|
-
const mockResult = { error: null };
|
|
362
|
-
mockSupabaseClient.auth.updateUser.mockResolvedValue(mockResult);
|
|
363
|
-
|
|
364
|
-
render(
|
|
365
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
366
|
-
<TestComponent />
|
|
367
|
-
</AuthProvider>
|
|
368
|
-
);
|
|
369
|
-
|
|
370
|
-
screen.getByText('Update Password').click();
|
|
371
|
-
|
|
372
|
-
expect(mockSupabaseClient.auth.updateUser).toHaveBeenCalledWith({
|
|
373
|
-
password: 'newpassword'
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it('handles session refresh', async () => {
|
|
378
|
-
const mockResult = { error: null };
|
|
379
|
-
mockSupabaseClient.auth.refreshSession.mockResolvedValue(mockResult);
|
|
380
|
-
|
|
381
|
-
render(
|
|
382
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
383
|
-
<TestComponent />
|
|
384
|
-
</AuthProvider>
|
|
385
|
-
);
|
|
386
|
-
|
|
387
|
-
screen.getByText('Refresh Session').click();
|
|
388
|
-
|
|
389
|
-
expect(mockSupabaseClient.auth.refreshSession).toHaveBeenCalled();
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
describe('Error Handling', () => {
|
|
394
|
-
it('handles sign in errors', async () => {
|
|
395
|
-
const mockError = new Error('Invalid credentials');
|
|
396
|
-
mockSupabaseClient.auth.signInWithPassword = vi.fn().mockResolvedValue({
|
|
397
|
-
data: null,
|
|
398
|
-
error: mockError
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
const TestErrorComponent = () => {
|
|
402
|
-
const { signIn } = useAuth();
|
|
403
|
-
const [error, setError] = React.useState<any>(null);
|
|
404
|
-
|
|
405
|
-
const handleSignIn = async () => {
|
|
406
|
-
const result = await signIn('test@example.com', 'password');
|
|
407
|
-
setError(result.error);
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
return (
|
|
411
|
-
<div>
|
|
412
|
-
<button onClick={handleSignIn}>Sign In</button>
|
|
413
|
-
<div data-testid="error">{error?.message || 'No error'}</div>
|
|
414
|
-
</div>
|
|
415
|
-
);
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
render(
|
|
419
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
420
|
-
<TestErrorComponent />
|
|
421
|
-
</AuthProvider>
|
|
422
|
-
);
|
|
423
|
-
|
|
424
|
-
screen.getByText('Sign In').click();
|
|
425
|
-
|
|
426
|
-
await waitFor(() => {
|
|
427
|
-
expect(screen.getByTestId('error')).toHaveTextContent('Invalid credentials');
|
|
428
|
-
});
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
it('handles missing supabase client', async () => {
|
|
432
|
-
const TestErrorComponent = () => {
|
|
433
|
-
const { signIn } = useAuth();
|
|
434
|
-
const [error, setError] = React.useState<any>(null);
|
|
435
|
-
|
|
436
|
-
const handleSignIn = async () => {
|
|
437
|
-
const result = await signIn('test@example.com', 'password');
|
|
438
|
-
setError(result.error);
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
return (
|
|
442
|
-
<div>
|
|
443
|
-
<button onClick={handleSignIn}>Sign In</button>
|
|
444
|
-
<div data-testid="error">{error?.message || 'No error'}</div>
|
|
445
|
-
</div>
|
|
446
|
-
);
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
render(
|
|
450
|
-
<AuthProvider>
|
|
451
|
-
<TestErrorComponent />
|
|
452
|
-
</AuthProvider>
|
|
453
|
-
);
|
|
454
|
-
|
|
455
|
-
screen.getByText('Sign In').click();
|
|
456
|
-
|
|
457
|
-
await waitFor(() => {
|
|
458
|
-
expect(screen.getByTestId('error')).toHaveTextContent('Supabase client not available');
|
|
459
|
-
});
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
it('handles auth state change errors gracefully', async () => {
|
|
463
|
-
// Mock the auth state change callback
|
|
464
|
-
let authStateCallback: any;
|
|
465
|
-
mockSupabaseClient.auth.onAuthStateChange.mockImplementation((callback: any) => {
|
|
466
|
-
authStateCallback = callback;
|
|
467
|
-
return { data: { subscription: { unsubscribe: vi.fn() } } };
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
render(
|
|
471
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
472
|
-
<TestComponent />
|
|
473
|
-
</AuthProvider>
|
|
474
|
-
);
|
|
475
|
-
|
|
476
|
-
// Simulate an error in auth state change
|
|
477
|
-
await act(async () => {
|
|
478
|
-
authStateCallback('ERROR', null);
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
// Should not crash and should set loading to false
|
|
482
|
-
await waitFor(() => {
|
|
483
|
-
expect(screen.getByTestId('authLoading')).toHaveTextContent('false');
|
|
484
|
-
});
|
|
485
|
-
});
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
describe('useAuth Hook', () => {
|
|
489
|
-
it('throws error when used outside provider', () => {
|
|
490
|
-
// Suppress console.error for this test
|
|
491
|
-
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
492
|
-
|
|
493
|
-
expect(() => {
|
|
494
|
-
render(<TestComponent />);
|
|
495
|
-
}).toThrow('useAuth must be used within an AuthProvider');
|
|
496
|
-
|
|
497
|
-
consoleSpy.mockRestore();
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
it('provides all required context values', () => {
|
|
501
|
-
const TestContextComponent = () => {
|
|
502
|
-
const auth = useAuth();
|
|
503
|
-
|
|
504
|
-
return (
|
|
505
|
-
<div>
|
|
506
|
-
<div data-testid="hasUser">{auth.user !== undefined ? 'true' : 'false'}</div>
|
|
507
|
-
<div data-testid="hasSession">{auth.session !== undefined ? 'true' : 'false'}</div>
|
|
508
|
-
<div data-testid="hasSupabase">{auth.supabase !== undefined ? 'true' : 'false'}</div>
|
|
509
|
-
<div data-testid="hasSignIn">{typeof auth.signIn === 'function' ? 'true' : 'false'}</div>
|
|
510
|
-
<div data-testid="hasSignUp">{typeof auth.signUp === 'function' ? 'true' : 'false'}</div>
|
|
511
|
-
<div data-testid="hasSignOut">{typeof auth.signOut === 'function' ? 'true' : 'false'}</div>
|
|
512
|
-
<div data-testid="hasResetPassword">{typeof auth.resetPassword === 'function' ? 'true' : 'false'}</div>
|
|
513
|
-
<div data-testid="hasUpdatePassword">{typeof auth.updatePassword === 'function' ? 'true' : 'false'}</div>
|
|
514
|
-
<div data-testid="hasRefreshSession">{typeof auth.refreshSession === 'function' ? 'true' : 'false'}</div>
|
|
515
|
-
</div>
|
|
516
|
-
);
|
|
517
|
-
};
|
|
518
|
-
|
|
519
|
-
render(
|
|
520
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
521
|
-
<TestContextComponent />
|
|
522
|
-
</AuthProvider>
|
|
523
|
-
);
|
|
524
|
-
|
|
525
|
-
expect(screen.getByTestId('hasUser')).toHaveTextContent('true');
|
|
526
|
-
expect(screen.getByTestId('hasSession')).toHaveTextContent('true');
|
|
527
|
-
expect(screen.getByTestId('hasSupabase')).toHaveTextContent('true');
|
|
528
|
-
expect(screen.getByTestId('hasSignIn')).toHaveTextContent('true');
|
|
529
|
-
expect(screen.getByTestId('hasSignUp')).toHaveTextContent('true');
|
|
530
|
-
expect(screen.getByTestId('hasSignOut')).toHaveTextContent('true');
|
|
531
|
-
expect(screen.getByTestId('hasResetPassword')).toHaveTextContent('true');
|
|
532
|
-
expect(screen.getByTestId('hasUpdatePassword')).toHaveTextContent('true');
|
|
533
|
-
expect(screen.getByTestId('hasRefreshSession')).toHaveTextContent('true');
|
|
534
|
-
});
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
describe('Cleanup', () => {
|
|
538
|
-
it('unsubscribes from auth state changes on unmount', () => {
|
|
539
|
-
const unsubscribe = vi.fn();
|
|
540
|
-
mockSupabaseClient.auth.onAuthStateChange.mockReturnValue({
|
|
541
|
-
data: { subscription: { unsubscribe } }
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
const { unmount } = render(
|
|
545
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
546
|
-
<div>Test</div>
|
|
547
|
-
</AuthProvider>
|
|
548
|
-
);
|
|
549
|
-
|
|
550
|
-
unmount();
|
|
551
|
-
expect(unsubscribe).toHaveBeenCalledTimes(1);
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
it('handles component unmount during async operations', async () => {
|
|
555
|
-
let resolvePromise: any;
|
|
556
|
-
const promise = new Promise(resolve => {
|
|
557
|
-
resolvePromise = resolve;
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
mockSupabaseClient.auth.getSession.mockReturnValue(promise);
|
|
561
|
-
|
|
562
|
-
const { unmount } = render(
|
|
563
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
564
|
-
<TestComponent />
|
|
565
|
-
</AuthProvider>
|
|
566
|
-
);
|
|
567
|
-
|
|
568
|
-
// Unmount before async operation completes
|
|
569
|
-
unmount();
|
|
570
|
-
|
|
571
|
-
// Resolve the promise after unmount
|
|
572
|
-
resolvePromise({ data: { session: null }, error: null });
|
|
573
|
-
|
|
574
|
-
// Should not cause any errors
|
|
575
|
-
expect(true).toBe(true);
|
|
576
|
-
});
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
describe('Error Suppression', () => {
|
|
580
|
-
it('sets up error suppression handlers', () => {
|
|
581
|
-
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
582
|
-
|
|
583
|
-
render(
|
|
584
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
585
|
-
<div>Test</div>
|
|
586
|
-
</AuthProvider>
|
|
587
|
-
);
|
|
588
|
-
|
|
589
|
-
// Test that the component renders without errors
|
|
590
|
-
expect(screen.getByText('Test')).toBeInTheDocument();
|
|
591
|
-
|
|
592
|
-
consoleWarnSpy.mockRestore();
|
|
593
|
-
});
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
describe('Context Value Stability', () => {
|
|
597
|
-
it('provides stable function references', () => {
|
|
598
|
-
const TestStabilityComponent = () => {
|
|
599
|
-
const auth = useAuth();
|
|
600
|
-
|
|
601
|
-
return (
|
|
602
|
-
<div>
|
|
603
|
-
<div data-testid="hasStableSignIn">{typeof auth.signIn === 'function' ? 'true' : 'false'}</div>
|
|
604
|
-
</div>
|
|
605
|
-
);
|
|
606
|
-
};
|
|
607
|
-
|
|
608
|
-
render(
|
|
609
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
610
|
-
<TestStabilityComponent />
|
|
611
|
-
</AuthProvider>
|
|
612
|
-
);
|
|
613
|
-
|
|
614
|
-
expect(screen.getByTestId('hasStableSignIn')).toHaveTextContent('true');
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
it('provides error alias for backward compatibility', () => {
|
|
618
|
-
const TestErrorAliasComponent = () => {
|
|
619
|
-
const auth = useAuth();
|
|
620
|
-
return (
|
|
621
|
-
<div>
|
|
622
|
-
<div data-testid="hasErrorAlias">{auth.error !== undefined ? 'true' : 'false'}</div>
|
|
623
|
-
<div data-testid="errorAliasMatch">{auth.error === auth.authError ? 'true' : 'false'}</div>
|
|
624
|
-
</div>
|
|
625
|
-
);
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
render(
|
|
629
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
630
|
-
<TestErrorAliasComponent />
|
|
631
|
-
</AuthProvider>
|
|
632
|
-
);
|
|
633
|
-
|
|
634
|
-
expect(screen.getByTestId('hasErrorAlias')).toHaveTextContent('true');
|
|
635
|
-
expect(screen.getByTestId('errorAliasMatch')).toHaveTextContent('true');
|
|
636
|
-
});
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
describe('Authentication State Edge Cases', () => {
|
|
640
|
-
it('handles user without session correctly', async () => {
|
|
641
|
-
const mockUser = testDataGenerators.user({ email: 'test@example.com' });
|
|
642
|
-
|
|
643
|
-
mockSupabaseClient.auth.getSession.mockResolvedValue({
|
|
644
|
-
data: { session: null },
|
|
645
|
-
error: null
|
|
646
|
-
});
|
|
647
|
-
mockSupabaseClient.auth.getUser.mockResolvedValue({
|
|
648
|
-
data: { user: mockUser },
|
|
649
|
-
error: null
|
|
650
|
-
});
|
|
651
|
-
|
|
652
|
-
render(
|
|
653
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
654
|
-
<TestComponent />
|
|
655
|
-
</AuthProvider>
|
|
656
|
-
);
|
|
657
|
-
|
|
658
|
-
await waitFor(() => {
|
|
659
|
-
expect(screen.getByTestId('user')).toHaveTextContent('test@example.com');
|
|
660
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('false');
|
|
661
|
-
expect(screen.getByTestId('authLoading')).toHaveTextContent('false');
|
|
662
|
-
});
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
it('handles session without user correctly', async () => {
|
|
666
|
-
const mockSession = testDataGenerators.session({
|
|
667
|
-
user: null as any
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
mockSupabaseClient.auth.getSession.mockResolvedValue({
|
|
671
|
-
data: { session: mockSession },
|
|
672
|
-
error: null
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
render(
|
|
676
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
677
|
-
<TestComponent />
|
|
678
|
-
</AuthProvider>
|
|
679
|
-
);
|
|
680
|
-
|
|
681
|
-
await waitFor(() => {
|
|
682
|
-
expect(screen.getByTestId('user')).toHaveTextContent('No user');
|
|
683
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('false');
|
|
684
|
-
expect(screen.getByTestId('authLoading')).toHaveTextContent('false');
|
|
685
|
-
});
|
|
686
|
-
});
|
|
687
|
-
|
|
688
|
-
it('handles TOKEN_REFRESHED event', async () => {
|
|
689
|
-
const mockSession = testDataGenerators.session({
|
|
690
|
-
user: testDataGenerators.user({ email: 'test@example.com' })
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
// Mock the auth state change callback
|
|
694
|
-
let authStateCallback: any;
|
|
695
|
-
mockSupabaseClient.auth.onAuthStateChange.mockImplementation((callback: any) => {
|
|
696
|
-
authStateCallback = callback;
|
|
697
|
-
return { data: { subscription: { unsubscribe: vi.fn() } } };
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
render(
|
|
701
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
702
|
-
<TestComponent />
|
|
703
|
-
</AuthProvider>
|
|
704
|
-
);
|
|
705
|
-
|
|
706
|
-
// Simulate TOKEN_REFRESHED event
|
|
707
|
-
await act(async () => {
|
|
708
|
-
authStateCallback('TOKEN_REFRESHED', mockSession);
|
|
709
|
-
});
|
|
710
|
-
|
|
711
|
-
await waitFor(() => {
|
|
712
|
-
expect(screen.getByTestId('user')).toHaveTextContent('test@example.com');
|
|
713
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('true');
|
|
714
|
-
});
|
|
715
|
-
});
|
|
716
|
-
|
|
717
|
-
it('handles INITIAL_SESSION event', async () => {
|
|
718
|
-
const mockSession = testDataGenerators.session({
|
|
719
|
-
user: testDataGenerators.user({ email: 'test@example.com' })
|
|
720
|
-
});
|
|
721
|
-
|
|
722
|
-
// Mock the auth state change callback
|
|
723
|
-
let authStateCallback: any;
|
|
724
|
-
mockSupabaseClient.auth.onAuthStateChange.mockImplementation((callback: any) => {
|
|
725
|
-
authStateCallback = callback;
|
|
726
|
-
return { data: { subscription: { unsubscribe: vi.fn() } } };
|
|
727
|
-
});
|
|
728
|
-
|
|
729
|
-
render(
|
|
730
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
731
|
-
<TestComponent />
|
|
732
|
-
</AuthProvider>
|
|
733
|
-
);
|
|
734
|
-
|
|
735
|
-
// Simulate INITIAL_SESSION event
|
|
736
|
-
await act(async () => {
|
|
737
|
-
authStateCallback('INITIAL_SESSION', mockSession);
|
|
738
|
-
});
|
|
739
|
-
|
|
740
|
-
await waitFor(() => {
|
|
741
|
-
expect(screen.getByTestId('user')).toHaveTextContent('test@example.com');
|
|
742
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('true');
|
|
743
|
-
});
|
|
744
|
-
});
|
|
745
|
-
|
|
746
|
-
it('handles auth state change with null session', async () => {
|
|
747
|
-
// Mock the auth state change callback
|
|
748
|
-
let authStateCallback: any;
|
|
749
|
-
mockSupabaseClient.auth.onAuthStateChange.mockImplementation((callback: any) => {
|
|
750
|
-
authStateCallback = callback;
|
|
751
|
-
return { data: { subscription: { unsubscribe: vi.fn() } } };
|
|
752
|
-
});
|
|
753
|
-
|
|
754
|
-
render(
|
|
755
|
-
<AuthProvider supabaseClient={mockSupabaseClient}>
|
|
756
|
-
<TestComponent />
|
|
757
|
-
</AuthProvider>
|
|
758
|
-
);
|
|
759
|
-
|
|
760
|
-
// Simulate SIGNED_IN event with null session
|
|
761
|
-
await act(async () => {
|
|
762
|
-
authStateCallback('SIGNED_IN', null);
|
|
763
|
-
});
|
|
764
|
-
|
|
765
|
-
await waitFor(() => {
|
|
766
|
-
expect(screen.getByTestId('user')).toHaveTextContent('No user');
|
|
767
|
-
expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('false');
|
|
768
|
-
});
|
|
769
|
-
});
|
|
770
|
-
});
|
|
771
|
-
});
|