@intellegens/cornerstone-client 0.0.9999-alpha-30 → 0.0.9999-alpha-32

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 (39) hide show
  1. package/dist/adapters/CollectionViewAdapter/index.integration.test.d.ts +1 -0
  2. package/dist/adapters/CollectionViewAdapter/index.integration.test.js +163 -0
  3. package/dist/adapters/SearchAdapter/index.d.ts +5 -1
  4. package/dist/adapters/SearchAdapter/index.js +25 -13
  5. package/dist/data/auth/dto/index.d.ts +1 -0
  6. package/dist/data/auth/dto/index.js +1 -0
  7. package/dist/services/api/ApiCrudControllerClient/index.integration.test.d.ts +1 -0
  8. package/dist/services/api/ApiCrudControllerClient/index.integration.test.js +34 -0
  9. package/dist/services/api/ApiReadControllerClient/index.integration.test.d.ts +1 -0
  10. package/dist/services/api/ApiReadControllerClient/index.integration.test.js +59 -0
  11. package/dist/services/api/HttpService/FetchHttpService.integration.test.d.ts +1 -0
  12. package/dist/services/api/HttpService/FetchHttpService.integration.test.js +52 -0
  13. package/dist/services/api/UserManagementControllerClient/index.d.ts +0 -6
  14. package/dist/services/api/UserManagementControllerClient/index.integration.test.d.ts +1 -0
  15. package/dist/services/api/UserManagementControllerClient/index.integration.test.js +60 -0
  16. package/dist/services/api/UserManagementControllerClient/index.js +0 -29
  17. package/dist/services/auth/client/AuthService/index.d.ts +10 -2
  18. package/dist/services/auth/client/AuthService/index.js +40 -0
  19. package/dist/services/auth/client/AuthorizationManagementControllerClient/index.integration.test.d.ts +1 -0
  20. package/dist/services/auth/client/AuthorizationManagementControllerClient/index.integration.test.js +89 -0
  21. package/package.json +6 -8
  22. package/src/adapters/CollectionViewAdapter/index.integration.test.ts +197 -0
  23. package/src/adapters/SearchAdapter/index.ts +30 -13
  24. package/src/data/api/dto/response/ApiSuccessResponseDto.ts +1 -1
  25. package/src/data/auth/dto/index.ts +1 -0
  26. package/src/services/api/ApiCrudControllerClient/index.integration.test.ts +46 -0
  27. package/src/services/api/ApiReadControllerClient/index.integration.test.ts +71 -0
  28. package/src/services/api/HttpService/FetchHttpService.integration.test.ts +65 -0
  29. package/src/services/api/UserManagementControllerClient/index.integration.test.ts +69 -0
  30. package/src/services/api/UserManagementControllerClient/index.ts +0 -32
  31. package/src/services/auth/client/AuthService/index.ts +48 -2
  32. package/src/services/auth/client/AuthorizationManagementControllerClient/index.integration.test.ts +110 -0
  33. package/vitest-setup.ts +43 -0
  34. package/vitest.config.ts +59 -0
  35. package/jest.config.js +0 -29
  36. package/tests/ApiClients.test.ts +0 -284
  37. package/tests/CollectionViewAdapter.test.ts +0 -392
  38. package/tests/HttpService.test.ts +0 -303
  39. package/tests/setup.ts +0 -76
@@ -1,284 +0,0 @@
1
- import { ApiReadControllerClient, ApiCrudControllerClient, UserManagementControllerClient, IHttpService, HttpResponse } from '../src';
2
- import { jest } from '@jest/globals';
3
- import { ReadSelectedLogicalOperator } from '../src/data';
4
-
5
- // Mock HTTP Service for testing
6
- class MockHttpService implements IHttpService {
7
- private mockResponses = new Map<string, HttpResponse>();
8
- public requestLog: Array<{ url: string; config?: any }> = [];
9
-
10
- setMockResponse(url: string, response: HttpResponse) {
11
- this.mockResponses.set(url, response);
12
- }
13
-
14
- clearMockResponses() {
15
- this.mockResponses.clear();
16
- this.requestLog = [];
17
- }
18
-
19
- async request<T = any>(url: string, config?: any): Promise<HttpResponse<T>> {
20
- this.requestLog.push({ url, config });
21
-
22
- const mockResponse = this.mockResponses.get(url);
23
- if (mockResponse) {
24
- return mockResponse as HttpResponse<T>;
25
- }
26
-
27
- // Default successful response
28
- return {
29
- ok: true,
30
- status: 200,
31
- statusText: 'OK',
32
- headers: {},
33
- json: async () => ({ success: true }) as T,
34
- text: async () => '{"success":true}',
35
- };
36
- }
37
- }
38
-
39
- // Mock API Initialization Service
40
- jest.unstable_mockModule('../src/services/api/ApiInitializationService/index', () => ({
41
- apiInitializationService: {
42
- getApiUrl: jest.fn((...paths: string[]) => Promise.resolve(`https://api.example.com${paths.join('')}`)),
43
- },
44
- }));
45
-
46
- describe('API Clients with HTTP Service Abstraction', () => {
47
- let mockHttpService: MockHttpService;
48
-
49
- beforeEach(() => {
50
- mockHttpService = new MockHttpService();
51
- jest.clearAllMocks();
52
- });
53
-
54
- describe('ApiReadControllerClient', () => {
55
- let client: ApiReadControllerClient<number, any, any>;
56
-
57
- beforeEach(() => {
58
- client = new ApiReadControllerClient('/api/test', mockHttpService);
59
- });
60
-
61
- it('should use injected HTTP service for readAll', async () => {
62
- const mockData = [
63
- { id: 1, name: 'Test' },
64
- { id: 2, name: 'Test2' },
65
- ];
66
- mockHttpService.setMockResponse('https://api.example.com/api/test/ReadAll', {
67
- ok: true,
68
- status: 200,
69
- statusText: 'OK',
70
- headers: {},
71
- json: async () => ({ success: true, result: mockData, metadata: {} }),
72
- text: async () => JSON.stringify({ result: mockData, metadata: {} }),
73
- });
74
-
75
- const result = await client.readAll();
76
-
77
- expect(result).toEqual({ result: mockData, metadata: {} });
78
- expect(mockHttpService.requestLog.length).toBe(1);
79
- expect(mockHttpService.requestLog[0].url).toBe('https://api.example.com/api/test/ReadAll');
80
- expect(mockHttpService.requestLog[0].config?.method).toBe('GET');
81
- });
82
-
83
- it('should use injected HTTP service for readSingle', async () => {
84
- const mockData = { id: 1, name: 'Test' };
85
- mockHttpService.setMockResponse('https://api.example.com/api/test/ReadSingle?id=1', {
86
- ok: true,
87
- status: 200,
88
- statusText: 'OK',
89
- headers: {},
90
- json: async () => ({ success: true, result: mockData, metadata: {} }),
91
- text: async () => JSON.stringify({ result: mockData, metadata: {} }),
92
- });
93
-
94
- const result = await client.readSingle(1);
95
-
96
- expect(result).toEqual({ result: mockData, metadata: {} });
97
- expect(mockHttpService.requestLog.length).toBe(1);
98
- expect(mockHttpService.requestLog[0].url).toBe('https://api.example.com/api/test/ReadSingle?id=1');
99
- });
100
-
101
- it('should use injected HTTP service for readSelected', async () => {
102
- const mockData = [{ id: 1, name: 'Test' }];
103
- const definition = {
104
- searchDefinition: {
105
- searches: [],
106
- logicalOperator: ReadSelectedLogicalOperator.And,
107
- },
108
- };
109
-
110
- mockHttpService.setMockResponse('https://api.example.com/api/test/ReadSelected', {
111
- ok: true,
112
- status: 200,
113
- statusText: 'OK',
114
- headers: {},
115
- json: async () => ({ success: true, result: mockData, metadata: {} }),
116
- text: async () => JSON.stringify({ result: mockData, metadata: {} }),
117
- });
118
-
119
- const result = await client.readSelected(definition);
120
-
121
- expect(result).toEqual({ result: mockData, metadata: {} });
122
- expect(mockHttpService.requestLog.length).toBe(1);
123
- expect(mockHttpService.requestLog[0].url).toBe('https://api.example.com/api/test/ReadSelected');
124
- expect(mockHttpService.requestLog[0].config?.method).toBe('POST');
125
- expect(mockHttpService.requestLog[0].config?.body).toBe(JSON.stringify(definition));
126
- });
127
-
128
- it('should handle HTTP errors properly', async () => {
129
- mockHttpService.setMockResponse('https://api.example.com/api/test/ReadAll', {
130
- ok: false,
131
- status: 500,
132
- statusText: 'Internal Server Error',
133
- headers: {},
134
- json: async () => ({ error: 'Server error' }),
135
- text: async () => '{"error":"Server error"}',
136
- });
137
-
138
- try {
139
- await client.readAll();
140
- fail('Should have thrown an error');
141
- } catch (error: any) {
142
- expect(error.message).toContain('Unknown error fetching all records');
143
- }
144
- });
145
- });
146
-
147
- describe('ApiCrudControllerClient', () => {
148
- let client: ApiCrudControllerClient<number, any, any, any, any>;
149
-
150
- beforeEach(() => {
151
- client = new ApiCrudControllerClient('/api/crud', mockHttpService);
152
- });
153
-
154
- it('should use injected HTTP service for create', async () => {
155
- const newItem = { id: 0, name: 'New Item' };
156
- const createdItem = { id: 1, name: 'New Item' };
157
-
158
- mockHttpService.setMockResponse('https://api.example.com/api/crud/Create', {
159
- ok: true,
160
- status: 201,
161
- statusText: 'Created',
162
- headers: {},
163
- json: async () => ({ success: true, result: createdItem, metadata: {} }),
164
- text: async () => JSON.stringify({ result: createdItem, metadata: {} }),
165
- });
166
-
167
- const result = await client.create(newItem);
168
-
169
- expect(result).toEqual({ result: createdItem, metadata: {} });
170
- expect(mockHttpService.requestLog.length).toBe(1);
171
- expect(mockHttpService.requestLog[0].url).toBe('https://api.example.com/api/crud/Create');
172
- expect(mockHttpService.requestLog[0].config?.method).toBe('POST');
173
- expect(mockHttpService.requestLog[0].config?.body).toBe(JSON.stringify(newItem));
174
- });
175
-
176
- it('should use injected HTTP service for update', async () => {
177
- const updateItem = { id: 1, name: 'Updated Item' };
178
-
179
- mockHttpService.setMockResponse('https://api.example.com/api/crud/Update?id=1', {
180
- ok: true,
181
- status: 200,
182
- statusText: 'OK',
183
- headers: {},
184
- json: async () => ({ success: true, result: updateItem, metadata: {} }),
185
- text: async () => JSON.stringify({ result: updateItem, metadata: {} }),
186
- });
187
-
188
- const result = await client.update(1, updateItem);
189
-
190
- expect(result).toEqual({ result: updateItem, metadata: {} });
191
- expect(mockHttpService.requestLog.length).toBe(1);
192
- expect(mockHttpService.requestLog[0].url).toBe('https://api.example.com/api/crud/Update?id=1');
193
- expect(mockHttpService.requestLog[0].config?.method).toBe('PUT');
194
- });
195
-
196
- it('should use injected HTTP service for delete', async () => {
197
- mockHttpService.setMockResponse('https://api.example.com/api/crud/Delete?id=1', {
198
- ok: true,
199
- status: 204,
200
- statusText: 'No Content',
201
- headers: {},
202
- json: async () => ({}),
203
- text: async () => '',
204
- });
205
-
206
- await client.delete(1);
207
-
208
- expect(mockHttpService.requestLog.length).toBe(1);
209
- expect(mockHttpService.requestLog[0].url).toBe('https://api.example.com/api/crud/Delete?id=1');
210
- expect(mockHttpService.requestLog[0].config?.method).toBe('DELETE');
211
- });
212
- });
213
-
214
- describe('UserManagementControllerClient', () => {
215
- let client: UserManagementControllerClient<number, any, any, any, any>;
216
-
217
- beforeEach(() => {
218
- client = new UserManagementControllerClient('/api/users', mockHttpService);
219
- });
220
-
221
- it('should use injected HTTP service for getUserRoles', async () => {
222
- const mockRoles = ['admin', 'user'];
223
-
224
- mockHttpService.setMockResponse('https://api.example.com/api/users/GetUserRoles/1', {
225
- ok: true,
226
- status: 200,
227
- statusText: 'OK',
228
- headers: {},
229
- json: async () => ({ success: true, result: mockRoles, metadata: {} }),
230
- text: async () => JSON.stringify({ result: mockRoles, metadata: {} }),
231
- });
232
-
233
- const result = await client.getUserRoles(1);
234
-
235
- expect(result).toEqual({ result: mockRoles, metadata: {} });
236
- expect(mockHttpService.requestLog.length).toBe(1);
237
- expect(mockHttpService.requestLog[0].url).toBe('https://api.example.com/api/users/GetUserRoles/1');
238
- expect(mockHttpService.requestLog[0].config?.method).toBe('GET');
239
- });
240
-
241
- it('should use injected HTTP service for getUserClaims', async () => {
242
- const mockClaims = [
243
- { type: 'role', value: 'admin' },
244
- { type: 'department', value: 'IT' },
245
- ];
246
-
247
- mockHttpService.setMockResponse('https://api.example.com/api/users/GetUserClaims/1', {
248
- ok: true,
249
- status: 200,
250
- statusText: 'OK',
251
- headers: {},
252
- json: async () => ({ success: true, result: mockClaims, metadata: {} }),
253
- text: async () => JSON.stringify({ result: mockClaims, metadata: {} }),
254
- });
255
-
256
- const result = await client.getUserClaims(1);
257
-
258
- expect(result).toEqual({ result: mockClaims, metadata: {} });
259
- expect(mockHttpService.requestLog.length).toBe(1);
260
- expect(mockHttpService.requestLog[0].url).toBe('https://api.example.com/api/users/GetUserClaims/1');
261
- expect(mockHttpService.requestLog[0].config?.method).toBe('GET');
262
- });
263
-
264
- it('should use injected HTTP service for getAllRoles', async () => {
265
- const mockRoles = ['admin', 'user', 'moderator'];
266
-
267
- mockHttpService.setMockResponse('https://api.example.com/api/users/GetAllRoles', {
268
- ok: true,
269
- status: 200,
270
- statusText: 'OK',
271
- headers: {},
272
- json: async () => ({ success: true, result: mockRoles, metadata: {} }),
273
- text: async () => JSON.stringify({ result: mockRoles, metadata: {} }),
274
- });
275
-
276
- const result = await client.getAllRoles();
277
-
278
- expect(result).toEqual({ result: mockRoles, metadata: {} });
279
- expect(mockHttpService.requestLog.length).toBe(1);
280
- expect(mockHttpService.requestLog[0].url).toBe('https://api.example.com/api/users/GetAllRoles');
281
- expect(mockHttpService.requestLog[0].config?.method).toBe('GET');
282
- });
283
- });
284
- });
@@ -1,392 +0,0 @@
1
- const mockReadSelectedAsync = jest.fn<(definition: unknown) => Promise<unknown>>();
2
-
3
- jest.mock('@services', () => {
4
- return {
5
- ApiReadControllerClient: jest.fn().mockImplementation(() => ({
6
- readSelectedAsync: mockReadSelectedAsync,
7
- })),
8
- };
9
- });
10
-
11
- jest.mock('@intellegens/cornerstone-client', () => {
12
- return {
13
- ReadSelectedOrderingDirection: {
14
- Ascending: 'Ascending',
15
- Descending: 'Descending',
16
- },
17
- ReadSelectedLogicalOperator: {
18
- And: 'And',
19
- Or: 'Or',
20
- },
21
- ReadSelectedComparisonOperator: {
22
- Contains: 'Contains',
23
- Equal: 'Equal',
24
- GreaterOrEqual: 'GreaterOrEqual',
25
- LessOrEqual: 'LessOrEqual',
26
- },
27
- ReadSelectedPropertyType: {
28
- String: 'String',
29
- Int: 'Int',
30
- DateTimeOffset: 'DateTimeOffset',
31
- },
32
- };
33
- });
34
-
35
- import { jest, expect, describe, test, beforeEach, afterEach } from '@jest/globals';
36
- import { ReadSelectedComparisonOperator, ReadSelectedLogicalOperator, ReadSelectedOrderingDirection, ReadSelectedPropertyType } from '../src/data';
37
- import { CollectionViewAdapter, CollectionViewAdapterOptions } from '../src/adapters';
38
- import { dateInterval, searchTerm } from '../src/utils';
39
-
40
- type TestDto = { id: number; name: string; createdAt: string; rowVersion: string };
41
-
42
- const makeItems = (n: number): TestDto[] =>
43
- Array.from({ length: n }, (_, i) => ({
44
- id: i + 1,
45
- name: `Item ${i + 1}`,
46
- createdAt: new Date(2024, 0, i + 1).toISOString(),
47
- rowVersion: '1',
48
- }));
49
-
50
- const fetchData = async () => {
51
- // _fetchCurrentPageData() in CollectionViewAdapter has 50ms timeout set
52
- jest.advanceTimersByTime(60);
53
- // allow any pending microtasks to resolve
54
- await Promise.resolve();
55
- };
56
-
57
- describe('CollectionViewAdapter', () => {
58
- beforeEach(() => {
59
- jest.useFakeTimers();
60
- mockReadSelectedAsync.mockReset();
61
- });
62
-
63
- afterEach(() => {
64
- jest.useRealTimers();
65
- });
66
-
67
- // overrides?: Partial for passing options properties from test:
68
- // const { adapter, callback } = setup({ useTotalItemCount: true });
69
- const setup = (overrides?: Partial<CollectionViewAdapterOptions<TestDto>>) => {
70
- const options: CollectionViewAdapterOptions<TestDto> = {
71
- pagination: {
72
- useTotalItemCount: true,
73
- pageSize: 10,
74
- pageNumber: 1,
75
- ...overrides?.pagination,
76
- },
77
- ordering: {
78
- maxActiveOrderingColumns: 1,
79
- orderByPaths: [],
80
- ...overrides?.ordering,
81
- },
82
- search: {
83
- textSearchableProperties: ['name'],
84
- numericSearchableProperties: ['id'],
85
- ...overrides?.search,
86
- },
87
- };
88
-
89
- const callback = jest.fn();
90
- const adapter = new CollectionViewAdapter<number, TestDto, TestDto>('ControllerName', callback, options);
91
-
92
- return { adapter, callback };
93
- };
94
-
95
- test('callback returns correct loading status and collection data', async () => {
96
- const data = makeItems(10);
97
- mockReadSelectedAsync.mockResolvedValueOnce({ result: data, metadata: { totalCount: 123 } });
98
-
99
- const { adapter, callback } = setup({ pagination: { useTotalItemCount: true } });
100
-
101
- await fetchData();
102
-
103
- expect(callback).toHaveBeenNthCalledWith(1, true, []);
104
- expect(callback).toHaveBeenLastCalledWith(false, data);
105
- expect(adapter.totalItemCount).toBe(123);
106
- });
107
-
108
- test('adapter options set correct ordering', async () => {
109
- mockReadSelectedAsync.mockResolvedValue({ result: makeItems(3), metadata: {} });
110
-
111
- const { adapter } = setup();
112
- const promise = adapter.setOrdering(['name'], ReadSelectedOrderingDirection.Descending);
113
-
114
- await fetchData();
115
- await promise;
116
-
117
- expect(mockReadSelectedAsync).toHaveBeenLastCalledWith(
118
- expect.objectContaining({
119
- orderingDefinition: {
120
- order: [
121
- {
122
- propertyPath: ['Name'], // PascalCased
123
- direction: ReadSelectedOrderingDirection.Descending,
124
- },
125
- ],
126
- },
127
- }),
128
- undefined, // The abort signal parameter
129
- );
130
-
131
- expect(adapter.getCurrentOrdering()).toEqual({
132
- orderByPath: ['name'],
133
- orderDirection: ReadSelectedOrderingDirection.Descending,
134
- });
135
- });
136
-
137
- test('text search is applied to adapter', async () => {
138
- mockReadSelectedAsync.mockResolvedValue({ result: makeItems(1), metadata: {} });
139
-
140
- const { adapter } = setup({ search: { textSearchableProperties: ['name'] } });
141
-
142
- const searchDefinition = searchTerm('Waldo', ['name'], ['id']);
143
- if (!searchDefinition) throw new Error('searchDefinition is undefined');
144
- const promise = adapter.setSearchDefinition(searchDefinition);
145
- await fetchData();
146
- await promise;
147
-
148
- expect(mockReadSelectedAsync).toHaveBeenLastCalledWith(
149
- expect.objectContaining({
150
- searchDefinition: expect.objectContaining({
151
- logicalOperator: expect.any(Number),
152
- propertyCriteria: expect.arrayContaining([
153
- expect.objectContaining({
154
- propertyPath: 'Name',
155
- comparisonOperator: ReadSelectedComparisonOperator.Contains,
156
- valueType: ReadSelectedPropertyType.String,
157
- value: 'Waldo',
158
- }),
159
- ]),
160
- }),
161
- }),
162
- undefined, // The abort signal parameter
163
- );
164
- });
165
-
166
- test('numeric search is applied to adapter', async () => {
167
- mockReadSelectedAsync.mockResolvedValue({ result: makeItems(1), metadata: {} });
168
-
169
- const { adapter } = setup({ search: { textSearchableProperties: ['id'] } });
170
-
171
- const searchDefinition = searchTerm('42', ['name'], ['id']);
172
- if (!searchDefinition) throw new Error('searchDefinition is undefined');
173
- const promise = adapter.setSearchDefinition(searchDefinition);
174
- await fetchData();
175
- await promise;
176
-
177
- expect(mockReadSelectedAsync).toHaveBeenLastCalledWith(
178
- expect.objectContaining({
179
- searchDefinition: expect.objectContaining({
180
- logicalOperator: expect.any(Number),
181
- propertyCriteria: expect.arrayContaining([
182
- expect.objectContaining({
183
- propertyPath: ['Id'],
184
- comparisonOperator: ReadSelectedComparisonOperator.Equal,
185
- valueType: ReadSelectedPropertyType.Int,
186
- value: 42,
187
- }),
188
- ]),
189
- }),
190
- }),
191
- undefined,
192
- );
193
- });
194
-
195
- test('applies date range filter from-to on the same date type property', async () => {
196
- mockReadSelectedAsync.mockResolvedValue({ result: makeItems(1), metadata: {} });
197
-
198
- const { adapter } = setup();
199
- const from = new Date('2024-01-05T00:00:00.000Z');
200
- const to = new Date('2024-01-20T00:00:00.000Z');
201
-
202
- const searchDefinition = dateInterval<TestDto>(
203
- from,
204
- to,
205
- ReadSelectedComparisonOperator.GreaterOrEqual,
206
- ReadSelectedComparisonOperator.LessOrEqual,
207
- ReadSelectedPropertyType.DateTimeOffset,
208
- 'createdAt',
209
- );
210
- if (!searchDefinition) throw new Error('searchDefinition is undefined');
211
- const promise = adapter.setSearchDefinition(searchDefinition);
212
- await fetchData();
213
- await promise;
214
-
215
- expect(mockReadSelectedAsync).toHaveBeenLastCalledWith(
216
- expect.objectContaining({
217
- searchDefinition: expect.objectContaining({
218
- logicalOperator: ReadSelectedLogicalOperator.And,
219
- propertyCriteria: expect.arrayContaining([
220
- expect.objectContaining({
221
- propertyPath: ['DateProperty'],
222
- comparisonOperator: ReadSelectedComparisonOperator.GreaterOrEqual,
223
- valueType: ReadSelectedPropertyType.DateTimeOffset,
224
- value: expect.any(Object),
225
- }),
226
- expect.objectContaining({
227
- propertyPath: ['DateProperty'],
228
- comparisonOperator: ReadSelectedComparisonOperator.LessOrEqual,
229
- valueType: ReadSelectedPropertyType.DateTimeOffset,
230
- value: expect.any(Object),
231
- }),
232
- ]),
233
- }),
234
- }),
235
- undefined, // The abort signal parameter
236
- );
237
- });
238
-
239
- test('applies pagination: page size and jump to page set skip/limit', async () => {
240
- mockReadSelectedAsync.mockResolvedValue({ result: makeItems(5), metadata: {} });
241
-
242
- const { adapter } = setup({ pagination: { useTotalItemCount: true, pageSize: 5, pageNumber: 1 } });
243
-
244
- const promise = adapter.jumpToPage(3);
245
- await fetchData();
246
- await promise;
247
-
248
- expect(mockReadSelectedAsync).toHaveBeenLastCalledWith(
249
- // ReadSelectedDefinitionDto
250
- expect.objectContaining({
251
- paginationDefinition: { skip: 10, limit: 5 },
252
- searchDefinition: undefined,
253
- }),
254
- undefined, // The abort signal parameter
255
- );
256
- });
257
-
258
- test('calculates total when useTotalItemCount=false and last page is partial', async () => {
259
- mockReadSelectedAsync.mockResolvedValue({ result: makeItems(3), metadata: {} });
260
-
261
- const { adapter } = setup({ pagination: { useTotalItemCount: false, pageSize: 5, pageNumber: 2 } });
262
-
263
- await fetchData();
264
-
265
- expect(adapter.totalItemCount).toBe(8); // 3 items on page 2 (5 skipped on first page) ... 5+3=8
266
- });
267
-
268
- test('setOrdering correctly handles PropertyPathDto array comparison and limits columns', async () => {
269
- mockReadSelectedAsync.mockResolvedValue({ result: makeItems(3), metadata: {} });
270
-
271
- const { adapter } = setup({ ordering: { maxActiveOrderingColumns: 2, orderByPaths: [] } });
272
-
273
- // Add first ordering
274
- let promise = adapter.setOrdering(['name'], ReadSelectedOrderingDirection.Descending);
275
- await fetchData();
276
- await promise;
277
-
278
- expect(adapter.getCurrentOrdering()).toEqual({
279
- orderByPath: ['name'],
280
- orderDirection: ReadSelectedOrderingDirection.Descending,
281
- });
282
-
283
- // Add second ordering
284
- promise = adapter.setOrdering(['id'], ReadSelectedOrderingDirection.Ascending);
285
- await fetchData();
286
- await promise;
287
-
288
- // Should have both orderings, with 'id' first (most recent)
289
- expect(mockReadSelectedAsync).toHaveBeenLastCalledWith(
290
- expect.objectContaining({
291
- orderingDefinition: {
292
- order: [
293
- {
294
- propertyPath: ['Id'], // PascalCased
295
- direction: ReadSelectedOrderingDirection.Ascending,
296
- },
297
- {
298
- propertyPath: ['Name'], // PascalCased
299
- direction: ReadSelectedOrderingDirection.Descending,
300
- },
301
- ],
302
- },
303
- }),
304
- undefined,
305
- );
306
-
307
- // Add third ordering - should remove the oldest one (name)
308
- promise = adapter.setOrdering(['createdAt'], ReadSelectedOrderingDirection.Descending);
309
- await fetchData();
310
- await promise;
311
-
312
- expect(mockReadSelectedAsync).toHaveBeenLastCalledWith(
313
- expect.objectContaining({
314
- orderingDefinition: {
315
- order: [
316
- {
317
- propertyPath: ['CreatedAt'], // PascalCased
318
- direction: ReadSelectedOrderingDirection.Descending,
319
- },
320
- {
321
- propertyPath: ['Id'], // PascalCased
322
- direction: ReadSelectedOrderingDirection.Ascending,
323
- },
324
- ],
325
- },
326
- }),
327
- undefined,
328
- );
329
-
330
- // Update existing ordering - should replace, not add
331
- promise = adapter.setOrdering(['id'], ReadSelectedOrderingDirection.Descending);
332
- await fetchData();
333
- await promise;
334
-
335
- expect(mockReadSelectedAsync).toHaveBeenLastCalledWith(
336
- expect.objectContaining({
337
- orderingDefinition: {
338
- order: [
339
- {
340
- propertyPath: ['Id'], // PascalCased - updated direction
341
- direction: ReadSelectedOrderingDirection.Descending,
342
- },
343
- {
344
- propertyPath: ['CreatedAt'], // PascalCased
345
- direction: ReadSelectedOrderingDirection.Descending,
346
- },
347
- ],
348
- },
349
- }),
350
- undefined,
351
- );
352
- });
353
-
354
- test('setOrdering correctly compares complex PropertyPathDto arrays', async () => {
355
- mockReadSelectedAsync.mockResolvedValue({ result: makeItems(3), metadata: {} });
356
-
357
- const { adapter } = setup({ ordering: { maxActiveOrderingColumns: 3, orderByPaths: [] } });
358
-
359
- // Add ordering with nested property path
360
- let promise = adapter.setOrdering(['user', 'profile', 'name'], ReadSelectedOrderingDirection.Ascending);
361
- await fetchData();
362
- await promise;
363
-
364
- // Add different nested path
365
- promise = adapter.setOrdering(['user', 'settings', 'theme'], ReadSelectedOrderingDirection.Descending);
366
- await fetchData();
367
- await promise;
368
-
369
- // Update the first nested path - should replace it, not add new
370
- promise = adapter.setOrdering(['user', 'profile', 'name'], ReadSelectedOrderingDirection.Descending);
371
- await fetchData();
372
- await promise;
373
-
374
- expect(mockReadSelectedAsync).toHaveBeenLastCalledWith(
375
- expect.objectContaining({
376
- orderingDefinition: {
377
- order: [
378
- {
379
- propertyPath: ['User', 'Profile', 'Name'], // PascalCased - updated direction
380
- direction: ReadSelectedOrderingDirection.Descending,
381
- },
382
- {
383
- propertyPath: ['User', 'Settings', 'Theme'], // PascalCased
384
- direction: ReadSelectedOrderingDirection.Descending,
385
- },
386
- ],
387
- },
388
- }),
389
- undefined,
390
- );
391
- });
392
- });