@jmruthers/pace-core 0.5.129 → 0.5.131

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.
Files changed (130) hide show
  1. package/dist/{DataTable-O2COE77K.js → DataTable-4TS2QVNU.js} +2 -2
  2. package/dist/{DataTable-D5cBRca8.d.ts → DataTable-C7GaRZye.d.ts} +3 -1
  3. package/dist/{chunk-BJ7MCGY6.js → chunk-3BHL6OVZ.js} +57 -42
  4. package/dist/chunk-3BHL6OVZ.js.map +1 -0
  5. package/dist/{chunk-MOOJ2TK6.js → chunk-IS4CMSYM.js} +5 -5
  6. package/dist/chunk-IS4CMSYM.js.map +1 -0
  7. package/dist/components.d.ts +2 -2
  8. package/dist/components.js +2 -2
  9. package/dist/hooks.d.ts +1 -1
  10. package/dist/index.d.ts +3 -3
  11. package/dist/index.js +2 -2
  12. package/dist/{types-D4TVpDa1.d.ts → types-D5rqZQXk.d.ts} +42 -1
  13. package/dist/utils.d.ts +2 -2
  14. package/dist/utils.js +1 -1
  15. package/docs/api/classes/ColumnFactory.md +1 -1
  16. package/docs/api/classes/ErrorBoundary.md +1 -1
  17. package/docs/api/classes/InvalidScopeError.md +1 -1
  18. package/docs/api/classes/MissingUserContextError.md +1 -1
  19. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  20. package/docs/api/classes/PermissionDeniedError.md +1 -1
  21. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  22. package/docs/api/classes/RBACAuditManager.md +1 -1
  23. package/docs/api/classes/RBACCache.md +1 -1
  24. package/docs/api/classes/RBACEngine.md +1 -1
  25. package/docs/api/classes/RBACError.md +1 -1
  26. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  27. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  28. package/docs/api/classes/StorageUtils.md +1 -1
  29. package/docs/api/enums/FileCategory.md +1 -1
  30. package/docs/api/interfaces/AggregateConfig.md +1 -1
  31. package/docs/api/interfaces/ButtonProps.md +1 -1
  32. package/docs/api/interfaces/CardProps.md +1 -1
  33. package/docs/api/interfaces/ColorPalette.md +1 -1
  34. package/docs/api/interfaces/ColorShade.md +1 -1
  35. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  36. package/docs/api/interfaces/DataRecord.md +1 -1
  37. package/docs/api/interfaces/DataTableAction.md +1 -1
  38. package/docs/api/interfaces/DataTableColumn.md +1 -1
  39. package/docs/api/interfaces/DataTableProps.md +40 -13
  40. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  41. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  42. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  43. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  44. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  45. package/docs/api/interfaces/FileMetadata.md +1 -1
  46. package/docs/api/interfaces/FileReference.md +1 -1
  47. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  48. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  49. package/docs/api/interfaces/FileUploadProps.md +1 -1
  50. package/docs/api/interfaces/FooterProps.md +1 -1
  51. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  52. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  53. package/docs/api/interfaces/InputProps.md +1 -1
  54. package/docs/api/interfaces/LabelProps.md +1 -1
  55. package/docs/api/interfaces/LoginFormProps.md +1 -1
  56. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  57. package/docs/api/interfaces/NavigationContextType.md +1 -1
  58. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  59. package/docs/api/interfaces/NavigationItem.md +1 -1
  60. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  61. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  62. package/docs/api/interfaces/Organisation.md +1 -1
  63. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  64. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  65. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  66. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  67. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  68. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  69. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  70. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  71. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  72. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  73. package/docs/api/interfaces/PaletteData.md +1 -1
  74. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  75. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  76. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  77. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  78. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  79. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  80. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  81. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  82. package/docs/api/interfaces/RBACConfig.md +1 -1
  83. package/docs/api/interfaces/RBACLogger.md +1 -1
  84. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  85. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  86. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  87. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  88. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  89. package/docs/api/interfaces/RouteConfig.md +1 -1
  90. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  91. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  92. package/docs/api/interfaces/StorageConfig.md +1 -1
  93. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  94. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  95. package/docs/api/interfaces/StorageListOptions.md +1 -1
  96. package/docs/api/interfaces/StorageListResult.md +1 -1
  97. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  98. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  99. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  100. package/docs/api/interfaces/StyleImport.md +1 -1
  101. package/docs/api/interfaces/SwitchProps.md +1 -1
  102. package/docs/api/interfaces/ToastActionElement.md +1 -1
  103. package/docs/api/interfaces/ToastProps.md +1 -1
  104. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  105. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  106. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  107. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  108. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  109. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  110. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  111. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  112. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  113. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  114. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  115. package/docs/api/interfaces/UserEventAccess.md +1 -1
  116. package/docs/api/interfaces/UserMenuProps.md +1 -1
  117. package/docs/api/interfaces/UserProfile.md +1 -1
  118. package/docs/api/modules.md +3 -3
  119. package/package.json +1 -1
  120. package/src/components/DataTable/DataTable.tsx +11 -0
  121. package/src/components/DataTable/components/DataTableCore.tsx +49 -40
  122. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +68 -11
  123. package/src/components/PaceAppLayout/PaceAppLayout.tsx +7 -4
  124. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +25 -7
  125. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +85 -3
  126. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +83 -14
  127. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +77 -6
  128. package/dist/chunk-BJ7MCGY6.js.map +0 -1
  129. package/dist/chunk-MOOJ2TK6.js.map +0 -1
  130. /package/dist/{DataTable-O2COE77K.js.map → DataTable-4TS2QVNU.js.map} +0 -0
@@ -295,8 +295,11 @@ describe('PaceAppLayout Security', () => {
295
295
  });
296
296
 
297
297
  it('prevents access when user lacks permission', async () => {
298
+ // Ensure super admin check completes first
299
+ mockIsSuperAdmin.mockResolvedValueOnce(false);
300
+
298
301
  // Mock useCan to return false (deny access)
299
- mockUseCan.mockReturnValueOnce({
302
+ mockUseCan.mockReturnValue({
300
303
  can: false,
301
304
  isLoading: false,
302
305
  error: null,
@@ -309,6 +312,14 @@ describe('PaceAppLayout Security', () => {
309
312
  </TestWrapper>
310
313
  );
311
314
 
315
+ // Wait for super admin check to complete and component to re-render
316
+ await waitFor(() => {
317
+ expect(mockIsSuperAdmin).toHaveBeenCalled();
318
+ }, { timeout: 1000 });
319
+
320
+ // Wait a bit for the component to process the super admin check result
321
+ await new Promise(resolve => setTimeout(resolve, 100));
322
+
312
323
  // Should show access denied when permission is denied
313
324
  await waitFor(() => {
314
325
  expect(screen.getByText('Access Denied')).toBeInTheDocument();
@@ -341,8 +352,11 @@ describe('PaceAppLayout Security', () => {
341
352
  });
342
353
 
343
354
  it('handles permission check failures securely', async () => {
355
+ // Ensure super admin check completes first
356
+ mockIsSuperAdmin.mockResolvedValueOnce(false);
357
+
344
358
  // Mock useCan to return an error state
345
- mockUseCan.mockReturnValueOnce({
359
+ mockUseCan.mockReturnValue({
346
360
  can: false,
347
361
  isLoading: false,
348
362
  error: new Error('Permission check failed'),
@@ -355,6 +369,14 @@ describe('PaceAppLayout Security', () => {
355
369
  </TestWrapper>
356
370
  );
357
371
 
372
+ // Wait for super admin check to complete and component to re-render
373
+ await waitFor(() => {
374
+ expect(mockIsSuperAdmin).toHaveBeenCalled();
375
+ }, { timeout: 1000 });
376
+
377
+ // Wait a bit for the component to process the super admin check result
378
+ await new Promise(resolve => setTimeout(resolve, 100));
379
+
358
380
  await waitFor(() => {
359
381
  // When permission check throws an error, should show Permission Error page
360
382
  expect(screen.getByText('Permission Error')).toBeInTheDocument();
@@ -674,8 +696,11 @@ describe('PaceAppLayout Security', () => {
674
696
  });
675
697
 
676
698
  it('handles permission errors securely', async () => {
699
+ // Ensure super admin check completes first
700
+ mockIsSuperAdmin.mockResolvedValueOnce(false);
701
+
677
702
  // Mock useCan to return false (deny access)
678
- mockUseCan.mockReturnValueOnce({
703
+ mockUseCan.mockReturnValue({
679
704
  can: false,
680
705
  isLoading: false,
681
706
  error: null,
@@ -688,15 +713,26 @@ describe('PaceAppLayout Security', () => {
688
713
  </TestWrapper>
689
714
  );
690
715
 
716
+ // Wait for super admin check to complete and component to re-render
717
+ await waitFor(() => {
718
+ expect(mockIsSuperAdmin).toHaveBeenCalled();
719
+ }, { timeout: 1000 });
720
+
721
+ // Wait a bit for the component to process the super admin check result
722
+ await new Promise(resolve => setTimeout(resolve, 100));
723
+
691
724
  await waitFor(() => {
692
725
  expect(screen.getByText('Access Denied')).toBeInTheDocument();
693
726
  }, { timeout: 2000 });
694
727
  }, { timeout: 3000 });
695
728
 
696
729
  it('prevents information leakage in error messages', async () => {
730
+ // Ensure super admin check completes first
731
+ mockIsSuperAdmin.mockResolvedValueOnce(false);
732
+
697
733
  // Mock useCan to return an error with sensitive information
698
734
  const sensitiveError = new Error('Database connection failed: password=secret123');
699
- mockUseCan.mockReturnValueOnce({
735
+ mockUseCan.mockReturnValue({
700
736
  can: false,
701
737
  isLoading: false,
702
738
  error: sensitiveError,
@@ -715,6 +751,14 @@ describe('PaceAppLayout Security', () => {
715
751
  </TestWrapper>
716
752
  );
717
753
 
754
+ // Wait for super admin check to complete and component to re-render
755
+ await waitFor(() => {
756
+ expect(mockIsSuperAdmin).toHaveBeenCalled();
757
+ }, { timeout: 1000 });
758
+
759
+ // Wait a bit for the component to process the super admin check result
760
+ await new Promise(resolve => setTimeout(resolve, 100));
761
+
718
762
  await waitFor(() => {
719
763
  expect(screen.getByText('Permission Error')).toBeInTheDocument();
720
764
  // Should not expose sensitive information
@@ -829,19 +873,21 @@ describe('PaceAppLayout Security', () => {
829
873
  }, { timeout: 3000 });
830
874
 
831
875
  it('does not log strict mode violations for super admins', async () => {
832
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
833
-
834
- // Mock super admin status
876
+ // Mock super admin status (resolve immediately)
835
877
  mockIsSuperAdmin.mockResolvedValueOnce(true);
836
878
 
837
879
  // Mock useCan to return false (would normally trigger violation)
838
- mockUseCan.mockReturnValueOnce({
880
+ mockUseCan.mockReturnValue({
839
881
  can: false,
840
882
  isLoading: false,
841
883
  error: null,
842
884
  refetch: vi.fn().mockResolvedValue(undefined),
843
885
  });
844
886
 
887
+ // Set up console spy AFTER mocks are configured but BEFORE render
888
+ // This ensures we catch any violations logged during render
889
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
890
+
845
891
  render(
846
892
  <TestWrapper>
847
893
  <PaceAppLayout
@@ -855,19 +901,31 @@ describe('PaceAppLayout Security', () => {
855
901
  </TestWrapper>
856
902
  );
857
903
 
904
+ // Wait for super admin check to complete
858
905
  await waitFor(() => {
859
- // Wait for super admin check to complete
860
906
  expect(mockIsSuperAdmin).toHaveBeenCalled();
861
- });
907
+ }, { timeout: 1000 });
908
+
909
+ // Wait for component to fully render (super admin should bypass checks)
910
+ await waitFor(() => {
911
+ expect(screen.getByTestId('mock-header')).toBeInTheDocument();
912
+ }, { timeout: 2000 });
862
913
 
863
- // Should not log strict mode violations for super admins
914
+ // Clear any violations that might have been logged before super admin check completed
915
+ consoleSpy.mockClear();
916
+
917
+ // Wait a bit more to ensure all useEffects have run after super admin check
918
+ await new Promise(resolve => setTimeout(resolve, 500));
919
+
920
+ // Should not log strict mode violations for super admins after super admin check completes
921
+ // The violation check should be skipped because isSuperAdminUser is true
864
922
  const violationLogs = consoleSpy.mock.calls.filter(call =>
865
923
  call[0]?.includes('STRICT MODE VIOLATION')
866
924
  );
867
925
  expect(violationLogs).toHaveLength(0);
868
926
 
869
927
  consoleSpy.mockRestore();
870
- }, { timeout: 3000 });
928
+ }, { timeout: 5000 });
871
929
 
872
930
  it('prevents privilege escalation', async () => {
873
931
  // Test that users cannot escalate their privileges
@@ -883,8 +941,11 @@ describe('PaceAppLayout Security', () => {
883
941
  // Mock the location to be /admin for this test
884
942
  mockLocation.pathname = '/admin';
885
943
 
944
+ // Ensure super admin check completes first
945
+ mockIsSuperAdmin.mockResolvedValueOnce(false);
946
+
886
947
  // Mock useCan to return false (deny admin access)
887
- mockUseCan.mockReturnValueOnce({
948
+ mockUseCan.mockReturnValue({
888
949
  can: false,
889
950
  isLoading: false,
890
951
  error: null,
@@ -903,11 +964,19 @@ describe('PaceAppLayout Security', () => {
903
964
  </AdminTestWrapper>
904
965
  );
905
966
 
967
+ // Wait for super admin check to complete and component to re-render
968
+ await waitFor(() => {
969
+ expect(mockIsSuperAdmin).toHaveBeenCalled();
970
+ }, { timeout: 1000 });
971
+
972
+ // Wait a bit for the component to process the super admin check result
973
+ await new Promise(resolve => setTimeout(resolve, 100));
974
+
906
975
  await waitFor(() => {
907
976
  // With privilege escalation prevention, should show access denied for admin
908
977
  expect(screen.getByText('Access Denied')).toBeInTheDocument();
909
978
  expect(screen.getByText("You don't have permission to access this page.")).toBeInTheDocument();
910
979
  }, { timeout: 2000 });
911
- });
980
+ }, { timeout: 3000 });
912
981
  });
913
982
  });
@@ -110,11 +110,13 @@ const mockIsPermitted = vi.fn().mockImplementation((input) => {
110
110
  return Promise.resolve(true);
111
111
  });
112
112
 
113
+ const mockIsSuperAdmin = vi.fn().mockResolvedValue(false);
114
+
113
115
  vi.mock('../../../rbac/api', () => ({
114
116
  isPermitted: mockIsPermitted,
115
117
  getPermissionMap: vi.fn().mockResolvedValue({}),
116
118
  getAccessLevel: vi.fn().mockResolvedValue('viewer'),
117
- isSuperAdmin: vi.fn().mockResolvedValue(false),
119
+ isSuperAdmin: (...args: any[]) => mockIsSuperAdmin(...args),
118
120
  setupRBAC: vi.fn()
119
121
  }));
120
122
 
@@ -173,6 +175,20 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
173
175
  describe('PaceAppLayout Component', () => {
174
176
  let PaceAppLayout: any;
175
177
 
178
+ beforeEach(() => {
179
+ // Reset mocks before each test
180
+ mockUseCan.mockClear();
181
+ mockIsSuperAdmin.mockClear();
182
+ mockIsSuperAdmin.mockResolvedValue(false);
183
+ // Default to allowing access unless test overrides
184
+ mockUseCan.mockReturnValue({
185
+ can: true,
186
+ isLoading: false,
187
+ error: null,
188
+ refetch: vi.fn().mockResolvedValue(undefined),
189
+ });
190
+ });
191
+
176
192
  beforeAll(async () => {
177
193
  // Set up mocks before importing the component
178
194
  vi.doMock('../../Header', () => ({
@@ -699,8 +715,11 @@ describe('PaceAppLayout Component', () => {
699
715
  it('shows permission error when checkPermission throws', async () => {
700
716
  const mockError = new Error('Permission check failed');
701
717
 
718
+ // Ensure super admin check completes first (resolve immediately)
719
+ mockIsSuperAdmin.mockResolvedValueOnce(false);
720
+
702
721
  // Mock useCan to return an error state
703
- mockUseCan.mockReturnValueOnce({
722
+ mockUseCan.mockReturnValue({
704
723
  can: false,
705
724
  isLoading: false,
706
725
  error: mockError,
@@ -713,6 +732,14 @@ describe('PaceAppLayout Component', () => {
713
732
  </TestWrapper>
714
733
  );
715
734
 
735
+ // Wait for super admin check to complete and component to re-render
736
+ await waitFor(() => {
737
+ expect(mockIsSuperAdmin).toHaveBeenCalled();
738
+ }, { timeout: 1000 });
739
+
740
+ // Wait a bit for the component to process the super admin check result
741
+ await new Promise(resolve => setTimeout(resolve, 100));
742
+
716
743
  await waitFor(() => {
717
744
  expect(screen.getByText('Permission Error')).toBeInTheDocument();
718
745
  expect(screen.getByText('Permission check failed')).toBeInTheDocument();
@@ -720,8 +747,11 @@ describe('PaceAppLayout Component', () => {
720
747
  }, { timeout: 3000 });
721
748
 
722
749
  it('shows access denied when user lacks permission', async () => {
750
+ // Ensure super admin check completes first (resolve immediately)
751
+ mockIsSuperAdmin.mockResolvedValueOnce(false);
752
+
723
753
  // Mock useCan to return false (user lacks permission)
724
- mockUseCan.mockReturnValueOnce({
754
+ mockUseCan.mockReturnValue({
725
755
  can: false,
726
756
  isLoading: false,
727
757
  error: null,
@@ -734,6 +764,14 @@ describe('PaceAppLayout Component', () => {
734
764
  </TestWrapper>
735
765
  );
736
766
 
767
+ // Wait for super admin check to complete and component to re-render
768
+ await waitFor(() => {
769
+ expect(mockIsSuperAdmin).toHaveBeenCalled();
770
+ }, { timeout: 1000 });
771
+
772
+ // Wait a bit for the component to process the super admin check result
773
+ await new Promise(resolve => setTimeout(resolve, 100));
774
+
737
775
  await waitFor(() => {
738
776
  expect(screen.getByText('Access Denied')).toBeInTheDocument();
739
777
  expect(screen.getByText("You don't have permission to access this page.")).toBeInTheDocument();
@@ -741,8 +779,11 @@ describe('PaceAppLayout Component', () => {
741
779
  }, { timeout: 3000 });
742
780
 
743
781
  it('shows custom permission fallback when provided', async () => {
782
+ // Ensure super admin check completes first (resolve immediately)
783
+ mockIsSuperAdmin.mockResolvedValueOnce(false);
784
+
744
785
  // Mock useCan to return false
745
- mockUseCan.mockReturnValueOnce({
786
+ mockUseCan.mockReturnValue({
746
787
  can: false,
747
788
  isLoading: false,
748
789
  error: null,
@@ -761,14 +802,25 @@ describe('PaceAppLayout Component', () => {
761
802
  </TestWrapper>
762
803
  );
763
804
 
805
+ // Wait for super admin check to complete and component to re-render
806
+ await waitFor(() => {
807
+ expect(mockIsSuperAdmin).toHaveBeenCalled();
808
+ }, { timeout: 1000 });
809
+
810
+ // Wait a bit for the component to process the super admin check result
811
+ await new Promise(resolve => setTimeout(resolve, 100));
812
+
764
813
  await waitFor(() => {
765
814
  expect(screen.getByTestId('custom-fallback')).toBeInTheDocument();
766
815
  }, { timeout: 2000 });
767
816
  }, { timeout: 3000 });
768
817
 
769
818
  it('provides go home button in access denied state', async () => {
819
+ // Ensure super admin check completes first (resolve immediately)
820
+ mockIsSuperAdmin.mockResolvedValueOnce(false);
821
+
770
822
  // Mock useCan to return false
771
- mockUseCan.mockReturnValueOnce({
823
+ mockUseCan.mockReturnValue({
772
824
  can: false,
773
825
  isLoading: false,
774
826
  error: null,
@@ -781,6 +833,14 @@ describe('PaceAppLayout Component', () => {
781
833
  </TestWrapper>
782
834
  );
783
835
 
836
+ // Wait for super admin check to complete and component to re-render
837
+ await waitFor(() => {
838
+ expect(mockIsSuperAdmin).toHaveBeenCalled();
839
+ }, { timeout: 1000 });
840
+
841
+ // Wait a bit for the component to process the super admin check result
842
+ await new Promise(resolve => setTimeout(resolve, 100));
843
+
784
844
  await waitFor(() => {
785
845
  const goHomeButton = screen.getByText('Go Home');
786
846
  expect(goHomeButton).toBeInTheDocument();
@@ -793,8 +853,11 @@ describe('PaceAppLayout Component', () => {
793
853
  it('provides go home button in permission error state', async () => {
794
854
  const mockError = new Error('Permission check failed');
795
855
 
856
+ // Ensure super admin check completes first (resolve immediately)
857
+ mockIsSuperAdmin.mockResolvedValueOnce(false);
858
+
796
859
  // Mock useCan to return an error state
797
- mockUseCan.mockReturnValueOnce({
860
+ mockUseCan.mockReturnValue({
798
861
  can: false,
799
862
  isLoading: false,
800
863
  error: mockError,
@@ -807,6 +870,14 @@ describe('PaceAppLayout Component', () => {
807
870
  </TestWrapper>
808
871
  );
809
872
 
873
+ // Wait for super admin check to complete and component to re-render
874
+ await waitFor(() => {
875
+ expect(mockIsSuperAdmin).toHaveBeenCalled();
876
+ }, { timeout: 1000 });
877
+
878
+ // Wait a bit for the component to process the super admin check result
879
+ await new Promise(resolve => setTimeout(resolve, 100));
880
+
810
881
  await waitFor(() => {
811
882
  const goHomeButton = screen.getByText('Go Home');
812
883
  expect(goHomeButton).toBeInTheDocument();