@slickgrid-universal/sql 0.0.1 → 10.4.0
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/README.md +11 -39
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/index.d.ts +4 -0
- package/dist/interfaces/index.d.ts.map +1 -0
- package/dist/interfaces/index.js +4 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/interfaces/sqlResult.interface.d.ts +6 -0
- package/dist/interfaces/sqlResult.interface.d.ts.map +1 -0
- package/dist/interfaces/sqlResult.interface.js +2 -0
- package/dist/interfaces/sqlResult.interface.js.map +1 -0
- package/dist/interfaces/sqlServiceApi.interface.d.ts +22 -0
- package/dist/interfaces/sqlServiceApi.interface.d.ts.map +1 -0
- package/dist/interfaces/sqlServiceApi.interface.js +2 -0
- package/dist/interfaces/sqlServiceApi.interface.js.map +1 -0
- package/dist/interfaces/sqlServiceOption.interface.d.ts +54 -0
- package/dist/interfaces/sqlServiceOption.interface.d.ts.map +1 -0
- package/dist/interfaces/sqlServiceOption.interface.js +2 -0
- package/dist/interfaces/sqlServiceOption.interface.js.map +1 -0
- package/dist/services/sql.service.d.ts +69 -0
- package/dist/services/sql.service.d.ts.map +1 -0
- package/dist/services/sql.service.js +600 -0
- package/dist/services/sql.service.js.map +1 -0
- package/package.json +44 -7
- package/src/index.ts +2 -0
- package/src/interfaces/index.ts +3 -0
- package/src/interfaces/sqlResult.interface.ts +6 -0
- package/src/interfaces/sqlServiceApi.interface.ts +27 -0
- package/src/interfaces/sqlServiceOption.interface.ts +61 -0
- package/src/services/__tests__/sql.service.spec.ts +1819 -0
- package/src/services/sql.service.ts +691 -0
|
@@ -0,0 +1,1819 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type BackendService,
|
|
3
|
+
type Column,
|
|
4
|
+
type ColumnFilter,
|
|
5
|
+
type ColumnFilters,
|
|
6
|
+
type ColumnSort,
|
|
7
|
+
type CurrentFilter,
|
|
8
|
+
type CurrentSorter,
|
|
9
|
+
type FilterChangedArgs,
|
|
10
|
+
type GridOption,
|
|
11
|
+
type MultiColumnSort,
|
|
12
|
+
type Pagination,
|
|
13
|
+
type SlickGrid,
|
|
14
|
+
} from '@slickgrid-universal/common';
|
|
15
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
16
|
+
import type { SqlServiceOption } from '../../interfaces/sqlServiceOption.interface.js';
|
|
17
|
+
import { SqlService } from './../sql.service.js';
|
|
18
|
+
|
|
19
|
+
function removeSpaces(text: string) {
|
|
20
|
+
// Replace multiple spaces with a single space, trim leading/trailing
|
|
21
|
+
return `${text}`.replace(/\s+/g, ' ').trim();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let gridOptionMock: GridOption;
|
|
25
|
+
|
|
26
|
+
const gridStub = {
|
|
27
|
+
autosizeColumns: vi.fn(),
|
|
28
|
+
getColumnIndex: vi.fn(),
|
|
29
|
+
getScrollbarDimensions: vi.fn(),
|
|
30
|
+
getOptions: () => gridOptionMock,
|
|
31
|
+
getColumns: vi.fn(),
|
|
32
|
+
setColumns: vi.fn(),
|
|
33
|
+
registerPlugin: vi.fn(),
|
|
34
|
+
setSelectedRows: vi.fn(),
|
|
35
|
+
setSortColumns: vi.fn(),
|
|
36
|
+
scrollTo: vi.fn(),
|
|
37
|
+
} as unknown as SlickGrid;
|
|
38
|
+
|
|
39
|
+
describe('SqlService', () => {
|
|
40
|
+
let mockColumns: Column[];
|
|
41
|
+
let service: SqlService;
|
|
42
|
+
let paginationOptions: Pagination;
|
|
43
|
+
let serviceOptions: SqlServiceOption;
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
mockColumns = [
|
|
47
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
48
|
+
{ id: 'field2', field: 'field2', width: 100 },
|
|
49
|
+
];
|
|
50
|
+
service = new SqlService();
|
|
51
|
+
serviceOptions = {
|
|
52
|
+
tableName: 'users',
|
|
53
|
+
};
|
|
54
|
+
paginationOptions = {
|
|
55
|
+
pageNumber: 1,
|
|
56
|
+
pageSizes: [5, 10, 25, 50, 100],
|
|
57
|
+
pageSize: 10,
|
|
58
|
+
totalItems: 100,
|
|
59
|
+
};
|
|
60
|
+
gridOptionMock = {
|
|
61
|
+
enablePagination: true,
|
|
62
|
+
defaultFilterRangeOperator: 'RangeInclusive',
|
|
63
|
+
backendServiceApi: {
|
|
64
|
+
service: service as unknown as BackendService,
|
|
65
|
+
options: { datasetName: '' },
|
|
66
|
+
preProcess: vi.fn(),
|
|
67
|
+
process: vi.fn(),
|
|
68
|
+
postProcess: vi.fn(),
|
|
69
|
+
},
|
|
70
|
+
} as unknown as GridOption;
|
|
71
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(mockColumns);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
afterEach(() => {
|
|
75
|
+
vi.clearAllMocks();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should create the service', () => {
|
|
79
|
+
expect(service).toBeTruthy();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('init method', () => {
|
|
83
|
+
it('should initialize the service and expect the service options and pagination to be set', () => {
|
|
84
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
85
|
+
expect(service.options).toEqual(serviceOptions);
|
|
86
|
+
expect(service.getCurrentPagination()).toEqual({
|
|
87
|
+
pageNumber: 1,
|
|
88
|
+
pageSize: 10,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should get the column definitions from "getColumns"', () => {
|
|
93
|
+
const columns = [
|
|
94
|
+
{ id: 'field4', field: 'field4', width: 50 },
|
|
95
|
+
{ id: 'field2', field: 'field2', width: 50 },
|
|
96
|
+
];
|
|
97
|
+
const spy = vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
98
|
+
|
|
99
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
100
|
+
|
|
101
|
+
expect(spy).toHaveBeenCalled();
|
|
102
|
+
expect(service['_columns']).toEqual(columns);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('buildQuery method', () => {
|
|
107
|
+
beforeEach(() => {
|
|
108
|
+
vi.resetAllMocks();
|
|
109
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(mockColumns);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should throw an error when no service options exists after service init', () => {
|
|
113
|
+
service.init(undefined as any);
|
|
114
|
+
expect(() => service.buildQuery()).toThrow();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should throw an error when no tableName is provided in the service options after service init', () => {
|
|
118
|
+
service.init({ tableName: undefined as any });
|
|
119
|
+
expect(() => service.buildQuery()).toThrow('SQL Service requires the "tableName" property and columns to properly build the SQL query');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should throw an error when no column definitions is provided in the service options after service init', () => {
|
|
123
|
+
service.init({ tableName: 'users' });
|
|
124
|
+
expect(() => service.buildQuery()).toThrow();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should return a simple SQL query with pagination set and includes all fields', () => {
|
|
128
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 10 OFFSET 0`;
|
|
129
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
130
|
+
const query = service.buildQuery();
|
|
131
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should return a SQL query with SELECT * when all columns are included', () => {
|
|
135
|
+
const columns = [
|
|
136
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
137
|
+
{ id: 'field2', field: 'field2', width: 100 },
|
|
138
|
+
];
|
|
139
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
140
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
141
|
+
const query = service.buildQuery();
|
|
142
|
+
expect(query.startsWith('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users"')).toBe(true);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should exclude a column and expect a query string without it', () => {
|
|
146
|
+
const columns = [
|
|
147
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
148
|
+
{ id: 'field2', field: 'field2', width: 100, excludeFromQuery: true },
|
|
149
|
+
];
|
|
150
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
151
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
152
|
+
const query = service.buildQuery();
|
|
153
|
+
expect(query).toContain('field1');
|
|
154
|
+
expect(query).not.toContain('field2');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should use "columnDefinitions" from the "serviceOptions" when private member is undefined and then return a simple query as usual', () => {
|
|
158
|
+
const columns = [{ id: 'field1', field: 'field1', width: 100 }];
|
|
159
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
160
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
161
|
+
const query = service.buildQuery();
|
|
162
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 10 OFFSET 0'));
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should return a simple query with pagination set and includes at least one field when the column definitions is an empty array', () => {
|
|
166
|
+
const columns: Column[] = [];
|
|
167
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
168
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
169
|
+
const query = service.buildQuery();
|
|
170
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 10 OFFSET 0'));
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should exclude a column and expect a query string without it', () => {
|
|
174
|
+
const columns = [
|
|
175
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
176
|
+
{ id: 'field2', field: 'field2', width: 100, excludeFromQuery: true },
|
|
177
|
+
];
|
|
178
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
179
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
180
|
+
const query = service.buildQuery();
|
|
181
|
+
expect(query).toContain('field1');
|
|
182
|
+
expect(query).not.toContain('field2');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should use default pagination when "paginationOptions" is not provided', () => {
|
|
186
|
+
const columns = [
|
|
187
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
188
|
+
{ id: 'field2', field: 'field2', width: 100, excludeFromQuery: true },
|
|
189
|
+
];
|
|
190
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
191
|
+
service.init({ tableName: 'users' }, undefined, gridStub);
|
|
192
|
+
const query = service.buildQuery();
|
|
193
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT "field1", COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 20 OFFSET 0'));
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should add extra fields from the "fields" property and expect them to be part of the query string', () => {
|
|
197
|
+
const columns = [
|
|
198
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
199
|
+
{ id: 'field2', field: 'field2', width: 100, fields: ['field3', 'field4'] },
|
|
200
|
+
];
|
|
201
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
202
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
203
|
+
const query = service.buildQuery();
|
|
204
|
+
// Should include field1, field2, field3, field4
|
|
205
|
+
expect(query).toContain('field1');
|
|
206
|
+
expect(query).toContain('field2');
|
|
207
|
+
expect(query).toContain('field3');
|
|
208
|
+
expect(query).toContain('field4');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should exclude a column field, and expect a query string without it, but still include any fields specified', () => {
|
|
212
|
+
const columns = [
|
|
213
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
214
|
+
{ id: 'field2', field: 'field2', fields: ['field3', 'field4', 'field5'], width: 100, excludeFieldFromQuery: true },
|
|
215
|
+
];
|
|
216
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
217
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
218
|
+
const query = service.buildQuery();
|
|
219
|
+
expect(query).toContain('field1');
|
|
220
|
+
expect(query).not.toContain('field2,'); // field2 should be excluded
|
|
221
|
+
expect(query).toContain('field3');
|
|
222
|
+
expect(query).toContain('field4');
|
|
223
|
+
expect(query).toContain('field5');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should exclude pagination from the query string when the option is disabled', () => {
|
|
227
|
+
gridOptionMock.enablePagination = false;
|
|
228
|
+
const columns = [
|
|
229
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
230
|
+
{ id: 'field2', field: 'field2', width: 100 },
|
|
231
|
+
];
|
|
232
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
233
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
234
|
+
// For SQL, if pagination is disabled, LIMIT/OFFSET should be omitted
|
|
235
|
+
const query = service.buildQuery();
|
|
236
|
+
expect(query).toBe('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users"');
|
|
237
|
+
gridOptionMock.enablePagination = true; // reset for other tests
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should have a different pagination offset when it is updated before calling the buildQuery query (presets does that)', () => {
|
|
241
|
+
const columns = [
|
|
242
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
243
|
+
{ id: 'field2', field: 'field2', width: 100 },
|
|
244
|
+
];
|
|
245
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
246
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
247
|
+
service.updatePagination(3, 20);
|
|
248
|
+
const query = service.buildQuery();
|
|
249
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 20 OFFSET 40'));
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should make sure the offset pagination is never below zero, even when new page is 0', () => {
|
|
253
|
+
const columns = [
|
|
254
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
255
|
+
{ id: 'field2', field: 'field2', width: 100 },
|
|
256
|
+
];
|
|
257
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
258
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
259
|
+
service.updatePagination(0, 20);
|
|
260
|
+
const query = service.buildQuery();
|
|
261
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 20 OFFSET 0'));
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('should make sure the offset pagination is never below zero, even when new is 1 the offset should remain 0', () => {
|
|
265
|
+
const columns = [
|
|
266
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
267
|
+
{ id: 'field2', field: 'field2', width: 100 },
|
|
268
|
+
];
|
|
269
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
270
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
271
|
+
service.updatePagination(1, 20);
|
|
272
|
+
const query = service.buildQuery();
|
|
273
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 20 OFFSET 0'));
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe('buildQuery method (parity with GraphQL)', () => {
|
|
278
|
+
beforeEach(() => {
|
|
279
|
+
vi.resetAllMocks();
|
|
280
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(mockColumns);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('should use "columnDefinitions" from the "serviceOptions" when private member is undefined and then return a simple query as usual', () => {
|
|
284
|
+
const columns = [{ id: 'field1', field: 'field1', width: 100 }];
|
|
285
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
286
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
287
|
+
const query = service.buildQuery();
|
|
288
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 10 OFFSET 0'));
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should return a simple query with pagination set and includes at least one field when the column definitions is an empty array', () => {
|
|
292
|
+
const columns: Column[] = [];
|
|
293
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
294
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
295
|
+
const query = service.buildQuery();
|
|
296
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 10 OFFSET 0'));
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should exclude a column and expect a query string without it', () => {
|
|
300
|
+
const columns = [
|
|
301
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
302
|
+
{ id: 'field2', field: 'field2', width: 100, excludeFromQuery: true },
|
|
303
|
+
];
|
|
304
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
305
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
306
|
+
const query = service.buildQuery();
|
|
307
|
+
expect(query).toContain('field1');
|
|
308
|
+
expect(query).not.toContain('field2');
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should use default pagination when "paginationOptions" is not provided', () => {
|
|
312
|
+
const columns = [
|
|
313
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
314
|
+
{ id: 'field2', field: 'field2', width: 100, excludeFromQuery: true },
|
|
315
|
+
];
|
|
316
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
317
|
+
service.init({ tableName: 'users' }, undefined, gridStub);
|
|
318
|
+
const query = service.buildQuery();
|
|
319
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT "field1", COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 20 OFFSET 0'));
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('should add extra fields from the "fields" property and expect them to be part of the query string', () => {
|
|
323
|
+
const columns = [
|
|
324
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
325
|
+
{ id: 'field2', field: 'field2', width: 100, fields: ['field3', 'field4'] },
|
|
326
|
+
];
|
|
327
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
328
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
329
|
+
const query = service.buildQuery();
|
|
330
|
+
// Should include field1, field2, field3, field4
|
|
331
|
+
expect(query).toContain('field1');
|
|
332
|
+
expect(query).toContain('field2');
|
|
333
|
+
expect(query).toContain('field3');
|
|
334
|
+
expect(query).toContain('field4');
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('should exclude a column field, and expect a query string without it, but still include any fields specified', () => {
|
|
338
|
+
const columns = [
|
|
339
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
340
|
+
{ id: 'field2', field: 'field2', fields: ['field3', 'field4', 'field5'], width: 100, excludeFieldFromQuery: true },
|
|
341
|
+
];
|
|
342
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
343
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
344
|
+
const query = service.buildQuery();
|
|
345
|
+
expect(query).toContain('field1');
|
|
346
|
+
expect(query).not.toContain('field2,'); // field2 should be excluded
|
|
347
|
+
expect(query).toContain('field3');
|
|
348
|
+
expect(query).toContain('field4');
|
|
349
|
+
expect(query).toContain('field5');
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('should exclude pagination from the query string when the option is disabled', () => {
|
|
353
|
+
gridOptionMock.enablePagination = false;
|
|
354
|
+
const columns = [
|
|
355
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
356
|
+
{ id: 'field2', field: 'field2', width: 100 },
|
|
357
|
+
];
|
|
358
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
359
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
360
|
+
// For SQL, if pagination is disabled, LIMIT/OFFSET should be omitted
|
|
361
|
+
const query = service.buildQuery();
|
|
362
|
+
expect(query).toBe('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users"');
|
|
363
|
+
gridOptionMock.enablePagination = true; // reset for other tests
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('should have a different pagination offset when it is updated before calling the buildQuery query (presets does that)', () => {
|
|
367
|
+
const columns = [
|
|
368
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
369
|
+
{ id: 'field2', field: 'field2', width: 100 },
|
|
370
|
+
];
|
|
371
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
372
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
373
|
+
service.updatePagination(3, 20);
|
|
374
|
+
const query = service.buildQuery();
|
|
375
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 20 OFFSET 40'));
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('should make sure the offset pagination is never below zero, even when new page is 0', () => {
|
|
379
|
+
const columns = [
|
|
380
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
381
|
+
{ id: 'field2', field: 'field2', width: 100 },
|
|
382
|
+
];
|
|
383
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
384
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
385
|
+
service.updatePagination(0, 20);
|
|
386
|
+
const query = service.buildQuery();
|
|
387
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 20 OFFSET 0'));
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('should make sure the offset pagination is never below zero, even when new is 1 the offset should remain 0', () => {
|
|
391
|
+
const columns = [
|
|
392
|
+
{ id: 'field1', field: 'field1', width: 100 },
|
|
393
|
+
{ id: 'field2', field: 'field2', width: 100 },
|
|
394
|
+
];
|
|
395
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
396
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
397
|
+
service.updatePagination(1, 20);
|
|
398
|
+
const query = service.buildQuery();
|
|
399
|
+
expect(removeSpaces(query)).toBe(removeSpaces('SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 20 OFFSET 0'));
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
describe('clearFilters method', () => {
|
|
404
|
+
it('should call "updateOptions" to clear all filters', () => {
|
|
405
|
+
const spy = vi.spyOn(service, 'updateOptions');
|
|
406
|
+
service.clearFilters();
|
|
407
|
+
expect(spy).toHaveBeenCalledWith({ filteringOptions: [] });
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
describe('clearSorters method', () => {
|
|
412
|
+
it('should call "updateOptions" to clear all sorting', () => {
|
|
413
|
+
const spy = vi.spyOn(service, 'updateOptions');
|
|
414
|
+
service.clearSorters();
|
|
415
|
+
expect(spy).toHaveBeenCalledWith({ sortingOptions: [] });
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
describe('getInitPaginationOptions method', () => {
|
|
420
|
+
beforeEach(() => {
|
|
421
|
+
paginationOptions.pageSize = 20;
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('should return the pagination options with current pagination', () => {
|
|
425
|
+
service.init({ tableName: 'users' }, paginationOptions);
|
|
426
|
+
const output = service.getInitPaginationOptions();
|
|
427
|
+
expect(output).toEqual({ pageSize: 20, offset: 0 });
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('should return the pagination options with default page size of 20 when paginationOptions is undefined', () => {
|
|
431
|
+
service.init({ tableName: 'users' }, undefined);
|
|
432
|
+
const output = service.getInitPaginationOptions();
|
|
433
|
+
expect(output).toEqual({ pageSize: 20, offset: 0 });
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('should return the pagination options with correct offset for pageNumber > 1', () => {
|
|
437
|
+
service.init({ tableName: 'users' }, { pageNumber: 3, pageSize: 10 } as Pagination);
|
|
438
|
+
const output = service.getInitPaginationOptions();
|
|
439
|
+
expect(output).toEqual({ pageSize: 10, offset: 20 });
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
describe('getDatasetName method', () => {
|
|
444
|
+
it('should return the dataset name when defined', () => {
|
|
445
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue([]);
|
|
446
|
+
service.init({ datasetName: 'employees', tableName: 'users' });
|
|
447
|
+
const output = service.getDatasetName();
|
|
448
|
+
expect(output).toBe('employees');
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('should return empty string when dataset name is undefined', () => {
|
|
452
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue([]);
|
|
453
|
+
service.init({ tableName: undefined as any });
|
|
454
|
+
const output = service.getDatasetName();
|
|
455
|
+
expect(output).toBe('');
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
describe('getTableName method', () => {
|
|
460
|
+
it('should return the table name when defined', () => {
|
|
461
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue([]);
|
|
462
|
+
service.init({ tableName: 'users' });
|
|
463
|
+
const output = service.getTableName();
|
|
464
|
+
expect(output).toBe('users');
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('should return empty string when table name is undefined', () => {
|
|
468
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue([]);
|
|
469
|
+
service.init({ tableName: undefined as any });
|
|
470
|
+
const output = service.getTableName();
|
|
471
|
+
expect(output).toBe('');
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
describe('escapeIdentifier', () => {
|
|
476
|
+
let service: SqlService;
|
|
477
|
+
beforeEach(() => {
|
|
478
|
+
service = new SqlService();
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('should escape identifiers with double quotes (default)', () => {
|
|
482
|
+
service.init({ tableName: 'myTable' });
|
|
483
|
+
expect(service['escapeIdentifier']('foo')).toBe('"foo"');
|
|
484
|
+
expect(service['escapeIdentifier']('my"Table')).toBe('"my""Table"');
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('should escape identifiers with backticks', () => {
|
|
488
|
+
service.init({ tableName: 'myTable', identifierEscapeStyle: 'backtick' });
|
|
489
|
+
expect(service['escapeIdentifier']('foo')).toBe('`foo`');
|
|
490
|
+
expect(service['escapeIdentifier']('my`Table')).toBe('`my``Table`');
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('should escape identifiers with brackets', () => {
|
|
494
|
+
service.init({ tableName: 'myTable', identifierEscapeStyle: 'bracket' });
|
|
495
|
+
expect(service['escapeIdentifier']('foo')).toBe('[foo]');
|
|
496
|
+
expect(service['escapeIdentifier']('my]Table')).toBe('[my]]Table]');
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it('should return empty string for undefined or empty identifier', () => {
|
|
500
|
+
service.init({ tableName: 'myTable' });
|
|
501
|
+
expect(service['escapeIdentifier']()).toBe('');
|
|
502
|
+
expect(service['escapeIdentifier']('')).toBe('');
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it('should escape both datasetName and tableName when both are provided', () => {
|
|
506
|
+
service.init({ datasetName: 'mySchema', tableName: 'myTable' });
|
|
507
|
+
// buildQuery will call escapeIdentifier for both datasetName and tableName
|
|
508
|
+
// We check that the generated query contains both escaped identifiers
|
|
509
|
+
// Need to provide columns to avoid error
|
|
510
|
+
(service as any)._columns = [{ field: 'id' }];
|
|
511
|
+
const query = service.buildQuery();
|
|
512
|
+
expect(query).toContain('FROM "mySchema"."myTable"');
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
describe('resetPaginationOptions method', () => {
|
|
517
|
+
beforeEach(() => {
|
|
518
|
+
paginationOptions.pageSize = 20;
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
it('should reset the pagination options with default pagination', () => {
|
|
522
|
+
const spy = vi.spyOn(service, 'updateOptions');
|
|
523
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue([]);
|
|
524
|
+
service.init({ tableName: 'users' }, paginationOptions);
|
|
525
|
+
service.resetPaginationOptions();
|
|
526
|
+
// SQL only resets pageNumber to 1, keeps pageSize
|
|
527
|
+
expect(service.getCurrentPagination()).toEqual({ pageNumber: 1, pageSize: 20 });
|
|
528
|
+
// Optionally, check updateOptions was not called with cursor args
|
|
529
|
+
expect(spy).not.toHaveBeenCalledWith({ paginationOptions: { first: 20 } });
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
describe('processOnFilterChanged method', () => {
|
|
534
|
+
it('should throw an error when grid is undefined', () => {
|
|
535
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
536
|
+
expect(() => service.processOnFilterChanged(null as any, { grid: undefined } as any)).toThrow();
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it('should return a query with the new filter', () => {
|
|
540
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" = 'female' LIMIT 10 OFFSET 0`;
|
|
541
|
+
const querySpy = vi.spyOn(service, 'buildQuery');
|
|
542
|
+
const resetSpy = vi.spyOn(service, 'resetPaginationOptions');
|
|
543
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
544
|
+
const mockColumnFilter = {
|
|
545
|
+
columnDef: mockColumn,
|
|
546
|
+
columnId: 'gender',
|
|
547
|
+
operator: 'EQ',
|
|
548
|
+
searchTerms: ['female'],
|
|
549
|
+
targetSelector: 'div.some-classes',
|
|
550
|
+
} as ColumnFilter;
|
|
551
|
+
const mockFilterChangedArgs = {
|
|
552
|
+
columnDef: mockColumn,
|
|
553
|
+
columnId: 'gender',
|
|
554
|
+
columnFilters: { gender: mockColumnFilter },
|
|
555
|
+
grid: gridStub,
|
|
556
|
+
operator: 'EQ',
|
|
557
|
+
searchTerms: ['female'],
|
|
558
|
+
shouldTriggerQuery: true,
|
|
559
|
+
targetSelector: 'div.some-classes',
|
|
560
|
+
} as FilterChangedArgs;
|
|
561
|
+
|
|
562
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
563
|
+
const query = service.processOnFilterChanged(null as any, mockFilterChangedArgs);
|
|
564
|
+
const currentFilters = service.getCurrentFilters();
|
|
565
|
+
|
|
566
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
567
|
+
expect(querySpy).toHaveBeenCalled();
|
|
568
|
+
expect(resetSpy).toHaveBeenCalled();
|
|
569
|
+
expect(currentFilters).toEqual([
|
|
570
|
+
expect.objectContaining({
|
|
571
|
+
columnId: 'gender',
|
|
572
|
+
operator: 'EQ',
|
|
573
|
+
searchTerms: ['female'],
|
|
574
|
+
targetSelector: 'div.some-classes',
|
|
575
|
+
}),
|
|
576
|
+
]);
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
describe('processOnPaginationChanged method', () => {
|
|
581
|
+
it('should return a query with the new pagination', () => {
|
|
582
|
+
const expectation = 'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 20 OFFSET 40';
|
|
583
|
+
const querySpy = vi.spyOn(service, 'buildQuery');
|
|
584
|
+
|
|
585
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
586
|
+
const query = service.processOnPaginationChanged(null as any, { newPage: 3, pageSize: 20 });
|
|
587
|
+
const currentPagination = service.getCurrentPagination();
|
|
588
|
+
|
|
589
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
590
|
+
expect(querySpy).toHaveBeenCalled();
|
|
591
|
+
expect(currentPagination).toEqual({ pageNumber: 3, pageSize: 20 });
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('should return a query with the new pagination and use pagination size options that was passed to service options when it is not provided as argument to "processOnPaginationChanged"', () => {
|
|
595
|
+
const expectation = 'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 10 OFFSET 20';
|
|
596
|
+
const querySpy = vi.spyOn(service, 'buildQuery');
|
|
597
|
+
|
|
598
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
599
|
+
const query = service.processOnPaginationChanged(null as any, { newPage: 3 } as any);
|
|
600
|
+
const currentPagination = service.getCurrentPagination();
|
|
601
|
+
|
|
602
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
603
|
+
expect(querySpy).toHaveBeenCalled();
|
|
604
|
+
expect(currentPagination).toEqual({ pageNumber: 3, pageSize: 10 });
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
it('should return a query with the new pagination and use default pagination size (25) when not provided as argument', () => {
|
|
608
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 20 OFFSET 40`;
|
|
609
|
+
const querySpy = vi.spyOn(service, 'buildQuery');
|
|
610
|
+
|
|
611
|
+
service.init(serviceOptions, undefined, gridStub);
|
|
612
|
+
const query = service.processOnPaginationChanged(null as any, { newPage: 3 } as any);
|
|
613
|
+
const currentPagination = service.getCurrentPagination();
|
|
614
|
+
|
|
615
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
616
|
+
expect(querySpy).toHaveBeenCalled();
|
|
617
|
+
expect(currentPagination).toEqual({ pageNumber: 3, pageSize: 20 });
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
describe('processOnSortChanged method', () => {
|
|
622
|
+
it('should return a query with the new sorting when using single sort', () => {
|
|
623
|
+
const expectation = 'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" ORDER BY "gender" DESC LIMIT 10 OFFSET 0';
|
|
624
|
+
const querySpy = vi.spyOn(service, 'buildQuery');
|
|
625
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
626
|
+
const mockSortChangedArgs = { columnId: 'gender', sortCol: mockColumn, sortAsc: false, multiColumnSort: false } as ColumnSort;
|
|
627
|
+
|
|
628
|
+
service.init({ ...serviceOptions, infiniteScroll: true }, { ...paginationOptions, pageSize: 10 }, gridStub);
|
|
629
|
+
const query = service.processOnSortChanged(null as any, mockSortChangedArgs);
|
|
630
|
+
|
|
631
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
632
|
+
expect(querySpy).toHaveBeenCalled();
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('should return a query with the multiple new sorting when using multiColumnSort', () => {
|
|
636
|
+
const expectation = 'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" ORDER BY "gender" DESC, "firstName" ASC LIMIT 10 OFFSET 0';
|
|
637
|
+
const querySpy = vi.spyOn(service, 'buildQuery');
|
|
638
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
639
|
+
const mockColumnName = { id: 'firstName', field: 'firstName' } as Column;
|
|
640
|
+
const mockColumnSort = { columnId: 'gender', sortCol: mockColumn, sortAsc: false } as ColumnSort;
|
|
641
|
+
const mockColumnSortName = { columnId: 'firstName', sortCol: mockColumnName, sortAsc: true } as ColumnSort;
|
|
642
|
+
const mockSortChangedArgs = { sortCols: [mockColumnSort, mockColumnSortName], multiColumnSort: true, grid: gridStub } as MultiColumnSort;
|
|
643
|
+
|
|
644
|
+
service.init(serviceOptions, { ...paginationOptions, pageSize: 10 }, gridStub);
|
|
645
|
+
const query = service.processOnSortChanged(null as any, mockSortChangedArgs);
|
|
646
|
+
|
|
647
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
648
|
+
expect(querySpy).toHaveBeenCalled();
|
|
649
|
+
});
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
describe('updateFilters method', () => {
|
|
653
|
+
beforeEach(() => {
|
|
654
|
+
const columns = [
|
|
655
|
+
{ id: 'company', field: 'company' },
|
|
656
|
+
{ id: 'gender', field: 'gender' },
|
|
657
|
+
{ id: 'name', field: 'name' },
|
|
658
|
+
];
|
|
659
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
it('should throw an error when filter columnId is not found to be part of the column definitions', () => {
|
|
663
|
+
const mockCurrentFilter = { columnDef: { id: 'city', field: 'city' }, columnId: 'city', operator: 'EQ', searchTerms: ['Boston'] } as CurrentFilter;
|
|
664
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
665
|
+
expect(() => service.updateFilters([mockCurrentFilter], true)).toThrow('[SQL Service]: Something went wrong in trying to get the column definition');
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it('should throw an error when neither "field" nor "name" are being part of the column definition', () => {
|
|
669
|
+
const mockColumnFilters = {
|
|
670
|
+
gender: { columnId: 'gender', columnDef: { id: 'gender' }, searchTerms: ['female'], operator: 'EQ' },
|
|
671
|
+
} as unknown as ColumnFilters;
|
|
672
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
673
|
+
expect(() => service.updateFilters(mockColumnFilters, false)).toThrow('SQL filter could not find the field name to query the search');
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it('should return a query with the new filter when filters are passed as a filter trigger by a filter event and is of type ColumnFilters', () => {
|
|
677
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" = 'female' LIMIT 10 OFFSET 0`;
|
|
678
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
679
|
+
const mockColumnFilters = {
|
|
680
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: 'string' },
|
|
681
|
+
} as ColumnFilters;
|
|
682
|
+
|
|
683
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
684
|
+
service.updateFilters(mockColumnFilters, false);
|
|
685
|
+
const query = service.buildQuery();
|
|
686
|
+
|
|
687
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it('should return a query without filtering when the filter "searchTerms" property is missing from the search', () => {
|
|
691
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 10 OFFSET 0`;
|
|
692
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
693
|
+
const mockColumnFilters = {
|
|
694
|
+
gender: { columnId: 'gender', columnDef: mockColumn, operator: 'EQ', type: 'string' },
|
|
695
|
+
} as unknown as ColumnFilters;
|
|
696
|
+
|
|
697
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
698
|
+
service.updateFilters(mockColumnFilters, false);
|
|
699
|
+
const query = service.buildQuery();
|
|
700
|
+
|
|
701
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
it('should return a query with multiple filters when the filters object has multiple search with Equal & "<>" and they are passed as a filter trigger by a filter event and is of type ColumnFilters', () => {
|
|
705
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" = 'female' AND "company" NOT LIKE '%abc%' LIMIT 10 OFFSET 0`;
|
|
706
|
+
const mockColumnGender = { id: 'gender', field: 'gender' } as Column;
|
|
707
|
+
const mockColumnCompany = { id: 'company', field: 'company' } as Column;
|
|
708
|
+
const mockColumnFilters = {
|
|
709
|
+
gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ', type: 'string' },
|
|
710
|
+
company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: '<>', type: 'string' },
|
|
711
|
+
} as ColumnFilters;
|
|
712
|
+
|
|
713
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
714
|
+
service.updateFilters(mockColumnFilters, false);
|
|
715
|
+
const query = service.buildQuery();
|
|
716
|
+
|
|
717
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it('should return a query with multiple filters when the filters object has multiple search with Equal & Not Contains and they are passed as a filter trigger by a filter event and is of type ColumnFilters', () => {
|
|
721
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" = 'female' AND "company" NOT LIKE '%abc%' LIMIT 10 OFFSET 0`;
|
|
722
|
+
const mockColumnGender = { id: 'gender', field: 'gender' } as Column;
|
|
723
|
+
const mockColumnCompany = { id: 'company', field: 'company' } as Column;
|
|
724
|
+
const mockColumnFilters = {
|
|
725
|
+
gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ', type: 'string' },
|
|
726
|
+
company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Not_Contains', type: 'string' },
|
|
727
|
+
} as ColumnFilters;
|
|
728
|
+
|
|
729
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
730
|
+
service.updateFilters(mockColumnFilters, false);
|
|
731
|
+
const query = service.buildQuery();
|
|
732
|
+
|
|
733
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
it('should return a query with multiple filters and expect same query string result as previous test even with "isUpdatedByPreset" enabled', () => {
|
|
737
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" = 'female' AND "company" LIKE '%abc%' LIMIT 10 OFFSET 0`;
|
|
738
|
+
const mockColumnGender = { id: 'gender', field: 'gender' } as Column;
|
|
739
|
+
const mockColumnCompany = { id: 'company', field: 'company' } as Column;
|
|
740
|
+
const mockColumnFilters = {
|
|
741
|
+
gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ', type: 'string' },
|
|
742
|
+
company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: 'string' },
|
|
743
|
+
} as ColumnFilters;
|
|
744
|
+
|
|
745
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
746
|
+
service.updateFilters(mockColumnFilters, true);
|
|
747
|
+
const query = service.buildQuery();
|
|
748
|
+
|
|
749
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
it('should return a query with the new filter when filters are passed as a Grid Preset of type CurrentFilter', () => {
|
|
753
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" = 'female' LIMIT 10 OFFSET 0`;
|
|
754
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
755
|
+
const mockCurrentFilter = { columnDef: mockColumn, columnId: 'gender', operator: 'EQ', searchTerms: ['female'] } as CurrentFilter;
|
|
756
|
+
|
|
757
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
758
|
+
service.updateFilters([mockCurrentFilter], true);
|
|
759
|
+
const query = service.buildQuery();
|
|
760
|
+
const currentFilters = service.getCurrentFilters();
|
|
761
|
+
|
|
762
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
763
|
+
expect(currentFilters).toEqual([{ columnId: 'gender', columnDef: mockColumn, operator: 'EQ', searchTerms: ['female'] }]);
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
it('should return a query with search having the operator StartsWith when search value has the "*" symbol as the last character', () => {
|
|
767
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" LIKE 'fem%' LIMIT 10 OFFSET 0`;
|
|
768
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
769
|
+
const mockColumnFilters = {
|
|
770
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['fem*'], type: 'string' },
|
|
771
|
+
} as ColumnFilters;
|
|
772
|
+
|
|
773
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
774
|
+
service.updateFilters(mockColumnFilters, false);
|
|
775
|
+
const query = service.buildQuery();
|
|
776
|
+
|
|
777
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
it('should return a query with search having the operator EndsWith when search value has the "*" symbol as the first character', () => {
|
|
781
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" LIKE '%le' LIMIT 10 OFFSET 0`;
|
|
782
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
783
|
+
const mockColumnFilters = {
|
|
784
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['*le'], type: 'string' },
|
|
785
|
+
} as ColumnFilters;
|
|
786
|
+
|
|
787
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
788
|
+
service.updateFilters(mockColumnFilters, false);
|
|
789
|
+
const query = service.buildQuery();
|
|
790
|
+
|
|
791
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
it('should return a query with search having the operator EndsWith when the operator was provided as "*z"', () => {
|
|
795
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" LIKE '%le' LIMIT 10 OFFSET 0`;
|
|
796
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
797
|
+
const mockColumnFilters = {
|
|
798
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le*'], operator: '*z', type: 'string' },
|
|
799
|
+
} as ColumnFilters;
|
|
800
|
+
|
|
801
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
802
|
+
service.updateFilters(mockColumnFilters, false);
|
|
803
|
+
const query = service.buildQuery();
|
|
804
|
+
|
|
805
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
it('should return a query with search having the operator StartsWith even when search value last char is "*" symbol but the operator provided is "*z"', () => {
|
|
809
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" LIKE 'le%' LIMIT 10 OFFSET 0`;
|
|
810
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
811
|
+
const mockColumnFilters = {
|
|
812
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le*'], operator: 'a*', type: 'string' },
|
|
813
|
+
} as ColumnFilters;
|
|
814
|
+
|
|
815
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
816
|
+
service.updateFilters(mockColumnFilters, false);
|
|
817
|
+
const query = service.buildQuery();
|
|
818
|
+
|
|
819
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it('should return a query with search having the operator EndsWith when the Column Filter was provided as "*z"', () => {
|
|
823
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" LIKE '%le' LIMIT 10 OFFSET 0`;
|
|
824
|
+
const mockColumn = { id: 'gender', field: 'gender', filter: { operator: '*z' } } as Column;
|
|
825
|
+
const mockColumnFilters = {
|
|
826
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], type: 'string' },
|
|
827
|
+
} as ColumnFilters;
|
|
828
|
+
|
|
829
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
830
|
+
service.updateFilters(mockColumnFilters, false);
|
|
831
|
+
const query = service.buildQuery();
|
|
832
|
+
|
|
833
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
it('should return a query with search having the operator EndsWith when the Column Filter was provided as EndsWith', () => {
|
|
837
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" LIKE '%le' LIMIT 10 OFFSET 0`;
|
|
838
|
+
const mockColumn = { id: 'gender', field: 'gender', filter: { operator: 'EndsWith' } } as Column;
|
|
839
|
+
const mockColumnFilters = {
|
|
840
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], type: 'string' },
|
|
841
|
+
} as ColumnFilters;
|
|
842
|
+
|
|
843
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
844
|
+
service.updateFilters(mockColumnFilters, false);
|
|
845
|
+
const query = service.buildQuery();
|
|
846
|
+
|
|
847
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
it('should return a query with search having the operator StartsWith when the operator was provided as "a*"', () => {
|
|
851
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" LIKE 'le%' LIMIT 10 OFFSET 0`;
|
|
852
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
853
|
+
const mockColumnFilters = {
|
|
854
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], operator: 'a*', type: 'string' },
|
|
855
|
+
} as ColumnFilters;
|
|
856
|
+
|
|
857
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
858
|
+
service.updateFilters(mockColumnFilters, false);
|
|
859
|
+
const query = service.buildQuery();
|
|
860
|
+
|
|
861
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
it('should return a query with search having the operator StartsWith when the operator was provided as StartsWith', () => {
|
|
865
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" LIKE 'le%' LIMIT 10 OFFSET 0`;
|
|
866
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
867
|
+
const mockColumnFilters = {
|
|
868
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], operator: 'StartsWith', type: 'string' },
|
|
869
|
+
} as ColumnFilters;
|
|
870
|
+
|
|
871
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
872
|
+
service.updateFilters(mockColumnFilters, false);
|
|
873
|
+
const query = service.buildQuery();
|
|
874
|
+
|
|
875
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
it('should return a query with search having the operator StartsWith & EndsWith when search value has the "*" symbol with chars on both side of it', () => {
|
|
879
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "name" LIKE 'Ca%' AND "name" LIKE '%le' LIMIT 10 OFFSET 0`;
|
|
880
|
+
const mockColumn = { id: 'name', field: 'name' } as Column;
|
|
881
|
+
const mockColumnFilters = {
|
|
882
|
+
name: { columnId: 'name', columnDef: mockColumn, searchTerms: ['Ca*le'], type: 'string' },
|
|
883
|
+
} as ColumnFilters;
|
|
884
|
+
|
|
885
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
886
|
+
service.updateFilters(mockColumnFilters, false);
|
|
887
|
+
const query = service.buildQuery();
|
|
888
|
+
|
|
889
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
it('should return a query with search having the operator StartsWithEndsWith when the operator was provided as "a*z"', () => {
|
|
893
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "name" LIKE 'Ca%' AND "name" LIKE '%le' LIMIT 10 OFFSET 0`;
|
|
894
|
+
const mockColumn = { id: 'name', field: 'name' } as Column;
|
|
895
|
+
const mockColumnFilters = {
|
|
896
|
+
name: { columnId: 'name', columnDef: mockColumn, searchTerms: ['Ca*le'], operator: 'a*z', type: 'string' },
|
|
897
|
+
} as ColumnFilters;
|
|
898
|
+
|
|
899
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
900
|
+
service.updateFilters(mockColumnFilters, false);
|
|
901
|
+
const query = service.buildQuery();
|
|
902
|
+
|
|
903
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
it('should bypass default behavior if filterQueryOverride is defined and does not return undefined', () => {
|
|
907
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "name" LIKE 'Ca%' AND "name" LIKE '%le' LIMIT 10 OFFSET 0`;
|
|
908
|
+
const mockColumn = { id: 'name', field: 'name' } as Column;
|
|
909
|
+
const mockColumnFilters = {
|
|
910
|
+
name: { columnId: 'name', columnDef: mockColumn, searchTerms: ['Ca*le'], operator: 'a*z', type: 'string' },
|
|
911
|
+
} as ColumnFilters;
|
|
912
|
+
|
|
913
|
+
const sOptions = { ...serviceOptions, filterQueryOverride: () => ({ field: 'foo', operator: 'EQ', value: 'bar' }) };
|
|
914
|
+
service.init(sOptions, paginationOptions, gridStub);
|
|
915
|
+
service.updateFilters(mockColumnFilters, false);
|
|
916
|
+
const query = service.buildQuery();
|
|
917
|
+
|
|
918
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
it('should continue with default behavior if filterQueryOverride returns undefined', () => {
|
|
922
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "name" LIKE 'Ca%' AND "name" LIKE '%le' LIMIT 10 OFFSET 0`;
|
|
923
|
+
const mockColumn = { id: 'name', field: 'name' } as Column;
|
|
924
|
+
const mockColumnFilters = {
|
|
925
|
+
name: { columnId: 'name', columnDef: mockColumn, searchTerms: ['Ca*le'], operator: 'a*z', type: 'string' },
|
|
926
|
+
} as ColumnFilters;
|
|
927
|
+
|
|
928
|
+
const sOptions = { ...serviceOptions, filterQueryOverride: () => undefined };
|
|
929
|
+
service.init(sOptions, paginationOptions, gridStub);
|
|
930
|
+
service.updateFilters(mockColumnFilters, false);
|
|
931
|
+
const query = service.buildQuery();
|
|
932
|
+
|
|
933
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
it('should continue with default behavior if filterQueryOverride is not provided', () => {
|
|
937
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "name" LIKE 'Ca%' AND "name" LIKE '%le' LIMIT 10 OFFSET 0`;
|
|
938
|
+
const mockColumn = { id: 'name', field: 'name' } as Column;
|
|
939
|
+
const mockColumnFilters = {
|
|
940
|
+
name: { columnId: 'name', columnDef: mockColumn, searchTerms: ['Ca*le'], operator: 'a*z', type: 'string' },
|
|
941
|
+
} as ColumnFilters;
|
|
942
|
+
|
|
943
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
944
|
+
service.updateFilters(mockColumnFilters, false);
|
|
945
|
+
const query = service.buildQuery();
|
|
946
|
+
|
|
947
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
it('should return a query with search having the operator Greater of Equal when the search value was provided as ">=10"', () => {
|
|
951
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "age" >= 10 LIMIT 10 OFFSET 0`;
|
|
952
|
+
const mockColumn = { id: 'age', field: 'age', type: 'number' } as Column;
|
|
953
|
+
const mockColumnFilters = {
|
|
954
|
+
age: { columnId: 'age', columnDef: mockColumn, searchTerms: ['>=10'], type: 'number' },
|
|
955
|
+
} as ColumnFilters;
|
|
956
|
+
|
|
957
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
958
|
+
service.updateFilters(mockColumnFilters, false);
|
|
959
|
+
const query = service.buildQuery();
|
|
960
|
+
|
|
961
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
it('should return a query with search NOT having the operator Greater of Equal when the search value was provided as ">=10" but "autoParseInputFilterOperator" is set to false', () => {
|
|
965
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "age" LIKE '%>=10%' LIMIT 10 OFFSET 0`;
|
|
966
|
+
const mockColumn = { id: 'age', field: 'age', autoParseInputFilterOperator: false } as Column;
|
|
967
|
+
const mockColumnFilters = {
|
|
968
|
+
age: { columnId: 'age', columnDef: mockColumn, searchTerms: ['>=10'], type: 'string' },
|
|
969
|
+
} as ColumnFilters;
|
|
970
|
+
|
|
971
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
972
|
+
service.updateFilters(mockColumnFilters, false);
|
|
973
|
+
const query = service.buildQuery();
|
|
974
|
+
|
|
975
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
it('should return a query with search having a range of exclusive numbers when the search value contains 2 dots (..) to represent a range of numbers', () => {
|
|
979
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" >= 2 AND "duration" <= 33 LIMIT 10 OFFSET 0`;
|
|
980
|
+
const mockColumn = { id: 'duration', field: 'duration', type: 'number' } as Column;
|
|
981
|
+
const mockColumnFilters = {
|
|
982
|
+
duration: { columnId: 'duration', columnDef: mockColumn, searchTerms: ['2..33'], type: 'number' },
|
|
983
|
+
} as ColumnFilters;
|
|
984
|
+
|
|
985
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
986
|
+
service.updateFilters(mockColumnFilters, false);
|
|
987
|
+
const query = service.buildQuery();
|
|
988
|
+
|
|
989
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
it('should return a query to filter a search value between an inclusive range of numbers using the 2 dots (..) separator, the "RangeInclusive" operator and the range has an unbounded end', () => {
|
|
993
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" >= 5 LIMIT 10 OFFSET 0`;
|
|
994
|
+
const mockColumnDuration = { id: 'duration', field: 'duration', type: 'number' } as Column;
|
|
995
|
+
const mockColumnFilters = {
|
|
996
|
+
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..'], operator: 'RangeInclusive', type: 'number' },
|
|
997
|
+
} as ColumnFilters;
|
|
998
|
+
|
|
999
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1000
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1001
|
+
const query = service.buildQuery();
|
|
1002
|
+
|
|
1003
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
it('should return a query to filter a search value between an inclusive range of numbers using the 2 dots (..) separator, the "RangeInclusive" operator and the range has an unbounded begin', () => {
|
|
1007
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" <= 5 LIMIT 10 OFFSET 0`;
|
|
1008
|
+
const mockColumnDuration = { id: 'duration', field: 'duration', type: 'number' } as Column;
|
|
1009
|
+
const mockColumnFilters = {
|
|
1010
|
+
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'RangeInclusive', type: 'number' },
|
|
1011
|
+
} as ColumnFilters;
|
|
1012
|
+
|
|
1013
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1014
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1015
|
+
const query = service.buildQuery();
|
|
1016
|
+
|
|
1017
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
it('should return a query to filter a search value between an inclusive range of numbers using the 2 dots (..) separator, the "RangeExclusive" operator and the range has an unbounded end', () => {
|
|
1021
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" > 5 LIMIT 10 OFFSET 0`;
|
|
1022
|
+
const mockColumnDuration = { id: 'duration', field: 'duration', type: 'number' } as Column;
|
|
1023
|
+
const mockColumnFilters = {
|
|
1024
|
+
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..'], operator: 'RangeExclusive', type: 'number' },
|
|
1025
|
+
} as ColumnFilters;
|
|
1026
|
+
|
|
1027
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1028
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1029
|
+
const query = service.buildQuery();
|
|
1030
|
+
|
|
1031
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
it('should return a query to filter a search value between an inclusive range of numbers using the 2 dots (..) separator, the "RangeExclusive" operator and the range has an unbounded begin', () => {
|
|
1035
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" < 5 LIMIT 10 OFFSET 0`;
|
|
1036
|
+
const mockColumnDuration = { id: 'duration', field: 'duration', type: 'number' } as Column;
|
|
1037
|
+
const mockColumnFilters = {
|
|
1038
|
+
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'RangeExclusive', type: 'number' },
|
|
1039
|
+
} as ColumnFilters;
|
|
1040
|
+
|
|
1041
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1042
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1043
|
+
const query = service.buildQuery();
|
|
1044
|
+
|
|
1045
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1046
|
+
});
|
|
1047
|
+
|
|
1048
|
+
it('should return a query with search having a range of inclusive numbers when 2 searchTerms numbers are provided and the operator is "RangeInclusive"', () => {
|
|
1049
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" >= 2 AND "duration" <= 33 LIMIT 10 OFFSET 0`;
|
|
1050
|
+
const mockColumn = { id: 'duration', field: 'duration' } as Column;
|
|
1051
|
+
const mockColumnFilters = {
|
|
1052
|
+
duration: { columnId: 'duration', columnDef: mockColumn, searchTerms: [2, 33], operator: 'RangeInclusive', type: 'number' },
|
|
1053
|
+
} as ColumnFilters;
|
|
1054
|
+
|
|
1055
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1056
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1057
|
+
const query = service.buildQuery();
|
|
1058
|
+
|
|
1059
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
it('should return a query to filter a search value between an inclusive range of numbers using the 2 dots (..) separator, "defaultFilterRangeOperator" is not set and operator is not set to "RangeInclusive" or "RangeExclusive"', () => {
|
|
1063
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" <= 5 LIMIT 10 OFFSET 0`;
|
|
1064
|
+
const mockColumnDuration = { id: 'duration', field: 'duration', type: 'number' } as Column;
|
|
1065
|
+
const mockColumnFilters = {
|
|
1066
|
+
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'Contains', type: 'number' },
|
|
1067
|
+
} as ColumnFilters;
|
|
1068
|
+
gridOptionMock.defaultFilterRangeOperator = undefined;
|
|
1069
|
+
|
|
1070
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1071
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1072
|
+
const query = service.buildQuery();
|
|
1073
|
+
|
|
1074
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
it('should return a query to filter a search value between an exclusive range of numbers using the 2 dots (..) separator, "defaultFilterRangeOperator" is set to "rangeExclusive" and operator is not set to "RangeInclusive" or "RangeExclusive"', () => {
|
|
1078
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" < 5 LIMIT 10 OFFSET 0`;
|
|
1079
|
+
const mockColumnDuration = { id: 'duration', field: 'duration', type: 'number' } as Column;
|
|
1080
|
+
const mockColumnFilters = {
|
|
1081
|
+
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'Contains', type: 'number' },
|
|
1082
|
+
} as ColumnFilters;
|
|
1083
|
+
gridOptionMock.defaultFilterRangeOperator = 'RangeExclusive';
|
|
1084
|
+
|
|
1085
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1086
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1087
|
+
const query = service.buildQuery();
|
|
1088
|
+
|
|
1089
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
it('should return a query with search having a range of exclusive dates when the search value contains 2 dots (..) to represent a range of dates', () => {
|
|
1093
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "startDate" >= '2001-01-01' AND "startDate" <= '2001-01-31' LIMIT 10 OFFSET 0`;
|
|
1094
|
+
const mockColumn = { id: 'startDate', field: 'startDate' } as Column;
|
|
1095
|
+
const mockColumnFilters = {
|
|
1096
|
+
startDate: { columnId: 'startDate', columnDef: mockColumn, searchTerms: ['2001-01-01..2001-01-31'], type: 'dateIso' },
|
|
1097
|
+
} as ColumnFilters;
|
|
1098
|
+
|
|
1099
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1100
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1101
|
+
const query = service.buildQuery();
|
|
1102
|
+
|
|
1103
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
it('should return a query with search having a range of inclusive dates when 2 searchTerms dates are provided and the operator is "RangeInclusive"', () => {
|
|
1107
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "startDate" >= '2001-01-01' AND "startDate" <= '2001-01-31' LIMIT 10 OFFSET 0`;
|
|
1108
|
+
const mockColumn = { id: 'startDate', field: 'startDate' } as Column;
|
|
1109
|
+
const mockColumnFilters = {
|
|
1110
|
+
startDate: {
|
|
1111
|
+
columnId: 'startDate',
|
|
1112
|
+
columnDef: mockColumn,
|
|
1113
|
+
searchTerms: ['2001-01-01', '2001-01-31'],
|
|
1114
|
+
operator: 'RangeInclusive',
|
|
1115
|
+
type: 'dateIso',
|
|
1116
|
+
},
|
|
1117
|
+
} as ColumnFilters;
|
|
1118
|
+
|
|
1119
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1120
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1121
|
+
const query = service.buildQuery();
|
|
1122
|
+
|
|
1123
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
it('should return a query with a date equal when only 1 searchTerms is provided and even if the operator is set to a range', () => {
|
|
1127
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "company" LIKE '%abc%' AND "updatedDate" = '2001-01-20' LIMIT 10 OFFSET 0`;
|
|
1128
|
+
const mockColumnCompany = { id: 'company', field: 'company' } as Column;
|
|
1129
|
+
const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: 'date' } as Column;
|
|
1130
|
+
const mockColumnFilters = {
|
|
1131
|
+
company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: 'string' },
|
|
1132
|
+
updatedDate: {
|
|
1133
|
+
columnId: 'updatedDate',
|
|
1134
|
+
columnDef: mockColumnUpdated,
|
|
1135
|
+
searchTerms: ['2001-01-20'],
|
|
1136
|
+
operator: 'RangeExclusive',
|
|
1137
|
+
type: 'dateIso',
|
|
1138
|
+
},
|
|
1139
|
+
} as ColumnFilters;
|
|
1140
|
+
|
|
1141
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1142
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1143
|
+
const query = service.buildQuery();
|
|
1144
|
+
|
|
1145
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
it('should return a query with a date operator when only 1 searchTerms', () => {
|
|
1149
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "company" LIKE '%abc%' AND "updatedDate" >= '2001-01-20' LIMIT 10 OFFSET 0`;
|
|
1150
|
+
const mockColumnCompany = { id: 'company', field: 'company' } as Column;
|
|
1151
|
+
const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: 'date' } as Column;
|
|
1152
|
+
const mockColumnFilters = {
|
|
1153
|
+
company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: 'string' },
|
|
1154
|
+
updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20'], operator: '>=', type: 'dateIso' },
|
|
1155
|
+
} as ColumnFilters;
|
|
1156
|
+
|
|
1157
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1158
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1159
|
+
const query = service.buildQuery();
|
|
1160
|
+
|
|
1161
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
it('should return a query without any date filtering when searchTerms is an empty array', () => {
|
|
1165
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "company" LIKE '%abc%' LIMIT 10 OFFSET 0`;
|
|
1166
|
+
const mockColumnCompany = { id: 'company', field: 'company' } as Column;
|
|
1167
|
+
const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: 'date' } as Column;
|
|
1168
|
+
const mockColumnFilters = {
|
|
1169
|
+
company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: 'string' },
|
|
1170
|
+
updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: [], operator: 'RangeExclusive', type: 'dateIso' },
|
|
1171
|
+
} as ColumnFilters;
|
|
1172
|
+
|
|
1173
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1174
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1175
|
+
const query = service.buildQuery();
|
|
1176
|
+
|
|
1177
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1180
|
+
it('should return a query with a CSV string when the filter operator is IN ', () => {
|
|
1181
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" IN ('female','male') LIMIT 10 OFFSET 0`;
|
|
1182
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
1183
|
+
const mockColumnFilters = {
|
|
1184
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], operator: 'IN', type: 'string' },
|
|
1185
|
+
} as ColumnFilters;
|
|
1186
|
+
|
|
1187
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1188
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1189
|
+
const query = service.buildQuery();
|
|
1190
|
+
|
|
1191
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
it('should return a query with a CSV string when the filter operator is NOT_IN', () => {
|
|
1195
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" NOT IN ('female','male') LIMIT 10 OFFSET 0`;
|
|
1196
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
1197
|
+
const mockColumnFilters = {
|
|
1198
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], operator: 'NOT_IN', type: 'string' },
|
|
1199
|
+
} as ColumnFilters;
|
|
1200
|
+
|
|
1201
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1202
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1203
|
+
const query = service.buildQuery();
|
|
1204
|
+
|
|
1205
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
it('should return a query with a CSV string and use the operator from the Column Definition Operator when provided', () => {
|
|
1209
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" NOT IN ('female','male') LIMIT 10 OFFSET 0`;
|
|
1210
|
+
const mockColumn = { id: 'gender', field: 'gender', filter: { operator: 'NOT_IN' } } as Column;
|
|
1211
|
+
const mockColumnFilters = {
|
|
1212
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], type: 'string' },
|
|
1213
|
+
} as ColumnFilters;
|
|
1214
|
+
|
|
1215
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1216
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1217
|
+
const query = service.buildQuery();
|
|
1218
|
+
|
|
1219
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
it('should return a query with mapped operator when no operator was provided but we have a column "type" property', () => {
|
|
1223
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" LIKE '%le%' AND "age" = 28 LIMIT 10 OFFSET 0`;
|
|
1224
|
+
const mockColumnGender = { id: 'gender', field: 'gender', type: 'string' } as Column;
|
|
1225
|
+
const mockColumnAge = { id: 'age', field: 'age', type: 'number' } as Column;
|
|
1226
|
+
const mockColumnFilters = {
|
|
1227
|
+
gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['le'], type: 'string' },
|
|
1228
|
+
age: { columnId: 'age', columnDef: mockColumnAge, searchTerms: [28], type: 'number' },
|
|
1229
|
+
} as ColumnFilters;
|
|
1230
|
+
|
|
1231
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1232
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1233
|
+
const query = service.buildQuery();
|
|
1234
|
+
|
|
1235
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
it('should return a query with mapped operator when neither operator nor column "type" property exists', () => {
|
|
1239
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" LIKE '%le%' AND "city" LIKE '%Bali%' LIMIT 10 OFFSET 0`;
|
|
1240
|
+
const mockColumnGender = { id: 'gender', field: 'gender' } as Column;
|
|
1241
|
+
const mockColumnCity = { id: 'city', field: 'city' } as Column;
|
|
1242
|
+
const mockColumnFilters = {
|
|
1243
|
+
gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['le'], type: 'string' },
|
|
1244
|
+
city: { columnId: 'city', columnDef: mockColumnCity, searchTerms: ['Bali'], type: 'string' },
|
|
1245
|
+
} as ColumnFilters;
|
|
1246
|
+
|
|
1247
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1248
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1249
|
+
const query = service.buildQuery();
|
|
1250
|
+
|
|
1251
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1252
|
+
});
|
|
1253
|
+
|
|
1254
|
+
it('should return a query with the new filter search value of empty string when searchTerms has an undefined value', () => {
|
|
1255
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" = '' LIMIT 10 OFFSET 0`;
|
|
1256
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
1257
|
+
const mockColumnFilters = {
|
|
1258
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [undefined as any], operator: 'EQ', type: 'string' },
|
|
1259
|
+
} as ColumnFilters;
|
|
1260
|
+
|
|
1261
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1262
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1263
|
+
const query = service.buildQuery();
|
|
1264
|
+
|
|
1265
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
it('should return a query using a different field to query when the column has a "queryField" defined in its definition', () => {
|
|
1269
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "isMale" = 'true' LIMIT 10 OFFSET 0`;
|
|
1270
|
+
const mockColumn = { id: 'gender', field: 'gender', queryField: 'isMale' } as Column;
|
|
1271
|
+
const mockColumnFilters = {
|
|
1272
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [true], operator: 'EQ', type: 'string' },
|
|
1273
|
+
} as ColumnFilters;
|
|
1274
|
+
|
|
1275
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1276
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1277
|
+
const query = service.buildQuery();
|
|
1278
|
+
|
|
1279
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
it('should return a query using a different field to query when the column has a "queryFieldFilter" defined in its definition', () => {
|
|
1283
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "hasPriority" = 'female' LIMIT 10 OFFSET 0`;
|
|
1284
|
+
const mockColumn = { id: 'gender', field: 'gender', queryField: 'isAfter', queryFieldFilter: 'hasPriority' } as Column;
|
|
1285
|
+
const mockColumnFilters = {
|
|
1286
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: 'string' },
|
|
1287
|
+
} as ColumnFilters;
|
|
1288
|
+
|
|
1289
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1290
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1291
|
+
const query = service.buildQuery();
|
|
1292
|
+
|
|
1293
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
it('should return a query using column name that is an HTML Element', () => {
|
|
1297
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "Gender" = 'female' LIMIT 10 OFFSET 0`;
|
|
1298
|
+
const nameElm = document.createElement('div');
|
|
1299
|
+
nameElm.innerHTML = `<span class="text-red">Gender</span>`;
|
|
1300
|
+
const mockColumn = { id: 'gender', name: nameElm } as unknown as Column;
|
|
1301
|
+
const mockColumnFilters = {
|
|
1302
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: 'string' },
|
|
1303
|
+
} as ColumnFilters;
|
|
1304
|
+
|
|
1305
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1306
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1307
|
+
const query = service.buildQuery();
|
|
1308
|
+
|
|
1309
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1310
|
+
});
|
|
1311
|
+
|
|
1312
|
+
it('should return a query using the column "name" property when "field" is not defined in its definition', () => {
|
|
1313
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" = 'female' LIMIT 10 OFFSET 0`;
|
|
1314
|
+
const mockColumn = { id: 'gender', name: 'gender' } as Column;
|
|
1315
|
+
const mockColumnFilters = {
|
|
1316
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: 'string' },
|
|
1317
|
+
} as ColumnFilters;
|
|
1318
|
+
|
|
1319
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1320
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1321
|
+
const query = service.buildQuery();
|
|
1322
|
+
|
|
1323
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1326
|
+
it('should return a query without any sorting after clearFilters was called', () => {
|
|
1327
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 10 OFFSET 0`;
|
|
1328
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
1329
|
+
const mockColumnFilters = {
|
|
1330
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: 'string' },
|
|
1331
|
+
} as ColumnFilters;
|
|
1332
|
+
|
|
1333
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1334
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1335
|
+
service.clearFilters();
|
|
1336
|
+
const currentFilters = service.getCurrentFilters();
|
|
1337
|
+
const query = service.buildQuery();
|
|
1338
|
+
|
|
1339
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1340
|
+
expect(currentFilters).toEqual([]);
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
describe('Verbatim ColumnFilters', () => {
|
|
1344
|
+
describe.each`
|
|
1345
|
+
description | verbatim | operator | searchTerms | expectation
|
|
1346
|
+
${'Verbatim false, Filter for null'} | ${false} | ${'EQ'} | ${null} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" LIMIT 10 OFFSET 0'}
|
|
1347
|
+
${'Verbatim true, Filter for null'} | ${true} | ${'EQ'} | ${null} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" WHERE \"gender\" IS NULL LIMIT 10 OFFSET 0'}
|
|
1348
|
+
${'Verbatim false, Empty string'} | ${false} | ${'EQ'} | ${''} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" LIMIT 10 OFFSET 0'}
|
|
1349
|
+
${'Verbatim true, Empty string'} | ${true} | ${'EQ'} | ${''} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" WHERE \"gender\" = \'\' LIMIT 10 OFFSET 0'}
|
|
1350
|
+
${'Verbatim false, Empty list'} | ${false} | ${'IN'} | ${[]} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" LIMIT 10 OFFSET 0'}
|
|
1351
|
+
${'Verbatim true, Empty list'} | ${true} | ${'IN'} | ${[]} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" LIMIT 10 OFFSET 0'}
|
|
1352
|
+
${'Verbatim false, Filter for null (in list)'} | ${false} | ${'IN'} | ${[null]} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" WHERE \"gender\" IN (\'\') LIMIT 10 OFFSET 0'}
|
|
1353
|
+
${'Verbatim true, Filter for null (in list)'} | ${true} | ${'IN'} | ${[null]} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" WHERE \"gender\" IS NULL LIMIT 10 OFFSET 0'}
|
|
1354
|
+
${'Verbatim false, Filter for empty string (in list)'} | ${false} | ${'IN'} | ${['']} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" WHERE \"gender\" IN (\'\') LIMIT 10 OFFSET 0'}
|
|
1355
|
+
${'Verbatim true, Filter for empty string (in list)'} | ${true} | ${'IN'} | ${['']} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" WHERE \"gender\" = \'\' LIMIT 10 OFFSET 0'}
|
|
1356
|
+
${'Verbatim false, Filter for female'} | ${false} | ${'IN'} | ${['female']} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" WHERE \"gender\" IN (\'female\') LIMIT 10 OFFSET 0'}
|
|
1357
|
+
${'Verbatim true, Filter for female'} | ${true} | ${'IN'} | ${['female']} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM \"users\" WHERE \"gender\" = \'female\' LIMIT 10 OFFSET 0'}
|
|
1358
|
+
${'Verbatim false, Filter for female/male'} | ${false} | ${'IN'} | ${['female', 'male']} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" IN (\'female\',\'male\') LIMIT 10 OFFSET 0'}
|
|
1359
|
+
${'Verbatim true, Filter for female/male'} | ${true} | ${'IN'} | ${['female', 'male']} | ${'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "gender" IN (\'female\',\'male\') LIMIT 10 OFFSET 0'}
|
|
1360
|
+
`(`$description`, ({ verbatim, operator, searchTerms, expectation }) => {
|
|
1361
|
+
const mockColumn = { id: 'gender', field: 'gender' } as Column;
|
|
1362
|
+
let mockColumnFilters: ColumnFilters;
|
|
1363
|
+
|
|
1364
|
+
beforeEach(() => {
|
|
1365
|
+
mockColumnFilters = {
|
|
1366
|
+
gender: { columnId: 'gender', columnDef: mockColumn, searchTerms, operator, type: 'string', verbatimSearchTerms: verbatim },
|
|
1367
|
+
} as ColumnFilters;
|
|
1368
|
+
|
|
1369
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1370
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1371
|
+
});
|
|
1372
|
+
|
|
1373
|
+
it(`buildQuery output matches ${expectation}`, () => {
|
|
1374
|
+
const query = service.buildQuery();
|
|
1375
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1376
|
+
});
|
|
1377
|
+
});
|
|
1378
|
+
});
|
|
1379
|
+
});
|
|
1380
|
+
|
|
1381
|
+
describe('presets', () => {
|
|
1382
|
+
let mockColumns: Column[] = [];
|
|
1383
|
+
beforeEach(() => {
|
|
1384
|
+
mockColumns = [
|
|
1385
|
+
{ id: 'company', field: 'company' },
|
|
1386
|
+
{ id: 'gender', field: 'gender' },
|
|
1387
|
+
{ id: 'duration', field: 'duration', type: 'number' },
|
|
1388
|
+
{ id: 'startDate', field: 'startDate' },
|
|
1389
|
+
];
|
|
1390
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(mockColumns);
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
afterEach(() => {
|
|
1394
|
+
vi.clearAllMocks();
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
it('should return a query with search having a range of exclusive numbers when the search value contains 2 dots (..) to represent a range of numbers', () => {
|
|
1398
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" >= 2 AND "duration" <= 33 LIMIT 10 OFFSET 0`;
|
|
1399
|
+
const presetFilters = [{ columnId: 'duration', searchTerms: ['2..33'] }] as CurrentFilter[];
|
|
1400
|
+
|
|
1401
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1402
|
+
service.updateFilters(presetFilters, true);
|
|
1403
|
+
const query = service.buildQuery();
|
|
1404
|
+
const currentFilters = service.getCurrentFilters();
|
|
1405
|
+
|
|
1406
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1407
|
+
expect(currentFilters).toEqual(presetFilters);
|
|
1408
|
+
});
|
|
1409
|
+
|
|
1410
|
+
it('should return a query with a filter with range of numbers with decimals when the preset is a filter range with 2 dots (..) separator and range ends with a fraction', () => {
|
|
1411
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" >= 0.5 AND "duration" <= 0.88 LIMIT 10 OFFSET 0`;
|
|
1412
|
+
const presetFilters = [{ columnId: 'duration', searchTerms: ['0.5...88'] }] as CurrentFilter[];
|
|
1413
|
+
|
|
1414
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1415
|
+
service.updateFilters(presetFilters, true);
|
|
1416
|
+
const query = service.buildQuery();
|
|
1417
|
+
const currentFilters = service.getCurrentFilters();
|
|
1418
|
+
|
|
1419
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1420
|
+
expect(currentFilters).toEqual(presetFilters);
|
|
1421
|
+
});
|
|
1422
|
+
|
|
1423
|
+
it('should return a query with search having a range of inclusive numbers when 2 searchTerms numbers are provided and the operator is "RangeInclusive"', () => {
|
|
1424
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" >= 2 AND "duration" <= 33 LIMIT 10 OFFSET 0`;
|
|
1425
|
+
const presetFilters = [{ columnId: 'duration', searchTerms: [2, 33], operator: 'RangeInclusive' }] as CurrentFilter[];
|
|
1426
|
+
|
|
1427
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1428
|
+
service.updateFilters(presetFilters, true);
|
|
1429
|
+
const query = service.buildQuery();
|
|
1430
|
+
const currentFilters = service.getCurrentFilters();
|
|
1431
|
+
|
|
1432
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1433
|
+
expect(currentFilters).toEqual(presetFilters);
|
|
1434
|
+
});
|
|
1435
|
+
|
|
1436
|
+
it('should return a query with search having a range of exclusive numbers when 2 searchTerms numbers are provided without any operator', () => {
|
|
1437
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" >= 2 AND "duration" <= 33 LIMIT 10 OFFSET 0`;
|
|
1438
|
+
const presetFilters = [{ columnId: 'duration', searchTerms: [2, 33] }] as CurrentFilter[];
|
|
1439
|
+
|
|
1440
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1441
|
+
service.updateFilters(presetFilters, true);
|
|
1442
|
+
const query = service.buildQuery();
|
|
1443
|
+
const currentFilters = service.getCurrentFilters();
|
|
1444
|
+
|
|
1445
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1446
|
+
expect(currentFilters).toEqual(presetFilters);
|
|
1447
|
+
});
|
|
1448
|
+
|
|
1449
|
+
it('should return a query with search having a range of exclusive dates when the search value contains 2 dots (..) to represent a range of dates', () => {
|
|
1450
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "startDate" >= '2001-01-01' AND "startDate" <= '2001-01-31' LIMIT 10 OFFSET 0`;
|
|
1451
|
+
const presetFilters = [{ columnId: 'startDate', searchTerms: ['2001-01-01..2001-01-31'] }] as CurrentFilter[];
|
|
1452
|
+
|
|
1453
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1454
|
+
service.updateFilters(presetFilters, true);
|
|
1455
|
+
const query = service.buildQuery();
|
|
1456
|
+
const currentFilters = service.getCurrentFilters();
|
|
1457
|
+
|
|
1458
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1459
|
+
expect(currentFilters).toEqual(presetFilters);
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
it('should return a query with search having a range of inclusive dates when 2 searchTerms dates are provided and the operator is "RangeInclusive"', () => {
|
|
1463
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "startDate" >= '2001-01-01' AND "startDate" <= '2001-01-31' LIMIT 10 OFFSET 0`;
|
|
1464
|
+
const presetFilters = [{ columnId: 'startDate', searchTerms: ['2001-01-01', '2001-01-31'], operator: 'RangeInclusive' }] as CurrentFilter[];
|
|
1465
|
+
|
|
1466
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1467
|
+
service.updateFilters(presetFilters, true);
|
|
1468
|
+
const query = service.buildQuery();
|
|
1469
|
+
const currentFilters = service.getCurrentFilters();
|
|
1470
|
+
|
|
1471
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1472
|
+
expect(currentFilters).toEqual(presetFilters);
|
|
1473
|
+
});
|
|
1474
|
+
|
|
1475
|
+
it('should return a query with search having a range of exclusive dates when 2 searchTerms dates are provided without any operator', () => {
|
|
1476
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "startDate" >= '2001-01-01' AND "startDate" <= '2001-01-31' LIMIT 10 OFFSET 0`;
|
|
1477
|
+
const presetFilters = [{ columnId: 'startDate', searchTerms: ['2001-01-01', '2001-01-31'] }] as CurrentFilter[];
|
|
1478
|
+
|
|
1479
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1480
|
+
service.updateFilters(presetFilters, true);
|
|
1481
|
+
const query = service.buildQuery();
|
|
1482
|
+
const currentFilters = service.getCurrentFilters();
|
|
1483
|
+
|
|
1484
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1485
|
+
expect(currentFilters).toEqual(presetFilters);
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
it('should return a query to filter a search value with a fraction of a number that is missing a leading 0', () => {
|
|
1489
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" = 0.22 LIMIT 10 OFFSET 0`;
|
|
1490
|
+
const mockColumnDuration = { id: 'duration', field: 'duration', type: 'number' } as Column;
|
|
1491
|
+
const mockColumnFilters = {
|
|
1492
|
+
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['.22'], type: 'string' },
|
|
1493
|
+
} as ColumnFilters;
|
|
1494
|
+
|
|
1495
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1496
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1497
|
+
const query = service.buildQuery();
|
|
1498
|
+
|
|
1499
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1500
|
+
});
|
|
1501
|
+
|
|
1502
|
+
it('should return a query without invalid characters to filter a search value that does contains invalid characters', () => {
|
|
1503
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" = -22 LIMIT 10 OFFSET 0`;
|
|
1504
|
+
const mockColumnDuration = { id: 'duration', field: 'duration', type: 'float' } as Column;
|
|
1505
|
+
const mockColumnFilters = {
|
|
1506
|
+
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-2a2'], type: 'float' },
|
|
1507
|
+
} as ColumnFilters;
|
|
1508
|
+
|
|
1509
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1510
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1511
|
+
const query = service.buildQuery();
|
|
1512
|
+
|
|
1513
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
it('should return a query without invalid characters to filter a search value with an integer that contains invalid characters', () => {
|
|
1517
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" = 22 LIMIT 10 OFFSET 0`;
|
|
1518
|
+
const mockColumnDuration = { id: 'duration', field: 'duration', type: 'integer' } as Column;
|
|
1519
|
+
const mockColumnFilters = {
|
|
1520
|
+
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['22;'], type: 'integer' },
|
|
1521
|
+
} as ColumnFilters;
|
|
1522
|
+
|
|
1523
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1524
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1525
|
+
const query = service.buildQuery();
|
|
1526
|
+
|
|
1527
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1528
|
+
});
|
|
1529
|
+
|
|
1530
|
+
it('should return a query without invalid characters to filter a search value with a number that only has a minus characters', () => {
|
|
1531
|
+
const expectation = `SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" WHERE "duration" = 0 LIMIT 10 OFFSET 0`;
|
|
1532
|
+
const mockColumnDuration = { id: 'duration', field: 'duration', type: 'number' } as Column;
|
|
1533
|
+
const mockColumnFilters = {
|
|
1534
|
+
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-'], type: 'string' },
|
|
1535
|
+
} as ColumnFilters;
|
|
1536
|
+
|
|
1537
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1538
|
+
service.updateFilters(mockColumnFilters, false);
|
|
1539
|
+
const query = service.buildQuery();
|
|
1540
|
+
|
|
1541
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1542
|
+
});
|
|
1543
|
+
});
|
|
1544
|
+
|
|
1545
|
+
describe('updateSorters method', () => {
|
|
1546
|
+
beforeEach(() => {
|
|
1547
|
+
const columns = [
|
|
1548
|
+
{ id: 'company', field: 'company' },
|
|
1549
|
+
{ id: 'gender', field: 'gender' },
|
|
1550
|
+
{ id: 'name', field: 'name' },
|
|
1551
|
+
];
|
|
1552
|
+
vi.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
|
|
1553
|
+
});
|
|
1554
|
+
|
|
1555
|
+
it('should return a query with the multiple new sorting when using multiColumnSort', () => {
|
|
1556
|
+
const expectation = 'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" ORDER BY "gender" DESC, "firstName" ASC LIMIT 10 OFFSET 0';
|
|
1557
|
+
const mockColumnSort = [
|
|
1558
|
+
{ columnId: 'gender', sortCol: { id: 'gender', field: 'gender' }, sortAsc: false },
|
|
1559
|
+
{ columnId: 'firstName', sortCol: { id: 'firstName', field: 'firstName' }, sortAsc: true },
|
|
1560
|
+
];
|
|
1561
|
+
|
|
1562
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1563
|
+
service.updateSorters(mockColumnSort);
|
|
1564
|
+
const query = service.buildQuery();
|
|
1565
|
+
const currentSorters = service.getCurrentSorters();
|
|
1566
|
+
|
|
1567
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1568
|
+
expect(currentSorters).toEqual([
|
|
1569
|
+
{ columnId: 'gender', direction: 'DESC' },
|
|
1570
|
+
{ columnId: 'firstName', direction: 'ASC' },
|
|
1571
|
+
]);
|
|
1572
|
+
});
|
|
1573
|
+
|
|
1574
|
+
it('should return a query when using presets array', () => {
|
|
1575
|
+
const expectation = 'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" ORDER BY "company" DESC, "firstName" ASC LIMIT 10 OFFSET 0';
|
|
1576
|
+
const presets = [
|
|
1577
|
+
{ columnId: 'company', direction: 'DESC' },
|
|
1578
|
+
{ columnId: 'firstName', direction: 'ASC' },
|
|
1579
|
+
] as CurrentSorter[];
|
|
1580
|
+
|
|
1581
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1582
|
+
service.updateSorters(undefined, presets);
|
|
1583
|
+
const query = service.buildQuery();
|
|
1584
|
+
const currentSorters = service.getCurrentSorters();
|
|
1585
|
+
|
|
1586
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1587
|
+
expect(currentSorters).toEqual(presets);
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1590
|
+
it('should return a query string using a different field to query when the column has a "queryField" defined in its definition', () => {
|
|
1591
|
+
const expectation = 'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" ORDER BY "gender" DESC, "firstName" ASC LIMIT 10 OFFSET 0';
|
|
1592
|
+
const mockColumnSort = [
|
|
1593
|
+
{ columnId: 'gender', sortCol: { id: 'gender', field: 'gender' }, sortAsc: false },
|
|
1594
|
+
{ columnId: 'name', sortCol: { id: 'name', field: 'name', queryField: 'firstName' }, sortAsc: true },
|
|
1595
|
+
];
|
|
1596
|
+
|
|
1597
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1598
|
+
service.updateSorters(mockColumnSort);
|
|
1599
|
+
const query = service.buildQuery();
|
|
1600
|
+
const currentSorters = service.getCurrentSorters();
|
|
1601
|
+
|
|
1602
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1603
|
+
expect(currentSorters).toEqual([
|
|
1604
|
+
{ columnId: 'gender', direction: 'DESC' },
|
|
1605
|
+
{ columnId: 'name', direction: 'ASC' },
|
|
1606
|
+
]);
|
|
1607
|
+
});
|
|
1608
|
+
|
|
1609
|
+
it('should return a query string using a different field to query when the column has a "queryFieldSorter" defined in its definition', () => {
|
|
1610
|
+
const expectation = 'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" ORDER BY "gender" DESC, "lastName" ASC LIMIT 10 OFFSET 0';
|
|
1611
|
+
const mockColumnSort = [
|
|
1612
|
+
{ columnId: 'gender', sortCol: { id: 'gender', field: 'gender' }, sortAsc: false },
|
|
1613
|
+
{ columnId: 'name', sortCol: { id: 'name', field: 'name', queryField: 'isAfter', queryFieldSorter: 'lastName' }, sortAsc: true },
|
|
1614
|
+
];
|
|
1615
|
+
|
|
1616
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1617
|
+
service.updateSorters(mockColumnSort);
|
|
1618
|
+
const query = service.buildQuery();
|
|
1619
|
+
const currentSorters = service.getCurrentSorters();
|
|
1620
|
+
|
|
1621
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1622
|
+
expect(currentSorters).toEqual([
|
|
1623
|
+
{ columnId: 'gender', direction: 'DESC' },
|
|
1624
|
+
{ columnId: 'name', direction: 'ASC' },
|
|
1625
|
+
]);
|
|
1626
|
+
});
|
|
1627
|
+
|
|
1628
|
+
it('should return a query without the field sorter when its field property is missing', () => {
|
|
1629
|
+
const expectation = 'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" ORDER BY "gender" DESC LIMIT 10 OFFSET 0';
|
|
1630
|
+
const mockColumnSort = [
|
|
1631
|
+
{ columnId: 'gender', sortCol: { id: 'gender', field: 'gender' }, sortAsc: false },
|
|
1632
|
+
{ columnId: 'firstName', sortCol: { id: 'firstName' }, sortAsc: true },
|
|
1633
|
+
] as ColumnSort[];
|
|
1634
|
+
|
|
1635
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1636
|
+
service.updateSorters(mockColumnSort);
|
|
1637
|
+
const query = service.buildQuery();
|
|
1638
|
+
const currentSorters = service.getCurrentSorters();
|
|
1639
|
+
|
|
1640
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1641
|
+
expect(currentSorters).toEqual([
|
|
1642
|
+
{ columnId: 'gender', direction: 'DESC' },
|
|
1643
|
+
{ columnId: 'firstName', direction: 'ASC' },
|
|
1644
|
+
]);
|
|
1645
|
+
});
|
|
1646
|
+
|
|
1647
|
+
it('should return a query without any sorting after clearSorters was called', () => {
|
|
1648
|
+
const expectation = 'SELECT *, COUNT(*) OVER() AS "totalCount" FROM "users" LIMIT 10 OFFSET 0';
|
|
1649
|
+
const mockColumnSort = [
|
|
1650
|
+
{ columnId: 'gender', sortCol: { id: 'gender', field: 'gender' }, sortAsc: false },
|
|
1651
|
+
{ columnId: 'firstName', sortCol: { id: 'firstName', field: 'firstName' }, sortAsc: true },
|
|
1652
|
+
];
|
|
1653
|
+
|
|
1654
|
+
service.init(serviceOptions, paginationOptions, gridStub);
|
|
1655
|
+
service.updateSorters(mockColumnSort);
|
|
1656
|
+
service.clearSorters();
|
|
1657
|
+
const query = service.buildQuery();
|
|
1658
|
+
const currentSorters = service.getCurrentSorters();
|
|
1659
|
+
|
|
1660
|
+
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
|
|
1661
|
+
expect(currentSorters).toEqual([]);
|
|
1662
|
+
});
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1665
|
+
describe('postProcess method', () => {
|
|
1666
|
+
it('should set pagination totalCount from the first row of SQL data result (COUNT(*) OVER())', () => {
|
|
1667
|
+
service.init({ tableName: 'users' }, paginationOptions, gridStub);
|
|
1668
|
+
// Simulate SQL result: array of rows, each row has totalCount
|
|
1669
|
+
const sqlResult = [
|
|
1670
|
+
{ id: 1, name: 'John', totalCount: 123 },
|
|
1671
|
+
{ id: 2, name: 'Jane', totalCount: 123 },
|
|
1672
|
+
];
|
|
1673
|
+
service.postProcess(sqlResult);
|
|
1674
|
+
expect(service.pagination?.totalItems).toBe(123);
|
|
1675
|
+
expect(paginationOptions.totalItems).toBe(123);
|
|
1676
|
+
});
|
|
1677
|
+
|
|
1678
|
+
it('should set pagination totalCount from the first row of a data array in an object result (explicit branch coverage)', () => {
|
|
1679
|
+
const freshPagination: Pagination = {
|
|
1680
|
+
pageNumber: 1,
|
|
1681
|
+
pageSizes: [5, 10, 25, 50, 100],
|
|
1682
|
+
pageSize: 10,
|
|
1683
|
+
};
|
|
1684
|
+
service.init({ tableName: 'users' }, freshPagination, gridStub);
|
|
1685
|
+
const sqlResult = {
|
|
1686
|
+
data: [
|
|
1687
|
+
{ id: 1, totalCount: 555 },
|
|
1688
|
+
{ id: 2, totalCount: 555 },
|
|
1689
|
+
],
|
|
1690
|
+
};
|
|
1691
|
+
service.postProcess(sqlResult);
|
|
1692
|
+
expect(service.pagination?.totalItems).toBe(555);
|
|
1693
|
+
expect(freshPagination.totalItems).toBe(555);
|
|
1694
|
+
});
|
|
1695
|
+
|
|
1696
|
+
it('should set pagination totalCount from the first row of SQL data result with custom totalCountField', () => {
|
|
1697
|
+
service.init({ tableName: 'users', totalCountField: 'myCount' }, paginationOptions, gridStub);
|
|
1698
|
+
// Simulate SQL result: array of rows, each row has custom count field
|
|
1699
|
+
const sqlResult = [
|
|
1700
|
+
{ id: 1, name: 'John', myCount: 77 },
|
|
1701
|
+
{ id: 2, name: 'Jane', myCount: 77 },
|
|
1702
|
+
];
|
|
1703
|
+
service.postProcess(sqlResult);
|
|
1704
|
+
expect(service.pagination?.totalItems).toBe(77);
|
|
1705
|
+
expect(paginationOptions.totalItems).toBe(77);
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
it('should set pagination totalCount from a top-level totalCount property (fallback)', () => {
|
|
1709
|
+
const freshPagination: Pagination = {
|
|
1710
|
+
pageNumber: 1,
|
|
1711
|
+
pageSizes: [5, 10, 25, 50, 100],
|
|
1712
|
+
pageSize: 10,
|
|
1713
|
+
};
|
|
1714
|
+
service.init({ tableName: 'users' }, freshPagination, gridStub);
|
|
1715
|
+
const sqlResult = { totalCount: 456 };
|
|
1716
|
+
service.postProcess(sqlResult);
|
|
1717
|
+
expect(service.pagination?.totalItems).toBe(456);
|
|
1718
|
+
expect(freshPagination.totalItems).toBe(456);
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
it('should set pagination totalCount from a top-level custom count field (fallback)', () => {
|
|
1722
|
+
const freshPagination: Pagination = {
|
|
1723
|
+
pageNumber: 1,
|
|
1724
|
+
pageSizes: [5, 10, 25, 50, 100],
|
|
1725
|
+
pageSize: 10,
|
|
1726
|
+
};
|
|
1727
|
+
service.init({ tableName: 'users', totalCountField: 'myCount' }, freshPagination, gridStub);
|
|
1728
|
+
const sqlResult = { myCount: 88 };
|
|
1729
|
+
service.postProcess(sqlResult);
|
|
1730
|
+
expect(service.pagination?.totalItems).toBe(88);
|
|
1731
|
+
expect(freshPagination.totalItems).toBe(88);
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
it('should set pagination totalCount from the first row of a data array in an object result', () => {
|
|
1735
|
+
const freshPagination: Pagination = {
|
|
1736
|
+
pageNumber: 1,
|
|
1737
|
+
pageSizes: [5, 10, 25, 50, 100],
|
|
1738
|
+
pageSize: 10,
|
|
1739
|
+
};
|
|
1740
|
+
service.init({ tableName: 'users' }, freshPagination, gridStub);
|
|
1741
|
+
const sqlResult = {
|
|
1742
|
+
data: [
|
|
1743
|
+
{ id: 1, totalCount: 999 },
|
|
1744
|
+
{ id: 2, totalCount: 999 },
|
|
1745
|
+
],
|
|
1746
|
+
};
|
|
1747
|
+
service.postProcess(sqlResult);
|
|
1748
|
+
expect(service.pagination?.totalItems).toBe(999);
|
|
1749
|
+
expect(freshPagination.totalItems).toBe(999);
|
|
1750
|
+
});
|
|
1751
|
+
});
|
|
1752
|
+
|
|
1753
|
+
describe('coverage for edge/fallback branches', () => {
|
|
1754
|
+
it('should set totalItems from data[0][totalCountField] in postProcess', () => {
|
|
1755
|
+
(service as any)['pagination'] = { totalItems: 0 };
|
|
1756
|
+
(service as any)['options'] = { tableName: 'users' };
|
|
1757
|
+
const totalCountField = 'totalCount';
|
|
1758
|
+
const result = { data: [{ id: 1, [totalCountField]: 42 }] };
|
|
1759
|
+
(service as any)['postProcess'](result);
|
|
1760
|
+
expect((service as any)['pagination'].totalItems).toBe(42);
|
|
1761
|
+
});
|
|
1762
|
+
|
|
1763
|
+
it('should use pageNumber=1 if pageNumber is missing', () => {
|
|
1764
|
+
service.init(serviceOptions, { pageSize: 10 }, gridStub);
|
|
1765
|
+
(service as any)['_currentPagination'] = { pageSize: 10 };
|
|
1766
|
+
const query = service.buildQuery();
|
|
1767
|
+
expect(query).toContain('OFFSET 0');
|
|
1768
|
+
});
|
|
1769
|
+
|
|
1770
|
+
it('should use infiniteScroll.fetchSize if provided', () => {
|
|
1771
|
+
service.init({ ...serviceOptions, infiniteScroll: { fetchSize: 42 } }, { pageSize: 10, pageNumber: 1 }, gridStub);
|
|
1772
|
+
const query = service.buildQuery();
|
|
1773
|
+
expect(query).toContain('LIMIT 42');
|
|
1774
|
+
});
|
|
1775
|
+
|
|
1776
|
+
it('should set totalItems from top-level totalCount in postProcess', () => {
|
|
1777
|
+
(service as any)['pagination'] = { totalItems: 0 };
|
|
1778
|
+
(service as any)['options'] = { tableName: 'users' };
|
|
1779
|
+
(service as any)['postProcess']({ totalCount: 123 });
|
|
1780
|
+
expect((service as any)['pagination'].totalItems).toBe(123);
|
|
1781
|
+
});
|
|
1782
|
+
|
|
1783
|
+
it('should handle Contains and NOT_CONTAINS operators in _buildWhereClause', () => {
|
|
1784
|
+
service['options'] = { tableName: 'users' };
|
|
1785
|
+
service['_columns'] = [{ id: 'foo', field: 'foo' }];
|
|
1786
|
+
service['options'].filteringOptions = [
|
|
1787
|
+
{ field: 'foo', operator: 'Contains', value: 'bar' },
|
|
1788
|
+
{ field: 'foo', operator: 'NOT_CONTAINS', value: 'baz' },
|
|
1789
|
+
];
|
|
1790
|
+
const where = service['buildWhereClause']();
|
|
1791
|
+
expect(where).toContain('LIKE');
|
|
1792
|
+
expect(where).toContain('NOT LIKE');
|
|
1793
|
+
});
|
|
1794
|
+
|
|
1795
|
+
it('should throw in _buildOrderByClause if options, tableName, or columns are missing', () => {
|
|
1796
|
+
expect(() => service['buildOrderByClause']()).toThrow();
|
|
1797
|
+
service.options = { tableName: 'users' };
|
|
1798
|
+
expect(() => service['buildOrderByClause']()).toThrow();
|
|
1799
|
+
service.options = undefined as any;
|
|
1800
|
+
service['_columns'] = [{ id: 'foo', field: 'foo' }];
|
|
1801
|
+
expect(() => service['buildOrderByClause']()).toThrow();
|
|
1802
|
+
});
|
|
1803
|
+
|
|
1804
|
+
it('should return empty string from _buildOrderByClause if no valid sorters', () => {
|
|
1805
|
+
service.options = { tableName: 'users' };
|
|
1806
|
+
service['_columns'] = [{ id: 'foo', field: 'foo' }];
|
|
1807
|
+
service.options.sortingOptions = [{ field: 'foo', direction: 'ASC' }];
|
|
1808
|
+
// simulate dot notation
|
|
1809
|
+
service['_columns'][0].field = 'foo.bar';
|
|
1810
|
+
const order = service['buildOrderByClause']();
|
|
1811
|
+
expect(order).toBe('');
|
|
1812
|
+
});
|
|
1813
|
+
|
|
1814
|
+
it('should return NULL from _escapeSql for null/undefined', () => {
|
|
1815
|
+
expect(service['_escapeSql'](null)).toBe('NULL');
|
|
1816
|
+
expect(service['_escapeSql'](undefined)).toBe('NULL');
|
|
1817
|
+
});
|
|
1818
|
+
});
|
|
1819
|
+
});
|