@jmruthers/pace-core 0.5.118 → 0.5.120
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DataTable-ZOAKQ3SU.js → DataTable-DGZDJUYM.js} +7 -7
- package/dist/{UnifiedAuthProvider-YFN7YGVN.js → UnifiedAuthProvider-UACKFATV.js} +3 -3
- package/dist/{chunk-7OTQLFVI.js → chunk-B4GZ2BXO.js} +3 -3
- package/dist/{chunk-KA3PSVNV.js → chunk-BHWIUEYH.js} +2 -1
- package/dist/chunk-BHWIUEYH.js.map +1 -0
- package/dist/{chunk-LFS45U62.js → chunk-CGURJ27Z.js} +2 -2
- package/dist/{chunk-PHDAXDHB.js → chunk-D6BOFXYR.js} +3 -3
- package/dist/{chunk-P3PUOL6B.js → chunk-FKFHZUGF.js} +4 -4
- package/dist/{chunk-2GJ5GL77.js → chunk-GKHF54DI.js} +2 -2
- package/dist/chunk-GKHF54DI.js.map +1 -0
- package/dist/{chunk-UKZWNQMB.js → chunk-HFBOFZ3Z.js} +5 -18
- package/dist/chunk-HFBOFZ3Z.js.map +1 -0
- package/dist/{chunk-O3FTRYEU.js → chunk-NZ32EONV.js} +2 -2
- package/dist/{chunk-2LM4QQGH.js → chunk-QPI2CCBA.js} +9 -9
- package/dist/chunk-QPI2CCBA.js.map +1 -0
- package/dist/{chunk-ECOVPXYS.js → chunk-RIEJGKD3.js} +4 -4
- package/dist/{chunk-HIWXXDXO.js → chunk-TDNI6ZWL.js} +5 -5
- package/dist/{chunk-VN3OOE35.js → chunk-ZYJ6O5CA.js} +2 -2
- package/dist/components.d.ts +1 -1
- package/dist/components.js +9 -9
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +8 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -12
- package/dist/providers.js +2 -2
- package/dist/rbac/index.js +7 -7
- package/dist/{useToast-Cs_g32bg.d.ts → useToast-C8gR5ir4.d.ts} +2 -2
- 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 +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +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/EventAppRoleData.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/GrantEventAppRoleParams.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 +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/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/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +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 +2 -2
- package/package.json +1 -1
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +697 -0
- package/src/components/DataTable/components/DataTableCore.tsx +5 -0
- package/src/components/DataTable/components/EditableRow.tsx +9 -18
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +616 -9
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +1004 -0
- package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +612 -0
- package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +266 -0
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +455 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/hooks/__tests__/index.unit.test.ts +223 -0
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +748 -0
- package/src/hooks/__tests__/useEvents.unit.test.ts +251 -0
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +1060 -0
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +958 -0
- package/src/hooks/__tests__/useFocusManagement.unit.test.ts +19 -9
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +540 -1
- package/src/hooks/__tests__/useIsMobile.unit.test.ts +205 -5
- package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +616 -1
- package/src/hooks/__tests__/useOrganisations.unit.test.ts +369 -0
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +661 -0
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +2 -0
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +371 -0
- package/src/hooks/__tests__/useToast.unit.test.tsx +449 -30
- package/src/hooks/useSecureDataAccess.test.ts +1 -0
- package/src/hooks/useToast.ts +4 -4
- package/src/rbac/audit-enhanced.ts +339 -0
- package/src/services/EventService.ts +1 -0
- package/src/services/__tests__/AuthService.test.ts +473 -0
- package/src/services/__tests__/EventService.test.ts +390 -0
- package/src/services/__tests__/InactivityService.test.ts +217 -0
- package/src/services/__tests__/OrganisationService.test.ts +371 -0
- package/src/styles/core.css +1 -0
- package/dist/chunk-2GJ5GL77.js.map +0 -1
- package/dist/chunk-2LM4QQGH.js.map +0 -1
- package/dist/chunk-KA3PSVNV.js.map +0 -1
- package/dist/chunk-UKZWNQMB.js.map +0 -1
- package/src/components/DataTable/utils/debugTools.ts +0 -609
- package/src/rbac/testing/index.tsx +0 -340
- /package/dist/{DataTable-ZOAKQ3SU.js.map → DataTable-DGZDJUYM.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-YFN7YGVN.js.map → UnifiedAuthProvider-UACKFATV.js.map} +0 -0
- /package/dist/{chunk-7OTQLFVI.js.map → chunk-B4GZ2BXO.js.map} +0 -0
- /package/dist/{chunk-LFS45U62.js.map → chunk-CGURJ27Z.js.map} +0 -0
- /package/dist/{chunk-PHDAXDHB.js.map → chunk-D6BOFXYR.js.map} +0 -0
- /package/dist/{chunk-P3PUOL6B.js.map → chunk-FKFHZUGF.js.map} +0 -0
- /package/dist/{chunk-O3FTRYEU.js.map → chunk-NZ32EONV.js.map} +0 -0
- /package/dist/{chunk-ECOVPXYS.js.map → chunk-RIEJGKD3.js.map} +0 -0
- /package/dist/{chunk-HIWXXDXO.js.map → chunk-TDNI6ZWL.js.map} +0 -0
- /package/dist/{chunk-VN3OOE35.js.map → chunk-ZYJ6O5CA.js.map} +0 -0
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
9
|
-
import { generateCSVContent, exportToCSV } from '../exportUtils';
|
|
9
|
+
import { generateCSVContent, exportToCSV, exportToCSVWithTableRows } from '../exportUtils';
|
|
10
|
+
import type { ExportColumn } from '../exportUtils';
|
|
10
11
|
|
|
11
12
|
// Mock DOM methods
|
|
12
13
|
const mockCreateElement = vi.fn();
|
|
@@ -486,3 +487,456 @@ describe('[unit] CSV Escaping and Security', () => {
|
|
|
486
487
|
expect(sanitized).toContain("'=FORMULA");
|
|
487
488
|
});
|
|
488
489
|
});
|
|
490
|
+
|
|
491
|
+
describe('[unit] exportToCSVWithTableRows', () => {
|
|
492
|
+
let mockLink: any;
|
|
493
|
+
let mockColumnIdToTableColumn: Map<string, any>;
|
|
494
|
+
|
|
495
|
+
beforeEach(() => {
|
|
496
|
+
vi.clearAllMocks();
|
|
497
|
+
vi.useFakeTimers();
|
|
498
|
+
|
|
499
|
+
mockLink = {
|
|
500
|
+
setAttribute: vi.fn(),
|
|
501
|
+
style: { display: 'none' },
|
|
502
|
+
click: vi.fn(() => {
|
|
503
|
+
// Simulate onclick being called after click
|
|
504
|
+
if (mockLink.onclick) {
|
|
505
|
+
mockLink.onclick();
|
|
506
|
+
}
|
|
507
|
+
}),
|
|
508
|
+
onclick: null as any,
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
mockCreateElement.mockReturnValue(mockLink);
|
|
512
|
+
mockCreateObjectURL.mockReturnValue('blob:mock-url');
|
|
513
|
+
mockColumnIdToTableColumn = new Map();
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
afterEach(() => {
|
|
517
|
+
vi.useRealTimers();
|
|
518
|
+
vi.clearAllMocks();
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
const createMockTableRow = (original: any, getValue: (columnId: string) => any = () => undefined) => ({
|
|
522
|
+
original,
|
|
523
|
+
getValue,
|
|
524
|
+
id: `row-${original.id}`,
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
it('exports CSV with table rows using getValue()', async () => {
|
|
528
|
+
const tableRows = [
|
|
529
|
+
createMockTableRow(
|
|
530
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com' },
|
|
531
|
+
(columnId) => {
|
|
532
|
+
if (columnId === 'name') return 'John Doe';
|
|
533
|
+
if (columnId === 'email') return 'john@example.com';
|
|
534
|
+
return undefined;
|
|
535
|
+
}
|
|
536
|
+
),
|
|
537
|
+
];
|
|
538
|
+
|
|
539
|
+
const columns: ExportColumn[] = [
|
|
540
|
+
{ id: 'name', header: 'Name', accessorKey: 'name' },
|
|
541
|
+
{ id: 'email', header: 'Email', accessorKey: 'email' },
|
|
542
|
+
];
|
|
543
|
+
|
|
544
|
+
mockColumnIdToTableColumn.set('name', {});
|
|
545
|
+
mockColumnIdToTableColumn.set('email', {});
|
|
546
|
+
|
|
547
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn);
|
|
548
|
+
|
|
549
|
+
// Advance timers to trigger onclick setTimeout
|
|
550
|
+
vi.advanceTimersByTime(100);
|
|
551
|
+
|
|
552
|
+
await promise;
|
|
553
|
+
|
|
554
|
+
const blobCall = mockCreateObjectURL.mock.calls[0][0];
|
|
555
|
+
const csvContent = blobCall.content[0];
|
|
556
|
+
|
|
557
|
+
expect(csvContent).toContain('"Name","Email"');
|
|
558
|
+
expect(csvContent).toContain('"John Doe","john@example.com"');
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('handles ID columns by getting directly from original data', async () => {
|
|
562
|
+
const tableRows = [
|
|
563
|
+
createMockTableRow(
|
|
564
|
+
{ id: 1, name: 'John Doe', referenceId: 'ref-123' },
|
|
565
|
+
(columnId) => {
|
|
566
|
+
if (columnId === 'name') return 'John Doe';
|
|
567
|
+
return undefined;
|
|
568
|
+
}
|
|
569
|
+
),
|
|
570
|
+
];
|
|
571
|
+
|
|
572
|
+
const columns: ExportColumn[] = [
|
|
573
|
+
{ id: 'name', header: 'Name', accessorKey: 'name' },
|
|
574
|
+
{ id: 'referenceId', header: 'Reference ID', accessorKey: 'referenceId', isIdColumn: true },
|
|
575
|
+
];
|
|
576
|
+
|
|
577
|
+
mockColumnIdToTableColumn.set('name', {});
|
|
578
|
+
|
|
579
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn);
|
|
580
|
+
|
|
581
|
+
// Advance timers to trigger onclick setTimeout
|
|
582
|
+
vi.advanceTimersByTime(100);
|
|
583
|
+
|
|
584
|
+
await promise;
|
|
585
|
+
|
|
586
|
+
const blobCall = mockCreateObjectURL.mock.calls[0][0];
|
|
587
|
+
const csvContent = blobCall.content[0];
|
|
588
|
+
|
|
589
|
+
expect(csvContent).toContain('"Reference ID"');
|
|
590
|
+
expect(csvContent).toContain('"ref-123"');
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
it('falls back to accessorFn when getValue() fails', async () => {
|
|
594
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
595
|
+
|
|
596
|
+
const tableRows = [
|
|
597
|
+
createMockTableRow(
|
|
598
|
+
{ id: 1, name: 'John Doe' },
|
|
599
|
+
() => {
|
|
600
|
+
throw new Error('getValue failed');
|
|
601
|
+
}
|
|
602
|
+
),
|
|
603
|
+
];
|
|
604
|
+
|
|
605
|
+
const columns: ExportColumn[] = [
|
|
606
|
+
{
|
|
607
|
+
id: 'name',
|
|
608
|
+
header: 'Name',
|
|
609
|
+
accessorKey: 'name',
|
|
610
|
+
accessorFn: (row: any) => row.name.toUpperCase(),
|
|
611
|
+
},
|
|
612
|
+
];
|
|
613
|
+
|
|
614
|
+
mockColumnIdToTableColumn.set('name', {});
|
|
615
|
+
|
|
616
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn);
|
|
617
|
+
|
|
618
|
+
// Advance timers to trigger onclick setTimeout
|
|
619
|
+
vi.advanceTimersByTime(100);
|
|
620
|
+
|
|
621
|
+
await promise;
|
|
622
|
+
|
|
623
|
+
const blobCall = mockCreateObjectURL.mock.calls[0][0];
|
|
624
|
+
const csvContent = blobCall.content[0];
|
|
625
|
+
|
|
626
|
+
expect(csvContent).toContain('"JOHN DOE"');
|
|
627
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
628
|
+
|
|
629
|
+
consoleWarnSpy.mockRestore();
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it('falls back to direct property access when getValue() fails and no accessorFn', async () => {
|
|
633
|
+
const tableRows = [
|
|
634
|
+
createMockTableRow(
|
|
635
|
+
{ id: 1, name: 'John Doe' },
|
|
636
|
+
() => {
|
|
637
|
+
throw new Error('getValue failed');
|
|
638
|
+
}
|
|
639
|
+
),
|
|
640
|
+
];
|
|
641
|
+
|
|
642
|
+
const columns: ExportColumn[] = [
|
|
643
|
+
{ id: 'name', header: 'Name', accessorKey: 'name' },
|
|
644
|
+
];
|
|
645
|
+
|
|
646
|
+
mockColumnIdToTableColumn.set('name', {});
|
|
647
|
+
|
|
648
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn);
|
|
649
|
+
|
|
650
|
+
// Advance timers to trigger onclick setTimeout
|
|
651
|
+
vi.advanceTimersByTime(100);
|
|
652
|
+
|
|
653
|
+
await promise;
|
|
654
|
+
|
|
655
|
+
const blobCall = mockCreateObjectURL.mock.calls[0][0];
|
|
656
|
+
const csvContent = blobCall.content[0];
|
|
657
|
+
|
|
658
|
+
expect(csvContent).toContain('"John Doe"');
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it('handles accessorFn error gracefully', async () => {
|
|
662
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
663
|
+
|
|
664
|
+
const tableRows = [
|
|
665
|
+
createMockTableRow(
|
|
666
|
+
{ id: 1, name: 'John Doe' },
|
|
667
|
+
() => {
|
|
668
|
+
throw new Error('getValue failed');
|
|
669
|
+
}
|
|
670
|
+
),
|
|
671
|
+
];
|
|
672
|
+
|
|
673
|
+
const columns: ExportColumn[] = [
|
|
674
|
+
{
|
|
675
|
+
id: 'name',
|
|
676
|
+
header: 'Name',
|
|
677
|
+
accessorKey: 'name',
|
|
678
|
+
accessorFn: () => {
|
|
679
|
+
throw new Error('accessorFn failed');
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
];
|
|
683
|
+
|
|
684
|
+
mockColumnIdToTableColumn.set('name', {});
|
|
685
|
+
|
|
686
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn);
|
|
687
|
+
|
|
688
|
+
// Advance timers to trigger onclick setTimeout
|
|
689
|
+
vi.advanceTimersByTime(100);
|
|
690
|
+
|
|
691
|
+
await promise;
|
|
692
|
+
|
|
693
|
+
const blobCall = mockCreateObjectURL.mock.calls[0][0];
|
|
694
|
+
const csvContent = blobCall.content[0];
|
|
695
|
+
|
|
696
|
+
// Should export empty value for failed accessorFn
|
|
697
|
+
expect(csvContent).toContain('"Name"');
|
|
698
|
+
expect(consoleWarnSpy).toHaveBeenCalled();
|
|
699
|
+
|
|
700
|
+
consoleWarnSpy.mockRestore();
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it('falls back to direct property access when column not in columnIdToTableColumn', async () => {
|
|
704
|
+
const tableRows = [
|
|
705
|
+
createMockTableRow({ id: 1, name: 'John Doe' }),
|
|
706
|
+
];
|
|
707
|
+
|
|
708
|
+
const columns: ExportColumn[] = [
|
|
709
|
+
{ id: 'name', header: 'Name', accessorKey: 'name' },
|
|
710
|
+
];
|
|
711
|
+
|
|
712
|
+
// Don't add to columnIdToTableColumn
|
|
713
|
+
|
|
714
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn);
|
|
715
|
+
|
|
716
|
+
// Advance timers to trigger onclick setTimeout
|
|
717
|
+
vi.advanceTimersByTime(100);
|
|
718
|
+
|
|
719
|
+
await promise;
|
|
720
|
+
|
|
721
|
+
const blobCall = mockCreateObjectURL.mock.calls[0][0];
|
|
722
|
+
const csvContent = blobCall.content[0];
|
|
723
|
+
|
|
724
|
+
expect(csvContent).toContain('"John Doe"');
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it('formats values according to locale', async () => {
|
|
728
|
+
const tableRows = [
|
|
729
|
+
createMockTableRow(
|
|
730
|
+
{ id: 1, value: 1234.56, date: new Date('2025-01-15'), active: true },
|
|
731
|
+
(columnId) => {
|
|
732
|
+
if (columnId === 'value') return 1234.56;
|
|
733
|
+
if (columnId === 'date') return new Date('2025-01-15');
|
|
734
|
+
if (columnId === 'active') return true;
|
|
735
|
+
return undefined;
|
|
736
|
+
}
|
|
737
|
+
),
|
|
738
|
+
];
|
|
739
|
+
|
|
740
|
+
const columns: ExportColumn[] = [
|
|
741
|
+
{ id: 'value', header: 'Value', accessorKey: 'value' },
|
|
742
|
+
{ id: 'date', header: 'Date', accessorKey: 'date' },
|
|
743
|
+
{ id: 'active', header: 'Active', accessorKey: 'active' },
|
|
744
|
+
];
|
|
745
|
+
|
|
746
|
+
mockColumnIdToTableColumn.set('value', {});
|
|
747
|
+
mockColumnIdToTableColumn.set('date', {});
|
|
748
|
+
mockColumnIdToTableColumn.set('active', {});
|
|
749
|
+
|
|
750
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn, 'test.csv', {
|
|
751
|
+
locale: 'en-US',
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
// Advance timers to trigger onclick setTimeout
|
|
755
|
+
vi.advanceTimersByTime(100);
|
|
756
|
+
|
|
757
|
+
await promise;
|
|
758
|
+
|
|
759
|
+
const blobCall = mockCreateObjectURL.mock.calls[0][0];
|
|
760
|
+
const csvContent = blobCall.content[0];
|
|
761
|
+
|
|
762
|
+
// Locale formatting should be applied
|
|
763
|
+
expect(csvContent).toBeTruthy();
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
it('sanitizes values for security by default', async () => {
|
|
767
|
+
const tableRows = [
|
|
768
|
+
createMockTableRow(
|
|
769
|
+
{ id: 1, name: '=SUM(A1:A10)' },
|
|
770
|
+
(columnId) => {
|
|
771
|
+
if (columnId === 'name') return '=SUM(A1:A10)';
|
|
772
|
+
return undefined;
|
|
773
|
+
}
|
|
774
|
+
),
|
|
775
|
+
];
|
|
776
|
+
|
|
777
|
+
const columns: ExportColumn[] = [
|
|
778
|
+
{ id: 'name', header: 'Name', accessorKey: 'name' },
|
|
779
|
+
];
|
|
780
|
+
|
|
781
|
+
mockColumnIdToTableColumn.set('name', {});
|
|
782
|
+
|
|
783
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn);
|
|
784
|
+
|
|
785
|
+
// Advance timers to trigger onclick setTimeout
|
|
786
|
+
vi.advanceTimersByTime(100);
|
|
787
|
+
|
|
788
|
+
await promise;
|
|
789
|
+
|
|
790
|
+
const blobCall = mockCreateObjectURL.mock.calls[0][0];
|
|
791
|
+
const csvContent = blobCall.content[0];
|
|
792
|
+
|
|
793
|
+
// Should sanitize CSV injection attempts
|
|
794
|
+
expect(csvContent).toContain("'=SUM");
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
it('allows disabling security sanitization', async () => {
|
|
798
|
+
const tableRows = [
|
|
799
|
+
createMockTableRow(
|
|
800
|
+
{ id: 1, name: '=SUM(A1:A10)' },
|
|
801
|
+
(columnId) => {
|
|
802
|
+
if (columnId === 'name') return '=SUM(A1:A10)';
|
|
803
|
+
return undefined;
|
|
804
|
+
}
|
|
805
|
+
),
|
|
806
|
+
];
|
|
807
|
+
|
|
808
|
+
const columns: ExportColumn[] = [
|
|
809
|
+
{ id: 'name', header: 'Name', accessorKey: 'name' },
|
|
810
|
+
];
|
|
811
|
+
|
|
812
|
+
mockColumnIdToTableColumn.set('name', {});
|
|
813
|
+
|
|
814
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn, 'test.csv', {
|
|
815
|
+
sanitizeForSecurity: false,
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
// Advance timers to trigger onclick setTimeout
|
|
819
|
+
vi.advanceTimersByTime(100);
|
|
820
|
+
|
|
821
|
+
await promise;
|
|
822
|
+
|
|
823
|
+
const blobCall = mockCreateObjectURL.mock.calls[0][0];
|
|
824
|
+
const csvContent = blobCall.content[0];
|
|
825
|
+
|
|
826
|
+
// Should still quote but not prefix with single quote
|
|
827
|
+
expect(csvContent).toContain('"=SUM');
|
|
828
|
+
expect(csvContent).not.toContain("'=SUM");
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
it('throws error when window is undefined (SSR)', async () => {
|
|
832
|
+
const originalWindow = global.window;
|
|
833
|
+
// @ts-expect-error - Testing SSR scenario
|
|
834
|
+
delete global.window;
|
|
835
|
+
|
|
836
|
+
const tableRows = [createMockTableRow({ id: 1 })];
|
|
837
|
+
const columns: ExportColumn[] = [{ id: 'id', header: 'ID', accessorKey: 'id' }];
|
|
838
|
+
|
|
839
|
+
await expect(
|
|
840
|
+
exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn)
|
|
841
|
+
).rejects.toThrow('CSV export is only available in browser environments');
|
|
842
|
+
|
|
843
|
+
global.window = originalWindow;
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
it('throws error when tableRows is empty', async () => {
|
|
847
|
+
const columns: ExportColumn[] = [{ id: 'id', header: 'ID', accessorKey: 'id' }];
|
|
848
|
+
|
|
849
|
+
await expect(
|
|
850
|
+
exportToCSVWithTableRows([], columns, mockColumnIdToTableColumn)
|
|
851
|
+
).rejects.toThrow('No data to export');
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
it('throws error when columns is empty', async () => {
|
|
855
|
+
const tableRows = [createMockTableRow({ id: 1 })];
|
|
856
|
+
|
|
857
|
+
await expect(
|
|
858
|
+
exportToCSVWithTableRows(tableRows, [], mockColumnIdToTableColumn)
|
|
859
|
+
).rejects.toThrow('No columns defined for export');
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
it('handles multiple rows correctly', async () => {
|
|
863
|
+
const tableRows = [
|
|
864
|
+
createMockTableRow(
|
|
865
|
+
{ id: 1, name: 'John Doe' },
|
|
866
|
+
(columnId) => columnId === 'name' ? 'John Doe' : undefined
|
|
867
|
+
),
|
|
868
|
+
createMockTableRow(
|
|
869
|
+
{ id: 2, name: 'Jane Smith' },
|
|
870
|
+
(columnId) => columnId === 'name' ? 'Jane Smith' : undefined
|
|
871
|
+
),
|
|
872
|
+
];
|
|
873
|
+
|
|
874
|
+
const columns: ExportColumn[] = [
|
|
875
|
+
{ id: 'name', header: 'Name', accessorKey: 'name' },
|
|
876
|
+
];
|
|
877
|
+
|
|
878
|
+
mockColumnIdToTableColumn.set('name', {});
|
|
879
|
+
|
|
880
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn);
|
|
881
|
+
|
|
882
|
+
// Advance timers to trigger onclick setTimeout
|
|
883
|
+
vi.advanceTimersByTime(100);
|
|
884
|
+
|
|
885
|
+
await promise;
|
|
886
|
+
|
|
887
|
+
const blobCall = mockCreateObjectURL.mock.calls[0][0];
|
|
888
|
+
const csvContent = blobCall.content[0];
|
|
889
|
+
|
|
890
|
+
expect(csvContent).toContain('"John Doe"');
|
|
891
|
+
expect(csvContent).toContain('"Jane Smith"');
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
it('uses custom filename when provided', async () => {
|
|
895
|
+
const tableRows = [createMockTableRow({ id: 1 })];
|
|
896
|
+
const columns: ExportColumn[] = [{ id: 'id', header: 'ID', accessorKey: 'id' }];
|
|
897
|
+
|
|
898
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn, 'custom-export.csv');
|
|
899
|
+
|
|
900
|
+
// Advance timers to trigger onclick setTimeout
|
|
901
|
+
vi.advanceTimersByTime(100);
|
|
902
|
+
|
|
903
|
+
await promise;
|
|
904
|
+
|
|
905
|
+
expect(mockLink.setAttribute).toHaveBeenCalledWith('download', 'custom-export.csv');
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
it('handles columns with computed values via accessorFn in getValue()', async () => {
|
|
909
|
+
const tableRows = [
|
|
910
|
+
createMockTableRow(
|
|
911
|
+
{ id: 1, firstName: 'John', lastName: 'Doe' },
|
|
912
|
+
(columnId) => {
|
|
913
|
+
if (columnId === 'fullName') return 'John Doe';
|
|
914
|
+
return undefined;
|
|
915
|
+
}
|
|
916
|
+
),
|
|
917
|
+
];
|
|
918
|
+
|
|
919
|
+
const columns: ExportColumn[] = [
|
|
920
|
+
{
|
|
921
|
+
id: 'fullName',
|
|
922
|
+
header: 'Full Name',
|
|
923
|
+
accessorFn: (row: any) => `${row.firstName} ${row.lastName}`,
|
|
924
|
+
},
|
|
925
|
+
];
|
|
926
|
+
|
|
927
|
+
mockColumnIdToTableColumn.set('fullName', {});
|
|
928
|
+
|
|
929
|
+
const promise = exportToCSVWithTableRows(tableRows, columns, mockColumnIdToTableColumn);
|
|
930
|
+
|
|
931
|
+
// Advance timers to trigger onclick setTimeout
|
|
932
|
+
vi.advanceTimersByTime(100);
|
|
933
|
+
|
|
934
|
+
await promise;
|
|
935
|
+
|
|
936
|
+
const blobCall = mockCreateObjectURL.mock.calls[0][0];
|
|
937
|
+
const csvContent = blobCall.content[0];
|
|
938
|
+
|
|
939
|
+
expect(csvContent).toContain('"Full Name"');
|
|
940
|
+
expect(csvContent).toContain('"John Doe"');
|
|
941
|
+
});
|
|
942
|
+
});
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* - Customizable positioning and styling
|
|
13
13
|
* - Swipe gestures for dismissal
|
|
14
14
|
* - Keyboard navigation support
|
|
15
|
-
* - Auto-dismiss with default
|
|
15
|
+
* - Auto-dismiss with default 5 second duration managed internally
|
|
16
16
|
* - Action buttons and close functionality
|
|
17
17
|
* - Responsive design
|
|
18
18
|
* - Accessibility compliant
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Hooks Index Exports Unit Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Hooks/__tests__
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Tests to verify that all exports from the hooks index file are properly accessible.
|
|
8
|
+
* This ensures the module structure is correct and all re-exports work as expected.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect } from 'vitest';
|
|
12
|
+
import * as Hooks from '../index';
|
|
13
|
+
|
|
14
|
+
describe('hooks index exports', () => {
|
|
15
|
+
describe('UI Interaction Hooks', () => {
|
|
16
|
+
it('should export useToast', () => {
|
|
17
|
+
expect(Hooks).toHaveProperty('useToast');
|
|
18
|
+
expect(typeof Hooks.useToast).toBe('function');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should export useFocusManagement', () => {
|
|
22
|
+
expect(Hooks).toHaveProperty('useFocusManagement');
|
|
23
|
+
expect(typeof Hooks.useFocusManagement).toBe('function');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should export useFocusTrap', () => {
|
|
27
|
+
expect(Hooks).toHaveProperty('useFocusTrap');
|
|
28
|
+
expect(typeof Hooks.useFocusTrap).toBe('function');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should export useKeyboardShortcuts', () => {
|
|
32
|
+
expect(Hooks).toHaveProperty('useKeyboardShortcuts');
|
|
33
|
+
expect(typeof Hooks.useKeyboardShortcuts).toBe('function');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should export useIsMobile', () => {
|
|
37
|
+
expect(Hooks).toHaveProperty('useIsMobile');
|
|
38
|
+
expect(typeof Hooks.useIsMobile).toBe('function');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('Event Theming Hook', () => {
|
|
43
|
+
it('should export useEventTheme', () => {
|
|
44
|
+
expect(Hooks).toHaveProperty('useEventTheme');
|
|
45
|
+
expect(typeof Hooks.useEventTheme).toBe('function');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('Data & State Hooks', () => {
|
|
50
|
+
it('should export useDebounce', () => {
|
|
51
|
+
expect(Hooks).toHaveProperty('useDebounce');
|
|
52
|
+
expect(typeof Hooks.useDebounce).toBe('function');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should export useDataTableState', () => {
|
|
56
|
+
expect(Hooks).toHaveProperty('useDataTableState');
|
|
57
|
+
expect(typeof Hooks.useDataTableState).toBe('function');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('Organisation Hooks', () => {
|
|
62
|
+
it('should export useOrganisationPermissions', () => {
|
|
63
|
+
expect(Hooks).toHaveProperty('useOrganisationPermissions');
|
|
64
|
+
expect(typeof Hooks.useOrganisationPermissions).toBe('function');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should export useSecureDataAccess', () => {
|
|
68
|
+
expect(Hooks).toHaveProperty('useSecureDataAccess');
|
|
69
|
+
expect(typeof Hooks.useSecureDataAccess).toBe('function');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should export useOrganisationSecurity', () => {
|
|
73
|
+
expect(Hooks).toHaveProperty('useOrganisationSecurity');
|
|
74
|
+
expect(typeof Hooks.useOrganisationSecurity).toBe('function');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('Form Hooks', () => {
|
|
79
|
+
it('should export useZodForm', () => {
|
|
80
|
+
expect(Hooks).toHaveProperty('useZodForm');
|
|
81
|
+
expect(typeof Hooks.useZodForm).toBe('function');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('Performance Hooks', () => {
|
|
86
|
+
it('should export useComponentPerformance', () => {
|
|
87
|
+
expect(Hooks).toHaveProperty('useComponentPerformance');
|
|
88
|
+
expect(typeof Hooks.useComponentPerformance).toBe('function');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should export useAppConfig', () => {
|
|
92
|
+
expect(Hooks).toHaveProperty('useAppConfig');
|
|
93
|
+
expect(typeof Hooks.useAppConfig).toBe('function');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should export usePerformanceMonitor', () => {
|
|
97
|
+
expect(Hooks).toHaveProperty('usePerformanceMonitor');
|
|
98
|
+
expect(typeof Hooks.usePerformanceMonitor).toBe('function');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should export useDataTablePerformance', () => {
|
|
102
|
+
expect(Hooks).toHaveProperty('useDataTablePerformance');
|
|
103
|
+
expect(typeof Hooks.useDataTablePerformance).toBe('function');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('File Display Hooks', () => {
|
|
108
|
+
it('should export useFileDisplay', () => {
|
|
109
|
+
expect(Hooks).toHaveProperty('useFileDisplay');
|
|
110
|
+
expect(typeof Hooks.useFileDisplay).toBe('function');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should export clearFileDisplayCache', () => {
|
|
114
|
+
expect(Hooks).toHaveProperty('clearFileDisplayCache');
|
|
115
|
+
expect(typeof Hooks.clearFileDisplayCache).toBe('function');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should export getFileDisplayCacheStats', () => {
|
|
119
|
+
expect(Hooks).toHaveProperty('getFileDisplayCacheStats');
|
|
120
|
+
expect(typeof Hooks.getFileDisplayCacheStats).toBe('function');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should export invalidateFileDisplayCache', () => {
|
|
124
|
+
expect(Hooks).toHaveProperty('invalidateFileDisplayCache');
|
|
125
|
+
expect(typeof Hooks.invalidateFileDisplayCache).toBe('function');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('Public Data Access Hooks', () => {
|
|
130
|
+
it('should export public hooks via wildcard export', () => {
|
|
131
|
+
// Wildcard export from './public' should include public hooks
|
|
132
|
+
// These are tested through actual usage, but we verify the export path exists
|
|
133
|
+
expect(Hooks).toBeDefined();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('Type Exports', () => {
|
|
138
|
+
it('should export UseOrganisationPermissionsReturn type', () => {
|
|
139
|
+
// Type exports are compile-time only, but we verify they're referenced
|
|
140
|
+
// This is mainly for documentation purposes
|
|
141
|
+
expect(Hooks).toBeDefined();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should export SecureDataAccessReturn type', () => {
|
|
145
|
+
// Type exports are compile-time only
|
|
146
|
+
expect(Hooks).toBeDefined();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should export OrganisationSecurityHook type', () => {
|
|
150
|
+
// Type exports are compile-time only
|
|
151
|
+
expect(Hooks).toBeDefined();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should export UseAppConfigReturn type', () => {
|
|
155
|
+
// Type exports are compile-time only
|
|
156
|
+
expect(Hooks).toBeDefined();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should export UseDataTablePerformanceOptions type', () => {
|
|
160
|
+
// Type exports are compile-time only
|
|
161
|
+
expect(Hooks).toBeDefined();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should export UseDataTablePerformanceReturn type', () => {
|
|
165
|
+
// Type exports are compile-time only
|
|
166
|
+
expect(Hooks).toBeDefined();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should export UseFileDisplayReturn type', () => {
|
|
170
|
+
// Type exports are compile-time only
|
|
171
|
+
expect(Hooks).toBeDefined();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should export UseFileDisplayOptions type', () => {
|
|
175
|
+
// Type exports are compile-time only
|
|
176
|
+
expect(Hooks).toBeDefined();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe('Module Structure', () => {
|
|
181
|
+
it('should maintain correct module structure for re-exports', () => {
|
|
182
|
+
// Verify that all hooks are functions
|
|
183
|
+
const hookNames = [
|
|
184
|
+
'useToast',
|
|
185
|
+
'useFocusManagement',
|
|
186
|
+
'useFocusTrap',
|
|
187
|
+
'useKeyboardShortcuts',
|
|
188
|
+
'useIsMobile',
|
|
189
|
+
'useEventTheme',
|
|
190
|
+
'useDebounce',
|
|
191
|
+
'useDataTableState',
|
|
192
|
+
'useOrganisationPermissions',
|
|
193
|
+
'useSecureDataAccess',
|
|
194
|
+
'useOrganisationSecurity',
|
|
195
|
+
'useZodForm',
|
|
196
|
+
'useComponentPerformance',
|
|
197
|
+
'useAppConfig',
|
|
198
|
+
'usePerformanceMonitor',
|
|
199
|
+
'useDataTablePerformance',
|
|
200
|
+
'useFileDisplay'
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
hookNames.forEach((hookName) => {
|
|
204
|
+
expect(Hooks).toHaveProperty(hookName);
|
|
205
|
+
expect(typeof Hooks[hookName as keyof typeof Hooks]).toBe('function');
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should export utility functions', () => {
|
|
210
|
+
const utilityFunctions = [
|
|
211
|
+
'clearFileDisplayCache',
|
|
212
|
+
'getFileDisplayCacheStats',
|
|
213
|
+
'invalidateFileDisplayCache'
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
utilityFunctions.forEach((funcName) => {
|
|
217
|
+
expect(Hooks).toHaveProperty(funcName);
|
|
218
|
+
expect(typeof Hooks[funcName as keyof typeof Hooks]).toBe('function');
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|