fix(operators): Prep operator args to handle void instance. Closes #519 , Closes #511

This commit is contained in:
Gervwyk 2021-04-24 10:14:03 +02:00
parent 04ba6f49f0
commit 5980f8799f
8 changed files with 225 additions and 115 deletions

View File

@ -15,64 +15,83 @@
*/
import runInstance from '../runInstance';
import { type } from '@lowdefy/helpers';
const prep = (args) => {
if (type.isNone(args[0])) {
args[0] = [];
}
return args;
};
const meta = {
concat: { validTypes: ['array'] },
concat: { prep, validTypes: ['array'] },
copyWithin: {
namedArgs: ['on', 'target', 'start', 'end'],
prep,
validTypes: ['array', 'object'],
},
every: {
namedArgs: ['on', 'callback'],
prep,
validTypes: ['array', 'object'],
},
fill: {
namedArgs: ['on', 'value', 'start', 'end'],
prep,
validTypes: ['array', 'object'],
},
filter: {
namedArgs: ['on', 'callback'],
prep,
validTypes: ['array', 'object'],
},
find: {
namedArgs: ['on', 'callback'],
prep,
validTypes: ['array', 'object'],
},
findIndex: {
namedArgs: ['on', 'callback'],
prep,
validTypes: ['array', 'object'],
},
flat: { namedArgs: ['on', 'depth'], validTypes: ['array', 'object'] },
includes: { namedArgs: ['on', 'value'], validTypes: ['array', 'object'] },
indexOf: { namedArgs: ['on', 'value'], validTypes: ['array', 'object'] },
join: { namedArgs: ['on', 'separator'], validTypes: ['array', 'object'] },
lastIndexOf: { namedArgs: ['on', 'value'], validTypes: ['array', 'object'] },
flat: { namedArgs: ['on', 'depth'], prep, validTypes: ['array', 'object'] },
includes: { namedArgs: ['on', 'value'], prep, validTypes: ['array', 'object'] },
indexOf: { namedArgs: ['on', 'value'], prep, validTypes: ['array', 'object'] },
join: { namedArgs: ['on', 'separator'], prep, validTypes: ['array', 'object'] },
lastIndexOf: { namedArgs: ['on', 'value'], prep, validTypes: ['array', 'object'] },
map: {
namedArgs: ['on', 'callback'],
prep,
validTypes: ['array', 'object'],
},
reduce: {
namedArgs: ['on', 'callback', 'initialValue'],
prep,
validTypes: ['array', 'object'],
},
reduceRight: {
namedArgs: ['on', 'callback', 'initialValue'],
prep,
validTypes: ['array', 'object'],
},
reverse: { validTypes: ['array'], singleArg: true },
slice: { namedArgs: ['on', 'start', 'end'], validTypes: ['array', 'object'] },
reverse: { prep, validTypes: ['array'], singleArg: true },
slice: { namedArgs: ['on', 'start', 'end'], prep, validTypes: ['array', 'object'] },
some: {
namedArgs: ['on', 'callback'],
prep,
validTypes: ['array', 'object'],
},
sort: { namedArgs: ['on'], validTypes: ['array'] },
sort: { namedArgs: ['on'], prep, validTypes: ['array'] },
splice: {
namedArgs: ['on', 'start', 'deleteCount'],
spreadArgs: 'insert',
returnInstance: true,
prep,
validTypes: ['array', 'object'],
},
length: { validTypes: ['array'], property: true },
length: { validTypes: ['array'], prep, property: true },
// some,
// forEach,
// pop: { namedArgs: ['on'] },

View File

@ -16,16 +16,43 @@
import runInstance from '../runInstance';
import runClass from '../runClass';
import { type } from '@lowdefy/helpers';
const prep = (args) => {
if (type.isNone(args[0])) {
args[0] = {};
}
return args;
};
const prepDescriptor = (args) => {
const descriptor = args[2] || {};
if (type.isNone(descriptor.enumerable)) {
descriptor.enumerable = true;
}
if (type.isNone(descriptor.configurable)) {
descriptor.configurable = true;
}
args[2] = descriptor;
if (type.isNone(args[0])) {
args[0] = {};
}
return args;
};
const metaInstance = {
hasOwnProperty: { namedArgs: ['on', 'prop'], validTypes: ['array', 'object'] },
hasOwnProperty: { namedArgs: ['on', 'prop'], validTypes: ['array', 'object'], prep },
};
const metaClass = {
keys: { singleArg: true, validTypes: ['object'] },
values: { singleArg: true, validTypes: ['object'] },
assign: { spreadArgs: true, validTypes: ['array'] },
defineProperty: { namedArgs: ['on', 'key', 'descriptor'], validTypes: ['array', 'object'] },
defineProperty: {
namedArgs: ['on', 'key', 'descriptor'],
validTypes: ['array', 'object'],
prep: prepDescriptor,
},
};
function _object({ params, location, methodName }) {

View File

@ -17,44 +17,83 @@
import runInstance from '../runInstance';
import { type } from '@lowdefy/helpers';
const prepRegex = (regexIndex, flagsIndex) => (args) => {
if (args[regexIndex]) {
args[regexIndex] = new RegExp(args[regexIndex], args[flagsIndex]);
}
if (type.isNone(args[0])) {
args[0] = '';
}
return args;
};
const prep = (args) => {
if (type.isNone(args[0])) {
args[0] = '';
}
return args;
};
const meta = {
charAt: { namedArgs: ['on', 'index'], validTypes: ['array', 'object'] },
charAt: { namedArgs: ['on', 'index'], prep, validTypes: ['array', 'object'] },
// 'charCodeAt',
concat: { validTypes: ['array'] },
endsWith: { namedArgs: ['on', 'searchString', 'length'], validTypes: ['array', 'object'] },
includes: { namedArgs: ['on', 'searchString', 'position'], validTypes: ['array', 'object'] },
indexOf: { namedArgs: ['on', 'searchValue', 'fromIndex'], validTypes: ['array', 'object'] },
lastIndexOf: { namedArgs: ['on', 'searchValue', 'fromIndex'], validTypes: ['array', 'object'] },
// 'localeCompare',
match: { namedArgs: ['on', 'regex', 'regexFlags'], validTypes: ['array', 'object'] },
// 'matchAll',
normalize: { namedArgs: ['on', 'form'], validTypes: ['array', 'object'] },
padEnd: { namedArgs: ['on', 'targetLength', 'padString'], validTypes: ['array', 'object'] },
padStart: { namedArgs: ['on', 'targetLength', 'padString'], validTypes: ['array', 'object'] },
repeat: { namedArgs: ['on', 'count'], validTypes: ['array', 'object'] },
replace: {
namedArgs: ['on', 'regex', 'newSubstr', 'regexFlags'],
concat: { prep, validTypes: ['array'] },
endsWith: { namedArgs: ['on', 'searchString', 'length'], prep, validTypes: ['array', 'object'] },
includes: {
namedArgs: ['on', 'searchString', 'position'],
prep,
validTypes: ['array', 'object'],
},
search: { namedArgs: ['on', 'regex', 'regexFlags'], validTypes: ['array', 'object'] },
slice: { namedArgs: ['on', 'start', 'end'], validTypes: ['array', 'object'] },
split: { namedArgs: ['on', 'separator'], validTypes: ['array', 'object'] },
startsWith: { namedArgs: ['on', 'searchString', 'position'], validTypes: ['array', 'object'] },
substring: { namedArgs: ['on', 'start', 'end'], validTypes: ['array', 'object'] },
indexOf: { namedArgs: ['on', 'searchValue', 'fromIndex'], prep, validTypes: ['array', 'object'] },
lastIndexOf: {
namedArgs: ['on', 'searchValue', 'fromIndex'],
prep,
validTypes: ['array', 'object'],
},
// 'localeCompare',
match: {
namedArgs: ['on', 'regex', 'regexFlags'],
prep: prepRegex(1, 2),
validTypes: ['array', 'object'],
},
// 'matchAll',
normalize: { namedArgs: ['on', 'form'], prep, validTypes: ['array', 'object'] },
padEnd: { namedArgs: ['on', 'targetLength', 'padString'], prep, validTypes: ['array', 'object'] },
padStart: {
namedArgs: ['on', 'targetLength', 'padString'],
prep,
validTypes: ['array', 'object'],
},
repeat: { namedArgs: ['on', 'count'], prep, validTypes: ['array', 'object'] },
replace: {
namedArgs: ['on', 'regex', 'newSubstr', 'regexFlags'],
prep: prepRegex(1, 3),
validTypes: ['array', 'object'],
},
search: {
namedArgs: ['on', 'regex', 'regexFlags'],
prep: prepRegex(1, 2),
validTypes: ['array', 'object'],
},
slice: { namedArgs: ['on', 'start', 'end'], prep, validTypes: ['array', 'object'] },
split: { namedArgs: ['on', 'separator'], prep, validTypes: ['array', 'object'] },
startsWith: {
namedArgs: ['on', 'searchString', 'position'],
prep,
validTypes: ['array', 'object'],
},
substring: { namedArgs: ['on', 'start', 'end'], prep, validTypes: ['array', 'object'] },
// toLocaleLowerCase: { namedArgs: ['on', 'locale'], validTypes: ['array', 'object'] },
// toLocaleUpperCase: { namedArgs: ['on', 'locale'], validTypes: ['array', 'object'] },
toLowerCase: { validTypes: ['string'], singleArg: true },
toUpperCase: { validTypes: ['string'], singleArg: true },
trim: { validTypes: ['string'], singleArg: true },
trimEnd: { validTypes: ['string'], singleArg: true },
trimStart: { validTypes: ['string'], singleArg: true },
length: { validTypes: ['string'], property: true },
toLowerCase: { validTypes: ['string'], singleArg: true, prep },
toUpperCase: { validTypes: ['string'], singleArg: true, prep },
trim: { validTypes: ['string'], singleArg: true, prep },
trimEnd: { validTypes: ['string'], singleArg: true, prep },
trimStart: { validTypes: ['string'], singleArg: true, prep },
length: { validTypes: ['string'], property: true, prep },
};
function _string({ params, location, methodName }) {
if (type.isObject(params) && params.regex) {
params.regex = new RegExp(params.regex, params.regexFlags);
}
return runInstance({
location,
meta,

View File

@ -84,6 +84,10 @@ const runClass = ({ location, meta, methodName, operator, params, functions, def
}
}
if (type.isFunction(meta[methodName].prep)) {
args = meta[methodName].prep(args);
}
// for property
if (meta[methodName].property) {
return functions[methodName];

View File

@ -34,6 +34,7 @@ const runInstance = ({ location, meta, methodName, operator, params, instanceTyp
Received: {"${operator}.${methodName}":${JSON.stringify(params)}} at ${location}.`
);
}
let instance;
let args = [];
if (meta[methodName].singleArg || meta[methodName].property) {
@ -62,9 +63,12 @@ const runInstance = ({ location, meta, methodName, operator, params, instanceTyp
args.push(...(params[meta[methodName].spreadArgs] || []));
}
}
// console.log(instance);
// console.log(type.typeOf(instance));
if (!instance || type.typeOf(instance) !== instanceType) {
if (type.isFunction(meta[methodName].prep)) {
[instance, ...args] = meta[methodName].prep([instance, ...args]);
}
if (type.typeOf(instance) !== instanceType) {
throw new Error(`Operator Error: ${operator}.${methodName} must be evaluated on an ${instanceType} instance. For named args provide an ${instanceType} instance to the "on" property, for listed args provide and ${instanceType} instance as the first element in the operator argument array.
Received: {"${operator}.${methodName}":${JSON.stringify(params)}} at ${location}.`);
}

View File

@ -38,6 +38,13 @@ describe('_array.concat', () => {
location,
})
).toEqual([1, 2, 3, 4, 5, 6]);
expect(
array({
params: [null, null, null],
methodName,
location,
})
).toEqual([null, null]);
expect(
array({
params: [
@ -101,18 +108,15 @@ describe('_array.copyWithin', () => {
location,
})
).toEqual(['c', 'b', 'c', 'd', 'e']);
});
test('throw', () => {
expect(() =>
expect(
array({
params: { target: 0 },
methodName,
location,
})
).toThrowErrorMatchingInlineSnapshot(`
"Operator Error: _array.copyWithin must be evaluated on an array instance. For named args provide an array instance to the \\"on\\" property, for listed args provide and array instance as the first element in the operator argument array.
Received: {\\"_array.copyWithin\\":{\\"target\\":0}} at locationId."
`);
).toEqual([]);
});
test('throw', () => {
expect(() =>
array({
params: { on: 1 },
@ -225,18 +229,15 @@ describe('_array.fill', () => {
location,
})
).toEqual([6, 6, 6, 6]);
});
test('throw', () => {
expect(() =>
expect(
array({
params: { value: 'x', start: 1, end: 2 },
methodName,
location,
})
).toThrowErrorMatchingInlineSnapshot(`
"Operator Error: _array.fill must be evaluated on an array instance. For named args provide an array instance to the \\"on\\" property, for listed args provide and array instance as the first element in the operator argument array.
Received: {\\"_array.fill\\":{\\"value\\":\\"x\\",\\"start\\":1,\\"end\\":2}} at locationId."
`);
).toEqual([]);
});
test('throw', () => {
expect(() =>
array({
params: 'x',
@ -442,18 +443,15 @@ describe('_array.flat', () => {
location,
})
).toEqual(['b', 'c', 'a', 'c', ['v']]);
});
test('throw', () => {
expect(() =>
expect(
array({
params: { depth: 1 },
methodName,
location,
})
).toThrowErrorMatchingInlineSnapshot(`
"Operator Error: _array.flat must be evaluated on an array instance. For named args provide an array instance to the \\"on\\" property, for listed args provide and array instance as the first element in the operator argument array.
Received: {\\"_array.flat\\":{\\"depth\\":1}} at locationId."
`);
).toEqual([]);
});
test('throw', () => {
expect(() =>
array({
params: [1, 2, 3],
@ -501,18 +499,15 @@ describe('_array.includes', () => {
location,
})
).toEqual(false);
});
test('throw', () => {
expect(() =>
expect(
array({
params: { value: 1 },
methodName,
location,
})
).toThrowErrorMatchingInlineSnapshot(`
"Operator Error: _array.includes must be evaluated on an array instance. For named args provide an array instance to the \\"on\\" property, for listed args provide and array instance as the first element in the operator argument array.
Received: {\\"_array.includes\\":{\\"value\\":1}} at locationId."
`);
).toEqual(false);
});
test('throw', () => {
expect(() =>
array({
params: [1, 2, 3],
@ -560,18 +555,15 @@ describe('_array.indexOf', () => {
location,
})
).toEqual(-1);
});
test('throw', () => {
expect(() =>
expect(
array({
params: { value: 1 },
methodName,
location,
})
).toThrowErrorMatchingInlineSnapshot(`
"Operator Error: _array.indexOf must be evaluated on an array instance. For named args provide an array instance to the \\"on\\" property, for listed args provide and array instance as the first element in the operator argument array.
Received: {\\"_array.indexOf\\":{\\"value\\":1}} at locationId."
`);
).toEqual(-1);
});
test('throw', () => {
expect(() =>
array({
params: [1, 2, 3],
@ -612,18 +604,15 @@ describe('_array.join', () => {
location,
})
).toEqual('a. b. c');
});
test('throw', () => {
expect(() =>
expect(
array({
params: { separator: '.' },
methodName,
location,
})
).toThrowErrorMatchingInlineSnapshot(`
"Operator Error: _array.join must be evaluated on an array instance. For named args provide an array instance to the \\"on\\" property, for listed args provide and array instance as the first element in the operator argument array.
Received: {\\"_array.join\\":{\\"separator\\":\\".\\"}} at locationId."
`);
).toEqual('');
});
test('throw', () => {
expect(() =>
array({
params: [1, 2, 3],
@ -671,18 +660,15 @@ describe('_array.lastIndexOf', () => {
location,
})
).toEqual(-1);
});
test('throw', () => {
expect(() =>
expect(
array({
params: { value: 1 },
methodName,
location,
})
).toThrowErrorMatchingInlineSnapshot(`
"Operator Error: _array.lastIndexOf must be evaluated on an array instance. For named args provide an array instance to the \\"on\\" property, for listed args provide and array instance as the first element in the operator argument array.
Received: {\\"_array.lastIndexOf\\":{\\"value\\":1}} at locationId."
`);
).toEqual(-1);
});
test('throw', () => {
expect(() =>
array({
params: [1, 2, 3],
@ -955,18 +941,15 @@ describe('_array.slice', () => {
location,
})
).toEqual(['c', 'a']);
});
test('throw', () => {
expect(() =>
expect(
array({
params: { start: 1 },
methodName,
location,
})
).toThrowErrorMatchingInlineSnapshot(`
"Operator Error: _array.slice must be evaluated on an array instance. For named args provide an array instance to the \\"on\\" property, for listed args provide and array instance as the first element in the operator argument array.
Received: {\\"_array.slice\\":{\\"start\\":1}} at locationId."
`);
).toEqual([]);
});
test('throw', () => {
expect(() =>
array({
params: 1,

View File

@ -28,6 +28,13 @@ describe('_object.hasOwnProperty', () => {
location,
})
).toEqual(true);
expect(
object({
params: [null, 'a'],
methodName,
location,
})
).toEqual(false);
expect(
object({
params: { on: { a: 1 }, prop: 'a' },
@ -42,18 +49,15 @@ describe('_object.hasOwnProperty', () => {
location,
})
).toEqual(false);
});
test('throw', () => {
expect(() =>
expect(
object({
params: { value: 'x', start: 1, end: 2 },
methodName,
location,
})
).toThrowErrorMatchingInlineSnapshot(`
"Operator Error: _object.hasOwnProperty must be evaluated on an object instance. For named args provide an object instance to the \\"on\\" property, for listed args provide and object instance as the first element in the operator argument array.
Received: {\\"_object.hasOwnProperty\\":{\\"value\\":\\"x\\",\\"start\\":1,\\"end\\":2}} at locationId."
`);
).toEqual(false);
});
test('throw', () => {
expect(() =>
object({
params: [1, { a: 1 }],
@ -244,25 +248,32 @@ describe('_object.defineProperty', () => {
methodName,
location,
});
expect(obj.new).toEqual(6);
expect(obj).toEqual({ a: 1, new: 6 });
expect(Object.keys(obj)).toEqual(['a', 'new']);
obj = { a: 2 };
object({
params: [obj, 'newer', { value: 6 }],
methodName,
location,
});
expect(obj.newer).toEqual(6);
});
test('throw', () => {
expect(() =>
expect(obj).toEqual({ a: 2, newer: 6 });
obj = { a: 3 };
object({
params: [obj, 'x', { value: 6, enumerable: false, configurable: false }],
methodName,
location,
});
expect(obj).toEqual({ a: 3 });
expect(obj.x).toEqual(6);
expect(
object({
params: [],
methodName,
location,
})
).toThrowErrorMatchingInlineSnapshot(
`"Operator Error: _object.defineProperty - Object.defineProperty called on non-object Received: {\\"_object.defineProperty\\":[]} at locationId."`
);
).toEqual({});
});
test('throw', () => {
expect(() =>
object({
params: 'x',

View File

@ -93,6 +93,20 @@ describe('_string.concat', () => {
location,
})
).toEqual('abcdef12');
expect(
string({
params: [null, 12, 'abc'],
methodName,
location,
})
).toEqual('12abc');
expect(
string({
params: ['', 12, 'abc'],
methodName,
location,
})
).toEqual('12abc');
expect(
string({
params: ['abcdef', true],
@ -418,6 +432,15 @@ describe('_string.match', () => {
})
)
).toEqual(JSON.stringify(['e']));
expect(
JSON.stringify(
string({
params: [null, 'e'],
methodName,
location,
})
)
).toEqual(JSON.stringify(null));
expect(
string({
params: ['abcdefe', '/e/g'],