@jmruthers/pace-core 0.5.110 → 0.5.111
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AuthService-DrHrvXNZ.d.ts → AuthService-CVgsgtaZ.d.ts} +8 -0
- package/dist/{DataTable-D3BK2FCN.js → DataTable-5W2HVLLV.js} +8 -8
- package/dist/{UnifiedAuthProvider-A7I23UCN.js → UnifiedAuthProvider-LUM3QLS5.js} +3 -3
- package/dist/{api-PIE4JRFS.js → api-SIZPFBFX.js} +5 -3
- package/dist/{audit-65VNHEV2.js → audit-5JI5T3SL.js} +2 -2
- package/dist/{chunk-3J5N2T2N.js → chunk-2BIDKXQU.js} +113 -116
- package/dist/chunk-2BIDKXQU.js.map +1 -0
- package/dist/{chunk-AWK2FAUN.js → chunk-ACYQNYHB.js} +7 -7
- package/dist/{chunk-D6MEKC27.js → chunk-EFVQBYFN.js} +2 -2
- package/dist/{chunk-EZ64QG2I.js → chunk-I5YM5BGS.js} +2 -2
- package/dist/{chunk-Q7APDV6H.js → chunk-IWJYNWXN.js} +13 -5
- package/dist/chunk-IWJYNWXN.js.map +1 -0
- package/dist/{chunk-YFMENCR4.js → chunk-JE2GFA3O.js} +3 -3
- package/dist/{chunk-AUXS7XSO.js → chunk-MW73E7SP.js} +35 -11
- package/dist/chunk-MW73E7SP.js.map +1 -0
- package/dist/{chunk-XRSP3H52.js → chunk-PXXS26G5.js} +57 -23
- package/dist/chunk-PXXS26G5.js.map +1 -0
- package/dist/{chunk-HGZSO43Y.js → chunk-TD4BXGPE.js} +4 -4
- package/dist/{chunk-EYSXQ756.js → chunk-TDFBX7KJ.js} +2 -2
- package/dist/{chunk-HADXAZT3.js → chunk-UGVU7L7N.js} +52 -90
- package/dist/chunk-UGVU7L7N.js.map +1 -0
- package/dist/{chunk-2W4WKJVF.js → chunk-X7SPKHYZ.js} +290 -255
- package/dist/chunk-X7SPKHYZ.js.map +1 -0
- package/dist/{chunk-7GBEBJLR.js → chunk-ZL45MG76.js} +45 -37
- package/dist/chunk-ZL45MG76.js.map +1 -0
- package/dist/components.js +10 -10
- package/dist/hooks.d.ts +11 -1
- package/dist/hooks.js +9 -7
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +13 -13
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +13 -8
- package/dist/rbac/index.js +9 -9
- package/dist/utils.js +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +4 -4
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +8 -8
- package/docs/api/classes/RBACCache.md +8 -8
- package/docs/api/classes/RBACEngine.md +4 -4
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- 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 +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/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 +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- 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/RBACLogger.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
- package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
- package/docs/api/interfaces/RouteAccessRecord.md +10 -10
- package/docs/api/interfaces/RouteConfig.md +19 -6
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +36 -36
- package/docs/api-reference/hooks.md +8 -4
- package/docs/architecture/rpc-function-standards.md +3 -1
- package/docs/best-practices/common-patterns.md +3 -3
- package/docs/best-practices/deployment.md +10 -4
- package/docs/best-practices/performance.md +11 -3
- package/docs/core-concepts/organisations.md +8 -8
- package/docs/core-concepts/permissions.md +133 -72
- package/docs/migration/rbac-migration.md +65 -66
- package/docs/rbac/advanced-patterns.md +15 -22
- package/docs/rbac/examples.md +12 -12
- package/docs/rbac/getting-started.md +3 -3
- package/docs/rbac/troubleshooting.md +2 -1
- package/package.json +1 -1
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +913 -0
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +609 -0
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +434 -0
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +120 -0
- package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +519 -0
- package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +316 -0
- package/src/components/DataTable/examples/__tests__/InitialPageSizeExample.test.tsx +211 -0
- package/src/components/FileUpload/FileUpload.tsx +2 -8
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +193 -63
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +102 -135
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +41 -2
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +61 -6
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +71 -21
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +113 -41
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +155 -45
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +30 -1
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +63 -5
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +156 -72
- package/src/hooks/__tests__/useRBAC.unit.test.ts +4 -38
- package/src/hooks/index.ts +1 -1
- package/src/hooks/useFileDisplay.ts +51 -0
- package/src/hooks/usePermissionCache.test.ts +112 -68
- package/src/hooks/usePermissionCache.ts +55 -15
- package/src/rbac/README.md +81 -39
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +3 -3
- package/src/rbac/__tests__/engine.comprehensive.test.ts +15 -6
- package/src/rbac/__tests__/rbac-core.test.tsx +1 -1
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +57 -4
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +3 -2
- package/src/rbac/adapters.tsx +4 -4
- package/src/rbac/api.test.ts +37 -13
- package/src/rbac/api.ts +25 -8
- package/src/rbac/audit.test.ts +2 -2
- package/src/rbac/audit.ts +14 -5
- package/src/rbac/cache.test.ts +12 -0
- package/src/rbac/cache.ts +29 -9
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +14 -14
- package/src/rbac/components/NavigationProvider.test.tsx +1 -1
- package/src/rbac/components/PagePermissionGuard.tsx +4 -3
- package/src/rbac/components/PagePermissionProvider.test.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +19 -15
- package/src/rbac/components/RoleBasedRouter.tsx +16 -9
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +123 -107
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +1 -1
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +121 -103
- package/src/rbac/docs/event-based-apps.md +6 -6
- package/src/rbac/engine.ts +12 -2
- package/src/rbac/hooks/useCan.test.ts +29 -2
- package/src/rbac/hooks/usePermissions.test.ts +25 -25
- package/src/rbac/hooks/usePermissions.ts +47 -23
- package/src/rbac/hooks/useRBAC.simple.test.ts +1 -8
- package/src/rbac/hooks/useRBAC.test.ts +3 -40
- package/src/rbac/hooks/useRBAC.ts +0 -55
- package/src/rbac/hooks/useResolvedScope.ts +23 -31
- package/src/rbac/permissions.test.ts +11 -7
- package/src/rbac/security.test.ts +2 -2
- package/src/rbac/security.ts +22 -7
- package/src/rbac/types.test.ts +2 -2
- package/src/rbac/types.ts +1 -2
- package/src/services/EventService.ts +41 -13
- package/src/services/__tests__/EventService.test.ts +25 -4
- package/src/services/interfaces/IEventService.ts +1 -0
- package/src/utils/file-reference.ts +9 -0
- package/dist/chunk-2W4WKJVF.js.map +0 -1
- package/dist/chunk-3J5N2T2N.js.map +0 -1
- package/dist/chunk-7GBEBJLR.js.map +0 -1
- package/dist/chunk-AUXS7XSO.js.map +0 -1
- package/dist/chunk-HADXAZT3.js.map +0 -1
- package/dist/chunk-Q7APDV6H.js.map +0 -1
- package/dist/chunk-XRSP3H52.js.map +0 -1
- /package/dist/{DataTable-D3BK2FCN.js.map → DataTable-5W2HVLLV.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-A7I23UCN.js.map → UnifiedAuthProvider-LUM3QLS5.js.map} +0 -0
- /package/dist/{api-PIE4JRFS.js.map → api-SIZPFBFX.js.map} +0 -0
- /package/dist/{audit-65VNHEV2.js.map → audit-5JI5T3SL.js.map} +0 -0
- /package/dist/{chunk-AWK2FAUN.js.map → chunk-ACYQNYHB.js.map} +0 -0
- /package/dist/{chunk-D6MEKC27.js.map → chunk-EFVQBYFN.js.map} +0 -0
- /package/dist/{chunk-EZ64QG2I.js.map → chunk-I5YM5BGS.js.map} +0 -0
- /package/dist/{chunk-YFMENCR4.js.map → chunk-JE2GFA3O.js.map} +0 -0
- /package/dist/{chunk-HGZSO43Y.js.map → chunk-TD4BXGPE.js.map} +0 -0
- /package/dist/{chunk-EYSXQ756.js.map → chunk-TDFBX7KJ.js.map} +0 -0
|
@@ -11,12 +11,12 @@ import { render, screen, waitFor } from '@testing-library/react';
|
|
|
11
11
|
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
12
12
|
import { ReactNode } from 'react';
|
|
13
13
|
import { NavigationGuard } from '../NavigationGuard';
|
|
14
|
-
import {
|
|
14
|
+
import { useMultiplePermissions } from '../../hooks/usePermissions';
|
|
15
15
|
import { useUnifiedAuth } from '../../../providers/UnifiedAuthProvider';
|
|
16
16
|
|
|
17
17
|
// Mock the RBAC hooks
|
|
18
|
-
vi.mock('../../hooks', () => ({
|
|
19
|
-
|
|
18
|
+
vi.mock('../../hooks/usePermissions', () => ({
|
|
19
|
+
useMultiplePermissions: vi.fn()
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
22
|
// Mock the auth provider
|
|
@@ -68,7 +68,7 @@ const TestLoading = () => (
|
|
|
68
68
|
);
|
|
69
69
|
|
|
70
70
|
describe('NavigationGuard Component', () => {
|
|
71
|
-
const
|
|
71
|
+
const mockUseMultiplePermissions = vi.mocked(useMultiplePermissions);
|
|
72
72
|
const mockUseUnifiedAuth = vi.mocked(useUnifiedAuth);
|
|
73
73
|
const mockCreateScopeFromEvent = vi.mocked(createScopeFromEvent);
|
|
74
74
|
|
|
@@ -83,19 +83,21 @@ describe('NavigationGuard Component', () => {
|
|
|
83
83
|
supabase: {} as any
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
87
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
88
88
|
isLoading: false,
|
|
89
|
-
error: null
|
|
89
|
+
error: null,
|
|
90
|
+
refetch: vi.fn()
|
|
90
91
|
});
|
|
91
92
|
});
|
|
92
93
|
|
|
93
94
|
describe('Rendering', () => {
|
|
94
95
|
it('renders children when permission is granted', async () => {
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
97
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
97
98
|
isLoading: false,
|
|
98
|
-
error: null
|
|
99
|
+
error: null,
|
|
100
|
+
refetch: vi.fn()
|
|
99
101
|
});
|
|
100
102
|
|
|
101
103
|
render(
|
|
@@ -111,10 +113,11 @@ describe('NavigationGuard Component', () => {
|
|
|
111
113
|
});
|
|
112
114
|
|
|
113
115
|
it('renders fallback when permission is denied', async () => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
117
|
+
results: { 'read:dashboard': false } as Record<string, boolean>,
|
|
116
118
|
isLoading: false,
|
|
117
|
-
error: null
|
|
119
|
+
error: null,
|
|
120
|
+
refetch: vi.fn()
|
|
118
121
|
});
|
|
119
122
|
|
|
120
123
|
render(
|
|
@@ -133,10 +136,11 @@ describe('NavigationGuard Component', () => {
|
|
|
133
136
|
});
|
|
134
137
|
|
|
135
138
|
it('shows loading state during permission check', () => {
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
140
|
+
results: {} as Record<string, boolean>,
|
|
138
141
|
isLoading: true,
|
|
139
|
-
error: null
|
|
142
|
+
error: null,
|
|
143
|
+
refetch: vi.fn()
|
|
140
144
|
});
|
|
141
145
|
|
|
142
146
|
render(
|
|
@@ -153,10 +157,11 @@ describe('NavigationGuard Component', () => {
|
|
|
153
157
|
});
|
|
154
158
|
|
|
155
159
|
it('uses default fallback when none provided', async () => {
|
|
156
|
-
|
|
157
|
-
|
|
160
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
161
|
+
results: { 'read:dashboard': false } as Record<string, boolean>,
|
|
158
162
|
isLoading: false,
|
|
159
|
-
error: null
|
|
163
|
+
error: null,
|
|
164
|
+
refetch: vi.fn()
|
|
160
165
|
});
|
|
161
166
|
|
|
162
167
|
render(
|
|
@@ -171,10 +176,11 @@ describe('NavigationGuard Component', () => {
|
|
|
171
176
|
});
|
|
172
177
|
|
|
173
178
|
it('uses default loading when none provided', () => {
|
|
174
|
-
|
|
175
|
-
|
|
179
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
180
|
+
results: {} as Record<string, boolean>,
|
|
176
181
|
isLoading: true,
|
|
177
|
-
error: null
|
|
182
|
+
error: null,
|
|
183
|
+
refetch: vi.fn()
|
|
178
184
|
});
|
|
179
185
|
|
|
180
186
|
render(
|
|
@@ -189,10 +195,11 @@ describe('NavigationGuard Component', () => {
|
|
|
189
195
|
|
|
190
196
|
describe('Permission Checking', () => {
|
|
191
197
|
it('enforces navigation permissions correctly', async () => {
|
|
192
|
-
|
|
193
|
-
|
|
198
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
199
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
194
200
|
isLoading: false,
|
|
195
|
-
error: null
|
|
201
|
+
error: null,
|
|
202
|
+
refetch: vi.fn()
|
|
196
203
|
});
|
|
197
204
|
|
|
198
205
|
render(
|
|
@@ -205,14 +212,13 @@ describe('NavigationGuard Component', () => {
|
|
|
205
212
|
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
206
213
|
}, { interval: 10 });
|
|
207
214
|
|
|
208
|
-
expect(
|
|
215
|
+
expect(mockUseMultiplePermissions).toHaveBeenCalledWith(
|
|
209
216
|
'user-123',
|
|
210
217
|
expect.objectContaining({
|
|
211
218
|
organisationId: 'org-123',
|
|
212
219
|
eventId: 'event-123'
|
|
213
220
|
}),
|
|
214
|
-
'read:dashboard',
|
|
215
|
-
'dashboard',
|
|
221
|
+
['read:dashboard'],
|
|
216
222
|
true
|
|
217
223
|
);
|
|
218
224
|
});
|
|
@@ -223,10 +229,11 @@ describe('NavigationGuard Component', () => {
|
|
|
223
229
|
permissions: ['read:dashboard', 'write:dashboard'] as const
|
|
224
230
|
};
|
|
225
231
|
|
|
226
|
-
|
|
227
|
-
|
|
232
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
233
|
+
results: { 'read:dashboard': true, 'write:dashboard': true } as Record<string, boolean>,
|
|
228
234
|
isLoading: false,
|
|
229
|
-
error: null
|
|
235
|
+
error: null,
|
|
236
|
+
refetch: vi.fn()
|
|
230
237
|
});
|
|
231
238
|
|
|
232
239
|
render(
|
|
@@ -239,15 +246,14 @@ describe('NavigationGuard Component', () => {
|
|
|
239
246
|
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
240
247
|
}, { interval: 10 });
|
|
241
248
|
|
|
242
|
-
// Should check
|
|
243
|
-
expect(
|
|
249
|
+
// Should check all permissions when multiple are provided
|
|
250
|
+
expect(mockUseMultiplePermissions).toHaveBeenCalledWith(
|
|
244
251
|
'user-123',
|
|
245
252
|
expect.objectContaining({
|
|
246
253
|
organisationId: 'org-123',
|
|
247
254
|
eventId: 'event-123'
|
|
248
255
|
}),
|
|
249
|
-
'read:dashboard',
|
|
250
|
-
'dashboard',
|
|
256
|
+
['read:dashboard', 'write:dashboard'],
|
|
251
257
|
true
|
|
252
258
|
);
|
|
253
259
|
});
|
|
@@ -258,10 +264,11 @@ describe('NavigationGuard Component', () => {
|
|
|
258
264
|
permissions: [] as const
|
|
259
265
|
};
|
|
260
266
|
|
|
261
|
-
|
|
262
|
-
|
|
267
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
268
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
263
269
|
isLoading: false,
|
|
264
|
-
error: null
|
|
270
|
+
error: null,
|
|
271
|
+
refetch: vi.fn()
|
|
265
272
|
});
|
|
266
273
|
|
|
267
274
|
render(
|
|
@@ -277,10 +284,11 @@ describe('NavigationGuard Component', () => {
|
|
|
277
284
|
|
|
278
285
|
it('handles permission checking errors gracefully', async () => {
|
|
279
286
|
const error = new Error('Permission check failed');
|
|
280
|
-
|
|
281
|
-
|
|
287
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
288
|
+
results: { 'read:dashboard': false } as Record<string, boolean>,
|
|
282
289
|
isLoading: false,
|
|
283
|
-
error
|
|
290
|
+
error,
|
|
291
|
+
refetch: vi.fn()
|
|
284
292
|
});
|
|
285
293
|
|
|
286
294
|
render(
|
|
@@ -306,10 +314,11 @@ describe('NavigationGuard Component', () => {
|
|
|
306
314
|
appId: 'custom-app'
|
|
307
315
|
};
|
|
308
316
|
|
|
309
|
-
|
|
310
|
-
|
|
317
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
318
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
311
319
|
isLoading: false,
|
|
312
|
-
error: null
|
|
320
|
+
error: null,
|
|
321
|
+
refetch: vi.fn()
|
|
313
322
|
});
|
|
314
323
|
|
|
315
324
|
render(
|
|
@@ -325,20 +334,20 @@ describe('NavigationGuard Component', () => {
|
|
|
325
334
|
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
326
335
|
}, { interval: 10 });
|
|
327
336
|
|
|
328
|
-
expect(
|
|
337
|
+
expect(mockUseMultiplePermissions).toHaveBeenCalledWith(
|
|
329
338
|
'user-123',
|
|
330
339
|
customScope,
|
|
331
|
-
'read:dashboard',
|
|
332
|
-
'dashboard',
|
|
340
|
+
['read:dashboard'],
|
|
333
341
|
true
|
|
334
342
|
);
|
|
335
343
|
});
|
|
336
344
|
|
|
337
345
|
it('resolves scope from organisation and event context', async () => {
|
|
338
|
-
|
|
339
|
-
|
|
346
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
347
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
340
348
|
isLoading: false,
|
|
341
|
-
error: null
|
|
349
|
+
error: null,
|
|
350
|
+
refetch: vi.fn()
|
|
342
351
|
});
|
|
343
352
|
|
|
344
353
|
render(
|
|
@@ -351,14 +360,13 @@ describe('NavigationGuard Component', () => {
|
|
|
351
360
|
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
352
361
|
}, { interval: 10 });
|
|
353
362
|
|
|
354
|
-
expect(
|
|
363
|
+
expect(mockUseMultiplePermissions).toHaveBeenCalledWith(
|
|
355
364
|
'user-123',
|
|
356
365
|
expect.objectContaining({
|
|
357
366
|
organisationId: 'org-123',
|
|
358
367
|
eventId: 'event-123'
|
|
359
368
|
}),
|
|
360
|
-
'read:dashboard',
|
|
361
|
-
'dashboard',
|
|
369
|
+
['read:dashboard'],
|
|
362
370
|
true
|
|
363
371
|
);
|
|
364
372
|
});
|
|
@@ -371,10 +379,11 @@ describe('NavigationGuard Component', () => {
|
|
|
371
379
|
supabase: {} as any
|
|
372
380
|
});
|
|
373
381
|
|
|
374
|
-
|
|
375
|
-
|
|
382
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
383
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
376
384
|
isLoading: false,
|
|
377
|
-
error: null
|
|
385
|
+
error: null,
|
|
386
|
+
refetch: vi.fn()
|
|
378
387
|
});
|
|
379
388
|
|
|
380
389
|
render(
|
|
@@ -387,14 +396,13 @@ describe('NavigationGuard Component', () => {
|
|
|
387
396
|
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
388
397
|
}, { interval: 10 });
|
|
389
398
|
|
|
390
|
-
expect(
|
|
399
|
+
expect(mockUseMultiplePermissions).toHaveBeenCalledWith(
|
|
391
400
|
'user-123',
|
|
392
401
|
expect.objectContaining({
|
|
393
402
|
organisationId: 'org-123',
|
|
394
403
|
eventId: undefined
|
|
395
404
|
}),
|
|
396
|
-
'read:dashboard',
|
|
397
|
-
'dashboard',
|
|
405
|
+
['read:dashboard'],
|
|
398
406
|
true
|
|
399
407
|
);
|
|
400
408
|
});
|
|
@@ -413,10 +421,11 @@ describe('NavigationGuard Component', () => {
|
|
|
413
421
|
appId: 'resolved-app'
|
|
414
422
|
});
|
|
415
423
|
|
|
416
|
-
|
|
417
|
-
|
|
424
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
425
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
418
426
|
isLoading: false,
|
|
419
|
-
error: null
|
|
427
|
+
error: null,
|
|
428
|
+
refetch: vi.fn()
|
|
420
429
|
});
|
|
421
430
|
|
|
422
431
|
render(
|
|
@@ -430,14 +439,13 @@ describe('NavigationGuard Component', () => {
|
|
|
430
439
|
}, { interval: 10 });
|
|
431
440
|
|
|
432
441
|
expect(mockCreateScopeFromEvent).toHaveBeenCalledWith({}, 'event-123');
|
|
433
|
-
expect(
|
|
442
|
+
expect(mockUseMultiplePermissions).toHaveBeenCalledWith(
|
|
434
443
|
'user-123',
|
|
435
444
|
expect.objectContaining({
|
|
436
445
|
organisationId: 'resolved-org',
|
|
437
446
|
eventId: 'event-123'
|
|
438
447
|
}),
|
|
439
|
-
'read:dashboard',
|
|
440
|
-
'dashboard',
|
|
448
|
+
['read:dashboard'],
|
|
441
449
|
true
|
|
442
450
|
);
|
|
443
451
|
});
|
|
@@ -494,10 +502,11 @@ describe('NavigationGuard Component', () => {
|
|
|
494
502
|
it('prevents bypassing in strict mode', async () => {
|
|
495
503
|
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
496
504
|
|
|
497
|
-
|
|
498
|
-
|
|
505
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
506
|
+
results: { 'read:dashboard': false } as Record<string, boolean>,
|
|
499
507
|
isLoading: false,
|
|
500
|
-
error: null
|
|
508
|
+
error: null,
|
|
509
|
+
refetch: vi.fn()
|
|
501
510
|
});
|
|
502
511
|
|
|
503
512
|
render(
|
|
@@ -529,10 +538,11 @@ describe('NavigationGuard Component', () => {
|
|
|
529
538
|
it('logs navigation access attempts for audit', async () => {
|
|
530
539
|
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
531
540
|
|
|
532
|
-
|
|
533
|
-
|
|
541
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
542
|
+
results: { 'read:dashboard': false } as Record<string, boolean>,
|
|
534
543
|
isLoading: false,
|
|
535
|
-
error: null
|
|
544
|
+
error: null,
|
|
545
|
+
refetch: vi.fn()
|
|
536
546
|
});
|
|
537
547
|
|
|
538
548
|
render(
|
|
@@ -565,10 +575,11 @@ describe('NavigationGuard Component', () => {
|
|
|
565
575
|
it('calls onDenied callback when access is denied', async () => {
|
|
566
576
|
const onDeniedSpy = vi.fn();
|
|
567
577
|
|
|
568
|
-
|
|
569
|
-
|
|
578
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
579
|
+
results: { 'read:dashboard': false } as Record<string, boolean>,
|
|
570
580
|
isLoading: false,
|
|
571
|
-
error: null
|
|
581
|
+
error: null,
|
|
582
|
+
refetch: vi.fn()
|
|
572
583
|
});
|
|
573
584
|
|
|
574
585
|
render(
|
|
@@ -591,10 +602,11 @@ describe('NavigationGuard Component', () => {
|
|
|
591
602
|
it('does not call onDenied when access is granted', async () => {
|
|
592
603
|
const onDeniedSpy = vi.fn();
|
|
593
604
|
|
|
594
|
-
|
|
595
|
-
|
|
605
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
606
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
596
607
|
isLoading: false,
|
|
597
|
-
error: null
|
|
608
|
+
error: null,
|
|
609
|
+
refetch: vi.fn()
|
|
598
610
|
});
|
|
599
611
|
|
|
600
612
|
render(
|
|
@@ -618,10 +630,11 @@ describe('NavigationGuard Component', () => {
|
|
|
618
630
|
it('respects strictMode setting', async () => {
|
|
619
631
|
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
620
632
|
|
|
621
|
-
|
|
622
|
-
|
|
633
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
634
|
+
results: { 'read:dashboard': false } as Record<string, boolean>,
|
|
623
635
|
isLoading: false,
|
|
624
|
-
error: null
|
|
636
|
+
error: null,
|
|
637
|
+
refetch: vi.fn()
|
|
625
638
|
});
|
|
626
639
|
|
|
627
640
|
render(
|
|
@@ -648,10 +661,11 @@ describe('NavigationGuard Component', () => {
|
|
|
648
661
|
it('respects auditLog setting', async () => {
|
|
649
662
|
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
650
663
|
|
|
651
|
-
|
|
652
|
-
|
|
664
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
665
|
+
results: { 'read:dashboard': false } as Record<string, boolean>,
|
|
653
666
|
isLoading: false,
|
|
654
|
-
error: null
|
|
667
|
+
error: null,
|
|
668
|
+
refetch: vi.fn()
|
|
655
669
|
});
|
|
656
670
|
|
|
657
671
|
render(
|
|
@@ -676,10 +690,11 @@ describe('NavigationGuard Component', () => {
|
|
|
676
690
|
});
|
|
677
691
|
|
|
678
692
|
it('respects requireAll setting', async () => {
|
|
679
|
-
|
|
680
|
-
|
|
693
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
694
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
681
695
|
isLoading: false,
|
|
682
|
-
error: null
|
|
696
|
+
error: null,
|
|
697
|
+
refetch: vi.fn()
|
|
683
698
|
});
|
|
684
699
|
|
|
685
700
|
render(
|
|
@@ -696,14 +711,13 @@ describe('NavigationGuard Component', () => {
|
|
|
696
711
|
}, { interval: 10 });
|
|
697
712
|
|
|
698
713
|
// Should still check the first permission as representative
|
|
699
|
-
expect(
|
|
714
|
+
expect(mockUseMultiplePermissions).toHaveBeenCalledWith(
|
|
700
715
|
'user-123',
|
|
701
716
|
expect.objectContaining({
|
|
702
717
|
organisationId: 'org-123',
|
|
703
718
|
eventId: 'event-123'
|
|
704
719
|
}),
|
|
705
|
-
'read:dashboard',
|
|
706
|
-
'dashboard',
|
|
720
|
+
['read:dashboard'],
|
|
707
721
|
true
|
|
708
722
|
);
|
|
709
723
|
});
|
|
@@ -718,10 +732,11 @@ describe('NavigationGuard Component', () => {
|
|
|
718
732
|
supabase: {} as any
|
|
719
733
|
});
|
|
720
734
|
|
|
721
|
-
|
|
722
|
-
|
|
735
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
736
|
+
results: { 'read:dashboard': false } as Record<string, boolean>,
|
|
723
737
|
isLoading: false,
|
|
724
|
-
error: null
|
|
738
|
+
error: null,
|
|
739
|
+
refetch: vi.fn()
|
|
725
740
|
});
|
|
726
741
|
|
|
727
742
|
render(
|
|
@@ -737,24 +752,24 @@ describe('NavigationGuard Component', () => {
|
|
|
737
752
|
expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
|
|
738
753
|
}, { interval: 10 });
|
|
739
754
|
|
|
740
|
-
expect(
|
|
755
|
+
expect(mockUseMultiplePermissions).toHaveBeenCalledWith(
|
|
741
756
|
'',
|
|
742
757
|
expect.objectContaining({
|
|
743
758
|
organisationId: 'org-123',
|
|
744
759
|
eventId: 'event-123'
|
|
745
760
|
}),
|
|
746
|
-
'read:dashboard',
|
|
747
|
-
'dashboard',
|
|
761
|
+
['read:dashboard'],
|
|
748
762
|
true
|
|
749
763
|
);
|
|
750
764
|
});
|
|
751
765
|
|
|
752
766
|
it('handles permission check errors', async () => {
|
|
753
767
|
const error = new Error('Database connection failed');
|
|
754
|
-
|
|
755
|
-
|
|
768
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
769
|
+
results: { 'read:dashboard': false } as Record<string, boolean>,
|
|
756
770
|
isLoading: false,
|
|
757
|
-
error
|
|
771
|
+
error,
|
|
772
|
+
refetch: vi.fn()
|
|
758
773
|
});
|
|
759
774
|
|
|
760
775
|
render(
|
|
@@ -785,10 +800,11 @@ describe('NavigationGuard Component', () => {
|
|
|
785
800
|
hidden: false
|
|
786
801
|
};
|
|
787
802
|
|
|
788
|
-
|
|
789
|
-
|
|
803
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
804
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
790
805
|
isLoading: false,
|
|
791
|
-
error: null
|
|
806
|
+
error: null,
|
|
807
|
+
refetch: vi.fn()
|
|
792
808
|
});
|
|
793
809
|
|
|
794
810
|
render(
|
|
@@ -801,14 +817,13 @@ describe('NavigationGuard Component', () => {
|
|
|
801
817
|
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
802
818
|
}, { interval: 10 });
|
|
803
819
|
|
|
804
|
-
expect(
|
|
820
|
+
expect(mockUseMultiplePermissions).toHaveBeenCalledWith(
|
|
805
821
|
'user-123',
|
|
806
822
|
expect.objectContaining({
|
|
807
823
|
organisationId: 'org-123',
|
|
808
824
|
eventId: 'event-123'
|
|
809
825
|
}),
|
|
810
|
-
'read:settings',
|
|
811
|
-
'settings',
|
|
826
|
+
['read:settings'],
|
|
812
827
|
true
|
|
813
828
|
);
|
|
814
829
|
});
|
|
@@ -819,10 +834,11 @@ describe('NavigationGuard Component', () => {
|
|
|
819
834
|
hidden: true
|
|
820
835
|
};
|
|
821
836
|
|
|
822
|
-
|
|
823
|
-
|
|
837
|
+
mockUseMultiplePermissions.mockReturnValue({
|
|
838
|
+
results: { 'read:dashboard': true } as Record<string, boolean>,
|
|
824
839
|
isLoading: false,
|
|
825
|
-
error: null
|
|
840
|
+
error: null,
|
|
841
|
+
refetch: vi.fn()
|
|
826
842
|
});
|
|
827
843
|
|
|
828
844
|
render(
|
|
@@ -947,7 +947,7 @@ describe('PagePermissionGuard Component', () => {
|
|
|
947
947
|
expect(mockUseCan).toHaveBeenCalledWith(
|
|
948
948
|
'',
|
|
949
949
|
expect.objectContaining({
|
|
950
|
-
organisationId:
|
|
950
|
+
organisationId: undefined, // Changed from empty string - undefined is used when not resolved
|
|
951
951
|
eventId: undefined,
|
|
952
952
|
appId: undefined // appId is optional and should be undefined when not resolved
|
|
953
953
|
}),
|