@builder6/query-mongodb 0.6.1

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.
@@ -0,0 +1,1112 @@
1
+ /* eslint-disable no-unused-expressions */
2
+ /* global suite, test */
3
+
4
+ const chai = require('chai');
5
+ const expect = chai.expect;
6
+ const qs = require('qs');
7
+
8
+ const {
9
+ getOptions,
10
+ private: {
11
+ fixFilterAndSearch,
12
+ validateAll,
13
+ check,
14
+ takeOptions,
15
+ skipOptions,
16
+ totalCountOptions,
17
+ sortOptions,
18
+ groupOptions,
19
+ totalSummaryOptions,
20
+ filterOptions,
21
+ searchOptions,
22
+ selectOptions,
23
+ sortOptionsChecker,
24
+ groupOptionsChecker,
25
+ summaryOptionsChecker,
26
+ asBool,
27
+ parse,
28
+ },
29
+ } = require('./options');
30
+
31
+ function testOptions(queryString, expectedResult, schema) {
32
+ const result = getOptions(qs.parse(queryString), schema);
33
+ // console.log(`Testing query ${queryString}`);
34
+ // console.log('Result: ', JSON.stringify(result, null, 2));
35
+
36
+ expect(result).to.eql(expectedResult);
37
+ }
38
+
39
+ suite('parse', function () {
40
+ test('parse correctly', function () {
41
+ const source = { test: 'text', test2: 42 };
42
+ expect(parse(JSON.stringify(source))).to.eql(source);
43
+ });
44
+
45
+ test('parse error', function () {
46
+ // For instance: forgotten quote
47
+ const sourceText = '{"field1":"thing",error:true}';
48
+ try {
49
+ parse(sourceText);
50
+ throw new Error('parse did not throw!');
51
+ } catch (e) {
52
+ // We want an error here that produces a proper string
53
+ // when stringified - so let's test that.
54
+ // Standard Error just gives {}.
55
+ expect(JSON.stringify(e)).to.not.eql('{}');
56
+ }
57
+ });
58
+ });
59
+
60
+ suite('asBool', function () {
61
+ test('true', function () {
62
+ expect(asBool(true)).to.be.true;
63
+ });
64
+
65
+ test('false', function () {
66
+ expect(asBool(false)).to.be.false;
67
+ });
68
+
69
+ test('true string', function () {
70
+ expect(asBool('TrUe')).to.be.true;
71
+ });
72
+
73
+ test('false string', function () {
74
+ expect(asBool('FaLsE')).to.be.false;
75
+ });
76
+
77
+ test('thruthy number', function () {
78
+ expect(asBool(7)).to.be.true;
79
+ });
80
+
81
+ test('falsy number', function () {
82
+ expect(asBool(0)).to.be.false;
83
+ });
84
+
85
+ test('thruthy string', function () {
86
+ expect(asBool('something')).to.be.true;
87
+ });
88
+
89
+ test('falsy string', function () {
90
+ expect(asBool('')).to.be.false;
91
+ });
92
+ });
93
+
94
+ suite('summaryOptionsChecker', function () {
95
+ test('valid', function () {
96
+ const result = summaryOptionsChecker.validate({
97
+ summaryType: 'sum',
98
+ selector: 'thing',
99
+ });
100
+ expect(result).to.eql(null);
101
+ });
102
+
103
+ test('extra prop', function () {
104
+ const result = summaryOptionsChecker.validate({
105
+ summaryType: 'sum',
106
+ selector: 'thing',
107
+ extra: 'thing',
108
+ });
109
+ expect(result).to.include({ name: 'ValidationError', type: 'noUnknown' });
110
+ });
111
+
112
+ test('invalid summaryType', function () {
113
+ const result = summaryOptionsChecker.validate({
114
+ summaryType: 'unknown',
115
+ selector: 'thing',
116
+ });
117
+ expect(result).to.include({ name: 'ValidationError', type: 'oneOf' });
118
+ });
119
+
120
+ test('invalid selector', function () {
121
+ const result = summaryOptionsChecker.validate({
122
+ summaryType: 'sum',
123
+ selector: true,
124
+ });
125
+ expect(result).to.include({ name: 'ValidationError', type: 'typeError' });
126
+ });
127
+ });
128
+
129
+ suite('groupOptionsChecker', function () {
130
+ test('valid', function () {
131
+ const result = groupOptionsChecker.validate({
132
+ selector: 'thing',
133
+ desc: true,
134
+ isExpanded: true,
135
+ groupInterval: 'year',
136
+ });
137
+ expect(result).to.eql(null);
138
+ });
139
+
140
+ test('valid with groupInterval integer', function () {
141
+ const result = groupOptionsChecker.validate({
142
+ selector: 'thing',
143
+ desc: true,
144
+ isExpanded: true,
145
+ groupInterval: 11,
146
+ });
147
+ expect(result).to.eql(null);
148
+ });
149
+
150
+ test('extra prop', function () {
151
+ const result = groupOptionsChecker.validate({
152
+ selector: 'thing',
153
+ desc: true,
154
+ isExpanded: true,
155
+ groupInterval: 'year',
156
+ extra: 'thing',
157
+ });
158
+ expect(result).to.include({ name: 'ValidationError', type: 'noUnknown' });
159
+ });
160
+
161
+ test('missing selector', function () {
162
+ const result = groupOptionsChecker.validate({
163
+ // selector: 'thing',
164
+ desc: true,
165
+ isExpanded: true,
166
+ groupInterval: 'year',
167
+ });
168
+ expect(result).to.include({ name: 'ValidationError', type: 'optionality' });
169
+ });
170
+
171
+ test('invalid desc', function () {
172
+ const result = groupOptionsChecker.validate({
173
+ selector: 'thing',
174
+ desc: 42,
175
+ isExpanded: true,
176
+ groupInterval: 'year',
177
+ });
178
+ expect(result).to.include({ name: 'ValidationError', type: 'typeError' });
179
+ });
180
+
181
+ test('invalid selector', function () {
182
+ const result = groupOptionsChecker.validate({
183
+ selector: 42,
184
+ desc: true,
185
+ isExpanded: true,
186
+ groupInterval: 'year',
187
+ });
188
+ expect(result).to.include({ name: 'ValidationError', type: 'typeError' });
189
+ });
190
+
191
+ test('invalid isExpanded', function () {
192
+ const result = groupOptionsChecker.validate({
193
+ selector: 'thing',
194
+ desc: true,
195
+ isExpanded: 42,
196
+ groupInterval: 'year',
197
+ });
198
+ expect(result).to.include({ name: 'ValidationError', type: 'typeError' });
199
+ });
200
+
201
+ test('invalid groupInterval - not string or number', function () {
202
+ const result = groupOptionsChecker.validate({
203
+ selector: 'thing',
204
+ desc: true,
205
+ isExpanded: true,
206
+ groupInterval: true,
207
+ }); //?
208
+ expect(result).to.include({ name: 'ValidationError', type: 'or' });
209
+ });
210
+
211
+ test('invalid groupInterval - not string or integer', function () {
212
+ const result = groupOptionsChecker.validate({
213
+ selector: 'thing',
214
+ desc: true,
215
+ isExpanded: true,
216
+ groupInterval: 10.3,
217
+ }); //?
218
+ expect(result).to.include({ name: 'ValidationError', type: 'or' });
219
+ });
220
+
221
+ test('invalid groupInterval - invalid string', function () {
222
+ const result = groupOptionsChecker.validate({
223
+ selector: 'thing',
224
+ desc: true,
225
+ isExpanded: true,
226
+ groupInterval: 'wrong string',
227
+ }); //?
228
+ expect(result).to.include({ name: 'ValidationError', type: 'or' });
229
+ });
230
+ });
231
+
232
+ suite('sortOptionsChecker', function () {
233
+ test('missing desc', function () {
234
+ const result = sortOptionsChecker.validate({ selector: 'thing' });
235
+ expect(result).to.include({ name: 'ValidationError', type: 'optionality' });
236
+ });
237
+
238
+ test('missing selector', function () {
239
+ const result = sortOptionsChecker.validate({ desc: true });
240
+ expect(result).to.include({ name: 'ValidationError', type: 'optionality' });
241
+ });
242
+
243
+ test('valid', function () {
244
+ const result = sortOptionsChecker.validate({
245
+ selector: 'thing',
246
+ desc: true,
247
+ });
248
+ expect(result).to.eql(null);
249
+ });
250
+
251
+ test('valid with isExpanded', function () {
252
+ const result = sortOptionsChecker.validate({
253
+ selector: 'thing',
254
+ desc: true,
255
+ isExpanded: 'random thing',
256
+ });
257
+ expect(result).to.eql(null);
258
+ });
259
+
260
+ test('extra prop', function () {
261
+ const result = sortOptionsChecker.validate({
262
+ selector: 'thing',
263
+ desc: true,
264
+ extra: 'thing',
265
+ });
266
+ expect(result).to.include({ name: 'ValidationError', type: 'noUnknown' });
267
+ });
268
+
269
+ test('incorrect desc type', function () {
270
+ const result = sortOptionsChecker.validate({
271
+ selector: 'thing',
272
+ desc: 42,
273
+ });
274
+ //console.log('Error (direct from validator): ' + JSON.stringify(result));
275
+ expect(result).to.include({ name: 'ValidationError', type: 'typeError' });
276
+ });
277
+
278
+ test('incorrect selector type', function () {
279
+ const result = sortOptionsChecker.validate({
280
+ selector: 42,
281
+ desc: true,
282
+ });
283
+ expect(result).to.include({ name: 'ValidationError', type: 'typeError' });
284
+ });
285
+ });
286
+
287
+ suite('takeOptions', function () {
288
+ test('valid', function () {
289
+ expect(takeOptions({ take: 10 })).to.eql({ loadOptions: { take: 10 } });
290
+ });
291
+
292
+ test('valid string value', function () {
293
+ expect(takeOptions({ take: '10' })).to.eql({ loadOptions: { take: 10 } });
294
+ });
295
+
296
+ test('missing parameter', function () {
297
+ expect(takeOptions({ tk: 10 })).to.eql({});
298
+ });
299
+
300
+ test('invalid value', function () {
301
+ expect(takeOptions({ take: -10 })).to.eql({
302
+ errors: [`Invalid 'take': -10`],
303
+ });
304
+ });
305
+ });
306
+
307
+ suite('skipOptions', function () {
308
+ test('valid', function () {
309
+ expect(skipOptions({ skip: 10 })).to.eql({ loadOptions: { skip: 10 } });
310
+ });
311
+
312
+ test('valid string value', function () {
313
+ expect(skipOptions({ skip: '10' })).to.eql({ loadOptions: { skip: 10 } });
314
+ });
315
+
316
+ test('missing parameter', function () {
317
+ expect(skipOptions({ skp: 10 })).to.eql({});
318
+ });
319
+
320
+ test('invalid value', function () {
321
+ expect(skipOptions({ skip: -10 })).to.eql({
322
+ errors: [`Invalid 'skip': -10`],
323
+ });
324
+ });
325
+ });
326
+
327
+ suite('totalCountOptions', function () {
328
+ test('valid', function () {
329
+ expect(totalCountOptions({ requireTotalCount: true })).to.eql({
330
+ loadOptions: { requireTotalCount: true },
331
+ });
332
+ });
333
+
334
+ test('valid string value', function () {
335
+ expect(totalCountOptions({ requireTotalCount: 'true' })).to.eql({
336
+ loadOptions: { requireTotalCount: true },
337
+ });
338
+ });
339
+
340
+ test('missing parameter', function () {
341
+ expect(totalCountOptions({ rqtc: true })).to.eql({});
342
+ });
343
+ });
344
+
345
+ suite('sortOptions', function () {
346
+ test('invalid', function () {
347
+ const result = sortOptions({ sort: ['thing'] });
348
+ expect(result.errors[0].message).to.match(
349
+ /^Sort parameter validation errors/
350
+ );
351
+ });
352
+
353
+ test('empty', function () {
354
+ const result = sortOptions({ sort: [] });
355
+ expect(result).to.eql({ errors: [`Invalid 'sort': `] });
356
+ });
357
+ });
358
+
359
+ suite('totalSummaryOptions', function () {
360
+ test('invalid', function () {
361
+ const result = totalSummaryOptions({ totalSummary: ['thing'] });
362
+ expect(result.errors[0].message).to.match(
363
+ /^Total summary parameter validation errors/
364
+ );
365
+ });
366
+
367
+ test('empty', function () {
368
+ const result = totalSummaryOptions({ totalSummary: [] });
369
+ expect(result).to.eql({ loadOptions: {} });
370
+ });
371
+
372
+ test('non-array', function () {
373
+ const result = totalSummaryOptions({ totalSummary: {} });
374
+ expect(result).to.eql({
375
+ errors: [`Invalid 'totalSummary': [object Object]`],
376
+ });
377
+ });
378
+ });
379
+
380
+ suite('filterOptions', function () {
381
+ test('valid array', function () {
382
+ const result = filterOptions({ filter: ['thing'] });
383
+ expect(result).to.eql({ loadOptions: { filter: ['thing'] } });
384
+ });
385
+
386
+ test('valid string', function () {
387
+ const result = filterOptions({ filter: 'thing' });
388
+ expect(result).to.eql({ loadOptions: { filter: 'thing' } });
389
+ });
390
+
391
+ test('valid string with an array inside', function () {
392
+ const result = filterOptions({ filter: '["thing"]' });
393
+ expect(result).to.eql({ loadOptions: { filter: ['thing'] } });
394
+ });
395
+
396
+ test('not string or array', function () {
397
+ const result = filterOptions({ filter: {} });
398
+ expect(result).to.eql({ errors: [`Invalid 'filter': [object Object]`] });
399
+ });
400
+ });
401
+
402
+ suite('searchOptions', function () {
403
+ test('valid with string', function () {
404
+ const result = searchOptions({
405
+ searchExpr: 'expr',
406
+ searchOperation: '=',
407
+ searchValue: 'val',
408
+ });
409
+ expect(result).to.eql({
410
+ loadOptions: {
411
+ searchExpr: 'expr',
412
+ searchOperation: '=',
413
+ searchValue: 'val',
414
+ },
415
+ });
416
+ });
417
+
418
+ test('valid with array', function () {
419
+ const result = searchOptions({
420
+ searchExpr: ['expr1', 'expr2'],
421
+ searchOperation: '=',
422
+ searchValue: 'val',
423
+ });
424
+ expect(result).to.eql({
425
+ loadOptions: {
426
+ searchExpr: ['expr1', 'expr2'],
427
+ searchOperation: '=',
428
+ searchValue: 'val',
429
+ },
430
+ });
431
+ });
432
+
433
+ test('invalid searchExpr', function () {
434
+ const result = searchOptions({
435
+ searchExpr: 42,
436
+ searchOperation: '=',
437
+ searchValue: 'val',
438
+ });
439
+ expect(result).to.eql({
440
+ errors: [
441
+ `Invalid 'searchExpr': 42`,
442
+ `Invalid 'searchOperation': =`,
443
+ `Invalid 'searchValue': val`,
444
+ ],
445
+ });
446
+ });
447
+ });
448
+
449
+ suite('selectOptions', function () {
450
+ test('valid string', function () {
451
+ const result = selectOptions({ select: 'something' });
452
+ expect(result).to.eql({ loadOptions: { select: ['something'] } });
453
+ });
454
+
455
+ test('valid array', function () {
456
+ const result = selectOptions({ select: ['something', 'other'] });
457
+ expect(result).to.eql({ loadOptions: { select: ['something', 'other'] } });
458
+ });
459
+
460
+ test('array with invalid content', function () {
461
+ const result = selectOptions({ select: ['something', 'other', 42] });
462
+ expect(result.errors[0].message).to.match(
463
+ /Select array parameter has invalid content/
464
+ );
465
+ });
466
+
467
+ test('empty array', function () {
468
+ const result = selectOptions({ select: [] });
469
+ expect(result).to.eql({ loadOptions: {} });
470
+ });
471
+
472
+ test('type other than string and array', function () {
473
+ const result = selectOptions({ select: 42 });
474
+ expect(result).to.eql({ errors: [`Invalid 'select': 42`] });
475
+ });
476
+ });
477
+
478
+ suite('groupOptions', function () {
479
+ test('invalid top level options', function () {
480
+ const result = groupOptions({ group: ['thing'] });
481
+ expect(result.errors[0].message).to.match(
482
+ /^Group parameter validation errors/
483
+ );
484
+ });
485
+
486
+ test('empty top level options', function () {
487
+ const result = groupOptions({ group: [] });
488
+ expect(result).to.eql({});
489
+ });
490
+
491
+ test('non-array top level options', function () {
492
+ const result = groupOptions({ group: {} });
493
+ expect(result).to.eql({ errors: [`Invalid 'group': [object Object]`] });
494
+ });
495
+
496
+ test('invalid group summary options', function () {
497
+ const result = groupOptions({
498
+ group: [{ selector: 'x' }],
499
+ groupSummary: ['thing'],
500
+ });
501
+ expect(result.errors[0].message).to.match(
502
+ /^Group summary parameter validation errors/
503
+ );
504
+ });
505
+
506
+ test('empty group summary options', function () {
507
+ const result = groupOptions({
508
+ group: [{ selector: 'x' }],
509
+ groupSummary: [],
510
+ });
511
+ expect(result).to.eql({
512
+ errors: [],
513
+ loadOptions: { group: [{ isExpanded: false, selector: 'x' }] },
514
+ processingOptions: {},
515
+ });
516
+ });
517
+
518
+ test('non-array group summary options', function () {
519
+ const result = groupOptions({
520
+ group: [{ selector: 'x' }],
521
+ groupSummary: {},
522
+ });
523
+ expect(result).to.eql({
524
+ errors: [`Invalid 'groupSummary': [object Object]`],
525
+ loadOptions: { group: [{ isExpanded: false, selector: 'x' }] },
526
+ processingOptions: {},
527
+ });
528
+ });
529
+ });
530
+
531
+ suite('check', function () {
532
+ test('default value for one option', function () {
533
+ expect(
534
+ check(
535
+ { one: 1, other: 2 },
536
+ 'nonexistent',
537
+ undefined,
538
+ undefined,
539
+ 'default value'
540
+ )
541
+ ).to.eql('default value');
542
+ });
543
+
544
+ test('default value for multiple options', function () {
545
+ expect(
546
+ check(
547
+ { one: 1, other: 2 },
548
+ ['one', 'nonexistent'],
549
+ undefined,
550
+ undefined,
551
+ 'default value'
552
+ )
553
+ ).to.eql('default value');
554
+ });
555
+
556
+ test('simple converter', function () {
557
+ expect(
558
+ check(
559
+ { one: 1, other: 2 },
560
+ ['one', 'other'],
561
+ (one, other) => ({
562
+ one,
563
+ other,
564
+ }),
565
+ (x) => x * 2
566
+ )
567
+ ).to.eql({ loadOptions: { one: 2, other: 4 } });
568
+ });
569
+
570
+ test('checker is unhappy', function () {
571
+ // not very well done at the moment
572
+ expect(
573
+ check({ one: 1, other: 2 }, ['one', 'other'], () => undefined)
574
+ ).to.eql({ errors: [`Invalid 'one': 1`, `Invalid 'other': 2`] });
575
+ });
576
+
577
+ test('checker is really unhappy', function () {
578
+ expect(
579
+ check({ one: 1, other: 2 }, ['one', 'other'], () => {
580
+ throw 'argh!';
581
+ })
582
+ ).to.eql({ errors: ['argh!'] });
583
+ });
584
+ });
585
+
586
+ suite('validateAll', function () {
587
+ test('simple short circuit test', function () {
588
+ const checker = { validate: (x) => x > 10 };
589
+ expect(validateAll([5, 7, 15, 25], checker, true)).to.eql({
590
+ valid: false,
591
+ errors: [true],
592
+ });
593
+ });
594
+
595
+ test('test without short circuit', function () {
596
+ // I believe this functionality is not used in the library code
597
+ const checker = { validate: (x) => x > 10 };
598
+ expect(validateAll([5, 7, 15, 25], checker, false)).to.eql({
599
+ valid: false,
600
+ errors: [true, true],
601
+ });
602
+ });
603
+ });
604
+
605
+ suite('fixFilterAndSearch', function () {
606
+ test('fixes filter int', function () {
607
+ expect(
608
+ fixFilterAndSearch({
609
+ int: 'int',
610
+ })({
611
+ filter: [['int', '=', '34']],
612
+ })
613
+ ).to.eql({
614
+ filter: [['int', '=', 34]],
615
+ });
616
+ });
617
+
618
+ test('accepts undefined query input', () => {
619
+ expect(fixFilterAndSearch('schema')(undefined)).to.eql(undefined);
620
+ });
621
+
622
+ test('fixes search int', function () {
623
+ expect(
624
+ fixFilterAndSearch({
625
+ int: 'int',
626
+ })({
627
+ searchExpr: 'int',
628
+ searchOperation: '=',
629
+ searchValue: '34',
630
+ })
631
+ ).to.eql({
632
+ searchExpr: 'int',
633
+ searchOperation: '=',
634
+ searchValue: 34,
635
+ });
636
+ });
637
+
638
+ test('no fix for field not defined in schema', function () {
639
+ expect(
640
+ fixFilterAndSearch({
641
+ int: 'int',
642
+ })({
643
+ searchExpr: 'other',
644
+ searchOperation: '=',
645
+ searchValue: '34',
646
+ })
647
+ ).to.eql({
648
+ searchExpr: 'other',
649
+ searchOperation: '=',
650
+ searchValue: '34',
651
+ });
652
+ });
653
+
654
+ test('fixes search expr list', function () {
655
+ // see comments above fixSearch implementation
656
+ expect(
657
+ fixFilterAndSearch({
658
+ int: 'int',
659
+ })({
660
+ searchExpr: ['int', 'other'],
661
+ searchOperation: '=',
662
+ searchValue: '34',
663
+ })
664
+ ).to.eql({
665
+ searchExpr: ['int', 'other'],
666
+ searchOperation: '=',
667
+ searchValue: 34,
668
+ });
669
+ });
670
+
671
+ test('no fix for expr list without schema entries', function () {
672
+ expect(
673
+ fixFilterAndSearch({
674
+ int: 'int',
675
+ })({
676
+ searchExpr: ['one', 'other'],
677
+ searchOperation: '=',
678
+ searchValue: '34',
679
+ })
680
+ ).to.eql({
681
+ searchExpr: ['one', 'other'],
682
+ searchOperation: '=',
683
+ searchValue: '34',
684
+ });
685
+ });
686
+
687
+ test('no fix for expr thats not string or array', function () {
688
+ expect(
689
+ fixFilterAndSearch({
690
+ int: 'int',
691
+ })({
692
+ searchExpr: 42,
693
+ searchOperation: '=',
694
+ searchValue: '34',
695
+ })
696
+ ).to.eql({
697
+ searchExpr: 42,
698
+ searchOperation: '=',
699
+ searchValue: '34',
700
+ });
701
+ });
702
+ });
703
+
704
+ suite('getOptions', function () {
705
+ test('take and total count', function () {
706
+ testOptions('take=10&requireTotalCount=true', {
707
+ errors: [],
708
+ loadOptions: {
709
+ take: 10,
710
+ requireTotalCount: true,
711
+ },
712
+ processingOptions: {},
713
+ });
714
+ });
715
+
716
+ test('take and total count with tzOffset', function () {
717
+ testOptions('take=10&requireTotalCount=true&tzOffset=-60', {
718
+ errors: [],
719
+ loadOptions: {
720
+ take: 10,
721
+ requireTotalCount: true,
722
+ },
723
+ processingOptions: {
724
+ timezoneOffset: -60,
725
+ },
726
+ });
727
+ });
728
+
729
+ test('take and total count with caseInsensitiveRegex', function () {
730
+ testOptions('take=10&requireTotalCount=true&caseInsensitiveRegex=false', {
731
+ errors: [],
732
+ loadOptions: {
733
+ take: 10,
734
+ requireTotalCount: true,
735
+ },
736
+ processingOptions: {
737
+ caseInsensitiveRegex: false,
738
+ },
739
+ });
740
+ });
741
+
742
+ test('take, skip, total count', function () {
743
+ testOptions('take=10&requireTotalCount=true&skip=30', {
744
+ errors: [],
745
+ loadOptions: {
746
+ take: 10,
747
+ skip: 30,
748
+ requireTotalCount: true,
749
+ },
750
+ processingOptions: {},
751
+ });
752
+ });
753
+
754
+ test('contains 3 digits', function () {
755
+ testOptions(
756
+ 'filter%5B0%5D%5B0%5D=field&filter%5B0%5D%5B1%5D=contains&filter%5B0%5D%5B2%5D=234',
757
+ {
758
+ errors: [],
759
+ loadOptions: {
760
+ filter: [['field', 'contains', '234']],
761
+ },
762
+ processingOptions: {},
763
+ }
764
+ );
765
+ });
766
+
767
+ test('contains 4 digits', function () {
768
+ testOptions(
769
+ 'filter%5B0%5D%5B0%5D=field&filter%5B0%5D%5B1%5D=contains&filter%5B0%5D%5B2%5D=2345',
770
+ {
771
+ errors: [],
772
+ loadOptions: {
773
+ filter: [['field', 'contains', '2345']],
774
+ },
775
+ processingOptions: {},
776
+ }
777
+ );
778
+ });
779
+
780
+ test('contains 4 digits with dashes', function () {
781
+ testOptions(
782
+ 'filter%5B0%5D%5B0%5D=field&filter%5B0%5D%5B1%5D=contains&filter%5B0%5D%5B2%5D=23-45',
783
+ {
784
+ errors: [],
785
+ loadOptions: {
786
+ filter: [['field', 'contains', '23-45']],
787
+ },
788
+ processingOptions: {},
789
+ }
790
+ );
791
+ });
792
+
793
+ test('contains 5 digits', function () {
794
+ testOptions(
795
+ 'filter%5B0%5D%5B0%5D=field&filter%5B0%5D%5B1%5D=contains&filter%5B0%5D%5B2%5D=23456',
796
+ {
797
+ errors: [],
798
+ loadOptions: {
799
+ filter: [['field', 'contains', '23456']],
800
+ },
801
+ processingOptions: {},
802
+ }
803
+ );
804
+ });
805
+
806
+ test('contains 3 chars', function () {
807
+ testOptions(
808
+ 'filter%5B0%5D%5B0%5D=field&filter%5B0%5D%5B1%5D=contains&filter%5B0%5D%5B2%5D=abc',
809
+ {
810
+ errors: [],
811
+ loadOptions: {
812
+ filter: [['field', 'contains', 'abc']],
813
+ },
814
+ processingOptions: {},
815
+ }
816
+ );
817
+ });
818
+
819
+ test('contains 4 chars', function () {
820
+ testOptions(
821
+ 'filter%5B0%5D%5B0%5D=field&filter%5B0%5D%5B1%5D=contains&filter%5B0%5D%5B2%5D=abcd',
822
+ {
823
+ errors: [],
824
+ loadOptions: {
825
+ filter: [['field', 'contains', 'abcd']],
826
+ },
827
+ processingOptions: {},
828
+ }
829
+ );
830
+ });
831
+
832
+ test('sort, take and total count', function () {
833
+ testOptions(
834
+ 'sort%5B0%5D%5Bselector%5D=date2&sort%5B0%5D%5Bdesc%5D=false&take=10&requireTotalCount=true',
835
+ {
836
+ errors: [],
837
+ loadOptions: {
838
+ sort: [
839
+ {
840
+ selector: 'date2',
841
+ desc: false,
842
+ },
843
+ ],
844
+ take: 10,
845
+ requireTotalCount: true,
846
+ },
847
+ processingOptions: {},
848
+ }
849
+ );
850
+ });
851
+
852
+ test('sort with correct parameter', function () {
853
+ testOptions('sort=[{%22selector%22:%22population%22,%22desc%22:true}]', {
854
+ errors: [],
855
+ loadOptions: {
856
+ sort: [
857
+ {
858
+ selector: 'population',
859
+ desc: true,
860
+ },
861
+ ],
862
+ },
863
+ processingOptions: {},
864
+ });
865
+ });
866
+
867
+ test('sort with incorrect parameter', function () {
868
+ const queryString =
869
+ 'sort=[{%22selectorX%22:%22population%22,%22desc%22:true}]';
870
+ const result = getOptions(qs.parse(queryString));
871
+
872
+ expect(result.errors.length).to.eql(1);
873
+ //console.log(typeof result.errors[0]);
874
+ //console.log('Error array from getOptions result: ' + JSON.stringify(result.errors));
875
+ expect(result.errors[0].message)
876
+ .to.be.a('string')
877
+ .and.satisfy((s) => s.startsWith('Sort parameter validation errors:'));
878
+ });
879
+
880
+ test('issue #10 - filter works when given as array', function () {
881
+ expect(
882
+ getOptions(
883
+ {
884
+ filter:
885
+ // '[["dtFinished",">=","2018-08-01T16:20:30.000Z"],"and",["dtFinished","<","2018-08-01T16:20:30.000Z"]]'
886
+ [
887
+ ['dtFinished', '>=', '2018-08-01T16:20:30.000Z'],
888
+ 'and',
889
+ ['dtFinished', '<', '2018-08-01T16:20:30.000Z'],
890
+ ],
891
+ },
892
+ { dtFinished: 'datetime' }
893
+ )
894
+ ).to.eql({
895
+ errors: [],
896
+ loadOptions: {
897
+ filter: [
898
+ ['dtFinished', '>=', new Date('2018-08-01T16:20:30.000Z')],
899
+ 'and',
900
+ ['dtFinished', '<', new Date('2018-08-01T16:20:30.000Z')],
901
+ ],
902
+ },
903
+ processingOptions: {},
904
+ });
905
+ });
906
+
907
+ test('filter works with a bool value', function () {
908
+ expect(
909
+ getOptions({
910
+ filter: [['done', '=', true]],
911
+ })
912
+ ).to.eql({
913
+ errors: [],
914
+ loadOptions: {
915
+ filter: [['done', '=', true]],
916
+ },
917
+ processingOptions: {},
918
+ });
919
+ });
920
+
921
+ test('filter works with a bool value given as a string', function () {
922
+ expect(
923
+ getOptions(
924
+ {
925
+ filter: [['done', '=', 'true']],
926
+ },
927
+ { done: 'bool' }
928
+ )
929
+ ).to.eql({
930
+ errors: [],
931
+ loadOptions: {
932
+ filter: [['done', '=', true]],
933
+ },
934
+ processingOptions: {},
935
+ });
936
+ });
937
+
938
+ test('issue #10 - filter works when given as string', function () {
939
+ expect(
940
+ getOptions(
941
+ {
942
+ filter:
943
+ '[["dtFinished",">=","2018-08-01T16:20:30.000Z"],"and",["dtFinished","<","2018-08-01T16:20:30.000Z"]]',
944
+ },
945
+ { dtFinished: 'datetime' }
946
+ )
947
+ ).to.eql({
948
+ errors: [],
949
+ loadOptions: {
950
+ filter: [
951
+ ['dtFinished', '>=', new Date('2018-08-01T16:20:30.000Z')],
952
+ 'and',
953
+ ['dtFinished', '<', new Date('2018-08-01T16:20:30.000Z')],
954
+ ],
955
+ },
956
+ processingOptions: {},
957
+ });
958
+ });
959
+
960
+ test('total count, group, group count', function () {
961
+ testOptions(
962
+ 'sort%5B0%5D%5Bselector%5D=date2&sort%5B0%5D%5Bdesc%5D=false&requireTotalCount=true&group%5B0%5D%5Bselector%5D=date2&group%5B0%5D%5BisExpanded%5D=false&requireGroupCount=true',
963
+ {
964
+ errors: [],
965
+ loadOptions: {
966
+ sort: [
967
+ {
968
+ selector: 'date2',
969
+ desc: false,
970
+ },
971
+ ],
972
+ requireTotalCount: true,
973
+ group: [
974
+ {
975
+ selector: 'date2',
976
+ isExpanded: false,
977
+ },
978
+ ],
979
+ requireGroupCount: true,
980
+ },
981
+ processingOptions: {},
982
+ }
983
+ );
984
+ });
985
+
986
+ test('sort, filter with datetime in schema', function () {
987
+ testOptions(
988
+ 'sort%5B0%5D%5Bselector%5D=date2&sort%5B0%5D%5Bdesc%5D=false&filter%5B0%5D%5B0%5D=date2&filter%5B0%5D%5B1%5D=%3D&filter%5B0%5D%5B2%5D=2017-07-13T00%3A00%3A00.000Z',
989
+ {
990
+ errors: [],
991
+ loadOptions: {
992
+ sort: [
993
+ {
994
+ selector: 'date2',
995
+ desc: false,
996
+ },
997
+ ],
998
+ filter: [['date2', '=', new Date(Date.parse('2017-07-13'))]],
999
+ },
1000
+ processingOptions: {},
1001
+ },
1002
+ { date2: 'datetime' }
1003
+ );
1004
+ });
1005
+
1006
+ test('take, total count, filter with int', function () {
1007
+ testOptions(
1008
+ 'take=10&requireTotalCount=true&filter%5B0%5D%5B0%5D=int1&filter%5B0%5D%5B1%5D=%3D&filter%5B0%5D%5B2%5D=4',
1009
+ {
1010
+ errors: [],
1011
+ loadOptions: {
1012
+ take: 10,
1013
+ requireTotalCount: true,
1014
+ filter: [['int1', '=', 4]],
1015
+ },
1016
+ processingOptions: {},
1017
+ },
1018
+ {
1019
+ int1: 'int',
1020
+ }
1021
+ );
1022
+ });
1023
+
1024
+ test('summaryQueryLimit, skip, take, requireTotalCount, totalSummary, tzOffset', function () {
1025
+ testOptions(
1026
+ 'summaryQueryLimit=500&skip=0&take=20&requireTotalCount=true&totalSummary=%5B%7B%22selector%22%3A%22date1%22%2C%22summaryType%22%3A%22max%22%7D%2C%7B%22selector%22%3A%22int1%22%2C%22summaryType%22%3A%22avg%22%7D%2C%7B%22selector%22%3A%22int1%22%2C%22summaryType%22%3A%22sum%22%7D%5D&tzOffset=-60',
1027
+ {
1028
+ errors: [],
1029
+ loadOptions: {
1030
+ skip: 0,
1031
+ take: 20,
1032
+ requireTotalCount: true,
1033
+ totalSummary: [
1034
+ {
1035
+ selector: 'date1',
1036
+ summaryType: 'max',
1037
+ },
1038
+ {
1039
+ selector: 'int1',
1040
+ summaryType: 'avg',
1041
+ },
1042
+ {
1043
+ selector: 'int1',
1044
+ summaryType: 'sum',
1045
+ },
1046
+ ],
1047
+ },
1048
+ processingOptions: {
1049
+ timezoneOffset: -60,
1050
+ summaryQueryLimit: 500,
1051
+ },
1052
+ }
1053
+ );
1054
+ });
1055
+
1056
+ test('summaryQueryLimit, skip, take, requireTotalCount, totalSummary, group, requireGroupCount, groupSummary, tzOffset', function () {
1057
+ testOptions(
1058
+ 'summaryQueryLimit=500&skip=0&take=20&requireTotalCount=true&totalSummary=%5B%7B%22selector%22%3A%22date1%22%2C%22summaryType%22%3A%22max%22%7D%2C%7B%22selector%22%3A%22int1%22%2C%22summaryType%22%3A%22avg%22%7D%2C%7B%22selector%22%3A%22int1%22%2C%22summaryType%22%3A%22sum%22%7D%5D&group=%5B%7B%22selector%22%3A%22int1%22%2C%22desc%22%3Afalse%2C%22isExpanded%22%3Afalse%7D%5D&requireGroupCount=true&groupSummary=%5B%7B%22selector%22%3A%22date1%22%2C%22summaryType%22%3A%22min%22%7D%2C%7B%22selector%22%3A%22int1%22%2C%22summaryType%22%3A%22avg%22%7D%2C%7B%22selector%22%3A%22int1%22%2C%22summaryType%22%3A%22sum%22%7D%2C%7B%22summaryType%22%3A%22count%22%7D%5D&tzOffset=-60',
1059
+ {
1060
+ errors: [],
1061
+ loadOptions: {
1062
+ skip: 0,
1063
+ take: 20,
1064
+ requireTotalCount: true,
1065
+ totalSummary: [
1066
+ {
1067
+ selector: 'date1',
1068
+ summaryType: 'max',
1069
+ },
1070
+ {
1071
+ selector: 'int1',
1072
+ summaryType: 'avg',
1073
+ },
1074
+ {
1075
+ selector: 'int1',
1076
+ summaryType: 'sum',
1077
+ },
1078
+ ],
1079
+ requireGroupCount: true,
1080
+ group: [
1081
+ {
1082
+ selector: 'int1',
1083
+ desc: false,
1084
+ isExpanded: false,
1085
+ },
1086
+ ],
1087
+ groupSummary: [
1088
+ {
1089
+ selector: 'date1',
1090
+ summaryType: 'min',
1091
+ },
1092
+ {
1093
+ selector: 'int1',
1094
+ summaryType: 'avg',
1095
+ },
1096
+ {
1097
+ selector: 'int1',
1098
+ summaryType: 'sum',
1099
+ },
1100
+ {
1101
+ summaryType: 'count',
1102
+ },
1103
+ ],
1104
+ },
1105
+ processingOptions: {
1106
+ timezoneOffset: -60,
1107
+ summaryQueryLimit: 500,
1108
+ },
1109
+ }
1110
+ );
1111
+ });
1112
+ });