@jmruthers/pace-core 0.5.193 → 0.6.1
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/CHANGELOG.md +29 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +372 -0
- package/cursor-rules/01-standards-compliance.mdc +275 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +341 -0
- package/cursor-rules/04-testing-standards.mdc +315 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +392 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
- package/cursor-rules/CHANGELOG.md +101 -0
- package/cursor-rules/README.md +191 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
- package/dist/{DataTable-5FU7IESH.js → DataTable-DQ7RSOHE.js} +6 -6
- package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +34 -155
- package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-ATAP5UTR.js} +2 -2
- package/dist/{chunk-6C4YBBJM 5.js → chunk-3QRJFVBR.js} +1 -1
- package/dist/chunk-3QRJFVBR.js.map +1 -0
- package/dist/{chunk-IIELH4DL.js → chunk-3XTALGJF.js} +2 -2
- package/dist/{chunk-IIELH4DL.js.map → chunk-3XTALGJF.js.map} +1 -1
- package/dist/{chunk-HWIIPPNI.js → chunk-4N5C5XZU.js} +20 -20
- package/dist/chunk-4N5C5XZU.js.map +1 -0
- package/dist/{chunk-7EQTDTTJ.js → chunk-4ZC4GX36.js} +5 -5
- package/dist/{chunk-7EQTDTTJ.js 2.map → chunk-4ZC4GX36.js.map} +1 -1
- package/dist/{chunk-7FLMSG37.js → chunk-BYFSK72L.js} +22 -22
- package/dist/chunk-BYFSK72L.js.map +1 -0
- package/dist/{chunk-LFNCN2SP.js → chunk-EXUD6RNJ.js} +46 -7
- package/dist/chunk-EXUD6RNJ.js.map +1 -0
- package/dist/{chunk-NOAYCWCX 5.js → chunk-GLK6VM3F.js} +167 -169
- package/dist/chunk-GLK6VM3F.js.map +1 -0
- package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-BC4IJKSL.js → chunk-JBKQ3SAO.js} +2 -2
- package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
- package/dist/{chunk-E3SPN4VZ 5.js → chunk-T33XF5ZC.js} +119 -114
- package/dist/chunk-T33XF5ZC.js.map +1 -0
- package/dist/{chunk-XNXXZ43G.js → chunk-XM25TVIE.js} +27 -4
- package/dist/chunk-XM25TVIE.js.map +1 -0
- package/dist/components.d.ts +3 -3
- package/dist/components.js +8 -8
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +17 -22
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +15 -16
- package/dist/index.js.map +1 -1
- package/dist/providers.js +1 -1
- package/dist/rbac/index.d.ts +1 -1
- package/dist/rbac/index.js +5 -5
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -3
- package/docs/getting-started/cursor-rules.md +262 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/MIGRATION_GUIDE.md +4 -4
- package/docs/migration/REACT_19_MIGRATION.md +227 -0
- package/docs/standards/README.md +39 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +11 -6
- package/scripts/audit-consuming-app.cjs +961 -0
- package/scripts/check-pace-core-compliance.cjs +34 -15
- package/scripts/install-cursor-rules.cjs +236 -0
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/components/Badge/Badge.tsx +2 -4
- package/src/components/Button/Button.tsx +5 -4
- package/src/components/Calendar/Calendar.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +13 -5
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/DataTableCore.tsx +4 -7
- package/src/components/DataTable/components/DataTableModals.tsx +1 -1
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +6 -8
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- package/src/components/DataTable/hooks/useColumnReordering.ts +2 -2
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +6 -5
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
- package/src/components/EventSelector/EventSelector.tsx +1 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +30 -26
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.tsx +28 -30
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +8 -8
- package/src/components/NavigationMenu/NavigationMenu.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +50 -50
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +1 -1
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
- package/src/components/Select/Select.tsx +33 -22
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/UserMenu/UserMenu.tsx +1 -1
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/public/usePublicEvent.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +1 -1
- package/src/hooks/public/usePublicRouteParams.ts +1 -1
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useFocusManagement.ts +2 -2
- package/src/hooks/useFocusTrap.ts +4 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +1 -1
- package/src/hooks/usePermissionCache.ts +1 -1
- package/src/hooks/useSecureDataAccess.ts +19 -4
- package/src/hooks/useToast.ts +2 -2
- package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/UnifiedAuthProvider.tsx +22 -22
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +24 -24
- package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +1 -1
- package/src/rbac/components/NavigationProvider.tsx +1 -1
- package/src/rbac/components/PagePermissionGuard.tsx +1 -1
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +1 -1
- package/src/rbac/components/RoleBasedRouter.tsx +1 -1
- package/src/rbac/components/SecureDataProvider.tsx +1 -1
- package/src/rbac/secureClient.ts +12 -0
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js +0 -628
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js.map +0 -1
- package/dist/chunk-7FLMSG37.js 2.map +0 -1
- package/dist/chunk-7FLMSG37.js.map +0 -1
- package/dist/chunk-E3SPN4VZ.js +0 -12917
- package/dist/chunk-E3SPN4VZ.js.map +0 -1
- package/dist/chunk-E66EQZE6 5.js +0 -37
- package/dist/chunk-E66EQZE6.js 2.map +0 -1
- package/dist/chunk-HWIIPPNI.js.map +0 -1
- package/dist/chunk-I7PSE6JW 5.js +0 -191
- package/dist/chunk-I7PSE6JW.js 2.map +0 -1
- package/dist/chunk-KNC55RTG.js 5.map +0 -1
- package/dist/chunk-KQCRWDSA.js 5.map +0 -1
- package/dist/chunk-LFNCN2SP.js 2.map +0 -1
- package/dist/chunk-LFNCN2SP.js.map +0 -1
- package/dist/chunk-LMC26NLJ 2.js +0 -84
- package/dist/chunk-NOAYCWCX.js +0 -4993
- package/dist/chunk-NOAYCWCX.js.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js.map +0 -1
- package/dist/chunk-QXHPKYJV 3.js +0 -113
- package/dist/chunk-R77UEZ4E 3.js +0 -68
- package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
- package/dist/chunk-XNXXZ43G.js.map +0 -1
- package/dist/chunk-ZSAAAMVR 6.js +0 -25
- package/dist/components.js 5.map +0 -1
- package/dist/styles/index 2.js +0 -12
- package/dist/styles/index.js 5.map +0 -1
- package/dist/theming/runtime 5.js +0 -19
- package/dist/theming/runtime.js 5.map +0 -1
- /package/dist/{DataTable-5FU7IESH.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
- /package/dist/{chunk-BC4IJKSL.js.map → chunk-JBKQ3SAO.js.map} +0 -0
- /package/dist/{chunk-QWWZ5CAQ.js 3.map → chunk-LXQLPRQ2.js.map} +0 -0
- /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
|
@@ -237,9 +237,11 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
237
237
|
// SECURITY: Phase 2 additions - complete organisation table mapping
|
|
238
238
|
'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',
|
|
239
239
|
// SECURITY: Emergency additions for Phase 1 fixes
|
|
240
|
-
'cake_meal', 'cake_mealtype',
|
|
241
|
-
// NOTE:
|
|
242
|
-
// are
|
|
240
|
+
'cake_meal', 'cake_mealtype',
|
|
241
|
+
// NOTE: core_person, core_member, core_contact, core_consent, core_identification,
|
|
242
|
+
// core_qualification, and medi_profile are person-scoped tables that do NOT have
|
|
243
|
+
// organisation_id columns. They were removed as part of the person-scoped profiles migration.
|
|
244
|
+
// Do NOT add organisation_id filters to these tables - it will cause 400 errors.
|
|
243
245
|
// SECURITY: Phase 3A additions - medical and personal data
|
|
244
246
|
// NOTE: medi_condition, medi_diet, medi_action_plan, medi_profile_versions are now person-scoped
|
|
245
247
|
// (via medi_profile) - removed from this list
|
|
@@ -370,7 +372,20 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
370
372
|
// SECURITY: Phase 2 additions - complete organisation table mapping
|
|
371
373
|
'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',
|
|
372
374
|
// SECURITY: Emergency additions for Phase 1 fixes
|
|
373
|
-
'cake_meal', 'cake_mealtype', 'core_person',
|
|
375
|
+
'cake_meal', 'cake_mealtype', 'core_person',
|
|
376
|
+
// SECURITY: These tables still have organisation_id columns and must be filtered
|
|
377
|
+
'core_member', 'core_contact', 'core_consent', 'core_identification', 'core_qualification',
|
|
378
|
+
'medi_profile',
|
|
379
|
+
// SECURITY: Phase 3A additions - medical and personal data
|
|
380
|
+
'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',
|
|
381
|
+
'core_identification_type',
|
|
382
|
+
'form_responses', 'form_response_values', 'forms',
|
|
383
|
+
// SECURITY: Phase 3B additions - remaining critical tables
|
|
384
|
+
'invoice', 'line_item', 'credit_balance', 'payment_method',
|
|
385
|
+
'form_contexts', 'form_field_config', 'form_fields',
|
|
386
|
+
'cake_delivery', 'cake_diettype', 'cake_diner', 'cake_dish', 'cake_item',
|
|
387
|
+
'cake_logistics', 'cake_mealplan', 'cake_package', 'cake_recipe', 'cake_supplier',
|
|
388
|
+
'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions'
|
|
374
389
|
];
|
|
375
390
|
|
|
376
391
|
if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {
|
package/src/hooks/useToast.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface ToastProps {
|
|
|
23
23
|
description?: React.ReactNode;
|
|
24
24
|
variant?: 'default' | 'destructive' | 'success';
|
|
25
25
|
onClose?: () => void;
|
|
26
|
-
action?: React.ReactElement
|
|
26
|
+
action?: React.ReactElement<any>;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -40,7 +40,7 @@ type ToasterToast = ToastProps & {
|
|
|
40
40
|
/** Optional description content */
|
|
41
41
|
description?: React.ReactNode
|
|
42
42
|
/** Optional action button */
|
|
43
|
-
action?: React.ReactElement
|
|
43
|
+
action?: React.ReactElement<any>
|
|
44
44
|
/** Open state */
|
|
45
45
|
open?: boolean
|
|
46
46
|
/** Open change handler */
|
|
@@ -17,9 +17,14 @@ vi.mock('../../utils/debugLogger', () => ({
|
|
|
17
17
|
},
|
|
18
18
|
}));
|
|
19
19
|
|
|
20
|
-
// Mock setOrganisationContext
|
|
20
|
+
// Mock setOrganisationContext - make it resolve immediately to avoid delays
|
|
21
21
|
vi.mock('../../utils/context/organisationContext', () => ({
|
|
22
|
-
setOrganisationContext: vi.fn().
|
|
22
|
+
setOrganisationContext: vi.fn().mockImplementation(() => Promise.resolve(undefined)),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
// Mock isSuperAdmin to return false (normal user, not super admin)
|
|
26
|
+
vi.mock('../../rbac/api', () => ({
|
|
27
|
+
isSuperAdmin: vi.fn().mockResolvedValue(false),
|
|
23
28
|
}));
|
|
24
29
|
|
|
25
30
|
// Create mock user and session
|
|
@@ -63,18 +68,36 @@ const createMockSupabaseClient = () => {
|
|
|
63
68
|
role: 'org_admin',
|
|
64
69
|
status: 'active',
|
|
65
70
|
granted_at: '2023-01-01T00:00:00Z',
|
|
71
|
+
granted_by: null,
|
|
66
72
|
revoked_at: null,
|
|
67
|
-
|
|
73
|
+
revoked_by: null,
|
|
74
|
+
notes: null,
|
|
75
|
+
created_at: '2023-01-01T00:00:00Z',
|
|
76
|
+
updated_at: '2023-01-01T00:00:00Z',
|
|
77
|
+
core_organisations: {
|
|
78
|
+
id: orgId,
|
|
79
|
+
name: 'Test Organisation 1',
|
|
80
|
+
display_name: 'Test Organisation 1',
|
|
81
|
+
subscription_tier: 'basic',
|
|
82
|
+
settings: {},
|
|
83
|
+
is_active: true,
|
|
84
|
+
parent_id: null,
|
|
85
|
+
created_at: '2023-01-01T00:00:00Z',
|
|
86
|
+
updated_at: '2023-01-01T00:00:00Z'
|
|
87
|
+
}
|
|
68
88
|
}],
|
|
69
89
|
error: null
|
|
70
90
|
})
|
|
71
91
|
};
|
|
72
92
|
|
|
93
|
+
// Mock RPC function for setOrganisationContext
|
|
94
|
+
const mockRpc = vi.fn().mockResolvedValue({
|
|
95
|
+
data: null,
|
|
96
|
+
error: null,
|
|
97
|
+
});
|
|
98
|
+
|
|
73
99
|
return {
|
|
74
|
-
rpc:
|
|
75
|
-
data: [],
|
|
76
|
-
error: null,
|
|
77
|
-
}),
|
|
100
|
+
rpc: mockRpc,
|
|
78
101
|
from: vi.fn((table: string) => {
|
|
79
102
|
if (table === 'rbac_organisation_roles') {
|
|
80
103
|
return mockQueryBuilder;
|
|
@@ -127,6 +150,8 @@ describe('OrganisationProvider', () => {
|
|
|
127
150
|
|
|
128
151
|
beforeEach(() => {
|
|
129
152
|
vi.clearAllMocks();
|
|
153
|
+
// Clear localStorage to ensure clean state
|
|
154
|
+
localStorage.clear();
|
|
130
155
|
mockSupabaseClient = createMockSupabaseClient();
|
|
131
156
|
});
|
|
132
157
|
|
|
@@ -140,10 +165,19 @@ describe('OrganisationProvider', () => {
|
|
|
140
165
|
|
|
141
166
|
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
142
167
|
|
|
143
|
-
// Wait for organisations to load
|
|
168
|
+
// Wait for organisations to load - OrganisationService initializes asynchronously
|
|
169
|
+
// The service calls notify() in finally block, and useOrganisationService has 50ms debounce
|
|
170
|
+
// Also need to account for the 2 second minimum retry delay in loadUserOrganisations
|
|
144
171
|
await waitFor(() => {
|
|
145
|
-
|
|
146
|
-
|
|
172
|
+
const count = screen.getByTestId('organisations-count').textContent;
|
|
173
|
+
const error = screen.getByTestId('error').textContent;
|
|
174
|
+
// Log for debugging if test fails
|
|
175
|
+
if (count !== '1' || error !== 'no-error') {
|
|
176
|
+
console.log('Test state:', { count, error, isLoading: screen.getByTestId('isLoading').textContent });
|
|
177
|
+
}
|
|
178
|
+
expect(count).toBe('1');
|
|
179
|
+
expect(error).toBe('no-error');
|
|
180
|
+
}, { timeout: 15000, interval: 200 });
|
|
147
181
|
|
|
148
182
|
expect(screen.getByTestId('isLoading')).toHaveTextContent('false');
|
|
149
183
|
expect(screen.getByTestId('error')).toHaveTextContent('no-error');
|
|
@@ -169,10 +203,20 @@ describe('OrganisationProvider', () => {
|
|
|
169
203
|
);
|
|
170
204
|
|
|
171
205
|
// Wait for organisations to load and selected organisation to be set
|
|
206
|
+
// The service calls notify() in finally block, and useOrganisationService has 50ms debounce
|
|
207
|
+
// Also need to account for the 2 second minimum retry delay in loadUserOrganisations
|
|
172
208
|
await waitFor(() => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
209
|
+
const count = screen.getByTestId('organisations-count').textContent;
|
|
210
|
+
const error = screen.getByTestId('error').textContent;
|
|
211
|
+
const selectedOrg = screen.getByTestId('selectedOrg').textContent;
|
|
212
|
+
// Log for debugging if test fails
|
|
213
|
+
if (count !== '1' || error !== 'no-error' || selectedOrg === 'no-org') {
|
|
214
|
+
console.log('Test state:', { count, error, selectedOrg, isLoading: screen.getByTestId('isLoading').textContent });
|
|
215
|
+
}
|
|
216
|
+
expect(count).toBe('1');
|
|
217
|
+
expect(error).toBe('no-error');
|
|
218
|
+
expect(selectedOrg).not.toBe('no-org');
|
|
219
|
+
}, { timeout: 15000, interval: 200 });
|
|
176
220
|
|
|
177
221
|
expect(screen.getByTestId('organisations-count')).toHaveTextContent('1');
|
|
178
222
|
expect(screen.getByTestId('isLoading')).toHaveTextContent('false');
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Tests for provider lifecycle management, state persistence, cleanup, and edge cases.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
10
|
+
import { render, screen, fireEvent, act } from '@testing-library/react';
|
|
11
11
|
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
12
12
|
import React, { ReactNode, useState } from 'react';
|
|
13
13
|
import { createMockSupabaseClient } from '../../__tests__/helpers/supabaseMock';
|
|
@@ -88,8 +88,12 @@ describe('Provider Lifecycle Tests', () => {
|
|
|
88
88
|
it('should cleanup subscription listeners', () => {
|
|
89
89
|
const cleanup = vi.fn();
|
|
90
90
|
const TestComponent = () => {
|
|
91
|
+
const cleanupRef = React.useRef(cleanup);
|
|
91
92
|
React.useEffect(() => {
|
|
92
|
-
|
|
93
|
+
// Return a function that calls cleanup from ref
|
|
94
|
+
return () => {
|
|
95
|
+
cleanupRef.current();
|
|
96
|
+
};
|
|
93
97
|
}, []);
|
|
94
98
|
return <div data-testid="listener-test">Test</div>;
|
|
95
99
|
};
|
|
@@ -98,7 +102,9 @@ describe('Provider Lifecycle Tests', () => {
|
|
|
98
102
|
|
|
99
103
|
expect(screen.getByTestId('listener-test')).toBeInTheDocument();
|
|
100
104
|
|
|
101
|
-
|
|
105
|
+
act(() => {
|
|
106
|
+
unmountComponent();
|
|
107
|
+
});
|
|
102
108
|
|
|
103
109
|
// Cleanup should be called on unmount
|
|
104
110
|
expect(cleanup).toHaveBeenCalled();
|
|
@@ -109,13 +115,22 @@ describe('Provider Lifecycle Tests', () => {
|
|
|
109
115
|
const cleanup2 = vi.fn();
|
|
110
116
|
|
|
111
117
|
const TestComponent = () => {
|
|
112
|
-
React.
|
|
113
|
-
React.
|
|
118
|
+
const cleanup1Ref = React.useRef(cleanup1);
|
|
119
|
+
const cleanup2Ref = React.useRef(cleanup2);
|
|
120
|
+
React.useEffect(() => {
|
|
121
|
+
return () => cleanup1Ref.current();
|
|
122
|
+
}, []);
|
|
123
|
+
React.useEffect(() => {
|
|
124
|
+
return () => cleanup2Ref.current();
|
|
125
|
+
}, []);
|
|
114
126
|
return <div data-testid="multi-listener">Test</div>;
|
|
115
127
|
};
|
|
116
128
|
|
|
117
129
|
const { unmount: unmountComponent } = render(<TestComponent />);
|
|
118
|
-
|
|
130
|
+
|
|
131
|
+
act(() => {
|
|
132
|
+
unmountComponent();
|
|
133
|
+
});
|
|
119
134
|
|
|
120
135
|
expect(cleanup1).toHaveBeenCalled();
|
|
121
136
|
expect(cleanup2).toHaveBeenCalled();
|
|
@@ -72,7 +72,7 @@ const TestComponent = () => {
|
|
|
72
72
|
|
|
73
73
|
describe('UnifiedAuthProvider', () => {
|
|
74
74
|
let mockSupabaseClient: ReturnType<typeof createMockSupabaseClient>;
|
|
75
|
-
const
|
|
75
|
+
const baseProps = {
|
|
76
76
|
idleTimeoutMs: 30 * 60 * 1000,
|
|
77
77
|
warnBeforeMs: 60 * 1000,
|
|
78
78
|
onIdleLogout: vi.fn(),
|
|
@@ -86,7 +86,7 @@ describe('UnifiedAuthProvider', () => {
|
|
|
86
86
|
describe('Rendering', () => {
|
|
87
87
|
it('renders without crashing', () => {
|
|
88
88
|
render(
|
|
89
|
-
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} {...
|
|
89
|
+
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} {...baseProps}>
|
|
90
90
|
<TestComponent />
|
|
91
91
|
</UnifiedAuthProvider>
|
|
92
92
|
);
|
|
@@ -96,7 +96,7 @@ describe('UnifiedAuthProvider', () => {
|
|
|
96
96
|
|
|
97
97
|
it('renders with custom app name', () => {
|
|
98
98
|
render(
|
|
99
|
-
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Custom App" {...
|
|
99
|
+
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Custom App" {...baseProps}>
|
|
100
100
|
<TestComponent />
|
|
101
101
|
</UnifiedAuthProvider>
|
|
102
102
|
);
|
|
@@ -118,7 +118,7 @@ describe('UnifiedAuthProvider', () => {
|
|
|
118
118
|
describe('Context Values', () => {
|
|
119
119
|
it('provides all required context values', async () => {
|
|
120
120
|
render(
|
|
121
|
-
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" {...
|
|
121
|
+
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" {...baseProps}>
|
|
122
122
|
<TestComponent />
|
|
123
123
|
</UnifiedAuthProvider>
|
|
124
124
|
);
|
|
@@ -182,7 +182,7 @@ describe('UnifiedAuthProvider', () => {
|
|
|
182
182
|
|
|
183
183
|
it('uses default configuration', () => {
|
|
184
184
|
render(
|
|
185
|
-
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} {...
|
|
185
|
+
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} {...baseProps}>
|
|
186
186
|
<TestComponent />
|
|
187
187
|
</UnifiedAuthProvider>
|
|
188
188
|
);
|
|
@@ -207,7 +207,7 @@ describe('UnifiedAuthProvider', () => {
|
|
|
207
207
|
};
|
|
208
208
|
|
|
209
209
|
render(
|
|
210
|
-
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" {...
|
|
210
|
+
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" {...baseProps}>
|
|
211
211
|
<TestCompositionComponent />
|
|
212
212
|
</UnifiedAuthProvider>
|
|
213
213
|
);
|
|
@@ -236,7 +236,7 @@ describe('UnifiedAuthProvider', () => {
|
|
|
236
236
|
};
|
|
237
237
|
|
|
238
238
|
render(
|
|
239
|
-
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" {...
|
|
239
|
+
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" {...baseProps}>
|
|
240
240
|
<TestCompleteContextComponent />
|
|
241
241
|
</UnifiedAuthProvider>
|
|
242
242
|
);
|
|
@@ -428,7 +428,7 @@ describe('UnifiedAuthProvider', () => {
|
|
|
428
428
|
};
|
|
429
429
|
|
|
430
430
|
render(
|
|
431
|
-
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" {...
|
|
431
|
+
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" {...baseProps}>
|
|
432
432
|
<TestHookIntegrationComponent />
|
|
433
433
|
</UnifiedAuthProvider>
|
|
434
434
|
);
|
|
@@ -494,7 +494,7 @@ describe('UnifiedAuthProvider', () => {
|
|
|
494
494
|
};
|
|
495
495
|
|
|
496
496
|
render(
|
|
497
|
-
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" {...
|
|
497
|
+
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" {...baseProps}>
|
|
498
498
|
<TestConcurrentComponent />
|
|
499
499
|
</UnifiedAuthProvider>
|
|
500
500
|
);
|
|
@@ -554,7 +554,7 @@ describe('UnifiedAuthProvider', () => {
|
|
|
554
554
|
};
|
|
555
555
|
|
|
556
556
|
render(
|
|
557
|
-
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" enableRBAC={true} {...
|
|
557
|
+
<UnifiedAuthProvider supabaseClient={mockSupabaseClient} appName="Test App" enableRBAC={true} {...baseProps}>
|
|
558
558
|
<TestRBACConfigComponent />
|
|
559
559
|
</UnifiedAuthProvider>
|
|
560
560
|
);
|
|
@@ -526,31 +526,31 @@ function UnifiedAuthContextProvider({
|
|
|
526
526
|
|
|
527
527
|
const hasErrors = !!(authError || organisationError || eventError || sessionRestoration.restorationError);
|
|
528
528
|
|
|
529
|
-
//
|
|
530
|
-
const signIn =
|
|
531
|
-
const signUp =
|
|
532
|
-
const signOut =
|
|
533
|
-
const resetPassword =
|
|
534
|
-
const updatePassword =
|
|
535
|
-
const refreshSession =
|
|
529
|
+
// React Compiler handles memoization automatically
|
|
530
|
+
const signIn = (email: string, password?: string) => authService.signIn(email, password);
|
|
531
|
+
const signUp = (email: string, password: string) => authService.signUp(email, password);
|
|
532
|
+
const signOut = () => authService.signOut();
|
|
533
|
+
const resetPassword = (email: string) => authService.resetPassword(email);
|
|
534
|
+
const updatePassword = (password: string) => authService.updatePassword(password);
|
|
535
|
+
const refreshSession = () => authService.refreshSession();
|
|
536
536
|
|
|
537
|
-
const switchOrganisation =
|
|
538
|
-
const getUserRole =
|
|
539
|
-
const validateOrganisationAccess =
|
|
540
|
-
const refreshOrganisations =
|
|
541
|
-
const ensureOrganisationContext =
|
|
542
|
-
const isOrganisationSecure =
|
|
543
|
-
const getPrimaryOrganisation =
|
|
537
|
+
const switchOrganisation = (orgId: string) => organisationService.switchOrganisation(orgId);
|
|
538
|
+
const getUserRole = (orgId?: string) => organisationService.getUserRole(orgId);
|
|
539
|
+
const validateOrganisationAccess = (orgId: string) => organisationService.validateOrganisationAccess(orgId);
|
|
540
|
+
const refreshOrganisations = () => organisationService.refreshOrganisations();
|
|
541
|
+
const ensureOrganisationContext = () => organisationService.ensureOrganisationContext();
|
|
542
|
+
const isOrganisationSecure = () => organisationService.isOrganisationSecure();
|
|
543
|
+
const getPrimaryOrganisation = () => organisationService.getPrimaryOrganisation();
|
|
544
544
|
|
|
545
|
-
const setSelectedEvent =
|
|
546
|
-
const refreshEvents =
|
|
545
|
+
const setSelectedEvent = (event: Event | null) => eventService.setSelectedEvent(event);
|
|
546
|
+
const refreshEvents = () => eventService.refreshEvents();
|
|
547
547
|
|
|
548
|
-
const resetActivity =
|
|
549
|
-
const startTracking =
|
|
550
|
-
const stopTracking =
|
|
551
|
-
const handleIdleLogout =
|
|
552
|
-
const handleStaySignedIn =
|
|
553
|
-
const handleSignOutNow =
|
|
548
|
+
const resetActivity = () => inactivityService.resetActivity();
|
|
549
|
+
const startTracking = () => inactivityService.startTracking();
|
|
550
|
+
const stopTracking = () => inactivityService.stopTracking();
|
|
551
|
+
const handleIdleLogout = () => inactivityService.handleIdleLogout();
|
|
552
|
+
const handleStaySignedIn = () => inactivityService.handleStaySignedIn();
|
|
553
|
+
const handleSignOutNow = () => inactivityService.handleSignOutNow();
|
|
554
554
|
|
|
555
555
|
// Use ref to track previous state for conditional logging (dev only)
|
|
556
556
|
const prevStateRef = useRef<{
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
12
|
-
import { render, screen, waitFor } from '@testing-library/react';
|
|
12
|
+
import { render, screen, waitFor, act } from '@testing-library/react';
|
|
13
13
|
import React from 'react';
|
|
14
14
|
import { AuthServiceProvider } from '../AuthServiceProvider';
|
|
15
15
|
import { useAuthService } from '../../../hooks/services/useAuthService';
|
|
@@ -195,16 +195,26 @@ describe('AuthServiceProvider Integration', () => {
|
|
|
195
195
|
}, { interval: 10 });
|
|
196
196
|
|
|
197
197
|
// Simulate auth state change using the captured callback
|
|
198
|
+
// Supabase auth state change callback receives (event, session) as arguments
|
|
198
199
|
if (capturedCallback) {
|
|
200
|
+
// Call the callback - AuthService will update state and notify subscribers
|
|
201
|
+
// The callback is synchronous, but notify() triggers subscribers which have debounce
|
|
199
202
|
capturedCallback('SIGNED_IN', mockSession);
|
|
203
|
+
|
|
204
|
+
// Wait for debounced subscriber updates (50ms debounce in useAuthService)
|
|
205
|
+
// The test component subscribes directly, so we need to wait for the debounce
|
|
206
|
+
await act(async () => {
|
|
207
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
208
|
+
});
|
|
200
209
|
}
|
|
201
210
|
|
|
202
|
-
// Wait for UI updates after state change
|
|
211
|
+
// Wait for UI updates after state change - need longer timeout for async updates
|
|
212
|
+
// The debounce in useAuthService adds a 50ms delay, so we need to wait longer
|
|
203
213
|
await waitFor(() => {
|
|
204
214
|
expect(screen.getByTestId('user-id')).toHaveTextContent('test-user-id');
|
|
205
215
|
expect(screen.getByTestId('is-authenticated')).toHaveTextContent('true');
|
|
206
216
|
expect(screen.getByTestId('is-loading')).toHaveTextContent('false');
|
|
207
|
-
}, { interval:
|
|
217
|
+
}, { interval: 50, timeout: 10000 });
|
|
208
218
|
});
|
|
209
219
|
});
|
|
210
220
|
|
|
@@ -91,7 +91,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
describe('PermissionGuard Component', () => {
|
|
94
|
-
const
|
|
94
|
+
const baseProps = {
|
|
95
95
|
userId: 'user-123' as UUID,
|
|
96
96
|
scope: { organisationId: 'org-123' as UUID },
|
|
97
97
|
permission: 'read:users' as Permission,
|
|
@@ -106,7 +106,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
106
106
|
error: null,
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
-
render(<PermissionGuard {...
|
|
109
|
+
render(<PermissionGuard {...baseProps} />);
|
|
110
110
|
|
|
111
111
|
expect(screen.getByText('Protected Content')).toBeInTheDocument();
|
|
112
112
|
});
|
|
@@ -119,7 +119,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
119
119
|
});
|
|
120
120
|
|
|
121
121
|
const fallback = <div>Access Denied</div>;
|
|
122
|
-
render(<PermissionGuard {...
|
|
122
|
+
render(<PermissionGuard {...baseProps} fallback={fallback} />);
|
|
123
123
|
|
|
124
124
|
expect(screen.getByText('Access Denied')).toBeInTheDocument();
|
|
125
125
|
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
|
|
@@ -133,7 +133,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
133
133
|
});
|
|
134
134
|
|
|
135
135
|
const loading = <div>Checking permissions...</div>;
|
|
136
|
-
render(<PermissionGuard {...
|
|
136
|
+
render(<PermissionGuard {...baseProps} loading={loading} />);
|
|
137
137
|
|
|
138
138
|
expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
|
|
139
139
|
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
|
|
@@ -146,7 +146,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
146
146
|
error: null,
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
-
render(<PermissionGuard {...
|
|
149
|
+
render(<PermissionGuard {...baseProps} />);
|
|
150
150
|
|
|
151
151
|
expect(screen.getByRole('status')).toBeInTheDocument();
|
|
152
152
|
expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
|
|
@@ -160,7 +160,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
160
160
|
});
|
|
161
161
|
|
|
162
162
|
const fallback = <div>Error occurred</div>;
|
|
163
|
-
render(<PermissionGuard {...
|
|
163
|
+
render(<PermissionGuard {...baseProps} fallback={fallback} />);
|
|
164
164
|
|
|
165
165
|
expect(screen.getByText('Error occurred')).toBeInTheDocument();
|
|
166
166
|
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
|
|
@@ -180,7 +180,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
180
180
|
|
|
181
181
|
render(
|
|
182
182
|
<PermissionGuard
|
|
183
|
-
{...
|
|
183
|
+
{...baseProps}
|
|
184
184
|
userId={'' as any}
|
|
185
185
|
fallback={<div>No Access</div>}
|
|
186
186
|
/>
|
|
@@ -202,7 +202,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
202
202
|
|
|
203
203
|
render(
|
|
204
204
|
<PermissionGuard
|
|
205
|
-
{...
|
|
205
|
+
{...baseProps}
|
|
206
206
|
userId={'' as any}
|
|
207
207
|
fallback={<div>No Access</div>}
|
|
208
208
|
/>
|
|
@@ -221,7 +221,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
221
221
|
error: null,
|
|
222
222
|
});
|
|
223
223
|
|
|
224
|
-
render(<PermissionGuard {...
|
|
224
|
+
render(<PermissionGuard {...baseProps} onDenied={onDenied} />);
|
|
225
225
|
|
|
226
226
|
expect(onDenied).toHaveBeenCalledTimes(1);
|
|
227
227
|
});
|
|
@@ -234,7 +234,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
234
234
|
error: null,
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
-
render(<PermissionGuard {...
|
|
237
|
+
render(<PermissionGuard {...baseProps} onDenied={onDenied} />);
|
|
238
238
|
|
|
239
239
|
expect(onDenied).not.toHaveBeenCalled();
|
|
240
240
|
});
|
|
@@ -255,7 +255,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
255
255
|
error: null,
|
|
256
256
|
});
|
|
257
257
|
|
|
258
|
-
render(<PermissionGuard {...
|
|
258
|
+
render(<PermissionGuard {...baseProps} auditLog={true} />);
|
|
259
259
|
|
|
260
260
|
// Note: Audit logging is currently commented out in PermissionGuard
|
|
261
261
|
// The component checks auditLog but doesn't actually log - this is expected behavior
|
|
@@ -278,7 +278,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
278
278
|
error: null,
|
|
279
279
|
});
|
|
280
280
|
|
|
281
|
-
render(<PermissionGuard {...
|
|
281
|
+
render(<PermissionGuard {...baseProps} auditLog={true} />);
|
|
282
282
|
|
|
283
283
|
// Note: Audit logging is currently commented out in PermissionGuard
|
|
284
284
|
// The component checks auditLog but doesn't actually log - this is expected behavior
|
|
@@ -301,7 +301,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
301
301
|
error: null,
|
|
302
302
|
});
|
|
303
303
|
|
|
304
|
-
render(<PermissionGuard {...
|
|
304
|
+
render(<PermissionGuard {...baseProps} strictMode={true} />);
|
|
305
305
|
|
|
306
306
|
expect(mockLoggerInstance.error).toHaveBeenCalledWith(
|
|
307
307
|
expect.stringContaining('STRICT MODE VIOLATION:'),
|
|
@@ -316,7 +316,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
316
316
|
});
|
|
317
317
|
|
|
318
318
|
describe('AccessLevelGuard Component', () => {
|
|
319
|
-
const
|
|
319
|
+
const baseProps = {
|
|
320
320
|
userId: 'user-123' as UUID,
|
|
321
321
|
scope: { organisationId: 'org-123' as UUID },
|
|
322
322
|
minLevel: 'admin' as const,
|
|
@@ -331,7 +331,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
331
331
|
error: null,
|
|
332
332
|
});
|
|
333
333
|
|
|
334
|
-
render(<AccessLevelGuard {...
|
|
334
|
+
render(<AccessLevelGuard {...baseProps} />);
|
|
335
335
|
|
|
336
336
|
expect(screen.getByText('Admin Content')).toBeInTheDocument();
|
|
337
337
|
});
|
|
@@ -344,7 +344,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
344
344
|
});
|
|
345
345
|
|
|
346
346
|
const fallback = <div>Insufficient Access</div>;
|
|
347
|
-
render(<AccessLevelGuard {...
|
|
347
|
+
render(<AccessLevelGuard {...baseProps} fallback={fallback} />);
|
|
348
348
|
|
|
349
349
|
expect(screen.getByText('Insufficient Access')).toBeInTheDocument();
|
|
350
350
|
expect(screen.queryByText('Admin Content')).not.toBeInTheDocument();
|
|
@@ -358,7 +358,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
358
358
|
});
|
|
359
359
|
|
|
360
360
|
const loading = <div>Checking access level...</div>;
|
|
361
|
-
render(<AccessLevelGuard {...
|
|
361
|
+
render(<AccessLevelGuard {...baseProps} loading={loading} />);
|
|
362
362
|
|
|
363
363
|
expect(screen.getByText('Checking access level...')).toBeInTheDocument();
|
|
364
364
|
expect(screen.queryByText('Admin Content')).not.toBeInTheDocument();
|
|
@@ -372,7 +372,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
372
372
|
});
|
|
373
373
|
|
|
374
374
|
const fallback = <div>Error occurred</div>;
|
|
375
|
-
render(<AccessLevelGuard {...
|
|
375
|
+
render(<AccessLevelGuard {...baseProps} fallback={fallback} />);
|
|
376
376
|
|
|
377
377
|
expect(screen.getByText('Error occurred')).toBeInTheDocument();
|
|
378
378
|
expect(screen.queryByText('Admin Content')).not.toBeInTheDocument();
|
|
@@ -387,7 +387,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
387
387
|
error: null,
|
|
388
388
|
});
|
|
389
389
|
|
|
390
|
-
render(<AccessLevelGuard {...
|
|
390
|
+
render(<AccessLevelGuard {...baseProps} minLevel="admin" />);
|
|
391
391
|
|
|
392
392
|
expect(screen.getByText('Admin Content')).toBeInTheDocument();
|
|
393
393
|
});
|
|
@@ -399,7 +399,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
399
399
|
error: null,
|
|
400
400
|
});
|
|
401
401
|
|
|
402
|
-
render(<AccessLevelGuard {...
|
|
402
|
+
render(<AccessLevelGuard {...baseProps} minLevel="admin" />);
|
|
403
403
|
|
|
404
404
|
expect(screen.getByText('Admin Content')).toBeInTheDocument();
|
|
405
405
|
});
|
|
@@ -411,7 +411,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
411
411
|
error: null,
|
|
412
412
|
});
|
|
413
413
|
|
|
414
|
-
render(<AccessLevelGuard {...
|
|
414
|
+
render(<AccessLevelGuard {...baseProps} minLevel="admin" />);
|
|
415
415
|
|
|
416
416
|
expect(screen.queryByText('Admin Content')).not.toBeInTheDocument();
|
|
417
417
|
});
|
|
@@ -423,7 +423,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
423
423
|
error: null,
|
|
424
424
|
});
|
|
425
425
|
|
|
426
|
-
render(<AccessLevelGuard {...
|
|
426
|
+
render(<AccessLevelGuard {...baseProps} minLevel="planner" />);
|
|
427
427
|
|
|
428
428
|
expect(screen.getByText('Admin Content')).toBeInTheDocument();
|
|
429
429
|
});
|
|
@@ -439,7 +439,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
439
439
|
|
|
440
440
|
render(
|
|
441
441
|
<AccessLevelGuard
|
|
442
|
-
{...
|
|
442
|
+
{...baseProps}
|
|
443
443
|
userId={'' as any}
|
|
444
444
|
minLevel="admin"
|
|
445
445
|
fallback={<div>No Access</div>}
|
|
@@ -462,7 +462,7 @@ describe('RBAC Adapters - Comprehensive Tests', () => {
|
|
|
462
462
|
|
|
463
463
|
render(
|
|
464
464
|
<AccessLevelGuard
|
|
465
|
-
{...
|
|
465
|
+
{...baseProps}
|
|
466
466
|
userId={'' as any}
|
|
467
467
|
minLevel="admin"
|
|
468
468
|
fallback={<div>No Access</div>}
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
* - Efficient filtering
|
|
50
50
|
*
|
|
51
51
|
* @dependencies
|
|
52
|
-
* - React
|
|
52
|
+
* - React 19+ - Component framework
|
|
53
53
|
* - NavigationProvider - Navigation permission context
|
|
54
54
|
* - NavigationGuard - Individual navigation item protection
|
|
55
55
|
* - RBAC types - Type definitions
|