@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.
- package/dist/adapters/CollectionViewAdapter/index.integration.test.d.ts +1 -0
- package/dist/adapters/CollectionViewAdapter/index.integration.test.js +163 -0
- package/dist/adapters/SearchAdapter/index.d.ts +5 -1
- package/dist/adapters/SearchAdapter/index.js +25 -13
- package/dist/data/auth/dto/index.d.ts +1 -0
- package/dist/data/auth/dto/index.js +1 -0
- package/dist/services/api/ApiCrudControllerClient/index.integration.test.d.ts +1 -0
- package/dist/services/api/ApiCrudControllerClient/index.integration.test.js +34 -0
- package/dist/services/api/ApiReadControllerClient/index.integration.test.d.ts +1 -0
- package/dist/services/api/ApiReadControllerClient/index.integration.test.js +59 -0
- package/dist/services/api/HttpService/FetchHttpService.integration.test.d.ts +1 -0
- package/dist/services/api/HttpService/FetchHttpService.integration.test.js +52 -0
- package/dist/services/api/UserManagementControllerClient/index.d.ts +0 -6
- package/dist/services/api/UserManagementControllerClient/index.integration.test.d.ts +1 -0
- package/dist/services/api/UserManagementControllerClient/index.integration.test.js +60 -0
- package/dist/services/api/UserManagementControllerClient/index.js +0 -29
- package/dist/services/auth/client/AuthService/index.d.ts +10 -2
- package/dist/services/auth/client/AuthService/index.js +40 -0
- package/dist/services/auth/client/AuthorizationManagementControllerClient/index.integration.test.d.ts +1 -0
- package/dist/services/auth/client/AuthorizationManagementControllerClient/index.integration.test.js +89 -0
- package/package.json +6 -8
- package/src/adapters/CollectionViewAdapter/index.integration.test.ts +197 -0
- package/src/adapters/SearchAdapter/index.ts +30 -13
- package/src/data/api/dto/response/ApiSuccessResponseDto.ts +1 -1
- package/src/data/auth/dto/index.ts +1 -0
- package/src/services/api/ApiCrudControllerClient/index.integration.test.ts +46 -0
- package/src/services/api/ApiReadControllerClient/index.integration.test.ts +71 -0
- package/src/services/api/HttpService/FetchHttpService.integration.test.ts +65 -0
- package/src/services/api/UserManagementControllerClient/index.integration.test.ts +69 -0
- package/src/services/api/UserManagementControllerClient/index.ts +0 -32
- package/src/services/auth/client/AuthService/index.ts +48 -2
- package/src/services/auth/client/AuthorizationManagementControllerClient/index.integration.test.ts +110 -0
- package/vitest-setup.ts +43 -0
- package/vitest.config.ts +59 -0
- package/jest.config.js +0 -29
- package/tests/ApiClients.test.ts +0 -284
- package/tests/CollectionViewAdapter.test.ts +0 -392
- package/tests/HttpService.test.ts +0 -303
- package/tests/setup.ts +0 -76
package/tests/ApiClients.test.ts
DELETED
|
@@ -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
|
-
});
|