Skip to content

Commit 4cdf41a

Browse files
authored
Feature/add better test coverage (#5)
* fix: allow overwritting default comparison operators * add: test for custom comparison operators * add: unit test for exception of unhandled value type * add: unit test for exception of invalid comparison operator * fix: replace not useful code with exception * add: further tests added * add: enhance unit tests * add: more tests * change: version to 1.2.0
1 parent 4dfaad5 commit 4cdf41a

File tree

4 files changed

+239
-54
lines changed

4 files changed

+239
-54
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rsql-query-builder",
3-
"version": "1.1.2",
3+
"version": "1.2.0",
44
"description": "Library for building RSQL query strings.",
55
"main": "dist/index.js",
66
"module": "dist/index.mjs",

src/RSQLBuilder.test.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,67 @@ describe('RSQLBuilder', () => {
8989
});
9090

9191
it('Test group()', () => {
92-
expect(new RSQLBuilder().equal('name', 'John').and().group(new RSQLBuilder().equal('age', 30).or().equal('age', null)).toString()).toBe('name==\"John\";(age==30,age==null)');
92+
expect(
93+
new RSQLBuilder()
94+
.equal('name', 'John')
95+
.and()
96+
.group(new RSQLBuilder().equal('age', 30).or().equal('age', null))
97+
.toString()
98+
).toBe('name=="John";(age==30,age==null)');
99+
});
100+
101+
it('Test concat()', () => {
102+
expect(
103+
new RSQLBuilder()
104+
.equal('name', 'John')
105+
.concat(new RSQLBuilder().equal('age', 30).or().equal('age', null))
106+
.toString()
107+
).toBe('name=="John";age==30,age==null');
108+
});
109+
110+
it('Test merge()', () => {
111+
expect(
112+
new RSQLBuilder()
113+
.equal('name', 'John')
114+
.merge([new RSQLBuilder().equal('age', 30).or().equal('age', null)])
115+
.toString()
116+
).toBe('name=="John";(age==30,age==null)');
117+
118+
expect(
119+
new RSQLBuilder()
120+
.equal('name', 'John')
121+
.merge([new RSQLBuilder().equal('age', 30).or().equal('age', null)], { logicOperator: 'and' })
122+
.toString()
123+
).toBe('name=="John";(age==30,age==null)');
124+
125+
expect(
126+
new RSQLBuilder()
127+
.equal('name', 'John')
128+
.merge([new RSQLBuilder().equal('age', 30).or().equal('age', null)], { logicOperator: 'or' })
129+
.toString()
130+
).toBe('name=="John",(age==30,age==null)');
131+
});
132+
133+
it('Test static merge()', () => {
134+
expect(
135+
RSQLBuilder.merge([
136+
new RSQLBuilder().equal('name', 'John'),
137+
new RSQLBuilder().equal('age', 30).or().equal('age', null)
138+
]).toString()
139+
).toBe('(name=="John");(age==30,age==null)');
140+
141+
expect(
142+
RSQLBuilder.merge(
143+
[new RSQLBuilder().equal('name', 'John'), new RSQLBuilder().equal('age', 30).or().equal('age', null)],
144+
{ logicOperator: 'and' }
145+
).toString()
146+
).toBe('(name=="John");(age==30,age==null)');
147+
148+
expect(
149+
RSQLBuilder.merge(
150+
[new RSQLBuilder().equal('name', 'John'), new RSQLBuilder().equal('age', 30).or().equal('age', null)],
151+
{ logicOperator: 'or' }
152+
).toString()
153+
).toBe('(name=="John"),(age==30,age==null)');
93154
});
94155
});

src/RSQLBuilderBase.ts

Lines changed: 29 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ export interface RSQLBuilderOptions<TComparisonOperator extends string = never>
2727
*
2828
* @template TComparisonOperator - The type of the custom comparison operators
2929
*/
30-
interface RSQLBuilderBaseOptions<TComparisonOperator extends string = never> extends RSQLBuilderOptions<TComparisonOperator> {
30+
interface RSQLBuilderBaseOptions<TComparisonOperator extends string = never>
31+
extends RSQLBuilderOptions<TComparisonOperator> {
3132
/* Custom comparison operators */
32-
customComparisonOperators?: ComparisonOperators<TComparisonOperator>;
33+
customComparisonOperators?: Partial<ComparisonOperators<TComparisonOperator>>;
3334
}
3435

3536
/** RSQL builder base class
@@ -54,8 +55,9 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
5455

5556
private defaultLogicOperator: LogicOperator = 'and';
5657
private replaceExistingLogicOperator: boolean = true;
57-
private customComparisonOperators: ComparisonOperators<TCustomComparisonOperator> =
58-
{} as ComparisonOperators<TCustomComparisonOperator>;
58+
private customComparisonOperators: Partial<
59+
ComparisonOperators<ComparisonOperatorsDefault | TCustomComparisonOperator>
60+
> = {} as ComparisonOperators<ComparisonOperatorsDefault | TCustomComparisonOperator>;
5961

6062
/** Create a new RSQL builder base instance.
6163
*
@@ -65,7 +67,9 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
6567
*
6668
* @returns The builder instance
6769
* */
68-
protected constructor(options: RSQLBuilderBaseOptions<TCustomComparisonOperator> = {}) {
70+
protected constructor(
71+
options: RSQLBuilderBaseOptions<ComparisonOperatorsDefault | TCustomComparisonOperator> = {}
72+
) {
6973
if (options.defaultLogicOperator) this.defaultLogicOperator = options.defaultLogicOperator;
7074
if (options.customComparisonOperators) this.customComparisonOperators = options.customComparisonOperators;
7175
}
@@ -98,7 +102,7 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
98102
if (typeof value === 'number') return value.toString();
99103
if (typeof value === 'boolean') return value === true ? 'true' : 'false';
100104
if (value instanceof Date) return value.toISOString();
101-
throw new Error('Unhandled value type.');
105+
throw new Error('Unhandled value type');
102106
}
103107

104108
/** Ensure that the string ends with an logic operator.
@@ -158,12 +162,14 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
158162
this.customComparisonOperators[comparisonOperator as TCustomComparisonOperator] ||
159163
this.comparisonOperators[comparisonOperator as ComparisonOperatorsDefault];
160164

161-
if (!operator) throw new Error(`Invalid comparison operator '${operator}'.`);
165+
if (!operator) throw new Error(`Invalid comparison operator '${operator}'`);
162166

163167
if (operator.isArray === true) {
164-
const strArray = Array.isArray(value)
165-
? value.map((value) => this.valueToString(value))
166-
: [this.valueToString(value)];
168+
if (!Array.isArray(value)) {
169+
throw new Error(`Array comparison operator '${operator}' requires an array value.`);
170+
}
171+
172+
const strArray = value.map((value) => this.valueToString(value));
167173

168174
this.rsqlStr += selector + operator.rsql + '(' + strArray.join(',') + ')';
169175
} else {
@@ -221,9 +227,7 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
221227
*
222228
* @returns The builder instance
223229
*/
224-
public concat(
225-
builder: this
226-
): this {
230+
public concat(builder: this): this {
227231
if (!builder.isEmpty()) {
228232
this.ensureLogicOperator();
229233
this.rsqlStr += builder.toString();
@@ -241,12 +245,9 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
241245
*
242246
* @returns The builder instance
243247
*/
244-
public merge(
245-
builders: this[],
246-
options?: { operator?: LogicOperator }
247-
): this {
248+
public merge(builders: this[], options?: { logicOperator?: LogicOperator }): this {
248249
for (const builder of builders) {
249-
this.ensureLogicOperator(options?.operator);
250+
this.ensureLogicOperator(options?.logicOperator);
250251
this.group(builder);
251252
}
252253

@@ -290,10 +291,7 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
290291
*
291292
* @returns The builder instance
292293
*/
293-
public equal(
294-
selector: TSelector,
295-
value: string | number | boolean | Date | null
296-
): this {
294+
public equal(selector: TSelector, value: string | number | boolean | Date | null): this {
297295
return this.addComparison(selector, 'equal', value);
298296
}
299297

@@ -304,10 +302,7 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
304302
*
305303
* @returns The builder instance
306304
*/
307-
public notEqual(
308-
selector: TSelector,
309-
value: string | number | boolean | Date | null
310-
): this {
305+
public notEqual(selector: TSelector, value: string | number | boolean | Date | null): this {
311306
return this.addComparison(selector, 'notEqual', value);
312307
}
313308

@@ -318,10 +313,7 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
318313
*
319314
* @returns The builder instance
320315
*/
321-
public lessThan(
322-
selector: TSelector,
323-
value: string | number | Date | null
324-
): this {
316+
public lessThan(selector: TSelector, value: string | number | Date | null): this {
325317
return this.addComparison(selector, 'lessThan', value);
326318
}
327319

@@ -332,10 +324,7 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
332324
*
333325
* @returns The builder instance
334326
*/
335-
public lessThanOrEqual(
336-
selector: TSelector,
337-
value: string | number | Date | null
338-
): this {
327+
public lessThanOrEqual(selector: TSelector, value: string | number | Date | null): this {
339328
return this.addComparison(selector, 'lessThanOrEqual', value);
340329
}
341330

@@ -346,10 +335,7 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
346335
*
347336
* @returns The builder instance
348337
*/
349-
public greaterThan(
350-
selector: TSelector,
351-
value: string | number | Date | null
352-
): this {
338+
public greaterThan(selector: TSelector, value: string | number | Date | null): this {
353339
return this.addComparison(selector, 'greaterThan', value);
354340
}
355341

@@ -360,10 +346,7 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
360346
*
361347
* @returns The builder instance
362348
*/
363-
public greaterThanOrEqual(
364-
selector: TSelector,
365-
value: string | number | Date | null
366-
): this {
349+
public greaterThanOrEqual(selector: TSelector, value: string | number | Date | null): this {
367350
return this.addComparison(selector, 'greaterThanOrEqual', value);
368351
}
369352

@@ -373,10 +356,7 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
373356
* @param values - The values to compare
374357
* @returns The builder instance
375358
*/
376-
public in(
377-
selector: TSelector,
378-
values: Array<string | number | boolean | null>
379-
): this {
359+
public in(selector: TSelector, values: Array<string | number | boolean | null>): this {
380360
return this.addComparison(selector, 'in', values);
381361
}
382362

@@ -387,10 +367,7 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
387367
*
388368
* @returns The builder instance
389369
*/
390-
public notIn(
391-
selector: TSelector,
392-
values: Array<string | number | boolean | null>
393-
): this {
370+
public notIn(selector: TSelector, values: Array<string | number | boolean | null>): this {
394371
return this.addComparison(selector, 'notIn', values);
395372
}
396373

@@ -406,9 +383,9 @@ class RSQLBuilderBase<TSelector extends string, TCustomComparisonOperator extend
406383
*/
407384
static merge<TSelector extends string, TCustomComparisonOperator extends string>(
408385
builders: RSQLBuilderBase<TSelector, TCustomComparisonOperator>[],
409-
options: RSQLBuilderOptions<TCustomComparisonOperator> = {}
386+
options?: { logicOperator?: LogicOperator }
410387
): RSQLBuilderBase<TSelector, TCustomComparisonOperator> {
411-
return new RSQLBuilderBase<TSelector, TCustomComparisonOperator>(options).merge(
388+
return new RSQLBuilderBase<TSelector, TCustomComparisonOperator>({defaultLogicOperator: options?.logicOperator}).merge(
412389
builders.filter((b) => b !== undefined && !b.isEmpty())
413390
);
414391
}

0 commit comments

Comments
 (0)