@jmruthers/pace-core 0.5.75 → 0.5.76
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-HWZQGASI.js → DataTable-4GAVPIEG.js} +48 -30
- package/dist/{PublicLoadingSpinner-BKNBT6b6.d.ts → PublicLoadingSpinner-BiNER8F5.d.ts} +28 -17
- package/dist/{chunk-33PHABLB.js → chunk-AFGTSUAD.js} +10 -127
- package/dist/chunk-AFGTSUAD.js.map +1 -0
- package/dist/{chunk-2DFZ432F.js → chunk-K34IM5CT.js} +3 -5
- package/dist/{chunk-2DFZ432F.js.map → chunk-K34IM5CT.js.map} +1 -1
- package/dist/{chunk-2CHATWBF.js → chunk-KHJS6VIA.js} +199 -35
- package/dist/chunk-KHJS6VIA.js.map +1 -0
- package/dist/{chunk-ZTT2AXMX.js → chunk-KK73ZB4E.js} +2 -2
- package/dist/{chunk-CY3AHGO4.js → chunk-M5IWZRBT.js} +1750 -2815
- package/dist/chunk-M5IWZRBT.js.map +1 -0
- package/dist/{chunk-DAXLNIDY.js → chunk-Y6TXWPJO.js} +6 -4
- package/dist/{chunk-DAXLNIDY.js.map → chunk-Y6TXWPJO.js.map} +1 -1
- package/dist/{chunk-YNUBMSMV.js → chunk-YCKPEMJA.js} +186 -263
- package/dist/chunk-YCKPEMJA.js.map +1 -0
- package/dist/components.d.ts +1 -1
- package/dist/components.js +7 -6
- package/dist/components.js.map +1 -1
- package/dist/hooks.d.ts +17 -40
- package/dist/hooks.js +6 -6
- package/dist/index.d.ts +3 -3
- package/dist/index.js +12 -10
- package/dist/index.js.map +1 -1
- package/dist/rbac/index.d.ts +54 -1
- package/dist/rbac/index.js +5 -4
- package/dist/utils.js +1 -1
- package/docs/TERMINOLOGY.md +231 -0
- 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/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +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/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +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/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +47 -0
- package/docs/api/interfaces/UseResolvedScopeReturn.md +47 -0
- 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 +57 -11
- package/docs/api-reference/providers.md +26 -7
- package/docs/best-practices/README.md +20 -0
- package/docs/best-practices/accessibility.md +566 -0
- package/docs/best-practices/performance-expansion.md +473 -0
- package/docs/core-concepts/authentication.md +15 -7
- package/docs/documentation-index.md +1 -1
- package/docs/documentation-templates.md +539 -0
- package/docs/getting-started/quick-start.md +16 -66
- package/docs/implementation-guides/component-styling.md +410 -0
- package/docs/implementation-guides/data-tables.md +1 -1
- package/docs/style-guide.md +39 -0
- package/package.json +1 -1
- package/src/__tests__/TEST_GUIDE_CURSOR.md +290 -0
- package/src/__tests__/helpers/supabaseMock.ts +48 -2
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +17 -6
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +73 -9
- package/src/components/DataTable/components/DataTableCore.tsx +280 -475
- package/src/components/DataTable/components/UnifiedTableBody.tsx +120 -153
- package/src/components/DataTable/components/index.ts +1 -2
- package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +208 -275
- package/src/components/DataTable/core/index.ts +1 -8
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +525 -0
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +570 -0
- package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +214 -0
- package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +224 -0
- package/src/components/DataTable/hooks/index.ts +6 -0
- package/src/components/DataTable/hooks/useColumnReordering.ts +1 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +149 -0
- package/src/components/DataTable/hooks/useDataTableState.ts +12 -6
- package/src/components/DataTable/hooks/useHierarchicalState.ts +26 -8
- package/src/components/DataTable/hooks/useTableColumns.ts +153 -0
- package/src/components/DataTable/index.ts +1 -9
- package/src/components/DataTable/utils/__tests__/COVERAGE_NOTE.md +89 -0
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +3 -6
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +462 -0
- package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +247 -0
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +8 -6
- package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +466 -0
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +265 -0
- package/src/components/DataTable/utils/errorHandling.ts +52 -460
- package/src/components/DataTable/utils/exportUtils.ts +46 -15
- package/src/components/DataTable/utils/hierarchicalSorting.ts +50 -3
- package/src/components/DataTable/utils/hierarchicalUtils.ts +167 -34
- package/src/components/DataTable/utils/index.ts +5 -0
- package/src/components/DataTable/utils/rowUtils.ts +68 -0
- package/src/components/EventSelector/EventSelector.test.tsx +672 -0
- package/src/components/Label/__tests__/Label.test.tsx +434 -0
- package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +190 -0
- package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +185 -0
- package/src/components/PublicLayout/__tests__/PublicPageProvider.test.tsx +313 -0
- package/src/components/Select/Select.test.tsx +143 -120
- package/src/components/Select/Select.tsx +47 -212
- package/src/components/Select/hooks.ts +36 -1
- package/src/components/Select/index.ts +2 -1
- package/src/hooks/services/__tests__/useServiceHooks.test.tsx +137 -0
- package/src/hooks/useSecureDataAccess.test.ts +32 -29
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +341 -0
- package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +437 -0
- package/src/rbac/hooks/index.ts +2 -0
- package/src/rbac/hooks/useResolvedScope.ts +232 -0
- package/src/services/__tests__/InactivityService.lifecycle.test.ts +411 -0
- package/src/services/__tests__/OrganisationService.pagination.test.ts +375 -0
- package/src/types/__tests__/README.md +114 -0
- package/src/types/__tests__/validation.test.ts +731 -0
- package/src/utils/__tests__/file-reference.test.ts +383 -0
- package/src/utils/__tests__/performanceBenchmark.test.ts +175 -0
- package/src/utils/appNameResolver.test.ts +54 -0
- package/src/validation/__tests__/csrf.unit.test.ts +63 -0
- package/src/validation/__tests__/passwordSchema.unit.test.ts +105 -0
- package/dist/chunk-2CHATWBF.js.map +0 -1
- package/dist/chunk-33PHABLB.js.map +0 -1
- package/dist/chunk-CY3AHGO4.js.map +0 -1
- package/dist/chunk-TYHR5X4W.js +0 -33
- package/dist/chunk-TYHR5X4W.js.map +0 -1
- package/dist/chunk-YNUBMSMV.js.map +0 -1
- package/dist/eventContext-BBA42P6G.js +0 -14
- package/dist/eventContext-BBA42P6G.js.map +0 -1
- package/docs/documentation-style-checklist.md +0 -294
- package/src/components/DataTable/components/DataTableBody.tsx +0 -488
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -144
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -515
- package/src/components/DataTable/core/ActionManager.ts +0 -235
- package/src/components/DataTable/core/ColumnManager.ts +0 -215
- package/src/components/DataTable/core/DataManager.ts +0 -188
- package/src/components/DataTable/core/DataTableContext.tsx +0 -181
- package/src/components/DataTable/core/LocalDataAdapter.ts +0 -264
- package/src/components/DataTable/core/PluginRegistry.ts +0 -229
- package/src/components/DataTable/core/StateManager.ts +0 -311
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -634
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -193
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -519
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -714
- package/src/components/DataTable/core/interfaces.ts +0 -338
- package/src/components/DataTable/utils/debugTools.ts +0 -583
- package/src/components/Select/Select.bug-test.tsx +0 -69
- package/src/components/Select/Select.refactored.tsx +0 -497
- /package/dist/{DataTable-HWZQGASI.js.map → DataTable-4GAVPIEG.js.map} +0 -0
- /package/dist/{chunk-ZTT2AXMX.js.map → chunk-KK73ZB4E.js.map} +0 -0
|
@@ -1,714 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file StateManager Unit Tests
|
|
3
|
-
* @package @jmruthers/pace-core
|
|
4
|
-
* @module Components/DataTable/Core/StateManager
|
|
5
|
-
* @since 0.3.0
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
9
|
-
import { StateManagerImpl } from '../StateManager';
|
|
10
|
-
import type { DataTableObserver, DataTableState, UIState } from '../interfaces';
|
|
11
|
-
import type { DataRecord } from '../../types';
|
|
12
|
-
|
|
13
|
-
// Test data types
|
|
14
|
-
interface TestDataRecord extends DataRecord {
|
|
15
|
-
id: string;
|
|
16
|
-
name: string;
|
|
17
|
-
email: string;
|
|
18
|
-
role: string;
|
|
19
|
-
isActive: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Test data fixtures
|
|
23
|
-
const createTestData = (overrides: Partial<TestDataRecord> = {}): TestDataRecord => ({
|
|
24
|
-
id: 'test-id-1',
|
|
25
|
-
name: 'Test User',
|
|
26
|
-
email: 'test@example.com',
|
|
27
|
-
role: 'user',
|
|
28
|
-
isActive: true,
|
|
29
|
-
...overrides,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const createTestDataArray = (count: number): TestDataRecord[] =>
|
|
33
|
-
Array.from({ length: count }, (_, i) => createTestData({
|
|
34
|
-
id: `test-id-${i + 1}`,
|
|
35
|
-
name: `Test User ${i + 1}`,
|
|
36
|
-
email: `test${i + 1}@example.com`,
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
const createTestObserver = (id: string = 'test-observer'): DataTableObserver<TestDataRecord> => ({
|
|
40
|
-
id,
|
|
41
|
-
onDataChange: vi.fn(),
|
|
42
|
-
onColumnChange: vi.fn(),
|
|
43
|
-
onStateChange: vi.fn(),
|
|
44
|
-
onError: vi.fn(),
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('[unit] StateManager', () => {
|
|
48
|
-
let stateManager: StateManagerImpl<TestDataRecord>;
|
|
49
|
-
let testObserver: DataTableObserver<TestDataRecord>;
|
|
50
|
-
|
|
51
|
-
beforeEach(() => {
|
|
52
|
-
stateManager = new StateManagerImpl();
|
|
53
|
-
testObserver = createTestObserver();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
afterEach(() => {
|
|
57
|
-
vi.clearAllMocks();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('Initialization', () => {
|
|
61
|
-
it('should initialize with default state', () => {
|
|
62
|
-
const state = stateManager.getState();
|
|
63
|
-
|
|
64
|
-
expect(state.data).toEqual([]);
|
|
65
|
-
expect(state.isLoading).toBe(false);
|
|
66
|
-
expect(state.error).toBe(null);
|
|
67
|
-
expect(state.columns).toEqual([]);
|
|
68
|
-
expect(state.actions).toEqual([]);
|
|
69
|
-
expect(state.features).toBeInstanceOf(Map);
|
|
70
|
-
expect(state.plugins).toBeInstanceOf(Map);
|
|
71
|
-
expect(state.ui).toMatchObject({
|
|
72
|
-
globalFilter: '',
|
|
73
|
-
columnFilters: [],
|
|
74
|
-
sorting: [],
|
|
75
|
-
grouping: [],
|
|
76
|
-
expanded: {},
|
|
77
|
-
pagination: { pageIndex: 0, pageSize: 10 },
|
|
78
|
-
rowSelection: {},
|
|
79
|
-
editing: {
|
|
80
|
-
rowId: null,
|
|
81
|
-
data: {},
|
|
82
|
-
isCreating: false,
|
|
83
|
-
creationData: {},
|
|
84
|
-
},
|
|
85
|
-
modals: {
|
|
86
|
-
import: false,
|
|
87
|
-
export: false,
|
|
88
|
-
view: false,
|
|
89
|
-
viewData: null,
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('should initialize with custom initial state', () => {
|
|
95
|
-
const customState = {
|
|
96
|
-
data: createTestDataArray(2),
|
|
97
|
-
isLoading: true,
|
|
98
|
-
error: new Error('Test error'),
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const manager = new StateManagerImpl(customState);
|
|
102
|
-
const state = manager.getState();
|
|
103
|
-
|
|
104
|
-
expect(state.data).toEqual(customState.data);
|
|
105
|
-
expect(state.isLoading).toBe(true);
|
|
106
|
-
expect(state.error).toBe(customState.error);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('should return copy of state', () => {
|
|
110
|
-
const state1 = stateManager.getState();
|
|
111
|
-
const state2 = stateManager.getState();
|
|
112
|
-
|
|
113
|
-
expect(state1).not.toBe(state2); // Different objects
|
|
114
|
-
expect(state1).toEqual(state2); // Same content
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
describe('State Updates', () => {
|
|
119
|
-
it('should update state with updater function', () => {
|
|
120
|
-
const newData = createTestDataArray(3);
|
|
121
|
-
|
|
122
|
-
stateManager.setState(state => ({
|
|
123
|
-
...state,
|
|
124
|
-
data: newData,
|
|
125
|
-
isLoading: true,
|
|
126
|
-
}));
|
|
127
|
-
|
|
128
|
-
const state = stateManager.getState();
|
|
129
|
-
expect(state.data).toEqual(newData);
|
|
130
|
-
expect(state.isLoading).toBe(true);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('should preserve other state properties when updating', () => {
|
|
134
|
-
const originalState = stateManager.getState();
|
|
135
|
-
|
|
136
|
-
stateManager.setState(state => ({
|
|
137
|
-
...state,
|
|
138
|
-
data: createTestDataArray(2),
|
|
139
|
-
}));
|
|
140
|
-
|
|
141
|
-
const newState = stateManager.getState();
|
|
142
|
-
expect(newState.data).toEqual(createTestDataArray(2));
|
|
143
|
-
expect(newState.isLoading).toBe(originalState.isLoading);
|
|
144
|
-
expect(newState.error).toBe(originalState.error);
|
|
145
|
-
expect(newState.columns).toEqual(originalState.columns);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('should handle complex state updates', () => {
|
|
149
|
-
const newData = createTestDataArray(5);
|
|
150
|
-
const newColumns = [{ id: 'name', header: 'Name' }];
|
|
151
|
-
const newActions = [{ label: 'Edit', onClick: vi.fn() }];
|
|
152
|
-
|
|
153
|
-
stateManager.setState(state => ({
|
|
154
|
-
...state,
|
|
155
|
-
data: newData,
|
|
156
|
-
columns: newColumns,
|
|
157
|
-
actions: newActions,
|
|
158
|
-
ui: {
|
|
159
|
-
...state.ui,
|
|
160
|
-
globalFilter: 'test filter',
|
|
161
|
-
pagination: { pageIndex: 2, pageSize: 20 },
|
|
162
|
-
},
|
|
163
|
-
}));
|
|
164
|
-
|
|
165
|
-
const state = stateManager.getState();
|
|
166
|
-
expect(state.data).toEqual(newData);
|
|
167
|
-
expect(state.columns).toEqual(newColumns);
|
|
168
|
-
expect(state.actions).toEqual(newActions);
|
|
169
|
-
expect(state.ui.globalFilter).toBe('test filter');
|
|
170
|
-
expect(state.ui.pagination).toEqual({ pageIndex: 2, pageSize: 20 });
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe('Observer Pattern', () => {
|
|
175
|
-
it('should subscribe function observer', () => {
|
|
176
|
-
const observerFn = vi.fn();
|
|
177
|
-
const unsubscribe = stateManager.subscribe(observerFn);
|
|
178
|
-
|
|
179
|
-
expect(typeof unsubscribe).toBe('function');
|
|
180
|
-
expect(stateManager.getObserverCount()).toBe(1);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('should subscribe object observer', () => {
|
|
184
|
-
const unsubscribe = stateManager.subscribe(testObserver);
|
|
185
|
-
|
|
186
|
-
expect(typeof unsubscribe).toBe('function');
|
|
187
|
-
expect(stateManager.getObserverCount()).toBe(1);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should subscribe to specific observer', () => {
|
|
191
|
-
const unsubscribe = stateManager.subscribeToObserver(testObserver);
|
|
192
|
-
|
|
193
|
-
expect(typeof unsubscribe).toBe('function');
|
|
194
|
-
expect(stateManager.getObserverCount()).toBe(1);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('should unsubscribe observer', () => {
|
|
198
|
-
const unsubscribe = stateManager.subscribe(testObserver);
|
|
199
|
-
expect(stateManager.getObserverCount()).toBe(1);
|
|
200
|
-
|
|
201
|
-
unsubscribe();
|
|
202
|
-
expect(stateManager.getObserverCount()).toBe(0);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it('should handle multiple observers', () => {
|
|
206
|
-
const observer1 = createTestObserver('observer-1');
|
|
207
|
-
const observer2 = createTestObserver('observer-2');
|
|
208
|
-
const observer3 = createTestObserver('observer-3');
|
|
209
|
-
|
|
210
|
-
stateManager.subscribe(observer1);
|
|
211
|
-
stateManager.subscribe(observer2);
|
|
212
|
-
stateManager.subscribe(observer3);
|
|
213
|
-
|
|
214
|
-
expect(stateManager.getObserverCount()).toBe(3);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('should notify observers on state change', () => {
|
|
218
|
-
const observerFn = vi.fn();
|
|
219
|
-
stateManager.subscribe(observerFn);
|
|
220
|
-
|
|
221
|
-
stateManager.setState(state => ({
|
|
222
|
-
...state,
|
|
223
|
-
data: createTestDataArray(2),
|
|
224
|
-
}));
|
|
225
|
-
|
|
226
|
-
expect(observerFn).toHaveBeenCalledWith(expect.objectContaining({
|
|
227
|
-
data: createTestDataArray(2),
|
|
228
|
-
}));
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
it('should notify object observers on state change', () => {
|
|
232
|
-
stateManager.subscribe(testObserver);
|
|
233
|
-
|
|
234
|
-
stateManager.setState(state => ({
|
|
235
|
-
...state,
|
|
236
|
-
data: createTestDataArray(2),
|
|
237
|
-
}));
|
|
238
|
-
|
|
239
|
-
expect(testObserver.onStateChange).toHaveBeenCalledWith(expect.objectContaining({
|
|
240
|
-
data: createTestDataArray(2),
|
|
241
|
-
}));
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
describe('Data Updates', () => {
|
|
246
|
-
it('should update data and notify observers', () => {
|
|
247
|
-
const newData = createTestDataArray(3);
|
|
248
|
-
stateManager.subscribe(testObserver);
|
|
249
|
-
|
|
250
|
-
stateManager.updateData(newData);
|
|
251
|
-
|
|
252
|
-
const state = stateManager.getState();
|
|
253
|
-
expect(state.data).toEqual(newData);
|
|
254
|
-
expect(testObserver.onDataChange).toHaveBeenCalledWith(newData);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('should not notify if data is the same reference', () => {
|
|
258
|
-
const data = createTestDataArray(2);
|
|
259
|
-
stateManager.subscribe(testObserver);
|
|
260
|
-
|
|
261
|
-
stateManager.updateData(data);
|
|
262
|
-
vi.clearAllMocks();
|
|
263
|
-
|
|
264
|
-
stateManager.updateData(data); // Same reference
|
|
265
|
-
|
|
266
|
-
expect(testObserver.onDataChange).not.toHaveBeenCalled();
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it('should notify when data reference changes', () => {
|
|
270
|
-
const data1 = createTestDataArray(2);
|
|
271
|
-
const data2 = createTestDataArray(3);
|
|
272
|
-
stateManager.subscribe(testObserver);
|
|
273
|
-
|
|
274
|
-
stateManager.updateData(data1);
|
|
275
|
-
vi.clearAllMocks();
|
|
276
|
-
|
|
277
|
-
stateManager.updateData(data2);
|
|
278
|
-
|
|
279
|
-
expect(testObserver.onDataChange).toHaveBeenCalledWith(data2);
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
describe('Column Updates', () => {
|
|
284
|
-
it('should update columns and notify observers', () => {
|
|
285
|
-
const newColumns = [
|
|
286
|
-
{ id: 'name', header: 'Name' },
|
|
287
|
-
{ id: 'email', header: 'Email' },
|
|
288
|
-
];
|
|
289
|
-
stateManager.subscribe(testObserver);
|
|
290
|
-
|
|
291
|
-
stateManager.updateColumns(newColumns);
|
|
292
|
-
|
|
293
|
-
const state = stateManager.getState();
|
|
294
|
-
expect(state.columns).toEqual(newColumns);
|
|
295
|
-
expect(testObserver.onColumnChange).toHaveBeenCalledWith(newColumns);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it('should not notify if columns are the same reference', () => {
|
|
299
|
-
const columns = [{ id: 'name', header: 'Name' }];
|
|
300
|
-
stateManager.subscribe(testObserver);
|
|
301
|
-
|
|
302
|
-
stateManager.updateColumns(columns);
|
|
303
|
-
vi.clearAllMocks();
|
|
304
|
-
|
|
305
|
-
stateManager.updateColumns(columns); // Same reference
|
|
306
|
-
|
|
307
|
-
expect(testObserver.onColumnChange).not.toHaveBeenCalled();
|
|
308
|
-
});
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
describe('Action Updates', () => {
|
|
312
|
-
it('should update actions', () => {
|
|
313
|
-
const newActions = [
|
|
314
|
-
{ label: 'Edit', onClick: vi.fn() },
|
|
315
|
-
{ label: 'Delete', onClick: vi.fn() },
|
|
316
|
-
];
|
|
317
|
-
|
|
318
|
-
stateManager.updateActions(newActions);
|
|
319
|
-
|
|
320
|
-
const state = stateManager.getState();
|
|
321
|
-
expect(state.actions).toEqual(newActions);
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
describe('UI State Updates', () => {
|
|
326
|
-
it('should update global filter', () => {
|
|
327
|
-
stateManager.updateGlobalFilter('test search');
|
|
328
|
-
|
|
329
|
-
const state = stateManager.getState();
|
|
330
|
-
expect(state.ui.globalFilter).toBe('test search');
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('should update column filters', () => {
|
|
334
|
-
const filters = [
|
|
335
|
-
{ id: 'name', value: 'John' },
|
|
336
|
-
{ id: 'role', value: 'admin' },
|
|
337
|
-
];
|
|
338
|
-
|
|
339
|
-
stateManager.updateColumnFilters(filters);
|
|
340
|
-
|
|
341
|
-
const state = stateManager.getState();
|
|
342
|
-
expect(state.ui.columnFilters).toEqual(filters);
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
it('should update sorting', () => {
|
|
346
|
-
const sorting = [
|
|
347
|
-
{ id: 'name', desc: false },
|
|
348
|
-
{ id: 'created_at', desc: true },
|
|
349
|
-
];
|
|
350
|
-
|
|
351
|
-
stateManager.updateSorting(sorting);
|
|
352
|
-
|
|
353
|
-
const state = stateManager.getState();
|
|
354
|
-
expect(state.ui.sorting).toEqual(sorting);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
it('should update grouping', () => {
|
|
358
|
-
const grouping = ['role', 'department'];
|
|
359
|
-
|
|
360
|
-
stateManager.updateGrouping(grouping);
|
|
361
|
-
|
|
362
|
-
const state = stateManager.getState();
|
|
363
|
-
expect(state.ui.grouping).toEqual(grouping);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
it('should update pagination', () => {
|
|
367
|
-
const pagination = { pageIndex: 2, pageSize: 25 };
|
|
368
|
-
|
|
369
|
-
stateManager.updatePagination(pagination);
|
|
370
|
-
|
|
371
|
-
const state = stateManager.getState();
|
|
372
|
-
expect(state.ui.pagination).toEqual(pagination);
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
it('should update row selection', () => {
|
|
376
|
-
const selection = { 'row-1': true, 'row-2': false, 'row-3': true };
|
|
377
|
-
|
|
378
|
-
stateManager.updateRowSelection(selection);
|
|
379
|
-
|
|
380
|
-
const state = stateManager.getState();
|
|
381
|
-
expect(state.ui.rowSelection).toEqual(selection);
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
it('should update editing state', () => {
|
|
385
|
-
const editing = {
|
|
386
|
-
rowId: 'row-1',
|
|
387
|
-
data: { name: 'Updated Name' },
|
|
388
|
-
isCreating: false,
|
|
389
|
-
creationData: {},
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
stateManager.updateEditing(editing);
|
|
393
|
-
|
|
394
|
-
const state = stateManager.getState();
|
|
395
|
-
expect(state.ui.editing).toEqual(editing);
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
it('should update modal state', () => {
|
|
399
|
-
const modals = {
|
|
400
|
-
import: true,
|
|
401
|
-
export: false,
|
|
402
|
-
view: true,
|
|
403
|
-
viewData: { id: 'test' },
|
|
404
|
-
};
|
|
405
|
-
|
|
406
|
-
stateManager.updateModals(modals);
|
|
407
|
-
|
|
408
|
-
const state = stateManager.getState();
|
|
409
|
-
expect(state.ui.modals).toEqual(modals);
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
it('should update multiple UI properties at once', () => {
|
|
413
|
-
const uiUpdates = {
|
|
414
|
-
globalFilter: 'search term',
|
|
415
|
-
pagination: { pageIndex: 1, pageSize: 20 },
|
|
416
|
-
sorting: [{ id: 'name', desc: false }],
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
stateManager.updateUI(uiUpdates);
|
|
420
|
-
|
|
421
|
-
const state = stateManager.getState();
|
|
422
|
-
expect(state.ui.globalFilter).toBe('search term');
|
|
423
|
-
expect(state.ui.pagination).toEqual({ pageIndex: 1, pageSize: 20 });
|
|
424
|
-
expect(state.ui.sorting).toEqual([{ id: 'name', desc: false }]);
|
|
425
|
-
});
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
describe('Feature and Plugin State', () => {
|
|
429
|
-
it('should update feature state', () => {
|
|
430
|
-
const featureState = { enabled: true, options: { theme: 'dark' } };
|
|
431
|
-
|
|
432
|
-
stateManager.updateFeatureState('theme', featureState);
|
|
433
|
-
|
|
434
|
-
const state = stateManager.getState();
|
|
435
|
-
expect(state.features.get('theme')).toEqual(featureState);
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
it('should update plugin state', () => {
|
|
439
|
-
const pluginState = { loaded: true, version: '1.0.0' };
|
|
440
|
-
|
|
441
|
-
stateManager.updatePluginState('analytics', pluginState);
|
|
442
|
-
|
|
443
|
-
const state = stateManager.getState();
|
|
444
|
-
expect(state.plugins.get('analytics')).toEqual(pluginState);
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
it('should handle multiple feature states', () => {
|
|
448
|
-
stateManager.updateFeatureState('theme', { enabled: true });
|
|
449
|
-
stateManager.updateFeatureState('notifications', { enabled: false });
|
|
450
|
-
|
|
451
|
-
const state = stateManager.getState();
|
|
452
|
-
expect(state.features.get('theme')).toEqual({ enabled: true });
|
|
453
|
-
expect(state.features.get('notifications')).toEqual({ enabled: false });
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
it('should handle multiple plugin states', () => {
|
|
457
|
-
stateManager.updatePluginState('analytics', { loaded: true });
|
|
458
|
-
stateManager.updatePluginState('logging', { loaded: false });
|
|
459
|
-
|
|
460
|
-
const state = stateManager.getState();
|
|
461
|
-
expect(state.plugins.get('analytics')).toEqual({ loaded: true });
|
|
462
|
-
expect(state.plugins.get('logging')).toEqual({ loaded: false });
|
|
463
|
-
});
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
describe('Loading and Error States', () => {
|
|
467
|
-
it('should set loading state', () => {
|
|
468
|
-
stateManager.setLoading(true);
|
|
469
|
-
|
|
470
|
-
const state = stateManager.getState();
|
|
471
|
-
expect(state.isLoading).toBe(true);
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
it('should set error state', () => {
|
|
475
|
-
const error = new Error('Test error');
|
|
476
|
-
stateManager.subscribe(testObserver);
|
|
477
|
-
|
|
478
|
-
stateManager.setError(error);
|
|
479
|
-
|
|
480
|
-
const state = stateManager.getState();
|
|
481
|
-
expect(state.error).toBe(error);
|
|
482
|
-
expect(testObserver.onError).toHaveBeenCalledWith(error);
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
it('should clear error state', () => {
|
|
486
|
-
stateManager.setError(new Error('Test error'));
|
|
487
|
-
stateManager.setError(null);
|
|
488
|
-
|
|
489
|
-
const state = stateManager.getState();
|
|
490
|
-
expect(state.error).toBe(null);
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
it('should not notify on error when error is null', () => {
|
|
494
|
-
stateManager.subscribe(testObserver);
|
|
495
|
-
stateManager.setError(null);
|
|
496
|
-
|
|
497
|
-
expect(testObserver.onError).not.toHaveBeenCalled();
|
|
498
|
-
});
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
describe('State Reset', () => {
|
|
502
|
-
it('should reset state to initial values', () => {
|
|
503
|
-
// Modify state
|
|
504
|
-
stateManager.setState(state => ({
|
|
505
|
-
...state,
|
|
506
|
-
data: createTestDataArray(2),
|
|
507
|
-
isLoading: true,
|
|
508
|
-
error: new Error('Test error'),
|
|
509
|
-
ui: {
|
|
510
|
-
...state.ui,
|
|
511
|
-
globalFilter: 'test',
|
|
512
|
-
pagination: { pageIndex: 2, pageSize: 20 },
|
|
513
|
-
},
|
|
514
|
-
}));
|
|
515
|
-
|
|
516
|
-
stateManager.subscribe(testObserver);
|
|
517
|
-
stateManager.reset();
|
|
518
|
-
|
|
519
|
-
const state = stateManager.getState();
|
|
520
|
-
expect(state.data).toEqual([]);
|
|
521
|
-
expect(state.isLoading).toBe(false);
|
|
522
|
-
expect(state.error).toBe(null);
|
|
523
|
-
expect(state.ui.globalFilter).toBe('');
|
|
524
|
-
expect(state.ui.pagination).toEqual({ pageIndex: 0, pageSize: 10 });
|
|
525
|
-
expect(testObserver.onStateChange).toHaveBeenCalledWith(state);
|
|
526
|
-
});
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
describe('Event Notifications', () => {
|
|
530
|
-
it('should notify data change event', () => {
|
|
531
|
-
stateManager.subscribe(testObserver);
|
|
532
|
-
const data = createTestDataArray(2);
|
|
533
|
-
|
|
534
|
-
stateManager.notify('dataChange', data);
|
|
535
|
-
|
|
536
|
-
expect(testObserver.onDataChange).toHaveBeenCalledWith(data);
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
it('should notify column change event', () => {
|
|
540
|
-
stateManager.subscribe(testObserver);
|
|
541
|
-
const columns = [{ id: 'name', header: 'Name' }];
|
|
542
|
-
|
|
543
|
-
stateManager.notify('columnChange', columns);
|
|
544
|
-
|
|
545
|
-
expect(testObserver.onColumnChange).toHaveBeenCalledWith(columns);
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
it('should notify state change event', () => {
|
|
549
|
-
stateManager.subscribe(testObserver);
|
|
550
|
-
const state = { data: createTestDataArray(1) };
|
|
551
|
-
|
|
552
|
-
stateManager.notify('stateChange', state);
|
|
553
|
-
|
|
554
|
-
expect(testObserver.onStateChange).toHaveBeenCalledWith(state);
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
it('should notify error event', () => {
|
|
558
|
-
stateManager.subscribe(testObserver);
|
|
559
|
-
const error = new Error('Test error');
|
|
560
|
-
|
|
561
|
-
stateManager.notify('error', error);
|
|
562
|
-
|
|
563
|
-
expect(testObserver.onError).toHaveBeenCalledWith(error);
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
it('should handle observers without all callback methods', () => {
|
|
567
|
-
const partialObserver = {
|
|
568
|
-
id: 'partial-observer',
|
|
569
|
-
onDataChange: vi.fn(),
|
|
570
|
-
// Missing other callbacks
|
|
571
|
-
} as DataTableObserver<TestDataRecord>;
|
|
572
|
-
|
|
573
|
-
stateManager.subscribe(partialObserver);
|
|
574
|
-
|
|
575
|
-
// Should not throw when calling missing callbacks
|
|
576
|
-
expect(() => {
|
|
577
|
-
stateManager.notify('columnChange', []);
|
|
578
|
-
stateManager.notify('stateChange', {});
|
|
579
|
-
stateManager.notify('error', new Error('Test'));
|
|
580
|
-
}).not.toThrow();
|
|
581
|
-
|
|
582
|
-
expect(partialObserver.onDataChange).not.toHaveBeenCalled();
|
|
583
|
-
});
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
describe('Observer Management', () => {
|
|
587
|
-
it('should handle observer unsubscription during notification', () => {
|
|
588
|
-
const observer1 = createTestObserver('observer-1');
|
|
589
|
-
const observer2 = createTestObserver('observer-2');
|
|
590
|
-
|
|
591
|
-
// Observer 1 unsubscribes itself when notified
|
|
592
|
-
observer1.onStateChange = vi.fn().mockImplementation(() => {
|
|
593
|
-
stateManager.unsubscribe('observer-1');
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
stateManager.subscribe(observer1);
|
|
597
|
-
stateManager.subscribe(observer2);
|
|
598
|
-
|
|
599
|
-
expect(stateManager.getObserverCount()).toBe(2);
|
|
600
|
-
|
|
601
|
-
stateManager.setState(state => ({ ...state, data: createTestDataArray(1) }));
|
|
602
|
-
|
|
603
|
-
expect(observer1.onStateChange).toHaveBeenCalled();
|
|
604
|
-
expect(observer2.onStateChange).toHaveBeenCalled();
|
|
605
|
-
expect(stateManager.getObserverCount()).toBe(1);
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
it('should handle multiple unsubscriptions', () => {
|
|
609
|
-
const unsubscribe1 = stateManager.subscribe(createTestObserver('obs-1'));
|
|
610
|
-
const unsubscribe2 = stateManager.subscribe(createTestObserver('obs-2'));
|
|
611
|
-
const unsubscribe3 = stateManager.subscribe(createTestObserver('obs-3'));
|
|
612
|
-
|
|
613
|
-
expect(stateManager.getObserverCount()).toBe(3);
|
|
614
|
-
|
|
615
|
-
unsubscribe1();
|
|
616
|
-
unsubscribe2();
|
|
617
|
-
unsubscribe3();
|
|
618
|
-
|
|
619
|
-
expect(stateManager.getObserverCount()).toBe(0);
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
it('should handle unsubscription of non-existent observer', () => {
|
|
623
|
-
expect(() => {
|
|
624
|
-
stateManager.unsubscribe('non-existent');
|
|
625
|
-
}).not.toThrow();
|
|
626
|
-
});
|
|
627
|
-
});
|
|
628
|
-
|
|
629
|
-
describe('Performance and Memory', () => {
|
|
630
|
-
it('should handle large datasets efficiently', () => {
|
|
631
|
-
const largeDataset = createTestDataArray(1000);
|
|
632
|
-
stateManager.updateData(largeDataset);
|
|
633
|
-
|
|
634
|
-
const state = stateManager.getState();
|
|
635
|
-
expect(state.data).toHaveLength(1000);
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
it('should handle many observers efficiently', () => {
|
|
639
|
-
const observers = Array.from({ length: 100 }, (_, i) =>
|
|
640
|
-
createTestObserver(`observer-${i}`)
|
|
641
|
-
);
|
|
642
|
-
|
|
643
|
-
observers.forEach(observer => stateManager.subscribe(observer));
|
|
644
|
-
expect(stateManager.getObserverCount()).toBe(100);
|
|
645
|
-
|
|
646
|
-
// Notify all observers
|
|
647
|
-
stateManager.setState(state => ({ ...state, data: createTestDataArray(1) }));
|
|
648
|
-
|
|
649
|
-
observers.forEach(observer => {
|
|
650
|
-
expect(observer.onStateChange).toHaveBeenCalled();
|
|
651
|
-
});
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
it('should not leak memory with repeated operations', () => {
|
|
655
|
-
// Perform many state updates
|
|
656
|
-
for (let i = 0; i < 100; i++) {
|
|
657
|
-
stateManager.setState(state => ({
|
|
658
|
-
...state,
|
|
659
|
-
data: createTestDataArray(i % 10),
|
|
660
|
-
}));
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
const state = stateManager.getState();
|
|
664
|
-
expect(state.data).toHaveLength(9); // Last iteration
|
|
665
|
-
});
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
describe('Edge Cases', () => {
|
|
669
|
-
it('should handle null/undefined state updates', () => {
|
|
670
|
-
expect(() => {
|
|
671
|
-
stateManager.setState(() => ({} as any));
|
|
672
|
-
}).not.toThrow();
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
it('should handle empty observer arrays', () => {
|
|
676
|
-
stateManager.notify('dataChange', []);
|
|
677
|
-
expect(stateManager.getObserverCount()).toBe(0);
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
it('should handle observers with null callbacks', () => {
|
|
681
|
-
const observer = {
|
|
682
|
-
id: 'null-callbacks',
|
|
683
|
-
onDataChange: null,
|
|
684
|
-
onColumnChange: null,
|
|
685
|
-
onStateChange: null,
|
|
686
|
-
onError: null,
|
|
687
|
-
} as any;
|
|
688
|
-
|
|
689
|
-
stateManager.subscribe(observer);
|
|
690
|
-
|
|
691
|
-
expect(() => {
|
|
692
|
-
stateManager.notify('dataChange', []);
|
|
693
|
-
stateManager.notify('columnChange', []);
|
|
694
|
-
stateManager.notify('stateChange', {});
|
|
695
|
-
stateManager.notify('error', new Error('Test'));
|
|
696
|
-
}).not.toThrow();
|
|
697
|
-
});
|
|
698
|
-
|
|
699
|
-
it('should handle rapid state updates', () => {
|
|
700
|
-
const observer = createTestObserver();
|
|
701
|
-
stateManager.subscribe(observer);
|
|
702
|
-
|
|
703
|
-
// Rapid updates
|
|
704
|
-
for (let i = 0; i < 10; i++) {
|
|
705
|
-
stateManager.setState(state => ({
|
|
706
|
-
...state,
|
|
707
|
-
data: createTestDataArray(i + 1),
|
|
708
|
-
}));
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
expect(observer.onStateChange).toHaveBeenCalledTimes(10);
|
|
712
|
-
});
|
|
713
|
-
});
|
|
714
|
-
});
|