feat: add package helpers

This commit is contained in:
Sam Tolmay 2020-10-05 17:13:42 +02:00
parent d17cbb0d87
commit 5ec50c4cc5
30 changed files with 1047 additions and 2 deletions

74
.pnp.js generated
View File

@ -50,6 +50,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"name": "@lowdefy/graphql",
"reference": "workspace:packages/graphql"
},
{
"name": "@lowdefy/helpers",
"reference": "workspace:packages/helpers"
},
{
"name": "@lowdefy/serializer",
"reference": "workspace:packages/serializer"
@ -67,6 +71,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@lowdefy/engine", ["workspace:packages/engine"]],
["@lowdefy/get", ["workspace:packages/get"]],
["@lowdefy/graphql", ["workspace:packages/graphql"]],
["@lowdefy/helpers", ["workspace:packages/helpers"]],
["@lowdefy/poc", ["workspace:."]],
["@lowdefy/poc-express", ["workspace:packages/express"]],
["@lowdefy/serializer", ["workspace:packages/serializer"]],
@ -4248,6 +4253,36 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "SOFT",
}]
]],
["@lowdefy/helpers", [
["workspace:packages/helpers", {
"packageLocation": "./packages/helpers/",
"packageDependencies": [
["@lowdefy/helpers", "workspace:packages/helpers"],
["@babel/cli", "virtual:10c70c57b8c041994c932fbc6065790a1801b95ea6f8bb47c7334c5a9a57ca5035df7a515298306323a03d95e4c0d72d9488454f45837690b0620ca3a249c7f5#npm:7.8.4"],
["@babel/compat-data", "npm:7.9.6"],
["@babel/core", "npm:7.9.6"],
["@babel/preset-env", "virtual:10c70c57b8c041994c932fbc6065790a1801b95ea6f8bb47c7334c5a9a57ca5035df7a515298306323a03d95e4c0d72d9488454f45837690b0620ca3a249c7f5#npm:7.9.6"],
["@lowdefy/delete", "workspace:packages/delete"],
["@lowdefy/serializer", "workspace:packages/serializer"],
["@lowdefy/type", "workspace:packages/type"],
["babel-jest", "virtual:10c70c57b8c041994c932fbc6065790a1801b95ea6f8bb47c7334c5a9a57ca5035df7a515298306323a03d95e4c0d72d9488454f45837690b0620ca3a249c7f5#npm:24.9.0"],
["eslint", "npm:6.8.0"],
["eslint-config-airbnb", "virtual:73f25cc0d3f57943fa9b1d737e4809af7a52a784e0ac5fed74b4e1e083308ab7ae2fd45a5424a8bc7ff7caab067690c9357630d657cbd636d6037acc1557fdc2#npm:18.2.0"],
["eslint-config-prettier", "virtual:73f25cc0d3f57943fa9b1d737e4809af7a52a784e0ac5fed74b4e1e083308ab7ae2fd45a5424a8bc7ff7caab067690c9357630d657cbd636d6037acc1557fdc2#npm:6.12.0"],
["eslint-plugin-import", "virtual:73f25cc0d3f57943fa9b1d737e4809af7a52a784e0ac5fed74b4e1e083308ab7ae2fd45a5424a8bc7ff7caab067690c9357630d657cbd636d6037acc1557fdc2#npm:2.22.1"],
["eslint-plugin-jsx-a11y", "virtual:73f25cc0d3f57943fa9b1d737e4809af7a52a784e0ac5fed74b4e1e083308ab7ae2fd45a5424a8bc7ff7caab067690c9357630d657cbd636d6037acc1557fdc2#npm:6.3.1"],
["eslint-plugin-prettier", "virtual:73f25cc0d3f57943fa9b1d737e4809af7a52a784e0ac5fed74b4e1e083308ab7ae2fd45a5424a8bc7ff7caab067690c9357630d657cbd636d6037acc1557fdc2#npm:3.1.4"],
["eslint-plugin-react", "virtual:73f25cc0d3f57943fa9b1d737e4809af7a52a784e0ac5fed74b4e1e083308ab7ae2fd45a5424a8bc7ff7caab067690c9357630d657cbd636d6037acc1557fdc2#npm:7.21.2"],
["eslint-plugin-react-hooks", "virtual:73f25cc0d3f57943fa9b1d737e4809af7a52a784e0ac5fed74b4e1e083308ab7ae2fd45a5424a8bc7ff7caab067690c9357630d657cbd636d6037acc1557fdc2#npm:4.1.2"],
["jest", "npm:24.9.0"],
["jest-diff", "npm:24.9.0"],
["lodash.merge", "npm:4.6.2"],
["prettier", "npm:2.1.2"],
["query-string", "npm:6.13.1"]
],
"linkType": "SOFT",
}]
]],
["@lowdefy/poc", [
["workspace:.", {
"packageLocation": "./",
@ -13990,6 +14025,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["lodash.merge", [
["npm:4.6.2", {
"packageLocation": "./.yarn/cache/lodash.merge-npm-4.6.2-77cb4416bf-4e2bb42a87.zip/node_modules/lodash.merge/",
"packageDependencies": [
["lodash.merge", "npm:4.6.2"]
],
"linkType": "HARD",
}]
]],
["lodash.set", [
["npm:4.3.2", {
"packageLocation": "./.yarn/cache/lodash.set-npm-4.3.2-7586c942c2-4dfedacae1.zip/node_modules/lodash.set/",
@ -16522,6 +16566,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["query-string", [
["npm:6.13.1", {
"packageLocation": "./.yarn/cache/query-string-npm-6.13.1-9e8c5bfe09-5e640f0cf6.zip/node_modules/query-string/",
"packageDependencies": [
["query-string", "npm:6.13.1"],
["decode-uri-component", "npm:0.2.0"],
["split-on-first", "npm:1.1.0"],
["strict-uri-encode", "npm:2.0.0"]
],
"linkType": "HARD",
}]
]],
["querystring", [
["npm:0.2.0", {
"packageLocation": "./.yarn/cache/querystring-npm-0.2.0-421b870c92-1e76c51462.zip/node_modules/querystring/",
@ -18151,6 +18207,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["split-on-first", [
["npm:1.1.0", {
"packageLocation": "./.yarn/cache/split-on-first-npm-1.1.0-e2f3ab5e4e-2ef26fee62.zip/node_modules/split-on-first/",
"packageDependencies": [
["split-on-first", "npm:1.1.0"]
],
"linkType": "HARD",
}]
]],
["split-string", [
["npm:3.1.0", {
"packageLocation": "./.yarn/cache/split-string-npm-3.1.0-df5d83450e-9b610d1509.zip/node_modules/split-string/",
@ -18283,6 +18348,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["strict-uri-encode", [
["npm:2.0.0", {
"packageLocation": "./.yarn/cache/strict-uri-encode-npm-2.0.0-1ec3189376-775012e88b.zip/node_modules/strict-uri-encode/",
"packageDependencies": [
["strict-uri-encode", "npm:2.0.0"]
],
"linkType": "HARD",
}]
]],
["string-length", [
["npm:2.0.0", {
"packageLocation": "./.yarn/cache/string-length-npm-2.0.0-72bc8bf050-44d79c40a4.zip/node_modules/string-length/",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

13
packages/helpers/.babelrc Normal file
View File

@ -0,0 +1,13 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "10",
"esmodules": true
}
}
]
]
}

View File

@ -0,0 +1,19 @@
module.exports = {
// Automatically clear mock calls and instances between every test
clearMocks: true,
// The directory where Jest should output its coverage files
coverageDirectory: 'coverage',
// An array of regexp pattern strings used to skip coverage collection
coveragePathIgnorePatterns: ['/node_modules/', '/dist/'],
// A list of reporter names that Jest uses when writing coverage reports
coverageReporters: ['text'],
// The test environment that will be used for testing
testEnvironment: 'node',
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
testPathIgnorePatterns: ['/node_modules/', '/dist/'],
};

View File

@ -0,0 +1,36 @@
{
"name": "@lowdefy/helpers",
"version": "1.0.0",
"main": "dist/index.js",
"scripts": {
"build": "babel src --out-dir dist",
"test": "jest --coverage",
"prepare": "yarn build",
"prepublishOnly": "yarn build"
},
"dependencies": {
"@lowdefy/delete": "1.0.1",
"@lowdefy/serializer": "1.0.0",
"@lowdefy/type": "1.0.1",
"lodash.merge": "4.6.2",
"query-string": "6.13.1"
},
"devDependencies": {
"@babel/cli": "7.8.4",
"@babel/compat-data": "7.9.6",
"@babel/core": "7.9.6",
"@babel/preset-env": "7.9.6",
"babel-jest": "24.9.0",
"eslint": "6.8.0",
"eslint-config-airbnb": "18.2.0",
"eslint-config-prettier": "6.12.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsx-a11y": "6.3.1",
"eslint-plugin-prettier": "3.1.4",
"eslint-plugin-react": "7.21.2",
"eslint-plugin-react-hooks": "4.1.2",
"jest": "24.9.0",
"jest-diff": "24.9.0",
"prettier": "2.1.2"
}
}

View File

@ -0,0 +1,17 @@
import type from '@lowdefy/type';
const applyArrayIndices = (arrayIndices, name) => {
if (!type.isArray(arrayIndices)) return name;
if (arrayIndices.length === 0) return name;
const copy = JSON.parse(JSON.stringify(arrayIndices));
const index = copy.shift();
let newName;
if (name.includes('$')) {
newName = name.replace('$', index.toString());
} else {
newName = name;
}
return applyArrayIndices(copy, newName);
};
export default applyArrayIndices;

View File

@ -0,0 +1,17 @@
import serializer from '@lowdefy/serializer';
function getFieldValues(operatorName, ...args) {
const result = new Set();
function reviver(key, value) {
if (key === operatorName) {
result.add(value);
}
return value;
}
[...args].forEach((element) => {
serializer.deserializeFromString(serializer.serializeToString(element), { reviver });
});
return [...result];
}
export default getFieldValues;

View File

@ -0,0 +1,18 @@
import type from '@lowdefy/type';
const getUniqueValues = (arr, key = 'value') => {
const arr2 = arr.map((o) => {
if (type.isPrimitive(o)) {
return JSON.stringify(o);
}
return JSON.stringify(o[key]);
});
return arr.filter((opt, i) => {
if (type.isPrimitive(opt)) {
return arr2.indexOf(JSON.stringify(opt)) === i;
}
return arr2.indexOf(JSON.stringify(opt[key])) === i;
});
};
export default getUniqueValues;

View File

@ -0,0 +1,26 @@
import type from '@lowdefy/type';
// eslint-disable-next-line consistent-return
const getIndex = (value, options, key = 'value') => {
// eslint-disable-next-line no-plusplus
for (let i = 0; i < options.length; i++) {
if (type.isPrimitive(options[i]) && options[i] === value) {
return i;
}
if (type.isObject(options[i]) && JSON.stringify(options[i][key]) === JSON.stringify(value)) {
return i;
}
}
};
const getValueIndex = (value, options, multiple, key) => {
if (!multiple) {
return getIndex(value, options, key);
}
const index = [];
value.forEach((val) => {
index.push(getIndex(val, options, key));
});
return index;
};
export default getValueIndex;

View File

@ -0,0 +1,23 @@
import applyArrayIndices from './applyArrayIndices';
import getFieldValues from './getFieldValues';
import getUniqueValues from './getUniqueValues';
import getValueIndex from './getValueIndex';
import intersect from './intersect';
import keyPermutations from './keyPermutations';
import mergeObjects from './mergeObjects';
import omit from './omit';
import swap from './swap';
import urlQuery from './urlQuery';
export {
applyArrayIndices,
getFieldValues,
getUniqueValues,
getValueIndex,
intersect,
keyPermutations,
mergeObjects,
omit,
swap,
urlQuery,
};

View File

@ -0,0 +1,5 @@
const intersect = (arrOne, arrTwo) => {
return arrOne.some((item) => arrTwo.includes(item));
};
export default intersect;

View File

@ -0,0 +1,34 @@
/* eslint-disable no-plusplus */
const regex = /(\.\d)/g;
const generateBitCount = (numOfMatches) => {
const maxBit = new Array(numOfMatches + 1).join('1');
const maxCount = parseInt(Number(maxBit), 2);
const result = [];
for (let i = 0; i <= maxCount; i++) {
result.push(new Array(maxBit.length - i.toString(2).length + 1).join('0') + i.toString(2));
}
return result;
};
const keyPermutations = (field) => {
if (!field.match(regex)) return [field];
// number of integer matches.
const numOfArrays = field.match(regex).length;
// generate bit counter array for number of matches
const bitCounter = generateBitCount(numOfArrays);
// loop through bit array and replace for 1 and push result
const result = [];
bitCounter.forEach((item) => {
let nth = 0;
result.push(
field.replace(regex, (match) => {
nth++;
return item[nth - 1] === '1' ? '.$' : match;
})
);
});
return result;
};
export default keyPermutations;

View File

@ -0,0 +1,12 @@
import type from '@lowdefy/type';
import merge from 'lodash.merge';
const mergeObjects = objects => {
let merged = objects;
if (type.isArray(objects)) {
merged = merge(...objects.filter(obj => type.isObject(obj)));
}
return merged;
};
export default mergeObjects;

View File

@ -0,0 +1,10 @@
import del from '@lowdefy/delete';
const omit = (obj, list) => {
list.forEach((item) => {
del(obj, item);
});
return obj;
};
export default omit;

View File

@ -0,0 +1,10 @@
import type from '@lowdefy/type';
const swap = (arr, from, to) => {
if (!type.isArray(arr) || from < 0 || to < 0 || from >= arr.length || to >= arr.length) {
return;
}
arr.splice(from, 1, arr.splice(to, 1, arr[from])[0]);
};
export default swap;

View File

@ -0,0 +1,29 @@
import type from '@lowdefy/type';
import queryString from 'query-string';
import serializer from '@lowdefy/serializer';
const parse = (str) => {
const parsed = queryString.parse(str);
const deserialized = {};
Object.keys(parsed).forEach((key) => {
try {
deserialized[key] = serializer.deserializeFromString(parsed[key]);
} catch (error) {
deserialized[key] = parsed[key];
}
});
return deserialized;
};
const stringify = (object) => {
if (!type.isObject(object)) {
return '';
}
const toSerialize = {};
Object.keys(object).forEach((key) => {
toSerialize[key] = serializer.serializeToString(object[key]);
});
return queryString.stringify(toSerialize);
};
export default { stringify, parse };

View File

@ -0,0 +1,47 @@
import applyArrayIndices from '../src/applyArrayIndices';
test('no arrayIndices', () => {
expect(applyArrayIndices(undefined, 'a')).toEqual('a');
});
test('no arrayIndices 1', () => {
expect(applyArrayIndices([], 'ab')).toEqual('ab');
});
test('no arrayIndices 2', () => {
expect(applyArrayIndices([], 'a.b')).toEqual('a.b');
});
test('arrayIndices with 1 index, primitive', () => {
expect(applyArrayIndices([1], 'a.$')).toEqual('a.1');
});
test('arrayIndices with 1 index, object', () => {
expect(applyArrayIndices([1], 'a.$.b')).toEqual('a.1.b');
});
test('arrayIndices with 1 index, no $', () => {
expect(applyArrayIndices([1], 'a')).toEqual('a');
});
test('arrayIndices with 2 indices', () => {
expect(applyArrayIndices([1, 2], 'a.$.$.b')).toEqual('a.1.2.b');
});
test('arrayIndices with 2 indices, no $', () => {
expect(applyArrayIndices([1, 2], 'a')).toEqual('a');
});
test('arrayIndices with 2 indices, 1 $', () => {
expect(applyArrayIndices([1, 2], 'a.$.b')).toEqual('a.1.b');
});
test('arrayIndices with 1 index, more than 1 $', () => {
expect(applyArrayIndices([1], 'a.$.a.$.b')).toEqual('a.1.a.$.b');
});
test('does not modify arrayIndices', () => {
const arrayIndices = [1];
expect(applyArrayIndices(arrayIndices, 'a.$')).toEqual('a.1');
expect(arrayIndices).toEqual([1]);
});

View File

@ -0,0 +1,35 @@
import getFieldValues from '../src/getFieldValues';
test('single object', () => {
expect(getFieldValues('_req', { _req: 1 })).toEqual([1]);
});
test('multiple objects', () => {
expect(getFieldValues('_req', { _req: 1 }, { _req: 2 }, { _req: 1 }, { _req: 4 })).toEqual([
1,
2,
4,
]);
});
test('multiple arrays', () => {
expect(
getFieldValues('_req', [{ _req: 1 }], [{ _req: 2 }], [{ _req: 1 }], [{ _req: 4 }])
).toEqual([1, 2, 4]);
});
test('multiple mixed', () => {
expect(getFieldValues('_req', [{ _req: 1 }], { _req: 2 }, { _req: 1 }, [{ _req: 4 }])).toEqual([
1,
2,
4,
]);
});
test('get on object of operator', () => {
const data = {
a: '1',
defaultValue: { _request: 'a' },
};
expect(getFieldValues('defaultValue', data)).toEqual([{ _request: 'a' }]);
});

View File

@ -0,0 +1,125 @@
import getUniqueValues from '../src/getUniqueValues';
test('primitive string - return all items', () => {
const arr = ['a', 'b', 'c'];
expect(getUniqueValues(arr)).toEqual(arr);
});
test('primitive number - return all items', () => {
const arr = [1, 3, 4, 4.5];
expect(getUniqueValues(arr)).toEqual(arr);
});
test('primitive boolean and none - return all items', () => {
const arr = [true, false, null, undefined];
expect(getUniqueValues(arr)).toEqual(arr);
});
test('object.value string - return all items', () => {
const arr = [
{ value: 'a', l: 'a' },
{ value: 'b', l: 'b' },
{ value: 'c', l: 'c' },
];
expect(getUniqueValues(arr)).toEqual(arr);
});
test('object[key] number - return all items', () => {
const arr = [
{ v: 1, l: 'a' },
{ v: 2, l: 'b' },
{ v: 3, l: 'c' },
];
expect(getUniqueValues(arr, 'v')).toEqual(arr);
});
test('primitive string - remove duplicate', () => {
const arr = ['a', 'a', 'b', 'c', 'b'];
expect(getUniqueValues(arr)).toEqual(['a', 'b', 'c']);
});
test('primitive number - remove duplicate', () => {
const arr = [1, 4, 2, 3, 4, 4.5, 3, 2, 2, 1];
expect(getUniqueValues(arr)).toEqual([1, 4, 2, 3, 4.5]);
});
test('primitive boolean and none - remove duplicate', () => {
const arr = [true, false, null, undefined, undefined, null, true, false];
expect(getUniqueValues(arr)).toEqual([true, false, null, undefined]);
});
test('object.value string - remove duplicate', () => {
const arr = [
{ value: 'a', l: 'a' },
{ value: 'b', l: 'b' },
{ value: 'a', l: 'y' },
{ value: 'c', l: 'c' },
{ value: 'b', l: 'x' },
];
expect(getUniqueValues(arr)).toEqual([
{ value: 'a', l: 'a' },
{ value: 'b', l: 'b' },
{ value: 'c', l: 'c' },
]);
});
test('object[key] number - remove duplicate', () => {
const arr = [
{ v: 1, l: 'a' },
{ v: 3, l: 'b' },
{ v: 3, l: '3' },
{ v: 3.1, l: 'b' },
{ v: 2, l: 'c' },
{ v: 1, l: 'x' },
];
expect(getUniqueValues(arr, 'v')).toEqual([
{ v: 1, l: 'a' },
{ v: 3, l: 'b' },
{ v: 3.1, l: 'b' },
{ v: 2, l: 'c' },
]);
});
test('object.value objects and arrays - remove duplicate', () => {
const arr = [
{ value: [1, 2], l: 'a' },
{ value: { a: 1, b: { c: 1 } }, l: 'b' },
{ value: [1, 2], l: '1' },
{ value: { a: 1, b: { c: 'x' } }, l: 'b' },
{ value: { a: 1, b: { c: 1 } }, l: 'y' },
];
expect(getUniqueValues(arr)).toEqual([
{ value: [1, 2], l: 'a' },
{ value: { a: 1, b: { c: 1 } }, l: 'b' },
{ value: { a: 1, b: { c: 'x' } }, l: 'b' },
]);
});
test('object.value mixed - remove duplicate', () => {
const arr = [
'a',
{ value: [1, 2], l: 'a' },
{ value: { a: 1, b: { c: 1 } }, l: 'b' },
{ value: [1, 2], l: '1' },
undefined,
{ value: 3, l: '1' },
{ value: null, l: 'a' },
{ value: { a: 1, b: { c: 'x' } }, l: 'b' },
'a',
{ value: { a: 1, b: { c: 1 } }, l: 'y' },
3,
{ value: 'a', l: '1' },
{ value: undefined, l: '1' },
{ value: 3, l: 'c' },
null,
];
expect(getUniqueValues(arr)).toEqual([
'a',
{ value: [1, 2], l: 'a' },
{ value: { a: 1, b: { c: 1 } }, l: 'b' },
undefined,
{ value: 3, l: '1' },
{ value: null, l: 'a' },
{ value: { a: 1, b: { c: 'x' } }, l: 'b' },
]);
});

View File

@ -0,0 +1,140 @@
import getValueIndex from '../src/getValueIndex';
test('primitive string single', () => {
expect(getValueIndex('b', ['a', 'b', 'c'])).toEqual(1);
});
test('primitive number single', () => {
expect(getValueIndex(1, [0, 'c', 1, 'c'])).toEqual(2);
});
test('primitive boolean single', () => {
expect(getValueIndex(false, [0, true, 1, false, 'c'])).toEqual(3);
expect(getValueIndex(true, [0, true, 1, false, 'c'])).toEqual(1);
});
test('primitive none single', () => {
expect(getValueIndex(null, [0, null, true, 1, false, 'c'])).toEqual(1);
expect(getValueIndex(undefined, [0, true, 1, false, undefined, 'c'])).toEqual(4);
});
test('primitive string multiple', () => {
expect(getValueIndex(['b', 'c'], ['a', 'b', 'd', 'c'], true)).toEqual([1, 3]);
});
test('primitive number multiple', () => {
expect(getValueIndex([1, 'x'], [0, 'x', 1, 'c'], true)).toEqual([2, 1]);
});
test('primitive boolean multiple', () => {
expect(getValueIndex(['c', false], [0, true, 1, false, 'c'], true)).toEqual([4, 3]);
expect(getValueIndex([true, 0], [0, true, 1, false, 'c'], true)).toEqual([1, 0]);
});
test('primitive none multiple', () => {
expect(getValueIndex([null, 'c'], [0, null, true, 1, false, 'c'], true)).toEqual([1, 5]);
expect(getValueIndex([undefined, false], [0, true, 1, false, undefined, 'c'], true)).toEqual([
4,
3,
]);
});
test('object.value string single', () => {
const options = [
{ value: 'a', l: 'a' },
{ value: 'b', l: 'b' },
{ value: 'c', l: 'c' },
];
expect(getValueIndex('c', options)).toEqual(2);
});
test('object[key] number single', () => {
const options = [
{ v: 1, l: 'a' },
{ v: 3, l: 'b' },
{ v: 3.1, l: 'b' },
{ v: 2, l: 'c' },
];
expect(getValueIndex(3.1, options, false, 'v')).toEqual(2);
});
test('object.value object single', () => {
const options = [
{ value: [1, 2], l: 'a' },
{ value: { a: 1, b: { c: 1 } }, l: 'b' },
{ value: { a: 1, b: { c: 'x' } }, l: 'b' },
];
expect(getValueIndex([1, 2], options)).toEqual(0);
expect(getValueIndex({ a: 1, b: { c: 1 } }, options)).toEqual(1);
});
test('object.value mixed single', () => {
const options = [
'a',
{ value: [1, 2], l: 'a' },
{ value: { a: 1, b: { c: 1 } }, l: 'b' },
undefined,
{ value: 3, l: '1' },
{ value: null, l: 'a' },
{ value: 'x', l: 'a' },
{ value: { a: 1, b: { c: 'x' } }, l: 'b' },
];
expect(getValueIndex('a', options)).toEqual(0);
expect(getValueIndex([1, 2], options)).toEqual(1);
expect(getValueIndex({ a: 1, b: { c: 1 } }, options)).toEqual(2);
expect(getValueIndex(undefined, options)).toEqual(3);
expect(getValueIndex(3, options)).toEqual(4);
expect(getValueIndex(null, options)).toEqual(5);
expect(getValueIndex('x', options)).toEqual(6);
expect(getValueIndex({ a: 1, b: { c: 'x' } }, options)).toEqual(7);
});
test('object.value string multiple', () => {
const options = [
{ value: 'a', l: 'a' },
{ value: 'b', l: 'b' },
{ value: 'c', l: 'c' },
];
expect(getValueIndex(['c', 'b'], options, true)).toEqual([2, 1]);
});
test('object[key] number multiple', () => {
const options = [
{ v: 1, l: 'a' },
{ v: 3, l: 'b' },
{ v: 3.1, l: 'b' },
{ v: 2, l: 'c' },
];
expect(getValueIndex([1, 2, 3.1], options, true, 'v')).toEqual([0, 3, 2]);
});
test('object.value object multiple', () => {
const options = [
{ value: [1, 2], l: 'a' },
{ value: { a: 1, b: { c: 1 } }, l: 'b' },
{ value: { a: 1, b: { c: 'x' } }, l: 'b' },
];
expect(getValueIndex([[1, 2], { a: 1, b: { c: 'x' } }], options, true)).toEqual([0, 2]);
expect(getValueIndex([{ a: 1, b: { c: 1 } }], options, true)).toEqual([1]);
});
test('object.value mixed multiple', () => {
const options = [
'a',
{ value: [1, 2], l: 'a' },
{ value: { a: 1, b: { c: 1 } }, l: 'b' },
undefined,
{ value: 3, l: '1' },
{ value: null, l: 'a' },
{ value: 'x', l: 'a' },
{ value: { a: 1, b: { c: 'x' } }, l: 'b' },
];
expect(getValueIndex(['a', null], options, true)).toEqual([0, 5]);
expect(getValueIndex(['x', [1, 2]], options, true)).toEqual([6, 1]);
expect(getValueIndex([{ a: 1, b: { c: 1 } }], options, true)).toEqual([2]);
expect(getValueIndex([undefined, 3, 'a'], options, true)).toEqual([3, 4, 0]);
expect(getValueIndex([3], options, true)).toEqual([4]);
expect(getValueIndex([null, { a: 1, b: { c: 'x' } }], options, true)).toEqual([5, 7]);
expect(getValueIndex(['x', [1, 2], 'x'], options, true)).toEqual([6, 1, 6]);
expect(getValueIndex([{ a: 1, b: { c: 'x' } }], options, true)).toEqual([7]);
});

View File

@ -0,0 +1,10 @@
import intersect from '../src/intersect';
test('intersect', () => {
expect(intersect(['0', '1'], ['0'])).toBe(true);
expect(intersect(['0', '1'], ['3'])).toBe(false);
expect(intersect(['1'], ['0', '2', '1'])).toBe(true);
expect(intersect([], ['3'])).toBe(false);
expect(intersect(['1'], [])).toBe(false);
expect(intersect([], [])).toBe(false);
});

View File

@ -0,0 +1,37 @@
import keyPermutations from '../src/keyPermutations';
test('no permutations on plain field names', () => {
expect(keyPermutations('a')).toEqual(['a']);
expect(keyPermutations('asdf')).toEqual(['asdf']);
expect(keyPermutations('asdf123')).toEqual(['asdf123']);
expect(keyPermutations('asdf123_sdf')).toEqual(['asdf123_sdf']);
});
test('no permutations on object field names', () => {
expect(keyPermutations('a.b')).toEqual(['a.b']);
expect(keyPermutations('asd.f')).toEqual(['asd.f']);
expect(keyPermutations('asd.f1.a23')).toEqual(['asd.f1.a23']);
expect(keyPermutations('asdf._d123._sd.f')).toEqual(['asdf._d123._sd.f']);
});
test('permutations on arrays field names', () => {
expect(keyPermutations('a.0.b')).toEqual(['a.0.b', 'a.$.b']);
expect(keyPermutations('a.0')).toEqual(['a.0', 'a.$']);
expect(keyPermutations('a.0.b.0')).toEqual(['a.0.b.0', 'a.0.b.$', 'a.$.b.0', 'a.$.b.$']);
expect(keyPermutations('a.0.b.0.c')).toEqual([
'a.0.b.0.c',
'a.0.b.$.c',
'a.$.b.0.c',
'a.$.b.$.c',
]);
expect(keyPermutations('a.0.0.0')).toEqual([
'a.0.0.0',
'a.0.0.$',
'a.0.$.0',
'a.0.$.$',
'a.$.0.0',
'a.$.0.$',
'a.$.$.0',
'a.$.$.$',
]);
});

View File

@ -0,0 +1,82 @@
import mergeObjects from '../src/mergeObjects';
test('object with no media unchanged', () => {
const obj = {
a: 'a',
b: 1,
c: { a: 'b' },
};
expect(mergeObjects(obj)).toEqual(obj);
});
test('object with no media unchanged', () => {
const obj1 = {
a: 'a',
b: 1,
c: { a: 'b', e: 1 },
};
const obj2 = {
b: 2,
c: { a: 'a' },
};
expect(mergeObjects([obj1, obj2])).toEqual({
a: 'a',
b: 2,
c: { a: 'a', e: 1 },
});
});
test('object with all media', () => {
const obj1 = {
a: 'a',
sm: { a: 'sm' },
md: { a: 'md' },
lg: { a: 'lg' },
xl: { a: 'xl' },
};
const obj2 = {
a: 'a',
x: 0,
sm: { b: 1 },
md: { a: 'md', b: 2 },
lg: { b: 3 },
xl: { a: 'xl', b: 4 },
};
expect(mergeObjects([obj1, obj2])).toMatchInlineSnapshot(`
Object {
"a": "a",
"lg": Object {
"a": "lg",
"b": 3,
},
"md": Object {
"a": "md",
"b": 2,
},
"sm": Object {
"a": "sm",
"b": 1,
},
"x": 0,
"xl": Object {
"a": "xl",
"b": 4,
},
}
`);
});
test('merge list of objects, larger indices overwrite smaller', () => {
expect(mergeObjects([{ a: 1 }, { sm: { b: 2 } }, { sm: { b: 4 } }, { md: { c: 2 } }]))
.toMatchInlineSnapshot(`
Object {
"a": 1,
"md": Object {
"c": 2,
},
"sm": Object {
"b": 4,
},
}
`);
});

View File

@ -0,0 +1,20 @@
import omit from '../src/omit';
test('omit flat keys', () => {
const obj = { a: 1, b: 2, c: 3, d: 4 };
omit(obj, ['a', 'd']);
expect(obj).toEqual({ b: 2, c: 3 });
});
test('omit nest keys', () => {
const obj = { a: 1, x: { b: 2, c: 3 }, d: 4 };
omit(obj, ['a', 'x.c']);
expect(obj).toEqual({ x: { b: 2 }, d: 4 });
});
// TODO: decide how arrays should be handled as this is how delete handles arrays.
test('omit array keys', () => {
const obj = { a: [1, 2, 3, 4], b: 1, d: 4 };
omit(obj, ['d', 'a.2']);
expect(obj).toEqual({ a: [1, 2, undefined, 4], b: 1 });
});

View File

@ -0,0 +1,21 @@
import swap from '../src/swap';
test('swap', () => {
const arr = [0, 1, 2, 3, 4];
swap(arr, 2, 3);
expect(arr).toEqual([0, 1, 3, 2, 4]);
});
test('swap out of bounds', () => {
const arr = [0, 1, 2, 3, 4];
swap(arr, -1, 3);
expect(arr).toEqual(arr);
swap(arr, 2, 8);
expect(arr).toEqual(arr);
});
test('not an array', () => {
const arr = 1;
swap(arr, 2, 3);
expect(arr).toEqual(1);
});

View File

@ -0,0 +1,125 @@
import urlQuery from '../src/urlQuery';
test('primitives', () => {
expect(urlQuery.stringify(1)).toEqual('');
expect(urlQuery.stringify('a')).toEqual('');
});
test('primitives in object', () => {
const string = urlQuery.stringify({
a: 1,
b: 'b',
});
expect(string).toEqual('a=1&b=%22b%22');
expect(urlQuery.parse(string)).toEqual({
a: 1,
b: 'b',
});
});
test('primitive in array', () => {
expect(urlQuery.stringify([1, 2, 3])).toEqual('');
});
test('array in object', () => {
const string = urlQuery.stringify({
arr: [1, 2, 3],
});
expect(string).toEqual('arr=%5B1%2C2%2C3%5D');
expect(urlQuery.parse(string)).toEqual({
arr: [1, 2, 3],
});
});
test('object in object', () => {
const string = urlQuery.stringify({
a: {
b: '1',
},
});
expect(string).toEqual('a=%7B%22b%22%3A%221%22%7D');
expect(urlQuery.parse(string)).toEqual({
a: {
b: '1',
},
});
});
test('object in array', () => {
const string = urlQuery.stringify({
arr: [
{
a: '0',
},
{
b: '1',
},
],
});
expect(string).toEqual('arr=%5B%7B%22a%22%3A%220%22%7D%2C%7B%22b%22%3A%221%22%7D%5D');
expect(urlQuery.parse(string)).toEqual({
arr: [
{
a: '0',
},
{
b: '1',
},
],
});
});
test('array in array', () => {
const string = urlQuery.stringify({
arr: [
[1, 2],
[3, 4],
],
});
expect(string).toEqual('arr=%5B%5B1%2C2%5D%2C%5B3%2C4%5D%5D');
expect(urlQuery.parse(string)).toEqual({
arr: [
[1, 2],
[3, 4],
],
});
});
test('object, primitive and array in object', () => {
const string = urlQuery.stringify({
a: 'a',
obj: {
b: '1',
},
arr: [1, 2],
});
expect(string).toEqual('a=%22a%22&arr=%5B1%2C2%5D&obj=%7B%22b%22%3A%221%22%7D');
expect(urlQuery.parse(string)).toEqual({
a: 'a',
obj: {
b: '1',
},
arr: [1, 2],
});
});
test('urlQuery parse string starts with ?', () => {
const string = '?a=%22a%22&arr=%5B1%2C2%5D&obj=%7B%22b%22%3A%221%22%7D';
expect(urlQuery.parse(string)).toEqual({
a: 'a',
obj: {
b: '1',
},
arr: [1, 2],
});
});
test('urlQuery parse string with params not serialized JSON', () => {
const string = 'a=a&b=1';
expect(urlQuery.parse(string)).toEqual({
a: 'a',
b: 1,
});
});

View File

@ -2708,7 +2708,7 @@ __metadata:
languageName: unknown
linkType: soft
"@lowdefy/delete@workspace:packages/delete":
"@lowdefy/delete@1.0.1, @lowdefy/delete@workspace:packages/delete":
version: 0.0.0-use.local
resolution: "@lowdefy/delete@workspace:packages/delete"
dependencies:
@ -2806,6 +2806,34 @@ __metadata:
languageName: unknown
linkType: soft
"@lowdefy/helpers@workspace:packages/helpers":
version: 0.0.0-use.local
resolution: "@lowdefy/helpers@workspace:packages/helpers"
dependencies:
"@babel/cli": 7.8.4
"@babel/compat-data": 7.9.6
"@babel/core": 7.9.6
"@babel/preset-env": 7.9.6
"@lowdefy/delete": 1.0.1
"@lowdefy/serializer": 1.0.0
"@lowdefy/type": 1.0.1
babel-jest: 24.9.0
eslint: 6.8.0
eslint-config-airbnb: 18.2.0
eslint-config-prettier: 6.12.0
eslint-plugin-import: 2.22.1
eslint-plugin-jsx-a11y: 6.3.1
eslint-plugin-prettier: 3.1.4
eslint-plugin-react: 7.21.2
eslint-plugin-react-hooks: 4.1.2
jest: 24.9.0
jest-diff: 24.9.0
lodash.merge: 4.6.2
prettier: 2.1.2
query-string: 6.13.1
languageName: unknown
linkType: soft
"@lowdefy/poc-express@workspace:packages/express":
version: 0.0.0-use.local
resolution: "@lowdefy/poc-express@workspace:packages/express"
@ -2845,7 +2873,7 @@ __metadata:
languageName: unknown
linkType: soft
"@lowdefy/serializer@workspace:packages/serializer":
"@lowdefy/serializer@1.0.0, @lowdefy/serializer@workspace:packages/serializer":
version: 0.0.0-use.local
resolution: "@lowdefy/serializer@workspace:packages/serializer"
dependencies:
@ -11115,6 +11143,13 @@ fsevents@~2.1.2:
languageName: node
linkType: hard
"lodash.merge@npm:4.6.2":
version: 4.6.2
resolution: "lodash.merge@npm:4.6.2"
checksum: 4e2bb42a87a148991458d7c384bc197e96f7115e9536fc8e2c86ae9e99ce1c1f693ff15eb85761952535f48d72253aed8e673d9f32dde3e671cd91e3fde220a7
languageName: node
linkType: hard
"lodash.set@npm:^4.3.2":
version: 4.3.2
resolution: "lodash.set@npm:4.3.2"
@ -13428,6 +13463,17 @@ fsevents@~2.1.2:
languageName: node
linkType: hard
"query-string@npm:6.13.1":
version: 6.13.1
resolution: "query-string@npm:6.13.1"
dependencies:
decode-uri-component: ^0.2.0
split-on-first: ^1.0.0
strict-uri-encode: ^2.0.0
checksum: 5e640f0cf6077f3fd4dcb6d8f3a88306bbb5d5638001450b15a244ca74f76e4a6a1d70baff114cef43ca15670d5d2523f92113f3567561b9dbd1fcb26489066a
languageName: node
linkType: hard
"querystring@npm:0.2.0":
version: 0.2.0
resolution: "querystring@npm:0.2.0"
@ -14938,6 +14984,13 @@ resolve@1.1.7:
languageName: node
linkType: hard
"split-on-first@npm:^1.0.0":
version: 1.1.0
resolution: "split-on-first@npm:1.1.0"
checksum: 2ef26fee62665be9547e8035734b856e658b08fd13e70271a2f258147f29d1f18e12b5cb7f7670d83e113c172a9c5fe3d87d9d7c02a1d3d57824818d75d942ab
languageName: node
linkType: hard
"split-string@npm:^3.0.1, split-string@npm:^3.0.2":
version: 3.1.0
resolution: "split-string@npm:3.1.0"
@ -15066,6 +15119,13 @@ resolve@1.1.7:
languageName: node
linkType: hard
"strict-uri-encode@npm:^2.0.0":
version: 2.0.0
resolution: "strict-uri-encode@npm:2.0.0"
checksum: 775012e88b9d8dff939d514bf376d615a15e8228a5dd587a94ac3c71fce41aa3635cd808aa796e2c1cd33f3f2fe2fbf89b74ee18a504a1905efa1854311e04bb
languageName: node
linkType: hard
"string-length@npm:^2.0.0":
version: 2.0.0
resolution: "string-length@npm:2.0.0"