mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
Co-authored-by: Charlie Kolb <charlie@n8n.io> Co-authored-by: Milorad FIlipović <milorad@n8n.io> Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
@@ -1017,9 +1017,23 @@ export interface ILoadOptionsFunctions extends FunctionsBase {
|
||||
options?: IGetNodeParameterOptions,
|
||||
): NodeParameterValueType | object | undefined;
|
||||
getCurrentNodeParameters(): INodeParameters | undefined;
|
||||
|
||||
helpers: RequestHelperFunctions & SSHTunnelFunctions;
|
||||
}
|
||||
|
||||
export type FieldValueOption = { name: string; type: FieldType | 'any' };
|
||||
|
||||
export type IWorkflowNodeContext = ExecuteFunctions.GetNodeParameterFn &
|
||||
Pick<FunctionsBase, 'getNode'>;
|
||||
|
||||
export interface ILocalLoadOptionsFunctions {
|
||||
getWorkflowNodeContext(nodeType: string): Promise<IWorkflowNodeContext | null>;
|
||||
}
|
||||
|
||||
export interface IWorkflowLoader {
|
||||
get(workflowId: string): Promise<IWorkflowBase>;
|
||||
}
|
||||
|
||||
export interface IPollFunctions
|
||||
extends FunctionsBaseWithRequiredKeys<'getMode' | 'getActivationMode'> {
|
||||
__emit(
|
||||
@@ -1293,14 +1307,18 @@ export interface INodePropertyTypeOptions {
|
||||
resourceMapper?: ResourceMapperTypeOptions;
|
||||
filter?: FilterTypeOptions;
|
||||
assignment?: AssignmentTypeOptions;
|
||||
minRequiredFields?: number; // Supported by: fixedCollection
|
||||
maxAllowedFields?: number; // Supported by: fixedCollection
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface ResourceMapperTypeOptions {
|
||||
resourceMapperMethod: string;
|
||||
mode: 'add' | 'update' | 'upsert';
|
||||
export interface ResourceMapperTypeOptionsBase {
|
||||
mode: 'add' | 'update' | 'upsert' | 'map';
|
||||
valuesLabel?: string;
|
||||
fieldWords?: { singular: string; plural: string };
|
||||
fieldWords?: {
|
||||
singular: string;
|
||||
plural: string;
|
||||
};
|
||||
addAllFields?: boolean;
|
||||
noFieldsError?: string;
|
||||
multiKeyMatch?: boolean;
|
||||
@@ -1310,8 +1328,23 @@ export interface ResourceMapperTypeOptions {
|
||||
description?: string;
|
||||
hint?: string;
|
||||
};
|
||||
showTypeConversionOptions?: boolean;
|
||||
}
|
||||
|
||||
// Enforce at least one of resourceMapperMethod or localResourceMapperMethod
|
||||
export type ResourceMapperTypeOptionsLocal = {
|
||||
resourceMapperMethod: string;
|
||||
localResourceMapperMethod?: never; // Explicitly disallows this property
|
||||
};
|
||||
|
||||
export type ResourceMapperTypeOptionsExternal = {
|
||||
localResourceMapperMethod: string;
|
||||
resourceMapperMethod?: never; // Explicitly disallows this property
|
||||
};
|
||||
|
||||
export type ResourceMapperTypeOptions = ResourceMapperTypeOptionsBase &
|
||||
(ResourceMapperTypeOptionsLocal | ResourceMapperTypeOptionsExternal);
|
||||
|
||||
type NonEmptyArray<T> = [T, ...T[]];
|
||||
|
||||
export type FilterTypeCombinator = 'and' | 'or';
|
||||
@@ -1583,6 +1616,9 @@ export interface INodeType {
|
||||
resourceMapping?: {
|
||||
[functionName: string]: (this: ILoadOptionsFunctions) => Promise<ResourceMapperFields>;
|
||||
};
|
||||
localResourceMapping?: {
|
||||
[functionName: string]: (this: ILocalLoadOptionsFunctions) => Promise<ResourceMapperFields>;
|
||||
};
|
||||
actionHandler?: {
|
||||
[functionName: string]: (
|
||||
this: ILoadOptionsFunctions,
|
||||
@@ -2651,6 +2687,9 @@ export type ResourceMapperValue = {
|
||||
value: { [key: string]: string | number | boolean | null } | null;
|
||||
matchingColumns: string[];
|
||||
schema: ResourceMapperField[];
|
||||
ignoreTypeMismatchErrors: boolean;
|
||||
attemptToConvertTypes: boolean;
|
||||
convertFieldsToString: boolean;
|
||||
};
|
||||
|
||||
export type FilterOperatorType =
|
||||
|
||||
@@ -1568,7 +1568,7 @@ export function getParameterIssues(
|
||||
data: option as INodeProperties,
|
||||
});
|
||||
}
|
||||
} else if (nodeProperties.type === 'fixedCollection') {
|
||||
} else if (nodeProperties.type === 'fixedCollection' && isDisplayed) {
|
||||
basePath = basePath ? `${basePath}.` : `${nodeProperties.name}.`;
|
||||
|
||||
let propertyOptions: INodePropertyCollection;
|
||||
@@ -1579,6 +1579,24 @@ export function getParameterIssues(
|
||||
propertyOptions.name,
|
||||
basePath.slice(0, -1),
|
||||
);
|
||||
|
||||
// Validate allowed field counts
|
||||
const valueArray = Array.isArray(value) ? value : [];
|
||||
const { minRequiredFields, maxAllowedFields } = nodeProperties.typeOptions ?? {};
|
||||
let error = '';
|
||||
|
||||
if (minRequiredFields && valueArray.length < minRequiredFields) {
|
||||
error = `At least ${minRequiredFields} ${minRequiredFields === 1 ? 'field is' : 'fields are'} required.`;
|
||||
}
|
||||
if (maxAllowedFields && valueArray.length > maxAllowedFields) {
|
||||
error = `At most ${maxAllowedFields} ${maxAllowedFields === 1 ? 'field is' : 'fields are'} allowed.`;
|
||||
}
|
||||
if (error) {
|
||||
foundIssues.parameters ??= {};
|
||||
foundIssues.parameters[nodeProperties.name] ??= [];
|
||||
foundIssues.parameters[nodeProperties.name].push(error);
|
||||
}
|
||||
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -974,7 +974,12 @@ export class WorkflowDataProxy {
|
||||
type: 'no_execution_data',
|
||||
});
|
||||
}
|
||||
return placeholdersDataInputData?.[name] ?? defaultValue;
|
||||
return (
|
||||
// TS does not know that the key exists, we need to address this in refactor
|
||||
(placeholdersDataInputData?.query as Record<string, unknown>)?.[name] ??
|
||||
placeholdersDataInputData?.[name] ??
|
||||
defaultValue
|
||||
);
|
||||
};
|
||||
|
||||
const base = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
NodeConnectionType,
|
||||
type INodeIssues,
|
||||
type INode,
|
||||
type INodeParameters,
|
||||
type INodeProperties,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
getNodeHints,
|
||||
isSubNodeType,
|
||||
applyDeclarativeNodeOptionParameters,
|
||||
getParameterIssues,
|
||||
} from '@/NodeHelpers';
|
||||
import type { Workflow } from '@/Workflow';
|
||||
|
||||
@@ -3607,4 +3609,590 @@ describe('NodeHelpers', () => {
|
||||
expect(nodeType.description.properties).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getParameterIssues', () => {
|
||||
const tests: Array<{
|
||||
description: string;
|
||||
input: {
|
||||
nodeProperties: INodeProperties;
|
||||
nodeValues: INodeParameters;
|
||||
path: string;
|
||||
node: INode;
|
||||
};
|
||||
output: INodeIssues;
|
||||
}> = [
|
||||
{
|
||||
description:
|
||||
'Fixed collection::Should not return issues if minimum or maximum field count is not set',
|
||||
input: {
|
||||
nodeProperties: {
|
||||
displayName: 'Workflow Inputs',
|
||||
name: 'workflowInputs',
|
||||
placeholder: 'Add Field',
|
||||
type: 'fixedCollection',
|
||||
description:
|
||||
'Define expected input fields. If no inputs are provided, all data from the calling workflow will be passed through.',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
sortable: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'@version': [
|
||||
{
|
||||
_cnd: {
|
||||
gte: 1.1,
|
||||
},
|
||||
},
|
||||
],
|
||||
inputSource: ['workflowInputs'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'values',
|
||||
displayName: 'Values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. fieldName',
|
||||
description: 'Name of the field',
|
||||
noDataExpression: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
description: 'The field value type',
|
||||
options: [
|
||||
{
|
||||
name: 'Allow Any Type',
|
||||
value: 'any',
|
||||
},
|
||||
{
|
||||
name: 'String',
|
||||
value: 'string',
|
||||
},
|
||||
{
|
||||
name: 'Number',
|
||||
value: 'number',
|
||||
},
|
||||
{
|
||||
name: 'Boolean',
|
||||
value: 'boolean',
|
||||
},
|
||||
{
|
||||
name: 'Array',
|
||||
value: 'array',
|
||||
},
|
||||
{
|
||||
name: 'Object',
|
||||
value: 'object',
|
||||
},
|
||||
],
|
||||
default: 'string',
|
||||
noDataExpression: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
nodeValues: {
|
||||
events: 'worklfow_call',
|
||||
inputSource: 'workflowInputs',
|
||||
workflowInputs: {},
|
||||
inputOptions: {},
|
||||
},
|
||||
path: '',
|
||||
node: {
|
||||
parameters: {
|
||||
events: 'worklfow_call',
|
||||
inputSource: 'workflowInputs',
|
||||
workflowInputs: {},
|
||||
inputOptions: {},
|
||||
},
|
||||
type: 'n8n-nodes-base.executeWorkflowTrigger',
|
||||
typeVersion: 1.1,
|
||||
position: [-140, -20],
|
||||
id: '9abdbdac-5f32-4876-b4d5-895d8ca4cb00',
|
||||
name: 'Test Node',
|
||||
} as INode,
|
||||
},
|
||||
output: {},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Fixed collection::Should not return issues if field count is within the specified range',
|
||||
input: {
|
||||
nodeProperties: {
|
||||
displayName: 'Workflow Inputs',
|
||||
name: 'workflowInputs',
|
||||
placeholder: 'Add Field',
|
||||
type: 'fixedCollection',
|
||||
description:
|
||||
'Define expected input fields. If no inputs are provided, all data from the calling workflow will be passed through.',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
sortable: true,
|
||||
minRequiredFields: 1,
|
||||
maxAllowedFields: 3,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'@version': [
|
||||
{
|
||||
_cnd: {
|
||||
gte: 1.1,
|
||||
},
|
||||
},
|
||||
],
|
||||
inputSource: ['workflowInputs'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'values',
|
||||
displayName: 'Values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. fieldName',
|
||||
description: 'Name of the field',
|
||||
noDataExpression: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
description: 'The field value type',
|
||||
options: [
|
||||
{
|
||||
name: 'Allow Any Type',
|
||||
value: 'any',
|
||||
},
|
||||
{
|
||||
name: 'String',
|
||||
value: 'string',
|
||||
},
|
||||
{
|
||||
name: 'Number',
|
||||
value: 'number',
|
||||
},
|
||||
{
|
||||
name: 'Boolean',
|
||||
value: 'boolean',
|
||||
},
|
||||
{
|
||||
name: 'Array',
|
||||
value: 'array',
|
||||
},
|
||||
{
|
||||
name: 'Object',
|
||||
value: 'object',
|
||||
},
|
||||
],
|
||||
default: 'string',
|
||||
noDataExpression: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
nodeValues: {
|
||||
events: 'worklfow_call',
|
||||
inputSource: 'workflowInputs',
|
||||
workflowInputs: {
|
||||
values: [
|
||||
{
|
||||
name: 'field1',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'field2',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
inputOptions: {},
|
||||
},
|
||||
path: '',
|
||||
node: {
|
||||
parameters: {
|
||||
events: 'worklfow_call',
|
||||
inputSource: 'workflowInputs',
|
||||
workflowInputs: {},
|
||||
inputOptions: {},
|
||||
},
|
||||
type: 'n8n-nodes-base.executeWorkflowTrigger',
|
||||
typeVersion: 1.1,
|
||||
position: [-140, -20],
|
||||
id: '9abdbdac-5f32-4876-b4d5-895d8ca4cb00',
|
||||
name: 'Test Node',
|
||||
} as INode,
|
||||
},
|
||||
output: {},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Fixed collection::Should return an issue if field count is lower than minimum specified',
|
||||
input: {
|
||||
nodeProperties: {
|
||||
displayName: 'Workflow Inputs',
|
||||
name: 'workflowInputs',
|
||||
placeholder: 'Add Field',
|
||||
type: 'fixedCollection',
|
||||
description:
|
||||
'Define expected input fields. If no inputs are provided, all data from the calling workflow will be passed through.',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
sortable: true,
|
||||
minRequiredFields: 1,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'@version': [
|
||||
{
|
||||
_cnd: {
|
||||
gte: 1.1,
|
||||
},
|
||||
},
|
||||
],
|
||||
inputSource: ['workflowInputs'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'values',
|
||||
displayName: 'Values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. fieldName',
|
||||
description: 'Name of the field',
|
||||
noDataExpression: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
description: 'The field value type',
|
||||
options: [
|
||||
{
|
||||
name: 'Allow Any Type',
|
||||
value: 'any',
|
||||
},
|
||||
{
|
||||
name: 'String',
|
||||
value: 'string',
|
||||
},
|
||||
{
|
||||
name: 'Number',
|
||||
value: 'number',
|
||||
},
|
||||
{
|
||||
name: 'Boolean',
|
||||
value: 'boolean',
|
||||
},
|
||||
{
|
||||
name: 'Array',
|
||||
value: 'array',
|
||||
},
|
||||
{
|
||||
name: 'Object',
|
||||
value: 'object',
|
||||
},
|
||||
],
|
||||
default: 'string',
|
||||
noDataExpression: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
nodeValues: {
|
||||
events: 'worklfow_call',
|
||||
inputSource: 'workflowInputs',
|
||||
workflowInputs: {},
|
||||
inputOptions: {},
|
||||
},
|
||||
path: '',
|
||||
node: {
|
||||
parameters: {
|
||||
events: 'worklfow_call',
|
||||
inputSource: 'workflowInputs',
|
||||
workflowInputs: {},
|
||||
inputOptions: {},
|
||||
},
|
||||
type: 'n8n-nodes-base.executeWorkflowTrigger',
|
||||
typeVersion: 1.1,
|
||||
position: [-140, -20],
|
||||
id: '9abdbdac-5f32-4876-b4d5-895d8ca4cb00',
|
||||
name: 'Test Node',
|
||||
} as INode,
|
||||
},
|
||||
output: {
|
||||
parameters: {
|
||||
workflowInputs: ['At least 1 field is required.'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Fixed collection::Should return an issue if field count is higher than maximum specified',
|
||||
input: {
|
||||
nodeProperties: {
|
||||
displayName: 'Workflow Inputs',
|
||||
name: 'workflowInputs',
|
||||
placeholder: 'Add Field',
|
||||
type: 'fixedCollection',
|
||||
description:
|
||||
'Define expected input fields. If no inputs are provided, all data from the calling workflow will be passed through.',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
sortable: true,
|
||||
maxAllowedFields: 1,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'@version': [
|
||||
{
|
||||
_cnd: {
|
||||
gte: 1.1,
|
||||
},
|
||||
},
|
||||
],
|
||||
inputSource: ['workflowInputs'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'values',
|
||||
displayName: 'Values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. fieldName',
|
||||
description: 'Name of the field',
|
||||
noDataExpression: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
description: 'The field value type',
|
||||
options: [
|
||||
{
|
||||
name: 'Allow Any Type',
|
||||
value: 'any',
|
||||
},
|
||||
{
|
||||
name: 'String',
|
||||
value: 'string',
|
||||
},
|
||||
{
|
||||
name: 'Number',
|
||||
value: 'number',
|
||||
},
|
||||
{
|
||||
name: 'Boolean',
|
||||
value: 'boolean',
|
||||
},
|
||||
{
|
||||
name: 'Array',
|
||||
value: 'array',
|
||||
},
|
||||
{
|
||||
name: 'Object',
|
||||
value: 'object',
|
||||
},
|
||||
],
|
||||
default: 'string',
|
||||
noDataExpression: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
nodeValues: {
|
||||
events: 'worklfow_call',
|
||||
inputSource: 'workflowInputs',
|
||||
workflowInputs: {
|
||||
values: [
|
||||
{
|
||||
name: 'field1',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'field2',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
inputOptions: {},
|
||||
},
|
||||
path: '',
|
||||
node: {
|
||||
parameters: {
|
||||
events: 'worklfow_call',
|
||||
inputSource: 'workflowInputs',
|
||||
workflowInputs: {},
|
||||
inputOptions: {},
|
||||
},
|
||||
type: 'n8n-nodes-base.executeWorkflowTrigger',
|
||||
typeVersion: 1.1,
|
||||
position: [-140, -20],
|
||||
id: '9abdbdac-5f32-4876-b4d5-895d8ca4cb00',
|
||||
name: 'Test Node',
|
||||
} as INode,
|
||||
},
|
||||
output: {
|
||||
parameters: {
|
||||
workflowInputs: ['At most 1 field is allowed.'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'Fixed collection::Should not return issues if the collection is hidden',
|
||||
input: {
|
||||
nodeProperties: {
|
||||
displayName: 'Workflow Inputs',
|
||||
name: 'workflowInputs',
|
||||
placeholder: 'Add Field',
|
||||
type: 'fixedCollection',
|
||||
description:
|
||||
'Define expected input fields. If no inputs are provided, all data from the calling workflow will be passed through.',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
sortable: true,
|
||||
maxAllowedFields: 1,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'@version': [
|
||||
{
|
||||
_cnd: {
|
||||
gte: 1.1,
|
||||
},
|
||||
},
|
||||
],
|
||||
inputSource: ['workflowInputs'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'values',
|
||||
displayName: 'Values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. fieldName',
|
||||
description: 'Name of the field',
|
||||
noDataExpression: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
description: 'The field value type',
|
||||
options: [
|
||||
{
|
||||
name: 'Allow Any Type',
|
||||
value: 'any',
|
||||
},
|
||||
{
|
||||
name: 'String',
|
||||
value: 'string',
|
||||
},
|
||||
{
|
||||
name: 'Number',
|
||||
value: 'number',
|
||||
},
|
||||
{
|
||||
name: 'Boolean',
|
||||
value: 'boolean',
|
||||
},
|
||||
{
|
||||
name: 'Array',
|
||||
value: 'array',
|
||||
},
|
||||
{
|
||||
name: 'Object',
|
||||
value: 'object',
|
||||
},
|
||||
],
|
||||
default: 'string',
|
||||
noDataExpression: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
nodeValues: {
|
||||
events: 'worklfow_call',
|
||||
inputSource: 'somethingElse',
|
||||
workflowInputs: {
|
||||
values: [
|
||||
{
|
||||
name: 'field1',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'field2',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
inputOptions: {},
|
||||
},
|
||||
path: '',
|
||||
node: {
|
||||
parameters: {
|
||||
events: 'worklfow_call',
|
||||
inputSource: 'workflowInputs',
|
||||
workflowInputs: {},
|
||||
inputOptions: {},
|
||||
},
|
||||
type: 'n8n-nodes-base.executeWorkflowTrigger',
|
||||
typeVersion: 1.1,
|
||||
position: [-140, -20],
|
||||
id: '9abdbdac-5f32-4876-b4d5-895d8ca4cb00',
|
||||
name: 'Test Node',
|
||||
} as INode,
|
||||
},
|
||||
output: {},
|
||||
},
|
||||
];
|
||||
|
||||
for (const testData of tests) {
|
||||
test(testData.description, () => {
|
||||
const result = getParameterIssues(
|
||||
testData.input.nodeProperties,
|
||||
testData.input.nodeValues,
|
||||
testData.input.path,
|
||||
testData.input.node,
|
||||
);
|
||||
expect(result).toEqual(testData.output);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user