UNPKG

32 kBJavaScriptView Raw
1/**
2 * Created by Andy Likuski on 2017.02.26
3 * Copyright (c) 2017 Andy Likuski
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 *
7 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 *
9 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 */
11
12import * as R from 'ramda';
13import * as f from './functions';
14import {Just} from 'folktale/maybe';
15import * as Result from 'folktale/result';
16
17import {of} from 'folktale/concurrency/task';
18import {fromPairsDeep} from './functions';
19import {replaceValuesAtDepth} from './functions';
20import {replaceValuesAtDepthAndStringify} from './functions';
21import {replaceValuesWithCountAtDepth} from './functions';
22import {mapObjToValues} from './functions';
23import {chainObjToValues} from './functions';
24import {flattenObj} from './functions';
25import {unflattenObj} from './functions';
26import {filterObjToValues} from './functions';
27import {overDeep} from './functions';
28import {keyStringToLensPath} from './functions';
29import {mapKeysAndValues} from './functions';
30import {omitDeep} from './functions';
31import {mergeDeepWithRecurseArrayItems} from './functions';
32import {omitDeepPaths} from './functions';
33import {pickDeepPaths} from './functions';
34import {splitAtInclusive} from './functions';
35import {reqStrPathThrowing} from './throwingFunctions';
36import {omitDeepBy} from './functions';
37import {eqPropsAll} from './functions';
38import {eqStrPath} from './functions';
39import {eqStrPathsAll} from './functions';
40import {toArrayIfNot} from './functions';
41import {unflattenObjNoArrays} from './functions';
42import {flattenObjUntil} from './functions';
43import {findMapped} from './functions';
44import {duplicateKey} from './functions';
45import {renameKey} from './functions';
46import {isObject} from './functions';
47
48describe('helperFunctions', () => {
49 test('Should be empty', () => {
50 expect(f.orEmpty(null)).toEqual('');
51 });
52
53 test('Should filter out null and undef values', () => {
54 expect(f.compact([1, null, 2])).toEqual([1, 2]);
55 });
56
57 test('Should filter out null and empty values', () => {
58 expect(f.compactEmpty(['', null, []])).toEqual([]);
59 });
60
61 test('emptyToNull', () => {
62 expect(f.emptyToNull('')).toEqual(null);
63 });
64
65 test('compactJoin should compact and join', () => {
66 expect(f.compactJoin('-', ['', 'a', null, 'b'])).toEqual('a-b');
67 expect(f.compactJoin('-', ['', null])).toEqual(null);
68 });
69
70 test('Should map bars', () => {
71 expect(f.mapProp('bar')([{bar: 1}, {bar: 2}])).toEqual([1, 2]);
72 });
73
74 test('Should map prop as key', () => {
75 expect(f.mapPropValueAsIndex('bar')([{bar: 1}, {bar: 2}])).toEqual(R.indexBy(R.prop('bar'), [{bar: 1}, {bar: 2}]));
76 });
77
78 test('Should remove duplicate objects with same prop key', () => {
79 expect(f.removeDuplicateObjectsByProp('bar')([{bar: 1, foo: 2}, {bar: 1, foo: 2}, {bar: 2}])).toEqual([{
80 bar: 1,
81 foo: 2
82 }, {bar: 2}]);
83 });
84
85 test('Should return an id from an object or the identify from a value', () => {
86 expect(f.idOrIdFromObj('foo')).toEqual('foo');
87 expect(f.idOrIdFromObj({id: 'foo'})).toEqual('foo');
88 });
89
90 test('Should deep merge objects', () => {
91 expect(f.mergeDeep(
92 {foo: 1, bar: {bizz: [2, 3], buzz: 7}},
93 {foo: 4, bar: {bizz: [5, 6]}}
94 )).toEqual({foo: 4, bar: {bizz: [5, 6], buzz: 7}});
95 });
96
97 test('Should deep merge objects with a function', () => {
98 expect(f.mergeDeepWith(
99 (l, r) => R.when(
100 R.is(Number),
101 R.add(l)
102 )(r),
103 {foo: 1, bar: {bizz: [2, 3], buzz: 7}},
104 {foo: 4, bar: {bizz: [5, 6]}}
105 )).toEqual({foo: 5, bar: {bizz: [5, 6], buzz: 7}});
106 });
107
108 test('Should deep merge objects and concat arrays of matching keys', () => {
109 expect(f.mergeDeepWithConcatArrays(
110 {foo: 1, bar: {bizz: [2, 3], buzz: 7}},
111 {foo: 4, bar: {bizz: [5, 6]}}
112 )).toEqual({foo: 4, bar: {bizz: [2, 3, 5, 6], buzz: 7}});
113 });
114
115 test('mergeDeepWithRecurseArrayItems', () => {
116 expect(f.mergeDeepWithRecurseArrayItems(
117 (l, r) => R.when(
118 R.is(Number),
119 R.add(l)
120 )(r),
121 {foo: 1, bar: {bizz: [2, {brewer: 9}], buzz: 7}},
122 {foo: 4, bar: {bizz: [5, {brewer: 10}]}}
123 )).toEqual({foo: 5, bar: {bizz: [7, {brewer: 19}], buzz: 7}});
124 });
125
126
127 test('mergeDeepWithRecurseArrayItemsByRight', () => {
128 expect(f.mergeDeepWithRecurseArrayItemsByRight(
129 (v, k) => R.when(isObject, R.propOr(v, 'id'))(v),
130 {foo: 1, bar: {bizz: [{buddy: 2, id: 2, cow: 4}, {brewer: 9}], buzz: 7}},
131 {foo: 4, bar: {bizz: [5, {buddy: 10, id: 2, snippy: 1}]}}
132 )).toEqual({foo: 4, bar: {bizz: [5, {buddy: 10, id: 2, cow: 4, snippy: 1}], buzz: 7}});
133 });
134
135 test('mergeDeepWithRecurseArrayItemsByAndMergeObjectByRight', () => {
136 const itemMatchBy = (v, k) => R.when(isObject, R.propOr(v, 'id'))(v);
137 const renameSnippy = (left, right) => {
138 // Recurse on each item usual, but rename snippy to snappy
139 const rename = renameKey(R.lensPath([]), 'snippy', 'snappy');
140 return R.mergeWithKey(
141 (kk, ll, rr) => {
142 return f.mergeDeepWithRecurseArrayItemsByAndMergeObjectByRight(
143 itemMatchBy,
144 renameSnippy,
145 ll,
146 rr,
147 kk
148 );
149 },
150 rename(left),
151 rename(right)
152 );
153 };
154 expect(f.mergeDeepWithRecurseArrayItemsByAndMergeObjectByRight(
155 itemMatchBy,
156 renameSnippy,
157 {foo: 1, bar: {bizz: [{buddy: 2, id: 2, cow: 4}, {brewer: 9}], buzz: 7}},
158 {foo: 4, bar: {bizz: [5, {buddy: 10, id: 2, snippy: 1}]}}
159 )).toEqual({foo: 4, bar: {bizz: [5, {buddy: 10, id: 2, cow: 4, snappy: 1}], buzz: 7}});
160 });
161
162 test('mergeDeepWithRecurseArrayItemsByRightHandlNull', () => {
163 expect(f.mergeDeepWithRecurseArrayItemsByRight(
164 () => {
165 },
166 {fever: 1},
167 null
168 )).toEqual({fever: 1});
169
170 expect(f.mergeDeepWithRecurseArrayItemsByRight(
171 () => {
172 },
173 [1],
174 null
175 )).toEqual([1]);
176 });
177
178 test('mergeDeepWithRecurseArrayItemsAndMapObjs', () => {
179 expect(f.mergeDeepWithRecurseArrayItemsAndMapObjs(
180 (l, r) => R.when(
181 R.is(Number),
182 R.add(l)
183 )(r),
184 (key, obj) => R.merge({key: R.toUpper(key.toString())}, obj),
185 {foo: 1, bar: {bizz: [2, {brewer: 9}], buzz: 7}},
186 {foo: 4, bar: {bizz: [5, {brewer: 10}]}}
187 )).toEqual({foo: 5, bar: {key: 'BAR', bizz: [7, {brewer: 19, key: 'BIZZ'}], buzz: 7}});
188 });
189
190 test('Should merge deep all objects', () => {
191 expect(f.mergeDeepAll([
192 {foo: 1, bar: {bizz: [2, 3], buzz: 7}},
193 {foo: 4, bar: {bizz: [5, 6]}},
194 {foo: 4, bar: {cat: [5, 6], pterodactyl: 'time is running out!'}}
195 ])).toEqual(
196 {foo: 4, bar: {bizz: [5, 6], buzz: 7, cat: [5, 6], pterodactyl: 'time is running out!'}}
197 );
198 });
199
200 test('Should capitalize first letter', () => {
201 expect(f.capitalize('good grief')).toEqual('Good grief');
202 });
203
204 test('Required path', () => {
205 expect(f.reqPath(['a'], {a: 1}).value).toBe(1);
206 expect(f.reqPath(['a', 'b'], {a: {c: 1}}).value).toEqual({
207 resolved: ['a'],
208 path: ['a', 'b']
209 });
210 });
211
212 test('reqStrPath', () => {
213 expect(f.reqStrPath('foo.bar.goo', {
214 foo: {
215 bar: {
216 goo: 1
217 }
218 }
219 })).toEqual(Result.Ok(1));
220
221 expect(f.reqStrPath('foo.bar.goo', {
222 foo: {
223 car: {
224 goo: 1
225 }
226 }
227 })).toEqual(Result.Error(
228 {
229 resolved: ['foo'],
230 path: ['foo', 'bar', 'goo']
231 })
232 );
233 });
234
235 test('strPath', () => {
236 expect(f.strPath('foo.bar.goo', {
237 foo: {
238 bar: {
239 goo: 1
240 }
241 }
242 })).toEqual(1);
243
244 expect(typeof f.strPath('foo.bar.goo', {
245 foo: {
246 car: {
247 goo: 1
248 }
249 }
250 })).toEqual('undefined');
251 });
252
253 test('strPathOr', () => {
254 expect(f.strPathOr(1, 'tan.khaki.pants', {tan: {khaki: {pants: false}}})).toEqual(false);
255 expect(f.strPathOr(1, 'tan.khaki.blazer', {tan: {khaki: {pants: false}}})).toEqual(1);
256 });
257
258 test('strPathOrNullOk', () => {
259 expect(f.strPathOrNullOk(1, 'tan.khaki.pants', {tan: {khaki: {pants: null}}})).toEqual(null);
260 expect(f.strPathOrNullOk(1, 'tan.khaki.blazer', {tan: {khaki: {pants: false}}})).toEqual(1);
261 expect(f.strPathOrNullOk(1, 'tan.khaki.blazer', {tan: {khaki: 'cabbage'}})).toEqual(1);
262 });
263
264 test('hasStrPath', () => {
265 expect(f.hasStrPath('tan.khaki.pants', {tan: {khaki: {pants: false}}})).toEqual(true);
266 expect(f.hasStrPath('tan.khaki.blazer', {tan: {khaki: {pants: false}}})).toEqual(false);
267 });
268
269 test('Required path prop equals', () => {
270 expect(f.reqPathPropEq(['a'], 1, {a: 1}).value).toBe(true);
271 expect(f.reqPathPropEq(['a', 'b'], 1, {a: {c: 1}}).value).toEqual({
272 resolved: ['a'],
273 path: ['a', 'b']
274 });
275 });
276
277 test('Should merge all with key', () => {
278 expect(
279 f.mergeAllWithKey(
280 (k, l, r) => k === 'a' ? R.concat(l, r) : r,
281 [{a: [1], b: 2}, {a: [2], c: 3}, {a: [3]}]
282 )).toEqual({a: [1, 2, 3], b: 2, c: 3});
283 });
284
285 test('Should reqPath of object', () => {
286 expect(
287 f.reqPath(['a', 'b', 1, 'c'], {a: {b: [null, {c: 2}]}})
288 ).toEqual(Result.Ok(2));
289 });
290
291 test('mapKeys', () => {
292 expect(f.mapKeys(
293 key => `${f.capitalize(key)} Taco`,
294 {fish: 'good', puppy: 'bad'})
295 ).toEqual(
296 {['Fish Taco']: 'good', ['Puppy Taco']: 'bad'}
297 );
298 });
299
300 test('mapKeysForLens', () => {
301 expect(f.mapKeysForLens(
302 R.lensPath(['x', 1, 'y']),
303 key => `${f.capitalize(key)} Taco`,
304 {x: [null, {y: {fish: 'good', puppy: 'bad'}}]}
305 )
306 ).toEqual(
307 {x: [null, {y: {['Fish Taco']: 'good', ['Puppy Taco']: 'bad'}}]}
308 );
309 });
310
311 test('mapDefault should rename the default import', () => {
312 expect(f.mapDefault('friend', {default: 'foo', other: 'boo'})).toEqual(
313 {friend: 'foo', other: 'boo'}
314 );
315 });
316
317 test('mapDefaultAndPrefixOthers should rename the default and prefix others', () => {
318 expect(f.mapDefaultAndPrefixOthers('friend', 'prefix', {default: 'foo', other: 'boo'})).toEqual(
319 {friend: 'foo', prefixOther: 'boo'}
320 );
321 });
322
323 test('transformKeys', () => {
324 expect(f.transformKeys(
325 f.camelCase,
326 {
327 who_made_me_with_slugs: 'the snail',
328 'what-kind-of-camel-Races': 'a dromedary'
329 }
330 )).toEqual(
331 {
332 whoMadeMeWithSlugs: 'the snail',
333 whatKindOfCamelRaces: 'a dromedary'
334 }
335 );
336 });
337
338 test('renameKey', () => {
339 expect(
340 f.renameKey(R.lensPath(['x', 'y']), 'z', 'and per se', {x: {y: {z: {cactus: 'blossoms'}}}})
341 ).toEqual(
342 {x: {y: {'and per se': {cactus: 'blossoms'}}}}
343 );
344 });
345
346 test('duplicateKey', () => {
347 expect(
348 f.duplicateKey(R.lensPath(['x', 'y']), 'z', ['and per se', 'a per se', 'o per se'], {x: {y: {z: {cactus: 'blossoms'}}}})
349 ).toEqual(
350 {
351 x: {
352 y: {
353 z: {cactus: 'blossoms'},
354 'and per se': {cactus: 'blossoms'},
355 'a per se': {cactus: 'blossoms'},
356 'o per se': {cactus: 'blossoms'}
357 }
358 }
359 }
360 );
361 expect(
362 f.duplicateKey(R.lensPath(['x', 'y']), 'z', 'and per se', {x: {y: {z: {cactus: 'blossoms'}}}})
363 ).toEqual(
364 {
365 x: {
366 y: {
367 z: {cactus: 'blossoms'},
368 'and per se': {cactus: 'blossoms'}
369 }
370 }
371 }
372 );
373 });
374
375 test('moveToKeys', () => {
376 expect(
377 f.moveToKeys(R.lensPath(['x', 'y']), 'z', ['and per se', 'a per se', 'o per se'], {x: {y: {z: {cactus: 'blossoms'}}}})
378 ).toEqual(
379 {
380 x: {
381 y: {
382 'and per se': {cactus: 'blossoms'},
383 'a per se': {cactus: 'blossoms'},
384 'o per se': {cactus: 'blossoms'}
385 }
386 }
387 }
388 );
389 });
390
391 test('findOne', () => {
392 // Works with objects
393 expect(
394 f.findOne(R.equals('Eli Whitney'), {a: 1, b: 'Eli Whitney'})
395 ).toEqual(
396 Result.Ok({b: 'Eli Whitney'})
397 );
398
399 // Works with arrays
400 expect(
401 f.findOne(R.equals('Eli Whitney'), [1, 'Eli Whitney'])
402 ).toEqual(
403 Result.Ok(['Eli Whitney'])
404 );
405
406 // None
407 expect(
408 f.findOne(R.equals('Eli Whitney'), {a: 1, b: 2})
409 ).toEqual(
410 Result.Error({all: {a: 1, b: 2}, matching: {}})
411 );
412
413 // Too many
414 expect(
415 f.findOne(R.equals('Eli Whitney'), {a: 'Eli Whitney', b: 'Eli Whitney'})
416 ).toEqual(
417 Result.Error({all: {a: 'Eli Whitney', b: 'Eli Whitney'}, matching: {a: 'Eli Whitney', b: 'Eli Whitney'}})
418 );
419 });
420
421 test('onlyOne', () => {
422 expect(f.onlyOne({a: 'Eli Whitney'})).toEqual(Result.Ok({a: 'Eli Whitney'}));
423
424 // None
425 expect(
426 f.onlyOne({})
427 ).toEqual(
428 Result.Error({all: {}, matching: {}})
429 );
430
431 // Too many
432 expect(
433 f.onlyOne({a: 'Eli Whitney', b: 'Eli Whitney'})
434 ).toEqual(
435 Result.Error({all: {a: 'Eli Whitney', b: 'Eli Whitney'}, matching: {a: 'Eli Whitney', b: 'Eli Whitney'}})
436 );
437 });
438
439 test('onlyOneValue', () => {
440 expect(f.onlyOneValue({a: 'Eli Whitney'})).toEqual(Result.Ok('Eli Whitney'));
441
442 // None
443 expect(
444 f.onlyOneValue({})
445 ).toEqual(
446 Result.Error({all: {}, matching: {}})
447 );
448
449 // Too many
450 expect(
451 f.onlyOneValue({a: 'Eli Whitney', b: 'Eli Whitney'})
452 ).toEqual(
453 Result.Error({all: {a: 'Eli Whitney', b: 'Eli Whitney'}, matching: {a: 'Eli Whitney', b: 'Eli Whitney'}})
454 );
455 });
456
457 test('mapToObjValue', () => {
458 expect(f.mapToObjValue(R.compose(f.camelCase, R.toLower), ['MY', 'SHOES_FIT'])).toEqual({
459 MY: 'my',
460 SHOES_FIT: 'shoesFit'
461 });
462 });
463
464 test('findOneValueByParams', () => {
465 const items = [
466 {brand: 'crush', flavor: 'grape'},
467 {brand: 'fanta', flavor: 'strawberry'},
468 {brand: 'crush', flavor: 'orange'}
469 ];
470 const params = {brand: 'crush', flavor: 'orange'};
471 expect(f.findOneValueByParams(params, items)).toEqual(
472 Result.Ok({brand: 'crush', flavor: 'orange'})
473 );
474 const badParams = {brand: 'crush', flavor: 'pretzel'};
475 expect(f.findOneValueByParams(badParams, items)).toEqual(
476 Result.Error(
477 {
478 all: [{brand: 'crush', flavor: 'grape'}, {
479 brand: 'fanta',
480 flavor: 'strawberry'
481 }, {brand: 'crush', flavor: 'orange'}], matching: []
482 }
483 )
484 );
485 const tooGoodParams = {brand: 'crush'};
486 expect(f.findOneValueByParams(tooGoodParams, items)).toEqual(
487 Result.Error(
488 {
489 all: [{brand: 'crush', flavor: 'grape'}, {
490 brand: 'fanta',
491 flavor: 'strawberry'
492 }, {brand: 'crush', flavor: 'orange'}],
493 matching: [{brand: 'crush', flavor: 'grape'}, {brand: 'crush', flavor: 'orange'}]
494 }
495 )
496 );
497 });
498
499 test('findByParams', () => {
500 const items = [
501 {brand: 'crush', flavor: 'grape'},
502 {brand: 'fanta', flavor: 'strawberry'},
503 {brand: 'crush', flavor: 'orange'}
504 ];
505 const params = {brand: 'crush', flavor: 'orange'};
506 expect(f.findByParams(params, items)).toEqual(
507 [{brand: 'crush', flavor: 'orange'}]
508 );
509 const badParams = {brand: 'crush', flavor: 'pretzel'};
510 expect(f.findByParams(badParams, items)).toEqual(
511 []
512 );
513 const tooGoodParams = {brand: 'crush'};
514 expect(f.findByParams(tooGoodParams, items)).toEqual(
515 [
516 {brand: 'crush', flavor: 'grape'},
517 {brand: 'crush', flavor: 'orange'}
518 ]
519 );
520 // With objs
521 const objs = {a: {foo: 1}, b: {foo: 2}};
522 expect(f.findByParams({foo: 2}, objs)).toEqual({b: {foo: 2}});
523 });
524
525 test('findMapped', () => {
526 expect(findMapped(R.prop('fri'), [{a: 1}, {b: 2}, {fri: 0}, {fi: 'willy'}, {fra: 4}])).toEqual(0);
527 });
528
529 test('alwaysFunc', () => {
530 const alwaysIWannaFuncWithYou = R.identity;
531 const str = 'and make believe with you';
532 expect(f.alwaysFunc(alwaysIWannaFuncWithYou)('and live in harmony')).toEqual('and live in harmony');
533 expect(f.alwaysFunc(str)(1, 1, 'was', 'a racehorse')).toEqual(str);
534 });
535
536 test('mapKeysAndValues', () => {
537 const obj = {neun: 'und_neinzig', luft: 'balons'};
538 expect(f.mapKeysAndValues((v, k) => [f.capitalize(k), f.camelCase(v)], obj)).toEqual(
539 {Neun: 'undNeinzig', Luft: 'balons'}
540 );
541 });
542
543 test('fromPairsDeep', () => {
544 // Outer element is non-pair array
545 const deepPairs = [
546 'Here',
547 'come',
548 'some',
549 'pairs:',
550 // This element is an array of pairs
551 [
552 ['I', [
553 ['A', [
554 ['i', [
555 // Some deep non-pair arrays
556 ['a', [1, 1, 'was', 'a', 'racehorse']],
557 ['b', [2, 2, 'was', 1, 2]],
558 ['c', {'I\'m': 'already an object!'}]
559 ]],
560 ['ii', [
561 ['d', 4],
562 ['e', 5],
563 ['f', 6]
564 ]]
565 ]]
566 ]]
567 ]
568 ];
569 expect(fromPairsDeep(deepPairs)).toEqual(
570 [
571 'Here',
572 'come',
573 'some',
574 'pairs:',
575 {
576 I: {
577 A: {
578 i: {
579 a: [1, 1, 'was', 'a', 'racehorse'],
580 b: [2, 2, 'was', 1, 2],
581 c: {'I\'m': 'already an object!'}
582 },
583 ii: {
584 d: 4,
585 e: 5,
586 f: 6
587 }
588 }
589 }
590 }
591 ]
592 );
593 /*
594 TODO does not work
595 expect(fromPairsDeep([[
596 "Kenya__Nairobi",
597 [
598 "2231585",
599 {
600 x: { o: 1 },
601 "location": {
602 "id": 2231585
603 }
604 }
605 ]
606 ])).toEqual(1)
607 */
608 });
609
610 test('replaceValuesAtDepth', () => {
611 // Test various depths
612 expect(
613 replaceValuesAtDepth(3, '...', {a: {A: {å: 1}, kitty: 2}})
614 ).toEqual({a: {A: {å: '...'}, kitty: 2}});
615 expect(
616 replaceValuesAtDepth(2, '...', {a: {A: {å: 1}}, kitty: 2})
617 ).toEqual({a: {A: '...'}, kitty: 2});
618 expect(
619 replaceValuesAtDepth(1, '...', {a: {A: {å: 1}}})
620 ).toEqual({a: '...'});
621 expect(
622 replaceValuesAtDepth(0, '...', {a: {A: {å: 1}}})
623 ).toEqual('...');
624
625 // Test arrays
626 expect(replaceValuesAtDepth(2, '...', [['A', ['a']], 'b'])).toEqual([['...', '...'], 'b']);
627
628 // Test replacement function that takes length of objects and leaves primitives alone
629 expect(
630 replaceValuesAtDepth(3, R.when(isObject, R.compose(R.length, R.keys)), {
631 a: {
632 A: {
633 å: [1, 2, 3],
634 moo: {cow: 'yes', sheep: 'no'},
635 ø: 'æ'
636 }, kitty: 2
637 }
638 })
639 ).toEqual({a: {A: {å: 3, moo: 2, ø: 'æ'}, kitty: 2}});
640 });
641
642 test('replaceValuesWithCountAtDepth', () => {
643 expect(
644 replaceValuesWithCountAtDepth(3, {a: {A: {å: [1, 2, 3], moo: {cow: 'yes', sheep: 'no'}, ø: 'æ'}, kitty: 2}})
645 ).toEqual({a: {A: {å: '[...3]', moo: '{...2}', ø: 'æ'}, kitty: 2}});
646 });
647
648 test('replaceValuesAtDepthAndStringify', () => {
649 expect(
650 replaceValuesAtDepthAndStringify(3, '...', {a: {A: {å: 1}}})
651 ).toEqual('{"a":{"A":{"å":"..."}}}');
652 });
653
654 test('mapObjToValues', () => {
655 // Make sure values aren't flattened
656 expect(mapObjToValues(R.ap([R.add(1)]), {a: [1], b: [2, 3]})).toEqual([[2], [3, 4]]);
657 });
658
659 test('filterObjToValues', () => {
660 // Make sure values aren't flattened
661 expect(filterObjToValues((v, k) => k !== 'toto', {dorothy: 1, toto: 2})).toEqual([1]);
662 });
663
664 test('chainObjToValues', () => {
665 // Make sure values aren't flattened more than one level
666 expect(chainObjToValues(R.ap([R.concat([1])]), {
667 a: [[1]],
668 b: [[2, 3], [4, 5]]
669 })).toEqual([[1, 1], [1, 2, 3], [1, 4, 5]]);
670 });
671
672 test('flattenObj', () => {
673 expect(flattenObj({a: 1})).toEqual({a: 1});
674 expect(flattenObj({a: 1, b: {johnny: 'b good'}})).toEqual({a: 1, 'b.johnny': 'b good'});
675 expect(flattenObj(
676 {a: 1, b: {johnny: 'b good', sam: [1, 2, 3]}}
677 )).toEqual(
678 {a: 1, 'b.johnny': 'b good', 'b.sam.0': 1, 'b.sam.1': 2, 'b.sam.2': 3}
679 );
680 expect(flattenObj([1, 2, 3])).toEqual({0: 1, 1: 2, 2: 3});
681 });
682
683 test('flattenObjUntil', () => {
684 expect(flattenObjUntil(R.propOr(false, 'cow'),
685 {a: 1, b: {johnny: 'b good', c: {cow: {pie: true}}, sam: [1, 2, 3]}}
686 )).toEqual(
687 {a: 1, 'b.johnny': 'b good', 'b.c': {cow: {pie: true}}, 'b.sam.0': 1, 'b.sam.1': 2, 'b.sam.2': 3}
688 );
689
690 expect(flattenObjUntil(Array.isArray,
691 {a: 1, b: {johnny: 'b good', sam: [1, 2, 3]}}
692 )).toEqual(
693 {a: 1, 'b.johnny': 'b good', 'b.sam': [1, 2, 3]}
694 );
695 });
696
697 test('unflattenObj', () => {
698 const pancake = R.compose(unflattenObj, flattenObj);
699 const x = [
700 {
701 id: '2226274',
702 country: 'Norway'
703 }
704 ];
705 expect(pancake(x)).toEqual(x);
706
707 expect(pancake({a: 1})).toEqual({a: 1});
708 expect(pancake({a: 1, b: {johnny: 'b good'}})).toEqual({a: 1, b: {johnny: 'b good'}});
709 expect(pancake({a: 1, b: {johnny: 'b good', sam: [1, 2, 3]}})).toEqual(
710 {a: 1, b: {johnny: 'b good', sam: [1, 2, 3]}}
711 );
712 });
713
714 test('unflattenObjNoArrays', () => {
715 const pancake = R.compose(unflattenObjNoArrays, flattenObj);
716 const x = [
717 {
718 id: '2226274',
719 country: 'Norway'
720 }
721 ];
722 expect(pancake(x)).toEqual({0: x[0]});
723
724 expect(pancake({a: 1})).toEqual({a: 1});
725 expect(pancake({a: 1, b: {johnny: 'b good'}})).toEqual({a: 1, b: {johnny: 'b good'}});
726 expect(pancake({a: 1, b: {johnny: 'b good', sam: [1, 2, 3]}})).toEqual(
727 {a: 1, b: {johnny: 'b good', sam: {0: 1, 1: 2, 2: 3}}}
728 );
729 });
730
731
732 test('overDeep', () => {
733 const res = overDeep(
734 (k, v) => R.merge({butter: `${R.toUpper(k.toString())} Butter`})(v),
735 {
736 peanut: {
737 almond: {
738 cashew: {
739 brazilNut: {}
740 },
741 filbert: [
742 {
743 walnut: {},
744 pecan: {}
745 }
746 ]
747 }
748 }
749 }
750 );
751 expect(res.peanut.almond.cashew.brazilNut).toEqual({butter: 'BRAZILNUT Butter'});
752 expect(res.peanut.almond.butter).toEqual('ALMOND Butter');
753 });
754
755 test('omitDeepBy', () => {
756 const whatTheFunc = () => {
757 return 'what the func';
758 };
759 const res = omitDeepBy(
760 (k, v) => R.startsWith('_')(k),
761 {
762 peanut: {
763 almond: {
764 cashew: {
765 _brazilNut: {},
766 pecan: [
767 {are: {the: {_best: true}}}
768 ]
769 },
770 _filbert: [
771 {
772 walnut: {}
773 }
774 ]
775 }
776 },
777 funkyNut: {
778 wingnut: 'yes',
779 cornnut: whatTheFunc
780 }
781 }
782 );
783 expect(res).toEqual({
784 peanut: {
785 almond: {
786 cashew: {
787 pecan: [
788 {are: {the: {}}}
789 ]
790 }
791 }
792 },
793 funkyNut: {
794 wingnut: 'yes',
795 cornnut: whatTheFunc
796 }
797 });
798 const res2 = omitDeepBy(
799 (k, v) => R.startsWith('_')(k),
800 {
801 _peanut: {
802 almond: {
803 cashew: {
804 _brazilNut: {}
805 },
806 _filbert: [
807 {
808 walnut: {},
809 pecan: {}
810 }
811 ]
812 }
813 }
814 }
815 );
816 expect(res2).toEqual({});
817 });
818
819 test('omitDeepByNullsStayNull', () => {
820 const obj = {
821 viewport: {
822 latitude: null,
823 longitude: null,
824 zoom: 1
825 }
826 };
827 expect(omitDeepBy(R.startsWith('_'), obj)).toEqual(
828 obj
829 );
830 });
831
832 test('keyStringToLensPath', () => {
833 expect(keyStringToLensPath('foo.bar.0.wopper')).toEqual(['foo', 'bar', 0, 'wopper']);
834 });
835
836 test('omitDeep', () => {
837 /*
838 expect(omitDeep(
839 ['foo'],
840 {foo: {bunny: 1}, boo: {funny: {foo: {sunny: 1}, soo: 3}}}
841 )).toEqual({
842 boo: {
843 funny: {soo: 3}
844 }
845 });
846 */
847
848 const tricky = {
849 urlObjSpots: [],
850 location: {},
851 routeResponses: [
852 {
853 status: 200,
854 headers: {
855 'content-type': 'application/json; charset=UTF-8',
856 date: 'Tue, 28 May 2019 13:22:04 GMT',
857 expires: 'Wed, 29 May 2019 13:22:04 GMT',
858 'cache-control': 'public, max-age=86400',
859 server: 'mafe',
860 'x-xss-protection': '0',
861 'x-frame-options': 'SAMEORIGIN',
862 'server-timing': 'gfet4t7; dur=57',
863 'alt-svc': 'quic=":443"; ma=2592000; v="46,44,43,39"',
864 'accept-ranges': 'none',
865 vary: 'Accept-Language,Accept-Encoding',
866 connection: 'close'
867 }
868 },
869 {
870 status: 200,
871 headers: {
872 'content-type': 'application/json; charset=UTF-8',
873 date: 'Tue, 28 May 2019 13:22:04 GMT',
874 expires: 'Wed, 29 May 2019 13:22:04 GMT',
875 'cache-control': 'public, max-age=86400',
876 server: 'mafe',
877 'x-xss-protection': '0',
878 'x-frame-options': 'SAMEORIGIN',
879 'server-timing': 'gfet4t7; dur=74',
880 'alt-svc': 'quic=":443"; ma=2592000; v="46,44,43,39"',
881 'accept-ranges': 'none',
882 vary: 'Accept-Language,Accept-Encoding',
883 connection: 'close'
884 }
885 }
886 ]
887 };
888 expect(omitDeep(
889 ['date', 'expires'],
890 tricky
891 )).toEqual(
892 {
893 urlObjSpots: [],
894 location: {},
895 routeResponses: [
896 {
897 status: 200,
898 headers: {
899 'content-type': 'application/json; charset=UTF-8',
900 'cache-control': 'public, max-age=86400',
901 server: 'mafe',
902 'x-xss-protection': '0',
903 'x-frame-options': 'SAMEORIGIN',
904 'server-timing': 'gfet4t7; dur=57',
905 'alt-svc': 'quic=":443"; ma=2592000; v="46,44,43,39"',
906 'accept-ranges': 'none',
907 vary: 'Accept-Language,Accept-Encoding',
908 connection: 'close'
909 }
910 },
911 {
912 status: 200,
913 headers: {
914 'content-type': 'application/json; charset=UTF-8',
915 'cache-control': 'public, max-age=86400',
916 server: 'mafe',
917 'x-xss-protection': '0',
918 'x-frame-options': 'SAMEORIGIN',
919 'server-timing': 'gfet4t7; dur=74',
920 'alt-svc': 'quic=":443"; ma=2592000; v="46,44,43,39"',
921 'accept-ranges': 'none',
922 vary: 'Accept-Language,Accept-Encoding',
923 connection: 'close'
924 }
925 }
926 ]
927 }
928 );
929 });
930
931 test('omitDeepPaths', () => {
932 expect(omitDeepPaths(
933 ['foo.bunny', 'boo.funny.foo.sunny.2', 'boo.funny.foo.sunny.1.4.go'],
934 {
935 foo: {
936 bunny: {
937 humorous: 'stuff'
938 }
939 },
940 boo: {
941 funny: {
942 foo: {
943 sunny: [
944 8,
945 [10,
946 'more',
947 'miles',
948 'to',
949 {
950 go: 'teo',
951 wo: 1
952 }
953 ], 9
954 ]
955 },
956 soo: 3
957 }
958 }
959 }
960 )).toEqual(
961 {
962 foo: {},
963 boo: {
964 funny: {
965 foo: {
966 sunny: [
967 8, [
968 10,
969 'more',
970 'miles',
971 'to', {
972 wo: 1
973 }
974 ]
975 ]
976 },
977 soo: 3
978 }
979 }
980 }
981 );
982 });
983
984 test('omitDeepBug', () => {
985 const buggy = {
986 geojson: {
987 type: 'FeatureCollection',
988 features: [
989 {
990 type: 'Feature',
991 id: 'node/248124950',
992 geometry: {
993 type: 'Point',
994 coordinates: [
995 -115.0646702,
996 49.5161346
997 ]
998 },
999 properties: {
1000 id: 248124950,
1001 meta: {},
1002 tags: {},
1003 type: 'node',
1004 relations: []
1005 },
1006 __typename: {}
1007 }
1008 ]
1009 }
1010 };
1011 expect(R.propOr('great', '__typename', reqStrPathThrowing('geojson.features.0', omitDeep(['__typename'], buggy)))).toEqual('great');
1012 });
1013
1014 test('pickDeepPaths', () => {
1015 expect(pickDeepPaths(
1016 ['foo.bunny',
1017 'boo.funny.foo.sunny.2',
1018 'boo.funny.foo.sunny.1.4.go',
1019 'coo.moo.*.cow./[1|6]/.*.forever'
1020 ],
1021 {
1022 foo: {bunny: {humorous: 'stuff'}},
1023 boo: {funny: {foo: {sunny: [8, [10, 'more', 'miles', 'to', {go: 'teo', wo: 1}], 9]}, soo: 3}},
1024 coo: {
1025 moo: [
1026 1,
1027 2,
1028 {
1029 cow: [
1030 'come',
1031 {me: {forever: 'hers'}, never: 'mine'},
1032 'baby',
1033 'say',
1034 {me: {forever: 'his'}, never: 'mine'},
1035 'love',
1036 {me: {forever: 'yours'}, never: 'mine'}
1037 ]
1038 },
1039 4
1040 ]
1041 }
1042 }
1043 )).toEqual(
1044 {
1045 foo:
1046 {
1047 bunny: {humorous: 'stuff'}
1048 },
1049 boo: {
1050 funny: {
1051 foo: {
1052 sunny: [[{go: 'teo'}], 9]
1053 }
1054 }
1055 },
1056 coo: {
1057 moo: [{cow: [{me: {forever: 'hers'}}, {me: {forever: 'yours'}}]}]
1058 }
1059 }
1060 );
1061 });
1062
1063 test('camelCase', () => {
1064 expect(f.camelCase('Tough_Mudder_Hubbard')).toEqual('toughMudderHubbard');
1065 });
1066
1067 test('splitAtInclusive', () => {
1068 expect(splitAtInclusive(1, [1, 2, 3, 4, 5])).toEqual(
1069 [[1, 2], [2, 3, 4, 5]]
1070 );
1071 expect(splitAtInclusive(0, [1, 2, 3, 4, 5])).toEqual(
1072 [[1], [1, 2, 3, 4, 5]]
1073 );
1074 expect(splitAtInclusive(-1, [1, 2, 3, 4, 5])).toEqual(
1075 [[1, 2, 3, 4, 5], [5]]
1076 );
1077 expect(splitAtInclusive(1, 'murderforajarofredrum')).toEqual(
1078 ['mu', 'urderforajarofredrum']
1079 );
1080 });
1081
1082 test('eqStrPath', () => {
1083 expect(eqStrPath('bubble.gum.0.soup',
1084 {bubble: {gum: [{soup: 'banana'}]}},
1085 {bubble: {gum: [{soup: 'banana'}]}}
1086 )).toEqual(true);
1087 expect(eqStrPath('bubble.gum.soup',
1088 {bubble: {gum: {soup: 'banana'}}},
1089 {bubble: {gum: {soup: 'goat'}}}
1090 )).toEqual(false);
1091 });
1092
1093 test('eqStrPathsAll', () => {
1094 expect(eqStrPathsAll(['a', 'b'], {a: 1, b: 2, c: 3}, {a: 1, b: 2, c: 'hubabalu'})).toEqual(true);
1095 expect(eqStrPathsAll(['a', 'b', 'c'], {a: 1, b: 2, c: 3}, {a: 1, b: 2, c: 'hubabalu'})).toEqual(false);
1096 expect(eqStrPathsAll(['a.goat', 'b'], {a: {goat: 1}, b: 2, c: 3}, {
1097 a: {goat: 1},
1098 b: 2,
1099 c: 'hubabalu'
1100 })).toEqual(true);
1101 expect(eqStrPathsAll(['a', 'b.pumpkin', 'c'], {a: 1, b: {pumpkin: null}, c: 3}, {
1102 a: 1,
1103 b: {pumpkin: null},
1104 c: 'hubabalu'
1105 })).toEqual(false);
1106 });
1107
1108 test('toArrayIfNot', () => {
1109 const eh = ['eh'];
1110 expect(toArrayIfNot(eh)).toBe(eh);
1111 expect(toArrayIfNot('eh')).toEqual(eh);
1112 });
1113});