feat: add get package

This commit is contained in:
Sam Tolmay 2020-10-05 16:50:02 +02:00
parent d0c60c4bfa
commit 3e59f0c954
7 changed files with 867 additions and 2 deletions

31
.pnp.js generated
View File

@ -34,6 +34,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"name": "@lowdefy/poc-express",
"reference": "workspace:packages/express"
},
{
"name": "@lowdefy/get",
"reference": "workspace:packages/get"
},
{
"name": "@lowdefy/graphql",
"reference": "workspace:packages/graphql"
@ -47,6 +51,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"ignorePatternData": "(^(?:\\.yarn\\/sdks(?:\\/(?!\\.)(?:(?:(?!(?:^|\\/)\\.).)*?)|$))$)",
"fallbackExclusionList": [
["@lowdefy/engine", ["workspace:packages/engine"]],
["@lowdefy/get", ["workspace:packages/get"]],
["@lowdefy/graphql", ["workspace:packages/graphql"]],
["@lowdefy/poc", ["workspace:."]],
["@lowdefy/poc-express", ["workspace:packages/express"]],
@ -4124,6 +4129,32 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "SOFT",
}]
]],
["@lowdefy/get", [
["workspace:packages/get", {
"packageLocation": "./packages/get/",
"packageDependencies": [
["@lowdefy/get", "workspace:packages/get"],
["@babel/cli", "virtual:7fa6405098723f150ab741c1e73c906de11a676b4cc641bac8b3397ea2dd6efbb913e72a780932220533241b442cc586b41b26c7b5ac786de486992cd2db054c#npm:7.8.4"],
["@babel/compat-data", "npm:7.9.6"],
["@babel/core", "npm:7.9.6"],
["@babel/preset-env", "virtual:7fa6405098723f150ab741c1e73c906de11a676b4cc641bac8b3397ea2dd6efbb913e72a780932220533241b442cc586b41b26c7b5ac786de486992cd2db054c#npm:7.9.6"],
["@lowdefy/type", "workspace:packages/type"],
["babel-jest", "virtual:73f25cc0d3f57943fa9b1d737e4809af7a52a784e0ac5fed74b4e1e083308ab7ae2fd45a5424a8bc7ff7caab067690c9357630d657cbd636d6037acc1557fdc2#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"],
["prettier", "npm:2.1.2"]
],
"linkType": "SOFT",
}]
]],
["@lowdefy/graphql", [
["workspace:packages/graphql", {
"packageLocation": "./packages/graphql/",

12
packages/get/.babelrc Normal file
View File

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

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/'],
};

33
packages/get/package.json Normal file
View File

@ -0,0 +1,33 @@
{
"name": "@lowdefy/get",
"version": "1.0.1",
"license": "MIT",
"main": "dist/get.js",
"scripts": {
"build": "babel src --out-dir dist",
"test": "jest --coverage",
"prepare": "yarn build",
"prepublishOnly": "yarn build"
},
"dependencies": {
"@lowdefy/type": "1.0.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"
}
}

131
packages/get/src/get.js Normal file
View File

@ -0,0 +1,131 @@
/* eslint-disable no-param-reassign */
// Source:
// https://github.com/jonschlinkert/get-value/blob/master/index.js
// https://www.npmjs.com/package/get-value
// The MIT License (MIT)
// Copyright (c) 2014-2018, Jon Schlinkert.
// 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:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 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.
import typeTest from '@lowdefy/type';
function join(segs, joinChar, options) {
if (typeof options.join === 'function') {
return options.join(segs);
}
return segs[0] + joinChar + segs[1];
}
function split(path, splitChar, options) {
if (typeof options.split === 'function') {
return options.split(path);
}
return path.split(splitChar);
}
function isValid(key, target, options) {
if (typeof options.isValid === 'function') {
return options.isValid(key, target);
}
return true;
}
function isValidObject(val) {
return typeTest.isObject(val) || Array.isArray(val) || typeof val === 'function';
}
function get(target, path, options) {
if (!typeTest.isObject(options)) {
options = { default: options };
}
if (typeTest.isNone(path) || !isValidObject(target)) {
return typeof options.default !== 'undefined' ? options.default : undefined;
}
if (typeof path === 'number') {
path = String(path);
}
const isArray = Array.isArray(path);
const isString = typeof path === 'string';
const splitChar = options.separator || '.';
const joinChar = options.joinChar || (typeof splitChar === 'string' ? splitChar : '.');
if (isString && path in target) {
return isValid(path, target, options) ? target[path] : options.default;
}
const segs = isArray ? path : split(path, splitChar, options);
const len = segs.length;
let idx = 0;
do {
let prop = segs[idx];
if (typeof prop === 'number') {
prop = String(prop);
}
while (prop && prop.slice(-1) === '\\') {
idx += 1;
prop = join([prop.slice(0, -1), segs[idx] || ''], joinChar, options);
}
if (prop in target) {
if (!isValid(prop, target, options)) {
return options.default;
}
target = target[prop];
} else {
let hasProp = false;
let n = idx + 1;
while (n < len) {
prop = join([prop, segs[n]], joinChar, options);
n += 1;
hasProp = prop in target;
if (hasProp) {
if (!isValid(prop, target, options)) {
return options.default;
}
target = target[prop];
idx = n - 1;
break;
}
}
if (!hasProp) {
return options.default;
}
}
// eslint-disable-next-line no-plusplus
} while (++idx < len && isValidObject(target));
if (idx === len) {
return target;
}
return options.default;
}
export default get;

View File

@ -0,0 +1,615 @@
import get from '../src/get';
const diff = require('jest-diff');
const expectToEqual = (result, value) => {
expect(result).toEqual(value);
};
const expectToStrictEqual = (result, value) => {
expect(result).toStrictEqual(value);
};
expect.extend({
deepStrictEqual(received, expected) {
const options = {};
const pass = this.equals(received, expected);
const message = pass
? () =>
`${this.utils.matcherHint('deepStrictEqual', undefined, undefined, options)}\n\n` +
`Expected: ${this.utils.printExpected(expected)}\n` +
`Received: ${this.utils.printReceived(received)}`
: () => {
const diffString = diff(expected, received, {
expand: this.expand,
});
return `${this.utils.matcherHint('deepStrictEqual', undefined, undefined, options)}\n\n${
diffString && diffString.includes('- Expect')
? `Difference:\n\n${diffString}`
: `Expected: ${this.utils.printExpected(
expected
)}\nReceived: ${this.utils.printReceived(received)}`
}`;
};
return { actual: received, message, pass };
},
});
const expectDeepStrictEqual = (result, value) => {
expect(result).deepStrictEqual(value);
};
test('get a', () => {
const objOne = {
a: 1,
};
expect(get(objOne, 'a')).toEqual(1);
});
test('get undefined', () => {
const objOne = {
a: 1,
};
expect(get(objOne, 'b')).toEqual(undefined);
});
test('get a.b', () => {
const objOne = {
a: { b: 1 },
};
expect(get(objOne, 'a.b')).toEqual(1);
});
test('get --b', () => {
const objOne = {
'--b': 10,
};
expect(get(objOne, '--b')).toEqual(10);
});
test('get a.--b', () => {
const objOne = {
a: { '--b': 1 },
};
expect(get(objOne, 'a.--b')).toEqual(1);
});
test('get a.undefined', () => {
const objOne = {
a: { b: 1 },
};
expect(get(objOne, 'a.c')).toEqual(undefined);
});
test('get a.0.b in array', () => {
const objOne = {
a: [{ b: 1 }, { c: 2 }],
};
expect(get(objOne, 'a.1.c')).toEqual(2);
});
test('return alt value', () => {
const objOne = {
a: 1,
};
expect(get(objOne, 'b', 3)).toEqual(3);
});
test('return null value', () => {
const objOne = {
a: null,
};
expect(get(objOne, 'a', 2)).toEqual(null);
});
test('return arr', () => {
const objOne = {
a: { b: [] },
};
expect(get(objOne, 'a.b')).toEqual([]);
});
// tests from
// https://github.com/jonschlinkert/get-value/blob/master/test/units.js
test('should return undefined when non-object given as the first argument', () => {
expect(get(null)).toEqual(undefined);
expect(get('foo')).toEqual(undefined);
expect(get(['a'])).toEqual(undefined);
expect(get({ a: 'a' })).toEqual(undefined);
});
test('should return default value when non-object given as the first argument with default value', () => {
expect(get(null, undefined, { default: 'default' })).toEqual('default');
expect(get('foo', undefined, { default: 'default' })).toEqual('default');
expect(get(['a'], undefined, { default: 'default' })).toEqual('default');
expect(get({ a: 'a' }, undefined, { default: 'default' })).toEqual('default');
});
test('should get a value', () => {
expectToEqual(get({ a: 'a', b: { c: 'd' } }, 'a'), 'a');
expectToEqual(get({ a: 'a', b: { c: 'd' } }, 'b.c'), 'd');
expectToEqual(get({ foo: 'bar' }, 'foo.bar'), undefined);
});
test('should get a property that has dots in the key', () => {
expectToEqual(get({ 'a.b': 'c' }, 'a.b'), 'c');
});
test('should support using dot notation to get nested values', () => {
const fixture = {
a: { locals: { name: { first: 'Brian' } } },
b: { locals: { name: { last: 'Woodward' } } },
c: { locals: { paths: ['a.txt', 'b.js', 'c.hbs'] } },
};
expectDeepStrictEqual(get(fixture, 'a.locals.name'), { first: 'Brian' });
expectDeepStrictEqual(get(fixture, 'b.locals.name'), { last: 'Woodward' });
expectToStrictEqual(get(fixture, 'b.locals.name.last'), 'Woodward');
expectToStrictEqual(get(fixture, 'c.locals.paths.0'), 'a.txt');
expectToStrictEqual(get(fixture, 'c.locals.paths.1'), 'b.js');
expectToStrictEqual(get(fixture, 'c.locals.paths.2'), 'c.hbs');
expectToStrictEqual(get(fixture, 'c.locals.paths.0'), 'a.txt');
expectToStrictEqual(get(fixture, 'c.locals.paths.1'), 'b.js');
expectToStrictEqual(get(fixture, 'c.locals.paths.2'), 'c.hbs');
});
test('should support a custom separator on options.separator', () => {
const fixture = { 'a.b': { c: { d: 'e' } } };
expectToStrictEqual(get(fixture, 'a.b/c/d', { separator: '/' }), 'e');
expectToStrictEqual(get(fixture, 'a\\.b.c.d', { separator: /\\?\./ }), 'e');
});
test('should support a custom split function', () => {
const fixture = { 'a.b': { c: { d: 'e' } } };
expectToStrictEqual(get(fixture, 'a.b/c/d', { split: (path) => path.split('/') }), 'e');
expectToStrictEqual(get(fixture, 'a\\.b.c.d', { split: (path) => path.split(/\\?\./) }), 'e');
});
test('should support a custom join character', () => {
const fixture = { 'a-b': { c: { d: 'e' } } };
const options = { joinChar: '-' };
expectToStrictEqual(get(fixture, 'a.b.c.d', options), 'e');
});
test('should support a custom join function', () => {
const fixture = { 'a-b': { c: { d: 'e' } } };
const options = {
split: (path) => path.split(/[-/]/),
join: (segs) => segs.join('-'),
};
expectToStrictEqual(get(fixture, 'a/b-c/d', options), 'e');
});
test('should support a default value as the last argument', () => {
const fixture = { foo: { c: { d: 'e' } } };
expectToEqual(get(fixture, 'foo.bar.baz', 'quz'), 'quz');
expectToEqual(get(fixture, 'foo.bar.baz', true), true);
expectToEqual(get(fixture, 'foo.bar.baz', false), false);
expectToEqual(get(fixture, 'foo.bar.baz', null), null);
});
test('should support options.default', () => {
const fixture = { foo: { c: { d: 'e' } } };
expectToEqual(get(fixture, 'foo.bar.baz', { default: 'qux' }), 'qux');
expectToEqual(get(fixture, 'foo.bar.baz', { default: true }), true);
expectToEqual(get(fixture, 'foo.bar.baz', { default: false }), false);
expectToEqual(get(fixture, 'foo.bar.baz', { default: null }), null);
expectDeepStrictEqual(get(fixture, 'foo.bar.baz', { default: { one: 'two' } }), { one: 'two' });
});
test('should support a custom function for validating the object', () => {
const isEnumerable = Object.prototype.propertyIsEnumerable;
const options = {
isValid(key, obj) {
return isEnumerable.call(obj, key);
},
};
const fixture = { 'a.b': { c: { d: 'e' } } };
expectToStrictEqual(get(fixture, 'a.b.c.d', options), 'e');
});
test('should support nested keys with dots', () => {
expectToStrictEqual(get({ 'a.b.c': 'd' }, 'a.b.c'), 'd');
expectToStrictEqual(get({ 'a.b': { c: 'd' } }, 'a.b.c'), 'd');
expectToStrictEqual(get({ 'a.b': { c: { d: 'e' } } }, 'a.b.c.d'), 'e');
expectToStrictEqual(get({ a: { b: { c: 'd' } } }, 'a.b.c'), 'd');
expectToStrictEqual(get({ a: { 'b.c': 'd' } }, 'a.b.c'), 'd');
expectToStrictEqual(get({ 'a.b.c.d': 'e' }, 'a.b.c.d'), 'e');
expectToStrictEqual(get({ 'a.b.c.d': 'e' }, 'a.b.c'), undefined);
expectToStrictEqual(get({ 'a.b.c.d.e.f': 'g' }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ 'a.b.c.d.e': { f: 'g' } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ 'a.b.c.d': { e: { f: 'g' } } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ 'a.b.c': { d: { e: { f: 'g' } } } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ 'a.b': { c: { d: { e: { f: 'g' } } } } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ a: { b: { c: { d: { e: { f: 'g' } } } } } }, 'a.b.c.d.e.f'), 'g');
expectDeepStrictEqual(get({ 'a.b.c.d.e': { f: 'g' } }, 'a.b.c.d.e'), {
f: 'g',
});
expectDeepStrictEqual(get({ 'a.b.c.d': { 'e.f': 'g' } }, 'a.b.c.d.e'), undefined);
expectDeepStrictEqual(get({ 'a.b.c': { 'd.e.f': 'g' } }, 'a.b.c'), {
'd.e.f': 'g',
});
expectDeepStrictEqual(get({ 'a.b': { 'c.d.e.f': 'g' } }, 'a.b'), {
'c.d.e.f': 'g',
});
expectDeepStrictEqual(get({ a: { 'b.c.d.e.f': 'g' } }, 'a'), {
'b.c.d.e.f': 'g',
});
expectToStrictEqual(get({ 'a.b.c.d.e': { f: 'g' } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ 'a.b.c.d': { 'e.f': 'g' } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ 'a.b.c': { 'd.e.f': 'g' } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ 'a.b': { 'c.d.e.f': 'g' } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ a: { 'b.c.d.e.f': 'g' } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ 'a.b': { 'c.d': { 'e.f': 'g' } } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ 'a.b': { c: { 'd.e.f': 'g' } } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ a: { 'b.c.d.e': { f: 'g' } } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ a: { 'b.c.d': { 'e.f': 'g' } } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ a: { 'b.c': { 'd.e.f': 'g' } } }, 'a.b.c.d.e.f'), 'g');
expectToStrictEqual(get({ a: { b: { 'c.d.e.f': 'g' } } }, 'a.b.c.d.e.f'), 'g');
});
test('should support return default when options.isValid returns false', () => {
const fixture = { foo: { bar: { baz: 'qux' }, 'a.b.c': 'xyx', yyy: 'zzz' } };
const options = (val) => ({
...{
default: val,
isValid(key) {
return key !== 'bar' && key !== 'a.b.c';
},
},
});
expectToEqual(get(fixture, 'foo.bar.baz', options('fez')), 'fez');
expectToEqual(get(fixture, 'foo.bar.baz', options(true)), true);
expectToEqual(get(fixture, 'foo.bar.baz', options(false)), false);
expectToEqual(get(fixture, 'foo.bar.baz', options(null)), null);
expectToEqual(get(fixture, 'foo.a.b.c', options('fez')), 'fez');
expectToEqual(get(fixture, 'foo.a.b.c', options(true)), true);
expectToEqual(get(fixture, 'foo.a.b.c', options(false)), false);
expectToEqual(get(fixture, 'foo.a.b.c', options(null)), null);
expectToEqual(get(fixture, 'foo.yyy', options('fez')), 'zzz');
});
test('should get a value from an array', () => {
const fixture = {
a: { paths: ['a.txt', 'a.js', 'a.hbs'] },
b: {
paths: {
0: 'b.txt',
1: 'b.js',
2: 'b.hbs',
3: 'b3.hbs',
},
},
};
expectToStrictEqual(get(fixture, 'a.paths.0'), 'a.txt');
expectToStrictEqual(get(fixture, 'a.paths.1'), 'a.js');
expectToStrictEqual(get(fixture, 'a.paths.2'), 'a.hbs');
expectToStrictEqual(get(fixture, 'b.paths.0'), 'b.txt');
expectToStrictEqual(get(fixture, 'b.paths.1'), 'b.js');
expectToStrictEqual(get(fixture, 'b.paths.2'), 'b.hbs');
expectToStrictEqual(get(fixture, 'b.paths.3'), 'b3.hbs');
});
test('should get a value from an object in an array', () => {
expectToStrictEqual(get({ a: { b: [{ c: 'd' }] } }, 'a.b.0.c'), 'd');
expectToStrictEqual(get({ a: { b: [{ c: 'd' }, { e: 'f' }] } }, 'a.b.1.e'), 'f');
expectToStrictEqual(get({ a: { b: [{ c: 'd' }] } }, 'a.b.0.c'), 'd');
expectToStrictEqual(get({ a: { b: [{ c: 'd' }, { e: 'f' }] } }, 'a.b.1.e'), 'f');
});
test('should return `undefined` if the path is not found', () => {
const fixture = { a: { b: {} } };
expectToStrictEqual(get(fixture, 'a.b.c'), undefined);
expectToStrictEqual(get(fixture, 'a.b.c.d'), undefined);
});
test('should get the specified property', () => {
expectDeepStrictEqual(get({ a: 'aaa', b: 'b' }, 'a'), 'aaa');
expectDeepStrictEqual(get({ first: 'Jon', last: 'Schlinkert' }, 'first'), 'Jon');
expectDeepStrictEqual(get({ locals: { a: 'a' }, options: { b: 'b' } }, 'locals'), { a: 'a' });
});
test('should support passing a property formatted as an array', () => {
expectDeepStrictEqual(get({ a: 'aaa', b: 'b' }, ['a']), 'aaa');
expectDeepStrictEqual(get({ a: { b: { c: 'd' } } }, ['a', 'b', 'c']), 'd');
expectDeepStrictEqual(get({ first: 'Harry', last: 'Potter' }, ['first']), 'Harry');
expectDeepStrictEqual(get({ locals: { a: 'a' }, options: { b: 'b' } }, ['locals']), { a: 'a' });
});
test('should support escaped dots', () => {
expectDeepStrictEqual(get({ 'a.b': 'a', b: { c: 'd' } }, 'a\\.b'), 'a');
expectDeepStrictEqual(get({ 'a.b': { b: { c: 'd' } } }, 'a\\.b.b.c'), 'd');
});
test('should get the value of a deeply nested property', () => {
expectToStrictEqual(get({ a: { b: 'c', c: { d: 'e', e: 'f', g: { h: 'i' } } } }, 'a.c.g.h'), 'i');
});
/**
* These tests are from the "dot-prop" library
*/
describe('dot-prop tests:', () => {
test('should pass dot-prop tests', () => {
const f1 = { foo: { bar: 1 } };
f1[''] = 'foo';
expectDeepStrictEqual(get(f1, ''), 'foo');
expectDeepStrictEqual(get(f1, 'foo'), f1.foo);
expectDeepStrictEqual(get({ foo: 1 }, 'foo'), 1);
expectDeepStrictEqual(get({ foo: null }, 'foo'), null);
expectDeepStrictEqual(get({ foo: undefined }, 'foo'), undefined);
expectDeepStrictEqual(get({ foo: { bar: true } }, 'foo.bar'), true);
expectDeepStrictEqual(get({ foo: { bar: { baz: true } } }, 'foo.bar.baz'), true);
expectDeepStrictEqual(get({ foo: { bar: { baz: null } } }, 'foo.bar.baz'), null);
expectDeepStrictEqual(get({ '\\': true }, '\\'), true);
expectDeepStrictEqual(get({ '\\foo': true }, '\\foo'), true);
expectDeepStrictEqual(get({ 'bar\\': true }, 'bar\\'), true);
expectDeepStrictEqual(get({ 'foo\\bar': true }, 'foo\\bar'), true);
expectDeepStrictEqual(get({ '\\.foo': true }, '\\\\.foo'), true);
expectDeepStrictEqual(get({ 'bar\\.': true }, 'bar\\\\.'), true);
expectDeepStrictEqual(get({ 'foo\\.bar': true }, 'foo\\\\.bar'), true);
expectDeepStrictEqual(get({ foo: 1 }, 'foo.bar'), undefined);
function fn() {}
fn.foo = { bar: 1 };
expectDeepStrictEqual(get(fn, 'foo'), fn.foo);
expectDeepStrictEqual(get(fn, 'foo.bar'), 1);
const f3 = { foo: null };
expectDeepStrictEqual(get(f3, 'foo.bar'), undefined);
expectDeepStrictEqual(get(f3, 'foo.bar', { default: 'some value' }), 'some value');
expectDeepStrictEqual(get({ 'foo.baz': { bar: true } }, 'foo\\.baz.bar'), true);
expectDeepStrictEqual(get({ 'fo.ob.az': { bar: true } }, 'fo\\.ob\\.az.bar'), true);
expectDeepStrictEqual(get(null, 'foo.bar', { default: false }), false);
expectDeepStrictEqual(get('foo', 'foo.bar', { default: false }), false);
expectDeepStrictEqual(get([], 'foo.bar', { default: false }), false);
expectDeepStrictEqual(get(undefined, 'foo.bar', { default: false }), false);
});
test('should use a custom options.isValid function', () => {
const isEnumerable = Object.prototype.propertyIsEnumerable;
const options = {
isValid: (key, obj) => isEnumerable.call(obj, key),
};
const target = {};
Object.defineProperty(target, 'foo', {
value: 'bar',
enumerable: false,
});
expectDeepStrictEqual(get(target, 'foo', options), undefined);
expectDeepStrictEqual(get({}, 'hasOwnProperty', options), undefined);
});
test('should return a default value', () => {
expectDeepStrictEqual(get({ foo: { bar: 'a' } }, 'foo.fake'), undefined);
expectDeepStrictEqual(get({ foo: { bar: 'a' } }, 'foo.fake.fake2'), undefined);
expectDeepStrictEqual(get({ foo: { bar: 'a' } }, 'foo.fake.fake2', 'some value'), 'some value');
});
test('should pass all of the dot-prop tests', () => {
const f1 = { foo: { bar: 1 } };
expectDeepStrictEqual(get(f1), undefined);
expectDeepStrictEqual(get(f1, 'foo'), f1.foo);
expectDeepStrictEqual(get({ foo: 1 }, 'foo'), 1);
expectDeepStrictEqual(get({ foo: null }, 'foo'), null);
expectDeepStrictEqual(get({ foo: undefined }, 'foo'), undefined);
expectDeepStrictEqual(get({ foo: { bar: true } }, 'foo.bar'), true);
expectDeepStrictEqual(get({ foo: { bar: { baz: true } } }, 'foo.bar.baz'), true);
expectDeepStrictEqual(get({ foo: { bar: { baz: null } } }, 'foo.bar.baz'), null);
expectDeepStrictEqual(get({ foo: { bar: 'a' } }, 'foo.fake.fake2'), undefined);
});
});
/**
* These tests are from the "object-path" library
*/
describe('object-path .get tests', () => {
function getTestObj() {
return {
a: 'b',
b: {
c: [],
d: ['a', 'b'],
e: [{}, { f: 'g' }],
f: 'i',
},
};
}
test('should return the value using unicode key', () => {
const obj = { '15\u00f8C': { '3\u0111': 1 } };
expectToEqual(get(obj, '15\u00f8C.3\u0111'), 1);
expectToEqual(get(obj, ['15\u00f8C', '3\u0111']), 1);
});
test('should return the value using dot in key (with array of segments)', () => {
const obj = { 'a.b': { 'looks.like': 1 } };
expectToEqual(get(obj, ['a.b', 'looks.like']), 1);
});
test('should return the value of an empty array', () => {
const obj = { a: { b: [] } };
expectToEqual(get(obj, 'a.b'), []);
});
// object-path fails this test
test('should return the value using dot in key', () => {
const obj = { 'a.b': { 'looks.like': 1 } };
expectToEqual(get(obj, 'a.b.looks.like'), 1);
});
test('should return the value under shallow object', () => {
const obj = getTestObj();
expectToEqual(get(obj, 'a'), 'b');
expectToEqual(get(obj, ['a']), 'b');
});
test('should work with number path', () => {
const obj = getTestObj();
expectToEqual(get(obj.b.d, 0), 'a');
expectToEqual(get(obj.b, 0), undefined);
});
test('should return the value under deep object', () => {
const obj = getTestObj();
expectToEqual(get(obj, 'b.f'), 'i');
expectToEqual(get(obj, ['b', 'f']), 'i');
});
test('should return the value under array', () => {
const obj = getTestObj();
expectToEqual(get(obj, 'b.d.0'), 'a');
expectToEqual(get(obj, ['b', 'd', 0]), 'a');
});
test('should return the value under array deep', () => {
const obj = getTestObj();
expectToEqual(get(obj, 'b.e.1.f'), 'g');
expectToEqual(get(obj, ['b', 'e', 1, 'f']), 'g');
});
test('should return undefined for missing values under object', () => {
const obj = getTestObj();
expectToEqual(get(obj, 'a.b'), undefined);
expectToEqual(get(obj, ['a', 'b']), undefined);
});
test('should return undefined for missing values under array', () => {
const obj = getTestObj();
expectToEqual(get(obj, 'b.d.5'), undefined);
expectToEqual(get(obj, ['b', 'd', '5']), undefined);
});
test('should return the value under integer-like key', () => {
const obj = { '1a': 'foo' };
expectToEqual(get(obj, '1a'), 'foo');
expectToEqual(get(obj, ['1a']), 'foo');
});
test('should return the default value when the key doesnt exist', () => {
const obj = { '1a': 'foo' };
expectToEqual(get(obj, '1b', null), null);
expectToEqual(get(obj, ['1b'], null), null);
});
// this test differs from behavior in object-path. I was unable to figure
// out exactly how the default values work in object-path.
test('should return the default value when path is empty', () => {
const obj = { '1a': 'foo' };
expectDeepStrictEqual(get(obj, '', null), null);
expectDeepStrictEqual(get(obj, []), undefined);
expectToEqual(get({}, ['1'], 'foo'), 'foo');
});
test('should return the default value when object is null or undefined', () => {
expectDeepStrictEqual(get(null, 'test', { default: 'a' }), 'a');
expectDeepStrictEqual(get(undefined, 'test', { default: 'a' }), 'a');
});
test('should not fail on an object with a null prototype', () => {
const foo = 'FOO';
const objWithNullProto = Object.create(null);
objWithNullProto.foo = foo;
expectToEqual(get(objWithNullProto, 'foo'), foo);
});
// this differs from object-path, which does not allow
// the user to get non-own properties for some reason.
test('should get non-"own" properties', () => {
const Base = function () {};
Base.prototype = {
one: {
two: true,
},
};
const Extended = function () {
Base.call(this, true);
};
Extended.prototype = Object.create(Base.prototype);
const extended = new Extended();
expectToEqual(get(extended, ['one', 'two']), true);
extended.enabled = true;
expectToEqual(get(extended, 'enabled'), true);
expectDeepStrictEqual(get(extended, 'one'), { two: true });
});
});
describe('deep-property unit tests', () => {
test('should handle invalid input', () => {
const a = undefined;
const b = {};
expectToEqual(get(a, 'sample'), undefined);
expectDeepStrictEqual(get(b, undefined), undefined);
expectDeepStrictEqual(get(b, ''), undefined);
expectDeepStrictEqual(get(b, '...'), undefined);
});
test('should get shallow properties', () => {
const fn = function () {};
const a = {
sample: 'string',
example: fn,
unknown: undefined,
};
expectToEqual(get(a, 'example'), fn);
expectToEqual(get(a, 'sample'), 'string');
expectToEqual(get(a, 'unknown'), undefined);
expectToEqual(get(a, 'invalid'), undefined);
});
test('should get deep properties', () => {
const a = {
b: { example: { type: 'vegetable' } },
c: { example: { type: 'mineral' } },
};
expectToEqual(get(a, 'b.example.type'), 'vegetable');
expectToEqual(get(a, 'c.example.type'), 'mineral');
expectToEqual(get(a, 'c.gorky.type'), undefined);
});
// test('should get properties of Error', () => {
// const e = new Error(403);
// console.log(e.message);
// expect(get(e, 'code')).toBe(403);
// });
test('should get properties on non-objects', () => {
const fn = function () {};
// the commented out lines are from from the "deep-property" lib,
// but it's invalid javascript. This is a good example of why it's always
// better to use "use strict" (and lint your code).
// const str = 'An example string';
// const num = 42;
fn.path = { to: { property: 'string' } };
// str.path = { to: { property: 'string' } };
// num.path = { to: { property: 'string' } };
expectToEqual(get(fn, 'path.to.property'), 'string');
// expectToEqual(get(str, 'path.to.property'), undefined);
// expectToEqual(get(num, 'path.to.property'), undefined);
});
});

View File

@ -2710,6 +2710,30 @@ __metadata:
languageName: unknown
linkType: soft
"@lowdefy/get@workspace:packages/get":
version: 0.0.0-use.local
resolution: "@lowdefy/get@workspace:packages/get"
dependencies:
"@babel/cli": 7.8.4
"@babel/compat-data": 7.9.6
"@babel/core": 7.9.6
"@babel/preset-env": 7.9.6
"@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
prettier: 2.1.2
languageName: unknown
linkType: soft
"@lowdefy/graphql@0.0.0-experimental.0, @lowdefy/graphql@workspace:packages/graphql":
version: 0.0.0-use.local
resolution: "@lowdefy/graphql@workspace:packages/graphql"
@ -2775,7 +2799,7 @@ __metadata:
languageName: unknown
linkType: soft
"@lowdefy/type@workspace:packages/type":
"@lowdefy/type@1.0.1, @lowdefy/type@workspace:packages/type":
version: 0.0.0-use.local
resolution: "@lowdefy/type@workspace:packages/type"
dependencies:
@ -10106,7 +10130,7 @@ fsevents@~2.1.2:
languageName: node
linkType: hard
"jest-diff@npm:^24.9.0":
"jest-diff@npm:24.9.0, jest-diff@npm:^24.9.0":
version: 24.9.0
resolution: "jest-diff@npm:24.9.0"
dependencies: