Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIO-9280 updated validation of value property #185

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { expect } from 'chai';
import { set } from 'lodash';
import { FieldError } from 'error';
import { SelectBoxesComponent } from 'types';
import {
simpleRadioField,
simpleSelectBoxes
} from './fixtures/components';
import { generateProcessorContext } from './fixtures/util';
import { validateValueProperty } from '../validateValueProperty';

describe('validateValueProperty', function () {
it('Validating a component with support for different types will return null', async function () {
const component = simpleRadioField;
const data = {
component: 'test',
};
const context = generateProcessorContext(component, data);
const result = await validateValueProperty(context);
expect(result).to.equal(null);
});

it('Validating a select boxes component with values data source will return null', async function () {
const component = simpleSelectBoxes;
const data = {
component: {
foo: false,
bar: false,
baz: false,
biz: false,
},
};
const context = generateProcessorContext(component, data);
const result = await validateValueProperty(context);
expect(result).to.equal(null);
});

it('Validating a select boxes component with url data source without options building will return null', async function () {
const component: SelectBoxesComponent = {
...simpleSelectBoxes,
dataSrc: 'url',
values: [],
data: {
url: 'http://localhost:8080/numbers',
headers: [],
},
};
const data = {component: { 'true': true }};

const context = generateProcessorContext(component, data);
const result = await validateValueProperty(context);
expect(result).to.equal(null);
});

it('Validating a select boxes component with url data source without options building will return error', async function () {
const component: SelectBoxesComponent = {
...simpleSelectBoxes,
dataSrc: 'url',
values: [],
data: {
url: 'http://localhost:8080/numbers',
headers: [],
},
};
const data = {component: { 'true': true }};

const context = generateProcessorContext(component, data);
set(context, 'instance.options.building', true);
const result = await validateValueProperty(context);
expect(result).to.be.instanceOf(FieldError);
expect(result?.errorKeyOrMessage).to.equal('invalidValueProperty');
});
});
39 changes: 12 additions & 27 deletions src/process/validation/rules/validateValueProperty.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { isEmpty, isUndefined, isObject } from 'lodash';
import { FieldError } from 'error';
import { ListComponent, RuleFn, RuleFnSync, ValidationContext } from 'types';
import { ProcessorInfo } from 'types/process/ProcessorInfo';
Expand All @@ -7,58 +6,44 @@ const isValidatableListComponent = (comp: any): comp is ListComponent => {
return (
comp &&
comp.type &&
(comp.type === 'radio' || comp.type === 'selectboxes' || comp.type === 'select')
comp.type === 'selectboxes'
);
};

export const shouldValidate = (context: ValidationContext) => {
const { component, value } = context;
const { component, instance} = context;
if (!isValidatableListComponent(component)) {
return false;
}
if (component.dataSrc !== 'url') {
return false;
}
if (!value || (typeof value === 'object' && isEmpty(value))) {
return false;
}
const valueProperty = component.valueProperty;
if (!valueProperty) {
return false;
if ((instance as any)?.options?.building) {
return true;
}
return true;
return false;
};

export const validateValueProperty: RuleFn = async (context: ValidationContext) => {
return validateValuePropertySync(context);
};

export const validateValuePropertySync: RuleFnSync = (context: ValidationContext) => {
const { component, value } = context;
const { value, instance } = context;
if (!shouldValidate(context)) {
return null;
}
const error = new FieldError('invalidValueProperty', context);
// TODO: at some point in the radio component's change pipeline, object values are coerced into strings; testing for
// '[object Object]' is an ugly way to determine whether or not the ValueProperty is invalid, but it'll have to do
// for now

if (
component.inputType === 'radio' &&
(isUndefined(value) || isObject(value) || value === '[object Object]')
Object.entries(value as any).some(
([key, value]) => value && (key === '[object Object]' || key === 'true' || key === 'false'),
) ||
(instance && instance.loadedOptions?.some(option => option.invalid))
) {
return error;
}
// TODO: a cousin to the above issue, but sometimes ValueProperty will resolve to a boolean value so the keys in
// e.g. SelectBoxes components will strings coerced from booleans; again, not pretty, but good enough for now
else if (component.inputType !== 'radio') {
if (
Object.entries(value as any).some(
([key, value]) => value && (key === '[object Object]' || key === 'true' || key === 'false'),
)
) {
return error;
}
}

return null;
};

Expand Down
1 change: 1 addition & 0 deletions src/types/PassedComponentInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export type PassedComponentInstance = {
evaluate: (expression: string, additionalContext?: Record<string, any>) => any;
interpolate: (text: string, additionalContext?: Record<string, any>) => string;
shouldSkipValidation: (data?: DataObject, row?: DataObject) => boolean;
loadedOptions?: Array<{invalid: boolean, value: any, label: string}>
};
Loading